修改:代码优化

This commit is contained in:
安浩浩 2024-03-31 22:47:18 +08:00
parent 2b891cb432
commit fa23ce144d
54 changed files with 538 additions and 826 deletions

View File

@ -6,7 +6,6 @@ import lombok.Getter;
import java.util.Arrays;
// TODO @anhaohaoIM 前缀还是要的哈
/**
* IM 会话类型枚举
* 参考 <a href="https://doc.rentsoft.cn/zh-Hans/sdks/enum/conversationType">会话类型</a> 文档
@ -15,13 +14,13 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum ConversationTypeEnum implements IntArrayValuable {
public enum ImConversationTypeEnum implements IntArrayValuable {
SINGLE(1, "单聊"),
GROUP(3, "群聊"),
NOTIFICATION(4, "通知会话");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ConversationTypeEnum::getType).toArray();
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ImConversationTypeEnum::getType).toArray();
/**
* 类型
@ -33,25 +32,26 @@ public enum ConversationTypeEnum implements IntArrayValuable {
*/
private final String name;
@Override
public int[] array() {
return ARRAYS;
}
/**
* 生成会话编号 TODO @anhaohao方法注释和下面参数之间要有空格
* @param fromUserId 发送者编号
* @param receiverId 接收者编号
* 生成会话编号
*
* @param fromUserId 发送者编号
* @param receiverId 接收者编号
* @param conversationType 会话类型
* @return 会话编号
*/
public static String generateConversationNo(Long fromUserId, Long receiverId, Integer conversationType) {
if (conversationType.equals(ConversationTypeEnum.SINGLE.getType())) {
if (conversationType.equals(ImConversationTypeEnum.SINGLE.getType())) {
return "s_" + fromUserId + "_" + receiverId;
} else if (conversationType.equals(ConversationTypeEnum.GROUP.getType())) {
} else if (conversationType.equals(ImConversationTypeEnum.GROUP.getType())) {
return "g_" + receiverId;
}
return null;
}
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -6,7 +6,6 @@ import lombok.Getter;
import java.util.Arrays;
// TODO @anhaohaoIM 前缀还是要的哈
/**
* IM 消息的类型枚举
* <p>
@ -16,7 +15,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum MessageContentTypeEnum implements IntArrayValuable {
public enum ImMessageContentTypeEnum implements IntArrayValuable {
TEXT(101, "文本消息"),
PICTURE(102, "图片消息"),
@ -57,7 +56,7 @@ public enum MessageContentTypeEnum implements IntArrayValuable {
BURN_CHANGE(1701, "阅后即焚开启或关闭通知"),
REVOKE(2101, "撤回消息通知");;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MessageContentTypeEnum::getType).toArray();
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ImMessageContentTypeEnum::getType).toArray();
/**
* 类型

View File

@ -6,24 +6,22 @@ import lombok.RequiredArgsConstructor;
import java.util.Arrays;
// TODO @anhaohaoIM 前缀还是要的哈
/**
* IM 消息的消息来源
*/
@RequiredArgsConstructor
@Getter
public enum MessageSourceEnum implements IntArrayValuable {
public enum ImMessageSourceEnum implements IntArrayValuable {
USER_SEND(100, "用户发送"),
SYSTEM_SEND(200, "系统发送");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MessageSourceEnum::getStatus).toArray();
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ImMessageSourceEnum::getSource).toArray();
// TODO @anhaohao应该是 source
/**
* 状态
*/
private final Integer status;
private final Integer source;
/**
* 名字
*/

View File

@ -6,27 +6,21 @@ import lombok.RequiredArgsConstructor;
import java.util.Arrays;
// TODO @anhaohaoIM 前缀还是要的哈
// TODO TODO 状态是这些哈客户端的视角
//
//- 草稿预留 0
//- 发送中 1
//- 发送成功 2
//- 发送失败 3
//- 已删除 4
//- 已撤回 5
/**
* IM 消息的状态枚举
*/
@RequiredArgsConstructor
@Getter
public enum MessageStatusEnum implements IntArrayValuable {
public enum ImMessageStatusEnum implements IntArrayValuable {
DRAFT(0, "草稿"),
SENDING(1, "发送中"),
SUCCEED(2, "发送成功"),
FAILED(3, "发送失败");
SUCCESS(2, "发送成功"),
FAILURE(3, "发送失败"),
DELETED(4, "已删除"),
RECALL(5, "已撤回");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MessageStatusEnum::getStatus).toArray();
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ImMessageStatusEnum::getStatus).toArray();
/**
* 状态

View File

@ -2,10 +2,10 @@ package cn.iocoder.yudao.module.im.controller.admin.conversation;
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.conversation.vo.ConversationLastTimeReqVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationUpdateLastReadTimeReqVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationRespVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationPinnedReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationUpdatePinnedReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
import cn.iocoder.yudao.module.im.service.conversation.ConversationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -17,13 +17,13 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
// TODO @anhaohaoim 前缀少啦
@Tag(name = "管理后台 - IM 会话")
@RestController
@RequestMapping("/im/conversation")
@Validated
public class ConversationController {
public class ImConversationController {
@Resource
private ConversationService conversationService;
@ -31,23 +31,21 @@ public class ConversationController {
@GetMapping("/list")
@Operation(summary = "获得用户的会话列表")
public CommonResult<List<ImConversationRespVO>> getConversationList() {
List<ConversationDO> conversationList = conversationService.getConversationList();
List<ImConversationDO> conversationList = conversationService.getConversationList();
return success(BeanUtils.toBean(conversationList, ImConversationRespVO.class));
}
// TODO @hao这个接口需要单独的 VO
@PostMapping("/update-pinned")
@Operation(summary = "置顶会话")
public CommonResult<Boolean> updatePinned(@Valid @RequestBody ConversationPinnedReqVO updateReqVO) {
conversationService.updatePinned(updateReqVO);
public CommonResult<Boolean> updatePinned(@Valid @RequestBody ImConversationUpdatePinnedReqVO updateReqVO) {
conversationService.updatePinned(getLoginUserId(),updateReqVO);
return success(true);
}
// TODO @hao这个接口需要单独的 VO
@PostMapping("/update-last-read-time")
@Operation(summary = "更新最后已读时间")
public CommonResult<Boolean> updateLastReadTime(@Valid @RequestBody ConversationLastTimeReqVO updateReqVO) {
conversationService.updateLastReadTime(updateReqVO);
public CommonResult<Boolean> updateLastReadTime(@Valid @RequestBody ImConversationUpdateLastReadTimeReqVO updateReqVO) {
conversationService.updateLastReadTime(getLoginUserId(),updateReqVO);
return success(true);
}

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.conversation.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
// TODO @anhaohao改成 ConversationUpdateLastReadTimeReqVO项目目前都是动名词哈更新置顶
@Schema(description = "管理后台 - 会话最后已读时间 Request VO")
@Data
public class ConversationLastTimeReqVO {
// TODO @anhaohaono 不用传递哈因为 userId + targetId 可以推出来
@Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "s_1_2")
@NotEmpty(message = "会话标志不能为空")
private String no;
@Schema(description = "最后已读时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-03-01 00:00:00")
@NotNull(message = "最后已读时间不能为空")
private LocalDateTime lastReadTime;
// TODO @anhaohaouserId 不用传递因为 token 已经能解析出当前用户
@Schema(description = "所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
@Schema(description = "聊天对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Long targetId;
@Schema(description = "会话类型",requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type; // 枚举 ConversationTypeEnum TODO 这里可以使用 @InEnum 校验这样这个注释就不用写了
}

View File

@ -1,35 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.conversation.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
// TODO @anhaohao改成 ConversationUpdatePinnedReqVO项目目前都是动名词哈更新置顶
@Schema(description = "管理后台 - 会话置顶 Request VO")
@Data
public class ConversationPinnedReqVO {
// TODO @anhaohaono 不用传递哈因为 userId + targetId 可以推出来
@Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "s_1_2")
@NotEmpty(message = "会话标志不能为空")
private String no;
@Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "是否置顶不能为空")
private Boolean pinned;
// TODO @anhaohaouserId 不用传递因为 token 已经能解析出当前用户
@Schema(description = "所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
@Schema(description = "聊天对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Long targetId;
@Schema(description = "会话类型",requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type; // 枚举 ConversationTypeEnum TODO 这里可以使用 @InEnum 校验这样这个注释就不用写了
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.im.controller.admin.conversation.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 会话最后已读时间 Request VO")
@Data
public class ImConversationUpdateLastReadTimeReqVO {
@Schema(description = "最后已读时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-03-01 00:00:00")
@NotNull(message = "最后已读时间不能为空")
private LocalDateTime lastReadTime;
@Schema(description = "聊天对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Long targetId;
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(value = ImConversationTypeEnum.class, message = "会话类型必须是 {value}")
private Integer type;
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.im.controller.admin.conversation.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 会话置顶 Request VO")
@Data
public class ImConversationUpdatePinnedReqVO {
@Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "是否置顶不能为空")
private Boolean pinned;
@Schema(description = "聊天对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Long targetId;
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(value = ImConversationTypeEnum.class, message = "会话类型必须是 {value}")
private Integer type;
}

View File

@ -25,8 +25,8 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
import cn.iocoder.yudao.module.im.service.group.ImGroupService;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.module.im.service.group.GroupService;
// TODO @芋艿得看看 createupdatedeletegetpage 这几个接口要保留哪些
@Tag(name = "管理后台 - 群")
@ -36,20 +36,20 @@ import cn.iocoder.yudao.module.im.service.group.ImGroupService;
public class ImGroupController {
@Resource
private ImGroupService imGroupService;
private GroupService groupService;
@PostMapping("/create")
@Operation(summary = "创建群")
@PreAuthorize("@ss.hasPermission('im:group:create')")
public CommonResult<Long> createGroup(@Valid @RequestBody ImGroupSaveReqVO createReqVO) {
return success(imGroupService.createGroup(createReqVO));
return success(groupService.createGroup(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新群")
@PreAuthorize("@ss.hasPermission('im:group:update')")
public CommonResult<Boolean> updateGroup(@Valid @RequestBody ImGroupSaveReqVO updateReqVO) {
imGroupService.updateGroup(updateReqVO);
groupService.updateGroup(updateReqVO);
return success(true);
}
@ -58,7 +58,7 @@ public class ImGroupController {
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('im:group:delete')")
public CommonResult<Boolean> deleteGroup(@RequestParam("id") Long id) {
imGroupService.deleteGroup(id);
groupService.deleteGroup(id);
return success(true);
}
@ -67,7 +67,7 @@ public class ImGroupController {
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('im:group:query')")
public CommonResult<ImGroupRespVO> getGroup(@RequestParam("id") Long id) {
GroupDO group = imGroupService.getGroup(id);
ImGroupDO group = groupService.getGroup(id);
return success(BeanUtils.toBean(group, ImGroupRespVO.class));
}
@ -75,22 +75,8 @@ public class ImGroupController {
@Operation(summary = "获得群分页")
@PreAuthorize("@ss.hasPermission('im:group:query')")
public CommonResult<PageResult<ImGroupRespVO>> getGroupPage(@Valid ImGroupPageReqVO pageReqVO) {
PageResult<GroupDO> pageResult = imGroupService.getGroupPage(pageReqVO);
PageResult<ImGroupDO> pageResult = groupService.getGroupPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ImGroupRespVO.class));
}
// TODO @anhaohao导出可以先不做哈
@GetMapping("/export-excel")
@Operation(summary = "导出群 Excel")
@PreAuthorize("@ss.hasPermission('im:group:export')")
@OperateLog(type = EXPORT)
public void exportGroupExcel(@Valid ImGroupPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<GroupDO> list = imGroupService.getGroupPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "群.xls", "数据", ImGroupRespVO.class,
BeanUtils.toBean(list, ImGroupRespVO.class));
}
}

View File

@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberRespVO;
import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberSaveReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
import cn.iocoder.yudao.module.im.service.groupmember.GroupMemberService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@ -66,7 +66,7 @@ public class ImGroupMemberController {
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('im:group-member:query')")
public CommonResult<ImGroupMemberRespVO> getGroupMember(@RequestParam("id") Long id) {
GroupMemberDO groupMember = groupMemberService.getGroupMember(id);
ImGroupMemberDO groupMember = groupMemberService.getGroupMember(id);
return success(BeanUtils.toBean(groupMember, ImGroupMemberRespVO.class));
}
@ -74,7 +74,7 @@ public class ImGroupMemberController {
@Operation(summary = "获得群成员分页")
@PreAuthorize("@ss.hasPermission('im:group-member:query')")
public CommonResult<PageResult<ImGroupMemberRespVO>> getGroupMemberPage(@Valid ImGroupMemberPageReqVO pageReqVO) {
PageResult<GroupMemberDO> pageResult = groupMemberService.getGroupMemberPage(pageReqVO);
PageResult<ImGroupMemberDO> pageResult = groupMemberService.getGroupMemberPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ImGroupMemberRespVO.class));
}
@ -85,7 +85,7 @@ public class ImGroupMemberController {
public void exportGroupMemberExcel(@Valid ImGroupMemberPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<GroupMemberDO> list = groupMemberService.getGroupMemberPage(pageReqVO).getList();
List<ImGroupMemberDO> list = groupMemberService.getGroupMemberPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "群成员.xls", "数据", ImGroupMemberRespVO.class,
BeanUtils.toBean(list, ImGroupMemberRespVO.class));

View File

@ -1,43 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
// TODO @anhaohaoim 前缀哈
@Schema(description = "管理后台 - 收件箱保存消息 Request VO")
@Data
public class InboxSaveMessageReqVO {
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long fromId; // 根据 conversationType 区分
@Schema(description = "发送人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
private String senderNickname;
@Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
private String senderAvatar;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
private Long receiverId;
@Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
private Long messageId;
@Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer contentType; // 参见 ImMessageTypeEnum 枚举
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
private String content;
@Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime sendTime;
// @Schema(description = "序号", requiredMode = Schema.RequiredMode.REQUIRED)
// private Long sequence;
}

View File

@ -0,0 +1,5 @@
/**
* @author anhaohao
* @since 2024/3/31 下午10:41
*/
package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;

View File

@ -0,0 +1,57 @@
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.ImMessageListReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageRespVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageSendReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageSendRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import cn.iocoder.yudao.module.im.service.message.MessageService;
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.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - IM 消息")
@RestController
@RequestMapping("/im/message")
@Validated
public class ImMessageController {
@Resource
private MessageService messageService;
@PostMapping("/send")
@Operation(summary = "发送消息")
public CommonResult<ImMessageSendRespVO> sendMessage(@Valid @RequestBody ImMessageSendReqVO imMessageSendReqVO) {
ImMessageDO message = messageService.sendMessage(getLoginUserId(), imMessageSendReqVO);
return success(BeanUtils.toBean(message, ImMessageSendRespVO.class));
}
@GetMapping("/pull")
@Operation(summary = "消息列表-拉取大于 sequence 的消息列表")
@Parameter(name = "sequence", description = "序号", required = true, example = "1")
@Parameter(name = "size", description = "条数", required = true, example = "10")
public CommonResult<List<ImMessageRespVO>> pullMessageList(@RequestParam("sequence") Long sequence,
@RequestParam("size") Integer size) {
List<ImMessageDO> messages = messageService.pullMessageList(getLoginUserId(), sequence, size);
return success(BeanUtils.toBean(messages, ImMessageRespVO.class));
}
@GetMapping("/list")
@Operation(summary = "消息列表-根据接收人和发送时间进行分页查询")
public CommonResult<List<ImMessageRespVO>> getMessageList(@Valid ImMessageListReqVO listReqVO) {
List<ImMessageDO> messagePage = messageService.getMessageList(getLoginUserId(), listReqVO);
return success(BeanUtils.toBean(messagePage, ImMessageRespVO.class));
}
}

View File

@ -1,59 +0,0 @@
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.MessagePageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.service.message.MessageService;
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.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - IM 消息")
@RestController
@RequestMapping("/im/message")
@Validated
public class MessageController {
@Resource
private MessageService messageService;
@PostMapping("/send")
@Operation(summary = "发送消息")
public CommonResult<SendMessageRespVO> sendMessage(@Valid @RequestBody SendMessageReqVO message) {
return success(messageService.sendMessage(getLoginUserId(), message));
}
// TODO @anhaohao我在想这个接口改成叫 pullMessageList会不会更好理解拉取消息列表
@GetMapping("/list-by-sequence")
@Operation(summary = "拉取大于 sequence 的消息列表")
@Parameter(name = "sequence", description = "序号", required = true, example = "1")
@Parameter(name = "size", description = "条数", required = true, example = "10")
public CommonResult<List<MessageReqVO>> getMessageListBySequence(@RequestParam("sequence") Long sequence,
@RequestParam("size") Integer size) {
List<MessageDO> messages = messageService.getMessageListBySequence(getLoginUserId(), sequence, size);
return success(BeanUtils.toBean(messages, MessageReqVO.class));
}
// TODO @anhaohao直接叫 getMessageList不叫历史哈因为它只是给用户叫历史对系统来说就是消息列表
@GetMapping("/history")
@Operation(summary = "查询聊天记录-根据会话标志和发送时间进行分页查询")
public CommonResult<List<MessageReqVO>> getHistoryMessage(@Valid MessagePageReqVO pageReqVO) {
//根据会话标志和发送时间进行分页查询
List<MessageDO> messagePage = messageService.getHistoryMessage(pageReqVO);
return success(BeanUtils.toBean(messagePage, MessageReqVO.class));
}
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - 消息列表 Request VO")
@Data
@ToString(callSuper = true)
public class ImMessageListReqVO {
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
@NotNull(message = "接收人编号不能为空")
private Long receiverId;
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@InEnum(value = ImConversationTypeEnum.class,message = "会话类型必须是 {value}")
@NotNull(message = "会话类型不能为空")
private Integer conversationType;
@Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-03-27 12:00:00")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDateTime sendTime;
}

View File

@ -1,42 +1,47 @@
package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
package cn.iocoder.yudao.module.im.controller.admin.message.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.message.ImMessageContentTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 收件箱保存消息 Request VO")
@Schema(description = "管理后台 - 消息 Response VO")
@Data
public class InboxSendMessageReqVO {
public class ImMessageRespVO {
@Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
private Long id;
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
@InEnum(value = ImConversationTypeEnum.class)
private Integer conversationType;
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long fromId; // 根据 conversationType 区分
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long senderId;
@Schema(description = "发送人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
private String senderNickname;
@Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://www.iocoder.cn/xxx.jpg")
private String senderAvatar;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Long receiverId;
@Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
private Long messageId;
@Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer contentType; // 参见 ImMessageTypeEnum 枚举
@InEnum(value = ImMessageContentTypeEnum.class)
private Integer contentType;
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好")
private String content;
@Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-03-27 12:00:00")
private LocalDateTime sendTime;
@Schema(description = "序号", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long sequence;
}

View File

@ -1,40 +1,36 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.message.MessageContentTypeEnum;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.message.ImMessageContentTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
// TODO @anhaohaoMessageSendReqVO消息发送
// TODO @anhaohao不应该有 excel 相关的注解
@Schema(description = "管理后台 - 发送消息 Request VO")
@Data
@ExcelIgnoreUnannotated
public class SendMessageReqVO {
public class ImMessageSendReqVO {
@Schema(description = "客户端消息编号 uuid用于排重", requiredMode = Schema.RequiredMode.REQUIRED, example = "3331")
@ExcelProperty("客户端消息编号 uuid用于排重")
@NotNull(message = "客户端消息编号不能为空")
private String clientMessageId;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
@ExcelProperty("接收人编号")
@NotNull(message = "接收人编号不能为空")
private Long receiverId;
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("会话类型")
@InEnum(value = ImConversationTypeEnum.class)
@NotNull(message = "会话类型不能为空")
private Integer conversationType;
@Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("消息类型")
@InEnum(MessageContentTypeEnum.class)
@InEnum(ImMessageContentTypeEnum.class)
@NotNull(message = "消息类型不能为空")
private Integer contentType;
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("消息内容")
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好")
@NotNull(message = "消息内容不能为空")
private String content;
}

View File

@ -10,13 +10,11 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @anhaohaoMessageSendRespVO消息发送结果
@Schema(description = "管理后台 - 发送消息 Response VO")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SendMessageRespVO {
public class ImMessageSendRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
private Long id;

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @anhaohao这个类不要交 Page而是消息列表MessageListReqVO
@Schema(description = "管理后台 - 消息分页 Request VO")
@Data
@ToString(callSuper = true)
public class MessagePageReqVO {
// TODO @anhaohao还是传递 targetId conversationType我们要弱化 no 的概念
@Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "g_1000")
private String conversationNo;
// TODO @anhaohao应该不传递时间范围而是传递分页的时间然后根据时间查询消息
@Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-03-27")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] sendTime;
}

View File

@ -1,52 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.message.MessageContentTypeEnum;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
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;
// TODO @anhaohaoMessageRespVO 消息响应 Response VO
// TODO @anahaohao每个 example 都写下啊哈漏了地方要补下因为 http mock 的时候可以根据它去生成
@Schema(description = "管理后台 - 消息 Request VO")
@Data
public class MessageReqVO {
@Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
private Long id;
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer conversationType; // 对应 ImConversationTypeEnum 枚举
// TODO @anhaohao这个应该是 senderId
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long fromId; // 根据 conversationType 区分
@Schema(description = "发送人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
private String senderNickname;
@Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
private String senderAvatar;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
private Long receiverId;
@Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer contentType; // 参见 ImMessageTypeEnum 枚举
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
private String content;
@Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime sendTime;
@Schema(description = "序号", requiredMode = Schema.RequiredMode.REQUIRED)
private Long sequence;
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.im.dal.dataobject.conversation;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -9,7 +9,6 @@ import lombok.*;
import java.time.LocalDateTime;
// TODO @anhaohao还是有 IM 前缀哈
/**
* IM 会话 DO
*
@ -23,7 +22,7 @@ import java.time.LocalDateTime;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ConversationDO extends BaseDO {
public class ImConversationDO extends BaseDO {
/**
* 编号
@ -39,7 +38,7 @@ public class ConversationDO extends BaseDO {
/**
* 会话类型
* <p>
* 枚举 {@link ConversationTypeEnum}
* 枚举 {@link ImConversationTypeEnum}
*/
private Integer type;
/**

View File

@ -6,7 +6,6 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
// TODO @anhaohao还是要有 IM
/**
* IM 群信息 DO
*
@ -20,7 +19,7 @@ import lombok.*;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GroupDO extends BaseDO {
public class ImGroupDO extends BaseDO {
/**
* 编号

View File

@ -4,7 +4,6 @@ import lombok.*;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
// TODO @anhaohao还是要有 IM
/**
* IM 群成员 DO
*
@ -18,7 +17,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GroupMemberDO extends BaseDO {
public class ImGroupMemberDO extends BaseDO {
/**
* 编号

View File

@ -1,14 +1,14 @@
package cn.iocoder.yudao.module.im.dal.dataobject.inbox;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
// TODO @anhaohao还是要有 IM
// TODO 我们要不要改成 ImMessageQueue 队列从理解上概念上可能都更清晰一点哈每个用户一个消息队列
/**
* IM 收件箱 DO
*
@ -22,7 +22,7 @@ import lombok.*;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InboxDO extends BaseDO {
public class ImInboxDO extends BaseDO {
/**
* 编号
@ -31,14 +31,14 @@ public class InboxDO extends BaseDO {
private Long id;
/**
* 用户编号
*
* TODO @hao写下 userId messageId 的关联字段
* <p>
* 关联 {@link ImMessageDO#getSenderId()} 或者 {@link ImMessageDO#getReceiverId()}
*/
private Long userId;
/**
* 消息编号
*
* 关联 {@link MessageDO#getId()}
* <p>
* 关联 {@link ImMessageDO#getId()}
*/
private Long messageId;
/**

View File

@ -1,10 +1,10 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.message.MessageContentTypeEnum;
import cn.iocoder.yudao.module.im.enums.message.MessageSourceEnum;
import cn.iocoder.yudao.module.im.enums.message.MessageStatusEnum;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.message.ImMessageContentTypeEnum;
import cn.iocoder.yudao.module.im.enums.message.ImMessageSourceEnum;
import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -12,7 +12,6 @@ import lombok.*;
import java.time.LocalDateTime;
// TODO @anhaohao还是要有 IM
/**
* IM 消息 DO
*
@ -26,7 +25,7 @@ import java.time.LocalDateTime;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MessageDO extends BaseDO {
public class ImMessageDO extends BaseDO {
/**
* 编号
@ -60,24 +59,24 @@ public class MessageDO extends BaseDO {
*/
private String senderAvatar;
/**
* 会话类型 枚举 {@link ConversationTypeEnum}
* 会话类型 枚举 {@link ImConversationTypeEnum}
*/
private Integer conversationType;
/**
* 会话标志
*
* 生成规则{@link ConversationTypeEnum#generateConversationNo(Long, Long, Integer)} 方法
* <p>
* 生成规则{@link ImConversationTypeEnum#generateConversationNo(Long, Long, Integer)} 方法
*/
private String conversationNo;
/**
* 消息类型
*
* 枚举 {@link MessageContentTypeEnum}
* <p>
* 枚举 {@link ImMessageContentTypeEnum}
*/
private Integer contentType;
/**
* 消息内容
*
* <p>
* JSON 格式 对应 dal/dataobject/message/content
*/
private String content;
@ -87,14 +86,14 @@ public class MessageDO extends BaseDO {
private LocalDateTime sendTime;
/**
* 消息来源
*
* 枚举 {@link MessageSourceEnum}
* <p>
* 枚举 {@link ImMessageSourceEnum}
*/
private Integer sendFrom;
/**
* 消息状态
*
* 枚举 {@link MessageStatusEnum}
* <p>
* 枚举 {@link ImMessageStatusEnum}
*/
private Integer messageStatus;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -8,7 +8,7 @@ import lombok.NoArgsConstructor;
// TODO @anhaohao要有 IM
// TODO @芋艿后续要挪到 api 包下主要是给外部接口使用
/**
* 语音消息的 {@link MessageDO 字段 content} 的内容
* 语音消息的 {@link ImMessageDO 字段 content} 的内容
*
* @author 芋道源码
*/

View File

@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 名片消息的 {@link MessageDO 字段 content} 的内容
* 名片消息的 {@link ImMessageDO 字段 content} 的内容
*
* @author anhaohao
* @since 2024/3/23 下午5:53

View File

@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 文件消息的 {@link MessageDO 字段 content} 的内容
* 文件消息的 {@link ImMessageDO 字段 content} 的内容
*
* @author 芋道源码
*/

View File

@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 图片消息的 {@link MessageDO 字段 content} 的内容
* 图片消息的 {@link ImMessageDO 字段 content} 的内容
*
* @author 芋道源码
*/

View File

@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 地址位置消息的 {@link MessageDO 字段 content} 的内容
* 地址位置消息的 {@link ImMessageDO 字段 content} 的内容
*
* @author 芋道源码
*/

View File

@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 文本消息的 {@link MessageDO 字段 content} 的内容
* 文本消息的 {@link ImMessageDO 字段 content} 的内容
*
* @author 芋道源码
*/

View File

@ -1,10 +1,10 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import lombok.Data;
/**
* 视频消息的 {@link MessageDO 字段 content} 的内容
* 视频消息的 {@link ImMessageDO 字段 content} 的内容
*
* @author 芋道源码
*/

View File

@ -1,8 +1,7 @@
package cn.iocoder.yudao.module.im.dal.mysql.conversation;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
import org.apache.ibatis.annotations.Mapper;
/**
@ -11,9 +10,9 @@ import org.apache.ibatis.annotations.Mapper;
* @author 芋道源码
*/
@Mapper
public interface ConversationMapper extends BaseMapperX<ConversationDO> {
public interface ConversationMapper extends BaseMapperX<ImConversationDO> {
default ConversationDO selectByNo(String no){
return selectOne(ConversationDO::getNo, no);
default ImConversationDO selectByNo(String no){
return selectOne(ImConversationDO::getNo, no);
}
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.im.dal.mysql.group;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
/**
* Mapper
*
* @author 芋道源码
*/
@Mapper
public interface GroupMapper extends BaseMapperX<ImGroupDO> {
default PageResult<ImGroupDO> selectPage(ImGroupPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ImGroupDO>()
.likeIfPresent(ImGroupDO::getGroupName, reqVO.getGroupName())
.eqIfPresent(ImGroupDO::getOwnerId, reqVO.getOwnerId())
.eqIfPresent(ImGroupDO::getHeadImage, reqVO.getHeadImage())
.eqIfPresent(ImGroupDO::getHeadImageThumb, reqVO.getHeadImageThumb())
.eqIfPresent(ImGroupDO::getNotice, reqVO.getNotice())
.eqIfPresent(ImGroupDO::getRemark, reqVO.getRemark())
.betweenIfPresent(ImGroupDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ImGroupDO::getId));
}
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.im.dal.mysql.group;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
/**
* Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ImGroupMapper extends BaseMapperX<GroupDO> {
default PageResult<GroupDO> selectPage(ImGroupPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<GroupDO>()
.likeIfPresent(GroupDO::getGroupName, reqVO.getGroupName())
.eqIfPresent(GroupDO::getOwnerId, reqVO.getOwnerId())
.eqIfPresent(GroupDO::getHeadImage, reqVO.getHeadImage())
.eqIfPresent(GroupDO::getHeadImageThumb, reqVO.getHeadImageThumb())
.eqIfPresent(GroupDO::getNotice, reqVO.getNotice())
.eqIfPresent(GroupDO::getRemark, reqVO.getRemark())
.betweenIfPresent(GroupDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(GroupDO::getId));
}
}

View File

@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.im.dal.mysql.groupmember;
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.groupmember.vo.ImGroupMemberPageReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 群成员 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface GroupMemberMapper extends BaseMapperX<ImGroupMemberDO> {
default PageResult<ImGroupMemberDO> selectPage(ImGroupMemberPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ImGroupMemberDO>()
.eqIfPresent(ImGroupMemberDO::getGroupId, reqVO.getGroupId())
.eqIfPresent(ImGroupMemberDO::getUserId, reqVO.getUserId())
.likeIfPresent(ImGroupMemberDO::getNickname, reqVO.getNickname())
.eqIfPresent(ImGroupMemberDO::getAvatar, reqVO.getAvatar())
.likeIfPresent(ImGroupMemberDO::getAliasName, reqVO.getAliasName())
.eqIfPresent(ImGroupMemberDO::getRemark, reqVO.getRemark())
.betweenIfPresent(ImGroupMemberDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ImGroupMemberDO::getId));
}
default List<ImGroupMemberDO> selectListByGroupId(Long groupId) {
return selectList(new LambdaQueryWrapperX<ImGroupMemberDO>().eq(ImGroupMemberDO::getGroupId, groupId));
}
}

View File

@ -1,35 +0,0 @@
package cn.iocoder.yudao.module.im.dal.mysql.groupmember;
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.groupmember.vo.ImGroupMemberPageReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 群成员 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ImGroupMemberMapper extends BaseMapperX<GroupMemberDO> {
default PageResult<GroupMemberDO> selectPage(ImGroupMemberPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<GroupMemberDO>()
.eqIfPresent(GroupMemberDO::getGroupId, reqVO.getGroupId())
.eqIfPresent(GroupMemberDO::getUserId, reqVO.getUserId())
.likeIfPresent(GroupMemberDO::getNickname, reqVO.getNickname())
.eqIfPresent(GroupMemberDO::getAvatar, reqVO.getAvatar())
.likeIfPresent(GroupMemberDO::getAliasName, reqVO.getAliasName())
.eqIfPresent(GroupMemberDO::getRemark, reqVO.getRemark())
.betweenIfPresent(GroupMemberDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(GroupMemberDO::getId));
}
default List<GroupMemberDO> selectListByGroupId(Long groupId) {
return selectList(new LambdaQueryWrapperX<GroupMemberDO>().eq(GroupMemberDO::getGroupId, groupId));
}
}

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.im.dal.mysql.inbox;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@ -13,27 +13,14 @@ import java.util.List;
* @author 芋道源码
*/
@Mapper
public interface InboxMapper extends BaseMapperX<InboxDO> {
public interface InboxMapper extends BaseMapperX<ImInboxDO> {
// TODO @anhaohao返回 List<InboxDO> 转换成 messageId 交给上层dao 尽量通用
default List<Long> selectMessageIdsByUserIdAndSequence(Long userId, Long sequence, Integer size) {
return selectList(new LambdaQueryWrapperX<InboxDO>()
.gt(InboxDO::getUserId, userId)
.gt(InboxDO::getSequence, sequence)
.orderByAsc(InboxDO::getSequence)
.last("limit 0," + size)) // TODO @anhaohao这里 limit 就可以了不用从 limit 0 开始
.stream()
.map(InboxDO::getMessageId)
.toList();
default List<ImInboxDO> selectListByUserIdAndSequence(Long userId, Long sequence, Integer size) {
return selectList(new LambdaQueryWrapperX<ImInboxDO>()
.gt(ImInboxDO::getUserId, userId)
.gt(ImInboxDO::getSequence, sequence)
.orderByAsc(ImInboxDO::getSequence)
.last("LIMIT " + size));
}
// default PageResult<InboxDO> selectPage(ImInboxPageReqVO reqVO) {
// return selectPage(reqVO, new LambdaQueryWrapperX<InboxDO>()
// .eqIfPresent(InboxDO::getUserId, reqVO.getUserId())
// .eqIfPresent(InboxDO::getMessageId, reqVO.getMessageId())
// .eqIfPresent(InboxDO::getSequence, reqVO.getSequence())
// .betweenIfPresent(InboxDO::getCreateTime, reqVO.getCreateTime())
// .orderByDesc(InboxDO::getId));
// }
}

View File

@ -2,10 +2,7 @@ package cn.iocoder.yudao.module.im.dal.mysql.message;
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.message.vo.MessagePageReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@ -16,13 +13,12 @@ import java.util.List;
* @author 芋道源码
*/
@Mapper
public interface MessageMapper extends BaseMapperX<MessageDO> {
public interface MessageMapper extends BaseMapperX<ImMessageDO> {
default List<MessageDO> getHistoryMessage(MessagePageReqVO pageReqVO) {
return selectList(new LambdaQueryWrapperX<MessageDO>()
.eqIfPresent(MessageDO::getConversationNo, pageReqVO.getConversationNo())
.betweenIfPresent(MessageDO::getSendTime, pageReqVO.getSendTime())
.orderByAsc(MessageDO::getSendTime));
default List<ImMessageDO> selectMessageList(ImMessageDO message) {
return selectList(new LambdaQueryWrapperX<ImMessageDO>()
.eqIfPresent(ImMessageDO::getConversationNo, message.getConversationNo())
.eqIfPresent(ImMessageDO::getSendTime, message.getSendTime())
.orderByAsc(ImMessageDO::getSendTime));
}
}

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.im.dal.redis.inbox;
import jakarta.annotation.Resource;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;
@ -15,7 +14,7 @@ import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.INBOX_SEQUE
* @author anhaohao
*/
@Repository
public class SequenceRedisDao {
public class SequenceRedisDAO {
@Resource
private RedisTemplate<String, Long> redisTemplate;

View File

@ -1,8 +1,8 @@
package cn.iocoder.yudao.module.im.service.conversation;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationLastTimeReqVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationPinnedReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationUpdateLastReadTimeReqVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationUpdatePinnedReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
import java.util.List;
@ -18,20 +18,22 @@ public interface ConversationService {
*
* @return 会话列表
*/
List<ConversationDO> getConversationList();
List<ImConversationDO> getConversationList();
/**
* 置顶会话
*
* @param loginUserId 登录用户编号
* @param updateReqVO 更新信息
*/
void updatePinned(ConversationPinnedReqVO updateReqVO);
void updatePinned(Long loginUserId, ImConversationUpdatePinnedReqVO updateReqVO);
/**
* 更新最后已读时间
*
* @param loginUserId 登录用户编号
* @param updateReqVO 更新信息
*/
void updateLastReadTime(ConversationLastTimeReqVO updateReqVO);
void updateLastReadTime(Long loginUserId, ImConversationUpdateLastReadTimeReqVO updateReqVO);
}

View File

@ -1,10 +1,10 @@
package cn.iocoder.yudao.module.im.service.conversation;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationLastTimeReqVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ConversationPinnedReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationUpdateLastReadTimeReqVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationUpdatePinnedReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
import cn.iocoder.yudao.module.im.dal.mysql.conversation.ConversationMapper;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import cn.iocoder.yudao.module.im.service.inbox.InboxService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@ -27,50 +27,50 @@ public class ConversationServiceImpl implements ConversationService {
private InboxService inboxService;
@Override
public List<ConversationDO> getConversationList() {
public List<ImConversationDO> getConversationList() {
return conversationMapper.selectList();
}
@Override
public void updatePinned(ConversationPinnedReqVO updateReqVO) {
// TODO @haoupdateTop updateLastReadTime 使用独立的逻辑实现不使用统一的 ImConversationSaveReqVO
// TODO 大体步骤建议
// 1. getOrderCreateConversation查询会话不存在则插入
// 2. 更新对应的字段
// 3. 做对应更新的 notify 推送
ConversationDO conversation = conversationMapper.selectByNo(updateReqVO.getNo());
public void updatePinned(Long loginUserId, ImConversationUpdatePinnedReqVO updateReqVO) {
// 1. 获得会话编号
String no = ImConversationTypeEnum.generateConversationNo(loginUserId, updateReqVO.getTargetId(), updateReqVO.getType());
// 2. 查询会话
ImConversationDO conversation = conversationMapper.selectByNo(no);
if (conversation == null) {
ConversationDO conversationDO = new ConversationDO();
// TODO @haono 不是前端传递哈后端生成另外其实可以把 insert 写成一个公用方法get会话拿不到就 insert接着处理 update 操作首次多 update 一次无所谓的没多少量的
conversationDO.setNo(updateReqVO.getNo());
conversationDO.setPinned(updateReqVO.getPinned());
conversationDO.setUserId(updateReqVO.getUserId());
conversationDO.setTargetId(updateReqVO.getTargetId());
conversationDO.setType(updateReqVO.getType());
conversationMapper.insert(conversationDO);
} else {
// 更新 TODO @anhaohao这里不要 toBean因为这里逻辑偏 tocnew ConversationDO 对象然后逐个 set 需要的值
ConversationDO updateObj = BeanUtils.toBean(updateReqVO, ConversationDO.class);
conversationMapper.updateById(updateObj);
// 2.1. 不存在则插入
conversation = insertConversation(no, loginUserId, updateReqVO.getTargetId(), updateReqVO.getType());
}
// 3. 更新会话
conversation.setPinned(updateReqVO.getPinned());
conversationMapper.updateById(conversation);
// 4. 做对应更新的 notify 推送
}
private ImConversationDO insertConversation(String no, Long userId, Long targetId, Integer type) {
ImConversationDO imConversationDO = new ImConversationDO();
imConversationDO.setNo(no);
imConversationDO.setUserId(userId);
imConversationDO.setTargetId(targetId);
imConversationDO.setType(type);
conversationMapper.insert(imConversationDO);
return imConversationDO;
}
@Override
public void updateLastReadTime(ConversationLastTimeReqVO updateReqVO) {
ConversationDO conversation = conversationMapper.selectByNo(updateReqVO.getNo());
public void updateLastReadTime(Long loginUserId, ImConversationUpdateLastReadTimeReqVO updateReqVO) {
// 1. 获得会话编号
String no = ImConversationTypeEnum.generateConversationNo(loginUserId, updateReqVO.getTargetId(), updateReqVO.getType());
// 2. 查询会话
ImConversationDO conversation = conversationMapper.selectByNo(no);
if (conversation == null) {
ConversationDO conversationDO = new ConversationDO();
conversationDO.setNo(updateReqVO.getNo());
conversationDO.setLastReadTime(updateReqVO.getLastReadTime());
conversationDO.setUserId(updateReqVO.getUserId());
conversationDO.setTargetId(updateReqVO.getTargetId());
conversationDO.setType(updateReqVO.getType());
conversationMapper.insert(conversationDO);
} else {
// 更新
ConversationDO updateObj = BeanUtils.toBean(updateReqVO, ConversationDO.class);
conversationMapper.updateById(updateObj);
// 2.1. 不存在则插入
conversation = insertConversation(no, loginUserId, updateReqVO.getTargetId(), updateReqVO.getType());
}
// 3. 更新会话
conversation.setLastReadTime(updateReqVO.getLastReadTime());
conversationMapper.updateById(conversation);
// 4. 做对应更新的 notify 推送
}
}

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.im.service.group;
import jakarta.validation.*;
import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
/**
@ -10,7 +10,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
*
* @author 芋道源码
*/
public interface ImGroupService {
public interface GroupService {
/**
* 创建群
@ -40,7 +40,7 @@ public interface ImGroupService {
* @param id 编号
* @return
*/
GroupDO getGroup(Long id);
ImGroupDO getGroup(Long id);
/**
* 获得群分页
@ -48,6 +48,6 @@ public interface ImGroupService {
* @param pageReqVO 分页查询
* @return 群分页
*/
PageResult<GroupDO> getGroupPage(ImGroupPageReqVO pageReqVO);
PageResult<ImGroupDO> getGroupPage(ImGroupPageReqVO pageReqVO);
}

View File

@ -5,11 +5,11 @@ import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMapper;
import cn.iocoder.yudao.module.im.dal.mysql.group.GroupMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
@ -21,16 +21,16 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
*/
@Service
@Validated
public class ImGroupServiceImpl implements ImGroupService {
public class GroupServiceImpl implements GroupService {
@Resource
private ImGroupMapper imGroupMapper;
private GroupMapper groupMapper;
@Override
public Long createGroup(ImGroupSaveReqVO createReqVO) {
// 插入
GroupDO group = BeanUtils.toBean(createReqVO, GroupDO.class);
imGroupMapper.insert(group);
ImGroupDO group = BeanUtils.toBean(createReqVO, ImGroupDO.class);
groupMapper.insert(group);
// 返回
return group.getId();
}
@ -40,8 +40,8 @@ public class ImGroupServiceImpl implements ImGroupService {
// 校验存在
validateGroupExists(updateReqVO.getId());
// 更新
GroupDO updateObj = BeanUtils.toBean(updateReqVO, GroupDO.class);
imGroupMapper.updateById(updateObj);
ImGroupDO updateObj = BeanUtils.toBean(updateReqVO, ImGroupDO.class);
groupMapper.updateById(updateObj);
}
@Override
@ -49,23 +49,23 @@ public class ImGroupServiceImpl implements ImGroupService {
// 校验存在
validateGroupExists(id);
// 删除
imGroupMapper.deleteById(id);
groupMapper.deleteById(id);
}
private void validateGroupExists(Long id) {
if (imGroupMapper.selectById(id) == null) {
if (groupMapper.selectById(id) == null) {
throw exception(GROUP_NOT_EXISTS);
}
}
@Override
public GroupDO getGroup(Long id) {
return imGroupMapper.selectById(id);
public ImGroupDO getGroup(Long id) {
return groupMapper.selectById(id);
}
@Override
public PageResult<GroupDO> getGroupPage(ImGroupPageReqVO pageReqVO) {
return imGroupMapper.selectPage(pageReqVO);
public PageResult<ImGroupDO> getGroupPage(ImGroupPageReqVO pageReqVO) {
return groupMapper.selectPage(pageReqVO);
}
}

View File

@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.im.service.groupmember;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberSaveReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
import jakarta.validation.Valid;
import java.util.List;
@ -43,7 +43,7 @@ public interface GroupMemberService {
* @param id 编号
* @return 群成员
*/
GroupMemberDO getGroupMember(Long id);
ImGroupMemberDO getGroupMember(Long id);
/**
* 获得群成员分页
@ -51,7 +51,7 @@ public interface GroupMemberService {
* @param pageReqVO 分页查询
* @return 群成员分页
*/
PageResult<GroupMemberDO> getGroupMemberPage(ImGroupMemberPageReqVO pageReqVO);
PageResult<ImGroupMemberDO> getGroupMemberPage(ImGroupMemberPageReqVO pageReqVO);
/**
* 根据群组id查询群成员
@ -59,5 +59,5 @@ public interface GroupMemberService {
* @param groupId 群组id
* @return 群成员列表
*/
List<GroupMemberDO> selectByGroupId(Long groupId);
List<ImGroupMemberDO> selectByGroupId(Long groupId);
}

View File

@ -5,11 +5,11 @@ import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.*;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.im.dal.mysql.groupmember.ImGroupMemberMapper;
import cn.iocoder.yudao.module.im.dal.mysql.groupmember.GroupMemberMapper;
import java.util.List;
@ -26,13 +26,13 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
public class GroupMemberServiceImpl implements GroupMemberService {
@Resource
private ImGroupMemberMapper imGroupMemberMapper;
private GroupMemberMapper groupMemberMapper;
@Override
public Long createGroupMember(ImGroupMemberSaveReqVO createReqVO) {
// 插入
GroupMemberDO groupMember = BeanUtils.toBean(createReqVO, GroupMemberDO.class);
imGroupMemberMapper.insert(groupMember);
ImGroupMemberDO groupMember = BeanUtils.toBean(createReqVO, ImGroupMemberDO.class);
groupMemberMapper.insert(groupMember);
// 返回
return groupMember.getId();
}
@ -42,8 +42,8 @@ public class GroupMemberServiceImpl implements GroupMemberService {
// 校验存在
validateGroupMemberExists(updateReqVO.getId());
// 更新
GroupMemberDO updateObj = BeanUtils.toBean(updateReqVO, GroupMemberDO.class);
imGroupMemberMapper.updateById(updateObj);
ImGroupMemberDO updateObj = BeanUtils.toBean(updateReqVO, ImGroupMemberDO.class);
groupMemberMapper.updateById(updateObj);
}
@Override
@ -51,28 +51,28 @@ public class GroupMemberServiceImpl implements GroupMemberService {
// 校验存在
validateGroupMemberExists(id);
// 删除
imGroupMemberMapper.deleteById(id);
groupMemberMapper.deleteById(id);
}
private void validateGroupMemberExists(Long id) {
if (imGroupMemberMapper.selectById(id) == null) {
if (groupMemberMapper.selectById(id) == null) {
throw exception(GROUP_MEMBER_NOT_EXISTS);
}
}
@Override
public GroupMemberDO getGroupMember(Long id) {
return imGroupMemberMapper.selectById(id);
public ImGroupMemberDO getGroupMember(Long id) {
return groupMemberMapper.selectById(id);
}
@Override
public PageResult<GroupMemberDO> getGroupMemberPage(ImGroupMemberPageReqVO pageReqVO) {
return imGroupMemberMapper.selectPage(pageReqVO);
public PageResult<ImGroupMemberDO> getGroupMemberPage(ImGroupMemberPageReqVO pageReqVO) {
return groupMemberMapper.selectPage(pageReqVO);
}
@Override
public List<GroupMemberDO> selectByGroupId(Long groupId) {
return imGroupMemberMapper.selectListByGroupId(groupId);
public List<ImGroupMemberDO> selectByGroupId(Long groupId) {
return groupMemberMapper.selectListByGroupId(groupId);
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.im.service.inbox;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import java.util.List;
@ -14,9 +14,9 @@ public interface InboxService {
/**
* 保存收件箱和发送消息
*
* @param inboxSaveMessageReqVO 收件箱保存消息 Request VO
* @param imMessageDO 收件箱保存消息 Request VO
*/
void saveInboxAndSendMessage(InboxSaveMessageReqVO inboxSaveMessageReqVO);
void saveInboxAndSendMessage(ImMessageDO imMessageDO);
/**
* 获得大于 sequence 的消息ids

View File

@ -2,18 +2,19 @@ package cn.iocoder.yudao.module.im.service.inbox;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSendMessageReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import cn.iocoder.yudao.module.im.dal.mysql.inbox.InboxMapper;
import cn.iocoder.yudao.module.im.dal.redis.inbox.InboxLockRedisDAO;
import cn.iocoder.yudao.module.im.dal.redis.inbox.SequenceRedisDao;
import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
import cn.iocoder.yudao.module.im.dal.redis.inbox.SequenceRedisDAO;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import cn.iocoder.yudao.module.im.service.groupmember.GroupMemberService;
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
import jakarta.annotation.Resource;
import org.dromara.hutool.core.date.DateUnit;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@ -33,50 +34,60 @@ public class InboxServiceImpl implements InboxService {
@Resource
private InboxMapper inboxMapper;
@Resource
private SequenceRedisDao sequenceRedisDao; // 序列生成器Redis DAO
private SequenceRedisDAO sequenceRedisDAO;
@Resource
private InboxLockRedisDAO inboxLockRedisDAO; // 收件箱的锁 Redis DAO
private InboxLockRedisDAO inboxLockRedisDAO;
@Resource
private WebSocketSenderApi webSocketSenderApi;
@Resource
private GroupMemberService groupMemberService;
// TODO @anhaohao下面的逻辑最好是1. 保存收件箱 + 发送消息给用户 2. xxx这样看的人会更有感觉哈
@Override
public void saveInboxAndSendMessage(InboxSaveMessageReqVO inboxSaveMessage) {
// 保存收件箱 + 发送消息给用户
saveInboxAndSendMessageForUser(inboxSaveMessage.getFromId(), inboxSaveMessage);
if (inboxSaveMessage.getConversationType().equals(ConversationTypeEnum.SINGLE.getType())) {
saveInboxAndSendMessageForUser(inboxSaveMessage.getReceiverId(), inboxSaveMessage);
} else if (inboxSaveMessage.getConversationType().equals(ConversationTypeEnum.GROUP.getType())) {
List<GroupMemberDO> groupMembers = groupMemberService.selectByGroupId(inboxSaveMessage.getReceiverId());
groupMembers.forEach(groupMemberDO -> saveInboxAndSendMessageForUser(groupMemberDO.getUserId(), inboxSaveMessage));
public void saveInboxAndSendMessage(ImMessageDO message) {
// 1. 保存收件箱 + 发送消息给发送人
saveInboxAndSendMessageForUser(message.getSenderId(), message);
// 2. 保存收件箱 + 发送消息给接收人
if (message.getConversationType().equals(ImConversationTypeEnum.SINGLE.getType())) {
// 2.1 如果是单聊直接发送给接收人
saveInboxAndSendMessageForUser(message.getReceiverId(), message);
} else if (message.getConversationType().equals(ImConversationTypeEnum.GROUP.getType())) {
// 2.2 如果是群聊发送给群聊的所有人
List<ImGroupMemberDO> groupMembers = groupMemberService.selectByGroupId(message.getReceiverId());
groupMembers.forEach(groupMemberDO -> saveInboxAndSendMessageForUser(groupMemberDO.getUserId(), message));
}
}
@Override
public List<Long> selectMessageIdsByUserIdAndSequence(Long userId, Long sequence, Integer size) {
return inboxMapper.selectMessageIdsByUserIdAndSequence(userId, sequence, size);
List<ImInboxDO> imInboxDOS = inboxMapper.selectListByUserIdAndSequence(userId, sequence, size);
return imInboxDOS.stream().map(ImInboxDO::getMessageId).toList();
}
private void saveInboxAndSendMessageForUser(Long userId, InboxSaveMessageReqVO inboxSaveMessage) {
inboxLockRedisDAO.lock(userId, INBOX_LOCK_TIMEOUT, () -> {
Long userSequence = sequenceRedisDao.generateSequence(userId);
// TODO @anhaohao链式调用
InboxDO inbox = new InboxDO();
inbox.setUserId(userId);
inbox.setMessageId(inboxSaveMessage.getMessageId());
inbox.setSequence(userSequence);
inboxMapper.insert(inbox);
// TODO @anhaohao是不是 send 不用在加锁里面哈
// TODO @anhaohao再进一步是不是用 spring @async 可以并发推送噢
InboxSendMessageReqVO message = BeanUtils.toBean(inboxSaveMessage, InboxSendMessageReqVO.class);
message.setSequence(userSequence);
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), userId, IM_MESSAGE_RECEIVE, message);
//TODO 多线程处理
public void saveInboxAndSendMessageForUser(Long userId, ImMessageDO message) {
inboxLockRedisDAO.lock(userId, INBOX_LOCK_TIMEOUT, () -> {
// 1. 生成序列号
Long userSequence = sequenceRedisDAO.generateSequence(userId);
// 2. 保存收件箱
ImInboxDO inbox = new ImInboxDO()
.setUserId(userId)
.setMessageId(message.getId())
.setSequence(userSequence);
inboxMapper.insert(inbox);
// 3. 发送消息
sendAsyncMessage(userId, message, userSequence);
});
}
@Async
public void sendAsyncMessage(Long userId, ImMessageDO message, Long userSequence) {
ImMessageRespVO messageRespVO = BeanUtils.toBean(message, ImMessageRespVO.class);
messageRespVO.setSequence(userSequence);
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), userId, IM_MESSAGE_RECEIVE, messageRespVO);
}
}

View File

@ -1,9 +1,8 @@
package cn.iocoder.yudao.module.im.service.message;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessagePageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageListReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageSendReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import java.util.List;
@ -17,26 +16,28 @@ public interface MessageService {
/**
* 获得历史消息
*
* @param pageReqVO 分页查询
* @param loginUserId 登录用户编号
* @param listReqVO 分页查询
* @return 消息分页
*/
List<MessageDO> getHistoryMessage(MessagePageReqVO pageReqVO);
List<ImMessageDO> getMessageList(Long loginUserId, ImMessageListReqVO listReqVO);
/**
* 拉取消息-大于 seq 的消息
*
* @param userId 用户id
* @param sequence 序列号
* @param size 数量
* @param loginUserId 登录用户编号
* @param sequence 序列号
* @param size 数量
* @return 消息列表
*/
List<MessageDO> getMessageListBySequence(Long userId, Long sequence, Integer size);
List<ImMessageDO> pullMessageList(Long loginUserId, Long sequence, Integer size);
/**
* 发送消息
*
* @param loginUserId 登录用户编号
* @param message 消息
* @param message 消息
* @return 消息编号
*/
SendMessageRespVO sendMessage(Long loginUserId, SendMessageReqVO message);
ImMessageDO sendMessage(Long loginUserId, ImMessageSendReqVO message);
}

View File

@ -1,15 +1,14 @@
package cn.iocoder.yudao.module.im.service.message;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.MessagePageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.SendMessageRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.GroupMemberDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.MessageDO;
import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageListReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.ImMessageSendReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
import cn.iocoder.yudao.module.im.dal.mysql.message.MessageMapper;
import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.message.MessageSourceEnum;
import cn.iocoder.yudao.module.im.enums.message.MessageStatusEnum;
import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.message.ImMessageSourceEnum;
import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
import cn.iocoder.yudao.module.im.service.groupmember.GroupMemberService;
import cn.iocoder.yudao.module.im.service.inbox.InboxService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@ -38,92 +37,66 @@ public class MessageServiceImpl implements MessageService {
@Resource
private MessageMapper messageMapper;
@Resource
private InboxService inboxService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private InboxService inboxService;
@Resource
private GroupMemberService groupMemberService;
@Override
public List<MessageDO> getHistoryMessage(MessagePageReqVO pageReqVO) {
return messageMapper.getHistoryMessage(pageReqVO);
public List<ImMessageDO> getMessageList(Long loginUserId, ImMessageListReqVO listReqVO) {
// 1. 获得会话编号
String no = ImConversationTypeEnum.generateConversationNo(loginUserId, listReqVO.getReceiverId(), listReqVO.getConversationType());
// 2. 查询历史消息
ImMessageDO message = new ImMessageDO()
.setSendTime(listReqVO.getSendTime())
.setConversationNo(no);
return messageMapper.selectMessageList(message);
}
@Override
public List<MessageDO> getMessageListBySequence(Long userId, Long sequence, Integer size) {
public List<ImMessageDO> pullMessageList(Long userId, Long sequence, Integer size) {
List<Long> messageIds = inboxService.selectMessageIdsByUserIdAndSequence(userId, sequence, size);
return messageMapper.selectBatchIds(messageIds);
}
@Override
public SendMessageRespVO sendMessage(Long fromUserId, SendMessageReqVO message) {
// 保存消息
// TODO @anhaohaoInboxSaveMessageReqVO 不用 new 出来传递到 saveMessage 方法里
InboxSaveMessageReqVO inboxSaveMessageReqVO = new InboxSaveMessageReqVO();
SendMessageRespVO sendMessageRespVO = saveMessage(fromUserId, message, inboxSaveMessageReqVO);
// 保存收件箱 + 发送消息给用户
// TODO @anhaohao考虑到少定义一些 VO直接传递 MessageDO 就完事了反正这两者也是强耦合的
inboxService.saveInboxAndSendMessage(inboxSaveMessageReqVO);
return sendMessageRespVO;
public ImMessageDO sendMessage(Long fromUserId, ImMessageSendReqVO imMessageSendReqVO) {
// 1. 保存消息
ImMessageDO message = saveMessage(fromUserId, imMessageSendReqVO);
// 2. 保存到收件箱并发送消息
inboxService.saveInboxAndSendMessage(message);
return message;
}
// TODO @anhaohao这个方法是不是定义成 private 然后返回是 MessageDO 对象设置最上面的 sendMessage 也是这个最终 controller 转成 SendMessageRespVO
public SendMessageRespVO saveMessage(Long fromUserId, SendMessageReqVO message, InboxSaveMessageReqVO inboxSaveMessageReqVO) {
// 需要校验 receiverId 存在
private ImMessageDO saveMessage(Long fromUserId, ImMessageSendReqVO message) {
// 1. 校验接收人是否存在
validateReceiverIdExists(message);
// 查询发送人昵称和发送人头像
// 2. 查询发送人信息
AdminUserRespDTO fromUser = adminUserApi.getUser(fromUserId);
// 使用链式调用创建 MessageDO 对象
// TODO @anhaohao一部分字段可以 beanutils tobean 搞定
// TODO @anhaohao链式 set 的时候要把相同的放在一行例如说setSenderNicknamesetSenderAvatar本质上就是为了同类在一行阅读起来简单
MessageDO messageDO = new MessageDO()
.setClientMessageId(message.getClientMessageId())
// 3. 保存消息
ImMessageDO imMessageDO = BeanUtil.copyProperties(message, ImMessageDO.class)
.setSenderNickname(fromUser.getNickname()).setSenderAvatar(fromUser.getAvatar())
.setSenderId(fromUserId)
.setReceiverId(message.getReceiverId())
.setSenderNickname(fromUser.getNickname())
.setSenderAvatar(fromUser.getAvatar())
.setConversationType(message.getConversationType())
.setContentType(message.getContentType())
.setConversationNo(ConversationTypeEnum.generateConversationNo(fromUserId, message.getReceiverId(), message.getConversationType()))
.setContent(message.getContent())
.setSendFrom(MessageSourceEnum.USER_SEND.getStatus())
.setSendTime(TimeUtil.now())
.setMessageStatus(MessageStatusEnum.SENDING.getStatus());
messageMapper.insert(messageDO);
// 设置 InboxSaveMessageReqVO 对象
inboxSaveMessageReqVO.setConversationType(message.getConversationType())
.setFromId(fromUserId)
.setReceiverId(message.getReceiverId())
.setMessageId(messageDO.getId())
.setContentType(message.getContentType())
.setContent(message.getContent())
.setSendTime(messageDO.getSendTime())
.setSenderNickname(fromUser.getNickname())
.setSenderAvatar(fromUser.getAvatar());
// 返回 SendMessageRespVO 对象
return new SendMessageRespVO(messageDO.getId(), messageDO.getSendTime());
.setConversationNo(ImConversationTypeEnum.generateConversationNo(fromUserId, message.getReceiverId(), message.getConversationType()))
.setSendFrom(ImMessageSourceEnum.USER_SEND.getSource())
.setMessageStatus(ImMessageStatusEnum.SENDING.getStatus())
.setSendTime(TimeUtil.now());
messageMapper.insert(imMessageDO);
return imMessageDO;
}
// TODO @anhaohaovalidateReceiver更简单一点哈不仅仅校验存在未来还可以校验自己是不是有好友关系群聊是否在群聊里面等等
private void validateReceiverIdExists(SendMessageReqVO message) {
// TODO @anhaohao这个不要这里校验交给 validator 校验掉
if (message.getReceiverId() == null) {
throw exception(MESSAGE_RECEIVER_NOT_EXISTS);
}
if (message.getConversationType().equals(ConversationTypeEnum.SINGLE.getType())) {
// TODO @anhaohao// 之后要空一个空格中英文协作习惯中文和英文之间不能连着最后也不用
//校验用户是否存在
private void validateReceiverIdExists(ImMessageSendReqVO message) {
if (message.getConversationType().equals(ImConversationTypeEnum.SINGLE.getType())) {
// 校验用户是否存在
AdminUserRespDTO receiverUser = adminUserApi.getUser(message.getReceiverId());
if (receiverUser == null) {
throw exception(MESSAGE_RECEIVER_NOT_EXISTS);
}
} else if (message.getConversationType().equals(ConversationTypeEnum.GROUP.getType())) {
//校验群聊是否存在
List<GroupMemberDO> groupMemberDOS = groupMemberService.selectByGroupId(message.getReceiverId());
if (groupMemberDOS.isEmpty()) {
} else if (message.getConversationType().equals(ImConversationTypeEnum.GROUP.getType())) {
// 校验群聊是否存在
List<ImGroupMemberDO> imGroupMemberDOS = groupMemberService.selectByGroupId(message.getReceiverId());
if (imGroupMemberDOS.isEmpty()) {
throw exception(MESSAGE_RECEIVER_NOT_EXISTS);
}
}

View File

@ -1,49 +0,0 @@
package cn.iocoder.yudao.module.im.websocket;
import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;
import cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;
import cn.iocoder.yudao.module.im.websocket.message.ImSendMessage;
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
// TODO @hao消息发送使用 http 上行因为在 cloud 框架下我们比较难去 Listener因为 im-server 不会自己启动 websocket 路径
/**
* WebSocket im
*
* @author 芋道源码
*/
@Component
@Slf4j
public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSendMessage> {
public static final String IM_MESSAGE_RECEIVE = "im-message-receive";
@Resource
private WebSocketSenderApi webSocketSenderApi; // WebSocket消息发送器
/**
* 处理WebSocket消息
*
* @param session WebSocket会话
* @param message 发送的IM消息
*/
@Override
public void onMessage(WebSocketSession session, ImSendMessage message) {
Long fromUserId = WebSocketFrameworkUtils.getLoginUserId(session); // 获取登录用户ID
log.info(message.toString());
}
/**
* 获取类型
*
* @return 类型
*/
@Override
public String getType() {
return "im-message-send";
}
}

View File

@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.im.websocket.message;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 消息发送 receive")
@Data
public class ImReceiveMessage {
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(ConversationTypeEnum.class)
private Integer conversationType;
// TODO @anhaohao我们应该是 senderId因为它和 receiverId 是相对应的哈
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long fromId; // 根据 conversationType 区分
// TODO @hao昵称和头像也直接发给接收人好了
@Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
private Long messageId;
@Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer contentType; // 参见 ImMessageTypeEnum 枚举
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
private String content;
@Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime sendTime;
@Schema(description = "序号", requiredMode = Schema.RequiredMode.REQUIRED)
private Long sequence;
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.im.websocket.message;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.conversation.ConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.message.MessageContentTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - IM 消息发送 send")
@Data
public class ImSendMessage {
@Schema(description = "客户端消息编号 uuid用于排重", requiredMode = Schema.RequiredMode.REQUIRED, example = "3331")
private String clientMessageId;
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(ConversationTypeEnum.class)
private Integer conversationType;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long receiverId; // 根据 conversationType 区分
@Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(MessageContentTypeEnum.class)
private Integer contentType;
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
private String content;
}