# 入口模块绑定最终设计方案

> 适用项目：`leaudit-platform`
>
> 参考来源：
> - 老系统：`/home/wren-dev/Porject/docauditai`
> - 新前端：`/home/wren-dev/Porject/leaudit-platform/new_doc_review`
> - 新后端：`/home/wren-dev/Porject/leaudit-platform/fastapi_modules`

---

## 1. 这份文档解决什么问题

本方案要解决的是：

1. 老系统“入口模块”到底是什么语义
2. 新系统里 `entry_modules / document_types / sys_routes / role_route` 各自该负责什么
3. 首页入口模块应该由谁来组装
4. 现有逻辑有哪些遗漏和隐患
5. 下一步如何把它收口成一套稳定方案

这份文档的目标不是直接讲实现细节，而是先把**边界、语义、数据流、遗漏点**固定下来，避免后续继续前后端双写规则。

---

## 2. 结论先行

### 2.1 核心结论

老系统里的“入口模块”**不是权限系统**，而是**首页业务入口编排系统**。

它的职责是：

- 决定首页显示哪些入口卡片
- 给每个入口卡片绑定一组文档类型
- 按地区启用/禁用入口

它**不负责**：

- 控制用户是否可以访问某个页面
- 控制用户是否可以调用某个 API
- 替代 RBAC 路由菜单系统

真正的权限系统始终是：

- `sys_routes`
- `role_route`
- `permissions`
- `role_permissions`

### 2.2 新系统必须收口的总原则

新系统应该明确分成三层：

1. **首页入口层**
   - 数据源：`leaudit_entry_modules`
   - 作用：决定首页显示什么入口

2. **业务归类层**
   - 数据源：`leaudit_document_types.entry_module_id`
   - 作用：决定某入口下挂哪些文档类型

3. **权限控制层**
   - 数据源：`sys_routes / role_route / permissions / role_permissions`
   - 作用：决定用户是否真的能访问页面和动作

### 2.3 当前已落地接口口径

当前首页入口已经统一走：

- `GET /api/home/entry-modules`

当前返回结构核心字段包括：

- `id`
- `name`
- `description`
- `targetPath`
- `routePath`
- `iconPath`
- `sortOrder`
- `requiresDocumentTypes`
- `areas`
- `documentTypes`

当前过滤顺序为：

1. `leaudit_entry_modules.is_enabled = true`
2. `leaudit_entry_modules.deleted_at is null`
3. 地区过滤
4. 若目标页面已存在于 `sys_routes`，则继续做 `role_route` 权限过滤

这意味着首页入口已经不是前端自己拼装，而是后端统一组装后返回。

---

## 3. 老系统深度分析结论

## 3.1 首页入口不是从 sys_routes 直接来

老系统首页入口不是直接根据 RBAC 路由树生成，而是单独查询入口模块配置。

对应证据：

- 老系统入口模块服务：
  - `/home/wren-dev/Porject/docauditai/app/services/entry_module_service.py`
- 老系统入口模块路由：
  - `/home/wren-dev/Porject/docauditai/app/routes/entry_modules.py`
- 老系统文档类型服务：
  - `/home/wren-dev/Porject/docauditai/app/services/document_type_service.py`

### 实际链路

老系统首页逻辑本质是：

1. 查 `entry_modules`
2. 按 `areas` 做地区过滤
3. 对每个入口模块，再查 `document_types where entry_module_id = module.id`
4. 拼成首页卡片
5. 点击卡片后跳转目标页面
6. 目标页面再走 RBAC 校验

所以首页入口和页面权限本来就是两套体系。

---

## 3.2 老系统中 entry_modules 的真实职责

老系统 `entry_modules` 本质上存的是“首页业务入口卡片配置”。

典型字段语义：

- `id`：入口模块主键
- `name`：入口名称
- `description`：说明
- `path`：旧系统里语义不稳定，常被当成资源/图片路径使用
- `areas`：地区配置
- `created_at / updated_at`

### 关键观察

老系统里 `path` 不是一个稳定可靠的“点击后跳转路由字段”。

也就是说：

- 它并不总是纯前端路由
- 它常常承担资源路径、展示配置、模块图片等混合语义

这也是老系统最容易让后续维护者误解的地方之一。

---

## 3.3 文档类型和入口模块的关系

老系统文档类型是通过 `entry_module_id` 绑定到入口模块的。

这说明：

- 一个入口模块下面挂很多文档类型
- 一个文档类型归属于一个入口模块

这个设计本身是合理的，属于“业务归类关系”，不属于“权限关系”。

### 当前判断

除非未来明确出现“一个文档类型必须出现在多个入口模块”的业务需求，否则：

- 新系统应继续保留 `entry_module_id`
- 没必要提前拆成中间表

---

## 3.4 真正的页面权限来自 RBAC

老系统页面权限来源一直是：

- `sys_routes`
- `role_route`

动作/API 权限来源是：

- `permissions`
- `role_permissions`

这意味着在老系统中可能出现：

1. 首页能看到某个入口模块
2. 点击后跳到某个页面
3. 页面又因为 RBAC 不通过而拒绝访问

这是老系统长期存在的结构性问题，不是 Bug，而是设计分层没有被清楚说明。

---

## 4. 新系统最终语义边界

## 4.1 `leaudit_entry_modules`

### 负责

- 首页显示哪些业务入口
- 每个入口的名称、图标、排序、地区启用规则
- 每个入口点击后去哪

### 不负责

- 页面访问权限
- API 权限
- 替代路由表

---

## 4.2 `leaudit_document_types`

### 负责

- 定义系统支持的文档类型
- 通过 `entry_module_id` 归属到某个入口模块

### 不负责

- 决定首页是否展示入口模块
- 决定菜单权限

---

## 4.3 `sys_routes / role_route`

### 负责

- 页面路由菜单权限
- 用户可访问哪些页面

### 不负责

- 首页模块展示编排
- 文档类型归类

---

## 4.4 `permissions / role_permissions`

### 负责

- API 权限
- 动作按钮权限
- 数据范围权限

### 不负责

- 首页入口展示
- 文档类型归属

---

## 5. 新系统最终数据模型建议

## 5.1 `leaudit_entry_modules` 建议字段

当前表建议正式化为以下语义：

- `id`
- `module_key`
- `name`
- `description`
- `icon_path`
- `target_path`
- `route_path`
- `module_type`
- `allow_empty_types`
- `areas`
- `sort_order`
- `is_enabled`
- `create_time`
- `update_time`

### 字段解释

#### `module_key`

稳定的业务标识，用于程序判断，不可依赖 `name`。

示例：

- `contract`
- `case_review`
- `assistant`
- `cross_review`

#### `icon_path`

首页卡片图标/图片路径。

应从旧的 `path` 语义中剥离出来，单独承担展示职责。

#### `target_path`

用户点击首页卡片后实际跳转的前端路径。

示例：

- `/contract-template/search`
- `/home`
- `/chat-with-llm/chat`
- `/cross-checking`

#### `route_path`

与 RBAC 路由树关联的目标页面路径，用于做“当前用户是否真的有访问权限”的判断。

通常可与 `target_path` 相同，但不强制必须相同。

#### `module_type`

建议枚举：

- `document`
- `assistant`
- `cross_review`
- `settings_link`

#### `allow_empty_types`

控制“没有绑定文档类型时是否仍然允许显示”。

建议：

- 文档类模块：`false`
- 助手类模块：`true`
- 交叉评查类模块：`true`

---

## 5.2 `leaudit_document_types`

继续保留：

- `entry_module_id`

表示“该文档类型归属哪个入口模块”。

### 当前不建议做的事

暂不建议改成：

- `leaudit_entry_module_document_type_bindings`

因为当前没有明确“一种文档类型要同时挂多个入口”的需求。

---

## 6. 首页统一接口最终设计

建议新增：

- `GET /api/home/entry-modules`

由后端直接根据当前登录用户返回首页入口列表。

前端不再自己拼 `entry_modules + document_types + area + 跳转规则`。

---

## 6.1 接口职责

后端统一完成：

1. 识别当前用户
2. 读取用户角色
3. 读取用户地区
4. 查询可用入口模块
5. 按地区过滤
6. 按页面权限做一次 RBAC 二次过滤
7. 挂载该入口下的文档类型
8. 返回最终首页可见入口列表

---

## 6.2 建议返回结构

```json
{
  "code": 200,
  "message": "ok",
  "data": {
    "items": [
      {
        "id": 1,
        "moduleKey": "contract",
        "name": "合同管理",
        "description": "合同智能审核与模板管理",
        "iconPath": "static/modules/contract.png",
        "targetPath": "/contract-template/search",
        "routePath": "/contract-template/search",
        "moduleType": "document",
        "allowEmptyTypes": false,
        "sortOrder": 10,
        "documentTypes": [
          {
            "id": 9,
            "code": "contract.sale",
            "name": "买卖合同"
          }
        ]
      }
    ]
  }
}
```

---

## 7. 首页统一接口的推荐过滤规则

## 7.1 基础过滤

必须满足：

- `is_enabled = true`

---

## 7.2 地区过滤

### 省级管理员

- `provincial_admin` 可查看所有启用入口模块

### 地市管理员 / 普通用户

- `admin`
- `common`

仅可看到 `areas` 中当前 `user.area` 且 `enabled = true` 的模块。

### 重要约束

地区过滤必须在后端完成，不能信前端传 area。

---

## 7.3 RBAC 二次过滤

如果入口模块配置了 `route_path`，则需要再检查当前用户是否有该页面的访问权限。

### 目的

避免出现：

- 首页看得到入口
- 点进去却被 RBAC 拒绝

### 推荐做法

基于当前用户角色集合，查询：

- `sys_routes.route_path = route_path`
- 是否被 `role_route` 授权

若未授权，则该入口模块不返回。

---

## 7.4 文档类型过滤

### 对 `module_type = document`

必须至少有一个启用中的文档类型，否则不返回。

### 对 `module_type = assistant / cross_review`

允许没有文档类型，只要模块本身启用并通过权限检查即可返回。

---

## 7.5 排序规则

建议优先级：

1. `areas[].sort_order`（如果当前地区有配置）
2. `leaudit_entry_modules.sort_order`
3. `id`

---

## 8. 前端必须删掉的旧逻辑

## 8.1 不能再由前端自己查 `entry_modules`

当前旧逻辑：

- 前端查入口模块
- 前端按地区过滤
- 前端再查文档类型

这会导致规则散落在前端，不利于后端统一治理。

应改为：

- 前端只调用 `GET /api/home/entry-modules`

---

## 8.2 不能再按模块名硬编码跳转

当前风险逻辑：

- 模块名包含“合同” -> `/contract-template/search`
- 模块名是“智慧法务助手” -> `/chat-with-llm/chat`

这类逻辑必须移除。

应改为直接使用后端返回的：

- `targetPath`

---

## 8.3 不能再在前端默认硬塞特殊模块

例如：

- “智慧法务助手”

不应再由前端强行 `push()` 到入口列表里。

必须和普通模块一样进入 `leaudit_entry_modules` 正式配置。

这样做的好处：

- 权限统一
- 排序统一
- 是否启用统一
- 图标统一
- 后台管理统一

---

## 9. 当前逻辑遗漏点深度检查

下面是本次分析中确认存在或高概率会出现的遗漏点。

## 9.1 首页可见 ≠ 页面可访问

这是老系统就存在的问题。

如果首页只按 `entry_modules` 过滤，不按 RBAC 再过滤，就会出现：

- 首页看到入口
- 点进去又被页面权限拦住

### 结论

首页接口必须做 RBAC 二次过滤。

---

## 9.2 `path` 字段语义不稳定

老系统里 `entry_modules.path` 混杂：

- 图片路径
- 资源路径
- 可能被误当成前端路由

### 结论

新系统不能继续复用一个 `path` 字段承担两种含义。

必须拆成：

- `icon_path`
- `target_path`

---

## 9.3 特殊模块没有文档类型时会被误过滤

如果首页统一规则是“模块下必须有文档类型”，那么：

- 智慧法务助手
- 交叉评查

这类模块会被错误隐藏。

### 结论

必须引入：

- `module_type`
- `allow_empty_types`

---

## 9.4 省级管理员是否应该看到所有模块

这个规则必须固定，不然以后省级账号行为会不一致。

### 建议

- `provincial_admin` 看全部已启用入口
- 但仍需受目标页面 RBAC 权限约束

---

## 9.5 文档类型全部停用后，文档类入口是否显示

如果一个“合同管理”模块下面一个启用文档类型都没有，继续显示没有意义。

### 建议

- `module_type = document` 且无可用文档类型 -> 不显示
- `assistant / cross_review` 不受此规则影响

---

## 9.6 入口模块与左侧菜单不是同一概念

首页入口模块是“业务入口卡片”，左侧菜单是“页面路由树”。

如果不写清楚，后面会经常出现误解：

- 为什么首页有入口，但侧边栏没有？
- 为什么有路由权限，但首页不显示？

### 结论

文档中必须明确：

- 首页入口 = 业务编排
- 左侧菜单 = RBAC 路由

---

## 9.7 交叉评查专属模式不应混入入口模块业务规则

当前前端已有：

- `CROSS_CHECKING_ONLY_MODE`

它本质上是部署态开关，不是入口模块自身规则。

### 结论

该逻辑应作为部署模式单独处理，不要污染 `entry_modules` 基础语义。

---

## 9.8 首页入口上下文仍然需要保留

即使首页入口最终完全由后端返回，前端仍然建议保留：

- `selectedModuleId`
- `selectedModuleName`
- `documentTypeIds`

写入 `sessionStorage`

### 原因

后续页面仍然需要知道：

- 当前用户是从哪个入口进入的
- 当前模块下允许哪些文档类型

---

## 10. 新系统最终推荐方案

## 10.1 强约束

新系统正式规则建议固定如下：

1. 首页入口只认 `leaudit_entry_modules`
2. 文档类型归属只认 `leaudit_document_types.entry_module_id`
3. 页面访问权限只认 `sys_routes + role_route`
4. API/动作权限只认 `permissions + role_permissions`
5. 首页入口列表必须由后端统一接口生成
6. 前端只能消费最终结果，不再自己拼规则

---

## 10.2 最终推荐模型

### 模块主数据

- `leaudit_entry_modules`

### 文档类型归属

- `leaudit_document_types.entry_module_id`

### 页面访问权限

- `sys_routes`
- `role_route`

### 动作/API 权限

- `permissions`
- `role_permissions`

---

## 10.3 首页统一接口

- `GET /api/home/entry-modules`

由后端返回：

- 当前用户可见的入口模块
- 每个入口模块的目标跳转路径
- 每个入口模块挂载的文档类型列表

---

## 11. 后续代码改造建议

本次先定方案，代码实现建议按以下顺序推进。

### 阶段 1：后端收口

1. 新增首页入口接口
2. 后端实现地区过滤
3. 后端实现 RBAC 二次过滤
4. 后端挂载 `document_types`

### 阶段 2：前端收口

1. 首页改为只调用后端统一接口
2. 删除前端拼 `entry_modules + document_types` 的逻辑
3. 删除按模块名硬编码跳转
4. 删除前端默认注入“智慧法务助手”

### 阶段 3：表结构正式化

1. 给 `leaudit_entry_modules` 增加：
   - `module_key`
   - `target_path`
   - `route_path`
   - `module_type`
   - `allow_empty_types`
2. 若历史字段 `path` 仍在使用，则明确其迁移方向：
   - 老 `path` -> `icon_path`

---

## 12. 当前审阅建议

本方案建议你重点审阅以下 5 个点：

1. 是否同意“入口模块不是权限系统”的边界
2. 是否同意首页入口改为后端统一生成
3. 是否同意给 `entry_modules` 新增 `module_key / target_path / route_path / module_type / allow_empty_types`
4. 是否同意“智慧法务助手”这类特殊入口也必须正式落表
5. 是否同意“首页可见”和“页面可访问”保留两层校验，但由后端首页接口统一兜一次

---

## 13. 一句话版最终结论

新系统应把“首页入口模块”正式定义为**首页业务入口编排层**，把“文档类型绑定”定义为**业务归类层**，把“页面/API权限”继续留在 **RBAC 层**，并由后端统一输出首页入口列表，彻底结束前端自己拼入口、自己判地区、自己硬编码跳转的旧模式。
