249 lines
14 KiB
Markdown
249 lines
14 KiB
Markdown
# 入口模块重构第二阶段实施计划
|
||
|
||
> **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,为既有 `<img>`、未使用变量类提示。
|
||
- 仍需继续做真实浏览器页面级验收:
|
||
- 入口模块管理页功能勾选保存和回显。
|
||
- 上传页真实表单选择文档类型、二级分组后提交。
|
||
- 规则分组页面真实 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 风格内的补充。
|