buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

< 2025年2月 >
26 27 28 29 30 31 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 1
2 3 4 5 6 7 8

统计

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

 

回到顶部

对账模型

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

【FAQ】

【Q】依据支付时间还是支付完成时间对账?

【A】按支付交易完成时间做对账。

【Q】银行单边账是怎么产生的?

【A】case1:银行不返回交易完成时间。这种情况下,在日切临界点,我们0:00前查询通道,0:00后收到通道响应,我们按我们服务器时间更新交易完成时间。这就出现我司与通道侧支付完成时间不一致的情况。case2:再一种银行单边由不靠谱程序产生。我司调用通道侧时使用了事务。结果因调用超时通道侧落单,我司没落单,从而出现通道侧单边。

回到顶部

统一数据词典

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

账单/对账单:bill
银行账单/银行对账单:bank bill batch

对账:bill check
对账批次:bill check batch

明细:detail

差异:diff

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

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

通道/银行:bank

交易:trans

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

交易笔数/交易量:qty

回到顶部

数据表设计

 数据表表名comment主要字段
银行账单 银行账单批次表 bank_bill_batch 银行账单表,每银行每天一条记录

batchNo-账单批次号(系统生成,PK)

bankId-系统里记录的银行通道编号

bankBatchNo-varchar-银行侧对账单批次号/对账文件名(没有则为空)

trans_date-int-交易日期/yyyyMMdd

-- total_amount-总金额

total_qty-总笔数

createTIme-记录创建时间,即账单的首次拉取时间

updateTime-最后更新时间

 

银行账单交易流水 bank_bill_detail 银行账单交易明细

id-PK

batchNo-账单批次号

bankId-系统里记录的银行通道编号

transOrderNo-系统请求银行的交易流水号(发生银行单边时,此字段为空)

bankOrderNo-银行侧交易单号

transState-银行侧交易状态(程序里转换为系统里的交易状态)

transAmount-银行侧交易金额,以分为单位存储

payeeAccount-银行侧收款方账号(银行卡号)

transTime-银行侧交易完成时间

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-金额 / BANKONLY-银行单边 / SYSONLY系统单边

check_time - 对账时间

diff_process_time - 差异处理时间

diff_process_remark - 差异处理备注

 

 

银行对账差异处理记录(Optional) bank_bill_check_diff_process 记录差异账的处理  (暂略)

 

 

 

 银行对账结果明细表->数据样例

id

系统请求银行

的交易流水号

银行侧

交易单号

账单批次号

/银行编号

系统/银行侧

收款人账号

系统/银行侧

交易金额

系统/银行侧

交易完成时间

系统/银行侧

交易状态

对账状态差异字段对账时间差异处理时间
1 2023120700901 231211110575607

B20231207PAB

/PAB

6217***1069

/6217***1069

10000/10000

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 BANKONLY

2023-12-08

01:06:00

2023-12-08

09:30:00

3 2023120700902  

B20231207PAB

/PAB

6217***1069

/-

1/-

-

/-

F/- D SYSONLY

2023-12-08

01:06:00

 
4 2023120701094 231211110575613

B20231207PAB

/PAB

9558****0631

/9558****0631

500/500

-

/-

F/F S -

2023-12-08

01:06:00

-

5 2023120701950 SCLY0906231725

B20231207ALI

/PAB

6228***7074

/6228***7074

12380/12380

2023-12-07

02:52:01

/

2023-12-07

02:52:01

S/S S -

2023-12-08

01:06:00

-

 

 

回到顶部

如何触发对账?

毋庸置疑,实现方案是使用定时任务(JOB)。每家银行出账单的时间是不一样的,例如有的是凌晨3:00生成,有的是上午8:00生成。因此,可以每隔20分钟触发JOB,调用各银行的API获取账单,直到拉取到,然后再进行对账。

拉取银行账单JOB银行对账JOB

获取需要拉取账单的银行通道列表-bankList

依次遍历 bankList

查询bank_bill_batch及bank_bill_check_batch,获取待对账的账单批次-batchList

依次遍历 batchList

 ↓

拉取银行账单方法 (bank) {

  if(已经拉取到) return;

  加锁,防重复执行控制

  组装银行请求参数,拉取银行账单

  解析数据,持久化入库,包括银行账单批次表和账单明细表(事务)

}

银行对账方法(){

  加锁,防重复执行控制

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

 

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

posted on   buguge  阅读(387)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
点击右上角即可分享
微信分享提示