1. 首页 >  小程序定制 >  系统里的资金交易如何与银行通道对账?我们这样设计

系统里的资金交易如何与银行通道对账?我们这样设计

以系统里的出金交易为例, 与银行对账不外乎做两件事:①T+1日拉取银行账单,保存银行账单交易流水;②银行账单交易流水与本系统里的通道交易流水比对,记录并处理差异。

先统一数据词典

T日:T日指交易日。银行系统在T+1日生成T日交易的账单,因此,对账发生在T+1日

账单:bill

对账:bill check

银行账单:bank bill batch

对账批次:bill check batch

明细:detail

差异:diff

定时任务:job。定时任务命名以-Job结尾,如对账Job命名为BankBillCheckJob

所涉及到的系统现有的词汇包括↓

通道/银行:bank

交易:trans

金额:amount,以“分”为单位存储

交易笔数/交易量:qty

数据表设计

| 数据表| 表名| comment| 主要字段

银行账单 银行账单批次表 bank_bill_batch 银行账单表,每银行每天一条记录 batchNo-批次号(系统生成,PK) bankId-系统里记录的银行通道编号 bankBatchNo-银行侧对账单批次号/对账文件名(没有则为空) trans_date-交易日期 -- total_amount-总金额 total_qty-总笔数 createTIme-记录创建时间,即账单的首次拉取时间 updateTime-最后更新时间
银行账单交易流水 bank_bill_detail 银行账单交易明细 batchNo-批次号(PK) bankId-系统里记录的银行通道编号 transOrderNo-系统里的银行交易流水号(发生单边时,此字段为空) bankOrderNo-银行侧交易单号 bankTransState-银行侧交易状态(程序里转换为系统里的交易状态) bankTransAmount-交易金额,以分为单位存储 bankPayeeAccount-银行侧收款方账号(银行卡号) bankTransTime-银行侧交易完成时间 createTIme-记录创建时间
银行对账 银行对账批次表 bank_bill_check_batch 与bank_bill_batch一对一 batchNo-批次号(PK,来自bank_bill_batch) bankId-系统里记录的银行通道编号 checkState-对账处理状态 -(I-初始待对账/P-系统对账中/D-差异待处理/S-对账完成) check_date-对账时间 trans_date-交易日期(针对哪天的交易做的对账)
银行对账明细表 bank_bill_check_detail 银行账单与系统交易对账结果 id-PK transOrderNo-系统里的银行交易流水号 bankOrderNo-银行侧交易单号 batchNo-批次号(来自bank_bill_batch) bankId-银行编号 双方记录的交易状态、收款人账号、金额、交易完成时间 checkState-对账处理状态 -(I-初始待对账/D-差异待处理/S-对账完成) diff_field-存在差异的字段,STATE-状态 / AMOUNT-金额 / BANKSIDE-银行单边 / SYSSIDE系统单边 check_time - 对账时间 diff_process_time - 差异处理时间 diff_process_remark - 差异处理备注
银行对账差异处理记录(Optional) bank_bill_check_diff_process 记录差异账的处理 (暂略)

银行对账明细表数据样例

id 系统里的 银行交易流水号 银行侧 交易单号 账单批次号 /银行编号 系统里/银行侧 收款人账号 系统里/银行侧 交易金额 系统里/银行侧 交易完成时间 系统里/银行侧 交易状态 对账状态 差异字段 对账时间 差异处理时间
1 2023120700901 231211110575607 B20231207PAB /PAB 62171069 /62171069 1000010000 2023-12-07 11:11:11 / 2023-12-07 11:11:11 S/F D STATE 2023-12-08 01:06:00
2 231211110575608 B20231207PAB /PAB - /6217***5638 -/1 /- -/F S BANKSIDE 2023-12-08 01:06:00 2023-12-08 09:30:00
3 2023120700902 B20231207PAB /PAB 6217***1069 /- 1/- - /- F/- D SYSSIDE 2023-12-08 01:06:00
4 2023120701094 231211110575613 B20231207PAB /PAB 9558*0631 /9558*0631 500500 - /- F/F S - 2023-12-08 01:06:00 -
5 2023120701950 SCLY0906231725 B20231207ALI /PAB 62287074 /62287074 1238012380 2023-12-07 02:52:01 / 2023-12-07 02:52:01 S/S S - 2023-12-08 01:06:00 -

如何触发对账?

毋庸置疑,实现方案是使用定时任务(JOB)。如,每隔30分钟从系统对接的各银行获取账单,再进行对账。

拉取银行账单JOB 银行对账JOB
获取需要拉取账单的银行通道列表-bankList 依次遍历 bankList 查询bank_bill_batch及bank_bill_check_batch,获取待对账的账单批次-batchList 依次遍历 batchList
拉取银行账单方法(){ 加锁,防重复执行控制 组装银行请求参数,拉取银行账单 解析数据,持久化入库,包括银行账单批次表和账单明细表(事务) } 银行对账方法(){ 加锁,防重复执行控制 更新batch的checkState=P 对该批次里的交易与系统里的T日交易进行check 对账完成后,标记batch的checkState,存在差异交易则为D,否则为S

实现要点

🍀业务防重复执行锁

key:日期+银行编号

TTL:拉取银行账单 与 银行对账,保证这两者在T+1日均执行一次即可。 因此可设置TTL=24h

🍀对账及时性保证

银行账单是在T+1日生成T日账单。不同银行的对账单的具体生成时间点有所不同,有的是01:00,有的可能是09:00,甚至有的是中午11:00。因此,定时JOB的开始时间可以从00:30开始,每隔半小时触发。银行账单一旦拉取完成,后续JOB触发时不再重复拉取。

上面表格里的方案是两个JOB,即将拉取银行账单JOB与银行对账JOB分开了。 这是有缺点的。——可能出现对账不及时的情况。

那么,如何优化呢? 保留一个JOB即可。 拉取银行账单的业务完成后,则异步触发银行对账,保证银行对账及时性。

🍀哪些通道需要对账?

最“朴实”的实现方式,不外乎是读取通道表得到bankList,针对这个集合里的每个通道做对账。

注意上面的朴实是加了双引号的。

比较好的姿势,是根据T日的通道交易流水,筛选出来真正需要对账的bankList。

【花絮】

我组的对账起初也是2个JOB(开发人员错误的以为分开为2个JOB才叫解耦),其中,拉取银行账单JOB是整点每隔1小时执行(cron=0 0 1-121 * * ?),银行对账JOB是整半点点每隔1小时执行(cron=0 30 1-121 * * ?)。后来,产品经理和结算人员反馈银行账单拉取不及时,对账也不及时。怎么办?开发人员就反复调整这2个cron表达式,让其触发时间间隔更接近。 例如,变更银行对账JOB的cron=cron=0 15 1-121 * * ?。 但这样依然无法根本解决对账不及时的问题。 因此,更合适的实现方案是,拉取到对账单后就异步触发对账,并且缩短JOB执行间隔为半小时。

【EOF】知识就是力量,但更重要的是…。欢迎关注我的微信公众号「靠谱的程序员」,解密靠谱程序员的日常,让我们一起做靠谱的程序员。

/xiao-cheng-xu-ding-zhi/xi-tong-li-de-zi-jin-jiao-yi-ru-he-yu-yin-xing-tong-dao-dui-zhang-wo-men-zhe-yang-she-ji-1503.html