Browse Source

好友优化(改为逻辑删除)

master
xsx 1 year ago
parent
commit
729fab4ef1
  1. 6
      im-common/src/main/java/com/bx/imcommon/enums/IMTerminalType.java
  2. 1
      im-platform/src/main/java/com/bx/implatform/config/MinIoClientConfig.java
  3. 21
      im-platform/src/main/java/com/bx/implatform/controller/FriendController.java
  4. 1
      im-platform/src/main/java/com/bx/implatform/controller/LoginController.java
  5. 1
      im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java
  6. 5
      im-platform/src/main/java/com/bx/implatform/entity/Friend.java
  7. 2
      im-platform/src/main/java/com/bx/implatform/enums/MessageType.java
  8. 3
      im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java
  9. 30
      im-platform/src/main/java/com/bx/implatform/service/FriendService.java
  10. 7
      im-platform/src/main/java/com/bx/implatform/service/SensitiveWordService.java
  11. 217
      im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java
  12. 2
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java
  13. 8
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java
  14. 30
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  15. 1
      im-platform/src/main/java/com/bx/implatform/task/consumer/GroupBannedConsumerTask.java
  16. 1
      im-platform/src/main/java/com/bx/implatform/task/consumer/GroupUnbanConsumerTask.java
  17. 3
      im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java
  18. 1
      im-platform/src/main/resources/application-dev.yml
  19. 2
      im-server/src/main/resources/application-dev.yml
  20. 33
      im-uniapp/App.vue
  21. 2
      im-uniapp/common/enums.js
  22. 35
      im-uniapp/pages/chat/chat-box.vue
  23. 32
      im-uniapp/pages/common/user-info.vue
  24. 7
      im-uniapp/pages/friend/friend-add.vue
  25. 9
      im-uniapp/pages/friend/friend.vue
  26. 3
      im-uniapp/pages/group/group-info.vue
  27. 8
      im-uniapp/pages/group/group-invite.vue
  28. 21
      im-uniapp/store/chatStore.js
  29. 29
      im-uniapp/store/friendStore.js
  30. 2
      im-web/src/api/enums.js
  31. 36
      im-web/src/components/chat/ChatBox.vue
  32. 9
      im-web/src/components/common/UserInfo.vue
  33. 9
      im-web/src/components/friend/AddFriend.vue
  34. 4
      im-web/src/components/group/AddGroupMember.vue
  35. 20
      im-web/src/store/chatStore.js
  36. 41
      im-web/src/store/friendStore.js
  37. 56
      im-web/src/view/Friend.vue
  38. 38
      im-web/src/view/Home.vue

6
im-common/src/main/java/com/bx/imcommon/enums/IMTerminalType.java

@ -20,7 +20,11 @@ public enum IMTerminalType {
/** /**
* pc * pc
*/ */
PC(2, "pc"); PC(2, "pc"),
/**
* 未知
*/
UNKNOW(-1, "未知");
private final Integer code; private final Integer code;

1
im-platform/src/main/java/com/bx/implatform/config/MinIoClientConfig.java

@ -2,7 +2,6 @@ package com.bx.implatform.config;
import com.bx.implatform.config.props.MinioProperties; import com.bx.implatform.config.props.MinioProperties;
import io.minio.MinioClient; import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;

21
im-platform/src/main/java/com/bx/implatform/controller/FriendController.java

@ -1,11 +1,9 @@
package com.bx.implatform.controller; package com.bx.implatform.controller;
import com.bx.implatform.annotation.RepeatSubmit; import com.bx.implatform.annotation.RepeatSubmit;
import com.bx.implatform.entity.Friend;
import com.bx.implatform.result.Result; import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils; import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.FriendService; import com.bx.implatform.service.FriendService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.vo.FriendVO; import com.bx.implatform.vo.FriendVO;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -15,7 +13,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@Tag(name = "好友") @Tag(name = "好友")
@RestController @RestController
@ -28,15 +25,7 @@ public class FriendController {
@GetMapping("/list") @GetMapping("/list")
@Operation(summary = "好友列表", description = "获取好友列表") @Operation(summary = "好友列表", description = "获取好友列表")
public Result<List<FriendVO>> findFriends() { public Result<List<FriendVO>> findFriends() {
List<Friend> friends = friendService.findFriendByUserId(SessionContext.getSession().getUserId()); return ResultUtils.success(friendService.findFriends());
List<FriendVO> vos = friends.stream().map(f -> {
FriendVO vo = new FriendVO();
vo.setId(f.getFriendId());
vo.setHeadImage(f.getFriendHeadImage());
vo.setNickName(f.getFriendNickName());
return vo;
}).collect(Collectors.toList());
return ResultUtils.success(vos);
} }
@ -62,13 +51,5 @@ public class FriendController {
return ResultUtils.success(); return ResultUtils.success();
} }
@PutMapping("/update")
@Operation(summary = "更新好友信息", description = "更新好友头像或昵称")
public Result modifyFriend(@Valid @RequestBody FriendVO vo) {
friendService.update(vo);
return ResultUtils.success();
}
} }

1
im-platform/src/main/java/com/bx/implatform/controller/LoginController.java

@ -1,6 +1,5 @@
package com.bx.implatform.controller; package com.bx.implatform.controller;
import com.bx.implatform.annotation.RepeatSubmit;
import com.bx.implatform.dto.LoginDTO; import com.bx.implatform.dto.LoginDTO;
import com.bx.implatform.dto.ModifyPwdDTO; import com.bx.implatform.dto.ModifyPwdDTO;
import com.bx.implatform.dto.RegisterDTO; import com.bx.implatform.dto.RegisterDTO;

1
im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java

@ -1,6 +1,5 @@
package com.bx.implatform.controller; package com.bx.implatform.controller;
import com.bx.implatform.annotation.OnlineCheck;
import com.bx.implatform.result.Result; import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils; import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.WebrtcPrivateService; import com.bx.implatform.service.WebrtcPrivateService;

5
im-platform/src/main/java/com/bx/implatform/entity/Friend.java

@ -45,6 +45,11 @@ public class Friend{
*/ */
private String friendHeadImage; private String friendHeadImage;
/**
* 是否已删除
*/
private Boolean deleted;
/** /**
* 创建时间 * 创建时间
*/ */

2
im-platform/src/main/java/com/bx/implatform/enums/MessageType.java

@ -33,6 +33,8 @@ public enum MessageType {
USER_BANNED(50,"用户封禁"), USER_BANNED(50,"用户封禁"),
GROUP_BANNED(51,"群聊封禁"), GROUP_BANNED(51,"群聊封禁"),
GROUP_UNBAN(52,"群聊解封"), GROUP_UNBAN(52,"群聊解封"),
FRIEND_NEW(80, "新增好友"),
FRIEND_DEL(81, "删除好友"),
RTC_CALL_VOICE(100, "语音呼叫"), RTC_CALL_VOICE(100, "语音呼叫"),
RTC_CALL_VIDEO(101, "视频呼叫"), RTC_CALL_VIDEO(101, "视频呼叫"),
RTC_ACCEPT(102, "接受"), RTC_ACCEPT(102, "接受"),

3
im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java

@ -17,6 +17,7 @@ import org.springframework.context.annotation.Lazy;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
@Slf4j @Slf4j
@ -31,7 +32,7 @@ public class PrivateMessageListener implements MessageListener<PrivateMessageVO>
for(IMSendResult<PrivateMessageVO> result : results){ for(IMSendResult<PrivateMessageVO> result : results){
PrivateMessageVO messageInfo = result.getData(); PrivateMessageVO messageInfo = result.getData();
// 更新消息状态,这里只处理成功消息,失败的消息继续保持未读状态 // 更新消息状态,这里只处理成功消息,失败的消息继续保持未读状态
if (result.getCode().equals(IMSendCode.SUCCESS.code())) { if (result.getCode().equals(IMSendCode.SUCCESS.code()) && !Objects.isNull(messageInfo.getId())) {
messageIds.add(messageInfo.getId()); messageIds.add(messageInfo.getId());
log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal()); log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal());
} }

30
im-platform/src/main/java/com/bx/implatform/service/FriendService.java

@ -17,14 +17,27 @@ public interface FriendService extends IService<Friend> {
*/ */
Boolean isFriend(Long userId1, Long userId2); Boolean isFriend(Long userId1, Long userId2);
/**
* 查询用户的所有好友,包括已删除的
*
* @return 好友列表
*/
List<Friend> findAllFriends();
/** /**
* 查询用户的所有好友 * 查询用户的所有好友
* *
* @param userId 用户id * @param friendIds 好友id
* @return 好友列表
*/
List<Friend> findByFriendIds(List<Long> friendIds);
/**
* 查询当前用户的所有好友
*
* @return 好友列表 * @return 好友列表
*/ */
List<Friend> findFriendByUserId(Long userId); List<FriendVO> findFriends();
/** /**
* 添加好友互相建立好友关系 * 添加好友互相建立好友关系
@ -41,17 +54,20 @@ public interface FriendService extends IService<Friend> {
void delFriend(Long friendId); void delFriend(Long friendId);
/** /**
* 更新好友信息主要是头像和昵称 * 查询指定的某个好友信息
* *
* @param vo 好友vo * @param friendId 好友的用户id
* @return 好友信息
*/ */
void update(FriendVO vo); FriendVO findFriend(Long friendId);
/** /**
* 查询指定的某个好友信息 * 绑定好友关系
* *
* @param userId 好友的id
* @param friendId 好友的用户id * @param friendId 好友的用户id
* @return 好友信息 * @return 好友信息
*/ */
FriendVO findFriend(Long friendId); void bindFriend(Long userId, Long friendId);
} }

7
im-platform/src/main/java/com/bx/implatform/service/SensitiveWordService.java

@ -1,14 +1,7 @@
package com.bx.implatform.service; package com.bx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.bx.implatform.dto.LoginDTO;
import com.bx.implatform.dto.ModifyPwdDTO;
import com.bx.implatform.dto.RegisterDTO;
import com.bx.implatform.entity.SensitiveWord; import com.bx.implatform.entity.SensitiveWord;
import com.bx.implatform.entity.User;
import com.bx.implatform.vo.LoginVO;
import com.bx.implatform.vo.OnlineTerminalVO;
import com.bx.implatform.vo.UserVO;
import java.util.List; import java.util.List;

217
im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java

@ -1,19 +1,31 @@
package com.bx.implatform.service.impl; package com.bx.implatform.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imclient.IMClient;
import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.IMPrivateMessage;
import com.bx.imcommon.model.IMUserInfo;
import com.bx.implatform.contant.RedisKey; import com.bx.implatform.contant.RedisKey;
import com.bx.implatform.entity.Friend; import com.bx.implatform.entity.Friend;
import com.bx.implatform.entity.PrivateMessage;
import com.bx.implatform.entity.User; import com.bx.implatform.entity.User;
import com.bx.implatform.enums.MessageStatus;
import com.bx.implatform.enums.MessageType;
import com.bx.implatform.exception.GlobalException; import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.mapper.FriendMapper; import com.bx.implatform.mapper.FriendMapper;
import com.bx.implatform.mapper.PrivateMessageMapper;
import com.bx.implatform.mapper.UserMapper; import com.bx.implatform.mapper.UserMapper;
import com.bx.implatform.service.FriendService; import com.bx.implatform.service.FriendService;
import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession; import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.vo.FriendVO; import com.bx.implatform.vo.FriendVO;
import com.bx.implatform.vo.PrivateMessageVO;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext; import org.springframework.aop.framework.AopContext;
@ -23,8 +35,10 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
@ -32,15 +46,33 @@ import java.util.Objects;
@CacheConfig(cacheNames = RedisKey.IM_CACHE_FRIEND) @CacheConfig(cacheNames = RedisKey.IM_CACHE_FRIEND)
public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> implements FriendService { public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> implements FriendService {
private final PrivateMessageMapper privateMessageMapper;
private final UserMapper userMapper; private final UserMapper userMapper;
private final IMClient imClient;
@Override @Override
public List<Friend> findFriendByUserId(Long userId) { public List<Friend> findAllFriends() {
LambdaQueryWrapper<Friend> queryWrapper = Wrappers.lambdaQuery(); Long userId = SessionContext.getSession().getUserId();
queryWrapper.eq(Friend::getUserId, userId); LambdaQueryWrapper<Friend> wrapper = Wrappers.lambdaQuery();
return this.list(queryWrapper); wrapper.eq(Friend::getUserId, userId);
return this.list(wrapper);
} }
@Override
public List<Friend> findByFriendIds(List<Long> friendIds) {
Long userId = SessionContext.getSession().getUserId();
LambdaQueryWrapper<Friend> wrapper = Wrappers.lambdaQuery();
wrapper.eq(Friend::getUserId, userId);
wrapper.in(Friend::getFriendId, friendIds);
wrapper.eq(Friend::getDeleted,false);
return this.list(wrapper);
}
@Override
public List<FriendVO> findFriends() {
List<Friend> friends = this.findAllFriends();
return friends.stream().map(this::conver).collect(Collectors.toList());
}
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
@ -50,53 +82,37 @@ public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> impleme
throw new GlobalException("不允许添加自己为好友"); throw new GlobalException("不允许添加自己为好友");
} }
// 互相绑定好友关系 // 互相绑定好友关系
FriendServiceImpl proxy = (FriendServiceImpl) AopContext.currentProxy(); FriendServiceImpl proxy = (FriendServiceImpl)AopContext.currentProxy();
proxy.bindFriend(userId, friendId); proxy.bindFriend(userId, friendId);
proxy.bindFriend(friendId, userId); proxy.bindFriend(friendId, userId);
// 推送添加好友提示
sendAddTipMessage(friendId);
log.info("添加好友,用户id:{},好友id:{}", userId, friendId); log.info("添加好友,用户id:{},好友id:{}", userId, friendId);
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public void delFriend(Long friendId) { public void delFriend(Long friendId) {
long userId = SessionContext.getSession().getUserId(); Long userId = SessionContext.getSession().getUserId();
// 互相解除好友关系,走代理清理缓存 // 互相解除好友关系,走代理清理缓存
FriendServiceImpl proxy = (FriendServiceImpl) AopContext.currentProxy(); FriendServiceImpl proxy = (FriendServiceImpl)AopContext.currentProxy();
proxy.unbindFriend(userId, friendId); proxy.unbindFriend(userId, friendId);
proxy.unbindFriend(friendId, userId); proxy.unbindFriend(friendId, userId);
// 推送解除好友提示
sendDelTipMessage(friendId);
log.info("删除好友,用户id:{},好友id:{}", userId, friendId); log.info("删除好友,用户id:{},好友id:{}", userId, friendId);
} }
@Cacheable(key = "#userId1+':'+#userId2") @Cacheable(key = "#userId1+':'+#userId2")
@Override @Override
public Boolean isFriend(Long userId1, Long userId2) { public Boolean isFriend(Long userId1, Long userId2) {
QueryWrapper<Friend> queryWrapper = new QueryWrapper<>(); LambdaQueryWrapper<Friend> wrapper = Wrappers.lambdaQuery();
queryWrapper.lambda() wrapper.eq(Friend::getUserId, userId1);
.eq(Friend::getUserId, userId1) wrapper.eq(Friend::getFriendId, userId2);
.eq(Friend::getFriendId, userId2); wrapper.eq(Friend::getDeleted,false);
return this.count(queryWrapper) > 0; return this.exists(wrapper);
} }
@Override
public void update(FriendVO vo) {
long userId = SessionContext.getSession().getUserId();
QueryWrapper<Friend> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Friend::getUserId, userId)
.eq(Friend::getFriendId, vo.getId());
Friend f = this.getOne(queryWrapper);
if (Objects.isNull(f)) {
throw new GlobalException("对方不是您的好友");
}
f.setFriendHeadImage(vo.getHeadImage());
f.setFriendNickName(vo.getNickName());
this.updateById(f);
}
/** /**
* 单向绑定好友关系 * 单向绑定好友关系
* *
@ -105,22 +121,23 @@ public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> impleme
*/ */
@CacheEvict(key = "#userId+':'+#friendId") @CacheEvict(key = "#userId+':'+#friendId")
public void bindFriend(Long userId, Long friendId) { public void bindFriend(Long userId, Long friendId) {
QueryWrapper<Friend> queryWrapper = new QueryWrapper<>(); QueryWrapper<Friend> wrapper = new QueryWrapper<>();
queryWrapper.lambda() wrapper.lambda().eq(Friend::getUserId, userId).eq(Friend::getFriendId, friendId);
.eq(Friend::getUserId, userId) Friend friend = this.getOne(wrapper);
.eq(Friend::getFriendId, friendId); if (Objects.isNull(friend)) {
if (this.count(queryWrapper) == 0) { friend = new Friend();
Friend friend = new Friend();
friend.setUserId(userId);
friend.setFriendId(friendId);
User friendInfo = userMapper.selectById(friendId);
friend.setFriendHeadImage(friendInfo.getHeadImage());
friend.setFriendNickName(friendInfo.getNickName());
this.save(friend);
} }
friend.setUserId(userId);
friend.setFriendId(friendId);
User friendInfo = userMapper.selectById(friendId);
friend.setFriendHeadImage(friendInfo.getHeadImage());
friend.setFriendNickName(friendInfo.getNickName());
friend.setDeleted(false);
this.saveOrUpdate(friend);
// 推送好友变化信息s
sendAddFriendMessage(userId, friendId, friend);
} }
/** /**
* 单向解除好友关系 * 单向解除好友关系
* *
@ -129,30 +146,112 @@ public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> impleme
*/ */
@CacheEvict(key = "#userId+':'+#friendId") @CacheEvict(key = "#userId+':'+#friendId")
public void unbindFriend(Long userId, Long friendId) { public void unbindFriend(Long userId, Long friendId) {
QueryWrapper<Friend> queryWrapper = new QueryWrapper<>(); // 逻辑删除
queryWrapper.lambda() LambdaUpdateWrapper<Friend> wrapper = Wrappers.lambdaUpdate();
.eq(Friend::getUserId, userId) wrapper.eq(Friend::getUserId, userId);
.eq(Friend::getFriendId, friendId); wrapper.eq(Friend::getFriendId, friendId);
List<Friend> friends = this.list(queryWrapper); wrapper.set(Friend::getDeleted,true);
friends.forEach(friend -> this.removeById(friend.getId())); this.update(wrapper);
// 推送好友变化信息
sendDelFriendMessage(userId, friendId);
} }
@Override @Override
public FriendVO findFriend(Long friendId) { public FriendVO findFriend(Long friendId) {
UserSession session = SessionContext.getSession(); UserSession session = SessionContext.getSession();
QueryWrapper<Friend> wrapper = new QueryWrapper<>(); LambdaQueryWrapper<Friend> wrapper = Wrappers.lambdaQuery();
wrapper.lambda() wrapper.eq(Friend::getUserId, session.getUserId());
.eq(Friend::getUserId, session.getUserId()) wrapper.eq(Friend::getFriendId, friendId);
.eq(Friend::getFriendId, friendId);
Friend friend = this.getOne(wrapper); Friend friend = this.getOne(wrapper);
if (Objects.isNull(friend)) { if (Objects.isNull(friend)) {
throw new GlobalException("对方不是您的好友"); throw new GlobalException("对方不是您的好友");
} }
return conver(friend);
}
private FriendVO conver(Friend f) {
FriendVO vo = new FriendVO(); FriendVO vo = new FriendVO();
vo.setId(friend.getFriendId()); vo.setId(f.getFriendId());
vo.setHeadImage(friend.getFriendHeadImage()); vo.setHeadImage(f.getFriendHeadImage());
vo.setNickName(friend.getFriendNickName()); vo.setNickName(f.getFriendNickName());
vo.setDeleted(f.getDeleted());
return vo; return vo;
} }
void sendAddFriendMessage(Long userId, Long friendId, Friend friend) {
// 推送好友状态信息
PrivateMessageVO msgInfo = new PrivateMessageVO();
msgInfo.setSendId(friendId);
msgInfo.setRecvId(userId);
msgInfo.setSendTime(new Date());
msgInfo.setType(MessageType.FRIEND_NEW.code());
FriendVO vo = conver(friend);
msgInfo.setContent(JSON.toJSONString(vo));
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(friendId, IMTerminalType.UNKNOW.code()));
sendMessage.setRecvId(userId);
sendMessage.setData(msgInfo);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
imClient.sendPrivateMessage(sendMessage);
}
void sendDelFriendMessage(Long userId, Long friendId) {
// 推送好友状态信息
PrivateMessageVO msgInfo = new PrivateMessageVO();
msgInfo.setSendId(friendId);
msgInfo.setRecvId(userId);
msgInfo.setSendTime(new Date());
msgInfo.setType(MessageType.FRIEND_DEL.code());
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(friendId, IMTerminalType.UNKNOW.code()));
sendMessage.setRecvId(userId);
sendMessage.setData(msgInfo);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
imClient.sendPrivateMessage(sendMessage);
}
void sendAddTipMessage(Long friendId) {
UserSession session = SessionContext.getSession();
PrivateMessage msg = new PrivateMessage();
msg.setSendId(session.getUserId());
msg.setRecvId(friendId);
msg.setContent("你们已成为好友,现在可以开始聊天了");
msg.setSendTime(new Date());
msg.setStatus(MessageStatus.UNSEND.code());
msg.setType(MessageType.TIP_TEXT.code());
privateMessageMapper.insert(msg);
// 推给对方
PrivateMessageVO messageInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(friendId);
sendMessage.setSendToSelf(false);
sendMessage.setData(messageInfo);
imClient.sendPrivateMessage(sendMessage);
// 推给自己
sendMessage.setRecvId(session.getUserId());
imClient.sendPrivateMessage(sendMessage);
}
void sendDelTipMessage(Long friendId){
UserSession session = SessionContext.getSession();
// 推送好友状态信息
PrivateMessage msg = new PrivateMessage();
msg.setSendId(session.getUserId());
msg.setRecvId(friendId);
msg.setSendTime(new Date());
msg.setType(MessageType.TIP_TEXT.code());
msg.setStatus(MessageStatus.UNSEND.code());
msg.setContent("你们的好友关系已被解除");
privateMessageMapper.insert(msg);
// 推送
PrivateMessageVO messageInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(friendId, IMTerminalType.UNKNOW.code()));
sendMessage.setRecvId(friendId);
sendMessage.setData(messageInfo);
imClient.sendPrivateMessage(sendMessage);
}
} }

2
im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java

@ -31,10 +31,8 @@ import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils; import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.util.SensitiveFilterUtil; import com.bx.implatform.util.SensitiveFilterUtil;
import com.bx.implatform.vo.GroupMessageVO; import com.bx.implatform.vo.GroupMessageVO;
import com.google.common.base.Splitter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;

8
im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java

@ -227,14 +227,12 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
throw new GlobalException("群聊人数不能大于" + Constant.MAX_GROUP_MEMBER + "人"); throw new GlobalException("群聊人数不能大于" + Constant.MAX_GROUP_MEMBER + "人");
} }
// 找出好友信息 // 找出好友信息
List<Friend> friends = friendsService.findFriendByUserId(session.getUserId()); List<Friend> friends = friendsService.findByFriendIds(vo.getFriendIds());
List<Friend> friendsList = vo.getFriendIds().stream() if (vo.getFriendIds().size() != friends.size()) {
.map(id -> friends.stream().filter(f -> f.getFriendId().equals(id)).findFirst().get()).toList();
if (friendsList.size() != vo.getFriendIds().size()) {
throw new GlobalException("部分用户不是您的好友,邀请失败"); throw new GlobalException("部分用户不是您的好友,邀请失败");
} }
// 批量保存成员数据 // 批量保存成员数据
List<GroupMember> groupMembers = friendsList.stream().map(f -> { List<GroupMember> groupMembers = friends.stream().map(f -> {
Optional<GroupMember> optional = Optional<GroupMember> optional =
members.stream().filter(m -> m.getUserId().equals(f.getFriendId())).findFirst(); members.stream().filter(m -> m.getUserId().equals(f.getFriendId())).findFirst();
GroupMember groupMember = optional.orElseGet(GroupMember::new); GroupMember groupMember = optional.orElseGet(GroupMember::new);

30
im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java

@ -1,6 +1,5 @@
package com.bx.implatform.service.impl; package com.bx.implatform.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -12,7 +11,6 @@ import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.IMPrivateMessage; import com.bx.imcommon.model.IMPrivateMessage;
import com.bx.imcommon.model.IMUserInfo; import com.bx.imcommon.model.IMUserInfo;
import com.bx.implatform.dto.PrivateMessageDTO; import com.bx.implatform.dto.PrivateMessageDTO;
import com.bx.implatform.entity.Friend;
import com.bx.implatform.entity.PrivateMessage; import com.bx.implatform.entity.PrivateMessage;
import com.bx.implatform.enums.MessageStatus; import com.bx.implatform.enums.MessageStatus;
import com.bx.implatform.enums.MessageType; import com.bx.implatform.enums.MessageType;
@ -31,7 +29,9 @@ import org.apache.commons.lang3.time.DateUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.*; import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
@ -135,29 +135,19 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
@Override @Override
public void pullOfflineMessage(Long minId) { public void pullOfflineMessage(Long minId) {
UserSession session = SessionContext.getSession(); UserSession session = SessionContext.getSession();
if (!imClient.isOnline(session.getUserId())) {
throw new GlobalException("网络连接失败,无法拉取离线消息");
}
// 查询用户好友列表
List<Friend> friends = friendService.findFriendByUserId(session.getUserId());
if (friends.isEmpty()) {
// 关闭加载中标志
this.sendLoadingMessage(false);
return;
}
// 开启加载中标志 // 开启加载中标志
this.sendLoadingMessage(true); this.sendLoadingMessage(true);
List<Long> friendIds = friends.stream().map(Friend::getFriendId).collect(Collectors.toList());
// 获取当前用户的消息 // 获取当前用户的消息
LambdaQueryWrapper<PrivateMessage> queryWrapper = Wrappers.lambdaQuery(); LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
// 只能拉取最近3个月的消息,移动端只拉取一个月消息 // 只能拉取最近3个月的消息,移动端只拉取一个月消息
int months = session.getTerminal().equals(IMTerminalType.APP.code()) ? 1 : 3; int months = session.getTerminal().equals(IMTerminalType.APP.code()) ? 1 : 3;
Date minDate = DateUtils.addMonths(new Date(), -months); Date minDate = DateUtils.addMonths(new Date(), -months);
queryWrapper.gt(PrivateMessage::getId, minId).ge(PrivateMessage::getSendTime, minDate).and(wrap -> wrap.and( wrapper.gt(PrivateMessage::getId, minId);
wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()).in(PrivateMessage::getRecvId, friendIds)) wrapper.ge(PrivateMessage::getSendTime, minDate);
.or(wp -> wp.eq(PrivateMessage::getRecvId, session.getUserId()).in(PrivateMessage::getSendId, friendIds))) wrapper.and(wp -> wp.eq(PrivateMessage::getSendId, session.getUserId()).or()
.orderByAsc(PrivateMessage::getId); .eq(PrivateMessage::getRecvId, session.getUserId()));
List<PrivateMessage> messages = this.list(queryWrapper); wrapper.orderByAsc(PrivateMessage::getId);
List<PrivateMessage> messages = this.list(wrapper);
// 推送消息 // 推送消息
for (PrivateMessage m : messages) { for (PrivateMessage m : messages) {
PrivateMessageVO vo = BeanUtils.copyProperties(m, PrivateMessageVO.class); PrivateMessageVO vo = BeanUtils.copyProperties(m, PrivateMessageVO.class);

1
im-platform/src/main/java/com/bx/implatform/task/consumer/GroupBannedConsumerTask.java

@ -61,7 +61,6 @@ public class GroupBannedConsumerTask extends RedisMQConsumer<GroupBanDTO> {
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>(); IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(Constant.SYS_USER_ID, IMTerminalType.PC.code())); sendMessage.setSender(new IMUserInfo(Constant.SYS_USER_ID, IMTerminalType.PC.code()));
sendMessage.setRecvIds(userIds); sendMessage.setRecvIds(userIds);
sendMessage.setSendResult(true);
sendMessage.setSendToSelf(false); sendMessage.setSendToSelf(false);
sendMessage.setData(msgInfo); sendMessage.setData(msgInfo);
imClient.sendGroupMessage(sendMessage); imClient.sendGroupMessage(sendMessage);

1
im-platform/src/main/java/com/bx/implatform/task/consumer/GroupUnbanConsumerTask.java

@ -60,7 +60,6 @@ public class GroupUnbanConsumerTask extends RedisMQConsumer<GroupUnbanDTO> {
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>(); IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(Constant.SYS_USER_ID, IMTerminalType.PC.code())); sendMessage.setSender(new IMUserInfo(Constant.SYS_USER_ID, IMTerminalType.PC.code()));
sendMessage.setRecvIds(userIds); sendMessage.setRecvIds(userIds);
sendMessage.setSendResult(true);
sendMessage.setSendToSelf(false); sendMessage.setSendToSelf(false);
sendMessage.setData(msgInfo); sendMessage.setData(msgInfo);
imClient.sendGroupMessage(sendMessage); imClient.sendGroupMessage(sendMessage);

3
im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java

@ -19,4 +19,7 @@ public class FriendVO {
@Schema(description = "好友头像") @Schema(description = "好友头像")
private String headImage; private String headImage;
@Schema(description = "是否已删除")
private Boolean deleted;
} }

1
im-platform/src/main/resources/application-dev.yml

@ -8,7 +8,6 @@ spring:
redis: redis:
host: localhost host: localhost
port: 6379 port: 6379
database: 1
minio: minio:
endpoint: http://127.0.0.1:9000 #内网地址 endpoint: http://127.0.0.1:9000 #内网地址

2
im-server/src/main/resources/application-dev.yml

@ -3,5 +3,3 @@ spring:
redis: redis:
host: 127.0.0.1 host: 127.0.0.1
port: 6379 port: 6379
database: 1

33
im-uniapp/App.vue

@ -138,10 +138,19 @@ export default {
this.chatStore.recallMessage(msg, chatInfo); this.chatStore.recallMessage(msg, chatInfo);
return; return;
} }
//
if (msg.type == this.$enums.MESSAGE_TYPE.FRIEND_NEW) {
this.friendStore.addFriend(JSON.parse(msg.content));
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.FRIEND_DEL) {
this.friendStore.removeFriend(friendId);
return;
}
// //
this.loadFriendInfo(friendId, (friend) => { let friend = this.loadFriendInfo(friendId);
this.insertPrivateMessage(friend, msg); this.insertPrivateMessage(friend, msg);
})
}, },
insertPrivateMessage(friend, msg) { insertPrivateMessage(friend, msg) {
// //
@ -280,17 +289,15 @@ export default {
}, },
loadFriendInfo(id, callback) { loadFriendInfo(id, callback) {
let friend = this.friendStore.findFriend(id); let friend = this.friendStore.findFriend(id);
if (friend) { if (!friend) {
callback(friend); console.log("未知用户:", id)
} else { friend = {
http({ id: id,
url: `/friend/find/${id}`, showNickName: "未知用户",
method: 'GET' headImage: ""
}).then((friend) => { }
this.friendStore.addFriend(friend);
callback(friend)
})
} }
return friend;
}, },
loadGroupInfo(id, callback) { loadGroupInfo(id, callback) {
let group = this.groupStore.findGroup(id); let group = this.groupStore.findGroup(id);

2
im-uniapp/common/enums.js

@ -14,6 +14,8 @@ const MESSAGE_TYPE = {
ACT_RT_VOICE: 40, ACT_RT_VOICE: 40,
ACT_RT_VIDEO: 41, ACT_RT_VIDEO: 41,
USER_BANNED: 50, USER_BANNED: 50,
FRIEND_NEW: 80,
FRIEND_DEL: 81,
RTC_CALL_VOICE: 100, RTC_CALL_VOICE: 100,
RTC_CALL_VIDEO: 101, RTC_CALL_VIDEO: 101,
RTC_ACCEPT: 102, RTC_ACCEPT: 102,

35
im-uniapp/pages/chat/chat-box.vue

@ -119,7 +119,7 @@ export default {
data() { data() {
return { return {
chat: {}, chat: {},
friend: {}, userInfo: {},
group: {}, group: {},
groupMembers: [], groupMembers: [],
isReceipt: false, // isReceipt: false, //
@ -578,7 +578,7 @@ export default {
}) })
} else { } else {
uni.navigateTo({ uni.navigateTo({
url: "/pages/common/user-info?id=" + this.friend.id url: "/pages/common/user-info?id=" + this.userInfo.id
}) })
} }
}, },
@ -657,15 +657,29 @@ export default {
this.groupMembers = groupMembers; this.groupMembers = groupMembers;
}); });
}, },
updateFriendInfo() {
if (this.isFriend) {
// storestore
let friend = JSON.parse(JSON.stringify(this.friend));
friend.headImage = this.userInfo.headImageThumb;
friend.nickName = this.userInfo.nickName;
friend.showNickName = friend.remarkNickName ? friend.remarkNickName : friend.nickName;
//
this.friendStore.updateFriend(friend);
//
this.chatStore.updateChatFromFriend(friend);
} else {
this.chatStore.updateChatFromUser(this.userInfo);
}
},
loadFriend(friendId) { loadFriend(friendId) {
// //
this.$http({ this.$http({
url: `/user/find/${friendId}`, url: `/user/find/${friendId}`,
method: 'GET' method: 'GET'
}).then((friend) => { }).then((userInfo) => {
this.friend = friend; this.userInfo = userInfo;
this.chatStore.updateChatFromFriend(friend); this.updateFriendInfo();
this.friendStore.updateFriend(friend);
}) })
}, },
rpxTopx(rpx) { rpxTopx(rpx) {
@ -807,7 +821,7 @@ export default {
} }
if (this.chat.type == "PRIVATE") { if (this.chat.type == "PRIVATE") {
msgInfo.recvId = this.mine.id msgInfo.recvId = this.mine.id
msgInfo.content = "该用户已被管理员封禁,原因:" + this.friend.reason msgInfo.content = "该用户已被管理员封禁,原因:" + this.userInfo.reason
} else { } else {
msgInfo.groupId = this.group.id; msgInfo.groupId = this.group.id;
msgInfo.content = "本群聊已被管理员封禁,原因:" + this.group.reason msgInfo.content = "本群聊已被管理员封禁,原因:" + this.group.reason
@ -823,6 +837,9 @@ export default {
mine() { mine() {
return this.userStore.userInfo; return this.userStore.userInfo;
}, },
friend() {
return this.friendStore.findFriend(this.userInfo.id);
},
title() { title() {
if (!this.chat) { if (!this.chat) {
return ""; return "";
@ -850,7 +867,7 @@ export default {
return this.chat.unreadCount; return this.chat.unreadCount;
}, },
isBanned() { isBanned() {
return (this.chat.type == "PRIVATE" && this.friend.isBanned) || return (this.chat.type == "PRIVATE" && this.userInfo.isBanned) ||
(this.chat.type == "GROUP" && this.group.isBanned) (this.chat.type == "GROUP" && this.group.isBanned)
}, },
atUserItems() { atUserItems() {

32
im-uniapp/pages/common/user-info.vue

@ -82,7 +82,8 @@ export default {
id: this.userInfo.id, id: this.userInfo.id,
nickName: this.userInfo.nickName, nickName: this.userInfo.nickName,
headImage: this.userInfo.headImageThumb, headImage: this.userInfo.headImageThumb,
online: this.userInfo.online online: this.userInfo.online,
deleted: false
} }
this.friendStore.addFriend(friend); this.friendStore.addFriend(friend);
uni.showToast({ uni.showToast({
@ -113,20 +114,17 @@ export default {
}) })
}, },
updateFriendInfo() { updateFriendInfo() {
// storestore if (this.isFriend) {
let friend = JSON.parse(JSON.stringify(this.friendInfo)); // storestore
friend.headImage = this.userInfo.headImageThumb; let friend = JSON.parse(JSON.stringify(this.friendInfo));
friend.nickName = this.userInfo.nickName; friend.headImage = this.userInfo.headImageThumb;
this.$http({ friend.nickName = this.userInfo.nickName;
url: "/friend/update",
method: "PUT",
data: friend
}).then(() => {
// //
this.friendStore.updateFriend(friend); this.friendStore.updateFriend(friend);
// //
this.chatStore.updateChatFromFriend(this.userInfo); this.chatStore.updateChatFromFriend(this.userInfo);
}) }
}, },
loadUserInfo(id) { loadUserInfo(id) {
this.$http({ this.$http({
@ -135,21 +133,17 @@ export default {
}).then((user) => { }).then((user) => {
this.userInfo = user; this.userInfo = user;
// //
if (this.isFriend && (this.userInfo.headImageThumb != this.friendInfo.headImage || this.updateFriendInfo()
this.userInfo.nickName != this.friendInfo.nickName)) {
this.updateFriendInfo()
}
}) })
} }
}, },
computed: { computed: {
isFriend() { isFriend() {
return !!this.friendInfo; return this.friendStore.isFriend(this.userInfo.id);
}, },
friendInfo() { friendInfo() {
let friends = this.friendStore.friends; return this.friendStore.findFriend(this.userInfo.id);
let friend = friends.find((f) => f.id == this.userInfo.id);
return friend;
} }
}, },
onLoad(options) { onLoad(options) {

7
im-uniapp/pages/friend/friend-add.vue

@ -61,7 +61,8 @@ export default {
id: user.id, id: user.id,
nickName: user.nickName, nickName: user.nickName,
headImage: user.headImage, headImage: user.headImage,
online: user.online online: user.online,
delete: false
} }
this.friendStore.addFriend(friend); this.friendStore.addFriend(friend);
uni.showToast({ uni.showToast({
@ -76,9 +77,7 @@ export default {
}) })
}, },
isFriend(userId) { isFriend(userId) {
let friends = this.friendStore.friends; return this.friendStore.isFriend(userId);
let friend = friends.find((f) => f.id == userId);
return !!friend;
} }
} }
} }

9
im-uniapp/pages/friend/friend.vue

@ -8,7 +8,7 @@
placeholder="点击搜索好友"></uni-search-bar> placeholder="点击搜索好友"></uni-search-bar>
</view> </view>
</view> </view>
<view class="friend-tip" v-if="friends.length == 0"> <view class="friend-tip" v-if="friendIdx.length == 0">
温馨提示您现在还没有任何好友快点击右上方'+'按钮添加好友吧~ 温馨提示您现在还没有任何好友快点击右上方'+'按钮添加好友吧~
</view> </view>
<view class="friend-items" v-else> <view class="friend-items" v-else>
@ -56,14 +56,11 @@ export default {
} }
}, },
computed: { computed: {
friends() {
return this.friendStore.friends;
},
friendGroupMap() { friendGroupMap() {
// //
let groupMap = new Map(); let groupMap = new Map();
this.friends.forEach((f) => { this.friendStore.friends.forEach((f) => {
if (this.searchText && !f.nickName.includes(this.searchText)) { if (f.deleted || (this.searchText && !f.nickName.includes(this.searchText))) {
return; return;
} }
let letter = this.firstLetter(f.nickName).toUpperCase(); let letter = this.firstLetter(f.nickName).toUpperCase();

3
im-uniapp/pages/group/group-info.vue

@ -113,8 +113,7 @@ export default {
url: "/pages/group/group" url: "/pages/group/group"
}); });
this.groupStore.removeGroup(this.groupId); this.groupStore.removeGroup(this.groupId);
this.chatStore.removeGroupChat(this this.chatStore.removeGroupChat(this.groupId);
.groupId);
}, 100) }, 100)
} }
}) })

8
im-uniapp/pages/group/group-invite.vue

@ -8,9 +8,9 @@
</view> </view>
<view class="friend-items"> <view class="friend-items">
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true"> <scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true">
<view v-for="friend in friendItems" v-show="!searchText || friend.nickName.includes(searchText)" <view v-for="friend in friendItems" :key="friend.id">
:key="friend.id"> <view v-show="!searchText || friend.nickName.includes(searchText)" class="friend-item"
<view class="friend-item" @click="onSwitchChecked(friend)" @click="onSwitchChecked(friend)"
:class="{ checked: friend.checked, disabled: friend.disabled }"> :class="{ checked: friend.checked, disabled: friend.disabled }">
<head-image :name="friend.nickName" :online="friend.online" <head-image :name="friend.nickName" :online="friend.online"
:url="friend.headImage"></head-image> :url="friend.headImage"></head-image>
@ -79,7 +79,7 @@ export default {
initFriendItems() { initFriendItems() {
this.friendItems = []; this.friendItems = [];
let friends = this.friendStore.friends; let friends = this.friendStore.friends;
friends.forEach((f => { friends.filter(f => !f.deleted).forEach((f => {
let item = { let item = {
id: f.id, id: f.id,
headImage: f.headImage, headImage: f.headImage,

21
im-uniapp/store/chatStore.js

@ -180,7 +180,7 @@ export default defineStore('chatStore', {
chat.sendNickName = msgInfo.sendNickName; chat.sendNickName = msgInfo.sendNickName;
// 未读加1 // 未读加1
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED && if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED &&
msgInfo.type != MESSAGE_TYPE.TIP_TEXT) { msgInfo.status != MESSAGE_STATUS.RECALL && msgInfo.type != MESSAGE_TYPE.TIP_TEXT) {
chat.unreadCount++; chat.unreadCount++;
} }
// 是否有人@我 // 是否有人@我
@ -270,7 +270,9 @@ export default defineStore('chatStore', {
chat.lastContent = m.content; chat.lastContent = m.content;
chat.lastSendTime = msgInfo.sendTime; chat.lastSendTime = msgInfo.sendTime;
chat.sendNickName = ''; chat.sendNickName = '';
chat.unreadCount++; if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED) {
chat.unreadCount++;
}
} }
// 被引用的消息也要撤回 // 被引用的消息也要撤回
if (m.quoteMessage && m.quoteMessage.id == msgInfo.id) { if (m.quoteMessage && m.quoteMessage.id == msgInfo.id) {
@ -284,15 +286,26 @@ export default defineStore('chatStore', {
}, },
updateChatFromFriend(friend) { updateChatFromFriend(friend) {
let chat = this.findChatByFriend(friend.id) let chat = this.findChatByFriend(friend.id)
if (chat && (chat.headImage != friend.headImageThumb || if (chat && (chat.headImage != friend.headImage ||
chat.showName != friend.nickName)) { chat.showName != friend.nickName)) {
// 更新会话中的群名和头像 // 更新会话中的群名和头像
chat.headImage = friend.headImageThumb; chat.headImage = friend.headImage;
chat.showName = friend.nickName; chat.showName = friend.nickName;
chat.stored = false; chat.stored = false;
this.saveToStorage(); this.saveToStorage();
} }
}, },
updateChatFromUser(user) {
let chat = this.findChatByFriend(user.id);
// 更新会话中的昵称和头像
if (chat && (chat.headImage != user.headImageThumb ||
chat.showName != user.nickName)) {
chat.headImage = user.headImageThumb;
chat.showName = user.nickName;
chat.stored = false;
this.saveToStorage();
}
},
updateChatFromGroup(group) { updateChatFromGroup(group) {
let chat = this.findChatByGroup(group.id); let chat = this.findChatByGroup(group.id);
if (chat && (chat.headImage != group.headImageThumb || if (chat && (chat.headImage != group.headImageThumb ||

29
im-uniapp/store/friendStore.js

@ -29,12 +29,16 @@ export default defineStore('friendStore', {
removeFriend(id) { removeFriend(id) {
this.friends.forEach((f, idx) => { this.friends.forEach((f, idx) => {
if (f.id == id) { if (f.id == id) {
this.friends.splice(idx, 1) this.friends[idx].deleted = true;
} }
}) })
}, },
addFriend(friend) { addFriend(friend) {
this.friends.push(friend); if (this.friends.find((f) => f.id == friend.id)) {
this.updateFriend(friend)
} else {
this.friends.unshift(friend);
}
}, },
setOnlineStatus(onlineTerminals) { setOnlineStatus(onlineTerminals) {
this.friends.forEach((f) => { this.friends.forEach((f) => {
@ -51,16 +55,16 @@ export default defineStore('friendStore', {
}); });
}, },
refreshOnlineStatus() { refreshOnlineStatus() {
if (this.friends.length > 0) { let userIds = this.friends.filter((f) => !f.deleted).map((f) => f.id);
let userIds = []; if (userIds.length == 0) {
this.friends.forEach(f => userIds.push(f.id)); return;
http({
url: '/user/terminal/online?userIds=' + userIds.join(','),
method: 'GET'
}).then((onlineTerminals) => {
this.setOnlineStatus(onlineTerminals);
})
} }
http({
url: '/user/terminal/online?userIds=' + userIds.join(','),
method: 'GET'
}).then((onlineTerminals) => {
this.setOnlineStatus(onlineTerminals);
})
// 30s后重新拉取 // 30s后重新拉取
clearTimeout(this.timer); clearTimeout(this.timer);
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
@ -88,6 +92,9 @@ export default defineStore('friendStore', {
} }
}, },
getters: { getters: {
isFriend: (state) => (userId) => {
return state.friends.filter((f) => !f.deleted).some((f) => f.id == userId);
},
findFriend: (state) => (id) => { findFriend: (state) => (id) => {
return state.friends.find((f) => f.id == id); return state.friends.find((f) => f.id == id);
} }

2
im-web/src/api/enums.js

@ -13,6 +13,8 @@ const MESSAGE_TYPE = {
ACT_RT_VOICE: 40, ACT_RT_VOICE: 40,
ACT_RT_VIDEO: 41, ACT_RT_VIDEO: 41,
USER_BANNED: 50, USER_BANNED: 50,
FRIEND_NEW: 80,
FRIEND_DEL: 81,
RTC_CALL_VOICE: 100, RTC_CALL_VOICE: 100,
RTC_CALL_VIDEO: 101, RTC_CALL_VIDEO: 101,
RTC_ACCEPT: 102, RTC_ACCEPT: 102,

36
im-web/src/components/chat/ChatBox.vue

@ -117,7 +117,7 @@ export default {
}, },
data() { data() {
return { return {
friend: {}, userInfo: {},
group: {}, group: {},
groupMembers: [], groupMembers: [],
sendImageUrl: "", sendImageUrl: "",
@ -540,15 +540,27 @@ export default {
this.groupMembers = groupMembers; this.groupMembers = groupMembers;
}); });
}, },
updateFriendInfo() {
if (this.isFriend) {
// storestore
let friend = JSON.parse(JSON.stringify(this.friend));
friend.headImage = this.userInfo.headImageThumb;
friend.nickName = this.userInfo.nickName;
friend.showNickName = friend.remarkNickName ? friend.remarkNickName : friend.nickName;
this.$store.commit("updateChatFromFriend", friend);
this.$store.commit("updateFriend", friend);
}else {
this.$store.commit("updateChatFromUser", this.userInfo);
}
},
loadFriend(friendId) { loadFriend(friendId) {
// //
this.$http({ this.$http({
url: `/user/find/${friendId}`, url: `/user/find/${friendId}`,
method: 'get' method: 'GET'
}).then((friend) => { }).then((userInfo) => {
this.friend = friend; this.userInfo = userInfo;
this.$store.commit("updateChatFromFriend", friend); this.updateFriendInfo();
this.$store.commit("updateFriend", friend);
}) })
}, },
showName(msgInfo) { showName(msgInfo) {
@ -624,7 +636,7 @@ export default {
} }
if (this.chat.type == "PRIVATE") { if (this.chat.type == "PRIVATE") {
msgInfo.recvId = this.mine.id msgInfo.recvId = this.mine.id
msgInfo.content = "该用户已被管理员封禁,原因:" + this.friend.reason msgInfo.content = "该用户已被管理员封禁,原因:" + this.userInfo.reason
} else { } else {
msgInfo.groupId = this.group.id; msgInfo.groupId = this.group.id;
msgInfo.content = "本群聊已被管理员封禁,原因:" + this.group.reason msgInfo.content = "本群聊已被管理员封禁,原因:" + this.group.reason
@ -640,6 +652,12 @@ export default {
mine() { mine() {
return this.$store.state.userStore.userInfo; return this.$store.state.userStore.userInfo;
}, },
isFriend() {
return this.$store.getters.isFriend(this.userInfo.id);
},
friend() {
return this.$store.getters.findFriend(this.userInfo.id)
},
title() { title() {
let title = this.chat.showName; let title = this.chat.showName;
if (this.chat.type == "GROUP") { if (this.chat.type == "GROUP") {
@ -661,7 +679,7 @@ export default {
return this.chat.messages.length; return this.chat.messages.length;
}, },
isBanned() { isBanned() {
return (this.chat.type == "PRIVATE" && this.friend.isBanned) || return (this.chat.type == "PRIVATE" && this.userInfo.isBanned) ||
(this.chat.type == "GROUP" && this.group.isBanned) (this.chat.type == "GROUP" && this.group.isBanned)
} }
}, },

9
im-web/src/components/common/UserInfo.vue

@ -68,13 +68,14 @@ export default {
params: { params: {
friendId: this.user.id friendId: this.user.id
} }
}).then((data) => { }).then(() => {
this.$message.success("添加成功,对方已成为您的好友"); this.$message.success("添加成功,对方已成为您的好友");
let friend = { let friend = {
id: this.user.id, id: this.user.id,
nickName: this.user.nickName, nickName: this.user.nickName,
headImage: this.user.headImageThumb, headImage: this.user.headImageThumb,
online: this.user.online online: this.user.online,
deleted: false
} }
this.$store.commit("addFriend", friend); this.$store.commit("addFriend", friend);
}) })
@ -87,9 +88,7 @@ export default {
}, },
computed: { computed: {
isFriend() { isFriend() {
let friends = this.$store.state.friendStore.friends; return this.$store.getters.isFriend(this.user.id);
let friend = friends.find((f) => f.id == this.user.id);
return friend != undefined;
} }
} }
} }

9
im-web/src/components/friend/AddFriend.vue

@ -74,21 +74,20 @@ export default {
params: { params: {
friendId: user.id friendId: user.id
} }
}).then((data) => { }).then(() => {
this.$message.success("添加成功,对方已成为您的好友"); this.$message.success("添加成功,对方已成为您的好友");
let friend = { let friend = {
id: user.id, id: user.id,
nickName: user.nickName, nickName: user.nickName,
headImage: user.headImage, headImage: user.headImage,
online: user.online online: user.online,
deleted: false
} }
this.$store.commit("addFriend", friend); this.$store.commit("addFriend", friend);
}) })
}, },
isFriend(userId) { isFriend(userId) {
let friends = this.$store.state.friendStore.friends; return this.$store.getters.isFriend(userId);
let friend = friends.find((f) => f.id == userId);
return friend != undefined;
} }
} }
} }

4
im-web/src/components/group/AddGroupMember.vue

@ -55,7 +55,6 @@ export default {
this.$emit("close"); this.$emit("close");
}, },
onOk() { onOk() {
let inviteVO = { let inviteVO = {
groupId: this.groupId, groupId: this.groupId,
friendIds: [] friendIds: []
@ -107,6 +106,9 @@ export default {
if (newData) { if (newData) {
this.friends = []; this.friends = [];
this.$store.state.friendStore.friends.forEach((f) => { this.$store.state.friendStore.friends.forEach((f) => {
if (f.deleted) {
return;
}
let friend = JSON.parse(JSON.stringify(f)) let friend = JSON.parse(JSON.stringify(f))
let m = this.members.filter((m) => !m.quit) let m = this.members.filter((m) => !m.quit)
.find((m) => m.userId == f.id); .find((m) => m.userId == f.id);

20
im-web/src/store/chatStore.js

@ -175,7 +175,7 @@ export default {
chat.sendNickName = msgInfo.sendNickName; chat.sendNickName = msgInfo.sendNickName;
// 未读加1 // 未读加1
if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED && if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED &&
msgInfo.type != MESSAGE_TYPE.TIP_TEXT) { msgInfo.status != MESSAGE_STATUS.RECALL && msgInfo.type != MESSAGE_TYPE.TIP_TEXT) {
chat.unreadCount++; chat.unreadCount++;
} }
// 是否有人@我 // 是否有人@我
@ -258,7 +258,7 @@ export default {
chat.lastContent = m.content; chat.lastContent = m.content;
chat.lastSendTime = msgInfo.sendTime; chat.lastSendTime = msgInfo.sendTime;
chat.sendNickName = ''; chat.sendNickName = '';
if(!msgInfo.selfSend){ if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED) {
chat.unreadCount++; chat.unreadCount++;
} }
} }
@ -275,14 +275,25 @@ export default {
updateChatFromFriend(state, friend) { updateChatFromFriend(state, friend) {
let chat = this.getters.findChatByFriend(friend.id); let chat = this.getters.findChatByFriend(friend.id);
// 更新会话中的群名和头像 // 更新会话中的群名和头像
if (chat && (chat.headImage != friend.headImageThumb || if (chat && (chat.headImage != friend.headImage ||
chat.showName != friend.nickName)) { chat.showName != friend.nickName)) {
chat.headImage = friend.headImageThumb; chat.headImage = friend.headImage;
chat.showName = friend.nickName; chat.showName = friend.nickName;
chat.stored = false; chat.stored = false;
this.commit("saveToStorage") this.commit("saveToStorage")
} }
}, },
updateChatFromUser(user) {
let chat = this.getters.findChatByFriend(user.id);
// 更新会话中的昵称和头像
if (chat && (chat.headImage != user.headImageThumb ||
chat.showName != user.nickName)) {
chat.headImage = user.headImageThumb;
chat.showName = user.nickName;
chat.stored = false;
this.saveToStorage();
}
},
updateChatFromGroup(state, group) { updateChatFromGroup(state, group) {
let chat = this.getters.findChatByGroup(group.id); let chat = this.getters.findChatByGroup(group.id);
if (chat && (chat.headImage != group.headImageThumb || if (chat && (chat.headImage != group.headImageThumb ||
@ -333,7 +344,6 @@ export default {
// 只存储有改动的会话 // 只存储有改动的会话
let chatKey = `${key}-${chat.type}-${chat.targetId}` let chatKey = `${key}-${chat.type}-${chat.targetId}`
if (!chat.stored) { if (!chat.stored) {
console.log(chatKey)
if (chat.delete) { if (chat.delete) {
localForage.removeItem(chatKey); localForage.removeItem(chatKey);
} else { } else {

41
im-web/src/store/friendStore.js

@ -28,23 +28,35 @@ export default {
}) })
}, },
activeFriend(state, idx) { activeFriend(state, idx) {
state.activeFriend = state.friends[idx]; if (idx < 0) {
},
removeFriend(state, idx) {
if (state.friends[idx] == state.activeFriend) {
state.activeFriend = null; state.activeFriend = null;
} else {
state.activeFriend = state.friends[idx];
}
},
removeFriend(state, id) {
for (let idx in state.friends) {
if (id && state.friends[idx].id == id) {
state.friends[idx].deleted = true;
if (state.friends[idx] == state.activeFriend) {
state.activeFriend = null;
}
return;
}
} }
state.friends.splice(idx, 1);
}, },
addFriend(state, friend) { addFriend(state, friend) {
state.friends.push(friend); if (state.friends.find((f) => f.id == friend.id)) {
this.commit("updateFriend", friend)
} else {
state.friends.unshift(friend);
}
}, },
refreshOnlineStatus(state) { refreshOnlineStatus(state) {
let userIds = []; let userIds = state.friends.filter((f) => !f.deleted).map((f) => f.id);
if (state.friends.length == 0) { if (userIds.length == 0) {
return; return;
} }
state.friends.forEach((f) => { userIds.push(f.id) });
http({ http({
url: '/user/terminal/online', url: '/user/terminal/online',
method: 'get', method: 'get',
@ -52,7 +64,6 @@ export default {
}).then((onlineTerminals) => { }).then((onlineTerminals) => {
this.commit("setOnlineStatus", onlineTerminals); this.commit("setOnlineStatus", onlineTerminals);
}) })
// 30s后重新拉取 // 30s后重新拉取
state.timer && clearTimeout(state.timer); state.timer && clearTimeout(state.timer);
state.timer = setTimeout(() => { state.timer = setTimeout(() => {
@ -100,10 +111,18 @@ export default {
context.commit("setFriends", friends); context.commit("setFriends", friends);
context.commit("refreshOnlineStatus"); context.commit("refreshOnlineStatus");
resolve() resolve()
}).catch((res) => { }).catch(() => {
reject(); reject();
}) })
}); });
} }
},
getters: {
isFriend: (state) => (userId) => {
return state.friends.filter((f)=>!f.deleted).some((f)=>f.id == userId);
},
findFriend: (state) => (userId) => {
return state.friends.find((f)=>f.id == userId);
}
} }
} }

56
im-web/src/view/Friend.vue

@ -11,9 +11,9 @@
</div> </div>
<el-scrollbar class="friend-list-items"> <el-scrollbar class="friend-list-items">
<div v-for="(friend, index) in $store.state.friendStore.friends" :key="index"> <div v-for="(friend, index) in $store.state.friendStore.friends" :key="index">
<friend-item v-show="friend.nickName.includes(searchText)" :index="index" <friend-item v-if="!friend.deleted" v-show="friend.nickName.includes(searchText)" :index="index"
:active="friend === $store.state.friendStore.activeFriend" @chat="onSendMessage(friend)" :active="friend === $store.state.friendStore.activeFriend" @chat="onSendMessage(friend)"
@delete="onDelItem(friend, index)" @click.native="onActiveItem(friend, index)"> @delete="onDelItem(friend)" @click.native="onActiveItem(friend, index)">
</friend-item> </friend-item>
</div> </div>
</el-scrollbar> </el-scrollbar>
@ -72,7 +72,8 @@ export default {
searchText: "", searchText: "",
showAddFriend: false, showAddFriend: false,
activeIdx: -1, activeIdx: -1,
userInfo: {} userInfo: {},
friend: {}
} }
}, },
methods: { methods: {
@ -85,9 +86,10 @@ export default {
onActiveItem(friend, idx) { onActiveItem(friend, idx) {
this.$store.commit("activeFriend", idx); this.$store.commit("activeFriend", idx);
this.activeIdx = idx this.activeIdx = idx
this.loadUserInfo(friend, idx); this.friend = friend;
this.loadUserInfo(friend.id);
}, },
onDelItem(friend, idx) { onDelItem(friend) {
this.$confirm(`确认删除'${friend.nickName}',并清空聊天记录吗?`, '确认解除?', { this.$confirm(`确认删除'${friend.nickName}',并清空聊天记录吗?`, '确认解除?', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
@ -96,9 +98,9 @@ export default {
this.$http({ this.$http({
url: `/friend/delete/${friend.id}`, url: `/friend/delete/${friend.id}`,
method: 'delete' method: 'delete'
}).then((data) => { }).then(() => {
this.$message.success("删除好友成功"); this.$message.success("删除好友成功");
this.$store.commit("removeFriend", idx); this.$store.commit("removeFriend", friend.id);
this.$store.commit("removePrivateChat", friend.id); this.$store.commit("removePrivateChat", friend.id);
}) })
}) })
@ -110,7 +112,7 @@ export default {
params: { params: {
friendId: user.id friendId: user.id
} }
}).then((data) => { }).then(() => {
this.$message.success("添加成功,对方已成为您的好友"); this.$message.success("添加成功,对方已成为您的好友");
let friend = { let friend = {
id: user.id, id: user.id,
@ -128,6 +130,7 @@ export default {
showName: user.nickName, showName: user.nickName,
headImage: user.headImageThumb, headImage: user.headImageThumb,
}; };
console.log("chat:",chat)
this.$store.commit("openChat", chat); this.$store.commit("openChat", chat);
this.$store.commit("activeChat", 0); this.$store.commit("activeChat", 0);
this.$router.push("/home/chat"); this.$router.push("/home/chat");
@ -137,31 +140,24 @@ export default {
this.$store.commit('showFullImageBox', this.userInfo.headImage); this.$store.commit('showFullImageBox', this.userInfo.headImage);
} }
}, },
updateFriendInfo(friend, user, index) { updateFriendInfo() {
// storestore if (this.isFriend) {
friend = JSON.parse(JSON.stringify(friend)); // storestore
friend.headImage = user.headImageThumb; let friend = JSON.parse(JSON.stringify(this.friend));
friend.nickName = user.nickName; friend.headImage = this.userInfo.headImageThumb;
this.$http({ friend.nickName = this.userInfo.nickName;
url: "/friend/update", this.$store.commit("updateChatFromFriend", friend);
method: "put",
data: friend
}).then(() => {
this.$store.commit("updateFriend", friend); this.$store.commit("updateFriend", friend);
this.$store.commit("updateChatFromFriend", user); }
})
}, },
loadUserInfo(friend, index) { loadUserInfo(id) {
//
this.$http({ this.$http({
url: `/user/find/${friend.id}`, url: `/user/find/${id}`,
method: 'get' method: 'GET'
}).then((user) => { }).then((userInfo) => {
this.userInfo = user; this.userInfo = userInfo;
// this.updateFriendInfo();
if (user.headImageThumb != friend.headImage ||
user.nickName != friend.nickName) {
this.updateFriendInfo(friend, user, index)
}
}) })
} }
}, },

38
im-web/src/view/Home.vue

@ -189,18 +189,27 @@ export default {
this.$store.commit("recallMessage", [msg, chatInfo]) this.$store.commit("recallMessage", [msg, chatInfo])
return; return;
} }
//
if (msg.type == this.$enums.MESSAGE_TYPE.FRIEND_NEW) {
this.$store.commit("addFriend", JSON.parse(msg.content));
return;
}
//
if (msg.type == this.$enums.MESSAGE_TYPE.FRIEND_DEL) {
this.$store.commit("removeFriend", friendId);
return;
}
// webrtc // webrtc
if (this.$msgType.isRtcPrivate(msg.type)) { if (this.$msgType.isRtcPrivate(msg.type)) {
this.$refs.rtcPrivateVideo.onRTCMessage(msg) this.$refs.rtcPrivateVideo.onRTCMessage(msg)
return; return;
} }
// id // id
this.loadFriendInfo(friendId).then((friend) => { let friend = this.loadFriendInfo(friendId);
this.insertPrivateMessage(friend, msg); this.insertPrivateMessage(friend, msg);
})
}, },
insertPrivateMessage(friend, msg) { insertPrivateMessage(friend, msg) {
let chatInfo = { let chatInfo = {
type: 'PRIVATE', type: 'PRIVATE',
targetId: friend.id, targetId: friend.id,
@ -322,20 +331,15 @@ export default {
this.showSettingDialog = false; this.showSettingDialog = false;
}, },
loadFriendInfo(id) { loadFriendInfo(id) {
return new Promise((resolve, reject) => { let friend = this.$store.state.friendStore.friends.find((f) => f.id == id);
let friend = this.$store.state.friendStore.friends.find((f) => f.id == id); if (!friend) {
if (friend) { friend = {
resolve(friend); id: id,
} else { showNickName: "未知用户",
this.$http({ headImage: ""
url: `/friend/find/${id}`,
method: 'get'
}).then((friend) => {
this.$store.commit("addFriend", friend);
resolve(friend)
})
} }
}); }
return friend;
}, },
loadGroupInfo(id) { loadGroupInfo(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

Loading…
Cancel
Save