Commit ded120902a0ff67e7c13d0c56ea485500405eaf8
1 parent
3b0abfb2
✨ feat(im): 初始化群申请 v0.4:第五把 review(多轮 finding 修复 + 通知静默化)
- 邀请路径写 addSource=INVITE;群主 / 管理员邀请绕过审批;inviteGroupMember 入参去重 - getGroupRequest 越权校验加成员有效状态判断;新增 list-by-group 接口 - 申请列表按 update_time 倒序,update(null, wrapper) 路径手动刷 updateTime - addByRequestId 不再 skip 同 id,复用记录刷新并置顶 - GroupRequestListDialog 单群模式订阅 store 增量同步;GroupMemberAddDialog 审批分支文案区分 - ConversationItem 增加 [X 条进群申请] 红字前缀;MessagePanel 顶部胶囊横幅 - 1505 / 1506 通知改静默:同意走群事件渲染系统提示,拒绝不再打扰;清掉 dead inviterUserId 字段
Showing
6 changed files
with
50 additions
and
17 deletions
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupRequestController.java
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.im.controller.admin.group; | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.im.controller.admin.group; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.util.ObjUtil; | 4 | import cn.hutool.core.util.ObjUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 6 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | 7 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| @@ -117,10 +118,14 @@ public class ImGroupRequestController { | @@ -117,10 +118,14 @@ public class ImGroupRequestController { | ||
| 117 | return success(CollUtil.getFirst(buildVOList(Collections.singletonList(request)))); | 118 | return success(CollUtil.getFirst(buildVOList(Collections.singletonList(request)))); |
| 118 | } | 119 | } |
| 119 | 120 | ||
| 120 | - /** 当前用户是否该群的群主 / 管理员 */ | 121 | + /** |
| 122 | + * 当前用户是否该群的有效群主 / 管理员 | ||
| 123 | + */ | ||
| 121 | private boolean isGroupOwnerOrAdmin(Long groupId, Long userId) { | 124 | private boolean isGroupOwnerOrAdmin(Long groupId, Long userId) { |
| 122 | ImGroupMemberDO member = groupMemberService.getGroupMember(groupId, userId); | 125 | ImGroupMemberDO member = groupMemberService.getGroupMember(groupId, userId); |
| 123 | - return member != null && ImGroupMemberRoleEnum.isOwnerOrAdmin(member.getRole()); | 126 | + return member != null |
| 127 | + && !CommonStatusEnum.DISABLE.getStatus().equals(member.getStatus()) | ||
| 128 | + && ImGroupMemberRoleEnum.isOwnerOrAdmin(member.getRole()); | ||
| 124 | } | 129 | } |
| 125 | 130 | ||
| 126 | /** 申请记录列表批量转 VO + 关联回填用户 / 群信息 */ | 131 | /** 申请记录列表批量转 VO + 关联回填用户 / 群信息 */ |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupRequestMapper.java
| @@ -25,16 +25,25 @@ public interface ImGroupRequestMapper extends BaseMapperX<ImGroupRequestDO> { | @@ -25,16 +25,25 @@ public interface ImGroupRequestMapper extends BaseMapperX<ImGroupRequestDO> { | ||
| 25 | .eq(ImGroupRequestDO::getUserId, userId)); | 25 | .eq(ImGroupRequestDO::getUserId, userId)); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | + default List<ImGroupRequestDO> selectListByGroupIdAndUserIds(Long groupId, Collection<Long> userIds) { | ||
| 29 | + return selectList(new LambdaQueryWrapperX<ImGroupRequestDO>() | ||
| 30 | + .eq(ImGroupRequestDO::getGroupId, groupId) | ||
| 31 | + .in(ImGroupRequestDO::getUserId, userIds)); | ||
| 32 | + } | ||
| 33 | + | ||
| 28 | default List<ImGroupRequestDO> selectListByGroupIdsAndHandleResult(Collection<Long> groupIds, Integer handleResult) { | 34 | default List<ImGroupRequestDO> selectListByGroupIdsAndHandleResult(Collection<Long> groupIds, Integer handleResult) { |
| 29 | return selectList(new LambdaQueryWrapperX<ImGroupRequestDO>() | 35 | return selectList(new LambdaQueryWrapperX<ImGroupRequestDO>() |
| 30 | .in(ImGroupRequestDO::getGroupId, groupIds) | 36 | .in(ImGroupRequestDO::getGroupId, groupIds) |
| 31 | .eq(ImGroupRequestDO::getHandleResult, handleResult) | 37 | .eq(ImGroupRequestDO::getHandleResult, handleResult) |
| 38 | + .orderByDesc(ImGroupRequestDO::getUpdateTime) | ||
| 32 | .orderByDesc(ImGroupRequestDO::getId)); | 39 | .orderByDesc(ImGroupRequestDO::getId)); |
| 33 | } | 40 | } |
| 34 | 41 | ||
| 35 | default List<ImGroupRequestDO> selectListByGroupId(Long groupId) { | 42 | default List<ImGroupRequestDO> selectListByGroupId(Long groupId) { |
| 43 | + // 同上,update_time 倒序优先于 id | ||
| 36 | return selectList(new LambdaQueryWrapperX<ImGroupRequestDO>() | 44 | return selectList(new LambdaQueryWrapperX<ImGroupRequestDO>() |
| 37 | .eq(ImGroupRequestDO::getGroupId, groupId) | 45 | .eq(ImGroupRequestDO::getGroupId, groupId) |
| 46 | + .orderByDesc(ImGroupRequestDO::getUpdateTime) | ||
| 38 | .orderByDesc(ImGroupRequestDO::getId)); | 47 | .orderByDesc(ImGroupRequestDO::getId)); |
| 39 | } | 48 | } |
| 40 | 49 |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupRequestServiceImpl.java
| @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; | @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; | ||
| 11 | import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; | 11 | import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; |
| 12 | import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupRequestDO; | 12 | import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupRequestDO; |
| 13 | import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupRequestMapper; | 13 | import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupRequestMapper; |
| 14 | +import cn.iocoder.yudao.module.im.enums.group.ImGroupAddSourceEnum; | ||
| 14 | import cn.iocoder.yudao.module.im.enums.group.ImGroupMemberRoleEnum; | 15 | import cn.iocoder.yudao.module.im.enums.group.ImGroupMemberRoleEnum; |
| 15 | import cn.iocoder.yudao.module.im.enums.group.ImGroupRequestHandleResultEnum; | 16 | import cn.iocoder.yudao.module.im.enums.group.ImGroupRequestHandleResultEnum; |
| 16 | import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; | 17 | import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; |
| @@ -99,6 +100,9 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { | @@ -99,6 +100,9 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { | ||
| 99 | // TODO @AI:看看是不是在 basemapperx 里,增加一个 updateXXXX;可以根据传递的 DO,深度更新的方法?应该匹配这个场景的对哇?【主要希望 service 不要出现 mapper 相关的类】 | 100 | // TODO @AI:看看是不是在 basemapperx 里,增加一个 updateXXXX;可以根据传递的 DO,深度更新的方法?应该匹配这个场景的对哇?【主要希望 service 不要出现 mapper 相关的类】 |
| 100 | ImGroupRequestDO request = groupRequestMapper.selectByGroupIdAndUserId(groupId, userId); | 101 | ImGroupRequestDO request = groupRequestMapper.selectByGroupIdAndUserId(groupId, userId); |
| 101 | if (request != null) { | 102 | if (request != null) { |
| 103 | + // update_time 显式置当前时间:update(null, wrapper) 不会触发 MetaObjectHandler.updateFill; | ||
| 104 | + // 列表查询按 update_time 倒序排,复用旧记录时必须刷新这一列才能让本次重新提交排到最前 | ||
| 105 | + LocalDateTime now = LocalDateTime.now(); | ||
| 102 | groupRequestMapper.update(null, new LambdaUpdateWrapper<ImGroupRequestDO>() | 106 | groupRequestMapper.update(null, new LambdaUpdateWrapper<ImGroupRequestDO>() |
| 103 | .eq(ImGroupRequestDO::getId, request.getId()) | 107 | .eq(ImGroupRequestDO::getId, request.getId()) |
| 104 | .set(ImGroupRequestDO::getApplyContent, reqVO.getApplyContent()) | 108 | .set(ImGroupRequestDO::getApplyContent, reqVO.getApplyContent()) |
| @@ -107,11 +111,12 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { | @@ -107,11 +111,12 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { | ||
| 107 | .set(ImGroupRequestDO::getInviterUserId, null) | 111 | .set(ImGroupRequestDO::getInviterUserId, null) |
| 108 | .set(ImGroupRequestDO::getHandleUserId, null) | 112 | .set(ImGroupRequestDO::getHandleUserId, null) |
| 109 | .set(ImGroupRequestDO::getHandleContent, null) | 113 | .set(ImGroupRequestDO::getHandleContent, null) |
| 110 | - .set(ImGroupRequestDO::getHandleTime, null)); | 114 | + .set(ImGroupRequestDO::getHandleTime, null) |
| 115 | + .set(ImGroupRequestDO::getUpdateTime, now)); | ||
| 111 | request.setApplyContent(reqVO.getApplyContent()).setAddSource(reqVO.getAddSource()) | 116 | request.setApplyContent(reqVO.getApplyContent()).setAddSource(reqVO.getAddSource()) |
| 112 | .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult()) | 117 | .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult()) |
| 113 | .setInviterUserId(null).setHandleUserId(null) | 118 | .setInviterUserId(null).setHandleUserId(null) |
| 114 | - .setHandleContent(null).setHandleTime(null); | 119 | + .setHandleContent(null).setHandleTime(null).setUpdateTime(now); |
| 115 | } else { | 120 | } else { |
| 116 | request = BeanUtils.toBean(reqVO, ImGroupRequestDO.class) | 121 | request = BeanUtils.toBean(reqVO, ImGroupRequestDO.class) |
| 117 | .setUserId(userId).setInviterUserId(null) | 122 | .setUserId(userId).setInviterUserId(null) |
| @@ -206,27 +211,35 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { | @@ -206,27 +211,35 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { | ||
| 206 | ImGroupDO group = groupService.validateGroupExists(groupId); | 211 | ImGroupDO group = groupService.validateGroupExists(groupId); |
| 207 | 212 | ||
| 208 | // 1. 每个被邀请人 upsert 一条 inviter_user_id=邀请人 的记录 | 213 | // 1. 每个被邀请人 upsert 一条 inviter_user_id=邀请人 的记录 |
| 209 | - // 已有记录(同一对 group_id, user_id 唯一):覆盖 inviter_user_id + 重置 handle_result + 清空旧处理痕迹(走 LambdaUpdateWrapper.set 显式置 null) | 214 | + // 已有记录(同一对 group_id, user_id 唯一):覆盖 inviter_user_id / add_source + 重置 handle_result + 清空旧处理痕迹(走 LambdaUpdateWrapper.set 显式置 null) |
| 215 | + // add_source 强制覆写为 INVITE,避免审批通过后把残留的旧来源回写进群成员留痕 | ||
| 216 | + Integer inviteSource = ImGroupAddSourceEnum.INVITE.getSource(); | ||
| 210 | List<ImGroupRequestDO> requests = new ArrayList<>(invitedUserIds.size()); | 217 | List<ImGroupRequestDO> requests = new ArrayList<>(invitedUserIds.size()); |
| 211 | // TODO @AI:看看是不是在 basemapperx 里,增加一个 updateXXXX;可以根据传递的 DO,深度更新的方法?应该匹配这个场景的对哇?【主要希望 service 不要出现 mapper 相关的类】 | 218 | // TODO @AI:看看是不是在 basemapperx 里,增加一个 updateXXXX;可以根据传递的 DO,深度更新的方法?应该匹配这个场景的对哇?【主要希望 service 不要出现 mapper 相关的类】 |
| 219 | + // 同 applyJoinGroup:update(null, wrapper) 不走 MetaObjectHandler.updateFill,复用旧记录必须显式刷 update_time,否则按 update_time 倒序排不到最前 | ||
| 220 | + LocalDateTime now = LocalDateTime.now(); | ||
| 212 | for (Long invitedUserId : invitedUserIds) { | 221 | for (Long invitedUserId : invitedUserIds) { |
| 213 | ImGroupRequestDO existing = groupRequestMapper.selectByGroupIdAndUserId(groupId, invitedUserId); | 222 | ImGroupRequestDO existing = groupRequestMapper.selectByGroupIdAndUserId(groupId, invitedUserId); |
| 214 | if (existing != null) { | 223 | if (existing != null) { |
| 215 | groupRequestMapper.update(null, new LambdaUpdateWrapper<ImGroupRequestDO>() | 224 | groupRequestMapper.update(null, new LambdaUpdateWrapper<ImGroupRequestDO>() |
| 216 | .eq(ImGroupRequestDO::getId, existing.getId()) | 225 | .eq(ImGroupRequestDO::getId, existing.getId()) |
| 217 | .set(ImGroupRequestDO::getInviterUserId, inviterUserId) | 226 | .set(ImGroupRequestDO::getInviterUserId, inviterUserId) |
| 227 | + .set(ImGroupRequestDO::getAddSource, inviteSource) | ||
| 218 | .set(ImGroupRequestDO::getHandleResult, ImGroupRequestHandleResultEnum.UNHANDLED.getResult()) | 228 | .set(ImGroupRequestDO::getHandleResult, ImGroupRequestHandleResultEnum.UNHANDLED.getResult()) |
| 219 | .set(ImGroupRequestDO::getHandleUserId, null) | 229 | .set(ImGroupRequestDO::getHandleUserId, null) |
| 220 | .set(ImGroupRequestDO::getHandleContent, null) | 230 | .set(ImGroupRequestDO::getHandleContent, null) |
| 221 | - .set(ImGroupRequestDO::getHandleTime, null)); | ||
| 222 | - existing.setInviterUserId(inviterUserId) | 231 | + .set(ImGroupRequestDO::getHandleTime, null) |
| 232 | + .set(ImGroupRequestDO::getUpdateTime, now)); | ||
| 233 | + existing.setInviterUserId(inviterUserId).setAddSource(inviteSource) | ||
| 223 | .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult()) | 234 | .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult()) |
| 224 | - .setHandleUserId(null).setHandleContent(null).setHandleTime(null); | 235 | + .setHandleUserId(null).setHandleContent(null).setHandleTime(null) |
| 236 | + .setUpdateTime(now); | ||
| 225 | requests.add(existing); | 237 | requests.add(existing); |
| 226 | continue; | 238 | continue; |
| 227 | } | 239 | } |
| 228 | ImGroupRequestDO insert = new ImGroupRequestDO() | 240 | ImGroupRequestDO insert = new ImGroupRequestDO() |
| 229 | .setGroupId(groupId).setUserId(invitedUserId).setInviterUserId(inviterUserId) | 241 | .setGroupId(groupId).setUserId(invitedUserId).setInviterUserId(inviterUserId) |
| 242 | + .setAddSource(inviteSource) | ||
| 230 | .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult()); | 243 | .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult()); |
| 231 | groupRequestMapper.insert(insert); | 244 | groupRequestMapper.insert(insert); |
| 232 | requests.add(insert); | 245 | requests.add(insert); |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java
| @@ -194,14 +194,15 @@ public class ImGroupServiceImpl implements ImGroupService { | @@ -194,14 +194,15 @@ public class ImGroupServiceImpl implements ImGroupService { | ||
| 194 | // 1.1 校验群存在 + 当前用户是群成员;同时拿到 role 供下面审批分支判断 | 194 | // 1.1 校验群存在 + 当前用户是群成员;同时拿到 role 供下面审批分支判断 |
| 195 | ImGroupDO group = validateGroupExists(groupId); | 195 | ImGroupDO group = validateGroupExists(groupId); |
| 196 | ImGroupMemberDO operator = groupMemberService.validateMemberInGroup(groupId, userId); | 196 | ImGroupMemberDO operator = groupMemberService.validateMemberInGroup(groupId, userId); |
| 197 | - // 1.2 排除已在群中的用户 | 197 | + // 1.2 入参去重 + 排除已在群中的用户 |
| 198 | List<ImGroupMemberDO> activeMembers = groupMemberService.getActiveGroupMemberListByGroupId(groupId); | 198 | List<ImGroupMemberDO> activeMembers = groupMemberService.getActiveGroupMemberListByGroupId(groupId); |
| 199 | - activeMembers.forEach(member -> inviteReqVO.getMemberUserIds().remove(member.getUserId())); | ||
| 200 | - if (CollUtil.isEmpty(inviteReqVO.getMemberUserIds())) { | 199 | + Set<Long> activeMemberUserIds = convertSet(activeMembers, ImGroupMemberDO::getUserId); |
| 200 | + List<Long> memberUserIds = new ArrayList<>(new LinkedHashSet<>(inviteReqVO.getMemberUserIds())); | ||
| 201 | + memberUserIds.removeAll(activeMemberUserIds); | ||
| 202 | + if (CollUtil.isEmpty(memberUserIds)) { | ||
| 201 | return; | 203 | return; |
| 202 | } | 204 | } |
| 203 | // 1.3 校验被邀请人都是当前用户的好友 | 205 | // 1.3 校验被邀请人都是当前用户的好友 |
| 204 | - List<Long> memberUserIds = inviteReqVO.getMemberUserIds(); | ||
| 205 | List<ImFriendDO> friends = friendService.getActiveFriendList(userId, memberUserIds); | 206 | List<ImFriendDO> friends = friendService.getActiveFriendList(userId, memberUserIds); |
| 206 | Set<Long> friendUserIds = convertSet(friends, ImFriendDO::getFriendUserId); | 207 | Set<Long> friendUserIds = convertSet(friends, ImFriendDO::getFriendUserId); |
| 207 | Collection<Long> notFriendUserIds = CollUtil.subtract(memberUserIds, friendUserIds); | 208 | Collection<Long> notFriendUserIds = CollUtil.subtract(memberUserIds, friendUserIds); |
| @@ -214,7 +215,7 @@ public class ImGroupServiceImpl implements ImGroupService { | @@ -214,7 +215,7 @@ public class ImGroupServiceImpl implements ImGroupService { | ||
| 214 | } | 215 | } |
| 215 | 216 | ||
| 216 | // 2. 情况一:群开启审批 + 邀请人是普通成员,落 group_request 等群主 / 管理员处理 | 217 | // 2. 情况一:群开启审批 + 邀请人是普通成员,落 group_request 等群主 / 管理员处理 |
| 217 | - // 群主 / 管理员邀请视同已审批通过,绕过审批直接拉人进群(对齐 OpenIM / 微信) | 218 | + // 群主 / 管理员邀请,直接拉人进群 |
| 218 | if (Boolean.TRUE.equals(group.getJoinApproval()) | 219 | if (Boolean.TRUE.equals(group.getJoinApproval()) |
| 219 | && !ImGroupMemberRoleEnum.isOwnerOrAdmin(operator.getRole())) { | 220 | && !ImGroupMemberRoleEnum.isOwnerOrAdmin(operator.getRole())) { |
| 220 | groupRequestService.createInviteRequestList(groupId, userId, memberUserIds); | 221 | groupRequestService.createInviteRequestList(groupId, userId, memberUserIds); |
| @@ -227,7 +228,7 @@ public class ImGroupServiceImpl implements ImGroupService { | @@ -227,7 +228,7 @@ public class ImGroupServiceImpl implements ImGroupService { | ||
| 227 | 228 | ||
| 228 | // 4. 发 GROUP_MEMBER_INVITE 通知给全员;本地拼 receivers(已查的 active + 新邀请)避免缓存刚 evict 后强制走 DB | 229 | // 4. 发 GROUP_MEMBER_INVITE 通知给全员;本地拼 receivers(已查的 active + 新邀请)避免缓存刚 evict 后强制走 DB |
| 229 | Set<Long> allReceivers = new HashSet<>(memberUserIds); | 230 | Set<Long> allReceivers = new HashSet<>(memberUserIds); |
| 230 | - activeMembers.forEach(member -> allReceivers.add(member.getUserId())); | 231 | + allReceivers.addAll(activeMemberUserIds); |
| 231 | groupMessageService.sendGroupMessage(userId, allReceivers, | 232 | groupMessageService.sendGroupMessage(userId, allReceivers, |
| 232 | ImGroupMessageSendDTO.ofGroupMemberInvite(groupId, userId, memberUserIds)); | 233 | ImGroupMessageSendDTO.ofGroupMemberInvite(groupId, userId, memberUserIds)); |
| 233 | } | 234 | } |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/group/GroupRequestApprovedNotification.java
| @@ -6,8 +6,7 @@ import lombok.EqualsAndHashCode; | @@ -6,8 +6,7 @@ import lombok.EqualsAndHashCode; | ||
| 6 | /** | 6 | /** |
| 7 | * 入群申请被同意通知 | 7 | * 入群申请被同意通知 |
| 8 | * <p> | 8 | * <p> |
| 9 | - * 走私聊通道定向推送给申请人 + 群主 + 全部管理员; | ||
| 10 | - * 申请人侧据此弹 toast、列表项更新;admin 侧据此把 unhandledCount 减 1 并从未处理列表移除 | 9 | + * 走私聊通道定向推送给申请人 + 群主 + 全部管理员;admin 侧据此把 unhandledCount 减 1 并从未处理列表移除 |
| 11 | */ | 10 | */ |
| 12 | @EqualsAndHashCode(callSuper = true) | 11 | @EqualsAndHashCode(callSuper = true) |
| 13 | @Data | 12 | @Data |
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupRequestServiceImplTest.java
| @@ -265,8 +265,14 @@ public class ImGroupRequestServiceImplTest extends BaseMockitoUnitTest { | @@ -265,8 +265,14 @@ public class ImGroupRequestServiceImplTest extends BaseMockitoUnitTest { | ||
| 265 | groupRequestService.createInviteRequestList(10L, 1L, List.of(2L, 3L)); | 265 | groupRequestService.createInviteRequestList(10L, 1L, List.of(2L, 3L)); |
| 266 | 266 | ||
| 267 | // 断言:插入 2 条申请记录 + 推 1503 给 owner(每条 1 帧)共 2 帧 | 267 | // 断言:插入 2 条申请记录 + 推 1503 给 owner(每条 1 帧)共 2 帧 |
| 268 | - verify(groupRequestMapper, times(2)).insert(any(ImGroupRequestDO.class)); | 268 | + ArgumentCaptor<ImGroupRequestDO> captor = ArgumentCaptor.forClass(ImGroupRequestDO.class); |
| 269 | + verify(groupRequestMapper, times(2)).insert(captor.capture()); | ||
| 269 | verify(websocketService, times(2)).sendPrivateMessageAsync(anyLong(), any(ImPrivateMessageDTO.class)); | 270 | verify(websocketService, times(2)).sendPrivateMessageAsync(anyLong(), any(ImPrivateMessageDTO.class)); |
| 271 | + // 断言:每条记录 inviterUserId=1 + addSource=INVITE,避免审批通过后回写群成员留痕的来源为空 / 脏带旧值 | ||
| 272 | + captor.getAllValues().forEach(insert -> { | ||
| 273 | + assertEquals(1L, insert.getInviterUserId()); | ||
| 274 | + assertEquals(ImGroupAddSourceEnum.INVITE.getSource(), insert.getAddSource()); | ||
| 275 | + }); | ||
| 270 | } | 276 | } |
| 271 | 277 | ||
| 272 | private AdminUserRespDTO buildUser(Long id, String nickname) { | 278 | private AdminUserRespDTO buildUser(Long id, String nickname) { |