From 865a58a646afb285fdd71ff75922fa406832c7c3 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 19 May 2026 17:18:49 +0800 Subject: [PATCH] ✨ feat(im): 继续优化频道的各种代码, --- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/ImChannelMessageController.java | 40 ---------------------------------------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/vo/ImChannelMessagePullRespVO.java | 30 ------------------------------ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelManagerController.java | 7 ++++--- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelMessageManagerController.java | 83 ----------------------------------------------------------------------------------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialPageReqVO.java | 4 ++-- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialRespVO.java | 4 ++-- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialSaveReqVO.java | 6 +++--- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessagePageReqVO.java | 28 ---------------------------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageRespVO.java | 40 ---------------------------------------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageSendReqVO.java | 20 -------------------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImChannelMessageManagerController.java | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessagePageReqVO.java | 28 ++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageRespVO.java | 40 ++++++++++++++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageSendReqVO.java | 20 ++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImChannelMessageController.java | 45 +++++++++++++++++++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/channel/ImChannelMessagePullRespVO.java | 30 ++++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMaterialDO.java | 13 +++++-------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMessageDO.java | 72 ------------------------------------------------------------------------ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImChannelMessageDO.java | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMaterialMapper.java | 13 ++----------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMessageMapper.java | 52 ---------------------------------------------------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImChannelMessageMapper.java | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/channel/ImChannelMaterialContentTypeEnum.java | 37 +++++++++++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialService.java | 21 +++++++++++++-------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialServiceImpl.java | 17 ++++++++--------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageService.java | 59 ----------------------------------------------------------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageServiceImpl.java | 88 ---------------------------------------------------------------------------------------- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelService.java | 21 +++++++++++++++++---- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelServiceImpl.java | 9 +++------ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageService.java | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImpl.java | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImChannelMessageDTO.java | 2 +- yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/message/MaterialMessage.java | 10 +++++++++- yudao-module-im/src/test/resources/sql/create_tables.sql | 2 +- yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java | 3 ++- 35 files changed, 620 insertions(+), 572 deletions(-) delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/ImChannelMessageController.java delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/vo/ImChannelMessagePullRespVO.java delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelMessageManagerController.java delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessagePageReqVO.java delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageRespVO.java delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageSendReqVO.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImChannelMessageManagerController.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessagePageReqVO.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageRespVO.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageSendReqVO.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImChannelMessageController.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/channel/ImChannelMessagePullRespVO.java delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMessageDO.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImChannelMessageDO.java delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMessageMapper.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImChannelMessageMapper.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/channel/ImChannelMaterialContentTypeEnum.java delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageService.java delete mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageServiceImpl.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageService.java create mode 100644 yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImpl.java diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/ImChannelMessageController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/ImChannelMessageController.java deleted file mode 100644 index 26f7f83..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/ImChannelMessageController.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.im.controller.admin.channel; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.im.controller.admin.channel.vo.ImChannelMessagePullRespVO; -import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMessageDO; -import cn.iocoder.yudao.module.im.service.channel.ImChannelMessageService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; - -@Tag(name = "用户 APP - IM 频道消息") -@RestController -@RequestMapping("/im/channel/message") -@Validated -public class ImChannelMessageController { - - @Resource - private ImChannelMessageService channelMessageService; - - @GetMapping("/pull") - @Operation(summary = "拉取频道消息(离线增量);按 minId 游标分页") - public CommonResult> pull( - @RequestParam(value = "minId", defaultValue = "0") Long minId, - @RequestParam(value = "size", defaultValue = "100") Integer size) { - List list = channelMessageService.getMessageListForPull(getLoginUserId(), minId, size); - return success(BeanUtils.toBean(list, ImChannelMessagePullRespVO.class)); - } - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/vo/ImChannelMessagePullRespVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/vo/ImChannelMessagePullRespVO.java deleted file mode 100644 index 154ea9b..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/vo/ImChannelMessagePullRespVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.im.controller.admin.channel.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "用户 APP - IM 频道消息拉取 Response VO") -@Data -public class ImChannelMessagePullRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9001") - private Long id; - - @Schema(description = "频道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long channelId; - - @Schema(description = "素材编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long materialId; - - @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "125") - private Integer type; - - @Schema(description = "消息内容;payload JSON 快照", requiredMode = Schema.RequiredMode.REQUIRED) - private String content; - - @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime sendTime; - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelManagerController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelManagerController.java index 4eda7e7..2afc95e 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelManagerController.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelManagerController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.im.controller.admin.manager.channel; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -73,9 +74,9 @@ public class ImChannelManagerController { @GetMapping("/simple-list") @Operation(summary = "获得启用的频道精简列表;前端表单选择频道时调用") - public CommonResult> getEnabledChannelList() { - // TODO @AI:getChannelListByXXX;更通用点; - List list = channelService.getEnabledChannelList(); + public CommonResult> getSimpleChannelList() { + // TODO DONE @AI:getChannelListByStatus 统一命名 + List list = channelService.getChannelListByStatus(CommonStatusEnum.ENABLE.getStatus()); return success(BeanUtils.toBean(list, ImChannelRespVO.class)); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelMessageManagerController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelMessageManagerController.java deleted file mode 100644 index 53da9fa..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelMessageManagerController.java +++ /dev/null @@ -1,83 +0,0 @@ -package cn.iocoder.yudao.module.im.controller.admin.manager.channel; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message.ImChannelMessagePageReqVO; -import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message.ImChannelMessageRespVO; -import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message.ImChannelMessageSendReqVO; -import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelDO; -import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMaterialDO; -import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMessageDO; -import cn.iocoder.yudao.module.im.service.channel.ImChannelMaterialService; -import cn.iocoder.yudao.module.im.service.channel.ImChannelMessageService; -import cn.iocoder.yudao.module.im.service.channel.ImChannelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; - -@Tag(name = "管理后台 - IM 频道消息") -@RestController -@RequestMapping("/im/manager/channel-message") -@Validated -public class ImChannelMessageManagerController { - - @Resource - private ImChannelMessageService channelMessageService; - @Resource - private ImChannelService channelService; - @Resource - private ImChannelMaterialService channelMaterialService; - - @PostMapping("/send") - @Operation(summary = "立即推送频道消息") - @PreAuthorize("@ss.hasPermission('im:manager:channel-message:send')") - public CommonResult sendMessage(@Valid @RequestBody ImChannelMessageSendReqVO reqVO) { - return success(channelMessageService.sendMessage(reqVO)); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除频道消息") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('im:manager:channel-message:delete')") - public CommonResult deleteMessage(@RequestParam("id") Long id) { - channelMessageService.deleteMessage(id); - return success(true); - } - - @GetMapping("/page") - @Operation(summary = "获得频道消息分页;回填频道名 / 素材标题") - @PreAuthorize("@ss.hasPermission('im:manager:channel-message:query')") - public CommonResult> getMessagePage(@Valid ImChannelMessagePageReqVO pageReqVO) { - PageResult pageResult = channelMessageService.getMessagePage(pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty(pageResult.getTotal())); - } - // TODO @AI:最好是 channelService 也包出一个 getChannelMap;类似别的模块 - Map channelMap = convertMap( - channelService.getChannelList(convertSet(pageResult.getList(), ImChannelMessageDO::getChannelId)), - ImChannelDO::getId); - // TODO @AI:最好是 channelService 也包出一个 getChannelMap;类似别的模块 - Map materialMap = convertMap( - channelMaterialService.getMaterialList(convertSet(pageResult.getList(), ImChannelMessageDO::getMaterialId)), - ImChannelMaterialDO::getId); - return success(BeanUtils.toBean(pageResult, ImChannelMessageRespVO.class, vo -> { - MapUtils.findAndThen(channelMap, vo.getChannelId(), c -> vo.setChannelName(c.getName())); - MapUtils.findAndThen(materialMap, vo.getMaterialId(), m -> vo.setMaterialTitle(m.getTitle())); - })); - } - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialPageReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialPageReqVO.java index 92f40f6..49cc930 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialPageReqVO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialPageReqVO.java @@ -18,8 +18,8 @@ public class ImChannelMaterialPageReqVO extends PageParam { @Schema(description = "频道编号", example = "1") private Long channelId; - @Schema(description = "素材类型", example = "125") - private Integer type; + @Schema(description = "内容类型", example = "1") + private Integer type; // 参见 ImChannelMaterialContentTypeEnum 枚举类 @Schema(description = "标题", example = "活动") private String title; diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialRespVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialRespVO.java index 4f0624c..033fac1 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialRespVO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialRespVO.java @@ -18,8 +18,8 @@ public class ImChannelMaterialRespVO { @Schema(description = "频道名称(关联查询填充)") private String channelName; - @Schema(description = "素材类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "125") - private Integer type; // 参见 ImMessageTypeEnum 枚举类 + @Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; // 参见 ImChannelMaterialContentTypeEnum 枚举类 @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED) private String title; diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialSaveReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialSaveReqVO.java index bc9a842..3f621a7 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialSaveReqVO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialSaveReqVO.java @@ -17,9 +17,9 @@ public class ImChannelMaterialSaveReqVO { @NotNull(message = "频道编号不能为空") private Long channelId; - @Schema(description = "素材类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "125") - @NotNull(message = "素材类型不能为空") - private Integer type; // 参见 ImMessageTypeEnum 枚举类 + @Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "内容类型不能为空") + private Integer type; // 参见 ImChannelMaterialContentTypeEnum 枚举类 @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "双十一活动来啦") @NotBlank(message = "标题不能为空") diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessagePageReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessagePageReqVO.java deleted file mode 100644 index 55a1c3f..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessagePageReqVO.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - IM 频道消息分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -public class ImChannelMessagePageReqVO extends PageParam { - - @Schema(description = "频道编号", example = "1") - private Long channelId; - - @Schema(description = "素材编号", example = "1024") - private Long materialId; - - @Schema(description = "发送时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] sendTime; - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageRespVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageRespVO.java deleted file mode 100644 index 05143e8..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageRespVO.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - IM 频道消息 Response VO") -@Data -public class ImChannelMessageRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "频道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long channelId; - - @Schema(description = "频道名称(关联查询填充)") - private String channelName; - - @Schema(description = "素材编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long materialId; - - @Schema(description = "素材标题(关联查询填充)") - private String materialTitle; - - @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "125") - private Integer type; // 参见 ImMessageTypeEnum 枚举类 - - @Schema(description = "消息内容;payload JSON 快照") - private String content; - - @Schema(description = "接收人编号列表;为空表示全员") - private List receiverUserIds; - - @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime sendTime; - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageSendReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageSendReqVO.java deleted file mode 100644 index e9bbd0c..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/message/ImChannelMessageSendReqVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.util.List; - -@Schema(description = "管理后台 - IM 频道消息推送 Request VO") -@Data -public class ImChannelMessageSendReqVO { - - @Schema(description = "素材编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "素材编号不能为空") - private Long materialId; - - @Schema(description = "接收人编号列表;为空表示全员", example = "[1024, 2048]") - private List receiverUserIds; - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImChannelMessageManagerController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImChannelMessageManagerController.java new file mode 100644 index 0000000..eeff931 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImChannelMessageManagerController.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.im.controller.admin.manager.message; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessagePageReqVO; +import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessageRespVO; +import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessageSendReqVO; +import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelDO; +import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMaterialDO; +import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; +import cn.iocoder.yudao.module.im.service.channel.ImChannelMaterialService; +import cn.iocoder.yudao.module.im.service.message.ImChannelMessageService; +import cn.iocoder.yudao.module.im.service.channel.ImChannelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管理后台 - IM 频道消息") +@RestController +@RequestMapping("/im/manager/channel-message") +@Validated +public class ImChannelMessageManagerController { + + @Resource + private ImChannelMessageService channelMessageService; + @Resource + private ImChannelService channelService; + @Resource + private ImChannelMaterialService channelMaterialService; + + @PostMapping("/send") + @Operation(summary = "立即推送频道消息") + @PreAuthorize("@ss.hasPermission('im:manager:channel-message:send')") + public CommonResult sendMessage(@Valid @RequestBody ImChannelMessageSendReqVO reqVO) { + return success(channelMessageService.sendMessage(reqVO)); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除频道消息") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('im:manager:channel-message:delete')") + public CommonResult deleteMessage(@RequestParam("id") Long id) { + channelMessageService.deleteMessage(id); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得频道消息分页;回填频道名 / 素材标题") + @PreAuthorize("@ss.hasPermission('im:manager:channel-message:query')") + public CommonResult> getMessagePage(@Valid ImChannelMessagePageReqVO pageReqVO) { + PageResult pageResult = channelMessageService.getMessagePage(pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + // 批量查询频道和素材,并回填频道名 / 素材标题 + Map channelMap = channelService.getChannelMap( + convertSet(pageResult.getList(), ImChannelMessageDO::getChannelId)); + Map materialMap = channelMaterialService.getMaterialMap( + convertSet(pageResult.getList(), ImChannelMessageDO::getMaterialId)); + return success(BeanUtils.toBean(pageResult, ImChannelMessageRespVO.class, vo -> { + MapUtils.findAndThen(channelMap, vo.getChannelId(), c -> vo.setChannelName(c.getName())); + MapUtils.findAndThen(materialMap, vo.getMaterialId(), m -> vo.setMaterialTitle(m.getTitle())); + })); + } + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessagePageReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessagePageReqVO.java new file mode 100644 index 0000000..48e1a44 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessagePageReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - IM 频道消息分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class ImChannelMessagePageReqVO extends PageParam { + + @Schema(description = "频道编号", example = "1") + private Long channelId; + + @Schema(description = "素材编号", example = "1024") + private Long materialId; + + @Schema(description = "发送时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] sendTime; + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageRespVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageRespVO.java new file mode 100644 index 0000000..07e11d7 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageRespVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - IM 频道消息 Response VO") +@Data +public class ImChannelMessageRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "频道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long channelId; + + @Schema(description = "频道名称(关联查询填充)") + private String channelName; + + @Schema(description = "素材编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long materialId; + + @Schema(description = "素材标题(关联查询填充)") + private String materialTitle; + + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "125") + private Integer type; // 参见 ImMessageTypeEnum 枚举类 + + @Schema(description = "消息内容;payload JSON 快照") + private String content; + + @Schema(description = "接收人编号列表;为空表示全员") + private List receiverUserIds; + + @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime sendTime; + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageSendReqVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageSendReqVO.java new file mode 100644 index 0000000..a69ff16 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageSendReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - IM 频道消息推送 Request VO") +@Data +public class ImChannelMessageSendReqVO { + + @Schema(description = "素材编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "素材编号不能为空") + private Long materialId; + + @Schema(description = "接收人编号列表;为空表示全员", example = "[1024, 2048]") + private List receiverUserIds; + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImChannelMessageController.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImChannelMessageController.java new file mode 100644 index 0000000..eb6f779 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImChannelMessageController.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.im.controller.admin.message; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.im.controller.admin.message.vo.channel.ImChannelMessagePullRespVO; +import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; +import cn.iocoder.yudao.module.im.service.message.ImChannelMessageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.PositiveOrZero; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 APP - IM 频道消息") +@RestController +@RequestMapping("/im/channel/message") +@Validated +public class ImChannelMessageController { + + @Resource + private ImChannelMessageService channelMessageService; + + @GetMapping("/pull") + @Operation(summary = "拉取频道消息(离线增量);按 minId 游标分页") + public CommonResult> pull( + @RequestParam(value = "minId", defaultValue = "0") @PositiveOrZero(message = "minId 不能小于 0") Long minId, + @RequestParam(value = "size", defaultValue = "100") + @Min(value = 1, message = "size 必须大于 0") + @Max(value = 200, message = "size 一次最多 200 条") Integer size) { + List list = channelMessageService.getMessageListForPull(getLoginUserId(), minId, size); + return success(BeanUtils.toBean(list, ImChannelMessagePullRespVO.class)); + } + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/channel/ImChannelMessagePullRespVO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/channel/ImChannelMessagePullRespVO.java new file mode 100644 index 0000000..3aecea1 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/channel/ImChannelMessagePullRespVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.im.controller.admin.message.vo.channel; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 APP - IM 频道消息拉取 Response VO") +@Data +public class ImChannelMessagePullRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9001") + private Long id; + + @Schema(description = "频道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long channelId; + + @Schema(description = "素材编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long materialId; + + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "125") + private Integer type; + + @Schema(description = "消息内容;payload JSON 快照", requiredMode = Schema.RequiredMode.REQUIRED) + private String content; + + @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime sendTime; + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMaterialDO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMaterialDO.java index d38dabc..cb85869 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMaterialDO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMaterialDO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.im.dal.dataobject.channel; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; +import cn.iocoder.yudao.module.im.enums.channel.ImChannelMaterialContentTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -39,9 +39,9 @@ public class ImChannelMaterialDO extends BaseDO { */ private Long channelId; /** - * 素材类型 + * 素材内容类型 *

- * 枚举 {@link ImMessageTypeEnum};当前仅 MATERIAL(125) + * 枚举 {@link ImChannelMaterialContentTypeEnum} */ private Integer type; /** @@ -56,15 +56,12 @@ public class ImChannelMaterialDO extends BaseDO { * 摘要 */ private String summary; - // TODO @AI:素材类型,是不是有个 type;这样一个 content、url 更好区分;然后这个 type ,是不是在 MATERIAL Message 类上,也带上; /** - * 正文;富文本 HTML + * 富文本 HTML;在 {@link ImChannelMaterialContentTypeEnum#CONTENT} 使用 */ private String content; /** - * 跳转链接 - *

- * 为空时点击在客户端内置详情页拉 {@link #content} 渲染;非空则跳 url + * 跳转链接;在 {@link ImChannelMaterialContentTypeEnum#LINK} 使用 */ private String url; diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMessageDO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMessageDO.java deleted file mode 100644 index b04dd2d..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMessageDO.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.yudao.module.im.dal.dataobject.channel; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; -import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -import java.time.LocalDateTime; -import java.util.List; - -/** - * IM 频道消息 DO - *

- * 业务语义: - * - 一次推送 1 行;{@link #receiverUserIds} 为空表示全员 - * - {@link #channelId} 冗余 {@link ImChannelMaterialDO#getChannelId()} 便于按频道检索 - * - {@link #content} 存推送时 payload 的 JSON 快照(title / coverUrl / summary / url);不含富文本正文 - * - * @author 芋道源码 - */ -@TableName(value = "im_channel_message", autoResultMap = true) -@KeySequence("im_channel_message_seq") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ImChannelMessageDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 频道编号 - *

- * 关联 {@link ImChannelDO#getId()};冗余便于按频道检索 - */ - private Long channelId; - /** - * 关联素材编号 - *

- * 关联 {@link ImChannelMaterialDO#getId()} - */ - private Long materialId; - /** - * 消息类型 - *

- * 枚举 {@link ImMessageTypeEnum} - */ - private Integer type; - /** - * 消息内容;推送时 payload 的 JSON 快照 - */ - private String content; - /** - * 接收人编号列表;为空表示全员 - */ - @TableField(typeHandler = LongListTypeHandler.class) - private List receiverUserIds; - /** - * 发送时间 - */ - private LocalDateTime sendTime; - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImChannelMessageDO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImChannelMessageDO.java new file mode 100644 index 0000000..c7f4171 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImChannelMessageDO.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.im.dal.dataobject.message; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * IM 频道消息 DO + *

+ * 业务语义: + * - 一次推送 1 行;{@link #receiverUserIds} 为空表示全员 + * - {@link #channelId} 冗余 {@link ImChannelMaterialDO#getChannelId()} 便于按频道检索 + * - {@link #content} 存推送时 payload 的 JSON 快照(title / coverUrl / summary / url);不含富文本正文 + * + * @author 芋道源码 + */ +@TableName(value = "im_channel_message", autoResultMap = true) +@KeySequence("im_channel_message_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ImChannelMessageDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 频道编号 + *

+ * 关联 {@link ImChannelDO#getId()};冗余便于按频道检索 + */ + private Long channelId; + /** + * 关联素材编号 + *

+ * 关联 {@link ImChannelMaterialDO#getId()} + */ + private Long materialId; + /** + * 消息类型 + *

+ * 枚举 {@link ImMessageTypeEnum} + */ + private Integer type; + /** + * 消息内容;推送时 payload 的 JSON 快照 + */ + private String content; + /** + * 接收人编号列表;为空表示全员 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List receiverUserIds; + /** + * 发送时间 + */ + private LocalDateTime sendTime; + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMaterialMapper.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMaterialMapper.java index a95f462..e7c2922 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMaterialMapper.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMaterialMapper.java @@ -20,21 +20,12 @@ public interface ImChannelMaterialMapper extends BaseMapperX selectPage(ImChannelMaterialPageReqVO reqVO) { - LambdaQueryWrapperX wrapper = new LambdaQueryWrapperX() + return selectPage(reqVO, new LambdaQueryWrapperX() .eqIfPresent(ImChannelMaterialDO::getChannelId, reqVO.getChannelId()) .eqIfPresent(ImChannelMaterialDO::getType, reqVO.getType()) .likeIfPresent(ImChannelMaterialDO::getTitle, reqVO.getTitle()) .betweenIfPresent(ImChannelMaterialDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(ImChannelMaterialDO::getId); - // TODO @AI:不用排除;列表问题不大的; - // 列表查询排除富文本 content 字段,避免 mediumtext 拖垮分页响应 - wrapper.select(ImChannelMaterialDO::getId, ImChannelMaterialDO::getChannelId, - ImChannelMaterialDO::getType, ImChannelMaterialDO::getTitle, - ImChannelMaterialDO::getCoverUrl, ImChannelMaterialDO::getSummary, - ImChannelMaterialDO::getUrl, ImChannelMaterialDO::getCreator, - ImChannelMaterialDO::getCreateTime, ImChannelMaterialDO::getUpdater, - ImChannelMaterialDO::getUpdateTime, ImChannelMaterialDO::getDeleted); - return selectPage(reqVO, wrapper); + .orderByDesc(ImChannelMaterialDO::getId)); } } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMessageMapper.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMessageMapper.java deleted file mode 100644 index 333c25f..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMessageMapper.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.im.dal.mysql.channel; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message.ImChannelMessagePageReqVO; -import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMessageDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IM 频道消息 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface ImChannelMessageMapper extends BaseMapperX { - - /** - * 拉取指定用户应收的频道消息 - *

- * 命中条件:id 大于游标 + (receiver_user_ids 为空表示全员 OR 逗号分隔列表里包含当前 userId) - * - * @param userId 当前用户编号 - * @param minId 游标;返回大于此值的消息 - * @param size 返回条数 - * @return 频道消息列表;按 id 升序 - */ - default List selectListByUserAndMinId(Long userId, Long minId, Integer size) { - return selectList(new LambdaQueryWrapperX() - .gt(ImChannelMessageDO::getId, minId) - .and(w -> w.isNull(ImChannelMessageDO::getReceiverUserIds) - .or().eq(ImChannelMessageDO::getReceiverUserIds, "") - .or().apply("FIND_IN_SET({0}, receiver_user_ids)", userId)) - .orderByAsc(ImChannelMessageDO::getId) - .last("LIMIT " + size)); - } - - default Long selectCountByMaterialId(Long materialId) { - return selectCount(ImChannelMessageDO::getMaterialId, materialId); - } - - default PageResult selectPage(ImChannelMessagePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(ImChannelMessageDO::getChannelId, reqVO.getChannelId()) - .eqIfPresent(ImChannelMessageDO::getMaterialId, reqVO.getMaterialId()) - .betweenIfPresent(ImChannelMessageDO::getSendTime, reqVO.getSendTime()) - .orderByDesc(ImChannelMessageDO::getId)); - } - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImChannelMessageMapper.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImChannelMessageMapper.java new file mode 100644 index 0000000..81f9238 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImChannelMessageMapper.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.im.dal.mysql.message; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessagePageReqVO; +import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * IM 频道消息 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ImChannelMessageMapper extends BaseMapperX { + + /** + * 拉取指定用户应收的频道消息 + *

+ * 命中条件:id 大于游标 + (receiver_user_ids 为空表示全员 OR 逗号分隔列表里包含当前 userId) + * + * @param userId 当前用户编号 + * @param minId 游标;返回大于此值的消息 + * @param size 返回条数 + * @return 频道消息列表;按 id 升序 + */ + default List selectListByUserAndMinId(Long userId, Long minId, Integer size) { + return selectList(new LambdaQueryWrapperX() + .gt(ImChannelMessageDO::getId, minId) + .and(w -> w.isNull(ImChannelMessageDO::getReceiverUserIds) + .or().eq(ImChannelMessageDO::getReceiverUserIds, "") + .or().apply("FIND_IN_SET({0}, receiver_user_ids)", userId)) + .orderByAsc(ImChannelMessageDO::getId) + .last("LIMIT " + size)); + } + + default Long selectCountByMaterialId(Long materialId) { + return selectCount(ImChannelMessageDO::getMaterialId, materialId); + } + + default PageResult selectPage(ImChannelMessagePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(ImChannelMessageDO::getChannelId, reqVO.getChannelId()) + .eqIfPresent(ImChannelMessageDO::getMaterialId, reqVO.getMaterialId()) + .betweenIfPresent(ImChannelMessageDO::getSendTime, reqVO.getSendTime()) + .orderByDesc(ImChannelMessageDO::getId)); + } + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/channel/ImChannelMaterialContentTypeEnum.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/channel/ImChannelMaterialContentTypeEnum.java new file mode 100644 index 0000000..90a2776 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/channel/ImChannelMaterialContentTypeEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.im.enums.channel; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * IM 频道素材内容类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum ImChannelMaterialContentTypeEnum implements ArrayValuable { + + /** + * 站内富文本;点击素材在客户端内置详情页拉 content 渲染 + */ + CONTENT(1, "站内富文本"), + /** + * 外链;点击素材跳 url 打开浏览器 + */ + LINK(2, "外链"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(ImChannelMaterialContentTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialService.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialService.java index a2d18f4..51ade59 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialService.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialService.java @@ -8,6 +8,9 @@ import jakarta.validation.Valid; import java.util.Collection; import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * IM 频道素材 Service 接口 @@ -19,14 +22,6 @@ public interface ImChannelMaterialService { // ==================== 用户端 ==================== /** - * 获取素材正文;用于客户端点击图文卡片时拉富文本 - * - * @param id 素材编号 - * @return 富文本 HTML - */ - String getMaterialContent(Long id); - - /** * 校验素材存在 * * @param id 素材编号 @@ -43,6 +38,16 @@ public interface ImChannelMaterialService { List getMaterialList(Collection ids); /** + * 按编号批量查询素材 Map + * + * @param ids 素材编号列表 + * @return id -> 素材 Map + */ + default Map getMaterialMap(Collection ids) { + return convertMap(getMaterialList(ids), ImChannelMaterialDO::getId); + } + + /** * 统计指定频道下的素材数量 * * @param channelId 频道编号 diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialServiceImpl.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialServiceImpl.java index 7e72ace..17a1c47 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialServiceImpl.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialServiceImpl.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.material.I import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.material.ImChannelMaterialSaveReqVO; import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMaterialDO; import cn.iocoder.yudao.module.im.dal.mysql.channel.ImChannelMaterialMapper; -import cn.iocoder.yudao.module.im.dal.mysql.channel.ImChannelMessageMapper; +import cn.iocoder.yudao.module.im.dal.mysql.message.ImChannelMessageMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -39,12 +39,6 @@ public class ImChannelMaterialServiceImpl implements ImChannelMaterialService { // ==================== 用户端 ==================== @Override - public String getMaterialContent(Long id) { - ImChannelMaterialDO material = validateMaterialExists(id); - return material.getContent(); - } - - @Override public ImChannelMaterialDO validateMaterialExists(Long id) { ImChannelMaterialDO material = channelMaterialMapper.selectById(id); if (material == null) { @@ -78,20 +72,25 @@ public class ImChannelMaterialServiceImpl implements ImChannelMaterialService { return channelMaterialMapper.selectById(id); } - // TODO @AI:类似别的模块,写全注释 @Override public Long createMaterial(ImChannelMaterialSaveReqVO reqVO) { + // 1. 校验所属频道存在 channelService.validateChannelExists(reqVO.getChannelId()); + + // 2. 插入素材 ImChannelMaterialDO material = BeanUtils.toBean(reqVO, ImChannelMaterialDO.class); channelMaterialMapper.insert(material); return material.getId(); } - // TODO @AI:类似别的模块,写全注释 @Override public void updateMaterial(ImChannelMaterialSaveReqVO reqVO) { + // 1.1 校验存在 validateMaterialExists(reqVO.getId()); + // 1.2 校验所属频道存在 channelService.validateChannelExists(reqVO.getChannelId()); + + // 2. 更新素材 ImChannelMaterialDO updateObj = BeanUtils.toBean(reqVO, ImChannelMaterialDO.class); channelMaterialMapper.updateById(updateObj); } diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageService.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageService.java deleted file mode 100644 index e2a21f0..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageService.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.im.service.channel; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message.ImChannelMessagePageReqVO; -import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message.ImChannelMessageSendReqVO; -import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMessageDO; -import jakarta.validation.Valid; - -import java.util.List; - -// TODO @AI:这块消息,我想挪到 message 里;后续,可能整合成一个表存储的; -/** - * IM 频道消息 Service 接口 - * - * @author 芋道源码 - */ -public interface ImChannelMessageService { - - // ==================== 用户端 ==================== - - /** - * 拉取当前用户应收的频道消息(离线增量) - * - * @param userId 当前用户编号 - * @param minId 游标;返回大于此值的消息 - * @param size 返回条数 - * @return 频道消息列表;按 id 升序 - */ - List getMessageListForPull(Long userId, Long minId, Integer size); - - - // ==================== 管理后台 ==================== - - /** - * 立即推送频道消息 - *

- * 流程:读 material → 组装 payload → INSERT 1 行 im_channel_message → WebSocket 扇出 - * - * @param reqVO 推送请求 - * @return 消息编号 - */ - Long sendMessage(@Valid ImChannelMessageSendReqVO reqVO); - - /** - * 分页查询消息 - * - * @param reqVO 分页查询条件 - * @return 消息分页 - */ - PageResult getMessagePage(ImChannelMessagePageReqVO reqVO); - - /** - * 删除消息 - * - * @param id 消息编号 - */ - void deleteMessage(Long id); - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageServiceImpl.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageServiceImpl.java deleted file mode 100644 index cfeeb58..0000000 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMessageServiceImpl.java +++ /dev/null @@ -1,88 +0,0 @@ -package cn.iocoder.yudao.module.im.service.channel; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message.ImChannelMessagePageReqVO; -import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.message.ImChannelMessageSendReqVO; -import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMaterialDO; -import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMessageDO; -import cn.iocoder.yudao.module.im.dal.mysql.channel.ImChannelMessageMapper; -import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; -import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; -import cn.iocoder.yudao.module.im.service.websocket.dto.ImChannelMessageDTO; -import cn.iocoder.yudao.module.im.service.websocket.dto.message.MaterialMessage; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.IM_CHANNEL_MESSAGE_NOT_EXISTS; - -/** - * IM 频道消息 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class ImChannelMessageServiceImpl implements ImChannelMessageService { - - @Resource - private ImChannelMessageMapper channelMessageMapper; - @Resource - private ImChannelMaterialService channelMaterialService; - @Resource - private ImWebSocketService webSocketService; - - // ==================== 用户端 ==================== - - @Override - public List getMessageListForPull(Long userId, Long minId, Integer size) { - return channelMessageMapper.selectListByUserAndMinId(userId, minId, size); - } - - // ==================== 管理后台 ==================== - - @Override - @Transactional(rollbackFor = Exception.class) - public Long sendMessage(ImChannelMessageSendReqVO reqVO) { - // 1. 校验素材存在 - ImChannelMaterialDO material = channelMaterialService.validateMaterialExists(reqVO.getMaterialId()); - - // 2.1 组装 payload(不带富文本正文);字段同名直接 BeanUtils 拷贝 - String payloadJson = JsonUtils.toJsonString(BeanUtils.toBean(material, MaterialMessage.class)); - // 2.2 落库 1 行 message;reqVO 同名字段(materialId / receiverUserIds)自动拷贝,剩余字段补 set - ImChannelMessageDO message = BeanUtils.toBean(reqVO, ImChannelMessageDO.class).setChannelId(material.getChannelId()) - .setType(ImMessageTypeEnum.MATERIAL.getType()).setContent(payloadJson).setSendTime(LocalDateTime.now()); - channelMessageMapper.insert(message); - - // 3. 异步推 WebSocket:指定用户走点对点;全员(receiverUserIds 为空)走广播 - ImChannelMessageDTO dto = ImChannelMessageDTO.ofSend(message); - if (CollUtil.isNotEmpty(reqVO.getReceiverUserIds())) { - webSocketService.sendChannelMessageAsync(reqVO.getReceiverUserIds(), dto); - } else { - webSocketService.broadcastChannelMessageAsync(dto); - } - return message.getId(); - } - - @Override - public PageResult getMessagePage(ImChannelMessagePageReqVO reqVO) { - return channelMessageMapper.selectPage(reqVO); - } - - @Override - public void deleteMessage(Long id) { - if (channelMessageMapper.selectById(id) == null) { - throw exception(IM_CHANNEL_MESSAGE_NOT_EXISTS); - } - channelMessageMapper.deleteById(id); - } - -} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelService.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelService.java index 5b1263f..b318743 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelService.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelService.java @@ -8,6 +8,9 @@ import jakarta.validation.Valid; import java.util.Collection; import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * IM 频道 Service 接口 @@ -18,13 +21,13 @@ public interface ImChannelService { // ==================== 用户端 ==================== - // TODO @AI:getChannelListByStatus 这种形式; /** - * 获取所有启用的频道,按 sort 升序 + * 按状态查询频道列表,按 sort 升序 * - * @return 启用的频道列表 + * @param status 状态;对应 CommonStatusEnum + * @return 频道列表 */ - List getEnabledChannelList(); + List getChannelListByStatus(Integer status); /** * 按编号批量查询频道 @@ -35,6 +38,16 @@ public interface ImChannelService { List getChannelList(Collection ids); /** + * 按编号批量查询频道 Map + * + * @param ids 频道编号列表 + * @return id -> 频道 Map + */ + default Map getChannelMap(Collection ids) { + return convertMap(getChannelList(ids), ImChannelDO::getId); + } + + /** * 校验频道存在 * * @param id 频道编号 diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelServiceImpl.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelServiceImpl.java index 1d04538..cdae8da 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelServiceImpl.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelServiceImpl.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.im.service.channel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.channel.ImChannelPageReqVO; @@ -18,9 +17,7 @@ import java.util.Collections; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.IM_CHANNEL_CODE_DUPLICATED; -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.IM_CHANNEL_HAS_MATERIAL; -import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.IM_CHANNEL_NOT_EXISTS; +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*; /** * IM 频道 Service 实现类 @@ -39,8 +36,8 @@ public class ImChannelServiceImpl implements ImChannelService { // ==================== 用户端 ==================== @Override - public List getEnabledChannelList() { - return channelMapper.selectListByStatusOrderBySort(CommonStatusEnum.ENABLE.getStatus()); + public List getChannelListByStatus(Integer status) { + return channelMapper.selectListByStatusOrderBySort(status); } @Override diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageService.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageService.java new file mode 100644 index 0000000..9c3cb2b --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageService.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.im.service.message; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessagePageReqVO; +import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessageSendReqVO; +import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * IM 频道消息 Service 接口 + * + * @author 芋道源码 + */ +public interface ImChannelMessageService { + + // ==================== 用户端 ==================== + + /** + * 拉取当前用户应收的频道消息(离线增量) + * + * @param userId 当前用户编号 + * @param minId 游标;返回大于此值的消息 + * @param size 返回条数 + * @return 频道消息列表;按 id 升序 + */ + List getMessageListForPull(Long userId, Long minId, Integer size); + + + // ==================== 管理后台 ==================== + + /** + * 立即推送频道消息 + * + * @param reqVO 推送请求 + * @return 消息编号 + */ + Long sendMessage(@Valid ImChannelMessageSendReqVO reqVO); + + /** + * 分页查询消息 + * + * @param reqVO 分页查询条件 + * @return 消息分页 + */ + PageResult getMessagePage(ImChannelMessagePageReqVO reqVO); + + /** + * 删除消息 + * + * @param id 消息编号 + */ + void deleteMessage(Long id); + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImpl.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImpl.java new file mode 100644 index 0000000..f8e3e51 --- /dev/null +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImpl.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.im.service.message; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessagePageReqVO; +import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessageSendReqVO; +import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMaterialDO; +import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; +import cn.iocoder.yudao.module.im.dal.mysql.message.ImChannelMessageMapper; +import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; +import cn.iocoder.yudao.module.im.service.channel.ImChannelMaterialService; +import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; +import cn.iocoder.yudao.module.im.service.websocket.dto.ImChannelMessageDTO; +import cn.iocoder.yudao.module.im.service.websocket.dto.message.MaterialMessage; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.IM_CHANNEL_MESSAGE_NOT_EXISTS; + +/** + * IM 频道消息 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ImChannelMessageServiceImpl implements ImChannelMessageService { + + @Resource + private ImChannelMessageMapper channelMessageMapper; + @Resource + private ImChannelMaterialService channelMaterialService; + @Resource + private ImWebSocketService webSocketService; + + // ==================== 用户端 ==================== + + @Override + public List getMessageListForPull(Long userId, Long minId, Integer size) { + return channelMessageMapper.selectListByUserAndMinId(userId, minId, size); + } + + // ==================== 管理后台 ==================== + + @Override + @Transactional(rollbackFor = Exception.class) + public Long sendMessage(ImChannelMessageSendReqVO reqVO) { + // 1. 校验素材存在 + ImChannelMaterialDO material = channelMaterialService.validateMaterialExists(reqVO.getMaterialId()); + + // 2.1 组装 payload(不带富文本正文);字段同名直接 BeanUtils 拷贝 + String payloadJson = JsonUtils.toJsonString(BeanUtils.toBean(material, MaterialMessage.class)); + // 2.2 落库 1 行 message;reqVO 同名字段(materialId / receiverUserIds)自动拷贝,剩余字段补 set + ImChannelMessageDO message = BeanUtils.toBean(reqVO, ImChannelMessageDO.class).setChannelId(material.getChannelId()) + .setType(ImMessageTypeEnum.MATERIAL.getType()).setContent(payloadJson).setSendTime(LocalDateTime.now()); + channelMessageMapper.insert(message); + + // 3. 异步推 WebSocket:指定用户走点对点;全员(receiverUserIds 为空)走广播 + ImChannelMessageDTO dto = ImChannelMessageDTO.ofSend(message); + if (CollUtil.isNotEmpty(reqVO.getReceiverUserIds())) { + webSocketService.sendChannelMessageAsync(reqVO.getReceiverUserIds(), dto); + } else { + webSocketService.broadcastChannelMessageAsync(dto); + } + return message.getId(); + } + + @Override + public PageResult getMessagePage(ImChannelMessagePageReqVO reqVO) { + return channelMessageMapper.selectPage(reqVO); + } + + @Override + public void deleteMessage(Long id) { + if (channelMessageMapper.selectById(id) == null) { + throw exception(IM_CHANNEL_MESSAGE_NOT_EXISTS); + } + channelMessageMapper.deleteById(id); + } + +} diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImChannelMessageDTO.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImChannelMessageDTO.java index 15bf71d..d137c25 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImChannelMessageDTO.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImChannelMessageDTO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.im.service.websocket.dto; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMessageDO; +import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; import lombok.Data; import lombok.experimental.Accessors; diff --git a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/message/MaterialMessage.java b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/message/MaterialMessage.java index a7c835c..10f8d4d 100644 --- a/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/message/MaterialMessage.java +++ b/yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/message/MaterialMessage.java @@ -15,6 +15,14 @@ import lombok.experimental.Accessors; public class MaterialMessage { /** + * 素材内容类型 + *

+ * 枚举 {@link cn.iocoder.yudao.module.im.enums.channel.ImChannelMaterialContentTypeEnum} + * 客户端按本字段判定点击行为:CONTENT 走站内详情页拉富文本;LINK 跳 url + */ + private Integer type; + + /** * 标题 */ private String title; @@ -27,7 +35,7 @@ public class MaterialMessage { */ private String summary; /** - * 跳转链接;为空时点击在客户端内置详情页按 materialId 拉 content 渲染;非空则跳 url + * 跳转链接 */ private String url; diff --git a/yudao-module-im/src/test/resources/sql/create_tables.sql b/yudao-module-im/src/test/resources/sql/create_tables.sql index 2822bcf..f10783a 100644 --- a/yudao-module-im/src/test/resources/sql/create_tables.sql +++ b/yudao-module-im/src/test/resources/sql/create_tables.sql @@ -255,7 +255,7 @@ CREATE TABLE IF NOT EXISTS "im_channel" ( CREATE TABLE IF NOT EXISTS "im_channel_material" ( "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号', "channel_id" bigint NOT NULL COMMENT '频道编号', - "type" smallint NOT NULL COMMENT '素材类型;对应 ImMessageTypeEnum', + "type" tinyint NOT NULL COMMENT '内容类型;1 站内富文本 2 外链', "title" varchar(128) NOT NULL COMMENT '标题', "cover_url" varchar(512) DEFAULT NULL COMMENT '封面图', "summary" varchar(255) DEFAULT NULL COMMENT '摘要', diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java index f619898..60263b6 100644 --- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java @@ -116,7 +116,8 @@ public class UserController { @GetMapping({"/list-all-simple", "/simple-list"}) @Operation(summary = "获取用户精简信息列表", description = "只包含被开启的用户,主要用于前端的下拉选项") - public CommonResult> getSimpleUserList(@RequestParam("id") Long deptId) { + public CommonResult> getSimpleUserList( + @RequestParam(value = "deptId", required = false) Long deptId) { List list; if (deptId != null) { List deptIds = Collections.singletonList(deptId); -- libgit2 0.21.4