Commit 9ed7c050b03c696bf92cba5e2a5332ff29b82411
1 parent
79de9233
feat(im):优化已读上报、群详情缓存与 RTC 通话状态
- 已读上报增加本地读位置覆盖判断,避免切换会话和当前会话自动已读时重复调用 read 接口 - 标记会话已读时同步推进本地 read 游标并写入 IndexedDB,接口失败仅记录日志 - 缓存私聊对方 maxReadMessageId,并在状态补拉、回执更新和退出 IM 时维护缓存 - 增加群详情 infoLoaded 内存标记,减少切群时重复拉取群详情,手动刷新和关键通知仍强制刷新 - 同步 GROUP_INFO_UPDATE 的 joinApproval,避免群审批配置在前端缓存中陈旧 - 优化群通话胶囊条状态,记录 participantsLoaded,按需补齐参与者并在通话无人时移除胶囊 - RTC_CALL_START 生成群通话最小胶囊条,后续由参与者事件和 getActiveCall 补齐 - 退出 IM 时清理 RTC 状态和群通话缓存 - Vben antd/antd-next 调整媒体元素为函数 ref,修复 MediaStream 与元素挂载时序问题 - 修复 Vben 消息历史弹窗回调类型标注
Showing
4 changed files
with
74 additions
and
4 deletions
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java
| ... | ... | @@ -149,7 +149,9 @@ public class ImGroupServiceImpl implements ImGroupService { |
| 149 | 149 | boolean nameChanged = StrUtil.isNotEmpty(updateReqVO.getName()); |
| 150 | 150 | boolean noticeChanged = updateReqVO.getNotice() != null; |
| 151 | 151 | boolean avatarChanged = StrUtil.isNotEmpty(updateReqVO.getAvatar()); |
| 152 | - if (nameChanged || noticeChanged || avatarChanged) { | |
| 152 | + boolean joinApprovalChanged = updateReqVO.getJoinApproval() != null | |
| 153 | + && ObjUtil.notEqual(group.getJoinApproval(), updateReqVO.getJoinApproval()); | |
| 154 | + if (nameChanged || noticeChanged || avatarChanged || joinApprovalChanged) { | |
| 153 | 155 | List<Long> memberUserIds = groupMemberService.getActiveGroupMemberUserIdsByGroupId(groupId); |
| 154 | 156 | if (nameChanged) { |
| 155 | 157 | groupMessageService.sendGroupMessage(userId, memberUserIds, ImGroupMessageSendDTO.ofGroupNameUpdate( |
| ... | ... | @@ -161,7 +163,11 @@ public class ImGroupServiceImpl implements ImGroupService { |
| 161 | 163 | } |
| 162 | 164 | if (avatarChanged) { |
| 163 | 165 | groupMessageService.sendGroupMessage(userId, memberUserIds, ImGroupMessageSendDTO.ofGroupInfoUpdate( |
| 164 | - groupId, userId, group.getAvatar(), updateReqVO.getAvatar())); | |
| 166 | + groupId, userId, group.getAvatar(), updateReqVO.getAvatar(), null, null)); | |
| 167 | + } | |
| 168 | + if (joinApprovalChanged) { | |
| 169 | + groupMessageService.sendGroupMessage(userId, memberUserIds, ImGroupMessageSendDTO.ofGroupInfoUpdate( | |
| 170 | + groupId, userId, null, null, group.getJoinApproval(), updateReqVO.getJoinApproval())); | |
| 165 | 171 | } |
| 166 | 172 | } |
| 167 | 173 | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/dto/ImGroupMessageSendDTO.java
| ... | ... | @@ -63,11 +63,15 @@ public class ImGroupMessageSendDTO { |
| 63 | 63 | .setType(ImContentTypeEnum.GROUP_CREATE.getType()).setContent(notification); |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | - public static ImGroupMessageSendDTO ofGroupInfoUpdate(Long groupId, Long operatorUserId, String oldAvatar, String newAvatar) { | |
| 66 | + public static ImGroupMessageSendDTO ofGroupInfoUpdate(Long groupId, Long operatorUserId, | |
| 67 | + String oldAvatar, String newAvatar, | |
| 68 | + Boolean oldJoinApproval, Boolean newJoinApproval) { | |
| 67 | 69 | GroupInfoUpdateNotification notification = new GroupInfoUpdateNotification(); |
| 68 | 70 | notification.setOperatorUserId(operatorUserId); |
| 69 | 71 | notification.setOldAvatar(oldAvatar); |
| 70 | 72 | notification.setNewAvatar(newAvatar); |
| 73 | + notification.setOldJoinApproval(oldJoinApproval); | |
| 74 | + notification.setNewJoinApproval(newJoinApproval); | |
| 71 | 75 | return new ImGroupMessageSendDTO().setGroupId(groupId) |
| 72 | 76 | .setType(ImContentTypeEnum.GROUP_INFO_UPDATE.getType()).setContent(notification); |
| 73 | 77 | } | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupInfoUpdateNotification.java
| ... | ... | @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.im.service.websocket.notification.group; |
| 3 | 3 | import lombok.Data; |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | - * 群信息变更事件通知(当前承载头像变更,NAME / NOTICE 走独立事件) | |
| 6 | + * 群信息变更事件通知(NAME / NOTICE 走独立事件) | |
| 7 | 7 | */ |
| 8 | 8 | @Data |
| 9 | 9 | public class GroupInfoUpdateNotification extends BaseGroupNotification { |
| ... | ... | @@ -16,5 +16,13 @@ public class GroupInfoUpdateNotification extends BaseGroupNotification { |
| 16 | 16 | * 新群头像 |
| 17 | 17 | */ |
| 18 | 18 | private String newAvatar; |
| 19 | + /** | |
| 20 | + * 旧进群审批开关 | |
| 21 | + */ | |
| 22 | + private Boolean oldJoinApproval; | |
| 23 | + /** | |
| 24 | + * 新进群审批开关 | |
| 25 | + */ | |
| 26 | + private Boolean newJoinApproval; | |
| 19 | 27 | |
| 20 | 28 | } | ... | ... |
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java
| ... | ... | @@ -209,6 +209,58 @@ public class ImGroupServiceImplTest extends BaseMockitoUnitTest { |
| 209 | 209 | } |
| 210 | 210 | } |
| 211 | 211 | |
| 212 | + @Test | |
| 213 | + public void testUpdateGroup_joinApprovalChanged() { | |
| 214 | + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | |
| 215 | + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImGroupServiceImpl.class))) | |
| 216 | + .thenReturn(groupService); | |
| 217 | + | |
| 218 | + // 准备 | |
| 219 | + ImGroupDO group = ImGroupDO.builder().id(10L).name("群").ownerUserId(1L) | |
| 220 | + .joinApproval(false).status(CommonStatusEnum.ENABLE.getStatus()).build(); | |
| 221 | + when(groupMapper.selectById(10L)).thenReturn(group); | |
| 222 | + | |
| 223 | + ImGroupUpdateReqVO reqVO = new ImGroupUpdateReqVO(); | |
| 224 | + reqVO.setId(10L); | |
| 225 | + reqVO.setJoinApproval(true); | |
| 226 | + | |
| 227 | + // 调用 | |
| 228 | + ImGroupDO result = groupService.updateGroup(reqVO, 1L); | |
| 229 | + | |
| 230 | + // 断言 | |
| 231 | + verify(groupMapper).updateById(any(ImGroupDO.class)); | |
| 232 | + assertTrue(result.getJoinApproval()); | |
| 233 | + ArgumentCaptor<ImGroupMessageSendDTO> dtoCaptor = ArgumentCaptor.forClass(ImGroupMessageSendDTO.class); | |
| 234 | + verify(groupMessageService).sendGroupMessage(eq(1L), anyCollection(), dtoCaptor.capture()); | |
| 235 | + assertEquals(ImContentTypeEnum.GROUP_INFO_UPDATE.getType(), dtoCaptor.getValue().getType()); | |
| 236 | + } | |
| 237 | + } | |
| 238 | + | |
| 239 | + @Test | |
| 240 | + public void testUpdateGroup_joinApprovalSame() { | |
| 241 | + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) { | |
| 242 | + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(ImGroupServiceImpl.class))) | |
| 243 | + .thenReturn(groupService); | |
| 244 | + | |
| 245 | + // 准备 | |
| 246 | + ImGroupDO group = ImGroupDO.builder().id(10L).name("群").ownerUserId(1L) | |
| 247 | + .joinApproval(true).status(CommonStatusEnum.ENABLE.getStatus()).build(); | |
| 248 | + when(groupMapper.selectById(10L)).thenReturn(group); | |
| 249 | + | |
| 250 | + ImGroupUpdateReqVO reqVO = new ImGroupUpdateReqVO(); | |
| 251 | + reqVO.setId(10L); | |
| 252 | + reqVO.setJoinApproval(true); | |
| 253 | + | |
| 254 | + // 调用 | |
| 255 | + ImGroupDO result = groupService.updateGroup(reqVO, 1L); | |
| 256 | + | |
| 257 | + // 断言 | |
| 258 | + verify(groupMapper).updateById(any(ImGroupDO.class)); | |
| 259 | + assertTrue(result.getJoinApproval()); | |
| 260 | + verify(groupMessageService, never()).sendGroupMessage(anyLong(), anyCollection(), any()); | |
| 261 | + } | |
| 262 | + } | |
| 263 | + | |
| 212 | 264 | // ========== dissolveGroup ========== |
| 213 | 265 | |
| 214 | 266 | @Test | ... | ... |