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

14 KiB

老项目交叉评查逻辑梳理

目标:整理老项目 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_progresscompleted
  • task_name:任务名称
  • doc_type:任务绑定的文档类型编码
  • task_type:任务类型,如 CITYDISTRICT

业务含义:

  • 一个任务绑定一组参与人和一批文档
  • 创建者和主要负责人拥有更高权限

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

业务含义:

  • 针对某个评查结果提出加分或减分建议
  • 常见状态:pendingapprovedrejected

3.4 cross_opinion_votes

交叉评查投票表。

核心字段:

  • proposal_id
  • voter_id
  • vote_type
  • deleted_at

业务含义:

  • 参与者对提案表态
  • 支持 agreedisagreecancel
  • 提案创建后,系统会自动给提案人补一票 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

主要规则:

  • 只有任务参与者能投票
  • 已经 approvedrejected 的提案不允许再投/撤销
  • 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_idprincipal_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