2225 lines
81 KiB
Markdown
2225 lines
81 KiB
Markdown
# 入口模块菜单配置与多租户重构实施计划
|
||
|
||
> **给后续执行开发的人:** 按本文任务逐步实施,不要跳着改。本文只做入口模块、菜单、文档归属、多租户范围的重构计划,不直接改动运行时规则评查逻辑。
|
||
|
||
**目标:** 让“入口模块”真正成为用户能理解的业务工作台,菜单显示、文档过滤、上传范围、规则分组、租户可见性都从入口模块配置出发,不再依赖“名称里是否包含合同/公文”这种硬编码判断。
|
||
|
||
**总体架构:** 保留 `leaudit_entry_modules` 作为首页入口模块主表,保留现有 `leaudit_entry_module_tenants` 作为租户可见性关系表。在入口模块上增加“菜单模板字段”`menu_profile` 和“功能清单字段”`features`,用它们决定该入口模块显示哪些功能菜单。角色权限系统继续负责页面和接口权限,入口模块只负责“业务工作台编排”和“业务范围”。
|
||
|
||
**涉及技术:** 后端接口、数据库原生查询、数据库迁移脚本、前端页面、前端侧边栏、现有角色权限系统、现有租户识别逻辑。
|
||
|
||
---
|
||
|
||
## 一、当前根因
|
||
|
||
现在系统的问题不是用户没配置好,而是代码在“猜业务类型”。
|
||
|
||
当前错误判断包括:
|
||
|
||
- 合同菜单靠“入口模块名称里有没有合同两个字”判断。
|
||
- 公文菜单靠“入口模块名称里有没有公文两个字”或固定路径判断。
|
||
- 公文列表页面强行显示成“内部公文”。
|
||
- 公文列表页面虽然读取了浏览器地址里的 `entryModuleId`,但没有传给后端列表接口。
|
||
- 文档列表和规则页面部分支持 `entryModuleId`,但侧边栏菜单不认它。
|
||
- 租户可见性已经有 `leaudit_entry_module_tenants`,但菜单功能没有和入口模块、租户上下文形成闭环。
|
||
|
||
目标链路应该变成:
|
||
|
||
```text
|
||
当前用户租户 -> 可见入口模块 -> 用户选择入口模块
|
||
入口模块 -> 菜单模板字段/功能清单字段 -> 生成左侧菜单
|
||
入口模块 -> 文档类型 -> 上传/列表/规则分组范围
|
||
文档 -> entry_module_id + type_id + group_id + tenant_code -> 后续评查/统计/列表过滤
|
||
```
|
||
|
||
## 二、用户应该怎么理解
|
||
|
||
管理员配置入口模块时,只需要按业务语言操作:
|
||
|
||
1. 新建入口模块,例如 `入口模块-测试`。
|
||
2. 选择菜单模板:合同工作台、公文工作台、交叉评查工作台、通用文档评查、自定义。
|
||
3. 勾选功能:文档列表、文件上传、规则配置、规则分组、模板搜索、模板列表、公文列表、公文上传等。
|
||
4. 分配租户:云浮、揭阳、梅州、公共资源域等。
|
||
5. 绑定一级文档类型。
|
||
6. 在规则分组里配置二级运行子类型和规则绑定。
|
||
|
||
用户不应该被迫把入口模块命名为“合同xxx”或“公文xxx”才能显示对应功能。
|
||
|
||
## 三、核心数据模型
|
||
|
||
### 3.1 保留现有表
|
||
|
||
继续使用:
|
||
|
||
- `leaudit_entry_modules`:入口模块主表,表示首页业务入口。
|
||
- `leaudit_entry_module_tenants`:入口模块和租户的可见关系。
|
||
- `leaudit_document_types`:文档类型,通过 `entry_module_id` 归属入口模块。
|
||
- `leaudit_evaluation_point_groups`:规则分组树。
|
||
- `leaudit_rule_group_bindings`:运行时规则绑定唯一事实源。
|
||
- `leaudit_documents`:文档记录,后续需要补 `entry_module_id`。
|
||
|
||
### 3.2 入口模块新增字段
|
||
|
||
给 `leaudit_entry_modules` 增加:
|
||
|
||
```sql
|
||
ALTER TABLE leaudit_entry_modules
|
||
ADD COLUMN IF NOT EXISTS menu_profile VARCHAR(64) NOT NULL DEFAULT 'document_review',
|
||
ADD COLUMN IF NOT EXISTS features JSONB NOT NULL DEFAULT '[]'::jsonb;
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_leaudit_entry_modules_menu_profile
|
||
ON leaudit_entry_modules(menu_profile)
|
||
WHERE deleted_at IS NULL;
|
||
```
|
||
|
||
字段解释:
|
||
|
||
- `menu_profile`:菜单模板字段,比如合同工作台、公文工作台、通用文档评查。
|
||
- `features`:功能清单字段,表示该入口模块启用哪些功能菜单。
|
||
|
||
### 3.3 文档表新增字段
|
||
|
||
给 `leaudit_documents` 增加:
|
||
|
||
```sql
|
||
ALTER TABLE leaudit_documents
|
||
ADD COLUMN IF NOT EXISTS entry_module_id BIGINT NULL REFERENCES leaudit_entry_modules(id);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_leaudit_documents_entry_module_id
|
||
ON leaudit_documents(entry_module_id);
|
||
```
|
||
|
||
作用:
|
||
|
||
- 文档创建后明确知道自己来自哪个入口模块。
|
||
- 文档列表、公文列表、统计、后续质量校验都可以按入口模块过滤。
|
||
|
||
## 四、菜单模板与功能编码
|
||
|
||
### 4.1 菜单模板
|
||
|
||
固定使用这些编码:
|
||
|
||
```text
|
||
document_review 通用文档评查
|
||
contract 合同工作台
|
||
govdoc 内部公文工作台
|
||
cross_checking 交叉评查工作台
|
||
custom 自定义工作台
|
||
```
|
||
|
||
### 4.2 功能编码
|
||
|
||
固定使用这些编码:
|
||
|
||
```text
|
||
home 首页/概览
|
||
documents 文档列表
|
||
upload 文件上传
|
||
rules 规则配置
|
||
rule_groups 规则分组
|
||
contract_template_search 合同模板搜索
|
||
contract_template_list 合同模板列表
|
||
govdoc_audits 公文列表
|
||
govdoc_upload 公文上传
|
||
cross_checking 交叉评查
|
||
cross_checking_upload 创建交叉评查任务
|
||
cross_checking_list 交叉评查任务列表
|
||
usage_stats 使用统计
|
||
```
|
||
|
||
### 4.3 默认功能
|
||
|
||
如果老数据没有配置功能清单字段,就按菜单模板字段自动补默认值:
|
||
|
||
```json
|
||
{
|
||
"document_review": ["home", "documents", "upload", "rules", "rule_groups"],
|
||
"contract": ["home", "documents", "upload", "rules", "contract_template_search", "contract_template_list"],
|
||
"govdoc": ["home", "govdoc_audits", "govdoc_upload", "rule_groups"],
|
||
"cross_checking": ["cross_checking", "cross_checking_upload", "cross_checking_list"],
|
||
"custom": ["home", "documents"]
|
||
}
|
||
```
|
||
|
||
## 五、多租户规则
|
||
|
||
这里必须沿用你当前项目已有模型,不新造租户体系。
|
||
|
||
### 5.1 入口模块可见性
|
||
|
||
入口模块是否对某个租户可见,继续由 `leaudit_entry_module_tenants` 控制。
|
||
|
||
判断规则:
|
||
|
||
```text
|
||
用户 tenant_code 命中 leaudit_entry_module_tenants.tenant_code
|
||
并且 leaudit_entry_module_tenants.is_enabled = true
|
||
并且 leaudit_entry_modules.is_enabled = true
|
||
并且入口模块没有 deleted_at
|
||
```
|
||
|
||
特殊情况:
|
||
|
||
- `PUBLIC` 可以作为公共入口模块。
|
||
- 超级管理员或全局管理员可以跨租户查看。
|
||
- 旧的地区字段暂时保留兼容,但不再作为新逻辑主模型。
|
||
|
||
### 5.2 功能菜单是否需要租户级差异
|
||
|
||
第一阶段不做“同一个入口模块不同租户显示不同功能”。
|
||
|
||
第一阶段规则:
|
||
|
||
```text
|
||
入口模块功能清单对所有可见租户一致
|
||
租户只控制入口模块是否可见
|
||
```
|
||
|
||
如果后续真有需求,例如同一个入口模块云浮显示模板搜索、梅州不显示模板搜索,再扩展:
|
||
|
||
```sql
|
||
ALTER TABLE leaudit_entry_module_tenants
|
||
ADD COLUMN IF NOT EXISTS features_override JSONB NULL,
|
||
ADD COLUMN IF NOT EXISTS menu_profile_override VARCHAR(64) NULL;
|
||
```
|
||
|
||
最终生效逻辑:
|
||
|
||
```text
|
||
租户关系表 features_override 有值 -> 用租户覆盖配置
|
||
否则入口模块 features 有值 -> 用入口模块配置
|
||
否则按 menu_profile 默认功能生成
|
||
```
|
||
|
||
第一阶段先不要上这个复杂度。
|
||
|
||
### 5.3 入口模块配置权限
|
||
|
||
入口模块是系统级业务工作台配置,但不要在代码里硬编码“只有某个角色名能配置”。正确口径是:谁被分配了入口模块管理权限,谁才能配置入口模块。
|
||
|
||
也就是说:
|
||
|
||
```text
|
||
不要写死 role_key = super_admin
|
||
不要写死 role_key = provincial_admin
|
||
不要写死市区管理员一定不能操作
|
||
```
|
||
|
||
后端只认权限点:
|
||
|
||
```text
|
||
entry_module:list:read 查看入口模块列表
|
||
entry_module:detail:read 查看入口模块详情
|
||
entry_module:create:write 创建入口模块
|
||
entry_module:update:write 编辑入口模块
|
||
entry_module:delete:delete 删除入口模块
|
||
entry_module:image:write 上传或替换入口模块图标
|
||
```
|
||
|
||
推荐默认分配策略:
|
||
|
||
```text
|
||
系统超级管理员角色:默认分配入口模块全部管理权限
|
||
省级管理员角色:默认不分配创建/编辑/删除入口模块权限,是否开放由实际授权决定
|
||
市区管理员角色:默认不分配创建/编辑/删除入口模块权限,是否开放由实际授权决定
|
||
```
|
||
|
||
所以最终判断不是“这个人是不是超级管理员”,而是:
|
||
|
||
```text
|
||
能不能创建入口模块 -> 看有没有 entry_module:create:write
|
||
能不能编辑入口模块 -> 看有没有 entry_module:update:write
|
||
能不能删除入口模块 -> 看有没有 entry_module:delete:delete
|
||
能不能上传图标 -> 看有没有 entry_module:image:write
|
||
```
|
||
|
||
三层边界必须分清:
|
||
|
||
```text
|
||
RBAC 权限点:控制谁能配置入口模块
|
||
入口模块租户关系:控制哪个租户能看到/使用入口模块
|
||
入口模块 features + 页面/API 权限:控制进入入口后能看到和使用哪些功能
|
||
```
|
||
|
||
举例:
|
||
|
||
```text
|
||
某个市区管理员没有 entry_module:create:write
|
||
即使他属于云浮租户,也不能新建入口模块
|
||
|
||
某个市区管理员有 entry_module:create:write
|
||
他就可以进入入口模块管理页执行创建动作
|
||
但创建出来的入口能被哪些租户看到,仍然必须写入 leaudit_entry_module_tenants
|
||
```
|
||
|
||
## 六、一级分组、二级分组、规则绑定设计
|
||
|
||
这一块是本次重构最关键的业务边界。入口模块只解决“用户从哪个工作台进来”,规则分组解决“这个工作台下有哪些业务类型”,规则绑定解决“这个业务类型运行哪套规则”。
|
||
|
||
### 6.1 最终业务口径
|
||
|
||
固定使用两层分组,不继续发散:
|
||
|
||
```text
|
||
入口模块
|
||
-> 一级分组:业务大类
|
||
-> 二级分组:具体运行类型
|
||
-> 规则绑定:绑定规则集
|
||
```
|
||
|
||
举例:
|
||
|
||
```text
|
||
入口模块:合同评查
|
||
一级分组:合同
|
||
二级分组:建设工程合同、买卖合同、租赁合同
|
||
规则绑定:建设工程合同 -> 建设工程合同规则集
|
||
```
|
||
|
||
再举例:
|
||
|
||
```text
|
||
入口模块:内部公文
|
||
一级分组:内部公文
|
||
二级分组:请示、通知、会议纪要
|
||
规则绑定:请示 -> 请示规则集
|
||
```
|
||
|
||
### 6.2 一级分组职责
|
||
|
||
一级分组只表达业务大类,不直接参与运行时规则命中。
|
||
|
||
数据库仍然复用:
|
||
|
||
```text
|
||
leaudit_evaluation_point_groups
|
||
```
|
||
|
||
一级分组判断:
|
||
|
||
```text
|
||
pid = 0
|
||
```
|
||
|
||
一级分组字段规则:
|
||
|
||
```text
|
||
entry_module_id 必填,表示这个一级分组属于哪个入口模块
|
||
document_type_id 为空,不直接绑定具体文档类型
|
||
name 使用业务大类名称,例如 合同、内部公文、行政卷宗
|
||
```
|
||
|
||
也就是说,入口模块页面里的“规则分组”应该先看到这个入口模块下的一级分组,而不是全系统所有分组。
|
||
|
||
### 6.3 二级分组职责
|
||
|
||
二级分组才是实际运行类型,也就是用户上传、列表过滤、评查执行真正要落的 `groupId`。
|
||
|
||
二级分组判断:
|
||
|
||
```text
|
||
pid != 0
|
||
```
|
||
|
||
二级分组字段规则:
|
||
|
||
```text
|
||
pid 指向一级分组 id
|
||
document_type_id 必填,表示这个二级分组对应哪个具体文档类型
|
||
entry_module_id 可以冗余保存,也可以从一级分组继承,但查询时必须能按入口模块过滤
|
||
```
|
||
|
||
前端显示时,用户理解成:
|
||
|
||
```text
|
||
一级分组 = 大类
|
||
二级分组 = 具体要评查的文档类型/运行类型
|
||
```
|
||
|
||
后端理解成:
|
||
|
||
```text
|
||
文档最终必须落到二级分组 group_id
|
||
```
|
||
|
||
### 6.4 规则绑定职责
|
||
|
||
规则绑定只允许挂二级分组,不允许挂一级分组。
|
||
|
||
继续使用:
|
||
|
||
```text
|
||
leaudit_rule_group_bindings
|
||
```
|
||
|
||
绑定关系:
|
||
|
||
```text
|
||
leaudit_rule_group_bindings.group_id = 二级分组 id
|
||
leaudit_rule_group_bindings.rule_set_id = 规则集 id
|
||
```
|
||
|
||
禁止口径:
|
||
|
||
```text
|
||
不要把规则集挂在一级分组
|
||
不要再按入口模块名称猜规则集
|
||
不要再绕回旧的文档类型规则绑定表作为主链路
|
||
```
|
||
|
||
运行时唯一主链路:
|
||
|
||
```text
|
||
document.group_id
|
||
-> leaudit_rule_group_bindings.group_id
|
||
-> rule_set_id
|
||
-> 当前可执行规则版本
|
||
```
|
||
|
||
### 6.5 多租户下规则怎么生效
|
||
|
||
分组树本身不建议第一阶段按租户复制一份。否则云浮、梅州、揭阳每个租户一套树,后面维护会爆炸。
|
||
|
||
第一阶段建议:
|
||
|
||
```text
|
||
入口模块租户关系:控制哪个租户看得到哪个入口模块
|
||
一级/二级分组:表达入口模块下的业务结构
|
||
规则绑定 tenant_code/scope_type:控制不同租户实际运行哪套规则集
|
||
```
|
||
|
||
也就是说:
|
||
|
||
```text
|
||
同一个二级分组,可以存在多条规则绑定
|
||
```
|
||
|
||
例如:
|
||
|
||
```text
|
||
二级分组:建设工程合同
|
||
云浮租户 -> 云浮建设工程合同规则集
|
||
梅州租户 -> 梅州建设工程合同规则集
|
||
省级兜底 -> 通用建设工程合同规则集
|
||
```
|
||
|
||
运行时选择顺序:
|
||
|
||
```text
|
||
优先当前租户绑定
|
||
没有当前租户绑定,再走省级/公共兜底绑定
|
||
仍然没有,就提示该二级分组未配置有效规则
|
||
```
|
||
|
||
### 6.6 上传、列表、评查的字段落点
|
||
|
||
上传时必须落这些字段:
|
||
|
||
```text
|
||
entry_module_id:从哪个入口进来
|
||
type_id:具体文档类型
|
||
group_id:二级分组 id
|
||
tenant_code:当前租户
|
||
```
|
||
|
||
如果上传时只传了文档类型,没有传二级分组,后端只能在“该文档类型只有一个可用二级分组”时兜底推断。只要有多个二级分组,就必须让用户明确选择,否则规则命中会不稳定。
|
||
|
||
列表过滤顺序:
|
||
|
||
```text
|
||
先按 tenant_code 做租户边界
|
||
再按 entry_module_id 做入口模块范围
|
||
再按 group_id/type_id 做具体业务过滤
|
||
```
|
||
|
||
评查执行顺序:
|
||
|
||
```text
|
||
读取文档 group_id
|
||
确认 group_id 是二级分组
|
||
按 group_id + tenant_code 找有效规则绑定
|
||
取 rule_set 当前可执行版本
|
||
执行评查
|
||
```
|
||
|
||
### 6.7 后台页面怎么改
|
||
|
||
入口模块管理页:
|
||
|
||
```text
|
||
只负责入口模块本身、菜单功能、租户可见性
|
||
不在这里直接绑规则集
|
||
```
|
||
|
||
文档类型管理页:
|
||
|
||
```text
|
||
负责维护具体文档类型属于哪个入口模块
|
||
可以辅助展示它被哪个二级分组使用
|
||
```
|
||
|
||
规则分组页:
|
||
|
||
```text
|
||
进入某入口模块后,只显示该入口模块下的一级分组
|
||
一级分组下面维护二级分组
|
||
二级分组上配置规则绑定
|
||
```
|
||
|
||
规则绑定页或弹窗:
|
||
|
||
```text
|
||
只能从二级分组进入
|
||
可以配置当前租户绑定、省级兜底绑定、公共绑定
|
||
保存到 leaudit_rule_group_bindings
|
||
```
|
||
|
||
### 6.8 必须加的校验
|
||
|
||
后端保存规则分组时必须校验:
|
||
|
||
```text
|
||
一级分组 pid = 0 时,必须有 entry_module_id
|
||
一级分组不允许绑定 rule_set
|
||
二级分组 pid != 0 时,必须有 document_type_id
|
||
二级分组所属一级分组必须和当前入口模块一致
|
||
二级分组才允许写 leaudit_rule_group_bindings
|
||
上传文档时传入的 group_id 必须是二级分组
|
||
上传文档时 group_id 对应的入口模块必须等于 entry_module_id
|
||
```
|
||
|
||
建议补充数据库约束或唯一索引:
|
||
|
||
```sql
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uq_leaudit_ep_groups_parent_doc_type_active
|
||
ON leaudit_evaluation_point_groups(pid, document_type_id)
|
||
WHERE deleted_at IS NULL
|
||
AND COALESCE(pid, 0) <> 0
|
||
AND document_type_id IS NOT NULL;
|
||
```
|
||
|
||
作用是避免同一个一级分组下面重复挂同一个文档类型,导致上传时无法稳定推断二级分组。
|
||
|
||
### 6.9 旧数据兼容策略
|
||
|
||
当前系统里可能还有旧结构:
|
||
|
||
```text
|
||
一级分组 = 具体文档类型
|
||
二级分组 = 通用
|
||
```
|
||
|
||
这个是过渡结构,不要继续加深。
|
||
|
||
兼容策略:
|
||
|
||
```text
|
||
短期:后端查询同时兼容旧结构,避免线上功能直接断
|
||
中期:迁移成 一级=业务大类、二级=具体文档类型
|
||
长期:规则绑定只认二级分组,旧结构下线
|
||
```
|
||
|
||
迁移示例:
|
||
|
||
```text
|
||
旧:
|
||
一级:建设工程合同
|
||
二级:通用
|
||
规则绑定:通用 -> 规则集
|
||
|
||
新:
|
||
一级:合同
|
||
二级:建设工程合同
|
||
规则绑定:建设工程合同 -> 原规则集
|
||
```
|
||
|
||
这一段迁移可以参考已有脚本:
|
||
|
||
```text
|
||
scripts/创建sql/migrate_rule_groups_to_business_roots.sql
|
||
scripts/创建sql/precheck_rule_group_migration.sql
|
||
```
|
||
|
||
## 七、后端改造范围
|
||
|
||
### 7.1 后端请求对象
|
||
|
||
文件:
|
||
|
||
```text
|
||
fastapi_modules/fastapi_leaudit/domian/Dto/entryModuleDto.py
|
||
```
|
||
|
||
给 `EntryModuleCreateDTO` 和 `EntryModuleUpdateDTO` 增加:
|
||
|
||
```python
|
||
menu_profile: str | None = Field(None, description="菜单模板:document_review/contract/govdoc/cross_checking/custom")
|
||
features: list[str] | None = Field(None, description="启用功能编码列表")
|
||
```
|
||
|
||
### 7.2 后端返回对象
|
||
|
||
文件:
|
||
|
||
```text
|
||
fastapi_modules/fastapi_leaudit/domian/vo/entryModuleAdminVo.py
|
||
fastapi_modules/fastapi_leaudit/domian/vo/homeVo.py
|
||
```
|
||
|
||
入口模块管理返回对象增加:
|
||
|
||
```python
|
||
menu_profile: str = Field("document_review", description="菜单模板")
|
||
features: list[str] = Field(default_factory=list, description="启用功能编码列表")
|
||
business_scope: EntryModuleBusinessScopeVO = Field(default_factory=EntryModuleBusinessScopeVO, description="业务范围摘要")
|
||
```
|
||
|
||
首页入口返回对象增加:
|
||
|
||
```python
|
||
menuProfile: str = Field("document_review", description="菜单模板")
|
||
features: list[str] = Field(default_factory=list, description="启用功能编码列表")
|
||
tenantCode: str | None = Field(None, description="当前命中的租户编码")
|
||
```
|
||
|
||
### 7.3 入口模块管理服务
|
||
|
||
文件:
|
||
|
||
```text
|
||
fastapi_modules/fastapi_leaudit/services/impl/entryModuleAdminServiceImpl.py
|
||
```
|
||
|
||
要做:
|
||
|
||
1. 确保菜单模板字段和功能清单字段存在。
|
||
2. 创建入口模块时保存菜单模板和功能清单。
|
||
3. 更新入口模块时保存菜单模板和功能清单。
|
||
4. 查询列表和详情时返回这两个字段。
|
||
5. 查询列表和详情时返回业务范围摘要 `business_scope`,用于前端展示已绑定业务大类和业务类型数量。
|
||
6. 校验菜单模板和功能清单只能使用允许的编码。
|
||
7. 继续沿用现有入口模块权限点做接口控制,不要额外硬编码角色名。
|
||
8. 如果当前用户没有对应权限点,后端直接返回 403,不能只靠前端隐藏按钮。
|
||
|
||
允许值:
|
||
|
||
```python
|
||
_ALLOWED_MENU_PROFILES = {"document_review", "contract", "govdoc", "cross_checking", "custom"}
|
||
_ALLOWED_FEATURES = {
|
||
"home",
|
||
"documents",
|
||
"upload",
|
||
"rules",
|
||
"rule_groups",
|
||
"contract_template_search",
|
||
"contract_template_list",
|
||
"govdoc_audits",
|
||
"govdoc_upload",
|
||
"cross_checking",
|
||
"cross_checking_upload",
|
||
"cross_checking_list",
|
||
"usage_stats",
|
||
}
|
||
```
|
||
|
||
### 7.4 首页入口服务
|
||
|
||
文件:
|
||
|
||
```text
|
||
fastapi_modules/fastapi_leaudit/services/impl/homeServiceImpl.py
|
||
```
|
||
|
||
要做:
|
||
|
||
1. 保持现有 `leaudit_entry_module_tenants` 过滤逻辑不变。
|
||
2. 查询入口模块时带出菜单模板和功能清单。
|
||
3. 返回给首页,前端点击入口时存起来。
|
||
4. 返回当前命中的租户编码,方便前端继续传递上下文。
|
||
|
||
### 7.5 文档服务
|
||
|
||
文件:
|
||
|
||
```text
|
||
fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py
|
||
fastapi_modules/fastapi_leaudit/controllers/documentController.py
|
||
```
|
||
|
||
要做:
|
||
|
||
1. 上传接口接收 `entryModuleId`。
|
||
2. 创建文档时写入 `entry_module_id`。
|
||
3. 如果前端没传,则按:
|
||
|
||
```text
|
||
二级分组 entry_module_id -> 一级分组 entry_module_id -> 文档类型 entry_module_id
|
||
```
|
||
|
||
兜底解析。
|
||
|
||
4. 文档列表过滤时使用:
|
||
|
||
```sql
|
||
COALESCE(d.entry_module_id, eg.entry_module_id, eg_parent.entry_module_id, dt.entry_module_id) = :entry_module_id
|
||
```
|
||
|
||
### 7.6 公文服务
|
||
|
||
文件:
|
||
|
||
```text
|
||
fastapi_modules/fastapi_leaudit/controllers/govdocController.py
|
||
fastapi_modules/fastapi_leaudit/services/impl/govdocServiceImpl.py
|
||
```
|
||
|
||
要做:
|
||
|
||
1. `/api/govdoc/documents` 增加 `entry_module_id` 查询参数。
|
||
2. `GovdocServiceImpl.ListDocuments` 增加 `EntryModuleId` 参数。
|
||
3. 查询时按:
|
||
|
||
```sql
|
||
COALESCE(d.entry_module_id, dt.entry_module_id) = :entry_module_id
|
||
```
|
||
|
||
过滤。
|
||
|
||
4. 继续保留现有:
|
||
|
||
```sql
|
||
COALESCE(d.engine_type, 'leaudit') = 'govdoc'
|
||
```
|
||
|
||
也就是说:
|
||
|
||
```text
|
||
公文列表 = 文档引擎类型是公文 + 当前入口模块范围
|
||
```
|
||
|
||
### 7.7 规则分组服务
|
||
|
||
文件:
|
||
|
||
```text
|
||
fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py
|
||
fastapi_modules/fastapi_leaudit/services/impl/ruleConfigServiceImpl.py
|
||
```
|
||
|
||
要做:
|
||
|
||
1. 规则组列表按 `entry_module_id` 过滤。
|
||
2. 过滤表达式统一用:
|
||
|
||
```sql
|
||
COALESCE(g.entry_module_id, parent.entry_module_id, dt.entry_module_id)
|
||
```
|
||
|
||
3. 入口模块只决定“用户看到哪些规则组”,不参与运行时选规则。
|
||
4. 运行时选规则继续保持:
|
||
|
||
```text
|
||
document.group_id -> leaudit_rule_group_bindings -> rule_set/rule_version
|
||
```
|
||
|
||
## 八、前端改造范围
|
||
|
||
### 8.0 前端 UI 风格约束
|
||
|
||
本次前端改造必须和当前系统 UI 设计、样式、配色保持统一,不允许为了新功能另起一套视觉风格。
|
||
|
||
统一要求:
|
||
|
||
```text
|
||
继续使用当前系统已有的 Card、Button、Table、FilterPanel、FilterSelect、SearchFilter、Pagination、Modal 等组件
|
||
继续使用当前页面已有的 CSS 变量和主题色
|
||
继续沿用现有后台管理页的布局、间距、圆角、阴影、表格操作列样式
|
||
继续沿用现有 toast、loading、empty state、403 提示样式
|
||
新增表单项要和当前入口模块编辑页的表单布局一致
|
||
新增功能勾选区要像现有权限/配置类页面一样清晰,不做花哨卡片风格
|
||
```
|
||
|
||
禁止:
|
||
|
||
```text
|
||
不要新增一套独立主题色
|
||
不要引入新的 UI 框架
|
||
不要写和当前后台风格不一致的大面积渐变、特殊字体、复杂动效
|
||
不要把入口模块管理页改成和系统其他管理页完全不同的视觉
|
||
不要为了功能勾选单独设计一套复杂组件
|
||
```
|
||
|
||
具体页面要求:
|
||
|
||
```text
|
||
入口模块列表页:沿用当前表格、筛选、操作按钮风格
|
||
入口模块编辑页:沿用当前表单分组风格,只增加菜单模板和功能勾选
|
||
侧边栏:沿用当前菜单高亮、图标、折叠、权限过滤表现
|
||
规则分组页:沿用当前树/列表/弹窗样式,不重做视觉
|
||
上传页:只补入口模块上下文和二级分组选择,不重做上传区视觉
|
||
公文列表页:只改标题和过滤参数,不重做列表视觉
|
||
```
|
||
|
||
如果必须新增样式,优先放在现有对应页面样式文件里,并复用当前 CSS 变量,例如:
|
||
|
||
```text
|
||
var(--color-primary-text)
|
||
var(--color-primary-text-muted)
|
||
var(--color-border)
|
||
var(--color-surface)
|
||
```
|
||
|
||
### 8.1 入口模块前端接口类型
|
||
|
||
文件:
|
||
|
||
```text
|
||
legal-platform-frontend/lib/api/legacy/entry-modules/entry-modules.ts
|
||
legal-platform-frontend/lib/api/legacy/entry-modules/request-body.ts
|
||
```
|
||
|
||
增加类型:
|
||
|
||
```ts
|
||
export type EntryModuleMenuProfile = "document_review" | "contract" | "govdoc" | "cross_checking" | "custom";
|
||
export type EntryModuleFeature =
|
||
| "home"
|
||
| "documents"
|
||
| "upload"
|
||
| "rules"
|
||
| "rule_groups"
|
||
| "contract_template_search"
|
||
| "contract_template_list"
|
||
| "govdoc_audits"
|
||
| "govdoc_upload"
|
||
| "cross_checking"
|
||
| "cross_checking_upload"
|
||
| "cross_checking_list"
|
||
| "usage_stats";
|
||
```
|
||
|
||
入口模块类型增加:
|
||
|
||
```ts
|
||
menu_profile?: EntryModuleMenuProfile | string;
|
||
features?: EntryModuleFeature[] | string[];
|
||
business_scope?: {
|
||
category_count: number;
|
||
business_type_count: number;
|
||
categories: string[];
|
||
} | null;
|
||
```
|
||
|
||
请求体增加:
|
||
|
||
```ts
|
||
menu_profile?: string | null;
|
||
features?: string[];
|
||
```
|
||
|
||
### 8.2 入口模块编辑页
|
||
|
||
文件:
|
||
|
||
```text
|
||
legal-platform-frontend/app/(audit)/entry-modules/new/EntryModuleNewClient.tsx
|
||
```
|
||
|
||
页面增加两个配置区:
|
||
|
||
1. 菜单模板选择:
|
||
|
||
```text
|
||
通用文档评查
|
||
合同工作台
|
||
内部公文工作台
|
||
自定义工作台
|
||
```
|
||
|
||
2. 功能勾选:
|
||
|
||
```text
|
||
文档列表
|
||
文件上传
|
||
规则配置
|
||
规则分组
|
||
模板搜索
|
||
模板列表
|
||
公文列表
|
||
公文上传
|
||
交叉评查
|
||
创建交叉评查任务
|
||
交叉评查任务列表
|
||
使用统计
|
||
```
|
||
|
||
用户选择菜单模板后,自动带出默认功能,用户可以再调整。
|
||
|
||
权限显示规则:
|
||
|
||
```text
|
||
没有 entry_module:create:write:隐藏新建入口模块按钮
|
||
没有 entry_module:update:write:隐藏或禁用保存修改按钮
|
||
没有 entry_module:delete:delete:隐藏删除按钮
|
||
没有 entry_module:image:write:隐藏或禁用上传图标能力
|
||
```
|
||
|
||
注意:前端隐藏只是体验优化,最终以后端权限点校验为准。
|
||
|
||
### 8.3 首页点击入口
|
||
|
||
文件:
|
||
|
||
```text
|
||
legal-platform-frontend/app/page.tsx
|
||
```
|
||
|
||
点击入口模块时,除了原来的 `selectedModuleId/selectedModuleName`,还要保存:
|
||
|
||
```ts
|
||
{
|
||
id,
|
||
name,
|
||
targetPath,
|
||
menuProfile,
|
||
features,
|
||
tenantCode,
|
||
documentTypeIds,
|
||
iconPath
|
||
}
|
||
```
|
||
|
||
建议统一封装到新文件:
|
||
|
||
```text
|
||
legal-platform-frontend/lib/auth/entry-module-context.ts
|
||
```
|
||
|
||
同时 URL 必须带:
|
||
|
||
```text
|
||
entryModuleId=xxx
|
||
documentTypeIds=1,2,3
|
||
```
|
||
|
||
不能只依赖 sessionStorage,否则刷新和复制链接会丢上下文。
|
||
|
||
### 8.4 侧边栏菜单
|
||
|
||
文件:
|
||
|
||
```text
|
||
legal-platform-frontend/components/layout/Sidebar.tsx
|
||
legal-platform-frontend/lib/auth/entry-module-menu.ts
|
||
```
|
||
|
||
新增 `entry-module-menu.ts`,把功能编码转成菜单:
|
||
|
||
```ts
|
||
documents -> 文档列表 /documents
|
||
upload -> 文件上传 /files/upload
|
||
rules -> 规则配置 /rules-test/list
|
||
rule_groups -> 规则分组 /rule-groups
|
||
contract_template_search -> 模板搜索 /contract-template/search
|
||
contract_template_list -> 模板列表 /contract-template/list
|
||
govdoc_audits -> 公文列表 /govdoc/audits
|
||
govdoc_upload -> 公文上传 /govdoc/upload
|
||
cross_checking -> 交叉评查 /cross-checking
|
||
cross_checking_upload -> 创建任务 /cross-checking/upload
|
||
cross_checking_list -> 评查任务列表 /cross-checking/list
|
||
```
|
||
|
||
侧边栏必须删除这些判断:
|
||
|
||
```ts
|
||
selectedModuleName.includes("合同")
|
||
selectedModuleName.includes("公文")
|
||
shouldUseGovdocAuditMenu(effectiveSelectedModuleName)
|
||
```
|
||
|
||
改成:
|
||
|
||
```text
|
||
读取当前入口模块功能清单
|
||
功能清单决定显示哪些菜单
|
||
角色权限系统再过滤用户没权限的菜单
|
||
```
|
||
|
||
重点:
|
||
|
||
```text
|
||
功能清单不能绕过角色权限系统
|
||
```
|
||
|
||
如果入口模块启用了“模板列表”,但用户角色没有模板列表页面权限,仍然不能显示或访问。
|
||
|
||
入口模块管理菜单本身也要按权限点显示:
|
||
|
||
```text
|
||
有 entry_module:list:read 才显示入口模块管理菜单
|
||
没有 entry_module:list:read 即使知道路径也不能正常加载数据
|
||
```
|
||
|
||
### 8.5 公文列表页面
|
||
|
||
文件:
|
||
|
||
```text
|
||
legal-platform-frontend/components/govdoc-audit/audits.tsx
|
||
legal-platform-frontend/lib/api/govdoc-audit/api.ts
|
||
```
|
||
|
||
要做:
|
||
|
||
1. 从 URL 或入口模块上下文读取 `entryModuleId`。
|
||
2. 调用 `api.listAudits()` 时传入 `entryModuleId`。
|
||
3. `api.listAudits()` 转成后端参数:
|
||
|
||
```text
|
||
entry_module_id=xxx
|
||
```
|
||
|
||
4. 页面标题/侧边栏显示当前入口模块名,不再写死“内部公文”。
|
||
|
||
### 8.6 上传页面
|
||
|
||
文件:
|
||
|
||
```text
|
||
legal-platform-frontend/app/(audit)/files/upload/FilesUploadClient.tsx
|
||
```
|
||
|
||
要做:
|
||
|
||
1. 上传时读取当前入口模块上下文。
|
||
2. 上传表单数据增加:
|
||
|
||
```text
|
||
entryModuleId
|
||
```
|
||
|
||
3. 继续保留:
|
||
|
||
```text
|
||
typeId
|
||
groupId
|
||
tenantCode
|
||
```
|
||
|
||
上传后的文档必须具备完整归属:
|
||
|
||
```text
|
||
entry_module_id + type_id + group_id + tenant_code
|
||
```
|
||
|
||
## 九、SQL 迁移脚本
|
||
|
||
建议新增:
|
||
|
||
```text
|
||
scripts/创建sql/entry_module_menu_profile_migration.sql
|
||
```
|
||
|
||
内容:
|
||
|
||
```sql
|
||
ALTER TABLE leaudit_entry_modules
|
||
ADD COLUMN IF NOT EXISTS menu_profile VARCHAR(64) NOT NULL DEFAULT 'document_review',
|
||
ADD COLUMN IF NOT EXISTS features JSONB NOT NULL DEFAULT '[]'::jsonb;
|
||
|
||
ALTER TABLE leaudit_documents
|
||
ADD COLUMN IF NOT EXISTS entry_module_id BIGINT NULL REFERENCES leaudit_entry_modules(id);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_leaudit_entry_modules_menu_profile
|
||
ON leaudit_entry_modules(menu_profile)
|
||
WHERE deleted_at IS NULL;
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_leaudit_documents_entry_module_id
|
||
ON leaudit_documents(entry_module_id);
|
||
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uq_leaudit_ep_groups_parent_doc_type_active
|
||
ON leaudit_evaluation_point_groups(pid, document_type_id)
|
||
WHERE deleted_at IS NULL
|
||
AND COALESCE(pid, 0) <> 0
|
||
AND document_type_id IS NOT NULL;
|
||
```
|
||
|
||
旧数据回填:
|
||
|
||
```sql
|
||
UPDATE leaudit_entry_modules
|
||
SET
|
||
menu_profile = CASE
|
||
WHEN path IN ('/govdoc/audits', '/govdoc', '/govdoc-audit') THEN 'govdoc'
|
||
WHEN path IN ('/contract-template', '/contract-template/list', '/contract-template/search') THEN 'contract'
|
||
ELSE COALESCE(NULLIF(menu_profile, ''), 'document_review')
|
||
END,
|
||
features = CASE
|
||
WHEN path IN ('/govdoc/audits', '/govdoc', '/govdoc-audit')
|
||
THEN '["home","govdoc_audits","govdoc_upload","rule_groups"]'::jsonb
|
||
WHEN path IN ('/contract-template', '/contract-template/list', '/contract-template/search')
|
||
THEN '["home","documents","upload","rules","contract_template_search","contract_template_list"]'::jsonb
|
||
WHEN features = '[]'::jsonb
|
||
THEN '["home","documents","upload","rules","rule_groups"]'::jsonb
|
||
ELSE features
|
||
END
|
||
WHERE deleted_at IS NULL;
|
||
```
|
||
|
||
文档归属回填:
|
||
|
||
```sql
|
||
UPDATE leaudit_documents d
|
||
SET entry_module_id = COALESCE(g.entry_module_id, parent.entry_module_id, dt.entry_module_id)
|
||
FROM leaudit_document_types dt
|
||
LEFT JOIN leaudit_evaluation_point_groups g ON g.id = d.group_id
|
||
LEFT JOIN leaudit_evaluation_point_groups parent ON parent.id = g.pid
|
||
WHERE d.type_id = dt.id
|
||
AND d.entry_module_id IS NULL;
|
||
```
|
||
|
||
验证脚本:
|
||
|
||
```sql
|
||
SELECT id, name, path, menu_profile, features
|
||
FROM leaudit_entry_modules
|
||
WHERE deleted_at IS NULL
|
||
ORDER BY sort_order, id;
|
||
|
||
SELECT COUNT(*) AS documents_without_entry_module
|
||
FROM leaudit_documents
|
||
WHERE deleted_at IS NULL
|
||
AND entry_module_id IS NULL;
|
||
```
|
||
|
||
## 十、实施顺序
|
||
|
||
### 第一阶段:先修用户能看到的问题
|
||
|
||
目标:
|
||
|
||
```text
|
||
入口模块名字不含合同/公文,也能按配置显示正确菜单
|
||
```
|
||
|
||
任务:
|
||
|
||
1. 后端入口模块请求对象和返回对象增加菜单模板、功能清单。
|
||
2. 入口模块管理页支持菜单模板和功能勾选。
|
||
3. 首页返回入口模块时带菜单模板、功能清单。
|
||
4. 首页点击入口时保存完整上下文。
|
||
5. 侧边栏改成按功能清单生成菜单。
|
||
6. 删除侧边栏按名称包含“合同/公文”的判断。
|
||
|
||
### 第二阶段:列表和上传真正按入口过滤
|
||
|
||
目标:
|
||
|
||
```text
|
||
进入哪个入口模块,就只看这个入口模块下的文档和公文
|
||
```
|
||
|
||
任务:
|
||
|
||
1. 文档上传保存 `entry_module_id`。
|
||
2. 普通文档列表按 `entry_module_id` 过滤。
|
||
3. 公文列表按 `entry_module_id` 过滤。
|
||
4. 公文列表前端传 `entryModuleId`。
|
||
5. 页面刷新后仍能从 URL 恢复入口上下文。
|
||
|
||
### 第三阶段:规则分组范围统一
|
||
|
||
目标:
|
||
|
||
```text
|
||
规则分组页面只显示当前入口模块相关规则组
|
||
```
|
||
|
||
任务:
|
||
|
||
1. 规则分组列表支持 `entry_module_id`。
|
||
2. 前端规则分组页面从 URL/session 读取当前入口。
|
||
3. 规则组创建时继承当前入口模块。
|
||
4. 二级分组绑定具体文档类型。
|
||
5. 规则绑定仍然只挂二级分组。
|
||
6. 上传和评查接口校验 `group_id` 必须是二级分组。
|
||
7. 规则绑定保存接口拒绝一级分组。
|
||
|
||
### 第四阶段:旧逻辑下线
|
||
|
||
目标:
|
||
|
||
```text
|
||
不再靠名字和路径猜业务类型
|
||
```
|
||
|
||
任务:
|
||
|
||
1. 删除 `includes("合同")` 决定合同菜单的逻辑。
|
||
2. 删除 `includes("公文")` 决定公文菜单的逻辑。
|
||
3. 删除 `/govdoc` 强制显示“内部公文”的逻辑。
|
||
4. 保留路径字段只作为跳转地址。
|
||
5. 旧地区字段保留兼容,但新写入只走租户关系。
|
||
|
||
## 十一、验收标准
|
||
|
||
### 11.1 合同入口验收
|
||
|
||
配置:
|
||
|
||
```text
|
||
入口模块名称:入口模块-测试
|
||
menu_profile:contract
|
||
features:contract_template_search, contract_template_list
|
||
租户:云浮
|
||
```
|
||
|
||
预期:
|
||
|
||
```text
|
||
云浮用户首页能看到该入口
|
||
点击后左侧显示模板搜索、模板列表
|
||
入口名字不包含“合同”也正常显示
|
||
其他未绑定租户看不到该入口
|
||
没有角色权限的用户仍然不能访问模板页面
|
||
```
|
||
|
||
### 11.2 公文入口验收
|
||
|
||
配置:
|
||
|
||
```text
|
||
入口模块名称:入口模块-测试
|
||
route_path:/govdoc/audits
|
||
menu_profile:govdoc
|
||
features:govdoc_audits
|
||
租户:梅州
|
||
```
|
||
|
||
预期:
|
||
|
||
```text
|
||
梅州用户首页能看到该入口
|
||
点击进入 /govdoc/audits
|
||
页面显示入口模块-测试,而不是固定显示内部公文
|
||
公文列表请求带 entry_module_id
|
||
列表只显示该入口模块范围内的公文
|
||
```
|
||
|
||
### 11.3 文档列表验收
|
||
|
||
配置:
|
||
|
||
```text
|
||
入口 A 绑定文档类型 A
|
||
入口 B 绑定文档类型 B
|
||
同一个租户下分别上传文档
|
||
```
|
||
|
||
预期:
|
||
|
||
```text
|
||
从入口 A 进入只看到 A 的文档
|
||
从入口 B 进入只看到 B 的文档
|
||
刷新页面后过滤条件不丢
|
||
```
|
||
|
||
### 11.3.1 交叉评查入口验收
|
||
|
||
配置:
|
||
|
||
```text
|
||
入口模块名称:入口模块-测试交叉评查
|
||
route_path:/cross-checking
|
||
menu_profile:cross_checking
|
||
features:cross_checking, cross_checking_upload, cross_checking_list
|
||
租户:目标租户或 PUBLIC
|
||
```
|
||
|
||
预期:
|
||
|
||
```text
|
||
目标租户首页能看到该入口
|
||
点击进入 /cross-checking
|
||
侧边栏显示交叉评查、创建任务、评查任务列表
|
||
没有交叉评查路由权限的用户即使入口模块启用交叉评查功能,也不能看到或访问交叉评查菜单
|
||
```
|
||
|
||
### 11.4 多租户验收
|
||
|
||
配置:
|
||
|
||
```text
|
||
入口模块 X 只绑定云浮
|
||
入口模块 Y 只绑定梅州
|
||
入口模块 Z 绑定 PUBLIC
|
||
```
|
||
|
||
预期:
|
||
|
||
```text
|
||
云浮用户看到 X 和 Z
|
||
梅州用户看到 Y 和 Z
|
||
普通用户看不到其他租户入口
|
||
超级管理员可以跨租户管理
|
||
角色权限仍然控制页面和接口能不能访问
|
||
```
|
||
|
||
### 11.5 规则分组和规则绑定验收
|
||
|
||
配置:
|
||
|
||
```text
|
||
入口模块:合同评查
|
||
一级分组:合同
|
||
二级分组:建设工程合同
|
||
规则绑定:建设工程合同 -> 建设工程合同规则集
|
||
租户:云浮
|
||
```
|
||
|
||
预期:
|
||
|
||
```text
|
||
从合同评查入口进入,只看到合同一级分组
|
||
建设工程合同显示在合同一级分组下面
|
||
规则绑定只能在建设工程合同这个二级分组上配置
|
||
一级分组合同不能直接绑定规则集
|
||
上传建设工程合同后,文档 group_id 等于建设工程合同二级分组 id
|
||
评查时按 group_id + tenant_code 命中云浮对应规则集
|
||
```
|
||
|
||
### 11.6 入口模块配置权限验收
|
||
|
||
配置:
|
||
|
||
```text
|
||
角色 A 分配 entry_module:list:read
|
||
角色 A 不分配 entry_module:create:write / entry_module:update:write / entry_module:delete:delete
|
||
|
||
角色 B 分配 entry_module:list:read / entry_module:create:write / entry_module:update:write
|
||
角色 B 不分配 entry_module:delete:delete
|
||
```
|
||
|
||
预期:
|
||
|
||
```text
|
||
角色 A 可以查看入口模块列表
|
||
角色 A 看不到新建、编辑保存、删除入口模块能力
|
||
角色 A 直接调用创建/编辑/删除接口返回 403
|
||
|
||
角色 B 可以查看入口模块列表
|
||
角色 B 可以创建和编辑入口模块
|
||
角色 B 不能删除入口模块
|
||
角色 B 直接调用删除接口返回 403
|
||
```
|
||
|
||
关键验收点:
|
||
|
||
```text
|
||
系统不通过 role_key 硬编码判断谁能配置入口模块
|
||
入口模块配置权限完全由 RBAC 权限点分配决定
|
||
入口模块租户关系只控制可见和使用,不控制谁能配置
|
||
```
|
||
|
||
## 十二、非目标
|
||
|
||
这次重构不做:
|
||
|
||
- 不删除角色权限系统。
|
||
- 不删除 `leaudit_rule_type_bindings`。
|
||
- 不改变“文档二级分组 -> 规则绑定 -> 规则集版本”的运行时主链路。
|
||
- 不把入口模块变成权限系统。
|
||
- 不第一阶段支持“同一入口不同租户不同菜单”。
|
||
- 不硬编码某个角色名才能配置入口模块,入口模块管理能力由权限点分配决定。
|
||
|
||
运行时规则仍然保持:
|
||
|
||
```text
|
||
document.group_id -> leaudit_rule_group_bindings -> rule_set/rule_version
|
||
```
|
||
|
||
入口模块只负责:
|
||
|
||
```text
|
||
用户从哪里进来
|
||
左侧显示哪些菜单
|
||
文档/规则/上传属于哪个业务范围
|
||
```
|
||
|
||
## 十三、风险控制
|
||
|
||
1. `menu_profile/features` 设置默认值,保证老入口模块不崩。
|
||
2. 旧地区字段暂时保留兼容,避免历史数据立即失效。
|
||
3. 功能清单生成菜单后还要经过角色权限过滤,不能绕过权限。
|
||
4. 浏览器地址参数必须带 `entryModuleId`,不能只靠浏览器临时缓存。
|
||
5. 文档上传必须保存 `entry_module_id/type_id/group_id/tenant_code`,后续列表和统计才稳定。
|
||
6. 第一阶段先解决菜单和入口体验,规则运行链路不乱动。
|
||
7. 一级分组只做业务大类,二级分组才做运行类型,规则绑定只能挂二级分组。
|
||
8. 入口模块管理不能靠前端隐藏按钮保证安全,后端必须按权限点返回 403。
|
||
|
||
## 十四、Superpowers 执行任务清单
|
||
|
||
本节是给后续开发直接执行的任务清单。执行时必须从上到下推进,每完成一个小任务就验证一次,不要一次性大改完再排错。
|
||
|
||
### 14.1 执行原则
|
||
|
||
- [x] 先做数据库和后端返回字段,再做前端展示。
|
||
- [x] 先解决入口模块菜单显示错误,再处理列表和上传过滤。
|
||
- [x] 不改运行时评查主链路,只补入口模块、分组、文档归属。
|
||
- [x] 不硬编码角色名判断入口模块管理权限,只使用 RBAC 权限点。
|
||
- [x] 每一步都保留旧数据兼容,避免老入口模块、老文档、旧规则分组直接失效。
|
||
|
||
### 14.2 数据库任务
|
||
|
||
- [x] 新增迁移脚本 `scripts/创建sql/entry_module_menu_profile_migration.sql`。
|
||
- [x] 给 `leaudit_entry_modules` 增加 `menu_profile` 字段。
|
||
- [x] 给 `leaudit_entry_modules` 增加 `features` 字段。
|
||
- [x] 给 `leaudit_documents` 增加 `entry_module_id` 字段。
|
||
- [x] 给 `leaudit_documents.entry_module_id` 增加索引。
|
||
- [x] 给 `leaudit_entry_modules.menu_profile` 增加索引。
|
||
- [x] 给 `leaudit_evaluation_point_groups(pid, document_type_id)` 增加二级分组唯一索引。
|
||
- [x] 回填老入口模块的菜单模板和功能清单。
|
||
- [x] 尝试按二级分组、一级分组、文档类型回填历史文档的 `entry_module_id`。
|
||
- [x] 写验证 SQL,检查入口模块字段、文档归属、重复二级分组。
|
||
|
||
### 14.3 后端入口模块任务
|
||
|
||
- [x] 修改 `fastapi_modules/fastapi_leaudit/domian/Dto/entryModuleDto.py`,创建和更新请求增加 `menu_profile/features`。
|
||
- [x] 修改 `fastapi_modules/fastapi_leaudit/domian/vo/entryModuleAdminVo.py`,入口模块管理返回增加 `menu_profile/features`。
|
||
- [x] 修改 `entryModuleAdminVo.py` 和 `entryModuleAdminServiceImpl.py`,入口模块列表和详情返回 `business_scope` 业务范围摘要。
|
||
- [x] 修改 `fastapi_modules/fastapi_leaudit/domian/vo/homeVo.py`,首页入口返回增加 `menuProfile/features/tenantCode`。
|
||
- [x] 修改 `fastapi_modules/fastapi_leaudit/services/impl/entryModuleAdminServiceImpl.py`,创建入口模块时保存菜单模板和功能清单。
|
||
- [x] 修改 `entryModuleAdminServiceImpl.py`,更新入口模块时保存菜单模板和功能清单。
|
||
- [x] 修改 `entryModuleAdminServiceImpl.py`,列表和详情查询返回菜单模板和功能清单。
|
||
- [x] 修改 `entryModuleAdminServiceImpl.py`,列表和详情查询返回已绑定业务大类数量、业务类型数量和大类名称列表。
|
||
- [x] 在 `entryModuleAdminServiceImpl.py` 增加菜单模板、功能编码白名单校验。
|
||
- [x] 保留 `entryModuleController.py` 现有权限点校验,不新增角色名硬编码。
|
||
- [x] 确认没有权限点时,创建、编辑、删除、上传图标接口都返回 403。
|
||
|
||
### 14.4 后端首页和入口上下文任务
|
||
|
||
- [x] 修改 `fastapi_modules/fastapi_leaudit/services/impl/homeServiceImpl.py`,首页入口查询带出 `menu_profile/features`。
|
||
- [x] 首页入口仍按 `leaudit_entry_module_tenants` 过滤当前租户可见入口。
|
||
- [x] 首页入口返回当前命中的租户编码。
|
||
- [x] 首页入口返回文档类型范围,方便前端拼接上下文。
|
||
- [x] 保持 `PUBLIC` 入口模块兼容。
|
||
- [x] 保持老地区字段兼容,但新链路以租户关系为准。
|
||
|
||
### 14.5 后端文档和公文任务
|
||
|
||
- [x] 修改文档上传接口,接收 `entryModuleId`。
|
||
- [x] 修改 `fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py`,创建文档时写入 `entry_module_id`。
|
||
- [x] 上传时校验 `group_id` 是二级分组。
|
||
- [x] 上传时校验 `group_id` 所属入口模块与 `entryModuleId` 一致。
|
||
- [x] 当前端没传 `group_id` 时,只允许在唯一可用二级分组场景下兜底推断。
|
||
- [x] 普通文档列表支持按 `entry_module_id` 过滤。
|
||
- [x] 修改 `fastapi_modules/fastapi_leaudit/controllers/govdocController.py`,公文列表接收 `entry_module_id`。
|
||
- [x] 修改 `fastapi_modules/fastapi_leaudit/services/impl/govdocServiceImpl.py`,公文列表按 `entry_module_id` 过滤。
|
||
- [x] 公文列表继续保留公文引擎类型过滤。
|
||
|
||
### 14.6 后端规则分组和规则绑定任务
|
||
|
||
- [x] 修改 `fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py`,规则分组列表支持按入口模块过滤。
|
||
- [ ] 一级分组创建时必须有 `entry_module_id`。
|
||
- [x] 一级分组不允许绑定规则集。
|
||
- [x] 二级分组创建时必须有 `document_type_id`。
|
||
- [x] 二级分组必须挂在当前入口模块下的一级分组下面。
|
||
- [x] 规则绑定保存接口只允许传二级分组 `group_id`。
|
||
- [x] 规则绑定保存接口拒绝一级分组。
|
||
- [x] 规则绑定仍保存到 `leaudit_rule_group_bindings`。
|
||
- [x] 评查运行时继续按 `document.group_id -> leaudit_rule_group_bindings -> rule_set` 命中规则。
|
||
- [x] 不把 `leaudit_rule_type_bindings` 重新作为新主链路。
|
||
|
||
### 14.7 前端入口模块管理任务
|
||
|
||
- [x] 所有新增 UI 必须沿用当前系统组件、CSS 变量、表格、表单、弹窗、按钮风格,不另起视觉体系。
|
||
- [x] 修改 `legal-platform-frontend/lib/api/legacy/entry-modules/entry-modules.ts`,增加菜单模板和功能清单类型。
|
||
- [x] 修改 `legal-platform-frontend/lib/api/legacy/entry-modules/entry-modules.ts`,入口模块类型增加 `business_scope` 摘要字段。
|
||
- [x] 修改 `legal-platform-frontend/lib/api/legacy/entry-modules/request-body.ts`,请求体支持菜单模板和功能清单。
|
||
- [x] 修改 `legal-platform-frontend/app/(audit)/entry-modules/EntryModulesClient.tsx`,列表展示菜单模板和功能摘要。
|
||
- [x] 修改 `EntryModulesClient.tsx`,业务范围列按 `business_scope` 展示真实大类和业务类型数量,不再写死“待配置”。
|
||
- [x] 修改 `legal-platform-frontend/app/(audit)/entry-modules/new/EntryModuleNewClient.tsx`,增加菜单模板选择。
|
||
- [x] 修改 `EntryModuleNewClient.tsx`,增加功能勾选区域。
|
||
- [x] 选择菜单模板时自动带出默认功能。
|
||
- [x] 新建按钮按 `entry_module:create:write` 显示。
|
||
- [x] 保存按钮按 `entry_module:update:write` 显示或禁用。
|
||
- [x] 删除按钮按 `entry_module:delete:delete` 显示。
|
||
- [x] 图标上传按 `entry_module:image:write` 显示或禁用。
|
||
- [x] 前端只做体验控制,后端权限点校验必须保留。
|
||
|
||
### 14.8 前端首页和侧边栏任务
|
||
|
||
- [x] 修改 `legal-platform-frontend/app/page.tsx`,点击入口模块时保存完整入口上下文。
|
||
- [x] 新增 `legal-platform-frontend/lib/auth/entry-module-context.ts`,统一读写入口模块上下文。
|
||
- [x] 入口跳转 URL 带 `entryModuleId`。
|
||
- [x] 入口跳转 URL 带 `documentTypeIds`。
|
||
- [x] 修改 `legal-platform-frontend/lib/auth/entry-module-menu.ts`,把功能编码转换为菜单项。
|
||
- [x] 入口模块支持 `cross_checking` 菜单模板和交叉评查功能编码。
|
||
- [x] 修改 `legal-platform-frontend/components/layout/Sidebar.tsx`,侧边栏按入口模块功能清单生成菜单。
|
||
- [x] 删除 `selectedModuleName.includes("合同")`。
|
||
- [x] 删除 `selectedModuleName.includes("公文")`。
|
||
- [x] 删除按 `/govdoc` 路径强行判断公文菜单的逻辑。
|
||
- [x] 侧边栏生成菜单后继续经过用户页面权限过滤。
|
||
- [x] 有 `entry_module:list:read` 才显示入口模块管理菜单。
|
||
|
||
### 14.9 前端公文、上传、规则分组任务
|
||
|
||
- [x] 公文、上传、规则分组页面只补业务上下文和过滤逻辑,不重做页面视觉。
|
||
- [x] 修改 `legal-platform-frontend/components/govdoc-audit/audits.tsx`,从 URL 或入口上下文读取 `entryModuleId`。
|
||
- [x] 修改 `legal-platform-frontend/lib/api/govdoc-audit/api.ts`,请求参数带 `entry_module_id`。
|
||
- [x] 公文页面标题使用当前入口模块名称,不再写死“内部公文”。
|
||
- [x] 修改 `legal-platform-frontend/app/(audit)/files/upload/FilesUploadClient.tsx`,上传表单带 `entryModuleId`。
|
||
- [x] 上传页选择二级分组后传 `groupId`。
|
||
- [x] 上传页在存在多个二级分组时必须让用户明确选择。
|
||
- [x] 修改 `legal-platform-frontend/app/(audit)/rule-groups/RuleGroupsClient.tsx`,规则分组页读取当前入口模块。
|
||
- [x] 规则分组页只展示当前入口模块下的一级分组。
|
||
- [x] 规则绑定入口只出现在二级分组上。
|
||
|
||
### 14.10 测试和验收任务
|
||
|
||
- [x] 验证入口模块名称不包含“合同”,但配置合同功能后侧边栏仍显示模板搜索和模板列表。
|
||
- [x] 验证入口模块名称不包含“公文”,但配置公文功能后仍进入公文列表。
|
||
- [x] 验证没有模板页面权限时,即使入口模块启用模板功能,侧边栏也不显示模板菜单。
|
||
- [x] 验证云浮租户只能看到分配给云浮或 `PUBLIC` 的入口模块。
|
||
- [x] 验证梅州租户只能看到分配给梅州或 `PUBLIC` 的入口模块。
|
||
- [x] 验证没有 `entry_module:create:write` 的用户不能创建入口模块。
|
||
- [x] 验证没有 `entry_module:update:write` 的用户不能编辑入口模块。
|
||
- [x] 验证没有 `entry_module:delete:delete` 的用户不能删除入口模块。
|
||
- [x] 验证普通文档列表按入口模块隔离。
|
||
- [x] 验证公文列表按入口模块隔离。
|
||
- [x] 验证上传文档写入 `entry_module_id/type_id/group_id/tenant_code`。
|
||
- [x] 验证规则绑定不能挂一级分组。
|
||
- [x] 验证规则绑定能挂二级分组。
|
||
- [x] 验证入口模块列表业务范围列展示真实绑定摘要,不再全部显示“待配置”。
|
||
- [x] 验证评查按文档二级分组命中正确规则集。
|
||
|
||
### 14.10.1 执行记录 2026-05-23
|
||
|
||
- [x] 使用 Playwright 真实账号 `000/admin06111` 登录验证首页入口和侧边栏。
|
||
- [x] 验证点击 `合同评查` 后进入 `/documents/list?entryModuleId=1&documentTypeIds=...`。
|
||
- [x] 验证侧边栏顺序为:首页、文件上传、文档列表、规则管理。
|
||
- [x] 验证文档列表菜单不重复。
|
||
- [x] 验证上传文档按钮可见。
|
||
- [x] 使用 Playwright 跑优先项 `2/3/4/5`:`node /tmp/leaudit-playwright-priority-2-5-v3.js`,结果 `25 pass / 0 fail`。
|
||
- [x] 修复二级分组绑定规则集 500:`evaluationPointGroupServiceImpl._get_binding_row` SQL 中 `{access_filter}` 未注入。
|
||
- [x] 回归验证:二级分组绑定规则集返回 200。
|
||
- [x] 回归验证:`pytest -q tests/test_rule_group_binding_scope.py` 结果 `4 passed`。
|
||
- [x] 回归验证:`legal-platform-frontend` 下 `npx tsc --noEmit --pretty false` 通过。
|
||
- [x] 回归验证:目标 eslint 结果 `0 error / 6 warnings`,warning 为既有 `<img>`、未使用变量类提示。
|
||
- [x] 使用 `./leaudit.sh status` 确认后端、前端、Worker、Beat 均运行。
|
||
- [x] 修复公文入口侧边栏仍使用旧“系统概览/文件管理”菜单的问题,统一为入口模块菜单:首页、公文列表、公文上传、规则分组。
|
||
- [x] 修复公文入口侧边栏混入普通“文件上传”的问题,避免和“公文上传”重复或语义冲突。
|
||
- [x] 使用 Playwright 真实账号 `000/admin06111` 跑页面入口 smoke:`node /tmp/leaudit-playwright-page-entry-suite.js`,结果 `31 pass / 0 fail`。
|
||
- [x] 页面入口 smoke 覆盖:合同入口名称不含“合同”仍显示模板搜索/模板列表;公文入口名称不含“公文”仍进入 `/govdoc/audits`;自定义入口只显示首页/文件上传/文档列表;三类入口刷新后不丢上下文。
|
||
- [x] 使用 Playwright 跑优先项 `2/3/4/5`:`node /tmp/leaudit-playwright-priority-2-5-v3.js`,结果 `25 pass / 0 fail`。
|
||
- [x] 优先项覆盖:上传写入 `entryModuleId/typeId/groupId/tenantCode`、入口 A/B 文档列表隔离、规则分组按入口过滤、一级分组拒绝绑定、二级分组可绑定、无入口管理权限接口返回 403。
|
||
- [x] 补充交叉评查入口模块配置能力:入口模块路由下拉支持 `/cross-checking`,菜单模板支持 `cross_checking`,功能清单支持 `cross_checking/cross_checking_upload/cross_checking_list`。
|
||
- [x] 修复首页交叉评查入口仍走旧“写死交叉评查卡片”的问题,改为按真实入口模块渲染,入口名称、入口 ID、功能清单全部来自 `leaudit_entry_modules`。
|
||
- [x] 修复 `/cross-checking` 入口点击不带入口上下文的问题,跳转统一进入 `/cross-checking/list?entryModuleId=...`。
|
||
- [x] 修复交叉评查入口侧边栏混入普通“文件上传”的问题,只显示 `创建任务/评查任务列表` 等交叉评查功能菜单。
|
||
- [x] 使用 Playwright 真实账号 `000/admin06111` 验证交叉评查入口模块:`node /tmp/leaudit-playwright-cross-entry.js`,结果 `8 pass / 0 fail`。
|
||
- [x] 新增数据库只读巡检脚本:`scripts/创建sql/verify_entry_module_menu_profile.sql`,覆盖字段、索引、非法菜单配置、文档入口归属、重复二级分组、未绑定入口模块一级分组。
|
||
- [x] 补充公文列表标题逻辑:有入口模块上下文时显示当前入口模块名称,例如 `入口模块-测试文档列表`;无上下文时才兜底 `内部公文文档列表`。
|
||
- [x] 补充入口模块管理菜单权限过滤:`/entry-modules` 设置子菜单必须带 `entry_module:list:read` 才显示。
|
||
- [x] 回归验证:`node --test --experimental-strip-types tests/govdoc-audit/govdoc-entry-title.test.mts` 结果 `2 pass / 0 fail`。
|
||
- [x] 回归验证:`node --test --experimental-strip-types tests/govdoc-audit/settings-menu-permission.test.mts` 结果 `1 pass / 0 fail`。
|
||
- [x] 修复 `Sidebar` 中无入口上下文时按 `/govdoc` 路径强行套公文菜单的问题;公文菜单只由入口模块 `menuProfile/features` 决定。
|
||
- [x] 使用 Playwright 真实账号 `000/admin06111` 跑剩余项验收:`node test-results/leaudit-playwright-entry-module-remaining-acceptance.js`,结果 `11 pass / 0 fail`。
|
||
- [x] 剩余项验收覆盖:名字不含“公文”的 govdoc 入口可进入;公文列表标题使用当前入口名称;侧栏只显示 `公文列表/公文上传/规则分组`;无 `entry_module:image:write` 的用户上传入口模块图标返回 403。
|
||
|
||
### 14.10.2 下一步验收和开发安排
|
||
|
||
- [x] Playwright 页面级验收入口模块管理页:新建入口、编辑入口、功能勾选保存、功能回显、租户回显。
|
||
- [x] Playwright 页面级验收上传页:文件上传菜单排第二,真实表单选择文档类型和二级分组后提交。
|
||
- [x] Playwright 页面级验收规则分组页:入口 A/B 切换后页面树隔离,一级分组不出现绑定入口,二级分组绑定弹窗可用。
|
||
- [x] Playwright 页面级验收公文入口:入口名称不含“公文”但配置 `govdoc_audits/govdoc_upload` 后,公文列表和上传可进入。
|
||
- [x] Playwright 页面级验收公文详情页:确认不出现平台 Sidebar + 公文工作区 Sidebar 双侧栏。
|
||
- [x] 多租户真实账号验收:云浮、揭阳、梅州分别只看到已分配或 `PUBLIC` 入口模块。
|
||
- [x] 多租户权限验收:没有入口模块管理权限的租户用户不能在 UI 和接口层创建、编辑、删除入口模块。
|
||
- [x] 评查命中验收:上传带二级分组的文档后,启动评查,确认命中该二级分组绑定规则集。
|
||
|
||
### 14.10.3 当前最新状态 2026-05-23
|
||
|
||
当前入口模块主链路已经完成:
|
||
|
||
- [x] 入口模块不再靠名称包含“合同/公文”决定菜单。
|
||
- [x] 入口模块支持 `document_review/contract/govdoc/cross_checking/custom` 菜单模板。
|
||
- [x] 入口模块支持 `features` 控制首页、上传、文档列表、规则、规则分组、模板、公文、交叉评查菜单。
|
||
- [x] 首页点击入口后会保存完整入口上下文,并在 URL 带 `entryModuleId/documentTypeIds`。
|
||
- [x] 侧边栏菜单由入口模块 `menuProfile/features` 生成,再经过用户页面权限过滤。
|
||
- [x] 普通文档上传和列表已经接入 `entry_module_id/type_id/group_id/tenant_code`。
|
||
- [x] 公文列表接口已经支持 `entry_module_id`,页面标题已经改为当前入口模块名称。
|
||
- [x] 规则分组列表已经支持入口模块过滤,规则绑定只允许挂二级分组。
|
||
- [x] 入口模块管理权限不硬编码角色名,创建、编辑、删除、上传图标走 RBAC 权限点。
|
||
- [x] 数据库迁移脚本和只读验证 SQL 已补齐。
|
||
|
||
当前仍然需要验收或收口:
|
||
|
||
- [x] 模板页面权限过滤:入口模块启用模板功能,但用户没有模板页面权限时,侧边栏不能显示模板菜单。
|
||
- [x] 多租户真实账号验收:云浮、揭阳、梅州只能看到自己租户或 `PUBLIC` 入口。
|
||
- [x] 公文列表数据隔离:同一租户下两个公文入口分别进入时,列表数据不能串入口。
|
||
- [x] 公文详情页侧栏:确认不出现平台 Sidebar 和公文工作区 Sidebar 双侧栏。
|
||
- [x] 入口模块管理页真实表单验收:新建、编辑、功能勾选、租户回显必须跑 Playwright。
|
||
- [x] 上传页真实表单验收:从入口进入后,选择文档类型和二级分组提交,后端落库字段正确。
|
||
- [x] 规则分组页真实页面验收:入口 A/B 切换后树隔离,一级分组无绑定入口,二级分组绑定弹窗可用。
|
||
- [x] 评查命中验收:上传带二级分组的文档后启动评查,确认按该二级分组命中规则集。
|
||
|
||
当前暂缓硬改:
|
||
|
||
- [ ] `一级分组创建时必须有 entry_module_id` 仍保留兼容口径,避免旧全局分组或历史数据立即断链。下一轮只先补告警和数据巡检,不直接硬拦。
|
||
|
||
### 14.10.4 执行记录 2026-05-24
|
||
|
||
- [x] 清理 Playwright 和手工验收产生的测试入口模块、测试业务大类、测试规则分组。
|
||
- [x] 测试数据清理采用软删除,未硬删历史文档,避免破坏审计记录和外键引用。
|
||
- [x] 已软删除测试入口模块 31 个:`37,39-68`。
|
||
- [x] 已软删除测试业务大类 24 个:`24-47`。
|
||
- [x] 已软删除测试规则分组 71 个:`52-122`。
|
||
- [x] 已软删除入口租户关联 34 条、规则分组绑定 3 条。
|
||
- [x] 反查验证:未删除测试入口模块 0 个、未删除测试业务大类 0 个、未删除测试规则分组 0 个、测试入口有效租户关联残留 0 条、测试分组有效规则绑定残留 0 条。
|
||
- [x] 修复入口模块列表“业务范围”列全部显示“待配置”的问题。
|
||
- [x] 根因确认:数据库中合同评查、案卷智能评查、内部公文已有业务范围绑定,但 `EntryModulesClient.tsx` 把业务范围列写死为“待配置”,后端入口模块列表也没有返回业务范围摘要。
|
||
- [x] 后端 `EntryModuleVO` 增加 `business_scope`,入口模块列表和详情返回业务大类数量、业务类型数量、大类名称列表。
|
||
- [x] `entryModuleAdminServiceImpl.py` 的 `business_scope` 查询兼容两种历史结构:通过 `leaudit_document_types.entry_module_id` 绑定业务大类,以及通过一级规则分组 `entry_module_id` 统计二级业务类型。
|
||
- [x] 前端 `EntryModule` 类型增加 `business_scope`。
|
||
- [x] 前端新增 `summarizeBusinessEntryScope()`,入口模块列表按真实业务范围展示,例如“建设工程合同、买卖合同、借款合同 等 10 类 / 覆盖 10 个业务类型”。
|
||
- [x] 真实数据库只读验证:合同评查 `10` 个业务大类、`10` 个业务类型;案卷智能评查 `10` 个业务大类、`10` 个业务类型;内部公文 `1` 个业务大类、`1` 个业务类型;智慧法务助手和交叉评查当前未配置业务范围,显示“待配置”符合数据事实。
|
||
- [x] 回归验证:`node --test --experimental-strip-types tests/govdoc-audit/business-entry-ui.test.mts` 结果 `7 pass / 0 fail`。
|
||
- [x] 回归验证:`legal-platform-frontend` 下 `npx tsc --noEmit --pretty false` 通过。
|
||
- [x] 回归验证:`.venv/bin/python -m py_compile fastapi_modules/fastapi_leaudit/domian/vo/entryModuleAdminVo.py fastapi_modules/fastapi_leaudit/services/impl/entryModuleAdminServiceImpl.py` 通过。
|
||
- [x] 回归验证:目标 eslint 通过,`0 error / 0 warning`。
|
||
- [x] RBAC 数据调整:删除 `省级管理员 / provincial_admin` 角色。
|
||
- [x] RBAC 删除级联清理:`user_role` 1 条、`role_permissions` 94 条、`role_route` 29 条。
|
||
- [x] 将 `000` 对应用户调整为系统超级管理员:当前库中该用户为 `sso_users.id=5, sub=000, username=admin`。
|
||
- [x] RBAC 反查验证:`provincial_admin` 剩余 0 个;`000` 用户当前只绑定 `super_admin / 系统超级管理员 / data_scope=ALL`。
|
||
- [x] 使用 Playwright 真实账号 `000/admin06111` 验收入口模块编辑页和上传页:`LEAUDIT_FRONTEND_URL=http://127.0.0.1:5193 node /tmp/leaudit-playwright-entry-upload-management-v1.js`,结果 `16 pass / 0 fail`。
|
||
- [x] 入口模块编辑页验收覆盖:4 步表单、菜单模板回显、功能勾选回显、PUBLIC 租户回显、业务范围回显。
|
||
- [x] 上传页验收覆盖:从入口上下文进入后按入口加载业务大类和业务类型,显示所属业务入口,上传接口和文档详情接口返回正确 `entryModuleId/typeId/groupId/tenantCode`。
|
||
- [x] 修复上传页单一业务类型时查不到二级分组的问题:`FilesUploadClient.tsx` 不再只在 `childDocumentTypeIds.length > 1` 时批量查询二级分组。
|
||
- [x] 后端文档上传、列表、详情 VO 返回 `entryModuleId`,方便前端和测试确认文档入口归属。
|
||
- [x] 修复公文详情页双侧栏风险:`/govdoc/detail/...` 和 `/govdoc-audit/detail/...` 不再渲染平台 Sidebar,只保留公文工作区布局。
|
||
- [x] 回归验证:`.venv/bin/python -m py_compile fastapi_modules/fastapi_leaudit/domian/vo/documentVo.py fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py` 通过。
|
||
- [x] 回归验证:`legal-platform-frontend` 下 `npx tsc --noEmit --pretty false` 通过。
|
||
- [x] 回归验证:目标 eslint 通过,`0 error / 1 warning`,warning 为 `files-upload.ts` 被 eslint ignore,非本次阻塞。
|
||
- [x] 本轮 Playwright 临时数据已清理:`PW入口编辑上传-pwui%`、`pw.root.pwui%`、`pw.child.pwui%`、`pw.type.pwui%`、`pw-ui-upload-pwui%` 有效残留均为 0。
|
||
- [x] 环境记录:`./leaudit.sh` 本轮出现 pidfile/后台进程状态不稳定,真实验收改用 TTY 手动启动后端 `run.py` 和前端 `npm run dev:dev`,Playwright 统一访问 `http://127.0.0.1:5193`。
|
||
- [x] 使用 Playwright 真实账号 `000/admin06111` 验收规则分组页:`LEAUDIT_FRONTEND_URL=http://127.0.0.1:5193 node /tmp/leaudit-playwright-rule-groups-scope-v1.js`,结果 `21 pass / 0 fail`。
|
||
- [x] 规则分组页验收覆盖:入口 A/B 树隔离、请求带 `entry_module_id`、一级分组不显示“配置规则”、后端拒绝一级分组绑定规则集、二级分组绑定弹窗可打开并保存。
|
||
- [x] 规则分组页验收发现一个非产品 BUG:临时脚本第一次按旧弹窗类名 `.modal-content/[role=dialog]` 断言失败;当前页面实际使用 `.rg-modal`,修正脚本后通过。
|
||
- [x] 规则分组 Playwright 临时数据已清理:`PW规则分组入口%`、`pw.rg.type.%`、`pw.rg.%`、`note like '%pwrg%'` 有效残留均为 0。
|
||
- [x] 使用 Playwright 真实账号 `000/admin06111` 验收公文入口深度链路:`LEAUDIT_FRONTEND_URL=http://127.0.0.1:5193 node /tmp/leaudit-playwright-govdoc-entry-scope-v1.js`,结果 `17 pass / 0 fail`。
|
||
- [x] 公文入口深度验收覆盖:创建 A/B 公文入口、创建 A/B 公文业务类型、A/B 公文列表请求带 `entry_module_id`、A/B 列表数据不串、标题使用当前入口模块名称、公文上传链接携带 `entryModuleId/documentTypeIds`、公文上传落库携带 `entry_module_id/type_id`、公文详情页不渲染平台 Sidebar。
|
||
- [x] 修正公文 Playwright 验收脚本:不再用 `request.postData()` 判断 multipart 表单,因为该方式对文件上传体不稳定;改为上传后解析新 `documentId` 并查询数据库验证 `leaudit_documents.entry_module_id/type_id`。
|
||
- [x] 公文 Playwright 临时数据已清理:`PW公文入口%`、`pw.govdoc.type.%`、`pw-govdoc-%` 有效文档残留均为 0。
|
||
- [x] 发现并记录一个非阻塞清理差异:公文删除接口会软删 `leaudit_documents`,但 `leaudit_document_files` 中历史测试文件记录仍保持 `deleted_at is null`;当前列表以文档主表删除态过滤,不影响入口隔离验收,后续如要严格清理可单独优化删除服务。
|
||
- [x] 使用 Playwright 真实账号验收多租户入口可见性和普通权限:`LEAUDIT_FRONTEND_URL=http://127.0.0.1:5193 node /tmp/leaudit-playwright-multitenant-entry-scope-v1.js`,结果 `37 pass / 0 fail`。
|
||
- [x] 多租户验收账号:`yf001/yfyc06111`、`jy001/jyyc06111`、`mz001/mzyc06111`、`001/gdyc06111`。
|
||
- [x] 多租户验收覆盖:云浮只看 `合同评查/案卷智能评查/内部公文`;揭阳只看 `合同评查/案卷智能评查/内部公文/交叉评查`;梅州地区管理员看 `合同评查/案卷智能评查/内部公文/智慧法务助手/交叉评查`;普通用户 `001` 看不到 `智慧法务助手`。
|
||
- [x] 权限验收覆盖:普通用户 `001` 首页不显示系统设置按钮,直调 `/api/v3/entry-modules` 返回 403。
|
||
- [x] 使用 Playwright APIRequestContext + 数据库只读查询验收评查命中链路:真实账号 `mz001/mzyc06111` 上传 `legal-platform-frontend/public/testWork/(最终版)智慧法务平台建设采购项目合同(1).docx`,传入 `typeId=1/groupId=2/entryModuleId=1/tenant_code=MZ/autoRun=true`。
|
||
- [x] 评查命中验收结果:上传生成 `documentId=232/runId=110`,`leaudit_documents` 写入 `type_id=1/group_id=2/entry_module_id=1/tenant_code=MZ/current_run_id=110`。
|
||
- [x] 规则集命中数据库证据:`leaudit_audit_runs.id=110` 写入 `rule_set_id=126/rule_version_id=500/group_id_snapshot=2/rule_binding_id_snapshot=101/tenant_code=MZ/scope_type_snapshot=TENANT`,符合 `leaudit_rule_group_bindings.group_id=2` 下梅州租户绑定。
|
||
- [x] 使用 Playwright 真实账号 `001/gdyc06111` 验收模板页面权限过滤:进入 `合同评查` 后 URL 为 `/documents/list?entryModuleId=1&documentTypeIds=...`,侧栏显示 `文件上传/文档列表`,不显示 `合同管理/模板搜索/模板列表`。
|
||
- [x] 模板权限验收补充证据:`001` 为 `common` 角色,无 `contract_template:*` 和 `entry_module:*` 权限;`/api/auth/session-data` 返回的合同入口 `features` 已被裁剪为 `home/documents/upload/rules/rule_groups`,`permissionMap` 不含 `/contract-template` 路由。
|
||
- [x] RBAC 权限数据收口:已按“入口模块只能系统超级管理员维护”口径移除 `admin / 地区管理员` 的 `entry_module:create/update/delete/image/list/detail` 权限和 `/entry-modules` 菜单。
|
||
- [x] 新增可重复执行 SQL:`scripts/创建sql/rbac_entry_module_super_admin_only.sql`,用于保证 `super_admin` 保留入口模块权限,同时移除 `admin/provincial_admin` 的入口模块权限和菜单。
|
||
- [x] 修复首页系统设置按钮跳转:不再固定跳 `/entry-modules`,而是跳当前用户有权限的第一个设置子菜单;没有任何设置子菜单时不显示系统设置按钮。
|
||
- [x] RBAC 收口真实验收:`mz001/mzyc06111` 登录后无 `entry_module:*` 权限,直调 `/api/v3/entry-modules` 返回 403,`settingsChildren` 不含 `/entry-modules`。
|
||
- [x] RBAC 收口真实验收:`000/admin06111` 登录后仍有入口模块 6 个权限,直调 `/api/v3/entry-modules` 返回 200,`settingsChildren` 包含 `/entry-modules`。
|
||
|
||
### 14.10.5 当前最新状态 2026-05-24
|
||
|
||
当前已完成并验证:
|
||
|
||
- [x] 入口模块列表业务范围列已经接真实后端摘要,不再统一显示“待配置”。
|
||
- [x] 合同评查、案卷智能评查、内部公文的业务范围摘要和当前数据库绑定一致。
|
||
- [x] 智慧法务助手、交叉评查当前没有绑定业务大类,所以继续显示“待配置”是正确状态。
|
||
- [x] 测试入口模块和测试业务大类已经清理,不会继续污染入口模块管理页和业务大类管理页。
|
||
- [x] `000/admin06111` 对应账号已变成系统超级管理员,后续 Playwright 验收使用该账号时具备系统级管理权限。
|
||
- [x] `provincial_admin` 已删除,后续计划和验收不要再依赖省级管理员这个角色。
|
||
- [x] 入口模块管理页和上传页已经完成真实 Playwright 验收,上传链路能落 `entry_module_id/type_id/group_id/tenant_code`。
|
||
- [x] 规则分组页已经完成真实 Playwright 验收,入口 A/B 不串树,一级不绑定规则,二级绑定弹窗可用。
|
||
- [x] 公文详情页双侧栏代码风险已修复,并已用真实公文详情页面 Playwright 确认。
|
||
- [x] 多租户真实账号验收已完成,云浮、揭阳、梅州入口可见性符合当前入口模块租户分配和 RBAC 菜单权限。
|
||
- [x] 模板页面权限过滤已完成真实 Playwright 验收,普通用户没有模板页面权限时,即使进入合同入口也不显示模板菜单。
|
||
- [x] 评查命中验收已完成,普通文档上传后按 `document.group_id -> leaudit_rule_group_bindings -> rule_set/current_version_id` 命中租户级规则集。
|
||
|
||
后续仍需继续验收:
|
||
|
||
- [x] RBAC 权限数据收口:已从 `admin / 地区管理员` 移除入口模块管理权限,当前只有系统超级管理员可以维护入口模块。
|
||
|
||
### 14.10.6 下一轮执行顺序
|
||
|
||
前置调整:
|
||
|
||
```text
|
||
真实前端 Playwright 全量验收前,先执行入口模块和文档类型的 UI/交互优化计划:
|
||
docs/superpowers/plans/2026-05-23-business-entry-ux-optimization.md
|
||
```
|
||
|
||
原因:
|
||
|
||
```text
|
||
当前“入口模块管理 + 文档类型管理 + 规则分组 + 上传”的技术语义学习成本过高。
|
||
如果先写真实前端测试,会把当前复杂交互固化下来。
|
||
应先把页面统一成“业务入口、业务大类、业务类型、规则配置”的用户语义,再做真实 Playwright 验收。
|
||
```
|
||
|
||
按下面顺序继续,不要先碰评查引擎:
|
||
|
||
1. P0:入口模块管理页 Playwright 验收。
|
||
|
||
验收内容:
|
||
|
||
```text
|
||
新建入口 -> 选择菜单模板 -> 勾选功能 -> 分配租户 -> 保存 -> 列表回显 -> 编辑回显
|
||
```
|
||
|
||
输出要求:
|
||
|
||
```text
|
||
已完成。继续维护时如果页面字段或回显错,先修前端表单和接口字段映射,不改运行时评查逻辑。
|
||
```
|
||
|
||
2. P1:上传页真实表单 Playwright 验收。
|
||
|
||
验收内容:
|
||
|
||
```text
|
||
从入口模块进入上传页 -> 文件上传菜单排第二 -> 选择文档类型 -> 选择二级分组 -> 提交
|
||
```
|
||
|
||
输出要求:
|
||
|
||
```text
|
||
已完成。验收确认数据库或接口返回里存在 entry_module_id/type_id/group_id/tenant_code。
|
||
```
|
||
|
||
3. P2:规则分组页 Playwright 验收。
|
||
|
||
验收内容:
|
||
|
||
```text
|
||
入口 A 只看到 A 的一级分组
|
||
入口 B 只看到 B 的一级分组
|
||
一级分组不出现绑定规则入口
|
||
二级分组可以打开绑定弹窗并保存
|
||
```
|
||
|
||
输出要求:
|
||
|
||
```text
|
||
已完成。验收确认前端透传 `entryModuleId`,后端列表过滤生效,一级/二级规则绑定边界正确。
|
||
```
|
||
|
||
4. P3:公文入口深度验收。
|
||
|
||
验收内容:
|
||
|
||
```text
|
||
公文列表按 entry_module_id 隔离
|
||
公文上传能从入口模块进入
|
||
公文详情页不出现双侧栏
|
||
```
|
||
|
||
输出要求:
|
||
|
||
```text
|
||
如果列表串数据,优先查 govdoc API 请求参数和 GovdocServiceImpl 查询过滤。
|
||
```
|
||
|
||
5. P4:多租户真实账号验收。
|
||
|
||
验收内容:
|
||
|
||
```text
|
||
云浮用户只看到云浮或 PUBLIC 入口
|
||
揭阳用户只看到揭阳或 PUBLIC 入口
|
||
梅州用户只看到梅州或 PUBLIC 入口
|
||
没有入口模块管理权限的租户用户 UI 不显示管理能力,接口返回 403
|
||
```
|
||
|
||
输出要求:
|
||
|
||
```text
|
||
如果缺少真实账号,先用现有 RBAC/租户数据造最小测试账号,并记录账号和租户。
|
||
```
|
||
|
||
6. P5:评查命中验收。
|
||
|
||
验收内容:
|
||
|
||
```text
|
||
上传带二级分组的文档 -> 绑定该二级分组规则集 -> 启动评查 -> 确认命中该规则集
|
||
```
|
||
|
||
输出要求:
|
||
|
||
```text
|
||
这里只验证 group_id -> leaudit_rule_group_bindings -> rule_set 主链路,不重构评查引擎。
|
||
```
|
||
|
||
### 14.10.7 下一轮可并行安排
|
||
|
||
可以拆成 3 个并行工作流。当前 3 个工作流均已完成,本节保留为后续复盘参考:
|
||
|
||
- [x] 工作流 A:入口模块管理页 + 上传页 Playwright 验收,主要看表单和字段落库。
|
||
- [x] 工作流 B:规则分组页 + 公文入口深度验收,主要看入口上下文过滤和侧栏。
|
||
- [x] 工作流 C:多租户真实账号 + 模板权限过滤 + 评查命中验收,主要看权限、租户和运行时规则命中。
|
||
|
||
并行约束:
|
||
|
||
- [x] 三个工作流都必须先执行 `./leaudit.sh status`,确认后端、前端、Worker、Beat 运行。
|
||
- [x] UI 验收必须使用 Playwright,账号优先使用 `000/admin06111`,多租户验收再补具体租户账号。
|
||
- [x] 每个工作流完成后都要回写本计划文档的执行记录。
|
||
- [x] 发现 BUG 先补最小复现脚本,再修代码,最后跑对应 Playwright 或 pytest 回归。
|
||
|
||
### 14.11 建议提交顺序
|
||
|
||
1. 数据库迁移脚本。
|
||
2. 后端 DTO/VO 和入口模块服务。
|
||
3. 首页入口返回和前端入口上下文。
|
||
4. 侧边栏菜单生成逻辑。
|
||
5. 入口模块管理页菜单模板和功能勾选。
|
||
6. 文档上传和列表入口模块归属。
|
||
7. 公文列表入口模块过滤。
|
||
8. 规则分组和规则绑定校验。
|
||
9. 删除名称包含“合同/公文”的旧判断。
|
||
10. 跑完整验收清单。
|
||
|
||
### 14.12 暂不执行的任务
|
||
|
||
- [ ] 暂不做同一个入口模块不同租户不同功能清单。
|
||
- [ ] 暂不删除旧地区字段。
|
||
- [ ] 暂不删除旧规则类型绑定表。
|
||
- [ ] 暂不重写评查运行引擎。
|
||
- [ ] 暂不把入口模块变成权限系统。
|
||
|
||
### 14.13 下一阶段收口计划:entry_module_id 强一致性
|
||
|
||
#### 14.13.1 先把概念说清楚
|
||
|
||
`entry_module_id` 不是给用户看的新概念,它在前端应该叫“业务入口”或“工作台”。
|
||
|
||
它的作用只有一个:让系统知道一条数据属于哪个入口模块。
|
||
|
||
如果没有 `entry_module_id`,系统只能靠下面这些不稳定方式猜:
|
||
|
||
```text
|
||
入口名字里有没有“合同”
|
||
当前 URL 是不是 /govdoc
|
||
文档类型刚好属于哪个入口
|
||
规则分组刚好挂在哪个父节点下面
|
||
```
|
||
|
||
这些猜法会导致同一个租户下多个入口互相串数据,例如:
|
||
|
||
```text
|
||
合同入口能看到案卷文档
|
||
公文入口侧边栏混入普通文件上传
|
||
规则分组 A/B 入口互相出现
|
||
上传后评查命中错规则集
|
||
```
|
||
|
||
所以新链路必须明确落字段:
|
||
|
||
```text
|
||
入口模块 leaudit_entry_modules.id
|
||
-> 业务大类/一级分组 leaudit_evaluation_point_groups.entry_module_id
|
||
-> 业务类型/二级分组 group_id
|
||
-> 文档 leaudit_documents.entry_module_id + group_id
|
||
-> 评查按 group_id 命中规则绑定
|
||
```
|
||
|
||
用户理解口径:
|
||
|
||
```text
|
||
我从哪个业务入口进来,上传、列表、规则配置就只看这个入口下的数据。
|
||
```
|
||
|
||
开发理解口径:
|
||
|
||
```text
|
||
entry_module_id 是入口隔离字段。
|
||
tenant_code 管租户边界。
|
||
group_id 管规则命中。
|
||
type_id 管文档分类。
|
||
features/menu_profile 管菜单显示。
|
||
```
|
||
|
||
#### 14.13.2 当前不直接强拦的原因
|
||
|
||
当前代码里“一级业务大类必须有 `entry_module_id`”还没有完全强拦,是刻意保留兼容。
|
||
|
||
原因:
|
||
|
||
```text
|
||
老数据里可能还有全局一级分组
|
||
老文档可能只能从 type_id 或 group_id 推导入口
|
||
如果现在直接 400,可能会把旧规则配置、旧上传链路或历史列表打断
|
||
```
|
||
|
||
因此下一阶段不能一步到位硬改,必须按下面顺序推进:
|
||
|
||
```text
|
||
先巡检旧数据
|
||
再修复或回填孤儿数据
|
||
再加后端软提示/日志
|
||
最后才把新建和编辑一级业务大类改成必须有入口模块
|
||
```
|
||
|
||
#### 14.13.3 实现顺序
|
||
|
||
1. 数据巡检。
|
||
|
||
执行只读巡检脚本:
|
||
|
||
```bash
|
||
psql "$DATABASE_URL" -f scripts/创建sql/verify_entry_module_menu_profile.sql
|
||
```
|
||
|
||
重点看这几类结果:
|
||
|
||
```text
|
||
文档 entry_module_id 为空
|
||
文档 entry_module_id 和 group/type 推导不一致
|
||
重复二级分组
|
||
一级分组 entry_module_id 为空
|
||
非法 menu_profile/features
|
||
```
|
||
|
||
2. 旧数据处理。
|
||
|
||
如果一级分组没有 `entry_module_id`,先按可推导关系回填:
|
||
|
||
```text
|
||
一级分组有 document_type_id -> 用 leaudit_document_types.entry_module_id 回填
|
||
一级分组下二级分组有 document_type_id -> 用二级分组对应文档类型的 entry_module_id 回填
|
||
确实无法推导 -> 先列清单人工判断,不自动乱填
|
||
```
|
||
|
||
注意:
|
||
|
||
```text
|
||
不允许为了清零数据随便塞 entry_module_id=1
|
||
不允许把跨入口共用的旧分组直接强绑到某一个入口
|
||
```
|
||
|
||
3. 后端创建校验收紧。
|
||
|
||
文件:
|
||
|
||
```text
|
||
fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py
|
||
```
|
||
|
||
目标:
|
||
|
||
```text
|
||
新建一级业务大类 pid=0 时,必须传 entry_module_id
|
||
新建一级业务大类不允许只靠 document_type_id 反推入口
|
||
新建二级业务类型 pid!=0 时,继承父级 entry_module_id
|
||
新建二级业务类型必须绑定 document_type_id
|
||
二级业务类型的 document_type_id 所属入口必须和父级入口一致
|
||
```
|
||
|
||
兼容策略:
|
||
|
||
```text
|
||
只强拦新建/编辑请求
|
||
不因为历史数据缺 entry_module_id 导致列表查询直接失败
|
||
```
|
||
|
||
4. 前端规则配置页补齐入口上下文。
|
||
|
||
文件:
|
||
|
||
```text
|
||
legal-platform-frontend/app/(audit)/rule-groups/RuleGroupsClient.tsx
|
||
```
|
||
|
||
目标:
|
||
|
||
```text
|
||
从业务入口进入规则配置页时,新增业务大类自动带当前 entryModuleId
|
||
页面顶部显示“当前业务入口:xxx”
|
||
如果没有入口上下文,不允许新增业务大类,只提示从首页业务入口进入
|
||
```
|
||
|
||
用户看到的是:
|
||
|
||
```text
|
||
当前业务入口:合同评查
|
||
新增业务大类时自动归属当前入口
|
||
```
|
||
|
||
用户不需要看到:
|
||
|
||
```text
|
||
entry_module_id
|
||
pid = 0
|
||
group_id
|
||
```
|
||
|
||
5. 上传页保持现有两级选择。
|
||
|
||
文件:
|
||
|
||
```text
|
||
legal-platform-frontend/app/(audit)/files/upload/FilesUploadClient.tsx
|
||
```
|
||
|
||
目标不变:
|
||
|
||
```text
|
||
先选业务大类
|
||
再选业务类型
|
||
提交 entryModuleId/typeId/groupId/tenantCode
|
||
```
|
||
|
||
这里不要做自动创建业务类型,也不要跳过业务类型选择。
|
||
|
||
6. 验收。
|
||
|
||
必须使用 Playwright 真实账号验收:
|
||
|
||
```text
|
||
000/admin06111:新增业务大类 -> 新增业务类型 -> 配置规则 -> 上传 -> 列表只看当前入口
|
||
mz001/mzyc06111:不能进入业务入口管理,能使用已分配入口
|
||
yf001/yfyc06111:只能看到云浮已分配入口
|
||
jy001/jyyc06111:只能看到揭阳已分配入口
|
||
```
|
||
|
||
数据库验收:
|
||
|
||
```text
|
||
leaudit_evaluation_point_groups 一级分组有 entry_module_id
|
||
leaudit_evaluation_point_groups 二级分组能通过父级拿到 entry_module_id
|
||
leaudit_documents 上传后有 entry_module_id/type_id/group_id/tenant_code
|
||
leaudit_audit_runs 按 group_id_snapshot 命中 rule_binding_id_snapshot
|
||
```
|
||
|
||
#### 14.13.4 推荐下一步直接做什么
|
||
|
||
下一步不先改代码,先做一次真实数据库巡检并记录结果。
|
||
|
||
执行顺序:
|
||
|
||
```text
|
||
1. 运行 verify_entry_module_menu_profile.sql
|
||
2. 把异常结果整理到本计划文档
|
||
3. 如果一级分组 entry_module_id 为空数量为 0,再开始后端强校验
|
||
4. 如果不为 0,先补一个只读清单和人工回填 SQL 草案
|
||
5. 回填完成后再改后端校验和前端规则配置页
|
||
```
|
||
|
||
这样做的原因:
|
||
|
||
```text
|
||
entry_module_id 是隔离字段,一旦强校验加错,会直接影响上传、规则配置、列表过滤和评查命中。
|
||
先把数据状态摸清,再收紧代码,风险最低。
|
||
```
|
||
|
||
#### 14.13.5 巡检记录 2026-05-24
|
||
|
||
已执行:
|
||
|
||
```bash
|
||
PGPASSWORD='zhfw*123*' psql -h nas.7bm.co -p 54302 -U docauditai_admin -d leaudit_platform -f scripts/创建sql/verify_entry_module_menu_profile.sql
|
||
```
|
||
|
||
同时修正巡检脚本字段错误:
|
||
|
||
```text
|
||
leaudit_documents 没有 filename 字段,实际字段为 normalized_name。
|
||
已把脚本中的 d.filename 改为 d.normalized_name AS document_name。
|
||
```
|
||
|
||
巡检结论:
|
||
|
||
```text
|
||
必要字段存在:
|
||
leaudit_entry_modules.menu_profile
|
||
leaudit_entry_modules.features
|
||
leaudit_documents.entry_module_id
|
||
|
||
必要索引存在:
|
||
idx_leaudit_entry_modules_menu_profile
|
||
idx_leaudit_documents_entry_module_id
|
||
|
||
非法 menu_profile:0 条
|
||
非法 feature 编码:0 条
|
||
重复二级分组:0 条
|
||
文档 entry_module_id 与 group/type 推导不一致:0 条
|
||
```
|
||
|
||
发现的问题:
|
||
|
||
```text
|
||
案卷智能评查 features 为空
|
||
智慧法务助手 features 为空
|
||
历史文档 entry_module_id 为空:68 条
|
||
其中 65 条可以通过 group/type 推导入口
|
||
其中 3 条公文示例没有 type_id/group_id,暂时无法自动推导入口
|
||
一级业务大类 entry_module_id 为空:14 条
|
||
```
|
||
|
||
68 条历史文档缺入口归属的推导结果:
|
||
|
||
```text
|
||
可推导为 合同评查 entry_module_id=1:47 条
|
||
可推导为 案卷智能评查 entry_module_id=2:14 条
|
||
可推导为 内部公文 entry_module_id=3:4 条
|
||
无法推导:3 条,均为旧公文示例,type_id/group_id 为空
|
||
```
|
||
|
||
14 条一级业务大类缺入口归属的来源:
|
||
|
||
```text
|
||
行政卷宗 root.casefile:可通过子业务类型推导为 案卷智能评查 entry_module_id=2
|
||
testmzceshi:测试残留,无子节点、无 document_type_id,需要人工确认是否删除
|
||
12 条 PW*/pw.* 测试业务大类:来自已软删除的 Playwright 临时入口,关联文档类型仍未软删
|
||
```
|
||
|
||
关键判断:
|
||
|
||
```text
|
||
当前正式 5 个入口本身没有结构性问题。
|
||
真正阻塞强校验的是历史数据和 Playwright 测试残留。
|
||
现在不能直接加“一级业务大类必须有 entry_module_id”的硬拦截,否则旧残留会继续污染巡检,也可能影响旧数据展示。
|
||
```
|
||
|
||
建议下一步:
|
||
|
||
```text
|
||
1. 先出只读清单 SQL,列出可自动回填的 65 条历史文档和 root.casefile。
|
||
2. 出数据修复 SQL 草案,但先不执行。
|
||
3. Playwright 测试残留建议统一软删:pw.* 文档类型、PW* 规则分组、对应临时入口关系。
|
||
4. testmzceshi 需要人工确认是否删除。
|
||
5. 3 条无 type_id/group_id 的旧公文示例不要自动回填,保留人工处理。
|
||
6. 数据修复完成后,再改后端强校验和前端规则配置页入口上下文。
|
||
```
|
||
|
||
#### 14.13.6 数据修复执行记录 2026-05-24
|
||
|
||
已新增并执行数据修复脚本:
|
||
|
||
```text
|
||
scripts/创建sql/repair_entry_module_scope_data_20260524.sql
|
||
```
|
||
|
||
执行前做了事务回滚演练:
|
||
|
||
```text
|
||
可回填历史文档:65 条
|
||
可回填一级业务大类:1 条,root.casefile 行政卷宗
|
||
可继承父级入口的二级业务类型:19 条
|
||
可安全软删测试规则分组:25 条
|
||
可补默认功能的入口模块:1 条,案卷智能评查
|
||
```
|
||
|
||
正式执行结果:
|
||
|
||
```text
|
||
UPDATE 65
|
||
UPDATE 1
|
||
UPDATE 19
|
||
UPDATE 25
|
||
UPDATE 1
|
||
COMMIT
|
||
```
|
||
|
||
修复内容:
|
||
|
||
```text
|
||
65 条历史文档已按 group/type 推导回填 entry_module_id。
|
||
行政卷宗 root.casefile 已回填 entry_module_id=2,归属案卷智能评查。
|
||
行政卷宗及合同正式二级业务类型已继承父级 entry_module_id。
|
||
testmzceshi 和 PW*/pw.* 无引用测试规则分组已软删除。
|
||
案卷智能评查已补齐默认功能:home/documents/upload/rules/rule_groups。
|
||
```
|
||
|
||
修复后巡检结果:
|
||
|
||
```text
|
||
非法 menu_profile:0 条
|
||
非法 feature 编码:0 条
|
||
文档 entry_module_id 与 group/type 推导不一致:0 条
|
||
重复二级分组:0 条
|
||
一级业务大类 entry_module_id 为空:0 条
|
||
活跃 PW 测试文档类型:0 条
|
||
活跃测试规则分组残留:0 条
|
||
活跃文档类型指向已删除入口:0 条
|
||
```
|
||
|
||
剩余问题:
|
||
|
||
```text
|
||
仍有 3 条旧公文示例文档 entry_module_id 为空。
|
||
这 3 条同时没有 type_id 和 group_id,无法确定属于哪个入口模块。
|
||
本次没有自动回填,避免把历史公文示例错误塞进内部公文或其他入口。
|
||
```
|
||
|
||
剩余 3 条:
|
||
|
||
```text
|
||
id=46 公文示例-瑕疵
|
||
id=47 公文示例-瑕疵
|
||
id=48 公文示例-合规-OOXML修正版
|
||
```
|
||
|
||
后续处理建议:
|
||
|
||
```text
|
||
如果确认它们是内部公文旧测试数据,可以单独回填 entry_module_id=3。
|
||
如果只是历史测试样例,也可以软删除。
|
||
在未确认前,不建议自动处理。
|
||
```
|
||
|
||
#### 14.13.7 旧公文示例回填记录 2026-05-24
|
||
|
||
用户已确认 3 条旧公文示例可以回填。
|
||
|
||
已新增并执行:
|
||
|
||
```text
|
||
scripts/创建sql/repair_govdoc_sample_entry_module_20260524.sql
|
||
```
|
||
|
||
执行条件:
|
||
|
||
```text
|
||
只更新 id IN (46, 47, 48)
|
||
只更新 deleted_at IS NULL
|
||
只更新 entry_module_id IS NULL
|
||
只更新 engine_type='govdoc' 且 review_scope='govdoc'
|
||
只回填 entry_module_id=3,不伪造 type_id/group_id
|
||
```
|
||
|
||
执行结果:
|
||
|
||
```text
|
||
UPDATE 3
|
||
COMMIT
|
||
```
|
||
|
||
回填后结果:
|
||
|
||
```text
|
||
id=46 公文示例-瑕疵 -> entry_module_id=3
|
||
id=47 公文示例-瑕疵 -> entry_module_id=3
|
||
id=48 公文示例-合规-OOXML修正版 -> entry_module_id=3
|
||
```
|
||
|
||
最终巡检结果:
|
||
|
||
```text
|
||
documents_without_entry_module = 0
|
||
文档 entry_module_id 与 group/type 推导不一致 = 0
|
||
一级业务大类 entry_module_id 为空 = 0
|
||
重复二级分组 = 0
|
||
非法 menu_profile = 0
|
||
非法 feature 编码 = 0
|
||
```
|