Commit fc90635abbf739518f18204ceeae90f3f00e748e
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<ImGroupMemberDO> { | @@ -90,7 +90,6 @@ public interface ImGroupMemberMapper extends BaseMapperX<ImGroupMemberDO> { | ||
| 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<ImSensitiveWordDO> { | @@ -35,4 +38,7 @@ public interface ImSensitiveWordMapper extends BaseMapperX<ImSensitiveWordDO> { | ||
| 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 | } |