docs(审计): 完成Phase 0工程与数据库基线核验

记录当前Git开发基线、Kingbase真实表结构、培训数据与权限状态。

补充数据库与代码差异、只读兼容性验证及PRD V1.0核心对象映射,未包含业务代码或数据库修改。
This commit is contained in:
wangzhibiao committed 2026-09-14 00:36:53 +08:00
1 parent e12eda0596
commit fb00f67d3b
6 files changed
+1174

No files matched your search

@@ -0,0 +1,102 @@
# Phase 0:当前 Git 基线分类
> 核验时间:2026-09-14
> 业务判断最高依据:`doc/RailwaySystem 智慧培训系统一期 PRD V1.0.md`
> 本文件记录生成 Phase 0 报告前的工作树。未执行 stage、commit、reset、clean、restore 或 checkout。
## 1. 当前 HEAD 与分支
| 项目 | 值 |
| --- | --- |
| HEAD | `e12eda059679b89eb5d09215ffe1ce7132f161f2` |
| branch | `main` |
| HEAD提交 | `docs(prd): 发布RailwaySystem智慧培训系统一期PRD V1.0正式业务基线` |
| 提交时间 | 2026-09-13 23:52:29 +0800 |
结论:后续业务判断应以当前 `main@e12eda05` 与 PRD V1.0 为起点。原 `00`—`11` 审计报告已经提交,但早于 PRD V1.0;涉及一期范围与模型优先级的旧结论由本轮 `12`—`17` 报告修正。
## 2. 已跟踪未提交文件
当前只有 3 个已跟踪删除:
```text
D doc/elk.bundled.js
D doc/railwaysystem.html
D doc/智慧培训系统一期详细开发说明书.md
```
这些文件的同名内容出现在未跟踪目录 `doc/wxy_doc/`,其中 `readme.md` 明确写着“王杏月师姐生成的文档(暂不使用)”。当前状态表现为一次尚未提交的旧材料归档/移动,不是业务代码删除。
## 3. 未跟踪文件
```text
doc/wxy_doc/智慧培训系统一期详细开发说明书.md
doc/wxy_doc/elk.bundled.js
doc/wxy_doc/railwaysystem.html
doc/wxy_doc/readme.md
```
没有未跟踪 Java、Mapper XML、Vue业务页面、前端API、SQL迁移或运行脚本。生成本轮报告后,`docs/codebase-audit/12_*.md` 至 `17_*.md` 会作为本任务授权的新文件出现在工作树中。
## 4. 工作树文件分类
| 分类 | 当前文件/状态 | 判断 |
| --- | --- | --- |
| 正式培训功能 | 当前无未提交项;成绩、宿舍、教室适配已在 `a03f4268` 提交 | 属于当前 HEAD正式代码基线 |
| 本地运行适配 | 当前无未提交项;`application.yml`、`logback.xml`、`login.vue`、`vue.config.js` 已在 `bd51fe1f` 提交 | 已纳入正式基线,但仍是开发环境适配性质 |
| Kingbase/Tailscale适配 | `application-druid.yml`、`start-local.sh` 已在 `92f0f030` 提交 | 已纳入正式基线;datasource 通过本地 4321 隧道访问 Kingbase |
| 临时文件 | 当前 `git status` 未发现 Office锁文件、`.DS_Store`、日志等 | 无未提交临时文件 |
| 遗留/实验页面 | 当前无改动;`build_ui/src/views/train/person`、`character`、`analysis`、`weather` 等仍被 Git跟踪 | 与PRD V1.0核心闭环无对应后端,应视为遗留/实验资产 |
| 旧业务材料 | 3个根目录旧文件被删除,并复制/移动到未跟踪 `doc/wxy_doc/` | PRD V1.0明确它们仅作参考;当前归档动作尚未提交 |
| 构建产物 | 当前状态中无新增构建产物 | 未发现未跟踪 JAR、target、dist或依赖目录 |
## 5. 指定文件逐项核对
### 5.1 成绩相关页面/API
以下文件已提交并且当前相对 HEAD 无修改:
- `build_ui/src/api/train/score.js`
- `build_ui/src/views/train/score/index.vue`
- `build_ui/src/views/train/course-score/index.vue`
- `build_ui/src/views/train/student-score/index.vue`
- `TrainScoreInfoController.java` 的 `/train/scoreinfo`、`/train/score` 别名
它们是正式培训功能代码,但仍实现旧的固定三列成绩模型,与 PRD V1.0 的“成绩项目 + 权重 + 参训学员得分”冲突。
### 5.2 宿舍相关页面/API
`build_ui/src/api/train/dormitory.js`、`build_ui/src/views/train/dormitory/index.vue` 与 `TrainDormitoryInfoController` 别名已在 `a03f4268` 提交,当前干净。它们属于正式培训 CRUD,但只能表达房间汇总;住宿安排和门禁授权仍耦合在旧模型中。
### 5.3 教室 API 别名
`TrainClassroomInfoController` 当前同时支持:
```text
/train/classroominfo
/train/classroom
/train/info
```
`build_ui/src/api/train/info.js` 和新增的 `classroom.js` 均已提交。别名修复属于正式接通代码,但多个入口并存需要在后续 API 契约阶段确定正式路径。
### 5.4 `start-local.sh`
已由提交 `92f0f030` 纳入 Git。脚本负责数据库 SSH隧道、Redis、后端 JAR与前端启动,属于本地开发基础设施,不是培训业务功能。
### 5.5 Node 22适配
`build_ui/vue.config.js` 中 MD4到 SHA-256的兼容补丁与前端端口 8080已在 `bd51fe1f` 提交,当前无本地修改。它属于本地工具链适配。
### 5.6 Kingbase相关修改
- `application-druid.yml` 的 JDBC地址已改为 `127.0.0.1:4321` 隧道入口,并在 `92f0f030` 提交。
- Kingbase驱动和 Maven依赖已在更早提交中存在。
- 当前没有未提交的 Kingbase配置或代码改动。
- 本轮只读核验发现的兼容问题未被自动修复。
## 6. 当前可作为正式开发基线的代码状态
代码版本以 `main@e12eda05` 为可识别基线,因为培训页面/API、本地运行适配、Kingbase隧道和 PRD V1.0 都已提交。工作树中的 3个删除与 `doc/wxy_doc/` 只涉及旧参考材料,不改变编译产物。
不过该 HEAD还不能直接称为“可开始业务开发”的稳定运行基线:实时数据库的若依系统表存在 schema同名冲突和角色关系异常,详见 `13_live_database_snapshot.md`、`15_live_permission_snapshot.md` 和 `16_kingbase_readonly_compatibility.md`。
@@ -0,0 +1,405 @@
# Phase 0:Kingbase 实时数据库快照
> 快照时间:数据库返回 `2026-09-14 00:07:58`
> 连接方式:复用 `application-druid.yml` 的 Kingbase JDBC配置,通过既有 SSH/Tailscale隧道连接 `127.0.0.1:4321`。
> 安全措施:JDBC连接开启只读事务,所有探测仅执行 `SELECT`、`SHOW` 和系统目录查询;最后执行 rollback并关闭连接。未输出口令、用户明细或培训人员明细。
## 1. 数据库基本信息
| 项目 | 实际值 |
| --- | --- |
| 数据库产品版本 | `KingbaseES V009R003C018` |
| JDBC驱动 | Kingbase8 JDBC Driver `V008R006B0001`;项目Maven坐标版本为 8.6.0 |
| database | `training` |
| schema | `public` |
| 当前用户 | `kingbase` |
| `database_mode` | `mysql` |
| 底层兼容版本设置 | `server_version=12.1` |
| 简单 `SELECT 1` | 成功 |
## 2. 14张培训表是否存在及行数
| 表名 | 是否存在 | schema | 精确行数 |
| --- | --- | --- | ---: |
| `train_teacher` | 是 | public | 1 |
| `train_student` | 是 | public | 0 |
| `train_course` | 是 | public | 0 |
| `train_class_plan` | 是 | public | 0 |
| `train_dormitory_info` | 是 | public | 0 |
| `train_dorm_auth` | 是 | public | 0 |
| `train_classroom_info` | 是 | public | 0 |
| `train_training_site` | 是 | public | 0 |
| `train_class_report` | 是 | public | 0 |
| `train_electronic_board` | 是 | public | 0 |
| `train_student_checkin` | 是 | public | 0 |
| `train_score_info` | 是 | public | 0 |
| `train_study_duration` | 是 | public | 0 |
| `train_student_portrait` | 是 | public | 0 |
`public` 中所有以 `train_` 开头的表恰好就是这 14 张,没有发现额外培训表。
## 3. 真实表结构
说明:`N` 表示 NOT NULL,`Y` 表示允许 NULL;未标默认值即无默认。`IDENTITY` 均为 `GENERATED BY DEFAULT AS IDENTITY`。实际时间类型为 Kingbase `timestamp`,实际小整数类型为 `smallint`。
### 3.1 `train_teacher`
```text
t_id varchar(32) N PK
t_name varchar(30) N
t_gender char(2) Y
t_birth date Y
t_department varchar(50) N
t_title varchar(30) Y
t_phone varchar(20) Y
t_email varchar(50) Y
create_user varchar(32) N
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
remark varchar(200) Y
```
- 主键/唯一:PK(`t_id`);无其他 UNIQUE。
- 外键:无。
- 索引:只有主键唯一索引。
- 触发器:`trig_train_teacher_modify` 在 UPDATE前调用 `func_train_update_modifytime()`。
### 3.2 `train_student`
```text
s_id varchar(32) N PK
s_name varchar(30) N
s_gender char(2) Y
s_birth date Y
s_department varchar(50) N
s_major varchar(50) N
s_class varchar(30) Y
s_phone varchar(20) Y
create_user varchar(32) N
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
remark varchar(200) Y
```
- 主键/唯一:PK(`s_id`)。
- 外键:无。
- 索引:只有主键唯一索引。
- 触发器:`trig_train_student_modify` 更新 `update_time`。
### 3.3 `train_course`
```text
c_id varchar(32) N PK
c_name varchar(100) N
c_type varchar(30) Y
c_duration integer Y
teaching_objective text Y
course_content text Y
attach_doc text Y
attach_video text Y
attach_model_link text Y
model_resource_ids text Y
create_user varchar(32) N
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
remark varchar(200) Y
```
- 主键/唯一:PK(`c_id`)。
- 外键:无。
- 索引:只有主键唯一索引。
- 触发器:`trig_train_course_modify` 更新 `update_time`。
### 3.4 `train_class_plan`
```text
plan_id varchar(32) N PK
plan_name varchar(100) N
c_id varchar(32) N FK -> train_course.c_id
start_time timestamp N
end_time timestamp N
train_address varchar(100) Y
t_id varchar(32) N FK -> train_teacher.t_id
recruit_num integer Y
apply_condition text Y
plan_status smallint Y DEFAULT 0
create_user varchar(32) N
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
remark varchar(200) Y
```
- 主键/唯一:PK(`plan_id`)。
- 外键:`fk_classplan_course`、`fk_classplan_teacher`,默认 NO ACTION。
- 索引:只有主键唯一索引;未发现 `c_id`、`t_id` 的独立索引。
- 触发器:`trig_train_classplan_modify` 更新 `update_time`。
### 3.5 `train_dormitory_info`
```text
dorm_id varchar(32) N PK
dorm_building varchar(50) Y
dorm_room varchar(30) N
bed_total integer Y
bed_used integer Y DEFAULT 0
lock_device_sn varchar(64) Y
dorm_status smallint Y DEFAULT 1
create_user varchar(32) N
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
remark varchar(200) Y
```
- 主键/唯一:PK(`dorm_id`);UNIQUE(`dorm_building`,`dorm_room`)。
- 外键:无。
- 索引:主键与房间组合唯一索引,无额外非唯一索引。
- 触发器:`trig_train_dorminfo_modify` 更新 `update_time`。
### 3.6 `train_dorm_auth`
```text
auth_id bigint N PK, IDENTITY
student_id varchar(32) N FK -> train_student.s_id
plan_id varchar(32) N FK -> train_class_plan.plan_id
dorm_id varchar(32) N FK -> train_dormitory_info.dorm_id
bed_no varchar(20) Y
face_sync_status smallint Y DEFAULT 0
auth_start timestamp Y
auth_end timestamp Y
create_user varchar(32) N
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
remark varchar(200) Y
```
- 主键/唯一:PK(`auth_id`);UNIQUE(`student_id`,`plan_id`)。
- 外键:`fk_dormauth_stu`、`fk_dormauth_plan`、`fk_dormauth_dorm`。
- 索引:主键与参训人组合唯一索引,无其他非唯一索引。
- 生成:内部 identity序列 `train_dorm_auth_auth_id_seq`。
- 触发器:`trig_train_dormauth_modify` 更新 `update_time`。
### 3.7 `train_classroom_info`
```text
class_room_id varchar(32) N PK
room_name varchar(60) N
room_location varchar(100) Y
capacity integer Y
room_status smallint Y DEFAULT 1
create_user varchar(32) N
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
remark varchar(200) Y
```
- 主键/唯一:PK(`class_room_id`)。
- 外键:无。
- 索引:只有主键唯一索引。
- 触发器:`trig_train_classroom_modify` 更新 `update_time`。
### 3.8 `train_training_site`
```text
site_id varchar(32) N PK
site_name varchar(80) N
site_location varchar(100) Y
capacity integer Y
site_device text Y
site_status smallint Y DEFAULT 1
create_user varchar(32) N
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
remark varchar(200) Y
```
- 主键/唯一:PK(`site_id`)。
- 外键:无。
- 索引:只有主键唯一索引。
- 触发器:`trig_train_trainsite_modify` 更新 `update_time`。
### 3.9 `train_class_report`
```text
report_id bigint N PK, IDENTITY
plan_id varchar(32) N FK -> train_class_plan.plan_id
student_id varchar(32) N FK -> train_student.s_id
report_way smallint Y
report_device_sn varchar(64) Y
report_time timestamp Y
report_status smallint Y DEFAULT 0
alloc_dorm_id varchar(32) Y FK -> train_dormitory_info.dorm_id
alloc_classroom_id varchar(32) Y FK -> train_classroom_info.class_room_id
alloc_site_id varchar(32) Y FK -> train_training_site.site_id
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
```
- 主键/唯一:PK(`report_id`);UNIQUE(`plan_id`,`student_id`)。
- 外键:计划、学员、宿舍、教室、实训场地共5个外键。
- 索引:主键与计划/学员唯一索引,无其他非唯一索引。
- 生成:identity序列 `train_class_report_report_id_seq`。
- 触发器:无。
### 3.10 `train_electronic_board`
```text
board_id varchar(32) N PK
board_sn varchar(64) N UNIQUE
bind_classroom_id varchar(32) Y FK -> train_classroom_info.class_room_id
bind_plan_id varchar(32) Y FK -> train_class_plan.plan_id
board_ip varchar(50) Y
board_content varchar(500) Y
board_playmode smallint Y DEFAULT 1
support_checkin_type varchar(30) Y
board_status smallint Y DEFAULT 1
last_heartbeat timestamp Y
create_user varchar(32) N
create_time timestamp Y DEFAULT CURRENT_TIMESTAMP
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
remark varchar(200) Y
```
- 主键/唯一:PK(`board_id`);UNIQUE(`board_sn`)。
- 外键:教室、开班计划。
- 索引:主键与设备序列号唯一索引,无其他非唯一索引。
- 触发器:`trig_train_board_modify` 更新 `update_time`。
### 3.11 `train_student_checkin`
```text
checkin_id bigint N PK, IDENTITY
plan_id varchar(32) N FK -> train_class_plan.plan_id
c_id varchar(32) N FK -> train_course.c_id
student_id varchar(32) N FK -> train_student.s_id
board_id varchar(32) Y FK -> train_electronic_board.board_id
checkin_way smallint Y
checkin_time timestamp Y
checkin_status smallint Y
lesson_start_time timestamp Y
lesson_end_time timestamp Y
```
- 主键/唯一:PK(`checkin_id`);UNIQUE(`plan_id`,`c_id`,`student_id`,`lesson_start_time`)。
- 外键:计划、课程、学员、班牌。
- 索引:主键与旧“课次+学员”组合唯一索引,无其他非唯一索引。
- 生成:identity序列 `train_student_checkin_checkin_id_seq`。
- 触发器:无。
### 3.12 `train_score_info`
```text
score_id bigint N PK, IDENTITY
plan_id varchar(32) Y FK -> train_class_plan.plan_id
c_id varchar(32) Y FK -> train_course.c_id
student_id varchar(32) Y FK -> train_student.s_id
course_score numeric(5,2) Y
train_score numeric(5,2) Y
exam_score numeric(5,2) Y
source_type smallint Y
source_system varchar(50) Y
import_time timestamp Y DEFAULT CURRENT_TIMESTAMP
```
- 主键/唯一:PK(`score_id`);UNIQUE(`plan_id`,`c_id`,`student_id`)。
- 外键:计划、课程、学员。
- 索引:主键与计划/课程/学员唯一索引,无其他非唯一索引。
- 生成:identity序列 `train_score_info_score_id_seq`。
- 触发器:无。
### 3.13 `train_study_duration`
```text
dur_id bigint N PK, IDENTITY
student_id varchar(32) Y FK -> train_student.s_id
plan_id varchar(32) Y FK -> train_class_plan.plan_id
c_id varchar(32) Y FK -> train_course.c_id
device_type varchar(50) Y
start_time timestamp Y
end_time timestamp Y
study_seconds integer Y
sync_time timestamp Y DEFAULT CURRENT_TIMESTAMP
```
- 主键/唯一:PK(`dur_id`);无其他 UNIQUE。
- 外键:学员、计划、课程。
- 索引:只有主键唯一索引。
- 生成:identity序列 `train_study_duration_dur_id_seq`。
- 触发器:无。
### 3.14 `train_student_portrait`
```text
portrait_id bigint N PK, IDENTITY
student_id varchar(32) N FK -> train_student.s_id
plan_id varchar(32) N FK -> train_class_plan.plan_id
total_study_hour numeric(8,2) Y
avg_score numeric(5,2) Y
checkin_rate numeric(5,2) Y
label_tag text Y
portrait_content text Y
update_time timestamp Y DEFAULT CURRENT_TIMESTAMP
```
- 主键/唯一:PK(`portrait_id`);UNIQUE(`student_id`,`plan_id`)。
- 外键:学员、计划。
- 索引:主键与学员/计划唯一索引,无其他非唯一索引。
- 生成:identity序列 `train_student_portrait_portrait_id_seq`。
- 触发器:无。
## 4. 主键、外键、索引与触发器汇总
- 14张表均有主键。
- 6张数字主键表均为 `bigint GENERATED BY DEFAULT AS IDENTITY`,JDBC元数据返回 `IS_AUTOINCREMENT=YES`;驱动返回 `supportsGetGeneratedKeys=true`。由于禁止写入,本次没有实际 INSERT验证回填。
- 8张字符串主键表没有默认生成器,仍由应用/用户提供业务编号。
- 实际有 24个培训外键关系,孤儿统计均为 0;当前除教师外各表为空,不能用现有数据证明复杂关联行为。
- 除主键和7组业务唯一约束外,`pg_indexes` 未发现额外非唯一索引;多个外键列没有独立索引。
- 9张有 `update_time` 的主数据表通过 `func_train_update_modifytime()` 触发器在 UPDATE前更新时间。
## 5. 数据安全统计
- 唯一现有培训业务数据:`train_teacher` 1行;未输出姓名、电话、邮箱或主键值。
- 8张字符串主键表均无 NULL或重复主键;其中7张为空,教师表1行。
- 24组已建外键关系的孤儿数均为0。
- `train_score_info` 为0行,当前没有真实成绩需要迁移。
- `train_class_plan`、`train_student`、报到、住宿、签到、时长、画像均为0行。
- 教师记录的 `create_time`、`update_time` 确实含非零时分秒;其他业务时间字段因对应表为空,无法用数据确认。
## 6. 关键 `sys_*` 表真实状态
以下均指明确限定的 `public.sys_*`,避免与 Kingbase系统视图混淆。
| 表 | 是否存在 | 行数 | 主要列是否覆盖若依模型 |
| --- | --- | ---: | --- |
| `public.sys_user` | 是 | 1 | 是:`user_id`至`pwd_update_date`齐全 |
| `public.sys_role` | 是 | 0 | 是:`role_id`、`role_key`、`data_scope`等齐全,但无数据 |
| `public.sys_menu` | 是 | 106 | 是:路由、component、perms、menu_type等齐全 |
| `public.sys_user_role` | 是 | 20 | 列齐全,但关系数据异常 |
| `public.sys_role_menu` | 是 | 170 | 列齐全,但关系数据异常 |
| `public.sys_dept` | 是 | 15 | 列齐全 |
| `public.sys_role_dept` | 是 | 0 | 列齐全 |
| `public.sys_config` | 是 | 0 | 列齐全,但无应用配置数据 |
| `public.sys_dict_type` | 是 | 44 | 列齐全 |
| `public.sys_dict_data` | 是 | 500 | 列齐全 |
| `public.sys_job` | 是 | 0 | 列齐全 |
| `public.sys_job_log` | 是 | 0 | 列齐全 |
关系完整性统计:
- `public.sys_user_role` 20行,其中18行引用不存在的应用用户,20行全部引用不存在的应用角色。
- `public.sys_role_menu` 170行,全部引用不存在的应用角色;另有9行引用不存在的菜单。
- `public.sys_role` 为0行,因此实时库没有可识别的培训角色或普通应用角色。
## 7. Kingbase系统视图同名冲突
当前隐式搜索顺序为:
```text
sys -> pg_catalog -> sys_catalog -> public
```
Kingbase在 `sys_catalog` 中也提供 `sys_user`、`sys_role`、`sys_config` 视图。现有若依 Mapper 使用未限定表名,实测:
- `select ... user_id ... from sys_user` 命中系统视图并报 `user_id` 不存在。
- `select ... role_id ... from sys_role` 命中系统视图并报 `role_id` 不存在。
- `select ... config_id ... from sys_config` 命中系统视图并报 `config_id` 不存在。
因此“public表结构与Entity/Mapper列名一致”仍不足以证明若依系统功能可用;schema解析冲突是独立阻塞项。
@@ -0,0 +1,185 @@
# Phase 0:设计文档、实时Kingbase与代码三方差异
> 业务判断以 `doc/RailwaySystem 智慧培训系统一期 PRD V1.0.md` 为最高依据。原始表设计只用于说明V1数据库来源,不能否定PRD V1.0已经确认的新业务对象。
## 1. 三方总览
14个 Mapper XML 的 `resultMap` 字段数与实时表列数逐表一致:157个实际列均在对应 Mapper映射中出现。当前 Entity/Mapper与旧14表可以静态对接;主要问题来自旧表模型与PRD V1.0业务对象不一致,以及若依系统表schema冲突。
| 表 | 原设计文档 | 实际Kingbase | Entity / Mapper | 是否一致 | 主要差异 |
| --- | --- | --- | --- | --- | --- |
| `train_teacher` | 12列,`t_id varchar(20)` | 12列,`t_id varchar(32)`,PK,更新时间触发器,1行 | 12列全映射,`TrainTeacher` | 部分一致 | ID长度不同;PRD不再强制院系/高校职称,缺启停等培训字段 |
| `train_student` | 12列,`s_id varchar(20)` | 12列,`s_id varchar(32)`,PK,更新时间触发器,0行 | 12列全映射,`TrainStudent` | 部分一致 | ID长度不同;缺加密身份证、人脸关联、培训型主档字段 |
| `train_course` | 14列 | 14列,PK,更新时间触发器,0行 | 14列全映射,`TrainCourse` | 字段一致 | 附件/模型为文本列;PRD要求可选择教学资源对象 |
| `train_class_plan` | 14列,单一`c_id/t_id` | 14列,`c_id/t_id`均NOT NULL且有FK,0行 | 14列全映射,`TrainClassPlan` | 旧模型一致 | 与PRD一班多课、课次教师关系直接冲突;缺报到截止规则 |
| `train_dormitory_info` | 11列 | 11列,房间组合UNIQUE,更新时间触发器,0行 | 11列全映射 | 字段一致 | 只有楼栋+房间汇总,无楼层、床位、男女属性 |
| `train_dorm_auth` | 12列,INT自增 | 12列,`bigint identity`,3个FK,学员/计划UNIQUE,0行 | 12列全映射,Long + generated keys | 语义部分一致 | 住宿安排与门禁授权耦合;`bed_no`是自由文本 |
| `train_classroom_info` | 9列 | 9列,PK,更新时间触发器,0行 | 9列全映射 | 一致 | 基础对象符合PRD;排课关系应由课次表达 |
| `train_training_site` | 10列 | 10列,PK,更新时间触发器,0行 | 10列全映射 | 一致 | 基础对象可留;`site_device`为文本而非资源关系 |
| `train_class_report` | 11列,INT自增 | 11列,`bigint identity`,5个FK,计划/学员UNIQUE,0行 | 11列全映射 | 旧模型一致 | 应基于参训关系;报到行混入宿舍/教室/场地分配 |
| `train_electronic_board` | 14列 | 14列,PK,SN唯一,教室/计划FK,0行 | 14列全映射 | 旧模型一致 | 缺设备名称/接口配置;直接绑计划不能表达当前课次 |
| `train_student_checkin` | 10列,INT自增 | 10列,`bigint identity`,4个FK,旧课次组合UNIQUE,0行 | 10列全映射 | 旧模型一致 | 无`lesson_id`和参训关系;起止时间重复保存;没有签到/签退事件模型 |
| `train_score_info` | 10列,固定三类成绩 | 10列,`bigint identity`,3个FK,计划/课程/学员UNIQUE,0行 | 10列全映射 | 旧模型一致 | 被PRD“成绩项目+权重+学员得分”正式替代 |
| `train_study_duration` | 9列,INT自增 | 9列,`bigint identity`,3个FK,0行 | 9列全映射 | 一致 | PRD需参训关系、学习内容/来源和可幂等接入信息 |
| `train_student_portrait` | 9列,INT自增 | 9列,`bigint identity`,学员/计划UNIQUE,0行 | 9列全映射 | 基础一致 | 与PRD简单画像接近;需参训关系和雷达展示所需基础指标 |
## 2. 旧设计与实际Kingbase的结构差异
### 2.1 类型映射
- 设计 `DATETIME` 在Kingbase实际为 `timestamp`。
- 设计 `TINYINT` 在实际为 `smallint`。
- 设计6个 `INT AUTO_INCREMENT` 主键在实际为 `bigint GENERATED BY DEFAULT AS IDENTITY`。
- `train_teacher.t_id`、`train_student.s_id` 由设计的 `varchar(20)`扩大为实际 `varchar(32)`。
- `numeric(5,2)`、`numeric(8,2)` 精度与设计一致。
这些类型差异与当前 Java `Date`、`Long`、`BigDecimal`、`String` 基本兼容。
### 2.2 实际约束超过设计文本
实时库明确存在24个外键,以及以下业务唯一约束:
- 报到:`plan_id + student_id`
- 宿舍授权:`student_id + plan_id`
- 宿舍房间:`dorm_building + dorm_room`
- 班牌:`board_sn`
- 成绩:`plan_id + c_id + student_id`
- 签到:`plan_id + c_id + student_id + lesson_start_time`
- 画像:`student_id + plan_id`
这些约束未体现在 Entity注解或业务校验中,错误主要由数据库返回。除主键/唯一索引外,外键列没有额外非唯一索引。
### 2.3 更新时间
教师、学员、课程、计划、宿舍、宿舍授权、教室、场地、班牌9张表有UPDATE触发器维护 `update_time`。部分 ServiceImpl同时设置更新时间,形成数据库与应用双重维护。
## 3. 一班多课与课次迁移影响
### 当前结构
```text
train_class_plan
├── c_id NOT NULL -> train_course
└── t_id NOT NULL -> train_teacher
```
这会把“培训班”“本期课程”“具体授课安排”压在一行中。
### PRD V1.0要求的初步迁移方向
```text
开班计划
↔ 开班课程
→ 课次(教师、时间、教室或实训场地)
```
- `train_class_plan`保留培训班核心信息。
- `c_id`对应的信息迁入独立“开班课程”关系,一个计划可多条。
- `t_id`不应继续代表整个培训班唯一教师;PRD将教师安排到具体课次。
- 计划应补充开班级报到截止规则。
- 当前 `train_class_plan` 为0行,实时库不存在需要搬迁的历史计划;这降低数据风险,但不消除 Controller、页面、Mapper和外键改造范围。
本报告不确定最终表名、主键或DDL。
## 4. 参训关系缺口与迁移影响
当前没有独立 `student + plan` 参训关系。以下6张表重复保存两者:
| 当前表 | 重复字段 | 新增参训关系后的初步引用对象 | 当前行数 |
| --- | --- | --- | ---: |
| `train_dorm_auth` | `student_id + plan_id` | 住宿安排/门禁授权所属参训人 | 0 |
| `train_class_report` | `student_id + plan_id` | 报到所属参训人 | 0 |
| `train_student_checkin` | `student_id + plan_id` | 考勤所属参训人 | 0 |
| `train_score_info` | `student_id + plan_id` | 成绩所属参训人 | 0 |
| `train_study_duration` | `student_id + plan_id` | 学习记录所属参训人 | 0 |
| `train_student_portrait` | `student_id + plan_id` | 画像所属参训人 | 0 |
新增参训关系后,上述对象可以引用参训关系,避免每个模块独立解释学员在本期的资格和状态。同一学员/计划应由参训关系保证唯一。
当前6张表均为空,没有现存业务行需要转换。若数据库V2前产生数据,迁移时必须先按唯一的 `student_id + plan_id` 生成参训记录,再把报到、考勤、成绩、学习、画像、住宿/授权关联到相应参训记录。
## 5. `lesson_id` 对现有数据和代码的影响
### 当前签到
`train_student_checkin` 用:
```text
plan_id + c_id + student_id + lesson_start_time
```
模拟课次身份,并保存 `lesson_end_time`;唯一约束也以这组字段为基础。
### 加入正式课次后的影响
- 签到/考勤需要引用课次和参训关系,旧 `plan_id/c_id/lesson_start_time/lesson_end_time` 不再承担课次主键作用。
- `train_class_plan.t_id` 中的教师关系应进入课次。
- 教室和实训场地应由课次安排,不应继续由 `train_class_report.alloc_classroom_id/alloc_site_id` 作为学员报到分配。
- 班牌保留与教室关系;当前展示内容应由教室在当前时段的课次决定,`bind_plan_id`不能覆盖多班多课排期。
- Controller、查询条件、前端签到表单、唯一约束、统计维度和Mock班牌接口都会受影响。
- 当前计划、签到、教室、场地和班牌表均为0行,不存在历史课次数据;未来迁移不能只凭相同起止时间推断课次,需在产生数据前冻结V2。
## 6. 宿舍:资源、床位、住宿安排、门禁授权
当前表达为:
```text
train_dormitory_info(房间汇总+bed_total/bed_used+门锁SN)
→ train_dorm_auth(学员+计划+房间+bed_no+人脸同步状态)
← train_class_report.alloc_dorm_id(报到记录再次保存房间)
```
无法完整表达PRD V1.0的:
```text
宿舍资源 → 床位 → 住宿安排 → 门禁授权
```
初步影响:
- `train_dormitory_info`可作为房间资源的来源,但缺楼层、床位实体、男女属性和可靠剩余量。
- `bed_no`只是授权表文本,不能单独管理床位占用。
- `train_dorm_auth`同时保存住宿事实和设备同步事实,需要拆开看待。
- `train_class_report.alloc_dorm_id`与报到前预分配冲突;报到应读取已有住宿安排,而不是创建住宿事实。
- 这3张表当前均0行,没有现有住宿数据需要搬迁;V2仍需定义从旧授权行拆出住宿安排和门禁授权的规则,以防切换前产生数据。
## 7. 成绩模型变化
### 当前字段
`train_score_info` 固定:
```text
plan_id, c_id, student_id,
course_score, train_score, exam_score,
source_type, source_system, import_time
```
### PRD V1.0目标
```text
培训班 → 成绩项目 → 权重
参训学员 → 成绩项目 → 得分
```
### 初步迁移判断
- `course_score`、`train_score`、`exam_score` 三个固定分数字段将不再是最终模型,未来可能废弃或仅作为迁移来源。
- `plan_id`、`c_id`、`student_id` 可用于识别旧成绩所属培训班/课程/学员;其中学员+计划应先映射参训关系,课程或其他考核类型应映射成绩项目。
- `source_type`、`source_system`、`import_time` 的来源追溯语义仍有保留价值,可随具体得分记录迁移。
- 旧唯一约束 `plan_id+c_id+student_id` 无法支持同课程多个成绩项目或非课程考核。
- 当前 `train_score_info` 精确行数为0,因此不存在真实成绩迁移;从数据角度现在是调整模型的低风险窗口。
- 代码层仍有 Entity、Mapper、Controller、API、成绩CRUD、课程成绩页和学员成绩页依赖固定三列,因此不能只改数据库。所谓“安全重构”仅指没有生产成绩数据需要保留,不代表代码改造可以无评审完成。
## 8. PRD V1.0明确不应再作为一期阻塞的旧要求
- 课程版本、课程审批、草稿/发布/归档、复杂标签、热门、初级/进阶:PRD V1.0明确不做;当前代码缺少这些能力不是一期阻塞。
- 复杂画像规则引擎:一期只需基础指标、简单标签、雷达图。当前画像表可作为简单结果载体,主要缺参训关系、指标生成和雷达展示。
- 自动结业规则引擎:一期按培训结束、状态完成、历史数据保留处理。当前不应新增“签到率+成绩+时长”自动结业复杂逻辑。
## 9. 实时数据库数据对迁移窗口的影响
- 必须保留/迁移:`train_teacher`唯一1行教师数据。
- 当前无历史业务数据:其余13张培训表全部0行,包括成绩、计划、参训相关明细、住宿、签到、学习和画像。
- 需要保留并重新评审的系统配置:23条培训菜单行。
- 需要先处理的数据异常:`public.sys_role`为空,而用户角色、角色菜单关系已有孤儿数据;不能把这些关系直接当作有效权限基线迁入V2。
@@ -0,0 +1,136 @@
# Phase 0:培训菜单与权限实时快照
> 数据来自 `public.sys_menu`、`public.sys_role`、`public.sys_user_role`、`public.sys_role_menu` 的只读统计,不输出用户身份或密码字段。
## 1. 培训菜单是否真实存在
是。实时 `public.sys_menu` 中有23条培训相关菜单:6个目录、17个页面菜单。所有记录均为 `visible=0`、`status=0`,路径和组件没有重复。
| ID | 菜单 | 类型 | path | component | perms |
| ---: | --- | --- | --- | --- | --- |
| 2154 | 教学设施 | 目录 | `equip` | — | 空 |
| 2155 | 教师信息 | 页面 | `teacher` | `train/teacher` | 空 |
| 2156 | 学员信息 | 页面 | `student` | `train/student` | 空 |
| 2157 | 教室信息 | 页面 | `classroom` | `train/classroom` | 空 |
| 2158 | 实训场地 | 页面 | `site` | `train/site` | 空 |
| 2159 | 人员管理 | 目录 | `person` | — | 空 |
| 2160 | 设备物联 | 页面 | `extra` | `train/extra` | 空 |
| 2161 | 宿舍信息 | 页面 | `dormitory` | `train/dormitory` | 空 |
| 2162 | 课程管理 | 目录 | `coursemgr` | — | 空 |
| 2163 | 课程信息 | 页面 | `course` | `train/course` | 空 |
| 2164 | 开班计划 | 页面 | `plan` | `train/plan` | 空 |
| 2165 | 教学成果 | 目录 | `result` | — | 空 |
| 2166 | 成绩信息 | 页面 | `score` | `train/score` | 空 |
| 2167 | 自主学习 | 目录 | `self` | — | 空 |
| 2168 | 电子设备 | 页面 | `equipment` | `train/equipment` | 空 |
| 2169 | 统计分析 | 目录 | `analysis` | — | 空 |
| 2170 | 开班签到 | 页面 | `report` | `train/report` | 空 |
| 2171 | 课程签到 | 页面 | `checkin` | `train/checkin` | 空 |
| 2172 | 学习时长 | 页面 | `duration` | `train/duration` | 空 |
| 2173 | 学员画像 | 页面 | `portrait` | `train/portrait` | 空 |
| 2174 | 电子班牌 | 页面 | `board` | `train/board` | 空 |
| 2175 | 课程成绩 | 页面 | `course-score` | `train/course-score` | 空 |
| 2176 | 学员成绩 | 页面 | `student-score` | `train/student-score` | 空 |
17个 component 对应的 Vue文件在 `build_ui/src/views/` 中均存在。因此“菜单记录和页面组件存在”成立。
## 2. 权限字符串真实状态
- `public.sys_menu` 总计106行,其中79行具有权限字符串。
- 23条培训目录/页面中,具有权限字符串的行数为0。
- 培训区没有按钮类型 `F` 菜单记录。
- 实时库未找到 `train:*`、`system:teacher:*`、`system:student:*`、`system:course:*`、`system:plan:*`、`system:info:*`、`system:report:*`、`system:auth:*`、`system:site:*`、`system:board:*`、`system:checkin:*`、`system:duration:*` 或 `system:portrait:*` 培训权限。
因此数据库虽然能生成菜单路由,却不能向普通角色授予 Controller要求的培训操作权限,也不能满足前端按钮权限指令。
## 3. 与前端 `v-hasPermi` 的比较
多数生成页面使用:
```text
train:teacher:*
train:student:*
train:course:*
train:plan:*
train:auth:*
train:info:*
train:site:*
train:report:*
train:board:*
train:checkin:*
train:duration:*
train:portrait:*
```
实时 `sys_menu.perms` 没有这些字符串。宿舍与成绩当前页面没有 `v-hasPermi`按钮控制,但这不等于后端允许访问。
结论:普通用户即使获得页面菜单,生成页面的受控按钮也会因权限集合缺失而隐藏。
## 4. 与 Controller `@PreAuthorize` 的比较
后端使用 `system:*` 前缀:
| 资源 | Controller权限 |
| --- | --- |
| 教师 | `system:teacher:*` |
| 学员 | `system:student:*` |
| 课程 | `system:course:*` |
| 开班计划 | `system:plan:*` |
| 宿舍权限 | `system:auth:*` |
| 场地 | `system:site:*` |
| 报到 | `system:report:*` |
| 班牌 | `system:board:*` |
| 签到 | `system:checkin:*` |
| 学习时长 | `system:duration:*` |
| 画像 | `system:portrait:*` |
| 宿舍、教室、成绩 | 共用 `system:info:*` |
实时 `sys_menu` 同样没有这些权限。宿舍、教室、成绩共用 `system:info:*` 还会造成资源授权过宽;它们无法独立控制。
结论:前端、后端、数据库三方权限字符串没有任何一组形成一致闭环。
## 5. 角色与关系表真实状态
| 项目 | 实际情况 |
| --- | --- |
| `public.sys_user` | 1个有效应用用户;不输出身份明细 |
| `public.sys_role` | 0行 |
| 培训角色 | 0个 |
| `public.sys_user_role` | 20行;18个用户引用孤儿,20个角色引用全部孤儿 |
| `public.sys_role_menu` | 170行;170个角色引用全部孤儿,9个菜单引用孤儿 |
| `public.sys_role_dept` | 0行 |
应用用户ID 1存在且有效,若依代码通常对管理员提供通配权限;但当前数据库层还存在下述同名表解析问题,不能据此确认管理员登录路径正常。
## 6. Kingbase同名系统视图对权限体系的影响
Kingbase的 `sys_catalog` 提供同名 `sys_user`、`sys_role`、`sys_config` 视图,而实际若依表在 `public`。当前schema搜索顺序把 `sys_catalog` 放在 `public` 前。
使用与 Mapper相同的未限定表名进行只读探测时:
- `sys_user.user_id` 报字段不存在。
- `sys_role.role_id` 报字段不存在。
- `sys_config.config_id` 报字段不存在。
现有 `SysUserMapper.xml`、`SysRoleMapper.xml`、`SysConfigMapper.xml` 都使用未限定表名。这会影响用户加载、角色加载和系统配置查询,是普通角色权限之外的基础运行阻塞项。
## 7. PRD V1.0菜单对象缺口
实时菜单仍围绕旧14个CRUD和遗留占位页组织,未发现以下PRD V1.0正式业务页面:
- 本期课程/开班课程
- 参训学员关系
- 课次/课程表
- 床位与住宿安排
- 独立门禁授权流程
- 成绩项目与权重
- 培训班工作台
“开班签到”被放在“统计分析”目录下,也不符合PRD V1.0按培训班流程组织页面的建议。以上只记录现状,不在本轮修改菜单。
## 8. 最终判断
1. 培训菜单记录真实存在,component文件也存在。
2. 当前无法确认用户能正常登录并看到菜单,因为核心若依表存在schema解析冲突。
3. 普通角色权限当前不能正常工作:应用角色为0、关系表有孤儿、培训perms为空、前后端权限字符串不同。
4. 没有发现重复培训path/component;主要问题是权限缺失和 `system:info:*` 过宽。
@@ -0,0 +1,135 @@
# Kingbase 只读兼容性核验
## 1. 核验边界与方法
本报告针对当前项目实际连接的 Kingbase 实例执行只读探测。连接复用现有数据源配置,通过项目已有 Kingbase JDBC 驱动建立;事务设置为只读,全部业务探测均使用 `SELECT`、`SHOW` 或系统目录查询,结束时执行回滚并关闭连接。未执行任何 DDL、DML、迁移或生成键写入测试,也未在报告中记录连接密码。
当前环境证据:
| 项目 | 实际结果 |
| --- | --- |
| 数据库产品 | KingbaseES V009R003C018 |
| JDBC 驱动 | Kingbase8 JDBC Driver V008R006B0001;Maven 依赖版本 8.6.0 |
| database | `training` |
| schema | `public` |
| `database_mode` | `mysql` |
| 服务端兼容基线 | `server_version` 返回 12.1 |
| 简单 SELECT | 正常 |
## 2. 指定语法的实测结果
| 探测项 | 无副作用测试形式 | 实际结果 | 分类 | 对当前代码的含义 |
| --- | --- | --- | --- | --- |
| `date_format` | 对固定 timestamp 格式化为 `%Y%m%d` | 返回 `20260102` | 兼容模式支持 | 若依 Mapper 中同类写法可被当前实例解析 |
| `ifnull` | `ifnull(null,'fallback')` | 返回 `fallback` | 兼容模式支持 | 当前 Mapper 中的空值表达式可被解析 |
| `find_in_set` | `find_in_set('2','1,2,3')` | 返回 2 | 兼容模式支持 | 若依数据权限中同类表达式具备语法基础 |
| `current_database()` | 查询当前数据库 | 返回 `training` | 当前实例直接支持 | 函数本身可用,但不能代替当前 schema,见第4节 |
| `sysdate()` | 查询当前时间 | 返回 timestamp | 兼容模式支持 | MySQL 风格当前时间函数可用 |
| `LIMIT 1 OFFSET 1` | 常规分页 SELECT | 正常返回 | 当前实例直接支持 | PostgreSQL/Kingbase 风格分页可用 |
| `LIMIT 0,1` | MySQL 风格分页 SELECT | 正常返回 | 兼容模式支持 | 当前 `PageHelper helperDialect=mysql` 具备语法基础 |
| `SELECT 1 FROM dual` | 数据源校验形式 | 返回 1 | 兼容模式支持 | 当前 Druid validation query 可用 |
| 反引号标识符 | 使用反引号访问 `public.sys_menu` 字段 | 正常返回 | 兼容模式支持 | 代码生成器中的反引号可被当前实例解析 |
本次明确列出的 MySQL 语法探测均通过。结论只适用于当前 `database_mode=mysql` 实例,不能据此推断其他 Kingbase 实例或其他兼容模式也会通过。
## 3. PageHelper 分页判断
配置依据:
- `ruoyi-admin/src/main/resources/application.yml` 中 PageHelper 使用 `helperDialect: mysql`。
- 当前实例能够执行 `LIMIT offset,row_count`,所以 PageHelper 常见的 MySQL 分页尾句在语法层面可用。
只读测试没有启动完整 Web 请求链,也没有核对每一种复杂 Mapper SQL 经 PageHelper 改写后的结果。当前可判定为“分页基础语法兼容”,完整插件行为仍需后续集成测试。
## 4. 已确认的应用级兼容冲突
### 4.1 代码生成器把 database 当作 schema
当前代码生成器在信息模式查询中使用:
```sql
table_schema = current_database()
```
当前 `current_database()` 是 `training`,实际业务表 schema 是 `public`。只读核验结果:
- 按当前生成器条件查询:0 张表;
- `public` schema 实际有 46 张表;
- `public.train_teacher` 实际有 12个字段,按当前生成器条件查询为0个字段。
因此这不是 SQL 函数不支持,而是 database 与 schema 语义被混用。当前代码生成模块无法依靠这组查询正确发现实际表结构,数据库 V2 前必须明确处理。
相关文件:
- `ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml`
- `ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml`
### 4.2 `sys_*` 名称与 Kingbase 系统视图冲突
当前隐式 schema 搜索顺序为:
```text
sys, pg_catalog, sys_catalog, public
```
`sys_catalog` 中存在 `sys_user`、`sys_role`、`sys_config` 系统视图,`public` 中同时存在若依同名业务表。当前若依 Mapper 普遍使用未限定 schema 的表名。只读探测显示:
- 未限定的 `sys_user.user_id` 查询失败,命中了系统视图;
- 未限定的 `sys_role.role_id` 查询失败,命中了系统视图;
- 未限定的 `sys_config.config_id` 查询失败,命中了系统视图;
- 显式访问 `public.sys_user`、`public.sys_role`、`public.sys_config` 时结构可正常读取。
因此若依登录、角色和配置等核心 Mapper 当前具备真实运行阻塞。该问题不能由“数据库支持 MySQL 函数”这一结论覆盖。
相关文件:
- `ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml`
- `ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml`
- `ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml`
## 5. generated keys 与 identity/sequence
当前6张数字主键业务表的主键实际为 `bigint GENERATED BY DEFAULT AS IDENTITY`,数据库目录能够识别对应内部 sequence。JDBC 元数据结果:
- `IS_AUTOINCREMENT=YES`;
- 驱动声明 `supportsGetGeneratedKeys=true`。
这说明 MyBatis `useGeneratedKeys` 具备数据库和驱动基础条件。由于实际插入会修改数据,本阶段没有执行 INSERT,也不能确认所有 Mapper 的生成键回填行为。该项分类为“基础条件满足,完整行为无法只读确认”。
## 6. 分类结论
### 当前实例直接支持
- `current_database()`;
- `LIMIT ... OFFSET ...`;
- 标准只读事务与系统目录查询;
- identity/sequence 元数据读取。
### 兼容模式支持
- `date_format`;
- `ifnull`;
- `find_in_set`;
- `sysdate()`;
- MySQL 风格 `LIMIT offset,row_count`;
- `DUAL`;
- 反引号标识符。
### 明确不支持
本次指定且可安全执行的语法项中,没有发现当前实例明确不支持的项。
### 无法只读确认
- MyBatis 在真实 INSERT 场景中的 generated keys 回填;
- PageHelper 对全部复杂 Mapper SQL 的改写和计数行为;
- 触发器、批量写入、导入导出等写链路的完整兼容性。
### 已确认但不属于语法缺失的问题
- 代码生成器以 `current_database()` 过滤 `table_schema`,当前返回空结果;
- 未限定 schema 的若依 `sys_*` Mapper 命中 Kingbase 系统视图,核心账号与权限查询会失败。
## 7. Phase 0判断
当前 Kingbase MySQL 兼容模式覆盖了项目已识别的主要 MySQL 函数和分页形式,但项目不能据此视为“已完成 Kingbase 适配”。数据库 V2 前应先确定 schema 解析策略、系统表访问策略和生成键集成验证方案;本报告只记录现状,不提出或实施源码、配置、数据库结构修复。
+211
View File
@@ -0,0 +1,211 @@
# RailwaySystem Phase 0 工程与数据库真实基线总结
> 业务判断最高依据:`doc/RailwaySystem 智慧培训系统一期 PRD V1.0.md`。旧数据库设计、V0.9、详细开发说明、HTML 原型和既有生成模型仅作为现状证据;与 V1.0 冲突时,以 V1.0 为准。
## 1. 当前可以作为正式开发基线的是哪个代码状态?
当前可识别的提交基线是:
- branch:`main`;
- HEAD:`e12eda059679b89eb5d09215ffe1ce7132f161f2`;
- 最新提交:`docs(prd): 发布RailwaySystem智慧培训系统一期PRD V1.0正式业务基线`;
- 该提交已经包含成绩、宿舍、教室接口别名、本地启动脚本、Node 22 适配、Kingbase 本地连接适配和 PRD V1.0。
当前工作树不能整体直接称为一个新的正式提交基线:其中有一组未提交的旧材料移动,即 `doc/` 下3个旧文件表现为已跟踪删除,`doc/wxy_doc/` 下对应材料表现为未跟踪文件;`doc/wxy_doc/readme.md` 明确标注这些资料暂不使用。除此之外,核验开始时没有未提交的 Java、Vue、API、SQL 或运行配置代码。
因此,继续开发应以 `e12eda0` 的代码内容和 PRD V1.0 为起点;开始数据库 V2 工作前,还应先决定是否正式接纳这组旧文档归档变化。本次新增的 `12`—`17` 报告是审计产物,没有提交。
## 2. 14张培训表实际存在多少张?
14张全部存在于 `public` schema,数据库中也没有第15张 `train_%` 表。
实际数据量:
- `train_teacher`:1行;
- 其余13张培训表:均为0行。
所有表均有主键;6张数字主键表使用 `bigint GENERATED BY DEFAULT AS IDENTITY`,8张字符串主键表没有数据库默认生成器。数据库还存在24个培训表外键、7组业务唯一约束和9个更新时间触发器。
## 3. 实际表结构和当前代码差异有哪些?
从“数据库字段是否能被当前生成代码映射”看,14个 Mapper XML 的 `resultMap` 字段数与实际表字段数一致,实际157个字段均能在当前 Mapper 中找到映射,Entity 的 `String`、`Date`、`Long`、`BigDecimal` 类型也基本兼容。
主要差异和风险是:
1. 旧设计文档中的 `t_id`、`s_id` 是 `varchar(20)`,实际为 `varchar(32)`;`DATETIME` 落为 timestamp,`TINYINT` 落为 smallint,6个 `INT AUTO_INCREMENT` 落为 bigint identity。
2. 实际数据库比旧设计文档多出外键、唯一约束和更新时间触发器,迁移时不能只按 Markdown 字段表重建。
3. 多个 Java 日期字段使用 `@JsonFormat(pattern = "yyyy-MM-dd")`,数据库能够保存时分秒,但接口序列化可能只暴露日期。
4. 当前代码与现有表结构相互匹配,不代表满足 PRD V1.0。`train_class_plan.c_id/t_id`、无参训关系、无课次、住宿与授权耦合、签到无 `lesson_id`、成绩固定三列,均与正式业务对象冲突。
5. 当前训练表 DDL 没有形成仓库内可追溯的初始化/迁移来源,现有数据库约束和触发器只能从实例读取。
详见 `13_live_database_snapshot.md` 和 `14_database_code_diff.md`。
## 4. 当前数据库是否真正使用Kingbase?
是。通过现有数据源配置和项目已有 JDBC 驱动成功建立只读连接,实测信息为:
- KingbaseES V009R003C018;
- database:`training`;
- schema:`public`;
- 当前用户:`kingbase`;
- `database_mode=mysql`;
- Maven 驱动依赖版本:8.6.0。
当前数据源通过既有的本地 `127.0.0.1:4321` SSH/Tailscale 隧道连接。简单 SELECT 正常。整个核验事务为只读并已回滚关闭,没有修改数据库。
## 5. 当前Kingbase兼容模式能支持多少现有MySQL语法?
本次指定且可安全执行的语法探测全部通过:
- `date_format`;
- `ifnull`;
- `find_in_set`;
- `current_database()`;
- `sysdate()`;
- `LIMIT ... OFFSET ...`;
- MySQL 风格 `LIMIT offset,row_count`;
- `SELECT 1 FROM dual`;
- 反引号标识符。
当前 `PageHelper helperDialect=mysql` 的分页尾句具备语法基础,identity 和 JDBC generated keys 的元数据条件也满足。由于本阶段禁止写入,真实 INSERT 生成键回填和完整 PageHelper 请求链仍无法只读确认。
同时已经确认两个比单项语法更严重的应用级问题:
1. 代码生成器用 `table_schema=current_database()`,当前 database 为 `training`、schema 为 `public`,所以生成器按现有 SQL 查到0张表、0个字段。
2. Kingbase 的 `sys_catalog` 存在 `sys_user`、`sys_role`、`sys_config` 系统视图;当前搜索顺序优先于 `public`。若依 Mapper 使用未限定 schema 的表名时会命中系统视图并因字段不匹配失败。
因此结论是“列出的 MySQL 语法在当前实例中全部可用”,不能表述为“项目已经完成 Kingbase 适配”。
## 6. 培训菜单是否真实存在?
真实存在。`public.sys_menu` 中有23条培训相关菜单记录:6个目录、17个组件页面,ID 范围为2154—2176。17个 component 均能在前端找到对应 Vue 页面。
但这些记录存在关键缺口:
- 全部 `perms` 为空;
- 没有培训按钮权限记录;
- 没有 plan-course、参训关系、课次/课表、床位/住宿安排、独立门禁授权、成绩项目/权重和培训班工作台页面;
- 前端按钮主要使用 `train:*`,Controller 主要使用 `system:*`,数据库菜单没有对应权限字符串。
所以“菜单数据存在”和“培训权限链路可用”是两个不同结论。
## 7. 普通角色权限是否可能正常工作?
按当前实时数据,不能认为普通角色权限可以正常工作:
1. `public.sys_role` 为0行,没有培训角色。
2. `public.sys_user_role` 有20行,其中18行找不到对应应用用户,20行全部找不到对应应用角色。
3. `public.sys_role_menu` 有170行,全部找不到对应应用角色,另有9行找不到菜单。
4. 培训菜单 `perms` 全为空,前端和后端权限字符串也不一致。
5. 未限定的核心 `sys_user`、`sys_role`、`sys_config` 查询会命中 Kingbase 系统视图,登录、角色和配置查询本身存在阻塞。
若依已有 RBAC、菜单、按钮、数据权限和日志框架仍值得复用,但当前数据库权限数据及 schema 解析必须先核实和修复,不能据此新造一套账号权限系统。本阶段没有修改任何 `sys_*` 表。
## 8. 哪些问题必须在数据库V2以前处理?
1. 固定正式代码基线,并决定当前旧文档归档移动是否进入后续提交,避免 V2 设计建立在不清晰工作树上。
2. 明确若依 `sys_*` 应用表的 schema 访问方式,解决与 Kingbase 系统视图的名称冲突;在解决前,登录和权限核验不具备可靠基础。
3. 查明 `sys_role` 为空及用户—角色—菜单关系大量孤儿的来源,制定非破坏性恢复或迁移方案;不能直接改 `sys_*` 表试错。
4. 建立可审查、可版本化的数据库结构来源,完整保留实际外键、唯一约束、identity 和触发器信息。
5. 按 PRD V1.0 先确认开班计划—课程、参训关系和课次三个核心关系,再决定当前 CRUD 的字段去留。
6. 确认床位、住宿安排和门禁授权的业务边界,避免延续当前住宿事实与设备授权耦合。
7. 确认成绩项目/权重与参训学员得分的迁移边界,停止把固定三列当成最终成绩模型。
8. 统一数据库菜单、前端 `v-hasPermi` 和 Controller `@PreAuthorize` 的权限命名,并补齐按钮权限设计。
9. 明确字符串业务主键、identity 主键和 MyBatis 生成键的统一策略,并用后续可回滚集成环境验证真实写链路。
10. 明确时间字段是否需要时分秒,避免数据库 timestamp 与 Java `yyyy-MM-dd` 输出之间继续产生语义差异。
11. 在学员 V2 模型中确定身份证等敏感字段的可逆加密、脱敏展示和访问权限方案。
12. 修正代码生成器对 database/schema 的错误假设后,再把它作为 V2 代码生成依据。
## 9. 哪些现有数据必须迁移/保留?
当前培训业务只有 `train_teacher` 的1行真实数据需要明确保留和迁移;其他13张培训表为空,没有成绩、签到、住宿、报到或画像历史业务数据。
仍需保留或纳入迁移评估的非业务明细包括:
- 23条培训菜单的层级、名称、component 和排序,可作为 V1.0 菜单迁移底稿;
- `sys_user_role`、`sys_role_menu` 的现有记录需要先确认来源,虽然当前为孤儿关系,也不应直接删除;
- 14张表的实际约束、identity、sequence 和触发器定义;
- 唯一一条教师记录的标识、业务字段及审计时间。
由于其他培训表均为空,以下业务模型的数据迁移成本当前较低:
- `train_class_plan.c_id/t_id` 转向开班计划—课程及课次关系;
- 重复的 `student_id + plan_id` 转向正式参训关系;
- 签到转向 `lesson_id`;
- 宿舍/授权拆分;
- 固定三列成绩转向成绩项目和学员得分。
“数据为空”只降低历史数据迁移风险,不降低代码、接口、菜单和约束迁移工作量。
## 10. 哪些现有CRUD值得继续复用?
### 可优先复用业务外壳
- 教师、学员、课程、教室、实训场地:列表、分页、表单校验、导入导出、权限注解和基础 Mapper 结构可继续作为实现底稿。
- 电子班牌:设备资源 CRUD 外壳可保留,但与班级、教室、课次的关系需按 V1.0 重新确认。
- 开班计划、报到、学习时长、简单画像:页面和接口骨架可复用,领域字段及关联对象需要调整。
### 只能部分复用
- 宿舍房间:资源维护部分可复用,必须补足床位对象;
- 宿舍授权:现有 CRUD 可作为历史字段和设备接入参考,不能继续同时承担住宿安排与门禁授权;
- 签到:采集来源、时间和状态字段有参考价值,关联主线需要从 plan/course/time 转向参训关系与课次;
- 成绩:导入来源、录入人、录入时间等审计能力可参考,固定三列成绩及当前唯一约束不能作为最终模型。
### 不应按现状继续扩展
- 遗留的 `person`、`character`、`analysis`、`weather` 等页面没有形成 PRD V1.0 核心业务链,不应被当作领域模型依据。
- 现有代码没有课程版本/审批、复杂画像规则和自动结业引擎,这些缺失不构成一期阻塞。
## 11. 哪些表可以原地改造,哪些领域明显需要新增表?
本节仅作领域级初步分类,不确定最终表名、字段、主键、外键或迁移 DDL。
### 可保留主体并调整关联或字段
- `train_teacher`、`train_student`、`train_course`;
- `train_class_plan` 的计划主体;
- `train_classroom_info`、`train_training_site`;
- `train_electronic_board`;
- `train_class_report`、`train_student_checkin`、`train_study_duration`、`train_student_portrait`。
### 明显需要拆分或由新模型承接
- `train_dormitory_info` 目前只有房间层级,床位需要成为独立正式对象;
- `train_dorm_auth` 同时承载住宿事实和授权事实,需要由住宿安排与门禁授权分别表达;
- `train_score_info` 固定三列成绩不符合 V1.0,应由成绩项目/权重与参训学员得分模型承接。
### PRD V1.0已经明确、当前缺失的正式关系或对象
- 开班计划—课程关系;
- 参训关系;
- 课次;
- 教学资源及课程资源关系;
- 床位;
- 住宿安排;
- 独立门禁授权;
- 成绩项目及权重;
- 参训学员按成绩项目的得分。
最终哪些对象使用新表、关系表或原表改造,应在数据库 V2 评审中确定。
## 12. 当前14张培训表与 PRD V1.0 核心对象的映射
| 当前表 | PRD V1.0对应对象 | 判断 |
| --- | --- | --- |
| `train_teacher` | 教师 | 修改 |
| `train_student` | 学员 | 修改 |
| `train_course` | 课程 | 修改 |
| `train_class_plan` | 开班计划 | 修改 |
| `train_dormitory_info` | 宿舍资源、房间、床位 | 拆分 |
| `train_dorm_auth` | 住宿安排、门禁授权 | 拆分 |
| `train_classroom_info` | 教室 | 原样保留 |
| `train_training_site` | 实训场地 | 原样保留 |
| `train_class_report` | 报到 | 修改 |
| `train_electronic_board` | 电子班牌 | 修改 |
| `train_student_checkin` | 课次考勤 | 修改 |
| `train_score_info` | 成绩项目、权重、参训学员得分 | 被新模型替代 |
| `train_study_duration` | 学习记录/学习时长 | 修改 |
| `train_student_portrait` | 基础指标、简单标签、雷达图 | 修改 |
这里的“原样保留”表示其资源主体可以继续作为 V2 基础,不代表无需增加审计字段、关联表或约束;“修改、拆分、被新模型替代”也只是 Phase 0 初判,不构成最终数据库 V2 设计。