# 入口模块重构第二阶段实施计划 > **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` - [x] 后端 `UploadDocument` 增加 `entryModuleId: int | None = Form(None)`。 - [x] `DocumentService.Upload(...)` 增加 `EntryModuleId` 参数。 - [x] 创建 `LeauditDocument` 后写入 `entry_module_id`。 - [x] 如果传了 `groupId`,校验该分组 `pid <> 0`。 - [x] 如果传了 `entryModuleId + groupId`,校验二级分组所属入口等于 `entryModuleId`。 - [x] 如果没传 `groupId`,只允许唯一二级分组时后端兜底;多个二级分组时报 400,让前端用户明确选择。 - [x] 前端上传 API 从 `readEntryModuleContext()` 读取 `entryModuleId/documentTypeIds`。 - [x] 上传表单把 `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` 的过滤基础。 - [x] 文档列表页面读取入口上下文作用域(URL/session,由入口首页写入)。 - [x] 请求参数带 `entry_module_id`。 - [x] 请求参数带 `type_ids`,作为入口文档类型范围的兜底过滤。 - [x] 保留现有搜索、分页、状态过滤,不改 UI。 - [x] 切换入口模块时清理 `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` - [x] 前端读取 `entryModuleId`。 - [x] 分组列表请求带 `entry_module_id`。 - [x] 后端列表用 `entry_module_id` 过滤一级分组。 - [x] 二级分组根据父级一级分组自然收口。 - [x] 新建一级分组默认带当前入口 `entry_module_id`。 - [x] 新建二级分组必须带 `document_type_id`,且文档类型属于当前入口。 - [x] 编辑二级分组时禁止移动到其他入口的一级分组下。 **验收:** - 合同入口看不到公文入口的分组。 - 公文入口看不到合同入口的分组。 - 二级分组不能跨入口挂载。 ### Task 4:规则绑定二级分组硬校验 **目标:** 规则只能绑定二级分组,不能绑定一级分组,也不能跨租户/跨入口污染。 **Files:** - Modify: `fastapi_modules/fastapi_leaudit/services/impl/evaluationPointGroupServiceImpl.py` - Review: `fastapi_modules/fastapi_leaudit/services/impl/ruleServiceImpl.py` - [x] 保留新接口已有 `pid == 0` 拒绝绑定逻辑。 - [x] 在绑定保存时再次校验 group 所属入口对当前用户可见。 - [x] `_load_binding_map` 增加绑定级租户过滤:当前租户、`PUBLIC`、`PROVINCIAL` 可见,其它租户不可见。 - [x] 旧规则绑定兼容接口只允许唯一可访问二级分组场景写入。 - [x] 旧接口写入新绑定表时必须带 `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` - [x] 确认哪些公文页面内部有 `GovdocWorkspaceSidebar`。 - [x] `Layout.tsx` 对公文详情/结果页隐藏平台 Sidebar。 - [x] 公文列表、上传、概览仍保留平台 Sidebar。 - [x] 不改公文工作区内部 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` - [x] 确认菜单先从 RBAC 路由结果生成,再按 features 过滤。 - [x] 没有合同模板页面权限时,即使入口启用合同模板 feature,也不显示模板菜单。 - [x] 没有规则分组权限时,即使入口启用 `rule_groups`,也不显示规则分组菜单。 - [x] 所有新增提示沿用现有 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 类型,另一个只改页面调用,避免冲突。 --- ## 五、验证命令 后端: ```bash 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 ``` 前端: ```bash 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,为既有 ``、未使用变量类提示。 - 仍需继续做真实浏览器页面级验收: - 入口模块管理页功能勾选保存和回显。 - 上传页真实表单选择文档类型、二级分组后提交。 - 规则分组页面真实 UI 下只展示当前入口范围。 - 公文列表、公文上传、公文详情页侧边栏真实页面检查。 - 多租户可见性需要用不同租户账号逐个验证。 下一步执行顺序: 1. Playwright 页面级验收入口模块管理页:新建/编辑入口,检查 `menu_profile/features/tenants` 保存和回显。 2. Playwright 页面级验收上传页:确认文件上传在 UI 上第二位,选择文档类型和二级分组后真实提交。 3. Playwright 页面级验收规则分组页:入口 A/B 切换后页面树、绑定按钮、绑定弹窗范围正确。 4. Playwright 页面级验收公文入口:名称不含“公文”但配置公文功能时,公文列表/上传可进入且标题不写死。 5. 多租户账号验证:分别用云浮、揭阳、梅州或可用测试账号验证入口模块可见性和入口管理权限。 6. 根据页面级验收结果修复前端展示问题,再回跑 `tsc`、目标 eslint、Playwright。 --- ## 六、暂缓项 - 暂不删除旧 `leaudit_rule_type_bindings` 表。 - 暂不重写评查运行引擎。 - 暂不做同一入口模块按租户覆盖 features。 - 暂不大改页面视觉,只做现有 UI 风格内的补充。