redis对账组件
前言
这个是我之前开发的用于代收付业务的对账组件。我了解了下,之前其它项目组开发对账模块,一般先把交易落库,然后是写Sql来对账或者循环遍历交易查询数据库状态来对账。当时一个大牛提醒我可以用Redis来做对账,Redis的sdiff方法很快,比较适合来对账。下边我说下主要的思路以及关键代码。
设计思路
业务需求
首先针对我们的代收付业务,我们要对账的元素是一笔订单的流水号、卡号、金额、状态,其中卡号和金额作为一个元素,即对账结果中不会区分卡号不一致还是金额不一致,只有卡号或者金额不一致,这种差异需要人工介入处理。一般我方的交易流水记录在数据库中有成功、失败、也有可能未明,合作方会在日切的时候通过文件给我们他们的记录一般是确认成功或者失败。即对账结果以对账为准。
关于日切时间点的交易,我们目前是等待隔日对的策略。
总体设计
Redis的sdiff方法可以快速的比较两个set集合的差异,以下用我方和他方分别代表要对账的两方。目前我们对账逻辑为:
- 把双方流水号记录到两个set集合中,通过两次sdiff分别获取我方多和他方多的流水号。
- 把流水号+卡号+金额拼成一个字符串分别记录到两个set集合中,sdiff获取结果为流水号不一致或者金额不一致或者卡号不一致。
- 步骤二的结果和步骤一的结果取差集即为卡号或金额不一致的结果。
- 同理,先流水号+卡号+金额+状态拼成一个字符串记录到两个集合中,sdiff后和步骤二的结果取差集即为状态不一致的流水号。
模拟流程
下边我用几条模拟数据模拟下整个对账流程
我方数据:
流水号 | 卡号 | 金额状态 | 状态 |
---|---|---|---|
a001 | 622238690 | 100 | S |
a002 | 622238691 | 200 | F |
a003 | 622238692 | 100 | S |
a004 | 622238693 | 500 | S |
a005 | 622238694 | 950 | F |
a008 | 622238695 | 800 | U |
他方数据:
流水号 | 卡号 | 金额状态 | 状态 |
---|---|---|---|
a001 | 622238699 | 100 | S |
a002 | 622238691 | 290 | F |
a003 | 622238692 | 100 | S |
a004 | 622238693 | 500 | F |
a005 | 622238694 | 950 | U |
a006 | 622238695 | 800 | U |
- 我的set集合命名规则为: 方向(即我方还是他方) + 业务类型(代付/代扣) + 渠道(银联/CP) + settleDate(清算日期) + fileId(文件名称)
- 我的set集合中元素的规则为,元素拼接起来以|为分隔符。例:包含流水号、金额、卡号的集合内元素为:a001|622238690|100|S
对账步骤
1. 对流水号
通过两次sdiff分别获取我方多和他们多的流水号集合
/** 我方流水号多的记录 * 元素为流水号*/
Set<String> myOverOrderIds = RedisUtil.getInstance().sets().sdiff(mySetName + "." + TradeInfoEnitity.ORDER_ID, thirdSetName + "." + TradeInfoEnitity.ORDER_ID);
/** 第三方流水号多的记录 * 元素为流水号 */
Set<String> thirdOverOrderIds = RedisUtil.getInstance().sets().sdiff(thirdSetName + "." + TradeInfoEnitity.ORDER_ID, mySetName + "." + TradeInfoEnitity.ORDER_ID);
最后得到我方多的流水号a008和他方多的流水号a006
2. 对金额或者卡号差异
先对出流水号、金额或者卡号有差异的记录
/** * 流水号不一样或金额或卡号不一样的记录 * 元素为 流水号 + 金额 + 卡号 */
Set<String> amountOrCardNoDiff = RedisUtil.getInstance().sets().sdiff(mySetName + "." + TradeInfoEnitity.ADD_AMOUNT_CARD_NO, thirdSetName + "." + TradeInfoEnitity.ADD_AMOUNT_CARD_NO);
对出的结果为:
"a001|622238690|100","a002|622238691|200","a008|622238695|800"(我的sdiff方法参数里,我方的集合名字在前,所以获取的结果是我方比第三方集合多的记录。),差异的流水号位a001,a002,a008,减去第一步结果a008得到金额或者卡号差异的记录为a001,a002
3. 对状态差异
和第二步就差不多了
/** * 流水号不一样或金额或卡号或状态不一样的记录 * 元素为 流水号 + 金额 + 卡号 + 状态 */
Set<String> allDiff =RedisUtil.getInstance().sets().sdiff(mySetName + "." + TradeInfoEnitity.ADD_STATUS, thirdSetName + "." + TradeInfoEnitity.ADD_STATUS);
对出的结果为:
"a001|622238690|100|S","a002|622238691|200|F","a004|622238693|500|S","a005|622238694|950|F","a008|622238695|800|U",差异流水号位a001,a002,a004,a005,a008,减去第二步的结果,得到状态差异的记录为a004,a005
以上就是模拟的流程了,通过这个流程可以获取所有的差异结果,剩下的就是不同的差异做相应的业务处理了。
异常处理
目前策略是把整个对账业务放在一个数据库事务里,发生异常时回滚所有数据库操作。每次对账前先把Redis的数据删掉,再进行相应的流程。
项目github地址
暂无
网友评论