# FMS 新系统核心架构设计 V2.0 > 文档版本:V2.0 > 编制日期:2026-09-30 > 文档状态:核心架构设计草案 > 适用范围:FMS/ERP 新系统第一阶段重构 ## 1. 文档目的 本文档重新定义 FMS 新系统的核心架构,重点解决以下问题: - SQL 是系统的第一等公民; - 代码风格和模块实现方式统一; - 第一阶段保持简单,不建设通用低代码平台; - 真实业务表、业务 SQL 和业务代码拥有清晰边界; - 支持一个租户多个公司,但不提前建设复杂的平台能力。 本文档替代旧设计中以 `s_module`、通用 CRUD 和动态 JSON 为中心的架构思路。旧表结构中的业务数据、工作流和可复用 SQL 可以迁移,但运行时中心需要重新组织。 ## 2. 根本改变 ### 2.1 旧架构 旧设计的主链路是: ```text s_module → s_field → s_module_schema → 通用 SQL 拼接 → 通用保存 → 页面 ``` 它的优点是页面和简单模块创建较快,缺点是业务模块逐渐依赖元数据解释器,容易变成一个低代码平台。 ### 2.2 新架构 新设计的主链路是: ```text 业务域代码包 → 真实 SQL 表和视图 → 业务查询与业务动作 → API / 页面 ``` 新系统的中心不再是全局模块元数据,而是**业务域代码包**。每个业务域自己拥有表结构、迁移、SQL、业务动作和页面入口。 ```text 客户域 ├── 数据库迁移 ├── 业务表和视图 ├── 查询 SQL ├── 保存 SQL ├── 业务动作 ├── 接口 ├── 页面 └── 多语言资源 ``` ### 2.3 核心原则 ```text 业务域是组织中心 SQL 表和视图是事实来源 SQL 文件是查询和处理的主要实现 业务代码负责动作和事务 页面是业务域的使用界面 配置只解决少量表现和参数问题 ``` ## 3. 总体架构 ```text 租户注册配置 │ ▼ 租户数据库路由 │ ▼ SQL Server 租户数据库 ├── 系统内核 │ ├── 用户、角色和业务上下文 │ ├── 公司、组织、仓库和账簿 │ ├── 多语言和审计 │ └── 文件和运行记录 ├── 业务域 │ ├── 客户和供应商 │ ├── 产品和基础资料 │ ├── 销售 │ ├── 采购 │ ├── 库存 │ └── 财务 ├── 工作流 └── SQL 视图和报表 ``` 运行时链路: ```text 请求 → 租户路由 → 建立用户和公司上下文 → 进入业务域 → 执行查询或业务动作 → SQL 事务 → 审计记录 → 返回结果 ``` ## 4. 核心内核 核心内核只提供所有业务域都需要的能力,不负责解释所有业务。 ### 4.1 租户路由 第一阶段采用一个租户一个数据库: - 租户注册信息使用配置文件; - 一个租户对应一个 SQL Server 数据库; - 租户库内不重复使用 `tenant_id` 过滤业务数据; - 数据库连接由统一的 `TenantRegistry` 获取; - 每个租户数据库单独执行迁移并记录版本。 平台库、在线租户开通和集中运维不进入第一阶段。 ### 4.2 业务上下文 每次请求建立统一上下文: ```text tenant_code user_id company_id organization_id warehouse_id ledger_id language timezone currency request_id ``` 上下文由运行时创建,业务域只能读取,不由页面自行拼接。SQL 和业务动作通过上下文得到当前公司、用户和账簿。 ### 4.3 基础表 第一阶段的系统基础表: ```text s_user s_role s_user_role s_company s_organization s_warehouse s_ledger s_user_company s_user_organization s_user_warehouse s_user_ledger s_i18n_type s_i18n s_audit_log s_file ``` 角色先采用简单模型,不实现角色继承和复杂拒绝规则。公司、组织、仓库和账簿是明确的独立对象,不使用一个 `org_id` 代替它们。 ## 5. 业务域代码包 ### 5.1 业务域定义 业务域是新系统的主要代码组织单位,例如: ```text customer supplier product sales purchase inventory finance workflow ``` 一个业务域负责自己的: - 业务表和表关系; - 数据库迁移; - 查询视图; - 查询 SQL; - 保存 SQL; - 业务动作; - 事务边界; - API; - 页面; - 多语言资源; - 审计事件。 业务域之间通过明确的业务表关系、SQL 查询和服务接口协作,不通过全局 JSON 规则互相解释。 ### 5.2 业务对象 业务对象仍然存在,但它属于业务域,而不是全局通用元数据引擎。 一个业务对象至少包含: ```text 对象编码 主表或视图 查询 SQL 保存方式 业务动作 ``` 例如销售订单: ```text sales_order ├── b_sales_order ├── b_sales_order_line ├── sales_order_list.sql ├── sales_order_save.sql ├── submit_sales_order() └── approve_sales_order() ``` ### 5.3 业务域目录 建议使用以下代码组织方式: ```text fms/ ├── core/ │ ├── tenant/ │ ├── context/ │ ├── auth/ │ ├── audit/ │ └── i18n/ ├── domains/ │ ├── customer/ │ │ ├── migration/ │ │ ├── sql/ │ │ ├── repository/ │ │ ├── service/ │ │ ├── api/ │ │ └── page/ │ ├── product/ │ ├── sales/ │ ├── purchase/ │ ├── inventory/ │ └── finance/ ├── workflow/ └── shared/ ``` 每个业务域使用同一种目录结构,不允许某个模块把 SQL 放数据库字段、另一个模块把 SQL 放页面代码、第三个模块再放配置 JSON。 ## 6. SQL 设计 ### 6.1 SQL 的职责 SQL 负责: - 查询; - 关联; - 分组和统计; - 排序和分页; - 批量更新; - 事务内的数据处理; - 报表和视图。 业务代码负责: - 业务动作入口; - 事务编排; - 状态变化; - 幂等处理; - 跨模块规则; - 库存和财务业务约束。 ### 6.2 SQL 文件规范 SQL 统一放在业务域目录下: ```text domains/sales/sql/ ├── order_list.sql ├── order_detail.sql ├── order_save.sql ├── order_submit.sql └── order_report.sql ``` SQL 命名统一使用: ```text 对象_动作.sql ``` 例如: ```text customer_list.sql customer_save.sql customer_disable.sql sales_order_submit.sql inventory_lock.sql ``` ### 6.3 通用 CRUD 的边界 新架构不移除通用 CRUD,但不把通用 CRUD 作为所有业务的默认核心。它可以用于简单基础资料,核心业务必须由业务域自己的 SQL 和业务动作实现。 以下内容不再作为系统核心: - 数据库中的 `b_query_sql` 作为所有模块的默认查询入口; - `b_save_table` 驱动所有业务模块的通用保存; - `b_order_sql` 在运行时任意拼接排序; - 全局 `data / virtual` 模块解释器; - 页面 JSON 中的业务公式和业务写入规则; - 一张通用配置表承载所有业务行为。 内部开发人员可以自由编写 SQL,但 SQL 由对应业务域维护,并与代码和迁移一起发布。 ### 6.4 通用保存边界 通用保存仅用于简单基础资料。以下内容必须使用业务域专用服务和 SQL: - 库存扣减、锁定和释放; - 采购和销售状态推进; - 财务过账和反过账; - 公司间交易; - 审批提交和审批动作; - 任何需要多个业务表一致提交的操作。 ## 7. 数据库模型 ### 7.1 表分层 ```text s_* 系统内核和控制数据 b_* 业务主表和业务明细表 wf_* 工作流定义和运行数据 ``` 核心业务表使用真实列和真实关系,不使用全量 EAV。 ### 7.2 公司字段 公司级业务表根据业务实际需要包含: ```text company_id organization_id warehouse_id ledger_id ``` 跨公司业务另外保存: ```text source_company_id target_company_id settlement_company_id ``` 共享基础资料不复制多份,使用共享范围或公司关联表表达。 ### 7.3 动态字段 第一阶段不支持普通用户通过页面配置直接创建数据库字段。新增业务字段使用: ```text 迁移脚本增加物理列或扩展表 → 更新查询 SQL 和视图 → 更新页面字段定义 → 增加多语言资源 ``` 字段分为: 1. 核心字段:真实列,参与业务逻辑和统计; 2. 扩展字段:通过迁移增加的可选列或扩展表字段; 3. 低频扩展:JSON,仅用于非核心展示。 需要筛选、排序、统计或导出的字段不能只放在 JSON 中。 运行时字段分为两种情况: - **表现字段**:标签、显隐、只读、排序和布局可以运行时调整; - **物理字段**:需要存储、查询、排序、统计或导出的字段,必须通过 SQL Migration 增加物理列或扩展表。 后续如果确实需要管理员在线增加物理字段,可以增加一个受控的字段变更服务,由它生成并执行 SQL Migration,同时更新视图、字段定义和页面配置。这个过程仍然是数据库结构变更,不是普通 JSON 配置。 ### 7.4 数据库约束 数据库负责结构完整性: - 主键; - 外键; - 唯一索引; - 非空约束; - 必要 CHECK; - 查询和关联索引。 不使用触发器替代库存、财务和审批业务流程。业务规则放在业务域服务和事务 SQL 中。 ## 8. 页面和配置 ### 8.1 页面不是元数据生成的 核心业务页面由业务域代码实现。页面可以复用通用列表、表单和查询组件,但页面的字段、动作和业务行为由业务域明确声明。 页面配置只保存: - 列显示顺序; - 列宽; - 表单分组; - 查询默认值; - 用户个人偏好; - 显示格式。 不使用 JSON 描述完整业务页面和业务处理器。 ### 8.2 模块表的定位 如果保留 `s_module`,它只用于: - 菜单和路由登记; - 权限对象登记; - 模块名称和多语言资源键; - 业务域对象的索引。 它不负责: - 生成所有 SQL; - 推断所有保存逻辑; - 承载业务状态; - 解释任意 JSON 规则。 ### 8.3 公司差异 第一阶段不建设通用的租户、公司、部门、角色多层配置继承。 确实存在公司差异时,在具体业务表中直接使用 `company_id`,例如: ```text s_number_rule(company_id, domain_code, field_code) wf_binding(company_id, domain_code, event_code) s_print_template(company_id, template_code) ``` 不把所有差异都抽象为一个通用覆盖引擎。 ## 9. 权限 权限采用简单的角色模型: ```text s_role s_user_role s_role_permission s_role_data_scope s_user_company ``` 权限对象绑定业务域和业务动作: ```text customer.read customer.create sales_order.submit sales_order.approve inventory.adjust finance.post ``` 第一阶段支持: - 用户可用公司; - 用户可用组织; - 用户可用仓库; - 模块访问权限; - 业务动作权限; - 简单数据范围。 不实现角色继承、拒绝权限和通用权限表达式。复杂范围由对应业务域的 SQL 实现。 ## 10. 工作流 工作流是业务域的流程编排器,不是业务规则中心。 工作流绑定业务域动作: ```text sales_order.submit sales_order.approve purchase_order.submit expense.approve ``` 工作流可以决定: - 下一节点; - 审批人; - 当前流程状态; - 需要调用哪个业务动作。 业务域负责真正的动作: - 销售订单批准后是否允许出库; - 采购收货如何增加库存; - 财务单据如何过账; - 审批失败如何补偿。 工作流实例固定流程版本,历史审批记录只追加不修改。 ## 11. 多语言 第一阶段保持简单: ```text s_i18n_type s_i18n ``` 系统文案使用稳定资源键: ```text sales.order.name sales.order.customer action.submit ``` 业务主数据使用业务域自己的翻译表: ```text b_product b_product_translation b_customer b_customer_translation ``` 系统资源可以放在代码资源文件或租户库中,但必须统一一种来源。第一阶段不实现三层 overlay、翻译变更集和运行时语言管理。 历史单据保存产品名称、客户名称、税率、地址、币种和金额等快照。 ## 12. 配置和版本 第一阶段不建设覆盖所有对象的通用配置版本引擎。 配置分为三类: | 类型 | 实现方式 | | --- | --- | | 数据库结构 | SQL Migration | | 业务逻辑 | 代码和业务 SQL | | 页面表现 | 页面代码和少量布局配置 | | 工作流 | 独立流程版本表 | | 业务参数 | 明确的参数表 | 只有工作流、编号规则、打印模板等确实需要历史追溯的内容,才单独设计版本。 ### 12.1 SQL Migration SQL Migration 是按顺序执行、可追踪的数据库结构变更脚本。例如: ```text migration/ ├── V001__create_company.sql ├── V002__create_customer.sql └── V003__add_customer_tax_no.sql ``` 迁移工具在每个租户数据库中记录已执行的版本。发布新代码时,先执行缺少的迁移,再启动依赖新结构的代码。 Migration 可以包含: - 建表、改表和删表; - 增加或停用字段; - 创建视图、索引和约束; - 初始化字典和系统数据; - 必要的数据回填。 Migration 不等于运行时页面配置。页面配置可以立即保存,数据库结构变化则必须经过迁移记录。 数据库迁移记录: ```text schema_version migration_name checksum executed_at ``` ## 13. 代码风格和目录规范 新系统的代码按业务域组织,不按页面组织,也不按一组通用配置表组织。 ```text domains/ sales/ migration/ sql/ repository/ service/ api/ page/ i18n/ ``` 统一要求: - 一个业务动作只有一个主要入口; - SQL 统一放业务域的 `sql` 目录; - 页面只调用业务域 API,不直接写业务 SQL; - 业务域不通过另一个域的内部表直接修改数据; - 数据库表、SQL、服务、接口和页面使用同一业务名称; - 新增抽象必须来自至少两个真实业务域的重复需求。 数据库命名在新系统中一次确定: - 表名使用小写 `snake_case`; - 系统表使用 `s_`; - 业务表使用 `b_` 或明确业务域前缀; - 工作流表使用 `wf_`; - 字段命名统一使用一套规则,不再混用 `b_`、`mx_` 和裸字段名。 ## 14. 第一阶段建设范围 第一阶段只建设架构内核和一条完整业务链路。 ### 14.1 必须完成 1. SQL Server 迁移工具和数据库版本表; 2. 租户数据库连接和路由; 3. 用户、角色、公司、组织、仓库和账簿; 4. 统一业务上下文; 5. 业务域目录和 SQL 调用约定; 6. 基础权限和公司范围; 7. 审计日志; 8. 基础多语言; 9. 一个业务域的表、SQL、服务、接口和页面完整闭环。 ### 14.2 建议示例 建议先做客户或产品域: ```text SQL Migration → 真实业务表 → 查询 SQL → 保存 SQL → 业务域服务 → API → 页面 → 公司权限 → 审计 ``` 这个示例必须验证“业务域代码包是中心”,不只是验证通用页面是否能显示。 ### 14.3 第一阶段不做 - 以全局 `s_module` 驱动的通用 CRUD 作为核心业务默认实现; - 全局 `s_field` 驱动的运行时字段创建; - 通用页面设计器; - 通用公式和规则解释器; - 全对象配置版本和变更集; - 多层公司配置覆盖; - 复杂多语言发布体系; - 集团分析库和跨租户业务查询。 ## 15. 第一阶段验收标准 ### 15.1 架构中心 - 一个业务域拥有自己的迁移、SQL、服务和页面; - 业务域可以不依赖通用模块解释器运行; - 页面配置不能替代业务代码; - 工作流只能调用明确登记的业务动作。 ### 15.2 SQL 驱动 - 业务查询可以直接在 SQL Server 中验证; - 复杂查询使用业务域 SQL 文件或视图; - 业务表和关联关系是真实 SQL 结构; - 新增可统计字段需要物理列或索引扩展; - 不为了页面功能复制业务表。 ### 15.3 简单和一致 - 同类业务域使用相同目录和调用流程; - 不存在同一类配置的多种格式; - 不存在同一业务动作的多个通用入口; - 数据库迁移和代码可以一起发布; - 第一阶段不依赖通用低代码运行时。 ## 16. 旧设计迁移原则 旧设计不是全部废弃,而是按职责迁移: | 旧内容 | 新系统处理方式 | | --- | --- | | 真实业务表 | 保留并按业务域归属整理 | | 业务 SQL | 移入对应业务域 `sql` 目录 | | `wf_*` 工作流表 | 保留,改为绑定业务动作 | | `s_module` | 降为菜单、权限和对象索引 | | `s_field` | 降为字段说明,不负责运行时造字段 | | `s_module_schema` | 页面局部配置,不作为核心运行时 | | 通用保存接口 | 仅保留简单基础资料使用 | | JSON 公式和动态规则 | 迁移到业务代码或 SQL | | `b_query_sql` 等全局 SQL 字段 | 逐步迁移到业务域 SQL 文件 | ## 17. 最终架构关系 ```text 业务域代码包是组织中心 SQL 表和视图是事实来源 SQL 文件是查询和处理的主要实现 业务代码负责动作和事务 页面负责交互 工作流负责流程编排 少量配置负责表现和参数 ``` 新系统不是旧系统增加一层配置,也不是把旧模块表重新命名。它的核心变化是: > **从元数据驱动的通用模块,改成业务域驱动的 SQL 应用。**