Commit ae71b7134c5545d703a940c2d00a79459efa9588

Authored by YunaiV
1 parent bed0a463

✨ feat(im): 初始化群申请 v0.2:第三把 review

yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java
@@ -74,6 +74,10 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> { @@ -74,6 +74,10 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> {
74 READ(2201, "已读", false, false), 74 READ(2201, "已读", false, false),
75 75
76 // ========== 好友通知(1201-1210 直接复用 OpenIM 段位编号) ========== 76 // ========== 好友通知(1201-1210 直接复用 OpenIM 段位编号) ==========
  77 + // TODO @芋艿:FRIEND_REQUEST_* 与 GROUP_REQUEST_* 都是 persistent=false 的 SysMsg,离线 pull 拉不到,
  78 + // 目前可能丢失「实时 toast 提醒」体验(业务状态不丢,前端上线 fetch{Friend,Group}RequestList 能补回来);
  79 + // 未来思考下怎么优化:候选方案 1)改 persistent=true 入私聊消息流 + 让客户端按 type 自渲染;
  80 + // 2)服务端补一个「未读通知拉取」接口给前端冷启动调用。
77 /** 81 /**
78 * 对应 OpenIM:FriendApplicationApprovedNotification 1201 82 * 对应 OpenIM:FriendApplicationApprovedNotification 1201
79 * 对应自己的类:FriendRequestApprovedNotification 83 * 对应自己的类:FriendRequestApprovedNotification
@@ -148,7 +152,7 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> { @@ -148,7 +152,7 @@ public enum ImMessageTypeEnum implements ArrayValuable<Integer> {
148 GROUP_INFO_UPDATE(1502, "群信息变更", true, false), 152 GROUP_INFO_UPDATE(1502, "群信息变更", true, false),
149 /** 153 /**
150 * 对应 OpenIM:sdkws.JoinGroupApplicationTips(JoinGroupApplicationNotification 1503) 154 * 对应 OpenIM:sdkws.JoinGroupApplicationTips(JoinGroupApplicationNotification 1503)
151 - * 对应自己的类:GroupRequestNotification 155 + * 对应自己的类:GroupRequestReceivedNotification
152 * 场景:用户申请加群 / 普通成员邀请待审批,定向私聊推送给群主 + 全部管理员(多端同步);不入群消息流 156 * 场景:用户申请加群 / 普通成员邀请待审批,定向私聊推送给群主 + 全部管理员(多端同步);不入群消息流
153 */ 157 */
154 GROUP_REQUEST_RECEIVED(1503, "收到新的入群申请", false, false), 158 GROUP_REQUEST_RECEIVED(1503, "收到新的入群申请", false, false),
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendRequestServiceImpl.java
1 package cn.iocoder.yudao.module.im.service.friend; 1 package cn.iocoder.yudao.module.im.service.friend;
2 2
3 -import cn.hutool.core.bean.BeanUtil;  
4 -import cn.hutool.core.bean.copier.CopyOptions;  
5 import cn.hutool.core.util.ObjUtil; 3 import cn.hutool.core.util.ObjUtil;
  4 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
6 import cn.hutool.extra.spring.SpringUtil; 5 import cn.hutool.extra.spring.SpringUtil;
7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -89,18 +88,22 @@ public class ImFriendRequestServiceImpl implements ImFriendRequestService { @@ -89,18 +88,22 @@ public class ImFriendRequestServiceImpl implements ImFriendRequestService {
89 } 88 }
90 89
91 // 2. 落库:upsert 语义;同一对 (from, to) 唯一,已有记录覆盖申请理由 / 备注 / 来源 + 重置为未处理 + 清空旧处理痕迹 90 // 2. 落库:upsert 语义;同一对 (from, to) 唯一,已有记录覆盖申请理由 / 备注 / 来源 + 重置为未处理 + 清空旧处理痕迹
  91 + // null 字段(handleContent / handleTime)走 LambdaUpdateWrapper.set 显式清空,updateById 默认会忽略 null
  92 + // TODO @AI:看看是不是在 basemapperx 里,增加一个 updateXXXX;可以根据传递的 DO,深度更新的方法?应该匹配这个场景的对哇?【主要希望 service 不要出现 mapper 相关的类】
92 ImFriendRequestDO request = friendRequestMapper.selectByFromUserIdAndToUserId(fromUserId, toUserId); 93 ImFriendRequestDO request = friendRequestMapper.selectByFromUserIdAndToUserId(fromUserId, toUserId);
93 if (request != null) { 94 if (request != null) {
94 - // TODO @AI:相同的放在一行里;然后 null 直接这样更新是清理不掉的;看看是不是在 basemapperx 里,增加一个 updateXXXX;可以根据传递的 DO,深度更新的方法?应该匹配这个场景的对哇?  
95 - ImFriendRequestDO updateObj = new ImFriendRequestDO()  
96 - .setApplyContent(reqVO.getApplyContent())  
97 - .setDisplayName(reqVO.getDisplayName()) 95 + friendRequestMapper.update(null, new LambdaUpdateWrapper<ImFriendRequestDO>()
  96 + .eq(ImFriendRequestDO::getId, request.getId())
  97 + .set(ImFriendRequestDO::getApplyContent, reqVO.getApplyContent())
  98 + .set(ImFriendRequestDO::getDisplayName, reqVO.getDisplayName())
  99 + .set(ImFriendRequestDO::getAddSource, reqVO.getAddSource())
  100 + .set(ImFriendRequestDO::getHandleResult, ImFriendRequestHandleResultEnum.UNHANDLED.getResult())
  101 + .set(ImFriendRequestDO::getHandleContent, null)
  102 + .set(ImFriendRequestDO::getHandleTime, null));
  103 + request.setApplyContent(reqVO.getApplyContent()).setDisplayName(reqVO.getDisplayName())
98 .setAddSource(reqVO.getAddSource()) 104 .setAddSource(reqVO.getAddSource())
99 .setHandleResult(ImFriendRequestHandleResultEnum.UNHANDLED.getResult()) 105 .setHandleResult(ImFriendRequestHandleResultEnum.UNHANDLED.getResult())
100 .setHandleContent(null).setHandleTime(null); 106 .setHandleContent(null).setHandleTime(null);
101 - updateObj.setId(request.getId());  
102 - friendRequestMapper.updateById(updateObj);  
103 - BeanUtil.copyProperties(updateObj, request, CopyOptions.create().setIgnoreNullValue(true));  
104 } else { 107 } else {
105 request = BeanUtils.toBean(reqVO, ImFriendRequestDO.class) 108 request = BeanUtils.toBean(reqVO, ImFriendRequestDO.class)
106 .setFromUserId(fromUserId).setToUserId(toUserId) 109 .setFromUserId(fromUserId).setToUserId(toUserId)
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupRequestServiceImpl.java
1 package cn.iocoder.yudao.module.im.service.group; 1 package cn.iocoder.yudao.module.im.service.group;
2 2
3 -import cn.hutool.core.bean.BeanUtil;  
4 -import cn.hutool.core.bean.copier.CopyOptions;  
5 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -21,7 +20,7 @@ import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; @@ -21,7 +20,7 @@ import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService;
21 import cn.iocoder.yudao.module.im.service.websocket.dto.ImPrivateMessageDTO; 20 import cn.iocoder.yudao.module.im.service.websocket.dto.ImPrivateMessageDTO;
22 import cn.iocoder.yudao.module.im.service.websocket.dto.notification.group.BaseGroupNotification; 21 import cn.iocoder.yudao.module.im.service.websocket.dto.notification.group.BaseGroupNotification;
23 import cn.iocoder.yudao.module.im.service.websocket.dto.notification.group.GroupRequestApprovedNotification; 22 import cn.iocoder.yudao.module.im.service.websocket.dto.notification.group.GroupRequestApprovedNotification;
24 -import cn.iocoder.yudao.module.im.service.websocket.dto.notification.group.GroupRequestNotification; 23 +import cn.iocoder.yudao.module.im.service.websocket.dto.notification.group.GroupRequestReceivedNotification;
25 import cn.iocoder.yudao.module.im.service.websocket.dto.notification.group.GroupRequestRejectedNotification; 24 import cn.iocoder.yudao.module.im.service.websocket.dto.notification.group.GroupRequestRejectedNotification;
26 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 25 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
27 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 26 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@@ -36,6 +35,7 @@ import java.time.LocalDateTime; @@ -36,6 +35,7 @@ import java.time.LocalDateTime;
36 import java.util.*; 35 import java.util.*;
37 36
38 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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.convertSet; 39 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
40 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*; 40 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
41 41
@@ -95,17 +95,23 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { @@ -95,17 +95,23 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService {
95 95
96 // 3. 情况二:群开启了审批;upsert 一条「主动申请」记录,inviterUserId=null 96 // 3. 情况二:群开启了审批;upsert 一条「主动申请」记录,inviterUserId=null
97 // 已有记录(无论之前是未处理 / 已同意 / 已拒绝),覆盖申请理由 + 来源 + 重置为未处理 + 清空旧处理痕迹 97 // 已有记录(无论之前是未处理 / 已同意 / 已拒绝),覆盖申请理由 + 来源 + 重置为未处理 + 清空旧处理痕迹
  98 + // null 字段(inviterUserId / handleUserId / handleContent / handleTime)走 LambdaUpdateWrapper.set 显式清空,updateById 默认会忽略 null
  99 + // TODO @AI:看看是不是在 basemapperx 里,增加一个 updateXXXX;可以根据传递的 DO,深度更新的方法?应该匹配这个场景的对哇?【主要希望 service 不要出现 mapper 相关的类】
98 ImGroupRequestDO request = groupRequestMapper.selectByGroupIdAndUserId(groupId, userId); 100 ImGroupRequestDO request = groupRequestMapper.selectByGroupIdAndUserId(groupId, userId);
99 if (request != null) { 101 if (request != null) {
100 - // TODO @AI:相同的放在一行里;然后 null 直接这样更新是清理不掉的;看看是不是在 basemapperx 里,增加一个 updateXXXX;可以根据传递的 DO,深度更新的方法?应该匹配这个场景的对哇?  
101 - ImGroupRequestDO updateObj = new ImGroupRequestDO()  
102 - .setInviterUserId(null)  
103 - .setApplyContent(reqVO.getApplyContent()).setAddSource(reqVO.getAddSource()) 102 + groupRequestMapper.update(null, new LambdaUpdateWrapper<ImGroupRequestDO>()
  103 + .eq(ImGroupRequestDO::getId, request.getId())
  104 + .set(ImGroupRequestDO::getApplyContent, reqVO.getApplyContent())
  105 + .set(ImGroupRequestDO::getAddSource, reqVO.getAddSource())
  106 + .set(ImGroupRequestDO::getHandleResult, ImGroupRequestHandleResultEnum.UNHANDLED.getResult())
  107 + .set(ImGroupRequestDO::getInviterUserId, null)
  108 + .set(ImGroupRequestDO::getHandleUserId, null)
  109 + .set(ImGroupRequestDO::getHandleContent, null)
  110 + .set(ImGroupRequestDO::getHandleTime, null));
  111 + request.setApplyContent(reqVO.getApplyContent()).setAddSource(reqVO.getAddSource())
104 .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult()) 112 .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult())
105 - .setHandleUserId(null).setHandleContent(null).setHandleTime(null);  
106 - updateObj.setId(request.getId());  
107 - groupRequestMapper.updateById(updateObj);  
108 - BeanUtil.copyProperties(updateObj, request, CopyOptions.create().setIgnoreNullValue(true)); 113 + .setInviterUserId(null).setHandleUserId(null)
  114 + .setHandleContent(null).setHandleTime(null);
109 } else { 115 } else {
110 request = BeanUtils.toBean(reqVO, ImGroupRequestDO.class) 116 request = BeanUtils.toBean(reqVO, ImGroupRequestDO.class)
111 .setUserId(userId).setInviterUserId(null) 117 .setUserId(userId).setInviterUserId(null)
@@ -115,9 +121,8 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { @@ -115,9 +121,8 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService {
115 121
116 // 4. 1503 私聊定向推群主 + 全部管理员(多端同步);payload 携带申请方昵称 / 头像 122 // 4. 1503 私聊定向推群主 + 全部管理员(多端同步);payload 携带申请方昵称 / 头像
117 AdminUserRespDTO applyUser = adminUserApi.getUser(userId); 123 AdminUserRespDTO applyUser = adminUserApi.getUser(userId);
118 - GroupRequestNotification payload = buildRequestNotification(group, request, applyUser);  
119 - for (Long receiverUserId : listOwnerAndAdminUserIds(group)) {  
120 - // TODO @AI:能不能发群的,只发给群管理员;就是 groupMessageService;只是不 persist 呀。 124 + GroupRequestReceivedNotification payload = buildRequestNotification(group, request, applyUser);
  125 + for (Long receiverUserId : getGroupMemberListByOwnerAndAdminUserIds(group)) {
121 websocketService.sendPrivateMessageAsync(receiverUserId, ImPrivateMessageDTO.ofGroupNotification( 126 websocketService.sendPrivateMessageAsync(receiverUserId, ImPrivateMessageDTO.ofGroupNotification(
122 ImMessageTypeEnum.GROUP_REQUEST_RECEIVED.getType(), userId, receiverUserId, payload)); 127 ImMessageTypeEnum.GROUP_REQUEST_RECEIVED.getType(), userId, receiverUserId, payload));
123 } 128 }
@@ -150,13 +155,9 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { @@ -150,13 +155,9 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService {
150 ImGroupMemberRoleEnum.NORMAL.getRole(), request.getAddSource(), request.getInviterUserId()); 155 ImGroupMemberRoleEnum.NORMAL.getRole(), request.getAddSource(), request.getInviterUserId());
151 156
152 // 5.1 1505 私聊推送给申请人 + 群主 + 全部管理员(每端单推) 157 // 5.1 1505 私聊推送给申请人 + 群主 + 全部管理员(每端单推)
153 - // TODO @AI:同类属性,放在一行,减少行占用;其它位置也是!  
154 - GroupRequestApprovedNotification payload = (GroupRequestApprovedNotification)  
155 - new GroupRequestApprovedNotification()  
156 - .setRequestId(request.getId())  
157 - .setGroupId(request.getGroupId())  
158 - .setUserId(request.getUserId())  
159 - .setOperatorUserId(userId); 158 + GroupRequestApprovedNotification payload = (GroupRequestApprovedNotification) new GroupRequestApprovedNotification()
  159 + .setRequestId(request.getId()).setGroupId(request.getGroupId()).setUserId(request.getUserId())
  160 + .setOperatorUserId(userId);
160 broadcastToOwnerAdminsAndApplicant(request.getGroupId(), request.getUserId(), payload, 161 broadcastToOwnerAdminsAndApplicant(request.getGroupId(), request.getUserId(), payload,
161 ImMessageTypeEnum.GROUP_REQUEST_APPROVED.getType(), userId); 162 ImMessageTypeEnum.GROUP_REQUEST_APPROVED.getType(), userId);
162 // 5.2 群事件:主动申请 → 1510 自由进群;被邀请 → 1509 成员加入 163 // 5.2 群事件:主动申请 → 1510 自由进群;被邀请 → 1509 成员加入
@@ -189,13 +190,9 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { @@ -189,13 +190,9 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService {
189 } 190 }
190 191
191 // 3. 1506 私聊推送给申请人 + 群主 + 全部管理员 192 // 3. 1506 私聊推送给申请人 + 群主 + 全部管理员
192 - GroupRequestRejectedNotification payload = (GroupRequestRejectedNotification)  
193 - new GroupRequestRejectedNotification()  
194 - .setRequestId(request.getId())  
195 - .setGroupId(request.getGroupId())  
196 - .setUserId(request.getUserId())  
197 - .setHandleContent(handleContent)  
198 - .setOperatorUserId(userId); 193 + GroupRequestRejectedNotification payload = (GroupRequestRejectedNotification) new GroupRequestRejectedNotification()
  194 + .setRequestId(request.getId()).setGroupId(request.getGroupId()).setUserId(request.getUserId())
  195 + .setHandleContent(handleContent).setOperatorUserId(userId);
199 broadcastToOwnerAdminsAndApplicant(request.getGroupId(), request.getUserId(), payload, 196 broadcastToOwnerAdminsAndApplicant(request.getGroupId(), request.getUserId(), payload,
200 ImMessageTypeEnum.GROUP_REQUEST_REJECTED.getType(), userId); 197 ImMessageTypeEnum.GROUP_REQUEST_REJECTED.getType(), userId);
201 } 198 }
@@ -209,17 +206,19 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { @@ -209,17 +206,19 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService {
209 ImGroupDO group = groupService.validateGroupExists(groupId); 206 ImGroupDO group = groupService.validateGroupExists(groupId);
210 207
211 // 1. 每个被邀请人 upsert 一条 inviter_user_id=邀请人 的记录 208 // 1. 每个被邀请人 upsert 一条 inviter_user_id=邀请人 的记录
212 - // 已有记录(同一对 group_id, user_id 唯一):覆盖 inviter_user_id + 重置 handle_result + 清空处理痕迹 209 + // 已有记录(同一对 group_id, user_id 唯一):覆盖 inviter_user_id + 重置 handle_result + 清空旧处理痕迹(走 LambdaUpdateWrapper.set 显式置 null)
213 List<ImGroupRequestDO> requests = new ArrayList<>(invitedUserIds.size()); 210 List<ImGroupRequestDO> requests = new ArrayList<>(invitedUserIds.size());
  211 + // TODO @AI:看看是不是在 basemapperx 里,增加一个 updateXXXX;可以根据传递的 DO,深度更新的方法?应该匹配这个场景的对哇?【主要希望 service 不要出现 mapper 相关的类】
214 for (Long invitedUserId : invitedUserIds) { 212 for (Long invitedUserId : invitedUserIds) {
215 ImGroupRequestDO existing = groupRequestMapper.selectByGroupIdAndUserId(groupId, invitedUserId); 213 ImGroupRequestDO existing = groupRequestMapper.selectByGroupIdAndUserId(groupId, invitedUserId);
216 if (existing != null) { 214 if (existing != null) {
217 - ImGroupRequestDO updateObj = new ImGroupRequestDO()  
218 - .setInviterUserId(inviterUserId)  
219 - .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult())  
220 - .setHandleUserId(null).setHandleContent(null).setHandleTime(null);  
221 - updateObj.setId(existing.getId());  
222 - groupRequestMapper.updateById(updateObj); 215 + groupRequestMapper.update(null, new LambdaUpdateWrapper<ImGroupRequestDO>()
  216 + .eq(ImGroupRequestDO::getId, existing.getId())
  217 + .set(ImGroupRequestDO::getInviterUserId, inviterUserId)
  218 + .set(ImGroupRequestDO::getHandleResult, ImGroupRequestHandleResultEnum.UNHANDLED.getResult())
  219 + .set(ImGroupRequestDO::getHandleUserId, null)
  220 + .set(ImGroupRequestDO::getHandleContent, null)
  221 + .set(ImGroupRequestDO::getHandleTime, null));
223 existing.setInviterUserId(inviterUserId) 222 existing.setInviterUserId(inviterUserId)
224 .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult()) 223 .setHandleResult(ImGroupRequestHandleResultEnum.UNHANDLED.getResult())
225 .setHandleUserId(null).setHandleContent(null).setHandleTime(null); 224 .setHandleUserId(null).setHandleContent(null).setHandleTime(null);
@@ -235,11 +234,10 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { @@ -235,11 +234,10 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService {
235 234
236 // 2. 推 1503 给群主 + 全部管理员;多端同步;每条申请单独推一帧 235 // 2. 推 1503 给群主 + 全部管理员;多端同步;每条申请单独推一帧
237 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(invitedUserIds); 236 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(invitedUserIds);
238 - List<Long> ownerAndAdmins = listOwnerAndAdminUserIds(group); 237 + List<Long> ownerAndAdmins = getGroupMemberListByOwnerAndAdminUserIds(group);
239 for (ImGroupRequestDO request : requests) { 238 for (ImGroupRequestDO request : requests) {
240 AdminUserRespDTO applyUser = userMap.get(request.getUserId()); 239 AdminUserRespDTO applyUser = userMap.get(request.getUserId());
241 - // TODO @AI:GroupRequestNotification 是不是要调整下命名?因为它是 RECEIVED 的,不然显得太公用了。  
242 - GroupRequestNotification payload = buildRequestNotification(group, request, applyUser); 240 + GroupRequestReceivedNotification payload = buildRequestNotification(group, request, applyUser);
243 for (Long receiverUserId : ownerAndAdmins) { 241 for (Long receiverUserId : ownerAndAdmins) {
244 websocketService.sendPrivateMessageAsync(receiverUserId, ImPrivateMessageDTO.ofGroupNotification( 242 websocketService.sendPrivateMessageAsync(receiverUserId, ImPrivateMessageDTO.ofGroupNotification(
245 ImMessageTypeEnum.GROUP_REQUEST_RECEIVED.getType(), inviterUserId, receiverUserId, payload)); 243 ImMessageTypeEnum.GROUP_REQUEST_RECEIVED.getType(), inviterUserId, receiverUserId, payload));
@@ -298,18 +296,14 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { @@ -298,18 +296,14 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService {
298 /** 296 /**
299 * 构建 1503 通知 payload;聚合申请方昵称 / 头像供前端直接渲染 297 * 构建 1503 通知 payload;聚合申请方昵称 / 头像供前端直接渲染
300 */ 298 */
301 - private GroupRequestNotification buildRequestNotification(ImGroupDO group, ImGroupRequestDO request,  
302 - AdminUserRespDTO applyUser) {  
303 - // TODO @AI:看看能不能链式调用;同类属性,相同行;  
304 - GroupRequestNotification payload = (GroupRequestNotification) new GroupRequestNotification()  
305 - .setRequestId(request.getId())  
306 - .setGroupId(group.getId())  
307 - .setUserId(request.getUserId()) 299 + private GroupRequestReceivedNotification buildRequestNotification(ImGroupDO group, ImGroupRequestDO request,
  300 + AdminUserRespDTO applyUser) {
  301 + Long operatorUserId = request.getInviterUserId() != null ? request.getInviterUserId() : request.getUserId();
  302 + GroupRequestReceivedNotification payload = (GroupRequestReceivedNotification) new GroupRequestReceivedNotification()
  303 + .setRequestId(request.getId()).setGroupId(group.getId()).setUserId(request.getUserId())
308 .setInviterUserId(request.getInviterUserId()) 304 .setInviterUserId(request.getInviterUserId())
309 - .setApplyContent(request.getApplyContent())  
310 - .setAddSource(request.getAddSource())  
311 - .setOperatorUserId(request.getInviterUserId() != null  
312 - ? request.getInviterUserId() : request.getUserId()); 305 + .setApplyContent(request.getApplyContent()).setAddSource(request.getAddSource())
  306 + .setOperatorUserId(operatorUserId);
313 if (applyUser != null) { 307 if (applyUser != null) {
314 payload.setUserNickname(applyUser.getNickname()).setUserAvatar(applyUser.getAvatar()); 308 payload.setUserNickname(applyUser.getNickname()).setUserAvatar(applyUser.getAvatar());
315 } 309 }
@@ -325,7 +319,7 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { @@ -325,7 +319,7 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService {
325 if (group == null) { 319 if (group == null) {
326 return; 320 return;
327 } 321 }
328 - Set<Long> receivers = new LinkedHashSet<>(listOwnerAndAdminUserIds(group)); 322 + Set<Long> receivers = new LinkedHashSet<>(getGroupMemberListByOwnerAndAdminUserIds(group));
329 receivers.add(applicantUserId); 323 receivers.add(applicantUserId);
330 for (Long receiverUserId : receivers) { 324 for (Long receiverUserId : receivers) {
331 websocketService.sendPrivateMessageAsync(receiverUserId, ImPrivateMessageDTO.ofGroupNotification( 325 websocketService.sendPrivateMessageAsync(receiverUserId, ImPrivateMessageDTO.ofGroupNotification(
@@ -334,16 +328,14 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService { @@ -334,16 +328,14 @@ public class ImGroupRequestServiceImpl implements ImGroupRequestService {
334 } 328 }
335 329
336 /** 330 /**
337 - * 列出群主 + 全部管理员的用户编号;以 group.ownerUserId 兜底,避免 group_member 表里 owner 角色记录异常缺失 331 + * 列出群主 + 全部管理员的用户编号
  332 + *
  333 + * @param group 群信息
  334 + * @return 群主 + 全部管理员的用户编号列表
338 */ 335 */
339 - // TODO @AI:有点过度设计了,直接 convertset 群成员即可!  
340 - private List<Long> listOwnerAndAdminUserIds(ImGroupDO group) {  
341 - Set<Long> set = new LinkedHashSet<>();  
342 - set.add(group.getOwnerUserId());  
343 - for (ImGroupMemberDO member : groupMemberService.getGroupMemberListByOwnerAndAdmin(group.getId())) {  
344 - set.add(member.getUserId());  
345 - }  
346 - return new ArrayList<>(set); 336 + private List<Long> getGroupMemberListByOwnerAndAdminUserIds(ImGroupDO group) {
  337 + return convertList(groupMemberService.getGroupMemberListByOwnerAndAdmin(group.getId()),
  338 + ImGroupMemberDO::getUserId);
347 } 339 }
348 340
349 } 341 }
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/group/GroupRequestNotification.java renamed to yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/notification/group/GroupRequestReceivedNotification.java
@@ -10,7 +10,7 @@ import lombok.EqualsAndHashCode; @@ -10,7 +10,7 @@ import lombok.EqualsAndHashCode;
10 */ 10 */
11 @EqualsAndHashCode(callSuper = true) 11 @EqualsAndHashCode(callSuper = true)
12 @Data 12 @Data
13 -public class GroupRequestNotification extends BaseGroupNotification { 13 +public class GroupRequestReceivedNotification extends BaseGroupNotification {
14 14
15 /** 15 /**
16 * 申请记录编号 16 * 申请记录编号
yudao-module-im/src/test/resources/sql/create_tables.sql
@@ -194,5 +194,6 @@ CREATE TABLE IF NOT EXISTS &quot;im_face_user_item&quot; ( @@ -194,5 +194,6 @@ CREATE TABLE IF NOT EXISTS &quot;im_face_user_item&quot; (
194 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 194 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
195 "deleted" bit NOT NULL DEFAULT FALSE, 195 "deleted" bit NOT NULL DEFAULT FALSE,
196 "tenant_id" bigint NOT NULL DEFAULT 0, 196 "tenant_id" bigint NOT NULL DEFAULT 0,
197 - PRIMARY KEY ("id") 197 + PRIMARY KEY ("id"),
  198 + CONSTRAINT "uk_im_face_user_item_user_url" UNIQUE ("user_id", "url", "tenant_id")
198 ) COMMENT 'IM 用户私有表情表'; 199 ) COMMENT 'IM 用户私有表情表';