# 规则域多租户方案 A 实施计划

> 适用范围：`leaudit-platform` 新平台规则配置、评查组、规则版本、评查运行结果链路  
> 方案结论：采用 `方案 A = 共享业务树 + 租户规则绑定 + 租户结果快照`  
> 更新日期：2026-05-21  
> 文档定位：把“规则域如何接入多租户”从概念方案落成可执行的修改计划，作为后续开发、联调、数据库迁移、验收的统一基线。

---

## 1. 定案结论

规则域本轮正式采用 `方案 A`，不直接做“每租户独立整棵业务树”，也不在第一阶段引入“租户扩展节点”。

当前统一口径如下：

1. `leaudit_evaluation_point_groups` 继续作为共享业务树
2. 租户差异主要放在：
   - `leaudit_rule_sets`
   - `leaudit_rule_versions`
   - `leaudit_rule_group_bindings`
   - `leaudit_audit_runs`
   - `leaudit_rule_results / leaudit_run_errors / leaudit_run_metrics`
3. 运行时规则生效顺序固定为：
   - `TENANT`
   - `PROVINCIAL`
   - `PUBLIC`
4. `tenant_code` 是唯一真实租户边界
5. `tenant_name` 只做展示与快照
6. `area / region / default / 公共 / 省级` 只允许留在兼容层与历史清洗层

---

## 2. 当前问题复盘

规则域目前不是“功能不可用”，而是“资产归属模型仍是全局共享思路”，因此天然不满足真正多租户隔离。

当前高风险点如下：

1. `leaudit_rule_sets` 仍缺 `tenant_code`
2. `leaudit_rule_versions` 仍缺租户快照
3. `leaudit_rule_group_bindings` 仍缺租户边界
4. `leaudit_audit_runs` 仍缺租户快照，导致结果链条没有稳定归属锚点
5. `leaudit_rule_results / leaudit_run_errors / leaudit_run_metrics` 只能靠 `run_id` 间接推断租户
6. `auditServiceImpl` 运行时按 `group_id` 取规则，但没有按租户选“当前生效规则”
7. `ruleServiceImpl` 仍以全局 `rule_type -> rule_set` 为主语义

这意味着：

1. 不同租户可能共用同一个规则集与版本链
2. 一个租户发布的新版本，可能直接影响另一个租户的运行结果
3. 规则结果报表、失败诊断、运行统计无法稳定按租户回溯

---

## 3. 方案 A 的边界定义

## 3.1 保持共享的部分

以下内容第一阶段继续共享：

1. 业务树：`leaudit_evaluation_point_groups`
2. 文档类型：`leaudit_document_types`
3. 入口模块与业务树的主挂载关系

共享的含义不是“不做租户控制”，而是：

1. 树结构本身平台统一维护
2. 哪些租户能看到哪些入口/业务节点，由可见性控制解决
3. 某业务节点下到底生效哪套规则，由租户绑定解决

## 3.2 租户化的部分

以下内容必须 tenant-first：

1. 规则集归属
2. 规则版本归属
3. 业务组到规则集的绑定关系
4. 评查运行记录
5. 评查结果、错误、指标等衍生记录

## 3.3 暂不纳入第一阶段的部分

本轮明确不做：

1. 每租户复制整棵业务树
2. 租户自定义树层级结构
3. 树节点级继承编辑器
4. 租户扩展节点能力

这些能力只预留扩展位，不进入本轮交付范围。

---

## 4. 目标架构

规则域的目标链路收口为：

`文档(tenant_code) -> 共享业务树 group -> 按 tenant_code 解析生效 binding -> 命中 tenant/provincial/public 规则集 -> 锁定规则版本 -> 创建带租户快照的 audit_run -> 结果/错误/指标带租户快照落库`

核心设计点：

1. 业务树是“分类锚点”
2. 规则绑定是“租户差异入口”
3. `audit_run` 是“历史快照锚点”
4. 结果表是“查询与报表加速层”，不再只靠运行时 join 推断租户

---

## 5. 数据模型修改计划

## 5.1 `leaudit_rule_sets`

### 新增字段

1. `tenant_code VARCHAR(64) NULL`
2. `scope_type VARCHAR(32) NOT NULL DEFAULT 'PROVINCIAL'`
3. `source_rule_set_id BIGINT NULL`
4. `tenant_name_snapshot VARCHAR(255) NULL`

### 约束与索引

1. 索引：`idx_leaudit_rule_sets_tenant_code`
2. 索引：`idx_leaudit_rule_sets_scope_type`
3. 唯一索引建议调整为：
   - `tenant_code + rule_type + deleted_at IS NULL`
   - 或 `COALESCE(tenant_code, '') + rule_type + deleted_at IS NULL`

### 语义

1. `TENANT`：租户私有规则集
2. `PROVINCIAL`：省级统一治理规则集
3. `PUBLIC`：真正公共资源域规则集
4. `source_rule_set_id`：租户规则集如果来自省级继承/复制，记录来源

## 5.2 `leaudit_rule_versions`

### 新增字段

1. `tenant_code_snapshot VARCHAR(64) NULL`
2. `scope_type_snapshot VARCHAR(32) NULL`
3. `source_version_id BIGINT NULL`

### 原则

1. 版本归属以创建时快照固化
2. 不允许完全依赖 join `rule_set` 才知道版本属于谁

## 5.3 `leaudit_rule_group_bindings`

### 新增字段

1. `tenant_code VARCHAR(64) NULL`
2. `scope_type VARCHAR(32) NOT NULL DEFAULT 'PROVINCIAL'`
3. `tenant_name_snapshot VARCHAR(255) NULL`

### 索引与约束

1. 索引：`idx_leaudit_rule_group_bindings_group_tenant`
2. 索引：`idx_leaudit_rule_group_bindings_scope_type`
3. 唯一索引调整为：
   - `group_id + rule_set_id + COALESCE(tenant_code, '') + deleted_at IS NULL`

### 语义

同一个 `group_id` 可以同时存在：

1. 一个租户私有绑定
2. 一个省级绑定
3. 一个公共绑定

运行时按优先级解析，不再只看 `priority`。

## 5.4 `leaudit_rule_type_bindings`

### 第一阶段处理原则

1. 不再作为新主链路真相来源
2. 补 `tenant_code / scope_type` 仅用于兼容老接口与迁移脚本
3. 新平台运行与配置以 `leaudit_rule_group_bindings` 为准

## 5.5 `leaudit_audit_runs`

### 必补字段

1. `tenant_code VARCHAR(64) NULL`
2. `tenant_name_snapshot VARCHAR(255) NULL`
3. `scope_type_snapshot VARCHAR(32) NULL`
4. `group_id_snapshot BIGINT NULL`
5. `rule_binding_id_snapshot BIGINT NULL`

### 原因

`audit_run` 是整个历史追溯的锚点。  
没有这层快照，后续规则换绑、租户改名、共享域清洗后，历史运行会失去稳定归属。

## 5.6 `leaudit_rule_results`

### 建议新增字段

1. `tenant_code VARCHAR(64) NULL`
2. `tenant_name_snapshot VARCHAR(255) NULL`

### 原则

虽然可以通过 `run_id` 反查，但结果表是高频列表、导出、统计来源，补快照能避免后续大量 join。

## 5.7 `leaudit_run_errors`

### 建议新增字段

1. `tenant_code VARCHAR(64) NULL`
2. `tenant_name_snapshot VARCHAR(255) NULL`

## 5.8 `leaudit_run_metrics`

### 建议新增字段

1. `tenant_code VARCHAR(64) NULL`

---

## 6. 历史数据回填策略

## 6.1 回填总原则

1. 现有规则资产默认回填为 `PROVINCIAL`
2. 不把现有全局规则直接回填成 `PUBLIC`
3. 历史 `region/default/公共/省级` 只作为映射依据，不作为新模型长期值

## 6.2 回填顺序

1. 先补字段与索引
2. 再给 `rule_sets / rule_versions / group_bindings` 回填省级归属
3. 再给 `audit_runs` 补租户快照
4. 最后给 `rule_results / run_errors / run_metrics` 做快照回填

## 6.3 回填映射口径

1. 原来全局共享规则集：回填 `tenant_code='PROVINCIAL'`
2. 原来 `default / 省级 / 省局`：统一映射到 `PROVINCIAL`
3. 原来真正公共资源：才回填 `PUBLIC`

---

## 7. 运行时解析修改计划

## 7.1 目标规则

运行时解析“某文档某分组到底用哪套规则”时，必须以文档 `tenant_code` 为第一主语义。

## 7.2 生效顺序

固定解析顺序：

1. 精确租户：`tenant_code = document.tenant_code`
2. 省级继承：`tenant_code = 'PROVINCIAL'`
3. 公共继承：`tenant_code = 'PUBLIC'`

即：

`TENANT -> PROVINCIAL -> PUBLIC`

## 7.3 修改点

文件：

- [auditServiceImpl.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/services/impl/auditServiceImpl.py)

需要改造：

1. `_resolve_rule_binding_from_group`
2. `_resolve_unique_group_binding_by_doc_type`
3. `Run`

目标：

1. 查询绑定时显式带入 `tenant_code`
2. 绑定结果返回：
   - `binding_id`
   - `binding tenant_code`
   - `binding scope_type`
   - `rule_set tenant_code`
3. 创建 `audit_run` 时直接写入租户快照与绑定快照

---

## 8. 后端改造任务拆分

## 8.1 `R1` 数据库迁移脚本

涉及：

1. 新增一套规则域迁移 SQL
2. 新增预检 SQL
3. 新增回填验证 SQL

建议文件：

1. `scripts/创建sql/schema_rule_domain_tenant_phase1.sql`
2. `scripts/创建sql/precheck_rule_domain_tenant_phase1.sql`
3. `scripts/创建sql/verify_rule_domain_tenant_phase1.sql`

交付目标：

1. 字段补齐
2. 索引补齐
3. 历史数据回填
4. 验证 SQL 可重复执行

## 8.2 `R2` 规则资产服务租户化

涉及文件：

1. [ruleServiceImpl.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/services/impl/ruleServiceImpl.py)
2. [ruleController.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/controllers/ruleController.py)

改造范围：

1. `ListSets`
2. `GetVersions`
3. `GetContent`
4. `CreateVersion`
5. `PublishRuleVersion`
6. `RollbackRuleVersion`

目标：

1. 规则集列表按当前租户 + 继承层展示
2. 创建新版本时，不再按全局 `rule_type` 命中资产
3. 发布/回滚只影响当前租户生效链

## 8.3 `R3` 业务组绑定租户化

涉及文件：

1. [evaluationPointGroupServiceImpl.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py)
2. [ruleGroupSupport.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/services/impl/ruleGroupSupport.py)

改造范围：

1. `ListBindings`
2. `CreateBinding`
3. `UpdateBinding`
4. `DeleteBinding`
5. `GetRuleTemplate`
6. `CreateRuleDraft`

目标：

1. 绑定表支持租户归属
2. 同组可见绑定区分：
   - 本租户
   - 继承省级
   - 继承公共
3. 后台页面能识别“当前生效绑定来自哪里”

## 8.4 `R4` 运行链路租户化

涉及文件：

1. [auditServiceImpl.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/services/impl/auditServiceImpl.py)
2. [storage_adapter.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/leaudit_bridge/storage_adapter.py)

改造范围：

1. 运行时选规则
2. 创建 `audit_run`
3. 结果、错误、指标落库

目标：

1. 运行链路 tenant-first
2. 所有衍生记录带租户快照
3. 后续报表不再依赖复杂 join 推断

## 8.5 `R5` 规则配置页聚合租户化

涉及文件：

1. [ruleConfigServiceImpl.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/services/impl/ruleConfigServiceImpl.py)

目标：

1. `ListPackSummaries` 与 `GetPack` 按当前租户解析生效规则
2. 返回新增标准字段：
   - `effectiveTenantCode`
   - `effectiveScopeType`
   - `isInherited`
   - `sourceRuleSetId`
3. 页面能区分：
   - 本租户私有规则
   - 继承省级规则
   - 继承公共规则

## 8.6 `R6` 鉴权与权限补齐

涉及文件：

1. [ruleController.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/controllers/ruleController.py)
2. [ruleConfigController.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/controllers/ruleConfigController.py)
3. [evaluationPointGroupController.py](/home/wren-dev/Porject/leaudit-platform/fastapi_modules/fastapi_leaudit/controllers/evaluationPointGroupController.py)

目标：

1. 所有规则域接口强制登录态
2. 功能权限校验与租户上下文透传统一化
3. 杜绝“裸接口 + 全局资产”的组合风险

---

## 9. 前端改造计划

## 9.1 规则配置页

目标：

1. 展示当前规则来源
2. 展示当前租户是否继承上层规则
3. 发布/回滚操作前明确告知影响范围

应补能力：

1. “当前生效租户域”标签
2. “继承来源”标签
3. “复制省级为租户私有规则”入口

## 9.2 评查组管理页

目标：

1. 共享业务树继续展示
2. 绑定列表显式展示租户域
3. 避免让用户误以为业务树已经按租户独立

## 9.3 历史兼容口径

前端主模型不再依赖：

1. `region`
2. `default`
3. 中文共享常量

前端应统一消费：

1. `tenant_code`
2. `tenant_name`
3. `scope_type`
4. `isInherited`

---

## 10. 验收矩阵

## 10.1 规则资产

1. 租户 A 创建规则版本，不影响租户 B 当前规则
2. 省级发布规则，未覆盖的租户自动继承
3. 已有租户私有规则的组，不再被省级发布直接覆盖

## 10.2 运行链路

1. 梅州文档运行时优先命中 `MZ`
2. `MZ` 无绑定时回退 `PROVINCIAL`
3. `PROVINCIAL` 无绑定时回退 `PUBLIC`
4. `audit_run` 落库后可直接看到租户快照

## 10.3 结果与报表

1. `rule_results` 可直接按 `tenant_code` 过滤
2. `run_errors` 可直接按 `tenant_code` 过滤
3. `run_metrics` 可直接按 `tenant_code` 聚合

## 10.4 页面行为

1. 规则配置页能显示“本租户规则 / 继承省级 / 继承公共”
2. 评查组绑定页能正确显示当前生效规则来源
3. 非当前租户用户不能修改本租户规则资产

---

## 11. 执行顺序

建议按下面顺序推进，不再交叉大爆改：

1. `R1` 数据库迁移脚本
2. `R4` 运行链路租户化
3. `R2` 规则资产服务租户化
4. `R3` 业务组绑定租户化
5. `R5` 规则配置页聚合租户化
6. `R6` 鉴权与权限补齐
7. 前端联调与发布级验收

执行原则：

1. 先补“数据真相字段”
2. 再改“运行时生效逻辑”
3. 最后改“后台配置与页面展示”

---

## 12. 风险与边界

## 12.1 高风险

1. `rule_type` 当前可能被多处假定为全局唯一
2. 历史运行结果回填需要保证与旧 `document.tenant_code` 一致
3. 省级与公共域清洗口径必须先定死，不能一边跑一边改

## 12.2 中风险

1. 前端若仍只展示“当前绑定”，用户会误判租户隔离是否生效
2. `rule_type_bindings` 老兼容链路如果不断回写，容易污染新模型

## 12.3 本轮不做

1. 不做租户扩展节点
2. 不做独立业务树
3. 不做历史规则资产自动差异合并

---

## 13. 最终交付定义

当以下 6 条同时满足时，规则域多租户方案 A 才算真正落地：

1. 规则集、规则版本、组绑定都有稳定 `tenant_code`
2. 运行时规则解析已改为 `TENANT -> PROVINCIAL -> PUBLIC`
3. `audit_run` 已带完整租户快照
4. `rule_results / run_errors / run_metrics` 已能直接按租户查询
5. 规则配置页能明确展示当前规则来源与继承关系
6. 发布级验收已证明不同租户互不串规则、互不串结果
