✨ 用户编辑支持数据管理权限多选组织并与角色权限并集

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-04 03:14:44 +08:00
parent fd3ca7496d
commit a0e30d5834
6 changed files with 193 additions and 9 deletions
@@ -32,6 +32,7 @@ import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.system.cache.RoleDataScopeCache;
import org.springblade.system.cache.SysCache;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.system.pojo.vo.RoleDataScopeVO;
import org.springblade.transport.mapper.ProjectApplyMapper;
@@ -41,6 +42,7 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -132,14 +134,14 @@ public final class TransportBusinessSupport {
}
/**
* 解析指定菜单下的角色数据权限(超管视为全部组织)。
* 解析指定菜单下的角色数据权限(超管视为全部组织),并与用户「数据管理权限」组织取并集。
*/
public static RoleDataScopeVO resolveRoleDataScope(String menuCode) {
if (isAdministrator()) {
return RoleDataScopeVO.all();
}
RoleDataScopeVO scope = RoleDataScopeCache.resolveByMenuCode(menuCode);
return scope == null ? RoleDataScopeVO.all() : scope;
return mergeUserManageDepts(scope == null ? RoleDataScopeVO.all() : scope);
}
/**
@@ -159,10 +161,43 @@ public final class TransportBusinessSupport {
if (isAdministrator()) {
return true;
}
RoleDataScopeVO scope = RoleDataScopeCache.resolveWidest();
RoleDataScopeVO scope = resolveWidestDataScope();
return scope == null || scope.isAllDept();
}
/**
* 解析最宽角色数据权限,并与用户「数据管理权限」组织取并集。
*/
public static RoleDataScopeVO resolveWidestDataScope() {
if (isAdministrator()) {
return RoleDataScopeVO.all();
}
RoleDataScopeVO scope = RoleDataScopeCache.resolveWidest();
return mergeUserManageDepts(scope == null ? RoleDataScopeVO.all() : scope);
}
/**
* 角色组织权限与用户数据管理组织取并集。
* <p>
* 全部组织 / 本人创建 不因用户管理组织改变;其余类型将用户管理组织(含下级)并入 deptIds。
*/
private static RoleDataScopeVO mergeUserManageDepts(RoleDataScopeVO scope) {
if (scope == null || scope.isAllDept() || scope.isOwnCreate()) {
return scope;
}
Long userId = AuthUtil.getUserId();
List<Long> manageDeptIds = UserCache.getManageDeptIds(userId);
if (Func.isEmpty(manageDeptIds)) {
return scope;
}
LinkedHashSet<Long> mergedDeptIds = new LinkedHashSet<>();
if (Func.isNotEmpty(scope.getDeptIds())) {
mergedDeptIds.addAll(scope.getDeptIds());
}
mergedDeptIds.addAll(manageDeptIds);
return RoleDataScopeVO.ofDepts(scope.getScopeType(), List.copyOf(mergedDeptIds));
}
/**
* 是否仅本人创建数据权限
*/
@@ -187,6 +222,29 @@ public final class TransportBusinessSupport {
return dept;
}
/**
* 列表行是否只读:与 {@link #assertCurrentDept(Long, String, String)} 判定保持一致。
* <p>
* 超管 / 全部组织 / 本人创建数据权限下可编辑;指定组织权限下,仅所属组织在授权范围内时可编辑;
* 否则仅当前登录用户所属组织的数据可编辑。
*
* @param deptId 业务数据所属组织
* @param menuCode 菜单编码
* @return true-前端仅展示查看
*/
public static boolean isDeptReadonly(Long deptId, String menuCode) {
RoleDataScopeVO scope = resolveRoleDataScope(menuCode);
if (scope.isAllDept() || scope.isOwnCreate()) {
return false;
}
List<Long> allowedDeptIds = scope.getDeptIds();
if (Func.isNotEmpty(allowedDeptIds)) {
return deptId == null || deptId <= 0 || !allowedDeptIds.contains(deptId);
}
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
return currentDeptId != null && !Objects.equals(deptId, currentDeptId);
}
/**
* 校验目标业务数据属于当前用户可操作的组织。
*/
@@ -214,7 +272,7 @@ public final class TransportBusinessSupport {
if (hasAllDeptScope()) {
return;
}
RoleDataScopeVO scope = RoleDataScopeCache.resolveWidest();
RoleDataScopeVO scope = resolveWidestDataScope();
if (scope != null && scope.isOwnCreate()) {
return;
}
@@ -396,7 +454,7 @@ public final class TransportBusinessSupport {
}
return Collections.emptyList();
}
RoleDataScopeVO scope = RoleDataScopeCache.resolveWidest();
RoleDataScopeVO scope = resolveWidestDataScope();
if (scope != null && scope.isOwnCreate()) {
if (Objects.equals(allDept, 1) && Func.isNotEmpty(deptId) && deptId > 0) {
return Collections.singletonList(deptId);