Commit ca0aadac49e43d3047667347642c86dfb2886171

Authored by YunaiV
1 parent e89724e5

✨ feat(im): 添加私聊历史消息列表请求对象及相关接口调整

yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImGroupMessageController.java
@@ -42,10 +42,10 @@ public class ImGroupMessageController { @@ -42,10 +42,10 @@ public class ImGroupMessageController {
42 @Operation(summary = "拉取群聊消息(增量)") 42 @Operation(summary = "拉取群聊消息(增量)")
43 @Parameter(name = "minId", description = "最小消息 id", required = true, example = "0") 43 @Parameter(name = "minId", description = "最小消息 id", required = true, example = "0")
44 @Parameter(name = "size", description = "拉取数量", required = true, example = "100") 44 @Parameter(name = "size", description = "拉取数量", required = true, example = "100")
45 - public CommonResult<List<ImGroupMessageRespVO>> pullGroupMessages( 45 + public CommonResult<List<ImGroupMessageRespVO>> pullGroupMessageList(
46 @RequestParam("minId") Long minId, 46 @RequestParam("minId") Long minId,
47 @RequestParam("size") Integer size) { 47 @RequestParam("size") Integer size) {
48 - List<ImGroupMessageDO> messages = imGroupMessageService.pullGroupMessages(getLoginUserId(), minId, size); 48 + List<ImGroupMessageDO> messages = imGroupMessageService.pullGroupMessageList(getLoginUserId(), minId, size);
49 return success(messages.stream().map(this::toRespVO).collect(Collectors.toList())); 49 return success(messages.stream().map(this::toRespVO).collect(Collectors.toList()));
50 } 50 }
51 51
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/privates/ImPrivateMessageListReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.controller.admin.message.vo.privates;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import jakarta.validation.constraints.Max;
  7 +import jakarta.validation.constraints.Min;
  8 +import jakarta.validation.constraints.NotNull;
  9 +
  10 +@Schema(description = "管理后台 - 私聊历史消息列表 Request VO")
  11 +@Data
  12 +public class ImPrivateMessageListReqVO {
  13 +
  14 + @Schema(description = "接收人编号(对方)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  15 + @NotNull(message = "接收人编号不能为空")
  16 + private Long receiverId;
  17 +
  18 + @Schema(description = "起始消息编号,从该 id 往前拉取(不含)。为空则从最新消息开始", example = "1024")
  19 + private Long maxId;
  20 +
  21 + @Schema(description = "拉取数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
  22 + @NotNull(message = "拉取数量不能为空")
  23 + @Min(value = 1, message = "拉取数量最小值为 1")
  24 + @Max(value = 200, message = "拉取数量最大值为 200")
  25 + private Integer limit;
  26 +
  27 +}
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImGroupMessageMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.im.dal.mysql.message; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.im.dal.mysql.message;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
5 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO; 6 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO;
6 import org.apache.ibatis.annotations.Mapper; 7 import org.apache.ibatis.annotations.Mapper;
7 8
@@ -15,42 +16,35 @@ import java.util.List; @@ -15,42 +16,35 @@ import java.util.List;
15 @Mapper 16 @Mapper
16 public interface ImGroupMessageMapper extends BaseMapperX<ImGroupMessageDO> { 17 public interface ImGroupMessageMapper extends BaseMapperX<ImGroupMessageDO> {
17 18
18 - /**  
19 - * 根据群编号和 minId 增量拉取群聊消息  
20 - *  
21 - * @param groupIds 用户所在的群编号列表  
22 - * @param minId 最小消息 id(不含)  
23 - * @param size 拉取数量  
24 - * @return 消息列表  
25 - */  
26 default List<ImGroupMessageDO> selectListByMinId(List<Long> groupIds, Long minId, Integer size) { 19 default List<ImGroupMessageDO> selectListByMinId(List<Long> groupIds, Long minId, Integer size) {
27 - return selectList(new LambdaQueryWrapperX<ImGroupMessageDO>()  
28 - .in(ImGroupMessageDO::getGroupId, groupIds)  
29 - .gt(ImGroupMessageDO::getId, minId)  
30 - .orderByAsc(ImGroupMessageDO::getId)  
31 - .last("LIMIT " + size)); 20 + QueryWrapperX<ImGroupMessageDO> wrapper = new QueryWrapperX<>();
  21 + wrapper.in("group_id", groupIds)
  22 + .gt("id", minId)
  23 + .orderByAsc("id");
  24 + wrapper.limitN(size);
  25 + return selectList(wrapper);
32 } 26 }
33 27
34 - /**  
35 - * 根据 senderId 和 clientMessageId 查询(幂等校验)  
36 - */  
37 default ImGroupMessageDO selectBySenderIdAndClientMessageId(Long senderId, String clientMessageId) { 28 default ImGroupMessageDO selectBySenderIdAndClientMessageId(Long senderId, String clientMessageId) {
38 return selectOne(new LambdaQueryWrapperX<ImGroupMessageDO>() 29 return selectOne(new LambdaQueryWrapperX<ImGroupMessageDO>()
39 .eq(ImGroupMessageDO::getSenderId, senderId) 30 .eq(ImGroupMessageDO::getSenderId, senderId)
40 .eq(ImGroupMessageDO::getClientMessageId, clientMessageId)); 31 .eq(ImGroupMessageDO::getClientMessageId, clientMessageId));
41 } 32 }
42 33
  34 + // TODO @芋艿:暂时没 review
43 /** 35 /**
44 * 获取群内最新消息 id 36 * 获取群内最新消息 id
45 */ 37 */
46 default Long selectMaxIdByGroupId(Long groupId) { 38 default Long selectMaxIdByGroupId(Long groupId) {
47 - ImGroupMessageDO msg = selectOne(new LambdaQueryWrapperX<ImGroupMessageDO>()  
48 - .eq(ImGroupMessageDO::getGroupId, groupId)  
49 - .orderByDesc(ImGroupMessageDO::getId)  
50 - .last("LIMIT 1")); 39 + QueryWrapperX<ImGroupMessageDO> wrapper = new QueryWrapperX<>();
  40 + wrapper.eq("group_id", groupId)
  41 + .orderByDesc("id");
  42 + wrapper.limitN(1);
  43 + ImGroupMessageDO msg = selectOne(wrapper);
51 return msg == null ? null : msg.getId(); 44 return msg == null ? null : msg.getId();
52 } 45 }
53 46
  47 + // TODO @芋艿:暂时没 review
54 /** 48 /**
55 * 获取群内需要回执的消息(状态为 PENDING) 49 * 获取群内需要回执的消息(状态为 PENDING)
56 */ 50 */
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageService.java
@@ -29,7 +29,7 @@ public interface ImGroupMessageService { @@ -29,7 +29,7 @@ public interface ImGroupMessageService {
29 * @param size 拉取数量 29 * @param size 拉取数量
30 * @return 消息列表 30 * @return 消息列表
31 */ 31 */
32 - List<ImGroupMessageDO> pullGroupMessages(Long userId, Long minId, Integer size); 32 + List<ImGroupMessageDO> pullGroupMessageList(Long userId, Long minId, Integer size);
33 33
34 /** 34 /**
35 * 标记群聊消息已读 35 * 标记群聊消息已读
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java
@@ -11,8 +11,12 @@ import cn.iocoder.yudao.module.im.dal.redis.group.GroupReadPositionRedisDAO; @@ -11,8 +11,12 @@ import cn.iocoder.yudao.module.im.dal.redis.group.GroupReadPositionRedisDAO;
11 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 11 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
12 import cn.iocoder.yudao.module.im.enums.message.ImGroupMessageReceiptStatusEnum; 12 import cn.iocoder.yudao.module.im.enums.message.ImGroupMessageReceiptStatusEnum;
13 import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; 13 import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
  14 +import cn.iocoder.yudao.module.im.enums.message.ImMessageSceneEnum;
14 import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; 15 import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum;
15 -import cn.iocoder.yudao.module.im.enums.message.ImWebSocketTypeConstants; 16 +import cn.iocoder.yudao.module.im.websocket.ImGroupMessageSendMessage;
  17 +import cn.iocoder.yudao.module.im.websocket.ImMessageReadMessage;
  18 +import cn.iocoder.yudao.module.im.websocket.ImMessageReceiptMessage;
  19 +import cn.iocoder.yudao.module.im.websocket.ImMessageRecallMessage;
16 import cn.iocoder.yudao.module.im.service.group.ImGroupService; 20 import cn.iocoder.yudao.module.im.service.group.ImGroupService;
17 import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService; 21 import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService;
18 import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; 22 import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService;
@@ -28,6 +32,7 @@ import java.util.stream.Collectors; @@ -28,6 +32,7 @@ import java.util.stream.Collectors;
28 32
29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
30 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*; 34 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
  35 +import static cn.iocoder.yudao.module.im.enums.ImCommonConstants.*;
31 36
32 /** 37 /**
33 * IM 群聊消息 Service 实现类 38 * IM 群聊消息 Service 实现类
@@ -39,8 +44,6 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*; @@ -39,8 +44,6 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
39 @Slf4j 44 @Slf4j
40 public class ImGroupMessageServiceImpl implements ImGroupMessageService { 45 public class ImGroupMessageServiceImpl implements ImGroupMessageService {
41 46
42 - private static final int MAX_PULL_SIZE = 1000;  
43 -  
44 @Resource 47 @Resource
45 private ImGroupMessageMapper imGroupMessageMapper; 48 private ImGroupMessageMapper imGroupMessageMapper;
46 @Resource 49 @Resource
@@ -100,8 +103,8 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @@ -100,8 +103,8 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
100 } 103 }
101 104
102 @Override 105 @Override
103 - public List<ImGroupMessageDO> pullGroupMessages(Long userId, Long minId, Integer size) {  
104 - if (size > MAX_PULL_SIZE) { 106 + public List<ImGroupMessageDO> pullGroupMessageList(Long userId, Long minId, Integer size) {
  107 + if (size > MESSAGE_MAX_PULL_SIZE) {
105 throw exception(MESSAGE_PULL_SIZE_EXCEEDED); 108 throw exception(MESSAGE_PULL_SIZE_EXCEEDED);
106 } 109 }
107 // 获取用户所在的所有群 110 // 获取用户所在的所有群
@@ -162,11 +165,11 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @@ -162,11 +165,11 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
162 groupReadPositionRedisDAO.updateReadPosition(groupId, userId, maxMessageId); 165 groupReadPositionRedisDAO.updateReadPosition(groupId, userId, maxMessageId);
163 166
164 // 4. 发送 READ 事件给自己的其他终端 167 // 4. 发送 READ 事件给自己的其他终端
165 - Map<String, Object> readEvent = new HashMap<>();  
166 - readEvent.put("groupId", groupId.toString());  
167 - readEvent.put("messageScene", "group"); 168 + ImMessageReadMessage readMessage = new ImMessageReadMessage()
  169 + .setGroupId(groupId)
  170 + .setScene(ImMessageSceneEnum.GROUP.getScene());
168 webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), userId, 171 webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), userId,
169 - ImWebSocketTypeConstants.READ, readEvent); 172 + ImMessageReadMessage.TYPE, readMessage);
170 173
171 // 5. 刷新群回执 174 // 5. 刷新群回执
172 refreshGroupReceipts(groupId); 175 refreshGroupReceipts(groupId);
@@ -195,17 +198,17 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @@ -195,17 +198,17 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
195 198
196 // 5. 发送 RECALL 事件给所有群成员 199 // 5. 发送 RECALL 事件给所有群成员
197 List<ImGroupMemberDO> members = imGroupMemberService.selectByGroupId(message.getGroupId()); 200 List<ImGroupMemberDO> members = imGroupMemberService.selectByGroupId(message.getGroupId());
198 - Map<String, Object> recallEvent = new HashMap<>();  
199 - recallEvent.put("messageId", messageId.toString());  
200 - recallEvent.put("groupId", message.getGroupId().toString());  
201 - recallEvent.put("messageScene", "group");  
202 - recallEvent.put("senderId", userId.toString()); 201 + ImMessageRecallMessage recallMessage = new ImMessageRecallMessage()
  202 + .setMessageId(messageId)
  203 + .setGroupId(message.getGroupId())
  204 + .setSenderId(userId)
  205 + .setScene(ImMessageSceneEnum.GROUP.getScene());
203 for (ImGroupMemberDO m : members) { 206 for (ImGroupMemberDO m : members) {
204 if (CommonStatusEnum.DISABLE.getStatus().equals(m.getStatus())) { 207 if (CommonStatusEnum.DISABLE.getStatus().equals(m.getStatus())) {
205 continue; 208 continue;
206 } 209 }
207 webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), m.getUserId(), 210 webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), m.getUserId(),
208 - ImWebSocketTypeConstants.RECALL, recallEvent); 211 + ImMessageRecallMessage.TYPE, recallMessage);
209 } 212 }
210 } 213 }
211 214
@@ -285,20 +288,21 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @@ -285,20 +288,21 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
285 } 288 }
286 289
287 // 6. 广播 RECEIPT 事件给活跃成员 290 // 6. 广播 RECEIPT 事件给活跃成员
288 - Map<String, Object> receiptEvent = new HashMap<>();  
289 - receiptEvent.put("messageId", msg.getId().toString());  
290 - receiptEvent.put("groupId", groupId.toString());  
291 - receiptEvent.put("readCount", readCount);  
292 - receiptEvent.put("receiptStatus", allRead  
293 - ? ImGroupMessageReceiptStatusEnum.DONE.getStatus()  
294 - : ImGroupMessageReceiptStatusEnum.PENDING.getStatus()); 291 + ImMessageReceiptMessage receiptMessage = new ImMessageReceiptMessage()
  292 + .setMessageId(msg.getId())
  293 + .setGroupId(groupId)
  294 + .setReadCount(readCount)
  295 + .setReceiptStatus(allRead
  296 + ? ImGroupMessageReceiptStatusEnum.DONE.getStatus()
  297 + : ImGroupMessageReceiptStatusEnum.PENDING.getStatus())
  298 + .setScene(ImMessageSceneEnum.GROUP.getScene());
295 299
296 for (ImGroupMemberDO m : allMembers) { 300 for (ImGroupMemberDO m : allMembers) {
297 if (CommonStatusEnum.DISABLE.getStatus().equals(m.getStatus())) { 301 if (CommonStatusEnum.DISABLE.getStatus().equals(m.getStatus())) {
298 continue; 302 continue;
299 } 303 }
300 webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), m.getUserId(), 304 webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), m.getUserId(),
301 - ImWebSocketTypeConstants.RECEIPT, receiptEvent); 305 + ImMessageReceiptMessage.TYPE, receiptMessage);
302 } 306 }
303 } 307 }
304 } 308 }
@@ -341,23 +345,18 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @@ -341,23 +345,18 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
341 */ 345 */
342 private void sendGroupMessageEvent(ImGroupMessageDO message, Long groupId) { 346 private void sendGroupMessageEvent(ImGroupMessageDO message, Long groupId) {
343 List<ImGroupMemberDO> members = imGroupMemberService.selectByGroupId(groupId); 347 List<ImGroupMemberDO> members = imGroupMemberService.selectByGroupId(groupId);
344 - Map<String, Object> event = new HashMap<>();  
345 - event.put("id", message.getId().toString());  
346 - event.put("clientMessageId", message.getClientMessageId());  
347 - event.put("senderId", message.getSenderId().toString());  
348 - event.put("groupId", message.getGroupId().toString());  
349 - event.put("type", message.getType());  
350 - event.put("content", message.getContent());  
351 - event.put("status", message.getStatus());  
352 - event.put("sendTime", message.getSendTime().toString());  
353 - event.put("messageScene", "group");  
354 - if (CollUtil.isNotEmpty(message.getAtUserIds())) {  
355 - event.put("atUserIds", message.getAtUserIds());  
356 - }  
357 - if (CollUtil.isNotEmpty(message.getReceiverUserIds())) {  
358 - event.put("receiverUserIds", message.getReceiverUserIds());  
359 - }  
360 - event.put("receiptStatus", message.getReceiptStatus()); 348 + ImGroupMessageSendMessage sendMessage = new ImGroupMessageSendMessage()
  349 + .setId(message.getId())
  350 + .setClientMessageId(message.getClientMessageId())
  351 + .setSenderId(message.getSenderId())
  352 + .setGroupId(message.getGroupId())
  353 + .setType(message.getType())
  354 + .setContent(message.getContent())
  355 + .setStatus(message.getStatus())
  356 + .setSendTime(message.getSendTime())
  357 + .setAtUserIds(message.getAtUserIds())
  358 + .setReceiverUserIds(message.getReceiverUserIds())
  359 + .setReceiptStatus(message.getReceiptStatus());
361 360
362 // 确定推送目标 361 // 确定推送目标
363 List<Long> receiverList = message.getReceiverUserIds(); 362 List<Long> receiverList = message.getReceiverUserIds();
@@ -372,7 +371,7 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @@ -372,7 +371,7 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
372 continue; 371 continue;
373 } 372 }
374 webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), member.getUserId(), 373 webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), member.getUserId(),
375 - ImWebSocketTypeConstants.GROUP_MESSAGE, event); 374 + ImGroupMessageSendMessage.TYPE, sendMessage);
376 } 375 }
377 } 376 }
378 377
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImpl.java
@@ -186,13 +186,13 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService { @@ -186,13 +186,13 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService {
186 ImMessageReceiptMessage.TYPE, receiptMessage); 186 ImMessageReceiptMessage.TYPE, receiptMessage);
187 } 187 }
188 188
189 - private ImPrivateMessageServiceImpl getSelf() {  
190 - return SpringUtil.getBean(getClass());  
191 - }  
192 -  
193 @Override 189 @Override
194 public List<ImPrivateMessageDO> getPrivateMessageList(Long userId, ImPrivateMessageListReqVO reqVO) { 190 public List<ImPrivateMessageDO> getPrivateMessageList(Long userId, ImPrivateMessageListReqVO reqVO) {
195 return privateMessageMapper.selectHistoryList(userId, reqVO.getReceiverId(), reqVO.getMaxId(), reqVO.getLimit()); 191 return privateMessageMapper.selectHistoryList(userId, reqVO.getReceiverId(), reqVO.getMaxId(), reqVO.getLimit());
196 } 192 }
197 193
  194 + private ImPrivateMessageServiceImpl getSelf() {
  195 + return SpringUtil.getBean(getClass());
  196 + }
  197 +
198 } 198 }