Commit bb0b7056cf0eda43444f737fa047415c928bc247

Authored by 安浩浩
1 parent dda09098

新建:im 模块

Showing 57 changed files with 2423 additions and 2 deletions
... ... @@ -15,7 +15,9 @@
15 15 <!-- 各种 module 拓展 -->
16 16 <module>yudao-module-system</module>
17 17 <module>yudao-module-infra</module>
18   -<!-- <module>yudao-module-member</module>-->
  18 + <module>yudao-module-im</module>
  19 + <module>yudao-module-im/yudao-module-im-api</module>
  20 + <!-- <module>yudao-module-member</module>-->
19 21 <!-- <module>yudao-module-bpm</module>-->
20 22 <!-- <module>yudao-module-report</module>-->
21 23 <!-- <module>yudao-module-mp</module>-->
... ... @@ -23,6 +25,7 @@
23 25 <!-- <module>yudao-module-mall</module>-->
24 26 <!-- <module>yudao-module-crm</module>-->
25 27 <!-- <module>yudao-module-erp</module>-->
  28 + <module>yudao-module-im</module>
26 29 <!-- 示例项目 -->
27 30 <!-- <module>yudao-example</module>-->
28 31 </modules>
... ...
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/handler/JsonWebSocketMessageHandler.java
... ... @@ -76,7 +76,7 @@ public class JsonWebSocketMessageHandler extends TextWebSocketHandler {
76 76 Long tenantId = WebSocketFrameworkUtils.getTenantId(session);
77 77 TenantUtils.execute(tenantId, () -> messageListener.onMessage(session, messageObj));
78 78 } catch (Throwable ex) {
79   - log.error("[handleTextMessage][session({}) message({}) 处理异常]", session.getId(), message.getPayload());
  79 + log.error("[handleTextMessage][session({}) message({}) 处理异常]", session.getId(), message.getPayload(), ex);
80 80 }
81 81 }
82 82  
... ...
yudao-module-im/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <groupId>cn.iocoder.boot</groupId>
  7 + <artifactId>yudao</artifactId>
  8 + <version>${revision}</version>
  9 + </parent>
  10 + <modules>
  11 + <module>yudao-module-im-api</module>
  12 + <module>yudao-module-im-biz</module>
  13 + </modules>
  14 + <modelVersion>4.0.0</modelVersion>
  15 + <artifactId>yudao-module-im</artifactId>
  16 + <packaging>pom</packaging>
  17 +
  18 + <name>${project.artifactId}</name>
  19 + <description>
  20 + im 模块,主要提供能力:
  21 + 1. 通讯能力,例如:消息发送、消息接收、消息撤回、消息已读等。
  22 + 2. 通讯会话,例如:单聊、群聊、聊天室等。
  23 + 3. 通讯消息,例如:文本、图片、语音、视频、文件等。
  24 + 4. 通讯消息的存储,例如:消息存储、消息索引、消息搜索等。
  25 + 5. 通讯消息的推送,例如:消息推送、消息通知等。
  26 + 6. 通讯消息的安全,例如:消息加密、消息签名等。
  27 + </description>
  28 +
  29 +</project>
0 30 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-api/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <groupId>cn.iocoder.boot</groupId>
  7 + <artifactId>yudao-module-im</artifactId>
  8 + <version>${revision}</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <artifactId>yudao-module-im-api</artifactId>
  12 + <packaging>jar</packaging>
  13 +
  14 + <name>${project.artifactId}</name>
  15 + <description>
  16 + im 模块 API,暴露给其它模块调用
  17 + </description>
  18 +
  19 + <dependencies>
  20 + <dependency>
  21 + <groupId>cn.iocoder.boot</groupId>
  22 + <artifactId>yudao-common</artifactId>
  23 + </dependency>
  24 + </dependencies>
  25 +
  26 +</project>
0 27 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/api/package-info.java 0 → 100644
  1 +/**
  2 + * @author anhaohao
  3 + * @date 2024/3/9 下午8:59
  4 + */
  5 +package cn.iocoder.yudao.module.im.api;
0 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 0 → 100644
  1 +package cn.iocoder.yudao.module.im.enums;
  2 +
  3 +import cn.iocoder.yudao.framework.common.exception.ErrorCode;
  4 +
  5 +/**
  6 + * IM 错误码枚举类
  7 + * <p>
  8 + * im 系统,使用 1-040-000-000 段
  9 + */
  10 +public interface ErrorCodeConstants {
  11 +
  12 + // ========== 会话 (1-040-100-000) ==========
  13 + ErrorCode CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_100_000, "会话不存在");
  14 +
  15 + // ========== 收件箱 (1-040-200-000) ==========
  16 + ErrorCode INBOX_NOT_EXISTS = new ErrorCode(1_040_200_000, "收件箱不存在");
  17 +
  18 + // ========== 消息 (1-040-300-000) ==========
  19 + ErrorCode MESSAGE_NOT_EXISTS = new ErrorCode(1_040_300_000, "消息不存在");
  20 +}
... ...
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/conversation/ImConversationTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.enums.conversation;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * IM 会话的类型枚举
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum ImConversationTypeEnum {
  14 +
  15 + PRIVATE(1, "单聊"),
  16 + GROUP(2, "群聊");
  17 +
  18 + /**
  19 + * 类型
  20 + */
  21 + private final Integer type;
  22 + /**
  23 + * 名字
  24 + */
  25 + private final String name;
  26 +
  27 +}
0 28 \ 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 0 → 100644
  1 +package cn.iocoder.yudao.module.im.enums.message;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * IM 消息的类型枚举
  8 + *
  9 + * 参考 <a href="https://doc.yunxin.163.com/messaging/docs/zg3NzA3NTA?platform=web#消息类型">“消息类型”</a> 文档
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Getter
  14 +@AllArgsConstructor
  15 +public enum ImMessageTypeEnum {
  16 +
  17 + TEXT(1, "文本"), // 消息内容为普通文本
  18 + IMAGE(2, "图片"), // 消息内容为图片 URL 地址、尺寸、图片大小等信息
  19 + AUDIO(3, "语音"), // 消息内容为语音文件的 URL 地址、时长、大小、格式等信息
  20 + VIDEO(4, "视频"), // 消息内容为视频文件的 URL 地址、时长、大小、格式等信息
  21 + FILE(5, "文件"), // 消息内容为文件的 URL 地址、大小、格式等信息
  22 + LOCATION(6, "地理位置"), // 消息内容为地理位置标题、经度、纬度信息
  23 + // TODO @芋艿:下面两种,貌似企业微信设计的更好:https://developer.work.weixin.qq.com/document/path/90240
  24 + TIP(7, "提示"), // 又叫做 Tip 消息,没有推送和通知栏提醒,主要用于会话内的通知提醒,例如进入会话时出现的欢迎消息,或是会话过程中命中敏感词后的提示消息等场景
  25 + NOTIFICATION(8, "通知"), // 主要用于群组、聊天室和超大群的事件通知,由服务端下发,客户端无法发送事件通知消息。通知类消息有在线、离线、漫游机制;没有通知栏提醒
  26 + ;
  27 +
  28 + /**
  29 + * 类型
  30 + */
  31 + private final Integer type;
  32 + /**
  33 + * 名字
  34 + */
  35 + private final String name;
  36 +
  37 +}
0 38 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/package-info.java 0 → 100644
  1 +/**
  2 + * @author anhaohao
  3 + * @date 2024/3/9 下午8:59
  4 + */
  5 +package cn.iocoder.yudao.module.im.enums;
0 6 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <artifactId>yudao-module-im</artifactId>
  7 + <groupId>cn.iocoder.boot</groupId>
  8 + <version>${revision}</version> <!-- 1. 修改 version 为 ${revision} -->
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <packaging>jar</packaging> <!-- 2. 新增 packaging 为 jar -->
  12 +
  13 + <artifactId>yudao-module-im-biz</artifactId>
  14 +
  15 + <name>${project.artifactId}</name> <!-- 3. 新增 name 为 ${project.artifactId} -->
  16 + <description> <!-- 4. 新增 description 为该模块的描述 -->
  17 + im 模块,主要实现 im 模块的业务逻辑。
  18 + </description>
  19 +
  20 + <dependencies> <!-- 5. 新增依赖,这里引入的都是比较常用的业务组件、技术组件 -->
  21 + <dependency>
  22 + <groupId>cn.iocoder.boot</groupId>
  23 + <artifactId>yudao-module-im-api</artifactId>
  24 + <version>${revision}</version>
  25 + </dependency>
  26 +
  27 + <!-- 业务组件 -->
  28 + <dependency>
  29 + <groupId>cn.iocoder.boot</groupId>
  30 + <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
  31 + </dependency>
  32 +
  33 + <!-- Web 相关 -->
  34 + <dependency>
  35 + <groupId>cn.iocoder.boot</groupId>
  36 + <artifactId>yudao-spring-boot-starter-web</artifactId>
  37 + </dependency>
  38 +
  39 + <dependency>
  40 + <groupId>cn.iocoder.boot</groupId>
  41 + <artifactId>yudao-spring-boot-starter-security</artifactId>
  42 + </dependency>
  43 +
  44 + <!-- DB 相关 -->
  45 + <dependency>
  46 + <groupId>cn.iocoder.boot</groupId>
  47 + <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
  48 + </dependency>
  49 +
  50 + <!-- Test 测试相关 -->
  51 + <dependency>
  52 + <groupId>cn.iocoder.boot</groupId>
  53 + <artifactId>yudao-spring-boot-starter-test</artifactId>
  54 + </dependency>
  55 + <dependency>
  56 + <groupId>cn.iocoder.boot</groupId>
  57 + <artifactId>yudao-spring-boot-starter-excel</artifactId>
  58 + </dependency>
  59 + <dependency>
  60 + <groupId>cn.iocoder.boot</groupId>
  61 + <artifactId>yudao-spring-boot-starter-websocket</artifactId>
  62 + </dependency>
  63 + </dependencies>
  64 +
  65 +</project>
0 66 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/ConversationController.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.conversation;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  8 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  9 +import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationPageReqVO;
  10 +import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationRespVO;
  11 +import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationSaveReqVO;
  12 +import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
  13 +import cn.iocoder.yudao.module.im.service.conversation.ConversationService;
  14 +import io.swagger.v3.oas.annotations.Operation;
  15 +import io.swagger.v3.oas.annotations.Parameter;
  16 +import io.swagger.v3.oas.annotations.tags.Tag;
  17 +import jakarta.annotation.Resource;
  18 +import jakarta.servlet.http.HttpServletResponse;
  19 +import jakarta.validation.Valid;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.bind.annotation.*;
  23 +
  24 +import java.io.IOException;
  25 +import java.util.List;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
  29 +
  30 +@Tag(name = "管理后台 - 会话")
  31 +@RestController
  32 +@RequestMapping("/im/conversation")
  33 +@Validated
  34 +public class ConversationController {
  35 +
  36 + @Resource
  37 + private ConversationService conversationService;
  38 +
  39 + @PostMapping("/create")
  40 + @Operation(summary = "创建会话")
  41 + @PreAuthorize("@ss.hasPermission('im:conversation:create')")
  42 + public CommonResult<Long> createConversation(@Valid @RequestBody ConversationSaveReqVO createReqVO) {
  43 + return success(conversationService.createConversation(createReqVO));
  44 + }
  45 +
  46 + @PutMapping("/update")
  47 + @Operation(summary = "更新会话")
  48 + @PreAuthorize("@ss.hasPermission('im:conversation:update')")
  49 + public CommonResult<Boolean> updateConversation(@Valid @RequestBody ConversationSaveReqVO updateReqVO) {
  50 + conversationService.updateConversation(updateReqVO);
  51 + return success(true);
  52 + }
  53 +
  54 + @DeleteMapping("/delete")
  55 + @Operation(summary = "删除会话")
  56 + @Parameter(name = "id", description = "编号", required = true)
  57 + @PreAuthorize("@ss.hasPermission('im:conversation:delete')")
  58 + public CommonResult<Boolean> deleteConversation(@RequestParam("id") Long id) {
  59 + conversationService.deleteConversation(id);
  60 + return success(true);
  61 + }
  62 +
  63 + @GetMapping("/get")
  64 + @Operation(summary = "获得会话")
  65 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  66 + @PreAuthorize("@ss.hasPermission('im:conversation:query')")
  67 + public CommonResult<ConversationRespVO> getConversation(@RequestParam("id") Long id) {
  68 + ConversationDO conversation = conversationService.getConversation(id);
  69 + return success(BeanUtils.toBean(conversation, ConversationRespVO.class));
  70 + }
  71 +
  72 + @GetMapping("/page")
  73 + @Operation(summary = "获得会话分页")
  74 + @PreAuthorize("@ss.hasPermission('im:conversation:query')")
  75 + public CommonResult<PageResult<ConversationRespVO>> getConversationPage(@Valid ConversationPageReqVO pageReqVO) {
  76 + PageResult<ConversationDO> pageResult = conversationService.getConversationPage(pageReqVO);
  77 + return success(BeanUtils.toBean(pageResult, ConversationRespVO.class));
  78 + }
  79 +
  80 + @GetMapping("/export-excel")
  81 + @Operation(summary = "导出会话 Excel")
  82 + @PreAuthorize("@ss.hasPermission('im:conversation:export')")
  83 + @OperateLog(type = EXPORT)
  84 + public void exportConversationExcel(@Valid ConversationPageReqVO pageReqVO,
  85 + HttpServletResponse response) throws IOException {
  86 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  87 + List<ConversationDO> list = conversationService.getConversationPage(pageReqVO).getList();
  88 + // 导出 Excel
  89 + ExcelUtils.write(response, "会话.xls", "数据", ConversationRespVO.class,
  90 + BeanUtils.toBean(list, ConversationRespVO.class));
  91 + }
  92 +
  93 +}
0 94 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/vo/ConversationPageReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.conversation.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 会话分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class ConversationPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "所属用户", example = "11545")
  21 + private Long userId;
  22 +
  23 + @Schema(description = "类型:1 单聊;2 群聊;4 通知会话(预留)", example = "1")
  24 + private Boolean conversationType;
  25 +
  26 + @Schema(description = "单聊时,用户编号;群聊时,群编号", example = "21454")
  27 + private String targetId;
  28 +
  29 + @Schema(description = "会话标志 单聊:s_{userId}_{targetId},需要排序 userId 和 targetId 群聊:g_groupId")
  30 + private String no;
  31 +
  32 + @Schema(description = "是否置顶 0否 1是")
  33 + private Boolean pinned;
  34 +
  35 + @Schema(description = "最后已读时间")
  36 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  37 + private LocalDateTime[] lastReadTime;
  38 +
  39 + @Schema(description = "创建时间")
  40 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  41 + private LocalDateTime[] createTime;
  42 +
  43 +}
0 44 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/vo/ConversationRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.conversation.vo;
  2 +
  3 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  4 +import com.alibaba.excel.annotation.ExcelProperty;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.Data;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - 会话 Response VO")
  11 +@Data
  12 +@ExcelIgnoreUnannotated
  13 +public class ConversationRespVO {
  14 +
  15 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13905")
  16 + @ExcelProperty("编号")
  17 + private Long id;
  18 +
  19 + @Schema(description = "所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "11545")
  20 + @ExcelProperty("所属用户")
  21 + private Long userId;
  22 +
  23 + @Schema(description = "类型:1 单聊;2 群聊;4 通知会话(预留)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  24 + @ExcelProperty("类型:1 单聊;2 群聊;4 通知会话(预留)")
  25 + private Boolean conversationType;
  26 +
  27 + @Schema(description = "单聊时,用户编号;群聊时,群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21454")
  28 + @ExcelProperty("单聊时,用户编号;群聊时,群编号")
  29 + private String targetId;
  30 +
  31 + @Schema(description = "会话标志 单聊:s_{userId}_{targetId},需要排序 userId 和 targetId 群聊:g_groupId", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + @ExcelProperty("会话标志 单聊:s_{userId}_{targetId},需要排序 userId 和 targetId 群聊:g_groupId")
  33 + private String no;
  34 +
  35 + @Schema(description = "是否置顶 0否 1是", requiredMode = Schema.RequiredMode.REQUIRED)
  36 + @ExcelProperty("是否置顶 0否 1是")
  37 + private Boolean pinned;
  38 +
  39 + @Schema(description = "最后已读时间")
  40 + @ExcelProperty("最后已读时间")
  41 + private LocalDateTime lastReadTime;
  42 +
  43 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  44 + @ExcelProperty("创建时间")
  45 + private LocalDateTime createTime;
  46 +
  47 +}
0 48 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/vo/ConversationSaveReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.conversation.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - 会话新增/修改 Request VO")
  11 +@Data
  12 +public class ConversationSaveReqVO {
  13 +
  14 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13905")
  15 + private Long id;
  16 +
  17 + @Schema(description = "所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "11545")
  18 + @NotNull(message = "所属用户不能为空")
  19 + private Long userId;
  20 +
  21 + @Schema(description = "类型:1 单聊;2 群聊;4 通知会话(预留)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  22 + @NotNull(message = "类型:1 单聊;2 群聊;4 通知会话(预留)不能为空")
  23 + private Boolean conversationType;
  24 +
  25 + @Schema(description = "单聊时,用户编号;群聊时,群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21454")
  26 + @NotEmpty(message = "单聊时,用户编号;群聊时,群编号不能为空")
  27 + private String targetId;
  28 +
  29 + @Schema(description = "会话标志 单聊:s_{userId}_{targetId},需要排序 userId 和 targetId 群聊:g_groupId", requiredMode = Schema.RequiredMode.REQUIRED)
  30 + @NotEmpty(message = "会话标志 单聊:s_{userId}_{targetId},需要排序 userId 和 targetId 群聊:g_groupId不能为空")
  31 + private String no;
  32 +
  33 + @Schema(description = "是否置顶 0否 1是", requiredMode = Schema.RequiredMode.REQUIRED)
  34 + @NotNull(message = "是否置顶 0否 1是不能为空")
  35 + private Boolean pinned;
  36 +
  37 + @Schema(description = "最后已读时间")
  38 + private LocalDateTime lastReadTime;
  39 +
  40 +}
0 41 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/inbox/InboxController.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.inbox;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  8 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  9 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxPageReqVO;
  10 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxRespVO;
  11 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveReqVO;
  12 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
  13 +import cn.iocoder.yudao.module.im.service.inbox.InboxService;
  14 +import io.swagger.v3.oas.annotations.Operation;
  15 +import io.swagger.v3.oas.annotations.Parameter;
  16 +import io.swagger.v3.oas.annotations.tags.Tag;
  17 +import jakarta.annotation.Resource;
  18 +import jakarta.servlet.http.HttpServletResponse;
  19 +import jakarta.validation.Valid;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.bind.annotation.*;
  23 +
  24 +import java.io.IOException;
  25 +import java.util.List;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
  29 +
  30 +@Tag(name = "管理后台 - 收件箱")
  31 +@RestController
  32 +@RequestMapping("/im/inbox")
  33 +@Validated
  34 +public class InboxController {
  35 +
  36 + @Resource
  37 + private InboxService inboxService;
  38 +
  39 + @PostMapping("/create")
  40 + @Operation(summary = "创建收件箱")
  41 + @PreAuthorize("@ss.hasPermission('im:inbox:create')")
  42 + public CommonResult<Long> createInbox(@Valid @RequestBody InboxSaveReqVO createReqVO) {
  43 + return success(inboxService.createInbox(createReqVO));
  44 + }
  45 +
  46 + @PutMapping("/update")
  47 + @Operation(summary = "更新收件箱")
  48 + @PreAuthorize("@ss.hasPermission('im:inbox:update')")
  49 + public CommonResult<Boolean> updateInbox(@Valid @RequestBody InboxSaveReqVO updateReqVO) {
  50 + inboxService.updateInbox(updateReqVO);
  51 + return success(true);
  52 + }
  53 +
  54 + @DeleteMapping("/delete")
  55 + @Operation(summary = "删除收件箱")
  56 + @Parameter(name = "id", description = "编号", required = true)
  57 + @PreAuthorize("@ss.hasPermission('im:inbox:delete')")
  58 + public CommonResult<Boolean> deleteInbox(@RequestParam("id") Long id) {
  59 + inboxService.deleteInbox(id);
  60 + return success(true);
  61 + }
  62 +
  63 + @GetMapping("/get")
  64 + @Operation(summary = "获得收件箱")
  65 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  66 + @PreAuthorize("@ss.hasPermission('im:inbox:query')")
  67 + public CommonResult<InboxRespVO> getInbox(@RequestParam("id") Long id) {
  68 + InboxDO inbox = inboxService.getInbox(id);
  69 + return success(BeanUtils.toBean(inbox, InboxRespVO.class));
  70 + }
  71 +
  72 + @GetMapping("/page")
  73 + @Operation(summary = "获得收件箱分页")
  74 + @PreAuthorize("@ss.hasPermission('im:inbox:query')")
  75 + public CommonResult<PageResult<InboxRespVO>> getInboxPage(@Valid InboxPageReqVO pageReqVO) {
  76 + PageResult<InboxDO> pageResult = inboxService.getInboxPage(pageReqVO);
  77 + return success(BeanUtils.toBean(pageResult, InboxRespVO.class));
  78 + }
  79 +
  80 + @GetMapping("/export-excel")
  81 + @Operation(summary = "导出收件箱 Excel")
  82 + @PreAuthorize("@ss.hasPermission('im:inbox:export')")
  83 + @OperateLog(type = EXPORT)
  84 + public void exportInboxExcel(@Valid InboxPageReqVO pageReqVO,
  85 + HttpServletResponse response) throws IOException {
  86 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  87 + List<InboxDO> list = inboxService.getInboxPage(pageReqVO).getList();
  88 + // 导出 Excel
  89 + ExcelUtils.write(response, "收件箱.xls", "数据", InboxRespVO.class,
  90 + BeanUtils.toBean(list, InboxRespVO.class));
  91 + }
  92 +
  93 +}
0 94 \ 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/InboxPageReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 收件箱分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class InboxPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "用户编号", example = "3979")
  21 + private Long userId;
  22 +
  23 + @Schema(description = "消息编号", example = "12454")
  24 + private Long messageId;
  25 +
  26 + @Schema(description = "序号,按照 user 递增")
  27 + private Long sequence;
  28 +
  29 + @Schema(description = "创建时间")
  30 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  31 + private LocalDateTime[] createTime;
  32 +
  33 +}
0 34 \ 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/InboxRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
  2 +
  3 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  4 +import com.alibaba.excel.annotation.ExcelProperty;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.Data;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - 收件箱 Response VO")
  11 +@Data
  12 +@ExcelIgnoreUnannotated
  13 +public class InboxRespVO {
  14 +
  15 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18389")
  16 + @ExcelProperty("编号")
  17 + private Long id;
  18 +
  19 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3979")
  20 + @ExcelProperty("用户编号")
  21 + private Long userId;
  22 +
  23 + @Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
  24 + @ExcelProperty("消息编号")
  25 + private Long messageId;
  26 +
  27 + @Schema(description = "序号,按照 user 递增", requiredMode = Schema.RequiredMode.REQUIRED)
  28 + @ExcelProperty("序号,按照 user 递增")
  29 + private Long sequence;
  30 +
  31 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + @ExcelProperty("创建时间")
  33 + private LocalDateTime createTime;
  34 +
  35 +}
0 36 \ 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/InboxSaveReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - 收件箱新增/修改 Request VO")
  8 +@Data
  9 +public class InboxSaveReqVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18389")
  12 + private Long id;
  13 +
  14 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3979")
  15 + @NotNull(message = "用户编号不能为空")
  16 + private Long userId;
  17 +
  18 + @Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
  19 + @NotNull(message = "消息编号不能为空")
  20 + private Long messageId;
  21 +
  22 + @Schema(description = "序号,按照 user 递增", requiredMode = Schema.RequiredMode.REQUIRED)
  23 + @NotNull(message = "序号,按照 user 递增不能为空")
  24 + private Long sequence;
  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/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.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  8 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  9 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessagePageReqVO;
  10 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessageRespVO;
  11 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessageSaveReqVO;
  12 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  13 +import cn.iocoder.yudao.module.im.service.message.MessageService;
  14 +import io.swagger.v3.oas.annotations.Operation;
  15 +import io.swagger.v3.oas.annotations.Parameter;
  16 +import io.swagger.v3.oas.annotations.tags.Tag;
  17 +import jakarta.annotation.Resource;
  18 +import jakarta.servlet.http.HttpServletResponse;
  19 +import jakarta.validation.Valid;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.bind.annotation.*;
  23 +
  24 +import java.io.IOException;
  25 +import java.util.List;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
  29 +
  30 +@Tag(name = "管理后台 - 消息")
  31 +@RestController
  32 +@RequestMapping("/im/message")
  33 +@Validated
  34 +public class MessageController {
  35 +
  36 + @Resource
  37 + private MessageService messageService;
  38 +
  39 + @PostMapping("/create")
  40 + @Operation(summary = "创建消息")
  41 + @PreAuthorize("@ss.hasPermission('im:message:create')")
  42 + public CommonResult<Long> createMessage(@Valid @RequestBody MessageSaveReqVO createReqVO) {
  43 + return success(messageService.createMessage(createReqVO));
  44 + }
  45 +
  46 + @PutMapping("/update")
  47 + @Operation(summary = "更新消息")
  48 + @PreAuthorize("@ss.hasPermission('im:message:update')")
  49 + public CommonResult<Boolean> updateMessage(@Valid @RequestBody MessageSaveReqVO updateReqVO) {
  50 + messageService.updateMessage(updateReqVO);
  51 + return success(true);
  52 + }
  53 +
  54 + @DeleteMapping("/delete")
  55 + @Operation(summary = "删除消息")
  56 + @Parameter(name = "id", description = "编号", required = true)
  57 + @PreAuthorize("@ss.hasPermission('im:message:delete')")
  58 + public CommonResult<Boolean> deleteMessage(@RequestParam("id") Long id) {
  59 + messageService.deleteMessage(id);
  60 + return success(true);
  61 + }
  62 +
  63 + @GetMapping("/get")
  64 + @Operation(summary = "获得消息")
  65 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  66 + @PreAuthorize("@ss.hasPermission('im:message:query')")
  67 + public CommonResult<MessageRespVO> getMessage(@RequestParam("id") Long id) {
  68 + MessageDO message = messageService.getMessage(id);
  69 + return success(BeanUtils.toBean(message, MessageRespVO.class));
  70 + }
  71 +
  72 + @GetMapping("/page")
  73 + @Operation(summary = "获得消息分页")
  74 + @PreAuthorize("@ss.hasPermission('im:message:query')")
  75 + public CommonResult<PageResult<MessageRespVO>> getMessagePage(@Valid MessagePageReqVO pageReqVO) {
  76 + PageResult<MessageDO> pageResult = messageService.getMessagePage(pageReqVO);
  77 + return success(BeanUtils.toBean(pageResult, MessageRespVO.class));
  78 + }
  79 +
  80 + @GetMapping("/export-excel")
  81 + @Operation(summary = "导出消息 Excel")
  82 + @PreAuthorize("@ss.hasPermission('im:message:export')")
  83 + @OperateLog(type = EXPORT)
  84 + public void exportMessageExcel(@Valid MessagePageReqVO pageReqVO,
  85 + HttpServletResponse response) throws IOException {
  86 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  87 + List<MessageDO> list = messageService.getMessagePage(pageReqVO).getList();
  88 + // 导出 Excel
  89 + ExcelUtils.write(response, "消息.xls", "数据", MessageRespVO.class,
  90 + BeanUtils.toBean(list, MessageRespVO.class));
  91 + }
  92 +
  93 + @PostMapping("/send")
  94 + @Operation(summary = "发送私聊消息")
  95 + public CommonResult<Long> sendMessage(@Valid @RequestBody MessageSaveReqVO messageSaveReqVO) {
  96 + return success(messageService.sendPrivateMessage(messageSaveReqVO));
  97 + }
  98 +
  99 +}
0 100 \ 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 cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 消息分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class MessagePageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "客户端消息编号 uuid,用于排重", example = "3331")
  21 + private String clientMessageId;
  22 +
  23 + @Schema(description = "发送人编号", example = "23239")
  24 + private Long senderId;
  25 +
  26 + @Schema(description = "接收人编号", example = "32494")
  27 + private Long receiverId;
  28 +
  29 + @Schema(description = "发送人昵称", example = "李四")
  30 + private String senderNickname;
  31 +
  32 + @Schema(description = "发送人头像")
  33 + private String senderAvatar;
  34 +
  35 + @Schema(description = "会话类型", example = "2")
  36 + private Boolean conversationType;
  37 +
  38 + @Schema(description = "会话标志")
  39 + private String conversationNo;
  40 +
  41 + @Schema(description = "消息类型", example = "1")
  42 + private Boolean contentType;
  43 +
  44 + @Schema(description = "消息内容")
  45 + private String content;
  46 +
  47 + @Schema(description = "发送时间")
  48 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  49 + private LocalDateTime[] sendTime;
  50 +
  51 + @Schema(description = "消息来源 100-用户发送;200-系统发送(一般是通知);")
  52 + private Boolean sendFrom;
  53 +
  54 + @Schema(description = "创建时间")
  55 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  56 + private LocalDateTime[] createTime;
  57 +
  58 +}
0 59 \ 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/MessageRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.controller.admin.message.vo;
  2 +
  3 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  4 +import com.alibaba.excel.annotation.ExcelProperty;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.Data;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - 消息 Response VO")
  11 +@Data
  12 +@ExcelIgnoreUnannotated
  13 +public class MessageRespVO {
  14 +
  15 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30713")
  16 + @ExcelProperty("编号")
  17 + private Long id;
  18 +
  19 + @Schema(description = "客户端消息编号 uuid,用于排重", requiredMode = Schema.RequiredMode.REQUIRED, example = "3331")
  20 + @ExcelProperty("客户端消息编号 uuid,用于排重")
  21 + private String clientMessageId;
  22 +
  23 + @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23239")
  24 + @ExcelProperty("发送人编号")
  25 + private Long senderId;
  26 +
  27 + @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
  28 + @ExcelProperty("接收人编号")
  29 + private Long receiverId;
  30 +
  31 + @Schema(description = "发送人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  32 + @ExcelProperty("发送人昵称")
  33 + private String senderNickname;
  34 +
  35 + @Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
  36 + @ExcelProperty("发送人头像")
  37 + private String senderAvatar;
  38 +
  39 + @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  40 + @ExcelProperty("会话类型")
  41 + private Boolean conversationType;
  42 +
  43 + @Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED)
  44 + @ExcelProperty("会话标志")
  45 + private String conversationNo;
  46 +
  47 + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  48 + @ExcelProperty("消息类型")
  49 + private Boolean contentType;
  50 +
  51 + @Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
  52 + @ExcelProperty("消息内容")
  53 + private String content;
  54 +
  55 + @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
  56 + @ExcelProperty("发送时间")
  57 + private LocalDateTime sendTime;
  58 +
  59 + @Schema(description = "消息来源 100-用户发送;200-系统发送(一般是通知);", requiredMode = Schema.RequiredMode.REQUIRED)
  60 + @ExcelProperty("消息来源 100-用户发送;200-系统发送(一般是通知);")
  61 + private Boolean sendFrom;
  62 +
  63 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  64 + @ExcelProperty("创建时间")
  65 + private LocalDateTime createTime;
  66 +
  67 +}
0 68 \ 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/MessageSaveReqVO.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 jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - 消息新增/修改 Request VO")
  11 +@Data
  12 +public class MessageSaveReqVO {
  13 +
  14 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30713")
  15 + private Long id;
  16 +
  17 + @Schema(description = "客户端消息编号 uuid,用于排重", requiredMode = Schema.RequiredMode.REQUIRED, example = "3331")
  18 + @NotEmpty(message = "客户端消息编号 uuid,用于排重不能为空")
  19 + private String clientMessageId;
  20 +
  21 + @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23239")
  22 + @NotNull(message = "发送人编号不能为空")
  23 + private Long senderId;
  24 +
  25 + @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
  26 + @NotNull(message = "接收人编号不能为空")
  27 + private Long receiverId;
  28 +
  29 + @Schema(description = "发送人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  30 + @NotEmpty(message = "发送人昵称不能为空")
  31 + private String senderNickname;
  32 +
  33 + @Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
  34 + @NotEmpty(message = "发送人头像不能为空")
  35 + private String senderAvatar;
  36 +
  37 + @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  38 + @NotNull(message = "会话类型不能为空")
  39 + private Boolean conversationType;
  40 +
  41 + @Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED)
  42 + @NotEmpty(message = "会话标志不能为空")
  43 + private String conversationNo;
  44 +
  45 + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  46 + @NotNull(message = "消息类型不能为空")
  47 + private Boolean contentType;
  48 +
  49 + @Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
  50 + @NotEmpty(message = "消息内容不能为空")
  51 + private String content;
  52 +
  53 + @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
  54 + @NotNull(message = "发送时间不能为空")
  55 + private LocalDateTime sendTime;
  56 +
  57 + @Schema(description = "消息来源 100-用户发送;200-系统发送(一般是通知);", requiredMode = Schema.RequiredMode.REQUIRED)
  58 + @NotNull(message = "消息来源 100-用户发送;200-系统发送(一般是通知);不能为空")
  59 + private Boolean sendFrom;
  60 +
  61 +}
0 62 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/conversation/ConversationDO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.conversation;
  2 +
  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.*;
  8 +
  9 +import java.time.LocalDateTime;
  10 +
  11 +/**
  12 + * 会话 DO
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@TableName("im_conversation")
  17 +@KeySequence("im_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@ToString(callSuper = true)
  21 +@Builder
  22 +@NoArgsConstructor
  23 +@AllArgsConstructor
  24 +public class ConversationDO extends BaseDO {
  25 +
  26 + /**
  27 + * 编号
  28 + */
  29 + @TableId
  30 + private Long id;
  31 + /**
  32 + * 所属用户
  33 + */
  34 + private Long userId;
  35 + /**
  36 + * 类型:1 单聊;2 群聊;4 通知会话(预留)
  37 + */
  38 + private Boolean conversationType;
  39 + /**
  40 + * 单聊时,用户编号;群聊时,群编号
  41 + */
  42 + private String targetId;
  43 + /**
  44 + * 会话标志 单聊:s_{userId}_{targetId},需要排序 userId 和 targetId 群聊:g_groupId
  45 + */
  46 + private String no;
  47 + /**
  48 + * 是否置顶 0否 1是
  49 + */
  50 + private Boolean pinned;
  51 + /**
  52 + * 最后已读时间
  53 + */
  54 + private LocalDateTime lastReadTime;
  55 +
  56 +}
0 57 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/inbox/InboxDO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.inbox;
  2 +
  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.*;
  8 +
  9 +/**
  10 + * 收件箱 DO
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@TableName("im_inbox")
  15 +@KeySequence("im_inbox_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +@Builder
  20 +@NoArgsConstructor
  21 +@AllArgsConstructor
  22 +public class InboxDO extends BaseDO {
  23 +
  24 + /**
  25 + * 编号
  26 + */
  27 + @TableId
  28 + private Long id;
  29 + /**
  30 + * 用户编号
  31 + */
  32 + private Long userId;
  33 + /**
  34 + * 消息编号
  35 + */
  36 + private Long messageId;
  37 + /**
  38 + * 序号,按照 user 递增
  39 + */
  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/dal/dataobject/message/MessageDO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.message;
  2 +
  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.*;
  8 +
  9 +import java.time.LocalDateTime;
  10 +
  11 +/**
  12 + * 消息 DO
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@TableName("im_message")
  17 +@KeySequence("im_message_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@ToString(callSuper = true)
  21 +@Builder
  22 +@NoArgsConstructor
  23 +@AllArgsConstructor
  24 +public class MessageDO extends BaseDO {
  25 +
  26 + /**
  27 + * 编号
  28 + */
  29 + @TableId
  30 + private Long id;
  31 + /**
  32 + * 客户端消息编号 uuid,用于排重
  33 + */
  34 + private String clientMessageId;
  35 + /**
  36 + * 发送人编号
  37 + */
  38 + private Long senderId;
  39 + /**
  40 + * 接收人编号
  41 + */
  42 + private Long receiverId;
  43 + /**
  44 + * 发送人昵称
  45 + */
  46 + private String senderNickname;
  47 + /**
  48 + * 发送人头像
  49 + */
  50 + private String senderAvatar;
  51 + /**
  52 + * 会话类型
  53 + */
  54 + private Boolean conversationType;
  55 + /**
  56 + * 会话标志
  57 + */
  58 + private String conversationNo;
  59 + /**
  60 + * 消息类型
  61 + */
  62 + private Boolean contentType;
  63 + /**
  64 + * 消息内容
  65 + */
  66 + private String content;
  67 + /**
  68 + * 发送时间
  69 + */
  70 + private LocalDateTime sendTime;
  71 + /**
  72 + * 消息来源 100-用户发送;200-系统发送(一般是通知);
  73 + */
  74 + private Boolean sendFrom;
  75 +
  76 +}
0 77 \ 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/ImAudioMessageBody.java 0 → 100644
  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 +}
0 32 \ 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/ImFileMessageBody.java 0 → 100644
  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 ImFileMessageBody implements ImMessageBody {
  16 +
  17 + /**
  18 + * 文件名
  19 + */
  20 + private String name;
  21 + /**
  22 + * 文件 URL
  23 + */
  24 + private String url;
  25 +
  26 + // TODO 芋艿:要不要以下字段?待定;云信有、企业微信没有
  27 +// "md5":"79d62a35fa3d34c367b20c66afc2a500", //文件MD5,按照字节流加密
  28 +// "ext":"ttf", //文件后缀类型
  29 +// "size":91680 //大小,单位为字节(Byte)
  30 +
  31 +}
0 32 \ 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 0 → 100644
  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 ImImageMessageBody implements ImMessageBody {
  16 +
  17 + /**
  18 + * 图片地址
  19 + */
  20 + private String url;
  21 +
  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 +}
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/body/ImLocationMessageBody.java 0 → 100644
  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 ImLocationMessageBody implements ImMessageBody {
  16 +
  17 + /**
  18 + * 地理位置
  19 + *
  20 + * 例如说:中国 浙江省 杭州市 网商路 599号
  21 + */
  22 + private String address;
  23 + /**
  24 + * 经度
  25 + */
  26 + private Double longitude;
  27 + /**
  28 + * 纬度
  29 + */
  30 + private Double latitude;
  31 +
  32 +}
0 33 \ 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 0 → 100644
  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 +}
0 13 \ 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/ImTextMessageBody.java 0 → 100644
  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 ImTextMessageBody implements ImMessageBody {
  16 +
  17 + /**
  18 + * 文本消息内容
  19 + */
  20 + private String content;
  21 +
  22 +}
0 23 \ 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/ImVideoMessageBody.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.dal.dataobject.message.body;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 视频消息的 {@link ImMessageBody}
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +@Data
  11 +public class ImVideoMessageBody implements ImMessageBody {
  12 +
  13 + /**
  14 + * 视频地址
  15 + */
  16 + private String url;
  17 +
  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 +}
0 28 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ConversationMapper.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.dal.mysql.conversation;
  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.conversation.vo.ConversationPageReqVO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +/**
  11 + * 会话 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface ConversationMapper extends BaseMapperX<ConversationDO> {
  17 +
  18 + default PageResult<ConversationDO> selectPage(ConversationPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<ConversationDO>()
  20 + .eqIfPresent(ConversationDO::getUserId, reqVO.getUserId())
  21 + .eqIfPresent(ConversationDO::getConversationType, reqVO.getConversationType())
  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));
  28 + }
  29 +
  30 +}
0 31 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/inbox/InboxMapper.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.dal.mysql.inbox;
  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.inbox.vo.InboxPageReqVO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +/**
  11 + * 收件箱 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface InboxMapper extends BaseMapperX<InboxDO> {
  17 +
  18 + default PageResult<InboxDO> selectPage(InboxPageReqVO 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 + }
  26 +
  27 +}
0 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/MessageMapper.java 0 → 100755
  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.MessagePageReqVO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +/**
  11 + * 消息 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface MessageMapper extends BaseMapperX<MessageDO> {
  17 +
  18 + default PageResult<MessageDO> selectPage(MessagePageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<MessageDO>()
  20 + .eqIfPresent(MessageDO::getClientMessageId, reqVO.getClientMessageId())
  21 + .eqIfPresent(MessageDO::getSenderId, reqVO.getSenderId())
  22 + .eqIfPresent(MessageDO::getReceiverId, reqVO.getReceiverId())
  23 + .likeIfPresent(MessageDO::getSenderNickname, reqVO.getSenderNickname())
  24 + .eqIfPresent(MessageDO::getSenderAvatar, reqVO.getSenderAvatar())
  25 + .eqIfPresent(MessageDO::getConversationType, reqVO.getConversationType())
  26 + .eqIfPresent(MessageDO::getConversationNo, reqVO.getConversationNo())
  27 + .eqIfPresent(MessageDO::getContentType, reqVO.getContentType())
  28 + .eqIfPresent(MessageDO::getContent, reqVO.getContent())
  29 + .betweenIfPresent(MessageDO::getSendTime, reqVO.getSendTime())
  30 + .eqIfPresent(MessageDO::getSendFrom, reqVO.getSendFrom())
  31 + .betweenIfPresent(MessageDO::getCreateTime, reqVO.getCreateTime())
  32 + .orderByDesc(MessageDO::getId));
  33 + }
  34 +
  35 +}
0 36 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/framework/package-info.java 0 → 100644
  1 +/**
  2 + * 属于 erp 模块的 framework 封装
  3 + *
  4 + * @author 芋道源码
  5 + */
  6 +package cn.iocoder.yudao.module.im.framework;
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/framework/web/config/ImWebConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.framework.web.config;
  2 +
  3 +import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration;
  4 +import org.springdoc.core.models.GroupedOpenApi;
  5 +import org.springframework.context.annotation.Bean;
  6 +import org.springframework.context.annotation.Configuration;
  7 +
  8 +/**
  9 + * im 模块的 web 组件的 Configuration
  10 + */
  11 +@Configuration(proxyBeanMethods = false)
  12 +public class ImWebConfiguration {
  13 +
  14 + /**
  15 + * im 模块的 API 分组
  16 + */
  17 + @Bean
  18 + public GroupedOpenApi imGroupedOpenApi() {
  19 + return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("im");
  20 + }
  21 +
  22 +}
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/framework/web/package-info.java 0 → 100644
  1 +/**
  2 + * trade 模块的 web 配置
  3 + */
  4 +package cn.iocoder.yudao.module.im.framework.web;
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/jackson/ImMessageBodyDeserializer.java 0 → 100644
  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 +public class ImMessageBodyDeserializer extends JsonDeserializer<ImMessageBody> {
  12 +
  13 + @Override
  14 + public ImMessageBody deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
  15 + JsonNode node = p.getCodec().readTree(p);
  16 + // 根据 node 中的内容来判断应该实例化哪个子类
  17 + if (node.has("content")) {
  18 + return new ImTextMessageBody(node.get("content").asText());
  19 + }
  20 + if (node.has("url")) {
  21 + String url = node.get("url").asText();
  22 + if (node.has("format")) {
  23 + return new ImAudioMessageBody(url, node.get("format").asText());
  24 + }
  25 + return new ImImageMessageBody(url);
  26 + }
  27 + if (node.has("name")) {
  28 + return new ImFileMessageBody(node.get("name").asText(), node.get("url").asText());
  29 + }
  30 + if (node.has("address")) {
  31 + return new ImLocationMessageBody(node.get("address").asText(), node.get("longitude").asDouble(), node.get("latitude").asDouble());
  32 + }
  33 + // 如果没有匹配的属性,抛出异常
  34 + throw ctxt.mappingException("Cannot deserialize to an instance of ImMessageBody");
  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/service/conversation/ConversationService.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.service.conversation;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationPageReqVO;
  5 +import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationSaveReqVO;
  6 +import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * 会话 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface ConversationService {
  15 +
  16 + /**
  17 + * 创建会话
  18 + *
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
  21 + */
  22 + Long createConversation(@Valid ConversationSaveReqVO createReqVO);
  23 +
  24 + /**
  25 + * 更新会话
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updateConversation(@Valid ConversationSaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除会话
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deleteConversation(Long id);
  37 +
  38 + /**
  39 + * 获得会话
  40 + *
  41 + * @param id 编号
  42 + * @return 会话
  43 + */
  44 + ConversationDO getConversation(Long id);
  45 +
  46 + /**
  47 + * 获得会话分页
  48 + *
  49 + * @param pageReqVO 分页查询
  50 + * @return 会话分页
  51 + */
  52 + PageResult<ConversationDO> getConversationPage(ConversationPageReqVO pageReqVO);
  53 +
  54 +}
0 55 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ConversationServiceImpl.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.service.conversation;
  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.conversation.vo.ConversationPageReqVO;
  6 +import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationSaveReqVO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
  8 +import cn.iocoder.yudao.module.im.dal.mysql.conversation.ConversationMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  14 +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.CONVERSATION_NOT_EXISTS;
  15 +
  16 +/**
  17 + * 会话 Service 实现类
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Service
  22 +@Validated
  23 +public class ConversationServiceImpl implements ConversationService {
  24 +
  25 + @Resource
  26 + private ConversationMapper conversationMapper;
  27 +
  28 + @Override
  29 + public Long createConversation(ConversationSaveReqVO createReqVO) {
  30 + // 插入
  31 + ConversationDO conversation = BeanUtils.toBean(createReqVO, ConversationDO.class);
  32 + conversationMapper.insert(conversation);
  33 + // 返回
  34 + return conversation.getId();
  35 + }
  36 +
  37 + @Override
  38 + public void updateConversation(ConversationSaveReqVO updateReqVO) {
  39 + // 校验存在
  40 + validateConversationExists(updateReqVO.getId());
  41 + // 更新
  42 + ConversationDO updateObj = BeanUtils.toBean(updateReqVO, ConversationDO.class);
  43 + conversationMapper.updateById(updateObj);
  44 + }
  45 +
  46 + @Override
  47 + public void deleteConversation(Long id) {
  48 + // 校验存在
  49 + validateConversationExists(id);
  50 + // 删除
  51 + conversationMapper.deleteById(id);
  52 + }
  53 +
  54 + private void validateConversationExists(Long id) {
  55 + if (conversationMapper.selectById(id) == null) {
  56 + throw exception(CONVERSATION_NOT_EXISTS);
  57 + }
  58 + }
  59 +
  60 + @Override
  61 + public ConversationDO getConversation(Long id) {
  62 + return conversationMapper.selectById(id);
  63 + }
  64 +
  65 + @Override
  66 + public PageResult<ConversationDO> getConversationPage(ConversationPageReqVO pageReqVO) {
  67 + return conversationMapper.selectPage(pageReqVO);
  68 + }
  69 +
  70 +}
0 71 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/InboxService.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.service.inbox;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxPageReqVO;
  5 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveReqVO;
  6 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * 收件箱 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface InboxService {
  15 +
  16 + /**
  17 + * 创建收件箱
  18 + *
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
  21 + */
  22 + Long createInbox(@Valid InboxSaveReqVO createReqVO);
  23 +
  24 + /**
  25 + * 更新收件箱
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updateInbox(@Valid InboxSaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除收件箱
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deleteInbox(Long id);
  37 +
  38 + /**
  39 + * 获得收件箱
  40 + *
  41 + * @param id 编号
  42 + * @return 收件箱
  43 + */
  44 + InboxDO getInbox(Long id);
  45 +
  46 + /**
  47 + * 获得收件箱分页
  48 + *
  49 + * @param pageReqVO 分页查询
  50 + * @return 收件箱分页
  51 + */
  52 + PageResult<InboxDO> getInboxPage(InboxPageReqVO pageReqVO);
  53 +
  54 +}
0 55 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/InboxServiceImpl.java 0 → 100755
  1 +package cn.iocoder.yudao.module.im.service.inbox;
  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.inbox.vo.InboxPageReqVO;
  6 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveReqVO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
  8 +import cn.iocoder.yudao.module.im.dal.mysql.inbox.InboxMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  14 +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.INBOX_NOT_EXISTS;
  15 +
  16 +/**
  17 + * 收件箱 Service 实现类
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Service
  22 +@Validated
  23 +public class InboxServiceImpl implements InboxService {
  24 +
  25 + @Resource
  26 + private InboxMapper inboxMapper;
  27 +
  28 + @Override
  29 + public Long createInbox(InboxSaveReqVO createReqVO) {
  30 + // 插入
  31 + InboxDO inbox = BeanUtils.toBean(createReqVO, InboxDO.class);
  32 + inboxMapper.insert(inbox);
  33 + // 返回
  34 + return inbox.getId();
  35 + }
  36 +
  37 + @Override
  38 + public void updateInbox(InboxSaveReqVO updateReqVO) {
  39 + // 校验存在
  40 + validateInboxExists(updateReqVO.getId());
  41 + // 更新
  42 + InboxDO updateObj = BeanUtils.toBean(updateReqVO, InboxDO.class);
  43 + inboxMapper.updateById(updateObj);
  44 + }
  45 +
  46 + @Override
  47 + public void deleteInbox(Long id) {
  48 + // 校验存在
  49 + validateInboxExists(id);
  50 + // 删除
  51 + inboxMapper.deleteById(id);
  52 + }
  53 +
  54 + private void validateInboxExists(Long id) {
  55 + if (inboxMapper.selectById(id) == null) {
  56 + throw exception(INBOX_NOT_EXISTS);
  57 + }
  58 + }
  59 +
  60 + @Override
  61 + public InboxDO getInbox(Long id) {
  62 + return inboxMapper.selectById(id);
  63 + }
  64 +
  65 + @Override
  66 + public PageResult<InboxDO> getInboxPage(InboxPageReqVO pageReqVO) {
  67 + return inboxMapper.selectPage(pageReqVO);
  68 + }
  69 +
  70 +}
0 71 \ 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.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessagePageReqVO;
  5 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessageSaveReqVO;
  6 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * 消息 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface MessageService {
  15 +
  16 + /**
  17 + * 创建消息
  18 + *
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
  21 + */
  22 + Long createMessage(@Valid MessageSaveReqVO createReqVO);
  23 +
  24 + /**
  25 + * 更新消息
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updateMessage(@Valid MessageSaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除消息
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deleteMessage(Long id);
  37 +
  38 + /**
  39 + * 获得消息
  40 + *
  41 + * @param id 编号
  42 + * @return 消息
  43 + */
  44 + MessageDO getMessage(Long id);
  45 +
  46 + /**
  47 + * 获得消息分页
  48 + *
  49 + * @param pageReqVO 分页查询
  50 + * @return 消息分页
  51 + */
  52 + PageResult<MessageDO> getMessagePage(MessagePageReqVO pageReqVO);
  53 +
  54 + /**
  55 + * 发送私聊消息
  56 + * @param messageSaveReqVO 消息信息
  57 + * @return 消息编号
  58 + */
  59 + Long sendPrivateMessage(MessageSaveReqVO messageSaveReqVO);
  60 +}
0 61 \ 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.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.MessagePageReqVO;
  6 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessageSaveReqVO;
  7 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  8 +import cn.iocoder.yudao.module.im.dal.mysql.message.MessageMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  14 +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.MESSAGE_NOT_EXISTS;
  15 +
  16 +/**
  17 + * 消息 Service 实现类
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Service
  22 +@Validated
  23 +public class MessageServiceImpl implements MessageService {
  24 +
  25 + @Resource
  26 + private MessageMapper messageMapper;
  27 +
  28 + @Override
  29 + public Long createMessage(MessageSaveReqVO createReqVO) {
  30 + // 插入
  31 + MessageDO message = BeanUtils.toBean(createReqVO, MessageDO.class);
  32 + messageMapper.insert(message);
  33 + // 返回
  34 + return message.getId();
  35 + }
  36 +
  37 + @Override
  38 + public void updateMessage(MessageSaveReqVO updateReqVO) {
  39 + // 校验存在
  40 + validateMessageExists(updateReqVO.getId());
  41 + // 更新
  42 + MessageDO updateObj = BeanUtils.toBean(updateReqVO, MessageDO.class);
  43 + messageMapper.updateById(updateObj);
  44 + }
  45 +
  46 + @Override
  47 + public void deleteMessage(Long id) {
  48 + // 校验存在
  49 + validateMessageExists(id);
  50 + // 删除
  51 + messageMapper.deleteById(id);
  52 + }
  53 +
  54 + private void validateMessageExists(Long id) {
  55 + if (messageMapper.selectById(id) == null) {
  56 + throw exception(MESSAGE_NOT_EXISTS);
  57 + }
  58 + }
  59 +
  60 + @Override
  61 + public MessageDO getMessage(Long id) {
  62 + return messageMapper.selectById(id);
  63 + }
  64 +
  65 + @Override
  66 + public PageResult<MessageDO> getMessagePage(MessagePageReqVO pageReqVO) {
  67 + return messageMapper.selectPage(pageReqVO);
  68 + }
  69 +
  70 + @Override
  71 + public Long sendPrivateMessage(MessageSaveReqVO messageSaveReqVO) {
  72 + return 0L;
  73 + }
  74 +
  75 +}
0 76 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/ImWebSocketMessageListener.java 0 → 100644
  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.enums.conversation.ImConversationTypeEnum;
  8 +import cn.iocoder.yudao.module.im.websocket.message.ImReceiveMessage;
  9 +import cn.iocoder.yudao.module.im.websocket.message.ImSendMessage;
  10 +import jakarta.annotation.Resource;
  11 +import org.springframework.stereotype.Component;
  12 +import org.springframework.web.socket.WebSocketSession;
  13 +
  14 +/**
  15 + * WebSocket 示例:单发消息
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +@Component
  20 +public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSendMessage> {
  21 +
  22 + @Resource
  23 + private WebSocketMessageSender webSocketMessageSender;
  24 +
  25 + @Override
  26 + public void onMessage(WebSocketSession session, ImSendMessage message) {
  27 + Long fromUserId = WebSocketFrameworkUtils.getLoginUserId(session);
  28 + // 私聊
  29 + if (message.getConversationType().equals(ImConversationTypeEnum.PRIVATE.getType())) {
  30 + ImReceiveMessage toMessage = new ImReceiveMessage();
  31 + toMessage.setToId(fromUserId);
  32 + toMessage.setConversationType(ImConversationTypeEnum.PRIVATE.getType());
  33 + //消息类型
  34 + toMessage.setType(message.getType());
  35 + toMessage.setBody(message.getBody());
  36 + webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), message.getToId(), // 给指定用户
  37 + "im-message-receive", toMessage);
  38 + }
  39 + }
  40 +
  41 + @Override
  42 + public String getType() {
  43 + return "im-message-send";
  44 + }
  45 +
  46 +}
... ...
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/message/ImReceiveMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.im.websocket.message;
  2 +
  3 +import cn.iocoder.yudao.module.im.dal.dataobject.message.body.ImMessageBody;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - 消息发送 send")
  8 +@Data
  9 +public class ImReceiveMessage {
  10 +
  11 + @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  12 + private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
  13 +
  14 + @Schema(description = "聊天对象,用户编号或群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  15 + private Long toId; // 根据 conversationType 区分
  16 +
  17 + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  18 + private Integer type; // 参见 ImMessageTypeEnum 枚举
  19 +
  20 + @Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
  21 + private ImMessageBody body;
  22 +
  23 +}
0 24 \ 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 0 → 100644
  1 +package cn.iocoder.yudao.module.im.websocket.message;
  2 +
  3 +import cn.iocoder.yudao.module.im.dal.dataobject.message.body.ImMessageBody;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - 消息发送 send")
  8 +@Data
  9 +public class ImSendMessage {
  10 +
  11 + @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  12 + private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
  13 +
  14 + @Schema(description = "聊天对象,用户编号或群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  15 + private Long toId; // 根据 conversationType 区分
  16 +
  17 + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  18 + private Integer type; // 参见 ImMessageTypeEnum 枚举
  19 +
  20 + @Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
  21 + private ImMessageBody body;
  22 +
  23 +}
0 24 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/conversation/ConversationMapper.xml 0 → 100755
  1 +<?xml version="1.0" encoding="UTF-8"?>
  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.ConversationMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 13 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/inbox/InboxMapper.xml 0 → 100755
  1 +<?xml version="1.0" encoding="UTF-8"?>
  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.inbox.InboxMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 13 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/message/MessageMapper.xml 0 → 100755
  1 +<?xml version="1.0" encoding="UTF-8"?>
  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.MessageMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 13 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/conversation/ConversationServiceImplTest.java 0 → 100755
  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 +import org.springframework.boot.test.mock.mockito.MockBean;
  6 +
  7 +import jakarta.annotation.Resource;
  8 +
  9 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  10 +
  11 +import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.*;
  12 +import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
  13 +import cn.iocoder.yudao.module.im.dal.mysql.conversation.ConversationMapper;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +
  16 +import jakarta.annotation.Resource;
  17 +import org.springframework.context.annotation.Import;
  18 +import java.util.*;
  19 +import java.time.LocalDateTime;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.*;
  22 +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  27 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  28 +import static org.junit.jupiter.api.Assertions.*;
  29 +import static org.mockito.Mockito.*;
  30 +
  31 +/**
  32 + * {@link ConversationServiceImpl} 的单元测试类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Import(ConversationServiceImpl.class)
  37 +public class ConversationServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private ConversationServiceImpl conversationService;
  41 +
  42 + @Resource
  43 + private ConversationMapper conversationMapper;
  44 +
  45 + @Test
  46 + public void testCreateConversation_success() {
  47 + // 准备参数
  48 + ConversationSaveReqVO createReqVO = randomPojo(ConversationSaveReqVO.class).setId(null);
  49 +
  50 + // 调用
  51 + Long conversationId = conversationService.createConversation(createReqVO);
  52 + // 断言
  53 + assertNotNull(conversationId);
  54 + // 校验记录的属性是否正确
  55 + ConversationDO conversation = conversationMapper.selectById(conversationId);
  56 + assertPojoEquals(createReqVO, conversation, "id");
  57 + }
  58 +
  59 + @Test
  60 + public void testUpdateConversation_success() {
  61 + // mock 数据
  62 + ConversationDO dbConversation = randomPojo(ConversationDO.class);
  63 + conversationMapper.insert(dbConversation);// @Sql: 先插入出一条存在的数据
  64 + // 准备参数
  65 + ConversationSaveReqVO updateReqVO = randomPojo(ConversationSaveReqVO.class, o -> {
  66 + o.setId(dbConversation.getId()); // 设置更新的 ID
  67 + });
  68 +
  69 + // 调用
  70 + conversationService.updateConversation(updateReqVO);
  71 + // 校验是否更新正确
  72 + ConversationDO conversation = conversationMapper.selectById(updateReqVO.getId()); // 获取最新的
  73 + assertPojoEquals(updateReqVO, conversation);
  74 + }
  75 +
  76 + @Test
  77 + public void testUpdateConversation_notExists() {
  78 + // 准备参数
  79 + ConversationSaveReqVO updateReqVO = randomPojo(ConversationSaveReqVO.class);
  80 +
  81 + // 调用, 并断言异常
  82 + assertServiceException(() -> conversationService.updateConversation(updateReqVO), CONVERSATION_NOT_EXISTS);
  83 + }
  84 +
  85 + @Test
  86 + public void testDeleteConversation_success() {
  87 + // mock 数据
  88 + ConversationDO dbConversation = randomPojo(ConversationDO.class);
  89 + conversationMapper.insert(dbConversation);// @Sql: 先插入出一条存在的数据
  90 + // 准备参数
  91 + Long id = dbConversation.getId();
  92 +
  93 + // 调用
  94 + conversationService.deleteConversation(id);
  95 + // 校验数据不存在了
  96 + assertNull(conversationMapper.selectById(id));
  97 + }
  98 +
  99 + @Test
  100 + public void testDeleteConversation_notExists() {
  101 + // 准备参数
  102 + Long id = randomLongId();
  103 +
  104 + // 调用, 并断言异常
  105 + assertServiceException(() -> conversationService.deleteConversation(id), CONVERSATION_NOT_EXISTS);
  106 + }
  107 +
  108 + @Test
  109 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  110 + public void testGetConversationPage() {
  111 + // mock 数据
  112 + ConversationDO dbConversation = randomPojo(ConversationDO.class, o -> { // 等会查询到
  113 + o.setUserId(null);
  114 + o.setConversationType(null);
  115 + o.setTargetId(null);
  116 + o.setNo(null);
  117 + o.setPinned(null);
  118 + o.setLastReadTime(null);
  119 + o.setCreateTime(null);
  120 + });
  121 + conversationMapper.insert(dbConversation);
  122 + // 测试 userId 不匹配
  123 + conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setUserId(null)));
  124 + // 测试 conversationType 不匹配
  125 + conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setConversationType(null)));
  126 + // 测试 targetId 不匹配
  127 + conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setTargetId(null)));
  128 + // 测试 no 不匹配
  129 + conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setNo(null)));
  130 + // 测试 pinned 不匹配
  131 + conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setPinned(null)));
  132 + // 测试 lastReadTime 不匹配
  133 + conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setLastReadTime(null)));
  134 + // 测试 createTime 不匹配
  135 + conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setCreateTime(null)));
  136 + // 准备参数
  137 + ConversationPageReqVO reqVO = new ConversationPageReqVO();
  138 + reqVO.setUserId(null);
  139 + reqVO.setConversationType(null);
  140 + reqVO.setTargetId(null);
  141 + reqVO.setNo(null);
  142 + reqVO.setPinned(null);
  143 + reqVO.setLastReadTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  144 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  145 +
  146 + // 调用
  147 + PageResult<ConversationDO> pageResult = conversationService.getConversationPage(reqVO);
  148 + // 断言
  149 + assertEquals(1, pageResult.getTotal());
  150 + assertEquals(1, pageResult.getList().size());
  151 + assertPojoEquals(dbConversation, pageResult.getList().get(0));
  152 + }
  153 +
  154 +}
0 155 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/inbox/InboxServiceImplTest.java 0 → 100755
  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 +import org.springframework.boot.test.mock.mockito.MockBean;
  6 +
  7 +import jakarta.annotation.Resource;
  8 +
  9 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  10 +
  11 +import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.*;
  12 +import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
  13 +import cn.iocoder.yudao.module.im.dal.mysql.inbox.InboxMapper;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +
  16 +import jakarta.annotation.Resource;
  17 +import org.springframework.context.annotation.Import;
  18 +import java.util.*;
  19 +import java.time.LocalDateTime;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.*;
  22 +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  27 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  28 +import static org.junit.jupiter.api.Assertions.*;
  29 +import static org.mockito.Mockito.*;
  30 +
  31 +/**
  32 + * {@link InboxServiceImpl} 的单元测试类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Import(InboxServiceImpl.class)
  37 +public class InboxServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private InboxServiceImpl inboxService;
  41 +
  42 + @Resource
  43 + private InboxMapper inboxMapper;
  44 +
  45 + @Test
  46 + public void testCreateInbox_success() {
  47 + // 准备参数
  48 + InboxSaveReqVO createReqVO = randomPojo(InboxSaveReqVO.class).setId(null);
  49 +
  50 + // 调用
  51 + Long inboxId = inboxService.createInbox(createReqVO);
  52 + // 断言
  53 + assertNotNull(inboxId);
  54 + // 校验记录的属性是否正确
  55 + InboxDO inbox = inboxMapper.selectById(inboxId);
  56 + assertPojoEquals(createReqVO, inbox, "id");
  57 + }
  58 +
  59 + @Test
  60 + public void testUpdateInbox_success() {
  61 + // mock 数据
  62 + InboxDO dbInbox = randomPojo(InboxDO.class);
  63 + inboxMapper.insert(dbInbox);// @Sql: 先插入出一条存在的数据
  64 + // 准备参数
  65 + InboxSaveReqVO updateReqVO = randomPojo(InboxSaveReqVO.class, o -> {
  66 + o.setId(dbInbox.getId()); // 设置更新的 ID
  67 + });
  68 +
  69 + // 调用
  70 + inboxService.updateInbox(updateReqVO);
  71 + // 校验是否更新正确
  72 + InboxDO inbox = inboxMapper.selectById(updateReqVO.getId()); // 获取最新的
  73 + assertPojoEquals(updateReqVO, inbox);
  74 + }
  75 +
  76 + @Test
  77 + public void testUpdateInbox_notExists() {
  78 + // 准备参数
  79 + InboxSaveReqVO updateReqVO = randomPojo(InboxSaveReqVO.class);
  80 +
  81 + // 调用, 并断言异常
  82 + assertServiceException(() -> inboxService.updateInbox(updateReqVO), INBOX_NOT_EXISTS);
  83 + }
  84 +
  85 + @Test
  86 + public void testDeleteInbox_success() {
  87 + // mock 数据
  88 + InboxDO dbInbox = randomPojo(InboxDO.class);
  89 + inboxMapper.insert(dbInbox);// @Sql: 先插入出一条存在的数据
  90 + // 准备参数
  91 + Long id = dbInbox.getId();
  92 +
  93 + // 调用
  94 + inboxService.deleteInbox(id);
  95 + // 校验数据不存在了
  96 + assertNull(inboxMapper.selectById(id));
  97 + }
  98 +
  99 + @Test
  100 + public void testDeleteInbox_notExists() {
  101 + // 准备参数
  102 + Long id = randomLongId();
  103 +
  104 + // 调用, 并断言异常
  105 + assertServiceException(() -> inboxService.deleteInbox(id), INBOX_NOT_EXISTS);
  106 + }
  107 +
  108 + @Test
  109 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  110 + public void testGetInboxPage() {
  111 + // mock 数据
  112 + InboxDO dbInbox = randomPojo(InboxDO.class, o -> { // 等会查询到
  113 + o.setUserId(null);
  114 + o.setMessageId(null);
  115 + o.setSequence(null);
  116 + o.setCreateTime(null);
  117 + });
  118 + inboxMapper.insert(dbInbox);
  119 + // 测试 userId 不匹配
  120 + inboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setUserId(null)));
  121 + // 测试 messageId 不匹配
  122 + inboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setMessageId(null)));
  123 + // 测试 sequence 不匹配
  124 + inboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setSequence(null)));
  125 + // 测试 createTime 不匹配
  126 + inboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setCreateTime(null)));
  127 + // 准备参数
  128 + InboxPageReqVO reqVO = new InboxPageReqVO();
  129 + reqVO.setUserId(null);
  130 + reqVO.setMessageId(null);
  131 + reqVO.setSequence(null);
  132 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  133 +
  134 + // 调用
  135 + PageResult<InboxDO> pageResult = inboxService.getInboxPage(reqVO);
  136 + // 断言
  137 + assertEquals(1, pageResult.getTotal());
  138 + assertEquals(1, pageResult.getList().size());
  139 + assertPojoEquals(dbInbox, pageResult.getList().get(0));
  140 + }
  141 +
  142 +}
0 143 \ No newline at end of file
... ...
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/MessageServiceImplTest.java 0 → 100755
  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 +import org.springframework.boot.test.mock.mockito.MockBean;
  6 +
  7 +import jakarta.annotation.Resource;
  8 +
  9 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  10 +
  11 +import cn.iocoder.yudao.module.im.controller.admin.message.vo.*;
  12 +import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
  13 +import cn.iocoder.yudao.module.im.dal.mysql.message.MessageMapper;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +
  16 +import jakarta.annotation.Resource;
  17 +import org.springframework.context.annotation.Import;
  18 +import java.util.*;
  19 +import java.time.LocalDateTime;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.*;
  22 +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  27 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  28 +import static org.junit.jupiter.api.Assertions.*;
  29 +import static org.mockito.Mockito.*;
  30 +
  31 +/**
  32 + * {@link MessageServiceImpl} 的单元测试类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Import(MessageServiceImpl.class)
  37 +public class MessageServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private MessageServiceImpl messageService;
  41 +
  42 + @Resource
  43 + private MessageMapper messageMapper;
  44 +
  45 + @Test
  46 + public void testCreateMessage_success() {
  47 + // 准备参数
  48 + MessageSaveReqVO createReqVO = randomPojo(MessageSaveReqVO.class).setId(null);
  49 +
  50 + // 调用
  51 + Long messageId = messageService.createMessage(createReqVO);
  52 + // 断言
  53 + assertNotNull(messageId);
  54 + // 校验记录的属性是否正确
  55 + MessageDO message = messageMapper.selectById(messageId);
  56 + assertPojoEquals(createReqVO, message, "id");
  57 + }
  58 +
  59 + @Test
  60 + public void testUpdateMessage_success() {
  61 + // mock 数据
  62 + MessageDO dbMessage = randomPojo(MessageDO.class);
  63 + messageMapper.insert(dbMessage);// @Sql: 先插入出一条存在的数据
  64 + // 准备参数
  65 + MessageSaveReqVO updateReqVO = randomPojo(MessageSaveReqVO.class, o -> {
  66 + o.setId(dbMessage.getId()); // 设置更新的 ID
  67 + });
  68 +
  69 + // 调用
  70 + messageService.updateMessage(updateReqVO);
  71 + // 校验是否更新正确
  72 + MessageDO message = messageMapper.selectById(updateReqVO.getId()); // 获取最新的
  73 + assertPojoEquals(updateReqVO, message);
  74 + }
  75 +
  76 + @Test
  77 + public void testUpdateMessage_notExists() {
  78 + // 准备参数
  79 + MessageSaveReqVO updateReqVO = randomPojo(MessageSaveReqVO.class);
  80 +
  81 + // 调用, 并断言异常
  82 + assertServiceException(() -> messageService.updateMessage(updateReqVO), MESSAGE_NOT_EXISTS);
  83 + }
  84 +
  85 + @Test
  86 + public void testDeleteMessage_success() {
  87 + // mock 数据
  88 + MessageDO dbMessage = randomPojo(MessageDO.class);
  89 + messageMapper.insert(dbMessage);// @Sql: 先插入出一条存在的数据
  90 + // 准备参数
  91 + Long id = dbMessage.getId();
  92 +
  93 + // 调用
  94 + messageService.deleteMessage(id);
  95 + // 校验数据不存在了
  96 + assertNull(messageMapper.selectById(id));
  97 + }
  98 +
  99 + @Test
  100 + public void testDeleteMessage_notExists() {
  101 + // 准备参数
  102 + Long id = randomLongId();
  103 +
  104 + // 调用, 并断言异常
  105 + assertServiceException(() -> messageService.deleteMessage(id), MESSAGE_NOT_EXISTS);
  106 + }
  107 +
  108 + @Test
  109 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  110 + public void testGetMessagePage() {
  111 + // mock 数据
  112 + MessageDO dbMessage = randomPojo(MessageDO.class, o -> { // 等会查询到
  113 + o.setClientMessageId(null);
  114 + o.setSenderId(null);
  115 + o.setReceiverId(null);
  116 + o.setSenderNickname(null);
  117 + o.setSenderAvatar(null);
  118 + o.setConversationType(null);
  119 + o.setConversationNo(null);
  120 + o.setContentType(null);
  121 + o.setContent(null);
  122 + o.setSendTime(null);
  123 + o.setSendFrom(null);
  124 + o.setCreateTime(null);
  125 + });
  126 + messageMapper.insert(dbMessage);
  127 + // 测试 clientMessageId 不匹配
  128 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setClientMessageId(null)));
  129 + // 测试 senderId 不匹配
  130 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSenderId(null)));
  131 + // 测试 receiverId 不匹配
  132 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setReceiverId(null)));
  133 + // 测试 senderNickname 不匹配
  134 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSenderNickname(null)));
  135 + // 测试 senderAvatar 不匹配
  136 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSenderAvatar(null)));
  137 + // 测试 conversationType 不匹配
  138 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setConversationType(null)));
  139 + // 测试 conversationNo 不匹配
  140 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setConversationNo(null)));
  141 + // 测试 contentType 不匹配
  142 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setContentType(null)));
  143 + // 测试 content 不匹配
  144 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setContent(null)));
  145 + // 测试 sendTime 不匹配
  146 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSendTime(null)));
  147 + // 测试 sendFrom 不匹配
  148 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setSendFrom(null)));
  149 + // 测试 createTime 不匹配
  150 + messageMapper.insert(cloneIgnoreId(dbMessage, o -> o.setCreateTime(null)));
  151 + // 准备参数
  152 + MessagePageReqVO reqVO = new MessagePageReqVO();
  153 + reqVO.setClientMessageId(null);
  154 + reqVO.setSenderId(null);
  155 + reqVO.setReceiverId(null);
  156 + reqVO.setSenderNickname(null);
  157 + reqVO.setSenderAvatar(null);
  158 + reqVO.setConversationType(null);
  159 + reqVO.setConversationNo(null);
  160 + reqVO.setContentType(null);
  161 + reqVO.setContent(null);
  162 + reqVO.setSendTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  163 + reqVO.setSendFrom(null);
  164 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  165 +
  166 + // 调用
  167 + PageResult<MessageDO> pageResult = messageService.getMessagePage(reqVO);
  168 + // 断言
  169 + assertEquals(1, pageResult.getTotal());
  170 + assertEquals(1, pageResult.getList().size());
  171 + assertPojoEquals(dbMessage, pageResult.getList().get(0));
  172 + }
  173 +
  174 +}
0 175 \ No newline at end of file
... ...
yudao-server/pom.xml
... ... @@ -101,6 +101,13 @@
101 101 <!-- <version>${revision}</version>-->
102 102 <!-- </dependency>-->
103 103  
  104 + <!-- IM 相关模块。默认注释,保证编译速度 -->
  105 + <dependency>
  106 + <groupId>cn.iocoder.boot</groupId>
  107 + <artifactId>yudao-module-im-biz</artifactId>
  108 + <version>${revision}</version>
  109 + </dependency>
  110 +
104 111 <!-- spring boot 配置所需依赖 -->
105 112 <dependency>
106 113 <groupId>org.springframework.boot</groupId>
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -189,6 +189,7 @@ logging:
189 189 cn.iocoder.yudao.module.statistics.dal.mysql: debug
190 190 cn.iocoder.yudao.module.crm.dal.mysql: debug
191 191 cn.iocoder.yudao.module.erp.dal.mysql: debug
  192 + cn.iocoder.yudao.module.im.dal.mysql: debug
192 193 org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示
193 194  
194 195 debug: false
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -183,6 +183,7 @@ yudao:
183 183 - cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants
184 184 - cn.iocoder.yudao.module.system.enums.ErrorCodeConstants
185 185 - cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants
  186 + - cn.iocoder.yudao.module.im.enums.ErrorCodeConstants
186 187 tenant: # 多租户相关配置项
187 188 enable: true
188 189 ignore-urls:
... ...