起因
周天正在带娃吭哧吭哧爬山,一个电话呼过来了:“线上紧急bug,app价格是错的,差了10倍,每单亏200多”,然后diss了开发和测试的质量。
我表示很懵逼啊!项目是一个月前离职同事交接过来的。线上稳定运行一个多月了也没有改动过这个逻辑啊!
然后电话仔细一沟通,只能说离谱,太他喵的离谱了。线上一个月前发版。稳定的错误运行了1个月,直到用户反馈怎么扣钱扣的少了。
啥骚操作导致的呢?
我们有一个中台用来配置这些购买套餐相关的配置信息。
原始的标准呢就是中台前端输入元(元比较符合大众认知)。数据存储的分(int存储不丢精度,准确,稳定)(很完美的设计此处需要掌声👏)。
我们调用微信支付,微信货币的单位是分,我们直接按照数据库存储的原始数据传递给支付组件(nice)。
后来我们对接了海外项目,海外印尼的项目,支付的货币是印尼盾(IDR),
对接支付的开发呢沿用了微信的调用逻辑,直接取数据库的值传递给印尼的midtrans支付。
中台测试验证过程中呢发现?哎我想要支付100Rp,我不能输入100,我必须输入1.才能正常支付。如果想要支付1Rp,必须输入0.01.(中台输入的元和实际调用支付的分差了100倍。IDR这个地方又不需要做这个单位转换)
测试这样凑合过了,产品也这样凑合过了,验收的时候紧急上线也这样凑合过了,安排了下个版本修复。
下个版本谁修复呢?bug提交给了中台,我们都没有被通知到,说中台设置了1,数据库保存了100,这不行,要所见即所得,设置1数据库保存1,调起支付为1.
嗯,这样看起来完全没毛病。(因为当时只有印尼的midtrans支付)
没毛病个大头鬼啊!
这他喵的数据库存储的基准单位都更换了(原来是0.01基准单位,现在变成了基准单位),只是通知了各个前端和中台的后台,愣是没有通知到我们这边开发。
关键是还存在版本并行开发, 一个新的版本mpay对接澳门通支付的版本。
mpay支付,数据库存储的分(我们认为),调用mpay支付,mpay需要货币单位为元。后台做了✖️0.01操作。
无巧不成书,巧合在什么地方呢?mpay测试环境不支持支付验证。
正式环境验证需要联系澳门的同事支付。三重buff叠加。嗯,我们的支付少了100倍。100倍啊!
复盘
开发的锅:内部沟通存在问题, 关键变更特别是影响金额的重大变更没有落实到纸面通知到位(邮件/文档等)
测试的锅:测试覆盖不全。
产品的锅:上线验收及线上运行监控不到位
以后涉及金额相关如何做?
支付相关涉及金额相关,
页面设置大家理解共识的基准单位:CNY(元)/Rp(印尼盾)/MOP(澳门元)/USD(美元)
数据库存储,单位为0.01基准单位(部分场景存储0.0001基准单位),比如1分=0.01元。
优点: int/long存储,计算方便,无需考虑精度丢失。准确,稳定。
缺点: 前端展示/其他计算需要手动进行相关的倍数还原计算。
为啥不按照基准单位存储?
当时产品也说:印尼的最小币值就是20了,都没有人民币分对应的概念。澳门我们包年费用就是199元,那3毛2分的我们看不着。
我们后续推出月租呢?再推出日租呢?分要不要毛要不要?
基准单位后面一般情况下是要冗余2-4位的,用来进行做对应的计算冗余。
为啥部分场景要按照0.0001基准单位存储?
国家电网的价格=0.5769元/度。当用电量比较大的时候小数点后四位才能表示的更加精准。
我们计算应收帐款的时候,针对10度电收费5.76元,剩余的0.009被舍弃掉,但是计算过程中需要精度更高才能计算的更精准。部分场景多冗余几位还是很有必要的。
印尼的最小单位就是印尼盾了,为啥Rp不直接入库原始数据,针对CNY/MOP数据库按照分存储?
中台是一个统一维护的平台, 针对不同的货币单位,做了不同的处理方式,后续数据对接就容易混乱,不如就奠定一个0.01基准单位的基调,后续都按照这个来处理。
业务侧用int处理还是long处理?
建议用long来处理,因为不知道公司会不会去对接津巴布韦币,我怕我小小的int不够用。
现在对接印尼盾int就已经有点捉襟见肘了。
int最大值:2147483647 去掉冗余的2位=21474836(2千万), 1元=2100印尼盾,int最大能表示到中国1万元。