修改:置顶会话和更新最后已读时间

This commit is contained in:
安浩浩 2024-03-28 21:39:24 +08:00
parent 4015b2f213
commit 51e724fc90
30 changed files with 281 additions and 749 deletions

View File

@ -2,15 +2,15 @@ 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.ImConversationRespVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationSaveReqVO;
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.service.conversation.ConversationService;
import io.swagger.v3.oas.annotations.Operation;
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.*;
@ -18,33 +18,27 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
// TODO @hao: 管理后台 - IM 会话
@Tag(name = "管理后台 - IM 会话")
@RestController
@RequestMapping("/im/conversation")
@Validated
public class ConversationController {
// TODO @hao: conversationService 即可
@Resource
private ConversationService conversationService;
// TODO @hao/list 会话列表一般 get 是给单个对象或者 get-count 这种然后 conversation 已经是模块名了所以可以简化
@GetMapping("/get-conversation")
@GetMapping("/list")
@Operation(summary = "获得用户的会话列表")
@PreAuthorize("hasAuthority('im:conversation:query')") // TODO @hao不用权限哈
public CommonResult<List<ImConversationRespVO>> getConversationList() {
List<ConversationDO> conversationList = conversationService.getConversationList();
return success(BeanUtils.toBean(conversationList, ImConversationRespVO.class));
}
// TODO @hao/update-pinned 保持和 db 字段一致哈
// TODO @hao这个接口需要单独的 VO
@PostMapping("/update-top")
@PostMapping("/update-pinned")
@Operation(summary = "置顶会话")
@PreAuthorize("hasAuthority('im:conversation:update')") // TODO @hao不用权限哈因为肯定会判断是不是自己的
public CommonResult<Boolean> updateTop(@Valid @RequestBody ImConversationSaveReqVO updateReqVO) {
conversationService.updateTop(updateReqVO);
public CommonResult<Boolean> updatePinned(@Valid @RequestBody ConversationPinnedReqVO updateReqVO) {
conversationService.updatePinned(updateReqVO);
return success(true);
}
@ -52,8 +46,7 @@ public class ConversationController {
// TODO @hao这个接口需要单独的 VO
@PostMapping("/update-last-read-time")
@Operation(summary = "更新最后已读时间")
@PreAuthorize("hasAuthority('im:conversation:update')") // TODO @hao不用权限哈因为肯定会判断是不是自己的
public CommonResult<Boolean> updateLastReadTime(@Valid @RequestBody ImConversationSaveReqVO updateReqVO) {
public CommonResult<Boolean> updateLastReadTime(@Valid @RequestBody ConversationLastTimeReqVO updateReqVO) {
conversationService.updateLastReadTime(updateReqVO);
return success(true);
}

View File

@ -0,0 +1,31 @@
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;
@Schema(description = "管理后台 - 会话最后已读时间 Request VO")
@Data
public class ConversationLastTimeReqVO {
@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;
@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
}

View File

@ -0,0 +1,34 @@
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;
@Schema(description = "管理后台 - 会话置顶 Request VO")
@Data
public class ConversationPinnedReqVO {
@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;
@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
}

View File

@ -1,44 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.conversation.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
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_HOUR_MINUTE_SECOND;
// TODO @hao这个貌似业务上暂时用不到可以考虑删除哈
@Schema(description = "管理后台 - IM 会话分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ImConversationPageReqVO extends PageParam {
@Schema(description = "所属用户", example = "11545")
private Long userId;
@Schema(description = "类型1 单聊2 群聊4 通知会话(预留)", example = "1")
private Integer type;
@Schema(description = "单聊时,用户编号;群聊时,群编号", example = "21454")
private Long targetId;
@Schema(description = "会话标志 单聊s_{userId}_{targetId},需要排序 userId 和 targetId 群聊g_groupId")
private String no;
@Schema(description = "是否置顶 0否 1是")
private Boolean pinned;
@Schema(description = "最后已读时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] lastReadTime;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -9,45 +9,30 @@ import java.time.LocalDateTime;
@Schema(description = "管理后台 - 会话 Response VO")
@Data
@ExcelIgnoreUnannotated // TODO @haoexcel 的注解可以先删除
public class ImConversationRespVO {
// TODO @haoexample 都写下
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13905")
@ExcelProperty("编号")
private Long id;
@Schema(description = "所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "11545")
@ExcelProperty("所属用户")
@Schema(description = "所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
// TODO @hao@Schema 可以改成会话类型不用把具体的数字写在上面哈这样后续改动会比较难改
@Schema(description = "类型1 单聊2 群聊4 通知会话(预留)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("类型1 单聊2 群聊4 通知会话(预留)")
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer conversationType;
// TODO @hao只写聊天对象编号
@Schema(description = "单聊时,用户编号;群聊时,群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21454")
@ExcelProperty("单聊时,用户编号;群聊时,群编号")
@Schema(description = "聊天对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Long targetId;
// TODO @hao只写 no 即可
@Schema(description = "会话标志 单聊s_{userId}_{targetId},需要排序 userId 和 targetId 群聊g_groupId", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("会话标志 单聊s_{userId}_{targetId},需要排序 userId 和 targetId 群聊g_groupId")
@Schema(description = "会话标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "s_1_2")
private String no;
// TODO @hao只写 是否置顶0 1 是数据库的结果哈
@Schema(description = "是否置顶 0否 1是", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("是否置顶 0否 1是")
@Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Boolean pinned;
@Schema(description = "最后已读时间")
@ExcelProperty("最后已读时间")
@Schema(description = "最后已读时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-03-01 00:00:00")
private LocalDateTime lastReadTime;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -1,41 +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 @hao这个貌似业务上暂时用不到可以考虑删除哈
@Schema(description = "管理后台 - 会话新增/修改 Request VO")
@Data
public class ImConversationSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13905")
private Long id;
@Schema(description = "所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "11545")
@NotNull(message = "所属用户不能为空")
private Long userId;
@Schema(description = "类型1 单聊2 群聊4 通知会话(预留)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "类型1 单聊2 群聊4 通知会话(预留)不能为空")
private Integer type;
@Schema(description = "单聊时,用户编号;群聊时,群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21454")
@NotEmpty(message = "单聊时,用户编号;群聊时,群编号不能为空")
private Long targetId;
@Schema(description = "会话标志 单聊s_{userId}_{targetId},需要排序 userId 和 targetId 群聊g_groupId", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "会话标志 单聊s_{userId}_{targetId},需要排序 userId 和 targetId 群聊g_groupId不能为空")
private String no;
@Schema(description = "是否置顶 0否 1是", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "是否置顶 0否 1是不能为空")
private Boolean pinned;
@Schema(description = "最后已读时间")
private LocalDateTime lastReadTime;
}

View File

@ -10,7 +10,7 @@ import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.ImGroupMemberP
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.service.groupmember.ImGroupMemberService;
import cn.iocoder.yudao.module.im.service.groupmember.GroupMemberService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -34,20 +34,20 @@ import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.E
public class ImGroupMemberController {
@Resource
private ImGroupMemberService imGroupMemberService;
private GroupMemberService groupMemberService;
@PostMapping("/create")
@Operation(summary = "创建群成员")
@PreAuthorize("@ss.hasPermission('im:group-member:create')")
public CommonResult<Long> createGroupMember(@Valid @RequestBody ImGroupMemberSaveReqVO createReqVO) {
return success(imGroupMemberService.createGroupMember(createReqVO));
return success(groupMemberService.createGroupMember(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新群成员")
@PreAuthorize("@ss.hasPermission('im:group-member:update')")
public CommonResult<Boolean> updateGroupMember(@Valid @RequestBody ImGroupMemberSaveReqVO updateReqVO) {
imGroupMemberService.updateGroupMember(updateReqVO);
groupMemberService.updateGroupMember(updateReqVO);
return success(true);
}
@ -56,7 +56,7 @@ public class ImGroupMemberController {
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('im:group-member:delete')")
public CommonResult<Boolean> deleteGroupMember(@RequestParam("id") Long id) {
imGroupMemberService.deleteGroupMember(id);
groupMemberService.deleteGroupMember(id);
return success(true);
}
@ -65,7 +65,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 = imGroupMemberService.getGroupMember(id);
GroupMemberDO groupMember = groupMemberService.getGroupMember(id);
return success(BeanUtils.toBean(groupMember, ImGroupMemberRespVO.class));
}
@ -73,7 +73,7 @@ public class ImGroupMemberController {
@Operation(summary = "获得群成员分页")
@PreAuthorize("@ss.hasPermission('im:group-member:query')")
public CommonResult<PageResult<ImGroupMemberRespVO>> getGroupMemberPage(@Valid ImGroupMemberPageReqVO pageReqVO) {
PageResult<GroupMemberDO> pageResult = imGroupMemberService.getGroupMemberPage(pageReqVO);
PageResult<GroupMemberDO> pageResult = groupMemberService.getGroupMemberPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ImGroupMemberRespVO.class));
}
@ -84,7 +84,7 @@ public class ImGroupMemberController {
public void exportGroupMemberExcel(@Valid ImGroupMemberPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<GroupMemberDO> list = imGroupMemberService.getGroupMemberPage(pageReqVO).getList();
List<GroupMemberDO> list = groupMemberService.getGroupMemberPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "群成员.xls", "数据", ImGroupMemberRespVO.class,
BeanUtils.toBean(list, ImGroupMemberRespVO.class));

View File

@ -1,93 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.inbox;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxRespVO;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
import cn.iocoder.yudao.module.im.service.inbox.ImInboxService;
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.servlet.http.HttpServletResponse;
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.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 收件箱")
@RestController
@RequestMapping("/im/inbox")
@Validated
public class ImInboxController {
@Resource
private ImInboxService imInboxService;
@PostMapping("/create")
@Operation(summary = "创建收件箱")
@PreAuthorize("@ss.hasPermission('im:inbox:create')")
public CommonResult<Long> createInbox(@Valid @RequestBody InboxSaveReqVO createReqVO) {
return success(imInboxService.createInbox(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新收件箱")
@PreAuthorize("@ss.hasPermission('im:inbox:update')")
public CommonResult<Boolean> updateInbox(@Valid @RequestBody InboxSaveReqVO updateReqVO) {
imInboxService.updateInbox(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除收件箱")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('im:inbox:delete')")
public CommonResult<Boolean> deleteInbox(@RequestParam("id") Long id) {
imInboxService.deleteInbox(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得收件箱")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('im:inbox:query')")
public CommonResult<ImInboxRespVO> getInbox(@RequestParam("id") Long id) {
InboxDO inbox = imInboxService.getInbox(id);
return success(BeanUtils.toBean(inbox, ImInboxRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得收件箱分页")
@PreAuthorize("@ss.hasPermission('im:inbox:query')")
public CommonResult<PageResult<ImInboxRespVO>> getInboxPage(@Valid ImInboxPageReqVO pageReqVO) {
PageResult<InboxDO> pageResult = imInboxService.getInboxPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ImInboxRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出收件箱 Excel")
@PreAuthorize("@ss.hasPermission('im:inbox:export')")
@OperateLog(type = EXPORT)
public void exportInboxExcel(@Valid ImInboxPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<InboxDO> list = imInboxService.getInboxPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "收件箱.xls", "数据", ImInboxRespVO.class,
BeanUtils.toBean(list, ImInboxRespVO.class));
}
}

View File

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

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
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_HOUR_MINUTE_SECOND;
// TODO @hao这个是不是删除掉应该不会有这个 VO
@Schema(description = "管理后台 - 收件箱分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ImInboxPageReqVO extends PageParam {
@Schema(description = "用户编号", example = "3979")
private Long userId;
@Schema(description = "消息编号", example = "12454")
private Long messageId;
@Schema(description = "序号,按照 user 递增")
private Long sequence;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -1,36 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
// TODO @hao这个是不是删除掉应该不会有这个 VO 应该给前端的是要屏蔽掉这个表最终返回的都是 Message 说白了这个 inbox 是后端的内部实现
@Schema(description = "管理后台 - 收件箱 Response VO")
@Data
@ExcelIgnoreUnannotated
public class ImInboxRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18389")
@ExcelProperty("编号")
private Long id;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3979")
@ExcelProperty("用户编号")
private Long userId;
@Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
@ExcelProperty("消息编号")
private Long messageId;
@Schema(description = "序号,按照 user 递增", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("序号,按照 user 递增")
private Long sequence;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -1,37 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.inbox.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// TODO @hao这个是不是删除掉应该不会有这个 VO
@Schema(description = "管理后台 - 收件箱新增/修改 Request VO")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class InboxSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18389")
private Long id;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3979")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12454")
@NotNull(message = "消息编号不能为空")
private Long messageId;
@Schema(description = "序号,按照 user 递增", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "序号,按照 user 递增不能为空")
private Long sequence;
public InboxSaveReqVO(Long userId, Long messageId, Long sequence) {
this.userId = userId;
this.messageId = messageId;
this.sequence = sequence;
}
}

View File

@ -3,7 +3,7 @@ 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.ImMessageReqVO;
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;
@ -40,18 +40,18 @@ public class MessageController {
@Operation(summary = "拉取大于 sequence 的消息列表")
@Parameter(name = "sequence", description = "序号", required = true, example = "1")
@Parameter(name = "size", description = "条数", required = true, example = "10")
public CommonResult<List<ImMessageReqVO>> loadMessage(@RequestParam("sequence") Long sequence,
@RequestParam("size") Integer size) {
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, ImMessageReqVO.class));
return success(BeanUtils.toBean(messages, MessageReqVO.class));
}
@GetMapping("/page")
@Operation(summary = "查询聊天记录-分页")
public CommonResult<List<ImMessageReqVO>> getMessagePage(@Valid MessagePageReqVO pageReqVO) {
@GetMapping("/history")
@Operation(summary = "查询聊天记录-根据会话标志和发送时间进行分页查询")
public CommonResult<List<MessageReqVO>> getHistoryMessage(@Valid MessagePageReqVO pageReqVO) {
//根据会话标志和发送时间进行分页查询
List<MessageDO> messagePage = messageService.getMessagePage(pageReqVO);
return success(BeanUtils.toBean(messagePage, ImMessageReqVO.class));
List<MessageDO> messagePage = messageService.getHistoryMessage(pageReqVO);
return success(BeanUtils.toBean(messagePage, MessageReqVO.class));
}
}

View File

@ -1,36 +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;
@Schema(description = "管理后台 - 消息 Request VO")
@Data
@ExcelIgnoreUnannotated
public class ImMessageReqVO {
@Schema(description = "客户端消息编号 uuid用于排重", requiredMode = Schema.RequiredMode.REQUIRED, example = "3331")
@ExcelProperty("客户端消息编号 uuid用于排重")
private String clientMessageId;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
@ExcelProperty("接收人编号")
private Long receiverId;
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("会话类型")
private Integer conversationType;
@Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("消息类型")
@InEnum(MessageContentTypeEnum.class)
private Integer contentType;
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("消息内容")
private String content;
}

View File

@ -1,63 +0,0 @@
package cn.iocoder.yudao.module.im.controller.admin.message.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;
@Schema(description = "管理后台 - 消息新增/修改 Request VO")
@Data
public class ImMessageSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30713")
private Long id;
@Schema(description = "客户端消息编号 uuid用于排重", requiredMode = Schema.RequiredMode.REQUIRED, example = "3331")
@NotEmpty(message = "客户端消息编号 uuid用于排重不能为空")
private String clientMessageId;
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23239")
@NotNull(message = "发送人编号不能为空")
private Long senderId;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32494")
@NotNull(message = "接收人编号不能为空")
private Long receiverId;
@Schema(description = "发送人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@NotEmpty(message = "发送人昵称不能为空")
private String senderNickname;
@Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "发送人头像不能为空")
private String senderAvatar;
@Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "会话类型不能为空")
private Integer conversationType;
@Schema(description = "会话标志 conversation_no = a_b", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "会话标志不能为空")
private String conversationNo;
@Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "消息类型不能为空")
private Integer contentType;
@Schema(description = "消息内容", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "消息内容不能为空")
private String content;
@Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime sendTime;
@Schema(description = "消息来源 100-用户发送200-系统发送(一般是通知);", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "消息来源 100-用户发送200-系统发送(一般是通知);不能为空")
private Integer sendFrom;
@Schema(description = "消息状态 1 发送中、2 发送成功、3 发送失败、4 已删除、5 已撤回", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "消息状态不能为空")
private Integer messageStatus;
}

View File

@ -0,0 +1,49 @@
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;
@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 枚举
@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,9 +1,7 @@
package cn.iocoder.yudao.module.im.dal.mysql.conversation;
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.conversation.vo.ImConversationPageReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ConversationDO;
import org.apache.ibatis.annotations.Mapper;
@ -15,20 +13,7 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ConversationMapper extends BaseMapperX<ConversationDO> {
default PageResult<ConversationDO> selectPage(ImConversationPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ConversationDO>()
.eqIfPresent(ConversationDO::getUserId, reqVO.getUserId())
.eqIfPresent(ConversationDO::getType, reqVO.getType())
.eqIfPresent(ConversationDO::getTargetId, reqVO.getTargetId())
.eqIfPresent(ConversationDO::getNo, reqVO.getNo())
.eqIfPresent(ConversationDO::getPinned, reqVO.getPinned())
.betweenIfPresent(ConversationDO::getLastReadTime, reqVO.getLastReadTime())
.betweenIfPresent(ConversationDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ConversationDO::getId));
}
// TODO @hao1no) {要有空格哈2可以直接 selectOne(ImConversationDO::getNo, no) 父类做了封装
default ConversationDO selectByNo(String no){
return selectOne(new LambdaQueryWrapperX<ConversationDO>().eq(ConversationDO::getNo, no));
return selectOne(ConversationDO::getNo, no);
}
}

View File

@ -1,27 +0,0 @@
package cn.iocoder.yudao.module.im.dal.mysql.inbox;
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.inbox.vo.ImInboxPageReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
import org.apache.ibatis.annotations.Mapper;
/**
* IM 收件箱 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ImInboxMapper extends BaseMapperX<InboxDO> {
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

@ -0,0 +1,38 @@
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 org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* IM 收件箱 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InboxMapper extends BaseMapperX<InboxDO> {
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))
.stream()
.map(InboxDO::getMessageId)
.toList();
}
// 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

@ -18,30 +18,7 @@ import java.util.List;
@Mapper
public interface MessageMapper extends BaseMapperX<MessageDO> {
// TODO @hao不链表哈先从 ImInboxDO 查询出 messageId然后再到 ImMessageDO IN
default List<MessageDO> getGreaterThanSequenceMessage(Long userId, Long sequence, Integer size) {
//查询 inbox 表中大于 sequence 的消息,关联 message 按照 inbox sequence 升序
return selectJoinList(MessageDO.class, new MPJLambdaWrapper<MessageDO>()
.selectAll(MessageDO.class)
.innerJoin(InboxDO.class, InboxDO::getMessageId, MessageDO::getId)
.eq(InboxDO::getUserId, userId)
.gt(InboxDO::getSequence, sequence)
.orderByAsc(InboxDO::getSequence)
.last("limit 0," + size));
}
// TODO @hao dao 使用 selectListByUserId查询用 select条件用 by这个算是 spring data method dsl
default List<MessageDO> getAllMessage(Long userId, Integer size) {
//查询 inbox 表中100条消息,关联 message 按照 inbox sequence 降序
return selectJoinList(MessageDO.class, new MPJLambdaWrapper<MessageDO>()
.selectAll(MessageDO.class)
.innerJoin(InboxDO.class, InboxDO::getMessageId, MessageDO::getId)
.eq(InboxDO::getUserId, userId)
.orderByDesc(InboxDO::getSequence)
.last("limit 0," + size));
}
default List<MessageDO> getMessagePage(MessagePageReqVO pageReqVO) {
default List<MessageDO> getHistoryMessage(MessagePageReqVO pageReqVO) {
return selectList(new LambdaQueryWrapperX<MessageDO>()
.eqIfPresent(MessageDO::getConversationNo, pageReqVO.getConversationNo())
.betweenIfPresent(MessageDO::getSendTime, pageReqVO.getSendTime())

View File

@ -1,10 +1,8 @@
package cn.iocoder.yudao.module.im.service.conversation;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationSaveReqVO;
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 jakarta.validation.Valid;
import java.util.List;
@ -15,45 +13,6 @@ import java.util.List;
*/
public interface ConversationService {
/**
* 创建会话
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createConversation(@Valid ImConversationSaveReqVO createReqVO);
/**
* 更新会话
*
* @param updateReqVO 更新信息
*/
void updateConversation(@Valid ImConversationSaveReqVO updateReqVO);
/**
* 删除会话
*
* @param id 编号
*/
void deleteConversation(Long id);
/**
* 获得会话
*
* @param id 编号
* @return 会话
*/
ConversationDO getConversation(Long id);
/**
* 获得会话分页
*
* @param pageReqVO 分页查询
* @return 会话分页
*/
PageResult<ConversationDO> getConversationPage(ImConversationPageReqVO pageReqVO);
/**
* 获得用户的会话列表
*
@ -66,13 +25,13 @@ public interface ConversationService {
*
* @param updateReqVO 更新信息
*/
void updateTop(ImConversationSaveReqVO updateReqVO);
void updatePinned(ConversationPinnedReqVO updateReqVO);
/**
* 更新最后已读时间
*
* @param updateReqVO 更新信息
*/
void updateLastReadTime(ImConversationSaveReqVO updateReqVO);
void updateLastReadTime(ConversationLastTimeReqVO updateReqVO);
}

View File

@ -1,21 +1,18 @@
package cn.iocoder.yudao.module.im.service.conversation;
import cn.hutool.core.date.DateUtil;
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.conversation.vo.ImConversationPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversationSaveReqVO;
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.dal.mysql.conversation.ConversationMapper;
import cn.iocoder.yudao.module.im.service.inbox.InboxService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Date;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.CONVERSATION_NOT_EXISTS;
/**
* IM 会话 Service 实现类
@ -26,52 +23,10 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.CONVERSATION_N
@Validated
public class ConversationServiceImpl implements ConversationService {
// TODO @hao: 自己模块的注入不用带 im 前缀哈
@Resource
private ConversationMapper conversationMapper;
// TODO @hao: 这个方法是不是不需要哈
@Override
public Long createConversation(ImConversationSaveReqVO createReqVO) {
ConversationDO conversation = BeanUtils.toBean(createReqVO, ConversationDO.class);
conversationMapper.insert(conversation);
return conversation.getId();
}
// TODO @hao: 这个方法是不是不需要哈
@Override
public void updateConversation(ImConversationSaveReqVO updateReqVO) {
// 校验存在
validateConversationExists(updateReqVO.getId());
// 更新
ConversationDO updateObj = BeanUtils.toBean(updateReqVO, ConversationDO.class);
conversationMapper.updateById(updateObj);
}
// TODO @hao: 考虑到可能和端上不同步可以不校验是不是存储另外不基于 id 删除要基于 no + userId 删除哈说白了对端上要屏蔽 id 字段
@Override
public void deleteConversation(Long id) {
// 校验存在
validateConversationExists(id);
// 删除
conversationMapper.deleteById(id);
}
private void validateConversationExists(Long id) {
if (conversationMapper.selectById(id) == null) {
throw exception(CONVERSATION_NOT_EXISTS);
}
}
@Override
public ConversationDO getConversation(Long id) {
return conversationMapper.selectById(id);
}
@Override
public PageResult<ConversationDO> getConversationPage(ImConversationPageReqVO pageReqVO) {
return conversationMapper.selectPage(pageReqVO);
}
@Resource
private InboxService inboxService;
@Override
public List<ConversationDO> getConversationList() {
@ -79,31 +34,39 @@ public class ConversationServiceImpl implements ConversationService {
}
@Override
public void updateTop(ImConversationSaveReqVO updateReqVO) {
createOrUpdateConversation(updateReqVO);
}
@Override
public void updateLastReadTime(ImConversationSaveReqVO updateReqVO) {
createOrUpdateConversation(updateReqVO);
}
// TODO @haoupdateTop updateLastReadTime 使用独立的逻辑实现不使用统一的 ImConversationSaveReqVO
// TODO 大体步骤建议
// 1. getOrderCreateConversation查询会话不存在则插入
// 2. 更新对应的字段
// 3. 做对应更新的 notify 推送
private void createOrUpdateConversation(ImConversationSaveReqVO updateReqVO) {
// 操作会话已读置顶才会延迟创建,要先判断是否存在根据 no 查询是否存在,不存在则新增
public void updatePinned(ConversationPinnedReqVO updateReqVO) {
// TODO @haoupdateTop updateLastReadTime 使用独立的逻辑实现不使用统一的 ImConversationSaveReqVO
// TODO 大体步骤建议
// 1. getOrderCreateConversation查询会话不存在则插入
// 2. 更新对应的字段
// 3. 做对应更新的 notify 推送
ConversationDO conversation = conversationMapper.selectByNo(updateReqVO.getNo());
if (conversation == null) {
ConversationDO conversationDO = new ConversationDO();
conversationDO.setNo(updateReqVO.getNo());
conversationDO.setPinned(updateReqVO.getPinned());
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);
}
}
@Override
public void updateLastReadTime(ConversationLastTimeReqVO updateReqVO) {
ConversationDO conversation = conversationMapper.selectByNo(updateReqVO.getNo());
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());
conversationDO.setPinned(updateReqVO.getPinned());
conversationDO.setLastReadTime(DateUtil.toLocalDateTime(new Date()));
conversationMapper.insert(conversationDO);
} else {
// 更新

View File

@ -13,7 +13,7 @@ import java.util.List;
*
* @author 芋道源码
*/
public interface ImGroupMemberService {
public interface GroupMemberService {
/**
* 创建群成员

View File

@ -23,7 +23,7 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
*/
@Service
@Validated
public class ImGroupMemberServiceImpl implements ImGroupMemberService {
public class GroupMemberServiceImpl implements GroupMemberService {
@Resource
private ImGroupMemberMapper imGroupMemberMapper;

View File

@ -1,61 +0,0 @@
package cn.iocoder.yudao.module.im.service.inbox;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveReqVO;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.inbox.InboxDO;
import jakarta.validation.Valid;
// TODO @hao不用的方法删除下哈
/**
* IM 收件箱 Service 接口
*
* @author 芋道源码
*/
public interface ImInboxService {
/**
* 创建收件箱
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createInbox(@Valid InboxSaveReqVO createReqVO);
/**
* 更新收件箱
*
* @param updateReqVO 更新信息
*/
void updateInbox(@Valid InboxSaveReqVO updateReqVO);
/**
* 删除收件箱
*
* @param id 编号
*/
void deleteInbox(Long id);
/**
* 获得收件箱
*
* @param id 编号
* @return 收件箱
*/
InboxDO getInbox(Long id);
/**
* 获得收件箱分页
*
* @param pageReqVO 分页查询
* @return 收件箱分页
*/
PageResult<InboxDO> getInboxPage(ImInboxPageReqVO pageReqVO);
/**
* 保存收件箱和发送消息
* @param inboxSaveMessageReqVO 收件箱保存消息 Request VO
*/
void saveInboxAndSendMessage(InboxSaveMessageReqVO inboxSaveMessageReqVO);
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.im.service.inbox;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
import java.util.List;
/**
* IM 收件箱 Service 接口
*
* @author 芋道源码
*/
public interface InboxService {
/**
* 保存收件箱和发送消息
*
* @param inboxSaveMessageReqVO 收件箱保存消息 Request VO
*/
void saveInboxAndSendMessage(InboxSaveMessageReqVO inboxSaveMessageReqVO);
/**
* 获得大于 sequence 的消息ids
*
* @param userId 用户编号
* @param sequence 序列号
* @param size 数量
* @return 消息编号列表
*/
List<Long> selectMessageIdsByUserIdAndSequence(Long userId, Long sequence, Integer size);
}

View File

@ -1,20 +1,17 @@
package cn.iocoder.yudao.module.im.service.inbox;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveMessageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.InboxSaveReqVO;
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.dal.mysql.inbox.ImInboxMapper;
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.service.groupmember.ImGroupMemberService;
import cn.iocoder.yudao.module.im.service.groupmember.GroupMemberService;
import jakarta.annotation.Resource;
import org.dromara.hutool.core.date.DateUnit;
import org.springframework.stereotype.Service;
@ -22,9 +19,6 @@ import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.INBOX_NOT_EXISTS;
/**
* 收件箱 Service 实现类
*
@ -32,13 +26,13 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.INBOX_NOT_EXIS
*/
@Service
@Validated
public class ImInboxServiceImpl implements ImInboxService {
public class InboxServiceImpl implements InboxService {
private static final Long INBOX_LOCK_TIMEOUT = 120 * DateUnit.SECOND.getMillis();
private static final String IM_MESSAGE_RECEIVE = "im-message-receive";
@Resource
private ImInboxMapper imInboxMapper;
private InboxMapper inboxMapper;
@Resource
private SequenceRedisDao sequenceRedisDao; // 序列生成器Redis DAO
@Resource
@ -46,47 +40,7 @@ public class ImInboxServiceImpl implements ImInboxService {
@Resource
private WebSocketMessageSender webSocketMessageSender; // WebSocket消息发送器
@Resource
private ImGroupMemberService imGroupMemberService;
@Override
public Long createInbox(InboxSaveReqVO createReqVO) {
InboxDO inbox = BeanUtils.toBean(createReqVO, InboxDO.class);
imInboxMapper.insert(inbox);
return inbox.getId();
}
@Override
public void updateInbox(InboxSaveReqVO updateReqVO) {
// 校验存在
validateInboxExists(updateReqVO.getId());
// 更新
InboxDO updateObj = BeanUtils.toBean(updateReqVO, InboxDO.class);
imInboxMapper.updateById(updateObj);
}
@Override
public void deleteInbox(Long id) {
// 校验存在
validateInboxExists(id);
// 删除
imInboxMapper.deleteById(id);
}
private void validateInboxExists(Long id) {
if (imInboxMapper.selectById(id) == null) {
throw exception(INBOX_NOT_EXISTS);
}
}
@Override
public InboxDO getInbox(Long id) {
return imInboxMapper.selectById(id);
}
@Override
public PageResult<InboxDO> getInboxPage(ImInboxPageReqVO pageReqVO) {
return imInboxMapper.selectPage(pageReqVO);
}
private GroupMemberService groupMemberService;
@Override
public void saveInboxAndSendMessage(InboxSaveMessageReqVO inboxSaveMessage) {
@ -96,11 +50,16 @@ public class ImInboxServiceImpl implements ImInboxService {
if (inboxSaveMessage.getConversationType().equals(ConversationTypeEnum.SINGLE.getType())) {
saveInboxAndSendMessageForUser(inboxSaveMessage.getReceiverId(), inboxSaveMessage);
} else if (inboxSaveMessage.getConversationType().equals(ConversationTypeEnum.GROUP.getType())) {
List<GroupMemberDO> groupMembers = imGroupMemberService.selectByGroupId(inboxSaveMessage.getReceiverId());
List<GroupMemberDO> groupMembers = groupMemberService.selectByGroupId(inboxSaveMessage.getReceiverId());
groupMembers.forEach(groupMemberDO -> saveInboxAndSendMessageForUser(groupMemberDO.getUserId(), inboxSaveMessage));
}
}
@Override
public List<Long> selectMessageIdsByUserIdAndSequence(Long userId, Long sequence, Integer size) {
return inboxMapper.selectMessageIdsByUserIdAndSequence(userId, sequence, size);
}
private void saveInboxAndSendMessageForUser(Long userId, InboxSaveMessageReqVO inboxSaveMessage) {
inboxLockRedisDAO.lock(userId, INBOX_LOCK_TIMEOUT, () -> {
Long userSequence = sequenceRedisDao.generateSequence(userId);
@ -108,12 +67,8 @@ public class ImInboxServiceImpl implements ImInboxService {
inbox.setUserId(userId);
inbox.setMessageId(inboxSaveMessage.getMessageId());
inbox.setSequence(userSequence);
imInboxMapper.insert(inbox);
inboxMapper.insert(inbox);
//是发送人不发送
if (userId.equals(inboxSaveMessage.getFromId())) {
return;
}
InboxSendMessageReqVO message = BeanUtils.toBean(inboxSaveMessage, InboxSendMessageReqVO.class);
message.setSequence(userSequence);
webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), userId, IM_MESSAGE_RECEIVE, message);

View File

@ -15,13 +15,12 @@ import java.util.List;
public interface MessageService {
/**
* 获得消息分页
* 获得历史消息
*
* @param pageReqVO 分页查询
* @return 消息分页
*/
List<MessageDO> getMessagePage(MessagePageReqVO pageReqVO);
List<MessageDO> getHistoryMessage(MessagePageReqVO pageReqVO);
/**
* 拉取消息-大于 seq 的消息

View File

@ -10,8 +10,8 @@ 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.service.groupmember.ImGroupMemberService;
import cn.iocoder.yudao.module.im.service.inbox.ImInboxService;
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;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
@ -38,20 +38,21 @@ public class MessageServiceImpl implements MessageService {
@Resource
private MessageMapper messageMapper;
@Resource
private ImInboxService imInboxService; // IM收件箱服务
private InboxService inboxService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private ImGroupMemberService imGroupMemberService;
private GroupMemberService groupMemberService;
@Override
public List<MessageDO> getMessagePage(MessagePageReqVO pageReqVO) {
return messageMapper.getMessagePage(pageReqVO);
public List<MessageDO> getHistoryMessage(MessagePageReqVO pageReqVO) {
return messageMapper.getHistoryMessage(pageReqVO);
}
@Override
public List<MessageDO> getMessageListBySequence(Long userId, Long sequence, Integer size) {
return messageMapper.getGreaterThanSequenceMessage(userId, sequence, size);
List<Long> messageIds = inboxService.selectMessageIdsByUserIdAndSequence(userId, sequence, size);
return messageMapper.selectBatchIds(messageIds);
}
@Override
@ -61,7 +62,7 @@ public class MessageServiceImpl implements MessageService {
SendMessageRespVO sendMessageRespVO = saveMessage(fromUserId, message, inboxSaveMessageReqVO);
// 保存收件箱 + 发送消息给用户
imInboxService.saveInboxAndSendMessage(inboxSaveMessageReqVO);
inboxService.saveInboxAndSendMessage(inboxSaveMessageReqVO);
return sendMessageRespVO;
}
@ -112,7 +113,7 @@ public class MessageServiceImpl implements MessageService {
} else if (message.getConversationType().equals(ConversationTypeEnum.GROUP.getType())) {
//校验群聊是否存在
List<GroupMemberDO> groupMemberDOS = imGroupMemberService.selectByGroupId(message.getReceiverId());
List<GroupMemberDO> groupMemberDOS = groupMemberService.selectByGroupId(message.getReceiverId());
if (groupMemberDOS.isEmpty()) {
throw exception(MESSAGE_RECEIVER_NOT_EXISTS);
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.inbox.ImInboxMapper">
<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.inbox.InboxMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。