借款对账 S3:银行终态确认与释放(09-17-company-loan-principal-repayment)
迁移 20261008010000 - 银行终态证据单独成表(不进 OCR、不改付款状态、不计回单),只能作废一次, 作废即令依赖事实失效;复核人可凭理由查看(审计、SHA-256 校验) - 每次银行提交一条终态事实:书面终态证据,或流水号等于指令号的严格失败回单; 同一指令的终态可了结该指令的处理中回单,其他提交不能代替 - 新终态 confirmed_not_paid;释放须清单已复核未变、每次提交有成立的终态事实、 导出均关联提交、回单均有解释、等待期满、无历史汇总 - 最终取消在同一事务内重验(与新提交、晚到回单串行);两条释放路径都检查等待期 - 防挪用:指令号跨批次唯一(含撤回)、失败回单绑定本批次指令、一笔失败交易 只支撑一个批次、同一书面证据不能用于两个批次、登记提交即离开“未发送” 验证:借款 HTTP 57 组、浏览器 7 组、77 迁移;server 4472;四轮同模型只读复核 阻断项均已修复,资金守卫变异测试均变红。 注意:界面未完成,不能单独部署;真实回单待验;补证后的恢复缺口尚未设计。 未推送、未部署。 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -741,6 +741,9 @@ model Employee {
|
||||
loan_bank_submissions_registered LoanBankSubmission[]
|
||||
loan_submission_attestations_proposed LoanBankSubmissionAttestation[] @relation("LoanSubmissionAttestationProposer")
|
||||
loan_submission_attestations_reviewed LoanBankSubmissionAttestation[] @relation("LoanSubmissionAttestationReviewer")
|
||||
loan_terminal_evidences_uploaded LoanBankTerminalEvidence[]
|
||||
loan_terminal_facts_proposed LoanBankTerminalFact[] @relation("LoanTerminalFactProposer")
|
||||
loan_terminal_facts_reviewed LoanBankTerminalFact[] @relation("LoanTerminalFactReviewer")
|
||||
@@map("employees")
|
||||
}
|
||||
|
||||
@@ -1985,6 +1988,9 @@ model PaymentBatch {
|
||||
loan_legacy_export_summary LoanLegacyExportSummary?
|
||||
loan_bank_submissions LoanBankSubmission[]
|
||||
loan_bank_submission_attestations LoanBankSubmissionAttestation[]
|
||||
loan_terminal_evidences LoanBankTerminalEvidence[]
|
||||
loan_terminal_facts LoanBankTerminalFact[]
|
||||
loan_failure_evidence_uses LoanFailureEvidenceUse[]
|
||||
@@map("payment_batches")
|
||||
}
|
||||
|
||||
@@ -2043,6 +2049,7 @@ model PaymentBatchReceiptAttachment {
|
||||
receipt_ocr_state String @default("legacy") @db.VarChar(32)
|
||||
receipt_ocr_started_at DateTime?
|
||||
|
||||
loan_terminal_facts LoanBankTerminalFact[]
|
||||
@@index([payment_batch_id, created_at], map: "idx_payment_batch_receipts_batch_created")
|
||||
@@index([uploaded_by], map: "idx_payment_batch_receipts_uploader")
|
||||
@@index([file_sha256], map: "idx_payment_batch_receipts_file_sha256")
|
||||
@@ -2066,6 +2073,7 @@ model PaymentReceiptVerification {
|
||||
claims PaymentReceiptClaim[]
|
||||
manual_proposals PaymentReceiptManualProposal[]
|
||||
|
||||
loan_terminal_facts LoanBankTerminalFact[]
|
||||
@@index([attachment_id, created_at])
|
||||
@@map("payment_receipt_verifications")
|
||||
}
|
||||
@@ -4004,6 +4012,7 @@ model LoanReceiptAttribution {
|
||||
manual_proposal_ids Json
|
||||
kind String @default("settlement") @db.VarChar(16)
|
||||
covered_submission_ids Json @default("[]")
|
||||
failure_scope String @default("batch") @db.VarChar(16)
|
||||
competitor_batch_ids Json
|
||||
basis_kind String @db.VarChar(32)
|
||||
basis_note String @db.VarChar(1000)
|
||||
@@ -4076,6 +4085,8 @@ model LoanBankSubmission {
|
||||
export PaymentOrderExport? @relation(fields: [export_id], references: [id], onDelete: Restrict)
|
||||
registrar Employee @relation(fields: [registered_by], references: [id], onDelete: Restrict)
|
||||
|
||||
terminal_facts LoanBankTerminalFact[]
|
||||
|
||||
@@index([payment_batch_id, status], map: "loan_bank_submissions_batch_idx")
|
||||
@@map("loan_bank_submissions")
|
||||
}
|
||||
@@ -4101,3 +4112,71 @@ model LoanBankSubmissionAttestation {
|
||||
|
||||
@@map("loan_bank_submission_attestations")
|
||||
}
|
||||
|
||||
// Loan bank-result reconciliation S3 (migration 20261008010000_loan_bank_terminal_facts).
|
||||
// Evidence is stored apart from payment receipts; history triggers live in the migration.
|
||||
model LoanBankTerminalEvidence {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
payment_batch_id String @db.Uuid
|
||||
storage_path String
|
||||
original_name String @db.VarChar(255)
|
||||
mime_type String @db.VarChar(100)
|
||||
file_size Int
|
||||
file_sha256 String @db.VarChar(64)
|
||||
uploaded_by String @db.Uuid
|
||||
uploaded_at DateTime @default(now())
|
||||
voided_by String? @db.Uuid
|
||||
voided_at DateTime?
|
||||
void_reason String? @db.VarChar(500)
|
||||
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict)
|
||||
uploader Employee @relation(fields: [uploaded_by], references: [id], onDelete: Restrict)
|
||||
facts LoanBankTerminalFact[]
|
||||
|
||||
@@index([payment_batch_id], map: "loan_bank_terminal_evidences_batch_idx")
|
||||
@@map("loan_bank_terminal_evidences")
|
||||
}
|
||||
|
||||
model LoanBankTerminalFact {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
payment_batch_id String @db.Uuid
|
||||
submission_id String @db.Uuid
|
||||
kind String @db.VarChar(32)
|
||||
terminal_status String @db.VarChar(16)
|
||||
evidence_id String? @db.Uuid
|
||||
receipt_attachment_id String? @db.Uuid
|
||||
verification_id String? @db.Uuid
|
||||
resolves_attachment_ids Json @default("[]")
|
||||
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)
|
||||
submission LoanBankSubmission @relation(fields: [submission_id], references: [id], onDelete: Restrict)
|
||||
evidence LoanBankTerminalEvidence? @relation(fields: [evidence_id], references: [id], onDelete: Restrict)
|
||||
receipt_attachment PaymentBatchReceiptAttachment? @relation(fields: [receipt_attachment_id], references: [id], onDelete: Restrict)
|
||||
verification PaymentReceiptVerification? @relation(fields: [verification_id], references: [id], onDelete: Restrict)
|
||||
proposer Employee @relation("LoanTerminalFactProposer", fields: [proposed_by], references: [id], onDelete: Restrict)
|
||||
reviewer Employee? @relation("LoanTerminalFactReviewer", fields: [reviewed_by], references: [id], onDelete: Restrict)
|
||||
|
||||
@@index([payment_batch_id, status], map: "loan_bank_terminal_facts_batch_idx")
|
||||
@@map("loan_bank_terminal_facts")
|
||||
}
|
||||
|
||||
// One failed bank transaction backs the release of at most one batch.
|
||||
model LoanFailureEvidenceUse {
|
||||
fingerprint String @id @db.VarChar(64)
|
||||
key_id String @db.VarChar(64)
|
||||
payment_batch_id String @db.Uuid
|
||||
source String @db.VarChar(32)
|
||||
created_at DateTime @default(now())
|
||||
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict)
|
||||
|
||||
@@map("loan_failure_evidence_uses")
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户