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