diff --git a/docs/codebase-audit/12_baseline_classification.md b/docs/codebase-audit/12_baseline_classification.md new file mode 100644 index 000000000..05378e5bd --- /dev/null +++ b/docs/codebase-audit/12_baseline_classification.md @@ -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`。 diff --git a/docs/codebase-audit/13_live_database_snapshot.md b/docs/codebase-audit/13_live_database_snapshot.md new file mode 100644 index 000000000..e0144780c --- /dev/null +++ b/docs/codebase-audit/13_live_database_snapshot.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解析冲突是独立阻塞项。 diff --git a/docs/codebase-audit/14_database_code_diff.md b/docs/codebase-audit/14_database_code_diff.md new file mode 100644 index 000000000..fc464358e --- /dev/null +++ b/docs/codebase-audit/14_database_code_diff.md @@ -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。 diff --git a/docs/codebase-audit/15_live_permission_snapshot.md b/docs/codebase-audit/15_live_permission_snapshot.md new file mode 100644 index 000000000..98615ceb4 --- /dev/null +++ b/docs/codebase-audit/15_live_permission_snapshot.md @@ -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:*` 过宽。 diff --git a/docs/codebase-audit/16_kingbase_readonly_compatibility.md b/docs/codebase-audit/16_kingbase_readonly_compatibility.md new file mode 100644 index 000000000..ee806c06b --- /dev/null +++ b/docs/codebase-audit/16_kingbase_readonly_compatibility.md @@ -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 解析策略、系统表访问策略和生成键集成验证方案;本报告只记录现状,不提出或实施源码、配置、数据库结构修复。 diff --git a/docs/codebase-audit/17_phase0_summary.md b/docs/codebase-audit/17_phase0_summary.md new file mode 100644 index 000000000..8c54b09dd --- /dev/null +++ b/docs/codebase-audit/17_phase0_summary.md @@ -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 设计。