Commit bf8eb273ec01bacc91369d079d5f7d6d2311bae2

Authored by YunaiV
1 parent 5e824ccb

✨ feat(message): 优化群聊的 enrichPullResultForViewer 已读状态、人数

yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java
... ... @@ -35,13 +35,9 @@ import java.time.LocalDateTime;
35 35 import java.util.*;
36 36  
37 37 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
38   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
39   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
40   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  38 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
41 39 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
42   -import static cn.iocoder.yudao.module.im.enums.ImCommonConstants.MESSAGE_GROUP_PULL_MAX_DAYS;
43   -import static cn.iocoder.yudao.module.im.enums.ImCommonConstants.MESSAGE_MAX_PULL_SIZE;
44   -import static cn.iocoder.yudao.module.im.enums.ImCommonConstants.MESSAGE_RECALL_TIMEOUT_MINUTES;
  40 +import static cn.iocoder.yudao.module.im.enums.ImCommonConstants.*;
45 41  
46 42 /**
47 43 * IM 群聊消息 Service 实现类
... ... @@ -145,8 +141,8 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
145 141 .sorted(Comparator.comparing(ImGroupMessageDO::getId))
146 142 .limit(size)
147 143 .toList();
148   - // 3.2 按当前用户补齐离线拉取结果:消息已读态(相对 Redis 已读游标)、本人发送的回执消息的已读人数
149   - enrichPullResultForViewer(userId, result);
  144 + // 3.2 按当前用户补齐:消息已读态(相对 Redis 已读游标)、本人发送的回执消息的已读人数
  145 + appendMessageStatusAndReceipt(userId, result);
150 146  
151 147 log.info("[pullGroupMessageList][userId({}) minId({}) size({}) result({})]",
152 148 userId, minId, size, result.size());
... ... @@ -154,26 +150,29 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
154 150 }
155 151  
156 152 /**
157   - * 离线拉取结果按当前用户补齐:消息已读态(相对 Redis 已读游标)、本人发送的回执消息的已读人数
  153 + * 补全消息已读态和回执已读人数
  154 + *
  155 + * 1. 消息已读态(status):根据 Redis 已读游标判断 READ / UNREAD
  156 + * 2. 回执已读人数(readCount):仅对本人发送的回执消息,计算可见成员中的已读人数
158 157 */
159   - @SuppressWarnings("StatementWithEmptyBody")
160   - private void enrichPullResultForViewer(Long userId, List<ImGroupMessageDO> messages) {
  158 + @SuppressWarnings({"StatementWithEmptyBody", "DataFlowIssue"})
  159 + private void appendMessageStatusAndReceipt(Long userId, List<ImGroupMessageDO> messages) {
161 160 if (CollUtil.isEmpty(messages)) {
162 161 return;
163 162 }
164   - Map<Long, Long> viewerReadCursorByGroup = new HashMap<>(); // 群 → 已读位置
  163 + Map<Long, Long> readMaxMessageIdsByGroup = new HashMap<>(); // 群 → 已读位置
165 164 Map<Long, Map<Long, Long>> readPositionsByGroup = new HashMap<>(); // 群 → (用户 → 已读位置)
166 165 Map<Long, List<ImGroupMemberDO>> membersByGroup = new HashMap<>(); // 群 → 全部成员列表
167 166 for (ImGroupMessageDO message : messages) {
168 167 // 消息已读态(status):撤回 > 已读 > 未读
169 168 Long groupId = message.getGroupId();
170   - long readCursor = viewerReadCursorByGroup.computeIfAbsent(groupId, gid -> {
171   - Long readMax = groupMessageReadRedisDAO.getReadMaxMessageId(gid, userId);
172   - return readMax != null ? readMax : -1L;
  169 + long readMaxMessageId = readMaxMessageIdsByGroup.computeIfAbsent(groupId, gid -> {
  170 + Long readMaxMsgId = groupMessageReadRedisDAO.getReadMaxMessageId(gid, userId);
  171 + return readMaxMsgId != null ? readMaxMsgId : -1L;
173 172 });
174 173 if (ImMessageStatusEnum.RECALL.getStatus().equals(message.getStatus())) {
175 174 // 保持撤回态
176   - } else if (readCursor >= message.getId()) {
  175 + } else if (readMaxMessageId >= message.getId()) {
177 176 message.setStatus(ImMessageStatusEnum.READ.getStatus());
178 177 } else {
179 178 message.setStatus(ImMessageStatusEnum.UNREAD.getStatus());
... ... @@ -190,15 +189,9 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
190 189 groupMemberService::getGroupMemberListByGroupId);
191 190 Set<Long> visibleUserIds = getVisibleUserIds(message, allMembers);
192 191 visibleUserIds.remove(message.getSenderId());
193   - int readCount = 0;
194   - if (CollUtil.isNotEmpty(visibleUserIds)) {
195   - for (Long uid : visibleUserIds) {
196   - Long pos = positions.get(uid);
197   - if (pos != null && pos >= message.getId()) {
198   - readCount++;
199   - }
200   - }
201   - }
  192 + int readCount = getSumValue(visibleUserIds,
  193 + uid -> positions.getOrDefault(uid, -1L) >= message.getId() ? 1 : null,
  194 + Integer::sum, 0);
202 195 message.setReadCount(readCount);
203 196 }
204 197 }
... ... @@ -394,29 +387,29 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
394 387  
395 388 /**
396 389 * 判断一条群消息对某个群成员是否可见
397   - * <p>
398   - * 规则:
399   - * 1. 入群时间晚于消息发送时间 → 不可见
400   - * 2. 已退群且退群时间早于消息发送时间 → 不可见
401   - * 3. 存在定向接收列表且当前用户既不在列表中也不是发送者 → 不可见
  390 + *
  391 + * @param msg 消息
  392 + * @param member 群成员
  393 + * @param userId 当前用户编号(用于定向消息过滤)
  394 + * @return 是否可见
402 395 */
403 396 private boolean isMessageVisible(ImGroupMessageDO msg, ImGroupMemberDO member, Long userId) {
404 397 if (member == null) {
405 398 return false;
406 399 }
  400 + // 1. 入群时间晚于消息发送时间 → 不可见
407 401 if (member.getJoinTime() != null && msg.getSendTime().isBefore(member.getJoinTime())) {
408 402 return false;
409 403 }
  404 + // 2. 已退群且退群时间早于消息发送时间 → 不可见
410 405 if (CommonStatusEnum.DISABLE.getStatus().equals(member.getStatus())
411 406 && member.getQuitTime() != null && msg.getSendTime().isAfter(member.getQuitTime())) {
412 407 return false;
413 408 }
414   - if (CollUtil.isNotEmpty(msg.getReceiverUserIds())
415   - && !msg.getReceiverUserIds().contains(userId)
416   - && ObjUtil.notEqual(msg.getSenderId(), userId)) {
417   - return false;
418   - }
419   - return true;
  409 + // 3. 存在定向接收列表且当前用户既不在列表中也不是发送者 → 不可见
  410 + return !CollUtil.isNotEmpty(msg.getReceiverUserIds())
  411 + || msg.getReceiverUserIds().contains(userId)
  412 + || !ObjUtil.notEqual(msg.getSenderId(), userId);
420 413 }
421 414  
422 415 /**
... ...