diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java index 1ac4ec79..e356c0b5 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java @@ -2116,7 +2116,7 @@ public class AihrPracticeSeedService { AND employment_status = 'active' LIMIT 1 """, (rs, rowNum) -> rs.getString("position_level"), args.toArray()); - if (positionLevels.isEmpty() || !canSeeProject(positionLevels.get(0))) { + if (positionLevels.stream().noneMatch(AihrPracticeSeedService::canSeeProject)) { throw new ServiceException("无主管权限,无法访问主管功能"); } return identity; @@ -2149,8 +2149,11 @@ public class AihrPracticeSeedService { if (owners.isEmpty()) { return TeamScope.scoped(List.of()); } - OrgScopeRow row = owners.get(0); - if (!canSeeProject(row.positionLevel())) { + OrgScopeRow row = owners.stream() + .filter(scope -> canSeeProject(scope.positionLevel())) + .findFirst() + .orElse(null); + if (row == null) { return TeamScope.scoped(List.of(owner)); } List ids = jdbcTemplate.query(""" diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrPracticeSeedServiceTest.java b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrPracticeSeedServiceTest.java index 09402316..fe178ef9 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrPracticeSeedServiceTest.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrPracticeSeedServiceTest.java @@ -241,6 +241,15 @@ public class AihrPracticeSeedServiceTest { assertEquals(List.of("000000", "13900001111", "13900001111"), jdbcTemplate.authorizationArgs); } + @Test + public void supervisorIdentityAcceptsSupervisorWhenPhoneAlsoMatchesFrontlineRow() { + SupervisorAuthorizationJdbcTemplate jdbcTemplate = new SupervisorAuthorizationJdbcTemplate( + "13900001111", List.of("一线", "主管")); + AihrPracticeSeedService service = new AihrPracticeSeedService(new ObjectMapper(), jdbcTemplate, null, null); + + assertEquals("13900001111", service.requireSupervisorIdentity("13900001111")); + } + @Test public void supervisorIdentityRejectsFrontlineUnknownAndBlankIdentity() { SupervisorAuthorizationJdbcTemplate jdbcTemplate = new SupervisorAuthorizationJdbcTemplate(Map.of( @@ -872,13 +881,18 @@ public class AihrPracticeSeedServiceTest { } private static final class SupervisorAuthorizationJdbcTemplate extends JdbcTemplate { - private final Map positionLevels; + private final Map> positionLevels; private String authorizationSql = ""; private List authorizationArgs = List.of(); private int authorizationQueryCount; private SupervisorAuthorizationJdbcTemplate(Map positionLevels) { - this.positionLevels = positionLevels; + this.positionLevels = positionLevels.entrySet().stream() + .collect(java.util.stream.Collectors.toMap(Map.Entry::getKey, entry -> List.of(entry.getValue()))); + } + + private SupervisorAuthorizationJdbcTemplate(String identity, List positionLevels) { + this.positionLevels = Map.of(identity, List.copyOf(positionLevels)); } @Override @@ -896,12 +910,14 @@ public class AihrPracticeSeedServiceTest { authorizationSql = sql; authorizationArgs = List.of(args); authorizationQueryCount += 1; - String positionLevel = positionLevels.get(String.valueOf(args[1])); - if (positionLevel == null) { + List positionRows = positionLevels.get(String.valueOf(args[1])); + if (positionRows == null) { return List.of(); } try { - return mapRows(rowMapper, List.of(Map.of("position_level", positionLevel))); + return mapRows(rowMapper, positionRows.stream() + .map(positionLevel -> Map.of("position_level", positionLevel)) + .toList()); } catch (SQLException e) { throw new IllegalStateException(e); } diff --git a/docs/BRD_IMPLEMENTATION_AUDIT.md b/docs/BRD_IMPLEMENTATION_AUDIT.md index 05800447..f5cc42f1 100644 --- a/docs/BRD_IMPLEMENTATION_AUDIT.md +++ b/docs/BRD_IMPLEMENTATION_AUDIT.md @@ -165,3 +165,4 @@ - 2026-07-14 线上资源只读核对:生产根站、`/h5/` 和 `/prod-api/auth/tenant/list` 均返回 `200`;生产管理端仍加载 `index-CJZ3Ax3Z.js`,本地当前管理端构建为 `index-D4ywKu9Y.js`。本次只读检查,未执行生产写入、静态资源同步或后端重启,因此不能把本地人工复核修复宣称为线上已生效。 - 2026-07-14 BRD G3 面试会话归属复核:发现候选人端 `answer/finish` 原先只按客户端提交的 `sessionId` 查内存会话,未知会话还会自动创建并落库;现由控制器把当前 APP 手机号传入服务,APP 请求必须匹配启动会话的候选人 ID,外部会话和未知会话统一拒绝;后台系统调用保留原有兼容行为。新增越权/未知会话回归测试与 `demo-check` marker,未修改生产环境。 - 2026-07-14 BRD G3 对练会话归属复核:发现三角色对练的 `start` 虽已绑定 APP 手机号,但 `turn/finish` 原先可用任意会话 ID继续提交,未知会话还会回退场景并落成训练记录;现由控制器把当前 APP 手机号传入服务,移动端回合与完成操作必须匹配启动会话的 `ext_party_id`,外部/未知会话统一拒绝;后台管理端调用保留原有演示兼容行为。新增越权/未知会话回归测试与 `demo-check` marker,未修改生产环境。 +- 2026-07-14 BRD G3 主管身份多记录复核:同一手机号可能对应多条在职组织记录,主管身份检查和团队范围解析现从全部匹配记录中选择“主管/项目经理”,不再因第一条记录是一线岗位而误拒主管或选错项目范围;新增多岗位手机号回归测试,未修改生产环境。 diff --git a/scripts/demo-check.sh b/scripts/demo-check.sh index b124ee87..cfe869fd 100755 --- a/scripts/demo-check.sh +++ b/scripts/demo-check.sh @@ -400,6 +400,7 @@ contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/control contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "practiceSeedService.turn(request, currentAppUsername())" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "practiceSeedService.finish(request, currentAppUsername())" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "训练会话不存在或无权访问" +contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "noneMatch(AihrPracticeSeedService::canSeeProject)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "@SaCheckLogin" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "sessions/{sessionId}/annotations" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPromptTemplateController.java "/api/aihr/prompt-templates"