背景
一个完整的业务方案,包括几个模块,如下图所示:
用户填写完每个模块的内容后,点击“提交申请”按钮,后台需要对这些内容做“完整性校验”,每个模块都有自己的校验规则,如在“业务基本信息”中,当且仅当业务类型=XXX时,业务币种必填。模块之间还有联动校验规则,如“业务低风险标志为是时,业务明细数量不能少于2条”。如果这些业务规则数量太多,放到一个“提交申请”接口里遍历校验,无论是对于用户体验,还是这个接口的开发/维护人员的体验,都非常不友好。
很容易想到,每个模块都有自己的保存接口,如“保存业务基本信息”“保存业务明细”等,我们应该把每个模块的规则分散在这些接口中,保证用户填写的每个模块的数据都是经过验证的,那么“提交申请”接口就不需要校验数据了。
要实现上面这点,首先要求UI设计成:每个模块按固定顺序填写,上一个模块未保存,不能进入下一个模块;若回到上一个模块修改,下一个模块的数据应该重新保存;所有模块填写完成才能点击“提交申请”。如果业务方案内容较少,比如就三个模块且每个模块字段少,那么这个设计没什么问题。如果业务方案内容多,那么这个设计第一是用户体验不好,第二是对前端要求高,一般不会采用的。
所以如果你的业务方案的页面和我的一样,左侧导航栏列出用户要填写的模块内容的标题,右侧展示模块页面,用户可以随意在每个模块之间切换页面,可以在未保存模块内容时点击“提交申请”按钮,那么“提交申请”接口就避不开“完整性校验”了。
想到的方法
我们想到过下面两种完整性校验的实现方法。
方法一:每个模块的保存接口执行保存成功后,记录下日志,这样“提交申请”接口只校验每个模块的保存接口是否都执行过,执行过则可以认为数据已经通过了校验。
这个方法我觉得是可行的,但最后没采用,没采用的原因有:
1、我们有些模块的保存是其他系统负责的,这块内容的校验需要另外开发
2、有些模块并不是所有情况下都必填,如“当业务类型=XXX时,业务标签模块不展示”,这会导致这个方法的实现代码需要根据特殊规则特殊处理,不是一套通用的代码(但是若建立一张配置表,配置内容是在XX情况下哪些模块必填,校验前先读这张配置表,只检查必填模块的保存接口的日志,最后的代码应该可以做到通用)
3、有些业务方案,是从别的系统的方案(这类方案的结构和内容与业务方案有很多重叠,但不是业务方案)初始化过来,这时候即使方案内容完整,用户也必须在每个模块点击保存,才能提交成功
方法二:只校验每个模块中的某几个字段是否为空,这些字段不为空则认为这个字段所在的模块执行过保存接口。
这是个偷懒的方法。这个方法首先要找出哪些字段不为空可以代表它所在的模块执行过保存接口,第一这些字段在业务方案初始化时一定为空(无论是不是从别的方案复制),第二这些字段在任何情况下都必填。
这个方法我们使用了一段时间,后来发现我们的业务方案中,并不是每个模块都存在符合这两点要求的字段,大部分字段只满足其中一个要求,导致经常漏校验。。
最后我根据方法二的使用经验,稍加改造,做出了方法三。
最后使用的方法
我从方法二的经验教训中发现,既然不存在符合要求的字段,那么只能校验每个模块的所有字段必填,才能保证每个模块的保存接口执行过。但每个模块都存在条件必填的字段,我必须先识别出哪些字段在当前条件下必填,再依次校验这些字段不为空。
因为业务方案的字段和规则很容易变更,所以应该用配置表,灵活配置每个模块下需要校验的字段,以及它们必填的条件,还能配置这些字段内容的正确性校验规则,如“业务币种”字段有个正确性规则:业务类型=A时,业务币种必须为人民币。
我设计的配置表结构如下:(为了方便理解,模块名称和字段名称都写了中文,实际上是代码里的字段名)
这张配置表,通过表达式计算(我使用的表达式引擎是 Aviator),可以判断出各个场景下必填的模块和字段,还能顺便判断值的正确性。必填判断表达式的计算结果为 TRUE 说明模块/字段必填,正确性判断表达式的计算结果为 TRUE 说明值正确。
根据这张配置表写出的校验代码逻辑也非常简单,先筛选出当前业务方案中哪些模块必填,再依次校验这些模块的数据:先筛选出哪些字段必填,校验这些字段不为空,若有配置正确性判断表达式,则校验这些必填字段值的正确性。
使用这个方法后,校验工作都由配置表完成,因为配置表结构固定,所以校验代码不太需要修改,这块内容无代码工作量。需要开发代码的部分,只有查询当前方案每个模块的数据(用于被校验),和查询用于表达式计算的字段数据(如配置表中的bizType)。
查询用于表达式计算的字段数据,我是在调用完整性校验方法前查询好的。查询每个模块的数据,我是在判断出这个模块必填时才去查询。每个模块有不同的查询方法,需要定义一个抽象的查询方法由每个模块自行实现,校验到哪个模块就调用哪个模块的查询方法即可。
这个方法也有一定的局限性:
- 只有那些校验规则简单的字段可以写入配置表。但是我们本身只是想通过校验一个模块下部分字段不为空来确认这个模块的保存接口执行过,所以少校验几个复杂的字段不影响结果
 - 查询用于表达式计算的字段数据,这个没有做成配置,若以后新增的规则中多了一个用于判断的字段(如除了业务类型以外,多了一个业务风险类型),则需要修改代码。现在我是未雨绸缪,把一些目前没用于规则判断的数据也查询出来了