Commit 4015b2f213daf1713d711a640dd8a5908ef93ae6

Authored by 安浩浩
1 parent 0e79d8ec

新增:消息发送和代码优化

Showing 69 changed files with 958 additions and 1798 deletions
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/api/package-info.java
1 1 /**
2 2 * @author anhaohao
3   - * @date 2024/3/9 下午8:59 TODO @hao:没有 @date 这个,应该是 @since 哈
  3 + * @since 2024/3/9 下午8:59
4 4 */
5 5 package cn.iocoder.yudao.module.im.api;
6 6 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java
... ... @@ -17,6 +17,7 @@ public interface ErrorCodeConstants {
17 17  
18 18 // ========== 消息 (1-040-300-000) ==========
19 19 ErrorCode MESSAGE_NOT_EXISTS = new ErrorCode(1_040_300_000, "消息不存在");
  20 + ErrorCode MESSAGE_RECEIVER_NOT_EXISTS = new ErrorCode(1_040_300_001, "接收人不存在");
20 21  
21 22 // ========== 群 (1-040-400-000) ==========
22 23 ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1_040_400_000, "群不存在");
... ...
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/conversation/ImConversationTypeEnum.java renamed to yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/conversation/ConversationTypeEnum.java
... ... @@ -4,26 +4,43 @@ import lombok.AllArgsConstructor;
4 4 import lombok.Getter;
5 5  
6 6 /**
7   - * IM 会话的类型枚举
  7 + * IM 会话类型枚举
  8 + * 参考 <a href="https://doc.rentsoft.cn/zh-Hans/sdks/enum/conversationType">“会话类型”</a> 文档
8 9 *
9   - * @author 芋道源码
  10 + * @author anhaohao
10 11 */
11 12 @Getter
12 13 @AllArgsConstructor
13   -public enum ImConversationTypeEnum {
  14 +public enum ConversationTypeEnum {
14 15  
15   - // TODO @hao:单聊,我们使用 SINGLE,主要 private 这个单词在 java 里太特殊了;
16   - PRIVATE(1, "单聊"),
17   - GROUP(2, "群聊"),
18   - NOTICE(4, "通知会话");
  16 + SINGLE(1, "单聊"),
  17 + GROUP(3, "群聊"),
  18 + NOTIFICATION(4, "通知会话");
19 19  
20 20 /**
21 21 * 类型
22 22 */
23 23 private final Integer type;
  24 +
24 25 /**
25 26 * 名字
26 27 */
27 28 private final String name;
28 29  
  30 + /**
  31 + * 生成会话编号
  32 + * @param fromUserId 发送者编号
  33 + * @param receiverId 接收者编号
  34 + * @param conversationType 会话类型
  35 + * @return 会话编号
  36 + */
  37 + public static String generateConversationNo(Long fromUserId, Long receiverId, Integer conversationType) {
  38 + if (conversationType.equals(ConversationTypeEnum.SINGLE.getType())) {
  39 + return "s_" + fromUserId + "_" + receiverId;
  40 + } else if (conversationType.equals(ConversationTypeEnum.GROUP.getType())) {
  41 + return "g_" + receiverId;
  42 + }
  43 + return null;
  44 + }
  45 +
29 46 }
30 47 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java deleted
1   -package cn.iocoder.yudao.module.im.enums.message;
2   -
3   -import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Getter;
6   -
7   -import java.util.Arrays;
8   -
9   -// TODO @hao:类型保持和 openim 一致
10   -/**
11   - * IM 消息的类型枚举
12   - * <p>
13   - * 参考 <a href="https://doc.yunxin.163.com/messaging/docs/zg3NzA3NTA?platform=web#消息类型">“消息类型”</a> 文档
14   - *
15   - * @author 芋道源码
16   - */
17   -@Getter
18   -@AllArgsConstructor
19   -public enum ImMessageTypeEnum implements IntArrayValuable {
20   -
21   - TEXT(1, "文本"), // 消息内容为普通文本
22   - IMAGE(2, "图片"), // 消息内容为图片 URL 地址、尺寸、图片大小等信息
23   - AUDIO(3, "语音"), // 消息内容为语音文件的 URL 地址、时长、大小、格式等信息
24   - VIDEO(4, "视频"), // 消息内容为视频文件的 URL 地址、时长、大小、格式等信息
25   - FILE(5, "文件"), // 消息内容为文件的 URL 地址、大小、格式等信息
26   - LOCATION(6, "地理位置"), // 消息内容为地理位置标题、经度、纬度信息
27   - // TODO @芋艿:下面两种,貌似企业微信设计的更好:https://developer.work.weixin.qq.com/document/path/90240
28   - TIP(7, "提示"), // 又叫做 Tip 消息,没有推送和通知栏提醒,主要用于会话内的通知提醒,例如进入会话时出现的欢迎消息,或是会话过程中命中敏感词后的提示消息等场景
29   - NOTIFICATION(8, "通知"), // 主要用于群组、聊天室和超大群的事件通知,由服务端下发,客户端无法发送事件通知消息。通知类消息有在线、离线、漫游机制;没有通知栏提醒
30   - ;
31   -
32   - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ImMessageTypeEnum::getType).toArray();
33   - /**
34   - * 类型
35   - */
36   - private final Integer type;
37   - /**
38   - * 名字
39   - */
40   - private final String name;
41   -
42   - @Override
43   - public int[] array() {
44   - return ARRAYS;
45   - }
46   -
47   -}
48 0 \ No newline at end of file
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/MessageContentTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.enums.message;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +
  10 +/**
  11 + * IM 消息的类型枚举
  12 + * <p>
  13 + * 参考 <a href="https://doc.rentsoft.cn/zh-Hans/sdks/enum/messageContentType">“消息类型”</a> 文档
  14 + *
  15 + * @author anhaohao
  16 + */
  17 +@Getter
  18 +@AllArgsConstructor
  19 +public enum MessageContentTypeEnum implements IntArrayValuable {
  20 +
  21 + TEXT(101, "文本消息"),
  22 + PICTURE(102, "图片消息"),
  23 + VOICE(103, "语音消息"),
  24 + VIDEO(104, "视频消息"),
  25 + FILE(105, "文件消息"),
  26 + AT_TEXT(106, "@消息"),
  27 + MERGE(107, "合并消息"),
  28 + CARD(108, "名片消息"),
  29 + LOCATION(109, "位置消息"),
  30 + CUSTOM(110, "自定义消息"),
  31 + REVOKE_RECEIPT(111, "撤回消息回执"),
  32 + C2C_RECEIPT(112, "单聊消息回执"),
  33 + TYPING(113, "输入状态"),
  34 + QUOTE(114, "引用消息"),
  35 + FACE(115, "表情消息"),
  36 + ADVANCED_REVOKE(118, "高级撤回消息"),
  37 + //好友通知 1200-1299
  38 + FRIEND_ADDED(1201, "双方成为好友通知"),
  39 + //系统通知 1400
  40 + OA_NOTIFICATION(1400, "系统通知"),
  41 + //群相关 1500-1599
  42 + GROUP_CREATED(1501, "群创建通知"),
  43 + GROUP_INFO_CHANGED(1502, "群信息改变通知"),
  44 + MEMBER_QUIT(1504, "群成员退出通知"),
  45 + GROUP_OWNER_CHANGED(1507, "群主更换通知"),
  46 + MEMBER_KICKED(1508, "群成员被踢通知"),
  47 + MEMBER_INVITED(1509, "邀请群成员通知"),
  48 + MEMBER_ENTER(1510, "群成员进群通知"),
  49 + GROUP_DISMISSED(1511, "解散群通知"),
  50 + GROUP_MEMBER_MUTED(1512, "群成员禁言通知"),
  51 + GROUP_MEMBER_CANCEL_MUTED(1513, "取消群成员禁言通知"),
  52 + GROUP_MUTED(1514, "群禁言通知"),
  53 + GROUP_CANCEL_MUTED(1515, "取消群禁言通知"),
  54 + GROUP_ANNOUNCEMENT_UPDATED(1519, "群公告改变通知"),
  55 + GROUP_NAME_UPDATED(1520, "群名称改变通知"),
  56 +
  57 + BURN_CHANGE(1701, "阅后即焚开启或关闭通知"),
  58 + REVOKE(2101, "撤回消息通知");;
  59 +
  60 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MessageContentTypeEnum::getType).toArray();
  61 +
  62 + /**
  63 + * 类型
  64 + */
  65 + private final Integer type;
  66 +
  67 + /**
  68 + * 名字
  69 + */
  70 + private final String name;
  71 +
  72 + @Override
  73 + public int[] array() {
  74 + return ARRAYS;
  75 + }
  76 +
  77 +}
0 78 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/MessageSourceEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.enums.message;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.Getter;
  5 +import lombok.RequiredArgsConstructor;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * IM 消息的消息来源 100-用户发送;200-系统发送(一般是通知);不能为空
  11 + */
  12 +@RequiredArgsConstructor
  13 +@Getter
  14 +public enum MessageSourceEnum implements IntArrayValuable {
  15 +
  16 + USER_SEND(100, "用户发送"),
  17 + SYSTEM_SEND(200, "系统发送");
  18 +
  19 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MessageSourceEnum::getStatus).toArray();
  20 +
  21 + /**
  22 + * 状态
  23 + */
  24 + private final Integer status;
  25 +
  26 + /**
  27 + * 名字
  28 + */
  29 + private final String name;
  30 +
  31 + @Override
  32 + public int[] array() {
  33 + return ARRAYS;
  34 + }
  35 +
  36 +}
0 37 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageStatusEnum.java renamed to yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/MessageStatusEnum.java
... ... @@ -6,21 +6,27 @@ import lombok.RequiredArgsConstructor;
6 6  
7 7 import java.util.Arrays;
8 8  
9   -// TODO @hao:注释哈;
  9 +/**
  10 + * IM 消息的状态枚举
  11 + */
10 12 @RequiredArgsConstructor
11 13 @Getter
12   -public enum ImMessageStatusEnum implements IntArrayValuable {
  14 +public enum MessageStatusEnum implements IntArrayValuable {
13 15  
14 16 SENDING(1, "发送中"),
15   - SUCCESS(2, "发送成功"),
16   - FAILURE(3, "发送失败"),
17   - DELETED(4, "已删除"),
18   - RECALL(5, "已撤回");
19   -
20   - // TODO @hao:静态变量,和普通变量,最好空一行;
21   - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ImMessageStatusEnum::getStatus).toArray();
22   - // TODO @hao:注释哈;
  17 + SUCCEED(2, "发送成功"),
  18 + FAILED(3, "发送失败");
  19 +
  20 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MessageStatusEnum::getStatus).toArray();
  21 +
  22 + /**
  23 + * 状态
  24 + */
23 25 private final Integer status;
  26 +
  27 + /**
  28 + * 名字
  29 + */
24 30 private final String name;
25 31  
26 32 @Override
... ...
yudao-module-im/yudao-module-im-biz/pom.xml
... ... @@ -5,19 +5,19 @@
5 5 <parent>
6 6 <artifactId>yudao-module-im</artifactId>
7 7 <groupId>cn.iocoder.boot</groupId>
8   - <version>${revision}</version> <!-- 1. 修改 version 为 ${revision} TODO @hao:这种都去掉哈; -->
  8 + <version>${revision}</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11   - <packaging>jar</packaging> <!-- 2. 新增 packaging 为 jar -->
  11 + <packaging>jar</packaging>
12 12  
13 13 <artifactId>yudao-module-im-biz</artifactId>
14 14  
15   - <name>${project.artifactId}</name> <!-- 3. 新增 name 为 ${project.artifactId} -->
16   - <description> <!-- 4. 新增 description 为该模块的描述 -->
  15 + <name>${project.artifactId}</name>
  16 + <description>
17 17 im 模块,主要实现 im 模块的业务逻辑。
18 18 </description>
19 19  
20   - <dependencies> <!-- 5. 新增依赖,这里引入的都是比较常用的业务组件、技术组件 -->
  20 + <dependencies>
21 21 <dependency>
22 22 <groupId>cn.iocoder.boot</groupId>
23 23 <artifactId>yudao-module-im-api</artifactId>
... ... @@ -41,34 +41,34 @@
41 41 <artifactId>yudao-spring-boot-starter-security</artifactId>
42 42 </dependency>
43 43  
  44 + <dependency>
  45 + <groupId>cn.iocoder.boot</groupId>
  46 + <artifactId>yudao-spring-boot-starter-websocket</artifactId>
  47 + </dependency>
  48 +
44 49 <!-- DB 相关 -->
45 50 <dependency>
46 51 <groupId>cn.iocoder.boot</groupId>
47 52 <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
48 53 </dependency>
49 54  
50   - <!-- Test 测试相关 -->
51 55 <dependency>
52 56 <groupId>cn.iocoder.boot</groupId>
53   - <artifactId>yudao-spring-boot-starter-test</artifactId>
  57 + <artifactId>yudao-spring-boot-starter-redis</artifactId>
54 58 </dependency>
55 59  
56   - <!-- TODO @hao:下面几个依赖的位置不太对。主要是它不属于 test,可以参考别的模块,顺序调整下哈 -->
57   -
  60 + <!-- 工具类相关 -->
58 61 <dependency>
59 62 <groupId>cn.iocoder.boot</groupId>
60 63 <artifactId>yudao-spring-boot-starter-excel</artifactId>
61 64 </dependency>
  65 +
  66 + <!-- Test 测试相关 -->
62 67 <dependency>
63 68 <groupId>cn.iocoder.boot</groupId>
64   - <artifactId>yudao-spring-boot-starter-websocket</artifactId>
  69 + <artifactId>yudao-spring-boot-starter-test</artifactId>
65 70 </dependency>
66 71  
67   - <!-- TODO @hao:引入我们自己的 redis starter 哈 -->
68   - <dependency>
69   - <groupId>org.redisson</groupId>
70   - <artifactId>redisson-spring-boot-starter</artifactId>
71   - </dependency>
72 72 </dependencies>
73 73  
74 74 </project>
75 75 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/ImConversationController.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/ConversationController.java
... ... @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 5 import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationRespVO;
6 6 import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationSaveReqVO;
7   -import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
8   -import cn.iocoder.yudao.module.im.service.conversation.ImConversationService;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
  8 +import cn.iocoder.yudao.module.im.service.conversation.ConversationService;
9 9 import io.swagger.v3.oas.annotations.Operation;
10 10 import io.swagger.v3.oas.annotations.tags.Tag;
11 11 import jakarta.annotation.Resource;
... ... @@ -19,22 +19,22 @@ import java.util.List;
19 19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
20 20  
21 21 // TODO @hao: 管理后台 - IM 会话
22   -@Tag(name = "管理后台 - 会话")
  22 +@Tag(name = "管理后台 - IM 会话")
23 23 @RestController
24 24 @RequestMapping("/im/conversation")
25 25 @Validated
26   -public class ImConversationController {
  26 +public class ConversationController {
27 27  
28 28 // TODO @hao: conversationService 即可
29 29 @Resource
30   - private ImConversationService imConversationService;
  30 + private ConversationService conversationService;
31 31  
32 32 // TODO @hao:/list 会话列表;一般 get 是给单个对象,或者 get-count 这种;然后 conversation 已经是模块名了,所以可以简化
33 33 @GetMapping("/get-conversation")
34 34 @Operation(summary = "获得用户的会话列表")
35 35 @PreAuthorize("hasAuthority('im:conversation:query')") // TODO @hao:不用权限哈
36 36 public CommonResult<List<ImConversationRespVO>> getConversationList() {
37   - List<ImConversationDO> conversationList = imConversationService.getConversationList();
  37 + List<ConversationDO> conversationList = conversationService.getConversationList();
38 38 return success(BeanUtils.toBean(conversationList, ImConversationRespVO.class));
39 39 }
40 40  
... ... @@ -44,7 +44,7 @@ public class ImConversationController {
44 44 @Operation(summary = "置顶会话")
45 45 @PreAuthorize("hasAuthority('im:conversation:update')") // TODO @hao:不用权限哈;因为肯定会判断是不是自己的
46 46 public CommonResult<Boolean> updateTop(@Valid @RequestBody ImConversationSaveReqVO updateReqVO) {
47   - imConversationService.updateTop(updateReqVO);
  47 + conversationService.updateTop(updateReqVO);
48 48 return success(true);
49 49 }
50 50  
... ... @@ -54,7 +54,7 @@ public class ImConversationController {
54 54 @Operation(summary = "更新最后已读时间")
55 55 @PreAuthorize("hasAuthority('im:conversation:update')") // TODO @hao:不用权限哈;因为肯定会判断是不是自己的
56 56 public CommonResult<Boolean> updateLastReadTime(@Valid @RequestBody ImConversationSaveReqVO updateReqVO) {
57   - imConversationService.updateLastReadTime(updateReqVO);
  57 + conversationService.updateLastReadTime(updateReqVO);
58 58 return success(true);
59 59 }
60 60  
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/vo/ImConversationPageReqVO.java
... ... @@ -22,7 +22,7 @@ public class ImConversationPageReqVO extends PageParam {
22 22 private Long userId;
23 23  
24 24 @Schema(description = "类型:1 单聊;2 群聊;4 通知会话(预留)", example = "1")
25   - private Integer conversationType;
  25 + private Integer type;
26 26  
27 27 @Schema(description = "单聊时,用户编号;群聊时,群编号", example = "21454")
28 28 private Long targetId;
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/vo/ImConversationSaveReqVO.java
... ... @@ -21,7 +21,7 @@ public class ImConversationSaveReqVO {
21 21  
22 22 @Schema(description = "类型:1 单聊;2 群聊;4 通知会话(预留)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
23 23 @NotNull(message = "类型:1 单聊;2 群聊;4 通知会话(预留)不能为空")
24   - private Integer conversationType;
  24 + private Integer type;
25 25  
26 26 @Schema(description = "单聊时,用户编号;群聊时,群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21454")
27 27 @NotEmpty(message = "单聊时,用户编号;群聊时,群编号不能为空")
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupController.java
... ... @@ -25,7 +25,7 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
25 25 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
26 26  
27 27 import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
28   -import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
  28 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
29 29 import cn.iocoder.yudao.module.im.service.group.ImGroupService;
30 30  
31 31 @Tag(name = "管理后台 - 群")
... ... @@ -66,7 +66,7 @@ public class ImGroupController {
66 66 @Parameter(name = "id", description = "编号", required = true, example = "1024")
67 67 @PreAuthorize("@ss.hasPermission('im:group:query')")
68 68 public CommonResult<ImGroupRespVO> getGroup(@RequestParam("id") Long id) {
69   - ImGroupDO group = imGroupService.getGroup(id);
  69 + GroupDO group = imGroupService.getGroup(id);
70 70 return success(BeanUtils.toBean(group, ImGroupRespVO.class));
71 71 }
72 72  
... ... @@ -74,7 +74,7 @@ public class ImGroupController {
74 74 @Operation(summary = "获得群分页")
75 75 @PreAuthorize("@ss.hasPermission('im:group:query')")
76 76 public CommonResult<PageResult<ImGroupRespVO>> getGroupPage(@Valid ImGroupPageReqVO pageReqVO) {
77   - PageResult<ImGroupDO> pageResult = imGroupService.getGroupPage(pageReqVO);
  77 + PageResult<GroupDO> pageResult = imGroupService.getGroupPage(pageReqVO);
78 78 return success(BeanUtils.toBean(pageResult, ImGroupRespVO.class));
79 79 }
80 80  
... ... @@ -85,7 +85,7 @@ public class ImGroupController {
85 85 public void exportGroupExcel(@Valid ImGroupPageReqVO pageReqVO,
86 86 HttpServletResponse response) throws IOException {
87 87 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
88   - List<ImGroupDO> list = imGroupService.getGroupPage(pageReqVO).getList();
  88 + List<GroupDO> list = imGroupService.getGroupPage(pageReqVO).getList();
89 89 // 导出 Excel
90 90 ExcelUtils.write(response, "群.xls", "数据", ImGroupRespVO.class,
91 91 BeanUtils.toBean(list, ImGroupRespVO.class));
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/groupmember/ImGroupMemberController.java
... ... @@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
9 9 import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberPageReqVO;
10 10 import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberRespVO;
11 11 import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberSaveReqVO;
12   -import cn.iocoder.yudao.module.im.dal.dataobject.groupmember.GroupMemberDO;
  12 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
13 13 import cn.iocoder.yudao.module.im.service.groupmember.ImGroupMemberService;
14 14 import io.swagger.v3.oas.annotations.Operation;
15 15 import io.swagger.v3.oas.annotations.Parameter;
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/ImInboxController.java
... ... @@ -8,8 +8,8 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
8 8 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
9 9 import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxPageReqVO;
10 10 import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxRespVO;
11   -import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxSaveReqVO;
12   -import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
  11 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveReqVO;
  12 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
13 13 import cn.iocoder.yudao.module.im.service.inbox.ImInboxService;
14 14 import io.swagger.v3.oas.annotations.Operation;
15 15 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -39,14 +39,14 @@ public class ImInboxController {
39 39 @PostMapping("/create")
40 40 @Operation(summary = "创建收件箱")
41 41 @PreAuthorize("@ss.hasPermission('im:inbox:create')")
42   - public CommonResult<Long> createInbox(@Valid @RequestBody ImInboxSaveReqVO createReqVO) {
  42 + public CommonResult<Long> createInbox(@Valid @RequestBody InboxSaveReqVO createReqVO) {
43 43 return success(imInboxService.createInbox(createReqVO));
44 44 }
45 45  
46 46 @PutMapping("/update")
47 47 @Operation(summary = "更新收件箱")
48 48 @PreAuthorize("@ss.hasPermission('im:inbox:update')")
49   - public CommonResult<Boolean> updateInbox(@Valid @RequestBody ImInboxSaveReqVO updateReqVO) {
  49 + public CommonResult<Boolean> updateInbox(@Valid @RequestBody InboxSaveReqVO updateReqVO) {
50 50 imInboxService.updateInbox(updateReqVO);
51 51 return success(true);
52 52 }
... ... @@ -65,7 +65,7 @@ public class ImInboxController {
65 65 @Parameter(name = "id", description = "编号", required = true, example = "1024")
66 66 @PreAuthorize("@ss.hasPermission('im:inbox:query')")
67 67 public CommonResult<ImInboxRespVO> getInbox(@RequestParam("id") Long id) {
68   - ImInboxDO inbox = imInboxService.getInbox(id);
  68 + InboxDO inbox = imInboxService.getInbox(id);
69 69 return success(BeanUtils.toBean(inbox, ImInboxRespVO.class));
70 70 }
71 71  
... ... @@ -73,7 +73,7 @@ public class ImInboxController {
73 73 @Operation(summary = "获得收件箱分页")
74 74 @PreAuthorize("@ss.hasPermission('im:inbox:query')")
75 75 public CommonResult<PageResult<ImInboxRespVO>> getInboxPage(@Valid ImInboxPageReqVO pageReqVO) {
76   - PageResult<ImInboxDO> pageResult = imInboxService.getInboxPage(pageReqVO);
  76 + PageResult<InboxDO> pageResult = imInboxService.getInboxPage(pageReqVO);
77 77 return success(BeanUtils.toBean(pageResult, ImInboxRespVO.class));
78 78 }
79 79  
... ... @@ -84,7 +84,7 @@ public class ImInboxController {
84 84 public void exportInboxExcel(@Valid ImInboxPageReqVO pageReqVO,
85 85 HttpServletResponse response) throws IOException {
86 86 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
87   - List<ImInboxDO> list = imInboxService.getInboxPage(pageReqVO).getList();
  87 + List<InboxDO> list = imInboxService.getInboxPage(pageReqVO).getList();
88 88 // 导出 Excel
89 89 ExcelUtils.write(response, "收件箱.xls", "数据", ImInboxRespVO.class,
90 90 BeanUtils.toBean(list, ImInboxRespVO.class));
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/InboxSaveMessageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - 收件箱保存消息 Request VO")
  9 +@Data
  10 +public class InboxSaveMessageReqVO {
  11 +
  12 + @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  13 + private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
  14 +
  15 + @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long fromId; // 根据 conversationType 区分
  17 +
  18 + @Schema(description = "发送人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  19 + private String senderNickname;
  20 +
  21 + @Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private String senderAvatar;
  23 +
  24 + @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
  25 + private Long receiverId;
  26 +
  27 + @Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
  28 + private Long messageId;
  29 +
  30 + @Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  31 + private Integer contentType; // 参见 ImMessageTypeEnum 枚举
  32 +
  33 + @Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
  34 + private String content;
  35 +
  36 + @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
  37 + private LocalDateTime sendTime;
  38 +
  39 +// @Schema(description = "序号", requiredMode = Schema.RequiredMode.REQUIRED)
  40 +// private Long sequence;
  41 +
  42 +}
0 43 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/ImInboxSaveReqVO.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/InboxSaveReqVO.java
... ... @@ -11,7 +11,7 @@ import lombok.NoArgsConstructor;
11 11 @Data
12 12 @AllArgsConstructor
13 13 @NoArgsConstructor
14   -public class ImInboxSaveReqVO {
  14 +public class InboxSaveReqVO {
15 15  
16 16 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18389")
17 17 private Long id;
... ... @@ -28,7 +28,7 @@ public class ImInboxSaveReqVO {
28 28 @NotNull(message = "序号,按照 user 递增不能为空")
29 29 private Long sequence;
30 30  
31   - public ImInboxSaveReqVO(Long userId, Long messageId, Long sequence) {
  31 + public InboxSaveReqVO(Long userId, Long messageId, Long sequence) {
32 32 this.userId = userId;
33 33 this.messageId = messageId;
34 34 this.sequence = sequence;
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/message/ImReceiveMessage.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/vo/InboxSendMessageReqVO.java
1   -package cn.iocoder.yudao.module.im.websocket.message;
  1 +package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
6 6 import java.time.LocalDateTime;
7 7  
8   -@Schema(description = "管理后台 - 消息发送 receive")
  8 +@Schema(description = "管理后台 - 收件箱保存消息 Request VO")
9 9 @Data
10   -public class ImReceiveMessage {
  10 +public class InboxSendMessageReqVO {
11 11  
12 12 @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
13 13 private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
... ... @@ -15,7 +15,14 @@ public class ImReceiveMessage {
15 15 @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
16 16 private Long fromId; // 根据 conversationType 区分
17 17  
18   - // TODO @hao:昵称和头像,也直接发给接收人好了;
  18 + @Schema(description = "发送人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  19 + private String senderNickname;
  20 +
  21 + @Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private String senderAvatar;
  23 +
  24 + @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
  25 + private Long receiverId;
19 26  
20 27 @Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
21 28 private Long messageId;
... ... @@ -29,10 +36,6 @@ public class ImReceiveMessage {
29 36 @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
30 37 private LocalDateTime sendTime;
31 38  
32   - // TODO @hao:inboxId 不需要哈;
33   - @Schema(description = "收件箱编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18389")
34   - private Long inboxId;
35   -
36 39 @Schema(description = "序号", requiredMode = Schema.RequiredMode.REQUIRED)
37 40 private Long sequence;
38 41  
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImMessageController.java deleted
1   -package cn.iocoder.yudao.module.im.controller.admin.message;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
5   -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessagePageReqVO;
7   -import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageRespVO;
8   -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
9   -import cn.iocoder.yudao.module.im.service.message.ImMessageService;
10   -import io.swagger.v3.oas.annotations.Operation;
11   -import io.swagger.v3.oas.annotations.Parameter;
12   -import io.swagger.v3.oas.annotations.tags.Tag;
13   -import jakarta.annotation.Resource;
14   -import jakarta.validation.Valid;
15   -import org.springframework.security.access.prepost.PreAuthorize;
16   -import org.springframework.validation.annotation.Validated;
17   -import org.springframework.web.bind.annotation.GetMapping;
18   -import org.springframework.web.bind.annotation.RequestMapping;
19   -import org.springframework.web.bind.annotation.RequestParam;
20   -import org.springframework.web.bind.annotation.RestController;
21   -
22   -import java.util.List;
23   -
24   -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
25   -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
26   -
27   -@Tag(name = "管理后台 - IM 消息")
28   -@RestController
29   -@RequestMapping("/im/message")
30   -@Validated
31   -public class ImMessageController {
32   -
33   - @Resource
34   - private ImMessageService imMessageService;
35   -
36   - @GetMapping("/get-message-by-sequence") // TODO @hao:list-by-sequence
37   - @Operation(summary = "拉取消息-增量拉取大于 seq 的消息") // TODO @hao:可以改成 拉取大于 sequence 的消息列表
38   - @Parameter(name = "sequence", description = "序号", required = true, example = "1")
39   - @Parameter(name = "size", description = "条数", required = true, example = "10")
40   - @PreAuthorize("@ss.hasPermission('im:message:query')") // TODO @hao:权限可以删除哈;
41   - public CommonResult<List<ImMessageRespVO>> loadMessage(@RequestParam("sequence") Long sequence,
42   - @RequestParam("size") Integer size) {
43   - // TODO @hao:方法名,可以改成 getMessageListBySequence
44   - // TODO @hao:如果是返回列表,变量要用 messages, 或者 messageList,体现出是复数
45   - List<ImMessageDO> message = imMessageService.loadMessage(getLoginUserId(), sequence, size);
46   - return success(BeanUtils.toBean(message, ImMessageRespVO.class));
47   - }
48   -
49   - // TODO @hao:这个接口的使用场景是哪个哈?
50   - @GetMapping("/get-all-message")
51   - @Operation(summary = "拉取全部消息")
52   - @Parameter(name = "size", description = "条数", required = true, example = "10")
53   - @PreAuthorize("@ss.hasPermission('im:message:query')")
54   - public CommonResult<List<ImMessageRespVO>> loadAllMessage(@RequestParam("size") Integer size) {
55   - List<ImMessageDO> message = imMessageService.loadAllMessage(getLoginUserId(), size);
56   - return success(BeanUtils.toBean(message, ImMessageRespVO.class));
57   - }
58   -
59   - // TODO @hao:是不是分页参数不太对哈?应该只有 conversationNo,sendTime 字段;然后,做链式分页的查询,不太适合传统的 pageSize + number 查询;
60   - @GetMapping("/page")
61   - @Operation(summary = "查询聊天记录-分页")
62   - @PreAuthorize("@ss.hasPermission('im:message:query')") // TODO @hao:权限可以删除哈;
63   - public CommonResult<PageResult<ImMessageRespVO>> getMessagePage(@Valid ImMessagePageReqVO pageReqVO) {
64   - PageResult<ImMessageDO> messagePage = imMessageService.getMessagePage(pageReqVO);
65   - return success(BeanUtils.toBean(messagePage, ImMessageRespVO.class));
66   - }
67   -
68   -}
69 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/MessageController.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.message;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessagePageReqVO;
  6 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageReqVO;
  7 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageReqVO;
  8 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageRespVO;
  9 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  10 +import cn.iocoder.yudao.module.im.service.message.MessageService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.validation.annotation.Validated;
  17 +import org.springframework.web.bind.annotation.*;
  18 +
  19 +import java.util.List;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  23 +
  24 +@Tag(name = "管理后台 - IM 消息")
  25 +@RestController
  26 +@RequestMapping("/im/message")
  27 +@Validated
  28 +public class MessageController {
  29 +
  30 + @Resource
  31 + private MessageService messageService;
  32 +
  33 + @PostMapping("/send")
  34 + @Operation(summary = "发送消息")
  35 + public CommonResult<SendMessageRespVO> sendMessage(@Valid @RequestBody SendMessageReqVO message) {
  36 + return success(messageService.sendMessage(getLoginUserId(), message));
  37 + }
  38 +
  39 + @GetMapping("/list-by-sequence")
  40 + @Operation(summary = "拉取大于 sequence 的消息列表")
  41 + @Parameter(name = "sequence", description = "序号", required = true, example = "1")
  42 + @Parameter(name = "size", description = "条数", required = true, example = "10")
  43 + public CommonResult<List<ImMessageReqVO>> loadMessage(@RequestParam("sequence") Long sequence,
  44 + @RequestParam("size") Integer size) {
  45 + List<MessageDO> messages = messageService.getMessageListBySequence(getLoginUserId(), sequence, size);
  46 + return success(BeanUtils.toBean(messages, ImMessageReqVO.class));
  47 + }
  48 +
  49 + @GetMapping("/page")
  50 + @Operation(summary = "查询聊天记录-分页")
  51 + public CommonResult<List<ImMessageReqVO>> getMessagePage(@Valid MessagePageReqVO pageReqVO) {
  52 + //根据会话标志和发送时间进行分页查询
  53 + List<MessageDO> messagePage = messageService.getMessagePage(pageReqVO);
  54 + return success(BeanUtils.toBean(messagePage, ImMessageReqVO.class));
  55 + }
  56 +
  57 +}
0 58 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/ImMessagePageReqVO.java deleted
1   -package cn.iocoder.yudao.module.im.controller.admin.message.vo;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
4   -import com.alibaba.excel.annotation.ExcelProperty;
5   -import io.swagger.v3.oas.annotations.media.Schema;
6   -import lombok.Data;
7   -import lombok.EqualsAndHashCode;
8   -import lombok.ToString;
9   -import org.springframework.format.annotation.DateTimeFormat;
10   -
11   -import java.time.LocalDateTime;
12   -
13   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
14   -
15   -@Schema(description = "管理后台 - 消息分页 Request VO")
16   -@Data
17   -@EqualsAndHashCode(callSuper = true)
18   -@ToString(callSuper = true)
19   -public class ImMessagePageReqVO extends PageParam {
20   -
21   - @Schema(description = "客户端消息编号 uuid,用于排重", example = "3331")
22   - private String clientMessageId;
23   -
24   - @Schema(description = "发送人编号", example = "23239")
25   - private Long senderId;
26   -
27   - @Schema(description = "接收人编号", example = "32494")
28   - private Long receiverId;
29   -
30   - @Schema(description = "发送人昵称", example = "李四")
31   - private String senderNickname;
32   -
33   - @Schema(description = "发送人头像")
34   - private String senderAvatar;
35   -
36   - @Schema(description = "会话类型", example = "2")
37   - private Integer conversationType;
38   -
39   - @Schema(description = "会话标志")
40   - private String conversationNo;
41   -
42   - @Schema(description = "消息类型", example = "1")
43   - private Integer contentType;
44   -
45   - @Schema(description = "消息内容")
46   - private String content;
47   -
48   - @Schema(description = "发送时间")
49   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
50   - private LocalDateTime[] sendTime;
51   -
52   - @Schema(description = "消息来源 100-用户发送;200-系统发送(一般是通知);")
53   - private Integer sendFrom;
54   -
55   - @Schema(description = "创建时间")
56   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
57   - private LocalDateTime[] createTime;
58   -
59   - @Schema(description = "消息状态 1 发送中、2 发送成功、3 发送失败、4 已删除、5 已撤回", requiredMode = Schema.RequiredMode.REQUIRED)
60   - private Integer messageStatus;
61   -
62   -}
63 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/ImMessageRespVO.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/ImMessageReqVO.java
1 1 package cn.iocoder.yudao.module.im.controller.admin.message.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4   -import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
5   -import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum;
  4 +import cn.iocoder.yudao.module.im.enums.message.MessageContentTypeEnum;
6 5 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
7 6 import com.alibaba.excel.annotation.ExcelProperty;
8 7 import io.swagger.v3.oas.annotations.media.Schema;
9   -import jakarta.validation.constraints.NotNull;
10 8 import lombok.Data;
11 9  
12   -import java.time.LocalDateTime;
13   -
14   -@Schema(description = "管理后台 - 消息 Response VO")
  10 +@Schema(description = "管理后台 - 消息 Request VO")
15 11 @Data
16 12 @ExcelIgnoreUnannotated
17   -public class ImMessageRespVO {
18   -
19   - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30713")
20   - @ExcelProperty("编号")
21   - private Long id;
  13 +public class ImMessageReqVO {
22 14  
23 15 @Schema(description = "客户端消息编号 uuid,用于排重", requiredMode = Schema.RequiredMode.REQUIRED, example = "3331")
24 16 @ExcelProperty("客户端消息编号 uuid,用于排重")
25 17 private String clientMessageId;
26 18  
27   - @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23239")
28   - @ExcelProperty("发送人编号")
29   - private Long senderId;
30   -
31 19 @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
32 20 @ExcelProperty("接收人编号")
33 21 private Long receiverId;
34 22  
35   - @Schema(description = "发送人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
36   - @ExcelProperty("发送人昵称")
37   - private String senderNickname;
38   -
39   - @Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
40   - @ExcelProperty("发送人头像")
41   - private String senderAvatar;
42   -
43 23 @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
44 24 @ExcelProperty("会话类型")
45 25 private Integer conversationType;
46 26  
47   - @Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED)
48   - @ExcelProperty("会话标志")
49   - private String conversationNo;
50   -
51 27 @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
52 28 @ExcelProperty("消息类型")
53   - @InEnum(ImMessageTypeEnum.class)
  29 + @InEnum(MessageContentTypeEnum.class)
54 30 private Integer contentType;
55 31  
56 32 @Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
57 33 @ExcelProperty("消息内容")
58 34 private String content;
59 35  
60   - @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
61   - @ExcelProperty("发送时间")
62   - private LocalDateTime sendTime;
63   -
64   - @Schema(description = "消息来源 100-用户发送;200-系统发送(一般是通知);", requiredMode = Schema.RequiredMode.REQUIRED)
65   - @ExcelProperty("消息来源 100-用户发送;200-系统发送(一般是通知);")
66   - private Integer sendFrom;
67   -
68   - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
69   - @ExcelProperty("创建时间")
70   - private LocalDateTime createTime;
71   -
72   - @Schema(description = "消息状态 1 发送中、2 发送成功、3 发送失败、4 已删除、5 已撤回", requiredMode = Schema.RequiredMode.REQUIRED)
73   - @ExcelProperty("消息状态")
74   - @InEnum(ImMessageStatusEnum.class)
75   - private Integer messageStatus;
76   -
77 36 }
78 37 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/MessagePageReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.message.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.ToString;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
  11 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  12 +
  13 +@Schema(description = "管理后台 - 消息分页 Request VO")
  14 +@Data
  15 +@ToString(callSuper = true)
  16 +public class MessagePageReqVO {
  17 +
  18 + @Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "g_1000")
  19 + private String conversationNo;
  20 +
  21 + @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-03-27")
  22 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  23 + private LocalDateTime[] sendTime;
  24 +
  25 +}
0 26 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/SendMessageReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.message.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.im.enums.message.MessageContentTypeEnum;
  5 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  6 +import com.alibaba.excel.annotation.ExcelProperty;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import lombok.Data;
  9 +
  10 +@Schema(description = "管理后台 - 发送消息 Request VO")
  11 +@Data
  12 +@ExcelIgnoreUnannotated
  13 +public class SendMessageReqVO {
  14 +
  15 + @Schema(description = "客户端消息编号 uuid,用于排重", requiredMode = Schema.RequiredMode.REQUIRED, example = "3331")
  16 + @ExcelProperty("客户端消息编号 uuid,用于排重")
  17 + private String clientMessageId;
  18 +
  19 + @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
  20 + @ExcelProperty("接收人编号")
  21 + private Long receiverId;
  22 +
  23 + @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  24 + @ExcelProperty("会话类型")
  25 + private Integer conversationType;
  26 +
  27 + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  28 + @ExcelProperty("消息类型")
  29 + @InEnum(MessageContentTypeEnum.class)
  30 + private Integer contentType;
  31 +
  32 + @Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
  33 + @ExcelProperty("消息内容")
  34 + private String content;
  35 +
  36 +}
0 37 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/SendMessageRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.message.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +
  9 +import java.time.LocalDateTime;
  10 +
  11 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  12 +
  13 +@Schema(description = "管理后台 - 发送消息 Response VO")
  14 +@Data
  15 +@AllArgsConstructor
  16 +@NoArgsConstructor
  17 +public class SendMessageRespVO {
  18 +
  19 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
  20 + private Long id;
  21 +
  22 + @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-03-27 12:00:00")
  23 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  24 + private LocalDateTime sendTime;
  25 +
  26 +}
0 27 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/conversation/ImConversationDO.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/conversation/ConversationDO.java
1 1 package cn.iocoder.yudao.module.im.dal.dataobject.conversation;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
4 5 import com.baomidou.mybatisplus.annotation.KeySequence;
5 6 import com.baomidou.mybatisplus.annotation.TableId;
6 7 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -21,31 +22,28 @@ import java.time.LocalDateTime;
21 22 @Builder
22 23 @NoArgsConstructor
23 24 @AllArgsConstructor
24   -public class ImConversationDO extends BaseDO {
  25 +public class ConversationDO extends BaseDO {
25 26  
26 27 /**
27 28 * 编号
28 29 */
29 30 @TableId
30 31 private Long id;
  32 +
31 33 /**
32 34 * 所属用户
33 35 */
34 36 private Long userId;
35   - // TODO @hao:注释可以改成,如下
  37 +
36 38 /**
37 39 * 会话类型
38   - *
39   - * 枚举 {@link cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum}
40   - */
41   - /**
42   - * 类型:1 单聊;2 群聊;4 通知会话(预留)
  40 + * <p>
  41 + * 枚举 {@link ConversationTypeEnum}
43 42 */
44   - // TODO @hao:conversationType 改成 type
45   - private Integer conversationType;
  43 + private Integer type;
46 44 /**
47 45 * 聊天对象编号
48   - *
  46 + * <p>
49 47 * 1. 单聊时,用户编号;群聊时,群编号
50 48 */
51 49 private Long targetId;
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/ImGroupDO.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/GroupDO.java
1 1 package cn.iocoder.yudao.module.im.dal.dataobject.group;
2 2  
3   -import lombok.*;
4   -import com.baomidou.mybatisplus.annotation.*;
5 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
6 8  
7 9 /**
8 10 * IM 群信息 DO
... ... @@ -17,7 +19,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
17 19 @Builder
18 20 @NoArgsConstructor
19 21 @AllArgsConstructor
20   -public class ImGroupDO extends BaseDO {
  22 +public class GroupDO extends BaseDO {
21 23  
22 24 /**
23 25 * 编号
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/groupmember/GroupMemberDO.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/GroupMemberDO.java
1   -package cn.iocoder.yudao.module.im.dal.dataobject.groupmember;
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.group;
2 2  
3 3 import lombok.*;
4   -import java.util.*;
5   -import java.time.LocalDateTime;
6   -import java.time.LocalDateTime;
7 4 import com.baomidou.mybatisplus.annotation.*;
8 5 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
9 6  
10   -// TODO @hao:这个挪到 group 包下;
11 7 /**
12 8 * IM 群成员 DO
13 9 *
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/inbox/ImInboxDO.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/inbox/InboxDO.java
... ... @@ -20,7 +20,7 @@ import lombok.*;
20 20 @Builder
21 21 @NoArgsConstructor
22 22 @AllArgsConstructor
23   -public class ImInboxDO extends BaseDO {
  23 +public class InboxDO extends BaseDO {
24 24  
25 25 /**
26 26 * 编号
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImMessageDO.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/MessageDO.java
1 1 package cn.iocoder.yudao.module.im.dal.dataobject.message;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
  5 +import cn.iocoder.yudao.module.im.enums.message.MessageContentTypeEnum;
  6 +import cn.iocoder.yudao.module.im.enums.message.MessageSourceEnum;
  7 +import cn.iocoder.yudao.module.im.enums.message.MessageStatusEnum;
4 8 import com.baomidou.mybatisplus.annotation.KeySequence;
5 9 import com.baomidou.mybatisplus.annotation.TableId;
6 10 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -21,7 +25,7 @@ import java.time.LocalDateTime;
21 25 @Builder
22 26 @NoArgsConstructor
23 27 @AllArgsConstructor
24   -public class ImMessageDO extends BaseDO {
  28 +public class MessageDO extends BaseDO {
25 29  
26 30 /**
27 31 * 编号
... ... @@ -32,57 +36,54 @@ public class ImMessageDO extends BaseDO {
32 36 * 客户端消息编号 uuid,用于排重
33 37 */
34 38 private String clientMessageId;
35   - // TODO @hao:senderId、receiverId 存储的具体值写下,对应的群之类的
36 39 /**
37   - * 发送人编号
  40 + * 发送人编号 存储的是用户编号
38 41 */
39 42 private Long senderId;
40 43 /**
41 44 * 接收人编号
  45 + * <p>
  46 + * 1. 单聊时,用户编号;群聊时,群编号
42 47 */
43 48 private Long receiverId;
44   - // TODO @hao:冗余字段,要说明下的。例如说
45 49 /**
46   - * 发送人昵称
47   - *
  50 + * 消息发送者昵称
  51 + * <p>
48 52 * 冗余 AdminUserDO 的 nickname 字段
49 53 */
50 54 private String senderNickname;
51 55 /**
52   - * 发送人头像
  56 + * 消息发送者头像
  57 + * <p>
  58 + * 冗余 AdminUserDO 的 avatar 字段
53 59 */
54 60 private String senderAvatar;
55   - // TODO @hao:关联枚举
56 61 /**
57   - * 会话类型
  62 + * 会话类型 枚举 {@link ConversationTypeEnum}
58 63 */
59 64 private Integer conversationType;
60   - // TODO @hao:关联字段
61 65 /**
62   - * 会话标志
  66 + * 会话标志 {@link ConversationTypeEnum} 的generateConversationNo() 方法生成
63 67 */
64 68 private String conversationNo;
65 69 /**
66   - * 消息类型
  70 + * 消息类型 枚举 {@link MessageContentTypeEnum}
67 71 */
68 72 private Integer contentType;
69   - // TODO @hao:说明下是 json 格式,在哪个包看具体的格式
70 73 /**
71   - * 消息内容
  74 + * 消息内容 JSON 格式 对应 dal/dataobject/message/content 包
72 75 */
73 76 private String content;
74 77 /**
75 78 * 发送时间
76 79 */
77 80 private LocalDateTime sendTime;
78   - // TODO @hao:搞个枚举,然后代码里注释说明下
79 81 /**
80   - * 消息来源 100-用户发送;200-系统发送(一般是通知);
  82 + * 消息来源 枚举 {@link MessageSourceEnum}
81 83 */
82 84 private Integer sendFrom;
83   - // TODO @hao:搞个枚举,然后代码里注释说明下
84 85 /**
85   - * 消息状态 1 发送中、2 发送成功、3 发送失败、4 已删除、5 已撤回
  86 + * 消息状态 枚举 {@link MessageStatusEnum}
86 87 */
87 88 private Integer messageStatus;
88 89  
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/body/ImAudioMessageBody.java deleted
1   -package cn.iocoder.yudao.module.im.dal.dataobject.message.body;
2   -
3   -import lombok.AllArgsConstructor;
4   -import lombok.Data;
5   -import lombok.NoArgsConstructor;
6   -
7   -/**
8   - * 语音消息的 {@link ImMessageBody}
9   - *
10   - * @author 芋道源码
11   - */
12   -@Data
13   -@AllArgsConstructor
14   -@NoArgsConstructor
15   -public class ImAudioMessageBody implements ImMessageBody {
16   -
17   - /**
18   - * 语音 URL
19   - */
20   - private String url;
21   - /**
22   - * 语音格式,例如说 arm、mp3、speex 等
23   - */
24   - private String format;
25   -
26   - // TODO 芋艿:要不要以下字段?待定;云信有、企业微信没有
27   -//"dur":4551, //语音持续时长ms
28   -// "md5":"87b94a090dec5c58f242b7132a530a01", //语音文件的md5值,按照字节流加密
29   -// "size":16420 //语音文件大小,单位为字节(Byte)
30   -
31   -}
32 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/body/ImMessageBody.java deleted
1   -package cn.iocoder.yudao.module.im.dal.dataobject.message.body;
2   -
3   -
4   -import cn.iocoder.yudao.module.im.jackson.ImMessageBodyDeserializer;
5   -import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
6   -
7   -/**
8   - * IM 消息的 body 内容
9   - */
10   -@JsonDeserialize(using = ImMessageBodyDeserializer.class)
11   -public interface ImMessageBody {
12   -}
13 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/AudioMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
  2 +
  3 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +
  8 +/**
  9 + * 语音消息的 {@link MessageDO 字段 content} 的内容
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Data
  14 +@AllArgsConstructor
  15 +@NoArgsConstructor
  16 +public class AudioMessage {
  17 +
  18 + /**
  19 + * 语音文件唯一 ID
  20 + */
  21 + private String uuid;
  22 +
  23 + /**
  24 + * 语音文件的本地路径
  25 + */
  26 + private String soundPath;
  27 +
  28 + /**
  29 + * 语音文件下载地址
  30 + */
  31 + private String sourceUrl;
  32 +
  33 + /**
  34 + * 语音文件大小
  35 + */
  36 + private int dataSize;
  37 +
  38 + /**
  39 + * 语音时长
  40 + */
  41 + private int duration;
  42 +
  43 + /**
  44 + * 语音文件类型
  45 + */
  46 + private String soundType;
  47 +
  48 +}
0 49 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/CardMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
  2 +
  3 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +
  8 +/**
  9 + * 名片消息的 {@link MessageDO 字段 content} 的内容
  10 + *
  11 + * @author anhaohao
  12 + * @since 2024/3/23 下午5:53
  13 + */
  14 +@Data
  15 +@AllArgsConstructor
  16 +@NoArgsConstructor
  17 +public class CardMessage {
  18 +
  19 + /**
  20 + * 用户 ID
  21 + */
  22 + private String userID;
  23 +
  24 + /**
  25 + * 用户名
  26 + */
  27 + private String nickname;
  28 +
  29 + /**
  30 + * 用户头像
  31 + */
  32 + private int faceURL;
  33 +
  34 + /**
  35 + * 扩展字段
  36 + */
  37 + private String ex;
  38 +}
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/body/ImFileMessageBody.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FileMessage.java
1   -package cn.iocoder.yudao.module.im.dal.dataobject.message.body;
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
2 2  
  3 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
3 4 import lombok.AllArgsConstructor;
4 5 import lombok.Data;
5 6 import lombok.NoArgsConstructor;
6 7  
7 8 /**
8   - * 文件消息的 {@link ImMessageBody}
  9 + * 文件消息的 {@link MessageDO 字段 content} 的内容
9 10 *
10 11 * @author 芋道源码
11 12 */
12 13 @Data
13 14 @AllArgsConstructor
14 15 @NoArgsConstructor
15   -public class ImFileMessageBody implements ImMessageBody {
  16 +public class FileMessage {
16 17  
17 18 /**
18 19 * 文件名
... ... @@ -23,9 +24,4 @@ public class ImFileMessageBody implements ImMessageBody {
23 24 */
24 25 private String url;
25 26  
26   - // TODO 芋艿:要不要以下字段?待定;云信有、企业微信没有
27   -// "md5":"79d62a35fa3d34c367b20c66afc2a500", //文件MD5,按照字节流加密
28   -// "ext":"ttf", //文件后缀类型
29   -// "size":91680 //大小,单位为字节(Byte)
30   -
31 27 }
32 28 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/body/ImImageMessageBody.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/ImageMessage.java
1   -package cn.iocoder.yudao.module.im.dal.dataobject.message.body;
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
2 2  
  3 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
3 4 import lombok.AllArgsConstructor;
4 5 import lombok.Data;
5 6 import lombok.NoArgsConstructor;
6 7  
7 8 /**
8   - * 图片消息的 {@link ImMessageBody}
  9 + * 图片消息的 {@link MessageDO 字段 content} 的内容
9 10 *
10 11 * @author 芋道源码
11 12 */
12 13 @Data
13 14 @AllArgsConstructor
14 15 @NoArgsConstructor
15   -public class ImImageMessageBody implements ImMessageBody {
  16 +public class ImageMessage {
16 17  
17 18 /**
18 19 * 图片地址
19 20 */
20 21 private String url;
21 22  
22   - // TODO 芋艿:要不要以下字段?待定;云信有、企业微信没有
23   -// "name":"图片发送于2015-05-07 13:59", //图片name
24   -// "md5":"9894907e4ad9de4678091277509361f7", //图片文件md5,按照字节流加密
25   -// "ext":"jpg", //图片后缀
26   -// "w":6814, //宽,单位为像素
27   -// "h":2332, //高,单位为像素
28   -// "size":388245 //图片文件大小,单位为字节(Byte)
29   -
30 23 }
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/body/ImLocationMessageBody.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/LocationMessage.java
1   -package cn.iocoder.yudao.module.im.dal.dataobject.message.body;
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
2 2  
  3 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
3 4 import lombok.AllArgsConstructor;
4 5 import lombok.Data;
5 6 import lombok.NoArgsConstructor;
6 7  
7 8 /**
8   - * 地址位置消息的 {@link ImMessageBody}
  9 + * 地址位置消息的 {@link MessageDO 字段 content} 的内容
9 10 *
10 11 * @author 芋道源码
11 12 */
12 13 @Data
13 14 @AllArgsConstructor
14 15 @NoArgsConstructor
15   -public class ImLocationMessageBody implements ImMessageBody {
  16 +public class LocationMessage {
16 17  
17 18 /**
18 19 * 地理位置
19   - *
  20 + * <p>
20 21 * 例如说:中国 浙江省 杭州市 网商路 599号
21 22 */
22 23 private String address;
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/body/ImTextMessageBody.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/TextMessage.java
1   -package cn.iocoder.yudao.module.im.dal.dataobject.message.body;
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
2 2  
  3 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
3 4 import lombok.AllArgsConstructor;
4 5 import lombok.Data;
5 6 import lombok.NoArgsConstructor;
6 7  
7 8 /**
8   - * 文本消息的 {@link ImMessageBody}
  9 + * 文本消息的 {@link MessageDO 字段 content} 的内容
9 10 *
10 11 * @author 芋道源码
11 12 */
12 13 @Data
13 14 @AllArgsConstructor
14 15 @NoArgsConstructor
15   -public class ImTextMessageBody implements ImMessageBody {
  16 +public class TextMessage {
16 17  
17 18 /**
18   - * 文本消息内容
  19 + * 文本消息的具体内容
19 20 */
20 21 private String content;
21 22  
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/body/ImVideoMessageBody.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/VideoMessage.java
1   -package cn.iocoder.yudao.module.im.dal.dataobject.message.body;
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
2 2  
  3 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
3 4 import lombok.Data;
4 5  
5 6 /**
6   - * 视频消息的 {@link ImMessageBody}
  7 + * 视频消息的 {@link MessageDO 字段 content} 的内容
7 8 *
8 9 * @author 芋道源码
9 10 */
10 11 @Data
11   -public class ImVideoMessageBody implements ImMessageBody {
  12 +public class VideoMessage {
12 13  
13 14 /**
14 15 * 视频地址
15 16 */
16 17 private String url;
17 18  
18   - // TODO 芋艿:要不要以下字段?待定;云信有、企业微信没有
19   -// "dur":8003, //视频持续时长ms
20   -// "md5":"da2cef3e5663ee9c3547ef5d127f7e3e", //视频文件的md5值,按照字节流加密
21   -// "w":360, //宽,单位为像素
22   -// "h":480, //高,单位为像素
23   -// "size":16420 //视频文件大小,单位为字节(Byte)】
24   -// "ext":"mp4", //视频格式
25   -
26   -
27 19 }
28 20 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ImConversationMapper.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ConversationMapper.java
... ... @@ -4,33 +4,31 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationPageReqVO;
7   -import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
10   -import java.util.Optional;
11   -
12 10 /**
13 11 * IM 会话 Mapper
14 12 *
15 13 * @author 芋道源码
16 14 */
17 15 @Mapper
18   -public interface ImConversationMapper extends BaseMapperX<ImConversationDO> {
  16 +public interface ConversationMapper extends BaseMapperX<ConversationDO> {
19 17  
20   - default PageResult<ImConversationDO> selectPage(ImConversationPageReqVO reqVO) {
21   - return selectPage(reqVO, new LambdaQueryWrapperX<ImConversationDO>()
22   - .eqIfPresent(ImConversationDO::getUserId, reqVO.getUserId())
23   - .eqIfPresent(ImConversationDO::getConversationType, reqVO.getConversationType())
24   - .eqIfPresent(ImConversationDO::getTargetId, reqVO.getTargetId())
25   - .eqIfPresent(ImConversationDO::getNo, reqVO.getNo())
26   - .eqIfPresent(ImConversationDO::getPinned, reqVO.getPinned())
27   - .betweenIfPresent(ImConversationDO::getLastReadTime, reqVO.getLastReadTime())
28   - .betweenIfPresent(ImConversationDO::getCreateTime, reqVO.getCreateTime())
29   - .orderByDesc(ImConversationDO::getId));
  18 + default PageResult<ConversationDO> selectPage(ImConversationPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<ConversationDO>()
  20 + .eqIfPresent(ConversationDO::getUserId, reqVO.getUserId())
  21 + .eqIfPresent(ConversationDO::getType, reqVO.getType())
  22 + .eqIfPresent(ConversationDO::getTargetId, reqVO.getTargetId())
  23 + .eqIfPresent(ConversationDO::getNo, reqVO.getNo())
  24 + .eqIfPresent(ConversationDO::getPinned, reqVO.getPinned())
  25 + .betweenIfPresent(ConversationDO::getLastReadTime, reqVO.getLastReadTime())
  26 + .betweenIfPresent(ConversationDO::getCreateTime, reqVO.getCreateTime())
  27 + .orderByDesc(ConversationDO::getId));
30 28 }
31 29  
32 30 // TODO @hao:1)no) {,要有空格哈;2)可以直接 selectOne(ImConversationDO::getNo, no) 父类做了封装
33   - default ImConversationDO selectByNo(String no){
34   - return selectOne(new LambdaQueryWrapperX<ImConversationDO>().eq(ImConversationDO::getNo, no));
  31 + default ConversationDO selectByNo(String no){
  32 + return selectOne(new LambdaQueryWrapperX<ConversationDO>().eq(ConversationDO::getNo, no));
35 33 }
36 34 }
37 35 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupMapper.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.im.dal.mysql.group;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6   -import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
  6 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
7 7 import org.apache.ibatis.annotations.Mapper;
8 8 import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
9 9  
... ... @@ -13,18 +13,18 @@ import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
13 13 * @author 芋道源码
14 14 */
15 15 @Mapper
16   -public interface ImGroupMapper extends BaseMapperX<ImGroupDO> {
  16 +public interface ImGroupMapper extends BaseMapperX<GroupDO> {
17 17  
18   - default PageResult<ImGroupDO> selectPage(ImGroupPageReqVO reqVO) {
19   - return selectPage(reqVO, new LambdaQueryWrapperX<ImGroupDO>()
20   - .likeIfPresent(ImGroupDO::getGroupName, reqVO.getGroupName())
21   - .eqIfPresent(ImGroupDO::getOwnerId, reqVO.getOwnerId())
22   - .eqIfPresent(ImGroupDO::getHeadImage, reqVO.getHeadImage())
23   - .eqIfPresent(ImGroupDO::getHeadImageThumb, reqVO.getHeadImageThumb())
24   - .eqIfPresent(ImGroupDO::getNotice, reqVO.getNotice())
25   - .eqIfPresent(ImGroupDO::getRemark, reqVO.getRemark())
26   - .betweenIfPresent(ImGroupDO::getCreateTime, reqVO.getCreateTime())
27   - .orderByDesc(ImGroupDO::getId));
  18 + default PageResult<GroupDO> selectPage(ImGroupPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<GroupDO>()
  20 + .likeIfPresent(GroupDO::getGroupName, reqVO.getGroupName())
  21 + .eqIfPresent(GroupDO::getOwnerId, reqVO.getOwnerId())
  22 + .eqIfPresent(GroupDO::getHeadImage, reqVO.getHeadImage())
  23 + .eqIfPresent(GroupDO::getHeadImageThumb, reqVO.getHeadImageThumb())
  24 + .eqIfPresent(GroupDO::getNotice, reqVO.getNotice())
  25 + .eqIfPresent(GroupDO::getRemark, reqVO.getRemark())
  26 + .betweenIfPresent(GroupDO::getCreateTime, reqVO.getCreateTime())
  27 + .orderByDesc(GroupDO::getId));
28 28 }
29 29  
30 30 }
31 31 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/groupmember/ImGroupMemberMapper.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberPageReqVO;
7   -import cn.iocoder.yudao.module.im.dal.dataobject.groupmember.GroupMemberDO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
10 10 import java.util.List;
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/inbox/ImInboxMapper.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxPageReqVO;
7   -import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
10 10 /**
... ... @@ -13,15 +13,15 @@ import org.apache.ibatis.annotations.Mapper;
13 13 * @author 芋道源码
14 14 */
15 15 @Mapper
16   -public interface ImInboxMapper extends BaseMapperX<ImInboxDO> {
  16 +public interface ImInboxMapper extends BaseMapperX<InboxDO> {
17 17  
18   - default PageResult<ImInboxDO> selectPage(ImInboxPageReqVO reqVO) {
19   - return selectPage(reqVO, new LambdaQueryWrapperX<ImInboxDO>()
20   - .eqIfPresent(ImInboxDO::getUserId, reqVO.getUserId())
21   - .eqIfPresent(ImInboxDO::getMessageId, reqVO.getMessageId())
22   - .eqIfPresent(ImInboxDO::getSequence, reqVO.getSequence())
23   - .betweenIfPresent(ImInboxDO::getCreateTime, reqVO.getCreateTime())
24   - .orderByDesc(ImInboxDO::getId));
  18 + default PageResult<InboxDO> selectPage(ImInboxPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<InboxDO>()
  20 + .eqIfPresent(InboxDO::getUserId, reqVO.getUserId())
  21 + .eqIfPresent(InboxDO::getMessageId, reqVO.getMessageId())
  22 + .eqIfPresent(InboxDO::getSequence, reqVO.getSequence())
  23 + .betweenIfPresent(InboxDO::getCreateTime, reqVO.getCreateTime())
  24 + .orderByDesc(InboxDO::getId));
25 25 }
26 26  
27 27 }
28 28 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImMessageMapper.java deleted
1   -package cn.iocoder.yudao.module.im.dal.mysql.message;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6   -import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessagePageReqVO;
7   -import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
8   -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
9   -import com.github.yulichang.wrapper.MPJLambdaWrapper;
10   -import org.apache.ibatis.annotations.Mapper;
11   -
12   -import java.util.List;
13   -
14   -/**
15   - * 消息 Mapper
16   - *
17   - * @author 芋道源码
18   - */
19   -@Mapper
20   -public interface ImMessageMapper extends BaseMapperX<ImMessageDO> {
21   -
22   - default PageResult<ImMessageDO> selectPage(ImMessagePageReqVO reqVO) {
23   - return selectPage(reqVO, new LambdaQueryWrapperX<ImMessageDO>()
24   - .eqIfPresent(ImMessageDO::getClientMessageId, reqVO.getClientMessageId())
25   - .eqIfPresent(ImMessageDO::getSenderId, reqVO.getSenderId())
26   - .eqIfPresent(ImMessageDO::getReceiverId, reqVO.getReceiverId())
27   - .likeIfPresent(ImMessageDO::getSenderNickname, reqVO.getSenderNickname())
28   - .eqIfPresent(ImMessageDO::getSenderAvatar, reqVO.getSenderAvatar())
29   - .eqIfPresent(ImMessageDO::getConversationType, reqVO.getConversationType())
30   - .eqIfPresent(ImMessageDO::getConversationNo, reqVO.getConversationNo())
31   - .eqIfPresent(ImMessageDO::getContentType, reqVO.getContentType())
32   - .eqIfPresent(ImMessageDO::getContent, reqVO.getContent())
33   - .betweenIfPresent(ImMessageDO::getSendTime, reqVO.getSendTime())
34   - .eqIfPresent(ImMessageDO::getSendFrom, reqVO.getSendFrom())
35   - .betweenIfPresent(ImMessageDO::getCreateTime, reqVO.getCreateTime())
36   - .orderByDesc(ImMessageDO::getId));
37   - }
38   -
39   - // TODO @hao:不链表哈;先从 ImInboxDO 查询出 messageId,然后再到 ImMessageDO 里 IN
40   - default List<ImMessageDO> getGreaterThanSequenceMessage(Long userId, Long sequence, Integer size) {
41   - //查询 inbox 表中,大于 sequence 的消息,关联 message 表,按照 inbox 表 sequence 升序
42   - return selectJoinList(ImMessageDO.class, new MPJLambdaWrapper<ImMessageDO>()
43   - .selectAll(ImMessageDO.class)
44   - .innerJoin(ImInboxDO.class, ImInboxDO::getMessageId, ImMessageDO::getId)
45   - .eq(ImInboxDO::getUserId, userId)
46   - .gt(ImInboxDO::getSequence, sequence)
47   - .orderByAsc(ImInboxDO::getSequence)
48   - .last("limit 0," + size));
49   - }
50   -
51   - // TODO @hao:在 dao 里,使用 selectListByUserId,查询用 select,条件用 by,这个算是 spring data 的 method dsl
52   - default List<ImMessageDO> getAllMessage(Long userId, Integer size) {
53   - //查询 inbox 表中,100条消息,关联 message 表,按照 inbox 表 sequence 降序
54   - return selectJoinList(ImMessageDO.class, new MPJLambdaWrapper<ImMessageDO>()
55   - .selectAll(ImMessageDO.class)
56   - .innerJoin(ImInboxDO.class, ImInboxDO::getMessageId, ImMessageDO::getId)
57   - .eq(ImInboxDO::getUserId, userId)
58   - .orderByDesc(ImInboxDO::getSequence)
59   - .last("limit 0," + size));
60   - }
61   -
62   -}
63 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/MessageMapper.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.dal.mysql.message;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessagePageReqVO;
  6 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  8 +import com.github.yulichang.wrapper.MPJLambdaWrapper;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * 消息 Mapper
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Mapper
  19 +public interface MessageMapper extends BaseMapperX<MessageDO> {
  20 +
  21 + // TODO @hao:不链表哈;先从 ImInboxDO 查询出 messageId,然后再到 ImMessageDO 里 IN
  22 + default List<MessageDO> getGreaterThanSequenceMessage(Long userId, Long sequence, Integer size) {
  23 + //查询 inbox 表中,大于 sequence 的消息,关联 message 表,按照 inbox 表 sequence 升序
  24 + return selectJoinList(MessageDO.class, new MPJLambdaWrapper<MessageDO>()
  25 + .selectAll(MessageDO.class)
  26 + .innerJoin(InboxDO.class, InboxDO::getMessageId, MessageDO::getId)
  27 + .eq(InboxDO::getUserId, userId)
  28 + .gt(InboxDO::getSequence, sequence)
  29 + .orderByAsc(InboxDO::getSequence)
  30 + .last("limit 0," + size));
  31 + }
  32 +
  33 + // TODO @hao:在 dao 里,使用 selectListByUserId,查询用 select,条件用 by,这个算是 spring data 的 method dsl
  34 + default List<MessageDO> getAllMessage(Long userId, Integer size) {
  35 + //查询 inbox 表中,100条消息,关联 message 表,按照 inbox 表 sequence 降序
  36 + return selectJoinList(MessageDO.class, new MPJLambdaWrapper<MessageDO>()
  37 + .selectAll(MessageDO.class)
  38 + .innerJoin(InboxDO.class, InboxDO::getMessageId, MessageDO::getId)
  39 + .eq(InboxDO::getUserId, userId)
  40 + .orderByDesc(InboxDO::getSequence)
  41 + .last("limit 0," + size));
  42 + }
  43 +
  44 + default List<MessageDO> getMessagePage(MessagePageReqVO pageReqVO) {
  45 + return selectList(new LambdaQueryWrapperX<MessageDO>()
  46 + .eqIfPresent(MessageDO::getConversationNo, pageReqVO.getConversationNo())
  47 + .betweenIfPresent(MessageDO::getSendTime, pageReqVO.getSendTime())
  48 + .orderByAsc(MessageDO::getSendTime));
  49 + }
  50 +
  51 +}
0 52 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/redis/RedisKeyConstants.java
... ... @@ -14,4 +14,11 @@ public interface RedisKeyConstants {
14 14 * VALUE 数据类型: String
15 15 */
16 16 String INBOX_SEQUENCE = "im_inbox_sequence:%s";
  17 +
  18 + /**
  19 + * 收件箱的分布式锁
  20 + * KEY 格式: im:inbox:lock:{userId}
  21 + * VALUE 数据类型: String
  22 + */
  23 + String INBOX_LOCK = "im_inbox_lock:%s";
17 24 }
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/redis/inbox/InboxLockRedisDAO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.dal.redis.inbox;
  2 +
  3 +import jakarta.annotation.Resource;
  4 +import org.redisson.api.RLock;
  5 +import org.redisson.api.RedissonClient;
  6 +import org.springframework.stereotype.Repository;
  7 +
  8 +import java.util.concurrent.TimeUnit;
  9 +
  10 +import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.INBOX_LOCK;
  11 +
  12 +/**
  13 + * 收件箱的锁 Redis DAO
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Repository
  18 +public class InboxLockRedisDAO {
  19 +
  20 + @Resource
  21 + private RedissonClient redissonClient;
  22 +
  23 + private static String formatKey(Long id) {
  24 + return String.format(INBOX_LOCK, id);
  25 + }
  26 +
  27 + public void lock(Long id, Long timeoutMillis, Runnable runnable) {
  28 + String lockKey = formatKey(id);
  29 + RLock lock = redissonClient.getLock(lockKey);
  30 + try {
  31 + lock.lock(timeoutMillis, TimeUnit.MILLISECONDS);
  32 + // 执行逻辑
  33 + runnable.run();
  34 + } finally {
  35 + lock.unlock();
  36 + }
  37 + }
  38 +
  39 +}
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/redis/inbox/SequenceGeneratorRedisDao.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/redis/inbox/SequenceRedisDao.java
1 1 package cn.iocoder.yudao.module.im.dal.redis.inbox;
2 2  
3 3 import jakarta.annotation.Resource;
  4 +import org.redisson.api.RedissonClient;
4 5 import org.springframework.data.redis.core.RedisTemplate;
5 6 import org.springframework.stereotype.Repository;
6 7  
7 8 import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.INBOX_SEQUENCE;
8 9  
9 10 // TODO @芋艿:这个名字,需要在考虑下;
  11 +
10 12 /**
11 13 * 序号生成器 Redis DAO
12 14 *
13 15 * @author anhaohao
14 16 */
15 17 @Repository
16   -public class SequenceGeneratorRedisDao {
  18 +public class SequenceRedisDao {
17 19  
18 20 @Resource
19 21 private RedisTemplate<String, Long> redisTemplate;
... ... @@ -22,6 +24,12 @@ public class SequenceGeneratorRedisDao {
22 24 return String.format(INBOX_SEQUENCE, userId);
23 25 }
24 26  
  27 + /**
  28 + * 生成序号
  29 + *
  30 + * @param userId 用户编号
  31 + * @return 序号
  32 + */
25 33 public Long generateSequence(Long userId) {
26 34 return redisTemplate.opsForValue().increment(formatKey(userId), 1);
27 35 }
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/jackson/ImMessageBodyDeserializer.java deleted
1   -package cn.iocoder.yudao.module.im.jackson;
2   -
3   -import cn.iocoder.yudao.module.im.dal.dataobject.message.body.*;
4   -import com.fasterxml.jackson.core.JsonParser;
5   -import com.fasterxml.jackson.databind.DeserializationContext;
6   -import com.fasterxml.jackson.databind.JsonDeserializer;
7   -import com.fasterxml.jackson.databind.JsonNode;
8   -
9   -import java.io.IOException;
10   -
11   -// TODO @hao:可以讨论下:是不是前端传递 type + content,后端根据 type 判断后,直接 jsonutil 解析到对应的 body 就 ok 了
12   -public class ImMessageBodyDeserializer extends JsonDeserializer<ImMessageBody> {
13   -
14   - @Override
15   - public ImMessageBody deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
16   - JsonNode node = p.getCodec().readTree(p);
17   - // 根据 node 中的内容来判断应该实例化哪个子类
18   - if (node.has("content")) {
19   - return new ImTextMessageBody(node.get("content").asText());
20   - }
21   - if (node.has("url")) {
22   - String url = node.get("url").asText();
23   - if (node.has("format")) {
24   - return new ImAudioMessageBody(url, node.get("format").asText());
25   - }
26   - return new ImImageMessageBody(url);
27   - }
28   - if (node.has("name")) {
29   - return new ImFileMessageBody(node.get("name").asText(), node.get("url").asText());
30   - }
31   - if (node.has("address")) {
32   - return new ImLocationMessageBody(node.get("address").asText(), node.get("longitude").asDouble(), node.get("latitude").asDouble());
33   - }
34   - // 如果没有匹配的属性,抛出异常
35   - throw ctxt.mappingException("Cannot deserialize to an instance of ImMessageBody");
36   - }
37   -
38   -}
39 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationService.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ConversationService.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.im.service.conversation;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationPageReqVO;
5 5 import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationSaveReqVO;
6   -import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
  6 +import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
7 7 import jakarta.validation.Valid;
8 8  
9 9 import java.util.List;
... ... @@ -13,7 +13,7 @@ import java.util.List;
13 13 *
14 14 * @author 芋道源码
15 15 */
16   -public interface ImConversationService {
  16 +public interface ConversationService {
17 17  
18 18 /**
19 19 * 创建会话
... ... @@ -43,7 +43,7 @@ public interface ImConversationService {
43 43 * @param id 编号
44 44 * @return 会话
45 45 */
46   - ImConversationDO getConversation(Long id);
  46 + ConversationDO getConversation(Long id);
47 47  
48 48 /**
49 49 * 获得会话分页
... ... @@ -51,7 +51,7 @@ public interface ImConversationService {
51 51 * @param pageReqVO 分页查询
52 52 * @return 会话分页
53 53 */
54   - PageResult<ImConversationDO> getConversationPage(ImConversationPageReqVO pageReqVO);
  54 + PageResult<ConversationDO> getConversationPage(ImConversationPageReqVO pageReqVO);
55 55  
56 56  
57 57 /**
... ... @@ -59,7 +59,7 @@ public interface ImConversationService {
59 59 *
60 60 * @return 会话列表
61 61 */
62   - List<ImConversationDO> getConversationList();
  62 + List<ConversationDO> getConversationList();
63 63  
64 64 /**
65 65 * 置顶会话
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationServiceImpl.java renamed to yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ConversationServiceImpl.java
... ... @@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 6 import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationPageReqVO;
7 7 import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationSaveReqVO;
8   -import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
9   -import cn.iocoder.yudao.module.im.dal.mysql.conversation.ImConversationMapper;
  8 +import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
  9 +import cn.iocoder.yudao.module.im.dal.mysql.conversation.ConversationMapper;
10 10 import jakarta.annotation.Resource;
11 11 import org.springframework.stereotype.Service;
12 12 import org.springframework.validation.annotation.Validated;
... ... @@ -24,17 +24,17 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.CONVERSATION_N
24 24 */
25 25 @Service
26 26 @Validated
27   -public class ImConversationServiceImpl implements ImConversationService {
  27 +public class ConversationServiceImpl implements ConversationService {
28 28  
29 29 // TODO @hao: 自己模块的注入,不用带 im 前缀哈;
30 30 @Resource
31   - private ImConversationMapper imConversationMapper;
  31 + private ConversationMapper conversationMapper;
32 32  
33 33 // TODO @hao: 这个方法,是不是不需要哈
34 34 @Override
35 35 public Long createConversation(ImConversationSaveReqVO createReqVO) {
36   - ImConversationDO conversation = BeanUtils.toBean(createReqVO, ImConversationDO.class);
37   - imConversationMapper.insert(conversation);
  36 + ConversationDO conversation = BeanUtils.toBean(createReqVO, ConversationDO.class);
  37 + conversationMapper.insert(conversation);
38 38 return conversation.getId();
39 39 }
40 40  
... ... @@ -44,8 +44,8 @@ public class ImConversationServiceImpl implements ImConversationService {
44 44 // 校验存在
45 45 validateConversationExists(updateReqVO.getId());
46 46 // 更新
47   - ImConversationDO updateObj = BeanUtils.toBean(updateReqVO, ImConversationDO.class);
48   - imConversationMapper.updateById(updateObj);
  47 + ConversationDO updateObj = BeanUtils.toBean(updateReqVO, ConversationDO.class);
  48 + conversationMapper.updateById(updateObj);
49 49 }
50 50  
51 51 // TODO @hao: 考虑到可能和端上不同步,可以不校验是不是存储。另外,不基于 id 删除。要基于 no + userId 删除哈。说白了,对端上要屏蔽 id 字段
... ... @@ -54,28 +54,28 @@ public class ImConversationServiceImpl implements ImConversationService {
54 54 // 校验存在
55 55 validateConversationExists(id);
56 56 // 删除
57   - imConversationMapper.deleteById(id);
  57 + conversationMapper.deleteById(id);
58 58 }
59 59  
60 60 private void validateConversationExists(Long id) {
61   - if (imConversationMapper.selectById(id) == null) {
  61 + if (conversationMapper.selectById(id) == null) {
62 62 throw exception(CONVERSATION_NOT_EXISTS);
63 63 }
64 64 }
65 65  
66 66 @Override
67   - public ImConversationDO getConversation(Long id) {
68   - return imConversationMapper.selectById(id);
  67 + public ConversationDO getConversation(Long id) {
  68 + return conversationMapper.selectById(id);
69 69 }
70 70  
71 71 @Override
72   - public PageResult<ImConversationDO> getConversationPage(ImConversationPageReqVO pageReqVO) {
73   - return imConversationMapper.selectPage(pageReqVO);
  72 + public PageResult<ConversationDO> getConversationPage(ImConversationPageReqVO pageReqVO) {
  73 + return conversationMapper.selectPage(pageReqVO);
74 74 }
75 75  
76 76 @Override
77   - public List<ImConversationDO> getConversationList() {
78   - return imConversationMapper.selectList();
  77 + public List<ConversationDO> getConversationList() {
  78 + return conversationMapper.selectList();
79 79 }
80 80  
81 81 @Override
... ... @@ -95,20 +95,20 @@ public class ImConversationServiceImpl implements ImConversationService {
95 95 // 3. 做对应更新的 notify 推送
96 96 private void createOrUpdateConversation(ImConversationSaveReqVO updateReqVO) {
97 97 // 操作会话(已读、置顶)时,才会延迟创建,要先判断是否存在,根据 no 查询是否存在,不存在则新增
98   - ImConversationDO conversation = imConversationMapper.selectByNo(updateReqVO.getNo());
  98 + ConversationDO conversation = conversationMapper.selectByNo(updateReqVO.getNo());
99 99 if (conversation == null) {
100   - ImConversationDO conversationDO = new ImConversationDO();
  100 + ConversationDO conversationDO = new ConversationDO();
101 101 conversationDO.setNo(updateReqVO.getNo());
102 102 conversationDO.setUserId(updateReqVO.getUserId());
103 103 conversationDO.setTargetId(updateReqVO.getTargetId());
104   - conversationDO.setConversationType(updateReqVO.getConversationType());
  104 + conversationDO.setType(updateReqVO.getType());
105 105 conversationDO.setPinned(updateReqVO.getPinned());
106 106 conversationDO.setLastReadTime(DateUtil.toLocalDateTime(new Date()));
107   - imConversationMapper.insert(conversationDO);
  107 + conversationMapper.insert(conversationDO);
108 108 } else {
109 109 // 更新
110   - ImConversationDO updateObj = BeanUtils.toBean(updateReqVO, ImConversationDO.class);
111   - imConversationMapper.updateById(updateObj);
  110 + ConversationDO updateObj = BeanUtils.toBean(updateReqVO, ConversationDO.class);
  111 + conversationMapper.updateById(updateObj);
112 112 }
113 113 }
114 114  
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupService.java
... ... @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.im.service.group;
2 2  
3 3 import jakarta.validation.*;
4 4 import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
5   -import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
  5 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 7  
8 8 /**
... ... @@ -40,7 +40,7 @@ public interface ImGroupService {
40 40 * @param id 编号
41 41 * @return 群
42 42 */
43   - ImGroupDO getGroup(Long id);
  43 + GroupDO getGroup(Long id);
44 44  
45 45 /**
46 46 * 获得群分页
... ... @@ -48,6 +48,6 @@ public interface ImGroupService {
48 48 * @param pageReqVO 分页查询
49 49 * @return 群分页
50 50 */
51   - PageResult<ImGroupDO> getGroupPage(ImGroupPageReqVO pageReqVO);
  51 + PageResult<GroupDO> getGroupPage(ImGroupPageReqVO pageReqVO);
52 52  
53 53 }
54 54 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java
... ... @@ -5,7 +5,7 @@ import jakarta.annotation.Resource;
5 5 import org.springframework.validation.annotation.Validated;
6 6  
7 7 import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
8   -import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
  8 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
9 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 11  
... ... @@ -29,7 +29,7 @@ public class ImGroupServiceImpl implements ImGroupService {
29 29 @Override
30 30 public Long createGroup(ImGroupSaveReqVO createReqVO) {
31 31 // 插入
32   - ImGroupDO group = BeanUtils.toBean(createReqVO, ImGroupDO.class);
  32 + GroupDO group = BeanUtils.toBean(createReqVO, GroupDO.class);
33 33 imGroupMapper.insert(group);
34 34 // 返回
35 35 return group.getId();
... ... @@ -40,7 +40,7 @@ public class ImGroupServiceImpl implements ImGroupService {
40 40 // 校验存在
41 41 validateGroupExists(updateReqVO.getId());
42 42 // 更新
43   - ImGroupDO updateObj = BeanUtils.toBean(updateReqVO, ImGroupDO.class);
  43 + GroupDO updateObj = BeanUtils.toBean(updateReqVO, GroupDO.class);
44 44 imGroupMapper.updateById(updateObj);
45 45 }
46 46  
... ... @@ -59,12 +59,12 @@ public class ImGroupServiceImpl implements ImGroupService {
59 59 }
60 60  
61 61 @Override
62   - public ImGroupDO getGroup(Long id) {
  62 + public GroupDO getGroup(Long id) {
63 63 return imGroupMapper.selectById(id);
64 64 }
65 65  
66 66 @Override
67   - public PageResult<ImGroupDO> getGroupPage(ImGroupPageReqVO pageReqVO) {
  67 + public PageResult<GroupDO> getGroupPage(ImGroupPageReqVO pageReqVO) {
68 68 return imGroupMapper.selectPage(pageReqVO);
69 69 }
70 70  
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/groupmember/ImGroupMemberService.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.im.service.groupmember;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberPageReqVO;
5 5 import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberSaveReqVO;
6   -import cn.iocoder.yudao.module.im.dal.dataobject.groupmember.GroupMemberDO;
  6 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
7 7 import jakarta.validation.Valid;
8 8  
9 9 import java.util.List;
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/groupmember/ImGroupMemberServiceImpl.java
... ... @@ -5,7 +5,7 @@ import jakarta.annotation.Resource;
5 5 import org.springframework.validation.annotation.Validated;
6 6  
7 7 import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.*;
8   -import cn.iocoder.yudao.module.im.dal.dataobject.groupmember.GroupMemberDO;
  8 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
9 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 11  
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxService.java
... ... @@ -2,8 +2,9 @@ package cn.iocoder.yudao.module.im.service.inbox;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxPageReqVO;
5   -import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxSaveReqVO;
6   -import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
  5 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveReqVO;
  6 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
7 8 import jakarta.validation.Valid;
8 9  
9 10 // TODO @hao:不用的方法,删除下哈;
... ... @@ -20,14 +21,14 @@ public interface ImInboxService {
20 21 * @param createReqVO 创建信息
21 22 * @return 编号
22 23 */
23   - Long createInbox(@Valid ImInboxSaveReqVO createReqVO);
  24 + Long createInbox(@Valid InboxSaveReqVO createReqVO);
24 25  
25 26 /**
26 27 * 更新收件箱
27 28 *
28 29 * @param updateReqVO 更新信息
29 30 */
30   - void updateInbox(@Valid ImInboxSaveReqVO updateReqVO);
  31 + void updateInbox(@Valid InboxSaveReqVO updateReqVO);
31 32  
32 33 /**
33 34 * 删除收件箱
... ... @@ -42,7 +43,7 @@ public interface ImInboxService {
42 43 * @param id 编号
43 44 * @return 收件箱
44 45 */
45   - ImInboxDO getInbox(Long id);
  46 + InboxDO getInbox(Long id);
46 47  
47 48 /**
48 49 * 获得收件箱分页
... ... @@ -50,6 +51,11 @@ public interface ImInboxService {
50 51 * @param pageReqVO 分页查询
51 52 * @return 收件箱分页
52 53 */
53   - PageResult<ImInboxDO> getInboxPage(ImInboxPageReqVO pageReqVO);
  54 + PageResult<InboxDO> getInboxPage(ImInboxPageReqVO pageReqVO);
54 55  
  56 + /**
  57 + * 保存收件箱和发送消息
  58 + * @param inboxSaveMessageReqVO 收件箱保存消息 Request VO
  59 + */
  60 + void saveInboxAndSendMessage(InboxSaveMessageReqVO inboxSaveMessageReqVO);
55 61 }
56 62 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxServiceImpl.java
1 1 package cn.iocoder.yudao.module.im.service.inbox;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;
5 7 import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxPageReqVO;
6   -import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxSaveReqVO;
7   -import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
  8 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
  9 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveReqVO;
  10 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSendMessageReqVO;
  11 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
  12 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
8 13 import cn.iocoder.yudao.module.im.dal.mysql.inbox.ImInboxMapper;
  14 +import cn.iocoder.yudao.module.im.dal.redis.inbox.InboxLockRedisDAO;
  15 +import cn.iocoder.yudao.module.im.dal.redis.inbox.SequenceRedisDao;
  16 +import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
  17 +import cn.iocoder.yudao.module.im.service.groupmember.ImGroupMemberService;
9 18 import jakarta.annotation.Resource;
  19 +import org.dromara.hutool.core.date.DateUnit;
10 20 import org.springframework.stereotype.Service;
11 21 import org.springframework.validation.annotation.Validated;
12 22  
  23 +import java.util.List;
  24 +
13 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
14 26 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.INBOX_NOT_EXISTS;
15 27  
... ... @@ -22,22 +34,33 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.INBOX_NOT_EXIS
22 34 @Validated
23 35 public class ImInboxServiceImpl implements ImInboxService {
24 36  
  37 + private static final Long INBOX_LOCK_TIMEOUT = 120 * DateUnit.SECOND.getMillis();
  38 + private static final String IM_MESSAGE_RECEIVE = "im-message-receive";
  39 +
25 40 @Resource
26 41 private ImInboxMapper imInboxMapper;
  42 + @Resource
  43 + private SequenceRedisDao sequenceRedisDao; // 序列生成器Redis DAO
  44 + @Resource
  45 + private InboxLockRedisDAO inboxLockRedisDAO; // 收件箱的锁 Redis DAO
  46 + @Resource
  47 + private WebSocketMessageSender webSocketMessageSender; // WebSocket消息发送器
  48 + @Resource
  49 + private ImGroupMemberService imGroupMemberService;
27 50  
28 51 @Override
29   - public Long createInbox(ImInboxSaveReqVO createReqVO) {
30   - ImInboxDO inbox = BeanUtils.toBean(createReqVO, ImInboxDO.class);
  52 + public Long createInbox(InboxSaveReqVO createReqVO) {
  53 + InboxDO inbox = BeanUtils.toBean(createReqVO, InboxDO.class);
31 54 imInboxMapper.insert(inbox);
32 55 return inbox.getId();
33 56 }
34 57  
35 58 @Override
36   - public void updateInbox(ImInboxSaveReqVO updateReqVO) {
  59 + public void updateInbox(InboxSaveReqVO updateReqVO) {
37 60 // 校验存在
38 61 validateInboxExists(updateReqVO.getId());
39 62 // 更新
40   - ImInboxDO updateObj = BeanUtils.toBean(updateReqVO, ImInboxDO.class);
  63 + InboxDO updateObj = BeanUtils.toBean(updateReqVO, InboxDO.class);
41 64 imInboxMapper.updateById(updateObj);
42 65 }
43 66  
... ... @@ -56,13 +79,45 @@ public class ImInboxServiceImpl implements ImInboxService {
56 79 }
57 80  
58 81 @Override
59   - public ImInboxDO getInbox(Long id) {
  82 + public InboxDO getInbox(Long id) {
60 83 return imInboxMapper.selectById(id);
61 84 }
62 85  
63 86 @Override
64   - public PageResult<ImInboxDO> getInboxPage(ImInboxPageReqVO pageReqVO) {
  87 + public PageResult<InboxDO> getInboxPage(ImInboxPageReqVO pageReqVO) {
65 88 return imInboxMapper.selectPage(pageReqVO);
66 89 }
67 90  
  91 + @Override
  92 + public void saveInboxAndSendMessage(InboxSaveMessageReqVO inboxSaveMessage) {
  93 + // 保存收件箱 + 发送消息给用户
  94 + saveInboxAndSendMessageForUser(inboxSaveMessage.getFromId(), inboxSaveMessage);
  95 +
  96 + if (inboxSaveMessage.getConversationType().equals(ConversationTypeEnum.SINGLE.getType())) {
  97 + saveInboxAndSendMessageForUser(inboxSaveMessage.getReceiverId(), inboxSaveMessage);
  98 + } else if (inboxSaveMessage.getConversationType().equals(ConversationTypeEnum.GROUP.getType())) {
  99 + List<GroupMemberDO> groupMembers = imGroupMemberService.selectByGroupId(inboxSaveMessage.getReceiverId());
  100 + groupMembers.forEach(groupMemberDO -> saveInboxAndSendMessageForUser(groupMemberDO.getUserId(), inboxSaveMessage));
  101 + }
  102 + }
  103 +
  104 + private void saveInboxAndSendMessageForUser(Long userId, InboxSaveMessageReqVO inboxSaveMessage) {
  105 + inboxLockRedisDAO.lock(userId, INBOX_LOCK_TIMEOUT, () -> {
  106 + Long userSequence = sequenceRedisDao.generateSequence(userId);
  107 + InboxDO inbox = new InboxDO();
  108 + inbox.setUserId(userId);
  109 + inbox.setMessageId(inboxSaveMessage.getMessageId());
  110 + inbox.setSequence(userSequence);
  111 + imInboxMapper.insert(inbox);
  112 +
  113 + //是发送人不发送
  114 + if (userId.equals(inboxSaveMessage.getFromId())) {
  115 + return;
  116 + }
  117 + InboxSendMessageReqVO message = BeanUtils.toBean(inboxSaveMessage, InboxSendMessageReqVO.class);
  118 + message.setSequence(userSequence);
  119 + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), userId, IM_MESSAGE_RECEIVE, message);
  120 + });
  121 + }
  122 +
68 123 }
69 124 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageService.java deleted
1   -package cn.iocoder.yudao.module.im.service.message;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessagePageReqVO;
5   -import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageSaveReqVO;
6   -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
7   -import cn.iocoder.yudao.module.im.websocket.message.ImSendMessage;
8   -import jakarta.validation.Valid;
9   -
10   -import java.util.List;
11   -
12   -/**
13   - * 消息 Service 接口
14   - *
15   - * @author 芋道源码
16   - */
17   -public interface ImMessageService {
18   -
19   - /**
20   - * 创建消息
21   - *
22   - * @param createReqVO 创建信息
23   - * @return 编号
24   - */
25   - Long createMessage(@Valid ImMessageSaveReqVO createReqVO);
26   -
27   - /**
28   - * 更新消息
29   - *
30   - * @param updateReqVO 更新信息
31   - */
32   - void updateMessage(@Valid ImMessageSaveReqVO updateReqVO);
33   -
34   - /**
35   - * 删除消息
36   - *
37   - * @param id 编号
38   - */
39   - void deleteMessage(Long id);
40   -
41   - /**
42   - * 获得消息
43   - *
44   - * @param id 编号
45   - * @return 消息
46   - */
47   - ImMessageDO getMessage(Long id);
48   -
49   - /**
50   - * 获得消息分页
51   - *
52   - * @param pageReqVO 分页查询
53   - * @return 消息分页
54   - */
55   - PageResult<ImMessageDO> getMessagePage(ImMessagePageReqVO pageReqVO);
56   -
57   -
58   - /**
59   - * 保存私聊消息
60   - *
61   - * @param imSendMessage 消息信息
62   - * @param fromUserId 发送人编号
63   - * @return id
64   - */
65   - ImMessageDO savePrivateMessage(ImSendMessage imSendMessage, Long fromUserId);
66   -
67   - /**
68   - * 更新消息状态
69   - *
70   - * @param messageId 消息id
71   - * @param messageStatus 消息状态
72   - */
73   - void updateMessageStatus(Long messageId, Integer messageStatus);
74   -
75   - /**
76   - * 保存群聊消息
77   - *
78   - * @param message 消息
79   - * @param fromUserId 发送者用户ID
80   - * @return id
81   - */
82   - ImMessageDO saveGroupMessage(ImSendMessage message, Long fromUserId);
83   -
84   - /**
85   - * 拉取消息-大于 seq 的消息
86   - *
87   - * @param userId 用户id
88   - * @param sequence 序列号
89   - * @param size 数量
90   - * @return 消息列表
91   - */
92   - List<ImMessageDO> loadMessage(Long userId, Long sequence, Integer size);
93   -
94   - // TODO @hao;这种接口,项目里叫 getMessageListByUserId,尽量保持 getList 获取列表哈;
95   - /**
96   - * 拉取全部消息
97   - *
98   - * @param userId 登录用户编号
99   - * @param size 数量
100   - * @return 消息列表
101   - */
102   - List<ImMessageDO> loadAllMessage(Long userId, Integer size);
103   -}
104 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageServiceImpl.java deleted
1   -package cn.iocoder.yudao.module.im.service.message;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5   -import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessagePageReqVO;
6   -import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageSaveReqVO;
7   -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
8   -import cn.iocoder.yudao.module.im.dal.mysql.message.ImMessageMapper;
9   -import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
10   -import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
11   -import cn.iocoder.yudao.module.im.websocket.message.ImSendMessage;
12   -import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
13   -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
14   -import jakarta.annotation.Resource;
15   -import org.dromara.hutool.core.date.TimeUtil;
16   -import org.springframework.stereotype.Service;
17   -import org.springframework.validation.annotation.Validated;
18   -
19   -import java.util.List;
20   -
21   -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22   -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.MESSAGE_NOT_EXISTS;
23   -
24   -/**
25   - * 消息 Service 实现类
26   - *
27   - * @author 芋道源码
28   - */
29   -@Service
30   -@Validated
31   -public class ImMessageServiceImpl implements ImMessageService {
32   -
33   - @Resource
34   - private ImMessageMapper imMessageMapper;
35   - @Resource
36   - private AdminUserApi adminUserApi;
37   -
38   - @Override
39   - public Long createMessage(ImMessageSaveReqVO createReqVO) {
40   - // 插入
41   - ImMessageDO message = BeanUtils.toBean(createReqVO, ImMessageDO.class);
42   - imMessageMapper.insert(message);
43   - // 返回
44   - return message.getId();
45   - }
46   -
47   - @Override
48   - public void updateMessage(ImMessageSaveReqVO updateReqVO) {
49   - // 校验存在
50   - validateMessageExists(updateReqVO.getId());
51   - // 更新
52   - ImMessageDO updateObj = BeanUtils.toBean(updateReqVO, ImMessageDO.class);
53   - imMessageMapper.updateById(updateObj);
54   - }
55   -
56   - @Override
57   - public void deleteMessage(Long id) {
58   - // 校验存在
59   - validateMessageExists(id);
60   - // 删除
61   - imMessageMapper.deleteById(id);
62   - }
63   -
64   - private void validateMessageExists(Long id) {
65   - if (imMessageMapper.selectById(id) == null) {
66   - throw exception(MESSAGE_NOT_EXISTS);
67   - }
68   - }
69   -
70   - @Override
71   - public ImMessageDO getMessage(Long id) {
72   - return imMessageMapper.selectById(id);
73   - }
74   -
75   - @Override
76   - public PageResult<ImMessageDO> getMessagePage(ImMessagePageReqVO pageReqVO) {
77   - return imMessageMapper.selectPage(pageReqVO);
78   - }
79   -
80   -
81   - @Override
82   - public ImMessageDO savePrivateMessage(ImSendMessage message, Long fromUserId) {
83   - return saveImMessageDO(message, fromUserId);
84   - }
85   -
86   - private ImMessageDO saveImMessageDO(ImSendMessage message, Long fromUserId) {
87   - // TODO @hao:可以搞成 messageDO
88   - // TODO @hao:链式调用
89   - ImMessageDO imMessageDO = new ImMessageDO();
90   - imMessageDO.setClientMessageId(message.getClientMessageId());
91   - imMessageDO.setSenderId(fromUserId);
92   - imMessageDO.setReceiverId(message.getReceiverId());
93   - // 查询发送人昵称和发送人头像
94   - AdminUserRespDTO user = adminUserApi.getUser(fromUserId);
95   - imMessageDO.setSenderNickname(user.getNickname());
96   - imMessageDO.setSenderAvatar(user.getAvatar());
97   - imMessageDO.setConversationType(message.getConversationType());
98   - imMessageDO.setContentType(message.getContentType());
99   - // 单聊:s_{userId}_{targetId} 群聊:群聊:g_{groupId}
100   - // TODO @hao:这里的 conversationNo 的生成,可以写到工具类里,或者干脆放到 ImConversationTypeEnum 里;
101   - if (message.getConversationType().equals(ImConversationTypeEnum.PRIVATE.getType())) {
102   - imMessageDO.setConversationNo("s_" + fromUserId + "_" + message.getReceiverId());
103   - } else if (message.getConversationType().equals(ImConversationTypeEnum.GROUP.getType())) {
104   - imMessageDO.setConversationNo("g_" + message.getReceiverId());
105   - }
106   - imMessageDO.setContent(message.getContent());
107   - // TODO @hao:枚举哈;
108   - // 消息来源 100-用户发送;200-系统发送(一般是通知);不能为空
109   - imMessageDO.setSendFrom(100);
110   - imMessageDO.setSendTime(TimeUtil.now());
111   - // TODO @hao:服务端的消息,就是默认生成哈;
112   - imMessageDO.setMessageStatus(ImMessageStatusEnum.SENDING.getStatus());
113   - imMessageMapper.insert(imMessageDO);
114   - return imMessageDO;
115   - }
116   -
117   - @Override
118   - public void updateMessageStatus(Long messageId, Integer messageStatus) {
119   - // 校验 id 是否存在
120   - validateMessageExists(messageId);
121   - //更新消息状态
122   - ImMessageDO imMessageDO = new ImMessageDO();
123   - imMessageDO.setId(messageId);
124   - imMessageDO.setMessageStatus(messageStatus);
125   - imMessageMapper.updateById(imMessageDO);
126   - }
127   -
128   - @Override
129   - public ImMessageDO saveGroupMessage(ImSendMessage message, Long fromUserId) {
130   - return saveImMessageDO(message, fromUserId);
131   - }
132   -
133   - @Override
134   - public List<ImMessageDO> loadMessage(Long userId, Long sequence, Integer size) {
135   - return imMessageMapper.getGreaterThanSequenceMessage(userId, sequence, size);
136   - }
137   -
138   - @Override
139   - public List<ImMessageDO> loadAllMessage(Long userId, Integer size) {
140   - return imMessageMapper.getAllMessage(userId, size);
141   - }
142   -
143   -}
144 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/MessageService.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.service.message;
  2 +
  3 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessagePageReqVO;
  4 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageReqVO;
  5 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageRespVO;
  6 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  7 +
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * 消息 Service 接口
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +public interface MessageService {
  16 +
  17 + /**
  18 + * 获得消息分页
  19 + *
  20 + * @param pageReqVO 分页查询
  21 + * @return 消息分页
  22 + */
  23 + List<MessageDO> getMessagePage(MessagePageReqVO pageReqVO);
  24 +
  25 +
  26 + /**
  27 + * 拉取消息-大于 seq 的消息
  28 + *
  29 + * @param userId 用户id
  30 + * @param sequence 序列号
  31 + * @param size 数量
  32 + * @return 消息列表
  33 + */
  34 + List<MessageDO> getMessageListBySequence(Long userId, Long sequence, Integer size);
  35 +
  36 + /**
  37 + * 发送消息
  38 + * @param loginUserId 登录用户编号
  39 + * @param message 消息
  40 + * @return 消息编号
  41 + */
  42 + SendMessageRespVO sendMessage(Long loginUserId, SendMessageReqVO message);
  43 +}
0 44 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/MessageServiceImpl.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.service.message;
  2 +
  3 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
  4 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessagePageReqVO;
  5 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageReqVO;
  6 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageRespVO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
  8 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  9 +import cn.iocoder.yudao.module.im.dal.mysql.message.MessageMapper;
  10 +import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
  11 +import cn.iocoder.yudao.module.im.enums.message.MessageSourceEnum;
  12 +import cn.iocoder.yudao.module.im.enums.message.MessageStatusEnum;
  13 +import cn.iocoder.yudao.module.im.service.groupmember.ImGroupMemberService;
  14 +import cn.iocoder.yudao.module.im.service.inbox.ImInboxService;
  15 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  16 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  17 +import jakarta.annotation.Resource;
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.dromara.hutool.core.date.TimeUtil;
  20 +import org.springframework.stereotype.Service;
  21 +import org.springframework.validation.annotation.Validated;
  22 +
  23 +import java.util.List;
  24 +
  25 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.MESSAGE_RECEIVER_NOT_EXISTS;
  27 +
  28 +/**
  29 + * 消息 Service 实现类
  30 + *
  31 + * @author 芋道源码
  32 + */
  33 +@Service
  34 +@Validated
  35 +@Slf4j
  36 +public class MessageServiceImpl implements MessageService {
  37 +
  38 + @Resource
  39 + private MessageMapper messageMapper;
  40 + @Resource
  41 + private ImInboxService imInboxService; // IM收件箱服务
  42 + @Resource
  43 + private AdminUserApi adminUserApi;
  44 + @Resource
  45 + private ImGroupMemberService imGroupMemberService;
  46 +
  47 + @Override
  48 + public List<MessageDO> getMessagePage(MessagePageReqVO pageReqVO) {
  49 + return messageMapper.getMessagePage(pageReqVO);
  50 + }
  51 +
  52 + @Override
  53 + public List<MessageDO> getMessageListBySequence(Long userId, Long sequence, Integer size) {
  54 + return messageMapper.getGreaterThanSequenceMessage(userId, sequence, size);
  55 + }
  56 +
  57 + @Override
  58 + public SendMessageRespVO sendMessage(Long fromUserId, SendMessageReqVO message) {
  59 + // 保存消息
  60 + InboxSaveMessageReqVO inboxSaveMessageReqVO = new InboxSaveMessageReqVO();
  61 + SendMessageRespVO sendMessageRespVO = saveMessage(fromUserId, message, inboxSaveMessageReqVO);
  62 +
  63 + // 保存收件箱 + 发送消息给用户
  64 + imInboxService.saveInboxAndSendMessage(inboxSaveMessageReqVO);
  65 + return sendMessageRespVO;
  66 + }
  67 +
  68 + public SendMessageRespVO saveMessage(Long fromUserId, SendMessageReqVO message, InboxSaveMessageReqVO inboxSaveMessageReqVO) {
  69 + //需要校验 receiverId 存在
  70 + validateReceiverIdExists(message);
  71 + // 查询发送人昵称和发送人头像
  72 + AdminUserRespDTO fromUser = adminUserApi.getUser(fromUserId);
  73 + // 使用链式调用创建 MessageDO 对象
  74 + MessageDO messageDO = new MessageDO()
  75 + .setClientMessageId(message.getClientMessageId())
  76 + .setSenderId(fromUserId)
  77 + .setReceiverId(message.getReceiverId())
  78 + .setSenderNickname(fromUser.getNickname())
  79 + .setSenderAvatar(fromUser.getAvatar())
  80 + .setConversationType(message.getConversationType())
  81 + .setContentType(message.getContentType())
  82 + .setConversationNo(ConversationTypeEnum.generateConversationNo(fromUserId, message.getReceiverId(), message.getConversationType()))
  83 + .setContent(message.getContent())
  84 + .setSendFrom(MessageSourceEnum.USER_SEND.getStatus())
  85 + .setSendTime(TimeUtil.now())
  86 + .setMessageStatus(MessageStatusEnum.SENDING.getStatus());
  87 + messageMapper.insert(messageDO);
  88 +
  89 + // 设置 InboxSaveMessageReqVO 对象
  90 + inboxSaveMessageReqVO.setConversationType(message.getConversationType());
  91 + inboxSaveMessageReqVO.setFromId(fromUserId);
  92 + inboxSaveMessageReqVO.setReceiverId(message.getReceiverId());
  93 + inboxSaveMessageReqVO.setMessageId(messageDO.getId());
  94 + inboxSaveMessageReqVO.setContentType(message.getContentType());
  95 + inboxSaveMessageReqVO.setContent(message.getContent());
  96 + inboxSaveMessageReqVO.setSendTime(messageDO.getSendTime());
  97 +
  98 + // 返回 SendMessageRespVO 对象
  99 + return new SendMessageRespVO(messageDO.getId(), messageDO.getSendTime());
  100 + }
  101 +
  102 + private void validateReceiverIdExists(SendMessageReqVO message) {
  103 + if (message.getReceiverId() == null) {
  104 + throw exception(MESSAGE_RECEIVER_NOT_EXISTS);
  105 + }
  106 + if (message.getConversationType().equals(ConversationTypeEnum.SINGLE.getType())) {
  107 + //校验用户是否存在;
  108 + AdminUserRespDTO receiverUser = adminUserApi.getUser(message.getReceiverId());
  109 + if (receiverUser == null) {
  110 + throw exception(MESSAGE_RECEIVER_NOT_EXISTS);
  111 + }
  112 +
  113 + } else if (message.getConversationType().equals(ConversationTypeEnum.GROUP.getType())) {
  114 + //校验群聊是否存在;
  115 + List<GroupMemberDO> groupMemberDOS = imGroupMemberService.selectByGroupId(message.getReceiverId());
  116 + if (groupMemberDOS.isEmpty()) {
  117 + throw exception(MESSAGE_RECEIVER_NOT_EXISTS);
  118 + }
  119 + }
  120 + }
  121 +
  122 +}
0 123 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/ImWebSocketMessageListener.java deleted
1   -package cn.iocoder.yudao.module.im.websocket;
2   -
3   -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4   -import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;
5   -import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;
6   -import cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;
7   -import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxSaveReqVO;
8   -import cn.iocoder.yudao.module.im.dal.dataobject.groupmember.GroupMemberDO;
9   -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
10   -import cn.iocoder.yudao.module.im.dal.redis.inbox.SequenceGeneratorRedisDao;
11   -import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
12   -import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
13   -import cn.iocoder.yudao.module.im.service.conversation.ImConversationService;
14   -import cn.iocoder.yudao.module.im.service.groupmember.ImGroupMemberService;
15   -import cn.iocoder.yudao.module.im.service.inbox.ImInboxService;
16   -import cn.iocoder.yudao.module.im.service.message.ImMessageService;
17   -import cn.iocoder.yudao.module.im.websocket.message.ImReceiveMessage;
18   -import cn.iocoder.yudao.module.im.websocket.message.ImSendMessage;
19   -import jakarta.annotation.Resource;
20   -import lombok.extern.slf4j.Slf4j;
21   -import org.springframework.stereotype.Component;
22   -import org.springframework.web.socket.WebSocketSession;
23   -
24   -import java.util.List;
25   -
26   -// TODO @hao:消息发送,使用 http 上行。因为在 cloud 框架下,我们比较难去 Listener。因为 im-server 不会自己启动 websocket 路径
27   -/**
28   - * WebSocket im
29   - *
30   - * @author 芋道源码
31   - */
32   -@Component
33   -@Slf4j
34   -public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSendMessage> {
35   -
36   - public static final String IM_MESSAGE_RECEIVE = "im-message-receive";
37   - @Resource
38   - private WebSocketMessageSender webSocketMessageSender; // WebSocket消息发送器
39   - @Resource
40   - private ImMessageService imMessageService; // IM消息服务
41   - @Resource
42   - private ImConversationService imConversationService; // IM会话服务
43   - @Resource
44   - private ImInboxService imInboxService; // IM收件箱服务
45   - @Resource
46   - private ImGroupMemberService imGroupMemberService; // 群成员服务
47   - @Resource
48   - private SequenceGeneratorRedisDao sequenceGeneratorRedisDao; // 序列生成器Redis DAO
49   -
50   - /**
51   - * 处理WebSocket消息
52   - *
53   - * @param session WebSocket会话
54   - * @param message 发送的IM消息
55   - */
56   - @Override
57   - public void onMessage(WebSocketSession session, ImSendMessage message) {
58   - Long fromUserId = WebSocketFrameworkUtils.getLoginUserId(session); // 获取登录用户ID
59   -
60   - // 如果是私人消息,处理私人消息
61   - if (message.getConversationType().equals(ImConversationTypeEnum.PRIVATE.getType())) {
62   - handlePrivateMessage(fromUserId, message);
63   - } else if (message.getConversationType().equals(ImConversationTypeEnum.GROUP.getType())) {
64   - // 处理群聊消息
65   - handleGroupMessage(fromUserId, message);
66   - } else {
67   - // TODO @hao:是不是应该会话类型哈?然后打印这种错误日志,最好把 message 整个打进去,这样更好定位完整的消息;
68   - log.error("[onMessage][消息类型({}) 未支持]", message.getConversationType());
69   - }
70   - }
71   -
72   - /**
73   - * 处理群聊消息
74   - *
75   - * @param fromUserId 发送者用户ID
76   - * @param message 发送的IM消息
77   - */
78   - private void handleGroupMessage(Long fromUserId, ImSendMessage message) {
79   - // TODO @hao:群存在;
80   - // TODO @hao:发送人在群里;
81   - ImMessageDO imMessageDO = imMessageService.saveGroupMessage(message, fromUserId); // 保存群聊消息
82   - Long groupId = message.getReceiverId();
83   -
84   - // 发送消息给群聊成员
85   - // TODO @hao:变量名可以叫 groupMembers;一般不用 DO 后缀,除非为了区分;
86   - List<GroupMemberDO> groupMemberDOList = imGroupMemberService.selectByGroupId(groupId);
87   - groupMemberDOList.forEach(groupMemberDO -> {
88   - // 过滤掉自己
89   - // TODO @hao:98 到 99 的代码,是不是可以融合到这里来;
90   - if (groupMemberDO.getUserId().equals(fromUserId)) {
91   - return;
92   - }
93   - Long receiverSequence = sequenceGeneratorRedisDao.generateSequence(groupMemberDO.getUserId()); // 生成接收者序列
94   - Long receiverInboxId = createAndSaveInbox(groupMemberDO.getUserId(), imMessageDO.getId(), receiverSequence); // 创建并保存接收者收件箱
95   - sendMessage(groupMemberDO.getUserId(), receiverInboxId, imMessageDO, message, receiverSequence);
96   - });
97   -
98   - Long fromUserSequence = sequenceGeneratorRedisDao.generateSequence(fromUserId); // 生成发送者序列
99   - Long fromUserInboxId = createAndSaveInbox(fromUserId, imMessageDO.getId(), fromUserSequence); // 创建并保存发送者收件箱
100   -
101   - // 发送消息给发送者
102   - sendMessage(fromUserId, fromUserInboxId, imMessageDO, message, fromUserSequence);
103   -
104   - // 更新消息状态为成功
105   - imMessageService.updateMessageStatus(imMessageDO.getId(), ImMessageStatusEnum.SUCCESS.getStatus());
106   - }
107   -
108   - /**
109   - * 处理私人消息
110   - *
111   - * @param fromUserId 发送者用户ID
112   - * @param message 发送的IM消息
113   - */
114   - private void handlePrivateMessage(Long fromUserId, ImSendMessage message) {
115   - // TODO @hao:需要校验 receiverId 存在;
116   - ImMessageDO imMessageDO = imMessageService.savePrivateMessage(message, fromUserId); // 保存私人消息
117   - Long receiverId = message.getReceiverId();
118   -
119   - // TODO @hao:sequenceGeneratorRedisDao.generateSequence 和 createAndSaveInbox 步骤,要加锁;不然会有并发问题;如果有疑问,可以微信招我沟通哈;另外,创建 seq、保存收件箱、发送,可以统一封装到 imInboxService 里哈;这样,后续要发某个消息,就是丢个 message 对象进去;
120   - Long fromUserSequence = sequenceGeneratorRedisDao.generateSequence(fromUserId); // 生成发送者序列
121   - Long fromUserInboxId = createAndSaveInbox(fromUserId, imMessageDO.getId(), fromUserSequence); // 创建并保存发送者收件箱
122   -
123   - Long receiverSequence = sequenceGeneratorRedisDao.generateSequence(message.getReceiverId()); // 生成接收者序列
124   - Long receiverInboxId = createAndSaveInbox(message.getReceiverId(), imMessageDO.getId(), receiverSequence); // 创建并保存接收者收件箱
125   -
126   - // 发送消息给接收者和发送者
127   - sendMessage(fromUserId, fromUserInboxId, imMessageDO, message, fromUserSequence);
128   - sendMessage(receiverId, receiverInboxId, imMessageDO, message, receiverSequence);
129   -
130   - // 更新消息状态为成功
131   - // TODO @hao:后端消息的发送,默认就是 success 哈,不要区分 status;status 更多是前端使用的;
132   - imMessageService.updateMessageStatus(imMessageDO.getId(), ImMessageStatusEnum.SUCCESS.getStatus());
133   - // 保存私人会话,只有在 client 操作会话(已读、置顶)时,才会延迟创建
134   - //imConversationService.savePrivateConversation(fromUserId, receiverId);
135   - }
136   -
137   - /**
138   - * 创建并保存收件箱
139   - *
140   - * @param userId 用户ID
141   - * @param messageId 消息ID
142   - * @param sequence 序列
143   - * @return 收件箱ID
144   - */
145   - private Long createAndSaveInbox(Long userId, Long messageId, Long sequence) {
146   - ImInboxSaveReqVO inboxSaveReqVO = new ImInboxSaveReqVO(userId, messageId, sequence); // 创建收件箱保存请求VO
147   - return imInboxService.createInbox(inboxSaveReqVO); // 创建收件箱
148   - }
149   -
150   - // TODO @hao:这个使用 WebSocketSenderApi 发送哈;
151   - /**
152   - * 发送消息
153   - *
154   - * @param fromUserId 发送者用户ID
155   - * @param inboxId 收件箱ID
156   - * @param imMessageDO IM消息数据对象
157   - * @param message 发送的IM消息
158   - * @param sequence 序列
159   - */
160   - private void sendMessage(Long fromUserId, Long inboxId, ImMessageDO imMessageDO, ImSendMessage message, Long sequence) {
161   - // TODO @hao:不要每个字段 set 占一行;想同类型的字段用一行,然后链式 set,会更简洁干净。
162   - ImReceiveMessage receiveMessage = new ImReceiveMessage(); // 创建接收消息
163   - receiveMessage.setFromId(fromUserId); // 设置发送者ID
164   - receiveMessage.setConversationType(message.getConversationType()); // 设置会话类型
165   - receiveMessage.setContentType(message.getContentType()); // 设置内容类型
166   - receiveMessage.setContent(message.getContent()); // 设置内容
167   - receiveMessage.setMessageId(imMessageDO.getId()); // 设置消息ID
168   - receiveMessage.setInboxId(inboxId); // 设置收件箱ID
169   - receiveMessage.setSendTime(imMessageDO.getSendTime()); // 设置发送时间
170   - receiveMessage.setSequence(sequence); // 设置序列
171   - webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), fromUserId, ImWebSocketMessageListener.IM_MESSAGE_RECEIVE, receiveMessage); // 发送消息
172   - }
173   -
174   - /**
175   - * 获取类型
176   - *
177   - * @return 类型
178   - */
179   - @Override
180   - public String getType() {
181   - return "im-message-send";
182   - }
183   -
184   -}
185 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/message/ImSendMessage.java deleted
1   -package cn.iocoder.yudao.module.im.websocket.message;
2   -
3   -import io.swagger.v3.oas.annotations.media.Schema;
4   -import lombok.Data;
5   -
6   -@Schema(description = "管理后台 - IM 消息发送 send")
7   -@Data
8   -public class ImSendMessage {
9   -
10   - @Schema(description = "客户端消息编号 uuid,用于排重", requiredMode = Schema.RequiredMode.REQUIRED, example = "3331")
11   - private String clientMessageId;
12   -
13   - @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
14   - // TODO @hao:用下 InEnum 校验。这样可以少写 15 的注释哈;类似 contentType 也是;
15   - private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
16   -
17   - @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
18   - private Long receiverId; // 根据 conversationType 区分
19   -
20   - @Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
21   - private Integer contentType; // 参见 ImMessageTypeEnum 枚举
22   -
23   - @Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
24   - private String content;
25   -
26   -}
27 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/conversation/ImConversationMapper.xml renamed to yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/conversation/ConversationMapper.xml
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3   -<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.conversation.ImConversationMapper">
  3 +<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.conversation.ConversationMapper">
4 4  
5 5 <!-- TODO @hao:xml 如果用不到,可以先删除哈 -->
6 6 <!--
... ...
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/message/ImMessageMapper.xml renamed to yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/message/MessageMapper.xml
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3   -<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.message.ImMessageMapper">
  3 +<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.message.MessageMapper">
4 4  
5 5 <!--
6 6 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
... ...
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationServiceImplTest.java deleted
1   -package cn.iocoder.yudao.module.im.service.conversation;
2   -
3   -import org.junit.jupiter.api.Disabled;
4   -import org.junit.jupiter.api.Test;
5   -
6   -import jakarta.annotation.Resource;
7   -
8   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
9   -
10   -import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.*;
11   -import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
12   -import cn.iocoder.yudao.module.im.dal.mysql.conversation.ImConversationMapper;
13   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
14   -
15   -import org.springframework.context.annotation.Import;
16   -
17   -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
18   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
19   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
20   -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
21   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
22   -import static org.junit.jupiter.api.Assertions.*;
23   -
24   -// TODO @hao:单测可以先都删除,等后面搞了在弄
25   -/**
26   - * {@link ImConversationServiceImpl} 的单元测试类
27   - *
28   - * @author 芋道源码
29   - */
30   -@Import(ImConversationServiceImpl.class)
31   -public class ImConversationServiceImplTest extends BaseDbUnitTest {
32   -
33   - @Resource
34   - private ImConversationServiceImpl conversationService;
35   -
36   - @Resource
37   - private ImConversationMapper imConversationMapper;
38   -
39   - @Test
40   - public void testCreateConversation_success() {
41   - // 准备参数
42   - ImConversationSaveReqVO createReqVO = randomPojo(ImConversationSaveReqVO.class).setId(null);
43   -
44   - // 调用
45   - Long conversationId = conversationService.createConversation(createReqVO);
46   - // 断言
47   - assertNotNull(conversationId);
48   - // 校验记录的属性是否正确
49   - ImConversationDO conversation = imConversationMapper.selectById(conversationId);
50   - assertPojoEquals(createReqVO, conversation, "id");
51   - }
52   -
53   - @Test
54   - public void testUpdateConversation_success() {
55   - // mock 数据
56   - ImConversationDO dbConversation = randomPojo(ImConversationDO.class);
57   - imConversationMapper.insert(dbConversation);// @Sql: 先插入出一条存在的数据
58   - // 准备参数
59   - ImConversationSaveReqVO updateReqVO = randomPojo(ImConversationSaveReqVO.class, o -> {
60   - o.setId(dbConversation.getId()); // 设置更新的 ID
61   - });
62   -
63   - // 调用
64   - conversationService.updateConversation(updateReqVO);
65   - // 校验是否更新正确
66   - ImConversationDO conversation = imConversationMapper.selectById(updateReqVO.getId()); // 获取最新的
67   - assertPojoEquals(updateReqVO, conversation);
68   - }
69   -
70   - @Test
71   - public void testUpdateConversation_notExists() {
72   - // 准备参数
73   - ImConversationSaveReqVO updateReqVO = randomPojo(ImConversationSaveReqVO.class);
74   -
75   - // 调用, 并断言异常
76   - assertServiceException(() -> conversationService.updateConversation(updateReqVO), CONVERSATION_NOT_EXISTS);
77   - }
78   -
79   - @Test
80   - public void testDeleteConversation_success() {
81   - // mock 数据
82   - ImConversationDO dbConversation = randomPojo(ImConversationDO.class);
83   - imConversationMapper.insert(dbConversation);// @Sql: 先插入出一条存在的数据
84   - // 准备参数
85   - Long id = dbConversation.getId();
86   -
87   - // 调用
88   - conversationService.deleteConversation(id);
89   - // 校验数据不存在了
90   - assertNull(imConversationMapper.selectById(id));
91   - }
92   -
93   - @Test
94   - public void testDeleteConversation_notExists() {
95   - // 准备参数
96   - Long id = randomLongId();
97   -
98   - // 调用, 并断言异常
99   - assertServiceException(() -> conversationService.deleteConversation(id), CONVERSATION_NOT_EXISTS);
100   - }
101   -
102   - @Test
103   - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
104   - public void testGetConversationPage() {
105   - // mock 数据
106   - ImConversationDO dbConversation = randomPojo(ImConversationDO.class, o -> { // 等会查询到
107   - o.setUserId(null);
108   - o.setConversationType(null);
109   - o.setTargetId(null);
110   - o.setNo(null);
111   - o.setPinned(null);
112   - o.setLastReadTime(null);
113   - o.setCreateTime(null);
114   - });
115   - imConversationMapper.insert(dbConversation);
116   - // 测试 userId 不匹配
117   - imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setUserId(null)));
118   - // 测试 conversationType 不匹配
119   - imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setConversationType(null)));
120   - // 测试 targetId 不匹配
121   - imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setTargetId(null)));
122   - // 测试 no 不匹配
123   - imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setNo(null)));
124   - // 测试 pinned 不匹配
125   - imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setPinned(null)));
126   - // 测试 lastReadTime 不匹配
127   - imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setLastReadTime(null)));
128   - // 测试 createTime 不匹配
129   - imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setCreateTime(null)));
130   - // 准备参数
131   - ImConversationPageReqVO reqVO = new ImConversationPageReqVO();
132   - reqVO.setUserId(null);
133   - reqVO.setConversationType(null);
134   - reqVO.setTargetId(null);
135   - reqVO.setNo(null);
136   - reqVO.setPinned(null);
137   - reqVO.setLastReadTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
138   - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
139   -
140   - // 调用
141   - PageResult<ImConversationDO> pageResult = conversationService.getConversationPage(reqVO);
142   - // 断言
143   - assertEquals(1, pageResult.getTotal());
144   - assertEquals(1, pageResult.getList().size());
145   - assertPojoEquals(dbConversation, pageResult.getList().get(0));
146   - }
147   -
148   -}
149 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java deleted
1   -package cn.iocoder.yudao.module.im.service.group;
2   -
3   -import org.junit.jupiter.api.Disabled;
4   -import org.junit.jupiter.api.Test;
5   -
6   -import jakarta.annotation.Resource;
7   -
8   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
9   -
10   -import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
11   -import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
12   -import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMapper;
13   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
14   -
15   -import org.springframework.context.annotation.Import;
16   -
17   -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
18   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
19   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
20   -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
21   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
22   -import static org.junit.jupiter.api.Assertions.*;
23   -
24   -/**
25   - * {@link ImGroupServiceImpl} 的单元测试类
26   - *
27   - * @author 芋道源码
28   - */
29   -@Import(ImGroupServiceImpl.class)
30   -public class ImGroupServiceImplTest extends BaseDbUnitTest {
31   -
32   - @Resource
33   - private ImGroupServiceImpl groupService;
34   -
35   - @Resource
36   - private ImGroupMapper imGroupMapper;
37   -
38   - @Test
39   - public void testCreateGroup_success() {
40   - // 准备参数
41   - ImGroupSaveReqVO createReqVO = randomPojo(ImGroupSaveReqVO.class).setId(null);
42   -
43   - // 调用
44   - Long groupId = groupService.createGroup(createReqVO);
45   - // 断言
46   - assertNotNull(groupId);
47   - // 校验记录的属性是否正确
48   - ImGroupDO group = imGroupMapper.selectById(groupId);
49   - assertPojoEquals(createReqVO, group, "id");
50   - }
51   -
52   - @Test
53   - public void testUpdateGroup_success() {
54   - // mock 数据
55   - ImGroupDO dbGroup = randomPojo(ImGroupDO.class);
56   - imGroupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
57   - // 准备参数
58   - ImGroupSaveReqVO updateReqVO = randomPojo(ImGroupSaveReqVO.class, o -> {
59   - o.setId(dbGroup.getId()); // 设置更新的 ID
60   - });
61   -
62   - // 调用
63   - groupService.updateGroup(updateReqVO);
64   - // 校验是否更新正确
65   - ImGroupDO group = imGroupMapper.selectById(updateReqVO.getId()); // 获取最新的
66   - assertPojoEquals(updateReqVO, group);
67   - }
68   -
69   - @Test
70   - public void testUpdateGroup_notExists() {
71   - // 准备参数
72   - ImGroupSaveReqVO updateReqVO = randomPojo(ImGroupSaveReqVO.class);
73   -
74   - // 调用, 并断言异常
75   - assertServiceException(() -> groupService.updateGroup(updateReqVO), GROUP_NOT_EXISTS);
76   - }
77   -
78   - @Test
79   - public void testDeleteGroup_success() {
80   - // mock 数据
81   - ImGroupDO dbGroup = randomPojo(ImGroupDO.class);
82   - imGroupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
83   - // 准备参数
84   - Long id = dbGroup.getId();
85   -
86   - // 调用
87   - groupService.deleteGroup(id);
88   - // 校验数据不存在了
89   - assertNull(imGroupMapper.selectById(id));
90   - }
91   -
92   - @Test
93   - public void testDeleteGroup_notExists() {
94   - // 准备参数
95   - Long id = randomLongId();
96   -
97   - // 调用, 并断言异常
98   - assertServiceException(() -> groupService.deleteGroup(id), GROUP_NOT_EXISTS);
99   - }
100   -
101   - @Test
102   - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
103   - public void testGetGroupPage() {
104   - // mock 数据
105   - ImGroupDO dbGroup = randomPojo(ImGroupDO.class, o -> { // 等会查询到
106   - o.setGroupName(null);
107   - o.setOwnerId(null);
108   - o.setHeadImage(null);
109   - o.setHeadImageThumb(null);
110   - o.setNotice(null);
111   - o.setRemark(null);
112   - o.setCreateTime(null);
113   - });
114   - imGroupMapper.insert(dbGroup);
115   - // 测试 groupName 不匹配
116   - imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setGroupName(null)));
117   - // 测试 ownerId 不匹配
118   - imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setOwnerId(null)));
119   - // 测试 headImage 不匹配
120   - imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setHeadImage(null)));
121   - // 测试 headImageThumb 不匹配
122   - imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setHeadImageThumb(null)));
123   - // 测试 notice 不匹配
124   - imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setNotice(null)));
125   - // 测试 remark 不匹配
126   - imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setRemark(null)));
127   - // 测试 createTime 不匹配
128   - imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setCreateTime(null)));
129   - // 准备参数
130   - ImGroupPageReqVO reqVO = new ImGroupPageReqVO();
131   - reqVO.setGroupName(null);
132   - reqVO.setOwnerId(null);
133   - reqVO.setHeadImage(null);
134   - reqVO.setHeadImageThumb(null);
135   - reqVO.setNotice(null);
136   - reqVO.setRemark(null);
137   - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
138   -
139   - // 调用
140   - PageResult<ImGroupDO> pageResult = groupService.getGroupPage(reqVO);
141   - // 断言
142   - assertEquals(1, pageResult.getTotal());
143   - assertEquals(1, pageResult.getList().size());
144   - assertPojoEquals(dbGroup, pageResult.getList().get(0));
145   - }
146   -
147   -}
148 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/groupmember/ImGroupMemberServiceImplTest.java deleted
1   -package cn.iocoder.yudao.module.im.service.groupmember;
2   -
3   -import org.junit.jupiter.api.Disabled;
4   -import org.junit.jupiter.api.Test;
5   -
6   -import jakarta.annotation.Resource;
7   -
8   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
9   -
10   -import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.*;
11   -import cn.iocoder.yudao.module.im.dal.dataobject.groupmember.GroupMemberDO;
12   -import cn.iocoder.yudao.module.im.dal.mysql.groupmember.ImGroupMemberMapper;
13   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
14   -
15   -import org.springframework.context.annotation.Import;
16   -
17   -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
18   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
19   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
20   -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
21   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
22   -import static org.junit.jupiter.api.Assertions.*;
23   -
24   -/**
25   - * {@link ImGroupMemberServiceImpl} 的单元测试类
26   - *
27   - * @author 芋道源码
28   - */
29   -@Import(ImGroupMemberServiceImpl.class)
30   -public class ImGroupMemberServiceImplTest extends BaseDbUnitTest {
31   -
32   - @Resource
33   - private ImGroupMemberServiceImpl groupMemberService;
34   -
35   - @Resource
36   - private ImGroupMemberMapper imGroupMemberMapper;
37   -
38   - @Test
39   - public void testCreateGroupMember_success() {
40   - // 准备参数
41   - ImGroupMemberSaveReqVO createReqVO = randomPojo(ImGroupMemberSaveReqVO.class).setId(null);
42   -
43   - // 调用
44   - Long groupMemberId = groupMemberService.createGroupMember(createReqVO);
45   - // 断言
46   - assertNotNull(groupMemberId);
47   - // 校验记录的属性是否正确
48   - GroupMemberDO groupMember = imGroupMemberMapper.selectById(groupMemberId);
49   - assertPojoEquals(createReqVO, groupMember, "id");
50   - }
51   -
52   - @Test
53   - public void testUpdateGroupMember_success() {
54   - // mock 数据
55   - GroupMemberDO dbGroupMember = randomPojo(GroupMemberDO.class);
56   - imGroupMemberMapper.insert(dbGroupMember);// @Sql: 先插入出一条存在的数据
57   - // 准备参数
58   - ImGroupMemberSaveReqVO updateReqVO = randomPojo(ImGroupMemberSaveReqVO.class, o -> {
59   - o.setId(dbGroupMember.getId()); // 设置更新的 ID
60   - });
61   -
62   - // 调用
63   - groupMemberService.updateGroupMember(updateReqVO);
64   - // 校验是否更新正确
65   - GroupMemberDO groupMember = imGroupMemberMapper.selectById(updateReqVO.getId()); // 获取最新的
66   - assertPojoEquals(updateReqVO, groupMember);
67   - }
68   -
69   - @Test
70   - public void testUpdateGroupMember_notExists() {
71   - // 准备参数
72   - ImGroupMemberSaveReqVO updateReqVO = randomPojo(ImGroupMemberSaveReqVO.class);
73   -
74   - // 调用, 并断言异常
75   - assertServiceException(() -> groupMemberService.updateGroupMember(updateReqVO), GROUP_MEMBER_NOT_EXISTS);
76   - }
77   -
78   - @Test
79   - public void testDeleteGroupMember_success() {
80   - // mock 数据
81   - GroupMemberDO dbGroupMember = randomPojo(GroupMemberDO.class);
82   - imGroupMemberMapper.insert(dbGroupMember);// @Sql: 先插入出一条存在的数据
83   - // 准备参数
84   - Long id = dbGroupMember.getId();
85   -
86   - // 调用
87   - groupMemberService.deleteGroupMember(id);
88   - // 校验数据不存在了
89   - assertNull(imGroupMemberMapper.selectById(id));
90   - }
91   -
92   - @Test
93   - public void testDeleteGroupMember_notExists() {
94   - // 准备参数
95   - Long id = randomLongId();
96   -
97   - // 调用, 并断言异常
98   - assertServiceException(() -> groupMemberService.deleteGroupMember(id), GROUP_MEMBER_NOT_EXISTS);
99   - }
100   -
101   - @Test
102   - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
103   - public void testGetGroupMemberPage() {
104   - // mock 数据
105   - GroupMemberDO dbGroupMember = randomPojo(GroupMemberDO.class, o -> { // 等会查询到
106   - o.setGroupId(null);
107   - o.setUserId(null);
108   - o.setNickname(null);
109   - o.setAvatar(null);
110   - o.setAliasName(null);
111   - o.setRemark(null);
112   - o.setCreateTime(null);
113   - });
114   - imGroupMemberMapper.insert(dbGroupMember);
115   - // 测试 groupId 不匹配
116   - imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setGroupId(null)));
117   - // 测试 userId 不匹配
118   - imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setUserId(null)));
119   - // 测试 nickname 不匹配
120   - imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setNickname(null)));
121   - // 测试 avatar 不匹配
122   - imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setAvatar(null)));
123   - // 测试 aliasName 不匹配
124   - imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setAliasName(null)));
125   - // 测试 remark 不匹配
126   - imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setRemark(null)));
127   - // 测试 createTime 不匹配
128   - imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setCreateTime(null)));
129   - // 准备参数
130   - ImGroupMemberPageReqVO reqVO = new ImGroupMemberPageReqVO();
131   - reqVO.setGroupId(null);
132   - reqVO.setUserId(null);
133   - reqVO.setNickname(null);
134   - reqVO.setAvatar(null);
135   - reqVO.setAliasName(null);
136   - reqVO.setRemark(null);
137   - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
138   -
139   - // 调用
140   - PageResult<GroupMemberDO> pageResult = groupMemberService.getGroupMemberPage(reqVO);
141   - // 断言
142   - assertEquals(1, pageResult.getTotal());
143   - assertEquals(1, pageResult.getList().size());
144   - assertPojoEquals(dbGroupMember, pageResult.getList().get(0));
145   - }
146   -
147   -}
148 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxServiceImplTest.java deleted
1   -package cn.iocoder.yudao.module.im.service.inbox;
2   -
3   -import org.junit.jupiter.api.Disabled;
4   -import org.junit.jupiter.api.Test;
5   -
6   -import jakarta.annotation.Resource;
7   -
8   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
9   -
10   -import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.*;
11   -import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
12   -import cn.iocoder.yudao.module.im.dal.mysql.inbox.ImInboxMapper;
13   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
14   -
15   -import org.springframework.context.annotation.Import;
16   -
17   -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
18   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
19   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
20   -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
21   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
22   -import static org.junit.jupiter.api.Assertions.*;
23   -
24   -/**
25   - * {@link ImInboxServiceImpl} 的单元测试类
26   - *
27   - * @author 芋道源码
28   - */
29   -@Import(ImInboxServiceImpl.class)
30   -public class ImInboxServiceImplTest extends BaseDbUnitTest {
31   -
32   - @Resource
33   - private ImInboxServiceImpl inboxService;
34   -
35   - @Resource
36   - private ImInboxMapper imInboxMapper;
37   -
38   - @Test
39   - public void testCreateInbox_success() {
40   - // 准备参数
41   - ImInboxSaveReqVO createReqVO = randomPojo(ImInboxSaveReqVO.class).setId(null);
42   -
43   - // 调用
44   - Long inboxId = inboxService.createInbox(createReqVO);
45   - // 断言
46   - assertNotNull(inboxId);
47   - // 校验记录的属性是否正确
48   - ImInboxDO inbox = imInboxMapper.selectById(inboxId);
49   - assertPojoEquals(createReqVO, inbox, "id");
50   - }
51   -
52   - @Test
53   - public void testUpdateInbox_success() {
54   - // mock 数据
55   - ImInboxDO dbInbox = randomPojo(ImInboxDO.class);
56   - imInboxMapper.insert(dbInbox);// @Sql: 先插入出一条存在的数据
57   - // 准备参数
58   - ImInboxSaveReqVO updateReqVO = randomPojo(ImInboxSaveReqVO.class, o -> {
59   - o.setId(dbInbox.getId()); // 设置更新的 ID
60   - });
61   -
62   - // 调用
63   - inboxService.updateInbox(updateReqVO);
64   - // 校验是否更新正确
65   - ImInboxDO inbox = imInboxMapper.selectById(updateReqVO.getId()); // 获取最新的
66   - assertPojoEquals(updateReqVO, inbox);
67   - }
68   -
69   - @Test
70   - public void testUpdateInbox_notExists() {
71   - // 准备参数
72   - ImInboxSaveReqVO updateReqVO = randomPojo(ImInboxSaveReqVO.class);
73   -
74   - // 调用, 并断言异常
75   - assertServiceException(() -> inboxService.updateInbox(updateReqVO), INBOX_NOT_EXISTS);
76   - }
77   -
78   - @Test
79   - public void testDeleteInbox_success() {
80   - // mock 数据
81   - ImInboxDO dbInbox = randomPojo(ImInboxDO.class);
82   - imInboxMapper.insert(dbInbox);// @Sql: 先插入出一条存在的数据
83   - // 准备参数
84   - Long id = dbInbox.getId();
85   -
86   - // 调用
87   - inboxService.deleteInbox(id);
88   - // 校验数据不存在了
89   - assertNull(imInboxMapper.selectById(id));
90   - }
91   -
92   - @Test
93   - public void testDeleteInbox_notExists() {
94   - // 准备参数
95   - Long id = randomLongId();
96   -
97   - // 调用, 并断言异常
98   - assertServiceException(() -> inboxService.deleteInbox(id), INBOX_NOT_EXISTS);
99   - }
100   -
101   - @Test
102   - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
103   - public void testGetInboxPage() {
104   - // mock 数据
105   - ImInboxDO dbInbox = randomPojo(ImInboxDO.class, o -> { // 等会查询到
106   - o.setUserId(null);
107   - o.setMessageId(null);
108   - o.setSequence(null);
109   - o.setCreateTime(null);
110   - });
111   - imInboxMapper.insert(dbInbox);
112   - // 测试 userId 不匹配
113   - imInboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setUserId(null)));
114   - // 测试 messageId 不匹配
115   - imInboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setMessageId(null)));
116   - // 测试 sequence 不匹配
117   - imInboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setSequence(null)));
118   - // 测试 createTime 不匹配
119   - imInboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setCreateTime(null)));
120   - // 准备参数
121   - ImInboxPageReqVO reqVO = new ImInboxPageReqVO();
122   - reqVO.setUserId(null);
123   - reqVO.setMessageId(null);
124   - reqVO.setSequence(null);
125   - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
126   -
127   - // 调用
128   - PageResult<ImInboxDO> pageResult = inboxService.getInboxPage(reqVO);
129   - // 断言
130   - assertEquals(1, pageResult.getTotal());
131   - assertEquals(1, pageResult.getList().size());
132   - assertPojoEquals(dbInbox, pageResult.getList().get(0));
133   - }
134   -
135   -}
136 0 \ No newline at end of file
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImMessageServiceImplTest.java deleted
1   -package cn.iocoder.yudao.module.im.service.message;
2   -
3   -import org.junit.jupiter.api.Disabled;
4   -import org.junit.jupiter.api.Test;
5   -
6   -import jakarta.annotation.Resource;
7   -
8   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
9   -
10   -import cn.iocoder.yudao.module.im.controller.admin.message.vo.*;
11   -import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
12   -import cn.iocoder.yudao.module.im.dal.mysql.message.ImMessageMapper;
13   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
14   -
15   -import org.springframework.context.annotation.Import;
16   -
17   -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
18   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
19   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
20   -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
21   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
22   -import static org.junit.jupiter.api.Assertions.*;
23   -
24   -/**
25   - * {@link ImMessageServiceImpl} 的单元测试类
26   - *
27   - * @author 芋道源码
28   - */
29   -@Import(ImMessageServiceImpl.class)
30   -public class ImMessageServiceImplTest extends BaseDbUnitTest {
31   -
32   - @Resource
33   - private ImMessageServiceImpl messageService;
34   -
35   - @Resource
36   - private ImMessageMapper imMessageMapper;
37   -
38   - @Test
39   - public void testCreateMessage_success() {
40   - // 准备参数
41   - ImMessageSaveReqVO createReqVO = randomPojo(ImMessageSaveReqVO.class).setId(null);
42   -
43   - // 调用
44   - Long messageId = messageService.createMessage(createReqVO);
45   - // 断言
46   - assertNotNull(messageId);
47   - // 校验记录的属性是否正确
48   - ImMessageDO message = imMessageMapper.selectById(messageId);
49   - assertPojoEquals(createReqVO, message, "id");
50   - }
51   -
52   - @Test
53   - public void testUpdateMessage_success() {
54   - // mock 数据
55   - ImMessageDO dbMessage = randomPojo(ImMessageDO.class);
56   - imMessageMapper.insert(dbMessage);// @Sql: 先插入出一条存在的数据
57   - // 准备参数
58   - ImMessageSaveReqVO updateReqVO = randomPojo(ImMessageSaveReqVO.class, o -> {
59   - o.setId(dbMessage.getId()); // 设置更新的 ID
60   - });
61   -
62   - // 调用
63   - messageService.updateMessage(updateReqVO);
64   - // 校验是否更新正确
65   - ImMessageDO message = imMessageMapper.selectById(updateReqVO.getId()); // 获取最新的
66   - assertPojoEquals(updateReqVO, message);
67   - }
68   -
69   - @Test
70   - public void testUpdateMessage_notExists() {
71   - // 准备参数
72   - ImMessageSaveReqVO updateReqVO = randomPojo(ImMessageSaveReqVO.class);
73   -
74   - // 调用, 并断言异常
75   - assertServiceException(() -> messageService.updateMessage(updateReqVO), MESSAGE_NOT_EXISTS);
76   - }
77   -
78   - @Test
79   - public void testDeleteMessage_success() {
80   - // mock 数据
81   - ImMessageDO dbMessage = randomPojo(ImMessageDO.class);
82   - imMessageMapper.insert(dbMessage);// @Sql: 先插入出一条存在的数据
83   - // 准备参数
84   - Long id = dbMessage.getId();
85   -
86   - // 调用
87   - messageService.deleteMessage(id);
88   - // 校验数据不存在了
89   - assertNull(imMessageMapper.selectById(id));
90   - }
91   -
92   - @Test
93   - public void testDeleteMessage_notExists() {
94   - // 准备参数
95   - Long id = randomLongId();
96   -
97   - // 调用, 并断言异常
98   - assertServiceException(() -> messageService.deleteMessage(id), MESSAGE_NOT_EXISTS);
99   - }
100   -
101   - @Test
102   - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
103   - public void testGetMessagePage() {
104   - // mock 数据
105   - ImMessageDO dbMessage = randomPojo(ImMessageDO.class, o -> { // 等会查询到
106   - o.setClientMessageId(null);
107   - o.setSenderId(null);
108   - o.setReceiverId(null);
109   - o.setSenderNickname(null);
110   - o.setSenderAvatar(null);
111   - o.setConversationType(null);
112   - o.setConversationNo(null);
113   - o.setContentType(null);
114   - o.setContent(null);
115   - o.setSendTime(null);
116   - o.setSendFrom(null);
117   - o.setCreateTime(null);
118   - });
119   - imMessageMapper.insert(dbMessage);
120   - // 测试 clientMessageId 不匹配
121   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setClientMessageId(null)));
122   - // 测试 senderId 不匹配
123   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSenderId(null)));
124   - // 测试 receiverId 不匹配
125   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setReceiverId(null)));
126   - // 测试 senderNickname 不匹配
127   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSenderNickname(null)));
128   - // 测试 senderAvatar 不匹配
129   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSenderAvatar(null)));
130   - // 测试 conversationType 不匹配
131   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setConversationType(null)));
132   - // 测试 conversationNo 不匹配
133   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setConversationNo(null)));
134   - // 测试 contentType 不匹配
135   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setContentType(null)));
136   - // 测试 content 不匹配
137   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setContent(null)));
138   - // 测试 sendTime 不匹配
139   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSendTime(null)));
140   - // 测试 sendFrom 不匹配
141   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSendFrom(null)));
142   - // 测试 createTime 不匹配
143   - imMessageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setCreateTime(null)));
144   - // 准备参数
145   - ImMessagePageReqVO reqVO = new ImMessagePageReqVO();
146   - reqVO.setClientMessageId(null);
147   - reqVO.setSenderId(null);
148   - reqVO.setReceiverId(null);
149   - reqVO.setSenderNickname(null);
150   - reqVO.setSenderAvatar(null);
151   - reqVO.setConversationType(null);
152   - reqVO.setConversationNo(null);
153   - reqVO.setContentType(null);
154   - reqVO.setContent(null);
155   - reqVO.setSendTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
156   - reqVO.setSendFrom(null);
157   - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
158   -
159   - // 调用
160   - PageResult<ImMessageDO> pageResult = messageService.getMessagePage(reqVO);
161   - // 断言
162   - assertEquals(1, pageResult.getTotal());
163   - assertEquals(1, pageResult.getList().size());
164   - assertPojoEquals(dbMessage, pageResult.getList().get(0));
165   - }
166   -
167   -}
168 0 \ No newline at end of file