Commit cce3dd61108431a91d2586de6a51e180965b8f37

Authored by YunaiV
1 parent 443d91d7

✨ feat(group): 添加群消息已读位置管理功能

新增删除用户在群中的已读位置和清理群所有用户已读位置的功能,支持成员退群和群解散场景。此功能提升了群消息管理的灵活性和用户体验。
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java
@@ -26,10 +26,14 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> { @@ -26,10 +26,14 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> {
26 RECEIPT(12, "回执"), // 暂无 26 RECEIPT(12, "回执"), // 暂无
27 TIP_TEXT(21, "提示文本"), // 对应 TextMessage 类 27 TIP_TEXT(21, "提示文本"), // 对应 TextMessage 类
28 28
29 - RECALL(10, "撤回"),  
30 - READ(11, "已读"),  
31 - RECEIPT(12, "回执"),  
32 - TIP_TEXT(21, "提示文本"); 29 + // ==================== 非单聊、群聊存储的类型,100 开始 ====================
  30 +
  31 + FRIEND_ADD(100, "好友添加"), // 暂无
  32 + FRIEND_DELETE(101, "好友删除"), // 暂无
  33 + FRIEND_UPDATE(102, "好友更新"), // 对应 FriendUpdateMessage 类
  34 +
  35 + GROUP_CREATE(200, "群创建"), // 暂无
  36 + GROUP_UPDATE(201, "群信息变更"); // 暂无
33 37
34 public static final Integer[] ARRAYS = Arrays.stream(values()).map(ImMessageTypeEnum::getType).toArray(Integer[]::new); 38 public static final Integer[] ARRAYS = Arrays.stream(values()).map(ImMessageTypeEnum::getType).toArray(Integer[]::new);
35 39
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/redis/message/GroupMessageReadRedisDAO.java
@@ -61,6 +61,27 @@ public class GroupMessageReadRedisDAO { @@ -61,6 +61,27 @@ public class GroupMessageReadRedisDAO {
61 return result; 61 return result;
62 } 62 }
63 63
  64 + /**
  65 + * 删除用户在某群的已读位置
  66 + *
  67 + * @param groupId 群编号
  68 + * @param userId 用户编号
  69 + */
  70 + public void deleteReadMaxMessageId(Long groupId, Long userId) {
  71 + String key = formatKey(groupId);
  72 + stringRedisTemplate.opsForHash().delete(key, userId.toString());
  73 + }
  74 +
  75 + /**
  76 + * 删除某群所有用户的已读位置(整个 Hash Key)
  77 + *
  78 + * @param groupId 群编号
  79 + */
  80 + public void deleteReadMaxMessageIdMap(Long groupId) {
  81 + String key = formatKey(groupId);
  82 + stringRedisTemplate.delete(key);
  83 + }
  84 +
64 private static String formatKey(Long groupId) { 85 private static String formatKey(Long groupId) {
65 return String.format(GROUP_MESSAGE_READ, groupId); 86 return String.format(GROUP_MESSAGE_READ, groupId);
66 } 87 }
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java
@@ -73,7 +73,6 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService { @@ -73,7 +73,6 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
73 return imGroupMemberMapper.selectById(id); 73 return imGroupMemberMapper.selectById(id);
74 } 74 }
75 75
76 -  
77 @Override 76 @Override
78 public List<ImGroupMemberDO> getGroupMemberListByGroupId(Long groupId) { 77 public List<ImGroupMemberDO> getGroupMemberListByGroupId(Long groupId) {
79 return imGroupMemberMapper.selectListByGroupId(groupId); 78 return imGroupMemberMapper.selectListByGroupId(groupId);
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupUpdateReqVO; @@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupUpdateReqVO;
12 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; 12 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
13 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; 13 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
14 import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMapper; 14 import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMapper;
  15 +import cn.iocoder.yudao.module.im.service.message.ImGroupMessageService;
15 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; 16 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService;
16 import cn.iocoder.yudao.module.im.service.websocket.dto.ImGroupMessageDTO; 17 import cn.iocoder.yudao.module.im.service.websocket.dto.ImGroupMessageDTO;
17 import jakarta.annotation.Resource; 18 import jakarta.annotation.Resource;
@@ -33,7 +34,6 @@ import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.GROUP; @@ -33,7 +34,6 @@ import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.GROUP;
33 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*; 34 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
34 35
35 // TODO @芋艿:群主的调整,暂时不考虑。后续在弄; 36 // TODO @芋艿:群主的调整,暂时不考虑。后续在弄;
36 -// DONE @AI:groupdo 增加一个缓存;spring cache 实现;  
37 /** 37 /**
38 * 群 Service 实现类 38 * 群 Service 实现类
39 * 39 *
@@ -50,6 +50,9 @@ public class ImGroupServiceImpl implements ImGroupService { @@ -50,6 +50,9 @@ public class ImGroupServiceImpl implements ImGroupService {
50 @Lazy // 避免循环依赖 50 @Lazy // 避免循环依赖
51 private ImGroupMemberService groupMemberService; 51 private ImGroupMemberService groupMemberService;
52 @Resource 52 @Resource
  53 + @Lazy // 避免循环依赖
  54 + private ImGroupMessageService groupMessageService;
  55 + @Resource
53 private ImWebSocketService webSocketService; 56 private ImWebSocketService webSocketService;
54 57
55 @Override 58 @Override
@@ -125,7 +128,8 @@ public class ImGroupServiceImpl implements ImGroupService { @@ -125,7 +128,8 @@ public class ImGroupServiceImpl implements ImGroupService {
125 updateObj.setDissolvedTime(LocalDateTime.now()); 128 updateObj.setDissolvedTime(LocalDateTime.now());
126 groupMapper.updateById(updateObj); 129 groupMapper.updateById(updateObj);
127 130
128 - // TODO @AI:清理已读缓存; 131 + // 4. 清理已读缓存
  132 + groupMessageService.deleteReadMaxMessageIdMap(id);
129 // TODO @AI:推送群解散的提示; 133 // TODO @AI:推送群解散的提示;
130 // TODO @AI:推送群信息变化; 134 // TODO @AI:推送群信息变化;
131 } 135 }
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageService.java
@@ -69,4 +69,24 @@ public interface ImGroupMessageService { @@ -69,4 +69,24 @@ public interface ImGroupMessageService {
69 */ 69 */
70 List<ImGroupMessageDO> getGroupMessageList(Long userId, ImGroupMessageListReqVO reqVO); 70 List<ImGroupMessageDO> getGroupMessageList(Long userId, ImGroupMessageListReqVO reqVO);
71 71
  72 + /**
  73 + * 清理用户在某群的已读位置缓存
  74 + * <p>
  75 + * 用于成员退群场景
  76 + *
  77 + * @param groupId 群编号
  78 + * @param userId 用户编号
  79 + */
  80 + void deleteReadMaxMessageId(Long groupId, Long userId);
  81 +
  82 + /**
  83 + * 清理某群所有用户的已读位置缓存
  84 + * <p>
  85 + * 用于群解散场景
  86 + *
  87 + * @param groupId 群编号
  88 + */
  89 + void deleteReadMaxMessageIdMap(Long groupId);
  90 +
72 } 91 }
  92 +
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java
@@ -471,9 +471,18 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @@ -471,9 +471,18 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
471 member -> isMessageVisible(msg, member, member.getUserId())); 471 member -> isMessageVisible(msg, member, member.getUserId()));
472 } 472 }
473 473
  474 + @Override
  475 + public void deleteReadMaxMessageId(Long groupId, Long userId) {
  476 + groupMessageReadRedisDAO.deleteReadMaxMessageId(groupId, userId);
  477 + }
  478 +
  479 + @Override
  480 + public void deleteReadMaxMessageIdMap(Long groupId) {
  481 + groupMessageReadRedisDAO.deleteReadMaxMessageIdMap(groupId);
  482 + }
474 483
475 private ImGroupMessageServiceImpl getSelf() { 484 private ImGroupMessageServiceImpl getSelf() {
476 return SpringUtil.getBean(getClass()); 485 return SpringUtil.getBean(getClass());
477 } 486 }
478 487
479 -} 488 -}
  489 +}
480 \ No newline at end of file 490 \ No newline at end of file