我遇到一个Bug,金额大于一千万就报错
https://www.flypeng.com win10系统 发布时间:2016-06-06 00:00 来源:未知

我在银行做系统开发,“金额”基本上是最常见的字段,也是最不能出错的字段了。每一个错误都代表着实打实的资金损失,无论是客户的还是银行的。所以,作为开发人员,对这个字段也相对敏感一些。金额这个字段的规则也相对其他字段更复杂一些。举个例子来说,金额是一个数字,可以是这样12;当然也有小数点的情况,比如这样12.34;人们还有这样的习惯,每隔3位有一个逗号分隔符,比如这样1,000.23。可是一千万是个什么特殊情况,为什么会有问题呢?

 

目前系统为了校验前端上送的数据,有一套统一的校验机制。大概过程就是,先使用jackson框架将前端上送的JSON数据进行解析,然后根据这个字段配置的正则表达式进行校验,校验不通过,就会报错。

 

这是一个工作日,在我们内部通讯工具上弹出来一个同事的窗口。“我们有一个交易上送金额,80099690.89元,校验就报错,但是8009969.89元校验就不报错。”我见到这个问题后,第一反应是:“怎么可能,这个校验规则在生产环境运行了那么久都没有出错,怎么会突然有问题呢?会不会是哪里搞错了?”然后,这位同事又发来“前端上送的JSON报文中,金额使用的数字类型 amount:80099690.89,但我们平时都上送的字符串类型 amount:'80,099,690.89'”。于是,我开始怀疑那个正则表达式是否正确,还在埋怨是谁写的正则,居然要求必须有逗号分隔符的时候,我看到了这正则:^(\d*|\d{1,3}(,\d{3})*)(\.\d{1,2})?。居然是对的!完美的考虑了带逗号和不带逗号的场景。这时候,这位同事又发来消息“把金额按string上送就不报错了amount:'80099690.89'”

 

WTF?!下面这个表情就是我当时的表情。

 

我们知道,在JavaScript中,string和number是可以随时互转的,难道amount:80099690.89和amount:'80099690.89'上送到后端会有不同的逻辑?几千万大小的数字也没有超过精度呀。

 

于是我开始看这篇文章开头提到的那个字段校验公共机制,先使用Jackson解析,然后再利用正则表达式校验。难道是Jackson有问题?看过官方文档,Jackson会将number默认解析成Java的Double型,string则转换为Java的String型,而Double在进行正则校验之前需要先转换为String。

 

Double amount1 = new Double("80099690.89");

Double amount2 = new Double("8009969.89");

System.out.print("amount1 = " + amount1.toString());

System.out.print("amount2 = " + amount2.toString());

 

打印结果为:

 

amount1 = 8.009969089E7

amount2 = 8009969.89

 

相信大家已经看出来了,原来是这样!Java里 Double 的 toString 方法当大于7位数的时候会取科学计数法。所以科学计数法的结果是无法匹配这个正则的 ^(\d*|\d{1,3}(,\d{3})*)(\.\d{1,2})?

 

从那以后,我终于明白编码规范中为什么要求前端都是用string类型了。我还专门看了接口文档,将类型都调整为了string。

 

其实无论一个bug看上去多么随机和不可思议,如果你挖的足够深的话,总能找到一个符合逻辑的解释,极少有真的“不相关”的错误,几乎都是你自己的错。这就是为什么我微博的简介里会写这么一句话“电脑确实比人靠谱,你和他们打交道时一旦有问题,肯定是你的问题。不像人,即使他们有问题也不轻易承认。

 

写在最后:文中提到的那位同事,现在已经去新的岗位提升自己去了,写这篇文章也算纪念一下我们一起共事的时光。^_^~

如果你有好的win10资讯或者win10教程,以及win10相关的问题想要获得win10系统下载的关注与报道。
欢迎加入发送邮件到#qq.com(#替换为@)。期待你的好消息!