写这篇文章主要是回顾了一下最近两周带来的2个错误,这2个错误都只是一行代码造成的,当然不是相同的一行。
我分别贴出下面的2行代码:
CorpContract corpContract = corpContractService.getById(contractId); boolean useAutoDateInSignature = Optional.ofNullable(corpContract) .map(CorpContract::getContractTemplateId) .map(it -> sysContractTemplateService.getById(it)) .map(it -> Objects.equals(it.getUseAutoDateInSignature(), 1)) .orElse(true);
第一处出错的地方是在orElse(true),第一次修改时,改为了false,应该是true.
第二处代码:
private FlexTaskPayService flexTaskPayService; public List<String> queryUrlList(String taskPayId, String bankCardNo) { FlexTaskPay flexTaskPay = flexTaskPayService.getById(taskPayId); return getReceiptUrl(flexTaskPay, bankCardNo); }
第二处出错的地方在flexTaskPayService字段上没有加上@Autowired,或者我常用的@Resource.
先回到第一个错误,这个错误引出了一个bug,就是证明盖章的时候,原来是有默认的盖章日期的,结果一改,就没有了。
第一个错误,主要还是逻辑思维问题,在想问题的时候,有点没有想清楚,在使用有点陌生的Optional.map()链式调用时,上面代码的逻辑是:如果corpContract不空,则判断它的contractTemplateId是否为空,如果不为空,则去查询SysContractTemplate,如果查询出来也不为空,则判断它的签名中是否带日期是开启的(值为1),如果是就是true,否则其它逻辑都会返回false. 这时候其它逻辑就是:corpContract为空,或者它的contractTemplateId为空,或者根据这个id查询出来的SysContractTemplate为空,或者SysContractTemplate中签名是否带日期是关闭的(值为0)。
为了兼容历史数据,SysContractTemplate中签名是否带日期默认值是1,也就是开启的,同时由于证明也是用了这段逻辑,所以orElse(true)才能兼容历史的情况,否则就会造成bug.把证明中的盖章日期弄丢了。
第二个错误就不是思维问题了,简单一点讲就是不小心引入的一个漏写的问题。这个错误,一般情况下很容易发现,因为少了注入就会抛NPE,结果在我们的日志中就是没有任何错误,同时这个项目的特殊性:只有内部可以调用,和支付相关。我们测试也只有在生产环境中才能测试,所以还一度怀疑自己,怀疑项目,不清楚任何问题,因为日志中只打印了一个code:1001,status:200,看上去正常,实则很慌,那会刚好这个接口用curl命令一调(很少用),服务也不打印其它的日志,还以为这个接口把服务整坏了,就一度担心和怀疑。但是写的代码就很简单,从controller传值,调用已经写的方法,只是中间写一个查询数据的方法,结果就是这个service没有注入导致的。如果在一般情况下,肯定会打印NPE的,能很快发现问题,结果在我们目前的框架下,就是发现不了,因为我们不会怀疑会有NPE,也不会想到忘了写@Autowired.然后那会我们的关注点还在传参数上,因为我平时写的GET请求都是用@RequestParam(“payTaskId”)String payTaskId,这样,结果和同事一起看下,他说不用带,还有就是我就省懒只写了一个@RequestParam String payTaskId,当然这些都是暴露了对于spring mvc对于参数注入知识理解和掌握的不到位。
从事后的角度来看,对于知识掌握的不足会让问题偏离方向。
在依赖看日志的时候,如果日志本身出了问题,那么就很有可能需要先修复日志本身的问题,但是这会带来额外的工作量,会延长本身问题的修复时间。
最终还是需要回归到代码层面,从代码找问题。
从这个小的问题来看有3个代办:(1)需要验证@RequestParam传参数问题。
(2)需要改进Springboot对于日志拦截曾的处理,不然一个很小错误,都会排查很久。
(3)可以用更好的方式避免,不写@Autowired带来的问题,那就是使用final,同时使用构造器注入的方式,由于这种方式在项目中很多处都不是这么写的,会带来代码的不一致问题,不过也是一个好的改进,如果是新的项目,值得推广。
最后从解决这个问题来看,写这些代码本质是为了解决另一个问题,今天所写只是临时解决了一个问题,从问题的根本性上来说,需要一个更加完美的自动化去解决问题,而不是再手动处理,尤其是需要一直这么手动处理的话,就更需要找到根本问题,同时让系统尽量自动化。