Commit fc90635abbf739518f18204ceeae90f3f00e748e

Authored by YunaiV
1 parent 37f0a8a0

✨ feat(im): 新增 friend、group 等管理后台接口

Showing 27 changed files with 282 additions and 278 deletions
yudao-dependencies/pom.xml
@@ -50,6 +50,7 @@ @@ -50,6 +50,7 @@
50 <!-- 工具类相关 --> 50 <!-- 工具类相关 -->
51 <anji-plus-captcha.version>1.4.0</anji-plus-captcha.version> 51 <anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>
52 <jsoup.version>1.21.2</jsoup.version> 52 <jsoup.version>1.21.2</jsoup.version>
  53 + <sensitive-word.version>0.29.5</sensitive-word.version>
53 <lombok.version>1.18.42</lombok.version> 54 <lombok.version>1.18.42</lombok.version>
54 <mapstruct.version>1.6.3</mapstruct.version> 55 <mapstruct.version>1.6.3</mapstruct.version>
55 <hutool-5.version>5.8.42</hutool-5.version> 56 <hutool-5.version>5.8.42</hutool-5.version>
@@ -558,6 +559,12 @@ @@ -558,6 +559,12 @@
558 <version>${jsoup.version}</version> 559 <version>${jsoup.version}</version>
559 </dependency> 560 </dependency>
560 561
  562 + <dependency>
  563 + <groupId>com.github.houbb</groupId>
  564 + <artifactId>sensitive-word</artifactId> <!-- 敏感词检测:trie 树高效匹配 -->
  565 + <version>${sensitive-word.version}</version>
  566 + </dependency>
  567 +
561 <!-- Vert.x --> 568 <!-- Vert.x -->
562 <dependency> 569 <dependency>
563 <groupId>io.vertx</groupId> 570 <groupId>io.vertx</groupId>
yudao-module-im/pom.xml
@@ -62,12 +62,10 @@ @@ -62,12 +62,10 @@
62 <artifactId>yudao-spring-boot-starter-excel</artifactId> 62 <artifactId>yudao-spring-boot-starter-excel</artifactId>
63 </dependency> 63 </dependency>
64 64
65 - <!-- 敏感词检测:houbb sensitive-word(trie 树高效匹配,支持忽略大小写 / 全半角 / 数字风格) -->  
66 - <!-- TODO @AI:放到基础 pom 里管理。 --> 65 + <!-- 敏感词检测:sensitive-word(trie 树高效匹配,支持忽略大小写 / 全半角 / 数字风格),版本由 yudao-dependencies 统一管理 -->
67 <dependency> 66 <dependency>
68 <groupId>com.github.houbb</groupId> 67 <groupId>com.github.houbb</groupId>
69 <artifactId>sensitive-word</artifactId> 68 <artifactId>sensitive-word</artifactId>
70 - <version>0.29.5</version>  
71 </dependency> 69 </dependency>
72 70
73 <!-- Test 测试相关 --> 71 <!-- Test 测试相关 -->
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/ImFriendManagerController.java
1 package cn.iocoder.yudao.module.im.controller.admin.manager.friend; 1 package cn.iocoder.yudao.module.im.controller.admin.manager.friend;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerPageReqVO; 8 import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerPageReqVO;
6 import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerRespVO; 9 import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerRespVO;
  10 +import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO;
7 import cn.iocoder.yudao.module.im.service.friend.ImFriendService; 11 import cn.iocoder.yudao.module.im.service.friend.ImFriendService;
  12 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  13 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
8 import io.swagger.v3.oas.annotations.Operation; 14 import io.swagger.v3.oas.annotations.Operation;
9 import io.swagger.v3.oas.annotations.tags.Tag; 15 import io.swagger.v3.oas.annotations.tags.Tag;
10 import jakarta.annotation.Resource; 16 import jakarta.annotation.Resource;
@@ -15,7 +21,12 @@ import org.springframework.web.bind.annotation.GetMapping; @@ -15,7 +21,12 @@ import org.springframework.web.bind.annotation.GetMapping;
15 import org.springframework.web.bind.annotation.RequestMapping; 21 import org.springframework.web.bind.annotation.RequestMapping;
16 import org.springframework.web.bind.annotation.RestController; 22 import org.springframework.web.bind.annotation.RestController;
17 23
  24 +import java.util.Map;
  25 +import java.util.Set;
  26 +import java.util.stream.Stream;
  27 +
18 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 28 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
19 30
20 @Tag(name = "管理后台 - IM 好友管理") 31 @Tag(name = "管理后台 - IM 好友管理")
21 @RestController 32 @RestController
@@ -25,13 +36,30 @@ public class ImFriendManagerController { @@ -25,13 +36,30 @@ public class ImFriendManagerController {
25 36
26 @Resource 37 @Resource
27 private ImFriendService friendService; 38 private ImFriendService friendService;
  39 + @Resource
  40 + private AdminUserApi adminUserApi;
28 41
29 @GetMapping("/page") 42 @GetMapping("/page")
30 @Operation(summary = "获得好友关系分页") 43 @Operation(summary = "获得好友关系分页")
31 @PreAuthorize("@ss.hasPermission('im:manager:friend:query')") 44 @PreAuthorize("@ss.hasPermission('im:manager:friend:query')")
32 public CommonResult<PageResult<ImFriendManagerRespVO>> getFriendPage( 45 public CommonResult<PageResult<ImFriendManagerRespVO>> getFriendPage(
33 @Valid ImFriendManagerPageReqVO pageReqVO) { 46 @Valid ImFriendManagerPageReqVO pageReqVO) {
34 - return success(friendService.getFriendPage(pageReqVO)); 47 + // 1. 分页查询
  48 + PageResult<ImFriendDO> pageResult = friendService.getFriendPage(pageReqVO);
  49 + if (CollUtil.isEmpty(pageResult.getList())) {
  50 + return success(PageResult.empty(pageResult.getTotal()));
  51 + }
  52 + // 2.1 一次性批量查询用户 + 好友的昵称
  53 + Set<Long> userIds = convertSetByFlatMap(pageResult.getList(),
  54 + f -> Stream.of(f.getUserId(), f.getFriendUserId()));
  55 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
  56 + // 2.2 转换为 VO,填充昵称
  57 + return success(BeanUtils.toBean(pageResult, ImFriendManagerRespVO.class, vo -> {
  58 + MapUtils.findAndThen(userMap, vo.getUserId(),
  59 + user -> vo.setUserNickname(user.getNickname()));
  60 + MapUtils.findAndThen(userMap, vo.getFriendUserId(),
  61 + user -> vo.setFriendNickname(user.getNickname()));
  62 + }));
35 } 63 }
36 64
37 } 65 }
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/ImGroupManagerController.java
1 package cn.iocoder.yudao.module.im.controller.admin.manager.group; 1 package cn.iocoder.yudao.module.im.controller.admin.manager.group;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerBanReqVO; 8 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerBanReqVO;
7 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerPageReqVO; 9 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerPageReqVO;
8 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerRespVO; 10 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerRespVO;
9 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; 11 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
  12 +import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService;
10 import cn.iocoder.yudao.module.im.service.group.ImGroupService; 13 import cn.iocoder.yudao.module.im.service.group.ImGroupService;
  14 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  15 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
11 import io.swagger.v3.oas.annotations.Operation; 16 import io.swagger.v3.oas.annotations.Operation;
12 import io.swagger.v3.oas.annotations.Parameter; 17 import io.swagger.v3.oas.annotations.Parameter;
13 import io.swagger.v3.oas.annotations.tags.Tag; 18 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -17,7 +22,10 @@ import org.springframework.security.access.prepost.PreAuthorize; @@ -17,7 +22,10 @@ import org.springframework.security.access.prepost.PreAuthorize;
17 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
18 import org.springframework.web.bind.annotation.*; 23 import org.springframework.web.bind.annotation.*;
19 24
  25 +import java.util.Map;
  26 +
20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 27 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
21 29
22 @Tag(name = "管理后台 - IM 群聊管理") 30 @Tag(name = "管理后台 - IM 群聊管理")
23 @RestController 31 @RestController
@@ -27,12 +35,32 @@ public class ImGroupManagerController { @@ -27,12 +35,32 @@ public class ImGroupManagerController {
27 35
28 @Resource 36 @Resource
29 private ImGroupService groupService; 37 private ImGroupService groupService;
  38 + @Resource
  39 + private ImGroupMemberService groupMemberService;
  40 +
  41 + @Resource
  42 + private AdminUserApi adminUserApi;
30 43
31 @GetMapping("/page") 44 @GetMapping("/page")
32 @Operation(summary = "获得群分页") 45 @Operation(summary = "获得群分页")
33 @PreAuthorize("@ss.hasPermission('im:manager:group:query')") 46 @PreAuthorize("@ss.hasPermission('im:manager:group:query')")
34 public CommonResult<PageResult<ImGroupManagerRespVO>> getGroupPage(@Valid ImGroupManagerPageReqVO pageReqVO) { 47 public CommonResult<PageResult<ImGroupManagerRespVO>> getGroupPage(@Valid ImGroupManagerPageReqVO pageReqVO) {
35 - return success(groupService.getGroupPage(pageReqVO)); 48 + // 1. 分页查询群
  49 + PageResult<ImGroupDO> pageResult = groupService.getGroupPage(pageReqVO);
  50 + if (CollUtil.isEmpty(pageResult.getList())) {
  51 + return success(PageResult.empty(pageResult.getTotal()));
  52 + }
  53 + // 2.1 批量查询相关数据
  54 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  55 + convertSet(pageResult.getList(), ImGroupDO::getOwnerUserId));
  56 + Map<Long, Long> memberCountMap = groupMemberService.getActiveMemberCountMap(
  57 + convertSet(pageResult.getList(), ImGroupDO::getId));
  58 + // 2.2 转换为 VO,填充群主昵称、群成员数量
  59 + return success(BeanUtils.toBean(pageResult, ImGroupManagerRespVO.class, vo -> {
  60 + MapUtils.findAndThen(userMap, vo.getOwnerUserId(),
  61 + user -> vo.setOwnerNickname(user.getNickname()));
  62 + vo.setMemberCount(memberCountMap.getOrDefault(vo.getId(), 0L).intValue());
  63 + }));
36 } 64 }
37 65
38 @GetMapping("/get") 66 @GetMapping("/get")
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/ImGroupMemberManagerController.java
1 package cn.iocoder.yudao.module.im.controller.admin.manager.group; 1 package cn.iocoder.yudao.module.im.controller.admin.manager.group;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.member.ImGroupMemberManagerRespVO; 7 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.member.ImGroupMemberManagerRespVO;
  8 +import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
5 import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService; 9 import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService;
  10 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  11 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
6 import io.swagger.v3.oas.annotations.Operation; 12 import io.swagger.v3.oas.annotations.Operation;
7 import io.swagger.v3.oas.annotations.Parameter; 13 import io.swagger.v3.oas.annotations.Parameter;
8 import io.swagger.v3.oas.annotations.tags.Tag; 14 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -14,9 +20,13 @@ import org.springframework.web.bind.annotation.RequestMapping; @@ -14,9 +20,13 @@ import org.springframework.web.bind.annotation.RequestMapping;
14 import org.springframework.web.bind.annotation.RequestParam; 20 import org.springframework.web.bind.annotation.RequestParam;
15 import org.springframework.web.bind.annotation.RestController; 21 import org.springframework.web.bind.annotation.RestController;
16 22
  23 +import java.util.Collections;
17 import java.util.List; 24 import java.util.List;
  25 +import java.util.Map;
  26 +import java.util.Set;
18 27
19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 28 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
20 30
21 @Tag(name = "管理后台 - IM 群成员管理") 31 @Tag(name = "管理后台 - IM 群成员管理")
22 @RestController 32 @RestController
@@ -26,13 +36,26 @@ public class ImGroupMemberManagerController { @@ -26,13 +36,26 @@ public class ImGroupMemberManagerController {
26 36
27 @Resource 37 @Resource
28 private ImGroupMemberService groupMemberService; 38 private ImGroupMemberService groupMemberService;
  39 + @Resource
  40 + private AdminUserApi adminUserApi;
29 41
30 @GetMapping("/list") 42 @GetMapping("/list")
31 @Operation(summary = "获得群成员列表") 43 @Operation(summary = "获得群成员列表")
32 @Parameter(name = "groupId", description = "群编号", required = true, example = "1024") 44 @Parameter(name = "groupId", description = "群编号", required = true, example = "1024")
33 @PreAuthorize("@ss.hasPermission('im:manager:group:query')") 45 @PreAuthorize("@ss.hasPermission('im:manager:group:query')")
34 public CommonResult<List<ImGroupMemberManagerRespVO>> getGroupMemberList(@RequestParam("groupId") Long groupId) { 46 public CommonResult<List<ImGroupMemberManagerRespVO>> getGroupMemberList(@RequestParam("groupId") Long groupId) {
35 - return success(groupMemberService.getGroupMemberList(groupId)); 47 + // 1. 查询有效群成员
  48 + List<ImGroupMemberDO> members = groupMemberService.getActiveGroupMemberListByGroupId(groupId);
  49 + if (CollUtil.isEmpty(members)) {
  50 + return success(Collections.emptyList());
  51 + }
  52 + // 2.1 批量查询用户信息
  53 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  54 + convertSet(members, ImGroupMemberDO::getUserId));
  55 + // 2.2 转换为 VO,填充昵称、头像
  56 + return success(BeanUtils.toBean(members, ImGroupMemberManagerRespVO.class, vo ->
  57 + MapUtils.findAndThen(userMap, vo.getUserId(), user ->
  58 + vo.setNickname(user.getNickname()).setAvatar(user.getAvatar()))));
36 } 59 }
37 60
38 } 61 }
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupManagerPageReqVO.java
@@ -21,8 +21,8 @@ public class ImGroupManagerPageReqVO extends PageParam { @@ -21,8 +21,8 @@ public class ImGroupManagerPageReqVO extends PageParam {
21 @Schema(description = "群主用户编号", example = "1024") 21 @Schema(description = "群主用户编号", example = "1024")
22 private Long ownerUserId; 22 private Long ownerUserId;
23 23
24 - @Schema(description = "群状态,参见 CommonStatusEnum 枚举类(0 正常 / 1 已解散)", example = "0")  
25 - private Integer status; 24 + @Schema(description = "群状态", example = "0")
  25 + private Integer status; // 参见 CommonStatusEnum 枚举类(0 正常 / 1 已解散)
26 26
27 @Schema(description = "是否封禁", example = "false") 27 @Schema(description = "是否封禁", example = "false")
28 private Boolean banned; 28 private Boolean banned;
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImGroupMessageManagerController.java
1 package cn.iocoder.yudao.module.im.controller.admin.manager.message; 1 package cn.iocoder.yudao.module.im.controller.admin.manager.message;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerPageReqVO; 8 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerPageReqVO;
7 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerRespVO; 9 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerRespVO;
  10 +import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
8 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO; 11 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO;
  12 +import cn.iocoder.yudao.module.im.service.group.ImGroupService;
9 import cn.iocoder.yudao.module.im.service.message.ImGroupMessageService; 13 import cn.iocoder.yudao.module.im.service.message.ImGroupMessageService;
  14 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  15 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
10 import io.swagger.v3.oas.annotations.Operation; 16 import io.swagger.v3.oas.annotations.Operation;
11 import io.swagger.v3.oas.annotations.Parameter; 17 import io.swagger.v3.oas.annotations.Parameter;
12 import io.swagger.v3.oas.annotations.tags.Tag; 18 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -14,9 +20,15 @@ import jakarta.annotation.Resource; @@ -14,9 +20,15 @@ import jakarta.annotation.Resource;
14 import jakarta.validation.Valid; 20 import jakarta.validation.Valid;
15 import org.springframework.security.access.prepost.PreAuthorize; 21 import org.springframework.security.access.prepost.PreAuthorize;
16 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
17 -import org.springframework.web.bind.annotation.*; 23 +import org.springframework.web.bind.annotation.GetMapping;
  24 +import org.springframework.web.bind.annotation.RequestMapping;
  25 +import org.springframework.web.bind.annotation.RequestParam;
  26 +import org.springframework.web.bind.annotation.RestController;
  27 +
  28 +import java.util.Map;
18 29
19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
20 32
21 @Tag(name = "管理后台 - IM 群聊消息") 33 @Tag(name = "管理后台 - IM 群聊消息")
22 @RestController 34 @RestController
@@ -26,13 +38,31 @@ public class ImGroupMessageManagerController { @@ -26,13 +38,31 @@ public class ImGroupMessageManagerController {
26 38
27 @Resource 39 @Resource
28 private ImGroupMessageService groupMessageService; 40 private ImGroupMessageService groupMessageService;
  41 + @Resource
  42 + private ImGroupService groupService;
  43 + @Resource
  44 + private AdminUserApi adminUserApi;
29 45
30 @GetMapping("/page") 46 @GetMapping("/page")
31 @Operation(summary = "获得群聊消息分页") 47 @Operation(summary = "获得群聊消息分页")
32 @PreAuthorize("@ss.hasPermission('im:manager:message:query')") 48 @PreAuthorize("@ss.hasPermission('im:manager:message:query')")
33 public CommonResult<PageResult<ImGroupMessageManagerRespVO>> getGroupMessagePage( 49 public CommonResult<PageResult<ImGroupMessageManagerRespVO>> getGroupMessagePage(
34 @Valid ImGroupMessageManagerPageReqVO pageReqVO) { 50 @Valid ImGroupMessageManagerPageReqVO pageReqVO) {
35 - return success(groupMessageService.getGroupMessagePage(pageReqVO)); 51 + // 1. 分页查询
  52 + PageResult<ImGroupMessageDO> pageResult = groupMessageService.getGroupMessagePage(pageReqVO);
  53 + if (CollUtil.isEmpty(pageResult.getList())) {
  54 + return success(PageResult.empty(pageResult.getTotal()));
  55 + }
  56 + // 2.1 批量查询群名称、发送人昵称
  57 + Map<Long, ImGroupDO> groupMap = groupService.getGroupMap(
  58 + convertSet(pageResult.getList(), ImGroupMessageDO::getGroupId));
  59 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  60 + convertSet(pageResult.getList(), ImGroupMessageDO::getSenderId));
  61 + // 2.2 转换为 VO,填充群名 / 发送人昵称
  62 + return success(BeanUtils.toBean(pageResult, ImGroupMessageManagerRespVO.class, vo -> {
  63 + MapUtils.findAndThen(groupMap, vo.getGroupId(), group -> vo.setGroupName(group.getName()));
  64 + MapUtils.findAndThen(userMap, vo.getSenderId(), user -> vo.setSenderNickname(user.getNickname()));
  65 + }));
36 } 66 }
37 67
38 @GetMapping("/get") 68 @GetMapping("/get")
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImPrivateMessageManagerController.java
1 package cn.iocoder.yudao.module.im.controller.admin.manager.message; 1 package cn.iocoder.yudao.module.im.controller.admin.manager.message;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerPageReqVO; 8 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerPageReqVO;
7 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerRespVO; 9 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerRespVO;
8 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO; 10 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO;
9 import cn.iocoder.yudao.module.im.service.message.ImPrivateMessageService; 11 import cn.iocoder.yudao.module.im.service.message.ImPrivateMessageService;
  12 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  13 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
10 import io.swagger.v3.oas.annotations.Operation; 14 import io.swagger.v3.oas.annotations.Operation;
11 import io.swagger.v3.oas.annotations.Parameter; 15 import io.swagger.v3.oas.annotations.Parameter;
12 import io.swagger.v3.oas.annotations.tags.Tag; 16 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -14,9 +18,16 @@ import jakarta.annotation.Resource; @@ -14,9 +18,16 @@ import jakarta.annotation.Resource;
14 import jakarta.validation.Valid; 18 import jakarta.validation.Valid;
15 import org.springframework.security.access.prepost.PreAuthorize; 19 import org.springframework.security.access.prepost.PreAuthorize;
16 import org.springframework.validation.annotation.Validated; 20 import org.springframework.validation.annotation.Validated;
17 -import org.springframework.web.bind.annotation.*; 21 +import org.springframework.web.bind.annotation.GetMapping;
  22 +import org.springframework.web.bind.annotation.RequestMapping;
  23 +import org.springframework.web.bind.annotation.RequestParam;
  24 +import org.springframework.web.bind.annotation.RestController;
  25 +
  26 +import java.util.Map;
  27 +import java.util.stream.Stream;
18 28
19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 29 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  30 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
20 31
21 @Tag(name = "管理后台 - IM 私聊消息") 32 @Tag(name = "管理后台 - IM 私聊消息")
22 @RestController 33 @RestController
@@ -26,13 +37,27 @@ public class ImPrivateMessageManagerController { @@ -26,13 +37,27 @@ public class ImPrivateMessageManagerController {
26 37
27 @Resource 38 @Resource
28 private ImPrivateMessageService privateMessageService; 39 private ImPrivateMessageService privateMessageService;
  40 + @Resource
  41 + private AdminUserApi adminUserApi;
29 42
30 @GetMapping("/page") 43 @GetMapping("/page")
31 @Operation(summary = "获得私聊消息分页") 44 @Operation(summary = "获得私聊消息分页")
32 @PreAuthorize("@ss.hasPermission('im:manager:message:query')") 45 @PreAuthorize("@ss.hasPermission('im:manager:message:query')")
33 public CommonResult<PageResult<ImPrivateMessageManagerRespVO>> getPrivateMessagePage( 46 public CommonResult<PageResult<ImPrivateMessageManagerRespVO>> getPrivateMessagePage(
34 @Valid ImPrivateMessageManagerPageReqVO pageReqVO) { 47 @Valid ImPrivateMessageManagerPageReqVO pageReqVO) {
35 - return success(privateMessageService.getPrivateMessagePage(pageReqVO)); 48 + // 1. 分页查询
  49 + PageResult<ImPrivateMessageDO> pageResult = privateMessageService.getPrivateMessagePage(pageReqVO);
  50 + if (CollUtil.isEmpty(pageResult.getList())) {
  51 + return success(PageResult.empty(pageResult.getTotal()));
  52 + }
  53 + // 2.1 一次性批量查询发送人 + 接收人昵称
  54 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(pageResult.getList(),
  55 + m -> Stream.of(m.getSenderId(), m.getReceiverId())));
  56 + // 2.2 转换为 VO,填充昵称
  57 + return success(BeanUtils.toBean(pageResult, ImPrivateMessageManagerRespVO.class, vo -> {
  58 + MapUtils.findAndThen(userMap, vo.getSenderId(), user -> vo.setSenderNickname(user.getNickname()));
  59 + MapUtils.findAndThen(userMap, vo.getReceiverId(), user -> vo.setReceiverNickname(user.getNickname()));
  60 + }));
36 } 61 }
37 62
38 @GetMapping("/get") 63 @GetMapping("/get")
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/group/ImGroupMessageManagerPageReqVO.java
@@ -21,12 +21,11 @@ public class ImGroupMessageManagerPageReqVO extends PageParam { @@ -21,12 +21,11 @@ public class ImGroupMessageManagerPageReqVO extends PageParam {
21 @Schema(description = "发送人编号", example = "1024") 21 @Schema(description = "发送人编号", example = "1024")
22 private Long senderId; 22 private Long senderId;
23 23
24 - // TODO @AI:参见 ImMessageTypeEnum 枚举类,都放到 // 注释后面。注意加到我的 memory 里。其它类似也处理下。im 的。  
25 - @Schema(description = "消息类型,参见 ImMessageTypeEnum 枚举类", example = "1")  
26 - private Integer type; 24 + @Schema(description = "消息类型", example = "1")
  25 + private Integer type; // 参见 ImMessageTypeEnum 枚举类
27 26
28 - @Schema(description = "消息状态,参见 ImMessageStatusEnum 枚举类", example = "0")  
29 - private Integer status; 27 + @Schema(description = "消息状态", example = "0")
  28 + private Integer status; // 参见 ImMessageStatusEnum 枚举类
30 29
31 @Schema(description = "发送时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]") 30 @Schema(description = "发送时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]")
32 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 31 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/privates/ImPrivateMessageManagerPageReqVO.java
@@ -21,11 +21,11 @@ public class ImPrivateMessageManagerPageReqVO extends PageParam { @@ -21,11 +21,11 @@ public class ImPrivateMessageManagerPageReqVO extends PageParam {
21 @Schema(description = "接收人编号", example = "2048") 21 @Schema(description = "接收人编号", example = "2048")
22 private Long receiverId; 22 private Long receiverId;
23 23
24 - @Schema(description = "消息类型,参见 ImMessageTypeEnum 枚举类", example = "1")  
25 - private Integer type; 24 + @Schema(description = "消息类型", example = "1")
  25 + private Integer type; // 参见 ImMessageTypeEnum 枚举类
26 26
27 - @Schema(description = "消息状态,参见 ImMessageStatusEnum 枚举类", example = "0")  
28 - private Integer status; 27 + @Schema(description = "消息状态", example = "0")
  28 + private Integer status; // 参见 ImMessageStatusEnum 枚举类
29 29
30 @Schema(description = "发送时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]") 30 @Schema(description = "发送时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]")
31 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 31 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordPageReqVO.java
@@ -18,8 +18,8 @@ public class ImSensitiveWordPageReqVO extends PageParam { @@ -18,8 +18,8 @@ public class ImSensitiveWordPageReqVO extends PageParam {
18 @Schema(description = "敏感词,模糊匹配", example = "敏感") 18 @Schema(description = "敏感词,模糊匹配", example = "敏感")
19 private String word; 19 private String word;
20 20
21 - @Schema(description = "状态,参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用)", example = "0")  
22 - private Integer status; 21 + @Schema(description = "状态", example = "0")
  22 + private Integer status; // 参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用)
23 23
24 @Schema(description = "创建时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]") 24 @Schema(description = "创建时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]")
25 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 25 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordSaveReqVO.java
@@ -18,9 +18,8 @@ public class ImSensitiveWordSaveReqVO { @@ -18,9 +18,8 @@ public class ImSensitiveWordSaveReqVO {
18 @Size(max = 64, message = "敏感词长度不能超过 64") 18 @Size(max = 64, message = "敏感词长度不能超过 64")
19 private String word; 19 private String word;
20 20
21 - @Schema(description = "状态,参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用)",  
22 - requiredMode = Schema.RequiredMode.REQUIRED, example = "0") 21 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
23 @NotNull(message = "状态不能为空") 22 @NotNull(message = "状态不能为空")
24 - private Integer status; 23 + private Integer status; // 参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用)
25 24
26 } 25 }
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupMemberMapper.java
@@ -90,7 +90,6 @@ public interface ImGroupMemberMapper extends BaseMapperX&lt;ImGroupMemberDO&gt; { @@ -90,7 +90,6 @@ public interface ImGroupMemberMapper extends BaseMapperX&lt;ImGroupMemberDO&gt; {
90 if (CollUtil.isEmpty(groupIds)) { 90 if (CollUtil.isEmpty(groupIds)) {
91 return Collections.emptyMap(); 91 return Collections.emptyMap();
92 } 92 }
93 - // TODO @AI:使用 mybatis plus join,可以简化这个操作么?  
94 List<Map<String, Object>> rows = selectMaps(Wrappers.<ImGroupMemberDO>query() 93 List<Map<String, Object>> rows = selectMaps(Wrappers.<ImGroupMemberDO>query()
95 .select("group_id AS groupId", "COUNT(*) AS cnt") 94 .select("group_id AS groupId", "COUNT(*) AS cnt")
96 .in("group_id", groupIds) 95 .in("group_id", groupIds)
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/sensitiveword/ImSensitiveWordMapper.java
@@ -6,7 +6,10 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -6,7 +6,10 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo.ImSensitiveWordPageReqVO; 6 import cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo.ImSensitiveWordPageReqVO;
7 import cn.iocoder.yudao.module.im.dal.dataobject.sensitiveword.ImSensitiveWordDO; 7 import cn.iocoder.yudao.module.im.dal.dataobject.sensitiveword.ImSensitiveWordDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
  9 +import org.apache.ibatis.annotations.Param;
  10 +import org.apache.ibatis.annotations.Select;
9 11
  12 +import java.time.LocalDateTime;
10 import java.util.List; 13 import java.util.List;
11 14
12 /** 15 /**
@@ -35,4 +38,7 @@ public interface ImSensitiveWordMapper extends BaseMapperX&lt;ImSensitiveWordDO&gt; { @@ -35,4 +38,7 @@ public interface ImSensitiveWordMapper extends BaseMapperX&lt;ImSensitiveWordDO&gt; {
35 .orderByDesc(ImSensitiveWordDO::getId)); 38 .orderByDesc(ImSensitiveWordDO::getId));
36 } 39 }
37 40
  41 + @Select("SELECT MAX(update_time) FROM im_sensitive_word")
  42 + LocalDateTime selectMaxUpdateTime(@Param("tenantId") Long tenantId);
  43 +
38 } 44 }
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/sensitiveword/ImSensitiveWordConfiguration.java deleted
1 -package cn.iocoder.yudao.module.im.framework.sensitiveword;  
2 -  
3 -import com.github.houbb.sensitive.word.bs.SensitiveWordBs;  
4 -import org.springframework.context.annotation.Bean;  
5 -import org.springframework.context.annotation.Configuration;  
6 -  
7 -/**  
8 - * IM 敏感词检测配置  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Configuration  
13 -public class ImSensitiveWordConfiguration {  
14 -  
15 - // TODO @AI:可能需要按需创建;因为多租户。  
16 - /**  
17 - * 装配 {@link SensitiveWordBs}:  
18 - * 词库来源 = {@link ImSensitiveWordDeny}(DB 里启用的敏感词),并开启常用规范化能力。  
19 - */  
20 - @Bean  
21 - public SensitiveWordBs sensitiveWordBs(ImSensitiveWordDeny imSensitiveWordDeny) {  
22 - return SensitiveWordBs.newInstance()  
23 - .wordDeny(imSensitiveWordDeny)  
24 - .ignoreCase(true)  
25 - .ignoreWidth(true) // 忽略全/半角  
26 - .ignoreNumStyle(true) // 忽略数字风格(中文/阿拉伯)  
27 - .ignoreChineseStyle(true) // 忽略繁简体  
28 - .enableWordCheck(true)  
29 - .init();  
30 - }  
31 -  
32 -}  
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/sensitiveword/ImSensitiveWordDeny.java deleted
1 -package cn.iocoder.yudao.module.im.framework.sensitiveword;  
2 -  
3 -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;  
4 -import cn.iocoder.yudao.module.im.dal.dataobject.sensitiveword.ImSensitiveWordDO;  
5 -import cn.iocoder.yudao.module.im.dal.mysql.sensitiveword.ImSensitiveWordMapper;  
6 -import com.github.houbb.sensitive.word.api.IWordDeny;  
7 -import jakarta.annotation.Resource;  
8 -import org.springframework.stereotype.Component;  
9 -  
10 -import java.util.List;  
11 -  
12 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;  
13 -  
14 -// TODO @AI:是不是可以,直接塞入一个数组。而不是这里查询???  
15 -/**  
16 - * 自定义敏感词数据来源:从 im_sensitive_word 表加载启用状态的敏感词  
17 - * <p>  
18 - * 由 {@link com.github.houbb.sensitive.word.bs.SensitiveWordBs} 在 init() 时回调,  
19 - * 因此 CRUD 完成后调用 sensitiveWordBs.init() 即可实现热更新。  
20 - *  
21 - * @author 芋道源码  
22 - */  
23 -@Component  
24 -public class ImSensitiveWordDeny implements IWordDeny {  
25 -  
26 - @Resource  
27 - private ImSensitiveWordMapper sensitiveWordMapper;  
28 -  
29 - @Override  
30 - public List<String> deny() {  
31 - return convertList(  
32 - sensitiveWordMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()),  
33 - ImSensitiveWordDO::getWord);  
34 - }  
35 -  
36 -}  
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendService.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.im.service.friend; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.im.service.friend;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.im.controller.admin.friend.vo.ImFriendUpdateReqVO; 4 import cn.iocoder.yudao.module.im.controller.admin.friend.vo.ImFriendUpdateReqVO;
5 import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerPageReqVO; 5 import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerPageReqVO;
6 -import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerRespVO;  
7 import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO; 6 import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO;
8 7
9 import java.util.Collection; 8 import java.util.Collection;
@@ -90,8 +89,8 @@ public interface ImFriendService { @@ -90,8 +89,8 @@ public interface ImFriendService {
90 // ==================== 管理后台 ==================== 89 // ==================== 管理后台 ====================
91 90
92 /** 91 /**
93 - * 【管理后台】分页查询好友关系(已填充用户 / 好友昵称) 92 + * 【管理后台】分页查询好友关系
94 */ 93 */
95 - PageResult<ImFriendManagerRespVO> getFriendPage(ImFriendManagerPageReqVO reqVO); 94 + PageResult<ImFriendDO> getFriendPage(ImFriendManagerPageReqVO reqVO);
96 95
97 } 96 }
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java
1 package cn.iocoder.yudao.module.im.service.friend; 1 package cn.iocoder.yudao.module.im.service.friend;
2 2
3 -import cn.hutool.core.collection.CollUtil;  
4 import cn.hutool.extra.spring.SpringUtil; 3 import cn.hutool.extra.spring.SpringUtil;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;  
8 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
9 import cn.iocoder.yudao.module.im.controller.admin.friend.vo.ImFriendUpdateReqVO; 6 import cn.iocoder.yudao.module.im.controller.admin.friend.vo.ImFriendUpdateReqVO;
10 import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerPageReqVO; 7 import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerPageReqVO;
11 -import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerRespVO;  
12 import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO; 8 import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO;
13 import cn.iocoder.yudao.module.im.dal.mysql.friend.ImFriendMapper; 9 import cn.iocoder.yudao.module.im.dal.mysql.friend.ImFriendMapper;
14 import cn.iocoder.yudao.module.im.service.message.ImPrivateMessageService; 10 import cn.iocoder.yudao.module.im.service.message.ImPrivateMessageService;
15 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; 11 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService;
16 import cn.iocoder.yudao.module.im.service.websocket.dto.ImPrivateMessageDTO; 12 import cn.iocoder.yudao.module.im.service.websocket.dto.ImPrivateMessageDTO;
17 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 13 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
18 -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;  
19 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
20 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
21 import org.springframework.cache.annotation.CacheEvict; 16 import org.springframework.cache.annotation.CacheEvict;
@@ -27,10 +22,11 @@ import org.springframework.transaction.annotation.Transactional; @@ -27,10 +22,11 @@ import org.springframework.transaction.annotation.Transactional;
27 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
28 23
29 import java.time.LocalDateTime; 24 import java.time.LocalDateTime;
30 -import java.util.*; 25 +import java.util.Collection;
  26 +import java.util.List;
  27 +import java.util.Objects;
31 28
32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
33 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;  
34 import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.FRIEND; 30 import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.FRIEND;
35 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.FRIEND_ADD_SELF; 31 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.FRIEND_ADD_SELF;
36 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.FRIEND_NOT_FRIEND; 32 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.FRIEND_NOT_FRIEND;
@@ -189,26 +185,9 @@ public class ImFriendServiceImpl implements ImFriendService { @@ -189,26 +185,9 @@ public class ImFriendServiceImpl implements ImFriendService {
189 185
190 // ==================== 管理后台 ==================== 186 // ==================== 管理后台 ====================
191 187
192 - // TODO @AI:拿到 controller 拼接数据,简化 service;  
193 @Override 188 @Override
194 - public PageResult<ImFriendManagerRespVO> getFriendPage(ImFriendManagerPageReqVO reqVO) {  
195 - // 1. 分页查询  
196 - PageResult<ImFriendDO> pageResult = imFriendMapper.selectPage(reqVO);  
197 - if (CollUtil.isEmpty(pageResult.getList())) {  
198 - return PageResult.empty(pageResult.getTotal());  
199 - }  
200 -  
201 - // 2. 一次性批量查询用户 + 好友的昵称  
202 - Set<Long> userIds = new HashSet<>();  
203 - userIds.addAll(convertSet(pageResult.getList(), ImFriendDO::getUserId));  
204 - userIds.addAll(convertSet(pageResult.getList(), ImFriendDO::getFriendUserId));  
205 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);  
206 -  
207 - // 3. 转换为 VO,填充昵称  
208 - return BeanUtils.toBean(pageResult, ImFriendManagerRespVO.class, vo -> {  
209 - MapUtils.findAndThen(userMap, vo.getUserId(), user -> vo.setUserNickname(user.getNickname()));  
210 - MapUtils.findAndThen(userMap, vo.getFriendUserId(), user -> vo.setFriendNickname(user.getNickname()));  
211 - }); 189 + public PageResult<ImFriendDO> getFriendPage(ImFriendManagerPageReqVO reqVO) {
  190 + return imFriendMapper.selectPage(reqVO);
212 } 191 }
213 192
214 } 193 }
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberService.java
1 package cn.iocoder.yudao.module.im.service.group; 1 package cn.iocoder.yudao.module.im.service.group;
2 2
3 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberUpdateReqVO; 3 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberUpdateReqVO;
4 -import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.member.ImGroupMemberManagerRespVO;  
5 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; 4 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
6 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
7 6
@@ -156,14 +155,4 @@ public interface ImGroupMemberService { @@ -156,14 +155,4 @@ public interface ImGroupMemberService {
156 */ 155 */
157 Map<Long, Long> getActiveMemberCountMap(Collection<Long> groupIds); 156 Map<Long, Long> getActiveMemberCountMap(Collection<Long> groupIds);
158 157
159 - // ==================== 管理后台 ====================  
160 -  
161 - /**  
162 - * 【管理后台】获取群成员列表  
163 - *  
164 - * @param groupId 群编号  
165 - * @return 群成员列表  
166 - */  
167 - List<ImGroupMemberManagerRespVO> getGroupMemberList(Long groupId);  
168 -  
169 } 158 }
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java
@@ -2,16 +2,12 @@ package cn.iocoder.yudao.module.im.service.group; @@ -2,16 +2,12 @@ package cn.iocoder.yudao.module.im.service.group;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;  
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberUpdateReqVO; 6 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberUpdateReqVO;
8 -import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.member.ImGroupMemberManagerRespVO;  
9 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; 7 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
10 import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMemberMapper; 8 import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMemberMapper;
11 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; 9 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService;
12 import cn.iocoder.yudao.module.im.service.websocket.dto.ImGroupMessageDTO; 10 import cn.iocoder.yudao.module.im.service.websocket.dto.ImGroupMessageDTO;
13 -import cn.iocoder.yudao.module.system.api.user.AdminUserApi;  
14 -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;  
15 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
16 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
17 import org.springframework.cache.annotation.CacheEvict; 13 import org.springframework.cache.annotation.CacheEvict;
@@ -44,9 +40,6 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService { @@ -44,9 +40,6 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
44 @Resource 40 @Resource
45 private ImWebSocketService webSocketService; 41 private ImWebSocketService webSocketService;
46 42
47 - @Resource  
48 - private AdminUserApi adminUserApi;  
49 -  
50 @Override 43 @Override
51 public ImGroupMemberDO getGroupMember(Long id) { 44 public ImGroupMemberDO getGroupMember(Long id) {
52 return groupMemberMapper.selectById(id); 45 return groupMemberMapper.selectById(id);
@@ -222,25 +215,4 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService { @@ -222,25 +215,4 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService {
222 return groupMemberMapper.selectCountMapByGroupIdsAndStatus(groupIds, CommonStatusEnum.ENABLE.getStatus()); 215 return groupMemberMapper.selectCountMapByGroupIdsAndStatus(groupIds, CommonStatusEnum.ENABLE.getStatus());
223 } 216 }
224 217
225 - // ==================== 管理后台 ====================  
226 -  
227 - // TODO @AI:这个拼接的逻辑,拿到 controller,这里只返回 list do;  
228 - @Override  
229 - public List<ImGroupMemberManagerRespVO> getGroupMemberList(Long groupId) {  
230 - // 1. 查询有效群成员  
231 - List<ImGroupMemberDO> members = getActiveGroupMemberListByGroupId(groupId);  
232 - if (CollUtil.isEmpty(members)) {  
233 - return Collections.emptyList();  
234 - }  
235 -  
236 - // 2. 批量查询用户信息  
237 - Set<Long> userIds = convertSet(members, ImGroupMemberDO::getUserId);  
238 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);  
239 -  
240 - // 3. 转换为 VO,填充昵称、头像  
241 - return BeanUtils.toBean(members, ImGroupMemberManagerRespVO.class, vo ->  
242 - MapUtils.findAndThen(userMap, vo.getUserId(), user ->  
243 - vo.setNickname(user.getNickname()).setAvatar(user.getAvatar())));  
244 - }  
245 -  
246 } 218 }
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupService.java
@@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMember @@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMember
7 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO; 7 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO;
8 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerBanReqVO; 8 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerBanReqVO;
9 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerPageReqVO; 9 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerPageReqVO;
10 -import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerRespVO;  
11 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; 10 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
12 import jakarta.validation.Valid; 11 import jakarta.validation.Valid;
13 12
@@ -138,15 +137,13 @@ public interface ImGroupService { @@ -138,15 +137,13 @@ public interface ImGroupService {
138 137
139 // ==================== 管理后台 ==================== 138 // ==================== 管理后台 ====================
140 139
141 - // TODO DONE @AI:是不是 controller 拆分哈;(getGroupPage 留在 ImGroupService;getGroupMemberList 已拆到 ImGroupMemberService + ImGroupMemberManagerController)  
142 - // TODO @AI:不对;应该 VO 放在 controller 拼接  
143 /** 140 /**
144 * 【管理后台】分页查询群列表 141 * 【管理后台】分页查询群列表
145 * 142 *
146 * @param pageReqVO 分页查询条件 143 * @param pageReqVO 分页查询条件
147 * @return 群分页列表 144 * @return 群分页列表
148 */ 145 */
149 - PageResult<ImGroupManagerRespVO> getGroupPage(ImGroupManagerPageReqVO pageReqVO); 146 + PageResult<ImGroupDO> getGroupPage(ImGroupManagerPageReqVO pageReqVO);
150 147
151 /** 148 /**
152 * 【管理后台】封禁群 149 * 【管理后台】封禁群
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java
@@ -8,7 +8,6 @@ import cn.hutool.core.util.StrUtil; @@ -8,7 +8,6 @@ import cn.hutool.core.util.StrUtil;
8 import cn.hutool.extra.spring.SpringUtil; 8 import cn.hutool.extra.spring.SpringUtil;
9 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 9 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
10 import cn.iocoder.yudao.framework.common.pojo.PageResult; 10 import cn.iocoder.yudao.framework.common.pojo.PageResult;
11 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;  
12 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
13 import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupCreateReqVO; 12 import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupCreateReqVO;
14 import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupUpdateReqVO; 13 import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupUpdateReqVO;
@@ -16,7 +15,6 @@ import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMember @@ -16,7 +15,6 @@ import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMember
16 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO; 15 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO;
17 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerBanReqVO; 16 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerBanReqVO;
18 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerPageReqVO; 17 import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerPageReqVO;
19 -import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerRespVO;  
20 import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO; 18 import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO;
21 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; 19 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
22 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; 20 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
@@ -288,24 +286,8 @@ public class ImGroupServiceImpl implements ImGroupService { @@ -288,24 +286,8 @@ public class ImGroupServiceImpl implements ImGroupService {
288 // ==================== 管理后台 ==================== 286 // ==================== 管理后台 ====================
289 287
290 @Override 288 @Override
291 - public PageResult<ImGroupManagerRespVO> getGroupPage(ImGroupManagerPageReqVO pageReqVO) {  
292 - // 1. 分页查询群  
293 - PageResult<ImGroupDO> pageResult = groupMapper.selectPage(pageReqVO);  
294 - if (CollUtil.isEmpty(pageResult.getList())) {  
295 - return PageResult.empty(pageResult.getTotal());  
296 - }  
297 -  
298 - // 2.1 批量查询群主昵称  
299 - Set<Long> ownerUserIds = convertSet(pageResult.getList(), ImGroupDO::getOwnerUserId);  
300 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(ownerUserIds);  
301 - // 2.2 一次 GROUP BY 查出本页所有群的活跃成员数,避免按行 N+1  
302 - Set<Long> groupIds = convertSet(pageResult.getList(), ImGroupDO::getId);  
303 - Map<Long, Long> memberCountMap = groupMemberService.getActiveMemberCountMap(groupIds);  
304 - // 2.3 转换为 VO,填充群主昵称、群成员数量  
305 - return BeanUtils.toBean(pageResult, ImGroupManagerRespVO.class, vo -> {  
306 - MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerNickname(user.getNickname()));  
307 - vo.setMemberCount(memberCountMap.getOrDefault(vo.getId(), 0L).intValue());  
308 - }); 289 + public PageResult<ImGroupDO> getGroupPage(ImGroupManagerPageReqVO pageReqVO) {
  290 + return groupMapper.selectPage(pageReqVO);
309 } 291 }
310 292
311 @Override 293 @Override
@@ -328,7 +310,6 @@ public class ImGroupServiceImpl implements ImGroupService { @@ -328,7 +310,6 @@ public class ImGroupServiceImpl implements ImGroupService {
328 throw exception(GROUP_NOT_EXISTS); 310 throw exception(GROUP_NOT_EXISTS);
329 } 311 }
330 // 2. 解封(保留 bannedReason / bannedTime 作为历史记录) 312 // 2. 解封(保留 bannedReason / bannedTime 作为历史记录)
331 - // TODO DONE @AI:不要使用 mybatis plus 的方法,直接操作。另外,感觉封禁时间、封禁原因,没必要置空,留着问题不大。  
332 groupMapper.updateById(new ImGroupDO().setId(id).setBanned(false)); 313 groupMapper.updateById(new ImGroupDO().setId(id).setBanned(false));
333 } 314 }
334 315
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageService.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.im.service.message; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.im.service.message;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerPageReqVO; 4 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerPageReqVO;
5 -import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerRespVO;  
6 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageListReqVO; 5 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageListReqVO;
7 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageSendReqVO; 6 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageSendReqVO;
8 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO; 7 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO;
@@ -118,9 +117,9 @@ public interface ImGroupMessageService { @@ -118,9 +117,9 @@ public interface ImGroupMessageService {
118 // ==================== 管理后台 ==================== 117 // ==================== 管理后台 ====================
119 118
120 /** 119 /**
121 - * 【管理后台】分页查询群聊消息(已填充群名称、发送人昵称) 120 + * 【管理后台】分页查询群聊消息
122 */ 121 */
123 - PageResult<ImGroupMessageManagerRespVO> getGroupMessagePage(ImGroupMessageManagerPageReqVO reqVO); 122 + PageResult<ImGroupMessageDO> getGroupMessagePage(ImGroupMessageManagerPageReqVO reqVO);
124 123
125 /** 124 /**
126 * 【管理后台】获取群聊消息详情 125 * 【管理后台】获取群聊消息详情
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java
@@ -7,14 +7,11 @@ import cn.hutool.core.util.ObjUtil; @@ -7,14 +7,11 @@ import cn.hutool.core.util.ObjUtil;
7 import cn.hutool.extra.spring.SpringUtil; 7 import cn.hutool.extra.spring.SpringUtil;
8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
9 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;  
11 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 10 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
12 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
13 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerPageReqVO; 12 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerPageReqVO;
14 -import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerRespVO;  
15 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageListReqVO; 13 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageListReqVO;
16 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageSendReqVO; 14 import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageSendReqVO;
17 -import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;  
18 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; 15 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
19 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO; 16 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO;
20 import cn.iocoder.yudao.module.im.dal.mysql.message.ImGroupMessageMapper; 17 import cn.iocoder.yudao.module.im.dal.mysql.message.ImGroupMessageMapper;
@@ -28,8 +25,6 @@ import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; @@ -28,8 +25,6 @@ import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService;
28 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; 25 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService;
29 import cn.iocoder.yudao.module.im.service.websocket.dto.ImGroupMessageDTO; 26 import cn.iocoder.yudao.module.im.service.websocket.dto.ImGroupMessageDTO;
30 import cn.iocoder.yudao.module.im.service.websocket.dto.message.RecallMessage; 27 import cn.iocoder.yudao.module.im.service.websocket.dto.message.RecallMessage;
31 -import cn.iocoder.yudao.module.system.api.user.AdminUserApi;  
32 -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;  
33 import jakarta.annotation.Resource; 28 import jakarta.annotation.Resource;
34 import lombok.extern.slf4j.Slf4j; 29 import lombok.extern.slf4j.Slf4j;
35 import org.springframework.scheduling.annotation.Async; 30 import org.springframework.scheduling.annotation.Async;
@@ -77,9 +72,6 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @@ -77,9 +72,6 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
77 @Resource 72 @Resource
78 private ImWebSocketService imWebSocketService; 73 private ImWebSocketService imWebSocketService;
79 74
80 - @Resource  
81 - private AdminUserApi adminUserApi;  
82 -  
83 @Override 75 @Override
84 public ImGroupMessageDO sendGroupMessage(Long senderId, ImGroupMessageSendReqVO reqVO) { 76 public ImGroupMessageDO sendGroupMessage(Long senderId, ImGroupMessageSendReqVO reqVO) {
85 // 1.1 幂等校验:根据 senderId + clientMessageId 查重 77 // 1.1 幂等校验:根据 senderId + clientMessageId 查重
@@ -554,26 +546,9 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { @@ -554,26 +546,9 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService {
554 546
555 // ==================== 管理后台 ==================== 547 // ==================== 管理后台 ====================
556 548
557 - // TODO @AI:这些挪到 controller 拼接 VO  
558 @Override 549 @Override
559 - public PageResult<ImGroupMessageManagerRespVO> getGroupMessagePage(ImGroupMessageManagerPageReqVO reqVO) {  
560 - // 1. 分页查询  
561 - PageResult<ImGroupMessageDO> pageResult = groupMessageMapper.selectPage(reqVO);  
562 - if (CollUtil.isEmpty(pageResult.getList())) {  
563 - return PageResult.empty(pageResult.getTotal());  
564 - }  
565 -  
566 - // 2. 批量查询群名称、发送人昵称  
567 - Set<Long> groupIds = convertSet(pageResult.getList(), ImGroupMessageDO::getGroupId);  
568 - Set<Long> senderIds = convertSet(pageResult.getList(), ImGroupMessageDO::getSenderId);  
569 - Map<Long, ImGroupDO> groupMap = groupService.getGroupMap(groupIds);  
570 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(senderIds);  
571 -  
572 - // 3. 转换为 VO,填充群名 / 发送人昵称  
573 - return BeanUtils.toBean(pageResult, ImGroupMessageManagerRespVO.class, vo -> {  
574 - MapUtils.findAndThen(groupMap, vo.getGroupId(), group -> vo.setGroupName(group.getName()));  
575 - MapUtils.findAndThen(userMap, vo.getSenderId(), user -> vo.setSenderNickname(user.getNickname()));  
576 - }); 550 + public PageResult<ImGroupMessageDO> getGroupMessagePage(ImGroupMessageManagerPageReqVO reqVO) {
  551 + return groupMessageMapper.selectPage(reqVO);
577 } 552 }
578 553
579 @Override 554 @Override
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageService.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.im.service.message; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.im.service.message;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerPageReqVO; 4 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerPageReqVO;
5 -import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerRespVO;  
6 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageListReqVO; 5 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageListReqVO;
7 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageSendReqVO; 6 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageSendReqVO;
8 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO; 7 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO;
@@ -91,9 +90,9 @@ public interface ImPrivateMessageService { @@ -91,9 +90,9 @@ public interface ImPrivateMessageService {
91 // ==================== 管理后台 ==================== 90 // ==================== 管理后台 ====================
92 91
93 /** 92 /**
94 - * 【管理后台】分页查询私聊消息(已填充发送人 / 接收人昵称) 93 + * 【管理后台】分页查询私聊消息
95 */ 94 */
96 - PageResult<ImPrivateMessageManagerRespVO> getPrivateMessagePage(ImPrivateMessageManagerPageReqVO reqVO); 95 + PageResult<ImPrivateMessageDO> getPrivateMessagePage(ImPrivateMessageManagerPageReqVO reqVO);
97 96
98 /** 97 /**
99 * 【管理后台】获取私聊消息详情 98 * 【管理后台】获取私聊消息详情
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImpl.java
1 package cn.iocoder.yudao.module.im.service.message; 1 package cn.iocoder.yudao.module.im.service.message;
2 2
3 -import cn.hutool.core.collection.CollUtil;  
4 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.util.IdUtil; 4 import cn.hutool.core.util.IdUtil;
6 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;  
9 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerPageReqVO; 9 import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerPageReqVO;
12 -import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerRespVO;  
13 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageListReqVO; 10 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageListReqVO;
14 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageSendReqVO; 11 import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageSendReqVO;
15 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO; 12 import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO;
@@ -21,8 +18,6 @@ import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService; @@ -21,8 +18,6 @@ import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService;
21 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; 18 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService;
22 import cn.iocoder.yudao.module.im.service.websocket.dto.ImPrivateMessageDTO; 19 import cn.iocoder.yudao.module.im.service.websocket.dto.ImPrivateMessageDTO;
23 import cn.iocoder.yudao.module.im.service.websocket.dto.message.RecallMessage; 20 import cn.iocoder.yudao.module.im.service.websocket.dto.message.RecallMessage;
24 -import cn.iocoder.yudao.module.system.api.user.AdminUserApi;  
25 -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;  
26 import jakarta.annotation.Resource; 21 import jakarta.annotation.Resource;
27 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
28 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
@@ -30,13 +25,9 @@ import org.springframework.transaction.annotation.Transactional; @@ -30,13 +25,9 @@ import org.springframework.transaction.annotation.Transactional;
30 import org.springframework.validation.annotation.Validated; 25 import org.springframework.validation.annotation.Validated;
31 26
32 import java.time.LocalDateTime; 27 import java.time.LocalDateTime;
33 -import java.util.HashSet;  
34 import java.util.List; 28 import java.util.List;
35 -import java.util.Map;  
36 -import java.util.Set;  
37 29
38 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
39 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;  
40 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*; 31 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*;
41 import static cn.iocoder.yudao.module.im.enums.ImCommonConstants.*; 32 import static cn.iocoder.yudao.module.im.enums.ImCommonConstants.*;
42 33
@@ -61,9 +52,6 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService { @@ -61,9 +52,6 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService {
61 @Resource 52 @Resource
62 private ImWebSocketService imWebSocketService; 53 private ImWebSocketService imWebSocketService;
63 54
64 - @Resource  
65 - private AdminUserApi adminUserApi;  
66 -  
67 @Override 55 @Override
68 public ImPrivateMessageDO sendPrivateMessage(Long senderId, ImPrivateMessageSendReqVO reqVO) { 56 public ImPrivateMessageDO sendPrivateMessage(Long senderId, ImPrivateMessageSendReqVO reqVO) {
69 // 1.1 幂等校验:根据 senderId + clientMessageId 查重 57 // 1.1 幂等校验:根据 senderId + clientMessageId 查重
@@ -183,26 +171,10 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService { @@ -183,26 +171,10 @@ public class ImPrivateMessageServiceImpl implements ImPrivateMessageService {
183 171
184 // ==================== 管理后台 ==================== 172 // ==================== 管理后台 ====================
185 173
186 - // TODO @AI:这些挪到 controller 拼接 VO 174 + // DONE @AI:这些挪到 controller 拼接 VO
187 @Override 175 @Override
188 - public PageResult<ImPrivateMessageManagerRespVO> getPrivateMessagePage(ImPrivateMessageManagerPageReqVO reqVO) {  
189 - // 1. 分页查询  
190 - PageResult<ImPrivateMessageDO> pageResult = privateMessageMapper.selectPage(reqVO);  
191 - if (CollUtil.isEmpty(pageResult.getList())) {  
192 - return PageResult.empty(pageResult.getTotal());  
193 - }  
194 -  
195 - // 2. 一次性批量查询发送人 + 接收人昵称  
196 - Set<Long> userIds = new HashSet<>();  
197 - userIds.addAll(convertSet(pageResult.getList(), ImPrivateMessageDO::getSenderId));  
198 - userIds.addAll(convertSet(pageResult.getList(), ImPrivateMessageDO::getReceiverId));  
199 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);  
200 -  
201 - // 3. 转换为 VO,填充昵称  
202 - return BeanUtils.toBean(pageResult, ImPrivateMessageManagerRespVO.class, vo -> {  
203 - MapUtils.findAndThen(userMap, vo.getSenderId(), user -> vo.setSenderNickname(user.getNickname()));  
204 - MapUtils.findAndThen(userMap, vo.getReceiverId(), user -> vo.setReceiverNickname(user.getNickname()));  
205 - }); 176 + public PageResult<ImPrivateMessageDO> getPrivateMessagePage(ImPrivateMessageManagerPageReqVO reqVO) {
  177 + return privateMessageMapper.selectPage(reqVO);
206 } 178 }
207 179
208 @Override 180 @Override
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/sensitiveword/ImSensitiveWordServiceImpl.java
@@ -3,21 +3,35 @@ package cn.iocoder.yudao.module.im.service.sensitiveword; @@ -3,21 +3,35 @@ package cn.iocoder.yudao.module.im.service.sensitiveword;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  10 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  11 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
8 import cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo.ImSensitiveWordPageReqVO; 12 import cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo.ImSensitiveWordPageReqVO;
9 import cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo.ImSensitiveWordSaveReqVO; 13 import cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo.ImSensitiveWordSaveReqVO;
10 import cn.iocoder.yudao.module.im.dal.dataobject.sensitiveword.ImSensitiveWordDO; 14 import cn.iocoder.yudao.module.im.dal.dataobject.sensitiveword.ImSensitiveWordDO;
11 import cn.iocoder.yudao.module.im.dal.mysql.sensitiveword.ImSensitiveWordMapper; 15 import cn.iocoder.yudao.module.im.dal.mysql.sensitiveword.ImSensitiveWordMapper;
12 import com.github.houbb.sensitive.word.bs.SensitiveWordBs; 16 import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
  17 +import com.google.common.cache.CacheLoader;
  18 +import com.google.common.cache.LoadingCache;
  19 +import com.google.common.util.concurrent.Futures;
  20 +import com.google.common.util.concurrent.ListenableFuture;
13 import jakarta.annotation.Resource; 21 import jakarta.annotation.Resource;
  22 +import lombok.AllArgsConstructor;
  23 +import lombok.Data;
14 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
15 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
16 import org.springframework.validation.annotation.Validated; 26 import org.springframework.validation.annotation.Validated;
17 27
  28 +import java.time.Duration;
  29 +import java.time.LocalDateTime;
18 import java.util.List; 30 import java.util.List;
  31 +import java.util.Objects;
19 32
20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
21 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.MESSAGE_SENSITIVE_WORD_BLOCKED; 35 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.MESSAGE_SENSITIVE_WORD_BLOCKED;
22 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.SENSITIVE_WORD_DUPLICATED; 36 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.SENSITIVE_WORD_DUPLICATED;
23 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS; 37 import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.SENSITIVE_WORD_NOT_EXISTS;
@@ -37,34 +51,92 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService { @@ -37,34 +51,92 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService {
37 @Resource 51 @Resource
38 private ImSensitiveWordMapper sensitiveWordMapper; 52 private ImSensitiveWordMapper sensitiveWordMapper;
39 53
40 - // TODO @AI:当前节点,立即刷新。其他节点,通过 job 扫描最后更新时间(只要有不同于目前最后一条记录的最大更新时间,就重新构建对应的树)。  
41 /** 54 /**
42 - * houbb 敏感词检测器,词库由 ImSensitiveWordDeny 在 init 时回调加载 55 + * 缓存条目
43 */ 56 */
44 - @Resource  
45 - private SensitiveWordBs sensitiveWordBs; 57 + @Data
  58 + @AllArgsConstructor
  59 + private static class SensitiveWordBsCache {
  60 +
  61 + /**
  62 + * 敏感词检测器
  63 + */
  64 + private SensitiveWordBs bs;
  65 + /**
  66 + * 构建本实例时数据库里的 max(update_time),作为下次刷新比对的基线
  67 + */
  68 + private LocalDateTime maxUpdateTime;
  69 +
  70 + }
46 71
47 - // TODO @AI:你看看现有仓库里,是否有类似这种的。按照最后更新判断,是否需要刷新本地缓存。  
48 /** 72 /**
49 - * 触发 houbb 词库重新加载(重建 trie) 73 + * 租户 → SensitiveWordBs 实例的本地缓存
50 * <p> 74 * <p>
51 - * 加 synchronized 是因为 SensitiveWordBs.init() 内部对 wordData 做就地写入,  
52 - * 若与并发的 contains() 重叠会读到半构建的 trie。互斥后短暂阻塞 contains 即可保证可见性。 75 + * 每分钟触发一次异步 reload,先读 max(update_time),没变就复用旧实例(避免 trie 重建),变了才重新读词库 + 重建。
  76 + * 单实例 CRUD 后另外通过 {@link #invalidateSensitiveWordBsCaches()} 立即让本机失效,多实例靠定时刷新最长 1 分钟内收敛。
  77 + */
  78 + @SuppressWarnings({"Convert2Diamond", "NullableProblems"})
  79 + private final LoadingCache<Long, SensitiveWordBsCache> sensitiveWordBsCaches = CacheUtils.buildAsyncReloadingCache(
  80 + Duration.ofMinutes(1L), // 1 分钟过期
  81 + new CacheLoader<Long, SensitiveWordBsCache>() {
  82 +
  83 + @Override
  84 + public SensitiveWordBsCache load(Long tenantId) {
  85 + return loadFresh(tenantId);
  86 + }
  87 +
  88 + @Override
  89 + public ListenableFuture<SensitiveWordBsCache> reload(Long tenantId, SensitiveWordBsCache oldValue) {
  90 + // 先比对 max(update_time);没变 → 复用旧实例,避免无谓地重建 trie
  91 + LocalDateTime currentMax = sensitiveWordMapper.selectMaxUpdateTime(tenantId);
  92 + if (Objects.equals(oldValue.getMaxUpdateTime(), currentMax)) {
  93 + return Futures.immediateFuture(oldValue);
  94 + }
  95 + // 变了 → 重新读词库并重建 trie
  96 + return Futures.immediateFuture(loadFresh(tenantId));
  97 + }
  98 +
  99 + });
  100 +
  101 + private SensitiveWordBsCache loadFresh(Long tenantId) {
  102 + return TenantUtils.execute(tenantId, () -> {
  103 + // 先取基线时间再读词库:反过来在两次查询之间出现的新插入会被漏感知
  104 + LocalDateTime maxUpdateTime = sensitiveWordMapper.selectMaxUpdateTime(tenantId);
  105 + List<ImSensitiveWordDO> words = sensitiveWordMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
  106 + // 构建敏感词检测器
  107 + SensitiveWordBs bs = SensitiveWordBs.newInstance()
  108 + .wordDeny(() -> convertList(words, ImSensitiveWordDO::getWord))
  109 + .ignoreCase(true)
  110 + .ignoreWidth(true) // 忽略全/半角
  111 + .ignoreNumStyle(true) // 忽略数字风格(中文/阿拉伯)
  112 + .ignoreChineseStyle(true) // 忽略繁简体
  113 + .enableWordCheck(true)
  114 + .init();
  115 + return new SensitiveWordBsCache(bs, maxUpdateTime);
  116 + });
  117 + }
  118 +
  119 + /**
  120 + * 强制让敏感词缓存失效,下次访问按最新 DB 重建
53 * <p> 121 * <p>
54 - * init() 本身幂等,单实例可以直接调;多实例部署时需配合 Redis pub/sub 广播让各实例 init。 122 + * 有租户上下文:仅失效该租户。无租户上下文(如系统级 / 跨租户清理):兜底失效所有租户。
55 */ 123 */
56 - private synchronized void reloadSensitiveWords() {  
57 - sensitiveWordBs.init();  
58 - log.info("[reloadSensitiveWords][敏感词库已 reload]"); 124 + private void invalidateSensitiveWordBsCaches() {
  125 + Long tenantId = TenantContextHolder.getTenantId();
  126 + if (tenantId != null) {
  127 + sensitiveWordBsCaches.invalidate(tenantId);
  128 + return;
  129 + }
  130 + sensitiveWordBsCaches.invalidateAll();
59 } 131 }
60 132
61 - // TODO @AI:需要按照租户来。  
62 @Override 133 @Override
63 public void validateText(String text) { 134 public void validateText(String text) {
64 if (StrUtil.isBlank(text)) { 135 if (StrUtil.isBlank(text)) {
65 return; 136 return;
66 } 137 }
67 - if (sensitiveWordBs.contains(text)) { 138 + SensitiveWordBs bs = sensitiveWordBsCaches.getUnchecked(TenantContextHolder.getRequiredTenantId()).getBs();
  139 + if (bs.contains(text)) {
68 throw exception(MESSAGE_SENSITIVE_WORD_BLOCKED); 140 throw exception(MESSAGE_SENSITIVE_WORD_BLOCKED);
69 } 141 }
70 } 142 }
@@ -89,9 +161,8 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService { @@ -89,9 +161,8 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService {
89 // 2.1 入库 161 // 2.1 入库
90 ImSensitiveWordDO word = BeanUtils.toBean(reqVO, ImSensitiveWordDO.class); 162 ImSensitiveWordDO word = BeanUtils.toBean(reqVO, ImSensitiveWordDO.class);
91 sensitiveWordMapper.insert(word); 163 sensitiveWordMapper.insert(word);
92 - // TODO @AI:houbb 这个注释有点奇怪!  
93 - // 2.2 触发 houbb 词库 reload  
94 - reloadSensitiveWords(); 164 + // 2.2 强制失效本机缓存(多实例靠定时刷新收敛)
  165 + invalidateSensitiveWordBsCaches();
95 return word.getId(); 166 return word.getId();
96 } 167 }
97 168
@@ -105,9 +176,8 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService { @@ -105,9 +176,8 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService {
105 // 2.1 更新 176 // 2.1 更新
106 ImSensitiveWordDO updateObj = BeanUtils.toBean(reqVO, ImSensitiveWordDO.class); 177 ImSensitiveWordDO updateObj = BeanUtils.toBean(reqVO, ImSensitiveWordDO.class);
107 sensitiveWordMapper.updateById(updateObj); 178 sensitiveWordMapper.updateById(updateObj);
108 - // 2.2 TODO @AI:houbb 这个注释有点奇怪!  
109 - // 2.2 触发 houbb 词库 reload  
110 - reloadSensitiveWords(); 179 + // 2.2 强制失效本机缓存
  180 + invalidateSensitiveWordBsCaches();
111 } 181 }
112 182
113 @Override 183 @Override
@@ -117,8 +187,8 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService { @@ -117,8 +187,8 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService {
117 187
118 // 2.1 删除 188 // 2.1 删除
119 sensitiveWordMapper.deleteById(id); 189 sensitiveWordMapper.deleteById(id);
120 - // 2.2 触发 houbb 词库 reload  
121 - reloadSensitiveWords(); 190 + // 2.2 强制失效本机缓存
  191 + invalidateSensitiveWordBsCaches();
122 } 192 }
123 193
124 194
@@ -127,11 +197,10 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService { @@ -127,11 +197,10 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService {
127 if (CollUtil.isEmpty(ids)) { 197 if (CollUtil.isEmpty(ids)) {
128 return; 198 return;
129 } 199 }
130 -  
131 // 1. 删除 200 // 1. 删除
132 sensitiveWordMapper.deleteByIds(ids); 201 sensitiveWordMapper.deleteByIds(ids);
133 - // 触发 houbb 词库 reload  
134 - reloadSensitiveWords(); 202 + // 2. 强制失效本机缓存
  203 + invalidateSensitiveWordBsCaches();
135 } 204 }
136 205
137 private void validateSensitiveWordExists(Long id) { 206 private void validateSensitiveWordExists(Long id) {
@@ -148,7 +217,6 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService { @@ -148,7 +217,6 @@ public class ImSensitiveWordServiceImpl implements ImSensitiveWordService {
148 if (exist == null) { 217 if (exist == null) {
149 return; 218 return;
150 } 219 }
151 - // TODO @AI:是不是 id 不用判断  
152 if (id == null || ObjUtil.notEqual(exist.getId(), id)) { 220 if (id == null || ObjUtil.notEqual(exist.getId(), id)) {
153 throw exception(SENSITIVE_WORD_DUPLICATED, word); 221 throw exception(SENSITIVE_WORD_DUPLICATED, word);
154 } 222 }