Files
workspace/code/fms/前端元数据运行时重构计划.md
T
2026-08-04 17:31:27 +08:00

13 KiB

FMS 前端元数据运行时重构计划

1. 目标

本次重构以内部 ERP 快速开发为目标,将模块配置、运行时解析、列表编辑和页面组合能力集中到前端公共层。业务开发优先通过配置和组合完成,只有特殊业务流程编写页面代码。

预期结果:

普通数据页面     配置 Module + Profile 后直接使用公共页面
组合业务页面     配置 PageNode,并通过公共运行时加载多个模块
特殊业务页面     复用公共运行时,只补充业务事件和业务流程
模块配置维护     统一在模块管理中完成,不修改后端

2. 硬约束

  • 不兼容旧系统、旧模块模型和旧业务数据。
  • 日常开发和运行期间不修改、不重启后端。
  • 不新增业务 Controller、Service 或专用接口。
  • 不修改 /data/loaddata、/data/page、/data/saveobjt、/data/nextid、/data/describe 的协议和处理逻辑。
  • 所有多表保存继续通过一次 /data/saveobjt 在同一事务中提交。
  • SQL Server bigint 在前端始终按字符串处理。
  • 数据库约束负责主外键、唯一性、类型和树结构的最终完整性。
  • 前端负责运行时解析、配置编排、交互校验、保存请求生成和错误定位。
  • 后端重启不作为前端功能开发、配置调整或页面接入的前置步骤。

3. 保留的核心模型

模型 稳定职责
Module 可复用资源及公共属性
FunctionNode 菜单目录和页面入口
PageNode 页面内部结构和模块引用
Profile 同一数据模块的不同使用方式
Field 模块级字段定义
Capability Profile 级列表、表单、查询等配置及 Module 级操作

字段继续属于 Module;列表、表单、查询、导入和导出继续属于 Profile。不得把这些配置重新合并为一张大表。

当前阶段正式支持 page 和 data 模块。report、dashboard、workflow 只保留数据库契约,在对应运行能力完成前不作为常规配置入口。

4. 目标前端结构

src/
  module-runtime/
    constants.js             元数据表名、视图名、节点类型和能力编码
    runtimeResolver.js       Module/Profile/PageNode 统一解析
    configLoader.js          字段及 Profile 配置统一加载
    conditionBuilder.js      固定条件、查询条件和页面参数组合
    saveRequestBuilder.js    inserts/updates/deletes 请求生成与排序
    fieldRegistry.js         字段控件和渲染器注册
    actionRegistry.js        通用操作和页面操作注册
    useModuleRuntime.js      面向页面的统一组合入口
    useModuleDataSource.js   分页、刷新、选择和查询状态
    useModuleEditor.js       新增、编辑、删除、临时 ID 和批量保存

目录名可在实施时结合现有结构调整,但职责边界不得重新散落到业务页面。

5. 公共运行时契约

5.1 模块定位

公共运行时支持以下三种明确定位方式:

  1. moduleId:使用该模块的默认 Profile
  2. profileId:精确定位 Profile 并反查所属 Module
  3. pageNodeId

同时提供 moduleId + profileId 时只用于校验两者归属关系。新页面不再使用 moduleCode + profileCode 定位,也不再通过页面路由后取“第一个 module_ref”作为隐式模块。一个页面存在多个模块引用时,必须通过 pageNodeId 明确定位。

5.2 运行时结果

useModuleRuntime 对调用方提供统一对象:

module            模块及数据源
profile           当前使用方式
fields            启用字段
listConfig        列表配置
formConfig        表单配置
queryConfig       查询配置
actions           操作目录及有效权限
groups            布局分组
runtimeKey        当前配置唯一键
loading/error     加载状态
reload()          重新加载配置

业务页面不再自行查询 s_module_field*、s_module_profile 或 v_s_module_runtime。

5.3 扩展点

通用页面通过明确扩展点承载差异:

  • fixedCondition
  • beforeLoad
  • afterLoad
  • beforeSave
  • afterSave
  • rowActions
  • actionHandlers
  • 表格和表单插槽

扩展点只处理业务差异,不重新实现分页、字段解析、脏数据和批量保存。

6. 实施阶段

阶段 0:基线和旧设计清理

状态:completed

实际结果:旧设计文档已标记为历史方案;公共常量已建立;otherdata 只使用正式的 group/module_ref 节点类型。

  • 以 新数据库结构.md 为唯一模块模型规范。
  • 将旧的 s_module_group、s_option_source、旧节点类型和编码型 i18n 设计标记为历史方案。
  • 建立前端元数据常量,统一 directory/entry、group/tab/section/module_ref 等枚举。
  • 修正 otherdata 中 category/group、module/module_ref 的混合判断。
  • 记录当前普通列表、可编辑列表、其他资料、用户管理、登录日志和多语言页面的行为基线。

验收:代码和有效文档中不再存在会参与运行的旧模型定义。

阶段 1:抽取运行时解析和配置加载

状态:completed

实际结果:已建立 ID 优先的公共解析、配置加载、并发版本控制和会话缓存;多模块页面必须使用 pageNodeId。

  • 从 FmsModuleListPage.vue 抽取模块定位、Profile 定位和配置加载逻辑。
  • 建立 useModuleRuntime,统一 ID 字符串转换、加载并发控制和错误状态。
  • 建立显式页面节点解析规则,删除“第一个数据节点”约定。
  • 保持现有 API 请求数量和运行行为,阶段内不调整页面视觉结构。
  • 为同一运行时键增加会话级缓存;模块管理保存后可以定向失效缓存。

验收:普通页面可通过 moduleId、profileId、pageNodeId 或唯一页面引用定位;同一页面可通过不同 pageNodeId 明确加载多个数据模块。

阶段 2:抽取数据源和编辑器

状态:completed

实际结果:分页查询、查询状态、临时 ID、脏行、批量保存、批量删除和拖拽排序已从页面组件抽离;一次保存仍只调用一次 saveobjt。

  • 从 FmsModuleListPage.vue 抽取分页、查询、排序和刷新状态到 useModuleDataSource。
  • 抽取新增、编辑、删除、临时 ID、自动编码和保存请求生成到 useModuleEditor。
  • 建立统一 saveRequestBuilder,负责表级变更合并及删除、更新、插入顺序。
  • 一次用户保存只发起一次 /data/saveobjt。
  • 保留 FmsModuleTable、FmsQueryToolbar 和现有页面插槽契约。

验收:FmsModuleListPage.vue 主要负责界面组合,不再同时承担元数据加载、数据访问和保存算法。

阶段 3:控件和操作注册中心

状态:completed

实际结果:字段注册中心已覆盖输入、长文本、数字、金额、日期、日期时间、布尔和选择控件;数字编辑使用字符串模式保护 bigint;操作注册中心已提供通用操作和页面 handler 扩展。

  • 建立字段控件注册中心,收敛输入、数字、日期、布尔、选择和自定义渲染器映射。
  • 建立操作注册中心,统一 create/update/delete/export 等通用行为。
  • 特殊业务操作由页面注册 handler,不修改公共运行时主体。
  • 注册项使用稳定 key;未知 key 明确报错并指出模块、Profile 和字段。
  • 表格行内重型控件继续按需挂载,任意时刻只保留当前编辑实例。

验收:新增一种字段控件或通用操作只修改注册中心及对应实现,不修改所有业务页面。

阶段 4:模块管理提效

状态:completed

实际结果:创建数据模块自动创建默认 Profile;同步字段自动补齐列表和表单配置;Profile 可完整复制;模块管理已增加实时完整性检查和列表预览;常规新建入口只开放 page/data。

  • 创建数据模块时自动创建 default Profile。
  • 同步字段后,新字段自动生成默认列表和表单配置;失效字段只停用。
  • 支持复制 Profile,并完整复制分组、列表、表单和查询配置。
  • 模块管理增加配置预览和完整性检查结果,但不展示底层保存顺序。
  • 常规入口只开放 page 和 data;预留模块类型放入明确的高级区域或隐藏。
  • 保存后定向刷新运行时缓存、菜单和权限数据,不刷新整个应用。

验收:技术人员可以完成“创建模块、同步字段、调整配置、加入页面、立即使用”的完整流程。

阶段 5:页面编排和业务页面接入

状态:completed

实际结果:contact、othercompany、user、login-log、i18n-type 已接入公共运行时;otherdata 通过 pageNodeId 加载所选模块引用,不再按模块编码定位运行时。

  • 页面组件通过 pageNodeId 或明确的 moduleId/profileId 加载数据模块。
  • otherdata 使用统一 PageNode 枚举和运行时解析,不再维护独立模块解析逻辑。
  • contact、othercompany、user、login-log、i18n-type 按风险从低到高接入公共运行时。
  • 普通页面只保留模块声明和必要插槽;特殊页面只保留业务流程。
  • 不为了接入公共运行时修改后端或重启后端。

验收:新增普通数据页面只需要配置 Module/Profile 和一个轻量页面入口。

阶段 6:清理与稳定性验证

状态:in_progress

已完成:公共运行时代码已通过 Node 语法检查、定向格式检查和 Vite 生产构建;异步请求均使用运行时键或版本号丢弃过期结果;bigint ID 和数字编辑按字符串处理。

待完成:连接实际运行数据后回归普通列表、可编辑列表、otherdata、多模块页面和机构切换,并确认每个路由页面只有一个启用的数据模块引用。

  • 删除已无调用方的重复加载、条件组合和保存函数。
  • 删除旧枚举适配、旧模块解析路径和演示数据。
  • 检查所有 bigint 比较、Map key、选择值和请求参数均按字符串处理。
  • 检查模块切换、路由切换和机构切换时不会应用过期异步结果。
  • 检查大列表滚动、编辑器按需挂载和批量保存请求数量。
  • 执行 pnpm fmt:check;除非用户明确要求,不执行构建、不启动或重启后端。

验收:公共运行时成为唯一模块解析入口,业务页面没有重复实现模块配置加载。

7. 优先改造文件

优先级 文件/目录 处理
P0 src/components/fms-module-list/FmsModuleListPage.vue 拆分运行时、数据源和编辑逻辑
P0 src/views/base/otherdata/index.vue 统一 PageNode 枚举和模块定位
P0 src/views/module-management/composables/useModuleResources.js 复用请求构造、缓存失效和字段同步能力
P1 src/views/module-management/composables/usePageComposition.js 使用统一节点规则和运行时定位
P1 src/views/module-management/composables/useFunctionStructure.js 使用统一节点常量和树校验
P1 src/stores/permissions.js 与运行时共享模块、节点和操作索引
P2 使用 FmsModuleListPage 的业务页面 逐页删除重复配置加载和保存逻辑

8. 性能和运行连续性

  • 元数据按 orgId + moduleId + profileId 缓存,避免同一页面重复请求。
  • 模块配置保存后只失效相关缓存,不清空所有模块缓存。
  • 页面或机构切换使用版本号丢弃过期请求结果。
  • 列表业务数据始终分页;不因元数据重构增加全表读取。
  • 多个元数据请求可并行加载,但不得在行循环中发起请求。
  • 批量编辑只生成一次保存请求,不逐行调用接口。
  • 前端变更通过 Vite 热更新生效;实施期间不重启后端。

9. 非目标

  • 不建立传统业务 Controller/Service/DAO 分层。
  • 不为每个模块生成后端代码。
  • 不实现旧模块表兼容、双写或旧数据转换。
  • 不在本次重构实现报表引擎、仪表盘设计器或工作流引擎。
  • 不重写 fms-table、文件组件或已有手写业务流程。
  • 不一次性改造所有业务页面;公共能力稳定后逐页接入。

10. 执行纪律

  • 每个阶段单独完成、验证和交付,不跨阶段进行大范围改写。
  • 开始一个阶段前记录受影响页面和当前行为。
  • 公共组件改动后优先回归普通列表、可编辑列表和 otherdata。
  • 发现通用接口无法满足需求时停止该项,说明缺口后再决定是否允许修改后端。
  • 每阶段完成后更新本文件状态和实际结果,未通过验收不得进入下一阶段。

11. 完成标准

满足以下条件后,本计划完成:

  • 技术人员可以不修改后端完成数据模块创建、字段同步、Profile 配置和页面挂载。
  • 开发人员可以通过公共组件或 composable 接入模块,不重复编写元数据查询和保存流程。
  • 普通模块新增和配置调整不需要后端重启。
  • 同一数据模块可被多个页面和多个 Profile 复用。
  • 页面包含多个模块时定位明确,不依赖隐式顺序。
  • 前端只保留一套有效模块模型和节点枚举。
  • 批量保存、权限刷新、缓存失效和异步切换行为可预测。