Commit bf8eb273ec01bacc91369d079d5f7d6d2311bae2
1 parent
5e824ccb
✨ feat(message): 优化群聊的 enrichPullResultForViewer 已读状态、人数
Showing
1 changed file
with
29 additions
and
36 deletions
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 | /** | ... | ... |