# 老项目交叉评查逻辑梳理 > 目标:整理老项目 `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`