Files
leaudit-platform-backend/docs/内部公文模块/内部公文模块现状偏差与对接补齐计划.md
T

720 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 内部公文模块现状偏差与对接补齐计划
## 1. 文档目的
本文档基于当前仓库代码、已有迁移设计文档,以及已确认的业务口径,回答 3 个问题:
- 当前 `govdoc` 模块业务上到底应该怎么跑
- 当前代码实际上跑成了什么状态
- 后续应该按什么顺序补齐,才能低风险落地
这份文档是后续前后端对接、数据库补齐、权限修复、页面改造的执行基线。
---
## 2. 已确认的业务结论
结合现有页面语义、引擎能力和你已确认的口径,当前正式业务结论如下。
### 2.1 模块定位
`govdoc` 不是公文流转系统,也不是审批系统。
它的第一阶段定位是:
- **内部公文格式审查模块**
核心能力是:
- 上传公文
- 自动发起格式审查
- 输出问题、规则结果、结构结果、报告产物
- 保留历史审查记录
### 2.2 业务主对象
当前应正式拍板:
- **主对象是文档**
- `run` 只是某份文档的一次审查执行记录
原因:
- 列表页已经确定是文档列表
- 删除、权限、原文下载天然属于文档
- 首页统计按文档最新结果算
- 用户认知对象是“这份公文”,不是“某次 run”
### 2.3 页面入口结论
详情页建议固定为:
- **按 `documentId` 进入详情**
详情页默认行为:
- 默认展示该文档最新一次审查结果
- 页面内可切换历史 run
因此正确语义是:
- 列表展示文档
- 详情入口指向文档
- 结果展示某个 run
### 2.4 审查流程结论
当前业务流程应为:
1. 用户上传公文
2. 创建平台文档主档,标记 `engine_type='govdoc'`
3. 自动创建一条新的 `run`
4. 投递异步任务
5. Worker 执行格式审查
6. 产出规则结果、结构结果、报告产物
7. 详情页默认展示最新 run,保留历史 run
### 2.5 权限与可见范围结论
- `common` 只能看自己上传的文档及结果
- `admin` 只能看本地区
- 报告、原文、findings、history 都必须继承文档可见性
- 删除是软删除
- 普通用户默认不开放文档更新/删除
- 需要保留重跑能力,但前端暂不开放按钮
### 2.6 指标与结果语义结论
- 分数是**公文格式规范符合度**
- 不是内容质量分
- 首页统计要按**每份文档的最新 run** 计算
- 不是把所有 run 累加
---
## 3. 当前实现的总体判断
结论先说:
- 当前仓库已经把 `govdoc` 的目录、接口骨架、前端页面壳、SQL 草案、Worker 编排骨架都铺出来了
- 但它还没有形成一个真正闭环的“可用模块”
当前状态更准确地说是:
- **已经完成模块迁移骨架**
- **但业务主模型、接口契约、运行链路、权限落地还没有完全收口**
这不是一个“修几个接口”的问题,而是一个“已经搭了半套系统,但业务模型还没完全统一”的问题。
---
## 4. 现状偏差分析
以下按 4 层来看:业务模型、前后端契约、运行链路、权限与统计。
### 4.1 业务模型偏差
#### 偏差 1:后端设计想走文档模型,前端详情仍然是 run 模型
后端控制器已经提供:
- `/govdoc/documents`
- `/govdoc/documents/{documentId}`
- `/govdoc/runs`
- `/govdoc/runs/{runId}/...`
说明后端设计方向其实已经倾向:
- 文档是主对象
- run 是子对象
但前端详情页实际还是:
- `/govdoc/[id]`
且页面直接把 `id` 当作 `auditId` 传给旧组件。
这意味着当前前端仍在延续旧的“run 即详情页主键”思路。
直接影响:
- 文档列表跳详情时主键语义不稳定
- 同一文档多次运行后,入口对象变成“当前 run”
- 后续历史 run 展示很难自然补进去
#### 偏差 2:列表页名义上是文档列表,数据上仍在混用文档 ID 和 run ID
当前前端列表适配时把:
- `audit_id = currentRunId ?? documentId`
这其实是在用一个字段混装两种对象:
- 有最新 run 时,这个 ID 代表 run
- 没有 run 时,这个 ID 代表 document
这会直接带来:
- 详情页路由语义漂移
- 删除、下载原文、查看结果时对象类型不确定
- 前端组件后续越来越难维护
#### 偏差 3:上传与发起审查的业务闭环还没有真正做完
业务上已经确认:
- 上传后应自动发起审查
但当前后端 `UploadDocument()` 只是占位返回;
`CreateRun()` 也是占位返回;
前端 `uploadAudit()` 虽然按“先上传、再建 run”的思路写了,但后端并没有真正完成这两个动作。
因此现在只是:
- 前端以为自己在走业务闭环
- 后端实际上还没有落地
### 4.2 前后端接口与路由契约偏差
#### 偏差 4:前端代理路径与实际代理实现不一致
当前前端 API 客户端里大量请求走的是:
- `/api/govdoc/...`
但仓库里实际存在的代理路由是:
- `/api/govdoc-audit/[...path]`
这代表至少在当前仓库状态下:
- 代码意图已经切到新模块路径
- 代理实现仍停留在旧路径
运行时风险很直接:
- 前端请求可能 404
- 或必须依赖外部额外 rewrite 才能工作
#### 偏差 5:前端页面路由已经叫 `govdoc`,但菜单和别名体系仍残留大量 `govdoc-audit`
当前仓库同时存在两套路由语义:
- 新路由:`/govdoc`
- 旧路由:`/govdoc-audit`
遗留点包括:
- 菜单映射
- 最小权限白名单
- breadcrumb
- 角色权限页面提示文案
- route alias 配置
- 某些入口页跳转目标
这意味着当前前端不是“已经迁完”,而是处于:
- 新旧路径并存
- 新路径只接了一半
直接后果:
- 菜单权限和真实页面可能不一致
- 用户访问入口与页面跳转链路可能绕回旧路径
- 后续排障会出现“接口没问题但入口不对”的假象
#### 偏差 6:SQL 菜单种子与前端真实详情路由不一致
当前 SQL 种子注册的是:
- `/govdoc/detail`
但前端真实详情页现在是:
- `/govdoc/[id]`
而业务推荐最终应为:
- `/govdoc/detail/:documentId`
这说明目前菜单/RBAC 路由、Next 页面路由、业务路由设计三者还没有统一。
### 4.3 后端服务实现偏差
#### 偏差 7`GovdocServiceImpl` 大部分接口仍是占位实现
当前真正有一定查询逻辑的主要只有:
- `GetRunResult()`
其余关键接口基本仍是骨架:
- `UploadDocument()`
- `ListDocuments()`
- `GetDocumentDetail()`
- `UpdateDocument()`
- `DeleteDocument()`
- `CreateRun()`
- `GetRunStatus()`
- `GetRunFindings()`
- `GetRunEntities()`
- `GetRunParagraphs()`
- `GetRunStructure()`
- `GetRunOutline()`
- `GetReportHtml()`
- `GetReportDocx()`
- `DownloadOriginal()`
这意味着:
- 控制器接口已经暴露出来
- 但绝大部分业务服务还没有真正接平台主档、结果表、OSS、权限范围
#### 偏差 8`GetRunResult()` 所依赖字段与当前建表 SQL 不一致
代码查询了:
- `govdoc_runs.rules_path`
- `govdoc_rule_results.skip_reason`
但当前建表 SQL 中并没有这两个字段。
这属于很典型的:
- 服务代码和 DDL 没同步
直接后果:
- 查询会报 SQL 字段不存在
- 或部署后不同环境表现不一致
#### 偏差 9:结果接口仍是 run 视角,缺少“文档详情聚合接口”
业务上真正需要的详情能力是:
- 输入 `documentId`
- 返回文档基础信息
- 返回最新 run 摘要
- 返回历史 run 列表
- 默认选中最新 run 的 findings / structure / outline / artifacts
当前后端虽然有:
- `GetDocumentDetail(documentId)`
但只是占位返回 `{"documentId": ...}`
这意味着真正业务需要的“文档详情聚合接口”还没做。
### 4.4 Worker / Bridge / 运行时偏差
#### 偏差 10Worker 调用 `GovdocRunner.Execute()` 时缺少必填 `RulesPath`
`GovdocRunner.Execute()` 的签名要求:
- `RulesPath: str`
但 Celery 任务调用时没有传这个参数。
这意味着当前一旦任务真正执行到这里,运行时就会直接报错。
#### 偏差 11:状态更新参数名不一致,`phase` 很可能写不进去
`StorageAdapter.UpdateRunStatus()` 定义的是:
- `Phase`
但调用时传的是:
- `phase=...`
由于这里大小写不一致,`Phase` 不会接收到值。
直接后果:
- `govdoc_runs.phase` 可能一直不更新
- 前端状态页看到的阶段信息不可信
#### 偏差 12:结果持久化只写了规则结果和产物,没有把文档维度聚合真正打通
当前 Bridge 更像是在做:
- run 生命周期管理
- 规则结果入库
- 报告产物索引入库
但还没有完全补齐:
- 文档最新 run 的选择逻辑
- 文档级状态汇总
- 文档级最新分数/最新结果统计口径
- 首页统计查询口径
所以底层虽然开始有 run 表,但“平台层文档视角”还没有真正建立。
### 4.5 权限、可见范围、删除语义偏差
#### 偏差 13:控制器做了登录校验,但还没真正落细粒度数据范围
当前控制器统一用了:
- `verify_access_token`
说明登录态有了。
但真正业务要求的是:
- `common` 仅本人
- `admin` 仅本地区
- 报告/原文/结果/历史全部继承文档范围
而这些逻辑目前还没有在 `GovdocServiceImpl` 中真正实现。
所以当前状态更接近:
- 只有“是否登录”
- 还没有“能看谁的数据”
#### 偏差 14:删除语义业务上要求软删除,但当前服务只是占位返回
业务上已经确认删除必须是:
- 软删除
当前 `DeleteDocument()` 只是返回:
- `{"documentId": ..., "deleted": True}`
没有真正处理:
- 文档主档软删
- 文件记录是否保留
- run 是否跟随隐藏
- 报告产物是否继续留存但不可见
#### 偏差 15:普通用户不应默认拥有更新/删除,但当前接口已开放,权限键还没真正落表
接口层已经有:
- `PATCH /documents/{documentId}`
- `DELETE /documents/{documentId}`
这本身没错,因为管理员要用。
但当前问题是:
- 角色默认能力
- 权限键
- 按角色/地区/本人范围控制
这些还没真正打通。
所以现在属于“接口先开出来了,但权限收口还没跟上”。
---
## 5. 当前代码实际反映出的真实业务状态
从代码现状反推,当前模块实际上处于下面这个阶段:
### 5.1 已经确定的部分
- 决定把 `govdoc-audit` 收口进主平台
- 决定复用主平台账号、权限、文档主档、OSS、Celery
- 决定新建 `govdoc_runs / govdoc_rule_results / govdoc_report_artifacts`
- 决定后端 API 挂到 `/govdoc`
- 决定前端页面也逐步迁到 `/govdoc`
这部分方向是对的。
### 5.2 还没有完全拍平的部分
- 详情页到底按 `documentId` 还是 `runId`
- 列表项到底代表文档还是运行
- 前端入口到底以 `govdoc` 还是 `govdoc-audit` 为准
- 路由种子、前端页面、菜单权限是否统一
- 文档级详情聚合接口长什么样
- 统计到底按 run 还是按文档最新结果
### 5.3 因此当前模块不是“坏了”,而是“未收口”
更准确地说:
- 架构方向基本正确
- 但业务主模型尚未完全统一
- 结果导致前端、后端、SQL、任务链各自朝着相近但不完全一致的方向在写
这就是当前所有错位问题的根因。
---
## 6. 建议的正式收口方案
为了后面少返工,建议现在先正式拍板以下模型,不再来回摇摆。
### 6.1 正式主模型
- 主对象:`document`
- 执行对象:`run`
- 结果对象:`rule_result`
- 产物对象:`artifact`
### 6.2 正式页面路由
- `/govdoc`
- `/govdoc/home`
- `/govdoc/upload`
- `/govdoc/audits``/govdoc/list`
- `/govdoc/detail/[documentId]`
- `/govdoc/rules`
建议最终二选一:
- 要么统一保留 `/govdoc/audits`
- 要么统一收敛为 `/govdoc/list`
不要两个同时长期并存。
从当前业务文案看,我更建议统一为:
- `/govdoc/list`
- `/govdoc/detail/[documentId]`
因为它更贴近“文档列表 / 文档详情”的业务语义。
### 6.3 正式接口模型
建议稳定成两层接口。
第一层:文档接口
- `POST /govdoc/documents`
- `GET /govdoc/documents`
- `GET /govdoc/documents/{documentId}`
- `PATCH /govdoc/documents/{documentId}`
- `DELETE /govdoc/documents/{documentId}`
- `GET /govdoc/documents/{documentId}/original`
- `GET /govdoc/documents/{documentId}/runs`
第二层:run 结果接口
- `POST /govdoc/runs`
- `GET /govdoc/runs/{runId}`
- `GET /govdoc/runs/{runId}/result`
- `GET /govdoc/runs/{runId}/findings`
- `GET /govdoc/runs/{runId}/entities`
- `GET /govdoc/runs/{runId}/structure`
- `GET /govdoc/runs/{runId}/outline`
- `GET /govdoc/runs/{runId}/paragraphs`
- `GET /govdoc/runs/{runId}/report/html`
- `GET /govdoc/runs/{runId}/report/docx`
其中:
- 页面入口以 `documentId` 为准
- 结果读取以 `runId` 为准
### 6.4 正式详情页返回模型
建议把 `GET /govdoc/documents/{documentId}` 做成真正的聚合详情:
- 文档基本信息
- 当前状态
- 当前最新 run 摘要
- 历史 run 列表
- 当前默认 runId
- 当前可用报告产物摘要
页面进入后:
- 先拿文档聚合详情
- 再按默认 runId 拉 findings / structure / outline / entities
这样前端会非常稳定。
---
## 7. 分阶段补齐计划
以下是建议执行顺序。顺序不能乱,因为前一层不收口,后一层会反复返工。
### Phase 1:先把业务主模型收口
目标:
- 明确 `document` 是主对象
- 统一详情入口为 `documentId`
- 统一列表项表达的是文档
具体动作:
- 前端详情路由改为 `/govdoc/detail/[documentId]`
- 列表接口返回文档维度 DTO,不再混用 `documentId/runId`
- 列表项保留 `latestRunId` 字段,但不能拿它充当主键
- SQL 路由种子、前端菜单、页面跳转统一成同一套路由
产出结果:
- 页面导航语义稳定
- 历史 run 可自然挂入详情页
- 后续删除、下载、权限继承都有明确主语
### Phase 2:补齐后端文档主链路
目标:
- 真正打通“上传文档 -> 创建主档 -> 标记为 govdoc -> 自动建 run”
具体动作:
- `UploadDocument()` 接入平台文档主档服务
- 文档主档写入 `engine_type='govdoc'`
- 上传后默认自动触发 `CreateRun()`
- `CreateRun()` 真正写入 `govdoc_runs`
- 投递 Celery 任务
- 回写 `leaudit_documents.processing_status/current_run_id`
产出结果:
- 用户上传后能真正看到一条 govdoc 文档
- 文档状态从上传到处理中可追踪
### Phase 3:修复 Worker 运行闭环
目标:
- 让异步任务真正可跑完
具体动作:
- 修复 `RulesPath` 来源与传参
- 修复 `UpdateRunStatus()``Phase/phase` 参数不一致
- 明确默认规则文件解析策略
- 补 run 开始时间、结束时间、失败信息
- 补结果持久化后的文档最新状态回写
产出结果:
- Celery worker 可以稳定完成一次 run
- 失败时数据库里有清晰可读的错误和状态
### Phase 4:补齐结果查询与详情聚合
目标:
- 让详情页真正能按业务方式展示
具体动作:
- `GetDocumentDetail()` 改为聚合查询
- 增加 `GET /documents/{documentId}/runs`
- `GetRunFindings()` / `GetRunEntities()` / `GetRunStructure()` / `GetRunOutline()` / `GetReportHtml()` / `GetReportDocx()` / `DownloadOriginal()` 全部接真实数据
- HTML/DOCX/original 下载统一继承文档权限
产出结果:
- 详情页默认展示最新结果
- 用户可切换历史 run
- 报告与原文下载链路完整
### Phase 5:补齐权限、数据范围、软删除
目标:
- 让模块真正符合平台权限模型
具体动作:
-`govdoc:*:*` 权限键
- `common` 按本人过滤
- `admin` 按地区过滤
- `provincial_admin/super_admin` 按更大范围过滤
- 删除改为主档软删
- 结果/报告/历史查询全部继承文档范围
产出结果:
- 模块可以上线到真实账号体系
- 不会出现越权查看他人公文结果
### Phase 6:清理前端旧壳与统计口径
目标:
- 把遗留的 `govdoc-audit` 痕迹真正收口
具体动作:
- 统一 `/govdoc``/govdoc-audit` 路由
- 菜单、breadcrumb、minimal-scope、route alias 统一
- 首页统计改为按文档最新 run 聚合
- 前端隐藏重跑按钮,但保留后端能力
产出结果:
- 前端不再混用新旧模块名
- 用户看到的是完整一致的一套业务入口
---
## 8. 优先级建议
如果要按最小可用闭环来排,我建议优先级如下:
### P0:不修就跑不起来
- 统一详情主模型为 `documentId`
- 修复 `/api/govdoc` 与实际代理不一致
- 实现 `UploadDocument()` / `CreateRun()`
- 修复 `RulesPath` 缺失
- 修复 `phase` 状态写回失败
- 修复 DDL 与服务代码字段不一致
### P1:能跑但还不能交付
- 实现文档聚合详情
- 实现历史 run 查询
- 实现 findings / structure / outline / artifacts 真实读取
- 实现原文/HTML/DOCX 下载
### P2:上线前必须补齐
- 权限键
- 数据范围
- 软删除
- 首页统计口径
- 新旧路由与菜单彻底收口
---
## 9. 最终建议
当前最重要的不是马上补某一个接口,而是先统一下面这句话:
- **内部公文模块是“文档为主、run 为辅、详情按 documentId 进入、结果按 runId 展示”的业务模型。**
只要这个模型拍板,后面所有实现都可以顺下来:
- 列表怎么查
- 详情怎么开
- 历史怎么挂
- 统计怎么算
- 权限怎么继承
- 删除怎么定义
如果这句话不拍板,后面无论先修前端还是后端,都会反复返工。
---
## 10. 审核结论
基于当前业务确认和代码现状,我给出的正式判断是:
- 当前模块方向是对的
- 当前实现还未闭环
- 最大问题不是单点 bug,而是“主业务模型尚未完全统一”
建议正式以本文方案收口后,再进入代码补齐阶段。
这样改一次,后面才不会反复拆。