记一次返工之后记
作者:Grey
时间:2018-11-25
原文地址: https://www.cnblogs.com/greyzeng/p/10018247.html
说明
之前做的功能, 又有新的需求了,原先的需求是支持某个类型的待办审批,现在是要支持系统所有类型待办的审批,虽然之前的代码写的很匆忙,比较“丑陋”,但是好在未出什么bug,新需求来了以后,我本想乘此机会重构一下自己之前写的比较丑陋的代码,但是因为陆陆续续有插入进来的优先级更高的任务,所以这个新需求一直被hold住,最后有一周的时间来做这个需求,由于要测试的流程比较多,而且创建流程的过程比较费时,为了给测试预留多一些时间,我放弃了重构的念头,先快速实现需求,在保证原有功能和新增需求没有问题的情况下,再一点点重构自己的代码。
原需求
有一个Web系统X,用户可以通过这个系统查看自己的待办信息,并且可以用于待办的审批,还有一个我们做的手机应用Y,Y系统需要支持查看X系统的待办信息并完成审批操作。
新需求
原需求里面说的待办信息只是某两个类型的待办,实际上这个X系统有很多待办类型,而且每个类型的处理逻辑,显示元素都是不一样的,我们现在要实现再Y上支持X系统的所有待办类型的审批。
资源
- X系统的源码。
- X系统虽然不是我们公司做的,但是我司运维人员对于X系统的业务相对熟悉。
开发前的准备
为了避免上一次返工的问题之一:需求没有梳理清楚。在写代码之前,开发和测试都需要非常了解业务逻辑,所以,在开发之前,我特意申请了一次培训,让运维人员给我们(我作为开发,还有一位测试人应)讲解一下整个系统的流程,也不需要特别正式的会议室,就几个人在电脑旁边,运维人员从头到尾给我们整个走一遍流程,有任何疑问都当下提出,当下解决, 解决不了的记录下来,运维人员后去请教实际用户这些问题。补充一点:最了解X系统业务的人当然是使用这个系统的用户,可是这个系统比较特殊的一点是,大部分用户都是领导,我们几乎不太可能让领导来花时间来给我们讲整个业务逻辑,只能是当我们遇到一些实际的业务问题,再去和他们请教。而运维人员,算是除了用户以外,相对比较了解X系统的人了,什么?你说X系统的开发人员?这个系统的开发商已经“跑路”了:)
在培训完业务流程后,我还没有急着做开发,而是先让测试人员在X系统上走一遍流程,并把走流程中的一些关键信息(如待办详情,操作按钮,下一步操作选择)截图,一来测试需要了解整个业务流程才知道如何设计测试用例,二来截图也可以给我开发做一些参考,比如需要显示待办详情里面的哪些信息,操作按钮要如何显示,下一步操作要如何处理等。
测试人员在X系统上走完流程以后,我自己也参考测试人员写的操作步骤和截图,走了一遍流程,算是心里有个底了。
遇到的问题
X系统有个测试环境,我们都是在X系统的测试环境中开发和测试的,但是这个环境很不稳定,因为运维人员经常为了排查X系统的问题,会经常把X系统正式环境的数据导入到测试环境,这样我们在X系统做的数据就会被重置,这就尴尬了,我们的开发周期是一周,想在这一周保证测试数据稳定是不可能的了,所以,我又做了一件事,找了一个新的服务器,在这个服务器上重新搭建一个X系统的测试环境(包括应用服务器,数据库),总算是解决了这个问题。
遇到的另外一个问题是,每次X系统中的待办建走到特定节点比较费时,而每个特定节点需要定制开发,比如我们的手机应用Y要支持X系统某个待办的第五个环节,那我需要在X系统上建一个待办,然后一步一步走到第五个环节,然后找这个环节需要显示的信息,需要做的操作,非常麻烦,而且一旦我在手机上操作了这个环节(比如审批了),这个环节就跳到了第六个环节了,如果我第五个环节的东西还没有完全开发完,我又要建一个流程并走到第五个环节,比较麻烦。所以我想了一个办法,就是在走到某个环节的时候,先备份一下数据库,走完这个环节如果想再回去看下的话,直接恢复备份数据库就可以了。
开发和测试
放弃重构想法以后,我开发相对比较谨慎,基本没有改动之前的代码,新的需求都是重新写的代码,并为考虑复用太多之前的代码,虽然几次很想重构,但是还是忍住了,求稳。
业务逻辑理清了,接下来就是要看懂X系统的源码了,X源码注释也比较少,充斥着类似以下这样的代码:
if(StringUtils.equalsIgnoreCase(subTypeId, "501")){
// do something
...
}else if(StringUtils.equalsIgnoreCase(subTypeId, "502")){
// do something
...
}else if(StringUtils.equalsIgnoreCase(subTypeId, "601")){
// do something
...
}else if(StringUtils.equalsIgnoreCase(subTypeId, "602")){
// do something
...
} else if(StringUtils.equalsIgnoreCase(subTypeId, "604")){
// do something
...
}
我必须一个一个节点走才知道这里的诸如:"501","604"是什么意思,然后把这里面对应的SQL拿出来分析,看下需要哪些参数,这些参数是如何获取的,而且有些SQL的查询还很复杂,参数很多,我本想直接用正则匹配页面中的这些参数值,后来想想,还是不能依赖X系统的页面信息,我都是直接查数据库获取需要的参数,分析SQL和获取查询参数,耗费了巨大的时间,不过事实证明,这样做是对的,我这相当于重做了一遍X系统了。
接下来是设计X系统集成到我们手机APP系统Y中以后的展示和操作,由于数据都拿到了,我就需要把这些数据转换成我们手机APP中展示的元素,比如:Table,KeyValue,List,这些东西实际上没有一个统一的标准,我们就把详情中比较关键的一些信息显示出来,至于是显示成Table还是KeyValue,这个没有严格的标准,用户似乎不太关注内容的排版,只要是显示了必要信息,简单明了,就足够了,所以这部分”设计“,就按照我自己的想法来做了。
接下来是审批操作,审批操作比较麻烦的一点是,你必须完全了解,点击这个待办的操作按钮背后的所有逻辑,因为我们是要集成待办的操作,所以我们实现的逻辑要和X系统一模一样,最简单粗暴的方案当然是模拟X系统的操作,比如用一些爬虫工具模拟点击操作的按钮,这样我们就完全不需要了解X系统的操作流程了,但是这并不保险,还是因为上一次返工给我带来的教训:万一操作的元素变动了一下,我们的操作就完全失效了。所以这次我还是选择直接理解操作背后的逻辑,说白了就是把操作对应的Ajax请求一个一个看懂并找到对应的参数值,然后发一个一模一样的Ajax操作,当然X系统点击操作还不是简单的拿一些现有的参数发请求,有一些是页面中的js拼装的一些数据,这部分也要完全理解并完全移植过来,一旦有一些操作没有移植过来,如果待办审批出了问题,是非常严重的,因为这些待办涉及比较重要的招投标流程,流程涉及的金额也是很大的。
我回忆了一下,真正写代码的时间实际上不多,花时间最多的是看X系统的源码,和熟悉X系统的业务流程。
最后是测试,因为要在手机APP上测试流程,但是我们自己的工号下面都没有对应的待办信息,需要登录相应的领导的账户才可以拿到待办信息,虽然是测试环境,但是我们手机APP是不会用用户的信息去登录测试的,因为登录后,用户的所有信息我们都可以看到了,解决办法就是,在获取用户待办的时候,我们只拿用户的X系统的待办,其他信息不获取,所以获取X系统的待办逻辑的代码,加了这么一小段逻辑,大概意思就是如下:
this.工号 = 获取登录人的工号(); // 登录人是自己人
Map<String,String> changedInfo = newHashMap();
changedInfo.put("自己人的工号1","有待办的人的工号1");
changedInfo.put("自己人的工号2","有待办的人的工号2");
changedInfo.put("自己人的工号3","有待办的人的工号3");
this.工号 = changedInfo.get(this.工号);
List todoList = 通过工号获取某人X系统的待办信息(this.工号);
感悟
这个需求开发并上线已经两周了,我监控了一段时间运行日志,暂时未发现问题,本次整个开发过程,吸取了上一次返工的教训,为我以后的开发工作也积累了一些新的经验,谨以这两篇博客(本篇博客, 上一篇博客)作为一个记录,也希望可以分享给有需要的人。