This commit is contained in:
2026-03-20 13:28:34 +08:00
parent f967c08b7b
commit 78705c2ada
14 changed files with 86 additions and 104 deletions

View File

@@ -55,6 +55,9 @@ public class OAuth2UserController {
public CommonResult<OAuth2UserInfoRespVO> getUserInfo() {
// 获得用户基本信息
AdminUserDO user = userService.getUser(getLoginUserId());
if (user == null) {
return success(null);
}
OAuth2UserInfoRespVO resp = BeanUtils.toBean(user, OAuth2UserInfoRespVO.class);
// 获得部门信息
if (user.getDeptId() != null) {

View File

@@ -1,8 +1,6 @@
package com.njcn.rdms.module.system.controller.admin.permission;
import cn.hutool.core.collection.CollUtil;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleDataScopeReqVO;
import com.njcn.rdms.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleMenuReqVO;
import com.njcn.rdms.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO;
import com.njcn.rdms.module.system.service.permission.PermissionService;
@@ -15,7 +13,6 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.Set;
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
@@ -51,14 +48,6 @@ public class PermissionController {
return success(true);
}
@PostMapping("/assign-role-data-scope")
@Operation(summary = "赋予角色数据权限")
@PreAuthorize("@ss.hasPermission('system:permission:assign-role-data-scope')")
public CommonResult<Boolean> assignRoleDataScope(@Valid @RequestBody PermissionAssignRoleDataScopeReqVO reqVO) {
permissionService.assignRoleDataScope(reqVO.getRoleId(), reqVO.getDataScope(), reqVO.getDataScopeDeptIds());
return success(true);
}
@Operation(summary = "获得管理员拥有的角色编号列表")
@Parameter(name = "userId", description = "用户编号", required = true)
@GetMapping("/list-user-roles")

View File

@@ -1,28 +0,0 @@
package com.njcn.rdms.module.system.controller.admin.permission.vo.permission;
import com.njcn.rdms.framework.common.validation.InEnum;
import com.njcn.rdms.module.system.enums.permission.DataScopeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.util.Collections;
import java.util.Set;
@Schema(description = "管理后台 - 赋予角色数据权限 Request VO")
@Data
public class PermissionAssignRoleDataScopeReqVO {
@Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "角色编号不能为空")
private Long roleId;
@Schema(description = "数据范围,参见 DataScopeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "数据范围不能为空")
@InEnum(value = DataScopeEnum.class, message = "数据范围必须是 {value}")
private Integer dataScope;
@Schema(description = "部门编号列表,只有范围类型为 DEPT_CUSTOM 时,该字段才需要", example = "1,3,5")
private Set<Long> dataScopeDeptIds = Collections.emptySet(); // 兜底
}

View File

@@ -10,7 +10,6 @@ import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Set;
@Schema(description = "管理后台 - 角色信息 Response VO")
@Data
@@ -45,14 +44,6 @@ public class RoleRespVO {
@Schema(description = "备注", example = "我是一个角色")
private String remark;
@Schema(description = "数据范围,参见 DataScopeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "数据范围", converter = DictConvert.class)
@DictFormat(DictTypeConstants.DATA_SCOPE)
private Integer dataScope;
@Schema(description = "数据范围(指定部门数组)", example = "1")
private Set<Long> dataScopeDeptIds;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
private LocalDateTime createTime;

View File

@@ -1,6 +1,7 @@
package com.njcn.rdms.module.system.controller.admin.user;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.enums.CommonStatusEnum;
import com.njcn.rdms.module.system.controller.admin.user.vo.profile.UserProfileRespVO;
import com.njcn.rdms.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
import com.njcn.rdms.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
@@ -54,8 +55,12 @@ public class UserProfileController {
public CommonResult<UserProfileRespVO> getUserProfile() {
// 获得用户基本信息
AdminUserDO user = userService.getUser(getLoginUserId());
if (user == null) {
return success(null);
}
// 获得用户角色
List<RoleDO> userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(user.getId()));
userRoles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus()));
// 获得部门信息
DeptDO dept = user.getDeptId() != null ? deptService.getDept(user.getDeptId()) : null;
// 获得主岗位信息

View File

@@ -2,6 +2,7 @@ package com.njcn.rdms.module.system.convert.auth;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO;
import com.njcn.rdms.module.system.dal.dataobject.permission.MenuDO;
@@ -32,7 +33,8 @@ public interface AuthConvert {
return AuthPermissionInfoRespVO.builder()
.user(BeanUtils.toBean(user, AuthPermissionInfoRespVO.UserVO.class))
.roles(convertSet(roleList, RoleDO::getCode))
.permissions(convertSet(menuList, MenuDO::getPermission))
.permissions(convertSet(filterList(menuList, menu -> StrUtil.isNotBlank(menu.getPermission())),
MenuDO::getPermission))
.menus(buildMenuTree(menuList))
.build();
}

View File

@@ -29,4 +29,13 @@ public interface MenuService {
List<MenuDO> getMenuList(Collection<Long> ids);
/**
* 校验菜单们是否有效。如下情况,视为无效:
* 1. 菜单编号不存在
* 2. 菜单被禁用
*
* @param ids 菜单编号数组
*/
void validateMenuList(Collection<Long> ids);
}

View File

@@ -18,7 +18,6 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -109,6 +108,9 @@ public class MenuServiceImpl implements MenuService {
public void deleteMenuList(List<Long> ids) {
// 校验是否还有子菜单
ids.forEach(id -> {
if (menuMapper.selectById(id) == null) {
throw exception(MENU_NOT_EXISTS);
}
if (menuMapper.selectCountByParentId(id) > 0) {
throw exception(MENU_EXISTS_CHILDREN);
}
@@ -197,6 +199,24 @@ public class MenuServiceImpl implements MenuService {
return menuMapper.selectByIds(ids);
}
@Override
public void validateMenuList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
List<MenuDO> menus = menuMapper.selectByIds(ids);
Map<Long, MenuDO> menuMap = convertMap(menus, MenuDO::getId);
ids.forEach(id -> {
MenuDO menu = menuMap.get(id);
if (menu == null) {
throw exception(MENU_NOT_EXISTS);
}
if (CommonStatusEnum.isDisable(menu.getStatus())) {
throw exception(MENU_NOT_ENABLE, menu.getName());
}
});
}
/**
* 校验父菜单是否合法
* <p>

View File

@@ -8,7 +8,7 @@ import static java.util.Collections.singleton;
/**
* 权限 Service 接口
* <p>
* 提供用户-角色、角色-菜单、角色-部门的关联权限处理
* 提供用户-角色、角色-菜单的关联权限处理
*
* @author hongawen
*/
@@ -122,16 +122,4 @@ public interface PermissionService {
*/
Set<Long> getUserRoleIdListByUserIdFromCache(Long userId);
// ========== 用户-部门的相关方法 ==========
/**
* 设置角色的数据权限
*
* @param roleId 角色编号
* @param dataScope 数据范围
* @param dataScopeDeptIds 部门编号数组
*/
void assignRoleDataScope(Long roleId, Integer dataScope, Set<Long> dataScopeDeptIds);
}

View File

@@ -13,7 +13,6 @@ import com.njcn.rdms.module.system.dal.dataobject.permission.UserRoleDO;
import com.njcn.rdms.module.system.dal.mysql.permission.RoleMenuMapper;
import com.njcn.rdms.module.system.dal.mysql.permission.UserRoleMapper;
import com.njcn.rdms.module.system.dal.redis.RedisKeyConstants;
import com.njcn.rdms.module.system.service.dept.DeptService;
import com.njcn.rdms.module.system.service.user.AdminUserService;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.google.common.annotations.VisibleForTesting;
@@ -49,8 +48,6 @@ public class PermissionServiceImpl implements PermissionService {
@Resource
private MenuService menuService;
@Resource
private DeptService deptService;
@Resource
private AdminUserService userService;
@Override
@@ -90,10 +87,14 @@ public class PermissionServiceImpl implements PermissionService {
if (CollUtil.isEmpty(menuIds)) {
return false;
}
List<MenuDO> menus = getEnablePermissionMenus(menuIds);
if (CollUtil.isEmpty(menus)) {
return false;
}
// 判断是否有权限
Set<Long> roleIds = convertSet(roles, RoleDO::getId);
for (Long menuId : menuIds) {
for (Long menuId : convertSet(menus, MenuDO::getId)) {
// 获得拥有该菜单的角色编号集合
Set<Long> menuRoleIds = getSelf().getMenuRoleIdListByMenuIdFromCache(menuId);
// 如果有交集,说明有权限
@@ -104,6 +105,29 @@ public class PermissionServiceImpl implements PermissionService {
return false;
}
/**
* 加载权限菜单自身及其父链后,再统一过滤禁用节点,避免仅查询按钮节点时误判父菜单缺失。
*/
private List<MenuDO> getEnablePermissionMenus(Collection<Long> menuIds) {
Set<Long> targetMenuIds = new HashSet<>(menuIds);
Map<Long, MenuDO> menuMap = new LinkedHashMap<>();
Set<Long> currentIds = new HashSet<>(menuIds);
while (CollUtil.isNotEmpty(currentIds)) {
List<MenuDO> currentMenus = menuService.getMenuList(currentIds);
if (CollUtil.isEmpty(currentMenus)) {
break;
}
currentMenus.forEach(menu -> menuMap.put(menu.getId(), menu));
Set<Long> parentIds = convertSet(currentMenus, MenuDO::getParentId);
parentIds.remove(MenuDO.ID_ROOT);
parentIds.removeIf(menuMap::containsKey);
currentIds = parentIds;
}
List<MenuDO> enabledMenus = menuService.filterDisableMenus(new ArrayList<>(menuMap.values()));
enabledMenus.removeIf(menu -> !targetMenuIds.contains(menu.getId()));
return enabledMenus;
}
@Override
public boolean hasAnyRoles(Long userId, String... roles) {
// 如果为空,说明已经有权限
@@ -133,6 +157,8 @@ public class PermissionServiceImpl implements PermissionService {
allEntries = true) // allEntries 清空所有缓存,主要一次更新涉及到的 menuIds 较多,反倒批量会更快
})
public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
roleService.validateRoleList(Collections.singleton(roleId));
menuService.validateMenuList(menuIds);
// 获得角色拥有菜单编号
Set<Long> dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId);
// 计算新增和删除的菜单编号
@@ -200,6 +226,8 @@ public class PermissionServiceImpl implements PermissionService {
@DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
@CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId")
public void assignUserRole(Long userId, Set<Long> roleIds) {
userService.validateUserList(Collections.singleton(userId));
roleService.validateRoleList(roleIds);
// 获得角色拥有角色编号
Set<Long> dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId),
UserRoleDO::getRoleId);
@@ -259,14 +287,6 @@ public class PermissionServiceImpl implements PermissionService {
return roles;
}
// ========== 用户-部门的相关方法 ==========
@Override
public void assignRoleDataScope(Long roleId, Integer dataScope, Set<Long> dataScopeDeptIds) {
roleService.updateRoleDataScope(roleId, dataScope, dataScopeDeptIds);
}
/**
* 获得自身的代理对象,解决 AOP 生效问题
*

View File

@@ -8,7 +8,6 @@ import jakarta.validation.Valid;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* 角色 Service 接口
@@ -47,15 +46,6 @@ public interface RoleService {
*/
void deleteRoleList(List<Long> ids);
/**
* 设置角色的数据权限
*
* @param id 角色编号
* @param dataScope 数据范围
* @param dataScopeDeptIds 部门编号数组
*/
void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds);
/**
* 获得角色
*

View File

@@ -14,7 +14,6 @@ import com.njcn.rdms.module.system.controller.admin.permission.vo.role.RoleSaveR
import com.njcn.rdms.module.system.dal.dataobject.permission.RoleDO;
import com.njcn.rdms.module.system.dal.mysql.permission.RoleMapper;
import com.njcn.rdms.module.system.dal.redis.RedisKeyConstants;
import com.njcn.rdms.module.system.enums.permission.DataScopeEnum;
import com.njcn.rdms.module.system.enums.permission.RoleCodeEnum;
import com.njcn.rdms.module.system.enums.permission.RoleTypeEnum;
import com.google.common.annotations.VisibleForTesting;
@@ -29,7 +28,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.njcn.rdms.framework.common.util.collection.CollectionUtils.convertMap;
@@ -62,8 +64,7 @@ public class RoleServiceImpl implements RoleService {
// 2. 插入到数据库
RoleDO role = BeanUtils.toBean(createReqVO, RoleDO.class)
.setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType()))
.setStatus(ObjUtil.defaultIfNull(createReqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus()))
.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限
.setStatus(ObjUtil.defaultIfNull(createReqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus()));
roleMapper.insert(role);
// 3. 记录操作日志上下文
@@ -90,20 +91,6 @@ public class RoleServiceImpl implements RoleService {
LogRecordContext.putVariable("role", role);
}
@Override
@CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
public void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds) {
// 校验是否可以更新
validateRoleForUpdate(id);
// 更新数据范围
RoleDO updateObject = new RoleDO();
updateObject.setId(id);
updateObject.setDataScope(dataScope);
updateObject.setDataScopeDeptIds(dataScopeDeptIds);
roleMapper.updateById(updateObject);
}
@Override
@Transactional(rollbackFor = Exception.class)
@CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
@@ -124,6 +111,7 @@ public class RoleServiceImpl implements RoleService {
@Override
@Transactional(rollbackFor = Exception.class)
@CacheEvict(value = RedisKeyConstants.ROLE, allEntries = true)
public void deleteRoleList(List<Long> ids) {
// 1. 校验是否可以删除
ids.forEach(this::validateRoleForUpdate);

View File

@@ -220,6 +220,7 @@ public class AdminUserServiceImpl implements AdminUserService {
// 2. 删除用户及其关联数据
userMapper.deleteById(id);
permissionService.processUserDeleted(id);
oauth2TokenService.removeAccessToken(id, UserTypeEnum.ADMIN.getValue());
// 3. 记录操作日志上下文
LogRecordContext.putVariable("user", user);
}
@@ -230,7 +231,10 @@ public class AdminUserServiceImpl implements AdminUserService {
// 1. 批量删除用户
userMapper.deleteByIds(ids);
// 2. 批量删除用户关联数据
ids.forEach(permissionService::processUserDeleted);
ids.forEach(id -> {
permissionService.processUserDeleted(id);
oauth2TokenService.removeAccessToken(id, UserTypeEnum.ADMIN.getValue());
});
}
@Override