From ba5b7dbd0aeb4db8ddf6b6d4d64cbd2844fb0a0f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 12 Apr 2026 15:27:21 +0800 Subject: [PATCH] 📝 refactor(messages): 重构消息类,简化字段命名和结构,增强可读性 --- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/AudioMessage.java | 42 ++++++++---------------------------------- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/CardMessage.java | 24 +++++------------------- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FileMessage.java | 23 ++++++++++++----------- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/ImageMessage.java | 27 ++++++++++++++++++--------- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/LocationMessage.java | 17 ++++------------- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/TextMessage.java | 13 +++---------- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/VideoMessage.java | 29 +++++++++++++++++++++++------ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberService.java | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java | 14 +++++++------- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImpl.java | 16 ++++++++-------- yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java | 22 +++++++++++++--------- yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImplTest.java | 24 ++++++++++++++---------- yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImWebSocketEventTest.java | 19 ++++++++++++------- 14 files changed, 295 insertions(+), 143 deletions(-) create mode 100644 yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberService.java create mode 100644 yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/AudioMessage.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/AudioMessage.java index e6f6cfb..0b399bd 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/AudioMessage.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/AudioMessage.java @@ -1,50 +1,24 @@ package cn.iocoder.yudao.module.im.dal.dataobject.message.content; -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; -// TODO @anhaohao:要有 IM; -// TODO @芋艿:后续要挪到 api 包下,主要是给外部接口使用 /** - * 语音消息的 {@link ImMessageDO 字段 content} 的内容 - * - * @author 芋道源码 + * 语音消息内容 */ @Data -@AllArgsConstructor -@NoArgsConstructor public class AudioMessage { /** - * 语音文件唯一 ID + * 语音文件 URL */ - private String uuid; - - /** - * 语音文件的本地路径 - */ - private String soundPath; - + private String url; /** - * 语音文件下载地址 + * 语音时长(秒) */ - private String sourceUrl; - - /** - * 语音文件大小 - */ - private int dataSize; - - /** - * 语音时长 - */ - private int duration; - + private Integer duration; /** - * 语音文件类型 + * 文件大小(字节) */ - private String soundType; + private Long size; -} \ No newline at end of file +} diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/CardMessage.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/CardMessage.java index 4199f79..fd1d082 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/CardMessage.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/CardMessage.java @@ -1,38 +1,24 @@ package cn.iocoder.yudao.module.im.dal.dataobject.message.content; -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; /** - * 名片消息的 {@link ImMessageDO 字段 content} 的内容 - * - * @author anhaohao - * @since 2024/3/23 下午5:53 + * 名片消息内容 */ @Data -@AllArgsConstructor -@NoArgsConstructor public class CardMessage { /** - * 用户 ID + * 用户编号 */ - private String userID; - + private Long userId; /** - * 用户名 + * 用户昵称 */ private String nickname; - /** * 用户头像 */ - private int faceURL; + private String avatar; - /** - * 扩展字段 - */ - private String ex; } diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FileMessage.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FileMessage.java index 0cb112e..aabba54 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FileMessage.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FileMessage.java @@ -1,27 +1,28 @@ package cn.iocoder.yudao.module.im.dal.dataobject.message.content; -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; /** - * 文件消息的 {@link ImMessageDO 字段 content} 的内容 - * - * @author 芋道源码 + * 文件消息内容 */ @Data -@AllArgsConstructor -@NoArgsConstructor public class FileMessage { /** + * 文件 URL + */ + private String url; + /** * 文件名 */ private String name; /** - * 文件 URL + * 文件大小(字节) */ - private String url; + private Long size; + /** + * 文件类型(扩展名) + */ + private String type; -} \ No newline at end of file +} diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/ImageMessage.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/ImageMessage.java index 2cd0dfb..547937c 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/ImageMessage.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/ImageMessage.java @@ -1,23 +1,32 @@ package cn.iocoder.yudao.module.im.dal.dataobject.message.content; -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; /** - * 图片消息的 {@link ImMessageDO 字段 content} 的内容 - * - * @author 芋道源码 + * 图片消息内容 */ @Data -@AllArgsConstructor -@NoArgsConstructor public class ImageMessage { /** - * 图片地址 + * 图片 URL */ private String url; + /** + * 缩略图 URL + */ + private String thumbnailUrl; + /** + * 图片宽度 + */ + private Integer width; + /** + * 图片高度 + */ + private Integer height; + /** + * 图片大小(字节) + */ + private Long size; } diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/LocationMessage.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/LocationMessage.java index 439b18f..fdbb7c7 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/LocationMessage.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/LocationMessage.java @@ -1,26 +1,17 @@ package cn.iocoder.yudao.module.im.dal.dataobject.message.content; -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; /** - * 地址位置消息的 {@link ImMessageDO 字段 content} 的内容 - * - * @author 芋道源码 + * 位置消息内容 */ @Data -@AllArgsConstructor -@NoArgsConstructor public class LocationMessage { /** - * 地理位置 - *

- * 例如说:中国 浙江省 杭州市 网商路 599号 + * 位置描述 */ - private String address; + private String description; /** * 经度 */ @@ -30,4 +21,4 @@ public class LocationMessage { */ private Double latitude; -} \ No newline at end of file +} diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/TextMessage.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/TextMessage.java index 35edfff..73a45b0 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/TextMessage.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/TextMessage.java @@ -1,23 +1,16 @@ package cn.iocoder.yudao.module.im.dal.dataobject.message.content; -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO; -import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; /** - * 文本消息的 {@link ImMessageDO 字段 content} 的内容 - * - * @author 芋道源码 + * 文本消息内容 */ @Data -@AllArgsConstructor -@NoArgsConstructor public class TextMessage { /** - * 文本消息的具体内容 + * 文本内容 */ private String content; -} \ No newline at end of file +} diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/VideoMessage.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/VideoMessage.java index 166954c..822042c 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/VideoMessage.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/VideoMessage.java @@ -1,19 +1,36 @@ package cn.iocoder.yudao.module.im.dal.dataobject.message.content; -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO; import lombok.Data; /** - * 视频消息的 {@link ImMessageDO 字段 content} 的内容 - * - * @author 芋道源码 + * 视频消息内容 */ @Data public class VideoMessage { /** - * 视频地址 + * 视频文件 URL */ private String url; + /** + * 视频封面 URL + */ + private String coverUrl; + /** + * 视频时长(秒) + */ + private Integer duration; + /** + * 视频宽度 + */ + private Integer width; + /** + * 视频高度 + */ + private Integer height; + /** + * 文件大小(字节) + */ + private Long size; -} \ No newline at end of file +} diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberService.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberService.java new file mode 100644 index 0000000..0f63bc4 --- /dev/null +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberService.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.im.service.group; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberPageReqVO; +import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberSaveReqVO; +import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * 群成员 Service 接口 + * + * @author 芋道源码 + */ +public interface ImGroupMemberService { + + /** + * 创建群成员 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createGroupMember(@Valid ImGroupMemberSaveReqVO createReqVO); + + /** + * 更新群成员 + * + * @param updateReqVO 更新信息 + */ + void updateGroupMember(@Valid ImGroupMemberSaveReqVO updateReqVO); + + /** + * 删除群成员 + * + * @param id 编号 + */ + void deleteGroupMember(Long id); + + /** + * 获得群成员 + * + * @param id 编号 + * @return 群成员 + */ + ImGroupMemberDO getGroupMember(Long id); + + /** + * 获得群成员分页 + * + * @param pageReqVO 分页查询 + * @return 群成员分页 + */ + PageResult getGroupMemberPage(ImGroupMemberPageReqVO pageReqVO); + + /** + * 根据群组id查询群成员 + * + * @param groupId 群组id + * @return 群成员列表 + */ + List selectByGroupId(Long groupId); + + /** + * 根据群编号和用户编号查询群成员 + * + * @param groupId 群编号 + * @param userId 用户编号 + * @return 群成员 + */ + ImGroupMemberDO getGroupMember(Long groupId, Long userId); + + /** + * 查询用户所在的所有群的成员记录 + * + * @param userId 用户编号 + * @return 群成员记录列表 + */ + List getGroupMembersByUserId(Long userId); +} diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java new file mode 100644 index 0000000..fac59ed --- /dev/null +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.im.service.group; + +import org.springframework.stereotype.Service; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; + +import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.*; +import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.im.dal.mysql.groupmember.ImGroupMemberMapper; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*; + +/** + * 群成员 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ImGroupMemberServiceImpl implements ImGroupMemberService { + + @Resource + private ImGroupMemberMapper imGroupMemberMapper; + + @Override + public Long createGroupMember(ImGroupMemberSaveReqVO createReqVO) { + // 插入 + ImGroupMemberDO groupMember = BeanUtils.toBean(createReqVO, ImGroupMemberDO.class); + imGroupMemberMapper.insert(groupMember); + // 返回 + return groupMember.getId(); + } + + @Override + public void updateGroupMember(ImGroupMemberSaveReqVO updateReqVO) { + // 校验存在 + validateGroupMemberExists(updateReqVO.getId()); + // 更新 + ImGroupMemberDO updateObj = BeanUtils.toBean(updateReqVO, ImGroupMemberDO.class); + imGroupMemberMapper.updateById(updateObj); + } + + @Override + public void deleteGroupMember(Long id) { + // 校验存在 + validateGroupMemberExists(id); + // 删除 + imGroupMemberMapper.deleteById(id); + } + + private void validateGroupMemberExists(Long id) { + if (imGroupMemberMapper.selectById(id) == null) { + throw exception(GROUP_MEMBER_NOT_EXISTS); + } + } + + @Override + public ImGroupMemberDO getGroupMember(Long id) { + return imGroupMemberMapper.selectById(id); + } + + @Override + public PageResult getGroupMemberPage(ImGroupMemberPageReqVO pageReqVO) { + return imGroupMemberMapper.selectPage(pageReqVO); + } + + @Override + public List selectByGroupId(Long groupId) { + return imGroupMemberMapper.selectListByGroupId(groupId); + } + + @Override + public ImGroupMemberDO getGroupMember(Long groupId, Long userId) { + return imGroupMemberMapper.selectByGroupIdAndUserId(groupId, userId); + } + + @Override + public List getGroupMembersByUserId(Long userId) { + return imGroupMemberMapper.selectListByUserId(userId); + } + +} diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java index 267385d..b2c9505 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java @@ -15,9 +15,9 @@ import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; import cn.iocoder.yudao.module.im.enums.message.ImWebSocketTypeConstants; import cn.iocoder.yudao.module.im.service.group.ImGroupService; -import cn.iocoder.yudao.module.im.service.groupmember.ImGroupMemberService; +import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService; import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; -import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; +import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -54,7 +54,7 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @Resource private GroupReadPositionRedisDAO groupReadPositionRedisDAO; @Resource - private WebSocketSenderApi webSocketSenderApi; + private WebSocketMessageSender webSocketMessageSender; @Override public ImGroupMessageDO sendMessage(Long senderId, ImGroupMessageSendReqVO reqVO) { @@ -170,7 +170,7 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { Map readEvent = new HashMap<>(); readEvent.put("groupId", groupId.toString()); readEvent.put("messageScene", "group"); - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), userId, + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), userId, ImWebSocketTypeConstants.READ, readEvent); // 5. 刷新群回执 @@ -209,7 +209,7 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { if (ImGroupMemberStatusEnum.QUIT.getStatus().equals(m.getStatus())) { continue; } - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), m.getUserId(), + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), m.getUserId(), ImWebSocketTypeConstants.RECALL, recallEvent); } } @@ -302,7 +302,7 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { if (ImGroupMemberStatusEnum.QUIT.getStatus().equals(m.getStatus())) { continue; } - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), m.getUserId(), + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), m.getUserId(), ImWebSocketTypeConstants.RECEIPT, receiptEvent); } } @@ -382,7 +382,7 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { && !member.getUserId().equals(message.getSenderId())) { continue; } - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), member.getUserId(), + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), member.getUserId(), ImWebSocketTypeConstants.GROUP_MESSAGE, event); } } diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImpl.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImpl.java index 05950af..d885d3f 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImpl.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImpl.java @@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; import cn.iocoder.yudao.module.im.enums.message.ImWebSocketTypeConstants; import cn.iocoder.yudao.module.im.service.friend.ImFriendService; import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; -import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; +import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -48,7 +48,7 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService { // TODO @AI:这个类貌似路径不对?! @Resource - private WebSocketSenderApi webSocketSenderApi; + private WebSocketMessageSender webSocketMessageSender; @Override public ImPrivateMessageDO sendMessage(Long senderId, ImPrivateMessageSendReqVO reqVO) { @@ -107,7 +107,7 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService { // TODO @AI:需要整理下,相关的 websocket DTO;可维护性变强! readEvent.put("friendId", friendId.toString()); readEvent.put("messageScene", "private"); - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), userId, + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), userId, ImWebSocketTypeConstants.READ, readEvent); // 3. 发送 RECEIPT 事件给对方(已读回执) @@ -115,7 +115,7 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService { // TODO @AI:需要整理下,相关的 websocket DTO;可维护性变强! receiptEvent.put("userId", userId.toString()); receiptEvent.put("messageScene", "private"); - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), friendId, + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), friendId, ImWebSocketTypeConstants.RECEIPT, receiptEvent); } @@ -147,11 +147,11 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService { recallEvent.put("messageId", messageId.toString()); recallEvent.put("messageScene", "private"); recallEvent.put("senderId", userId.toString()); - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), message.getReceiverId(), + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), message.getReceiverId(), ImWebSocketTypeConstants.RECALL, recallEvent); // 3.2 也通知发送方自己的其他终端 // TODO @AI:需要整理下,相关的 websocket DTO;可维护性变强! - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), userId, + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), userId, ImWebSocketTypeConstants.RECALL, recallEvent); } @@ -171,10 +171,10 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService { event.put("sendTime", message.getSendTime().toString()); event.put("messageScene", "private"); // 推送给接收方 - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), message.getReceiverId(), + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), message.getReceiverId(), ImWebSocketTypeConstants.PRIVATE_MESSAGE, event); // 推送给发送方自己的其他终端(多端同步) - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), message.getSenderId(), + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), message.getSenderId(), ImWebSocketTypeConstants.PRIVATE_MESSAGE, event); } diff --git a/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java b/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java index cfbf5d8..837a5f7 100644 --- a/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java +++ b/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.im.service.message; import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImGroupMessageSendReqVO; +import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageSendReqVO; import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO; @@ -11,9 +11,9 @@ import cn.iocoder.yudao.module.im.enums.group.ImGroupMemberStatusEnum; import cn.iocoder.yudao.module.im.enums.message.ImGroupMessageReceiptStatusEnum; import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; import cn.iocoder.yudao.module.im.service.group.ImGroupService; -import cn.iocoder.yudao.module.im.service.groupmember.ImGroupMemberService; +import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService; import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; -import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; +import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -51,7 +51,7 @@ public class ImGroupMessageServiceImplTest { @Mock private GroupReadPositionRedisDAO groupReadPositionRedisDAO; @Mock - private WebSocketSenderApi webSocketSenderApi; + private WebSocketMessageSender webSocketMessageSender; private ImGroupMessageSendReqVO buildSendReqVO() { ImGroupMessageSendReqVO reqVO = new ImGroupMessageSendReqVO(); @@ -89,7 +89,11 @@ public class ImGroupMessageServiceImplTest { .status(ImGroupMemberStatusEnum.NORMAL.getStatus()).build() ); when(imGroupMemberService.selectByGroupId(10L)).thenReturn(allMembers); - when(imGroupMessageMapper.insert(any())).thenReturn(1); + when(imGroupMessageMapper.insert(any(ImGroupMessageDO.class))).thenAnswer(invocation -> { + ImGroupMessageDO msg = invocation.getArgument(0); + msg.setId(99L); + return 1; + }); // 调用 ImGroupMessageDO result = groupMessageService.sendMessage(1L, reqVO); @@ -102,7 +106,7 @@ public class ImGroupMessageServiceImplTest { assertEquals(ImGroupMessageReceiptStatusEnum.NO_RECEIPT.getStatus(), result.getReceiptStatus()); // 验证推送给 3 个群成员 - verify(webSocketSenderApi, times(3)).sendObject(anyInt(), anyLong(), anyString(), any()); + verify(webSocketMessageSender, times(3)).sendObject(anyInt(), anyLong(), anyString(), any()); } @Test @@ -121,7 +125,7 @@ public class ImGroupMessageServiceImplTest { // 断言 assertEquals(100L, result.getId()); - verify(imGroupMessageMapper, never()).insert(any()); + verify(imGroupMessageMapper, never()).insert(any(ImGroupMessageDO.class)); } @Test @@ -262,7 +266,7 @@ public class ImGroupMessageServiceImplTest { .id(50L).senderId(1L).groupId(10L) .status(ImMessageStatusEnum.NORMAL).build(); when(imGroupMessageMapper.selectById(50L)).thenReturn(message); - when(imGroupMessageMapper.updateById(any())).thenReturn(1); + when(imGroupMessageMapper.updateById(any(ImGroupMessageDO.class))).thenReturn(1); List members = List.of( ImGroupMemberDO.builder().groupId(10L).userId(1L) @@ -278,7 +282,7 @@ public class ImGroupMessageServiceImplTest { groupMessageService.recallMessage(1L, 50L); // 断言:只给 2 个活跃成员发送 RECALL 事件(userId=3 已退群) - verify(webSocketSenderApi, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); } // ========== 群已读测试 ========== diff --git a/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImplTest.java b/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImplTest.java index ddb59a6..6abb416 100644 --- a/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImplTest.java +++ b/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImplTest.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.im.dal.mysql.message.ImPrivateMessageMapper; import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; import cn.iocoder.yudao.module.im.service.friend.ImFriendService; import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; -import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; +import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -40,7 +40,7 @@ public class ImPrivateMessageServiceImplTest { @Mock private ImSensitiveWordService imSensitiveWordService; @Mock - private WebSocketSenderApi webSocketSenderApi; + private WebSocketMessageSender webSocketMessageSender; private ImPrivateMessageSendReqVO buildSendReqVO() { ImPrivateMessageSendReqVO reqVO = new ImPrivateMessageSendReqVO(); @@ -59,7 +59,11 @@ public class ImPrivateMessageServiceImplTest { ImPrivateMessageSendReqVO reqVO = buildSendReqVO(); when(imPrivateMessageMapper.selectBySenderIdAndClientMessageId(1L, "test-uuid-001")) .thenReturn(null); - when(imPrivateMessageMapper.insert(any())).thenReturn(1); + when(imPrivateMessageMapper.insert(any(ImPrivateMessageDO.class))).thenAnswer(invocation -> { + ImPrivateMessageDO msg = invocation.getArgument(0); + msg.setId(99L); + return 1; + }); // 调用 ImPrivateMessageDO result = privateMessageService.sendMessage(1L, reqVO); @@ -75,8 +79,8 @@ public class ImPrivateMessageServiceImplTest { // 验证调用 verify(imFriendService).validateIsFriend(1L, 2L); verify(imSensitiveWordService).validateText(reqVO.getContent()); - verify(imPrivateMessageMapper).insert(any()); - verify(webSocketSenderApi, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); + verify(imPrivateMessageMapper).insert(any(ImPrivateMessageDO.class)); + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); } @Test @@ -96,7 +100,7 @@ public class ImPrivateMessageServiceImplTest { // 断言:返回已存在的消息 assertEquals(100L, result.getId()); // 验证不会重复插入 - verify(imPrivateMessageMapper, never()).insert(any()); + verify(imPrivateMessageMapper, never()).insert(any(ImPrivateMessageDO.class)); } @Test @@ -151,7 +155,7 @@ public class ImPrivateMessageServiceImplTest { privateMessageService.readMessages(1L, 2L); // 断言:发送了 READ 和 RECEIPT 事件 - verify(webSocketSenderApi, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); } // ========== 撤回测试 ========== @@ -163,15 +167,15 @@ public class ImPrivateMessageServiceImplTest { .id(10L).senderId(1L).receiverId(2L) .status(ImMessageStatusEnum.UNREAD.getStatus()).build(); when(imPrivateMessageMapper.selectById(10L)).thenReturn(message); - when(imPrivateMessageMapper.updateById(any())).thenReturn(1); + when(imPrivateMessageMapper.updateById(any(ImPrivateMessageDO.class))).thenReturn(1); // 调用 privateMessageService.recallMessage(1L, 10L); // 断言 - verify(imPrivateMessageMapper).updateById(any()); + verify(imPrivateMessageMapper).updateById(any(ImPrivateMessageDO.class)); // 验证推送了 RECALL 事件(给接收方和发送方) - verify(webSocketSenderApi, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), anyString(), any()); } @Test diff --git a/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImWebSocketEventTest.java b/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImWebSocketEventTest.java index 8732178..23ffa94 100644 --- a/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImWebSocketEventTest.java +++ b/yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImWebSocketEventTest.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.im.dal.mysql.message.ImPrivateMessageMapper; import cn.iocoder.yudao.module.im.enums.message.ImWebSocketTypeConstants; import cn.iocoder.yudao.module.im.service.friend.ImFriendService; import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; -import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; +import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -40,7 +40,7 @@ public class ImWebSocketEventTest { @Mock private ImSensitiveWordService imSensitiveWordService; @Mock - private WebSocketSenderApi webSocketSenderApi; + private WebSocketMessageSender webSocketMessageSender; @Test public void testSendMessage_websocketEvent() { @@ -52,7 +52,12 @@ public class ImWebSocketEventTest { reqVO.setContent("{\"content\":\"事件测试\"}"); when(imPrivateMessageMapper.selectBySenderIdAndClientMessageId(1L, "evt-uuid")).thenReturn(null); - when(imPrivateMessageMapper.insert(any())).thenReturn(1); + // mock insert:通过 doAnswer 模拟 MyBatis-Plus 设置 ID + when(imPrivateMessageMapper.insert(any(ImPrivateMessageDO.class))).thenAnswer(invocation -> { + ImPrivateMessageDO msg = invocation.getArgument(0); + msg.setId(99L); + return 1; + }); // 调用 privateMessageService.sendMessage(1L, reqVO); @@ -60,7 +65,7 @@ public class ImWebSocketEventTest { // 捕获 WebSocket 调用 ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor contentCaptor = ArgumentCaptor.forClass(Object.class); - verify(webSocketSenderApi, times(2)).sendObject(anyInt(), anyLong(), + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), typeCaptor.capture(), contentCaptor.capture()); // 验证消息事件类型 @@ -87,7 +92,7 @@ public class ImWebSocketEventTest { // 捕获 ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor userCaptor = ArgumentCaptor.forClass(Long.class); - verify(webSocketSenderApi, times(2)).sendObject(anyInt(), userCaptor.capture(), + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), userCaptor.capture(), typeCaptor.capture(), any()); // 第一次:发给自己的 READ 事件 @@ -104,7 +109,7 @@ public class ImWebSocketEventTest { ImPrivateMessageDO message = ImPrivateMessageDO.builder() .id(10L).senderId(1L).receiverId(2L).status(0).build(); when(imPrivateMessageMapper.selectById(10L)).thenReturn(message); - when(imPrivateMessageMapper.updateById(any())).thenReturn(1); + when(imPrivateMessageMapper.updateById(any(ImPrivateMessageDO.class))).thenReturn(1); // 调用 privateMessageService.recallMessage(1L, 10L); @@ -112,7 +117,7 @@ public class ImWebSocketEventTest { // 捕获 ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor contentCaptor = ArgumentCaptor.forClass(Object.class); - verify(webSocketSenderApi, times(2)).sendObject(anyInt(), anyLong(), + verify(webSocketMessageSender, times(2)).sendObject(anyInt(), anyLong(), typeCaptor.capture(), contentCaptor.capture()); // 验证 RECALL 事件类型 -- libgit2 0.21.4