Files
railwaysystem/docs/visual/testing.html
T

637 lines
32 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>质量与验证体系 - RailwaySystem SYFJGYD学员管理系统</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%231d4ed8' d='M12 2c-4 0-8 .5-8 4v9.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h2.23l2-2H14l2 2h2v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V6c0-3.5-3.58-4-8-4z'/></svg>">
<link rel="stylesheet" href="assets/docs.css">
<style>
/* 测试金字塔层级展示 */
.pyramid-wrap {
background: #ffffff;
border: 1px solid var(--border-light);
border-radius: var(--radius-lg);
padding: 2rem;
box-shadow: var(--shadow-card);
margin-bottom: 2.5rem;
}
.pyramid-layers {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 860px;
margin: 1.5rem auto 0;
}
.pyramid-tier {
border: 1px solid var(--border-light);
border-radius: var(--radius-md);
padding: 1rem 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.pyramid-tier:hover {
transform: translateX(4px);
box-shadow: var(--shadow-card-hover);
}
.tier-4 { background: #eff6ff; border-color: #bfdbfe; margin: 0 12%; }
.tier-3 { background: #f0fdf4; border-color: #bbf7d0; margin: 0 8%; }
.tier-2 { background: #fefce8; border-color: #fef08a; margin: 0 4%; }
.tier-1 { background: #f8fafc; border-color: #e2e8f0; margin: 0 0; }
.tier-info h3 {
font-size: 0.98rem;
font-weight: 700;
color: var(--navy-900);
display: flex;
align-items: center;
gap: 0.5rem;
}
.tier-info p {
font-size: 0.82rem;
color: var(--text-secondary);
margin-top: 0.2rem;
}
/* Scenario 卡片网格 */
.scenario-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
@media (max-width: 900px) {
.scenario-grid { grid-template-columns: 1fr; }
.tier-4, .tier-3, .tier-2, .tier-1 { margin: 0; }
}
.scenario-card {
background: #ffffff;
border: 1px solid var(--border-light);
border-radius: var(--radius-md);
padding: 1.5rem;
box-shadow: var(--shadow-card);
display: flex;
flex-direction: column;
}
.scenario-card:hover {
border-color: var(--border-medium);
box-shadow: var(--shadow-card-hover);
}
.scenario-point-list {
list-style: none;
margin: 0.85rem 0;
flex: 1;
}
.scenario-point-item {
display: flex;
align-items: flex-start;
gap: 0.5rem;
padding: 0.35rem 0;
font-size: 0.85rem;
color: var(--text-secondary);
line-height: 1.6;
}
.scenario-point-item::before {
content: '✓';
color: var(--pass-color);
font-weight: 700;
flex-shrink: 0;
}
/* 浏览器验收卡片网格 */
.browser-category-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.25rem;
}
@media (max-width: 960px) {
.browser-category-grid { grid-template-columns: 1fr; }
}
.browser-card {
background: #ffffff;
border: 1px solid var(--border-light);
border-radius: var(--radius-md);
padding: 1.25rem;
box-shadow: var(--shadow-card);
}
/* Cleanup 口径对比 */
.cleanup-box {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.25rem;
margin-top: 1rem;
}
@media (max-width: 768px) {
.cleanup-box { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<!-- 顶部主导航 -->
<nav class="navbar">
<div class="nav-brand-wrap">
<a href="index.html" class="nav-brand">
<svg viewBox="0 0 24 24"><path d="M12 2c-4 0-8 .5-8 4v9.5C4 17.43 5.57 19 7.5 19L6 20.5v.5h2.23l2-2H14l2 2h2v-.5L16.5 19c1.93 0 3.5-1.57 3.5-3.5V6c0-3.5-3.58-4-8-4zM7.5 17c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm9 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6H6V6h12v5z"/></svg>
<div>
<div class="brand-title">RailwaySystem</div>
<div class="brand-sub">SYFJGYD学员管理系统</div>
</div>
</a>
<span class="brand-pill">Final Candidate · 待正式部署</span>
</div>
<ul class="nav-menu">
<li><a href="index.html" class="nav-link">首页</a></li>
<li><a href="business-flow.html" class="nav-link">业务</a></li>
<li><a href="architecture.html" class="nav-link">系统</a></li>
<li><a href="testing.html" class="nav-link active">质量</a></li>
<li><a href="release-status.html" class="nav-link">发布</a></li>
<li><a href="modules.html" class="nav-link">14大业务模块清单</a></li>
<li><a href="data-model.html" class="nav-link">实体关系模型(ER)</a></li>
<li><a href="deployment.html" class="nav-link">部署拓扑与脚本Delta</a></li>
<li><a href="http://192.168.139.218:9980/sict/railwaysystem/-/blob/feat/cicd-pipeline/docs/handbook/08-deployment-and-release.md" target="_blank" rel="noopener noreferrer" class="nav-handbook-link">&gt; 查阅部署发布手册</a></li>
<li><a href="http://192.168.139.218:9980/sict/railwaysystem/-/blob/feat/cicd-pipeline/docs/README.md" target="_blank" rel="noopener noreferrer" class="nav-back-link">&larr; 文档中心</a></li>
</ul>
</nav>
<!-- 页面主体容器 -->
<main class="page-container">
<header class="page-hero">
<div class="hero-pretitle">Quality Assurance & Verification</div>
<h1 class="hero-title-main">质量指标与全层级验证矩阵</h1>
<p class="hero-lead-text">
严格基于 <code>scripts/qa/core-business-scenario-runner.py</code>、<code>docs/_meta/core-business-test-matrix.md</code> 及真实浏览器端到端验收报告生成。杜绝任何未经代码或测试证实的扩写。
</p>
</header>
<!-- 1. 顶部真实指标 Dashboard (7个精准真实KPI) -->
<div class="kpi-dashboard">
<div class="kpi-card kpi-pass">
<div class="kpi-value">331 / 331</div>
<div class="kpi-label">后端单元与集成测试</div>
<div class="kpi-sub">2026-09-17一期综合验收基线:331项后端测试通过</div>
</div>
<div class="kpi-card kpi-pass">
<div class="kpi-value">6 / 6</div>
<div class="kpi-label">核心业务场景套件</div>
<div class="kpi-sub">SC-01 ~ SC-06 全部通过</div>
</div>
<div class="kpi-card kpi-info">
<div class="kpi-value">110</div>
<div class="kpi-label">真实 HTTP API 探测</div>
<div class="kpi-sub">自动化请求全量记录</div>
</div>
<div class="kpi-card kpi-pass">
<div class="kpi-value">0</div>
<div class="kpi-label">服务端 500 异常</div>
<div class="kpi-sub">HTTP 500 Count = 0</div>
</div>
<div class="kpi-card kpi-pass">
<div class="kpi-value">11 / 11</div>
<div class="kpi-label">Kingbase 持久化断言</div>
<div class="kpi-sub">实体与外键完整性无孤儿</div>
</div>
<div class="kpi-card kpi-pass">
<div class="kpi-value">PASS</div>
<div class="kpi-label">浏览器真实 UI 验收</div>
<div class="kpi-sub">Final Browser Acceptance</div>
</div>
<div class="kpi-card kpi-pass">
<div class="kpi-value">0</div>
<div class="kpi-label">控制台阻断错误</div>
<div class="kpi-sub">Console Error = 0</div>
</div>
</div>
<!-- 2. 测试金字塔 (Testing Pyramid) -->
<section class="content-section">
<div class="section-header">
<div>
<h2 class="section-title">分层质量防御金字塔</h2>
<p class="section-subtitle">从离线单元逻辑、跨模块端到端集成、物理数据库落库校验到浏览器界面交互的四级防护体系。</p>
</div>
</div>
<div class="pyramid-wrap">
<div class="pyramid-layers">
<!-- 顶层: 浏览器验收 -->
<div class="pyramid-tier tier-4">
<div class="tier-info">
<h3><span>第 4 层:真实浏览器端到端验收 (Final Browser Acceptance)</span> <span class="badge badge-pass">PASS</span></h3>
<p>真实浏览器端到端交互:登录、表单建档、计划排课、冲突 Warning 弹窗确认、报到确认及 Web 班牌。</p>
</div>
<span class="card-code">Console Error: 0</span>
</div>
<!-- 第 3 层: 数据库持久化只读断言 -->
<div class="pyramid-tier tier-3">
<div class="tier-info">
<h3><span>第 3 层:Kingbase 数据库落库只读断言 (DB Assertions)</span> <span class="badge badge-pass">PASS</span></h3>
<p>11 项物理 SQL 只读断言:外键无孤儿记录、场地 XOR 互斥、逻辑删除标志、结训完成时间及退训防破坏保护。</p>
</div>
<span class="card-code">11 / 11 PASS</span>
</div>
<!-- 第 2 层: 场景集成测试套件 -->
<div class="pyramid-tier tier-2">
<div class="tier-info">
<h3><span>第 2 层:核心业务场景集成测试 (SC-01 ~ SC-06)</span> <span class="badge badge-pass">PASS</span></h3>
<p>Python 自动化套件覆盖正向全链路、报名拦截、排课预检 Warning/Block、班牌降级、结训事务及报到考勤闭环。</p>
</div>
<span class="card-code">6 / 6 PASS · 110 Requests</span>
</div>
<!-- 底层: 单元与契约测试 -->
<div class="pyramid-tier tier-1">
<div class="tier-info">
<h3><span>第 1 层:后端单元与集成测试 (Backend Unit / Integration)</span> <span class="badge badge-pass">PASS</span></h3>
<p>Maven Surefire 全量测试集,涵盖状态机流转、排课冲突算法、脱敏工具类与权限注解断言。</p>
</div>
<span class="card-code">331 PASS (2026-09-17验收基线)</span>
</div>
</div>
</div>
</section>
<!-- 3. SC-01 ~ SC-06 场景测试网格 (精简密度:3个关键事实 + 折叠详情) -->
<section class="content-section">
<div class="section-header">
<div>
<h2 class="section-title">核心业务场景集成测试 (SCENARIO-01 ~ SCENARIO-06)</h2>
<p class="section-subtitle">严格对齐 <code>scripts/qa/core-business-scenario-runner.py</code> 真实代码逻辑,默认呈现关键结论,点击可查阅详细步骤。</p>
</div>
</div>
<div class="scenario-grid">
<!-- SC-01 -->
<div class="scenario-card">
<div class="card-top">
<span class="badge badge-pass">SCENARIO-01 · PASS</span>
<span class="card-code">Happy Path</span>
</div>
<h3 class="card-title">完整正向业务闭环全链</h3>
<ul class="scenario-point-list">
<li class="scenario-point-item"><strong>基础资源就绪</strong>:成功创建 Teacher、Course、Classroom 及 TrainingSite。</li>
<li class="scenario-point-item"><strong>开班排课全链</strong>:拟定计划、绑定课程师资、学员入班待报到、课表聚合正常。</li>
<li class="scenario-point-item"><strong>自动结训完成</strong>:计划流转至已结束,活动学员自动结训完成并写入 complete_time。</li>
</ul>
<details class="rule-collapse" style="margin-top: 0.6rem;">
<summary style="font-size: 0.78rem; color: var(--rail-blue); cursor: pointer; font-weight: 600;">展开 15 步详细交互验证</summary>
<div class="rule-content" style="padding-top: 0.4rem; font-size: 0.78rem; color: var(--text-secondary); line-height: 1.6;">
<div>· 开班计划采用 32 位 UUID 主键,初始状态为 0:草稿。</div>
<div>· Timetable 课表接口准确聚合课程名称、授课教师及场地名。</div>
<div>· 调用 <code>POST /train/plans/{id}/complete</code> 强事务批量流转状态。</div>
</div>
</details>
<div class="card-footer-action">
<span class="card-code">覆盖 15 步交互验证</span>
<span class="badge badge-pass">验证通过</span>
</div>
</div>
<!-- SC-02 -->
<div class="scenario-card">
<div class="card-top">
<span class="badge badge-pass">SCENARIO-02 · PASS</span>
<span class="card-code">Enrollment Rules</span>
</div>
<h3 class="card-title">学员报名异常规则与状态区分</h3>
<ul class="scenario-point-list">
<li class="scenario-point-item"><strong>重复报名拦截</strong>:同一计划重复报名触发唯一索引,准确返回 <code>40030</code>。</li>
<li class="scenario-point-item"><strong>容量超限拦截</strong>:入班人数超过计划 <code>recruit_num</code> 上限时拦截返回 <code>40031</code>。</li>
<li class="scenario-point-item"><strong>状态严格区分</strong>:学员主档状态 (正常/停用) 与参训状态机独立解耦流转。</li>
</ul>
<details class="rule-collapse" style="margin-top: 0.6rem;">
<summary style="font-size: 0.78rem; color: var(--rail-blue); cursor: pointer; font-weight: 600;">展开异常校验与拦截细节</summary>
<div class="rule-content" style="padding-top: 0.4rem; font-size: 0.78rem; color: var(--text-secondary); line-height: 1.6;">
<div>· 不存在的非法学员严格拒绝入班。</div>
<div>· 停用学员 (status=0) 严禁报名入班。</div>
<div>· 参训状态机严格维护 0待报到/1已报到/2请假/3退训/4已完成。</div>
</div>
</details>
<div class="card-footer-action">
<span class="card-code">业务错误码精准契约</span>
<span class="badge badge-pass">验证通过</span>
</div>
</div>
<!-- SC-03 -->
<div class="scenario-card">
<div class="card-top">
<span class="badge badge-pass">SCENARIO-03 · PASS</span>
<span class="card-code">Scheduling Engine</span>
</div>
<h3 class="card-title">排课预检 Warning 与 Hard Block 硬阻断</h3>
<ul class="scenario-point-list">
<li class="scenario-point-item"><strong>四维冲突预检</strong>:教师冲突硬阻断;教室/场地/班级重叠返回 Warning 提示。</li>
<li class="scenario-point-item"><strong>非阻断合规</strong>:Warning 级别由教务员确认保存,严格禁止 force 旁路参数。</li>
<li class="scenario-point-item"><strong>互斥与锁定</strong>:场地 XOR 互斥强校验;已结训计划强阻断变更 (<code>40012</code>)。</li>
</ul>
<details class="rule-collapse" style="margin-top: 0.6rem;">
<summary style="font-size: 0.78rem; color: var(--rail-blue); cursor: pointer; font-weight: 600;">展开预检机制与阻断契约</summary>
<div class="rule-content" style="padding-top: 0.4rem; font-size: 0.78rem; color: var(--text-secondary); line-height: 1.6;">
<div>· 时间倒挂阻断:startTime >= endTime 拦截返回 <code>40010</code>。</div>
<div>· 教室与场地同时为空或同时赋值强行拦截,DB 层面 <code>invalid venue XOR = 0</code>。</div>
<div>· 已结训计划禁止排课变更,返回 <code>40012: PLAN_LOCKED</code>。</div>
</div>
</details>
<div class="card-footer-action">
<span class="card-code">无 force 旁路安全参数</span>
<span class="badge badge-pass">验证通过</span>
</div>
</div>
<!-- SC-04 -->
<div class="scenario-card">
<div class="card-top">
<span class="badge badge-pass">SCENARIO-04 · PASS</span>
<span class="card-code">Electronic Board</span>
</div>
<h3 class="card-title">电子班牌绑定与课表降级映射</h3>
<ul class="scenario-point-list">
<li class="scenario-point-item"><strong>UUID 主键规范</strong>:班牌创建自动生成 32 位 UUID,默认在线状态 <code>0</code>。</li>
<li class="scenario-point-item"><strong>课表降级规则</strong>:0 块板为 null,恰好 1 块板返回对应 SN,多块板降级为 null。</li>
<li class="scenario-point-item"><strong>逻辑删除保障</strong>:调用删除置 <code>del_flag='2'</code>,业务接口隔离且物理数据保留。</li>
</ul>
<details class="rule-collapse" style="margin-top: 0.6rem;">
<summary style="font-size: 0.78rem; color: var(--rail-blue); cursor: pointer; font-weight: 600;">展开教室绑定与降级细节</summary>
<div class="rule-content" style="padding-top: 0.4rem; font-size: 0.78rem; color: var(--text-secondary); line-height: 1.6;">
<div>· 教室详情接口:0 块板返回空数组,1 块板返回长度 1,N 块板返回长度 N。</div>
<div>· 课表 boardSn 降级消除多设备展示歧义。</div>
<div>· 物理库 SQL 断言确认 <code>del_flag='2'</code> 匹配成功。</div>
</div>
</details>
<div class="card-footer-action">
<span class="card-code">板号降级语义实测</span>
<span class="badge badge-pass">验证通过</span>
</div>
</div>
<!-- SC-05 -->
<div class="scenario-card">
<div class="card-top">
<span class="badge badge-pass">SCENARIO-05 · PASS</span>
<span class="card-code">Plan Completion</span>
</div>
<h3 class="card-title">结训生命周期与退训学员保护</h3>
<ul class="scenario-point-list">
<li class="scenario-point-item"><strong>计划结训 API</strong>:调用结训接口,Plan 状态成功流转为 <code>2:已结束</code>。</li>
<li class="scenario-point-item"><strong>活动学员结训</strong>:待报到/已报到学员批量流转为 <code>4:已完成</code> 并记录时间。</li>
<li class="scenario-point-item"><strong>退训学员防覆盖</strong>:已退训学员 (<code>status=3</code>) 状态与完成时间受严格保护。</li>
</ul>
<details class="rule-collapse" style="margin-top: 0.6rem;">
<summary style="font-size: 0.78rem; color: var(--rail-blue); cursor: pointer; font-weight: 600;">展开核心不变量与事务保障</summary>
<div class="rule-content" style="padding-top: 0.4rem; font-size: 0.78rem; color: var(--text-secondary); line-height: 1.6;">
<div>· 接口契约:<code>POST /train/plans/{id}/complete</code> 强事务批量更新。</div>
<div>· 核心不变量:已退训记录 complete_time 严格保持为 null。</div>
<div>· 物理 SQL 断言证实历史退训记录零覆盖、零损坏。</div>
</div>
</details>
<div class="card-footer-action">
<span class="card-code">强事务一致性保护</span>
<span class="badge badge-pass">验证通过</span>
</div>
</div>
<!-- SC-06 -->
<div class="scenario-card">
<div class="card-top">
<span class="badge badge-pass">SCENARIO-06 · PASS</span>
<span class="card-code">Report & Attendance</span>
</div>
<h3 class="card-title">现场报到与课堂考勤完整闭环</h3>
<ul class="scenario-point-list">
<li class="scenario-point-item"><strong>现场报到确认</strong>:调用报到接口返回 reportId,参训状态流转为 <code>1:已报到</code>。</li>
<li class="scenario-point-item"><strong>考勤打卡幂等</strong>:首次记录打卡时间,重复签到签退严格保持初次时间不变。</li>
<li class="scenario-point-item"><strong>人工代签留痕</strong>:后台调整考勤自动置 <code>manual_flag = 1</code> 留存审计线索。</li>
</ul>
<details class="rule-collapse" style="margin-top: 0.6rem;">
<summary style="font-size: 0.78rem; color: var(--rail-blue); cursor: pointer; font-weight: 600;">展开接口与重复拦截细节</summary>
<div class="rule-content" style="padding-top: 0.4rem; font-size: 0.78rem; color: var(--text-secondary); line-height: 1.6;">
<div>· 报到确认接口:<code>POST /train/enrollments/{id}/report</code> 强事务写入。</div>
<div>· 重复报到防范:对已报到学员再次报到触发拦截返回 <code>40050</code>。</div>
<div>· 人工考勤接口:<code>PUT /train/attendance/{id}</code> 记录调整人与备注。</div>
</div>
</details>
<div class="card-footer-action">
<span class="card-code">幂等性与审计留痕</span>
<span class="badge badge-pass">验证通过</span>
</div>
</div>
</div>
</section>
<!-- 4. 浏览器端到端验收分类卡片 (真实客观实测事实) -->
<section class="content-section">
<div class="section-header">
<div>
<h2 class="section-title">真实浏览器端到端交互验收 (Final Browser Acceptance)</h2>
<p class="section-subtitle">基于真实运行的端到端验收套件客观记录,据实描述界面打开、列表查询、表单验证与接口调用结果。</p>
</div>
</div>
<div class="browser-category-grid">
<!-- 分类 1: 基础主数据 -->
<div class="browser-card">
<div class="card-top">
<h3 class="card-title" style="font-size: 0.95rem;">基础主数据管理</h3>
<span class="badge badge-pass">PASS</span>
</div>
<ul style="padding-left: 1.15rem; font-size: 0.82rem; line-height: 1.75; color: var(--text-secondary); margin-top: 0.5rem;">
<li><strong>Teacher 教师</strong>:页面成功打开、列表查询、手机号唯一性强校验生效。</li>
<li><strong>Student 学员</strong>:建档表单校验通过,身份证末4位脱敏掩码展示正常。</li>
<li><strong>Classroom 教室</strong>:理论教室容量与状态设置正常渲染。</li>
<li><strong>TrainingSite 场地</strong>:实训场地类型与容量管理正常。</li>
</ul>
</div>
<!-- 分类 2: 核心教务编排 -->
<div class="browser-card">
<div class="card-top">
<h3 class="card-title" style="font-size: 0.95rem;">核心教务编排交互</h3>
<span class="badge badge-pass">PASS</span>
</div>
<ul style="padding-left: 1.15rem; font-size: 0.82rem; line-height: 1.75; color: var(--text-secondary); margin-top: 0.5rem;">
<li><strong>Plan 培训计划</strong>:拟定 32 位 UUID 计划,生命周期初始为草稿。</li>
<li><strong>PlanCourse 课程绑定</strong>:课程选择与师资指派正常,总学时联动累加。</li>
<li><strong>Enrollment 学员报名</strong>:入班名单展示、招生容量上限校验拦截生效。</li>
<li><strong>Timetable 可视化排课</strong>:课表渲染,冲突 Warning 取消/确认入库均正常。</li>
</ul>
</div>
<!-- 分类 3: 现场执行与系统工具 -->
<div class="browser-card">
<div class="card-top">
<h3 class="card-title" style="font-size: 0.95rem;">现场执行与系统工具</h3>
<span class="badge badge-pass">PASS</span>
</div>
<ul style="padding-left: 1.15rem; font-size: 0.82rem; line-height: 1.75; color: var(--text-secondary); margin-top: 0.5rem;">
<li><strong>开班报到确认</strong>:前端菜单「开班签到」,实际调用 <code>/report</code> 接口 200 OK,状态由待报到流转为已报到。</li>
<li><strong>电子班牌终端</strong>:前端物联路由 <code>/equip/board</code> 页面正常渲染。</li>
<li><strong>统计分析 (Analytics)</strong>:报到、考勤、学时、画像报表只读查询正常。</li>
<li><strong>系统接口平台 (Swagger)</strong>:<code>系统工具 → 系统接口</code> 在线渲染,<code>v3/api-docs</code> 返回 200。</li>
</ul>
</div>
</div>
</section>
<!-- 5. 数据清理口径澄清 (严格区分两种测试) -->
<section class="content-section">
<div class="section-header">
<div>
<h2 class="section-title">测试数据清理与审计口径规范</h2>
<p class="section-subtitle">严格区分自动化集成测试与浏览器真实验收的数据保留策略,杜绝将两者混写。</p>
</div>
</div>
<div class="cleanup-box">
<div class="standard-card" style="border-left: 4px solid var(--pass-color);">
<div class="card-top">
<h3 class="card-title" style="font-size: 0.98rem;">自动化集成测试清理 (Integration Cleanup)</h3>
<span class="badge badge-pass">0 残留 (Residue = 0)</span>
</div>
<p class="card-body-text">
<code>core-business-scenario-runner.py</code> 在测试执行过程中自动记录所创建的精确 ID 资源清单 (<code>created-resources.json</code>)。测试断言结束后,严格按逆序调用解绑与清理机制,实现:
<br>· 课次、参训名单、电子班牌精确删除;
<br>· <strong>自动化测试产生的数据残留为 0,保持数据库纯净</strong>。
</p>
</div>
<div class="standard-card" style="border-left: 4px solid var(--pending-color);">
<div class="card-top">
<h3 class="card-title" style="font-size: 0.98rem;">浏览器端到端验收数据 (Browser Acceptance Data)</h3>
<span class="badge badge-pending">保留供审计 (KEEP_FOR_AUDIT)</span>
</div>
<p class="card-body-text">
在最终浏览器真实 UI 验收中创建及流转的测试学员、开班计划、报到流水及代签考勤记录,<strong>显式全量保留在数据库中</strong>,供发布审查、合规抽检及操作日志审计核对,<strong>非 0 残留</strong>。
</p>
</div>
</div>
</section>
<!-- 6. 技术细节折叠手风琴 (Kingbase SQL 物理断言) -->
<section class="content-section" style="margin-bottom: 0;">
<div class="section-header">
<div>
<h2 class="section-title">技术底层细节与物理 SQL 断言 (展开查阅)</h2>
<p class="section-subtitle">由 DbAssertionChecker 直接在 Kingbase 物理库上执行的只读一致性核查。</p>
</div>
</div>
<div class="accordion-item">
<div class="accordion-header">
<div class="accordion-title-group">
<span class="badge badge-pass">11 项 SQL 断言通过</span>
<span style="font-size: 0.95rem; font-weight: 600; color: var(--navy-900);">查看 Kingbase 物理库只读一致性断言清单</span>
</div>
<svg class="accordion-arrow" viewBox="0 0 20 20"><path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"/></svg>
</div>
<div class="accordion-body">
<div class="data-table-wrap">
<table class="data-table">
<thead>
<tr>
<th>断言项目</th>
<th>验证逻辑与 SQL 条件</th>
<th>预期值</th>
<th>实测结果</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>duplicate enrollment</strong></td>
<td>校验同一计划内是否存在相同学员的重复报名记录</td>
<td><code>0</code></td>
<td><span class="badge badge-pass">PASS (0)</span></td>
</tr>
<tr>
<td><strong>orphan lesson</strong></td>
<td>校验是否存在未关联有效计划课程的孤立课次</td>
<td><code>0</code></td>
<td><span class="badge badge-pass">PASS (0)</span></td>
</tr>
<tr>
<td><strong>orphan enrollment</strong></td>
<td>校验是否存在未关联有效培训计划的孤立报名档案</td>
<td><code>0</code></td>
<td><span class="badge badge-pass">PASS (0)</span></td>
</tr>
<tr>
<td><strong>invalid venue XOR</strong></td>
<td>校验课次中教室与实训场地二选一互斥,严禁同时为空或同时设置</td>
<td><code>0</code></td>
<td><span class="badge badge-pass">PASS (0)</span></td>
</tr>
<tr>
<td><strong>del_flag = '2'</strong></td>
<td>电子班牌逻辑删除后底层标志位规范设置为 '2'</td>
<td><code>MATCH</code></td>
<td><span class="badge badge-pass">PASS</span></td>
</tr>
<tr>
<td><strong>Plan completion 状态联动</strong></td>
<td>结训后学员参训状态正确流转至 4 且 complete_time 真实写入</td>
<td><code>status=4, time!=null</code></td>
<td><span class="badge badge-pass">PASS</span></td>
</tr>
<tr>
<td><strong>历史退训保护</strong></td>
<td>已退训记录未被结训批量任务修改覆盖,complete_time 保持为 null</td>
<td><code>status=3, time=null</code></td>
<td><span class="badge badge-pass">PASS</span></td>
</tr>
<tr>
<td><strong>orphan report</strong></td>
<td>校验是否存在未关联有效报名记录的孤立报到流水</td>
<td><code>0</code></td>
<td><span class="badge badge-pass">PASS (0)</span></td>
</tr>
<tr>
<td><strong>orphan checkin</strong></td>
<td>校验是否存在未关联有效课次或报名的孤立考勤打卡</td>
<td><code>0</code></td>
<td><span class="badge badge-pass">PASS (0)</span></td>
</tr>
<tr>
<td><strong>manual_flag = 1</strong></td>
<td>人工修正考勤记录自动置审计标志位 manual_flag = 1</td>
<td><code>1</code></td>
<td><span class="badge badge-pass">PASS (1)</span></td>
</tr>
<tr>
<td><strong>report enrollment status</strong></td>
<td>报到流水关联的报名记录状态必须为 1(已报到) 或 4(已结训完成)</td>
<td><code>status IN (1, 4)</code></td>
<td><span class="badge badge-pass">PASS</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
</main>
<!-- 页脚 -->
<footer class="site-footer">
<p>RailwaySystem SYFJGYD学员管理系统 · 共享试用就绪 (Shared Trial Ready)</p>
<p style="margin-top: 0.35rem; color: var(--text-muted);">
质量门禁与测试报告 · <a href="../reports/integrated-acceptance/FINAL_PHASE1_20260917_101044/build-test-final.md">查阅综合测试终检报告</a> · <a href="../reports/integrated-acceptance/FINAL_PHASE1_20260917_101044/browser-e2e-final.md">查阅浏览器端到端报告</a>
</p>
</footer>
<script src="assets/docs.js"></script>
</body>
</html>