Commit 43690bd978ad03a195f9fe57bad2ea2ca91a6e3c
1 parent
439339f0
用户列表 展示角色字段,扩展用户体系方法,角色从缓存获取
Showing
7 changed files
with
185 additions
and
7 deletions
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
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}}", | ... | ... |