借款对账 S1、S2:导出与银行提交分开登记、确认失败收紧及失败回单识别口径(09-17-company-loan-principal-repayment)
S1(迁移 20261007010000) - 借款导出逐次记录;存量批次保留已知导出次数为历史汇总,不视为真实导出,存在即拦截 - 银行提交登记(只能撤回一次)与提交清单确认;独立复核,复核后任何登记、撤回、导出即失效 - 确认失败只计未作废附件;作废附件无法证明为明确失败或确属他笔付款即阻断 - S3 前临时拦截:多次导出、多次提交、清单未复核或历史汇总均不能确认失败 - 确认失败改按付款后规则;确认人不得兼任导出、提交登记、清单确认 S2(迁移 20261007020000) - 存在同金额候选时须有已复核、覆盖提交清单的失败归属;结算与失败归属分槽 失败回单识别口径(用户同意并入) - 新增原始状态措辞字段(多字段原文不一致即为空);失败事实严格判定:单页完整、 仅“交易失败/转账失败/付款失败/支付失败”、唯一问题为交易失败、字段齐全; 已撤销、已冲正、已退汇不接受;顶层识别状态与成功匹配不变 - 测试改走真实识别与归一化链,仅替换云端 OCR 响应 验证:借款 HTTP 41 组、浏览器 7 组、76 迁移;server 4463;共享回单测试 237; 三轮同模型只读复核的阻断项均已修复。 注意:界面未完成,不能单独部署;真实回单待验。未推送、未部署。 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -737,6 +737,10 @@ model Employee {
|
||||
|
||||
loan_receipt_attributions_proposed LoanReceiptAttribution[] @relation("LoanReceiptAttributionProposer")
|
||||
loan_receipt_attributions_reviewed LoanReceiptAttribution[] @relation("LoanReceiptAttributionReviewer")
|
||||
payment_order_exports_made PaymentOrderExport[]
|
||||
loan_bank_submissions_registered LoanBankSubmission[]
|
||||
loan_submission_attestations_proposed LoanBankSubmissionAttestation[] @relation("LoanSubmissionAttestationProposer")
|
||||
loan_submission_attestations_reviewed LoanBankSubmissionAttestation[] @relation("LoanSubmissionAttestationReviewer")
|
||||
@@map("employees")
|
||||
}
|
||||
|
||||
@@ -1977,6 +1981,10 @@ model PaymentBatch {
|
||||
@@index([payment_account_id], map: "idx_payment_batches_payment_account")
|
||||
@@index([payment_order_last_exported_by], map: "idx_payment_batches_order_exported_by")
|
||||
loan_receipt_attributions LoanReceiptAttribution[]
|
||||
payment_order_exports PaymentOrderExport[]
|
||||
loan_legacy_export_summary LoanLegacyExportSummary?
|
||||
loan_bank_submissions LoanBankSubmission[]
|
||||
loan_bank_submission_attestations LoanBankSubmissionAttestation[]
|
||||
@@map("payment_batches")
|
||||
}
|
||||
|
||||
@@ -3994,6 +4002,8 @@ model LoanReceiptAttribution {
|
||||
key_id String @db.VarChar(64)
|
||||
verification_ids Json
|
||||
manual_proposal_ids Json
|
||||
kind String @default("settlement") @db.VarChar(16)
|
||||
covered_submission_ids Json @default("[]")
|
||||
competitor_batch_ids Json
|
||||
basis_kind String @db.VarChar(32)
|
||||
basis_note String @db.VarChar(1000)
|
||||
@@ -4013,3 +4023,81 @@ model LoanReceiptAttribution {
|
||||
@@index([payment_batch_id, fingerprint], map: "loan_receipt_attributions_batch_fingerprint_idx")
|
||||
@@map("loan_receipt_attributions")
|
||||
}
|
||||
|
||||
// Loan bank-result reconciliation S1 (migration 20261007010000_loan_bank_submissions).
|
||||
// Append-only/one-way history triggers and partial unique index live in the migration.
|
||||
model PaymentOrderExport {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
payment_batch_id String @db.Uuid
|
||||
sequence Int
|
||||
exported_at DateTime @default(now())
|
||||
exported_by String @db.Uuid
|
||||
file_sha256 String @db.VarChar(64)
|
||||
customer_serials Json
|
||||
total_amount Decimal @db.Decimal(14, 2)
|
||||
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict)
|
||||
exporter Employee @relation(fields: [exported_by], references: [id], onDelete: Restrict)
|
||||
submissions LoanBankSubmission[]
|
||||
|
||||
@@unique([payment_batch_id, sequence], map: "payment_order_exports_batch_sequence_key")
|
||||
@@map("payment_order_exports")
|
||||
}
|
||||
|
||||
model LoanLegacyExportSummary {
|
||||
payment_batch_id String @id @db.Uuid
|
||||
known_export_count Int
|
||||
last_exported_at DateTime?
|
||||
last_exported_by String? @db.Uuid
|
||||
unknown_facts String @default("per_export_detail,file_digest,bank_submission_mapping") @db.VarChar(200)
|
||||
created_at DateTime @default(now())
|
||||
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict)
|
||||
|
||||
@@map("loan_legacy_export_summaries")
|
||||
}
|
||||
|
||||
model LoanBankSubmission {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
payment_batch_id String @db.Uuid
|
||||
export_id String? @db.Uuid
|
||||
channel String @db.VarChar(32)
|
||||
submitted_at DateTime
|
||||
bank_instruction_ref String @db.VarChar(120)
|
||||
customer_serial String? @db.VarChar(64)
|
||||
note String @db.VarChar(1000)
|
||||
status String @default("active") @db.VarChar(16)
|
||||
withdrawn_reason String? @db.VarChar(500)
|
||||
withdrawn_at DateTime?
|
||||
registered_by String @db.Uuid
|
||||
registered_at DateTime @default(now())
|
||||
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict)
|
||||
export PaymentOrderExport? @relation(fields: [export_id], references: [id], onDelete: Restrict)
|
||||
registrar Employee @relation(fields: [registered_by], references: [id], onDelete: Restrict)
|
||||
|
||||
@@index([payment_batch_id, status], map: "loan_bank_submissions_batch_idx")
|
||||
@@map("loan_bank_submissions")
|
||||
}
|
||||
|
||||
model LoanBankSubmissionAttestation {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
payment_batch_id String @db.Uuid
|
||||
submission_ids Json
|
||||
all_submission_ids Json
|
||||
export_ids Json
|
||||
note String @db.VarChar(1000)
|
||||
status String @default("proposed") @db.VarChar(16)
|
||||
proposed_by String @db.Uuid
|
||||
proposed_at DateTime @default(now())
|
||||
reviewed_by String? @db.Uuid
|
||||
reviewed_at DateTime?
|
||||
review_note String? @db.VarChar(500)
|
||||
superseded_at DateTime?
|
||||
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict)
|
||||
proposer Employee @relation("LoanSubmissionAttestationProposer", fields: [proposed_by], references: [id], onDelete: Restrict)
|
||||
reviewer Employee? @relation("LoanSubmissionAttestationReviewer", fields: [reviewed_by], references: [id], onDelete: Restrict)
|
||||
|
||||
@@map("loan_bank_submission_attestations")
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户