diff --git a/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java b/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java index e070276..9623605 100644 --- a/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java +++ b/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java @@ -96,7 +96,8 @@ public class UserController { * * @param request 用于取 AppAuthFilter 设置的 appKey * @param userId 用户ID(必填) - * @param schId 学校ID/组ID(可选;为 null 时按"主学校→主归属组"规则推断) + * @param schId 学校ID/组ID(可选;用于限定「学校上下文」:非空时仅取该学校子树内的真实归属组参与判定, + * 为空时取用户全部真实归属组,与权限一览一致) * @return 该应用下用户已授权模块(扁平化;无权限时返回空列表) */ @GetMapping("modules") diff --git a/src/main/java/com/yunhai/userbase/model/oapilog/SysOapiLogListVo.java b/src/main/java/com/yunhai/userbase/model/oapilog/SysOapiLogListVo.java index 3da66fc..37a5acd 100644 --- a/src/main/java/com/yunhai/userbase/model/oapilog/SysOapiLogListVo.java +++ b/src/main/java/com/yunhai/userbase/model/oapilog/SysOapiLogListVo.java @@ -38,6 +38,8 @@ public class SysOapiLogListVo { private String operationDesc; /** 请求参数JSON(脱敏后) */ private String requestParams; + /** 响应结果(GZIP 解压后原文,脱敏后;随列表带出,详情弹窗直接展示) */ + private String responseData; /** HTTP 状态码 */ private Integer statusCode; /** 耗时(ms) */ diff --git a/src/main/java/com/yunhai/userbase/service/impl/GroupAuthzService.java b/src/main/java/com/yunhai/userbase/service/impl/GroupAuthzService.java index 4e9af69..a7cfe08 100644 --- a/src/main/java/com/yunhai/userbase/service/impl/GroupAuthzService.java +++ b/src/main/java/com/yunhai/userbase/service/impl/GroupAuthzService.java @@ -1,9 +1,7 @@ package com.yunhai.userbase.service.impl; import com.yunhai.platform.utils.exception.DException; -import com.yunhai.platform.utils.model.BoaGroup; import com.yunhai.platform.utils.model.BoaModule; -import com.yunhai.platform.utils.model.BoaUser; import com.yunhai.userbase.entity.*; import com.yunhai.userbase.enums.GroupTypeEnum; import com.yunhai.platform.utils.enums.IEnum; @@ -104,9 +102,6 @@ public class GroupAuthzService implements IGroupAuthzService { @Autowired private SysUserAppRoleRelRepository sysUserAppRoleRelRepository; - @Autowired - private UserCenterService userCenterService; - // ===================== 新增:沿祖先链收集「本组+所有祖先组开通的应用ID并集」(应用池联动GetSchoolAppId) // ===================== @@ -523,23 +518,43 @@ public class GroupAuthzService implements IGroupAuthzService { } // 3. 收集该用户启用角色下=属于目标应用的模块ID(角色授权并集,不要求组先开通应用) Set roleModuleIdsInApp = collectEnabledRoleModuleIdsForApp(userId, appId); - // 4. 上下文组解析(schId 非空=只认这一条链路;schId 空=按"主学校→主归属组"规则推断) - Integer effectiveGroupId = resolveEffectiveGroupId(userId, schId); - // 5. 若上下文组有效=构建其祖先链,并判定:该祖先链开通池里是否包含目标 appId(应用开通闸门) - // 上下文组无效(用户无学校/无组)时,仍可能因角色授权而返回模块,不能直接返回空 - Set selfGroupIds = new LinkedHashSet<>(); + // 4. 归属组集合解析(与权限一览 findUserPermissionTree 语义对齐,修复"只返回学校节点"的 bug): + // - schId 非空:只取用户真实归属组中「位于该学校子树内」的组(含直接挂学校组本身), + // 每个组独立建链向上判定;子树内无任何归属组 → 保留空 selfGroupIds(方案A:仅靠角色授权拿模块, + // 不把 schId 冒充本组,否则会绕过子组继承闸门、丢本组数据); + // - schId 空:取用户全部真实归属组(与权限一览「用户全局模式」一致)。 + List userRels = sysUserGroupRelRepository.findByUserId(userId); Map allGroupMap = buildAllGroupMap(); + Set selfGroupIds = new LinkedHashSet<>(); + if (schId != null) { + for (SysUserGroupRel rel : userRels) { + Integer gid = rel.getGroupId(); + if (gid == null) { + continue; + } + if (gid.equals(schId) || ancestorChain(gid, allGroupMap).contains(schId)) { + selfGroupIds.add(gid); + } + } + } else { + for (SysUserGroupRel rel : userRels) { + if (rel.getGroupId() != null) { + selfGroupIds.add(rel.getGroupId()); + } + } + } + // 5. 构建各归属组的祖先链与开通池,并判定:任意归属组开通池里是否包含目标 appId(应用开通闸门)。 + // 归属组为空(该学校子树内无组 / 无学校无组)时仍可能因角色授权而返回模块,不能直接返回空。 Map> chainMap = new HashMap<>(); Set allChainIds = new LinkedHashSet<>(); Map> groupToApps = new HashMap<>(); boolean appOpened = false; - if (effectiveGroupId != null) { - selfGroupIds.add(effectiveGroupId); - List chain = ancestorChain(effectiveGroupId, allGroupMap); - chainMap.put(effectiveGroupId, chain); + for (Integer gid : selfGroupIds) { + List chain = ancestorChain(gid, allGroupMap); + chainMap.put(gid, chain); allChainIds.addAll(chain); - Set openedApps = collectAppIdsForAuthzScope(effectiveGroupId); - groupToApps.put(effectiveGroupId, openedApps); + Set openedApps = collectAppIdsForAuthzScope(gid); + groupToApps.put(gid, openedApps); if (openedApps.contains(appId)) { appOpened = true; } @@ -612,33 +627,6 @@ public class GroupAuthzService implements IGroupAuthzService { return result; } - /** - * 上下文组解析(与开放接口 modules 语义严格一致): - * - schId 非空:直接用其作为唯一 selfGroupId; - * - schId 为空:取用户主学校组(BoaUser.school.id); - * - 仍空:取用户主归属组 id(groups 第 0 个); - * - 全空:返回 null(此时只能靠角色授权拿模块)。 - */ - private Integer resolveEffectiveGroupId(Integer userId, Integer schId) { - if (schId != null) { - return schId; - } - BoaUser boa = userCenterService.getUserInfoWithDeptById(userId); - if (boa == null) { - return null; - } - BoaGroup school = boa.getSchool(); - if (school != null && school.getId() != null) { - return school.getId(); - } - List groups = boa.getGroups(); - if (groups != null && !groups.isEmpty()) { - BoaGroup main = groups.get(0); - return main == null ? null : main.getId(); - } - return null; - } - private static Map buildAppModuleMap(List appModules) { Map map = new HashMap<>(); for (AppModule m : appModules) { diff --git a/src/main/java/com/yunhai/userbase/service/impl/SysGroupService.java b/src/main/java/com/yunhai/userbase/service/impl/SysGroupService.java index f7e292d..fbb7cd5 100644 --- a/src/main/java/com/yunhai/userbase/service/impl/SysGroupService.java +++ b/src/main/java/com/yunhai/userbase/service/impl/SysGroupService.java @@ -65,9 +65,9 @@ public class SysGroupService implements ISysGroupService { // 区域组:只能挂在根节点下(由根节点的白名单约束),其下可挂 学段组/学校组/普通组 ALLOWED_CHILDREN.put(GroupTypeEnum.AREA_GROUP, EnumSet.of( GroupTypeEnum.STAGE_GROUP, GroupTypeEnum.SCHOOL_GROUP, GroupTypeEnum.NORMAL_GROUP)); - // 学段组(高中/初中/小学):学校组的上级,可含 学校组/普通组 + // 学段组(高中/初中/小学):学校组的上级,可含 学校组/年级组 ALLOWED_CHILDREN.put(GroupTypeEnum.STAGE_GROUP, EnumSet.of( - GroupTypeEnum.SCHOOL_GROUP, GroupTypeEnum.NORMAL_GROUP)); + GroupTypeEnum.SCHOOL_GROUP, GroupTypeEnum.GRADE_GROUP)); // 学校组:可含 教师组/学生组/普通组 ALLOWED_CHILDREN.put(GroupTypeEnum.SCHOOL_GROUP, EnumSet.of( GroupTypeEnum.TEACHER_GROUP, GroupTypeEnum.STUDENT_GROUP, GroupTypeEnum.NORMAL_GROUP)); diff --git a/src/main/java/com/yunhai/userbase/service/impl/SysOapiLogServiceImpl.java b/src/main/java/com/yunhai/userbase/service/impl/SysOapiLogServiceImpl.java index 542b9c9..e6327c7 100644 --- a/src/main/java/com/yunhai/userbase/service/impl/SysOapiLogServiceImpl.java +++ b/src/main/java/com/yunhai/userbase/service/impl/SysOapiLogServiceImpl.java @@ -89,6 +89,10 @@ public class SysOapiLogServiceImpl implements ISysOapiLogService { typed.setFirstResult((pageIndex - 1) * pageSize); typed.setMaxResults(pageSize); List content = typed.getResultList(); + // 响应结果随列表带出(GZIP 解压后),供详情弹窗直接展示,无需再单独请求 + for (SysOapiLogListVo vo : content) { + vo.setResponseData(getResponseData(vo.getId())); + } int totalPages = (int) Math.ceil((double) total / pageSize); boolean hasNext = pageIndex < totalPages; @@ -165,9 +169,11 @@ public class SysOapiLogServiceImpl implements ISysOapiLogService { } if (StringUtils.isNotBlank(query.getKeyword())) { String kw = "%" + query.getKeyword().trim() + "%"; + // 单输入框同时匹配:请求路径(requestUri) / 应用名(callerAppName) / appKey(callerAppKey) predicates.add(cb.or( cb.like(root.get("requestUri"), kw), - cb.like(root.get("callerAppName"), kw))); + cb.like(root.get("callerAppName"), kw), + cb.like(cb.toString(root.get("callerAppKey")), kw))); } if (query.getStartTime() != null) { predicates.add(cb.greaterThanOrEqualTo(root.get("createTime"), query.getStartTime()));