Appearance
对账中心系统设计
定位:幂等保证单笔正确,对账中心保证存量正确。它是资金系统的巡检雷达,只读分析、只产差异单、永不直接修数。三横一纵的概念与差错处理语义见幂等与对账-资金安全底座。文件批次协议见清结算与资方对接。本篇回答系统层问题:模块怎么划、文件怎么解析、亿级数据怎么对碰、差异怎么自动化收口。
一、模块划分:三横一纵 + 差错中心
三横一纵的概念定义与各层语义见幂等与对账-资金安全底座,此处不重复展开。各层语义:渠道对账挡外部、内部对账挡跨系统、总分核对挡账内、余额连续性防状态篡改。系统层做两件事。一是把四条核对线落成模块(按下表的数据源与节奏划分)。二是在其上加第⑤块差错中心统一收口:
| 模块 | 数据源 | 节奏 | 抓的错 |
|---|---|---|---|
| 渠道对账 | 渠道/资方账单文件 | T+1 | 掉单、掉账、金额不符、手续费口径 |
| 内部对账 | 业务事件 vs 账务凭证 | 准实时增量 + T+1 全量 | 事件丢失、记账漏记 |
| 总分核对 | 总账/分户账 | 日切 | 汇总批漏跑、明细汇总脱节 |
| 余额连续性 | 全账户流水 | 日切 | 任何人直接 UPDATE 过余额 |
| 差错中心 | 上述全部产出 | 常驻 | (收口,不抓错) |
模块化纪律:对账中心是独立的系统(独立库、独立算力、独立告警),不复用业务系统代码。它必须能怀疑所有人,包括生产数据的那些系统。
二、对账文件解析插件化
渠道账单格式千人千面(CSV/定长/加密 Excel/自造格式)。解析层用插件隔离差异,核心引擎只认统一中间模型:
java
public interface ReconFileParser {
boolean supports(ChannelFile file); // 渠道+版本路由到具体插件
FileManifest verifyManifest(ChannelFile file); // 批次清单三要素: 行数/金额合计/MD5
Stream<CheckRecord> parse(ChannelFile file); // 流式解析, 整文件不进内存
}
public class CheckRecord { // 统一中间模型(核心引擎唯一输入)
private String channelTradeNo; // 渠道交易号(对碰主键, 渠道侧唯一)
private String bizOrderNo; // 我方业务单号
private BigDecimal amount;
private String direction; // 收/付
private String status; // 终态
private LocalDate acctDate; // 账务日期
}sql
CREATE TABLE recon_batch ( -- 对账批次: 幂等重跑的单位
id BIGINT PRIMARY KEY,
recon_date DATE NOT NULL,
channel_code VARCHAR(32) NOT NULL,
recon_type VARCHAR(16) NOT NULL, -- CHANNEL/INTERNAL/BRANCH
file_manifest VARCHAR(512), -- 三要素留档
status VARCHAR(16) NOT NULL, -- DOWNLOADED/PARSED/MERGED/DONE/FAILED
UNIQUE KEY uk_date_chn_type (recon_date, channel_code, recon_type)
);插件化的三个工程点:
- 清单校验先于解析:行数、金额合计、MD5 三要素不过,文件直接拒收。防止"解析到 800 万行发现尾部截断"的算力浪费与半批脏数据。
- 新渠道接入 = 新增一个 Parser 插件,核心对碰引擎零改动。格式变更 = 插件内加版本分支。协议要素见清结算与资方对接。
- 解析产物落中间表而非直通对碰:解析与对碰解耦——解析失败重解析,对碰失败不必重新下载解密。
三、对碰算法:排序归并
对碰 = 两个有序流的双指针归并,不是逐条嵌套查询(N×M 是对账系统的第一死法)。双指针逐格走位——两边都有即平、单边即长/短款疑点、单号相同金额不符挂账待查。逐行演示见幂等与对账-资金安全底座的对碰图。系统层关心的是亿级规模的三个实现要点:
- 排序下推数仓:T+1 全量对碰就是一次 sorted merge join(Hive/Spark 一条 SQL),对账库只接增量与小渠道。应用层外部排序/分片归并只兜小文件。容量问题的答案是"下推给谁",不是堆内存。
- 对碰主键选渠道交易号:它是渠道侧唯一键。业务单号在掉单场景根本不存在,做主键会把"他有我无"误判成解析失败。
- 差异率本身是核心监控:昨天 0 今天 0.1%,不需要逐笔看。先怀疑批量脚本或渠道格式变更,先查面再查点。
sql
CREATE TABLE recon_diff (
id BIGINT PRIMARY KEY,
batch_id BIGINT NOT NULL,
diff_type VARCHAR(16) NOT NULL, -- LONG(长款)/SHORT(短款)/AMOUNT(金额不符)
biz_order_no VARCHAR(64),
channel_trade_no VARCHAR(64),
our_amount DECIMAL(16,2),
their_amount DECIMAL(16,2),
status VARCHAR(16) NOT NULL, -- 差错单状态机, 见第四节
root_cause VARCHAR(32), -- 闭环后回写根因(掉单/重复/bug/渠道)
UNIQUE KEY uk_batch_key (batch_id, channel_trade_no, diff_type)
);四、差错单状态机与自动化处理率
差异单生命周期沿用底座篇的 DISCOVERED(发现)→CONFIRMED(确认归类)→PROCESSING(处理中)→CLOSED(关闭) 状态机。含 FALSE_ALARM(在途到账/重复差异复核后关闭)与 REJECTED(处置方案驳回退回)两条旁路。任一状态停留超 SLA 即升级告警(资金敏感 30 分钟、普通 24 小时)。完整状态机图见幂等与对账-资金安全底座。
自动化处理率的边界——能自动的只有"补信息",改钱永远人工:
| 差异类型 | 自动化路径 | 自动化程度 |
|---|---|---|
| 长款(掉单) | 自动调渠道查单 → 复用正常入账幂等路径补单 | 高(90%+) |
| 在途/跨日时差 | 自动挂"在途",T+1 复对自动关闭 | 高 |
| 短款(我方误记) | 只自动建单派人工,冲正凭证必须审批 | 低 |
| 金额不符 | 自动比对原始凭证辅助定位,裁决归人工 | 低 |
自动化处理率 = 自动关闭的差异单 / 全部差异单,是差错中心的核心 KPI。每张关闭的差异单回写根因分类,月度归因反哺幂等与对账规则。自动化率的上限不在脚本写得多好,在上游幂等做得好坏。补单复用幂等路径的逻辑见幂等与对账-资金安全底座。
五、准实时对账的架构取舍
准实时与 T+1 的逐维对照(数据源/延迟/覆盖/成本)见幂等与对账-资金安全底座的对照表。一句话分工:准实时吃交易消息流、分钟级延迟,管掉单快速止血。T+1 吃账单文件与日切快照、次日验收,管资金终态。系统层的实现取舍在下面两处:
准实时的实现是消息驱动的轻量核对:
渠道受理成功(10:00) ──▶ 等待我方业务终态
├─ 10:02 终态到达 ──▶ 勾对完成, 结束
└─ 10:15 仍无终态 ──▶ 准实时对账告警 ──▶ 自动查单
├─ 查到成功 ──▶ 复用幂等入账路径补单
└─ 查不到 ──▶ 差错单人工跟进取舍结论:双轨是必需而非冗余。渠道的资金事实以它的账单文件为准(文件是渠道日切轧账后的终态)。所以准实时买的不是正确性,是发现速度。"渠道成功 15 分钟我方还没入账"在用户投诉前就被发现并触发补单。T+1 是不可绕过的最终验收,准实时规则再全也有盲区(手续费、结算批次只在文件里有)。只有准实时:差异可能永远差一点没人看;只有 T+1:掉单要等用户当客诉报上来。
六、异常与降级
| 故障 | 预案 |
|---|---|
| 对账文件未到 | 超时告警(核心渠道 30 分钟级)+ 催重推通道;差异累积可见,绝不"文件没来当没差异" |
| 对账作业失败 | 批次幂等(uk_date_chn_type)断点重跑,不产生重复差异单 |
| 数仓延迟 | 对碰窗口顺延并告警,绝不跳过——跳过的日子就是审计黑洞 |
| 准实时链路故障 | 退化为纯 T+1(可接受降级),恢复后补扫在途 |
| 差异单积压超阈值 | SLA 升级 + 差异率告警双通道;联动冻结上游清算(带差异不清算,见清结算与资方对接) |
七、追问链
Q1:对账程序为什么永不直接修数据? 两个理由:修数的正确性需要业务上下文(长款可能是掉单也可能是重复推送),自动判断越权。对账系统的信任模型建立在"它只读"上。一旦它能写,它自己的 bug 就成了新的差异源,且无法用对账自证。发现与修复分离,修复复用正常业务路径。
Q2:差异率从 0 突然涨到 0.1%,逐笔查吗? 不。差异率是面指标。ppm 级到 bp 级的跳变几乎必然是系统性原因(批量脚本变更、渠道格式升级、时钟漂移)。先看变更记录与分布(是否集中某渠道/某时段),再抽点验证。"先查面再查点"是差异处理的第一响应纪律。
Q3:内部对账和总分核对是不是重复建设? 不重复:内部对账是业务系统与账务系统之间的跨系统核对(抓事件丢失)。总分核对是账务系统内部总账与分户账的核对(抓汇总脱节)。前者对不上可能业务漏发事件、账务没错,后者对不上一定是账务内部问题。混在一个作业里,每次告警先吵半天归属。
Q4:自动补单会不会把差异修成第二个错? 补单走与正常回调完全相同的幂等入账路径(CAS + 唯一约束),已入账则幂等返回。补单不引入新写路径,幂等性天然继承。真正禁止的是另起炉灶的"修数脚本",那是二次事故之源。
Q5:亿级文件双方流内存放不下怎么办? 排序归并的本质是两个游标,不需要整文件在内存。解析即流式(Stream),排序下推数仓或做外部排序(分片→各片排序→归并),应用层只需 O(1) 的双指针缓冲。对账的容量问题永远是"把排序下推给谁"的问题。
Q6:对账中心自己挂了谁发现? 三层:文件到达监控(渠道文件未到告警独立于对账作业)。作业心跳与批次 SLA(状态超时未推进告警)。外部巡检(日终对账日报缺失即告警)。对账失效是静默失效——它不报警不等于没问题,所以"对账在跑"本身必须被监控。
Q7:差错单为什么要有 SLA 升级,关闭了不就行? 差异单是负债不是记录:开着的差异单意味着有一笔钱归属不明。SLA 升级(资金敏感 30 分钟、普通 24 小时)保证差异单不会变成"永久挂起"。挂账科目账龄巡检(超 N 天升级)与之配套——蓄水池不排水就是事故前兆。
八、面试官视角小结
面试官在对账设计里检查四件事:
- 模块观:三横一纵是不是按"抓的错"划分的——按渠道或按表划分,说明没想过对账的分治逻辑;
- 工程底线:对碰是不是排序归并、差异是不是只产工单不修数。嵌套循环对碰和自动改钱是一票否决项;
- 自动化边界:能不能说清"补信息可自动、改钱必人工"及其原因;
- 双轨取舍:准实时和 T+1 各买什么,说得出"文件才是资金终态事实源"才算懂。
一句话总结:三横一纵抓四类错,插件解析排序对碰,只产差异永不修数,准实时止血 T+1 验收。
2026-10 新增(金融信贷域 · 系统设计层)。