Commit 43690bd978ad03a195f9fe57bad2ea2ca91a6e3c

Authored by 颜惠青
1 parent 439339f0

用户列表 展示角色字段,扩展用户体系方法,角色从缓存获取

urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/admin/user/UserController.java
... ... @@ -11,9 +11,12 @@ import com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils;
11 11 import com.zteits.urbanops.module.system.controller.admin.user.vo.user.*;
12 12 import com.zteits.urbanops.module.system.convert.user.UserConvert;
13 13 import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO;
  14 +import com.zteits.urbanops.module.system.dal.dataobject.permission.RoleDO;
14 15 import com.zteits.urbanops.module.system.dal.dataobject.user.AdminUserDO;
15 16 import com.zteits.urbanops.module.system.enums.common.SexEnum;
16 17 import com.zteits.urbanops.module.system.service.dept.DeptService;
  18 +import com.zteits.urbanops.module.system.service.permission.PermissionService;
  19 +import com.zteits.urbanops.module.system.service.permission.RoleService;
17 20 import com.zteits.urbanops.module.system.service.user.AdminUserService;
18 21 import io.swagger.v3.oas.annotations.Operation;
19 22 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -31,6 +34,8 @@ import java.io.IOException;
31 34 import java.util.Arrays;
32 35 import java.util.List;
33 36 import java.util.Map;
  37 +import java.util.Objects;
  38 +import java.util.stream.Collectors;
34 39  
35 40 import static com.zteits.urbanops.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
36 41 import static com.zteits.urbanops.framework.common.pojo.CommonResult.success;
... ... @@ -47,6 +52,12 @@ public class UserController {
47 52 @Resource
48 53 private DeptService deptService;
49 54  
  55 + @Resource
  56 + private PermissionService permissionService;
  57 +
  58 + @Resource
  59 + private RoleService roleService;
  60 +
50 61 @PostMapping("/create")
51 62 @Operation(summary = "新增用户")
52 63 @PreAuthorize("@ss.hasPermission('system:user:create')")
... ... @@ -101,16 +112,47 @@ public class UserController {
101 112 @Operation(summary = "获得用户分页列表")
102 113 @PreAuthorize("@ss.hasPermission('system:user:query')")
103 114 public CommonResult<PageResult<UserRespVO>> getUserPage(@Valid UserPageReqVO pageReqVO) {
104   - // 获得用户分页列表
  115 + // 1. 获得用户分页列表(AdminUserDO 是数据库实体)
105 116 PageResult<AdminUserDO> pageResult = userService.getUserPage(pageReqVO);
106 117 if (CollUtil.isEmpty(pageResult.getList())) {
107   - return success(new PageResult<>(pageResult.getTotal()));
  118 + // 无数据时,返回空分页结果(保持返回结构一致)
  119 + return CommonResult.success(new PageResult<>(pageResult.getTotal()));
108 120 }
109   - // 拼接数据
  121 +
  122 + // 2. 提取当前页所有用户ID(用于批量查询,避免N+1查询问题)
  123 + List<Long> userIdList = pageResult.getList().stream()
  124 + .map(AdminUserDO::getId)
  125 + .collect(Collectors.toList());
  126 +
  127 + // 3. 批量拼接关联数据:部门信息(原有逻辑,保持不变)
110 128 Map<Long, DeptDO> deptMap = deptService.getDeptMap(
111   - convertList(pageResult.getList(), AdminUserDO::getDeptId));
112   - return success(new PageResult<>(UserConvert.INSTANCE.convertList(pageResult.getList(), deptMap),
113   - pageResult.getTotal()));
  129 + pageResult.getList().stream().map(AdminUserDO::getDeptId).collect(Collectors.toList())
  130 + );
  131 +
  132 + // 4. 批量拼接关联数据:角色信息(核心修正与完善)
  133 + // 4.1 批量获取「用户ID-角色ID列表」的映射(从权限服务获取,批量查询提升效率)
  134 + Map<Long, List<Long>> userId2RoleIdListMap = permissionService.getUserRoleIdListByUserIds(userIdList);
  135 + // 4.2 提取所有涉及的角色ID,去重后批量查询角色信息(从缓存获取,符合芋道缓存规范)
  136 + List<Long> allRoleIdList = userId2RoleIdListMap.values().stream()
  137 + .flatMap(List::stream)
  138 + .distinct()
  139 + .collect(Collectors.toList());
  140 + Map<Long, RoleDO> roleMap = roleService.getRoleMapFromCache(allRoleIdList); // 批量查询角色缓存,返回「角色ID-角色DO」映射
  141 + // 4.3 封装「用户ID-角色列表」的映射(供转换VO使用)
  142 + Map<Long, List<RoleDO>> userId2RoleListMap = userId2RoleIdListMap.entrySet().stream()
  143 + .collect(Collectors.toMap(
  144 + Map.Entry::getKey,
  145 + entry -> entry.getValue().stream()
  146 + .map(roleMap::get) // 从角色映射中获取对应RoleDO(可能返回null)
  147 + .filter(Objects::nonNull) // 过滤null的RoleDO对象,解决报错核心
  148 + .collect(Collectors.toList())
  149 + ));
  150 +
  151 + // 5. 转换VO并拼接关联数据(修改转换器,支持接收角色映射)
  152 + List<UserRespVO> userRespVOList = UserConvert.INSTANCE.convertList(pageResult.getList(), deptMap, userId2RoleListMap);
  153 +
  154 + // 6. 封装并返回结果
  155 + return CommonResult.success(new PageResult<>(userRespVOList, pageResult.getTotal()));
114 156 }
115 157  
116 158 @PostMapping("/listForPage")
... ...
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/admin/user/vo/user/UserRespVO.java
... ... @@ -38,6 +38,9 @@ public class UserRespVO{
38 38 @ExcelProperty("部门名称")
39 39 private String deptName;
40 40  
  41 + @Schema(description = "角色名称")
  42 + private String roleNames;
  43 +
41 44 @Schema(description = "岗位编号数组", example = "1")
42 45 private Set<Long> postIds;
43 46  
... ...
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/convert/user/UserConvert.java
1 1 package com.zteits.urbanops.module.system.convert.user;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import com.zteits.urbanops.framework.common.util.collection.CollectionUtils;
4 5 import com.zteits.urbanops.framework.common.util.collection.MapUtils;
5 6 import com.zteits.urbanops.framework.common.util.object.BeanUtils;
... ... @@ -16,14 +17,79 @@ import com.zteits.urbanops.module.system.dal.dataobject.user.AdminUserDO;
16 17 import org.mapstruct.Mapper;
17 18 import org.mapstruct.factory.Mappers;
18 19  
  20 +import java.util.ArrayList;
19 21 import java.util.List;
20 22 import java.util.Map;
  23 +import java.util.stream.Collectors;
21 24  
22   -@Mapper
  25 +@Mapper(componentModel = "spring")
23 26 public interface UserConvert {
24 27  
25 28 UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
26 29  
  30 + /**
  31 + * 转换单个用户VO,拼接部门和角色信息
  32 + */
  33 + default UserRespVO convert(AdminUserDO adminUserDO, Map<Long, DeptDO> deptMap, Map<Long, List<RoleDO>> userId2RoleListMap) {
  34 + // 1. 判空处理,避免空指针
  35 + if (adminUserDO == null) {
  36 + return null;
  37 + }
  38 + // 2. 新建VO对象,手动拷贝所有字段(对应原MapStruct自动映射的同名字段)
  39 + UserRespVO userRespVO = BeanUtils.toBean(adminUserDO,UserRespVO.class);
  40 + // 3. 手动调用辅助方法,设置拼接后的部门名称和角色名称
  41 + userRespVO.setDeptName(getDeptName(adminUserDO.getDeptId(), deptMap));
  42 + userRespVO.setRoleNames(getRoleNames(adminUserDO.getId(), userId2RoleListMap));
  43 +
  44 + return userRespVO;
  45 + }
  46 + /**
  47 + * 批量转换用户VO
  48 + */
  49 + default List<UserRespVO> convertList(List<AdminUserDO> adminUserDOList, Map<Long, DeptDO> deptMap, Map<Long, List<RoleDO>> userId2RoleListMap) {
  50 + // 1. 判空处理
  51 + if (CollUtil.isEmpty(adminUserDOList)) {
  52 + return new ArrayList<>();
  53 + }
  54 +
  55 + // 2. 遍历单个转换,收集结果
  56 + List<UserRespVO> userRespVOList = new ArrayList<>();
  57 + for (AdminUserDO adminUserDO : adminUserDOList) {
  58 + UserRespVO userRespVO = convert(adminUserDO, deptMap, userId2RoleListMap);
  59 + if (userRespVO != null) {
  60 + userRespVOList.add(userRespVO);
  61 + }
  62 + }
  63 +
  64 + return userRespVOList;
  65 + }
  66 + /**
  67 + * 从部门映射中获取部门名称
  68 + */
  69 + default String getDeptName(Long deptId, Map<Long, DeptDO> deptMap) {
  70 + if (deptId == null || deptMap == null) {
  71 + return null;
  72 + }
  73 + DeptDO deptDO = deptMap.get(deptId);
  74 + return deptDO != null ? deptDO.getName() : null;
  75 + }
  76 +
  77 + /**
  78 + * 从角色映射中获取角色名称(拼接为逗号分隔字符串)
  79 + */
  80 + default String getRoleNames(Long userId, Map<Long, List<RoleDO>> userId2RoleListMap) {
  81 + if (userId == null || userId2RoleListMap == null) {
  82 + return null;
  83 + }
  84 + List<RoleDO> roleDOList = userId2RoleListMap.get(userId);
  85 + if (CollUtil.isEmpty(roleDOList)) {
  86 + return null;
  87 + }
  88 + // 拼接角色名称为逗号分隔的字符串(如:"管理员,普通用户")
  89 + return roleDOList.stream()
  90 + .map(RoleDO::getName)
  91 + .collect(Collectors.joining(","));
  92 + }
27 93 default List<UserRespVO> convertList(List<AdminUserDO> list, Map<Long, DeptDO> deptMap) {
28 94 return CollectionUtils.convertList(list, user -> convert(user, deptMap.get(user.getDeptId())));
29 95 }
... ...
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/permission/PermissionService.java
... ... @@ -3,6 +3,8 @@ package com.zteits.urbanops.module.system.service.permission;
3 3 import com.zteits.urbanops.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
4 4  
5 5 import java.util.Collection;
  6 +import java.util.List;
  7 +import java.util.Map;
6 8 import java.util.Set;
7 9  
8 10 import static java.util.Collections.singleton;
... ... @@ -26,6 +28,13 @@ public interface PermissionService {
26 28 boolean hasAnyPermissions(Long userId, String... permissions);
27 29  
28 30 /**
  31 + * 获取多个用户的角色信息
  32 + * @param userIdList
  33 + * @return
  34 + */
  35 + public Map<Long, List<Long>> getUserRoleIdListByUserIds(List<Long> userIdList);
  36 +
  37 + /**
29 38 * 判断是否有角色,任一一个即可
30 39 *
31 40 * @param roles 角色数组
... ...
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/permission/PermissionServiceImpl.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.CollectionUtil;
5 5 import cn.hutool.core.util.ArrayUtil;
6 6 import cn.hutool.extra.spring.SpringUtil;
  7 +import com.baomidou.mybatisplus.core.toolkit.Wrappers;
7 8 import com.zteits.urbanops.framework.common.enums.CommonStatusEnum;
8 9 import com.zteits.urbanops.framework.common.util.collection.CollectionUtils;
9 10 import com.zteits.urbanops.framework.datapermission.core.annotation.DataPermission;
... ... @@ -32,6 +33,7 @@ import org.springframework.transaction.annotation.Transactional;
32 33 import jakarta.annotation.Resource;
33 34 import java.util.*;
34 35 import java.util.function.Supplier;
  36 +import java.util.stream.Collectors;
35 37  
36 38 import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.convertSet;
37 39 import static com.zteits.urbanops.framework.common.util.json.JsonUtils.toJsonString;
... ... @@ -60,6 +62,22 @@ public class PermissionServiceImpl implements PermissionService {
60 62 private AdminUserService userService;
61 63  
62 64 @Override
  65 + public Map<Long, List<Long>> getUserRoleIdListByUserIds(List<Long> userIdList) {
  66 + if (CollUtil.isEmpty(userIdList)) {
  67 + return Collections.emptyMap();
  68 + }
  69 + // 查询用户-角色关联数据
  70 + List<UserRoleDO> userRoleDOList = userRoleMapper.selectList(Wrappers.<UserRoleDO>lambdaQuery()
  71 + .in(UserRoleDO::getUserId, userIdList));
  72 + // 分组封装为「用户ID-角色ID列表」
  73 + return userRoleDOList.stream()
  74 + .collect(Collectors.groupingBy(
  75 + UserRoleDO::getUserId,
  76 + Collectors.mapping(UserRoleDO::getRoleId, Collectors.toList())
  77 + ));
  78 + }
  79 +
  80 + @Override
63 81 public boolean hasAnyPermissions(Long userId, String... permissions) {
64 82 // 如果为空,说明已经有权限
65 83 if (ArrayUtil.isEmpty(permissions)) {
... ...
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/permission/RoleService.java
... ... @@ -9,6 +9,7 @@ import jakarta.validation.Valid;
9 9  
10 10 import java.util.Collection;
11 11 import java.util.List;
  12 +import java.util.Map;
12 13 import java.util.Set;
13 14  
14 15 /**
... ... @@ -19,6 +20,13 @@ import java.util.Set;
19 20 public interface RoleService {
20 21  
21 22 /**
  23 + * 从缓存中批量获取角色信息,返回「角色ID-角色DO」的映射
  24 + * @param roleIdList 角色ID列表
  25 + * @return Map<Long, RoleDO> 角色ID为key,RoleDO为value
  26 + */
  27 + Map<Long, RoleDO> getRoleMapFromCache(List<Long> roleIdList);
  28 +
  29 + /**
22 30 * 创建角色
23 31 *
24 32 * @param createReqVO 创建角色信息
... ...
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/permission/RoleServiceImpl.java
... ... @@ -5,6 +5,7 @@ import cn.hutool.core.collection.CollectionUtil;
5 5 import cn.hutool.core.util.ObjUtil;
6 6 import cn.hutool.core.util.ObjectUtil;
7 7 import cn.hutool.extra.spring.SpringUtil;
  8 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 9 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
9 10 import com.zteits.urbanops.framework.common.enums.CommonStatusEnum;
10 11 import com.zteits.urbanops.framework.common.pojo.PageResult;
... ... @@ -34,6 +35,7 @@ import org.springframework.transaction.annotation.Transactional;
34 35 import org.springframework.util.StringUtils;
35 36  
36 37 import java.util.*;
  38 +import java.util.stream.Collectors;
37 39  
38 40 import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception;
39 41 import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.convertMap;
... ... @@ -58,6 +60,36 @@ public class RoleServiceImpl implements RoleService {
58 60 @Resource
59 61 private UserRoleMapper userRoleMapper;
60 62  
  63 + /**
  64 + * 从缓存中批量获取角色信息,缓存未命中则查询数据库并缓存结果
  65 + */
  66 + @Override
  67 + @Cacheable(value = "role", key = "#roleIdList.toString()", unless = "#result.isEmpty()")
  68 + // 缓存注解说明:
  69 + // value:缓存名称,区分不同模块缓存
  70 + // key:缓存键,用角色ID列表拼接作为唯一键
  71 + // unless:结果为空时不缓存,避免无效缓存
  72 + public Map<Long, RoleDO> getRoleMapFromCache(List<Long> roleIdList) {
  73 + // 1. 空值处理,避免无效查询
  74 + if (CollUtil.isEmpty(roleIdList)) {
  75 + return Map.of(); // 返回空不可变Map,避免空指针
  76 + }
  77 +
  78 + // 2. 批量查询数据库(缓存未命中时执行)
  79 + List<RoleDO> roleDOList = roleMapper.selectList(
  80 + new LambdaQueryWrapper<RoleDO>()
  81 + .in(RoleDO::getId, roleIdList) // 批量查询,仅一次数据库访问
  82 + );
  83 +
  84 + // 3. 转换为「角色ID-角色DO」的Map,方便后续取值
  85 + return roleDOList.stream()
  86 + .collect(Collectors.toMap(
  87 + RoleDO::getId, // 以角色ID作为key
  88 + roleDO -> roleDO, // 以角色DO作为value
  89 + (existing, replacement) -> existing // 解决重复ID冲突(取已有值,实际不会出现)
  90 + ));
  91 + }
  92 +
61 93 @Override
62 94 @Transactional(rollbackFor = Exception.class)
63 95 @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_CREATE_SUB_TYPE, bizNo = "{{#role.id}}",
... ...