docs: reorganize backend project documentation
This commit is contained in:
+34
-23
@@ -1,29 +1,40 @@
|
||||
# 接口文档目录
|
||||
# 接口文档导航
|
||||
|
||||
这个目录专门放当前 `leaudit-platform` 已落地接口的使用说明,重点记录:
|
||||
> 最后整理:2026-05-04
|
||||
> 说明:这里记录的是 `leaudit-platform` 当前主线已落地、正在联调、或明确作为迁移目标的接口文档。
|
||||
|
||||
- 接口用途
|
||||
- 参数说明
|
||||
- 业务逻辑
|
||||
- 请求示例
|
||||
- 返回示例
|
||||
## 阅读顺序
|
||||
|
||||
当前已整理:
|
||||
1. `docs/HANDOFF.md`
|
||||
2. `文档上传与列表接口分析.md`
|
||||
3. `入口模块绑定最终设计方案.md`
|
||||
4. `用户权限与权限点清单.md`
|
||||
5. 根据具体模块继续往下看
|
||||
|
||||
- `文档上传与评查接口.md`
|
||||
- `新系统版_documents_list接口.md`
|
||||
- `用户权限与权限点清单.md`
|
||||
- `用户权限初始化SQL.sql`
|
||||
- `老用户迁移脚本说明.md`
|
||||
- `首页入口接口落地说明.md`
|
||||
- `系统设置入口恢复说明.md`
|
||||
## 按模块查找
|
||||
|
||||
建议阅读顺序:
|
||||
| 模块 | 文档 | 说明 |
|
||||
|------|------|------|
|
||||
| 首页入口 / 菜单 | `入口模块绑定最终设计方案.md` | 入口模块、文档类型、规则链路绑定模型 |
|
||||
| 文档上传 / 列表 / 评查 | `文档上传与列表接口分析.md` | 上传、列表、详情、更新、删除、评查触发、数据隔离 |
|
||||
| 文档类型 / 评查组 | `评查点分组目标结构与迁移方案.md` | 文档类型、一级分组、二级分组、规则集与迁移口径 |
|
||||
| 评查点分组迁移 | `评查点分组目标结构与迁移方案.md` | 新老分组结构对齐方案 |
|
||||
| 评查点分组迁移 | `评查点分组迁移执行前检查清单.md` | 正式迁移前检查项 |
|
||||
| 权限 / 路由 | `用户权限与权限点清单.md` | RBAC、路由、权限点清单 |
|
||||
| 用户迁移 | `老用户迁移脚本说明.md` | 老用户迁移脚本与校验说明 |
|
||||
|
||||
1. 先看 `文档上传与评查接口.md`
|
||||
2. 再看 `新系统版_documents_list接口.md`
|
||||
3. 再看 `用户权限与权限点清单.md`
|
||||
4. 如果要初始化角色与权限数据,再执行 `用户权限初始化SQL.sql`
|
||||
5. 如果要迁移老系统用户,再看 `老用户迁移脚本说明.md`
|
||||
6. 如果要理解 worker 并发执行,再结合 `docs/规则编辑/worker并发执行改造方案.md`
|
||||
7. 如果要理解底层数据结构,再看 `docs/leaudit/document_schema_design.md`
|
||||
## 本次归并
|
||||
|
||||
- `新系统版_documents_list接口.md` 已并入 `文档上传与列表接口分析.md`
|
||||
- `文档上传与评查接口.md` 已并入 `文档上传与列表接口分析.md`
|
||||
- `首页入口接口落地说明.md` 已并入 `入口模块绑定最终设计方案.md`
|
||||
- `评查点分组正式迁移执行建议.md` 已并入 `评查点分组迁移执行前检查清单.md`
|
||||
- `文档类型与评查组关联方案.md` 已并入 `评查点分组目标结构与迁移方案.md`
|
||||
- `首页菜单最小可用收口说明.md`、`系统设置入口恢复说明.md` 的阶段性收口内容已并入 `docs/HANDOFF.md`
|
||||
- `前端联调404与资源缺口收口清单.md` 属于前端联调阶段记录,已从后端主文档目录移除
|
||||
|
||||
## 使用约束
|
||||
|
||||
- 这里的文档优先级高于 `new_doc_review/auth_doc/` 的历史快照资料
|
||||
- 如果某份文档描述和当前代码不一致,以当前代码与 `docs/HANDOFF.md` 为准,并尽快回补文档
|
||||
- 新接口如果已经用于当前主链路,必须同步补到本目录
|
||||
|
||||
@@ -0,0 +1,762 @@
|
||||
# 入口模块绑定最终设计方案
|
||||
|
||||
> 适用项目:`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 层**,并由后端统一输出首页入口列表,彻底结束前端自己拼入口、自己判地区、自己硬编码跳转的旧模式。
|
||||
+109
-5
@@ -1,6 +1,8 @@
|
||||
# 文档上传与列表 — 接口分析 & 接入方案
|
||||
|
||||
> 所有信息已逐文件验证。源代码位置均已标注。
|
||||
>
|
||||
> 说明:这份文档已经吸收旧的 `新系统版_documents_list接口.md` 与 `文档上传与评查接口.md`,后续以本文件作为文档上传、列表、详情、更新、删除、评查触发与状态查询的统一说明。
|
||||
|
||||
---
|
||||
|
||||
@@ -23,7 +25,7 @@ URL: POST /api/upload
|
||||
| `typeCode` | str | typeId/typeCode 二选一 | None | 文档类型编码,如 `contract.construction` |
|
||||
| `region` | str | 否 | `"default"` | 地区标识 |
|
||||
| `fileRole` | str | 否 | `"primary"` | 文件角色:primary/attachment/template |
|
||||
| `createdBy` | int | 否 | None | 上传用户 ID |
|
||||
| `createdBy` | int | 否 | None | 前端可传,但后端当前以 JWT 中的 `user_id` 为准 |
|
||||
| `autoRun` | bool | 否 | `false` | 是否上传后自动触发评查 |
|
||||
| `speed` | str | 否 | `"normal"` | urgent / normal |
|
||||
|
||||
@@ -124,17 +126,119 @@ URL: GET /api/documents/list
|
||||
|
||||
---
|
||||
|
||||
### 1.3 缺失的后端接口
|
||||
### 1.3 文档详情 / 更新 / 删除(现已实现)
|
||||
|
||||
#### GET /api/documents/{id}
|
||||
|
||||
- 用途:获取单个文档详情
|
||||
- 返回:`Result[DocumentDetailVO]`
|
||||
- 关键字段:
|
||||
- 继承列表接口的版本链、文件、run、统计字段
|
||||
- 新增 `documentNumber`、`remark`、`isTestDocument`、`auditStatus`、`pageCount`
|
||||
- 历史版本:`historyVersions` 返回同一 `version_group_key` 下除当前文档外的其它未删除版本
|
||||
|
||||
#### PUT /api/documents/{id}
|
||||
|
||||
- 用途:更新文档元数据
|
||||
- 请求体:`DocumentUpdateDTO`
|
||||
|
||||
```json
|
||||
{
|
||||
"documentNumber": "粤烟合同〔2026〕001号",
|
||||
"remark": "地市管理员修订备注",
|
||||
"isTestDocument": false,
|
||||
"auditStatus": 1
|
||||
}
|
||||
```
|
||||
|
||||
- 字段说明:
|
||||
- `documentNumber`:若 `leaudit_documents.document_number` 列存在则写入
|
||||
- `remark`:若 `leaudit_documents.remark` 列存在则写入
|
||||
- `isTestDocument`:若 `leaudit_documents.is_test_document` 列存在则写入
|
||||
- `auditStatus`:若 `leaudit_documents.audit_status` 列存在则写入;若当前环境未加该列,则接口忽略该字段但不报错
|
||||
|
||||
#### DELETE /api/documents/{id}
|
||||
|
||||
- 用途:软删除文档
|
||||
- 行为:
|
||||
- `leaudit_documents.deleted_at = now()`
|
||||
- 当前文档的 active file 置为 `is_active = false`
|
||||
- 若删除的是当前最新版本,则自动把同版本链中剩余的最高版本提升为 `is_latest_version = true`
|
||||
|
||||
#### 数据隔离规则(已在后端强制执行)
|
||||
|
||||
| 角色 | 范围 |
|
||||
|------|------|
|
||||
| `super_admin` / `provincial_admin` | 全量文档 |
|
||||
| `admin` | **仅本地市 `region = user.area`** |
|
||||
| `common` | **仅自己上传的文档 `created_by = current_user_id`** |
|
||||
|
||||
额外约束:
|
||||
- `admin` 若在列表接口传入其他地市 `region`,后端直接返回空结果
|
||||
- `common` 若试图访问、修改、删除他人文档,后端返回“文档不存在或无权访问”
|
||||
|
||||
#### 尚未完成
|
||||
|
||||
| 操作 | 当前 | 需要 |
|
||||
|------|------|------|
|
||||
| 删除文档 | ❌ | `DELETE /api/documents/{id}` — 软删除 |
|
||||
| 编辑元数据 | ❌ | `PUT /api/documents/{id}` — 更新备注、测试标记等 |
|
||||
| 文档详情 | ❌ | `GET /api/documents/{id}` — 单文档查询 |
|
||||
| 附件追加 | ❌ | `POST /api/documents/{id}/attachments` — fileRole=attachment |
|
||||
|
||||
---
|
||||
|
||||
### 1.4 自动评查 / 手动评查 / 状态结果查询
|
||||
|
||||
#### POST /api/upload
|
||||
|
||||
- 上传成功且 `autoRun=true` 时,后端会直接调用 `AuditService.Run()` 创建 run
|
||||
- 队列档位当前只保留:
|
||||
- `urgent`
|
||||
- `normal`
|
||||
|
||||
#### POST /api/audit/run
|
||||
|
||||
- 用途:对已存在文档手动触发评查
|
||||
- 典型参数:
|
||||
- `documentId`
|
||||
- `speed=normal|urgent`
|
||||
- 当前收口:
|
||||
- 若同一文档已有活动 run,后端优先复用,不重复创建
|
||||
|
||||
#### GET /api/audit/run/{runId}
|
||||
|
||||
- 用途:查询评查任务状态
|
||||
- 关键状态:
|
||||
- `queued`
|
||||
- `running`
|
||||
- `completed`
|
||||
- `failed`
|
||||
- 关键阶段:
|
||||
- `dispatch`
|
||||
- `prepare`
|
||||
- `ocr / extraction / evaluation / rescue / persist`
|
||||
|
||||
#### GET /api/audit/result/{runId}
|
||||
|
||||
- 用途:查询评查结果
|
||||
- 返回来源:
|
||||
- `leaudit_rule_results`
|
||||
- `leaudit_field_results`
|
||||
- `leaudit_run_metrics`
|
||||
- `leaudit_run_errors`
|
||||
|
||||
#### 当前评查链路口径
|
||||
|
||||
1. 上传文档
|
||||
2. 建立 `leaudit_documents / leaudit_document_files`
|
||||
3. 创建 `leaudit_audit_runs`
|
||||
4. 按 `speed` 投递 `leaudit.urgent` 或 `leaudit.normal`
|
||||
5. worker 按 `runId` 查库
|
||||
6. bridge 下载文档与规则 YAML 到本地临时文件
|
||||
7. `NativeRunner` 构建原生 `AuditCtx`
|
||||
8. `AuditService.audit(ctx)` 执行
|
||||
9. `StorageAdapter` 结果写回 `leaudit_*`
|
||||
|
||||
---
|
||||
|
||||
## 二、前端调用现状(已验证)
|
||||
|
||||
### 2.1 上传 — `uploadDocumentToServer()`
|
||||
|
||||
@@ -1,692 +0,0 @@
|
||||
# 文档上传与评查接口
|
||||
|
||||
这份文档描述当前已经落地的文档上传、文档列表、自动评查、手动评查、状态查询、结果查询接口。
|
||||
|
||||
当前接口围绕以下业务语义设计:
|
||||
|
||||
- 每次前端上传都会形成一个平台内部文档实例
|
||||
- 同名文档会尝试归入同一个版本组
|
||||
- 同名且内容相同:
|
||||
- 不新建版本
|
||||
- `duplicateUpload=true`
|
||||
- 如果 `autoRun=true`,仍然可以重新走一次评查流程
|
||||
- 同名但内容变化:
|
||||
- 新建版本
|
||||
- 形成 `v2 / v3 / ...`
|
||||
- 评查任务走 worker 异步执行
|
||||
- 队列只有两档:
|
||||
- `urgent`
|
||||
- `normal`
|
||||
|
||||
---
|
||||
|
||||
## 1. 上传接口
|
||||
|
||||
### 路径
|
||||
|
||||
```http
|
||||
POST /upload
|
||||
```
|
||||
|
||||
### Content-Type
|
||||
|
||||
```http
|
||||
multipart/form-data
|
||||
```
|
||||
|
||||
### 用途
|
||||
|
||||
- 上传文档
|
||||
- 创建或命中文档版本
|
||||
- 建立 `leaudit_documents / leaudit_document_files`
|
||||
- 可选自动触发评查
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---:|---|
|
||||
| `file` | file | 是 | 上传文件 |
|
||||
| `typeId` | int | 否 | 文档类型 ID,和 `typeCode` 二选一至少传一个 |
|
||||
| `typeCode` | string | 否 | 文档类型编码,例如 `contract.sale` |
|
||||
| `region` | string | 否 | 区域,默认 `default` |
|
||||
| `fileRole` | string | 否 | 文件角色,默认 `primary` |
|
||||
| `createdBy` | int | 否 | 上传用户 ID |
|
||||
| `autoRun` | bool | 否 | 是否上传后自动触发评查,默认 `false` |
|
||||
| `speed` | string | 否 | 执行速度档位:`normal` / `urgent`,默认 `normal` |
|
||||
|
||||
### 版本匹配逻辑
|
||||
|
||||
上传时会先做版本候选匹配:
|
||||
|
||||
1. 归一化文件名,得到 `normalized_name`
|
||||
2. 按以下条件查找最新版本候选:
|
||||
- `type_id` 相同
|
||||
- `region` 相同
|
||||
- `normalized_name` 相同
|
||||
- `is_latest_version = true`
|
||||
- 主文件 `file_role = 'primary'`
|
||||
3. 比较最新版本主文件的 `sha256`
|
||||
|
||||
结果分三种:
|
||||
|
||||
- 找不到候选
|
||||
- 新建版本组
|
||||
- 当前版本为 `v1`
|
||||
- 找到候选且 `sha256` 相同
|
||||
- 视为重复上传
|
||||
- 不新建版本
|
||||
- `duplicateUpload=true`
|
||||
- 找到候选但 `sha256` 不同
|
||||
- 新建版本
|
||||
- 当前版本为 `v2 / v3 / ...`
|
||||
- 旧版本 `is_latest_version=false`
|
||||
- 新版本 `is_latest_version=true`
|
||||
|
||||
### 队列路由逻辑
|
||||
|
||||
- `speed=urgent` -> 投递 `leaudit.urgent`
|
||||
- `speed=normal` -> 投递 `leaudit.normal`
|
||||
|
||||
### 请求示例:普通上传,不自动评查
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://127.0.0.1:8096/api/upload' \
|
||||
-F 'file=@/path/to/合同.docx' \
|
||||
-F 'typeCode=contract.sale' \
|
||||
-F 'region=default' \
|
||||
-F 'fileRole=primary' \
|
||||
-F 'autoRun=false' \
|
||||
-F 'speed=normal'
|
||||
```
|
||||
|
||||
### 返回示例:首次上传,命中 `v1`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"documentId": 11,
|
||||
"internalDocumentNo": 1777426812904262854,
|
||||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||||
"versionNo": 1,
|
||||
"previousVersionId": null,
|
||||
"rootVersionId": 11,
|
||||
"duplicateUpload": false,
|
||||
"fileId": 12,
|
||||
"typeId": 9,
|
||||
"typeCode": "contract.sale",
|
||||
"region": "default",
|
||||
"fileName": "版本归档验证合同.docx",
|
||||
"ossUrl": "bdocs/default/contract.sale/2026/04/11/v1/primary__版本归档验证合同.docx",
|
||||
"speed": "normal",
|
||||
"processingStatus": "waiting",
|
||||
"autoRunTriggered": false,
|
||||
"run": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 返回示例:重复上传,不升版
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"documentId": 11,
|
||||
"internalDocumentNo": 1777426812904262854,
|
||||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||||
"versionNo": 1,
|
||||
"previousVersionId": null,
|
||||
"rootVersionId": 11,
|
||||
"duplicateUpload": true,
|
||||
"fileId": 12,
|
||||
"typeId": 9,
|
||||
"typeCode": "contract.sale",
|
||||
"region": "default",
|
||||
"fileName": "版本归档验证合同.docx",
|
||||
"ossUrl": "bdocs/default/contract.sale/2026/04/11/v1/primary__版本归档验证合同.docx",
|
||||
"speed": "normal",
|
||||
"processingStatus": "waiting",
|
||||
"autoRunTriggered": false,
|
||||
"run": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 返回示例:同名但内容变化,自动形成 `v2`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"documentId": 12,
|
||||
"internalDocumentNo": 1777426813574315361,
|
||||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||||
"versionNo": 2,
|
||||
"previousVersionId": 11,
|
||||
"rootVersionId": 11,
|
||||
"duplicateUpload": false,
|
||||
"fileId": 13,
|
||||
"typeId": 9,
|
||||
"typeCode": "contract.sale",
|
||||
"region": "default",
|
||||
"fileName": "版本归档验证合同.docx",
|
||||
"ossUrl": "bdocs/default/contract.sale/2026/04/12/v2/primary__版本归档验证合同.docx",
|
||||
"speed": "normal",
|
||||
"processingStatus": "waiting",
|
||||
"autoRunTriggered": false,
|
||||
"run": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 返回示例:重复上传但自动重新评查
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"documentId": 13,
|
||||
"internalDocumentNo": 1777427235286905027,
|
||||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||||
"versionNo": 3,
|
||||
"previousVersionId": 12,
|
||||
"rootVersionId": 11,
|
||||
"duplicateUpload": true,
|
||||
"fileId": 14,
|
||||
"typeId": 9,
|
||||
"typeCode": "contract.sale",
|
||||
"region": "default",
|
||||
"fileName": "版本归档验证合同.docx",
|
||||
"ossUrl": "bdocs/default/contract.sale/2026/04/13/v3/primary__版本归档验证合同.docx",
|
||||
"speed": "normal",
|
||||
"processingStatus": "queued",
|
||||
"autoRunTriggered": true,
|
||||
"run": {
|
||||
"runId": 13,
|
||||
"documentId": 13,
|
||||
"runNo": 2,
|
||||
"documentFileId": 14,
|
||||
"status": "queued",
|
||||
"phase": "dispatch",
|
||||
"resultStatus": null,
|
||||
"ruleSetId": 29,
|
||||
"ruleVersionId": 9,
|
||||
"ruleTypeId": "contract.sale",
|
||||
"rescueApplied": false,
|
||||
"totalScore": null,
|
||||
"passedCount": null,
|
||||
"failedCount": null,
|
||||
"skippedCount": null,
|
||||
"startedAt": null,
|
||||
"finishedAt": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 文档列表接口
|
||||
|
||||
### 路径
|
||||
|
||||
```http
|
||||
GET /documents/list
|
||||
```
|
||||
|
||||
### 用途
|
||||
|
||||
- 返回文档主列表
|
||||
- 只返回每个版本组的最新版本
|
||||
- 每条记录附带历史版本摘要,前端可以直接做“展开历史版本”
|
||||
|
||||
### 查询参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---:|---|
|
||||
| `page` | int | 否 | 页码,从 `1` 开始,默认 `1` |
|
||||
| `pageSize` | int | 否 | 每页数量,默认 `20`,最大 `100` |
|
||||
| `keyword` | string | 否 | 文件名 / 归一化名称模糊搜索 |
|
||||
| `typeCode` | string | 否 | 文档类型编码,例如 `contract.sale` |
|
||||
| `region` | string | 否 | 区域过滤 |
|
||||
| `processingStatus` | string | 否 | 文档处理状态过滤 |
|
||||
| `resultStatus` | string | 否 | 最新 run 的结果状态过滤 |
|
||||
|
||||
### 查询逻辑
|
||||
|
||||
- 主查询只看 `leaudit_documents.is_latest_version = true`
|
||||
- 只关联主文件:
|
||||
- `leaudit_document_files.is_active = true`
|
||||
- `leaudit_document_files.file_role = 'primary'`
|
||||
- 当前评查状态来自 `leaudit_audit_runs`
|
||||
- 历史版本按 `version_group_key` 再查一次并挂到 `historyVersions`
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl 'http://127.0.0.1:8096/api/documents/list?page=1&pageSize=5'
|
||||
```
|
||||
|
||||
### 带筛选请求示例
|
||||
|
||||
```bash
|
||||
curl 'http://127.0.0.1:8096/api/documents/list?page=1&pageSize=2&keyword=版本归档&typeCode=contract.sale®ion=default'
|
||||
```
|
||||
|
||||
### 返回示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"total": 1,
|
||||
"page": 1,
|
||||
"pageSize": 2,
|
||||
"totalPages": 1,
|
||||
"documents": [
|
||||
{
|
||||
"documentId": 13,
|
||||
"internalDocumentNo": 1777427235286905027,
|
||||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||||
"versionNo": 3,
|
||||
"rootVersionId": 11,
|
||||
"previousVersionId": 12,
|
||||
"typeId": 9,
|
||||
"typeCode": "contract.sale",
|
||||
"region": "default",
|
||||
"normalizedName": "版本归档验证合同",
|
||||
"fileId": 14,
|
||||
"fileName": "版本归档验证合同.docx",
|
||||
"fileExt": "docx",
|
||||
"mimeType": "application/octet-stream",
|
||||
"fileSize": 587279,
|
||||
"ossUrl": "bdocs/default/contract.sale/2026/04/13/v3/primary__版本归档验证合同.docx",
|
||||
"processingStatus": "completed",
|
||||
"currentRunId": 13,
|
||||
"runStatus": "completed",
|
||||
"resultStatus": "review",
|
||||
"totalScore": 92.0,
|
||||
"passedCount": 25,
|
||||
"failedCount": 3,
|
||||
"skippedCount": 0,
|
||||
"updatedAt": "2026-04-29T01:50:05.241397+00:00",
|
||||
"hasHistory": true,
|
||||
"totalVersions": 3,
|
||||
"historyVersions": [
|
||||
{
|
||||
"documentId": 12,
|
||||
"fileId": 13,
|
||||
"versionNo": 2,
|
||||
"fileName": "版本归档验证合同.docx",
|
||||
"fileExt": "docx",
|
||||
"processingStatus": "waiting",
|
||||
"runStatus": null,
|
||||
"resultStatus": null,
|
||||
"updatedAt": "2026-04-29T01:47:15.250697+00:00"
|
||||
},
|
||||
{
|
||||
"documentId": 11,
|
||||
"fileId": 12,
|
||||
"versionNo": 1,
|
||||
"fileName": "版本归档验证合同.docx",
|
||||
"fileExt": "docx",
|
||||
"processingStatus": "waiting",
|
||||
"runStatus": null,
|
||||
"resultStatus": null,
|
||||
"updatedAt": "2026-04-29T01:40:13.538839+00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 返回字段说明
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `documents[]` | 主列表,仅最新版本 |
|
||||
| `versionGroupKey` | 同一版本链的归档组键 |
|
||||
| `versionNo` | 当前版本号 |
|
||||
| `rootVersionId` | 版本链根文档 ID |
|
||||
| `previousVersionId` | 上一版本文档 ID |
|
||||
| `hasHistory` | 是否存在历史版本 |
|
||||
| `totalVersions` | 该版本组的总版本数 |
|
||||
| `historyVersions[]` | 历史版本摘要,按 `versionNo DESC` 排序 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 手动触发评查
|
||||
|
||||
### 路径
|
||||
|
||||
```http
|
||||
POST /audit/run
|
||||
```
|
||||
|
||||
### 用途
|
||||
|
||||
- 对指定 `documentId` 手动触发一次新的评查 run
|
||||
- 不改变文档版本
|
||||
- 只新增 `leaudit_audit_runs`
|
||||
|
||||
### 请求体
|
||||
|
||||
```json
|
||||
{
|
||||
"documentId": 13,
|
||||
"ruleType": null,
|
||||
"force": false,
|
||||
"speed": "normal"
|
||||
}
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---:|---|
|
||||
| `documentId` | int | 是 | 文档 ID |
|
||||
| `ruleType` | string/null | 否 | 指定规则类型编码 |
|
||||
| `force` | bool | 否 | 是否强制重跑 |
|
||||
| `speed` | string | 否 | `normal` / `urgent` |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://127.0.0.1:8096/api/audit/run' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"documentId": 13,
|
||||
"force": false,
|
||||
"speed": "urgent"
|
||||
}'
|
||||
```
|
||||
|
||||
### 返回示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"runId": 15,
|
||||
"documentId": 13,
|
||||
"runNo": 3,
|
||||
"documentFileId": 14,
|
||||
"status": "queued",
|
||||
"phase": "dispatch",
|
||||
"resultStatus": null,
|
||||
"ruleSetId": 29,
|
||||
"ruleVersionId": 9,
|
||||
"ruleTypeId": "contract.sale",
|
||||
"rescueApplied": false,
|
||||
"totalScore": null,
|
||||
"passedCount": null,
|
||||
"failedCount": null,
|
||||
"skippedCount": null,
|
||||
"startedAt": null,
|
||||
"finishedAt": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 查询运行状态
|
||||
|
||||
### 路径
|
||||
|
||||
```http
|
||||
GET /audit/run/{runId}
|
||||
```
|
||||
|
||||
### 用途
|
||||
|
||||
- 查询 run 当前状态
|
||||
- 适合前端轮询
|
||||
|
||||
### 状态说明
|
||||
|
||||
常见状态:
|
||||
|
||||
- `queued`
|
||||
- `running`
|
||||
- `completed`
|
||||
- `failed`
|
||||
|
||||
常见阶段:
|
||||
|
||||
- `dispatch`
|
||||
- `prepare`
|
||||
- `ocr`
|
||||
- `extract`
|
||||
- `evaluate`
|
||||
- `rescue`
|
||||
- `persist`
|
||||
- `executed`
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl 'http://127.0.0.1:8096/api/audit/run/11'
|
||||
```
|
||||
|
||||
### 返回示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"runId": 11,
|
||||
"documentId": 10,
|
||||
"runNo": 1,
|
||||
"documentFileId": 11,
|
||||
"status": "completed",
|
||||
"phase": "executed",
|
||||
"resultStatus": "review",
|
||||
"ruleSetId": 29,
|
||||
"ruleVersionId": 9,
|
||||
"ruleTypeId": "contract.sale",
|
||||
"rescueApplied": true,
|
||||
"totalScore": 89.0,
|
||||
"passedCount": 24,
|
||||
"failedCount": 4,
|
||||
"skippedCount": 0,
|
||||
"startedAt": "2026-04-28T19:01:01.766352+08:00",
|
||||
"finishedAt": "2026-04-28T19:03:11.044894+08:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 查询评查结果
|
||||
|
||||
### 路径
|
||||
|
||||
```http
|
||||
GET /audit/result/{runId}
|
||||
```
|
||||
|
||||
### 用途
|
||||
|
||||
- 查询本次 run 的完整结果
|
||||
- 包括:
|
||||
- 规则结果
|
||||
- 抽取字段
|
||||
- 运行错误
|
||||
- rescue 结果
|
||||
- metrics
|
||||
- artifacts
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl 'http://127.0.0.1:8096/api/audit/result/11'
|
||||
```
|
||||
|
||||
### 返回结构说明
|
||||
|
||||
顶层字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `runId` | 运行 ID |
|
||||
| `documentId` | 文档 ID |
|
||||
| `documentFileId` | 本次锁定的文件 ID |
|
||||
| `status` | 运行状态 |
|
||||
| `totalScore` | 总分 |
|
||||
| `passedCount` | 通过数 |
|
||||
| `failedCount` | 失败数 |
|
||||
| `skippedCount` | 跳过数 |
|
||||
| `phase` | 当前阶段 |
|
||||
| `resultStatus` | 总体结果 |
|
||||
| `rescueApplied` | 是否执行 rescue |
|
||||
| `ruleSetId` | 规则集 ID |
|
||||
| `ruleVersionId` | 规则版本 ID |
|
||||
| `startedAt` / `finishedAt` | 起止时间 |
|
||||
| `rules` | 规则结果列表 |
|
||||
| `fields` | 抽取字段列表 |
|
||||
| `errors` | 错误列表 |
|
||||
| `rescueOutcomes` | 补救结果列表 |
|
||||
| `metrics` | 阶段指标 |
|
||||
| `artifacts` | 产物列表 |
|
||||
|
||||
### 返回示例(节选)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"runId": 11,
|
||||
"documentId": 10,
|
||||
"documentFileId": 11,
|
||||
"status": "completed",
|
||||
"totalScore": 89.0,
|
||||
"passedCount": 24,
|
||||
"failedCount": 4,
|
||||
"skippedCount": 0,
|
||||
"phase": "executed",
|
||||
"resultStatus": "review",
|
||||
"rescueApplied": true,
|
||||
"ruleSetId": 29,
|
||||
"ruleVersionId": 9,
|
||||
"startedAt": "2026-04-28T19:01:01.766352+08:00",
|
||||
"finishedAt": "2026-04-28T19:03:11.044894+08:00",
|
||||
"rules": [
|
||||
{
|
||||
"ruleId": "MM-SALE-012",
|
||||
"ruleName": "甲方信用代码校验",
|
||||
"passed": false,
|
||||
"status": "executed",
|
||||
"risk": "medium",
|
||||
"score": 3.0,
|
||||
"failMessage": "甲方统一社会信用代码校验位错误"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "合同名称",
|
||||
"valueText": "智慧法务平台建设采购项目合同",
|
||||
"confidence": 0.9991
|
||||
}
|
||||
],
|
||||
"rescueOutcomes": [
|
||||
{
|
||||
"ruleId": "MM-SALE-012",
|
||||
"status": "final_fail",
|
||||
"finalStatus": "review",
|
||||
"requiresHumanReview": true,
|
||||
"failureReason": "Agent (4 iter, requires_human): token_budget_exhausted"
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"ocrSeconds": 79.06,
|
||||
"extractSeconds": 11.87,
|
||||
"evaluateSeconds": 9.9,
|
||||
"totalSeconds": 100.83,
|
||||
"pageCount": 2,
|
||||
"fieldCount": 35,
|
||||
"ruleCount": 28,
|
||||
"rescueRuleCount": 5,
|
||||
"artifactCount": 8
|
||||
},
|
||||
"artifacts": [
|
||||
{
|
||||
"artifactType": "ocr_json",
|
||||
"fileName": "ocr_result.json",
|
||||
"fileExt": "json",
|
||||
"mimeType": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. worker 日志怎么看
|
||||
|
||||
worker 关键日志已经做了可读化,重点看这两类:
|
||||
|
||||
### 投递日志
|
||||
|
||||
```text
|
||||
run_id=13 已投递到 worker 队列: queue=leaudit.normal, speed=normal, task_id=...
|
||||
```
|
||||
|
||||
### 执行日志
|
||||
|
||||
```text
|
||||
run_id=13 worker开始执行: queue=leaudit.normal, speed=normal, filename=版本归档验证合同.docx
|
||||
```
|
||||
|
||||
结合状态查询接口可以快速判断:
|
||||
|
||||
- 是否已经成功投递
|
||||
- 是否已被 worker 消费
|
||||
- 跑的是 `urgent` 还是 `normal`
|
||||
|
||||
---
|
||||
|
||||
## 7. 前端建议接法
|
||||
|
||||
### 文档上传页
|
||||
|
||||
1. 调 `POST /upload`
|
||||
2. 读取返回:
|
||||
- `documentId`
|
||||
- `versionGroupKey`
|
||||
- `versionNo`
|
||||
- `duplicateUpload`
|
||||
- `run`
|
||||
|
||||
### 自动评查场景
|
||||
|
||||
如果 `autoRun=true` 且返回里 `run != null`:
|
||||
|
||||
1. 取 `run.runId`
|
||||
2. 轮询 `GET /audit/run/{runId}`
|
||||
3. `status=completed/failed` 后停止轮询
|
||||
4. 再调 `GET /audit/result/{runId}`
|
||||
|
||||
### 列表页
|
||||
|
||||
列表页建议默认只展示:
|
||||
|
||||
- `is_latest_version = true` 的 document
|
||||
|
||||
点击某条后,再按:
|
||||
|
||||
- `versionGroupKey`
|
||||
|
||||
展开其历史版本。
|
||||
@@ -1,127 +0,0 @@
|
||||
# 文档类型与评查组关联 — 老项目分析 & 新方案
|
||||
|
||||
## 一、老项目模型(docauditai)
|
||||
|
||||
### 三层间接绑定
|
||||
|
||||
```
|
||||
DocumentType → TopLevelGroup(s) → ChildGroups → EvaluationPoints
|
||||
│ │ │ │
|
||||
│ evaluation_ │ pid=0 │ pid={top} │ evaluation_
|
||||
│ point_groups │ │ │ point_groups_id
|
||||
│ _ids (JSONB) │ │ │
|
||||
```
|
||||
|
||||
- `document_types.evaluation_point_groups_ids` — JSONB 数组,存顶层组 ID,如 `[3]` 或 `[1,5]`
|
||||
- `evaluation_point_groups` — 两级树:`pid=0` 是顶层组,"pid>0" 是子组
|
||||
- `evaluation_points` — 属于子组,含 `extraction_config`(提取规则)和 `evaluation_config`(评查规则),按 `area` + `document_attribute_type` 过滤
|
||||
|
||||
### 运行时解析流程
|
||||
|
||||
```
|
||||
doc_type_code
|
||||
→ 查 document_types.evaluation_point_groups_ids[0]
|
||||
→ 查 evaluation_point_groups WHERE pid = {top_group_id}
|
||||
→ 查 evaluation_points WHERE evaluation_point_groups_id = {child_id}
|
||||
→ 按 area + attribute_type 过滤
|
||||
→ 返回最终评查点列表
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、新平台模型(leaudit-platform)
|
||||
|
||||
### 直接绑定
|
||||
|
||||
```
|
||||
DocumentType ──→ RuleTypeBinding ──→ RuleSet ──→ RuleVersions
|
||||
```
|
||||
|
||||
**`leaudit_rule_type_bindings`**:
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `doc_type_id` | FK → leaudit_document_types.id |
|
||||
| `rule_set_id` | FK → leaudit_rule_sets.id |
|
||||
| `binding_mode` | "explicit"(一对一)/ "inherit"(继承) |
|
||||
| `region` | 地区隔离 |
|
||||
| `priority` | 同一 doc_type 多个 rule_set 时的优先级 |
|
||||
|
||||
**运行时**(已在 auditServiceImpl.py 实现):
|
||||
```sql
|
||||
SELECT b.rule_set_id, rs.current_version_id
|
||||
FROM leaudit_rule_type_bindings b
|
||||
JOIN leaudit_rule_sets rs ON rs.id = b.rule_set_id
|
||||
WHERE b.doc_type_id = :doc_type_id
|
||||
AND b.region = :region
|
||||
AND b.is_active = true
|
||||
ORDER BY b.priority DESC
|
||||
```
|
||||
|
||||
### 与老模型的关键差异
|
||||
|
||||
| | 老 | 新 |
|
||||
|---|---|---|
|
||||
| 绑定粒度 | 文档类型 → 顶层组(间接) | 文档类型 → 规则集(直接) |
|
||||
| 评查点层级 | 三级(类型→组→子组→点) | 两级(类型→规则集→版本) |
|
||||
| 地区隔离 | evaluation_points.area | rule_type_bindings.region |
|
||||
| 规则版本 | 无版本概念 | 规则集 + 版本管理 |
|
||||
|
||||
实际上新模型更简洁:老系统里的"顶层组"≈新系统的"规则集",老系统里的"子组+评查点"≈新系统的"规则版本内容"。新系统去掉了中间的子组层级。
|
||||
|
||||
---
|
||||
|
||||
## 三、已落地 vs 待落地
|
||||
|
||||
### ✅ 已落地
|
||||
|
||||
| 项 | 说明 |
|
||||
|----|------|
|
||||
| `leaudit_document_types` 表 | 有 20 条种子数据,含 code/name/entry_module_id/prompt_config |
|
||||
| `leaudit_rule_sets` 表 | 规则集 + 版本管理 |
|
||||
| `leaudit_rule_type_bindings` 表 | 20 条绑定记录,已全部配对 |
|
||||
| 运行时绑定解析 | auditServiceImpl.Run() 中查 bindings → 找 rule_set + version |
|
||||
| `GET /api/document-types` | 刚加的,返回 id/name/code |
|
||||
|
||||
### ❌ 待落地
|
||||
|
||||
| 优先 | 项 | 说明 |
|
||||
|------|----|------|
|
||||
| 高 | 文档类型 CRUD 后端 | POST/PUT/DELETE /api/document-types |
|
||||
| 高 | 规则绑定 CRUD 后端 | 在文档类型编辑中配 rule_set_id(s) |
|
||||
| 高 | 前端文档类型管理页 | 对应旧 `document-types._index.tsx` |
|
||||
| 中 | 规则绑定前端 UI | 文档类型编辑页中选规则集的控件 |
|
||||
| 中 | 前端规则集选择器 | 对接 `/api/rule-sets` 或现有规则接口 |
|
||||
| 低 | prompt_config 编辑 | 每个文档类型的提示词模板配置 |
|
||||
|
||||
---
|
||||
|
||||
## 四、推荐接入方案
|
||||
|
||||
### 第 1 步:文档类型 CRUD 后端
|
||||
|
||||
```
|
||||
GET /api/document-types ← 已实现
|
||||
POST /api/document-types ← 新增
|
||||
PUT /api/document-types/{id} ← 新增
|
||||
DELETE /api/document-types/{id} ← 新增
|
||||
```
|
||||
|
||||
字段:`code`, `name`, `description`, `entry_module_id`, `prompt_config`, `rule_set_ids`(一次性处理绑定)
|
||||
|
||||
### 第 2 步:规则绑定 CRUD
|
||||
|
||||
两种方案:
|
||||
- **方案 A**(推荐):绑定内嵌到文档类型接口。创建/更新文档类型时传 `rule_set_ids: [21, 31]`,后端自动维护 `leaudit_rule_type_bindings`。
|
||||
- **方案 B**:独立绑定接口 `POST/DELETE /api/rule-type-bindings`。
|
||||
|
||||
推荐方案 A,因为绑定是文档类型的属性,一起管理更自然。
|
||||
|
||||
### 第 3 步:前端文档类型管理页
|
||||
|
||||
参考旧前端 `document-types._index.tsx` / `document-types.new.tsx`,用新 API 重写:
|
||||
- 列表页:表格展示 code/name/entry_module/规则集数量
|
||||
- 新建/编辑页:表单填 code/name/entry_module,多选规则集
|
||||
|
||||
### 第 4 步:上传页关联
|
||||
|
||||
上传时将 `typeCode` 写入 `leaudit_documents.type_id` → 运行时自动找绑定 → 加载规则集 → 执行评查。整个链路已通,只需确保文档类型数据存在。
|
||||
@@ -1,307 +0,0 @@
|
||||
# 新系统版 `documents/list` 接口
|
||||
|
||||
这份文档专门说明当前 `leaudit-platform` 里已经落地的“新系统版文档列表接口”。
|
||||
|
||||
目标很明确:
|
||||
|
||||
- 前端文档列表只看“最新版本”
|
||||
- 同名文档的历史版本直接归到同一个版本链
|
||||
- 列表接口直接返回历史版本摘要,前端不用自己再拼版本关系
|
||||
|
||||
---
|
||||
|
||||
## 1. 接口路径
|
||||
|
||||
```http
|
||||
GET /api/documents/list
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前接口语义
|
||||
|
||||
这个接口不是“把所有上传记录平铺出来”。
|
||||
|
||||
它的语义是:
|
||||
|
||||
- 每个 `version_group_key` 只返回一条“最新版本文档”
|
||||
- 这条最新版本文档下面附带 `historyVersions`
|
||||
- `historyVersions` 里放的是同组下更老的版本摘要
|
||||
|
||||
也就是说,前端主列表看到的是:
|
||||
|
||||
- 当前版本
|
||||
- 是否有历史版本
|
||||
- 一共有多少版本
|
||||
- 历史版本有哪些
|
||||
|
||||
---
|
||||
|
||||
## 3. 请求参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---:|---|
|
||||
| `page` | int | 否 | 页码,从 `1` 开始,默认 `1` |
|
||||
| `pageSize` | int | 否 | 每页数量,默认 `20`,最大 `100` |
|
||||
| `keyword` | string | 否 | 按文件名或归一化名称模糊搜索 |
|
||||
| `typeCode` | string | 否 | 文档类型编码,例如 `contract.sale` |
|
||||
| `region` | string | 否 | 区域 |
|
||||
| `processingStatus` | string | 否 | 文档处理状态 |
|
||||
| `resultStatus` | string | 否 | 最新 run 的结果状态 |
|
||||
|
||||
请求示例:
|
||||
|
||||
```bash
|
||||
curl 'http://127.0.0.1:8096/api/documents/list?page=1&pageSize=5'
|
||||
```
|
||||
|
||||
带筛选示例:
|
||||
|
||||
```bash
|
||||
curl 'http://127.0.0.1:8096/api/documents/list?page=1&pageSize=2&keyword=版本归档&typeCode=contract.sale®ion=default'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 返回结构
|
||||
|
||||
返回模型是分页结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"total": 1,
|
||||
"page": 1,
|
||||
"pageSize": 20,
|
||||
"totalPages": 1,
|
||||
"documents": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其中 `documents[]` 的单条结构核心字段如下:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `documentId` | 当前最新版本文档 ID |
|
||||
| `internalDocumentNo` | 平台内部追踪号 |
|
||||
| `versionGroupKey` | 版本归档组键 |
|
||||
| `versionNo` | 当前版本号 |
|
||||
| `rootVersionId` | 版本链根文档 ID |
|
||||
| `previousVersionId` | 上一版本文档 ID |
|
||||
| `typeId` | 文档类型 ID |
|
||||
| `typeCode` | 文档类型编码 |
|
||||
| `region` | 区域 |
|
||||
| `normalizedName` | 归一化后的名称 |
|
||||
| `fileId` | 当前主文件 ID |
|
||||
| `fileName` | 文件名 |
|
||||
| `fileExt` | 文件扩展名 |
|
||||
| `mimeType` | MIME 类型 |
|
||||
| `fileSize` | 文件大小 |
|
||||
| `ossUrl` | 对象存储路径 |
|
||||
| `processingStatus` | 文档处理状态 |
|
||||
| `currentRunId` | 当前 run ID |
|
||||
| `runStatus` | 当前 run 状态 |
|
||||
| `resultStatus` | 当前 run 结果状态 |
|
||||
| `totalScore` | 总分 |
|
||||
| `passedCount` | 通过数 |
|
||||
| `failedCount` | 失败数 |
|
||||
| `skippedCount` | 跳过数 |
|
||||
| `updatedAt` | 更新时间 |
|
||||
| `hasHistory` | 是否有历史版本 |
|
||||
| `totalVersions` | 总版本数 |
|
||||
| `historyVersions` | 历史版本摘要列表 |
|
||||
|
||||
`historyVersions[]` 结构:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `documentId` | 历史版本文档 ID |
|
||||
| `fileId` | 历史版本文件 ID |
|
||||
| `versionNo` | 历史版本号 |
|
||||
| `fileName` | 文件名 |
|
||||
| `fileExt` | 文件扩展名 |
|
||||
| `processingStatus` | 处理状态 |
|
||||
| `runStatus` | 运行状态 |
|
||||
| `resultStatus` | 结果状态 |
|
||||
| `updatedAt` | 更新时间 |
|
||||
|
||||
---
|
||||
|
||||
## 5. SQL 逻辑
|
||||
|
||||
### 5.1 主列表计数 SQL
|
||||
|
||||
```sql
|
||||
SELECT COUNT(*)
|
||||
FROM leaudit_documents d
|
||||
JOIN leaudit_document_files f
|
||||
ON f.document_id = d.id
|
||||
LEFT JOIN leaudit_document_types dt
|
||||
ON dt.id = d.type_id
|
||||
LEFT JOIN leaudit_audit_runs ar
|
||||
ON ar.id = d.current_run_id
|
||||
WHERE d.is_latest_version = true
|
||||
AND d.deleted_at IS NULL
|
||||
AND f.is_active = true
|
||||
AND f.file_role = 'primary'
|
||||
-- 可选过滤:
|
||||
-- AND (f.file_name ILIKE :keyword OR d.normalized_name ILIKE :keyword)
|
||||
-- AND dt.code = :type_code
|
||||
-- AND d.region = :region
|
||||
-- AND d.processing_status = :processing_status
|
||||
-- AND ar.result_status = :result_status
|
||||
```
|
||||
|
||||
### 5.2 主列表分页 SQL
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
d.id AS document_id,
|
||||
d.biz_document_id AS internal_document_no,
|
||||
d.version_group_key,
|
||||
d.version_no,
|
||||
d.root_version_id,
|
||||
d.previous_version_id,
|
||||
d.type_id,
|
||||
dt.code AS type_code,
|
||||
d.region,
|
||||
d.normalized_name,
|
||||
d.processing_status,
|
||||
d.current_run_id,
|
||||
d.updated_at,
|
||||
f.id AS file_id,
|
||||
f.file_name,
|
||||
f.file_ext,
|
||||
f.mime_type,
|
||||
f.file_size,
|
||||
f.oss_url,
|
||||
ar.status AS run_status,
|
||||
ar.result_status,
|
||||
ar.total_score,
|
||||
ar.passed_count,
|
||||
ar.failed_count,
|
||||
ar.skipped_count,
|
||||
vc.total_versions,
|
||||
COALESCE(vc.total_versions, 1) > 1 AS has_history
|
||||
FROM leaudit_documents d
|
||||
JOIN leaudit_document_files f
|
||||
ON f.document_id = d.id
|
||||
LEFT JOIN leaudit_document_types dt
|
||||
ON dt.id = d.type_id
|
||||
LEFT JOIN leaudit_audit_runs ar
|
||||
ON ar.id = d.current_run_id
|
||||
LEFT JOIN (
|
||||
SELECT version_group_key, COUNT(*) AS total_versions
|
||||
FROM leaudit_documents
|
||||
WHERE deleted_at IS NULL
|
||||
GROUP BY version_group_key
|
||||
) vc
|
||||
ON vc.version_group_key = d.version_group_key
|
||||
WHERE d.is_latest_version = true
|
||||
AND d.deleted_at IS NULL
|
||||
AND f.is_active = true
|
||||
AND f.file_role = 'primary'
|
||||
ORDER BY d.updated_at DESC, d.id DESC
|
||||
LIMIT :limit OFFSET :offset
|
||||
```
|
||||
|
||||
### 5.3 历史版本摘要 SQL
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
d.version_group_key,
|
||||
d.id AS document_id,
|
||||
d.version_no,
|
||||
d.processing_status,
|
||||
d.updated_at,
|
||||
f.id AS file_id,
|
||||
f.file_name,
|
||||
f.file_ext,
|
||||
ar.status AS run_status,
|
||||
ar.result_status
|
||||
FROM leaudit_documents d
|
||||
JOIN leaudit_document_files f
|
||||
ON f.document_id = d.id
|
||||
AND f.is_active = true
|
||||
AND f.file_role = 'primary'
|
||||
LEFT JOIN leaudit_audit_runs ar
|
||||
ON ar.id = d.current_run_id
|
||||
WHERE d.version_group_key = ANY(:group_keys)
|
||||
AND d.is_latest_version = false
|
||||
AND d.deleted_at IS NULL
|
||||
ORDER BY d.version_group_key, d.version_no DESC, d.id DESC
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 为什么新系统要这么做
|
||||
|
||||
因为现在我们已经不是老系统那种“文档记录 + 旁路版本信息”的模式了。
|
||||
|
||||
当前新系统已经有真正的版本链字段:
|
||||
|
||||
- `version_group_key`
|
||||
- `version_no`
|
||||
- `previous_version_id`
|
||||
- `root_version_id`
|
||||
- `is_latest_version`
|
||||
- `normalized_name`
|
||||
|
||||
所以列表天然应该是:
|
||||
|
||||
- 主列表 = 最新版本
|
||||
- 展开项 = 历史版本
|
||||
|
||||
而不是把所有版本平铺在一个列表里。
|
||||
|
||||
---
|
||||
|
||||
## 7. 当前代码落点
|
||||
|
||||
实现代码在这些文件:
|
||||
|
||||
- 路由:`fastapi_modules/fastapi_leaudit/controllers/documentController.py`
|
||||
- 服务接口:`fastapi_modules/fastapi_leaudit/services/documentService.py`
|
||||
- VO:`fastapi_modules/fastapi_leaudit/domian/vo/documentVo.py`
|
||||
- 具体实现:`fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py`
|
||||
|
||||
---
|
||||
|
||||
## 8. 当前验证结果
|
||||
|
||||
已经实际验证通过:
|
||||
|
||||
```bash
|
||||
curl 'http://127.0.0.1:8096/api/documents/list?page=1&pageSize=5'
|
||||
```
|
||||
|
||||
以及:
|
||||
|
||||
```bash
|
||||
curl 'http://127.0.0.1:8096/api/documents/list?page=1&pageSize=2&keyword=版本归档&typeCode=contract.sale®ion=default'
|
||||
```
|
||||
|
||||
验证结论:
|
||||
|
||||
- 接口正常返回
|
||||
- 主列表只返回最新版本
|
||||
- `historyVersions` 能正确带出历史版本摘要
|
||||
- 已验证版本组 `4e02e455aa504cb9b75a254727f1bb4c`
|
||||
- `documentId=13` 是当前最新 `v3`
|
||||
- `documentId=12` 是 `v2`
|
||||
- `documentId=11` 是 `v1`
|
||||
|
||||
---
|
||||
|
||||
## 9. 下一步建议
|
||||
|
||||
如果后面前端需要更完整的“版本展开页”,再补一个:
|
||||
|
||||
```http
|
||||
GET /api/documents/{documentId}/versions
|
||||
```
|
||||
|
||||
但当前列表页场景下,`/documents/list` 已经够用了。
|
||||
@@ -412,7 +412,7 @@ SQL 里初始化了以下核心菜单:
|
||||
如果你后面忘了这套体系总原则,看这里:
|
||||
|
||||
- 总设计:`docs/用户与地区权限完整设计方案.md`
|
||||
- 老系统分析:`docs/老系统_docauditai_用户权限架构深度分析.md`
|
||||
- 导航说明:`docs/权限与地区隔离文档导航.md`
|
||||
- 本文:`docs/接口/用户权限与权限点清单.md`
|
||||
- SQL:`docs/接口/用户权限初始化SQL.sql`
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ WITH upsert_routes AS (
|
||||
('/documents', '文档管理', 'Layout', NULL, 10, TRUE, TRUE, '{"icon":"files"}'::jsonb, NOW(), NOW()),
|
||||
('/documents/list', '文档列表', 'documents/list', NULL, 11, TRUE, TRUE, '{"icon":"table"}'::jsonb, NOW(), NOW()),
|
||||
('/rules', '规则管理', 'Layout', NULL, 20, TRUE, TRUE, '{"icon":"rule"}'::jsonb, NOW(), NOW()),
|
||||
('/rules/sets', '规则集管理', 'rules/sets', NULL, 21, TRUE, TRUE, '{"icon":"yaml"}'::jsonb, NOW(), NOW()),
|
||||
('/rules/sets', '规则管理', 'rules/sets', NULL, 21, TRUE, TRUE, '{"icon":"yaml"}'::jsonb, NOW(), NOW()),
|
||||
('/audit', '评查任务', 'Layout', NULL, 30, TRUE, TRUE, '{"icon":"audit"}'::jsonb, NOW(), NOW()),
|
||||
('/audit/runs', '评查运行', 'audit/runs', NULL, 31, TRUE, TRUE, '{"icon":"history"}'::jsonb, NOW(), NOW()),
|
||||
('/system', '系统管理', 'Layout', NULL, 90, TRUE, TRUE, '{"icon":"setting"}'::jsonb, NOW(), NOW()),
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
# 系统设置入口恢复说明
|
||||
|
||||
## 本次恢复范围
|
||||
|
||||
本轮不是把整个旧“系统设置域”一次性全部接回,而是先恢复最关键的两块:
|
||||
|
||||
- `入口模块管理`:`/entry-modules`
|
||||
- `角色权限管理`:`/role-permissions`
|
||||
|
||||
同时恢复首页右上角 `系统设置` 入口,但当前只会进入上述两个已经承接的新能力。
|
||||
|
||||
## 当前策略
|
||||
|
||||
### 1. 首页与菜单
|
||||
|
||||
- 后端 `/api/rbac/user/routes` 已重新返回 `/settings`
|
||||
- `/settings` 当前子路由只保留:
|
||||
- `/entry-modules`
|
||||
- `/role-permissions`
|
||||
- `config-lists / document-types / prompts` 仍未重新放开
|
||||
|
||||
### 2. 入口模块管理
|
||||
|
||||
已承接后端接口:
|
||||
|
||||
- `GET /api/v3/entry-modules`
|
||||
- `GET /api/v3/entry-modules/{id}`
|
||||
- `POST /api/v3/entry-modules`
|
||||
- `PUT /api/v3/entry-modules/{id}`
|
||||
- `DELETE /api/v3/entry-modules/{id}`
|
||||
- `POST /api/v3/entry-modules/{id}/image`
|
||||
|
||||
数据源:
|
||||
|
||||
- 表:`leaudit_entry_modules`
|
||||
- 图标:OSS `leaudit` 桶
|
||||
|
||||
说明:
|
||||
|
||||
- 前端展示字段 `path` 现在对应数据库 `icon_path`
|
||||
- 前端编辑页面中的跳转路径仍走数据库 `path`
|
||||
|
||||
### 3. 角色权限管理
|
||||
|
||||
已承接后端接口:
|
||||
|
||||
- `GET /api/v3/rbac/roles`
|
||||
- `POST /api/v3/rbac/roles`
|
||||
- `PUT /api/v3/rbac/roles/{roleId}`
|
||||
- `DELETE /api/v3/rbac/roles/{roleId}`
|
||||
- `GET /api/v3/rbac/users`
|
||||
- `POST /api/v3/rbac/users/{userId}/roles`
|
||||
- `DELETE /api/v3/rbac/users/{userId}/roles/{roleId}`
|
||||
- `GET /api/v3/rbac/users/{userId}/roles`
|
||||
- `GET /api/rbac/roles/{roleId}/routes`
|
||||
- `PUT /api/rbac/roles/{roleId}/routes`
|
||||
- `GET /api/v3/rbac/role-permissions`
|
||||
- `POST /api/v3/rbac/role-permissions`
|
||||
- `GET /api/v3/routes`
|
||||
- `GET /api/v3/routes/{routeId}/permissions`
|
||||
|
||||
数据源:
|
||||
|
||||
- `roles`
|
||||
- `sso_users`
|
||||
- `user_role`
|
||||
- `sys_routes`
|
||||
- `role_route`
|
||||
- `permissions`
|
||||
- `role_permissions`
|
||||
|
||||
## 自动补种策略
|
||||
|
||||
为避免当前数据库里的 `sys_routes / permissions` 与现前端路由结构不一致,这次后端在 RBAC 管理接口里增加了“按需补种”:
|
||||
|
||||
- 自动补齐最小可用路由:
|
||||
- `/home`
|
||||
- `/chat-with-llm`
|
||||
- `/files`
|
||||
- `/files/upload`
|
||||
- `/documents`
|
||||
- `/settings`
|
||||
- `/entry-modules`
|
||||
- `/role-permissions`
|
||||
- 自动补齐入口模块管理、角色权限管理所需的权限定义
|
||||
|
||||
这样做的目的,是先把“入口模块 + 权限架构”真正跑通,不再依赖旧系统那套不匹配的 `sys_routes` 种子。
|
||||
|
||||
## 首页入口联动提醒
|
||||
|
||||
入口模块管理恢复后,首页入口是否显示,不只取决于 `leaudit_entry_modules` 本身,还取决于:
|
||||
|
||||
- 入口模块 `path`
|
||||
- `sys_routes`
|
||||
- `role_route`
|
||||
|
||||
例如这次的真实案例:
|
||||
|
||||
- `内部公文` 入口模块配置存在
|
||||
- 其跳转路径是 `/home`
|
||||
- 但 `provincial_admin` 之前没有 `/home` 的 `role_route` 授权
|
||||
- 所以首页接口不会返回该入口
|
||||
|
||||
已处理动作:
|
||||
|
||||
- 2026-04-29 已补充 `provincial_admin -> /home` 路由授权
|
||||
|
||||
后续如果首页入口“配置了却不显示”,先不要只盯 `leaudit_entry_modules`,要连同下面三层一起查:
|
||||
|
||||
1. `leaudit_entry_modules.path`
|
||||
2. `sys_routes.route_path`
|
||||
3. `role_route(role_id, route_id, status)`
|
||||
|
||||
## 暂未恢复项
|
||||
|
||||
以下仍未在本轮开放:
|
||||
|
||||
- `config-lists`
|
||||
- `document-types`
|
||||
- `prompts`
|
||||
|
||||
原因:
|
||||
|
||||
- 当前数据库里没有旧 `configurations` 表
|
||||
- `document-types / prompts` 仍依赖另一批尚未承接完的后台能力
|
||||
|
||||
## 下一步建议
|
||||
|
||||
后续按这个顺序继续最稳:
|
||||
|
||||
1. 先联调验证 `入口模块管理` 与 `角色权限管理`
|
||||
2. 再补 `config-lists` 新表与新接口
|
||||
3. 最后恢复 `document-types / prompts`
|
||||
@@ -0,0 +1,190 @@
|
||||
# 老用户迁移脚本说明
|
||||
|
||||
这份说明对应两个文件:
|
||||
|
||||
- `scripts/migrate_legacy_users.py`
|
||||
- `scripts/user_rbac_migration_audit.sql`
|
||||
|
||||
目标很明确:
|
||||
|
||||
- 把老系统 `docauditai` 的用户主数据迁到新系统 `leaudit_platform`
|
||||
- 当前先迁:`sso_users` + `user_role`
|
||||
- 新系统角色基础数据使用当前新库已经初始化好的 `roles`
|
||||
|
||||
---
|
||||
|
||||
## 1. 为什么现在先这样迁
|
||||
|
||||
当前真实情况是:
|
||||
|
||||
- 老库 `sso_users = 4106`
|
||||
- 老库 `user_role = 11`
|
||||
- 也就是大多数老用户没有显式角色记录
|
||||
|
||||
所以这次迁移不能只“复制 user_role”,还必须带一条默认规则:
|
||||
|
||||
- 老用户没有角色时,自动落到 `common`
|
||||
|
||||
这正是 `scripts/migrate_legacy_users.py` 已经处理好的逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 2. 脚本会做什么
|
||||
|
||||
脚本默认只做 dry-run,不写入。
|
||||
|
||||
它会:
|
||||
|
||||
1. 连接老库 `docauditai`
|
||||
2. 连接新库 `leaudit_platform`
|
||||
3. 读取老库 `sso_users`
|
||||
4. 读取老库 `user_role + roles`
|
||||
5. 按新系统允许的角色集合收口:
|
||||
- `provincial_admin`
|
||||
- `admin`
|
||||
- `common`
|
||||
- `super_admin`
|
||||
6. 如果老用户没有角色,则自动指定 `common`
|
||||
7. 迁移 `sso_users`
|
||||
8. 迁移 `user_role`
|
||||
9. 重置新库 `sso_users.id` 序列
|
||||
|
||||
---
|
||||
|
||||
## 3. 脚本的关键规则
|
||||
|
||||
## 3.1 保留老用户 ID
|
||||
|
||||
脚本会尽量保留老库 `sso_users.id` 写入新库。
|
||||
|
||||
这样做的好处是:
|
||||
|
||||
- 后续如果业务表开始引用用户 ID,语义更稳定
|
||||
- 后台排查时新旧库用户编号一致,方便核对
|
||||
|
||||
## 3.2 地区字段只认 `area`
|
||||
|
||||
迁移时:
|
||||
|
||||
- 会把老库 `sso_users.area` 带到新库
|
||||
- 并做基础 trim / 别名归一化
|
||||
|
||||
## 3.3 无角色用户自动补 `common`
|
||||
|
||||
这是当前最重要的迁移策略。
|
||||
|
||||
因为老库 4106 个用户里,4098 个没有显式 `user_role`。
|
||||
|
||||
如果不补:
|
||||
|
||||
- 新系统大量用户会迁过去但无法使用
|
||||
|
||||
## 3.4 幂等设计
|
||||
|
||||
脚本不是只适合跑一次。
|
||||
|
||||
它支持:
|
||||
|
||||
- `sub` 已存在时更新用户资料
|
||||
- `user_role` 已存在时跳过重复插入
|
||||
|
||||
所以可以重复执行。
|
||||
|
||||
---
|
||||
|
||||
## 4. 使用方式
|
||||
|
||||
## 4.1 先做 dry-run
|
||||
|
||||
```bash
|
||||
python3 scripts/migrate_legacy_users.py
|
||||
```
|
||||
|
||||
我已经实际跑过 dry-run,当前输出结果是:
|
||||
|
||||
- `legacy_users_total: 4106`
|
||||
- `default_common_role: 4098`
|
||||
- `insert_user: 4106`
|
||||
- `admin: 4`
|
||||
- `common: 4101`
|
||||
- `provincial_admin: 1`
|
||||
- `id_conflicts: 0`
|
||||
|
||||
这说明:
|
||||
|
||||
- 迁移逻辑当前是通的
|
||||
- 角色映射结果和老库真实分布一致
|
||||
- 当前没有发现 ID 冲突
|
||||
|
||||
## 4.2 正式执行迁移
|
||||
|
||||
```bash
|
||||
python3 scripts/migrate_legacy_users.py --apply
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 迁移前审计 SQL
|
||||
|
||||
如果你想先单独跑审计,用这个:
|
||||
|
||||
```bash
|
||||
psql -h 172.16.0.81 -p 54302 -U docauditai_admin -d docauditai -f scripts/user_rbac_migration_audit.sql
|
||||
```
|
||||
|
||||
它会检查:
|
||||
|
||||
- 用户总数
|
||||
- 地区分布
|
||||
- 重复 `sub`
|
||||
- 重复 `username`
|
||||
- 空地区用户
|
||||
- 无角色用户
|
||||
- 脏 `user_role`
|
||||
- 脏 `role_permissions`
|
||||
- 脏 `role_route`
|
||||
|
||||
---
|
||||
|
||||
## 6. 当前不迁什么
|
||||
|
||||
这版脚本当前不迁:
|
||||
|
||||
- `permissions`
|
||||
- `role_permissions`
|
||||
- `sys_routes`
|
||||
- `role_route`
|
||||
|
||||
原因不是不能迁,而是:
|
||||
|
||||
- 新系统已经有自己当前阶段的 seed 权限集
|
||||
- 当前最急的是先把“用户能登录、能识别角色、能拿到地区”打通
|
||||
|
||||
也就是说,这一版脚本优先解决:
|
||||
|
||||
- 用户主数据迁入
|
||||
- 默认角色补齐
|
||||
- 登录可用
|
||||
|
||||
---
|
||||
|
||||
## 7. 推荐执行顺序
|
||||
|
||||
建议严格按这个顺序:
|
||||
|
||||
1. 确认 `scripts/user_rbac_schema_patch.sql` 已执行
|
||||
2. 确认 `scripts/user_rbac_seed.sql` 已执行
|
||||
3. 先跑 `scripts/user_rbac_migration_audit.sql`
|
||||
4. 再跑 `python3 scripts/migrate_legacy_users.py` 做 dry-run
|
||||
5. 最后跑 `python3 scripts/migrate_legacy_users.py --apply`
|
||||
|
||||
---
|
||||
|
||||
## 8. 下一步衔接
|
||||
|
||||
老用户迁进去之后,下一步应该继续做:
|
||||
|
||||
1. 用新迁入用户实际验证 `/auth/login` 和 `/auth/me`
|
||||
2. 把文档列表、评查结果接口接上 `ALL / DEPT / SELF` 数据范围控制
|
||||
3. 再决定是否迁移老库的完整 `permissions / role_permissions / sys_routes / role_route`
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
# 评查点分组、文档类型与规则集目标结构与迁移方案
|
||||
|
||||
更新时间:2026-05-03
|
||||
|
||||
## 1. 产品确认后的最终口径
|
||||
|
||||
最终目标结构统一为:
|
||||
|
||||
- 一级分组 = 业务大类
|
||||
- 例如:`合同`、`卷宗`
|
||||
- 后续若出现新的入口业务,也允许继续新增一级分组
|
||||
- 二级分组 = 该业务大类下的具体业务类型
|
||||
- 例如合同下:`建设工程合同`、`买卖合同`
|
||||
- 例如卷宗下:`处罚-一般程序`、`许可-停业办理`
|
||||
- 规则集 = 挂在二级分组下
|
||||
- 入口模块 = 绑定一级分组
|
||||
|
||||
补充关系口径:
|
||||
|
||||
- 文档类型 = 具体业务类型主数据
|
||||
- 文档类型页 = 主数据维护与汇总展示
|
||||
- 评查点分组页 = 实际运行绑定维护页
|
||||
|
||||
这套结构对应的运行链路是:
|
||||
|
||||
- 入口模块
|
||||
- 一级分组(业务大类)
|
||||
- 二级分组(具体业务类型)
|
||||
- 规则集
|
||||
- 规则版本
|
||||
|
||||
其中:
|
||||
|
||||
- 入口模块:决定当前页面进入的是哪条业务线
|
||||
- 一级分组:承接该业务线的一级分类容器
|
||||
- 二级分组:决定这次上传/评查具体命中的业务类型
|
||||
- 规则集:决定跑哪组规则
|
||||
- 规则版本:决定跑规则集的哪一版
|
||||
|
||||
也就是说,当前正式口径是:
|
||||
|
||||
`入口模块 -> 一级分组(业务大类) -> 二级分组(具体业务类型) -> 规则集 -> 规则版本`
|
||||
|
||||
文档类型在这条链路中的位置是:
|
||||
|
||||
- 作为“具体业务类型主数据”
|
||||
- 通过 `document_type_id` 与二级分组形成稳定对应关系
|
||||
- 用于上传页、规则页、文档列表等业务页面识别当前业务类型
|
||||
|
||||
## 1.1 老系统与新系统的关系
|
||||
|
||||
### 老系统(docauditai)
|
||||
|
||||
老系统更接近:
|
||||
|
||||
```text
|
||||
DocumentType -> TopLevelGroup(s) -> ChildGroups -> EvaluationPoints
|
||||
```
|
||||
|
||||
特点:
|
||||
|
||||
- `document_types.evaluation_point_groups_ids` 绑定顶层组
|
||||
- `evaluation_point_groups` 是两级树
|
||||
- `evaluation_points` 实际挂在子组下
|
||||
- 运行时还会按 `area + document_attribute_type` 过滤
|
||||
|
||||
### 当前新平台(leaudit-platform)
|
||||
|
||||
当前新平台已经同时存在两套表达:
|
||||
|
||||
1. 规则执行主链
|
||||
- `DocumentType -> RuleTypeBinding -> RuleSet -> RuleVersion`
|
||||
2. 分组管理主链
|
||||
- `EntryModule -> Level1Group -> Level2Group -> RuleSet`
|
||||
|
||||
这就是大家容易混淆的根源。
|
||||
|
||||
### 当前统一理解
|
||||
|
||||
应统一理解为:
|
||||
|
||||
- 文档类型是业务类型主数据
|
||||
- 二级分组是运行时具体命中的业务类型节点
|
||||
- 规则集挂在二级分组下
|
||||
- `leaudit_rule_type_bindings` 继续承担执行链快速查找规则集的职责
|
||||
|
||||
也就是说:
|
||||
|
||||
- 文档类型页负责“主数据”
|
||||
- 分组页负责“运行绑定”
|
||||
- `rule_type_bindings` 负责“执行收口”
|
||||
|
||||
## 2. 现在系统为什么会让人混淆
|
||||
|
||||
当前系统的过渡态更接近:
|
||||
|
||||
- 一级分组 = 具体文档类型
|
||||
- 如:`建设工程合同`、`停业办理`
|
||||
- 二级分组 = 该文档类型下的默认子类型
|
||||
- 如:`通用`
|
||||
|
||||
这与产品最新口径不一致,主要有三个问题:
|
||||
|
||||
1. 一级分组被错误地做成了“具体文档类型”,而不是“业务大类容器”。
|
||||
2. 上传页看到的“子类型”其实只是默认二级分组,业务人员会误以为配置还没做好。
|
||||
3. 入口模块与一级分组之间缺少明确绑定语义,后续无法优雅支持新入口。
|
||||
|
||||
## 3. 目标树形样例
|
||||
|
||||
### 3.1 合同类
|
||||
|
||||
```text
|
||||
合同(一级 = 业务大类)
|
||||
├── 建设工程合同(二级 = 具体业务类型)
|
||||
│ ├── 规则集:建设工程合同-正文审查
|
||||
│ └── 规则集:建设工程合同-签署审查
|
||||
├── 买卖合同(二级 = 具体业务类型)
|
||||
│ └── 规则集:买卖合同-通用审查
|
||||
└── 委托合同(二级 = 具体业务类型)
|
||||
└── 规则集:委托合同-通用审查
|
||||
```
|
||||
|
||||
### 3.2 卷宗类
|
||||
|
||||
```text
|
||||
卷宗(一级 = 业务大类)
|
||||
├── 处罚-一般程序(二级 = 具体业务类型)
|
||||
│ └── 规则集:行政处罚-一般程序审查
|
||||
├── 处罚-简易程序(二级 = 具体业务类型)
|
||||
│ └── 规则集:行政处罚-简易程序审查
|
||||
├── 许可-停业办理(二级 = 具体业务类型)
|
||||
│ └── 规则集:行政许可-停业办理审查
|
||||
└── 许可-新办办理(二级 = 具体业务类型)
|
||||
└── 规则集:行政许可-新办办理审查
|
||||
```
|
||||
|
||||
### 3.3 将来新增入口
|
||||
|
||||
```text
|
||||
内部公文(一级 = 业务大类)
|
||||
├── 请示类公文(二级)
|
||||
├── 通知类公文(二级)
|
||||
└── 纪要类公文(二级)
|
||||
```
|
||||
|
||||
即:
|
||||
|
||||
- 不需要预先写死“只有合同、卷宗”
|
||||
- 后续新增业务时,直接新建一级、新建二级、挂规则集、再把一级绑定给入口模块即可
|
||||
|
||||
## 4. 页面职责重定义
|
||||
|
||||
### 4.1 评查点分组页 `/rule-groups`
|
||||
|
||||
这是唯一的运行绑定页,负责:
|
||||
|
||||
- 维护一级分组(业务大类)
|
||||
- 维护二级分组(具体业务类型)
|
||||
- 维护二级分组绑定的规则集
|
||||
- 维护一级分组与入口模块的绑定关系
|
||||
|
||||
统一文案:
|
||||
|
||||
- 一级:业务大类
|
||||
- 二级:具体业务类型
|
||||
- 规则集:实际运行绑定
|
||||
- 入口模块:一级分组归属入口
|
||||
|
||||
### 4.2 文档类型页 `/document-types`
|
||||
|
||||
文档类型页保留为“类型主数据页”,职责调整为:
|
||||
|
||||
- 维护具体文档类型名称、编码、描述
|
||||
- 展示该类型的汇总规则集
|
||||
- 不再承担一级/二级分组结构维护职责
|
||||
|
||||
统一文案:
|
||||
|
||||
- “汇总规则集”
|
||||
- “仅用于总览,实际运行绑定请在评查点分组页维护”
|
||||
|
||||
补充说明:
|
||||
|
||||
- 当前文档类型与规则集之间仍可保留“汇总绑定 / 快速绑定”能力
|
||||
- 但最终运行时应以“二级分组绑定规则集”为准
|
||||
- 因此文档类型页更适合做:
|
||||
- 主数据编辑
|
||||
- 入口模块归属展示
|
||||
- 汇总规则集总览
|
||||
|
||||
### 4.3 上传页 `/files/upload`
|
||||
|
||||
上传页统一交互为:
|
||||
|
||||
1. 先根据入口模块确定可用的一级分组
|
||||
2. 再根据一级分组或文档类型加载二级分组
|
||||
3. 用户选择本次上传实际命中的二级分组
|
||||
4. 最终上传时携带 `groupId`
|
||||
|
||||
## 5. 数据模型建议
|
||||
|
||||
## 5.1 继续复用 `leaudit_evaluation_point_groups`
|
||||
|
||||
当前表不必推倒重建,建议继续复用,并明确字段语义:
|
||||
|
||||
- `pid = 0`
|
||||
- 表示一级分组(业务大类)
|
||||
- `pid != 0`
|
||||
- 表示二级分组(具体业务类型)
|
||||
|
||||
字段语义建议:
|
||||
|
||||
- 一级分组:
|
||||
- `document_type_id` 可为空
|
||||
- `entry_module_id` 可为空,但新模型建议填写
|
||||
- `name` = 业务大类名称,如 `合同` / `卷宗`
|
||||
- 二级分组:
|
||||
- `document_type_id` 建议必填,对应实际具体文档类型
|
||||
- `pid` 指向对应一级分组
|
||||
- `entry_module_id` 通常从一级继承,不需要单独维护
|
||||
|
||||
## 5.2 新增或正式启用 `entry_module_id`
|
||||
|
||||
为支撑“一级分组绑定入口模块”,分组表需要显式支持:
|
||||
|
||||
- `entry_module_id`
|
||||
|
||||
用途:
|
||||
|
||||
- 绑定一级分组与入口模块的归属关系
|
||||
- 后续上传页、首页导航、入口权限控制都可以围绕该关系展开
|
||||
|
||||
## 5.3 与 `leaudit_rule_type_bindings` 的关系
|
||||
|
||||
当前不建议粗暴删除 `leaudit_rule_type_bindings`。
|
||||
|
||||
更合理的口径是:
|
||||
|
||||
- 分组树负责表达“业务结构”和“规则集挂载关系”
|
||||
- `leaudit_rule_type_bindings` 负责表达“执行时文档类型如何快速命中规则集”
|
||||
|
||||
可以把它理解为:
|
||||
|
||||
- 分组树 = 业务真相源
|
||||
- `rule_type_bindings` = 执行加速索引 / 收口层
|
||||
|
||||
## 6. 迁移原则
|
||||
|
||||
### 6.1 旧“一级=具体文档类型”是过渡态
|
||||
|
||||
当前数据库里已经有一批:
|
||||
|
||||
- 一级 = `建设工程合同`
|
||||
- 二级 = `通用`
|
||||
|
||||
这些不是最终结构,只是为了先把运行链路跑通。
|
||||
|
||||
### 6.2 未来迁移目标
|
||||
|
||||
应逐步迁成:
|
||||
|
||||
- 一级 = `合同`
|
||||
- 二级 = `建设工程合同`
|
||||
- 规则集继续挂二级
|
||||
|
||||
也就是说,未来需要把“当前一级里的具体文档类型”下沉成真正的二级。
|
||||
|
||||
## 7. 推荐迁移步骤
|
||||
|
||||
### 步骤 1:冻结语义
|
||||
|
||||
先统一团队口径:
|
||||
|
||||
- 一级 = 业务大类
|
||||
- 二级 = 具体业务类型
|
||||
- 规则集 = 只挂二级
|
||||
- 一级 = 入口模块绑定对象
|
||||
|
||||
### 步骤 2:后端先支持双语义兼容
|
||||
|
||||
后端接口先支持:
|
||||
|
||||
- 一级可绑定 `entry_module_id`
|
||||
- 二级继续绑定 `document_type_id`
|
||||
- `/by-document-types` 允许从“二级文档类型”反查所属一级
|
||||
|
||||
这样即使数据库还没完全迁,也能先把接口语义准备好。
|
||||
|
||||
### 步骤 3:前端页面改口径
|
||||
|
||||
`/rule-groups` 需逐步改为:
|
||||
|
||||
- 一级显示业务大类
|
||||
- 二级显示具体业务类型
|
||||
- 页面突出一级与入口模块关系
|
||||
|
||||
### 步骤 4:测试库迁移旧树
|
||||
|
||||
建议先在测试库按以下思路演练:
|
||||
|
||||
1. 建立一级:`合同`
|
||||
2. 建立一级:`卷宗`
|
||||
3. 将现有“建设工程合同 / 买卖合同 / 停业办理 ...”迁成各自二级
|
||||
4. 把原二级下规则集保留在新二级
|
||||
5. 清理旧默认 `通用` 子类型,仅在确实没有继续细分的情况下保留
|
||||
|
||||
### 步骤 5:再落正式库
|
||||
|
||||
正式库迁移必须在测试通过后执行,避免规则命中错位。
|
||||
|
||||
## 8. 运行时唯一正确规则
|
||||
|
||||
迁移完成后,唯一正确的运行规则应为:
|
||||
|
||||
1. 用户进入某入口模块
|
||||
2. 系统确定该入口可见的一级分组
|
||||
3. 用户选择具体二级分组
|
||||
4. 后端按 `groupId` 查询 `leaudit_rule_group_bindings`
|
||||
5. 找到对应规则集
|
||||
6. 取规则集可执行版本执行评查
|
||||
|
||||
即:
|
||||
|
||||
- 文档类型页:总览
|
||||
- 分组页:运行绑定
|
||||
- 上传页:按二级分组命中
|
||||
|
||||
## 9. 当前开发阶段结论
|
||||
|
||||
截至当前:
|
||||
|
||||
- “规则集挂二级”这部分方向是对的
|
||||
- “一级=具体文档类型”这部分已被产品新口径推翻
|
||||
- 后续开发应转向“一级=业务大类、一级绑定入口、二级=具体业务类型”
|
||||
|
||||
因此接下来所有实现都应围绕这条新口径继续推进,而不是再沿旧过渡模型加深。
|
||||
@@ -0,0 +1,379 @@
|
||||
# 评查点分组迁移执行前检查清单
|
||||
|
||||
更新时间:2026-05-03
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本清单用于在正式执行 `scripts/migrate_rule_groups_to_doc_type_roots.sql` 之前,先把当前库里的旧数据、兼容态数据和潜在冲突点查清楚。
|
||||
|
||||
如果希望直接执行一份只读巡检 SQL,可使用:
|
||||
|
||||
- `scripts/precheck_rule_group_migration.sql`
|
||||
|
||||
本次迁移的目标仍然是:
|
||||
|
||||
- 一级分组 = 业务大类
|
||||
- 二级分组 = 具体业务类型
|
||||
- 规则集 = 挂在二级分组下
|
||||
- 入口模块 = 绑定一级分组
|
||||
|
||||
## 2. 当前风险结论
|
||||
|
||||
正式迁移前,必须先确认下面 4 类风险:
|
||||
|
||||
1. 是否仍存在“一级直接挂具体文档类型”的旧根数据。
|
||||
2. 是否存在同一个 `document_type_id` 被多个二级分组同时承接的情况。
|
||||
3. 是否存在规则集同时挂在旧一级根和旧默认子级上,导致迁移后重复。
|
||||
4. `document_types.entry_module_id` 是否足够可靠,能支撑“先按入口模块粗分到一级业务大类”。
|
||||
|
||||
如果这 4 类风险没有检查清楚,不应直接跑迁移脚本。
|
||||
|
||||
## 2.1 当前已知巡检结论(2026-05-03)
|
||||
|
||||
基于本次实际巡检结果:
|
||||
|
||||
- `leaudit_document_types` 共 20 条,入口归属完整
|
||||
- 合同管理:10 条
|
||||
- 案卷智能评查:10 条
|
||||
- 当前仍有 20 个旧一级根
|
||||
- 即:`一级 = 具体文档类型`
|
||||
- 当前已经存在 2 个新一级业务大类根
|
||||
- `root.contract -> 合同`
|
||||
- `root.casefile -> 行政卷宗`
|
||||
- 当前有 20 个默认二级分组
|
||||
- 即:`二级 = 通用 / *.default`
|
||||
- 当前没有发现明显冲突:
|
||||
- 没有“同一文档类型被多个二级分组重复承接”
|
||||
- 没有“同一规则集同时挂在旧一级根和默认子级”
|
||||
- 没有“二级分组未绑定规则集”
|
||||
|
||||
结论:
|
||||
|
||||
- 当前库已经具备正式迁移的基础条件
|
||||
- 但还没有完成最后的结构补齐
|
||||
- 正式迁移前仍要先补 `entry_module_id`,再重跑巡检
|
||||
|
||||
## 3. 必查项
|
||||
|
||||
### 3.1 一级旧根检查
|
||||
|
||||
确认当前是否还有“一级 = 具体文档类型”的历史根:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
id,
|
||||
code,
|
||||
name,
|
||||
document_type_id,
|
||||
entry_module_id,
|
||||
sort_order,
|
||||
is_enabled
|
||||
FROM leaudit_evaluation_point_groups
|
||||
WHERE deleted_at IS NULL
|
||||
AND COALESCE(pid, 0) = 0
|
||||
AND document_type_id IS NOT NULL
|
||||
ORDER BY id;
|
||||
```
|
||||
|
||||
判定原则:
|
||||
|
||||
- 查出来的这些记录,都属于兼容态旧根。
|
||||
- 正式迁移后,它们不应再继续作为最终一级分组存在。
|
||||
|
||||
### 3.2 一级业务大类根检查
|
||||
|
||||
确认当前是否已经存在真正的业务大类根:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
id,
|
||||
code,
|
||||
name,
|
||||
entry_module_id,
|
||||
sort_order,
|
||||
is_enabled
|
||||
FROM leaudit_evaluation_point_groups
|
||||
WHERE deleted_at IS NULL
|
||||
AND COALESCE(pid, 0) = 0
|
||||
AND document_type_id IS NULL
|
||||
ORDER BY sort_order, id;
|
||||
```
|
||||
|
||||
判定原则:
|
||||
|
||||
- 正常目标结构下,这里应该看到如 `合同`、`卷宗` 这类一级业务大类。
|
||||
- 如果这里为空,说明当前库还没有真正切到目标结构。
|
||||
|
||||
### 3.3 二级分组唯一性检查
|
||||
|
||||
确认同一个文档类型是否被多个二级分组重复承接:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
document_type_id,
|
||||
COUNT(*) AS child_count,
|
||||
STRING_AGG(name, ' / ' ORDER BY id) AS child_names
|
||||
FROM leaudit_evaluation_point_groups
|
||||
WHERE deleted_at IS NULL
|
||||
AND COALESCE(pid, 0) <> 0
|
||||
AND document_type_id IS NOT NULL
|
||||
GROUP BY document_type_id
|
||||
HAVING COUNT(*) > 1
|
||||
ORDER BY document_type_id;
|
||||
```
|
||||
|
||||
判定原则:
|
||||
|
||||
- 如果结果为空,说明每个文档类型目前最多只挂到一个二级分组,迁移最安全。
|
||||
- 如果结果不为空,要逐条判断:
|
||||
- 是合理的“一个文档类型拆成多个业务子类型”
|
||||
- 还是历史脏数据
|
||||
|
||||
### 3.4 旧默认子级检查
|
||||
|
||||
确认当前是否存在旧默认子级:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
child.id,
|
||||
child.pid,
|
||||
parent.name AS parent_name,
|
||||
child.name,
|
||||
child.code,
|
||||
child.document_type_id
|
||||
FROM leaudit_evaluation_point_groups child
|
||||
JOIN leaudit_evaluation_point_groups parent
|
||||
ON parent.id = child.pid
|
||||
AND parent.deleted_at IS NULL
|
||||
WHERE child.deleted_at IS NULL
|
||||
AND COALESCE(child.pid, 0) <> 0
|
||||
AND (
|
||||
child.name = '通用'
|
||||
OR child.code LIKE '%.default'
|
||||
)
|
||||
ORDER BY child.id;
|
||||
```
|
||||
|
||||
判定原则:
|
||||
|
||||
- 这些记录多数是旧链路下的“默认子类型”兼容数据。
|
||||
- 迁移时如果它们已经被真实二级业务类型替代,应考虑把规则集迁走后再停用或清理。
|
||||
|
||||
### 3.5 规则集重复挂载检查
|
||||
|
||||
确认同一个文档类型下,旧一级根和默认子级是否同时挂了相同规则集:
|
||||
|
||||
```sql
|
||||
WITH target_groups AS (
|
||||
SELECT
|
||||
g.id,
|
||||
g.document_type_id,
|
||||
g.name,
|
||||
g.code,
|
||||
CASE
|
||||
WHEN COALESCE(g.pid, 0) = 0 THEN 'old_root'
|
||||
WHEN g.name = '通用' OR g.code LIKE '%.default' THEN 'default_child'
|
||||
ELSE 'other_child'
|
||||
END AS group_kind
|
||||
FROM leaudit_evaluation_point_groups g
|
||||
WHERE g.deleted_at IS NULL
|
||||
AND g.document_type_id IS NOT NULL
|
||||
)
|
||||
SELECT
|
||||
tg.document_type_id,
|
||||
rgb.rule_set_id,
|
||||
COUNT(*) AS binding_count,
|
||||
STRING_AGG(tg.group_kind || ':' || tg.name, ' / ' ORDER BY tg.id) AS group_sources
|
||||
FROM leaudit_rule_group_bindings rgb
|
||||
JOIN target_groups tg
|
||||
ON tg.id = rgb.group_id
|
||||
WHERE rgb.deleted_at IS NULL
|
||||
GROUP BY tg.document_type_id, rgb.rule_set_id
|
||||
HAVING COUNT(*) > 1
|
||||
ORDER BY tg.document_type_id, rgb.rule_set_id;
|
||||
```
|
||||
|
||||
判定原则:
|
||||
|
||||
- 如果结果不为空,迁移脚本执行前要先确认是否允许去重。
|
||||
- 否则迁移后可能出现:
|
||||
- 同一规则集被重复搬到新二级分组
|
||||
- 或者旧运行汇总结果不稳定
|
||||
|
||||
### 3.6 文档类型与入口模块映射检查
|
||||
|
||||
确认 `document_types` 是否都有稳定入口归属:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
dt.id,
|
||||
dt.code,
|
||||
dt.name,
|
||||
dt.entry_module_id,
|
||||
em.name AS entry_module_name
|
||||
FROM leaudit_document_types dt
|
||||
LEFT JOIN leaudit_entry_modules em
|
||||
ON em.id = dt.entry_module_id
|
||||
WHERE dt.deleted_at IS NULL
|
||||
ORDER BY dt.id;
|
||||
```
|
||||
|
||||
重点看:
|
||||
|
||||
- 是否存在 `entry_module_id IS NULL`
|
||||
- 是否存在入口模块已被删除、停用或名称异常
|
||||
- 是否存在“明明是合同类型却挂在卷宗入口”这类错误归属
|
||||
|
||||
### 3.7 规则集可运行性检查
|
||||
|
||||
迁移前需要确认二级分组绑定过去的规则集不是空壳:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
rs.id,
|
||||
rs.rule_name,
|
||||
rs.rule_type,
|
||||
rs.current_version_id,
|
||||
rs.fallback_version_id
|
||||
FROM leaudit_rule_sets rs
|
||||
WHERE rs.deleted_at IS NULL
|
||||
ORDER BY rs.id;
|
||||
```
|
||||
|
||||
这一步建议再配合业务接口实际看:
|
||||
|
||||
- 是否有可用版本
|
||||
|
||||
## 4. 正式迁移推荐执行顺序
|
||||
|
||||
### 步骤 1:先备份
|
||||
|
||||
必须先备份这几张表:
|
||||
|
||||
- `leaudit_evaluation_point_groups`
|
||||
- `leaudit_rule_group_bindings`
|
||||
- `leaudit_rule_type_bindings`
|
||||
- `leaudit_document_types`
|
||||
|
||||
### 步骤 2:先补字段,不迁数据
|
||||
|
||||
先补结构层缺口:
|
||||
|
||||
1. 给 `leaudit_evaluation_point_groups` 增加 `entry_module_id`
|
||||
2. 建索引
|
||||
3. 给已有一级业务大类根补入口绑定
|
||||
|
||||
建议值:
|
||||
|
||||
- `root.contract -> entry_module_id = 1`
|
||||
- `root.casefile -> entry_module_id = 2`
|
||||
|
||||
### 步骤 3:重新跑巡检
|
||||
|
||||
字段补完后,重新执行:
|
||||
|
||||
- `scripts/precheck_rule_group_migration.sql`
|
||||
|
||||
确认:
|
||||
|
||||
- 一级业务大类根能正确看到入口模块字段
|
||||
- 巡检结果仍无重复承接、重复绑定、空壳规则集等问题
|
||||
|
||||
### 步骤 4:测试库执行正式迁移
|
||||
|
||||
再执行:
|
||||
|
||||
- `scripts/migrate_rule_groups_to_business_roots.sql`
|
||||
|
||||
说明:
|
||||
|
||||
- 旧脚本 `scripts/migrate_rule_groups_to_doc_type_roots.sql` 仅保留兼容历史引用
|
||||
- 不建议继续作为正式迁移脚本使用
|
||||
|
||||
### 步骤 5:迁后验证 4 个页面
|
||||
|
||||
必须验证:
|
||||
|
||||
1. `/rule-groups`
|
||||
2. `/files/upload`
|
||||
3. `/document-types/new?id=...`
|
||||
4. `/documents/list`
|
||||
|
||||
## 5. 迁移脚本调整口径
|
||||
|
||||
基于当前巡检结果,正式脚本建议继续遵守:
|
||||
|
||||
1. 不要再硬编码“只有合同和卷宗”
|
||||
2. 优先复用已存在业务大类根,不重复新造
|
||||
3. 一级根编码必须统一,例如继续沿用:
|
||||
- `root.contract`
|
||||
- `root.casefile`
|
||||
- 可用规则数是否大于 0
|
||||
|
||||
否则迁完结构后,上传仍然会失败,只是失败原因从“结构问题”变成“规则集不可运行”。
|
||||
|
||||
## 4. 执行前必须人工确认的业务映射
|
||||
|
||||
SQL 只能粗分,业务归属必须人工确认:
|
||||
|
||||
### 4.1 一级业务大类清单
|
||||
|
||||
至少要先列清:
|
||||
|
||||
- 哪些文档类型归到 `合同`
|
||||
- 哪些文档类型归到 `卷宗`
|
||||
- 是否已经存在第 3 个业务大类,如 `内部公文`
|
||||
|
||||
### 4.2 二级业务类型命名规范
|
||||
|
||||
要确认:
|
||||
|
||||
- 二级显示名称是否直接用 `document_types.name`
|
||||
- 还是需要改成更贴近业务的名称
|
||||
- 例如:`处罚-一般程序`
|
||||
- 而不是数据库里原始的技术名
|
||||
|
||||
### 4.3 默认子类型保留策略
|
||||
|
||||
需要人工确认:
|
||||
|
||||
- 哪些文档类型仍保留 `默认子类型(通用)`
|
||||
- 哪些必须拆成多个真实二级业务类型
|
||||
|
||||
## 5. 执行顺序建议
|
||||
|
||||
推荐按下面顺序推进:
|
||||
|
||||
1. 跑本清单中的查询 SQL,导出当前状态。
|
||||
2. 形成一份 `document_type -> 一级业务大类 -> 目标二级名称` 的确认表。
|
||||
3. 在测试库执行迁移脚本。
|
||||
4. 验证以下页面:
|
||||
- `/rule-groups`
|
||||
- `/files/upload`
|
||||
- `/document-types/new?id=...`
|
||||
- `/documents/list`
|
||||
5. 验证上传链路:
|
||||
- 能否正确选择子类型
|
||||
- 上传后是否命中预期规则集
|
||||
- 合同附件追加链路是否仍正常
|
||||
6. 测试通过后,再评估正式库执行窗口。
|
||||
|
||||
## 6. 当前不建议直接执行的情况
|
||||
|
||||
只要出现以下任一情况,都不建议直接执行迁移脚本:
|
||||
|
||||
- `document_types.entry_module_id` 还有大量空值
|
||||
- 同一文档类型被多个旧组重复承接,但业务还没定论
|
||||
- 规则集存在大量“可用规则数 = 0”
|
||||
- 一级业务大类并不只有 `合同 / 卷宗`,但映射方案还没补
|
||||
- 业务方还没确认二级命名与保留策略
|
||||
|
||||
## 7. 当前结论
|
||||
|
||||
截至当前,这套系统已经具备:
|
||||
|
||||
- 前端按新链路展示和解释的能力
|
||||
- 后端兼容新旧结构的能力
|
||||
- 新配置回写旧运行绑定表的能力
|
||||
|
||||
但数据库正式迁移这一步,仍然必须先做“数据盘点 + 业务确认 + 测试库演练”三件事,不能直接在正式库硬切。
|
||||
@@ -1,188 +0,0 @@
|
||||
# 首页入口接口落地说明
|
||||
|
||||
## 1. 已落地内容
|
||||
|
||||
- 后端新增统一接口:`GET /api/home/entry-modules`
|
||||
- 后端来源统一收口到:
|
||||
- `leaudit_entry_modules`
|
||||
- `leaudit_document_types`
|
||||
- `sys_routes + role_route`(仅做页面可见性补充校验)
|
||||
- 前端首页改为只调用后端统一接口,不再直接查 PostgREST 的 `entry_modules + document_types`
|
||||
- 前端首页不再手工注入“智慧法务助手”模块
|
||||
|
||||
## 2. 当前接口返回语义
|
||||
|
||||
返回结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "合同智能审查",
|
||||
"description": "合同类文档入口,进入合同检索与评查主页",
|
||||
"targetPath": "/contract-template/search",
|
||||
"routePath": "/contract-template/search",
|
||||
"iconPath": "entryModule/contract.png",
|
||||
"sortOrder": 10,
|
||||
"requiresDocumentTypes": true,
|
||||
"areas": [],
|
||||
"documentTypes": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "建设工程合同",
|
||||
"code": "contract.construction.general"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
- `targetPath`
|
||||
- 首页点击后真正跳转的前端路由
|
||||
- `routePath`
|
||||
- 当前阶段与 `targetPath` 同值
|
||||
- 预留给后续把“展示路径”和“权限判断路径”拆开
|
||||
- `iconPath`
|
||||
- 模块图标路径
|
||||
- 若以 `/images/` 开头,前端按静态资源处理
|
||||
- 否则前端按 `DOCUMENT_URL + iconPath` 处理
|
||||
- `requiresDocumentTypes`
|
||||
- `true`:模块必须挂文档类型才能进入
|
||||
- `false`:允许零文档类型直接进入
|
||||
- 当前兼容规则:
|
||||
- `/chat-with-llm/chat` → `false`
|
||||
- `/cross-checking` → `false`
|
||||
- 其他路径 → `true`
|
||||
|
||||
## 3. 当前过滤逻辑
|
||||
|
||||
后端按以下顺序过滤首页入口:
|
||||
|
||||
1. `leaudit_entry_modules.is_enabled = true`
|
||||
2. `leaudit_entry_modules.deleted_at is null`
|
||||
3. 地区过滤
|
||||
- `super_admin / provincial_admin` 跳过地区过滤
|
||||
- 其他用户按 `sso_users.area` 匹配 `leaudit_entry_modules.areas`
|
||||
- 若 `areas` 为空,则视为全地区可见
|
||||
4. 页面权限过滤
|
||||
- 若 `sys_routes` 里存在与 `path` 相同的页面路由,则要求当前用户角色在 `role_route` 中已授权
|
||||
- 若 `sys_routes` 尚未配置该路由,则当前阶段放行,不阻塞首页展示
|
||||
|
||||
### 3.1 `/home` 路由补充说明(2026-04-29)
|
||||
|
||||
这次联调里出现过一个容易忘的问题:
|
||||
|
||||
- `leaudit_entry_modules` 里的 `内部公文` 入口配置本身是存在的
|
||||
- 它当前配置的跳转路径是 `path = /home`
|
||||
- 但如果当前角色在 `role_route` 里没有 `/home` 的启用记录,首页接口会把这个入口过滤掉
|
||||
|
||||
实际排查结论:
|
||||
|
||||
- 不是“内部公文入口模块丢了”
|
||||
- 也不是“地区过滤没命中”
|
||||
- 而是“入口模块目标路径存在,但角色没有对应页面路由授权”
|
||||
|
||||
本次已补充:
|
||||
|
||||
- 给 `provincial_admin` 角色补上 `/home` 的 `role_route`
|
||||
|
||||
因此后续如果再次出现“首页某个入口配置明明在库里,但前端没显示”,优先按下面顺序排查:
|
||||
|
||||
1. `leaudit_entry_modules` 里该入口是否存在且 `is_enabled = true`
|
||||
2. 该入口 `areas` 是否覆盖当前用户地区
|
||||
3. 该入口 `path` 对应的页面路由是否存在于 `sys_routes`
|
||||
4. 当前角色是否在 `role_route` 中对该 `path` 有 `status = 1`
|
||||
|
||||
特别注意:
|
||||
|
||||
- `内部公文 -> /home` 这类首页型入口,对 `role_route` 很敏感
|
||||
- 只配入口模块、不配页面路由授权,首页接口不会放出来
|
||||
|
||||
## 4. 当前表语义
|
||||
|
||||
### `leaudit_entry_modules`
|
||||
|
||||
当前阶段它只负责“首页业务入口配置”,核心字段语义如下:
|
||||
|
||||
- `name`:首页模块名称
|
||||
- `description`:首页模块描述
|
||||
- `path`:当前阶段同时承担 `targetPath / routePath`
|
||||
- `icon_path`:首页卡片图标
|
||||
- `areas`:地区可见范围
|
||||
- `sort_order`:首页排序
|
||||
- `is_enabled`:是否启用
|
||||
|
||||
### `leaudit_document_types`
|
||||
|
||||
- `entry_module_id`:文档类型归属的首页入口模块
|
||||
- 首页进入某模块后,前端把该模块下的 `documentTypes[].id` 写入 `sessionStorage.documentTypeIds`
|
||||
- 后续列表、首页统计、规则页面仍复用现有 `documentTypeIds` 逻辑
|
||||
|
||||
## 5. 前端改造点
|
||||
|
||||
已改造:
|
||||
|
||||
- `new_doc_review/app/api/home/home.ts`
|
||||
- 改为请求 `/home/entry-modules`
|
||||
- `new_doc_review/app/routes/_index.tsx`
|
||||
- 直接使用后端返回的 `targetPath`
|
||||
- 直接使用后端返回的 `iconPath`
|
||||
- 按 `requiresDocumentTypes` 判断是否允许进入
|
||||
|
||||
未改造但仍兼容:
|
||||
|
||||
- 系统设置入口仍沿用现有 `getUserRoutesByRole()` 逻辑
|
||||
- 交叉评查专属端口模式仍保留
|
||||
|
||||
## 6. 初始化数据
|
||||
|
||||
已补脚本:
|
||||
|
||||
- `scripts/seed_home_entry_modules.sql`
|
||||
|
||||
脚本内容:
|
||||
|
||||
- 按老系统真实数据初始化 5 个入口模块:
|
||||
- `合同管理`
|
||||
- `案卷智能评查`
|
||||
- `内部公文`
|
||||
- `智慧法务助手`
|
||||
- `交叉评查`
|
||||
- 其中:
|
||||
- `path` 存前端跳转路由
|
||||
- `icon_path` 存老系统入口图片路径
|
||||
- 自动把:
|
||||
- `contract.%` 绑定到 `合同管理`
|
||||
- `行政卷宗.%` 绑定到 `案卷智能评查`
|
||||
- `NBGW / internal.document` 绑定到 `内部公文`
|
||||
|
||||
## 7. 当前兼容性取舍
|
||||
|
||||
这次落地刻意没有直接改库结构新增:
|
||||
|
||||
- `module_key`
|
||||
- `module_type`
|
||||
- `allow_empty_types`
|
||||
- `target_path`
|
||||
- `route_path`
|
||||
|
||||
而是先用现有字段完成可运行版本:
|
||||
|
||||
- `path` 兼容成 `targetPath + routePath`
|
||||
- 特殊模块是否允许空文档类型,先按路径规则判断
|
||||
|
||||
同时和老系统保持语义对齐:
|
||||
|
||||
- `icon_path` 承接老库 `entry_modules.path` 的图片路径语义
|
||||
|
||||
这样做的目的:
|
||||
|
||||
- 先把首页入口能力真正跑起来
|
||||
- 不阻塞当前前后端联调
|
||||
- 后续若需要长期治理,再补结构性字段升级
|
||||
Reference in New Issue
Block a user