569 lines
14 KiB
Markdown
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`
|