Files
railwaysystem/docs/codebase-audit/18_phase05_runtime_fix.md
wangzhibiao 4b450f46af docs(审计): 输出Phase 0.5基础运行修复与端到端验证审计报告
- 发布 docs/codebase-audit/18_phase05_runtime_fix.md,完整记录 search_path 实验结果、数据源双保险配置方案、登录链验证数据及代码生成器元数据验证
- 在 docs/README.md 核心综述清单中收录 18_phase05_runtime_fix.md 并更新审计报告索引至 19 篇
2026-09-14 00:56:18 +08:00

16 KiB
Raw Permalink Blame History

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、SysRoleMapper.xml、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 与 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 默认连接探测

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 中显式执行:

SET search_path TO public, sys_catalog;

再次查询状态与数据:

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 运行配置 master 数据源 URL 增加 currentSchema=public,sys_catalog,并注入 connectionInitSqls
2 GenTableMapper.xml 框架映射 将表检索中 3 处 (select current_database()) 改为 (select current_schema())
3 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(待导入表分页查询):
      <!-- 修改前 -->
      where table_schema = (select current_database())
      <!-- 修改后 -->
      where table_schema = (select current_schema())
      
    • selectDbTableListByNames(批量表查询):
      <!-- 修改前 -->
      and table_schema = (select current_database())
      <!-- 修改后 -->
      and table_schema = (select current_schema())
      
    • selectTableByName(单表元数据查询):
      <!-- 修改前 -->
      and table_schema = (select current_database())
      <!-- 修改后 -->
      and table_schema = (select current_schema())
      
  2. GenTableColumnMapper.xml:
    • selectDbTableColumnsByName(表列结构查询):
      <!-- 修改前 -->
      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 --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 <![CDATA[ <> ]]> '' and table_schema = (select current_database())
+		where table_comment <![CDATA[ <> ]]> '' 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 设计与模型重构”的准入基线。