Просмотр исходного кода

im:code review 消息发送的逻辑

YunaiV 1 год назад
Родитель
Сommit
0e79d8ec53
21 измененных файлов с 74 добавлено и 35 удалено
  1. 1 0
      yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/conversation/ImConversationTypeEnum.java
  2. 3 0
      yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageStatusEnum.java
  3. 1 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/ImInboxPageReqVO.java
  4. 1 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/ImInboxRespVO.java
  5. 1 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/ImInboxSaveReqVO.java
  6. 11 11
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImMessageController.java
  7. 2 1
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ImConversationMapper.java
  8. 1 1
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/inbox/ImInboxMapper.java
  9. 3 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImMessageMapper.java
  10. 1 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/redis/inbox/SequenceGeneratorRedisDao.java
  11. 2 1
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationService.java
  12. 10 3
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationServiceImpl.java
  13. 2 1
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxService.java
  14. 0 2
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxServiceImpl.java
  15. 1 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageService.java
  16. 9 4
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageServiceImpl.java
  17. 14 2
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/ImWebSocketMessageListener.java
  18. 7 6
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/message/ImReceiveMessage.java
  19. 2 3
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/message/ImSendMessage.java
  20. 1 0
      yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/conversation/ImConversationMapper.xml
  21. 1 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java

+ 1 - 0
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/conversation/ImConversationTypeEnum.java

@@ -12,6 +12,7 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum ImConversationTypeEnum {
 
+    // TODO @hao:单聊,我们使用 SINGLE,主要 private 这个单词在 java 里太特殊了;
     PRIVATE(1, "单聊"),
     GROUP(2, "群聊"),
     NOTICE(4, "通知会话");

+ 3 - 0
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageStatusEnum.java

@@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
 
 import java.util.Arrays;
 
+// TODO @hao:注释哈;
 @RequiredArgsConstructor
 @Getter
 public enum ImMessageStatusEnum implements IntArrayValuable {
@@ -16,7 +17,9 @@ public enum ImMessageStatusEnum implements IntArrayValuable {
     DELETED(4, "已删除"),
     RECALL(5, "已撤回");
 
+    // TODO @hao:静态变量,和普通变量,最好空一行;
     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ImMessageStatusEnum::getStatus).toArray();
+    // TODO @hao:注释哈;
     private final Integer status;
     private final String name;
 

+ 1 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/ImInboxPageReqVO.java

@@ -11,6 +11,7 @@ import java.time.LocalDateTime;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 
+// TODO @hao:这个是不是删除掉,应该不会有这个 VO 哈
 @Schema(description = "管理后台 - 收件箱分页 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)

+ 1 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/ImInboxRespVO.java

@@ -7,6 +7,7 @@ import lombok.Data;
 
 import java.time.LocalDateTime;
 
+// TODO @hao:这个是不是删除掉,应该不会有这个 VO 哈;应该给前端的,是要屏蔽掉这个表,最终返回的都是 Message 哈;说白了,这个 inbox 是后端的内部实现
 @Schema(description = "管理后台 - 收件箱 Response VO")
 @Data
 @ExcelIgnoreUnannotated

+ 1 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/ImInboxSaveReqVO.java

@@ -6,6 +6,7 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+// TODO @hao:这个是不是删除掉,应该不会有这个 VO 哈
 @Schema(description = "管理后台 - 收件箱新增/修改 Request VO")
 @Data
 @AllArgsConstructor

+ 11 - 11
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImMessageController.java

@@ -24,7 +24,7 @@ import java.util.List;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 
-@Tag(name = "管理后台 - 消息")
+@Tag(name = "管理后台 - IM 消息")
 @RestController
 @RequestMapping("/im/message")
 @Validated
@@ -33,19 +33,20 @@ public class ImMessageController {
     @Resource
     private ImMessageService imMessageService;
 
-    @GetMapping("/get-message-by-sequence")
-    @Operation(summary = "拉取消息-增量拉取大于 seq 的消息")
+    @GetMapping("/get-message-by-sequence") // TODO @hao:list-by-sequence
+    @Operation(summary = "拉取消息-增量拉取大于 seq 的消息") // TODO @hao:可以改成 拉取大于 sequence 的消息列表
     @Parameter(name = "sequence", description = "序号", required = true, example = "1")
     @Parameter(name = "size", description = "条数", required = true, example = "10")
-    @PreAuthorize("@ss.hasPermission('im:message:query')")
-    public CommonResult<List<ImMessageRespVO>> loadMessage(
-            @RequestParam("sequence") Long sequence,
-            @RequestParam("size") Integer size) {
+    @PreAuthorize("@ss.hasPermission('im:message:query')") // TODO @hao:权限可以删除哈;
+    public CommonResult<List<ImMessageRespVO>> loadMessage(@RequestParam("sequence") Long sequence,
+                                                           @RequestParam("size") Integer size) {
+        // TODO @hao:方法名,可以改成 getMessageListBySequence
+        // TODO @hao:如果是返回列表,变量要用 messages, 或者 messageList,体现出是复数
         List<ImMessageDO> message = imMessageService.loadMessage(getLoginUserId(), sequence, size);
         return success(BeanUtils.toBean(message, ImMessageRespVO.class));
     }
 
-
+    // TODO @hao:这个接口的使用场景是哪个哈?
     @GetMapping("/get-all-message")
     @Operation(summary = "拉取全部消息")
     @Parameter(name = "size", description = "条数", required = true, example = "10")
@@ -55,14 +56,13 @@ public class ImMessageController {
         return success(BeanUtils.toBean(message, ImMessageRespVO.class));
     }
 
-
+    // TODO @hao:是不是分页参数不太对哈?应该只有 conversationNo,sendTime 字段;然后,做链式分页的查询,不太适合传统的 pageSize + number 查询;
     @GetMapping("/page")
     @Operation(summary = "查询聊天记录-分页")
-    @PreAuthorize("@ss.hasPermission('im:message:query')")
+    @PreAuthorize("@ss.hasPermission('im:message:query')") // TODO @hao:权限可以删除哈;
     public CommonResult<PageResult<ImMessageRespVO>> getMessagePage(@Valid ImMessagePageReqVO pageReqVO) {
         PageResult<ImMessageDO> messagePage = imMessageService.getMessagePage(pageReqVO);
         return success(BeanUtils.toBean(messagePage, ImMessageRespVO.class));
     }
 
-
 }

+ 2 - 1
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ImConversationMapper.java

@@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Mapper;
 import java.util.Optional;
 
 /**
- * 会话 Mapper
+ * IM 会话 Mapper
  *
  * @author 芋道源码
  */
@@ -29,6 +29,7 @@ public interface ImConversationMapper extends BaseMapperX<ImConversationDO> {
                 .orderByDesc(ImConversationDO::getId));
     }
 
+    // TODO @hao:1)no) {,要有空格哈;2)可以直接 selectOne(ImConversationDO::getNo, no) 父类做了封装
     default ImConversationDO selectByNo(String no){
         return selectOne(new LambdaQueryWrapperX<ImConversationDO>().eq(ImConversationDO::getNo, no));
     }

+ 1 - 1
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/inbox/ImInboxMapper.java

@@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
 import org.apache.ibatis.annotations.Mapper;
 
 /**
- * 收件箱 Mapper
+ * IM 收件箱 Mapper
  *
  * @author 芋道源码
  */

+ 3 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImMessageMapper.java

@@ -36,6 +36,7 @@ public interface ImMessageMapper extends BaseMapperX<ImMessageDO> {
                 .orderByDesc(ImMessageDO::getId));
     }
 
+    // TODO @hao:不链表哈;先从 ImInboxDO 查询出 messageId,然后再到 ImMessageDO 里 IN
     default List<ImMessageDO> getGreaterThanSequenceMessage(Long userId, Long sequence, Integer size) {
         //查询 inbox 表中,大于 sequence 的消息,关联 message 表,按照 inbox 表 sequence 升序
         return selectJoinList(ImMessageDO.class, new MPJLambdaWrapper<ImMessageDO>()
@@ -47,6 +48,7 @@ public interface ImMessageMapper extends BaseMapperX<ImMessageDO> {
                 .last("limit 0," + size));
     }
 
+    // TODO @hao:在 dao 里,使用 selectListByUserId,查询用 select,条件用 by,这个算是 spring data 的 method dsl
     default List<ImMessageDO> getAllMessage(Long userId, Integer size) {
         //查询 inbox 表中,100条消息,关联 message 表,按照 inbox 表 sequence 降序
         return selectJoinList(ImMessageDO.class, new MPJLambdaWrapper<ImMessageDO>()
@@ -56,4 +58,5 @@ public interface ImMessageMapper extends BaseMapperX<ImMessageDO> {
                 .orderByDesc(ImInboxDO::getSequence)
                 .last("limit 0," + size));
     }
+
 }

+ 1 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/redis/inbox/SequenceGeneratorRedisDao.java

@@ -6,6 +6,7 @@ import org.springframework.stereotype.Repository;
 
 import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.INBOX_SEQUENCE;
 
+// TODO @芋艿:这个名字,需要在考虑下;
 /**
  * 序号生成器 Redis DAO
  *

+ 2 - 1
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationService.java

@@ -9,7 +9,7 @@ import jakarta.validation.Valid;
 import java.util.List;
 
 /**
- * 会话 Service 接口
+ * IM 会话 Service 接口
  *
  * @author 芋道源码
  */
@@ -74,4 +74,5 @@ public interface ImConversationService {
      * @param updateReqVO 更新信息
      */
     void updateLastReadTime(ImConversationSaveReqVO updateReqVO);
+
 }

+ 10 - 3
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationServiceImpl.java

@@ -18,7 +18,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.CONVERSATION_NOT_EXISTS;
 
 /**
- * 会话 Service 实现类
+ * IM 会话 Service 实现类
  *
  * @author 芋道源码
  */
@@ -26,18 +26,19 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.CONVERSATION_N
 @Validated
 public class ImConversationServiceImpl implements ImConversationService {
 
+    // TODO @hao: 自己模块的注入,不用带 im 前缀哈;
     @Resource
     private ImConversationMapper imConversationMapper;
 
+    // TODO @hao: 这个方法,是不是不需要哈
     @Override
     public Long createConversation(ImConversationSaveReqVO createReqVO) {
-        // 插入
         ImConversationDO conversation = BeanUtils.toBean(createReqVO, ImConversationDO.class);
         imConversationMapper.insert(conversation);
-        // 返回
         return conversation.getId();
     }
 
+    // TODO @hao: 这个方法,是不是不需要哈
     @Override
     public void updateConversation(ImConversationSaveReqVO updateReqVO) {
         // 校验存在
@@ -47,6 +48,7 @@ public class ImConversationServiceImpl implements ImConversationService {
         imConversationMapper.updateById(updateObj);
     }
 
+    // TODO @hao: 考虑到可能和端上不同步,可以不校验是不是存储。另外,不基于 id 删除。要基于 no + userId 删除哈。说白了,对端上要屏蔽 id 字段
     @Override
     public void deleteConversation(Long id) {
         // 校验存在
@@ -86,6 +88,11 @@ public class ImConversationServiceImpl implements ImConversationService {
         createOrUpdateConversation(updateReqVO);
     }
 
+    // TODO @hao:updateTop 和 updateLastReadTime 使用独立的逻辑实现,不使用统一的 ImConversationSaveReqVO;
+    // TODO 大体步骤建议:
+    // 1. 先 getOrderCreateConversation,查询会话,不存在则插入;
+    // 2. 更新对应的字段
+    // 3. 做对应更新的 notify 推送
     private void createOrUpdateConversation(ImConversationSaveReqVO updateReqVO) {
         // 操作会话(已读、置顶)时,才会延迟创建,要先判断是否存在,根据 no 查询是否存在,不存在则新增
         ImConversationDO conversation = imConversationMapper.selectByNo(updateReqVO.getNo());

+ 2 - 1
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxService.java

@@ -6,8 +6,9 @@ import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxSaveReqVO;
 import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
 import jakarta.validation.Valid;
 
+// TODO @hao:不用的方法,删除下哈;
 /**
- * 收件箱 Service 接口
+ * IM 收件箱 Service 接口
  *
  * @author 芋道源码
  */

+ 0 - 2
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxServiceImpl.java

@@ -27,10 +27,8 @@ public class ImInboxServiceImpl implements ImInboxService {
 
     @Override
     public Long createInbox(ImInboxSaveReqVO createReqVO) {
-        // 插入
         ImInboxDO inbox = BeanUtils.toBean(createReqVO, ImInboxDO.class);
         imInboxMapper.insert(inbox);
-        // 返回
         return inbox.getId();
     }
 

+ 1 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageService.java

@@ -91,6 +91,7 @@ public interface ImMessageService {
      */
     List<ImMessageDO> loadMessage(Long userId, Long sequence, Integer size);
 
+    // TODO @hao;这种接口,项目里叫 getMessageListByUserId,尽量保持 getList 获取列表哈;
     /**
      * 拉取全部消息
      *

+ 9 - 4
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageServiceImpl.java

@@ -84,26 +84,31 @@ public class ImMessageServiceImpl implements ImMessageService {
     }
 
     private ImMessageDO saveImMessageDO(ImSendMessage message, Long fromUserId) {
+        // TODO @hao:可以搞成 messageDO
+        // TODO @hao:链式调用
         ImMessageDO imMessageDO = new ImMessageDO();
         imMessageDO.setClientMessageId(message.getClientMessageId());
         imMessageDO.setSenderId(fromUserId);
         imMessageDO.setReceiverId(message.getReceiverId());
-        //查询发送人昵称和发送人头像
+        // 查询发送人昵称和发送人头像
         AdminUserRespDTO user = adminUserApi.getUser(fromUserId);
         imMessageDO.setSenderNickname(user.getNickname());
         imMessageDO.setSenderAvatar(user.getAvatar());
         imMessageDO.setConversationType(message.getConversationType());
         imMessageDO.setContentType(message.getContentType());
-        //单聊:s_{userId}_{targetId} 群聊:群聊:g_{groupId}
+        // 单聊:s_{userId}_{targetId} 群聊:群聊:g_{groupId}
+        // TODO @hao:这里的 conversationNo 的生成,可以写到工具类里,或者干脆放到 ImConversationTypeEnum 里;
         if (message.getConversationType().equals(ImConversationTypeEnum.PRIVATE.getType())) {
             imMessageDO.setConversationNo("s_" + fromUserId + "_" + message.getReceiverId());
         } else if (message.getConversationType().equals(ImConversationTypeEnum.GROUP.getType())) {
             imMessageDO.setConversationNo("g_" + message.getReceiverId());
         }
         imMessageDO.setContent(message.getContent());
-        //消息来源 100-用户发送;200-系统发送(一般是通知);不能为空
+        // TODO @hao:枚举哈;
+        // 消息来源 100-用户发送;200-系统发送(一般是通知);不能为空
         imMessageDO.setSendFrom(100);
         imMessageDO.setSendTime(TimeUtil.now());
+        // TODO @hao:服务端的消息,就是默认生成哈;
         imMessageDO.setMessageStatus(ImMessageStatusEnum.SENDING.getStatus());
         imMessageMapper.insert(imMessageDO);
         return imMessageDO;
@@ -111,7 +116,7 @@ public class ImMessageServiceImpl implements ImMessageService {
 
     @Override
     public void updateMessageStatus(Long messageId, Integer messageStatus) {
-        //校验 id 是否存在
+        // 校验 id 是否存在
         validateMessageExists(messageId);
         //更新消息状态
         ImMessageDO imMessageDO = new ImMessageDO();

+ 14 - 2
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/ImWebSocketMessageListener.java

@@ -23,6 +23,7 @@ import org.springframework.web.socket.WebSocketSession;
 
 import java.util.List;
 
+// TODO @hao:消息发送,使用 http 上行。因为在 cloud 框架下,我们比较难去 Listener。因为 im-server 不会自己启动 websocket 路径
 /**
  * WebSocket im
  *
@@ -60,9 +61,10 @@ public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSe
         if (message.getConversationType().equals(ImConversationTypeEnum.PRIVATE.getType())) {
             handlePrivateMessage(fromUserId, message);
         } else if (message.getConversationType().equals(ImConversationTypeEnum.GROUP.getType())) {
-            //处理群聊消息
+            // 处理群聊消息
             handleGroupMessage(fromUserId, message);
         } else {
+            // TODO @hao:是不是应该会话类型哈?然后打印这种错误日志,最好把 message 整个打进去,这样更好定位完整的消息;
             log.error("[onMessage][消息类型({}) 未支持]", message.getConversationType());
         }
     }
@@ -74,13 +76,17 @@ public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSe
      * @param message    发送的IM消息
      */
     private void handleGroupMessage(Long fromUserId, ImSendMessage message) {
+        // TODO @hao:群存在;
+        // TODO @hao:发送人在群里;
         ImMessageDO imMessageDO = imMessageService.saveGroupMessage(message, fromUserId); // 保存群聊消息
         Long groupId = message.getReceiverId();
 
         // 发送消息给群聊成员
+        // TODO @hao:变量名可以叫 groupMembers;一般不用 DO 后缀,除非为了区分;
         List<GroupMemberDO> groupMemberDOList = imGroupMemberService.selectByGroupId(groupId);
         groupMemberDOList.forEach(groupMemberDO -> {
-            //过滤掉自己
+            // 过滤掉自己
+            // TODO @hao:98 到 99 的代码,是不是可以融合到这里来;
             if (groupMemberDO.getUserId().equals(fromUserId)) {
                 return;
             }
@@ -106,11 +112,14 @@ public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSe
      * @param message    发送的IM消息
      */
     private void handlePrivateMessage(Long fromUserId, ImSendMessage message) {
+        // TODO @hao:需要校验 receiverId 存在;
         ImMessageDO imMessageDO = imMessageService.savePrivateMessage(message, fromUserId); // 保存私人消息
         Long receiverId = message.getReceiverId();
 
+        // TODO @hao:sequenceGeneratorRedisDao.generateSequence 和 createAndSaveInbox 步骤,要加锁;不然会有并发问题;如果有疑问,可以微信招我沟通哈;另外,创建 seq、保存收件箱、发送,可以统一封装到 imInboxService 里哈;这样,后续要发某个消息,就是丢个 message 对象进去;
         Long fromUserSequence = sequenceGeneratorRedisDao.generateSequence(fromUserId); // 生成发送者序列
         Long fromUserInboxId = createAndSaveInbox(fromUserId, imMessageDO.getId(), fromUserSequence); // 创建并保存发送者收件箱
+
         Long receiverSequence = sequenceGeneratorRedisDao.generateSequence(message.getReceiverId()); // 生成接收者序列
         Long receiverInboxId = createAndSaveInbox(message.getReceiverId(), imMessageDO.getId(), receiverSequence); // 创建并保存接收者收件箱
 
@@ -119,6 +128,7 @@ public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSe
         sendMessage(receiverId, receiverInboxId, imMessageDO, message, receiverSequence);
 
         // 更新消息状态为成功
+        // TODO @hao:后端消息的发送,默认就是 success 哈,不要区分 status;status 更多是前端使用的;
         imMessageService.updateMessageStatus(imMessageDO.getId(), ImMessageStatusEnum.SUCCESS.getStatus());
         // 保存私人会话,只有在 client 操作会话(已读、置顶)时,才会延迟创建
         //imConversationService.savePrivateConversation(fromUserId, receiverId);
@@ -137,6 +147,7 @@ public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSe
         return imInboxService.createInbox(inboxSaveReqVO); // 创建收件箱
     }
 
+    // TODO @hao:这个使用 WebSocketSenderApi 发送哈;
     /**
      * 发送消息
      *
@@ -147,6 +158,7 @@ public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSe
      * @param sequence    序列
      */
     private void sendMessage(Long fromUserId, Long inboxId, ImMessageDO imMessageDO, ImSendMessage message, Long sequence) {
+        // TODO @hao:不要每个字段 set 占一行;想同类型的字段用一行,然后链式 set,会更简洁干净。
         ImReceiveMessage receiveMessage = new ImReceiveMessage(); // 创建接收消息
         receiveMessage.setFromId(fromUserId); // 设置发送者ID
         receiveMessage.setConversationType(message.getConversationType()); // 设置会话类型

+ 7 - 6
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/message/ImReceiveMessage.java

@@ -1,8 +1,6 @@
 package cn.iocoder.yudao.module.im.websocket.message;
 
-import cn.iocoder.yudao.module.im.dal.dataobject.message.body.ImMessageBody;
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
 import lombok.Data;
 
 import java.time.LocalDateTime;
@@ -17,22 +15,25 @@ public class ImReceiveMessage {
     @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     private Long fromId;  // 根据 conversationType 区分
 
+    // TODO @hao:昵称和头像,也直接发给接收人好了;
+
+    @Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
+    private Long messageId;
+
     @Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Integer contentType; // 参见 ImMessageTypeEnum 枚举
 
     @Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
     private String content;
 
-    @Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
-    private Long messageId;
-
     @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime sendTime;
 
+    // TODO @hao:inboxId 不需要哈;
     @Schema(description = "收件箱编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18389")
     private Long inboxId;
 
-    @Schema(description = "序号,按照 user 递增", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Schema(description = "序号", requiredMode = Schema.RequiredMode.REQUIRED)
     private Long sequence;
 
 }

+ 2 - 3
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/message/ImSendMessage.java

@@ -1,11 +1,9 @@
 package cn.iocoder.yudao.module.im.websocket.message;
 
-import cn.iocoder.yudao.module.im.dal.dataobject.message.body.ImMessageBody;
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
 
-@Schema(description = "管理后台 - 消息发送 send")
+@Schema(description = "管理后台 - IM 消息发送 send")
 @Data
 public class ImSendMessage {
 
@@ -13,6 +11,7 @@ public class ImSendMessage {
     private String clientMessageId;
 
     @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    // TODO @hao:用下 InEnum 校验。这样可以少写 15 的注释哈;类似 contentType 也是;
     private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
 
     @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")

+ 1 - 0
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/conversation/ImConversationMapper.xml

@@ -2,6 +2,7 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.conversation.ImConversationMapper">
 
+    <!-- TODO @hao:xml 如果用不到,可以先删除哈 -->
     <!--
         一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
         无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。

+ 1 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java

@@ -41,6 +41,7 @@ public class AdminUserRespDTO {
      */
     private String mobile;
 
+    // TODO @hao:这种字段的添加,最好和 do 保持一致的顺序哈;
     /**
      * 用户头像
      */