银行流水号引发的 PHP 编程思考与实践
流水号在银行业最为常见。相信很多人接触到流水号都是从银行相关的凭条或银行系统查询获知。
自从进入金融相关的公司或业务开发之后,对流水号的应用开发就有了更深刻的认知。
那么,今天我们通过流水号来应用到实际开发中。解决我们开发中的问题。
一、流水号的特点:唯一性
流水号,对于整个系统而言是全局唯一。这算是流水号最基础最重要的特点。这个特点,能解决最根本实际开发中最实际的问题。
二、实际案例:卡券发放
A 系统属于核心系统。提供了各种各样的核心功能,以及暴露一些 API 接口。这些接口可以给围绕 A 系统做运营功能的系统使用。
假如,现在有一个 B 系统有一个需求:
制作一个导流的活动页面,在该页面输入手机号并接收验证码之后实现快捷注册,然后给这个手机号对应的账户赠送一张减免 10 元的卡券。
为什么不直接在导流的注册流程里面直接赠送呢?
因为,有以下几个原因:
- (1)导流页面是调用 A 系统的注册接口。A 系统属于核心业务系统,不会经常为了这些活动而定制改动系统代码,影响系统稳定性。
- (2)B 系统只有 A 系统的可读权限或可读权限都没有。所以,无法直接干涉核心业务数据库的功能。
- (3)B 系统引流注册,如果用户此时只是注册,并不会去下载 App 登录的话。那么,这时赠送卡券将变得毫无意义。只会产生更多的垃圾数据。
那么,该如何做?
- (1)A 系统提供快捷注册接口、短信发送接口、卡券发送接口。
- (2)B 系统制作一个注册导流 H5 页面。用户填写手机号,获取验证码,输入验证码点击,点击提交调用 A 系统快捷注册接口实现注册用户。
- (3)B 系统调用 A 系统的注册接口成功之后,在自己的系统存储手机号、用户ID、注册时间、导流 H5 的页面唯一标识(自己定一个)。
- (4)A 系统在系统当中实现一系列事件(登录、注册、下单、充值、提现)。并且写入队列。然后,把这些事件实时推送给 B 系统。
- (5)B 系统收到这些事件之后,根据自己是否有建立在这些事件上的后续功能。比如,我们这个导流功能,就需要导流注册成功之后,用户首次登录的时候赠送 10 元卡券。
上面的 (1)、(2)、(3)、(4)都很好实现。其中,第(4)步主要是为了实现系统解耦。并且这也是目前比较常用的方案。我们今天要讲的是怎样通过流水号来解决第(5)步的问题。
那么第(5)步会有什么问题?
比如,现在 B 系统收到了导流的用户成功首次登录的事件推送了。结果,去调用 A 系统提供的发送卡券功能赠送 10 元卡券。结果,调用卡券发送失败了。于是,准备在程序里面实现一个功能:失败之后重发。最后成功了。但是,真的成功了吗?
我们来分析一下。卡券发送失败的可能原因:
- 请求被 A 系统正确接收。但是,由于网络原因 B 系统中断了。
- 请求没有被 A 系统接收。
- 请求被 A 系统正确接收,也正确发送了卡券。但是, A 系统故障了。
- 请求被 A 系统正确接收,也正确发送了卡券。B 系统自己故障了。
当然,情况不止上面这几种,还有其他原因。不管何种原因,当我们要重发的时候,问题就来了:会导致卡券发送多次。
这时候我们可以利用流水号的特性。每次导流注册成功之后,给每条入库的记录一个唯一的流水号。流水号一定要保证它的唯一性。该流水号在 A 系统当中的发送卡券接口也要实现防重发的功能。
怎样在 A 系统当中的发送卡券接口防重复呢?
很简单。我们正确接收到 B 系统的请求之后,以此流水号写入缓存。然后,接着处理卡券发送的业务。当第二次同样的请求发送过来的时候,检查这个流水号是否在我们的缓存当中,如果存在立即查询发送的状态。并返回发送的结果。
这样 A 系统不管因为何种原因导致发送失败,重发的时候都不会导致福利卡券发送失败。
当然,A 系统也有可能会发送失败。这时我们可以找到发送失败的原因修复代码。然后, B 系统重新处理就好了。
以上只是一个理论分析。没有给出具体的 PHP代码示例。在我司的金融产品当中,我们就用该思想理论,实现了诸如此类的重发问题。