✨ 角色菜单数据权限替代用户级组织/客商权限

将后台数据权限迁移到角色×菜单配置,运行时按 menuCode 解析;组织树取最宽合并;停用用户侧组织与客商授权过滤。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-27 04:05:01 +08:00
parent 18065d6ae4
commit 8737c4b5f9
34 changed files with 1640 additions and 160 deletions
@@ -0,0 +1,111 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.cache;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.system.feign.IRoleDataScopeClient;
import org.springblade.system.pojo.vo.RoleDataScopeVO;
import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
/**
* 角色数据权限缓存
*
* @author Chill
*/
public final class RoleDataScopeCache {
private static final String CACHE_MENU = "role:data-scope:menu:";
private static final String CACHE_WIDEST = "role:data-scope:widest:";
private static IRoleDataScopeClient roleDataScopeClient;
private RoleDataScopeCache() {
}
private static IRoleDataScopeClient getClient() {
if (roleDataScopeClient == null) {
roleDataScopeClient = SpringUtil.getBean(IRoleDataScopeClient.class);
}
return roleDataScopeClient;
}
/**
* 按菜单编码解析当前登录用户的角色数据权限
*/
public static RoleDataScopeVO resolveByMenuCode(String menuCode) {
if (AuthUtil.isAdministrator()) {
return RoleDataScopeVO.all();
}
String roleIds = AuthUtil.getUser() == null ? StringPool.EMPTY : Func.toStr(AuthUtil.getUser().getRoleId());
String deptIds = AuthUtil.getDeptId();
if (Func.isBlank(roleIds)) {
return RoleDataScopeVO.all();
}
String cacheKey = roleIds + StringPool.COLON + Func.toStr(menuCode) + StringPool.COLON + Func.toStr(deptIds);
return CacheUtil.get(SYS_CACHE, CACHE_MENU, cacheKey, () -> {
R<RoleDataScopeVO> result = getClient().resolveByMenuCode(roleIds, menuCode, deptIds);
if (result == null || !result.isSuccess() || result.getData() == null) {
return RoleDataScopeVO.all();
}
return result.getData();
});
}
/**
* 解析当前登录用户全部角色菜单的最宽数据权限
*/
public static RoleDataScopeVO resolveWidest() {
if (AuthUtil.isAdministrator()) {
return RoleDataScopeVO.all();
}
String roleIds = AuthUtil.getUser() == null ? StringPool.EMPTY : Func.toStr(AuthUtil.getUser().getRoleId());
String deptIds = AuthUtil.getDeptId();
if (Func.isBlank(roleIds)) {
return RoleDataScopeVO.all();
}
String cacheKey = roleIds + StringPool.COLON + Func.toStr(deptIds);
return CacheUtil.get(SYS_CACHE, CACHE_WIDEST, cacheKey, () -> {
R<RoleDataScopeVO> result = getClient().resolveWidest(roleIds, deptIds);
if (result == null || !result.isSuccess() || result.getData() == null) {
return RoleDataScopeVO.all();
}
return result.getData();
});
}
/**
* 清理角色数据权限缓存
*/
public static void clear() {
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
}
}
@@ -0,0 +1,74 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.feign;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.tool.api.R;
import org.springblade.system.pojo.vo.RoleDataScopeVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 角色数据权限 Feign
*
* @author Chill
*/
@FeignClient(
value = AppConstant.APPLICATION_SYSTEM_NAME,
fallback = IRoleDataScopeClientFallback.class
)
public interface IRoleDataScopeClient {
String API_PREFIX = "/feign/client/role-data-scope";
String RESOLVE_BY_MENU_CODE = API_PREFIX + "/resolve-by-menu-code";
String RESOLVE_WIDEST = API_PREFIX + "/resolve-widest";
/**
* 按菜单编码解析当前用户角色合并后的数据权限
*
* @param roleIds 用户角色ID,逗号分隔
* @param menuCode 菜单编码
* @param deptIds 用户所属组织ID,逗号分隔
* @return 解析结果
*/
@GetMapping(RESOLVE_BY_MENU_CODE)
R<RoleDataScopeVO> resolveByMenuCode(@RequestParam("roleIds") String roleIds,
@RequestParam("menuCode") String menuCode,
@RequestParam(value = "deptIds", required = false) String deptIds);
/**
* 解析当前用户全部角色菜单配置的最宽数据权限(组织树等公共场景)
*
* @param roleIds 用户角色ID,逗号分隔
* @param deptIds 用户所属组织ID,逗号分隔
* @return 解析结果
*/
@GetMapping(RESOLVE_WIDEST)
R<RoleDataScopeVO> resolveWidest(@RequestParam("roleIds") String roleIds,
@RequestParam(value = "deptIds", required = false) String deptIds);
}
@@ -0,0 +1,50 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.feign;
import org.springblade.core.tool.api.R;
import org.springblade.system.pojo.vo.RoleDataScopeVO;
import org.springframework.stereotype.Component;
/**
* 角色数据权限 Feign 降级
*
* @author Chill
*/
@Component
public class IRoleDataScopeClientFallback implements IRoleDataScopeClient {
@Override
public R<RoleDataScopeVO> resolveByMenuCode(String roleIds, String menuCode, String deptIds) {
return R.data(RoleDataScopeVO.all());
}
@Override
public R<RoleDataScopeVO> resolveWidest(String roleIds, String deptIds) {
return R.data(RoleDataScopeVO.all());
}
}
@@ -0,0 +1,59 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.pojo.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 角色菜单数据权限批量保存
*
* @author Chill
*/
@Data
@Schema(description = "角色菜单数据权限保存参数")
public class RoleMenuDataScopeDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "角色主键")
private Long roleId;
@Schema(description = "菜单主键集合")
private List<Long> menuIds;
@Schema(description = "数据权限类型:1所在部门及下级 2全部组织 3本人创建 4自定义组织")
private Integer scopeType;
@Schema(description = "自定义组织ID集合(scopeType=4 时必填)")
private List<Long> deptIds;
}
@@ -0,0 +1,68 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.pojo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.tenant.mp.TenantEntity;
import java.io.Serial;
/**
* 角色菜单数据权限
*
* @author Chill
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("blade_role_menu_data_scope")
@Schema(description = "RoleMenuDataScope对象")
public class RoleMenuDataScope extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 角色主键
*/
@Schema(description = "角色主键")
private Long roleId;
/**
* 菜单主键
*/
@Schema(description = "菜单主键")
private Long menuId;
/**
* 数据权限类型:1所在部门及下级 2全部组织 3本人创建 4自定义组织
*/
@Schema(description = "数据权限类型")
private Integer scopeType;
}
@@ -0,0 +1,65 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 角色菜单数据权限自定义组织
*
* @author Chill
*/
@Data
@TableName("blade_role_menu_data_scope_dept")
@Schema(description = "RoleMenuDataScopeDept对象")
public class RoleMenuDataScopeDept implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@JsonSerialize(using = ToStringSerializer.class)
@Schema(description = "主键")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@JsonSerialize(using = ToStringSerializer.class)
@Schema(description = "角色菜单数据权限主键")
private Long scopeId;
@JsonSerialize(using = ToStringSerializer.class)
@Schema(description = "组织主键")
private Long deptId;
}
@@ -0,0 +1,84 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.pojo.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 角色菜单数据权限类型
*
* @author Chill
*/
@Getter
@AllArgsConstructor
public enum RoleDataScopeType {
/**
* 所在部门及下级
*/
DEPT_AND_CHILD(1, "所在部门及下级"),
/**
* 全部组织(默认)
*/
ALL(2, "全部组织"),
/**
* 本人创建
*/
OWN_CREATE(3, "本人创建"),
/**
* 自定义组织
*/
CUSTOM(4, "自定义组织");
private final int code;
private final String name;
/**
* 默认权限类型:全部组织
*/
public static final int DEFAULT_CODE = ALL.code;
public static RoleDataScopeType of(Integer code) {
if (code == null) {
return ALL;
}
for (RoleDataScopeType type : values()) {
if (type.code == code) {
return type;
}
}
return ALL;
}
public static String nameOf(Integer code) {
return of(code).getName();
}
public boolean matches(Integer code) {
return code != null && this.code == code;
}
}
@@ -0,0 +1,87 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springblade.system.pojo.enums.RoleDataScopeType;
import java.io.Serial;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
/**
* 当前用户解析后的角色数据权限
*
* @author Chill
*/
@Data
@Schema(description = "角色数据权限解析结果")
public class RoleDataScopeVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "数据权限类型")
private Integer scopeType;
@Schema(description = "是否全部组织")
private boolean allDept;
@Schema(description = "是否仅本人创建")
private boolean ownCreate;
@Schema(description = "允许的组织ID集合(已展开)")
private List<Long> deptIds;
public static RoleDataScopeVO all() {
RoleDataScopeVO scope = new RoleDataScopeVO();
scope.setScopeType(RoleDataScopeType.ALL.getCode());
scope.setAllDept(true);
scope.setOwnCreate(false);
scope.setDeptIds(Collections.emptyList());
return scope;
}
public static RoleDataScopeVO ownCreate() {
RoleDataScopeVO scope = new RoleDataScopeVO();
scope.setScopeType(RoleDataScopeType.OWN_CREATE.getCode());
scope.setAllDept(false);
scope.setOwnCreate(true);
scope.setDeptIds(Collections.emptyList());
return scope;
}
public static RoleDataScopeVO ofDepts(Integer scopeType, List<Long> deptIds) {
RoleDataScopeVO scope = new RoleDataScopeVO();
scope.setScopeType(scopeType == null ? RoleDataScopeType.CUSTOM.getCode() : scopeType);
scope.setAllDept(false);
scope.setOwnCreate(false);
scope.setDeptIds(deptIds == null ? Collections.emptyList() : List.copyOf(deptIds));
return scope;
}
}
@@ -0,0 +1,58 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springblade.core.tool.node.TreeNode;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 角色数据权限配置树
*
* @author Chill
*/
@Data
@Schema(description = "角色数据权限配置树")
public class RoleMenuDataScopeTreeVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "角色名称")
private String roleName;
@Schema(description = "菜单树")
private List<TreeNode> menuTree = new ArrayList<>();
@Schema(description = "已配置的菜单数据权限")
private List<RoleMenuDataScopeVO> scopes = new ArrayList<>();
}
@@ -0,0 +1,67 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.pojo.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 角色菜单数据权限配置项
*
* @author Chill
*/
@Data
@Schema(description = "角色菜单数据权限配置项")
public class RoleMenuDataScopeVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@JsonSerialize(using = ToStringSerializer.class)
@Schema(description = "菜单主键")
private Long menuId;
@Schema(description = "菜单编码")
private String menuCode;
@Schema(description = "数据权限类型")
private Integer scopeType;
@Schema(description = "数据权限类型名称")
private String scopeTypeName;
@JsonSerialize(contentUsing = ToStringSerializer.class)
@Schema(description = "自定义组织ID集合")
private List<Long> deptIds = new ArrayList<>();
}