数据动态验证的简单应用

Source

一. 需求介绍和选型

近期接到一个需求,需求是需要将订单数据发送给第三方开一张发票。
为了减少双方数据的交互,外部系统提供了各个字段的校验规则,需要由调用方先进行预处理,确定这些数据是否都合法,满足校验规则再传送给对方进行开票。这样做尽量减少无效交互
校验规则列举如下:
XXXX代码,选填,取值范围为“空”、“01”、“02”、“03”、“04”、“05”、“06”、“07”、“08”、“09”、“10”、“11”、“12
XXXX识别号,必填,最大长度26位,由数字和大写字母组成
XXX名称,必填,最大长度300位
XXXX银行账号,选填,最大长度100位
等等
在已知的未来可想而知,这些预验证,可能随着时间的推移对方版本的迭代会增加的越来越多,或者有可能对方有很多字段的校验规则也没有在文档上面体现出来,会随着项目的上线,这方面的需求也会越来越多。 那样动态的去增加这些验证配置,减少上线的次数呢?
这类校验,都是为了满足某种规则,所以首先想到的是用到规则引擎,比如easyrule, drools等,这些可以规则引擎确实可以通过配置一些自己的语法,可以配置对应的语法到缓存或者Redis 中,每次调用按写好的语法动态去校验对应的规则。从而反馈结果。
但是为了这个简单的需求,去专门引入一个工程,另外后续别人接手这个工程的时候还要去熟悉这种规则引擎让人有杀鸡用牛刀的感觉
另外也发现其实可以通过Spring Spel语法和thymeleaf等语法引擎其实也可以实现。最终结合语法,性能等因素考虑,Spring Spel语法解析器其实完全可以满足本项目需求。

二. 需求实现

因为Spring Spel依赖域Spring-core模块,所以大抵用Spring项目的工程就无需另外引入Maven。

1. 创建一张简单表

由于功能比较简单,判断是否满足条件,满足某种条件就报错,所以创建俩个字段一个是判断条件,一个是抛出异常消息,如下:

CREATE TABLE `xx_sale_pre_create_process` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '表ID,主键',
  `config_type` varchar(30) NOT NULL DEFAULT '' COMMENT '请求类型',
  `line_no` int(11) NOT NULL DEFAULT '1' COMMENT '行号',
  `pre_process` varchar(2000) NOT NULL DEFAULT '' COMMENT '前置判断',
  `post_message` varchar(240) NOT NULL DEFAULT '' COMMENT '返回错误信息', 
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_ae_sale_pre_create_process_config_type` (`config_type`,`line_no`)
) ENGINE=InnoDB AUTO_INCREMENT=114 DEFAULT CHARSET=utf8 COMMENT='创建发票前置处理器'

2.增加校验规则

  比如
  ![在这里插入图片描述](https://img-blog.csdnimg.cn/fcf25bd6c685425a9ab746c70e541177.png)

3.增加校验代码

其中AeSaleLq15InvoiceObj为传送给SpringSpel 的对象,设置对象值为lq,当满足表中设置的表达式的时候则不报错,如果当不满足该条件的时候则报错。

  AeSaleLq15InvoiceObj openBodyObjs = aeSaleEOpenInvoiceService.buildLq15OpenBodyObj(envContext, openInvoice, invoiceTypeObjs);
EvaluationContext context = new StandardEvaluationContext();
ExpressionParser parser = new SpelExpressionParser();
for (AeSalePreCreateProcess process : processes) {
    
      
    context.setVariable("lq", openBodyObjs);
    Boolean isPass = parser.parseExpression(process.getPreProcess()).getValue(context, Boolean.class);
    if (!isPass) {
    
      
        throw new BizException(process.getPostMessage());
    }
}
return openBodyObjs;

三. 需求总结

功能很简单,但是从根本上可以解决很多不确定的校验工作,并且其实从该需求可以拓展,比如满足什么条件下的时候,可以将对应的值进行修改等
比如适用场景: 经常跟外围接口进行请求的时候,外围接口往往会抛出一堆的错误,在接口调用的时候往往会发生很多错误信息,比如网络问题,数据错误或者请求参数有误(比如请求结果往往依赖与上一个接口的结果,上一个结果可以重试) ,这里有一些错误是无需重试的,有写错误是可以尝试通过重试机制进行解决的,那么我们要怎么判断哪些错误是可以重试,哪些场景是不能重试的呢?

PS: 可以参考官网https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions
对于Spring Spel语法介绍有一个比较详细的介绍,这里还介绍了自定义函数,自定义模板等功能。