Files
caiwuzongzhang/docs/decisions/005-canonical-transfer-matching.md
T

64 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 005 规范转账事件与双边归并技术决策
对应 IssueB-43/B-83/B-114`docs/issues/006-p1-canonical-transfer-matching.md`)。
## 分层模型
- 银行源行是不可变观察;匹配层从不修改 `source_rows`
- 新增“不可变决定日志 + 可重建当前投影”的规范事件层:
- `transfer_match_decisions` 等证据表只增不改(UPDATE/DELETE 被触发器拒绝)。
- `current_transfer_decisions``transfer_observation_claims` 是可更新、可重建的
当前投影,`source_row_id` 主键保证一条观察不可能同时属于两个当前事件。
- B-44 余额计算只读 `eligible_intercompany_events` 视图,只含
`active + current + classification='intercompany'`,且单边事件必须
`locked = 1`(管理员按证据确认)才可计算;paired 事件不受锁定状态限制;
单边内部、待审、同公司调拨、外部事件全部排除。
## 已确认业务口径(B-114
1. 单边流水即使双方公司都能由批准账号唯一确认,也先进入未决金额,不进入 B-44
已确认往来余额;只有完成双边归并(`intercompany` + `paired`)或管理员按证据确认
`intercompany` + locked 单边)后才可计算。
2. 跨日事件经济日期取付款方 outgoing 银行入账时间,与导入顺序无关。
3. 自动窗口 v1:非空参考号相同且账号镜像 <= 3 自然日(M1);无参考号精确账号镜像
<= 1 自然日(M2);别名/个人映射镜像 <= 1 自然日且参考号或摘要相等(M3)。
超窗或同层多候选全部进入人工审核,禁止按行号、导入顺序或名字破平局。
4. 个人过账映射绑定具体账号、生效区间、允许方向和代表公司,必须管理员批准;
姓名只作辅助证据,不能单独定案。
## 参与方解析
- 本方:源行 `own_account` 精确命中生效期内已批准账户(`own_exact`);缺失时才用
上传时持久化的批准上传账户(`upload_account`),且必须与批次公司一致。冲突进入
审核,不以 `company_id` 或户名反推。
- 对方:严格按账号层解析——精确账号 -> 已审核账号别名 -> 已批准且在窗口内、方向
允许的个人过账映射;每层唯一最佳结果才继续。户名/户名别名只用于一致性/冲突证据,
冲突降级到审核。
- 方向:`expense > 0 且 income = 0` 为 outgoing`income > 0 且 expense = 0`
incoming;双正/双零/负数/缺币种进入 `unresolved`。金额用 `Decimal` 精确相等,
不设手续费容差。
## 匹配与状态机
- 候选硬门槛:双方工作表 confirmed、方向相反、金额与币种相等、公司端点互为反向。
- M1/M2/M3 唯一候选自动 paired;R1(端点可确认但镜像/参考号证据不足)与 R2(同层
多候选)进入 `needs_review`;待审行不能被其他自动匹配抢占。
- 状态:`unresolved -> internal_single -> matched`、`needs_review -> matched |
same_company_transfer | external`;单边确认两端同公司立即为同公司调拨。
- 幂等:rule_version + 当前观察集合 + 证据未变时零写入;locked 人工决定自动重跑
跳过;纠错只能通过 `reverse``mode='reversal'` 新记录)+ 原因,之后允许重跑。
## 事务、并发与回滚
- “确认工作表 + 为新增确认行建立/更新事件 + audit”在同一个服务事务,任一失败整体
回滚,不存在 confirmed 但未匹配的半成品。
- 独立重跑使用 `BEGIN IMMEDIATE`,按 `source_row_id` 升序写,SQLite 30 秒 busy
timeout 保留;投影可从只增日志重建(`rebuild_current_projection`)。
- migration down 只用于测试/上线前回退;生产产生决定后默认保留 migration 5 与审计
数据,不执行破坏性 down。
## Decimal
金额全程以规范十进制字符串存储和比较,任何路径都不转 `float``100.0` 与 `100.00`
视为相等,`100.00` 与 `100.01` 不匹配。