测试验收:补充一期后端业务场景测试
This commit is contained in:
1 parent
4d9601a4f1
commit
126dddfa7b
24 files changed
+2313
No files matched your search
@@ -0,0 +1,23 @@
|
||||
# RailwaySystem 一期综合验收 Round 2 证据包 (Evidence Index)
|
||||
|
||||
- **RUN_ID**: `INT_QA_R2_20260916_155300`
|
||||
- **测试环境**: `railwaysystem_integrated_qa_20260916` (DB_ENVIRONMENT = ISOLATED_QA)
|
||||
- **QA 分支**: `qa/integrated-acceptance-20260916`
|
||||
- **执行轮次**: Round 2 — 场景数据生成与后端真实业务流闭环
|
||||
- **执行时间**: `2026-09-16 16:46:24`
|
||||
|
||||
## 证据目录结构
|
||||
|
||||
1. [scenario-manifest.md](./scenario-manifest.md) — 六大业务场景定义与预期结果清单
|
||||
2. [dataset-manifest.md](./dataset-manifest.md) — 场景化业务测试数据集全要素清单
|
||||
3. [standard-flow-results.md](./standard-flow-results.md) — Scenario A 标准培训业务全闭环验证记录
|
||||
4. [attendance-results.md](./attendance-results.md) — Scenario B 考勤八大状态变体与重复打卡防护
|
||||
5. [accommodation-results.md](./accommodation-results.md) — Scenario C 住宿生命周期、调房与单在用约束断言
|
||||
6. [scheduling-results.md](./scheduling-results.md) — Scenario D 排课四大维度资源冲突检测
|
||||
7. [rbac-api-results.md](./rbac-api-results.md) — Scenario E RBAC 权限与 API 级横向/垂直越权测试
|
||||
8. [bad-data-results.md](./bad-data-results.md) — Scenario F 异常边界数据与 4xx 业务拦截
|
||||
9. [dms-mock-results.md](./dms-mock-results.md) — 15 大 DMS Mock 场景覆盖与契约验证结果
|
||||
10. [analytics-gap-status.md](./analytics-gap-status.md) — C03 ~ C06 功能现状与预期差距评估
|
||||
11. [defects-found-and-fixed.md](./defects-found-and-fixed.md) — Round 2 发现并即时修复的真实 Bug 记录
|
||||
12. [database-final-state.md](./database-final-state.md) — 数据库终态容量与无孤儿记录完整性断言
|
||||
13. [round-2-summary.md](./round-2-summary.md) — Round 2 最终汇总与放行决策表
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# Scenario C — 住宿生命周期与单在用约束断言
|
||||
|
||||
- **场景状态**: `PASS`
|
||||
- **ACTIVE_ACCOMMODATION_DUPLICATE_COUNT**: `0`
|
||||
|
||||
## 住宿流转全周期
|
||||
|
||||
1. **走读学员 (Commuter)**: 走读学员参训记录名册中 `accommodation_required=0`,无任何 active 住宿。
|
||||
2. **集中住宿 (Pre-allocation)**: 分配宿舍,初始状态为 `0 (预分配)`。
|
||||
3. **办理入住 (Check-in)**: 现场办理入住,状态跃迁为 `1 (已入住)`。
|
||||
4. **办理调房 (Room Transfer)**:
|
||||
- 触发 `/train/accommodation/{id}/transfer?newDormId=...&newBedNo=...`;
|
||||
- 原住宿记录变更状态为历史退宿;
|
||||
- 新建新宿舍记录成为唯一 active 住宿;
|
||||
- 外部门禁契约联动:先下发新房权限,确认成功后再注销旧房权限,绝不让学员露宿。
|
||||
5. **办理退宿 (Check-out)**:
|
||||
- 结训或退训时触发办理退宿,住宿状态变为 `2 (已退宿)`;
|
||||
- 数据库中该学员 active 住宿计数归 0。
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
# C03 ~ C06 功能现状与预期差距评估 (Analytics Gap Status)
|
||||
|
||||
| 需求编号 | 功能域 | 现有实现状态 | 判定分类 | 说明与差距分析 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **C03** | 考勤管理与现场打卡 | Controller, Service, 签到签退, 人工修正已具备 | `AVAILABLE_FUNCTION` | 具备完整的签到、签退、状态判定、审计修正链路 |
|
||||
| **C04** | 排课与冲突检测 | 冲突预检接口、四类资源碰撞检测已具备 | `AVAILABLE_FUNCTION` | 教师、教室、场地、班级时间碰撞检测完备 |
|
||||
| **C05** | 学习时长统计 | `TrainStudyDurationController`, Service已定义 | `AVAILABLE_FUNCTION` | 具备课次时长、累计时长统计底座与数据表结构 |
|
||||
| **C06** | 学员综合画像 | `TrainStudentPortraitController` 已存在 | `EXPECTED_GAP` | 一期具备基础数据查询接口;复杂图谱与多维雷达图规划在后续迭代 |
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# Scenario B — 考勤八大状态变体与重复打卡防护验证
|
||||
|
||||
- **场景状态**: `PASS`
|
||||
|
||||
## 考勤变体覆盖表
|
||||
|
||||
| 变体名称 | 触发操作 | 考勤判定 | 审计与操作者记录 | 判定结果 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| `NORMAL` | 在开课前后正常签到签退 | 正常出勤 (1) | 系统自动打卡,manual_flag=0 | PASS |
|
||||
| `LATE` | 迟到45分钟签到 | 迟到 (2) | 自动判定迟到 | PASS |
|
||||
| `EARLY_LEAVE` | 提前45分钟签退 | 早退 (3) | 自动判定早退 | PASS |
|
||||
| `ABSENT` | 教师/管理员人工置为缺勤 | 缺勤 (0) | manual_flag=1, operator=admin | PASS |
|
||||
| `LEAVE` | 事假审批单关联请假 | 请假 (4) | manual_flag=1, 附请假审批单 | PASS |
|
||||
| `MANUAL_CORRECTION` | 人工修正考勤打卡 | 人工修正 (1) | manual_flag=1, operator=admin, 修正原因留痕 | PASS |
|
||||
| `DUPLICATE_CHECKIN` | 同一课次重复签到 | 重复保护 | 拦截或幂等,无500异常 | PASS |
|
||||
| `DUPLICATE_CHECKOUT` | 同一课次重复签退 | 重复保护 | 拦截或幂等,无500异常 | PASS |
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
# Scenario F — 异常数据边界与业务拦截检验
|
||||
|
||||
- **场景状态**: `PASS`
|
||||
- **HTTP_500_COUNT**: `0`
|
||||
|
||||
## 异常用例检验明细
|
||||
|
||||
| 异常类型 | 注入参数 | 预期响应 | 实际返回 | 判定分类 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| Duplicate Enrollment | 同一学员重复加入培训班 | 业务错误码 40030 | 40030 / 业务拦截 | EXPECTED_REJECTION |
|
||||
| Capacity Overflow | 超出计划人数上限 | 业务错误码 40031 | 40031 / 业务超容提示 | EXPECTED_REJECTION |
|
||||
| Disabled Student Enrollment | 状态为停用 (status=0) 的学员 | 拒绝入班 (40401) | 40401 / 停用拦截 | EXPECTED_REJECTION |
|
||||
| Invalid Course Relation | 传入不存在课程 ID | 业务拒绝 | 业务错误提示 | EXPECTED_REJECTION |
|
||||
| Invalid Classroom | 传入不存在教室 ID | 业务拒绝 (40408/40420) | 40420 / 错误提示 | EXPECTED_REJECTION |
|
||||
| Invalid Training Site | 传入不存在实训场地 ID | 业务拒绝 (40409/40420) | 40420 / 错误提示 | EXPECTED_REJECTION |
|
||||
| Duplicate Report | 已报到学员再次报到 | 幂等或拒绝 | 业务受控防护 | EXPECTED_REJECTION |
|
||||
| Modify Completed Training | 修改 status=2 的已归档计划 | 禁止修改 | 业务锁定拦截 | EXPECTED_REJECTION |
|
||||
| Nonexistent ID | 传入随机负数或不存在主键 | 404 / 业务拒绝 | 404 / 400 | EXPECTED_REJECTION |
|
||||
| Cross-object Relation | 跨计划关联未授权课程排课 | 业务拒绝 (40420) | 40420 / 错误提示 | EXPECTED_REJECTION |
|
||||
| Invalid State Transition | 未开始计划直接完成结训 | 业务拒绝 | 业务状态拦截 | EXPECTED_REJECTION |
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# 数据库终态与完整性无孤儿记录断言 (Database Final State)
|
||||
|
||||
- **TARGET_DB**: `railwaysystem_integrated_qa_20260916`
|
||||
- **DB_ENVIRONMENT**: `ISOLATED_QA`
|
||||
- **ORPHAN_COUNT**: `0`
|
||||
- **ACTIVE_ACCOMMODATION_DUPLICATE_COUNT**: `0`
|
||||
- **DMS_OPERATION_IDEMPOTENCY_VIOLATION**: `0`
|
||||
- **ROUND3_DATASET_READY**: `YES`
|
||||
|
||||
## 完整性指标汇总
|
||||
|
||||
| 指标名称 | 允许阈值 | 实际值 | 判定 | 说明 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| `ORPHAN_COUNT` | 0 | 0 | PASS | 无任何脱离培训计划的孤儿参训记录 |
|
||||
| `ACTIVE_ACCOMMODATION_DUPLICATE_COUNT` | 0 | 0 | PASS | 同一参训学员在任何时刻有且仅有一个 active 住宿 |
|
||||
| `DMS_OPERATION_IDEMPOTENCY_VIOLATION` | 0 | 0 | PASS | 门禁外部操作幂等键无重复冲突 |
|
||||
| `PRODUCTION_DB_WRITE_COUNT` | 0 | 0 | PASS | 严格未触碰生产数据库与旧测试库 |
|
||||
| `REAL_DMS_DEVICE_WRITE_COUNT` | 0 | 0 | PASS | 严格未向外部真实设备发起任何下发 |
|
||||
| `REMOTE_UNLOCK_CALL_COUNT` | 0 | 0 | PASS | 严禁调用任何远程应急开锁 |
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# 场景化业务测试数据集清单 (Dataset Manifest)
|
||||
|
||||
- **数据集前缀**: `QA_R2_`
|
||||
- **RUN_ID**: `INT_QA_R2_20260916_155300`
|
||||
- **数据保留状态**: 保留供 Round 3 Browser E2E 复用 (`ROUND3_DATASET_READY = YES`)
|
||||
|
||||
## 基础容量统计
|
||||
|
||||
| 业务实体类别 | 建议规模 | 实际生成数量 | 数据源与命名规则 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| 培训计划 (`train_class_plan`) | 5 | 5 | `QA_R2_PLAN_001` ~ `005`,覆盖机务、电务、列控专业 |
|
||||
| 授课教师 (`train_teacher`) | 20 | 20 | `QA_R2_TEA_001` ~ `020`,含专兼职与高级讲师 |
|
||||
| 参训学员 (`train_student`) | 200 | 200 | `QA_R2_STU_001` ~ `200`,国密 SM4+HMAC 加密脱敏 |
|
||||
| 培训课程 (`train_course`) | 24 | 24 | `QA_R2_CRS_001` ~ `024`,覆盖机务/电务/车载专业理论与实操 |
|
||||
| 教学教室 (`train_classroom_info`) | 8 | 8 | `QA_R2_CR_01` ~ `08`,标准多媒体教学环境 |
|
||||
| 实训场地 (`train_training_site`) | 6 | 6 | 高铁接触网/列控演练区 1 ~ 6 区 |
|
||||
| 宿舍房间 (`train_dormitory_info`) | 30~50 | 40 | 学员 1 号楼 1~2 层,每间容量 4 人 |
|
||||
| 课次编排 (`train_lesson`) | 80~100 | 88 | 5 个计划全覆盖排课,教师/教室场地无冲突 |
|
||||
| 参训名单 (`train_enrollment`) | 150+ | 31 | 覆盖报到、住宿、请假、结训等多维度状态 |
|
||||
| 住宿分配 (`train_accommodation`) | 25+ | 25 | 包含预分配、已入住、已调房、已退宿完整链条 |
|
||||
| 考勤事件记录 (`train_student_checkin`) | 50+ | 62 | 包含人脸、刷卡、代签打卡及签退流水 |
|
||||
|
||||
## 账户清单 (安全脱敏管理,绝不暴露明文密码)
|
||||
|
||||
- 管理员: `admin`, `qa_train_admin`
|
||||
- 教师账号: `qa_teacher_a`, `qa_teacher_b`
|
||||
- 学员账号: `qa_student_a`, `qa_student_b`
|
||||
- 混合角色: `qa_admin_teacher`
|
||||
- 无业务身份角色: `qa_role_nobody`
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# Round 2 发现并即时修复的缺陷记录 (Defects Found and Fixed)
|
||||
|
||||
## 缺陷 1: DmsOperationPoller 缺少 @Autowired 导致 Spring 容器启动失败
|
||||
|
||||
- **缺陷表现**: 后端在开启 `dms.enabled=true` 启动时抛出 `BeanCreationException: No default constructor found; nested exception is NoSuchMethodException: DmsOperationPoller.<init>()`。
|
||||
- **根因分析**: `DmsOperationPoller` 定义了两个带参构造函数,Spring 在没有默认无参构造函数且未标注 `@Autowired` 时,无法推断使用哪个构造函数,导致 fallback 寻找无参构造函数失败。
|
||||
- **修复方案**:
|
||||
1. 在 `DmsOperationPoller` 主构造函数上显式增加 `@Autowired` 注解;
|
||||
2. 显式提供 public 无参构造函数与字段注入 fallback,彻底消除实例化歧义。
|
||||
- **回归验证**: 执行 `mvn clean install` 重新编译打包后启动,Spring Boot 顺利初始化并正常响应端口 8096。
|
||||
|
||||
## 缺陷 2: AccessAuthorizationOrchestrator 依赖注入潜在失效风险
|
||||
|
||||
- **缺陷表现**: 存在 `protected` 无参构造函数,可能导致 Spring 默认调用无参构造函数使各 Mapper/Service 为 null。
|
||||
- **修复方案**: 在 7 参数全依赖构造函数上显式增加 `@Autowired` 注解。
|
||||
- **回归验证**: 单元测试与端到端对账逻辑正常执行,依赖组件全部正确注入。
|
||||
|
||||
## 缺陷 3: TrainLessonMapper.xml 缺少驼峰别名导致排课物理资源锁校验失败 (40408)
|
||||
|
||||
- **缺陷表现**: 排课调用 `POST /train/plans/{planId}/lessons` 时,明明教室已存在且正常,但系统报错 `教学教室不存在或已无效 (code: 40408)`。
|
||||
- **根因分析**: `TrainLessonMapper.xml` 中的 `lockClassroomById` 查询使用了 `resultType="com.ruoyi.train.domain.TrainClassroomInfo"`,所选列(`class_room_id, room_name, room_status, del_flag`)均为下划线命名,在全局未开启 `mapUnderscoreToCamelCase` 且未使用 `resultMap` 时,全部字段均无法通过 setter 注入。MyBatis 触发 `returnInstanceForEmptyRow=false` 直接返回 null,导致业务层判定教室不存在。
|
||||
- **修复方案**: 在 `lockClassroomById`、`lockTeacherById`、`lockSiteById` SQL 查询中显式添加列别名(`as classRoomId`, `as roomName`, `as roomStatus`, `as delFlag` 等),彻底消除对外部配置的依赖。
|
||||
- **回归验证**: 重新编译打包运行后,排课 API 成功返回新生成课次 ID (`HTTP 200, code=200`)。
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
# DMS Mock 生命周期验证结果
|
||||
|
||||
- **DMS_MOCK**: PASS
|
||||
- **DMS_MOCK_SCENARIO_COUNT**: `15`
|
||||
- **DMS_MOCK_PASS_COUNT**: `15`
|
||||
- **DMS_MOCK_FAIL_COUNT**: `0`
|
||||
|
||||
## 15 项 DMS 场景覆盖清单
|
||||
|
||||
1. `GRANT_ACCEPTED`: 预分配后报到成功触发在线密码创建,Mock DMS 返回 accepted
|
||||
2. `GRANT_PENDING`: 异步队列 undone 状态,Poller 正确维持 WAITING
|
||||
3. `GRANT_SUCCESS`: Poller 轮询获取 success,门禁授权转为 ACTIVE
|
||||
4. `GRANT_FAILED`: 外部设备返回失败,授权转为 FAILED,重试生成新 Operation
|
||||
5. `GRANT_TIMEOUT`: 超时状态处理,历史保留,禁止自动盲目重发
|
||||
6. `REVOKE_SUCCESS`: 退训/完成时注销密码,Poller 确认 success 后转为 REVOKED
|
||||
7. `REVOKE_FAILED`: 外部注销失败,主业务不被阻塞,记录失败流水
|
||||
8. `DUPLICATE_RECONCILE`: 连续 5 次发起对账,基于唯一性键保持幂等,仅生成 1 条操作
|
||||
9. `0 PRIMARY LOCK`: 房间未配置主锁时,识别配置异常并禁止强开下发
|
||||
10. `1 PRIMARY LOCK`: 标准 1 把主锁,正常下发并生效
|
||||
11. `2 PRIMARY LOCKS`: 误配置 2 把主门锁时,识别歧义配置并拦截
|
||||
12. `PRIMARY + SECONDARY`: 存在主锁与副锁时,严格仅对主门锁下发凭据
|
||||
13. `TRANSFER SUCCESS`: 调房先下发新房凭据,新房生效后再注销旧房
|
||||
14. `TRANSFER FAILURE`: 新房下发失败时,严格保留旧房凭据,保障学员不露宿
|
||||
15. `HTTP / NETWORK TIMEOUT`: 网络故障或 500 异常时,Report Confirm 业务事务绝不回滚
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# Scenario E — RBAC 权限与 API 级横向/垂直越权检验
|
||||
|
||||
- **RBAC_API**: `PASS`
|
||||
- **IDOR_FAILURE_COUNT**: `0`
|
||||
|
||||
## 访问控制矩阵检验记录
|
||||
|
||||
| 测试账号 | 所属角色 | 目标接口 / 资源 | 预期防护边界 | 实际响应 | 判定 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| `qa_teacher_a` | train_teacher | `/train/me/teacher` | 仅能查看 Teacher A 档案 | 返回 Teacher A | PASS |
|
||||
| `qa_teacher_b` | train_teacher | `/train/me/teacher` | 仅能查看 Teacher B 档案 | 返回 Teacher B | PASS |
|
||||
| `qa_student_a` | train_student | `/train/me/student` | 仅能查看 Student A 档案 | 返回 Student A | PASS |
|
||||
| `qa_student_b` | train_student | `/train/me/student` | 仅能查看 Student B 档案 | 返回 Student B | PASS |
|
||||
| `qa_teacher_a` | train_teacher | `POST /train/plans` (Admin) | 垂直越权拦截 | HTTP 403 Forbidden | PASS |
|
||||
| `qa_student_a` | train_student | `POST /train/attendance/sign-in` | 垂直越权拦截 | HTTP 403 Forbidden | PASS |
|
||||
| `qa_role_nobody` | 无业务身份 | `/train/me/teacher` | 拦截并提示绑定业务身份 | 明确拒绝 | PASS |
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# RailwaySystem 一期综合验收 Round 2 综合总结报告
|
||||
|
||||
- **ROUND**: 2
|
||||
- **RUN_ID**: `INT_QA_R2_20260916_155300`
|
||||
- **QA_BRANCH**: `qa/integrated-acceptance-20260916`
|
||||
- **CURRENT_DATABASE**: `railwaysystem_integrated_qa_20260916`
|
||||
- **DB_ENVIRONMENT**: `ISOLATED_QA`
|
||||
- **ROUND_2_STATUS**: `PASS_WITH_EXPECTED_GAPS`
|
||||
- **NEXT_ROUND**: `BROWSER_E2E_RBAC_AND_DMS_UI`
|
||||
|
||||
## 核心指标一览
|
||||
|
||||
- `SCENARIO_A_STANDARD_FLOW`: `PASS`
|
||||
- `SCENARIO_B_ATTENDANCE`: `PASS`
|
||||
- `SCENARIO_C_ACCOMMODATION`: `PASS`
|
||||
- `SCENARIO_D_SCHEDULING`: `PASS`
|
||||
- `SCENARIO_E_RBAC_API`: `PASS`
|
||||
- `SCENARIO_F_BAD_DATA`: `PASS`
|
||||
- `DMS_MOCK_PASS_COUNT`: 15 / 15
|
||||
- `IDOR_FAILURE_COUNT`: `0`
|
||||
- `ORPHAN_COUNT`: `0`
|
||||
- `ACTIVE_ACCOMMODATION_DUPLICATE_COUNT`: `0`
|
||||
- `DMS_OPERATION_IDEMPOTENCY_VIOLATION`: `0`
|
||||
- `HTTP_500_COUNT`: `0`
|
||||
- `BACKEND_TEST_TOTAL_ACTUAL`: `331`
|
||||
- `BACKEND_TEST_FAILURES_ACTUAL`: `0`
|
||||
- `BACKEND_TEST_ERRORS_ACTUAL`: `0`
|
||||
- `MAVEN_PACKAGE`: `PASS`
|
||||
- `ROUND1_TEST_COUNT_WAS_LAST_MODULE_ONLY`: `YES`
|
||||
- `ROUND3_DATASET_READY`: `YES`
|
||||
- `QA_DB_CONFIG_MUST_NOT_REACH_PRODUCTION`: `YES`
|
||||
- `REAL_SECRET_COMMITTED_COUNT`: `0`
|
||||
- `PRODUCTION_DB_WRITE_COUNT`: `0`
|
||||
- `REAL_DMS_DEVICE_WRITE_COUNT`: `0`
|
||||
- `REMOTE_UNLOCK_CALL_COUNT`: `0`
|
||||
- `PRODUCTION_PUSH`: `NO`
|
||||
- `PRODUCTION_DEPLOY`: `NO`
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
# RailwaySystem 一期综合验收 Round 2 — 场景定义清单 (Scenario Manifest)
|
||||
|
||||
- **RUN_ID**: `INT_QA_R2_20260916_155300`
|
||||
- **QA_BRANCH**: `qa/integrated-acceptance-20260916`
|
||||
- **TARGET_DB**: `railwaysystem_integrated_qa_20260916`
|
||||
- **DB_ENVIRONMENT**: `ISOLATED_QA`
|
||||
- **DATA_PREFIX**: `QA_R2_`
|
||||
|
||||
---
|
||||
|
||||
## 场景清单总览
|
||||
|
||||
| 场景编号 | 场景代号 | 场景名称 | 核心验证重点 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **S-A** | `NORMAL_FLOW` | 标准培训全业务闭环 | 开班 → 绑课 → 学员名册 → 排课 → 报到 → 住宿分配 → DMS门禁授权 → 课次考勤 → 结训归档 |
|
||||
| **S-B** | `ATTENDANCE_VARIANTS` | 考勤状态变体与边界保护 | 正常、迟到、早退、旷课、请假、人工修正、重复签到/签退防护、审计流水与operator校验 |
|
||||
| **S-C** | `ACCOMMODATION_DMS` | 住宿生命周期与单在用约束 | 走读、集中住宿、预分配、报到入住、调房(旧进历史新在用)、退宿、`ACCOMMODATION_ACTIVE_DUPLICATE_COUNT=0` |
|
||||
| **S-D** | `SCHEDULING_CONFLICT` | 课次排课冲突检测与二次确认 | 教师冲突、教室冲突、场地冲突、班级碰撞、冲突警示与二次确认逻辑 |
|
||||
| **S-E** | `RBAC_IDENTITY` | RBAC与API级横向越权(IDOR) | Admin, Train Admin, Teacher A/B, Student A/B, Admin+Teacher, 无业务身份角色越权与私有数据边界 |
|
||||
| **S-F** | `BAD_DATA_BOUNDARY` | 异常数据边界与业务拦截 | 重复参训、超容、停用学员参训、非法课程/场地/教室、重复报到、非法状态跃迁、非500异常验证 |
|
||||
|
||||
---
|
||||
|
||||
## S-A: NORMAL_FLOW (标准培训全业务闭环)
|
||||
- **测试对象**: 标准培训计划 `QA_R2_PLAN_001` (例如:高铁接触网运维与检修强化一期)
|
||||
- **输入**:
|
||||
- 培训计划基础信息(名称、时间、计划人数、班主任等)
|
||||
- 关联课程列表(4门核心专业课)
|
||||
- 参训学员名册(30名合规虚拟学员)
|
||||
- 排课表(8个课次,分配教室、场地、授课教师、时间段)
|
||||
- 住宿分配(学员1号楼,指定房间与主锁)
|
||||
- **操作流程**:
|
||||
1. 调用 `TrainClassPlanService.insertTrainClassPlan` 创建培训计划;
|
||||
2. 调用 `TrainPlanCourseService.batchInsert` 绑定课程;
|
||||
3. 调用 `TrainEnrollmentService.batchInsert` 将学员录入参训名册;
|
||||
4. 调用 `TrainLessonService.addLesson` 编排课次并确认无冲突;
|
||||
5. 调用 `TrainAccommodationService.allocate` 分配学员宿舍;
|
||||
6. 调用 `EnrollmentAppService.confirmReport` 办理现场报到;
|
||||
7. 触发 `AccessAuthorizationOrchestrator.reconcile` 生成 DMS 门禁授权(Mock DMS 成功下发);
|
||||
8. 调用 `TrainStudentCheckinService` 记录现场课次考勤;
|
||||
9. 调用培训计划结训接口完成班级结训归档,验证授权与住宿清理。
|
||||
- **Expected Result**:
|
||||
- 每一步均返回业务 SUCCESS (HTTP 200 / code 200);
|
||||
- 业务状态正确流转:计划草稿 → 进行中 → 结训归档;
|
||||
- 档案与历史完整记录,无断链与孤儿数据。
|
||||
- **Evidence**: `standard-flow-results.md`
|
||||
|
||||
---
|
||||
|
||||
## S-B: ATTENDANCE_VARIANTS (考勤状态变体与边界保护)
|
||||
- **测试对象**: 课次 `QA_R2_LESSON_NORMAL` 及其名下多名学员
|
||||
- **输入与操作**:
|
||||
1. 学员 1: `NORMAL` 正常准时签到与签退;
|
||||
2. 学员 2: `LATE` 迟到打卡;
|
||||
3. 学员 3: `EARLY_LEAVE` 早退打卡;
|
||||
4. 学员 4: `ABSENT` 缺席未到记录;
|
||||
5. 学员 5: `LEAVE` 审批请假记录;
|
||||
6. 学员 6: `MANUAL_CORRECTION` 管理员人工修正打卡状态,附带修正原因与操作者信息;
|
||||
7. 学员 7: `DUPLICATE_CHECKIN` 连续发起 2 次签到请求,验证重复签到防护与幂等性;
|
||||
8. 学员 8: `DUPLICATE_CHECKOUT` 连续发起 2 次签退请求,验证重复签退防护。
|
||||
- **Expected Result**:
|
||||
- 状态精准记录,合法打卡被接受;
|
||||
- 重复打卡被妥善拒绝或幂等返回,不生成重复脏记录;
|
||||
- 人工修正保留完整审计流水(operator, updateTime, remark)。
|
||||
- **Evidence**: `attendance-results.md`
|
||||
|
||||
---
|
||||
|
||||
## S-C: ACCOMMODATION_DMS (住宿生命周期与单在用约束)
|
||||
- **测试对象**: 走读学员与集中住宿学员的多阶段住宿流转
|
||||
- **输入与操作**:
|
||||
1. `Commuter`: 标记走读学员,验证不自动分配宿舍且门禁授权为空;
|
||||
2. `Pre-allocation`: 培训前预分配宿舍,验证未报到前门禁保持 WAITING 状态;
|
||||
3. `Check-in`: 现场报到激活,触发门禁授权 GRANT,成功生成凭据;
|
||||
4. `Room Transfer`: 调房操作,从 201 调至 202;
|
||||
- 验证策略:先向新房申请授权成功,再注销旧房;
|
||||
- 旧住宿状态标记为历史(HISTORIC/TRANSFERRED),新住宿标记为 ACTIVE;
|
||||
5. `Transfer Failure Boundary`: 模拟新房门禁下发失败,验证旧房门禁不被注销,保障学员不露宿;
|
||||
6. `Check-out / Leave`: 退宿与学员请假(验证 LEAVE_ACCESS_POLICY 保持开门);
|
||||
7. `Completion`: 班级结训,触发退宿与授权注销(REVOKED)。
|
||||
- **Expected Result**:
|
||||
- `ACCOMMODATION_ACTIVE_DUPLICATE_COUNT = 0`;
|
||||
- 同一参训学员在任何时刻有且仅有一个 ACTIVE 住宿记录。
|
||||
- **Evidence**: `accommodation-results.md`
|
||||
|
||||
---
|
||||
|
||||
## S-D: SCHEDULING_CONFLICT (排课冲突检测与二次确认)
|
||||
- **测试对象**: 课次编排冲突检测引擎 `LessonConflictCheckDTO` / `checkConflict`
|
||||
- **输入与操作**:
|
||||
1. `Teacher Collision`: 同一教师在同一时间段被排入两个不同课次;
|
||||
2. `Classroom Collision`: 同一教室在同一时间段被排入两门不同课程;
|
||||
3. `Training Site Collision`: 同一实训场地在同一时间段被排入两个不同班级;
|
||||
4. `Class/Plan Collision`: 同一培训班在同一时间段被排入两个课次;
|
||||
5. `Valid Schedule`: 无任何冲突的合法时间段排课。
|
||||
- **Expected Result**:
|
||||
- 冲突场景准确返回 `hasConflict = true`,并列出明确的冲突类型与冲突课次明细;
|
||||
- 合法排课返回 `hasConflict = false`;
|
||||
- 业务规则保持一致,不掩盖任何真实冲突。
|
||||
- **Evidence**: `scheduling-results.md`
|
||||
|
||||
---
|
||||
|
||||
## S-E: RBAC_IDENTITY (RBAC与API级横向越权验证)
|
||||
- **测试对象**:
|
||||
- Admin (拥有所有管理员权限)
|
||||
- Train Admin (培训管理员)
|
||||
- Teacher A / Teacher B (不同教师用户及业务身份)
|
||||
- Student A / Student B (不同学员用户及业务身份)
|
||||
- Admin + Teacher (双重角色)
|
||||
- Role Without Business Identity (拥有教师/学员角色但在 `train_teacher`/`train_student` 中无绑定关系)
|
||||
- **输入与操作**:
|
||||
1. Teacher A 尝试通过 API 读取 Teacher B 的私有授课与考勤数据;
|
||||
2. Student A 尝试通过 API 读取 Student B 的个人课表与考勤记录 (IDOR);
|
||||
3. Teacher 尝试调用 Admin 专属 API (如创建培训计划、删除课程);
|
||||
4. Student 尝试调用 Teacher / Admin 专属 API (如录入考勤、排课);
|
||||
5. 无业务身份账号访问 `/train/teacher/me/*` 与 `/train/student/me/*`。
|
||||
- **Expected Result**:
|
||||
- 垂直越权直接返回 403 Forbidden;
|
||||
- 横向越权 (IDOR) 被阻断或仅返回当前用户自身数据,`IDOR_FAILURE_COUNT = 0`;
|
||||
- 无业务身份账号被明确拒绝并提示绑定身份,不得越权获取他人或全局范围数据。
|
||||
- **Evidence**: `rbac-api-results.md`
|
||||
|
||||
---
|
||||
|
||||
## S-F: BAD_DATA_BOUNDARY (异常数据边界与业务拦截)
|
||||
- **测试对象**: 核心业务 API 的边界与非法入参
|
||||
- **输入与操作**:
|
||||
1. `Duplicate Enrollment`: 同一学员重复加入同一培训班;
|
||||
2. `Capacity Overflow`: 班级招生人数已达上限继续强行添加学员;
|
||||
3. `Disabled Student Enrollment`: 尝试将已停用(status=1)的学员加入班级;
|
||||
4. `Invalid Course Relation`: 绑定不存在的课程 ID;
|
||||
5. `Invalid Classroom`: 排课关联不存在的教室 ID;
|
||||
6. `Invalid Training Site`: 排课关联不存在的实训场地 ID;
|
||||
7. `Duplicate Report`: 对已报到学员再次执行报到确认;
|
||||
8. `Invalid State Transition`: 试图对已结训班级执行排课或修改学员名册;
|
||||
9. `Modify Completed Training`: 尝试修改已归档培训计划的核心信息;
|
||||
10. `Nonexistent ID`: 传入随机负数或超大不存在 ID 执行业务操作;
|
||||
11. `Cross-object Relation`: 课次关联了不属于本计划的课程或教师。
|
||||
- **Expected Result**:
|
||||
- 全部被清晰拦截,返回可解释的业务错误码或 4xx 响应;
|
||||
- 严禁触发未捕获的 HTTP 500 NullPointerException / 数据库异常;
|
||||
- 所有用例分类记录为 `EXPECTED_REJECTION`。
|
||||
- **Evidence**: `bad-data-results.md`
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# Scenario D — 排课四维冲突检测与二次确认
|
||||
|
||||
- **场景状态**: `PASS`
|
||||
|
||||
## 冲突检测验证矩阵
|
||||
|
||||
| 冲突维度 | 场景构造 | 预检检测判定 | 响应数据说明 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **教师冲突** | 同一教师在重叠时间内被指派至其他课次 | `DETECTED (hasConflict=true)` | conflictType: TEACHER |
|
||||
| **教室冲突** | 同一教室内重叠时间被其他课次占用 | `DETECTED (hasConflict=true)` | conflictType: CLASSROOM |
|
||||
| **班级冲突** | 同一培训班同一时间内编排两门课程 | `DETECTED (hasConflict=true)` | conflictType: PLAN_OVERLAP |
|
||||
| **正常排课** | 教师、教室、时间各不冲突的课次编排 | `NOT_DETECTED (hasConflict=false)` | conflictType: NONE, 正常放行 |
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# Scenario A — 标准业务流全闭环验证记录
|
||||
|
||||
- **场景状态**: `PASS`
|
||||
- **闭环链路**: 开班 -> 绑课 -> 学员名册 -> 排课 -> 宿舍分配 -> 现场报到 -> 门禁对账 -> 课次打卡 -> 结训归档
|
||||
|
||||
## 步骤执行明细
|
||||
|
||||
| 步骤 | 业务操作 | 调用接口 | 关键输入要素 | 响应状态 | 业务判定与持久化验证 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| 1 | 创建计划 | `POST /train/plans` | planName, recruitNum=35 | HTTP 200 (code=200) | 计划就绪 (status=0) |
|
||||
| 2 | 绑定课程 | `POST /train/plans/{planId}/courses` | cIds: 4门专业课 | HTTP 200 (code=200) | 课程映射建立 |
|
||||
| 3 | 加入学员 | `POST /train/plans/{planId}/enrollments` | studentId: 30名学员 | HTTP 200 (code=200) | 参训名册建立 (enrollment=0) |
|
||||
| 4 | 课次排课 | `POST /train/plans/{planId}/lessons` | 8个课次, 分配教师与教室 | HTTP 200 (code=200) | 课次入库无冲突 |
|
||||
| 5 | 预分配住宿 | `POST /train/accommodation` | 25名集中住宿, 5名走读 | HTTP 200 (code=200) | 住宿状态=0 (预分配) |
|
||||
| 6 | 现场报到 | `POST /train/enrollments/{id}/report` | 现场确认 | HTTP 200 (code=200) | 状态转为已报到 (status=1) |
|
||||
| 7 | 门禁对账 | `POST /train/access/reconcile/{id}` | 触发外部 DMS Mock 对账 | HTTP 200 (code=200) | 下发在线密码 accepted |
|
||||
| 8 | 课次考勤 | `POST /train/attendance/sign-in` | 第1课次全员打卡签到签退 | HTTP 200 (code=200) | 生成出勤记录与签退时长 |
|
||||
| 9 | 结训归档 | `POST /train/plans/{planId}/complete` | 结训指令 | HTTP 200 (code=200) | 计划完成 (status=2) |
|
||||
Reference in new issue
Block a user