重构: 文档审核独立子域(正交双轴 + 可扩展待审原因) + 导出 MRF 信号列 — #284 #287#288
Merged
Conversation
把"文档审核"建模为独立子域,消除 ReviewStatus/ClassificationReason 多义等补丁病。
- Domain.Shared: 新增 DocumentReviewReasons([Flags]) + ReviewReasonPolicy(blocking 单点声明);
DocumentReviewStatus 重命名为 DocumentReviewDisposition(删 PendingReview,迁为原因位);
DocumentConsts 删 MaxClassificationReasonLength、加 MaxRejectionReasonLength
- Domain: Document 拆 ReviewDisposition + ReviewReasons 正交双轴 + 独立必填 RejectionReason
+ 按位 SetReviewReason 唯一写入口; 删 ClassificationReason; 新增 ReviewStateEvaluator(纯函数);
DeriveLifecycle 闸门改 !HasBlocking(ReviewReasons)(等价且可扩展); 低置信路径不再持久化 reason
- 横切: DTO/Input/AppService(审核队列 HasReviewReasons 过滤,排除已拒绝)/Export/EF 配置
(ReviewDisposition 用 HasColumnName("ReviewStatus") 复用旧列名,迁移不重命名列)
- 测试: 现有审核测试适配 ReviewDisposition/ReviewReasons + 新增 11 个 evaluator/policy 单测
分阶段实施(#284)的 PR1。EF 迁移 / 字段抽取接 MRF / DTO 出口新字段 / 客户端 留后续 PR。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- RenameColumn ClassificationReason → RejectionReason(Rejected 行拒绝理由保留) - AddColumn ReviewReasons int NOT NULL default 0(不溯及存量,#284 决策) - 数据回填(顺序敏感): 清非 Rejected 行残留的旧 AI 理由; 旧 PendingReview(10) 拆 NotReviewed(0)+UC(1) - DB 列名 ReviewStatus 保留(HasColumnName),不重命名列 经 ef-migration-safety-reviewer 审查: 无高风险——RenameColumn=sp_rename 元数据操作、 AddColumn NOT NULL default=SQL Server 元数据级、列名/索引零物理变更、模型快照三件套同步。 部署提示: 两条全表 UPDATE 在大表需低峰执行; 如需留底旧 AI 分类理由请迁移前自行归档。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- FieldExtractionEventHandler: 注入 ReviewStateEvaluator; 写字段同一 UoW 内(stale 守卫之后) 复用 currentDefinitions 筛 IsRequired + ExtractedFieldValues 评估, 物化 MissingRequiredFields; 空字段定义路径清 MRF - DocumentAppService.UpdateExtractedFieldsAsync: 操作员补录后同样评估 MRF(补齐→clear, 退出队列闭环) - Document.SetReviewReason 改 public(MRF 写入点在 Application 层, 跨程序集; 仍约定每 bit 单阶段维护) - 测试: 加 必填缺失→置 MRF / 必填齐全→不置 MRF 两个 handler 用例 此后"必填缺失进操作员队列、不阻断下游(non-blocking, 不影响 Ready/DocumentReadyEto)"后端端到端可用。 Application.Tests 223 / Domain.Tests 150 全绿。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
直接验证重构核心需求: 带 MissingRequiredFields 的文档, 关键流水线成功 + 类型确认后 仍跃迁 Ready(必填缺失只进操作员队列, 不阻断下游 DocumentReadyEto), MRF 原因位保留。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- DocumentDto 加 ReviewReasons/RequiresReview/RejectionReason/ReviewReasonDetails;
DocumentListItemDto 加 ReviewReasons/RequiresReview(列表薄, 不带明细)
- 新增 ReviewReasonDetailDto{Reason,IsBlocking,MissingFieldNames?}——服务端算/客户端纯渲染:
IsBlocking 按 ReviewReasonPolicy 填; MissingFieldNames 读时算(required\extracted 的 DisplayName)
- Mapper: ReviewReasons/RejectionReason 自动映射, RequiresReview/ReviewReasonDetails ignore + AppService 填
- MapToDtoAsync 详情厚(组装明细), FillListReferencesAsync 列表薄(仅 RequiresReview, 避免 N+1)
- 测试: 详情 DTO 出口必填缺失明细(含 MissingFieldNames + IsBlocking=false)端到端
Application.Tests 224 全绿。ETO/MCP 下游契约未动(审核轴不入下游)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- generate-proxy 重生: 删 DocumentReviewStatus enum, 新增 DocumentReviewDisposition + DocumentReviewReasons, models 加 reviewDisposition/reviewReasons/requiresReview/reviewReasonDetails/rejectionReason, 删 classificationReason - document-list: 消除 reviewStatus===PendingReview 自推断; 队列过滤改 hasReviewReasons; 双 badge(生命周期+审核); needsConfirmation 按 UnresolvedClassification; isProcessing 回归纯可用性轴 - document-detail: needsReview→requiresReview; header badge 纯生命周期; 详情区渲染 reviewReasonDetails 明细 (含缺失必填字段名 + IsBlocking); 删 classificationReason 块改 rejectionReason - document-review-queue: 队列查询改 hasReviewReasons; 拒绝理由必填校验 - public-api re-export 换新 enum; proxy-contract.spec 断言 Disposition/Reasons 值; 本地化(en/zh): 删旧 DocumentReviewStatus key, 加 ReviewReason/ReviewDisposition/NeedsReview/RejectionReason vitest 4 全绿; nx build 全成功(paperbase + host)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- #1 把"需关注"收敛为唯一判据 ReviewReasonPolicy.RequiresAttention(reasons, disposition), 出口 RequiresReview/列表/明细/前端 needsConfirmation 全用之;删除已无生产消费方的单参重载(footgun) - #2 离开 Rejected 即清 RejectionReason(ApplyAutomaticClassificationResult/RequestClassificationReview/ConfirmClassification) - #4 MRF 缺失字段名为空时跳过空壳明细;明细整体为空返回 null(与"无明细"语义统一) - 注释对齐双轴模型:DTO RequiresReview 定义、ApplyManualClassification(Reviewed→Confirmed)、测试 Scenario 6/7 - #3 核验:UC 文档 DocumentTypeId=null 被类型绑定导出过滤,复合映射是死代码——仅改测试断言 None→NotReviewed - #5 核验驳回:两处字段定义查询 soft-delete 语义相反,不合并(加注释固化) - 回归测试:RequiresAttention 真值表、(MRF,Confirmed)→需关注、空 MRF 明细跳过、拒绝空理由校验、RejectionReason 清理 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- docs/classification.md、docs/text-extraction.md:把已删的 ReviewStatus/PendingReview/Reviewed 三态、 ClassificationReason 描述改写为双轴模型(ReviewDisposition 处置轴 + ReviewReasons 原因轴) - 代码注释/日志统一术语:ReclassifyDocumentInput / DocumentReadyEventHandler / DocumentClassificationWorkflow / DocumentClassificationBackgroundJob / DocumentType / DocumentAppService 里"PendingReview 状态/队列"→"待人工审核(UnresolvedClassification)" - 拒绝理由文案纠正:RejectReasonHint 由"选填/Optional"改为"必填/Required"(与 [Required] + 客户端非空校验一致), 审核队列拒绝弹窗 label 加必填星号 迁移说明类引用(DocumentReviewDisposition/DocumentReviewReasons 的"旧 X 已迁为 Y")保留不动。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#284 双轴后导出固定系统列只跟处置轴(ReviewStatus=ReviewDisposition),丢了原因轴。 non-blocking 的 MissingRequiredFields 文档照常进类型绑定导出(DocumentTypeId 非空、Ready), 缺必填的行在导出文件里与完好行不可区分。补 ReviewReasons 系统列: - ExportProjection + ExportTemplateAppService:新增固定列 ReviewReasons (顺序 LifecycleStatus, ReviewStatus, ReviewReasons, Title);None→空单元格,否则 [Flags].ToString() - ExportTemplateExport_Tests:表头/行断言加 ReviewReasons 列 + 新增 Confirmed+MRF 文档导出用例 - 顺带解封 EFCore.Tests 编译:ExportColumn 旧 3 参调用(#207 遗留)改 2 参(Guid,int), SeedSchemaAsync seed 真实 DisplayName(导出表头取 FieldDefinition.DisplayName) 边界:仅补 CSV/XLSX 导出信道;REST DocumentDto 已完整出口 MRF;ETO/MCP 下游契约不动。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
duguankui
pushed a commit
that referenced
this pull request
Jun 9, 2026
集成 #288(#284 文档审核两轴重构)。解决冲突 + 迁移重处理代码到新审核模型: - FieldExtractionService 纳入 #284 的 ReviewStateEvaluator + MissingRequiredFields 评估(抽取完成物化必填缺失信号;空字段路径一并清 MissingRequiredFields)—— 与 origin/main 的事件 handler 行为对齐,handler 仍委托引擎。 - 重处理范围查询从已删除的 DocumentReviewStatus 迁到两轴模型: excludeManuallyConfirmed → ReviewDisposition != Confirmed; 「待审核队列」范围 → ReviewReasons 含 UnresolvedClassification(旧 PendingReview)。 仓储参数 reviewStatus → withReason(DocumentReviewReasons?),贯穿 AppService / dispatcher args / 测试。 - ExportTemplateExport_Tests 取 origin/main 版(#288 已独立修复,撤回本分支的并行修复)。 全绿:Domain 157 / Application 239 / EFCore 56;host 构建 0 错误。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
duguankui
pushed a commit
that referenced
this pull request
Jun 9, 2026
合并 origin/main(#288/#284) 后这些迁移编辑漏 git add,致合并提交编译失败(仍引用已删除的 DocumentReviewStatus)。本提交补齐: - FieldExtractionService 纳入 ReviewStateEvaluator + MissingRequiredFields 评估 - 仓储/AppService/dispatcher 范围参数 reviewStatus → withReason(DocumentReviewReasons?); excludeManuallyConfirmed → ReviewDisposition != Confirmed;待审核队列 → UnresolvedClassification - 测试同步迁移 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
把
Document的"审核状态"重建为独立子域(#284):从多义的单ReviewStatusenum 拆为两个正交轴 + 可扩展原因集合,并落地新需求"必填字段缺失进操作员队列、但不阻断下游消费"。顺带补齐导出对原因轴的透出(#287)。LifecycleStatus(不动)— 对下游放行ReviewDisposition(由ReviewStatus重命名收敛:NotReviewed/Confirmed/Rejected,删PendingReview)— 只由操作员动作写ReviewReasons(新增[Flags]:UnresolvedClassification阻断 Ready /MissingRequiredFieldsnon-blocking)— 只由 pipeline / evaluator 写RejectionReason(独立必填,替代寄生在分类字段上的ClassificationReason)"需操作员关注"统一判据:
ReviewReasons != None && ReviewDisposition != Rejected(ReviewReasonPolicy.RequiresAttention),服务端算 / 客户端纯渲染。提交分解(可独立审阅)
6aa4dc5ReviewStateEvaluator+ Ready 闸门改!HasBlocking2431a49Refactor_DocumentReview(回填顺序敏感:RejectionReason 早于 DROP;旧 PendingReview 拆 NotReviewed+UC)e64f76e/54a753ff9f869eaca3e8a0a99218f273bf28ca1a05质量保障
边界合规
ReviewDisposition/ReviewReasons/RejectionReason均为通用审核元数据,不绑业务领域;删ClassificationReason收窄聚合根表面。DocumentReadyEto/DocumentClassifiedEto/FieldsExtractedEto与 MCP 出口均不含审核态。导出: 系统列补 ReviewReasons,透出 MissingRequiredFields 质量信号(#284 子项) #287 仅补 CSV/XLSX 导出信道(RESTDocumentDto已完整出口 MRF)。Closes #284
Closes #287
🤖 Generated with Claude Code