- 发布 docs/codebase-audit/18_phase05_runtime_fix.md,完整记录 search_path 实验结果、数据源双保险配置方案、登录链验证数据及代码生成器元数据验证 - 在 docs/README.md 核心综述清单中收录 18_phase05_runtime_fix.md 并更新审计报告索引至 19 篇
16 KiB
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 系统目录视图冲突
- 隐式模式搜索优先级:KingbaseES 当前连接默认的有效模式搜索顺序(
current_schemas(true))为{sys, pg_catalog, sys_catalog, public}。 - 系统同名视图拦截:Kingbase 内置系统目录模式
sys_catalog中预置了sys_user、sys_role、sys_config等系统级视图(如sys_catalog.sys_user包含usesysid,userepl等列,无user_id列)。 - 若依未限定 SQL 命中异常:RuoYi 核心系统 Mapper(如 SysUserMapper.xml、SysRoleMapper.xml、SysConfigMapper.xml)普遍使用未限定表名查询(
from sys_user、from sys_role、from sys_config)。 - 运行后果:执行未限定查询时直接命中
sys_catalog视图,抛出ERROR: column "user_id" does not exist、column "role_id" does not exist,导致登录认证与系统初始化彻底瘫痪。
1.2 代码生成器混淆 database 与 schema
- 函数语义错位:代码生成器 GenTableMapper.xml 与 GenTableColumnMapper.xml 中使用
where table_schema = (select current_database())。 - 检索为空:在 Kingbase 体系下,
current_database()返回的是数据库名(training),而实际表与字段所属的元数据模式为public(information_schema.tables.table_schema = 'public')。 - 运行后果:代码生成器在“导入表”与“列信息加载”时无法匹配到任何物理表(返回 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、修改高度集中”的要求,采用应用连接池层双保险适配方案:
- 第一层:JDBC URL
currentSchema参数- 将 JDBC URL 中的
currentSchema=public升级为currentSchema=public,sys_catalog。 - Kingbase JDBC 驱动在建立物理 Socket 握手初始化时,直接依据该参数初始化会话
search_path。
- 将 JDBC URL 中的
- 第二层:Druid 连接池
connectionInitSqls机制- 在 Druid 数据源配置中声明:
connectionInitSqls: ["SET search_path TO public, sys_catalog"]。 - DruidDataSource 保证在每次物理连接创建(
createPhysicalConnection)完成后,都会执行一次该初始化 SQL,杜绝连接归还与重用过程中可能出现的模式搜索漂移。
- 在 Druid 数据源配置中声明:
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 具体修改点
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())
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 张;
- 模式隔离验证:全部来源于
publicschema,未混入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. 尚未解决的问题
按照本阶段的原则与禁令,以下历史遗留问题在本轮保持原样,留待后续专项阶段处理:
sys_role记录缺失与孤儿关联:- 数据库中
public.sys_role仍然为 0 行记录; sys_user_role与sys_role_menu仍保留指向已缺失 roleId 的孤儿历史数据;- 普通角色与培训专用角色权限尚未恢复(需在权限模型重构阶段专项建设)。
- 数据库中
- 培训业务表命名与结构规范冲突:
- 现有
train_*表存在下划线与驼峰命名混用; - PRD V1.0 要求的“选课管理(Enrollment)”、“排课信息(Lesson/Schedule)”等实体模型在现有数据库中缺失对应表结构;
- 成绩与画像表仍存在与 PRD 边界不符的字段定义。
- 现有
- 生成的自增主键(Generated Keys)写链路完整适配:
- 现有环境已验证驱动声明支持
useGeneratedKeys,但写操作(批量 INSERT、事务回滚)的边界行为仍需在数据库 V2 落地时配合单元测试核验。
- 现有环境已验证驱动声明支持
10. 是否满足进入数据库 V2 设计的条件
判定结论:完全满足。
判定依据:
- 基础运行阻断已被清除:
search_path双保险机制消除了 RuoYi 底层 Mapper 与 Kingbase 内置视图的同名冲突,登录、权限与动态路由链路在真实服务环境下完全调通。 - 元数据逆向工程能力就绪:代码生成器(Generator)已具备精确读取 Kingbase public schema 下业务表、字段、类型、主键元数据的能力,为后续通过工具链辅助重构与生成实体代码扫清了障碍。
- 业务边界零污染:本轮修复未修改任何业务表、未修改任何培训模型、未删除数据库数据、未改动 PRD,代码工作区处于完全整洁受控的状态,符合进入“数据库 V2 设计与模型重构”的准入基线。