14 KiB
入口模块重构第二阶段实施计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 补齐入口模块到普通文档、上传、规则分组、页面布局的闭环,让用户从某个入口进去后,只看到、上传、配置该入口范围内的业务数据。
Architecture: 第一阶段已完成入口上下文、菜单 features 过滤、公文列表/上传入口归属。第二阶段继续沿用 selectedEntryModuleContext,所有业务页面从该上下文读取 entryModuleId/documentTypeIds,后端继续用 entry_module_id/type_id/group_id/tenant_code 做数据收口,不新增权限体系。
Tech Stack: FastAPI、SQLAlchemy 原生 SQL、Next.js、React、现有 RBAC 权限、现有 leaudit_entry_modules / leaudit_document_types / leaudit_evaluation_point_groups / leaudit_documents 表。
一、执行原则
- 不再用入口名称判断业务类型。
- 不改现有 UI 视觉风格,只补数据范围、参数和必要提示。
- 不删除旧接口,先补安全约束和兼容字段。
- 先做普通文档链路,再做规则分组链路,最后处理重复侧边栏和验收。
- 每一批改完都跑
py_compile、tsc、目标eslint。
二、文件责任
legal-platform-frontend/lib/auth/entry-module-context.ts:继续作为前端入口上下文唯一读写入口。legal-platform-frontend/lib/api/legacy/files/documents.ts:普通文档列表、上传 API 参数补入口模块字段。legal-platform-frontend/app/(audit)/files/upload/*或实际上传客户端文件:上传表单带entryModuleId/typeId/groupId。legal-platform-frontend/app/(audit)/documents/*或实际文档列表客户端文件:文档列表按入口上下文传entryModuleId/documentTypeIds。fastapi_modules/fastapi_leaudit/controllers/documentController.py:普通上传接口接收entryModuleId。fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py:创建文档写入entry_module_id,校验group_id是二级分组且属于当前入口。legal-platform-frontend/app/(audit)/rule-groups/RuleGroupsClient.tsx:规则分组页读取入口上下文,只展示当前入口分组。fastapi_modules/fastapi_leaudit/controllers/evaluationPointGroupController.py:分组列表/创建/更新接收或校验入口模块参数。fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py:一级/二级分组边界、入口模块归属、绑定二级分组校验。legal-platform-frontend/components/layout/Layout.tsx:处理公文详情页内部工作区侧栏和平台 Sidebar 重复问题。
三、任务拆分
Task 1:普通文档上传写入入口模块
目标: 用户从入口模块进入上传页时,上传出来的文档写入 entry_module_id/type_id/group_id/tenant_code。
Files:
-
Modify:
fastapi_modules/fastapi_leaudit/controllers/documentController.py -
Modify:
fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py -
Modify:
legal-platform-frontend/lib/api/legacy/files/documents.ts -
Modify: 实际上传页面客户端,先用
rg -n "DocumentUpload|uploadDocument|/upload" legal-platform-frontend/app legal-platform-frontend/components -
后端
UploadDocument增加entryModuleId: int | None = Form(None)。 -
DocumentService.Upload(...)增加EntryModuleId参数。 -
创建
LeauditDocument后写入entry_module_id。 -
如果传了
groupId,校验该分组pid <> 0。 -
如果传了
entryModuleId + groupId,校验二级分组所属入口等于entryModuleId。 -
如果没传
groupId,只允许唯一二级分组时后端兜底;多个二级分组时报 400,让前端用户明确选择。 -
前端上传 API 从
readEntryModuleContext()读取entryModuleId/documentTypeIds。 -
上传表单把
entryModuleId、选中的typeId、选中的groupId一起提交。
验收:
- 上传成功后数据库
leaudit_documents.entry_module_id有值。 - 多个二级分组时,不选
groupId不能上传。 - 传一级分组 ID 时后端返回 400。
Task 2:普通文档列表按入口模块过滤
目标: 从不同入口进入普通文档列表,只看到当前入口范围的文档。
Files:
-
Modify:
legal-platform-frontend/lib/api/legacy/files/documents.ts -
Modify: 普通文档列表客户端,先用
rg -n "listDocuments|getDocuments|documents.searchParams" legal-platform-frontend/app legal-platform-frontend/components -
Backend already has:
documentController.py的entry_module_id/type_ids和documentServiceImpl.py的过滤基础。 -
文档列表页面读取入口上下文作用域(URL/session,由入口首页写入)。
-
请求参数带
entry_module_id。 -
请求参数带
type_ids,作为入口文档类型范围的兜底过滤。 -
保留现有搜索、分页、状态过滤,不改 UI。
-
切换入口模块时清理
documents.searchParams,避免旧入口搜索条件污染新入口。
验收:
- 入口 A 上传的文档不出现在入口 B。
- 没有入口上下文时,保留原有列表行为。
Task 3:规则分组页按入口模块收口
目标: 规则分组页只展示当前入口模块下的一级分组和二级分组。
Files:
-
Modify:
legal-platform-frontend/app/(audit)/rule-groups/RuleGroupsClient.tsx -
Modify:
legal-platform-frontend/app/api/rule-groups/* -
Modify:
fastapi_modules/fastapi_leaudit/controllers/evaluationPointGroupController.py -
Modify:
fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py -
前端读取
entryModuleId。 -
分组列表请求带
entry_module_id。 -
后端列表用
entry_module_id过滤一级分组。 -
二级分组根据父级一级分组自然收口。
-
新建一级分组默认带当前入口
entry_module_id。 -
新建二级分组必须带
document_type_id,且文档类型属于当前入口。 -
编辑二级分组时禁止移动到其他入口的一级分组下。
验收:
- 合同入口看不到公文入口的分组。
- 公文入口看不到合同入口的分组。
- 二级分组不能跨入口挂载。
Task 4:规则绑定二级分组硬校验
目标: 规则只能绑定二级分组,不能绑定一级分组,也不能跨租户/跨入口污染。
Files:
-
Modify:
fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py -
Review:
fastapi_modules/fastapi_leaudit/services/impl/ruleServiceImpl.py -
保留新接口已有
pid == 0拒绝绑定逻辑。 -
在绑定保存时再次校验 group 所属入口对当前用户可见。
-
_load_binding_map增加绑定级租户过滤:当前租户、PUBLIC、PROVINCIAL可见,其它租户不可见。 -
旧规则绑定兼容接口只允许唯一可访问二级分组场景写入。
-
旧接口写入新绑定表时必须带
tenant_code/scope_type,保持第一阶段补丁。
验收:
- 一级分组绑定返回 400。
- 普通租户看不到其它租户绑定。
- 全局管理员能看到公共/省级范围绑定。
Task 5:重复侧边栏处理
目标: 公文详情页不要同时出现平台 Sidebar 和公文工作区 Sidebar。
Files:
-
Modify:
legal-platform-frontend/components/layout/Layout.tsx -
Review:
legal-platform-frontend/components/govdoc-audit/GovdocAuditResultPage.tsx -
确认哪些公文页面内部有
GovdocWorkspaceSidebar。 -
Layout.tsx对公文详情/结果页隐藏平台 Sidebar。 -
公文列表、上传、概览仍保留平台 Sidebar。
-
不改公文工作区内部 UI,只处理外层布局冲突。
验收:
/govdoc/audits只有平台 Sidebar。/govdoc/[id]或详情页只有公文工作区 Sidebar。- 移动端布局不新增横向溢出。
Task 6:前端权限和 UI 一致性复查
目标: features 只是入口功能编排,不能绕过 RBAC 页面/API 权限。
Files:
-
Review:
legal-platform-frontend/components/layout/Sidebar.tsx -
Review:
legal-platform-frontend/lib/auth/entry-module-menu.ts -
Review:
legal-platform-frontend/lib/auth/user-routes.ts -
确认菜单先从 RBAC 路由结果生成,再按 features 过滤。
-
没有合同模板页面权限时,即使入口启用合同模板 feature,也不显示模板菜单。
-
没有规则分组权限时,即使入口启用
rule_groups,也不显示规则分组菜单。 -
所有新增提示沿用现有 CSS 变量和组件风格。
验收:
- 入口 features 不能“放大权限”,只能“收窄菜单”。
四、并行安排
建议用 subagent 并行:
- Worker A:Task 1 普通文档上传链路。
- Worker B:Task 2 普通文档列表过滤。
- Worker C:Task 3/4 规则分组和绑定后端校验。
- Worker D:Task 5 重复侧边栏处理。
- 主线程:整合冲突、跑验证、更新计划文档。
注意:Worker A 和 Worker B 可能同时改 lib/api/legacy/files/documents.ts,需要一个人先做 API 类型,另一个只改页面调用,避免冲突。
五、验证命令
后端:
python -m py_compile fastapi_modules/fastapi_leaudit/controllers/documentController.py fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py fastapi_modules/fastapi_leaudit/controllers/evaluationPointGroupController.py fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py fastapi_modules/fastapi_leaudit/services/impl/ruleServiceImpl.py
前端:
cd legal-platform-frontend
npx tsc --noEmit --pretty false
npx eslint --no-ignore lib/auth/entry-module-context.ts lib/auth/entry-module-menu.ts components/layout/Sidebar.tsx components/layout/Layout.tsx
手工验收:
- 创建一个名称不含“合同”的合同入口,启用合同模板功能,检查模板搜索/模板列表显示。
- 创建一个名称不含“公文”的公文入口,启用公文功能,检查公文列表/上传显示。
- 从入口 A 上传普通文档,入口 B 不应看到。
- 从入口 A 进入规则分组,只看到入口 A 的一级/二级分组。
- 尝试把规则绑到一级分组,应失败。
- 公文详情页不出现双侧栏。
执行记录(2026-05-23):
- 已跑后端目标编译:
python -m py_compile ...,通过。 - 已跑前端类型检查:
npx tsc --noEmit --pretty false,通过。 - 已跑目标 eslint:0 error,剩余 31 个 warning 为既有未使用变量 / any / hook dependency 类警告。
- 已用 Playwright 真实账号
000/admin06111做首页和侧边栏 smoke:- 登录成功。
- 点击入口后进入
/documents/list?entryModuleId=...&documentTypeIds=...。 - 侧边栏顺序为:首页、文件上传、文档列表、规则管理。
- 文档列表没有重复。
- 上传文档按钮可见。
- 已用 Playwright 真实后端链路跑优先项
2/3/4/5:node /tmp/leaudit-playwright-priority-2-5-v3.js,结果25 pass / 0 fail。- 上传携带
entryModuleId/groupId成功,文档详情返回正确typeId/groupId/tenantCode。 - 文档列表按入口 A/B 隔离通过。
- 规则分组按入口 A/B 隔离通过。
- 一级分组绑定规则集返回 400。
- 二级分组绑定规则集返回 200。
- 无入口模块创建/编辑/删除权限用户直接调接口返回 403。
- 上传携带
- 修复一次 Playwright 暴露的后端 500:
- 文件:
fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py - 方法:
_get_binding_row - 原因:SQL 文本没有使用 f-string,
{access_filter}原样发送到 PostgreSQL。 - 修复:把查询文本改为 f-string,入口模块/租户可见性过滤正常注入。
- 文件:
- 已跑后端规则绑定范围单测:
pytest -q tests/test_rule_group_binding_scope.py,结果4 passed。 - 已重新跑前端类型检查:
npx tsc --noEmit --pretty false,通过。 - 已重新跑目标 eslint:0 error,剩余 6 个 warning,为既有
<img>、未使用变量类提示。 - 仍需继续做真实浏览器页面级验收:
- 入口模块管理页功能勾选保存和回显。
- 上传页真实表单选择文档类型、二级分组后提交。
- 规则分组页面真实 UI 下只展示当前入口范围。
- 公文列表、公文上传、公文详情页侧边栏真实页面检查。
- 多租户可见性需要用不同租户账号逐个验证。
下一步执行顺序:
- Playwright 页面级验收入口模块管理页:新建/编辑入口,检查
menu_profile/features/tenants保存和回显。 - Playwright 页面级验收上传页:确认文件上传在 UI 上第二位,选择文档类型和二级分组后真实提交。
- Playwright 页面级验收规则分组页:入口 A/B 切换后页面树、绑定按钮、绑定弹窗范围正确。
- Playwright 页面级验收公文入口:名称不含“公文”但配置公文功能时,公文列表/上传可进入且标题不写死。
- 多租户账号验证:分别用云浮、揭阳、梅州或可用测试账号验证入口模块可见性和入口管理权限。
- 根据页面级验收结果修复前端展示问题,再回跑
tsc、目标 eslint、Playwright。
六、暂缓项
- 暂不删除旧
leaudit_rule_type_bindings表。 - 暂不重写评查运行引擎。
- 暂不做同一入口模块按租户覆盖 features。
- 暂不大改页面视觉,只做现有 UI 风格内的补充。