Commit bca66fee74f1999e482ac6f86b8d5833aaaba511

Authored by YunaiV
1 parent 4f9dc0e4

fix(im): 修复历史退群群未读和成员加载问题

- 已读上报使用会话末条消息编号兜底
- 历史退群群不再请求群成员列表
- 群聊 read 放开当前成员校验,保留可见性校验
- Vben 群详情补齐 joinStatus
- 补充退群群已读边界测试
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java
... ... @@ -262,9 +262,7 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
262 262 throw exception(MESSAGE_GROUP_READ_DISABLED);
263 263 }
264 264 Assert.notNull(messageId, "已读消息编号不能为空");
265   - // 1.1 校验用户在群中(权限校验)
266   - groupMemberService.validateMemberInGroup(groupId, userId);
267   - // 1.2 校验消息属于当前群,且对当前用户可见(按快照)
  265 + // 1. 校验消息属于当前群,且对当前用户可见(按快照)
268 266 ImGroupMessageDO message = groupMessageMapper.selectById(messageId);
269 267 if (message == null
270 268 || ObjUtil.notEqual(message.getGroupId(), groupId)
... ...
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java
... ... @@ -460,15 +460,17 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest {
460 460 // ========== 群已读测试 ==========
461 461  
462 462 @Test
463   - public void testReadMessages_notInGroup() {
464   - // 准备:当前用户不在群中
465   - when(groupMemberService.validateMemberInGroup(10L, 1L))
466   - .thenThrow(new ServiceException(GROUP_MEMBER_NOT_IN_GROUP.getCode(), GROUP_MEMBER_NOT_IN_GROUP.getMsg()));
  463 + public void testReadMessages_messageInvisible() {
  464 + // 准备:消息不在用户接收快照内
  465 + when(groupMessageMapper.selectById(100L)).thenReturn(ImGroupMessageDO.builder()
  466 + .id(100L).groupId(10L).senderId(2L).receiverUserIds(List.of(2L))
  467 + .sendTime(LocalDateTime.now()).build());
467 468  
468   - // 调用并断言:越权校验
  469 + // 调用并断言
469 470 ServiceException exception = assertThrows(ServiceException.class,
470 471 () -> groupMessageService.readGroupMessages(1L, 10L, 100L));
471   - assertEquals(GROUP_MEMBER_NOT_IN_GROUP.getCode(), exception.getCode());
  472 + assertEquals(MESSAGE_NOT_IN_GROUP.getCode(), exception.getCode());
  473 + verify(conversationReadService, never()).updateConversationReadPosition(anyLong(), anyInt(), anyLong(), anyLong());
472 474 }
473 475  
474 476 @Test
... ... @@ -636,11 +638,7 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest {
636 638 springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImGroupMessageServiceImpl.class)))
637 639 .thenReturn(groupMessageService);
638 640  
639   - // 准备:用户在群;已读位置从 5 前进到 100
640   - ImGroupMemberDO member = ImGroupMemberDO.builder()
641   - .groupId(10L).userId(1L)
642   - .status(CommonStatusEnum.ENABLE.getStatus()).build();
643   - when(groupMemberService.validateMemberInGroup(10L, 1L)).thenReturn(member);
  641 + // 准备:消息对用户可见;已读位置从 5 前进到 100
644 642 when(groupMessageMapper.selectById(100L)).thenReturn(ImGroupMessageDO.builder()
645 643 .id(100L).groupId(10L).senderId(2L).receiverUserIds(List.of(1L))
646 644 .sendTime(LocalDateTime.now()).build());
... ... @@ -657,6 +655,7 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest {
657 655 // 断言:已读位置更新 + READ 事件
658 656 verify(conversationReadService).updateConversationReadPosition(1L, ImConversationTypeEnum.GROUP.getType(), 10L, 100L);
659 657 verify(imWebSocketService).sendNotificationAsync(eq(1L), anyInt(), anyInt(), any());
  658 + verify(groupMemberService, never()).validateMemberInGroup(10L, 1L);
660 659 }
661 660 }
662 661  
... ... @@ -715,10 +714,6 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest {
715 714 @Test
716 715 public void testReadGroupMessages_cursorAlreadyAhead() {
717 716 // 准备:已读游标已 >= 目标,直接返回
718   - ImGroupMemberDO member = ImGroupMemberDO.builder()
719   - .groupId(10L).userId(1L)
720   - .status(CommonStatusEnum.ENABLE.getStatus()).build();
721   - when(groupMemberService.validateMemberInGroup(10L, 1L)).thenReturn(member);
722 717 when(groupMessageMapper.selectById(100L)).thenReturn(ImGroupMessageDO.builder()
723 718 .id(100L).groupId(10L).senderId(2L).receiverUserIds(List.of(1L))
724 719 .sendTime(LocalDateTime.now()).build());
... ... @@ -738,11 +733,7 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest {
738 733  
739 734 @Test
740 735 public void testReadGroupMessages_messageNotInGroup() {
741   - // 准备:用户在群,但 messageId 属于其它群
742   - ImGroupMemberDO member = ImGroupMemberDO.builder()
743   - .groupId(10L).userId(1L)
744   - .status(CommonStatusEnum.ENABLE.getStatus()).build();
745   - when(groupMemberService.validateMemberInGroup(10L, 1L)).thenReturn(member);
  736 + // 准备:messageId 属于其它群
746 737 when(groupMessageMapper.selectById(100L)).thenReturn(ImGroupMessageDO.builder()
747 738 .id(100L).groupId(20L).senderId(2L).sendTime(LocalDateTime.now()).build());
748 739  
... ... @@ -753,6 +744,31 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest {
753 744 verify(conversationReadService, never()).updateConversationReadPosition(anyLong(), anyInt(), anyLong(), anyLong());
754 745 }
755 746  
  747 + @Test
  748 + public void testReadGroupMessages_quitMemberVisibleMessage_success() {
  749 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  750 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImGroupMessageServiceImpl.class)))
  751 + .thenReturn(groupMessageService);
  752 +
  753 + // 准备:历史消息的接收快照包含用户
  754 + when(groupMessageMapper.selectById(100L)).thenReturn(ImGroupMessageDO.builder()
  755 + .id(100L).groupId(10L).senderId(2L).receiverUserIds(List.of(1L, 2L))
  756 + .sendTime(LocalDateTime.now().minusMinutes(1)).build());
  757 + when(conversationReadService.getConversationReadMessageId(1L, ImConversationTypeEnum.GROUP.getType(), 10L))
  758 + .thenReturn(0L);
  759 + when(conversationReadService.updateConversationReadPosition(1L, ImConversationTypeEnum.GROUP.getType(), 10L, 100L))
  760 + .thenReturn(true);
  761 + when(groupMessageMapper.selectListByGroupIdAndPendingReceipt(10L, 0L, 100L)).thenReturn(List.of());
  762 +
  763 + // 调用
  764 + groupMessageService.readGroupMessages(1L, 10L, 100L);
  765 +
  766 + // 断言:不校验当前在群,直接推进已读位置
  767 + verify(groupMemberService, never()).validateMemberInGroup(10L, 1L);
  768 + verify(conversationReadService).updateConversationReadPosition(1L, ImConversationTypeEnum.GROUP.getType(), 10L, 100L);
  769 + }
  770 + }
  771 +
756 772 // ========== 回执:DONE 状态迁移 ==========
757 773  
758 774 @Test
... ...