Files
leaudit-platform-backend/docs/交叉评查/老项目交叉评查逻辑梳理.md
T

569 lines
14 KiB
Markdown

# 老项目交叉评查逻辑梳理
> 目标:整理老项目 `docauditai` 中“交叉评查”模块的真实业务链路、数据模型、权限边界和当前新项目承接情况,便于后续迁移、重构和对照实现。
## 1. 项目路径与主要参考文件
### 1.1 老项目路径
- 老项目根目录:`/home/wren-dev/Porject/docauditai`
### 1.2 当前新项目前端路径
- 新前端目录:`/home/wren-dev/Porject/leaudit-platform/new_doc_review`
### 1.3 本次整理的核心参考文件
- 老逻辑总说明:`/home/wren-dev/Porject/docauditai/dcos/cross_review_business_logic.md`
- 老表关系图:`/home/wren-dev/Porject/docauditai/dcos/cross_review_table_relationships.md`
- 老路由主文件:`/home/wren-dev/Porject/docauditai/app/routes/v2/crossreview/cross_review.py`
- 老服务主文件:`/home/wren-dev/Porject/docauditai/services/documents/v2/cross_review_service.py`
- 历史接口文档:`new_doc_review/auth_doc/交叉评查接口文档.md`
## 2. 一句话定义
交叉评查不是重新跑 OCR、抽取、规则评查,而是围绕已经存在的评查结果做“多人协同复核”。
它的核心目标是:
- 把一批文档挂到一个交叉评查任务里
- 让任务参与人围绕具体评查点发起加分/减分提案
- 让参与人对提案投票
- 提案通过后,把分数回写到原始评查结果
- 由负责人手动确认文档评查完成,并最终推动任务完成
换句话说,这是一条“评后协作复核”流程,不是底层文档处理流水线。
## 3. 核心数据模型
交叉评查主要依赖以下几张表:
### 3.1 `cross_examination_tasks`
交叉评查任务主表。
核心字段:
- `assigner_id`:任务创建者
- `user_ids`:参与评查的用户列表
- `principal_user_ids`:主要负责人列表
- `task_status`:任务状态,常见值有 `in_progress``completed`
- `task_name`:任务名称
- `doc_type`:任务绑定的文档类型编码
- `task_type`:任务类型,如 `CITY``DISTRICT`
业务含义:
- 一个任务绑定一组参与人和一批文档
- 创建者和主要负责人拥有更高权限
### 3.2 `cross_task_document_mapping`
交叉评查任务与文档的映射表。
核心字段:
- `task_id`
- `document_id`
- `audit_status`
- `deleted_at`
业务含义:
- 一条记录表示某文档被纳入某个交叉评查任务
- `audit_status=1` 表示该文档在该任务内已确认完成
- 这里的 `audit_status` 是任务内状态,不等于 `documents.audit_status`
### 3.3 `cross_scoring_proposals`
交叉评分提案表。
核心字段:
- `document_id`
- `evaluation_point_id`
- `evaluation_result_id`
- `proposer_id`
- `proposed_score`
- `reason`
- `status`
- `deleted_at`
业务含义:
- 针对某个评查结果提出加分或减分建议
- 常见状态:`pending``approved``rejected`
### 3.4 `cross_opinion_votes`
交叉评查投票表。
核心字段:
- `proposal_id`
- `voter_id`
- `vote_type`
- `deleted_at`
业务含义:
- 参与者对提案表态
- 支持 `agree``disagree``cancel`
- 提案创建后,系统会自动给提案人补一票 `agree`
### 3.5 `evaluation_results`
旧评查结果表,是交叉评查当前的评分底座。
主要作用:
- 读取当前机器分 / 最终分 / 评查结果消息
- 在提案通过后更新 `final_score`
### 3.6 `evaluation_points`
评查点定义表。
交叉评查依赖它读取:
- 单点评查满分 `score`
- 风险级别
- `fail_message`
- `post_action`
- `suggestion_message_type`
## 4. 主业务链路
## 4.1 创建任务
核心服务:
- `assign_cross_examination_tasks()`
- 文件位置:`/home/wren-dev/Porject/docauditai/services/documents/v2/cross_review_service.py`
处理逻辑:
1.`assigner_id` 强制加入 `user_ids` 并去重
2. 创建 `cross_examination_tasks`
3. 为每个文档写入 `cross_task_document_mapping`
4. 初始状态设置为 `task_status = in_progress`
补充说明:
- 在老项目里,任务创建很多时候不是单独走一个“先建任务”的管理入口
- 更常见的是在上传文档后直接触发任务分配
- 相关入口可见:`/home/wren-dev/Porject/docauditai/app/routes/v2/documents/documents.py`
## 4.2 任务列表
接口:
- `POST /tasks/user_tasks`
实现位置:
- 路由:`/home/wren-dev/Porject/docauditai/app/routes/v2/crossreview/cross_review.py`
逻辑特点:
-`current_user.id = ANY(user_ids)` 查询用户参与的任务
- 进度不是看 `documents.audit_status`
- 而是看 `cross_task_document_mapping.audit_status`
返回内容通常包括:
- 任务 ID
- 任务名称
- 任务状态
- 文档类型
- 任务类型
- 创建时间
- 进度百分比
- 总文档数
- 参与地区
## 4.3 任务下文档列表
老项目实际上有两套接口思路。
### 旧列表接口
- `POST /tasks/{task_id}/documents`
特点:
- 返回任务下文档的分页列表
- 聚合每个文档的评查统计、问题摘要、得分等信息
### 新版版本归纳接口
- `GET /tasks/{task_id}/documents`
对应服务:
- `get_task_documents_with_versions()`
- 文件:`/home/wren-dev/Porject/docauditai/services/documents/v2/cross_review_service.py`
版本归纳规则:
- 同一任务内 `(name, type_id)` 相同的文档归为一组
- 组内按 `created_at` 降序排列
- 最新上传的是当前版本
- 其他的是历史版本
统计信息来源:
- 文档实体:`documents`
- 评查统计:`evaluation_results + evaluation_points`
- 得分规则:优先 `final_score`,否则按机器结果/通过结果推导
## 4.4 发起评分提案
接口:
- `POST /proposals`
路由位置:
- `/home/wren-dev/Porject/docauditai/app/routes/v2/crossreview/cross_review.py`
服务位置:
- `create_scoring_proposal()`
- `/home/wren-dev/Porject/docauditai/services/documents/v2/cross_review_service.py`
核心校验规则:
1. 用户必须有文档访问权限
2. `evaluation_result_id` 必填
3. `evaluation_result_id` 必须真实属于当前 `document_id`
4. 文档必须已纳入某个交叉评查任务
5. 提案人必须是该任务参与者
6. 同一用户不能对同一文档、同一评查点重复创建未删除提案
7. 不允许创建 0 分提案
8. 当前分数已经为 0 时,不允许继续扣分
9. 当前分数已经达到该评查点满分时,不允许继续加分
创建成功后:
1. 插入 `cross_scoring_proposals`
2. 自动给提案人插入一条 `agree` 投票
3. 立即触发提案状态检查
## 4.5 投票
接口:
- `POST /proposals/{proposal_id}/votes`
服务:
- `create_opinion_vote()`
支持动作:
- `agree`
- `disagree`
- `cancel`
主要规则:
- 只有任务参与者能投票
- 已经 `approved``rejected` 的提案不允许再投/撤销
- `cancel` 本质是把投票软删除
## 4.6 提案状态判定
内部方法:
- `_check_and_process_proposal_status()`
判定规则:
1. 找到提案关联文档
2. 取该文档所在的最新任务
3. 获取任务参与人数 `n`
4. 统计同意票 `a` 与反对票 `d`
5. 通过阈值为 `floor(n / 2) + 1`
状态流转:
- `agree >= threshold` -> `approved`
- `disagree >= threshold` -> `rejected`
- 即使剩余未投票者全部同意也无法达到阈值 -> `rejected`
- 否则保持 `pending`
这说明老逻辑走的是“绝对多数制”,不是全票制。
## 4.7 提案通过后的分数回写
内部方法:
- `_update_evaluation_result_score()`
处理逻辑:
1. 找到提案关联的 `evaluation_result_id`
2. 读取当前 `evaluation_results.final_score / machine_score`
3. 读取评查点满分 `evaluation_points.score`
4.`proposed_score` 累加到最终分数
5. 做边界保护:
- 最低不小于 0
- 最高不超过评查点满分
结论:
- 老交叉评查的“最终结果”是直接回写旧评查结果表 `evaluation_results.final_score`
## 4.8 查看文档上的所有提案
接口:
- `POST /proposals/document`
服务:
- `get_proposals_by_document()`
典型返回字段:
- 提案 ID
- 评查点名称
- 建议分值
- 理由
- 提案人
- 已投票列表
- 同意人
- 反对人
- 待投票人
- 当前用户是否可投票
- 发现问题文案
- 提案状态
这是详情页“交叉意见区”的核心数据接口。
## 4.9 获取待处理提案列表
接口:
- `POST /proposals/details`
服务:
- `get_proposals_with_details()`
作用:
- 查当前用户需要处理的待投票提案
过滤规则:
- 只看 `pending`
- 排除自己发起的提案
- 只看自己参与任务下的文档
## 4.10 检查是否存在未投票用户
接口:
- `POST /proposals/document/check_pending_votes`
服务:
- `check_pending_votes_by_document()`
权限要求:
- 只有任务创建者或主要负责人可以调用
作用:
- 在确认完成前检查该文档下是否还有提案未完成投票
## 4.11 确认文档评查完成
接口:
- `POST /tasks/{task_id}/documents/{document_id}/complete`
服务:
- `complete_document_audit()`
主要规则:
- 只有 `assigner_id``principal_user_ids` 中的用户才有权限确认
- 确认后会把 `cross_task_document_mapping.audit_status` 改为 `1`
- 如果任务下所有文档都完成,则把 `cross_examination_tasks.task_status` 改成 `completed`
注意:
- 文档是否完成是“任务内状态”
- 任务是否完成是所有任务内文档完成后的汇总状态
## 4.12 检查当前用户是否可确认完成
接口:
- `GET /tasks/{task_id}/can-confirm`
服务:
- `check_confirm_permission()`
作用:
- 前端先调用此接口,再决定是否显示“确认完成”按钮
## 4.13 向任务补文档 / 补附件
相关接口:
- `POST /tasks/{task_id}/documents/{document_id}/append_attachments`
- `POST /tasks/{task_id}/upload_documents`
核心规则:
- 只有任务创建者或主要负责人可以操作
- 追加附件会创建新文档,而不是覆盖老文档
- 新文档会重新写入 `cross_task_document_mapping`
- 新文档默认 `audit_status = 0`
- 同名同类型文档会自动并入版本链
## 5. 权限模型
交叉评查存在三层权限。
### 5.1 文档访问权限
发起提案和查看文档提案前,会先检查用户是否有权访问该文档。
### 5.2 任务参与权限
任务参与者来自 `cross_examination_tasks.user_ids`
参与者可以:
- 发起提案
- 对提案投票
- 查看自己参与任务下的任务、文档、提案和进度
### 5.3 高权限用户
高权限用户包括:
- 任务创建者 `assigner_id`
- 主要负责人 `principal_user_ids`
高权限用户可以:
- 确认文档评查完成
- 检查文档下是否仍有待投票人员
- 为任务文档追加附件
- 向任务继续上传新文档
## 6. 交叉评查与 PostgREST 权限扩展
老项目还有一个关键点:交叉评查不是只靠业务接口做权限控制,代理层也会扩展文档可见范围。
关键文件:
- `/home/wren-dev/Porject/docauditai/app/routes/postgrest.py`
它的作用是:
- 获取用户参与的交叉评查任务
- 提取这些任务关联的文档 ID
- 把这些文档加入用户可访问范围
这意味着:
- 用户即使不在原始文档所属部门
- 只要参与了交叉评查任务,也可能被允许跨部门访问这些任务文档
这部分是老系统交叉评查跨部门协同的重要权限补丁。
## 7. 几个必须记住的业务事实
- 交叉评查不是重新跑评查,而是消费旧 `evaluation_results`
- 提案通过后会直接回写 `evaluation_results.final_score`
- 文档完成状态落在 `cross_task_document_mapping.audit_status`
- 任务完成状态落在 `cross_examination_tasks.task_status`
- 文档完成不是投票自动完成,而是负责人手动确认
- 同名同类型文档在任务内会被视为一条版本链
## 8. 当前新项目承接情况
当前新项目前端已经有完整交叉评查页面,但其业务底座仍然大量依赖老接口和老表思路。
### 8.1 新前端页面
- `new_doc_review/app/routes/cross-checking._index.tsx`
- `new_doc_review/app/routes/cross-checking.result.tsx`
- `new_doc_review/app/routes/cross-checking.upload.tsx`
### 8.2 新前端接口封装
- `new_doc_review/app/api/cross-checking/cross-files.ts`
- `new_doc_review/app/api/cross-checking/cross-file-result.ts`
- `new_doc_review/app/api/cross-checking/cross-files-upload.ts`
### 8.3 仍在使用的老接口口径
前端仍直接调用这些接口:
- `/api/v2/cross_review/...`
- `/admin/v2/cross_review/...`
- `/admin/v2/documents/cross_review/documents/upload_and_assign`
- `/admin/cross_review/tasks/assign`
### 8.4 当前观察结论
- 新前端页面壳子已经迁到 `new_doc_review`
- 但交叉评查业务底座仍大体延续老项目 `docauditai` 的接口和数据模型
-`fastapi_modules/fastapi_leaudit` 中,没有检索到一套完整的新交叉评查业务路由/服务闭环
- 只看到少量兼容读取老表的痕迹,例如 `cross_scoring_proposals`
## 9. 后续迁移时最难拆的三个耦合点
如果后续要把交叉评查彻底迁到新平台,最难拆的不是页面,而是以下 3 个耦合点:
### 9.1 提案通过后回写旧评查结果
- `cross_scoring_proposals` -> `evaluation_results.final_score`
### 9.2 任务内文档完成状态
- `cross_task_document_mapping.audit_status` 决定任务内文档是否完成
### 9.3 跨任务的文档访问权限扩展
- `app/routes/postgrest.py` 对交叉任务文档做了额外放行
## 10. 迁移判断
从当前代码和前端调用情况看,可以做出一个比较明确的判断:
- 现在的交叉评查“前端承载是新的”
- 但“业务模型、接口语义、分数回写、权限扩展”仍然主要是老系统延续下来的
因此,后续如果要迁移:
- 不能只迁页面
- 也不能只补几个接口
- 必须连同任务模型、提案模型、投票模型、评分回写模型、跨部门访问权限一起迁
## 11. 推荐继续查看的文件
如果后续要继续深挖,建议按这个顺序看:
1. `/home/wren-dev/Porject/docauditai/dcos/cross_review_business_logic.md`
2. `/home/wren-dev/Porject/docauditai/services/documents/v2/cross_review_service.py`
3. `/home/wren-dev/Porject/docauditai/app/routes/v2/crossreview/cross_review.py`
4. `new_doc_review/app/api/cross-checking/cross-files.ts`
5. `new_doc_review/app/api/cross-checking/cross-file-result.ts`
---
整理时间:`2026-05-07`