Files
workspace/code/fms/模块管理简化与架构收敛方案.md
T
2026-10-09 17:32:14 +08:00

318 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FMS 模块管理简化与架构收敛方案
> 文档状态:方案评审稿
> 目标:降低新增业务模块的配置成本,明确元数据配置与业务代码的边界
> 约束:不迁移旧模块配置或旧业务数据;不提供旧表、旧接口、旧配置格式的兼容层
## 1. 结论摘要
当前模块管理的主要问题不是能力不足,而是把“登记业务对象”“定义字段”“设计页面”“配置查询”“配置权限”“定义删除规则”等不同职责,集中成了一个通用模块配置器。简单模块因此也要理解和维护一套近似低代码平台的概念。
建议将系统收敛为以下模式:
```text
业务域代码包(表、SQL、服务、API、页面)
│
├── 代码清单登记业务对象、页面和权限点
└── 数据库维护模块启停、菜单结构和角色授权
```
核心决策:
1. **业务域代码包成为业务实现的唯一中心。**业务表、查询、保存、校验、业务动作和页面由业务域负责。
2. **模块管理不再设计业务页面。**它只管理代码中已经存在的业务对象及其启停、名称和顺序。
3. **菜单和权限分别管理。**菜单只能指向代码清单中已登记的页面;权限由代码声明,数据库维护用户或角色授权。
4. **保留共享组件,不保留通用业务解释器作为核心路径。**简单资料可复用列表、表单、查询组件;复杂行为进入明确的业务服务。
5. **从全新数据库和新接口开始。**旧 `s_module` 配置、旧 schema JSON、旧通用模块运行时和旧接口不迁移、不兼容、不双写。
## 2. 当前设计的主要负担
当前配置模型大致是:
```text
s_module
├── s_field
├── s_module_schema(view / edit / query JSON)
├── s_autocode
├── s_power
├── s_relation
├── s_delete_rule
└── s_i18n
```
这套设计能让通用列表和表单快速出现,但也产生了几类持续成本:
- **配置入口太多。**新增模块要在多个面板间切换;字段定义与列表、表单、查询布局分别维护,用户需要理解内部元数据结构。
- **模块概念混杂。**导航树节点、数据对象、虚拟查询对象和页面入口都被叫作“模块”,导致一个模块既像目录又像数据模型和页面定义。
- **运行时过度依赖配置解释。**配置错误会在运行时才表现为页面空白、字段不可写或保存失败,而不是在编译、启动或发布阶段尽早暴露。
- **业务规则被推向通用化。**关系、级联删除、公式、权限和 SQL 配置逐渐形成跨业务解释规则;规则越多,编辑器、校验器和运行时之间的耦合越高。
- **安全边界需要后端兜底。**模块中心在前端限制管理员访问,但元数据和业务写入权限不能依赖路由隐藏或前端角色判断。
问题的根因是:**把少数简单 CRUD 的便利,扩展成所有业务都必须经过的运行时模型。**
## 3. 目标与非目标
### 3.1 目标
- 新增业务对象时,业务规则和数据访问可以在代码中搜索、评审、测试和发布。
- 管理员只做运行配置:菜单组织、对象启停、角色授权和少量展示偏好。
- 简单资料复用统一组件;单据、库存、财务、审批等复杂业务使用专用 API 和服务。
- 页面引用的数据字段、权限点和业务动作可由代码清单静态登记并校验。
- 配置改动有明确的操作权限、审计信息和发布责任。
### 3.2 非目标
- 不建设新的通用低代码平台或通用 JSON 规则引擎。
- 不让数据库配置动态创建业务表、推断保存逻辑或执行任意业务 SQL。
- 不要求每个业务页面都可由管理员拖拽生成。
- 不迁移旧模块配置、旧业务数据或旧接口调用方。
- 不为兼容旧版而保留双运行时、双字段定义或旧配置转换器。
- 第一阶段不做通用代码生成平台;先用一个真实业务域验证代码模板。
## 4. 目标概念模型
必须把四个概念分开:
| 概念 | 含义 | 权威来源 |
| --- | --- | --- |
| 业务域 | 一组共同业务职责,如 customer、sales、inventory | 后端和前端代码目录 |
| 业务对象 | 可被查询或操作的对象,如 customer、sales_order | 业务域代码清单 |
| 页面 | 列表、详情、工作台等用户界面 | 前端代码路由/页面清单 |
| 菜单 | 用户进入页面的导航结构 | 数据库菜单配置 |
菜单不再承担业务对象树的职责;业务对象不再通过树节点推断页面行为;页面也不能靠数据库中任意组件名动态加载。
### 4.1 业务域代码包
建议沿用新架构草案中的业务域组织方式:
```text
domains/
customer/
migration/
sql/
repository/
service/
api/
page/
i18n/
sales/
inventory/
```
每个业务域明确拥有自己的数据库变更、查询 SQL、保存方式、业务动作、API、页面和多语言资源。跨域调用通过接口或明确的业务关系完成,不通过任意 JSON 规则互相解释。
### 4.2 代码清单
每个业务域提供静态清单,列出可供系统登记的对象、页面和权限点。清单是可执行代码的一部分,不能由数据库传入任意类名或组件名覆盖。
示意:
```text
customer 域
对象:customer
页面:customer-list、customer-detail
权限:customer.read、customer.create、customer.update、customer.disable
```
启动或发布校验至少检查:
- 菜单引用的页面 key 必须存在于代码清单;
- 模块登记的业务对象必须存在于代码清单;
- 授权引用的权限 key 必须已声明;
- 一个页面声明使用的对象必须在清单中登记;
- 数据库表、视图和必要字段必须通过业务域校验。
## 5. 新的模块管理体验
将“模块管理”改名为 **业务对象与模块登记**,不再展示字段设计器、列表设计器、表单设计器、查询设计器、自动编码、模块关系和删除 SQL 规则等运行时配置面板。
### 5.1 业务对象登记
业务对象由代码包提供,管理页面只允许维护少量运行属性:
- 显示名称或启用的多语言资源;
- 是否启用;
- 管理列表中的排序;
- 必要的说明信息。
不允许在这里填写保存表、查询 SQL、字段列表或业务规则。对象登记不是“凭空创建一个业务模块”:新增对象先有代码,再由清单登记。
### 5.2 菜单管理
菜单继续负责导航树和页面入口:
- 只能从代码清单选择页面 key;
- 菜单层级、排序、名称和启停保存在数据库;
- 菜单到业务对象的绑定明确记录,不从父子树或模块类型推断;
- 不允许数据库配置任意前端路由组件。
### 5.3 权限管理
- 权限点由业务域清单声明,代码决定具体操作含义。
- 管理界面只分配已声明的权限给角色或用户。
- 页面入口权限、对象读写权限、业务动作权限分别判定。
- 后端 API 对每次请求重新校验登录身份、对象/动作权限和业务数据范围;前端隐藏按钮只作为体验优化。
## 6. 页面和数据配置边界
### 6.1 业务页面
页面默认由代码实现,并复用现有通用列表、表单、查询、选择器和附件等 UI 组件。业务页面的字段、校验、动作和调用哪个 API 由页面和业务域代码明确声明。
建议的简单资料实现链路:
```text
Migration
→ 业务表 / 查询视图
→ list/detail/save SQL
→ repository / service
→ API
→ 共享列表与表单组件
→ 页面
```
复杂单据的提交、撤回、审批、过账、库存变更等动作必须由业务服务实现,不由表单 JSON、通用保存接口或可编辑 SQL 字段推断。
### 6.2 允许的展示偏好
只在多个业务域确实重复需要时,才保留受限展示配置,例如:
- 用户个人列表列顺序、列宽;
- 少量查询默认值;
- 只影响展示的格式偏好。
这些配置不得改变字段真实类型、保存映射、数据权限、业务校验或事务行为。默认布局优先由代码提供;个人偏好是覆盖,不是创建业务页面的必要步骤。
### 6.3 数据库是事实来源
- Migration 定义真实表结构;
- SQL 文件或受控 repository 实现查询和保存;
- 代码模型定义 API 输入输出和字段语义;
- 代码或受控资源维护系统文案;
- 数据库运行配置只登记启停、菜单和授权。
可以保留 `describe` 能力用于开发期校验或脚手架辅助,但不把数据库实时反射变成生产运行时的字段定义器。
## 7. 数据表与接口调整方向
以下是职责方向,不要求为每个旧表设计转换路径:
| 当前职责 | 目标处理方式 |
| --- | --- |
| `s_module` 中的模块树、视图表、保存表、通用 SQL | 模块登记表只保留稳定对象编码、业务域/对象 key、名称、启停和排序;业务 SQL 移入域代码 |
| `s_field` 运行时字段定义 | 不作为业务页面运行时字段事实来源;字段由业务 API 和页面代码定义 |
| `s_module_schema` 的 view/edit/query JSON | 移除通用解释器依赖;页面结构由代码定义,只可选保留受限展示偏好 |
| `s_autocode` | 编号规则归业务域所有,由业务服务原子获取和验证 |
| `s_relation` | 真实数据关系由 SQL 约束、查询和业务服务表达;不由全局模块关系解释器驱动 |
| `s_delete_rule` | 删除保护和级联行为由业务域服务及数据库约束明确实现 |
| `s_power` 权限点定义 | 权限 key 由代码清单声明,数据库维护角色授权;禁止客户端任意造权限 key |
| `s_menu` / 菜单绑定 | 保留导航职责,但页面目标必须指向代码清单中的安全 page key |
| `s_user_module_pref` | 如有真实用户偏好需求,可保留为展示偏好;不可承载权限或业务规则 |
模块登记表的候选字段:
```text
b_id 业务对象编码
b_domain_code 业务域编码
b_object_code 域内对象编码
b_name 显示名称
b_i18n 多语言资源 key
b_canuse 是否启用
b_xh 管理列表排序
b_bz 说明
```
`(b_domain_code, b_object_code)` 应有唯一约束。最终字段名和表结构在确认代码清单、权限模型和菜单模型后再定;不要把 `view_table`、`save_table`、任意 SQL 或业务配置 JSON 原样搬进新表。
## 8. 安全要求
这次收敛必须同时明确服务端边界:
1. 模块登记、菜单配置、角色授权的写接口必须有服务端系统管理员权限校验。
2. 通用数据写入不能只按客户端传入的 `table` 和 `key_field` 决定是否允许写;应使用受控表清单或专用业务 API。
3. 业务 API 必须校验用户对对象和动作的权限,以及公司、组织等数据范围。
4. 权限定义和页面 key 来自代码清单,不接受页面传入任意字符串作为授权依据。
5. SQL 参数值使用参数绑定;表、列和排序标识符必须来自服务端登记的标识符清单。
6. 关键管理操作记录操作人、对象、变更前后值和请求追踪信息。
## 9. 全新替换策略
本方案不提供旧数据或旧接口的兼容路径:
- 新环境从新的 Migration 基线建库;
- 不迁移旧 `s_module`、`s_field`、`s_module_schema` 或其他模块配置数据;
- 不实现旧 JSON 转换器、双读、双写、旧 API 代理或回退到旧页面的逻辑;
- 新版模块中心不读取旧配置表;
- 旧模块运行时、旧管理路由和仅服务旧运行时的接口随新实现整体移除;
- 旧业务数据迁移不属于本方案范围,需另立项目决策,不在新模块管理中隐式处理。
因此部署策略是新系统按新 schema 初始化,而不是在旧数据库上做无损升级。旧环境如需留档,由环境和备份策略处理,不由新版运行时兼容。
## 10. 实施顺序
### 阶段 A:锁定边界
- 定义业务域代码清单格式;
- 定义对象、页面和权限 key 的命名规则;
- 明确模块登记、菜单和角色授权三者关系;
- 明确简单 CRUD 与复杂业务 API 的使用边界;
- 完成后端管理员授权和通用写接口策略设计。
### 阶段 B:建立新内核
- 使用全新 Migration 建立最小模块登记、菜单、权限和审计结构;
- 实现代码清单校验;
- 实现服务端菜单/授权管理 API;
- 新模块中心只提供登记、启停、排序和菜单/授权入口。
### 阶段 C:验证一个简单业务域
选择客户或产品资料,完整实现:
```text
Migration → SQL → repository/service → API → 页面 → 菜单 → 授权 → 审计
```
该对象必须不依赖 `s_field`、`s_module_schema` 或通用模块解释器才能运行。
### 阶段 D:验证一个复杂业务动作
选择一个真实动作(例如单据提交或审批绑定),验证业务校验、事务和权限均在明确的业务服务中完成;工作流只调用已登记的业务动作。
### 阶段 E:移除旧模块中心实现
- 移除旧模块配置面板和仅供其使用的 JSON 解释逻辑;
- 移除旧运行时对模块字段/schema 的依赖;
- 删除旧模块专属接口与无调用点代码;
- 确认所有菜单页面来自代码清单,所有关键写操作经服务端权限校验。
不为旧数据补转换步骤,也不设置旧新并行期。
## 11. 验收标准
- 开发者新增业务对象时,有一致的业务域模板和单一 API/服务入口。
- 管理员新增菜单时只能选择已编译、已登记的页面和对象。
- 建立简单资料不需要分别维护字段表、列表 JSON、表单 JSON、查询 JSON 才能运行。
- 复杂业务动作的校验、事务和 SQL 均可在对应业务域代码中定位。
- 没有业务页面依赖全局模块 JSON 解释器才能启动。
- 未授权用户直接调用 API 不能修改模块、菜单、权限或业务数据。
- 新数据库初始化后,完整业务示例不需要导入任何旧模块配置即可工作。
- 配置偏好只影响展示,不能放宽数据权限或改变保存行为。
## 12. 代价与风险
- **优点:**运行时更容易理解和测试;业务逻辑可代码评审;减少管理员配置步骤;SQL、权限和事务责任明确。
- **代价:**新增业务对象需要开发和发布,不再承诺管理员零代码创建任意模块。
- **控制新增代码成本:**先建立简单资料业务域模板并复用共享组件;只有至少两个业务域出现相同重复后,再抽取脚手架或通用能力。
- **主要风险:**若大多数模块都要求管理员自行创建且业务结构高度同质,完全代码化会降低业务人员自助能力。应先用真实客户/产品模块验证开发成本,再决定是否增加“生成代码”的开发工具;不要因此恢复生产运行时的通用配置解释器。
## 13. 评审时需要确认
1. 新模块由开发者通过代码清单登记,管理员只能启停和配置菜单/授权,是否作为默认流程?
2. 列表列宽和列顺序是否只保留用户个人偏好,系统级默认布局是否完全由代码维护?
3. 简单 CRUD 的代码模板是否足够,是否先不做自动代码生成器?
4. 新数据库 schema 是否允许直接移除旧模块配置表和旧通用模块 API?
以上四项确定后,再据此制定数据库表结构、API 清单和分阶段实施任务。本方案本身不改代码、不执行数据库变更,也不包含旧数据兼容或迁移实现。