背景
Erp 系统中要增加一个能查看每天账户余额和收支情况的功能,以方便客户对账。开发在实现的时候遇到一个难题:在用户余额表不断被更新的情况下,怎么能拿到前一天结束时瞬时的余额数据以保存到余额历史表中。直接这么说可能有点不好理解,接下来将详细介绍实际的业务场景。
当前业务逻辑
当前的业务逻辑如上图所示,主要涉及到3个表:
- 流水表:所有涉及资金相关的操作都会被 insert 到流水表中。
- 待结算表:商户的收支会被同时记录到待结算表中,结算Job每5分钟执行一次查询待结算表中数据并基于账户维度汇总处理后更新到用户余额表中,然后将已结算的数据标记为删除。
- 用户余额表:存储用户的余额信息,包括收入余额和支出余额。根据支出余额判断商户是否能正常使用系统。由于充值需要实时到账,所以数据并不会写入待结算表走结算逻辑,而是直接修改用户余额表。
当前情况下,商户对账仅依赖于流水表(根据业务订单对比收支流水),根据流水表中 created_at 字段查询时间范围内收支情况。
新业务需求
为了提高产品优势、加强用户对平台资金流水的信任度,现计划增加一个能查看每天账户余额(当天结束时余额)和收支情况的功能。例如今天是2月3日,用户能选择分别查看2月1日和2月2日的收支余额变化情况,然后对比2月2日整天的收支情况来判断平台资金处理是否准确。
如上图所示,2024年02月01日结束时的收入余额为100、支出余额为200。而 2024年02月02日的收入项合计为30、支出项合计为8,那么2024年02月02日结束时的收入余额就应该为130、支出余额就应该为192(收入项和支出项金额数据来源为流水表计算结果,当天结算后的收支余额则从余额历史表中获取)。
实现难点
看着好像只要增加一个余额历史表,每天凌晨整点的时候查询下用户余额表中的值,然后直接插入到余额历史表中就可以了。
但是实际操作下来就会发现这样做最后流水表中统计的收支金额和历史余额表中昨天合计的收支结余对应不上。原因是结算Job每5分钟执行一次,如果02月02日凌晨整点的时候查询用户余额表,那此时用户余额表中还只是23:55分时结算后的值(00:00 - 23:55区间的数据才刚开始处理),将此时的收支余额写入到余额历史表中,就会导致到时候从历史余额表查看02月01号的收支余额比着流水表中少了5分钟的结算数据,账单和流水就对不上了。
向后推几分钟等00:00开始执行的结算Job跑完数据再查用户余额表的数据也不行,因为充值的金额会直接修改用户余额表,哪怕晚一秒如果有用户在凌晨 00:00:01 充值就会把第二天充值的钱算到前一天的结算余额里。
基于 MySQL binlog 的实现方案
最后几个同事讨论下来,认为基于消费 MySQL binlog 去更新余额历史表是一种可行的方案。如下图所示
配置 canal 实例只匹配用户余额表数据,然后消费Task从 canal 拿到数据后根据 update 语句中的 updated_at 字段日期判断要将值更新到哪一天的收支余额中(结算Job侧会对处理的数据做判断,如果未跨天那么更新时间设置为当前系统时间,如果跨天则将更新时间设置为job上一次执行时使用的更新时间)。
这样即使结算Job在第二天 02/03 00:00:00 开始处理 02/03 00:00:00 到前一天 02/02 23:55:00 的数据,到 02/03 00:02:00 才处理完成,并且同时在 00:01:16 还有充值操作直接更新了余额表也没有关系。所有的 update 操作都会被消费task顺序更新到余额历史表对应的日期上。
本文采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。
如果您的问题未解决,欢迎微信扫描右侧二维码与我联系。