Files
railwaysystem/docs/visual/data-model.html
T

659 lines
36 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>核心数据模型与实体关系 (ER) - 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>
/* ER 关系图视觉容器 */
.er-canvas-card {
background: #ffffff;
border: 1px solid var(--border-light);
border-radius: var(--radius-lg);
padding: 2rem;
box-shadow: var(--shadow-card);
margin-bottom: 2.5rem;
}
.er-svg-wrap {
width: 100%;
overflow-x: auto;
padding: 0.5rem 0;
}
.er-svg {
min-width: 960px;
width: 100%;
height: auto;
display: block;
}
.er-entity {
cursor: pointer;
transition: all 0.15s ease;
}
.er-entity:hover {
filter: drop-shadow(0 4px 10px rgba(30, 64, 175, 0.2));
}
.er-entity:hover rect.entity-box {
stroke: var(--rail-blue);
stroke-width: 2px;
}
/* 实体网格卡片 */
.entity-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.25rem;
margin-top: 1.5rem;
}
.entity-card {
background: #ffffff;
border: 1px solid var(--border-light);
border-radius: var(--radius-md);
padding: 1.25rem;
box-shadow: var(--shadow-card);
cursor: pointer;
transition: all 0.2s ease;
display: flex;
flex-direction: column;
}
.entity-card:hover {
border-color: var(--border-medium);
box-shadow: var(--shadow-card-hover);
transform: translateY(-2px);
}
</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">质量</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 active">实体关系模型(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">Entity Relationship & Persistence</div>
<h1 class="hero-title-main">核心业务实体关系模型 (ER)</h1>
<p class="hero-lead-text">
聚焦于 10 大核心教务实体的关联拓扑与物理约束。杜绝平铺冗长数据库大表,点击任意实体卡片或图谱节点即可查看物理表名、PK主键生成机制、外键引用与唯一性约束。
</p>
</header>
<!-- 简化 ER 关系图 SVG 容器 -->
<section class="er-canvas-card">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; flex-wrap: wrap; gap: 0.75rem;">
<div>
<h2 style="font-size: 1.25rem; font-weight: 700; color: var(--navy-900);">10 大核心教务实体关系拓扑</h2>
<p style="font-size: 0.82rem; color: var(--text-muted); margin-top: 0.2rem;">点击实体节点展开物理表与约束细节</p>
</div>
<div style="font-size: 0.8rem; color: var(--text-muted);">
<span>1:N 一对多关联</span> · <span>N:M 多对多关联</span> · <span>XOR 互斥排他</span>
</div>
</div>
<div class="er-svg-wrap">
<svg class="er-svg" viewBox="0 0 1000 520" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="er-arrow" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
<path d="M0,1 L7,4 L0,7 Z" fill="#1d4ed8" />
</marker>
</defs>
<!-- 关系连线 -->
<!-- Plan -> PlanCourse (1:N) -->
<line x1="420" y1="100" x2="490" y2="100" stroke="#1d4ed8" stroke-width="2" marker-end="url(#er-arrow)" />
<text x="455" y="90" text-anchor="middle" font-size="10" fill="#64748b">1:N</text>
<!-- Course -> PlanCourse (1:N) -->
<line x1="720" y1="100" x2="650" y2="100" stroke="#1d4ed8" stroke-width="2" marker-end="url(#er-arrow)" />
<text x="685" y="90" text-anchor="middle" font-size="10" fill="#64748b">1:N</text>
<!-- Plan -> Enrollment (1:N) -->
<line x1="340" y1="140" x2="340" y2="210" stroke="#1d4ed8" stroke-width="2" marker-end="url(#er-arrow)" />
<text x="355" y="180" font-size="10" fill="#64748b">1:N</text>
<!-- Student -> Enrollment (1:N) -->
<line x1="170" y1="250" x2="260" y2="250" stroke="#1d4ed8" stroke-width="2" marker-end="url(#er-arrow)" />
<text x="215" y="240" text-anchor="middle" font-size="10" fill="#64748b">1:N</text>
<!-- PlanCourse -> Lesson (1:N) -->
<line x1="570" y1="140" x2="570" y2="210" stroke="#1d4ed8" stroke-width="2" marker-end="url(#er-arrow)" />
<text x="585" y="180" font-size="10" fill="#64748b">1:N</text>
<!-- Classroom -> Lesson (1:N, XOR) -->
<line x1="780" y1="220" x2="650" y2="240" stroke="#1d4ed8" stroke-width="1.5" stroke-dasharray="3 3" marker-end="url(#er-arrow)" />
<text x="720" y="225" font-size="10" fill="#64748b">XOR</text>
<!-- Site -> Lesson (1:N, XOR) -->
<line x1="780" y1="280" x2="650" y2="260" stroke="#1d4ed8" stroke-width="1.5" stroke-dasharray="3 3" marker-end="url(#er-arrow)" />
<text x="720" y="280" font-size="10" fill="#64748b">XOR</text>
<!-- Enrollment -> Report (1:1) -->
<line x1="340" y1="290" x2="340" y2="360" stroke="#1d4ed8" stroke-width="2" marker-end="url(#er-arrow)" />
<text x="355" y="330" font-size="10" fill="#64748b">1:1</text>
<!-- Enrollment -> Checkin (1:N) -->
<line x1="410" y1="280" x2="490" y2="380" stroke="#1d4ed8" stroke-width="1.5" marker-end="url(#er-arrow)" />
<!-- Lesson -> Checkin (1:N) -->
<line x1="570" y1="290" x2="570" y2="360" stroke="#1d4ed8" stroke-width="2" marker-end="url(#er-arrow)" />
<text x="585" y="330" font-size="10" fill="#64748b">1:N</text>
<!-- 1. Plan 实体 -->
<g class="er-entity" onclick="showEntityDetail('plan')">
<rect class="entity-box" x="260" y="60" width="160" height="80" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="260" y="60" width="160" height="26" rx="8" fill="#eff6ff" />
<text x="340" y="78" text-anchor="middle" font-size="12" font-weight="700" fill="#1e40af">Plan (培训计划)</text>
<text x="275" y="102" font-size="11" fill="#334155">PK: plan_id (UUID)</text>
<text x="275" y="122" font-size="10" fill="#64748b">plan_status, recruit_num</text>
</g>
<!-- 2. PlanCourse 实体 -->
<g class="er-entity" onclick="showEntityDetail('plancourse')">
<rect class="entity-box" x="490" y="60" width="160" height="80" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="490" y="60" width="160" height="26" rx="8" fill="#eff6ff" />
<text x="570" y="78" text-anchor="middle" font-size="12" font-weight="700" fill="#1e40af">PlanCourse (计划课程)</text>
<text x="505" y="102" font-size="11" fill="#334155">PK: plan_course_id</text>
<text x="505" y="122" font-size="10" fill="#64748b">FK: plan_id, c_id, t_id</text>
</g>
<!-- 3. Course 实体 -->
<g class="er-entity" onclick="showEntityDetail('course')">
<rect class="entity-box" x="720" y="60" width="150" height="80" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="720" y="60" width="150" height="26" rx="8" fill="#eff6ff" />
<text x="795" y="78" text-anchor="middle" font-size="12" font-weight="700" fill="#1e40af">Course (课程大纲)</text>
<text x="735" y="102" font-size="11" fill="#334155">PK: c_id</text>
<text x="735" y="122" font-size="10" fill="#64748b">c_code (UK), c_duration</text>
</g>
<!-- 4. Student 实体 -->
<g class="er-entity" onclick="showEntityDetail('student')">
<rect class="entity-box" x="20" y="210" width="150" height="80" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="20" y="210" width="150" height="26" rx="8" fill="#eff6ff" />
<text x="95" y="228" text-anchor="middle" font-size="12" font-weight="700" fill="#1e40af">Student (学员档案)</text>
<text x="35" y="252" font-size="11" fill="#334155">PK: s_id (UUID)</text>
<text x="35" y="272" font-size="10" fill="#64748b">id_card_last4, status</text>
</g>
<!-- 5. Enrollment 实体 -->
<g class="er-entity" onclick="showEntityDetail('enrollment')">
<rect class="entity-box" x="260" y="210" width="160" height="80" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="260" y="210" width="160" height="26" rx="8" fill="#eff6ff" />
<text x="340" y="228" text-anchor="middle" font-size="12" font-weight="700" fill="#1e40af">Enrollment (参训名单)</text>
<text x="275" y="252" font-size="11" fill="#334155">PK: enrollment_id</text>
<text x="275" y="272" font-size="10" fill="#64748b">UK(plan_id, student_id)</text>
</g>
<!-- 6. Lesson 实体 -->
<g class="er-entity" onclick="showEntityDetail('lesson')">
<rect class="entity-box" x="490" y="210" width="160" height="80" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="490" y="210" width="160" height="26" rx="8" fill="#eff6ff" />
<text x="570" y="228" text-anchor="middle" font-size="12" font-weight="700" fill="#1e40af">Lesson (排课课次)</text>
<text x="505" y="252" font-size="11" fill="#334155">PK: lesson_id</text>
<text x="505" y="272" font-size="10" fill="#64748b">venue_type (XOR互斥)</text>
</g>
<!-- 7. Classroom 实体 -->
<g class="er-entity" onclick="showEntityDetail('classroom')">
<rect class="entity-box" x="780" y="195" width="160" height="55" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="780" y="195" width="160" height="22" rx="8" fill="#eff6ff" />
<text x="860" y="211" text-anchor="middle" font-size="11" font-weight="700" fill="#1e40af">Classroom (理论教室)</text>
<text x="795" y="235" font-size="10" fill="#334155">PK: class_room_id, capacity</text>
</g>
<!-- 8. Site 实体 -->
<g class="er-entity" onclick="showEntityDetail('site')">
<rect class="entity-box" x="780" y="265" width="160" height="55" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="780" y="265" width="160" height="22" rx="8" fill="#eff6ff" />
<text x="860" y="281" text-anchor="middle" font-size="11" font-weight="700" fill="#1e40af">Site (实训场地)</text>
<text x="795" y="305" font-size="10" fill="#334155">PK: site_id (UUID), capacity</text>
</g>
<!-- 9. Report 实体 -->
<g class="er-entity" onclick="showEntityDetail('report')">
<rect class="entity-box" x="260" y="360" width="160" height="80" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="260" y="360" width="160" height="26" rx="8" fill="#eff6ff" />
<text x="340" y="378" text-anchor="middle" font-size="12" font-weight="700" fill="#1e40af">Report (现场报到)</text>
<text x="275" y="402" font-size="11" fill="#334155">PK: report_id</text>
<text x="275" y="422" font-size="10" fill="#64748b">FK: enrollment_id, time</text>
</g>
<!-- 10. Checkin 实体 -->
<g class="er-entity" onclick="showEntityDetail('checkin')">
<rect class="entity-box" x="490" y="360" width="160" height="80" rx="8" fill="#ffffff" stroke="#1d4ed8" stroke-width="1.5" />
<rect x="490" y="360" width="160" height="26" rx="8" fill="#eff6ff" />
<text x="570" y="378" text-anchor="middle" font-size="12" font-weight="700" fill="#1e40af">Checkin (课堂考勤)</text>
<text x="505" y="402" font-size="11" fill="#334155">PK: checkin_id</text>
<text x="505" y="422" font-size="10" fill="#64748b">FK: enrollment_id, lesson_id</text>
</g>
</svg>
</div>
</section>
<!-- 实体卡片速查网格 (点击展开 Drawer) -->
<section class="content-section">
<div class="section-header">
<div>
<h2 class="section-title">10 大核心实体物理映射与核心约束</h2>
<p class="section-subtitle">点击卡片可快速展开物理表结构、主键生成机制与外键约束详情。</p>
</div>
</div>
<div class="entity-grid">
<div class="entity-card" onclick="showEntityDetail('plan')">
<div class="card-top">
<span class="badge badge-pass">实体 1</span>
<span class="card-code">train_class_plan</span>
</div>
<h3 class="card-title">培训班计划 (ClassPlan)</h3>
<p class="card-body-text">主键为 32 位 UUID,严格维护起止时间窗口与生命周期状态(0草稿/1进行中/2已结束)。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
<div class="entity-card" onclick="showEntityDetail('plancourse')">
<div class="card-top">
<span class="badge badge-pass">实体 2</span>
<span class="card-code">train_plan_course</span>
</div>
<h3 class="card-title">计划绑定课程 (PlanCourse)</h3>
<p class="card-body-text">班级与课程 N:M 映射枢纽,关联授课教师,外键依赖计划与课程主表。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
<div class="entity-card" onclick="showEntityDetail('course')">
<div class="card-top">
<span class="badge badge-pass">实体 3</span>
<span class="card-code">train_course</span>
</div>
<h3 class="card-title">课程主档 (Course)</h3>
<p class="card-body-text">课程编码全局唯一(c_code),维护理论学时与实操学时,供开班总学时累加。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
<div class="entity-card" onclick="showEntityDetail('enrollment')">
<div class="card-top">
<span class="badge badge-pass">实体 4</span>
<span class="card-code">train_enrollment</span>
</div>
<h3 class="card-title">参训报名名单 (Enrollment)</h3>
<p class="card-body-text">物理唯一约束 uk_plan_student 杜绝重复入班,维护学员在班级的参训状态流转。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
<div class="entity-card" onclick="showEntityDetail('student')">
<div class="card-top">
<span class="badge badge-pass">实体 5</span>
<span class="card-code">train_student</span>
</div>
<h3 class="card-title">学员基础档案 (Student)</h3>
<p class="card-body-text">32位UUID主键,身份证密文哈希存储与末4位掩码展示,维护学员主档状态(1正常/0停用)。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
<div class="entity-card" onclick="showEntityDetail('lesson')">
<div class="card-top">
<span class="badge badge-pass">实体 6</span>
<span class="card-code">train_lesson</span>
</div>
<h3 class="card-title">排课课次 (Lesson)</h3>
<p class="card-body-text">上课时段与师资配置,严格校验场地 XOR 互斥约束,禁止跨计划绑定课程。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
<div class="entity-card" onclick="showEntityDetail('classroom')">
<div class="card-top">
<span class="badge badge-pass">实体 7</span>
<span class="card-code">train_classroom_info</span>
</div>
<h3 class="card-title">理论教室 (Classroom)</h3>
<p class="card-body-text">主键 class_room_id,维护物理容量上限与可用状态,支撑 0/1/N 电子班牌挂载。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
<div class="entity-card" onclick="showEntityDetail('site')">
<div class="card-top">
<span class="badge badge-pass">实体 8</span>
<span class="card-code">train_training_site</span>
</div>
<h3 class="card-title">实训演练场地 (Site)</h3>
<p class="card-body-text">主键 32 位 UUID,维护专业演练场地类型与最大容量,排课中参与场地互斥冲突校验。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
<div class="entity-card" onclick="showEntityDetail('report')">
<div class="card-top">
<span class="badge badge-pass">实体 9</span>
<span class="card-code">train_class_report</span>
</div>
<h3 class="card-title">现场开班报到流水 (Report)</h3>
<p class="card-body-text">学员现场核验凭证,外键强关联 enrollment_id,报到成功强事务联动学员参训状态为已报到。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
<div class="entity-card" onclick="showEntityDetail('checkin')">
<div class="card-top">
<span class="badge badge-pass">实体 10</span>
<span class="card-code">train_student_checkin</span>
</div>
<h3 class="card-title">课次考勤打卡 (StudentCheckin)</h3>
<p class="card-body-text">记录学员课次打卡时间,保障签到签退幂等性防覆盖,人工代签自动置 manual_flag=1 审计留痕。</p>
<div style="font-size: 0.78rem; color: var(--rail-blue); font-weight: 600;">查看物理定义 &rarr;</div>
</div>
</div>
</section>
<!-- 完整 21 表事实源引导 (不平铺冗长表格,提供事实源折叠与指引) -->
<section class="content-section" style="margin-bottom: 0;">
<div class="accordion-item">
<div class="accordion-header">
<div class="accordion-title-group">
<span class="badge badge-neutral">完整 21 张物理业务表</span>
<span style="font-size: 0.95rem; font-weight: 600; color: var(--navy-900);">查看系统全量数据表归属与数据库事实源索引</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">
<p style="margin-bottom: 1rem; color: var(--text-secondary);">
系统一期由 <strong>21 张培训业务表</strong> (<code>train_*</code>) 与 <strong>8 张平台底座权限审计表</strong> (<code>sys_*</code>) 构成,表清单契约与 DDL 严格冻结于 <code>docs/database-v2/</code> 目录下:
</p>
<div class="data-table-wrap">
<table class="data-table">
<thead>
<tr>
<th>表分类</th>
<th>物理表清单</th>
<th>职责描述</th>
<th>事实源文件</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>基础资料表 (9)</strong></td>
<td><code>train_teacher</code>, <code>train_student</code>, <code>train_course</code>, <code>train_resource</code>, <code>train_course_resource</code>, <code>train_classroom_info</code>, <code>train_training_site</code>, <code>train_electronic_board</code>, <code>train_dormitory_info</code></td>
<td>师资、学员、课程、统一资源、教室、场地、班牌设备、宿舍房间基础信息</td>
<td><code>docs/database-v2/01_table_inventory.md</code></td>
</tr>
<tr>
<td><strong>培训组织表 (4)</strong></td>
<td><code>train_class_plan</code>, <code>train_plan_course</code>, <code>train_enrollment</code>, <code>train_lesson</code></td>
<td>开班计划、课程绑定、参训报名名单、教学活动课次核心教务编排</td>
<td><code>docs/database-v2/01_table_inventory.md</code></td>
</tr>
<tr>
<td><strong>培训执行表 (4)</strong></td>
<td><code>train_accommodation</code>, <code>train_dorm_auth</code>, <code>train_class_report</code>, <code>train_student_checkin</code></td>
<td>学员住宿安排、门禁授权同步、现场报到核验、课次打卡考勤流水</td>
<td><code>docs/database-v2/01_table_inventory.md</code></td>
</tr>
<tr>
<td><strong>培训结果表 (4)</strong></td>
<td><code>train_score_item</code>, <code>train_student_score</code>, <code>train_study_duration</code>, <code>train_student_portrait</code></td>
<td>考核项配置、学员单项成绩、自主学时、学员全期画像评估 (只读模型 PASS)</td>
<td><code>docs/database-v2/01_table_inventory.md</code></td>
</tr>
<tr>
<td><strong>系统底座表 (8)</strong></td>
<td><code>sys_user</code>, <code>sys_role</code>, <code>sys_menu</code>, <code>sys_dept</code>, <code>sys_oper_log</code>, <code>sys_logininfor</code>, <code>sys_dict_type</code>, <code>sys_dict_data</code></td>
<td>RBAC 鉴权、路由菜单、操作审计日志与系统字典底座</td>
<td><code>docs/database-v2/00_init_ruoyi_sys.sql</code></td>
</tr>
</tbody>
</table>
</div>
<div style="margin-top: 1rem;">
<a href="http://192.168.139.218:9980/sict/railwaysystem/-/blob/feat/cicd-pipeline/docs/handbook/04-data-model-overview.md" target="_blank" rel="noopener noreferrer" class="btn btn-secondary" style="font-size: 0.82rem;">查阅数据库全量设计手册 &rarr;</a>
</div>
</div>
</div>
</section>
</main>
<!-- 侧边抽屉组件 (Drawer) -->
<div class="drawer-backdrop" id="drawer-backdrop">
<div class="drawer-panel">
<div class="drawer-header">
<h3 class="drawer-title" id="drawer-title">实体定义</h3>
<button class="drawer-close" id="drawer-close-btn" type="button" aria-label="关闭">&times;</button>
</div>
<div class="drawer-body" id="drawer-body">
<!-- 动态注入实体详情 -->
</div>
</div>
</div>
<!-- 页脚 -->
<footer class="site-footer">
<p>RailwaySystem SYFJGYD学员管理系统 · 交付候选版 (Final Candidate)</p>
<p style="margin-top: 0.35rem; color: var(--text-muted);">
数据模型与实体关系 · <a href="http://192.168.139.218:9980/sict/railwaysystem/-/blob/feat/cicd-pipeline/docs/handbook/04-data-model-overview.md" target="_blank" rel="noopener noreferrer">查阅数据模型手册</a>
</p>
</footer>
<script src="assets/docs.js"></script>
<script>
const entityDict = {
plan: {
title: "实体定义: ClassPlan (培训班主档计划)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_class_plan</code></p>
<p><strong>主键</strong>:<code>plan_id VARCHAR(32)</code>(后端自动生成 32 位 UUID,全局唯一)</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">核心业务字段与类型</div>
<ul style="padding-left:1.2rem; line-height:1.8;">
<li><code>plan_name VARCHAR(100)</code>:培训班名称</li>
<li><code>plan_status INT</code>:0:草稿, 1:进行中, 2:已结束</li>
<li><code>start_time TIMESTAMP</code>:培训开始时间</li>
<li><code>end_time TIMESTAMP</code>:培训结束时间</li>
<li><code>report_deadline TIMESTAMP</code>:现场报到截止时间</li>
<li><code>recruit_num INT</code>:招生容量上限(硬拦截依据)</li>
</ul>
</div>
<div class="drawer-section">
<div class="drawer-section-title">核心关联与业务约束</div>
<p>作为教务编排的根实体,下游一对多关联 <code>train_plan_course</code>、<code>train_enrollment</code>;结训时通过强事务批量完成关联活动学员。</p>
</div>
`
},
plancourse: {
title: "实体定义: PlanCourse (计划绑定课程)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_plan_course</code></p>
<p><strong>主键</strong>:<code>plan_course_id BIGINT</code>(自增/序列主键)</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">核心关联外键</div>
<ul style="padding-left:1.2rem; line-height:1.8;">
<li><code>plan_id VARCHAR(32)</code>:所属培训计划外键</li>
<li><code>c_id BIGINT</code>:绑定课程大纲外键</li>
<li><code>t_id VARCHAR(32)</code>:指派主讲教师外键</li>
</ul>
</div>
<div class="drawer-section">
<div class="drawer-section-title">核心约束</div>
<p>作为排课课次 <code>train_lesson</code> 的唯一课程依赖,排课时强校验课次所属计划与 <code>plan_course_id</code> 严格一致,严禁跨计划错链。</p>
</div>
`
},
course: {
title: "实体定义: Course (课程大纲主档)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_course</code></p>
<p><strong>主键</strong>:<code>c_id BIGINT</code>(主键标识)</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">唯一约束与核心字段</div>
<ul style="padding-left:1.2rem; line-height:1.8;">
<li><code>c_code VARCHAR(64) UNIQUE</code>:课程编码全局唯一</li>
<li><code>c_name VARCHAR(100)</code>:课程名称</li>
<li><code>c_duration INT</code>:标准课时数(大于 0)</li>
<li><code>status INT</code>:1:可用, 0:停用</li>
</ul>
</div>
`
},
enrollment: {
title: "实体定义: Enrollment (参训报名档案)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_enrollment</code></p>
<p><strong>主键</strong>:<code>enrollment_id BIGINT</code>(自增/序列主键)</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">核心唯一约束 (防重复报名)</div>
<p><code>UNIQUE KEY uk_plan_student (plan_id, student_id)</code>:数据库层面强制杜绝同一学员在同一班级重复入班。</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">参训状态机字段</div>
<p><code>enrollment_status INT</code>:0:待报到, 1:已报到, 2:请假, 3:退训, 4:已完成</p>
<p><code>complete_time TIMESTAMP</code>:结训完成时间(退训学员保持为空,受防破坏保护)</p>
</div>
`
},
student: {
title: "实体定义: Student (学员基础档案)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_student</code></p>
<p><strong>主键</strong>:<code>s_id VARCHAR(32)</code>(32 位 UUID)</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">敏感信息脱敏与存储安全</div>
<ul style="padding-left:1.2rem; line-height:1.8;">
<li><code>id_card_ciphertext VARCHAR(255)</code>:身份证密文</li>
<li><code>id_card_hash VARCHAR(64)</code>:身份证哈希索引(用于精确检索)</li>
<li><code>id_card_last4 VARCHAR(4)</code>:末4位掩码(接口返回显示)</li>
<li><code>status INT</code>:1:正常, 0:停用(停用学员禁止报名)</li>
</ul>
</div>
`
},
lesson: {
title: "实体定义: Lesson (排课课次)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_lesson</code></p>
<p><strong>主键</strong>:<code>lesson_id BIGINT</code>(自增主键)</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">场地互斥约束 (Venue XOR)</div>
<p><code>venue_type INT</code> (1:理论教室, 2:实训场地)。物理业务逻辑强校验 <code>class_room_id</code> 与 <code>site_id</code> 满足二选一严格互斥,不可同时为空或同时赋值。</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">时间与状态约束</div>
<p>必须满足 <code>start_time &lt; end_time</code>,且计划结训后强阻断新增或修改课次。</p>
</div>
`
},
classroom: {
title: "实体定义: Classroom (理论教室)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_classroom_info</code></p>
<p><strong>主键</strong>:<code>class_room_id VARCHAR(32)</code></p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">字段与物联扩展</div>
<p><code>room_name VARCHAR(100)</code>:教室门牌名称</p>
<p><code>capacity INT</code>:标准座位容量</p>
<p>支持下游一对多绑定 <code>train_electronic_board</code> 电子班牌终端。</p>
</div>
`
},
site: {
title: "实体定义: TrainingSite (实训演练场地)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_training_site</code></p>
<p><strong>主键</strong>:<code>site_id VARCHAR(32)</code>(32 位 UUID)</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">专业场地特征</div>
<p><code>site_name VARCHAR(100)</code>:场地名称(如信号演练场、机务整备场)</p>
<p><code>capacity INT</code>:最大容纳实训人数</p>
<p>排课预检中触发 <code>SITE</code> 级别 Warning 预警。</p>
</div>
`
},
report: {
title: "实体定义: ClassReport (开班报到流水)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_class_report</code></p>
<p><strong>主键</strong>:<code>report_id BIGINT</code>(自增主键)</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">强事务联动与防重约束</div>
<p><code>enrollment_id BIGINT</code>:外键关联参训记录。</p>
<p>报到动作自动流转 <code>enrollment_status = 1</code>,若已报到则强行拦截防重复报到 (40050)。</p>
</div>
`
},
checkin: {
title: "实体定义: StudentCheckin (课次考勤打卡)",
html: `
<div class="drawer-section">
<div class="drawer-section-title">物理表与主键</div>
<p><strong>物理表名</strong>:<code>train_student_checkin</code></p>
<p><strong>主键</strong>:<code>checkin_id BIGINT</code>(自增主键)</p>
</div>
<div class="drawer-section">
<div class="drawer-section-title">幂等性与人工审计标志</div>
<ul style="padding-left:1.2rem; line-height:1.8;">
<li><code>sign_in_time TIMESTAMP</code>:首次签到时间(重复签到保持首次不变)</li>
<li><code>sign_out_time TIMESTAMP</code>:首次签退时间(重复签退保持首次不变)</li>
<li><code>manual_flag INT</code>:0:自动打卡, 1:人工代签/补签(审计标志)</li>
</ul>
</div>
`
}
};
function showEntityDetail(key) {
const data = entityDict[key];
if (!data) return;
window.openDrawer(data.title, data.html);
}
</script>
</body>
</html>