# 内部公文模块现状偏差与对接补齐计划 ## 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 / 运行时偏差 #### 偏差 10:Worker 调用 `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,而是“主业务模型尚未完全统一” 建议正式以本文方案收口后,再进入代码补齐阶段。 这样改一次,后面才不会反复拆。