im:code review 消息发送的逻辑

This commit is contained in:
YunaiV 2024-03-21 13:54:49 +08:00
parent 9c1764e36e
commit 0e79d8ec53
21 changed files with 74 additions and 35 deletions

View File

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

View File

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

View File

@ -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; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @hao这个是不是删除掉应该不会有这个 VO
@Schema(description = "管理后台 - 收件箱分页 Request VO") @Schema(description = "管理后台 - 收件箱分页 Request VO")
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
import jakarta.validation.Valid; import jakarta.validation.Valid;
// TODO @hao不用的方法删除下哈
/** /**
* 收件箱 Service 接口 * IM 收件箱 Service 接口
* *
* @author 芋道源码 * @author 芋道源码
*/ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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