feat: migrate cross review to v3 leaudit flow
This commit is contained in:
@@ -0,0 +1,562 @@
|
||||
# 新项目交叉评查实现方案设计稿
|
||||
|
||||
> 目标:基于 `leaudit-platform` 当前代码结构,给出交叉评查在新项目中的推荐实现方案,并明确数据模型、接口设计、目录落点、迁移顺序与实施边界。
|
||||
|
||||
## 1. 背景判断
|
||||
|
||||
结合当前仓库代码可以确认:
|
||||
|
||||
- 前端交叉评查页面已经存在于 `new_doc_review`
|
||||
- RBAC 路由中已经存在 `/cross-checking`
|
||||
- 新平台文档与评查底座已经切到 `leaudit_*`
|
||||
- 但交叉评查协作层仍未完整迁入 `fastapi_modules/fastapi_leaudit`
|
||||
|
||||
当前现状更接近:
|
||||
|
||||
- 页面壳子是新的
|
||||
- 文档与评查引擎底座是新的
|
||||
- 交叉评查业务逻辑仍然大量沿用老系统思维和老接口口径
|
||||
|
||||
因此,交叉评查在新项目中的核心任务,不是“新做一个页面”,而是把“协作复核层”从老系统迁到新平台。
|
||||
|
||||
## 2. 总体设计原则
|
||||
|
||||
推荐遵循以下原则:
|
||||
|
||||
### 2.1 交叉评查是协作层,不是评查引擎层
|
||||
|
||||
交叉评查不负责:
|
||||
|
||||
- OCR
|
||||
- 文本抽取
|
||||
- 规则执行
|
||||
- 原始机器评查结果生成
|
||||
|
||||
交叉评查只负责:
|
||||
|
||||
- 任务
|
||||
- 参与人
|
||||
- 文档挂载
|
||||
- 提案
|
||||
- 投票
|
||||
- 完成确认
|
||||
- 结果展示叠加
|
||||
|
||||
### 2.2 原始评查结果尽量不可变
|
||||
|
||||
不建议像老系统那样把通过提案直接回写到旧 `evaluation_results.final_score`。
|
||||
|
||||
新平台里更合理的方式是:
|
||||
|
||||
- `leaudit_rule_results` 保留机器评查真相
|
||||
- `leaudit_review_point_audits` 保留人工审核覆盖
|
||||
- `leaudit_cross_review_proposals` 保留交叉评查加减分提案
|
||||
- 页面展示时动态聚合出最终展示分数
|
||||
|
||||
### 2.3 交叉评查单独建模,不污染 LeAudit 核心表
|
||||
|
||||
推荐新增专属表:
|
||||
|
||||
- `leaudit_cross_review_tasks`
|
||||
- `leaudit_cross_review_task_members`
|
||||
- `leaudit_cross_review_task_documents`
|
||||
- `leaudit_cross_review_proposals`
|
||||
- `leaudit_cross_review_votes`
|
||||
|
||||
### 2.4 版本归纳使用新平台版本能力
|
||||
|
||||
不要继续使用老系统“同名 + 同类型推断版本”的方案作为主路径。
|
||||
|
||||
新平台已有:
|
||||
|
||||
- `leaudit_documents.versionGroupKey`
|
||||
- `leaudit_documents.versionNo`
|
||||
- `leaudit_documents.previousVersionId`
|
||||
- `leaudit_documents.rootVersionId`
|
||||
- `leaudit_documents.isLatestVersion`
|
||||
|
||||
交叉评查列表应优先基于这些字段实现版本归纳。
|
||||
|
||||
### 2.5 权限必须后端收口
|
||||
|
||||
前端可以决定是否显示按钮,但最终权限校验必须在后端 service 层完成。
|
||||
|
||||
不能继续依赖前端自己拼 PostgREST 查询判断权限。
|
||||
|
||||
## 3. 新项目里可直接复用的基础能力
|
||||
|
||||
## 3.1 文档主链路
|
||||
|
||||
现有能力:
|
||||
|
||||
- 上传文档:`fastapi_modules/fastapi_leaudit/controllers/documentController.py`
|
||||
- 文档服务:`fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py`
|
||||
- 文档主表:`fastapi_modules/fastapi_leaudit/models/leauditDocument.py`
|
||||
- 文档文件表:`fastapi_modules/fastapi_leaudit/models/leauditDocumentFile.py`
|
||||
|
||||
可复用点:
|
||||
|
||||
- 文档上传
|
||||
- OSS 存储
|
||||
- 文档版本管理
|
||||
- 附件追加
|
||||
- 文档详情读取
|
||||
|
||||
## 3.2 评查运行与结果主链路
|
||||
|
||||
现有能力:
|
||||
|
||||
- 评查运行主表:`fastapi_modules/fastapi_leaudit/models/leauditAuditRun.py`
|
||||
- 评查运行控制器:`fastapi_modules/fastapi_leaudit/controllers/auditController.py`
|
||||
- 评查详情聚合:`DocumentServiceImpl.GetReviewPoints()`
|
||||
|
||||
可复用点:
|
||||
|
||||
- 评查运行追踪
|
||||
- 详情页规则结果聚合
|
||||
- 人工审核结果叠加
|
||||
|
||||
## 3.3 详情页结果聚合
|
||||
|
||||
现有 VO:
|
||||
|
||||
- `fastapi_modules/fastapi_leaudit/domian/vo/reviewPointVo.py`
|
||||
|
||||
现有能力:
|
||||
|
||||
- `ReviewPointsAggregateVO`
|
||||
- `scoring_proposals`
|
||||
- `ReviewPointResultVO.finalScore`
|
||||
- `ReviewPointResultVO.machineScore`
|
||||
|
||||
这意味着:
|
||||
|
||||
- 详情页主结构已经可承载交叉评查提案数据
|
||||
- 只需把“提案来源”和“最终分数计算方式”替换为新模型
|
||||
|
||||
## 4. 目标架构
|
||||
|
||||
推荐架构分成两层:
|
||||
|
||||
### 4.1 LeAudit 评查结果层
|
||||
|
||||
职责:
|
||||
|
||||
- 文档上传
|
||||
- 规则执行
|
||||
- 机器评查结果生成
|
||||
- 人工审核覆盖记录
|
||||
|
||||
主要表:
|
||||
|
||||
- `leaudit_documents`
|
||||
- `leaudit_document_files`
|
||||
- `leaudit_audit_runs`
|
||||
- `leaudit_rule_results`
|
||||
- `leaudit_review_point_audits`
|
||||
|
||||
### 4.2 Cross Review 协作层
|
||||
|
||||
职责:
|
||||
|
||||
- 交叉评查任务
|
||||
- 成员管理
|
||||
- 任务文档挂载
|
||||
- 加减分提案
|
||||
- 投票
|
||||
- 文档完成确认
|
||||
- 协作态进度管理
|
||||
|
||||
主要表:
|
||||
|
||||
- `leaudit_cross_review_tasks`
|
||||
- `leaudit_cross_review_task_members`
|
||||
- `leaudit_cross_review_task_documents`
|
||||
- `leaudit_cross_review_proposals`
|
||||
- `leaudit_cross_review_votes`
|
||||
|
||||
## 5. 新数据模型设计
|
||||
|
||||
## 5.1 `leaudit_cross_review_tasks`
|
||||
|
||||
任务主表。
|
||||
|
||||
建议字段:
|
||||
|
||||
- `id`
|
||||
- `task_name`
|
||||
- `task_type`
|
||||
- `doc_type_id`
|
||||
- `doc_type_code`
|
||||
- `assigner_id`
|
||||
- `status`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- `deleted_at`
|
||||
|
||||
说明:
|
||||
|
||||
- `status` 建议值:`pending`、`in_progress`、`completed`
|
||||
- `doc_type_id` 可用于快速关联新平台文档类型
|
||||
- `doc_type_code` 用于兼容前端和历史口径
|
||||
|
||||
## 5.2 `leaudit_cross_review_task_members`
|
||||
|
||||
任务成员表。
|
||||
|
||||
建议字段:
|
||||
|
||||
- `id`
|
||||
- `task_id`
|
||||
- `user_id`
|
||||
- `member_role`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- `deleted_at`
|
||||
|
||||
说明:
|
||||
|
||||
- `member_role` 建议值:`participant`、`principal`
|
||||
- 任务创建者不建议仅放数组,应显式保存在任务主表 `assigner_id`
|
||||
|
||||
## 5.3 `leaudit_cross_review_task_documents`
|
||||
|
||||
任务文档表。
|
||||
|
||||
建议字段:
|
||||
|
||||
- `id`
|
||||
- `task_id`
|
||||
- `document_id`
|
||||
- `audit_status`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- `deleted_at`
|
||||
|
||||
说明:
|
||||
|
||||
- `audit_status=0` 表示未完成
|
||||
- `audit_status=1` 表示已确认完成
|
||||
- 这是任务内状态,不是文档全局状态
|
||||
|
||||
## 5.4 `leaudit_cross_review_proposals`
|
||||
|
||||
提案表。
|
||||
|
||||
建议字段:
|
||||
|
||||
- `id`
|
||||
- `task_id`
|
||||
- `document_id`
|
||||
- `rule_result_id`
|
||||
- `proposer_id`
|
||||
- `proposed_score_delta`
|
||||
- `reason`
|
||||
- `status`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- `deleted_at`
|
||||
|
||||
说明:
|
||||
|
||||
- `rule_result_id` 应关联 `leaudit_rule_results.id`
|
||||
- `proposed_score_delta` 使用增量值,正数加分,负数扣分
|
||||
- `status` 建议值:`pending`、`approved`、`rejected`、`cancelled`
|
||||
|
||||
## 5.5 `leaudit_cross_review_votes`
|
||||
|
||||
投票表。
|
||||
|
||||
建议字段:
|
||||
|
||||
- `id`
|
||||
- `proposal_id`
|
||||
- `voter_id`
|
||||
- `vote_type`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- `deleted_at`
|
||||
|
||||
说明:
|
||||
|
||||
- `vote_type` 建议值:`agree`、`disagree`
|
||||
- `cancel` 建议用软删除表示,不必单独存为最终值
|
||||
|
||||
## 6. 关键业务规则如何映射到新平台
|
||||
|
||||
## 6.1 创建任务
|
||||
|
||||
目标保留的老规则:
|
||||
|
||||
- 创建者强制加入任务成员
|
||||
- 可指定普通参与人和主要负责人
|
||||
- 任务创建时可同时挂一批文档
|
||||
|
||||
新实现建议:
|
||||
|
||||
- 创建任务时先写 `leaudit_cross_review_tasks`
|
||||
- 再写 `leaudit_cross_review_task_members`
|
||||
- 再写 `leaudit_cross_review_task_documents`
|
||||
|
||||
## 6.2 文档访问校验
|
||||
|
||||
目标保留的老规则:
|
||||
|
||||
- 只有任务成员才能看任务文档
|
||||
- 提案必须绑定任务内文档
|
||||
|
||||
新实现建议:
|
||||
|
||||
- 不再让前端自己查 `cross_task_document_mapping`
|
||||
- 提供统一后端方法:
|
||||
- `CheckTaskMember(taskId, userId)`
|
||||
- `CheckTaskDocument(taskId, documentId)`
|
||||
- `CheckTaskDocumentRuleResult(documentId, ruleResultId)`
|
||||
|
||||
## 6.3 提案创建
|
||||
|
||||
目标保留的老规则:
|
||||
|
||||
- 必须是任务参与者
|
||||
- 同一用户不能对同一评查点重复创建有效提案
|
||||
- 不允许 0 分提案
|
||||
- 当前分数为 0 时不能继续扣
|
||||
- 当前已满分时不能继续加
|
||||
|
||||
新实现建议:
|
||||
|
||||
- 通过 `rule_result_id` 读取对应规则结果
|
||||
- 计算当前展示分数
|
||||
- 校验是否还能继续加减
|
||||
- 写入 `leaudit_cross_review_proposals`
|
||||
- 自动插入一条提案人同意票
|
||||
|
||||
## 6.4 投票与状态流转
|
||||
|
||||
目标保留的老规则:
|
||||
|
||||
- 采用绝对多数制
|
||||
- 阈值为 `floor(n / 2) + 1`
|
||||
|
||||
新实现建议:
|
||||
|
||||
- `n` 基于 `leaudit_cross_review_task_members` 统计活跃成员
|
||||
- 同意达到阈值 -> `approved`
|
||||
- 反对达到阈值 -> `rejected`
|
||||
- 剩余票全部同意也无法通过 -> `rejected`
|
||||
|
||||
## 6.5 文档完成确认
|
||||
|
||||
目标保留的老规则:
|
||||
|
||||
- 只有创建者或负责人可确认
|
||||
- 文档完成改的是任务内状态
|
||||
- 全部文档完成后任务才完成
|
||||
|
||||
新实现建议:
|
||||
|
||||
- 确认文档完成时更新 `leaudit_cross_review_task_documents.audit_status`
|
||||
- 若同任务下所有未删除文档都为完成,则更新 `leaudit_cross_review_tasks.status = completed`
|
||||
|
||||
## 6.6 分数计算
|
||||
|
||||
这是新实现里最重要的变化。
|
||||
|
||||
### 不建议
|
||||
|
||||
- 直接更新 `leaudit_rule_results.score`
|
||||
- 直接写回机器结果源字段
|
||||
|
||||
### 建议
|
||||
|
||||
按聚合方式计算展示分数:
|
||||
|
||||
- `machineScore`:来自 `leaudit_rule_results`
|
||||
- `auditOverride`:来自 `leaudit_review_point_audits`
|
||||
- `crossReviewDelta`:来自所有已通过的 `leaudit_cross_review_proposals`
|
||||
- `finalScore`:在详情页聚合阶段动态计算
|
||||
|
||||
建议公式:
|
||||
|
||||
1. 先根据机器结果与人工审核确定基础结果
|
||||
2. 基础结果换算出基础得分
|
||||
3. 加上 `approved` 提案累计增量
|
||||
4. 最终再做上下限裁剪:
|
||||
- 不低于 0
|
||||
- 不高于规则满分
|
||||
|
||||
## 7. 详情页如何接入新实现
|
||||
|
||||
当前详情页主入口已经存在:
|
||||
|
||||
- `fastapi_modules/fastapi_leaudit/controllers/documentController.py`
|
||||
- `GET /v3/review-points/{DocumentId}`
|
||||
|
||||
建议做法:
|
||||
|
||||
### 7.1 保留 `GetReviewPoints()` 作为主入口
|
||||
|
||||
这样前端详情页无需大改。
|
||||
|
||||
### 7.2 修改 `_loadScoringProposals()`
|
||||
|
||||
当前只是兼容读取旧 `cross_scoring_proposals`。
|
||||
|
||||
建议改为:
|
||||
|
||||
- 优先读取 `leaudit_cross_review_proposals`
|
||||
- 若尚未迁移完成,可保留旧表兼容兜底一段时间
|
||||
|
||||
### 7.3 修改评查点聚合逻辑
|
||||
|
||||
在 `_loadReviewPointResults()` 或其上层聚合逻辑中:
|
||||
|
||||
- 读取单点所有已批准提案的分值增量
|
||||
- 计算 `finalScore`
|
||||
- 继续把提案列表放入 `scoring_proposals`
|
||||
|
||||
### 7.4 前端详情页不用重构为新的页面协议
|
||||
|
||||
保持 `ReviewPointsAggregateVO` 不变或小幅扩展即可。
|
||||
|
||||
## 8. 任务列表和任务文档列表如何实现
|
||||
|
||||
## 8.1 任务列表
|
||||
|
||||
返回建议字段:
|
||||
|
||||
- `task_id`
|
||||
- `task_name`
|
||||
- `task_status`
|
||||
- `doc_type`
|
||||
- `task_type`
|
||||
- `task_created_at`
|
||||
- `progress`
|
||||
- `total_documents`
|
||||
- `evaluation_region`
|
||||
|
||||
实现来源:
|
||||
|
||||
- 任务主表
|
||||
- 成员表
|
||||
- 任务文档表
|
||||
|
||||
## 8.2 任务文档列表
|
||||
|
||||
建议按以下逻辑组织:
|
||||
|
||||
1. 先取任务绑定文档
|
||||
2. 通过 `leaudit_documents.versionGroupKey` 做版本归组
|
||||
3. 当前版本优先取 `isLatestVersion = true`
|
||||
4. 统计信息来自 `leaudit_rule_results`
|
||||
5. 最终展示分数叠加 `approved` 提案增量
|
||||
|
||||
## 8.3 不建议继续使用旧版“同名同类型猜版本”
|
||||
|
||||
因为新平台已经有更稳定的版本链字段,不必再依赖脆弱的文件名规则。
|
||||
|
||||
## 9. 上传与追加附件的实现建议
|
||||
|
||||
## 9.1 推荐长期方案
|
||||
|
||||
采用“文档系统”和“交叉评查系统”解耦:
|
||||
|
||||
1. 先用通用文档接口上传
|
||||
2. 拿到 `documentId`
|
||||
3. 再把文档挂入交叉任务
|
||||
|
||||
优点:
|
||||
|
||||
- 复用现有 `DocumentServiceImpl.Upload`
|
||||
- 文档生命周期统一
|
||||
- 附件处理逻辑不重复
|
||||
|
||||
## 9.2 推荐短期兼容方案
|
||||
|
||||
为兼容当前前端,可先提供聚合接口:
|
||||
|
||||
- `UploadAndCreateTask`
|
||||
- `UploadDocumentsToTask`
|
||||
- `AppendTaskDocumentAttachments`
|
||||
|
||||
这些接口内部再调用:
|
||||
|
||||
- `DocumentService.Upload`
|
||||
- `DocumentService.AppendAttachments`
|
||||
|
||||
这样前端先不需要大改。
|
||||
|
||||
## 10. 权限设计
|
||||
|
||||
建议保留现有 RBAC 权限点:
|
||||
|
||||
- `cross_review:task:read`
|
||||
- `cross_review:task:create`
|
||||
- `cross_review:progress:view`
|
||||
- `cross_review:proposal:create`
|
||||
- `cross_review:proposal:read`
|
||||
- `cross_review:proposal:delete`
|
||||
- `cross_review:proposal:vote`
|
||||
- `cross_review:document:complete`
|
||||
|
||||
同时叠加业务层权限判断:
|
||||
|
||||
- 是否任务成员
|
||||
- 是否创建者
|
||||
- 是否主要负责人
|
||||
- 文档是否属于任务
|
||||
- 评查点结果是否属于该文档
|
||||
|
||||
## 11. 推荐实施顺序
|
||||
|
||||
### 第一阶段:先建协作层表和后端接口
|
||||
|
||||
目标:
|
||||
|
||||
- 新建 `leaudit_cross_review_*` 表
|
||||
- 新建 `CrossReviewController / Service / DTO / VO`
|
||||
- 打通任务、提案、投票、完成确认
|
||||
|
||||
### 第二阶段:详情页切新提案逻辑
|
||||
|
||||
目标:
|
||||
|
||||
- `GetReviewPoints()` 改读新提案表
|
||||
- 动态计算最终分数
|
||||
- 详情页不再依赖旧 `cross_scoring_proposals`
|
||||
|
||||
### 第三阶段:任务文档列表切新版本底座
|
||||
|
||||
目标:
|
||||
|
||||
- 改用 `leaudit_documents` 版本字段归组
|
||||
- 任务内分数与统计改读新平台结果
|
||||
|
||||
### 第四阶段:前端路径和历史兼容收口
|
||||
|
||||
目标:
|
||||
|
||||
- 逐步下线 `/admin/v2/cross_review/*` 历史兼容口径
|
||||
- 清理前端直查旧表逻辑
|
||||
- 清理旧 `cross_*` 兼容读取
|
||||
|
||||
## 12. 风险与注意事项
|
||||
|
||||
### 12.1 最大风险:最终分数语义变化
|
||||
|
||||
如果继续沿用老系统“直接回写”的做法,会污染新平台结果底座。
|
||||
|
||||
建议统一改为聚合展示分数。
|
||||
|
||||
### 12.2 第二风险:前端隐式依赖旧接口字段
|
||||
|
||||
当前 `new_doc_review/app/api/cross-checking/*` 里仍存在很多历史字段名和旧路径假设。
|
||||
|
||||
新后端第一期最好兼容这些字段,避免前端同步改动过大。
|
||||
|
||||
### 12.3 第三风险:权限校验分散
|
||||
|
||||
当前部分前端仍尝试自行查表判断权限。
|
||||
|
||||
迁移时应尽快把权限判断收敛到后端。
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
# 新项目交叉评查新表结构与接口清单
|
||||
|
||||
> 目标:给出交叉评查在新平台中的建议表结构、接口清单以及代码文件落点,便于直接拆任务实施。
|
||||
|
||||
## 1. 推荐新增表
|
||||
|
||||
## 1.1 `leaudit_cross_review_tasks`
|
||||
|
||||
用途:
|
||||
|
||||
- 交叉评查任务主表
|
||||
|
||||
建议字段:
|
||||
|
||||
```sql
|
||||
id bigint primary key,
|
||||
task_name varchar(255) not null,
|
||||
task_type varchar(32) not null,
|
||||
doc_type_id bigint null,
|
||||
doc_type_code varchar(64) null,
|
||||
assigner_id bigint not null,
|
||||
status varchar(32) not null default 'in_progress',
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now(),
|
||||
deleted_at timestamptz null
|
||||
```
|
||||
|
||||
建议索引:
|
||||
|
||||
- `idx_lcrt_assigner_id`
|
||||
- `idx_lcrt_status`
|
||||
- `idx_lcrt_doc_type_id`
|
||||
|
||||
## 1.2 `leaudit_cross_review_task_members`
|
||||
|
||||
用途:
|
||||
|
||||
- 任务参与人和负责人关系表
|
||||
|
||||
建议字段:
|
||||
|
||||
```sql
|
||||
id bigint primary key,
|
||||
task_id bigint not null,
|
||||
user_id bigint not null,
|
||||
member_role varchar(32) not null,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now(),
|
||||
deleted_at timestamptz null
|
||||
```
|
||||
|
||||
约束建议:
|
||||
|
||||
- 唯一约束:`(task_id, user_id, deleted_at is null)` 语义上唯一
|
||||
|
||||
建议索引:
|
||||
|
||||
- `idx_lcrtm_task_id`
|
||||
- `idx_lcrtm_user_id`
|
||||
- `idx_lcrtm_role`
|
||||
|
||||
## 1.3 `leaudit_cross_review_task_documents`
|
||||
|
||||
用途:
|
||||
|
||||
- 任务绑定文档
|
||||
|
||||
建议字段:
|
||||
|
||||
```sql
|
||||
id bigint primary key,
|
||||
task_id bigint not null,
|
||||
document_id bigint not null,
|
||||
audit_status integer not null default 0,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now(),
|
||||
deleted_at timestamptz null
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `audit_status=0` 未完成
|
||||
- `audit_status=1` 已完成
|
||||
|
||||
建议索引:
|
||||
|
||||
- `idx_lcrtd_task_id`
|
||||
- `idx_lcrtd_document_id`
|
||||
- `idx_lcrtd_task_status`
|
||||
|
||||
## 1.4 `leaudit_cross_review_proposals`
|
||||
|
||||
用途:
|
||||
|
||||
- 交叉评查加减分提案
|
||||
|
||||
建议字段:
|
||||
|
||||
```sql
|
||||
id bigint primary key,
|
||||
task_id bigint not null,
|
||||
document_id bigint not null,
|
||||
rule_result_id bigint not null,
|
||||
proposer_id bigint not null,
|
||||
proposed_score_delta numeric(10,2) not null,
|
||||
reason text not null,
|
||||
status varchar(32) not null default 'pending',
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now(),
|
||||
deleted_at timestamptz null
|
||||
```
|
||||
|
||||
建议索引:
|
||||
|
||||
- `idx_lcrp_task_id`
|
||||
- `idx_lcrp_document_id`
|
||||
- `idx_lcrp_rule_result_id`
|
||||
- `idx_lcrp_proposer_id`
|
||||
- `idx_lcrp_status`
|
||||
|
||||
业务唯一性建议:
|
||||
|
||||
- 同一用户对同一 `document_id + rule_result_id` 同时只能存在一条有效提案
|
||||
|
||||
## 1.5 `leaudit_cross_review_votes`
|
||||
|
||||
用途:
|
||||
|
||||
- 提案投票
|
||||
|
||||
建议字段:
|
||||
|
||||
```sql
|
||||
id bigint primary key,
|
||||
proposal_id bigint not null,
|
||||
voter_id bigint not null,
|
||||
vote_type varchar(16) not null,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now(),
|
||||
deleted_at timestamptz null
|
||||
```
|
||||
|
||||
建议索引:
|
||||
|
||||
- `idx_lcrv_proposal_id`
|
||||
- `idx_lcrv_voter_id`
|
||||
|
||||
业务唯一性建议:
|
||||
|
||||
- 同一提案、同一用户仅保留一条有效投票
|
||||
|
||||
## 2. 推荐后端文件落点
|
||||
|
||||
## 2.1 Models
|
||||
|
||||
建议新增:
|
||||
|
||||
- `fastapi_modules/fastapi_leaudit/models/leauditCrossReviewTask.py`
|
||||
- `fastapi_modules/fastapi_leaudit/models/leauditCrossReviewTaskMember.py`
|
||||
- `fastapi_modules/fastapi_leaudit/models/leauditCrossReviewTaskDocument.py`
|
||||
- `fastapi_modules/fastapi_leaudit/models/leauditCrossReviewProposal.py`
|
||||
- `fastapi_modules/fastapi_leaudit/models/leauditCrossReviewVote.py`
|
||||
|
||||
## 2.2 DTO
|
||||
|
||||
建议新增:
|
||||
|
||||
- `fastapi_modules/fastapi_leaudit/domian/Dto/crossReviewDto.py`
|
||||
|
||||
建议包含:
|
||||
|
||||
- `CrossReviewTaskCreateDTO`
|
||||
- `CrossReviewProposalCreateDTO`
|
||||
- `CrossReviewVoteDTO`
|
||||
- `CrossReviewTaskDocumentQueryDTO`
|
||||
|
||||
## 2.3 VO
|
||||
|
||||
建议新增:
|
||||
|
||||
- `fastapi_modules/fastapi_leaudit/domian/vo/crossReviewVo.py`
|
||||
|
||||
建议包含:
|
||||
|
||||
- `CrossReviewTaskItemVO`
|
||||
- `CrossReviewTaskPageVO`
|
||||
- `CrossReviewTaskDocumentVO`
|
||||
- `CrossReviewTaskDocumentPageVO`
|
||||
- `CrossReviewProposalVO`
|
||||
- `CrossReviewProposalPageVO`
|
||||
- `CrossReviewPermissionVO`
|
||||
- `CrossReviewCompleteVO`
|
||||
|
||||
## 2.4 Service
|
||||
|
||||
建议新增:
|
||||
|
||||
- `fastapi_modules/fastapi_leaudit/services/crossReviewService.py`
|
||||
- `fastapi_modules/fastapi_leaudit/services/impl/crossReviewServiceImpl.py`
|
||||
|
||||
## 2.5 Controller
|
||||
|
||||
建议新增:
|
||||
|
||||
- `fastapi_modules/fastapi_leaudit/controllers/crossReviewController.py`
|
||||
|
||||
## 3. 推荐服务接口
|
||||
|
||||
建议在 `crossReviewService.py` 中定义以下能力:
|
||||
|
||||
- `CreateTask(...)`
|
||||
- `GetUserTasks(...)`
|
||||
- `GetTaskDocuments(...)`
|
||||
- `CanConfirmTaskDocument(...)`
|
||||
- `CompleteTaskDocument(...)`
|
||||
- `CreateProposal(...)`
|
||||
- `VoteProposal(...)`
|
||||
- `CancelProposal(...)`
|
||||
- `GetDocumentProposals(...)`
|
||||
- `GetPendingProposalDetails(...)`
|
||||
- `CheckPendingVotesByDocument(...)`
|
||||
- `UploadDocumentsToTask(...)`
|
||||
- `AppendTaskDocumentAttachments(...)`
|
||||
- `LoadApprovedProposalDeltas(...)`
|
||||
|
||||
## 4. 推荐接口清单
|
||||
|
||||
## 4.1 任务接口
|
||||
|
||||
### `POST /api/v2/cross_review/tasks`
|
||||
|
||||
用途:
|
||||
|
||||
- 创建交叉评查任务
|
||||
|
||||
请求体建议:
|
||||
|
||||
```json
|
||||
{
|
||||
"document_ids": [101, 102],
|
||||
"user_ids": [11, 12],
|
||||
"principal_user_ids": [21],
|
||||
"task_name": "市局间交叉评查-合同类",
|
||||
"doc_type_id": 3,
|
||||
"doc_type_code": "XZCF",
|
||||
"task_type": "CITY"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v2/cross_review/tasks/user_tasks`
|
||||
|
||||
用途:
|
||||
|
||||
- 获取当前用户参与的任务列表
|
||||
|
||||
### `GET /api/v2/cross_review/tasks/{taskId}/documents`
|
||||
|
||||
用途:
|
||||
|
||||
- 获取任务下文档列表
|
||||
- 支持版本归纳
|
||||
|
||||
查询参数建议:
|
||||
|
||||
- `page`
|
||||
- `page_size`
|
||||
- `keyword`
|
||||
|
||||
### `GET /api/v2/cross_review/tasks/{taskId}/can-confirm`
|
||||
|
||||
用途:
|
||||
|
||||
- 检查当前用户是否能确认文档评查完成
|
||||
|
||||
### `POST /api/v2/cross_review/tasks/{taskId}/documents/{documentId}/complete`
|
||||
|
||||
用途:
|
||||
|
||||
- 确认任务内文档完成
|
||||
|
||||
### `POST /api/v2/cross_review/tasks/{taskId}/upload_documents`
|
||||
|
||||
用途:
|
||||
|
||||
- 向已有任务追加新文档
|
||||
|
||||
### `POST /api/v2/cross_review/tasks/{taskId}/documents/{documentId}/append_attachments`
|
||||
|
||||
用途:
|
||||
|
||||
- 给任务文档追加附件并生成新版本
|
||||
|
||||
## 4.2 提案接口
|
||||
|
||||
### `POST /api/v2/cross_review/proposals`
|
||||
|
||||
用途:
|
||||
|
||||
- 创建提案
|
||||
|
||||
请求体建议:
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": 1,
|
||||
"document_id": 101,
|
||||
"rule_result_id": 10001,
|
||||
"proposed_score_delta": -5,
|
||||
"reason": "该点应扣 5 分"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v2/cross_review/proposals/{proposalId}/votes`
|
||||
|
||||
用途:
|
||||
|
||||
- 对提案投票
|
||||
|
||||
请求体建议:
|
||||
|
||||
```json
|
||||
{
|
||||
"vote_type": "agree"
|
||||
}
|
||||
```
|
||||
|
||||
### `DELETE /api/v2/cross_review/proposals/{proposalId}`
|
||||
|
||||
用途:
|
||||
|
||||
- 撤销提案
|
||||
|
||||
### `POST /api/v2/cross_review/proposals/document`
|
||||
|
||||
用途:
|
||||
|
||||
- 获取某文档的提案列表
|
||||
|
||||
请求体建议:
|
||||
|
||||
```json
|
||||
{
|
||||
"document_id": 101,
|
||||
"page": 1,
|
||||
"page_size": 10
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/v2/cross_review/proposals/details`
|
||||
|
||||
用途:
|
||||
|
||||
- 获取当前用户待处理提案列表
|
||||
|
||||
### `POST /api/v2/cross_review/proposals/document/check_pending_votes`
|
||||
|
||||
用途:
|
||||
|
||||
- 检查某文档是否仍有提案未完成投票
|
||||
|
||||
## 5. 与现有 `DocumentServiceImpl` 的衔接点
|
||||
|
||||
## 5.1 详情页提案列表
|
||||
|
||||
当前衔接点:
|
||||
|
||||
- `DocumentServiceImpl._loadScoringProposals()`
|
||||
|
||||
建议:
|
||||
|
||||
- 改成读 `leaudit_cross_review_proposals`
|
||||
- 后续移除对旧 `cross_scoring_proposals` 的直接依赖
|
||||
|
||||
## 5.2 最终得分计算
|
||||
|
||||
当前衔接点:
|
||||
|
||||
- `DocumentServiceImpl._loadReviewPointResults()`
|
||||
|
||||
建议:
|
||||
|
||||
- 增加按 `rule_result_id` 聚合已通过提案分数的逻辑
|
||||
- 返回前写入 `ReviewPointResultVO.finalScore`
|
||||
|
||||
## 5.3 文档上传与附件追加
|
||||
|
||||
建议复用:
|
||||
|
||||
- `DocumentServiceImpl.Upload()`
|
||||
- `DocumentServiceImpl.AppendAttachments()`
|
||||
|
||||
交叉评查只负责:
|
||||
|
||||
- 上传后把文档挂入任务
|
||||
- 维护任务内状态和版本展示
|
||||
|
||||
## 6. 建议前端改造策略
|
||||
|
||||
## 6.1 第一阶段
|
||||
|
||||
前端尽量少改,只调整 API 基址和字段映射。
|
||||
|
||||
重点文件:
|
||||
|
||||
- `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`
|
||||
|
||||
## 6.2 第二阶段
|
||||
|
||||
逐步清理这些旧依赖:
|
||||
|
||||
- 前端直接查 `cross_examination_tasks`
|
||||
- 前端直接查 `cross_task_document_mapping`
|
||||
- 前端假设旧路径 `/admin/cross_review/*`
|
||||
|
||||
## 7. 推荐实施拆解
|
||||
|
||||
### P0:表结构与模型
|
||||
|
||||
- 建新表
|
||||
- 建模型
|
||||
- 建基础迁移
|
||||
|
||||
### P1:任务与提案接口
|
||||
|
||||
- 创建任务
|
||||
- 任务列表
|
||||
- 文档列表
|
||||
- 提案创建
|
||||
- 投票
|
||||
- 完成确认
|
||||
|
||||
### P2:详情页聚合接入
|
||||
|
||||
- `GetReviewPoints()` 改接新提案表
|
||||
- 动态计算最终分数
|
||||
|
||||
### P3:上传与版本链闭环
|
||||
|
||||
- 任务上传文档
|
||||
- 追加附件
|
||||
- 版本归纳改用 `versionGroupKey`
|
||||
|
||||
### P4:清理旧兼容
|
||||
|
||||
- 清理旧表直查
|
||||
- 清理前端旧路径
|
||||
- 清理旧逻辑兜底
|
||||
|
||||
@@ -0,0 +1,594 @@
|
||||
# 新项目交叉评查状态流转图与边界案例清单
|
||||
|
||||
> 目标:把新项目交叉评查的状态流转、时序过程和高风险边界案例一次性说清,避免实现时出现“以为如此,实际上不是”的尴尬。
|
||||
|
||||
## 1. 为什么需要这份文档
|
||||
|
||||
交叉评查最容易出错的地方,不是 CRUD,而是:
|
||||
|
||||
- 状态什么时候变
|
||||
- 谁能触发状态变更
|
||||
- 分数什么时候生效
|
||||
- 文档什么时候算完成
|
||||
- 任务什么时候算完成
|
||||
- 新版本文档进来后旧版本怎么处理
|
||||
|
||||
这些地方一旦实现错了,前端看起来也许还能跑,但业务上会很尴尬。
|
||||
|
||||
所以这份文档专门回答三类问题:
|
||||
|
||||
- 状态怎么流转
|
||||
- 链路怎么串起来
|
||||
- 边界情况怎么处理
|
||||
|
||||
## 2. 五类核心状态
|
||||
|
||||
建议在实现时,把状态拆成五套,不要混用。
|
||||
|
||||
### 2.1 文档处理状态
|
||||
|
||||
来源:
|
||||
|
||||
- `leaudit_documents.processing_status`
|
||||
- `leaudit_audit_runs.status`
|
||||
|
||||
这套状态表示:
|
||||
|
||||
- 文档是否完成机器评查
|
||||
|
||||
它不表示:
|
||||
|
||||
- 是否完成交叉评查
|
||||
|
||||
### 2.2 任务状态
|
||||
|
||||
来源:
|
||||
|
||||
- `leaudit_cross_review_tasks.status`
|
||||
|
||||
建议值:
|
||||
|
||||
- `pending`
|
||||
- `in_progress`
|
||||
- `completed`
|
||||
|
||||
这套状态表示:
|
||||
|
||||
- 任务层面的协作进度
|
||||
|
||||
### 2.3 任务文档状态
|
||||
|
||||
来源:
|
||||
|
||||
- `leaudit_cross_review_task_documents.audit_status`
|
||||
|
||||
建议值:
|
||||
|
||||
- `0`:未完成
|
||||
- `1`:已完成
|
||||
|
||||
这套状态表示:
|
||||
|
||||
- 某个文档在某个任务里是否被负责人确认完成
|
||||
|
||||
### 2.4 提案状态
|
||||
|
||||
来源:
|
||||
|
||||
- `leaudit_cross_review_proposals.status`
|
||||
|
||||
建议值:
|
||||
|
||||
- `pending`
|
||||
- `approved`
|
||||
- `rejected`
|
||||
- `cancelled`
|
||||
|
||||
### 2.5 投票状态
|
||||
|
||||
来源:
|
||||
|
||||
- `leaudit_cross_review_votes.vote_type`
|
||||
|
||||
建议值:
|
||||
|
||||
- `agree`
|
||||
- `disagree`
|
||||
|
||||
取消投票建议通过软删除表达,不额外引入 `cancel` 作为终态值。
|
||||
|
||||
## 3. 任务状态流转图
|
||||
|
||||
```text
|
||||
创建任务
|
||||
|
|
||||
v
|
||||
in_progress
|
||||
|
|
||||
| 所有任务文档 audit_status = 1
|
||||
v
|
||||
completed
|
||||
```
|
||||
|
||||
### 3.1 任务创建后状态
|
||||
|
||||
- 默认直接进入 `in_progress`
|
||||
|
||||
### 3.2 任务完成条件
|
||||
|
||||
只有当:
|
||||
|
||||
- 该任务下所有未删除任务文档都为 `audit_status = 1`
|
||||
|
||||
才允许变为:
|
||||
|
||||
- `completed`
|
||||
|
||||
### 3.3 明确不是任务完成条件的事件
|
||||
|
||||
以下事件都不能直接导致任务完成:
|
||||
|
||||
- 文档机器评查跑完
|
||||
- 所有提案都投票结束
|
||||
- 没有提案
|
||||
- 所有人都看过详情页
|
||||
- 追加了新附件
|
||||
|
||||
## 4. 任务文档状态流转图
|
||||
|
||||
```text
|
||||
文档被挂入任务
|
||||
|
|
||||
v
|
||||
audit_status = 0
|
||||
|
|
||||
| assigner 或 principal 确认完成
|
||||
v
|
||||
audit_status = 1
|
||||
```
|
||||
|
||||
### 4.1 进入任务默认状态
|
||||
|
||||
- 新挂入任务的文档默认 `audit_status = 0`
|
||||
|
||||
### 4.2 谁能把 `0` 改成 `1`
|
||||
|
||||
只有:
|
||||
|
||||
- `assigner`
|
||||
- `principal`
|
||||
|
||||
普通参与人不允许确认完成。
|
||||
|
||||
### 4.3 新版本文档进入任务后的状态
|
||||
|
||||
如果文档追加附件或上传了新版本:
|
||||
|
||||
- 新文档进入任务后必须重新记为 `audit_status = 0`
|
||||
|
||||
原因:
|
||||
|
||||
- 新版本代表新的评查对象
|
||||
- 不能继承旧版本的“已完成”
|
||||
|
||||
## 5. 提案状态流转图
|
||||
|
||||
```text
|
||||
创建提案
|
||||
|
|
||||
v
|
||||
pending
|
||||
| \
|
||||
| \
|
||||
| \ 提案人主动撤销
|
||||
| \
|
||||
| v
|
||||
| cancelled
|
||||
|
|
||||
| agree 票达到阈值
|
||||
v
|
||||
approved
|
||||
|
||||
pending
|
||||
|
|
||||
| disagree 票达到阈值
|
||||
| 或剩余票全部同意也无法通过
|
||||
v
|
||||
rejected
|
||||
```
|
||||
|
||||
### 5.1 初始状态
|
||||
|
||||
- 提案创建后默认 `pending`
|
||||
|
||||
### 5.2 通过条件
|
||||
|
||||
若:
|
||||
|
||||
- `agree >= floor(n / 2) + 1`
|
||||
|
||||
则:
|
||||
|
||||
- `approved`
|
||||
|
||||
其中 `n` 为任务有效成员总数。
|
||||
|
||||
### 5.3 否决条件
|
||||
|
||||
若:
|
||||
|
||||
- `disagree >= floor(n / 2) + 1`
|
||||
|
||||
则:
|
||||
|
||||
- `rejected`
|
||||
|
||||
### 5.4 提前否决条件
|
||||
|
||||
若:
|
||||
|
||||
- 即使所有剩余未投票成员都改投同意,提案也不可能过阈值
|
||||
|
||||
则:
|
||||
|
||||
- 直接 `rejected`
|
||||
|
||||
### 5.5 撤销条件
|
||||
|
||||
只有提案人可以撤销,且必须满足:
|
||||
|
||||
- 当前状态仍为 `pending`
|
||||
|
||||
撤销后:
|
||||
|
||||
- 提案变为 `cancelled`
|
||||
|
||||
### 5.6 终态不可再操作
|
||||
|
||||
提案一旦进入:
|
||||
|
||||
- `approved`
|
||||
- `rejected`
|
||||
- `cancelled`
|
||||
|
||||
即视为终态:
|
||||
|
||||
- 不允许再投票
|
||||
- 不允许再撤销
|
||||
- 不允许再改状态
|
||||
|
||||
## 6. 投票时序图
|
||||
|
||||
```text
|
||||
参与人 -> CrossReviewController: POST /proposals/{id}/votes
|
||||
CrossReviewController -> CrossReviewService: VoteProposal()
|
||||
CrossReviewService -> DB: 校验提案存在且状态= pending
|
||||
CrossReviewService -> DB: 校验当前用户是任务成员
|
||||
CrossReviewService -> DB: 写入或更新投票
|
||||
CrossReviewService -> DB: 重新统计 agree/disagree/remaining
|
||||
CrossReviewService -> DB: 如达阈值则更新 proposal.status
|
||||
CrossReviewService --> CrossReviewController: 返回 proposal_status
|
||||
CrossReviewController --> 参与人: success + 最新状态
|
||||
```
|
||||
|
||||
### 6.1 投票后必须立即判定状态
|
||||
|
||||
不要把“投票成功”和“状态判定”拆成异步延迟任务。
|
||||
|
||||
推荐:
|
||||
|
||||
- 每次投票后同步判定提案状态
|
||||
|
||||
原因:
|
||||
|
||||
- 前端能立刻拿到最新状态
|
||||
- 避免多人并发时出现短暂假状态
|
||||
|
||||
## 7. 提案创建时序图
|
||||
|
||||
```text
|
||||
参与人 -> CrossReviewController: POST /proposals
|
||||
CrossReviewController -> CrossReviewService: CreateProposal()
|
||||
CrossReviewService -> DB: 校验任务/文档/规则结果关系
|
||||
CrossReviewService -> DB: 校验用户是否为任务成员
|
||||
CrossReviewService -> DB: 校验是否重复提案
|
||||
CrossReviewService -> DB: 校验分值边界
|
||||
CrossReviewService -> DB: 写入 proposal(status=pending)
|
||||
CrossReviewService -> DB: 自动写入 proposer agree 投票
|
||||
CrossReviewService -> DB: 判定 proposal 是否已直接过阈值
|
||||
CrossReviewService --> CrossReviewController: proposal + latest_status
|
||||
CrossReviewController --> 参与人: success
|
||||
```
|
||||
|
||||
### 7.1 自动同意票后的特殊情况
|
||||
|
||||
如果任务成员数很少,例如:
|
||||
|
||||
- 只有 1 人
|
||||
|
||||
那么提案创建后可能因为自动同意票立即通过。
|
||||
|
||||
这是允许的,只要符合阈值规则。
|
||||
|
||||
## 8. 文档完成确认时序图
|
||||
|
||||
```text
|
||||
负责人 -> CrossReviewController: POST /tasks/{taskId}/documents/{documentId}/complete
|
||||
CrossReviewController -> CrossReviewService: CompleteTaskDocument()
|
||||
CrossReviewService -> DB: 校验任务存在
|
||||
CrossReviewService -> DB: 校验用户是 assigner 或 principal
|
||||
CrossReviewService -> DB: 校验文档属于该任务
|
||||
CrossReviewService -> DB: 更新 task_document.audit_status = 1
|
||||
CrossReviewService -> DB: 统计任务下是否所有文档已完成
|
||||
CrossReviewService -> DB: 若是,则更新 task.status = completed
|
||||
CrossReviewService --> CrossReviewController: document completed / task completed
|
||||
CrossReviewController --> 负责人: success
|
||||
```
|
||||
|
||||
### 8.1 确认完成时是否强制要求“所有提案已投完”
|
||||
|
||||
不建议强阻断。
|
||||
|
||||
建议策略:
|
||||
|
||||
- 后端检测是否还有 `pending` 提案或未投票成员
|
||||
- 返回提示信息
|
||||
- 由前端给出二次确认
|
||||
|
||||
原因:
|
||||
|
||||
- 真实业务里负责人可能需要“带风险确认”
|
||||
- 如果完全强阻断,会把流程卡死
|
||||
|
||||
## 9. 详情页分数计算时序图
|
||||
|
||||
```text
|
||||
前端详情页 -> DocumentController: GET /v3/review-points/{documentId}
|
||||
DocumentController -> DocumentServiceImpl: GetReviewPoints()
|
||||
DocumentServiceImpl -> DB: 读取 leaudit_rule_results
|
||||
DocumentServiceImpl -> DB: 读取 leaudit_review_point_audits
|
||||
DocumentServiceImpl -> DB: 读取 approved proposals
|
||||
DocumentServiceImpl: 计算 baseScore
|
||||
DocumentServiceImpl: 计算 crossDelta
|
||||
DocumentServiceImpl: 计算 finalScore = clamp(baseScore + crossDelta)
|
||||
DocumentServiceImpl --> DocumentController: ReviewPointsAggregateVO
|
||||
DocumentController --> 前端详情页: data + stats + scoring_proposals
|
||||
```
|
||||
|
||||
### 9.1 分数生效时点
|
||||
|
||||
提案只有在:
|
||||
|
||||
- `approved`
|
||||
|
||||
后,才计入 `crossDelta`。
|
||||
|
||||
以下状态都不计入:
|
||||
|
||||
- `pending`
|
||||
- `rejected`
|
||||
- `cancelled`
|
||||
|
||||
## 10. 版本链时序图
|
||||
|
||||
```text
|
||||
用户上传新版本/追加附件
|
||||
|
|
||||
v
|
||||
DocumentService 创建新 leaudit_document
|
||||
|
|
||||
v
|
||||
新文档进入原 versionGroupKey
|
||||
|
|
||||
v
|
||||
CrossReviewService 将新 document_id 挂入任务
|
||||
|
|
||||
v
|
||||
task_document.audit_status = 0
|
||||
|
|
||||
v
|
||||
任务列表按 versionGroupKey 归组展示
|
||||
```
|
||||
|
||||
### 10.1 旧版本是否自动失效
|
||||
|
||||
不建议把旧版本任务记录自动删掉。
|
||||
|
||||
建议:
|
||||
|
||||
- 保留历史版本
|
||||
- 页面默认展示最新版本
|
||||
- 历史版本折叠展示
|
||||
|
||||
### 10.2 新版本是否自动继承提案
|
||||
|
||||
不建议继承。
|
||||
|
||||
原因:
|
||||
|
||||
- 提案是针对具体 `rule_result_id`
|
||||
- 新版本会产生新的 `rule_result_id`
|
||||
- 旧提案不能自动迁移到新结果上
|
||||
|
||||
## 11. 边界案例清单
|
||||
|
||||
下面这些情况,后端必须提前定好策略。
|
||||
|
||||
## 11.1 只有 1 个任务成员
|
||||
|
||||
### 情况
|
||||
|
||||
- 创建者自己给自己建任务,且没有别的成员
|
||||
|
||||
### 正确处理
|
||||
|
||||
- 阈值 = 1
|
||||
- 提案创建后自动同意票即可直接 `approved`
|
||||
|
||||
### 不要出错
|
||||
|
||||
- 不要硬编码“至少两人才能交叉评查”
|
||||
|
||||
## 11.2 只有 2 个任务成员
|
||||
|
||||
### 情况
|
||||
|
||||
- A 发起提案
|
||||
- A 自动同意
|
||||
- B 反对
|
||||
|
||||
### 正确处理
|
||||
|
||||
- 成员数 `n=2`
|
||||
- 阈值 `floor(2/2)+1 = 2`
|
||||
- A 同意数 1,不足通过
|
||||
- B 反对数 1,也不足否决
|
||||
- 但此时无剩余票,提案已不可能达到 2 票同意
|
||||
- 应直接 `rejected`
|
||||
|
||||
### 不要出错
|
||||
|
||||
- 不要把它一直挂在 `pending`
|
||||
|
||||
## 11.3 提案人重复点击提交
|
||||
|
||||
### 情况
|
||||
|
||||
- 网络慢,用户多次点提交
|
||||
|
||||
### 正确处理
|
||||
|
||||
- 通过唯一性规则防重
|
||||
- 返回“已存在有效提案”
|
||||
|
||||
### 不要出错
|
||||
|
||||
- 不要产生两条同时有效的同目标提案
|
||||
|
||||
## 11.4 文档已有新版本,但旧版本已完成
|
||||
|
||||
### 情况
|
||||
|
||||
- 旧版本在任务里已确认完成
|
||||
- 新版本追加进来
|
||||
|
||||
### 正确处理
|
||||
|
||||
- 新版本独立新增一条 `task_document`
|
||||
- `audit_status = 0`
|
||||
- 旧版本保持历史完成状态
|
||||
|
||||
### 不要出错
|
||||
|
||||
- 不要把新版本直接继承旧版本的完成状态
|
||||
|
||||
## 11.5 提案通过后又有人想继续改
|
||||
|
||||
### 情况
|
||||
|
||||
- 某条提案已经 `approved`
|
||||
- 又有人想针对同一个规则点继续提分或扣分
|
||||
|
||||
### 正确处理
|
||||
|
||||
- 允许新建后续提案,但必须满足唯一性规则
|
||||
- 即同一用户不能重复,但不同用户可继续提出新的有效提案
|
||||
|
||||
### 不要出错
|
||||
|
||||
- 不要错误地把“某点已有 approved 提案”理解成“该点以后都不能再提案”
|
||||
|
||||
## 11.6 文档没有任何提案
|
||||
|
||||
### 情况
|
||||
|
||||
- 任务成员只看文档,不提出任何提案
|
||||
|
||||
### 正确处理
|
||||
|
||||
- 允许负责人直接确认完成
|
||||
|
||||
### 不要出错
|
||||
|
||||
- 不要把“没有提案”当成“不能完成”
|
||||
|
||||
## 11.7 存在 `pending` 提案,但负责人仍要完成
|
||||
|
||||
### 情况
|
||||
|
||||
- 业务上要强行收口
|
||||
|
||||
### 正确处理
|
||||
|
||||
- 推荐允许,但返回 warning 信息
|
||||
|
||||
### 不要出错
|
||||
|
||||
- 不要一刀切强阻断,除非产品明确要求
|
||||
|
||||
## 11.8 文档重跑评查后,旧提案怎么办
|
||||
|
||||
### 情况
|
||||
|
||||
- 同一个文档重新触发 audit run
|
||||
- 产生新 `rule_result_id`
|
||||
|
||||
### 正确处理建议
|
||||
|
||||
- 提案绑定的是具体 `rule_result_id`
|
||||
- 如果是同一 document 的新 run,建议只对“当前有效 run”的结果展示可新建提案
|
||||
- 旧 run 上的提案保留历史,不再作为主展示对象
|
||||
|
||||
### 不要出错
|
||||
|
||||
- 不要把旧 run 的提案直接混到新 run 的点位上
|
||||
|
||||
## 11.9 删除任务成员后,历史票怎么办
|
||||
|
||||
### 情况
|
||||
|
||||
- 某成员中途被移出任务
|
||||
|
||||
### 正确处理建议
|
||||
|
||||
- 历史投票保留
|
||||
- 后续阈值计算以当前有效成员还是任务创建时成员为准,必须提前定死
|
||||
|
||||
### 推荐口径
|
||||
|
||||
- 阈值按“当前有效成员数”计算
|
||||
|
||||
### 注意
|
||||
|
||||
- 这个规则一旦确定,就不能前后漂移
|
||||
|
||||
## 12. 推荐的最终业务口径
|
||||
|
||||
为了避免后续实现反复改口,建议最终统一采用以下口径:
|
||||
|
||||
- 任务默认 `in_progress`
|
||||
- 文档进入任务默认未完成
|
||||
- 文档完成只能由创建者或负责人确认
|
||||
- 任务完成由“所有任务文档完成”推导
|
||||
- 提案默认 `pending`
|
||||
- 提案自动给提案人一票同意
|
||||
- 提案采用绝对多数制
|
||||
- 提案通过后只影响展示层最终分,不改机器原始结果
|
||||
- 版本归纳优先使用 `versionGroupKey`
|
||||
- 权限由后端统一判断
|
||||
|
||||
## 13. 开发前最后核对清单
|
||||
|
||||
开始写代码前,建议逐项确认:
|
||||
|
||||
- 是否已经把五类状态拆开
|
||||
- 是否已经明确最终分不是回写机器结果
|
||||
- 是否已经明确任务完成只看任务文档状态
|
||||
- 是否已经明确新版本进任务后默认未完成
|
||||
- 是否已经明确提案绑定到 `rule_result_id`
|
||||
- 是否已经明确提案通过阈值算法
|
||||
- 是否已经明确权限是 RBAC + 业务归属双校验
|
||||
- 是否已经明确详情页主入口仍可复用 `GetReviewPoints()`
|
||||
|
||||
如果这几项都确认过,交叉评查的实现基本就不会跑偏。
|
||||
|
||||
@@ -0,0 +1,771 @@
|
||||
# 新项目交叉评查详细业务逻辑定稿
|
||||
|
||||
> 目标:把新项目交叉评查的业务逻辑收敛成一份“可直接指导实现”的定稿,重点避免实现阶段把任务状态、评分语义、权限边界、版本逻辑和完成条件做错。
|
||||
|
||||
## 1. 定位
|
||||
|
||||
交叉评查在新项目中必须被视为:
|
||||
|
||||
- 一层独立的“协作复核业务”
|
||||
- 建立在 `leaudit` 已有文档、评查运行、规则结果之上
|
||||
- 不负责重新生成底层规则结果
|
||||
- 只负责任务协作、提案、投票、完成确认和展示分数叠加
|
||||
|
||||
因此,交叉评查不是评查引擎的一部分,而是评查结果之上的二次协作层。
|
||||
|
||||
## 2. 核心业务对象
|
||||
|
||||
新项目中交叉评查至少包含五类核心对象:
|
||||
|
||||
### 2.1 任务
|
||||
|
||||
任务是交叉评查的协作容器,负责绑定:
|
||||
|
||||
- 一组参与人
|
||||
- 一组主要负责人
|
||||
- 一批文档
|
||||
- 一个文档类型语义
|
||||
- 一个任务状态
|
||||
|
||||
任务是交叉评查权限判断的顶层边界。
|
||||
|
||||
### 2.2 任务成员
|
||||
|
||||
任务成员分为两类:
|
||||
|
||||
- `participant`:普通参与人
|
||||
- `principal`:主要负责人
|
||||
|
||||
另有一个特殊角色:
|
||||
|
||||
- `assigner`:任务创建者
|
||||
|
||||
实现上建议:
|
||||
|
||||
- `assigner` 保存在任务主表
|
||||
- `participant/principal` 保存在成员表
|
||||
|
||||
### 2.3 任务文档
|
||||
|
||||
任务文档表示:
|
||||
|
||||
- 哪些文档进入了交叉评查任务
|
||||
- 每个文档在该任务内的完成状态
|
||||
|
||||
注意:
|
||||
|
||||
- 任务文档状态是“任务内状态”
|
||||
- 不等于文档全局状态
|
||||
- 不等于底层评查运行状态
|
||||
|
||||
### 2.4 提案
|
||||
|
||||
提案表示:
|
||||
|
||||
- 某个参与人针对某个“规则结果”提出的分数调整建议
|
||||
|
||||
提案的本质是:
|
||||
|
||||
- 绑定到具体 `rule_result`
|
||||
- 以“增量分值”表达加分或扣分
|
||||
|
||||
### 2.5 投票
|
||||
|
||||
投票表示:
|
||||
|
||||
- 任务成员对某条提案的同意或反对
|
||||
|
||||
投票只作用于提案状态流转,不直接改文档状态。
|
||||
|
||||
## 3. 不可搞错的几个真相源
|
||||
|
||||
实现阶段最容易尴尬的就是“到底哪张表是真相源”。
|
||||
|
||||
这里必须定死:
|
||||
|
||||
### 3.1 文档真相源
|
||||
|
||||
- `leaudit_documents`
|
||||
|
||||
### 3.2 文档文件与版本真相源
|
||||
|
||||
- `leaudit_document_files`
|
||||
- `leaudit_documents.versionGroupKey`
|
||||
- `leaudit_documents.versionNo`
|
||||
- `leaudit_documents.previousVersionId`
|
||||
- `leaudit_documents.rootVersionId`
|
||||
- `leaudit_documents.isLatestVersion`
|
||||
|
||||
### 3.3 机器评查结果真相源
|
||||
|
||||
- `leaudit_rule_results`
|
||||
|
||||
### 3.4 人工审核覆盖真相源
|
||||
|
||||
- `leaudit_review_point_audits`
|
||||
|
||||
### 3.5 交叉评查协作真相源
|
||||
|
||||
- `leaudit_cross_review_tasks`
|
||||
- `leaudit_cross_review_task_members`
|
||||
- `leaudit_cross_review_task_documents`
|
||||
- `leaudit_cross_review_proposals`
|
||||
- `leaudit_cross_review_votes`
|
||||
|
||||
## 4. 明确禁止的实现方式
|
||||
|
||||
以下做法不要再用:
|
||||
|
||||
### 4.1 不要直接修改 `leaudit_rule_results` 原始分数
|
||||
|
||||
原因:
|
||||
|
||||
- 会污染机器评查原始结果
|
||||
- 会让重新跑批、调试、审计对账变得混乱
|
||||
|
||||
### 4.2 不要把交叉评查完成状态写回文档全局状态
|
||||
|
||||
原因:
|
||||
|
||||
- 交叉评查完成只是任务内完成
|
||||
- 同一文档可能同时出现在多个任务或多个业务视角下
|
||||
|
||||
### 4.3 不要靠前端自行拼表判断权限
|
||||
|
||||
原因:
|
||||
|
||||
- 字段口径容易漂移
|
||||
- 任务成员、负责人和创建者语义容易搞混
|
||||
- 前端无法作为权限真相源
|
||||
|
||||
### 4.4 不要再用“同名同类型猜版本”作为主逻辑
|
||||
|
||||
原因:
|
||||
|
||||
- 新平台已经有正式版本字段
|
||||
- 同名同类型只能作为兼容或兜底
|
||||
|
||||
## 5. 任务业务逻辑定稿
|
||||
|
||||
## 5.1 创建任务
|
||||
|
||||
### 输入
|
||||
|
||||
- 文档 ID 列表
|
||||
- 参与人 ID 列表
|
||||
- 主要负责人 ID 列表
|
||||
- 任务名称
|
||||
- 任务类型
|
||||
- 文档类型信息
|
||||
|
||||
### 必须规则
|
||||
|
||||
1. `assigner_id` 必须自动加入任务成员集合
|
||||
2. 成员列表必须去重
|
||||
3. 主要负责人必须同时是任务成员
|
||||
4. 所有文档必须真实存在
|
||||
5. 所有文档必须可被当前用户纳入交叉评查
|
||||
6. 初始状态必须是 `in_progress`
|
||||
|
||||
### 输出
|
||||
|
||||
- 任务主记录
|
||||
- 任务成员记录
|
||||
- 任务文档记录
|
||||
|
||||
### 注意
|
||||
|
||||
- 创建任务不等于重新跑评查
|
||||
- 默认不触发新的机器评查
|
||||
- 如果文档上传接口内包含自动评查,是上传链路行为,不是任务创建行为
|
||||
|
||||
## 5.2 任务列表
|
||||
|
||||
任务列表只返回当前用户参与的任务。
|
||||
|
||||
### 当前用户可见任务的判定
|
||||
|
||||
用户满足以下任一条件即可见:
|
||||
|
||||
- 是 `assigner`
|
||||
- 是成员表里的 `participant`
|
||||
- 是成员表里的 `principal`
|
||||
|
||||
### 返回字段必须稳定
|
||||
|
||||
- `task_id`
|
||||
- `task_name`
|
||||
- `task_status`
|
||||
- `doc_type`
|
||||
- `task_type`
|
||||
- `task_created_at`
|
||||
- `progress`
|
||||
- `total_documents`
|
||||
- `evaluation_region`
|
||||
|
||||
### 进度定义
|
||||
|
||||
`progress = 已完成任务文档数 / 任务文档总数 * 100`
|
||||
|
||||
这里的“已完成”只看:
|
||||
|
||||
- `leaudit_cross_review_task_documents.audit_status`
|
||||
|
||||
不看:
|
||||
|
||||
- `leaudit_documents.audit_status`
|
||||
- `leaudit_audit_runs.status`
|
||||
- `leaudit_rule_results`
|
||||
|
||||
## 5.3 任务状态
|
||||
|
||||
任务状态建议保留三态:
|
||||
|
||||
- `pending`
|
||||
- `in_progress`
|
||||
- `completed`
|
||||
|
||||
但如果当前产品没有明确“待启动”态,实际上可以只使用:
|
||||
|
||||
- `in_progress`
|
||||
- `completed`
|
||||
|
||||
### 状态流转规则
|
||||
|
||||
- 创建任务 -> `in_progress`
|
||||
- 所有任务内文档完成 -> `completed`
|
||||
- 只要还有未完成文档 -> 保持 `in_progress`
|
||||
|
||||
### 禁止的隐式流转
|
||||
|
||||
不要因为:
|
||||
|
||||
- 提案全部投完
|
||||
- 文档所有规则点都看过
|
||||
- 有人上传了附件
|
||||
|
||||
就自动把任务置完成。
|
||||
|
||||
任务完成只能由“所有任务内文档被确认完成”推导出来。
|
||||
|
||||
## 6. 任务文档业务逻辑定稿
|
||||
|
||||
## 6.1 文档进入任务
|
||||
|
||||
文档进入任务只有两种方式:
|
||||
|
||||
- 创建任务时挂入
|
||||
- 向已有任务追加文档
|
||||
|
||||
### 文档进入任务后默认状态
|
||||
|
||||
- `audit_status = 0`
|
||||
|
||||
即:
|
||||
|
||||
- 新纳入任务的文档,默认都还未完成交叉评查
|
||||
|
||||
## 6.2 文档完成的真正含义
|
||||
|
||||
任务内文档“完成”表示:
|
||||
|
||||
- 负责人或创建者确认:该文档在这个任务内的交叉评查工作结束
|
||||
|
||||
它不表示:
|
||||
|
||||
- 文档机器评查已完成
|
||||
- 所有人都看过
|
||||
- 所有提案都被通过
|
||||
- 文档在全局业务上彻底完结
|
||||
|
||||
## 6.3 谁可以确认文档完成
|
||||
|
||||
只有:
|
||||
|
||||
- 任务创建者 `assigner`
|
||||
- 主要负责人 `principal`
|
||||
|
||||
普通 `participant` 不可确认完成。
|
||||
|
||||
## 6.4 确认完成前的业务建议
|
||||
|
||||
建议在后端允许确认前做提醒型校验,而不是强阻断:
|
||||
|
||||
- 文档下是否还有 `pending` 提案
|
||||
- 是否存在未投票成员
|
||||
|
||||
推荐策略:
|
||||
|
||||
- 默认允许负责人确认完成
|
||||
- 如果仍有 `pending` 提案,返回告警信息给前端二次确认
|
||||
|
||||
这样更贴近真实业务,不会因为个别滞后投票把流程卡死。
|
||||
|
||||
## 7. 提案业务逻辑定稿
|
||||
|
||||
## 7.1 提案的最小绑定单位
|
||||
|
||||
提案必须绑定到:
|
||||
|
||||
- 一个任务
|
||||
- 一个文档
|
||||
- 一个规则结果 `rule_result_id`
|
||||
|
||||
不要只绑定:
|
||||
|
||||
- 文档 + 评查点名称
|
||||
- 文档 + 规则名
|
||||
|
||||
因为这些都不够稳定。
|
||||
|
||||
## 7.2 谁可以提案
|
||||
|
||||
只有该任务成员可以提案。
|
||||
|
||||
### 必须满足
|
||||
|
||||
1. 用户是任务成员
|
||||
2. 文档属于该任务
|
||||
3. `rule_result_id` 属于该文档
|
||||
4. 当前提案目标不是已删除/无效结果
|
||||
|
||||
## 7.3 提案唯一性规则
|
||||
|
||||
建议规则:
|
||||
|
||||
- 同一用户对同一任务下同一文档的同一 `rule_result_id`
|
||||
- 同时只能存在一条“有效提案”
|
||||
|
||||
有效提案指:
|
||||
|
||||
- `pending`
|
||||
- `approved`
|
||||
- `rejected`
|
||||
|
||||
如果需要允许历史重提,建议前提是:
|
||||
|
||||
- 原提案已 `cancelled`
|
||||
- 或被软删
|
||||
|
||||
## 7.4 提案分值规则
|
||||
|
||||
### 必须禁止
|
||||
|
||||
- `0` 分提案
|
||||
|
||||
### 必须校验
|
||||
|
||||
1. 当前分值已为 `0` 时,不允许继续扣分
|
||||
2. 当前分值已达该规则满分时,不允许继续加分
|
||||
3. 提案增量应用后,理论结果不能越界
|
||||
|
||||
边界是:
|
||||
|
||||
- 最低 `0`
|
||||
- 最高该规则满分
|
||||
|
||||
## 7.5 提案创建后的自动动作
|
||||
|
||||
提案创建成功后:
|
||||
|
||||
1. 自动写入一条提案人 `agree` 投票
|
||||
2. 立刻触发提案状态判定
|
||||
|
||||
## 8. 投票业务逻辑定稿
|
||||
|
||||
## 8.1 谁可以投票
|
||||
|
||||
只有该任务成员可以投票。
|
||||
|
||||
### 额外限制
|
||||
|
||||
- 非成员不能投
|
||||
- 已删除提案不能投
|
||||
- 已结束提案不能投
|
||||
|
||||
## 8.2 提案人能否投票
|
||||
|
||||
建议保留老逻辑:
|
||||
|
||||
- 提案创建后自动算提案人一票同意
|
||||
|
||||
这意味着:
|
||||
|
||||
- 提案人不需要再手动投一次
|
||||
- 后续若要改票,只能通过显式撤销/重投逻辑支持
|
||||
|
||||
## 8.3 投票值
|
||||
|
||||
推荐只保留:
|
||||
|
||||
- `agree`
|
||||
- `disagree`
|
||||
|
||||
“取消投票”不作为状态值保存,而是:
|
||||
|
||||
- 将该投票软删
|
||||
|
||||
## 8.4 一人一票
|
||||
|
||||
同一提案、同一成员:
|
||||
|
||||
- 同一时刻只能存在一条有效投票
|
||||
|
||||
如重复投票:
|
||||
|
||||
- 覆盖旧投票类型
|
||||
|
||||
## 9. 提案状态机定稿
|
||||
|
||||
提案状态建议固定为:
|
||||
|
||||
- `pending`
|
||||
- `approved`
|
||||
- `rejected`
|
||||
- `cancelled`
|
||||
|
||||
## 9.1 创建后的初始状态
|
||||
|
||||
- `pending`
|
||||
|
||||
## 9.2 通过规则
|
||||
|
||||
设任务有效成员数为 `n`
|
||||
|
||||
通过阈值:
|
||||
|
||||
- `threshold = floor(n / 2) + 1`
|
||||
|
||||
若:
|
||||
|
||||
- `agree >= threshold`
|
||||
|
||||
则:
|
||||
|
||||
- `proposal.status = approved`
|
||||
|
||||
## 9.3 否决规则
|
||||
|
||||
若:
|
||||
|
||||
- `disagree >= threshold`
|
||||
|
||||
则:
|
||||
|
||||
- `proposal.status = rejected`
|
||||
|
||||
## 9.4 提前否决规则
|
||||
|
||||
若:
|
||||
|
||||
- 即使所有剩余未投票者都改投同意,`agree` 也达不到 `threshold`
|
||||
|
||||
则:
|
||||
|
||||
- `proposal.status = rejected`
|
||||
|
||||
## 9.5 撤销规则
|
||||
|
||||
只有提案人可撤销,且只允许撤销:
|
||||
|
||||
- `pending`
|
||||
|
||||
撤销后:
|
||||
|
||||
- `status = cancelled`
|
||||
- 同时软删或停用关联投票
|
||||
|
||||
## 9.6 提案终态不可再操作
|
||||
|
||||
对于:
|
||||
|
||||
- `approved`
|
||||
- `rejected`
|
||||
- `cancelled`
|
||||
|
||||
不允许再继续投票或修改状态。
|
||||
|
||||
## 10. 分数语义定稿
|
||||
|
||||
这是实现中最容易“做着做着又回到老系统”的地方,必须说清楚。
|
||||
|
||||
## 10.1 三层分数
|
||||
|
||||
新项目详情页涉及三层分数:
|
||||
|
||||
### 机器分
|
||||
|
||||
来自:
|
||||
|
||||
- `leaudit_rule_results`
|
||||
|
||||
含义:
|
||||
|
||||
- 规则执行后机器给出的原始结果分值
|
||||
|
||||
### 人工审核分
|
||||
|
||||
来自:
|
||||
|
||||
- `leaudit_review_point_audits`
|
||||
|
||||
含义:
|
||||
|
||||
- 人工对规则结果的覆盖性判断
|
||||
|
||||
### 交叉评查调整分
|
||||
|
||||
来自:
|
||||
|
||||
- 所有已通过的 `leaudit_cross_review_proposals`
|
||||
|
||||
含义:
|
||||
|
||||
- 协作层加减分累计值
|
||||
|
||||
## 10.2 推荐最终展示分数计算
|
||||
|
||||
建议按以下顺序:
|
||||
|
||||
1. 先计算基础分 `baseScore`
|
||||
2. 再叠加交叉评查增量 `crossDelta`
|
||||
3. 再做边界裁剪
|
||||
|
||||
### `baseScore` 计算建议
|
||||
|
||||
若存在人工审核覆盖:
|
||||
|
||||
- 按人工审核后的通过/不通过结果计算基础分
|
||||
|
||||
否则:
|
||||
|
||||
- 按机器结果计算基础分
|
||||
|
||||
### `crossDelta`
|
||||
|
||||
取该 `rule_result_id` 下所有:
|
||||
|
||||
- `status = approved`
|
||||
- `deleted_at is null`
|
||||
|
||||
提案的 `proposed_score_delta` 累加值
|
||||
|
||||
### `finalScore`
|
||||
|
||||
`finalScore = clamp(baseScore + crossDelta, 0, ruleMaxScore)`
|
||||
|
||||
## 10.3 绝对禁止
|
||||
|
||||
不要:
|
||||
|
||||
- 直接更新 `leaudit_rule_results.score`
|
||||
- 直接把聚合后的最终分落回机器结果表
|
||||
|
||||
## 11. 详情页聚合逻辑定稿
|
||||
|
||||
当前详情页主入口已经是:
|
||||
|
||||
- `DocumentServiceImpl.GetReviewPoints()`
|
||||
|
||||
这条主链路可以保留。
|
||||
|
||||
## 11.1 详情页返回内容应包含
|
||||
|
||||
- 规则点列表
|
||||
- 单点机器分
|
||||
- 单点人工审核覆盖结果
|
||||
- 单点最终展示分
|
||||
- 交叉评查提案列表
|
||||
- 文档级统计信息
|
||||
|
||||
## 11.2 提案列表读取规则
|
||||
|
||||
提案列表优先从:
|
||||
|
||||
- `leaudit_cross_review_proposals`
|
||||
|
||||
读取。
|
||||
|
||||
如果需要历史兼容,可短期允许:
|
||||
|
||||
- 新表优先
|
||||
- 旧 `cross_scoring_proposals` 兜底
|
||||
|
||||
但这必须是过渡方案,不是长期方案。
|
||||
|
||||
## 11.3 文档总分统计
|
||||
|
||||
文档总分建议由每个规则点的最终展示分累加得到。
|
||||
|
||||
即:
|
||||
|
||||
- 总分不是单独存储字段
|
||||
- 而是聚合计算结果
|
||||
|
||||
## 12. 版本归纳逻辑定稿
|
||||
|
||||
## 12.1 主规则
|
||||
|
||||
任务文档列表必须优先基于:
|
||||
|
||||
- `versionGroupKey`
|
||||
|
||||
归组。
|
||||
|
||||
### 当前版本判定
|
||||
|
||||
优先使用:
|
||||
|
||||
- `isLatestVersion = true`
|
||||
|
||||
### 历史版本排序
|
||||
|
||||
按:
|
||||
|
||||
- `versionNo desc`
|
||||
|
||||
或时间倒序
|
||||
|
||||
## 12.2 兜底规则
|
||||
|
||||
只有在极端兼容场景下,才允许用:
|
||||
|
||||
- `normalizedName + typeId`
|
||||
|
||||
做辅助兜底。
|
||||
|
||||
## 12.3 追加附件后的语义
|
||||
|
||||
追加附件如果产生新文档,则该新文档:
|
||||
|
||||
- 应进入同一版本链
|
||||
- 自动挂到当前任务
|
||||
- 默认 `audit_status = 0`
|
||||
|
||||
原因:
|
||||
|
||||
- 新版本进入任务后,需要重新做交叉评查确认
|
||||
|
||||
## 13. 权限逻辑定稿
|
||||
|
||||
## 13.1 权限判断分两层
|
||||
|
||||
### 第一层:RBAC 权限点
|
||||
|
||||
例如:
|
||||
|
||||
- `cross_review:task:read`
|
||||
- `cross_review:task:create`
|
||||
- `cross_review:proposal:create`
|
||||
- `cross_review:proposal:vote`
|
||||
- `cross_review:document:complete`
|
||||
|
||||
### 第二层:业务归属校验
|
||||
|
||||
例如:
|
||||
|
||||
- 是否是该任务成员
|
||||
- 是否是负责人
|
||||
- 文档是否属于该任务
|
||||
- 规则结果是否属于该文档
|
||||
|
||||
两层都通过,才允许操作。
|
||||
|
||||
## 13.2 读权限
|
||||
|
||||
用户可读某任务,当且仅当:
|
||||
|
||||
- 是 `assigner`
|
||||
- 或是任务成员
|
||||
|
||||
## 13.3 写权限
|
||||
|
||||
### 创建提案
|
||||
|
||||
- 任务成员即可
|
||||
|
||||
### 投票
|
||||
|
||||
- 任务成员即可
|
||||
|
||||
### 撤销提案
|
||||
|
||||
- 仅提案人
|
||||
|
||||
### 确认文档完成
|
||||
|
||||
- 仅 `assigner` 或 `principal`
|
||||
|
||||
### 上传任务文档 / 追加附件
|
||||
|
||||
- 建议仅 `assigner` 或 `principal`
|
||||
|
||||
## 14. 实施时的接口行为定稿
|
||||
|
||||
## 14.1 后端必须兜住这些错误
|
||||
|
||||
- 文档不存在
|
||||
- 文档不属于任务
|
||||
- 规则结果不属于文档
|
||||
- 用户不是任务成员
|
||||
- 用户不是负责人
|
||||
- 提案重复
|
||||
- 提案已结束
|
||||
- 分值越界
|
||||
|
||||
## 14.2 后端返回建议
|
||||
|
||||
每个关键接口尽量返回:
|
||||
|
||||
- `success`
|
||||
- `message`
|
||||
- 必要结果对象
|
||||
- 必要告警信息
|
||||
|
||||
例如确认完成时可以带:
|
||||
|
||||
- `has_pending_proposals`
|
||||
- `pending_proposal_count`
|
||||
- `has_pending_votes`
|
||||
|
||||
## 15. 推荐开发顺序
|
||||
|
||||
为了避免做到一半逻辑漂移,建议严格按以下顺序:
|
||||
|
||||
### 第一步
|
||||
|
||||
先落表结构和状态定义。
|
||||
|
||||
### 第二步
|
||||
|
||||
实现 service 层纯业务逻辑:
|
||||
|
||||
- 任务
|
||||
- 提案
|
||||
- 投票
|
||||
- 完成确认
|
||||
|
||||
### 第三步
|
||||
|
||||
实现 controller 接口。
|
||||
|
||||
### 第四步
|
||||
|
||||
把详情页聚合接到新提案表。
|
||||
|
||||
### 第五步
|
||||
|
||||
再做上传、附件、版本链整合。
|
||||
|
||||
## 16. 最终一句话定稿
|
||||
|
||||
新项目交叉评查的正确实现方式应该是:
|
||||
|
||||
- 用 `leaudit_*` 作为文档与评查结果底座
|
||||
- 用 `leaudit_cross_review_*` 承接协作层
|
||||
- 用聚合计算得到最终展示分
|
||||
- 用任务内状态管理完成进度
|
||||
- 用后端统一收口权限和状态流转
|
||||
|
||||
只要这五点不偏,整体实现就不会跑歪。
|
||||
|
||||
@@ -0,0 +1,568 @@
|
||||
# 老项目交叉评查逻辑梳理
|
||||
|
||||
> 目标:整理老项目 `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`
|
||||
Reference in New Issue
Block a user