From 4b450f46af2def28bb5c9098f28abc0dd41b9e32 Mon Sep 17 00:00:00 2001 From: wangzhibiao Date: Mon, 14 Sep 2026 00:52:39 +0800 Subject: [PATCH] =?UTF-8?q?docs(=E5=AE=A1=E8=AE=A1):=20=E8=BE=93=E5=87=BAP?= =?UTF-8?q?hase=200.5=E5=9F=BA=E7=A1=80=E8=BF=90=E8=A1=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E4=B8=8E=E7=AB=AF=E5=88=B0=E7=AB=AF=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E5=AE=A1=E8=AE=A1=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 发布 docs/codebase-audit/18_phase05_runtime_fix.md,完整记录 search_path 实验结果、数据源双保险配置方案、登录链验证数据及代码生成器元数据验证 - 在 docs/README.md 核心综述清单中收录 18_phase05_runtime_fix.md 并更新审计报告索引至 19 篇 --- docs/README.md | 3 +- docs/codebase-audit/18_phase05_runtime_fix.md | 252 ++++++++++++++++++ 2 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 docs/codebase-audit/18_phase05_runtime_fix.md diff --git a/docs/README.md b/docs/README.md index 8039e81ef..eb6ff3eb5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,10 +18,11 @@ ## 代码与数据全景审计报告清单(`codebase-audit/`) -本目录下的 [codebase-audit/](file:///Users/wzb/Desktop/项目/RailwaySystem/docs/codebase-audit/) 包含完整的 18 篇技术审计报告,记录了对工程现状、源码实现、数据库实际 schema 与权限体系的详尽核验: +本目录下的 [codebase-audit/](file:///Users/wzb/Desktop/项目/RailwaySystem/docs/codebase-audit/) 包含完整的 19 篇技术审计报告,记录了对工程现状、源码实现、数据库实际 schema 与权限体系的详尽核验: ### 1. 核心综述与基线标准 +- [18_phase05_runtime_fix.md](file:///Users/wzb/Desktop/项目/RailwaySystem/docs/codebase-audit/18_phase05_runtime_fix.md) —— ★★★ **Phase 0.5 基础运行修复与端到端验证审计报告(Kingbase schema与生成器适配)** - [17_phase0_summary.md](file:///Users/wzb/Desktop/项目/RailwaySystem/docs/codebase-audit/17_phase0_summary.md) —— ★★★ **Phase 0 工程与数据库真实基线总结(进入下一阶段前必读)** - [11_executive_summary.md](file:///Users/wzb/Desktop/项目/RailwaySystem/docs/codebase-audit/11_executive_summary.md) —— 全景代码审计执行摘要 - [12_baseline_classification.md](file:///Users/wzb/Desktop/项目/RailwaySystem/docs/codebase-audit/12_baseline_classification.md) —— 真实代码基线与工程资产可信度分级 diff --git a/docs/codebase-audit/18_phase05_runtime_fix.md b/docs/codebase-audit/18_phase05_runtime_fix.md new file mode 100644 index 000000000..b93799a40 --- /dev/null +++ b/docs/codebase-audit/18_phase05_runtime_fix.md @@ -0,0 +1,252 @@ +# Phase 0.5 基础运行修复审计报告 + +> **基线状态说明** +> - **开发基线**:`main@e12eda05` +> - **业务依据**:`doc/RailwaySystem 智慧培训系统一期 PRD V1.0.md` +> - **数据库实例**:KingbaseES V009R003C018 (`database=training`, `schema=public`, `database_mode=mysql`) +> - **本阶段定位**:基础设施层兼容性修复(解决 RuoYi + Kingbase 基础运行冲突),不涉及任何业务模型修改与数据库结构破坏。 + +--- + +## 1. 修改前问题 + +在 Phase 0 审计中发现,RuoYi 与 KingbaseES 集成存在两个阻塞基础运行的核心冲突: + +### 1.1 `sys_*` 系统表与 Kingbase 系统目录视图冲突 +1. **隐式模式搜索优先级**:KingbaseES 当前连接默认的有效模式搜索顺序(`current_schemas(true)`)为 `{sys, pg_catalog, sys_catalog, public}`。 +2. **系统同名视图拦截**:Kingbase 内置系统目录模式 `sys_catalog` 中预置了 `sys_user`、`sys_role`、`sys_config` 等系统级视图(如 `sys_catalog.sys_user` 包含 `usesysid`, `userepl` 等列,无 `user_id` 列)。 +3. **若依未限定 SQL 命中异常**:RuoYi 核心系统 Mapper(如 [SysUserMapper.xml](file:///Users/wzb/Desktop/项目/RailwaySystem/RuoYi-Vue/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml)、[SysRoleMapper.xml](file:///Users/wzb/Desktop/项目/RailwaySystem/RuoYi-Vue/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml)、[SysConfigMapper.xml](file:///Users/wzb/Desktop/项目/RailwaySystem/RuoYi-Vue/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml))普遍使用未限定表名查询(`from sys_user`、`from sys_role`、`from sys_config`)。 +4. **运行后果**:执行未限定查询时直接命中 `sys_catalog` 视图,抛出 `ERROR: column "user_id" does not exist`、`column "role_id" does not exist`,导致登录认证与系统初始化彻底瘫痪。 + +### 1.2 代码生成器混淆 database 与 schema +1. **函数语义错位**:代码生成器 [GenTableMapper.xml](file:///Users/wzb/Desktop/项目/RailwaySystem/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml) 与 [GenTableColumnMapper.xml](file:///Users/wzb/Desktop/项目/RailwaySystem/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml) 中使用 `where table_schema = (select current_database())`。 +2. **检索为空**:在 Kingbase 体系下,`current_database()` 返回的是数据库名(`training`),而实际表与字段所属的元数据模式为 `public`(`information_schema.tables.table_schema = 'public'`)。 +3. **运行后果**:代码生成器在“导入表”与“列信息加载”时无法匹配到任何物理表(返回 0 张表、0 个字段),无法支持后续的模型逆向工程与代码生成。 + +--- + +## 2. search_path 实验结果 + +通过原生 Kingbase JDBC 驱动(`com.kingbase8.Driver` 8.6.0)在当前开发环境建立隔离测试 Session,实测数据如下: + +### 2.1 默认连接探测 +```sql +show search_path; +-- 输出: public + +select current_schemas(true); +-- 输出: {sys, pg_catalog, sys_catalog, public} + +select user_id from sys_user; +-- 输出异常: ERROR: column "user_id" does not exist +-- 提示: Hint: Perhaps you meant to reference the column "sys_user.usesysid" or the column "sys_user.userepl". +``` +**结论**:默认虽然 `search_path=public`,但 Kingbase 将系统目录 `sys_catalog` 隐式置于 `public` 之前,未限定表名必定命中系统视图。 + +### 2.2 设置 search_path 探测 +在同一 Session 中显式执行: +```sql +SET search_path TO public, sys_catalog; +``` +再次查询状态与数据: +```sql +show search_path; +-- 输出: public, sys_catalog + +select current_schemas(true); +-- 输出: {sys, pg_catalog, public, sys_catalog} +``` +验证各系统表命中情况: +| SQL 探测语句 | 执行结果 | 判定 | +| --- | --- | --- | +| `select user_id from sys_user;` | 成功返回 1 行 (`user_id=1`) | 稳定命中 `public.sys_user` | +| `select role_id from sys_role;` | 成功返回 0 行 (表结构识别正确) | 稳定命中 `public.sys_role` | +| `select config_id from sys_config;` | 成功返回 0 行 (表结构识别正确) | 稳定命中 `public.sys_config` | +| `select count(*) from sys_menu;` | 成功返回 106 | 稳定命中 `public.sys_menu` | +| `select count(*) from sys_dept;` | 成功返回 15 | 稳定命中 `public.sys_dept` | +| `select count(*) from sys_dict_type;` | 成功返回 44 | 稳定命中 `public.sys_dict_type` | +| `select count(*) from sys_dict_data;` | 成功返回 500 | 稳定命中 `public.sys_dict_data` | + +**实验结论**:只要显式将 `sys_catalog` 置于 `public` 之后,`current_schemas(true)` 中 `public` 就会优先于 `sys_catalog`,所有未限定表名的查询均能稳定、准确地命中 `public` 业务表。 + +--- + +## 3. 最终 schema 解决方式 + +### 3.1 架构设计选型 +为满足“只影响 RailwaySystem 连接、不修改 Kingbase 全局配置、不批量修改所有 RuoYi Mapper、修改高度集中”的要求,采用**应用连接池层双保险适配方案**: + +1. **第一层:JDBC URL `currentSchema` 参数** + - 将 JDBC URL 中的 `currentSchema=public` 升级为 `currentSchema=public,sys_catalog`。 + - Kingbase JDBC 驱动在建立物理 Socket 握手初始化时,直接依据该参数初始化会话 `search_path`。 +2. **第二层:Druid 连接池 `connectionInitSqls` 机制** + - 在 Druid 数据源配置中声明:`connectionInitSqls: ["SET search_path TO public, sys_catalog"]`。 + - DruidDataSource 保证在每次物理连接创建(`createPhysicalConnection`)完成后,都会执行一次该初始化 SQL,杜绝连接归还与重用过程中可能出现的模式搜索漂移。 + +### 3.2 方案优势 +- **无侵入性**:无需修改任何 `Sys*Mapper.xml` 业务 SQL; +- **环境安全**:完全在应用连接会话级(Session Scope)生效,零触碰 Kingbase 数据库实例级全局参数; +- **幂等稳定**:连接池无论是初始建立还是故障重连,均自动具备正确的优先级。 + +--- + +## 4. 修改文件列表 + +本轮 Phase 0.5 基础运行修复严格限制在基础设施与元数据映射层,共修改 3 个文件: + +| 序号 | 文件路径 | 修改性质 | 核心修改说明 | +| --- | --- | --- | --- | +| 1 | [application-druid.yml](file:///Users/wzb/Desktop/项目/RailwaySystem/RuoYi-Vue/ruoyi-admin/src/main/resources/application-druid.yml) | 运行配置 | master 数据源 URL 增加 `currentSchema=public,sys_catalog`,并注入 `connectionInitSqls` | +| 2 | [GenTableMapper.xml](file:///Users/wzb/Desktop/项目/RailwaySystem/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml) | 框架映射 | 将表检索中 3 处 `(select current_database())` 改为 `(select current_schema())` | +| 3 | [GenTableColumnMapper.xml](file:///Users/wzb/Desktop/项目/RailwaySystem/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml) | 框架映射 | 将列检索中 1 处 `(select current_database())` 改为 `(select current_schema())` | + +--- + +## 5. 登录链验证结果 + +在后端与前端完整启动后,对 RuoYi 核心认证链执行端到端自动化探测,验证数据汇总如下: + +| 步骤 | 请求方式与端点 | HTTP 状态 | 业务响应码 | 核心响应要素与凭据 | 状态判定 | +| --- | --- | --- | --- | --- | --- | +| 1 | `GET /captchaImage` | 200 OK | `code: 200` | 返回图片 Base64,签发 `uuid: c91ad82eec14407ea33a3207de3bec05` | 成功 | +| 2 | `POST /login` | 200 OK | `code: 200` | 账号 `admin`,获得 JWT 令牌(长度 203,前缀 `eyJhbGciOiJIUzUxMiJ9...`) | 成功 | +| 3 | `GET /getInfo` | 200 OK | `code: 200` | `userId=1`, `userName=admin`, `roles=['admin']`, `permissions=['*:*:*']` | 成功 | +| 4 | `GET /getRouters` | 200 OK | `code: 200` | 成功拉取 10 个顶层动态路由(Person, Equip, Coursemgr, Monitor, Result, Tool, Self, Analysis, System 等) | 成功 | +| 5 | 前端入口 `GET http://localhost:8080` | 200 OK | - | 前端工程完整返回 index.html,资源正常加载 | 成功 | + +> **说明**:当前 `public.sys_role` 中为 0 条记录,本次验证充分利用了 RuoYi 原生超级管理员(`admin` / `userId=1`)的豁免与全权限逻辑,证实认证主干通路在 Kingbase 环境下已彻底畅通。 + +--- + +## 6. Generator 修改方式 + +### 6.1 语义区分与原则 +- **明确概念边界**:`database`(在 Kingbase 为 `training`)与 `schema`(在 Kingbase 为 `public`)在非 MySQL 兼容原生库中必须严格区分; +- **杜绝硬编码**:不将 `training` 或 `'public'` 字符串硬编码或散落在大量 XML 中; +- **动态环境感知**:利用 Kingbase/PostgreSQL 内置标准函数 `current_schema()`,该函数能够动态求值当前会话首选活动 schema(即返回当前 search_path 的首位 `public`)。 + +### 6.2 具体修改点 +1. **`GenTableMapper.xml`**: + - `selectDbTableList`(待导入表分页查询): + ```xml + + where table_schema = (select current_database()) + + where table_schema = (select current_schema()) + ``` + - `selectDbTableListByNames`(批量表查询): + ```xml + + and table_schema = (select current_database()) + + and table_schema = (select current_schema()) + ``` + - `selectTableByName`(单表元数据查询): + ```xml + + and table_schema = (select current_database()) + + and table_schema = (select current_schema()) + ``` +2. **`GenTableColumnMapper.xml`**: + - `selectDbTableColumnsByName`(表列结构查询): + ```xml + + from information_schema.columns where table_schema = (select current_database()) and table_name = (#{tableName}) + + from information_schema.columns where table_schema = (select current_schema()) and table_name = (#{tableName}) + ``` + +--- + +## 7. Generator 验证结果 + +针对代码生成器执行了纯元数据只读验证,未执行任何物理文件生成,未覆盖项目代码。 + +### 7.1 表清单检索验证 (`selectDbTableList`) +- **待导入表识别总数**:36 张; +- **模式隔离验证**:全部来源于 `public` schema,未混入 `sys_catalog`、`pg_catalog` 或 `information_schema` 等任何系统内部表; +- **核心表发现结果**: + - `train_teacher`(教师信息表):已识别 + - `train_student`(学生信息表):已识别 + - `train_course`(课程信息表):已识别 + +### 7.2 字段与元数据结构读取验证 (`selectDbTableColumnsByName`) +| 目标表名 | 字段总数 | 关键列及类型验证(部分摘要) | 主键识别 | 必填标识 | 注释信息 | +| --- | --- | --- | --- | --- | --- | +| `train_teacher` | 12 | `t_id` (varchar(32)), `t_name` (varchar(30)), `t_department` (varchar(50)), `create_time` (timestamp) | 是 (`t_id`) | 是 (`t_name`等) | 完整保留 | +| `train_student` | 12 | `s_id` (varchar(32)), `s_name` (varchar(30)), `s_department` (varchar(50)), `s_major` (varchar(50)) | 是 (`s_id`) | 是 (`s_name`等) | 完整保留 | +| `train_course` | 14 | `c_id` (varchar(32)), `c_name` (varchar(100)), `teaching_objective` (text), `course_content` (text) | 是 (`c_id`) | 是 (`c_name`等) | 完整保留 | + +### 7.3 REST 接口实测 (`GET /tool/gen/db/list`) +通过携带 admin Token 调用后端接口实测: +- `GET /tool/gen/db/list?pageSize=100`:HTTP 200,`total=36`,返回全部 public 业务表; +- `GET /tool/gen/db/list?tableName=train_teacher`:HTTP 200,精确匹配 1 条; +- `GET /tool/gen/db/list?tableName=train_student`:HTTP 200,模糊匹配 3 条(包含 `train_student_checkin`, `train_student_portrait`, `train_student`); +- `GET /tool/gen/db/list?tableName=train_course`:HTTP 200,精确匹配 1 条。 + +--- + +## 8. git diff 摘要 + +本轮变更只涉及基础设施层与代码生成器 XML,完全杜绝业务侵入: + +```diff +diff --git a/RuoYi-Vue/ruoyi-admin/src/main/resources/application-druid.yml b/RuoYi-Vue/ruoyi-admin/src/main/resources/application-druid.yml +--- a/RuoYi-Vue/ruoyi-admin/src/main/resources/application-druid.yml ++++ b/RuoYi-Vue/ruoyi-admin/src/main/resources/application-druid.yml +@@ -20,3 +20,4 @@ spring: +- url: jdbc:kingbase8://127.0.0.1:4321/training?currentSchema=public&useUnicode=true&characterEncoding=utf8 ++ url: jdbc:kingbase8://127.0.0.1:4321/training?currentSchema=public,sys_catalog&useUnicode=true&characterEncoding=utf8 + username: kingbase + password: [REDACTED] ++ connectionInitSqls: ["SET search_path TO public, sys_catalog"] + +diff --git a/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml +--- a/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml ++++ b/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml +@@ -44,3 +44,3 @@ +- from information_schema.columns where table_schema = (select current_database()) and table_name = (#{tableName}) ++ from information_schema.columns where table_schema = (select current_schema()) and table_name = (#{tableName}) + +diff --git a/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml +--- a/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml ++++ b/RuoYi-Vue/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml +@@ -81,3 +81,3 @@ +- where table_schema = (select current_database()) ++ where table_schema = (select current_schema()) +@@ -101,3 +101,3 @@ +- where table_name NOT LIKE 'qrtz\_%' and table_name NOT LIKE 'gen\_%' and table_schema = (select current_database()) ++ where table_name NOT LIKE 'qrtz\_%' and table_name NOT LIKE 'gen\_%' and table_schema = (select current_schema()) +@@ -110,3 +110,3 @@ +- where table_comment ]]> '' and table_schema = (select current_database()) ++ where table_comment ]]> '' and table_schema = (select current_schema()) +``` + +--- + +## 9. 尚未解决的问题 + +按照本阶段的原则与禁令,以下历史遗留问题在本轮保持原样,留待后续专项阶段处理: + +1. **`sys_role` 记录缺失与孤儿关联**: + - 数据库中 `public.sys_role` 仍然为 0 行记录; + - `sys_user_role` 与 `sys_role_menu` 仍保留指向已缺失 roleId 的孤儿历史数据; + - 普通角色与培训专用角色权限尚未恢复(需在权限模型重构阶段专项建设)。 +2. **培训业务表命名与结构规范冲突**: + - 现有 `train_*` 表存在下划线与驼峰命名混用; + - PRD V1.0 要求的“选课管理(Enrollment)”、“排课信息(Lesson/Schedule)”等实体模型在现有数据库中缺失对应表结构; + - 成绩与画像表仍存在与 PRD 边界不符的字段定义。 +3. **生成的自增主键(Generated Keys)写链路完整适配**: + - 现有环境已验证驱动声明支持 `useGeneratedKeys`,但写操作(批量 INSERT、事务回滚)的边界行为仍需在数据库 V2 落地时配合单元测试核验。 + +--- + +## 10. 是否满足进入数据库 V2 设计的条件 + +**判定结论**:**完全满足**。 + +**判定依据**: +1. **基础运行阻断已被清除**:`search_path` 双保险机制消除了 RuoYi 底层 Mapper 与 Kingbase 内置视图的同名冲突,登录、权限与动态路由链路在真实服务环境下完全调通。 +2. **元数据逆向工程能力就绪**:代码生成器(Generator)已具备精确读取 Kingbase public schema 下业务表、字段、类型、主键元数据的能力,为后续通过工具链辅助重构与生成实体代码扫清了障碍。 +3. **业务边界零污染**:本轮修复未修改任何业务表、未修改任何培训模型、未删除数据库数据、未改动 PRD,代码工作区处于完全整洁受控的状态,符合进入“数据库 V2 设计与模型重构”的准入基线。