Commit 4d2f470499f38894d040055747544ff699a7f626

Authored by YunaiV
1 parent 6e823f84

✨ feat(im): 增加群消息的置顶

yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupController.java
@@ -2,15 +2,8 @@ package cn.iocoder.yudao.module.im.controller.admin.group; @@ -2,15 +2,8 @@ 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.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 -import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupAdminAddReqVO;  
8 -import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupAdminRemoveReqVO;  
9 -import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupCreateReqVO;  
10 -import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupMessagePinReqVO;  
11 -import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupRespVO;  
12 -import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupTransferOwnerReqVO;  
13 -import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupUpdateReqVO; 6 +import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
14 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberInviteReqVO; 7 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberInviteReqVO;
15 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO; 8 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO;
16 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageRespVO; 9 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageRespVO;
@@ -28,17 +21,11 @@ import jakarta.validation.Valid; @@ -28,17 +21,11 @@ import jakarta.validation.Valid;
28 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
29 import org.springframework.web.bind.annotation.*; 22 import org.springframework.web.bind.annotation.*;
30 23
31 -import java.util.Collections;  
32 -import java.util.List;  
33 -import java.util.Map;  
34 -import java.util.Objects;  
35 -import java.util.Set;  
36 -import java.util.stream.Collectors; 24 +import java.util.*;
37 import java.util.stream.Stream; 25 import java.util.stream.Stream;
38 26
39 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 27 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
40 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;  
41 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; 28 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
42 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; 29 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
43 30
44 @Tag(name = "管理后台 - 群") 31 @Tag(name = "管理后台 - 群")
@@ -175,25 +162,21 @@ public class ImGroupController { @@ -175,25 +162,21 @@ public class ImGroupController {
175 if (CollUtil.isEmpty(groups)) { 162 if (CollUtil.isEmpty(groups)) {
176 return Collections.emptyList(); 163 return Collections.emptyList();
177 } 164 }
178 - // 仅当前用户是有效成员的群才允许回填置顶消息(按 groupId 一次性 IN 查成员关系) 165 + // 仅当前用户是有效成员的群才允许回填置顶消息
179 Set<Long> activeGroupIds = convertSet( 166 Set<Long> activeGroupIds = convertSet(
180 groupMemberService.getActiveGroupMemberListByUserId(loginUserId), ImGroupMemberDO::getGroupId); 167 groupMemberService.getActiveGroupMemberListByUserId(loginUserId), ImGroupMemberDO::getGroupId);
181 - // 把 active 群的 pinnedMessageIds 一次性聚合 IN 查询,避免 N+1  
182 Set<Long> allMessageIds = convertSetByFlatMap(groups, group -> activeGroupIds.contains(group.getId()) 168 Set<Long> allMessageIds = convertSetByFlatMap(groups, group -> activeGroupIds.contains(group.getId())
183 ? CollUtil.emptyIfNull(group.getPinnedMessageIds()).stream() : Stream.empty()); 169 ? CollUtil.emptyIfNull(group.getPinnedMessageIds()).stream() : Stream.empty());
184 Map<Long, ImGroupMessageDO> messageMap = groupMessageService.getGroupMessageMap(allMessageIds); 170 Map<Long, ImGroupMessageDO> messageMap = groupMessageService.getGroupMessageMap(allMessageIds);
185 - return CollectionUtils.convertList(groups, group -> { 171 + // 转换输出
  172 + return convertList(groups, group -> {
186 ImGroupRespVO vo = BeanUtils.toBean(group, ImGroupRespVO.class); 173 ImGroupRespVO vo = BeanUtils.toBean(group, ImGroupRespVO.class);
187 if (!activeGroupIds.contains(group.getId()) || CollUtil.isEmpty(group.getPinnedMessageIds())) { 174 if (!activeGroupIds.contains(group.getId()) || CollUtil.isEmpty(group.getPinnedMessageIds())) {
188 return vo; 175 return vo;
189 } 176 }
190 // 按 pin 顺序输出,已被删除的消息(messageMap 没命中)跳过 177 // 按 pin 顺序输出,已被删除的消息(messageMap 没命中)跳过
191 - // TODO @AI:使用 CollUtils convert 进一步简化。  
192 - List<ImGroupMessageRespVO> pinned = group.getPinnedMessageIds().stream()  
193 - .map(messageMap::get).filter(Objects::nonNull)  
194 - .map(msg -> BeanUtils.toBean(msg, ImGroupMessageRespVO.class))  
195 - .collect(Collectors.toList());  
196 - return vo.setPinnedMessages(pinned); 178 + List<ImGroupMessageDO> pinnedMesages = convertList(group.getPinnedMessageIds(), messageMap::get);
  179 + return vo.setPinnedMessages(BeanUtils.toBean(pinnedMesages, ImGroupMessageRespVO.class));
197 }); 180 });
198 } 181 }
199 182
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImGroupMessageController.java
@@ -64,8 +64,7 @@ public class ImGroupMessageController { @@ -64,8 +64,7 @@ public class ImGroupMessageController {
64 return success(BeanUtils.toBean(message, ImGroupMessageRespVO.class)); 64 return success(BeanUtils.toBean(message, ImGroupMessageRespVO.class));
65 } 65 }
66 66
67 - // TODO @AI:改成 get-read-user-ids  
68 - @GetMapping("/read-users") 67 + @GetMapping("/get-read-user-ids")
69 @Operation(summary = "获取群消息已读用户列表") 68 @Operation(summary = "获取群消息已读用户列表")
70 @Parameter(name = "groupId", description = "群编号", required = true, example = "1") 69 @Parameter(name = "groupId", description = "群编号", required = true, example = "1")
71 @Parameter(name = "messageId", description = "消息编号", required = true, example = "1") 70 @Parameter(name = "messageId", description = "消息编号", required = true, example = "1")
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java
@@ -160,13 +160,16 @@ public enum ImMessageTypeEnum implements ArrayValuable&lt;Integer&gt; { @@ -160,13 +160,16 @@ public enum ImMessageTypeEnum implements ArrayValuable&lt;Integer&gt; {
160 * 场景:用户改自己的群免打扰 / 群备注,仅推该用户其他在线终端做多端同步 160 * 场景:用户改自己的群免打扰 / 群备注,仅推该用户其他在线终端做多端同步
161 */ 161 */
162 GROUP_MEMBER_SETTING_UPDATE(1530, "群成员个人设置变更", false, false), 162 GROUP_MEMBER_SETTING_UPDATE(1530, "群成员个人设置变更", false, false),
163 - // TODO @AI:注释风格,还是参考上面的  
164 /** 163 /**
165 - * 自有扩展:群主 / 管理员置顶一条群消息,全员广播;前端把 messageId 加入 group.pinnedMessageIds 164 + * 对应 OpenIM:无(OpenIM 无群消息置顶功能,自有扩展)
  165 + * 对应自己的类:GroupMessagePinNotification
  166 + * 场景:群主 / 管理员置顶一条群消息,全员广播;payload 直接带消息对象,前端把 message push 进 group.pinnedMessages
166 */ 167 */
167 GROUP_MESSAGE_PIN(1531, "群消息置顶", true, false), 168 GROUP_MESSAGE_PIN(1531, "群消息置顶", true, false),
168 /** 169 /**
169 - * 自有扩展:群主 / 管理员取消置顶,全员广播;前端把 messageId 从 group.pinnedMessageIds 移除 170 + * 对应 OpenIM:无(OpenIM 无群消息置顶功能,自有扩展)
  171 + * 对应自己的类:GroupMessageUnpinNotification
  172 + * 场景:群主 / 管理员取消置顶,全员广播;前端按 messageId 从 group.pinnedMessages 移除
170 */ 173 */
171 GROUP_MESSAGE_UNPIN(1532, "群消息取消置顶", true, false); 174 GROUP_MESSAGE_UNPIN(1532, "群消息取消置顶", true, false);
172 175