Browse Source

新增:群聊发送

安浩浩 1 year ago
parent
commit
9cee1b3ceb
32 changed files with 1322 additions and 70 deletions
  1. 6 0
      yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java
  2. 94 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupController.java
  3. 39 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupPageReqVO.java
  4. 46 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupRespVO.java
  5. 33 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupSaveReqVO.java
  6. 93 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/groupmember/ImGroupMemberController.java
  7. 42 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/groupmember/vo/ImGroupMemberPageReqVO.java
  8. 47 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/groupmember/vo/ImGroupMemberRespVO.java
  9. 33 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/groupmember/vo/ImGroupMemberSaveReqVO.java
  10. 52 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/ImGroupDO.java
  11. 55 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/groupmember/GroupMemberDO.java
  12. 6 1
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ImConversationMapper.java
  13. 30 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupMapper.java
  14. 35 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/groupmember/ImGroupMemberMapper.java
  15. 1 1
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/inbox/ImInboxMapper.java
  16. 11 13
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationServiceImpl.java
  17. 53 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupService.java
  18. 71 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java
  19. 63 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/groupmember/ImGroupMemberService.java
  20. 78 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/groupmember/ImGroupMemberServiceImpl.java
  21. 8 8
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxServiceImpl.java
  22. 8 0
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageService.java
  23. 13 4
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageServiceImpl.java
  24. 58 14
      yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/ImWebSocketMessageListener.java
  25. 1 1
      yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/conversation/ImConversationMapper.xml
  26. 1 1
      yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/group/ImGroupMapper.xml
  27. 12 0
      yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/groupmember/ImGroupMemberMapper.xml
  28. 12 0
      yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/inbox/ImInboxMapper.xml
  29. 15 15
      yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationServiceImplTest.java
  30. 147 0
      yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java
  31. 147 0
      yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/groupmember/ImGroupMemberServiceImplTest.java
  32. 12 12
      yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxServiceImplTest.java

+ 6 - 0
yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java

@@ -17,4 +17,10 @@ public interface ErrorCodeConstants {
 
     // ========== 消息 (1-040-300-000) ==========
     ErrorCode MESSAGE_NOT_EXISTS = new ErrorCode(1_040_300_000, "消息不存在");
+
+    // ========== 群 (1-040-400-000) ==========
+    ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1_040_400_000, "群不存在");
+
+    // ========== 群成员 (1-040-500-000) ==========
+    ErrorCode GROUP_MEMBER_NOT_EXISTS = new ErrorCode(1_040_500_000, "群成员不存在");
 }

+ 94 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupController.java

@@ -0,0 +1,94 @@
+package cn.iocoder.yudao.module.im.controller.admin.group;
+
+import org.springframework.web.bind.annotation.*;
+import jakarta.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import jakarta.validation.*;
+import jakarta.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+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.ImGroupDO;
+import cn.iocoder.yudao.module.im.service.group.ImGroupService;
+
+@Tag(name = "管理后台 - 群")
+@RestController
+@RequestMapping("/im/group")
+@Validated
+public class ImGroupController {
+
+    @Resource
+    private ImGroupService imGroupService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建群")
+    @PreAuthorize("@ss.hasPermission('im:group:create')")
+    public CommonResult<Long> createGroup(@Valid @RequestBody ImGroupSaveReqVO createReqVO) {
+        return success(imGroupService.createGroup(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新群")
+    @PreAuthorize("@ss.hasPermission('im:group:update')")
+    public CommonResult<Boolean> updateGroup(@Valid @RequestBody ImGroupSaveReqVO updateReqVO) {
+        imGroupService.updateGroup(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除群")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('im:group:delete')")
+    public CommonResult<Boolean> deleteGroup(@RequestParam("id") Long id) {
+        imGroupService.deleteGroup(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得群")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('im:group:query')")
+    public CommonResult<ImGroupRespVO> getGroup(@RequestParam("id") Long id) {
+        ImGroupDO group = imGroupService.getGroup(id);
+        return success(BeanUtils.toBean(group, ImGroupRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得群分页")
+    @PreAuthorize("@ss.hasPermission('im:group:query')")
+    public CommonResult<PageResult<ImGroupRespVO>> getGroupPage(@Valid ImGroupPageReqVO pageReqVO) {
+        PageResult<ImGroupDO> pageResult = imGroupService.getGroupPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, ImGroupRespVO.class));
+    }
+
+    @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<ImGroupDO> list = imGroupService.getGroupPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "群.xls", "数据", ImGroupRespVO.class,
+                        BeanUtils.toBean(list, ImGroupRespVO.class));
+    }
+
+}

+ 39 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupPageReqVO.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.im.controller.admin.group.vo;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+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
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ImGroupPageReqVO extends PageParam {
+
+    @Schema(description = "群名字", example = "芋艿")
+    private String groupName;
+
+    @Schema(description = "群主id", example = "31460")
+    private Long ownerId;
+
+    @Schema(description = "群头像")
+    private String headImage;
+
+    @Schema(description = "群头像缩略图")
+    private String headImageThumb;
+
+    @Schema(description = "群公告")
+    private String notice;
+
+    @Schema(description = "群备注", example = "你说的对")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 46 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupRespVO.java

@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.im.controller.admin.group.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 群 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class ImGroupRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1003")
+    @ExcelProperty("编号")
+    private Long id;
+
+    @Schema(description = "群名字", example = "芋艿")
+    @ExcelProperty("群名字")
+    private String groupName;
+
+    @Schema(description = "群主id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31460")
+    @ExcelProperty("群主id")
+    private Long ownerId;
+
+    @Schema(description = "群头像")
+    @ExcelProperty("群头像")
+    private String headImage;
+
+    @Schema(description = "群头像缩略图")
+    @ExcelProperty("群头像缩略图")
+    private String headImageThumb;
+
+    @Schema(description = "群公告")
+    @ExcelProperty("群公告")
+    private String notice;
+
+    @Schema(description = "群备注", example = "你说的对")
+    @ExcelProperty("群备注")
+    private String remark;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 33 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupSaveReqVO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.im.controller.admin.group.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import jakarta.validation.constraints.*;
+
+@Schema(description = "管理后台 - 群新增/修改 Request VO")
+@Data
+public class ImGroupSaveReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1003")
+    private Long id;
+
+    @Schema(description = "群名字", example = "芋艿")
+    private String groupName;
+
+    @Schema(description = "群主id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31460")
+    @NotNull(message = "群主id不能为空")
+    private Long ownerId;
+
+    @Schema(description = "群头像")
+    private String headImage;
+
+    @Schema(description = "群头像缩略图")
+    private String headImageThumb;
+
+    @Schema(description = "群公告")
+    private String notice;
+
+    @Schema(description = "群备注", example = "你说的对")
+    private String remark;
+
+}

+ 93 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/groupmember/ImGroupMemberController.java

@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.im.controller.admin.groupmember;
+
+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.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.groupmember.GroupMemberDO;
+import cn.iocoder.yudao.module.im.service.groupmember.ImGroupMemberService;
+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/group-member")
+@Validated
+public class ImGroupMemberController {
+
+    @Resource
+    private ImGroupMemberService imGroupMemberService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建群成员")
+    @PreAuthorize("@ss.hasPermission('im:group-member:create')")
+    public CommonResult<Long> createGroupMember(@Valid @RequestBody ImGroupMemberSaveReqVO createReqVO) {
+        return success(imGroupMemberService.createGroupMember(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新群成员")
+    @PreAuthorize("@ss.hasPermission('im:group-member:update')")
+    public CommonResult<Boolean> updateGroupMember(@Valid @RequestBody ImGroupMemberSaveReqVO updateReqVO) {
+        imGroupMemberService.updateGroupMember(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除群成员")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('im:group-member:delete')")
+    public CommonResult<Boolean> deleteGroupMember(@RequestParam("id") Long id) {
+        imGroupMemberService.deleteGroupMember(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得群成员")
+    @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);
+        return success(BeanUtils.toBean(groupMember, ImGroupMemberRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得群成员分页")
+    @PreAuthorize("@ss.hasPermission('im:group-member:query')")
+    public CommonResult<PageResult<ImGroupMemberRespVO>> getGroupMemberPage(@Valid ImGroupMemberPageReqVO pageReqVO) {
+        PageResult<GroupMemberDO> pageResult = imGroupMemberService.getGroupMemberPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, ImGroupMemberRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出群成员 Excel")
+    @PreAuthorize("@ss.hasPermission('im:group-member:export')")
+    @OperateLog(type = EXPORT)
+    public void exportGroupMemberExcel(@Valid ImGroupMemberPageReqVO pageReqVO,
+                                       HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<GroupMemberDO> list = imGroupMemberService.getGroupMemberPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "群成员.xls", "数据", ImGroupMemberRespVO.class,
+                BeanUtils.toBean(list, ImGroupMemberRespVO.class));
+    }
+
+}

+ 42 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/groupmember/vo/ImGroupMemberPageReqVO.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.im.controller.admin.groupmember.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;
+
+@Schema(description = "管理后台 - 群成员分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ImGroupMemberPageReqVO extends PageParam {
+
+    @Schema(description = "群 id", example = "13279")
+    private Long groupId;
+
+    @Schema(description = "用户id", example = "21730")
+    private Long userId;
+
+    @Schema(description = "昵称", example = "芋艿")
+    private String nickname;
+
+    @Schema(description = "头像")
+    private String avatar;
+
+    @Schema(description = "组内显示名称", example = "芋艿")
+    private String aliasName;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 47 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/groupmember/vo/ImGroupMemberRespVO.java

@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.im.controller.admin.groupmember.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;
+
+@Schema(description = "管理后台 - 群成员 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class ImGroupMemberRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17071")
+    @ExcelProperty("编号")
+    private Long id;
+
+    @Schema(description = "群 id", example = "13279")
+    @ExcelProperty("群 id")
+    private Long groupId;
+
+    @Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "21730")
+    @ExcelProperty("用户id")
+    private Long userId;
+
+    @Schema(description = "昵称", example = "芋艿")
+    @ExcelProperty("昵称")
+    private String nickname;
+
+    @Schema(description = "头像")
+    @ExcelProperty("头像")
+    private String avatar;
+
+    @Schema(description = "组内显示名称", example = "芋艿")
+    @ExcelProperty("组内显示名称")
+    private String aliasName;
+
+    @Schema(description = "备注", example = "你说的对")
+    @ExcelProperty("备注")
+    private String remark;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 33 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/groupmember/vo/ImGroupMemberSaveReqVO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.im.controller.admin.groupmember.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 群成员新增/修改 Request VO")
+@Data
+public class ImGroupMemberSaveReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17071")
+    private Long id;
+
+    @Schema(description = "群 id", example = "13279")
+    private Long groupId;
+
+    @Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "21730")
+    @NotNull(message = "用户id不能为空")
+    private Long userId;
+
+    @Schema(description = "昵称", example = "芋艿")
+    private String nickname;
+
+    @Schema(description = "头像")
+    private String avatar;
+
+    @Schema(description = "组内显示名称", example = "芋艿")
+    private String aliasName;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+}

+ 52 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/ImGroupDO.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.im.dal.dataobject.group;
+
+import lombok.*;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 群 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("im_group")
+@KeySequence("im_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ImGroupDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 群名字
+     */
+    private String groupName;
+    /**
+     * 群主id
+     */
+    private Long ownerId;
+    /**
+     * 群头像
+     */
+    private String headImage;
+    /**
+     * 群头像缩略图
+     */
+    private String headImageThumb;
+    /**
+     * 群公告
+     */
+    private String notice;
+    /**
+     * 群备注
+     */
+    private String remark;
+
+}

+ 55 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/groupmember/GroupMemberDO.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.im.dal.dataobject.groupmember;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 群成员 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("im_group_member")
+@KeySequence("im_group_member_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class GroupMemberDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 群 id
+     */
+    private Long groupId;
+    /**
+     * 用户id
+     */
+    private Long userId;
+    /**
+     * 昵称
+     */
+    private String nickname;
+    /**
+     * 头像
+     */
+    private String avatar;
+    /**
+     * 组内显示名称
+     */
+    private String aliasName;
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 6 - 1
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ConversationMapper.java → yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ImConversationMapper.java

@@ -7,13 +7,15 @@ import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.ImConversatio
 import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.Optional;
+
 /**
  * 会话 Mapper
  *
  * @author 芋道源码
  */
 @Mapper
-public interface ConversationMapper extends BaseMapperX<ImConversationDO> {
+public interface ImConversationMapper extends BaseMapperX<ImConversationDO> {
 
     default PageResult<ImConversationDO> selectPage(ImConversationPageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<ImConversationDO>()
@@ -27,4 +29,7 @@ public interface ConversationMapper extends BaseMapperX<ImConversationDO> {
                 .orderByDesc(ImConversationDO::getId));
     }
 
+    default ImConversationDO selectByNo(String no){
+        return selectOne(new LambdaQueryWrapperX<ImConversationDO>().eq(ImConversationDO::getNo, no));
+    }
 }

+ 30 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupMapper.java

@@ -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 ImGroupMapper 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));
+    }
+
+}

+ 35 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/groupmember/ImGroupMemberMapper.java

@@ -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.groupmember.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));
+    }
+}

+ 1 - 1
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/inbox/InboxMapper.java → yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/inbox/ImInboxMapper.java

@@ -13,7 +13,7 @@ import org.apache.ibatis.annotations.Mapper;
  * @author 芋道源码
  */
 @Mapper
-public interface InboxMapper extends BaseMapperX<ImInboxDO> {
+public interface ImInboxMapper extends BaseMapperX<ImInboxDO> {
 
     default PageResult<ImInboxDO> selectPage(ImInboxPageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<ImInboxDO>()

+ 11 - 13
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationServiceImpl.java

@@ -5,9 +5,8 @@ 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.dal.dataobject.conversation.ImConversationDO;
-import cn.iocoder.yudao.module.im.dal.mysql.conversation.ConversationMapper;
+import cn.iocoder.yudao.module.im.dal.mysql.conversation.ImConversationMapper;
 import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
@@ -25,13 +24,13 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.CONVERSATION_N
 public class ImConversationServiceImpl implements ImConversationService {
 
     @Resource
-    private ConversationMapper conversationMapper;
+    private ImConversationMapper imConversationMapper;
 
     @Override
     public Long createConversation(ImConversationSaveReqVO createReqVO) {
         // 插入
         ImConversationDO conversation = BeanUtils.toBean(createReqVO, ImConversationDO.class);
-        conversationMapper.insert(conversation);
+        imConversationMapper.insert(conversation);
         // 返回
         return conversation.getId();
     }
@@ -42,7 +41,7 @@ public class ImConversationServiceImpl implements ImConversationService {
         validateConversationExists(updateReqVO.getId());
         // 更新
         ImConversationDO updateObj = BeanUtils.toBean(updateReqVO, ImConversationDO.class);
-        conversationMapper.updateById(updateObj);
+        imConversationMapper.updateById(updateObj);
     }
 
     @Override
@@ -50,23 +49,23 @@ public class ImConversationServiceImpl implements ImConversationService {
         // 校验存在
         validateConversationExists(id);
         // 删除
-        conversationMapper.deleteById(id);
+        imConversationMapper.deleteById(id);
     }
 
     private void validateConversationExists(Long id) {
-        if (conversationMapper.selectById(id) == null) {
+        if (imConversationMapper.selectById(id) == null) {
             throw exception(CONVERSATION_NOT_EXISTS);
         }
     }
 
     @Override
     public ImConversationDO getConversation(Long id) {
-        return conversationMapper.selectById(id);
+        return imConversationMapper.selectById(id);
     }
 
     @Override
     public PageResult<ImConversationDO> getConversationPage(ImConversationPageReqVO pageReqVO) {
-        return conversationMapper.selectPage(pageReqVO);
+        return imConversationMapper.selectPage(pageReqVO);
     }
 
     @Override
@@ -86,10 +85,9 @@ public class ImConversationServiceImpl implements ImConversationService {
         conversation.setPinned(false);
 
         // 根据 no 查询是否存在,不存在则新增
-        QueryWrapper<ImConversationDO> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq("no", conversation.getNo());
-        if (conversationMapper.selectOne(queryWrapper) == null) {
-            conversationMapper.insert(conversation);
+        ImConversationDO imConversationDO = imConversationMapper.selectByNo(conversation.getNo());
+        if (imConversationDO == null) {
+            imConversationMapper.insert(conversation);
         }
     }
 

+ 53 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupService.java

@@ -0,0 +1,53 @@
+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.ImGroupDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+/**
+ * 群 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface ImGroupService {
+
+    /**
+     * 创建群
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createGroup(@Valid ImGroupSaveReqVO createReqVO);
+
+    /**
+     * 更新群
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateGroup(@Valid ImGroupSaveReqVO updateReqVO);
+
+    /**
+     * 删除群
+     *
+     * @param id 编号
+     */
+    void deleteGroup(Long id);
+
+    /**
+     * 获得群
+     *
+     * @param id 编号
+     * @return 群
+     */
+    ImGroupDO getGroup(Long id);
+
+    /**
+     * 获得群分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 群分页
+     */
+    PageResult<ImGroupDO> getGroupPage(ImGroupPageReqVO pageReqVO);
+
+}

+ 71 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java

@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.im.service.group;
+
+import org.springframework.stereotype.Service;
+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.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 static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
+
+/**
+ * 群 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class ImGroupServiceImpl implements ImGroupService {
+
+    @Resource
+    private ImGroupMapper imGroupMapper;
+
+    @Override
+    public Long createGroup(ImGroupSaveReqVO createReqVO) {
+        // 插入
+        ImGroupDO group = BeanUtils.toBean(createReqVO, ImGroupDO.class);
+        imGroupMapper.insert(group);
+        // 返回
+        return group.getId();
+    }
+
+    @Override
+    public void updateGroup(ImGroupSaveReqVO updateReqVO) {
+        // 校验存在
+        validateGroupExists(updateReqVO.getId());
+        // 更新
+        ImGroupDO updateObj = BeanUtils.toBean(updateReqVO, ImGroupDO.class);
+        imGroupMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteGroup(Long id) {
+        // 校验存在
+        validateGroupExists(id);
+        // 删除
+        imGroupMapper.deleteById(id);
+    }
+
+    private void validateGroupExists(Long id) {
+        if (imGroupMapper.selectById(id) == null) {
+            throw exception(GROUP_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public ImGroupDO getGroup(Long id) {
+        return imGroupMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<ImGroupDO> getGroupPage(ImGroupPageReqVO pageReqVO) {
+        return imGroupMapper.selectPage(pageReqVO);
+    }
+
+}

+ 63 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/groupmember/ImGroupMemberService.java

@@ -0,0 +1,63 @@
+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.groupmember.GroupMemberDO;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/**
+ * 群成员 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface ImGroupMemberService {
+
+    /**
+     * 创建群成员
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createGroupMember(@Valid ImGroupMemberSaveReqVO createReqVO);
+
+    /**
+     * 更新群成员
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateGroupMember(@Valid ImGroupMemberSaveReqVO updateReqVO);
+
+    /**
+     * 删除群成员
+     *
+     * @param id 编号
+     */
+    void deleteGroupMember(Long id);
+
+    /**
+     * 获得群成员
+     *
+     * @param id 编号
+     * @return 群成员
+     */
+    GroupMemberDO getGroupMember(Long id);
+
+    /**
+     * 获得群成员分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 群成员分页
+     */
+    PageResult<GroupMemberDO> getGroupMemberPage(ImGroupMemberPageReqVO pageReqVO);
+
+    /**
+     * 根据群组id查询群成员
+     *
+     * @param groupId 群组id
+     * @return 群成员列表
+     */
+    List<GroupMemberDO> selectByGroupId(Long groupId);
+}

+ 78 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/groupmember/ImGroupMemberServiceImpl.java

@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.module.im.service.groupmember;
+
+import org.springframework.stereotype.Service;
+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.groupmember.GroupMemberDO;
+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 java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
+
+/**
+ * 群成员 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class ImGroupMemberServiceImpl implements ImGroupMemberService {
+
+    @Resource
+    private ImGroupMemberMapper imGroupMemberMapper;
+
+    @Override
+    public Long createGroupMember(ImGroupMemberSaveReqVO createReqVO) {
+        // 插入
+        GroupMemberDO groupMember = BeanUtils.toBean(createReqVO, GroupMemberDO.class);
+        imGroupMemberMapper.insert(groupMember);
+        // 返回
+        return groupMember.getId();
+    }
+
+    @Override
+    public void updateGroupMember(ImGroupMemberSaveReqVO updateReqVO) {
+        // 校验存在
+        validateGroupMemberExists(updateReqVO.getId());
+        // 更新
+        GroupMemberDO updateObj = BeanUtils.toBean(updateReqVO, GroupMemberDO.class);
+        imGroupMemberMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteGroupMember(Long id) {
+        // 校验存在
+        validateGroupMemberExists(id);
+        // 删除
+        imGroupMemberMapper.deleteById(id);
+    }
+
+    private void validateGroupMemberExists(Long id) {
+        if (imGroupMemberMapper.selectById(id) == null) {
+            throw exception(GROUP_MEMBER_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public GroupMemberDO getGroupMember(Long id) {
+        return imGroupMemberMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<GroupMemberDO> getGroupMemberPage(ImGroupMemberPageReqVO pageReqVO) {
+        return imGroupMemberMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<GroupMemberDO> selectByGroupId(Long groupId) {
+        return imGroupMemberMapper.selectListByGroupId(groupId);
+    }
+
+}

+ 8 - 8
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxServiceImpl.java

@@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxPageReqVO;
 import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxSaveReqVO;
 import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
-import cn.iocoder.yudao.module.im.dal.mysql.inbox.InboxMapper;
+import cn.iocoder.yudao.module.im.dal.mysql.inbox.ImInboxMapper;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
@@ -23,13 +23,13 @@ import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.INBOX_NOT_EXIS
 public class ImInboxServiceImpl implements ImInboxService {
 
     @Resource
-    private InboxMapper inboxMapper;
+    private ImInboxMapper imInboxMapper;
 
     @Override
     public Long createInbox(ImInboxSaveReqVO createReqVO) {
         // 插入
         ImInboxDO inbox = BeanUtils.toBean(createReqVO, ImInboxDO.class);
-        inboxMapper.insert(inbox);
+        imInboxMapper.insert(inbox);
         // 返回
         return inbox.getId();
     }
@@ -40,7 +40,7 @@ public class ImInboxServiceImpl implements ImInboxService {
         validateInboxExists(updateReqVO.getId());
         // 更新
         ImInboxDO updateObj = BeanUtils.toBean(updateReqVO, ImInboxDO.class);
-        inboxMapper.updateById(updateObj);
+        imInboxMapper.updateById(updateObj);
     }
 
     @Override
@@ -48,23 +48,23 @@ public class ImInboxServiceImpl implements ImInboxService {
         // 校验存在
         validateInboxExists(id);
         // 删除
-        inboxMapper.deleteById(id);
+        imInboxMapper.deleteById(id);
     }
 
     private void validateInboxExists(Long id) {
-        if (inboxMapper.selectById(id) == null) {
+        if (imInboxMapper.selectById(id) == null) {
             throw exception(INBOX_NOT_EXISTS);
         }
     }
 
     @Override
     public ImInboxDO getInbox(Long id) {
-        return inboxMapper.selectById(id);
+        return imInboxMapper.selectById(id);
     }
 
     @Override
     public PageResult<ImInboxDO> getInboxPage(ImInboxPageReqVO pageReqVO) {
-        return inboxMapper.selectPage(pageReqVO);
+        return imInboxMapper.selectPage(pageReqVO);
     }
 
 }

+ 8 - 0
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageService.java

@@ -69,4 +69,12 @@ public interface ImMessageService {
      * @param messageStatus 消息状态
      */
     void updateMessageStatus(Long messageId, Integer messageStatus);
+
+    /**
+     * 保存群聊消息
+     * @param message 消息
+     * @param fromUserId 发送者用户ID
+     * @return id
+     */
+    ImMessageDO saveGroupMessage(ImSendMessage message, Long fromUserId);
 }

+ 13 - 4
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImMessageServiceImpl.java

@@ -76,18 +76,22 @@ public class ImMessageServiceImpl implements ImMessageService {
 
 
     @Override
-    public ImMessageDO savePrivateMessage(ImSendMessage message, Long senderId) {
+    public ImMessageDO savePrivateMessage(ImSendMessage message, Long fromUserId) {
+        return saveImMessageDO(message, fromUserId);
+    }
+
+    private ImMessageDO saveImMessageDO(ImSendMessage message, Long fromUserId) {
         ImMessageDO imMessageDO = new ImMessageDO();
         imMessageDO.setClientMessageId(message.getClientMessageId());
-        imMessageDO.setSenderId(senderId);
+        imMessageDO.setSenderId(fromUserId);
         imMessageDO.setReceiverId(message.getReceiverId());
         //查询发送人昵称和发送人头像
-        AdminUserRespDTO user = adminUserApi.getUser(senderId);
+        AdminUserRespDTO user = adminUserApi.getUser(fromUserId);
         imMessageDO.setSenderNickname(user.getNickname());
         imMessageDO.setSenderAvatar(user.getAvatar());
         imMessageDO.setConversationType(message.getConversationType());
         imMessageDO.setContentType(message.getContentType());
-        imMessageDO.setConversationNo(senderId + "_" + message.getReceiverId());
+        imMessageDO.setConversationNo(fromUserId + "_" + message.getReceiverId());
         imMessageDO.setContent(message.getContent());
         //消息来源 100-用户发送;200-系统发送(一般是通知);不能为空
         imMessageDO.setSendFrom(100);
@@ -108,4 +112,9 @@ public class ImMessageServiceImpl implements ImMessageService {
         imMessageMapper.updateById(imMessageDO);
     }
 
+    @Override
+    public ImMessageDO saveGroupMessage(ImSendMessage message, Long fromUserId) {
+        return saveImMessageDO(message, fromUserId);
+    }
+
 }

+ 58 - 14
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/websocket/ImWebSocketMessageListener.java

@@ -5,11 +5,13 @@ import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListen
 import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;
 import cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;
 import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.ImInboxSaveReqVO;
+import cn.iocoder.yudao.module.im.dal.dataobject.groupmember.GroupMemberDO;
 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImMessageDO;
 import cn.iocoder.yudao.module.im.dal.redis.inbox.SequenceGeneratorRedisDao;
 import cn.iocoder.yudao.module.im.enums.conversation.ImConversationTypeEnum;
 import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
 import cn.iocoder.yudao.module.im.service.conversation.ImConversationService;
+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.message.ImMessageService;
 import cn.iocoder.yudao.module.im.websocket.message.ImReceiveMessage;
@@ -19,6 +21,8 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import org.springframework.web.socket.WebSocketSession;
 
+import java.util.List;
+
 /**
  * WebSocket im
  *
@@ -28,6 +32,7 @@ import org.springframework.web.socket.WebSocketSession;
 @Slf4j
 public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSendMessage> {
 
+    public static final String IM_MESSAGE_RECEIVE = "im-message-receive";
     @Resource
     private WebSocketMessageSender webSocketMessageSender; // WebSocket消息发送器
     @Resource
@@ -37,6 +42,8 @@ public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSe
     @Resource
     private ImInboxService imInboxService; // IM收件箱服务
     @Resource
+    private ImGroupMemberService imGroupMemberService; // 群成员服务
+    @Resource
     private SequenceGeneratorRedisDao sequenceGeneratorRedisDao; // 序列生成器Redis DAO
 
     /**
@@ -51,32 +58,70 @@ public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSe
 
         // 如果是私人消息,处理私人消息
         if (message.getConversationType().equals(ImConversationTypeEnum.PRIVATE.getType())) {
-            ImMessageDO imMessageDO = imMessageService.savePrivateMessage(message, fromUserId); // 保存私人消息
-            handlePrivateMessage(fromUserId, imMessageDO, message);
+            handlePrivateMessage(fromUserId, message);
+        } else if (message.getConversationType().equals(ImConversationTypeEnum.GROUP.getType())) {
+            //处理群聊消息
+            handleGroupMessage(fromUserId, message);
+        } else {
+            log.error("[onMessage][消息类型({}) 未支持]", message.getConversationType());
         }
     }
 
+    /**
+     * 处理群聊消息
+     *
+     * @param fromUserId 发送者用户ID
+     * @param message    发送的IM消息
+     */
+    private void handleGroupMessage(Long fromUserId, ImSendMessage message) {
+        ImMessageDO imMessageDO = imMessageService.saveGroupMessage(message, fromUserId); // 保存群聊消息
+        Long groupId = message.getReceiverId();
+
+        // 发送消息给群聊成员
+        List<GroupMemberDO> groupMemberDOList = imGroupMemberService.selectByGroupId(groupId);
+        groupMemberDOList.forEach(groupMemberDO -> {
+            //过滤掉自己
+            if (groupMemberDO.getUserId().equals(fromUserId)) {
+                return;
+            }
+            Long receiverSequence = sequenceGeneratorRedisDao.generateSequence(groupMemberDO.getUserId()); // 生成接收者序列
+            Long receiverInboxId = createAndSaveInbox(groupMemberDO.getUserId(), imMessageDO.getId(), receiverSequence); // 创建并保存接收者收件箱
+            sendMessage(groupMemberDO.getUserId(), receiverInboxId, imMessageDO, message, receiverSequence);
+        });
+
+        Long fromUserSequence = sequenceGeneratorRedisDao.generateSequence(fromUserId); // 生成发送者序列
+        Long fromUserInboxId = createAndSaveInbox(fromUserId, imMessageDO.getId(), fromUserSequence); // 创建并保存发送者收件箱
+
+        // 发送消息给发送者
+        sendMessage(fromUserId, fromUserInboxId, imMessageDO, message, fromUserSequence);
+
+        // 更新消息状态为成功
+        imMessageService.updateMessageStatus(imMessageDO.getId(), ImMessageStatusEnum.SUCCESS.getStatus());
+    }
+
     /**
      * 处理私人消息
      *
-     * @param fromUserId  发送者用户ID
-     * @param imMessageDO IM消息数据对象
-     * @param message     发送的IM消息
+     * @param fromUserId 发送者用户ID
+     * @param message    发送的IM消息
      */
-    private void handlePrivateMessage(Long fromUserId, ImMessageDO imMessageDO, ImSendMessage message) {
+    private void handlePrivateMessage(Long fromUserId, ImSendMessage message) {
+        ImMessageDO imMessageDO = imMessageService.savePrivateMessage(message, fromUserId); // 保存私人消息
+        Long receiverId = message.getReceiverId();
+
         Long fromUserSequence = sequenceGeneratorRedisDao.generateSequence(fromUserId); // 生成发送者序列
         Long fromUserInboxId = createAndSaveInbox(fromUserId, imMessageDO.getId(), fromUserSequence); // 创建并保存发送者收件箱
-        Long receiverSequence = sequenceGeneratorRedisDao.generateSequence(fromUserId); // 生成接收者序列
+        Long receiverSequence = sequenceGeneratorRedisDao.generateSequence(message.getReceiverId()); // 生成接收者序列
         Long receiverInboxId = createAndSaveInbox(message.getReceiverId(), imMessageDO.getId(), receiverSequence); // 创建并保存接收者收件箱
 
         // 发送消息给接收者和发送者
-        sendMessage(fromUserId, receiverInboxId, imMessageDO, message, "im-message-receive", fromUserSequence);
-        sendMessage(fromUserId, fromUserInboxId, imMessageDO, message, "im-message-receive", receiverSequence);
+        sendMessage(fromUserId, fromUserInboxId, imMessageDO, message, fromUserSequence);
+        sendMessage(receiverId, receiverInboxId, imMessageDO, message, receiverSequence);
 
         // 更新消息状态为成功
         imMessageService.updateMessageStatus(imMessageDO.getId(), ImMessageStatusEnum.SUCCESS.getStatus());
         // 保存私人会话,只有在 client 操作会话(已读、置顶)时,才会延迟创建
-        //imConversationService.savePrivateConversation(fromUserId, message.getReceiverId());
+        //imConversationService.savePrivateConversation(fromUserId, receiverId);
     }
 
     /**
@@ -99,20 +144,19 @@ public class ImWebSocketMessageListener implements WebSocketMessageListener<ImSe
      * @param inboxId     收件箱ID
      * @param imMessageDO IM消息数据对象
      * @param message     发送的IM消息
-     * @param messageType 消息类型
      * @param sequence    序列
      */
-    private void sendMessage(Long fromUserId, Long inboxId, ImMessageDO imMessageDO, ImSendMessage message, String messageType, Long sequence) {
+    private void sendMessage(Long fromUserId, Long inboxId, ImMessageDO imMessageDO, ImSendMessage message, Long sequence) {
         ImReceiveMessage receiveMessage = new ImReceiveMessage(); // 创建接收消息
         receiveMessage.setFromId(fromUserId); // 设置发送者ID
-        receiveMessage.setConversationType(ImConversationTypeEnum.PRIVATE.getType()); // 设置会话类型为私人
+        receiveMessage.setConversationType(message.getConversationType()); // 设置会话类型
         receiveMessage.setContentType(message.getContentType()); // 设置内容类型
         receiveMessage.setContent(message.getContent()); // 设置内容
         receiveMessage.setMessageId(imMessageDO.getId()); // 设置消息ID
         receiveMessage.setInboxId(inboxId); // 设置收件箱ID
         receiveMessage.setSendTime(imMessageDO.getSendTime()); // 设置发送时间
         receiveMessage.setSequence(sequence); // 设置序列
-        webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), fromUserId, messageType, receiveMessage); // 发送消息
+        webSocketMessageSender.sendObject(UserTypeEnum.ADMIN.getValue(), fromUserId, ImWebSocketMessageListener.IM_MESSAGE_RECEIVE, receiveMessage); // 发送消息
     }
 
     /**

+ 1 - 1
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/conversation/ConversationMapper.xml → yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/conversation/ImConversationMapper.xml

@@ -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.conversation.ConversationMapper">
+<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.conversation.ImConversationMapper">
 
     <!--
         一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。

+ 1 - 1
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/inbox/InboxMapper.xml → yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/group/ImGroupMapper.xml

@@ -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.InboxMapper">
+<mapper namespace="cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMapper">
 
     <!--
         一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。

+ 12 - 0
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/groupmember/ImGroupMemberMapper.xml

@@ -0,0 +1,12 @@
+<?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.groupmember.ImGroupMemberMapper">
+
+    <!--
+        一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
+        无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
+        代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
+        文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
+     -->
+
+</mapper>

+ 12 - 0
yudao-module-im/yudao-module-im-biz/src/main/resources/mapper/inbox/ImInboxMapper.xml

@@ -0,0 +1,12 @@
+<?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 进行 CRUD 增删改查即可。
+        无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
+        代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
+        文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
+     -->
+
+</mapper>

+ 15 - 15
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationServiceImplTest.java

@@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 
 import cn.iocoder.yudao.module.im.controller.admin.conversation.vo.*;
 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.dal.mysql.conversation.ImConversationMapper;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 
 import org.springframework.context.annotation.Import;
@@ -33,7 +33,7 @@ public class ImConversationServiceImplTest extends BaseDbUnitTest {
     private ImConversationServiceImpl conversationService;
 
     @Resource
-    private ConversationMapper conversationMapper;
+    private ImConversationMapper imConversationMapper;
 
     @Test
     public void testCreateConversation_success() {
@@ -45,7 +45,7 @@ public class ImConversationServiceImplTest extends BaseDbUnitTest {
         // 断言
         assertNotNull(conversationId);
         // 校验记录的属性是否正确
-        ImConversationDO conversation = conversationMapper.selectById(conversationId);
+        ImConversationDO conversation = imConversationMapper.selectById(conversationId);
         assertPojoEquals(createReqVO, conversation, "id");
     }
 
@@ -53,7 +53,7 @@ public class ImConversationServiceImplTest extends BaseDbUnitTest {
     public void testUpdateConversation_success() {
         // mock 数据
         ImConversationDO dbConversation = randomPojo(ImConversationDO.class);
-        conversationMapper.insert(dbConversation);// @Sql: 先插入出一条存在的数据
+        imConversationMapper.insert(dbConversation);// @Sql: 先插入出一条存在的数据
         // 准备参数
         ImConversationSaveReqVO updateReqVO = randomPojo(ImConversationSaveReqVO.class, o -> {
             o.setId(dbConversation.getId()); // 设置更新的 ID
@@ -62,7 +62,7 @@ public class ImConversationServiceImplTest extends BaseDbUnitTest {
         // 调用
         conversationService.updateConversation(updateReqVO);
         // 校验是否更新正确
-        ImConversationDO conversation = conversationMapper.selectById(updateReqVO.getId()); // 获取最新的
+        ImConversationDO conversation = imConversationMapper.selectById(updateReqVO.getId()); // 获取最新的
         assertPojoEquals(updateReqVO, conversation);
     }
 
@@ -79,14 +79,14 @@ public class ImConversationServiceImplTest extends BaseDbUnitTest {
     public void testDeleteConversation_success() {
         // mock 数据
         ImConversationDO dbConversation = randomPojo(ImConversationDO.class);
-        conversationMapper.insert(dbConversation);// @Sql: 先插入出一条存在的数据
+        imConversationMapper.insert(dbConversation);// @Sql: 先插入出一条存在的数据
         // 准备参数
         Long id = dbConversation.getId();
 
         // 调用
         conversationService.deleteConversation(id);
        // 校验数据不存在了
-       assertNull(conversationMapper.selectById(id));
+       assertNull(imConversationMapper.selectById(id));
     }
 
     @Test
@@ -111,21 +111,21 @@ public class ImConversationServiceImplTest extends BaseDbUnitTest {
            o.setLastReadTime(null);
            o.setCreateTime(null);
        });
-       conversationMapper.insert(dbConversation);
+       imConversationMapper.insert(dbConversation);
        // 测试 userId 不匹配
-       conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setUserId(null)));
+       imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setUserId(null)));
        // 测试 conversationType 不匹配
-       conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setConversationType(null)));
+       imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setConversationType(null)));
        // 测试 targetId 不匹配
-       conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setTargetId(null)));
+       imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setTargetId(null)));
        // 测试 no 不匹配
-       conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setNo(null)));
+       imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setNo(null)));
        // 测试 pinned 不匹配
-       conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setPinned(null)));
+       imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setPinned(null)));
        // 测试 lastReadTime 不匹配
-       conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setLastReadTime(null)));
+       imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setLastReadTime(null)));
        // 测试 createTime 不匹配
-       conversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setCreateTime(null)));
+       imConversationMapper.insert(cloneIgnoreId(dbConversation, o -> o.setCreateTime(null)));
        // 准备参数
        ImConversationPageReqVO reqVO = new ImConversationPageReqVO();
        reqVO.setUserId(null);

+ 147 - 0
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java

@@ -0,0 +1,147 @@
+package cn.iocoder.yudao.module.im.service.group;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import jakarta.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
+import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
+import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link ImGroupServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(ImGroupServiceImpl.class)
+public class ImGroupServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private ImGroupServiceImpl groupService;
+
+    @Resource
+    private ImGroupMapper imGroupMapper;
+
+    @Test
+    public void testCreateGroup_success() {
+        // 准备参数
+        ImGroupSaveReqVO createReqVO = randomPojo(ImGroupSaveReqVO.class).setId(null);
+
+        // 调用
+        Long groupId = groupService.createGroup(createReqVO);
+        // 断言
+        assertNotNull(groupId);
+        // 校验记录的属性是否正确
+        ImGroupDO group = imGroupMapper.selectById(groupId);
+        assertPojoEquals(createReqVO, group, "id");
+    }
+
+    @Test
+    public void testUpdateGroup_success() {
+        // mock 数据
+        ImGroupDO dbGroup = randomPojo(ImGroupDO.class);
+        imGroupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        ImGroupSaveReqVO updateReqVO = randomPojo(ImGroupSaveReqVO.class, o -> {
+            o.setId(dbGroup.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        groupService.updateGroup(updateReqVO);
+        // 校验是否更新正确
+        ImGroupDO group = imGroupMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, group);
+    }
+
+    @Test
+    public void testUpdateGroup_notExists() {
+        // 准备参数
+        ImGroupSaveReqVO updateReqVO = randomPojo(ImGroupSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> groupService.updateGroup(updateReqVO), GROUP_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteGroup_success() {
+        // mock 数据
+        ImGroupDO dbGroup = randomPojo(ImGroupDO.class);
+        imGroupMapper.insert(dbGroup);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbGroup.getId();
+
+        // 调用
+        groupService.deleteGroup(id);
+       // 校验数据不存在了
+       assertNull(imGroupMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteGroup_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> groupService.deleteGroup(id), GROUP_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetGroupPage() {
+       // mock 数据
+       ImGroupDO dbGroup = randomPojo(ImGroupDO.class, o -> { // 等会查询到
+           o.setGroupName(null);
+           o.setOwnerId(null);
+           o.setHeadImage(null);
+           o.setHeadImageThumb(null);
+           o.setNotice(null);
+           o.setRemark(null);
+           o.setCreateTime(null);
+       });
+       imGroupMapper.insert(dbGroup);
+       // 测试 groupName 不匹配
+       imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setGroupName(null)));
+       // 测试 ownerId 不匹配
+       imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setOwnerId(null)));
+       // 测试 headImage 不匹配
+       imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setHeadImage(null)));
+       // 测试 headImageThumb 不匹配
+       imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setHeadImageThumb(null)));
+       // 测试 notice 不匹配
+       imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setNotice(null)));
+       // 测试 remark 不匹配
+       imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setRemark(null)));
+       // 测试 createTime 不匹配
+       imGroupMapper.insert(cloneIgnoreId(dbGroup, o -> o.setCreateTime(null)));
+       // 准备参数
+       ImGroupPageReqVO reqVO = new ImGroupPageReqVO();
+       reqVO.setGroupName(null);
+       reqVO.setOwnerId(null);
+       reqVO.setHeadImage(null);
+       reqVO.setHeadImageThumb(null);
+       reqVO.setNotice(null);
+       reqVO.setRemark(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<ImGroupDO> pageResult = groupService.getGroupPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbGroup, pageResult.getList().get(0));
+    }
+
+}

+ 147 - 0
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/groupmember/ImGroupMemberServiceImplTest.java

@@ -0,0 +1,147 @@
+package cn.iocoder.yudao.module.im.service.groupmember;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import jakarta.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.im.controller.admin.groupmember.vo.*;
+import cn.iocoder.yudao.module.im.dal.dataobject.groupmember.GroupMemberDO;
+import cn.iocoder.yudao.module.im.dal.mysql.groupmember.ImGroupMemberMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link ImGroupMemberServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(ImGroupMemberServiceImpl.class)
+public class ImGroupMemberServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private ImGroupMemberServiceImpl groupMemberService;
+
+    @Resource
+    private ImGroupMemberMapper imGroupMemberMapper;
+
+    @Test
+    public void testCreateGroupMember_success() {
+        // 准备参数
+        ImGroupMemberSaveReqVO createReqVO = randomPojo(ImGroupMemberSaveReqVO.class).setId(null);
+
+        // 调用
+        Long groupMemberId = groupMemberService.createGroupMember(createReqVO);
+        // 断言
+        assertNotNull(groupMemberId);
+        // 校验记录的属性是否正确
+        GroupMemberDO groupMember = imGroupMemberMapper.selectById(groupMemberId);
+        assertPojoEquals(createReqVO, groupMember, "id");
+    }
+
+    @Test
+    public void testUpdateGroupMember_success() {
+        // mock 数据
+        GroupMemberDO dbGroupMember = randomPojo(GroupMemberDO.class);
+        imGroupMemberMapper.insert(dbGroupMember);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        ImGroupMemberSaveReqVO updateReqVO = randomPojo(ImGroupMemberSaveReqVO.class, o -> {
+            o.setId(dbGroupMember.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        groupMemberService.updateGroupMember(updateReqVO);
+        // 校验是否更新正确
+        GroupMemberDO groupMember = imGroupMemberMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, groupMember);
+    }
+
+    @Test
+    public void testUpdateGroupMember_notExists() {
+        // 准备参数
+        ImGroupMemberSaveReqVO updateReqVO = randomPojo(ImGroupMemberSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> groupMemberService.updateGroupMember(updateReqVO), GROUP_MEMBER_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteGroupMember_success() {
+        // mock 数据
+        GroupMemberDO dbGroupMember = randomPojo(GroupMemberDO.class);
+        imGroupMemberMapper.insert(dbGroupMember);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbGroupMember.getId();
+
+        // 调用
+        groupMemberService.deleteGroupMember(id);
+       // 校验数据不存在了
+       assertNull(imGroupMemberMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteGroupMember_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> groupMemberService.deleteGroupMember(id), GROUP_MEMBER_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetGroupMemberPage() {
+       // mock 数据
+       GroupMemberDO dbGroupMember = randomPojo(GroupMemberDO.class, o -> { // 等会查询到
+           o.setGroupId(null);
+           o.setUserId(null);
+           o.setNickname(null);
+           o.setAvatar(null);
+           o.setAliasName(null);
+           o.setRemark(null);
+           o.setCreateTime(null);
+       });
+       imGroupMemberMapper.insert(dbGroupMember);
+       // 测试 groupId 不匹配
+       imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setGroupId(null)));
+       // 测试 userId 不匹配
+       imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setUserId(null)));
+       // 测试 nickname 不匹配
+       imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setNickname(null)));
+       // 测试 avatar 不匹配
+       imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setAvatar(null)));
+       // 测试 aliasName 不匹配
+       imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setAliasName(null)));
+       // 测试 remark 不匹配
+       imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setRemark(null)));
+       // 测试 createTime 不匹配
+       imGroupMemberMapper.insert(cloneIgnoreId(dbGroupMember, o -> o.setCreateTime(null)));
+       // 准备参数
+       ImGroupMemberPageReqVO reqVO = new ImGroupMemberPageReqVO();
+       reqVO.setGroupId(null);
+       reqVO.setUserId(null);
+       reqVO.setNickname(null);
+       reqVO.setAvatar(null);
+       reqVO.setAliasName(null);
+       reqVO.setRemark(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<GroupMemberDO> pageResult = groupMemberService.getGroupMemberPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbGroupMember, pageResult.getList().get(0));
+    }
+
+}

+ 12 - 12
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/inbox/ImInboxServiceImplTest.java

@@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 
 import cn.iocoder.yudao.module.im.controller.admin.inbox.vo.*;
 import cn.iocoder.yudao.module.im.dal.dataobject.inbox.ImInboxDO;
-import cn.iocoder.yudao.module.im.dal.mysql.inbox.InboxMapper;
+import cn.iocoder.yudao.module.im.dal.mysql.inbox.ImInboxMapper;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 
 import org.springframework.context.annotation.Import;
@@ -33,7 +33,7 @@ public class ImInboxServiceImplTest extends BaseDbUnitTest {
     private ImInboxServiceImpl inboxService;
 
     @Resource
-    private InboxMapper inboxMapper;
+    private ImInboxMapper imInboxMapper;
 
     @Test
     public void testCreateInbox_success() {
@@ -45,7 +45,7 @@ public class ImInboxServiceImplTest extends BaseDbUnitTest {
         // 断言
         assertNotNull(inboxId);
         // 校验记录的属性是否正确
-        ImInboxDO inbox = inboxMapper.selectById(inboxId);
+        ImInboxDO inbox = imInboxMapper.selectById(inboxId);
         assertPojoEquals(createReqVO, inbox, "id");
     }
 
@@ -53,7 +53,7 @@ public class ImInboxServiceImplTest extends BaseDbUnitTest {
     public void testUpdateInbox_success() {
         // mock 数据
         ImInboxDO dbInbox = randomPojo(ImInboxDO.class);
-        inboxMapper.insert(dbInbox);// @Sql: 先插入出一条存在的数据
+        imInboxMapper.insert(dbInbox);// @Sql: 先插入出一条存在的数据
         // 准备参数
         ImInboxSaveReqVO updateReqVO = randomPojo(ImInboxSaveReqVO.class, o -> {
             o.setId(dbInbox.getId()); // 设置更新的 ID
@@ -62,7 +62,7 @@ public class ImInboxServiceImplTest extends BaseDbUnitTest {
         // 调用
         inboxService.updateInbox(updateReqVO);
         // 校验是否更新正确
-        ImInboxDO inbox = inboxMapper.selectById(updateReqVO.getId()); // 获取最新的
+        ImInboxDO inbox = imInboxMapper.selectById(updateReqVO.getId()); // 获取最新的
         assertPojoEquals(updateReqVO, inbox);
     }
 
@@ -79,14 +79,14 @@ public class ImInboxServiceImplTest extends BaseDbUnitTest {
     public void testDeleteInbox_success() {
         // mock 数据
         ImInboxDO dbInbox = randomPojo(ImInboxDO.class);
-        inboxMapper.insert(dbInbox);// @Sql: 先插入出一条存在的数据
+        imInboxMapper.insert(dbInbox);// @Sql: 先插入出一条存在的数据
         // 准备参数
         Long id = dbInbox.getId();
 
         // 调用
         inboxService.deleteInbox(id);
        // 校验数据不存在了
-       assertNull(inboxMapper.selectById(id));
+       assertNull(imInboxMapper.selectById(id));
     }
 
     @Test
@@ -108,15 +108,15 @@ public class ImInboxServiceImplTest extends BaseDbUnitTest {
            o.setSequence(null);
            o.setCreateTime(null);
        });
-       inboxMapper.insert(dbInbox);
+       imInboxMapper.insert(dbInbox);
        // 测试 userId 不匹配
-       inboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setUserId(null)));
+       imInboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setUserId(null)));
        // 测试 messageId 不匹配
-       inboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setMessageId(null)));
+       imInboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setMessageId(null)));
        // 测试 sequence 不匹配
-       inboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setSequence(null)));
+       imInboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setSequence(null)));
        // 测试 createTime 不匹配
-       inboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setCreateTime(null)));
+       imInboxMapper.insert(cloneIgnoreId(dbInbox, o -> o.setCreateTime(null)));
        // 准备参数
        ImInboxPageReqVO reqVO = new ImInboxPageReqVO();
        reqVO.setUserId(null);