--- name: field-name-sync-from-old-db overview: 创建可复用的字段名同步服务,从旧库 s_columnLib.col_Caption 同步到新库 s_module_field.b_name,按视图名和字段名匹配,首批执行 v_b_othercompany。 todos: - id: fix-old-db-ssl content: 修复 g3hd-old.properties:移除 sslProtocol=TLSv1 参数,JDK 21 驱动自动协商 TLSv1.2 即可连通 status: completed - id: create-sync-service content: 新建 FieldNameSyncService.java:实现跨库 UPDATE SQL,按 viewName 匹配 s_columnLib,将 col_Caption 写入 s_module_field.b_name,不匹配字段不改动,返回更新行数 status: completed dependencies: - fix-old-db-ssl - id: create-admin-controller content: 新建 AdminController.java,添加 POST /api/admin/sync-field-names 端点,JWT 认证,viewName 参数白名单校验,调用 FieldNameSyncService 并返回 ApiResponse status: completed dependencies: - create-sync-service - id: add-sql-migration-ref content: 新建 023_sync_field_names.sql 参考脚本,记录跨库同步 SQL 作为纯 SQL 手动执行备选方案 status: completed --- ## 用户需求 构建一个可复用的工具,将旧库 `G3HY2025.dbo.s_columnLib` 中存储的字段中文名称(`col_Caption`)同步到新库 `fms.dbo.s_module_field.b_name`。 ### 匹配规则 - `s_module.b_viewtable` = `s_columnLib.col_TableName`(模块视图名对应旧库表名) - `s_module_field.b_field` = `s_columnLib.col_FieldName`(字段名精确匹配) - 匹配不上的字段,`b_name` 保持不变 ### 首批同步目标 视图 `v_b_othercompany` ### 可复用要求 以后还有更多视图需要同步,工具须参数化视图名,方便批量复用。 ## 技术方案 ### 方案选择:跨库 SQL + 管理端点 **核心决策**:新旧库在同一 SQL Server 实例(`118.89.70.199:1433`),使用**跨数据库 SQL** 直接从新库连接执行 `UPDATE ... FROM G3HY2025.dbo.s_columnLib`,无需单独连接旧库。 ```sql UPDATE sf SET sf.b_name = LTRIM(RTRIM(sc.col_Caption)) FROM dbo.s_module_field sf JOIN dbo.s_module m ON m.b_id = sf.b_module_id JOIN G3HY2025.dbo.s_columnLib sc ON sc.col_TableName = m.b_viewtable AND sc.col_FieldName = sf.b_field WHERE m.b_viewtable = ? AND sc.col_Caption IS NOT NULL AND LTRIM(RTRIM(sc.col_Caption)) != ''; ``` **为何需要新端点而非复用通用端点**: - 通用 `saveObjectApi` 只能操作当前 org 数据库的单表 insert/update/delete,无法表达跨库 JOIN 逻辑 - 通用 `loadDataApi` 只能查询当前 org 的表,无法读取旧库 `s_columnLib` - 此操作为管理类批量数据同步,不在业务 CRUD 范畴内 ### 实现架构 ```mermaid flowchart LR A[前端/管理员] -->|POST /api/admin/sync-field-names| B[AdminController] B -->|校验 viewName 参数| C[FieldNameSyncService] C -->|获取新库 Connection| D[OrgRoutingDataSource] D -->|fms 连接| E[(SQL Server)] E -->|跨库 JOIN| F[G3HY2025.dbo.s_columnLib] E -->|UPDATE| G[fms.dbo.s_module_field] C -->|返回同步数量| B B -->|ApiResponse| A ``` ### 模块划分 | 模块 | 文件 | 职责 | | --- | --- | --- | | 配置修复 | `config/dbconfigs/g3hd-old.properties` | 移除 `sslProtocol=TLSv1` | | 同步服务 | `service/FieldNameSyncService.java` | 执行跨库 UPDATE,校验参数,返回影响行数 | | 管理控制器 | `controller/AdminController.java` | `POST /api/admin/sync-field-names` 端点,JWT 保护 | | 参考脚本 | `config/migrations/023_sync_field_names.sql` | 纯 SQL 版本供手动执行 | ### 数据流 1. 前端/管理员发送 `POST /api/admin/sync-field-names`,请求体 `{ "viewName": "v_b_othercompany" }` 2. `AdminController` 校验 `viewName` 为合法标识符(字母/下划线/数字,最长128) 3. `FieldNameSyncService` 通过 `@Resource DataSource` 获取新库连接 4. 执行跨库 PreparedStatement UPDATE 5. 返回 `{ "updatedCount": 42, "message": "v_b_othercompany 同步完成,共更新 42 个字段名称" }` ### 安全措施 - `viewName` 参数:白名单正则 `^[A-Za-z_][A-Za-z0-9_]{0,127},校验后作为 PreparedStatement 参数绑定(非字符串拼接) - JWT 认证:`AdminController` 路径 `/api/admin/**` 经 `JwtAuthFilter` 保护,仅登录用户可调用 - 失败不影响现有数据:UPDATE 在一条 SQL 内执行,单条匹配失败不阻断其他行 ### 执行说明 - **热路径**:单条 UPDATE SQL 跨库 JOIN,性能取决于 `s_module_field` 行数和索引;对单个视图(~50 字段)毫秒级完成 - **日志**:服务层记录同步开始/完成/失败日志,使用 `@Slf4j`,级别 INFO/ERROR - **事务**:使用 Spring 声明式事务 `@Transactional`,确保整批同步原子提交或回滚 - **向后兼容**:不修改现有 `DataController` 和通用端点,不影响业务数据流 ## Agent Extensions ### SubAgent - **code-explorer** - 目的:探索旧库连接方式、现有 Service/Controller 模式、参数校验惯例 - 预期结果:确认 `OrgDataSourceManager` 的 API 使用方式和现有异常处理/日志模式