permittedModuleIds = new LinkedHashSet<>();
for (AppModule m : allModules) {
if (roleModuleIds.contains(m.getId())
- || hasUserModulePermission(m.getId(), m.getAppId(), selfGroupIds, chainMap, groupToApps, modulePermMap)) {
+ || hasUserModulePermission(m.getId(), m.getAppId(), selfGroupIds, chainMap, groupToApps,
+ modulePermMap, allGroupMap)) {
permittedModuleIds.add(m.getId());
}
}
@@ -839,14 +853,25 @@ public class GroupAuthzService implements IGroupAuthzService {
}
/**
- * 用户维度权限判定:用户对指定模块是否有权限。
- * 复用 hasModulePermission 的祖先链继承逻辑,但基于已预热的批量查询结果。
+ * 用户维度权限判定:用户对指定模块是否有权限(两段式 + 学校截止 + 超管兜底(超管在入口短路,这里只处理普通用户))。
+ *
+ *
+ * 两段式(用户拍板:边界1选B):
+ * ① 先查「本组」(归属组 selfGroupId)有没有 sys_group_module_perm
+ * 记录(仅记录存在即=显式设置,无论inherit_flag值)→ 有 = 本组说了算,直接true,不再查祖先;
+ * ② 本组无记录 = 没有显式设置 = 进入继承查找:沿祖先链向上找,直到第一个学校组(包含学校组本身,不跨学校到教育局/根),第一个「记录存在且
+ * inherit_flag=true」的组即继承成功;
+ * ③ 学校组之上的节点(区域/教育局/根)都不查找,避免跨学校继承到未开通的应用(用户新增边界3)。
+ *
+ *
+ * 其余不变:归属组或其祖先需已开通该模块所属应用(双保险,防越权)。
*/
private boolean hasUserModulePermission(Integer moduleId, Integer appId,
- Set selfGroupIds,
- Map> chainMap,
- Map> groupToApps,
- Map> modulePermMap) {
+ Set selfGroupIds,
+ Map> chainMap,
+ Map> groupToApps,
+ Map> modulePermMap,
+ Map allGroupMap) {
Map permMap = modulePermMap.get(moduleId);
if (permMap == null || permMap.isEmpty()) {
return false;
@@ -860,27 +885,124 @@ public class GroupAuthzService implements IGroupAuthzService {
if (chain == null || chain.isEmpty()) {
continue;
}
+ // 第一段:本组显式设置优先(记录存在=true,完全按本组来,不继承不向上)
+ SysGroupModulePerm selfPerm = permMap.get(selfGroupId);
+ if (selfPerm != null) {
+ return true;
+ }
+ // 第二段:本组无记录 → 向上继承查找;遇到第一个学校组(SCHOOL_GROUP)就包含它自己,处理完停止(不跨学校)
for (Integer ancestorId : chain) {
- SysGroupModulePerm perm = permMap.get(ancestorId);
- if (perm == null) {
+ if (Objects.equals(ancestorId, selfGroupId)) {
continue;
}
- if (Objects.equals(ancestorId, selfGroupId)) {
+ SysGroup g = allGroupMap.get(ancestorId);
+ SysGroupModulePerm perm = permMap.get(ancestorId);
+ boolean isSchool = g != null && g.getType() == GroupTypeEnum.SCHOOL_GROUP;
+ if (perm != null && Boolean.TRUE.equals(perm.getInheritFlag())) {
return true;
}
- if (Boolean.TRUE.equals(perm.getInheritFlag())) {
- return true;
+ // 用户要求:向上查找只查到学校组这一层,包含学校组本身(上面已判断学校组的perm),之后不再跨学校
+ if (isSchool) {
+ break;
}
}
}
return false;
}
+ // ===================== 超级管理员 & 工具方法 =====================
+
+ /**
+ * 超级管理员 type=1(UserTypeEnum.SUPER_ADMIN / DicUtil.USER_TYPE_SUPER_MANAGER=1)
+ */
+ private static boolean isSuperAdmin(UserTypeEnum userType) {
+ return userType != null && UserTypeEnum.SUPER_ADMIN.getKey().equals(userType.getKey());
+ }
+
+ /**
+ * 超级管理员全模块权限:不依赖组/继承,直接「所有已分类应用下的模块」全量返回。
+ * 应用范围 = 所有已分类(category 存在)且有模块的应用,按应用过滤构建分类→应用→模块树。
+ */
+ private List buildSuperAdminAllPermissions() {
+ List categories = appCategoryRepository.findAllByOrderBySortNoAsc();
+ List allApps = applicationInfoRepository.findAll();
+ allApps.sort(appOrder());
+ Map appMap = new HashMap<>();
+ Set allAppIds = new LinkedHashSet<>();
+ for (ApplicationInfo app : allApps) {
+ appMap.put(app.getId(), app);
+ allAppIds.add(app.getId());
+ }
+ if (allAppIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List allModules = appModuleRepository.findByAppIdIn(new ArrayList<>(allAppIds));
+ if (allModules.isEmpty()) {
+ return Collections.emptyList();
+ }
+ Map> appModulesMap = new HashMap<>();
+ Set allModuleIds = new LinkedHashSet<>();
+ for (AppModule m : allModules) {
+ allModuleIds.add(m.getId());
+ appModulesMap.computeIfAbsent(m.getAppId(), k -> new ArrayList<>()).add(m);
+ }
+ Map> categoryAppMap = new HashMap<>();
+ for (Map.Entry> entry : appModulesMap.entrySet()) {
+ Integer appId = entry.getKey();
+ ApplicationInfo app = appMap.get(appId);
+ if (app == null) {
+ continue;
+ }
+ List moduleTree = buildUserPermissionModuleTree(entry.getValue(), allModuleIds);
+ if (moduleTree.isEmpty()) {
+ continue;
+ }
+ UserPermissionAppVo appVo = new UserPermissionAppVo();
+ appVo.setId(app.getId());
+ appVo.setName(app.getName());
+ appVo.setAppKey(app.getAppKey());
+ appVo.setPcIcon(app.getPcIcon());
+ appVo.setSortNo(app.getSortNo());
+ appVo.setModules(moduleTree);
+ Integer categoryId = app.getCategoryId() == null ? 0 : app.getCategoryId();
+ categoryAppMap.computeIfAbsent(categoryId, k -> new ArrayList<>()).add(appVo);
+ }
+ if (categoryAppMap.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List result = new ArrayList<>();
+ for (AppCategory category : categories) {
+ List appVos = categoryAppMap.remove(category.getId());
+ if (appVos == null || appVos.isEmpty()) {
+ continue;
+ }
+ appVos.sort(userPermissionAppOrder());
+ UserPermissionCategoryVo c = new UserPermissionCategoryVo();
+ c.setId(category.getId());
+ c.setName(category.getName());
+ c.setSortNo(category.getSortNo());
+ c.setApps(appVos);
+ result.add(c);
+ }
+ List remain = categoryAppMap.remove(0);
+ if (remain != null && !remain.isEmpty()) {
+ remain.sort(userPermissionAppOrder());
+ UserPermissionCategoryVo uncategorized = new UserPermissionCategoryVo();
+ uncategorized.setId(0);
+ uncategorized.setName("未分类");
+ uncategorized.setSortNo(Integer.MAX_VALUE);
+ uncategorized.setApps(remain);
+ result.add(uncategorized);
+ }
+ result.sort(Comparator.comparingInt(c -> c.getSortNo() == null ? Integer.MAX_VALUE : c.getSortNo()));
+ return result;
+ }
+
/**
* 构建用户权限一览的模块树:仅保留有权限的模块,父节点无权限时子节点挂到根级。
*/
private List buildUserPermissionModuleTree(List modules,
- Set permittedModuleIds) {
+ Set permittedModuleIds) {
// 先过滤
List permitted = new ArrayList<>();
for (AppModule m : modules) {
@@ -918,7 +1040,7 @@ public class GroupAuthzService implements IGroupAuthzService {
}
private UserPermissionModuleVo buildUserPermissionModuleNode(AppModule node,
- Map> childrenMap) {
+ Map> childrenMap) {
UserPermissionModuleVo vo = new UserPermissionModuleVo();
BeanUtils.copyProperties(node, vo);
List children = childrenMap.get(node.getId());
@@ -1009,7 +1131,7 @@ public class GroupAuthzService implements IGroupAuthzService {
allGroupMap.put(g.getId(), g);
}
// 3. 前置校验:所属组必须已开通模块所属应用(最终权限 = 继承的模块权限 ∩ 已开通应用);
- // 通过校验的组构建祖先链(含自身),并收集祖先链并集用于一次批量 IN 查询
+ // 通过校验的组构建祖先链(含自身),并收集祖先链并集用于一次批量 IN 查询
Map> chainMap = new HashMap<>();
Set allChainIds = new LinkedHashSet<>();
for (SysUserGroupRel rel : userRels) {
@@ -1029,8 +1151,8 @@ public class GroupAuthzService implements IGroupAuthzService {
}
// 4. 一次批量查询目标模块在所有祖先链节点上的权限记录
Map permMap = new HashMap<>();
- List perms =
- sysGroupModulePermRepository.findByModuleIdAndGroupIdIn(moduleId, new ArrayList<>(allChainIds));
+ List perms = sysGroupModulePermRepository.findByModuleIdAndGroupIdIn(moduleId,
+ new ArrayList<>(allChainIds));
for (SysGroupModulePerm perm : perms) {
permMap.put(perm.getGroupId(), perm);
}
@@ -1081,7 +1203,7 @@ public class GroupAuthzService implements IGroupAuthzService {
* 组装单个应用下的模块权限树(沿用 AppModuleService 的组装方式:childrenMap + sortNo 排序)。
*/
private List buildModuleTree(List modules,
- Map permMap) {
+ Map permMap) {
// parentId -> 子节点列表(null 视为 0)
Map> childrenMap = new HashMap<>();
for (AppModule m : modules) {
@@ -1112,8 +1234,8 @@ public class GroupAuthzService implements IGroupAuthzService {
* 递归组装模块节点,并按权限记录回显 checked / inheritFlag。
*/
private GroupAuthzModuleVo buildModuleNode(AppModule node,
- Map> childrenMap,
- Map permMap) {
+ Map> childrenMap,
+ Map permMap) {
GroupAuthzModuleVo vo = new GroupAuthzModuleVo();
BeanUtils.copyProperties(node, vo);
SysGroupModulePerm perm = permMap.get(node.getId());