3.3 对账:订单与库存的核对

财务同学第一次来我们工位是来吵架的:上个月的账,渠道流水比订单系统多 1700 元,17 笔支付成功但订单状态还是待支付。「你们的系统不是有事务吗?」她问。解释完异步回调的不可靠性之后,她说了一句影响整个项目的话:系统可信不可信,看你们每天敢不敢对账。

场景:三个数据源的三张表

对账的本质是核对三个独立数据源:订单系统的 t_order、库存系统的 t_stock 变动流水、支付渠道的下载账单。三者各自独立演进,任何一步失败都可能让它们不一致。不一致不一定是 bug(也可能是时间差),但没有对账就没有发现机制,等到月底财务才发现已经太晚了。

方案:三张核对表

对账作业每天凌晨执行,产出三个方向的核对:

核对项 双方口径 发现的不一致
订单与渠道 订单金额 vs 渠道流水 掉单、金额错
订单与库存 状态与冻结标记 漏释放、重复扣
渠道与库存 成功流水 vs 实扣 少扣、超扣

对账作业的实现方式走的是「快照 + diff」模式。每日两点做库存快照(每个 SKU 的 available、frozen、version 写入快照表),对账时用快照与流水倒推,而不是实时查业务表,避免大查询影响线上。

对账流程的伪代码:

public void reconcile(LocalDate day) {
    Map<String, Order> orders = orderDao.listPaidOrders(day);
    Map<String, ChannelBill> bills = channelDao.downloadBill(day);
    Set<String> diffs = new HashSet<>();
    diffs.addAll(missingInBills(orders, bills));   // 渠行无此单
    diffs.addAll(missingInOrders(orders, bills));  // 订单系统无
    diffs.addAll(amountMismatch(orders, bills));   // 金额不符
    diffDao.save(diffs);                            // 落差异表
    alert(diffs);                                   // 报警
}

踩坑:对账本身的三个坑

第一个是渠道流水下载的幂等。渠道账单文件一天可能被下载两次(手工触发的补跑),不去重会把上一笔差异再报一次。处理方式是以渠道流水号为唯一键,处理前先查差异表是否已存在。

第二个是时间口径。支付成功的回调时间、渠道记账时间、订单状态更新时间三者有秒级差异。以渠道记账时间为主口径,因为它最准。订单状态变更以渠道账单为准做修正,把延迟到达的回调修正掉。这实际上是掉单补偿的另外一种实现,主动对账发现的问题比被动等待回调更及时,相关的主动查单机制在 掉单补偿 一节展开。

第三个是对账作业自己出 bug 怎么办。我们的对账是双写的:差异表同时写一份本地文件,每日对账报告本身也要对账。听起来有点套娃,但确实有用:一次 MySQL 主从延迟让对账结果本身少了三行,报告报了假阳性,幸亏本地文件对比上了。

差异单的处理流程也很关键:差异单报警进入工单队列,由值班同学人工确认,确认后修数据要走状态机接口而不许直接改库。这些治理细节与 服务治理实践 的故障处理流程是一个思路。

另外对账的产出物「差异单数量」是核心监控指标。我们的经验值:上线第一个月日均 40 单,到第三个月降到 3 单以内。差异单没有降到零之前,说明链路里还有没堵住的洞。这与一些 云上架构实践 中的稳定性指标思路一致。

进阶实战:差异单分类处理

差异单不是均匀的,按类别分流处理效率最高。我们运行三个月后总结出四类差异,各有专属处理流程。

差异类别 典型来源 处理方式
掉单 回调丢失 主动查单补偿
金额差 优惠券口径不同 人工核对后统一口径
状态漂移 回调延迟到达 补偿任务修正状态
未知差异 系统未知 bug 升级到技术组排查

90% 的差异单都是掉单与状态漂移,自动补偿脚本就能处理,人工只需关注自动处理失败的部分。差异单处理平台上线后,财务从每天处理单据变成每周看一次自动汇总报表。

对账还有一个延伸价值:它量化了系统的真实可靠性。每月对账报告里的「差异率」是上线新功能后稳定性最直接的反馈。我们观察到每次大改支付链路后,差异率会在随后两周有小幅波动然后回落,消化期一过就迎来新的低点。这个曲线已经成了技术债的晴雨表。

速查清单

对账体系的四个组成:

组成 职责
快照采集 每日两点的库存与订单快照
三源 diff 订单、库存、渠道流水核对
差异工单池 分类、自动补偿、人工兜底
差异率报表 晴雨表与考核口径

小结

对账是订单中心可信度的体检报告:三源核对、快照比对、差异工单化、修数据走接口。差异单数量是衡量系统健康度的最好指标,没有之一。下一篇讲超卖兜底,数据库的最后一道防线。

常见误解:对账的三个陷阱

第一个陷阱是「对账就是两条数据比对」。真做起来对账是三个数据源、多个时间口径、多种异常类型的组合。光是金额口径就分:订单实付金额、渠道清算金额、到账金额,三者可能差几分钱(渠道手续费的分摊规则)。财务定义差异其一是「金额差超过一分才算差异」,这个阈值要先和财务谈清楚,不然天天为几分钱的尾差报警。

陷阱 实际情况
对账就是比对两数 三源多口径的组合
对账能自动修数据 自动只做有把握的部分
对账替代补偿机制 对账是兜底不是主力

第二个陷阱是「对账自动修数据」。对账系统自动修复掉单是合理的,但金额类差异千万不要自动改账,必须人工确认。原因很直接:金额差的原因没查清之前,自动修账会把错误固化进系统。自动与人工的界限是「业务可验证」(掉单自动处理)与「资金相关」(人工处理)。

第三个陷阱是「把对账当唯一的补救手段」。如果大部分掉单都靠日终对账发现,说明实时链路补偿机制不到位。对账是兜底,实时查单与定时捞单才是主力。我们的目标是日终对账只发现个位数的异常,超过这个数先去修实时链路,而不是扩充对账团队。

团队协作:财务与技术的月度联席会

对账要真正发挥作用,财务必须是参与者而不只是报表接收方。我们建立了月度联席会:技术讲本月差异单分类与趋势,财务讲对账口径的变化与新需求,共同复盘典型差异单。联席会上定下来几件事显著提升效率:差异单状态工单化,财务可以随时查看处理进度;金额口径书面化,双方各执一份文档;大额差异双人复核,任何一方不能单独关单。

联席会议题 产出
差异单趋势 重点改进项清单
口径问题 口径文档修订
典型案例 自动补偿规则

一个意外收获是财务反过来技术服务:财务指出某些回调字段「渠道清算日期」比「支付完成日期」更适合做对账主键,我们调整对账任务的时间口径后,差异单直接降了一档。技术与业务的相互理解,永远比单方面优化系统有效。对账不是技术部门的家务事,是整个公司的资金信任体系。联席会还有一个习惯值得沿用:每次会议结束前留十分钟「逛差异单」,随机抽三单从头讲一遍,既是对新人的培训,也常常意外发现隐藏的口径问题。比如有一次发现渠道的清算时间对账口径其实在月底有特殊规则,差点让全月报表差出一截。自那以后每月初的对账报告中专门有一小节讲「当月口径变更」,压缩这类问题的发生空间。对账体系长期跑下来,团队形成了一句口头禅:数据自己会说话,对账就是它的麦克风。能把差异率持续压在低位,靠的不是多跑几个任务,而是财务、技术、运营三方共同养成的对账习惯。财务后来逢会就夸的一句是:你们的账,我们自己看得懂。

延伸阅读