没有规则,不能是圆;一、背景前段时间在做项目重构的时候,遇到了很多需要判断的条件。当然可以用很多if-else的判断来解决,但是当时不知道是怎么回事,就想玩点别的。于是乎,我就去研究规则引擎。当然,市面上有很多成熟的规则引擎,功能多,性能好。不过,我就是想玩点不一样的(做技术选型的时候不要这样,这是反面教材)。最后,URule的一个规则引擎吸引了我,主要是可以直接用浏览器配置,不需要太多安装,可视化规则也做得很好。经过一系列的研究,后来融入到项目中,顺便记录一下研究的结果。2.简介规则引擎实际上是一个可以嵌入到程序中的组件。将程序复杂的判断规则与业务代码分离,让程序只需要关心自己的业务,不需要做复杂的逻辑判断;简单的理解就是规则接受一组输入数据并通过预定的规则进行配置,并输出一组结果。当然,市面上有很多成熟的规则引擎,比如:Drools、Aviator、EasyRules等。但是可以运行在Windows、Linux、Unix等各类操作系统上的URule,采用的是纯浏览器编辑模式,无需安装工具,直接在浏览器上编辑测试规则。当然,这个规则引擎有开源版和专业版的区别。至于什么是专业版,懂的人都能明白。这里有一个表格来了解具体的区别。是否有决策树,是否有决策流,是否有决策表,是否有交叉决策表,是否有复杂记分卡,是否有文件名,项目名是否重构,是否有参数名,变量常量名是否重构,是否有Excel决策表,是否有规则集模板保存加载有无中文项目名文件名支持有无服务器推送知识包到客户端的支持知识包优化和压缩支持有或没有推拉支持客户端-服务器模式下的大型知识包有或没有规则执行组支持规则中的所有节点是否流向向导式条件和动作配置支持循环规则是否支持多循环单元循环规则是否支持无条件执行有无导入项目自动重命名功能是否支持对象搜索索引是否支持规则树短路计算是否有是否支持规则条件的冗余计算和缓存是否有知识包版本控制是否有SpringBean和Java类的热部署是否有技术支持三、安装和使用在实际使用中,URulePro有四种使用方式,即嵌入式模式、本地模式、分布式计算模式和独立服务模式。但我们在这里不考虑URulePro。我们自己的开源版是在开源版中集成springboot的基础上二次开发的。找了一圈,其实是有解决办法的。一般项目模块如下:自己创建一个空数据库,只需要修改edas-rule-server服务中数据库的配置,然后启动服务即可。第一次启动完成后,会在数据库中创建一张表。spring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/urule-data?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=falsespring.datasource.username=rootspring.datasource.password=mysql上面说的是纯粹使用浏览器编辑配置规则,打开浏览器,回车地址:http://localhost:8090/urule/frame,看到这个界面就说明启动成功了。4.基本概念3.1总体介绍先说一下URule的组成部分,主要有两个部分:1.设计器部分2.规则执行引擎。设计器部分主要由库文件和规则文件组成。下面我们来看一下整体结构图。3.2库文件如上图介绍,库文件有变量库、参数库、常量库和动作库4种。其实类似于Java开发的系统中的实体对象、枚举、常量和方法。如上所述,规则是可视化配置的。在配置规则的过程中,需要引入已经定义好的各种库文件,然后结合业务需求配置符合业务场景的业务规则,所以到处都是库文件。3.2.1变量库文件在业务开发中,我们会创建很多Getter和Setter的Java类,比如PO、VO、BO、DTO、POJO等,其实这些新对象的主要作用就是数据的载体,用于传输数据。在URule中,通过变量库来映射这些对象,然后在规则中使用,最终完成业务与规则的交互。最后一张图片用于创建变量库。对了,视觉配置废话就这么多。这是第一次展示配置界面。我很惭愧。上图一目了然。在“库”菜单下右击,然后点击添加变量库,最后定义你喜欢的变量库的名称。当然,名称只支持中文或英文,其他字符不可用。创建变量库后,您可以编辑变量库。可以认为是在不讲任何术语的情况下为POJO添加属性。这是我个人的理解。图中左边是创建类,其中name是它的别名,配置规则用它来代替这个类。图片右侧是类的属性。这里随便写了几个,估计看完就明白了。最后在业务系统中创建相应的类。注意全限定名与配置变量库的类路径一致。packagecom.cicada;importcom.bstek.urule.model.Label;importlombok.Data;/***@author往事如风*@version1.0*@date2023/3/315:38*@description*/@DatapublicclassStu{@Label("name")私有字符串名称;@Label("年龄")私人年龄;@Label("class")privateStringclasses;}最后说一下这个@Label注解,它是由URule提供的注解,主要是描述字段的属性,应该和变量的title列保持一致图书馆。听官方介绍,可以使用这个注解实现POJO属性和变量库属性的映射。即写完POJO,然后规则对应的变量库不需要重写,直接生成即可。反正有这个功能,这里就直接说了。3.2.2常量库文件说到常量库,在我们的Java系统中可以认为是常量和枚举。例如,对于性别,你应该定义一个枚举;例如,对于一个对接组织,你也可以定义一个枚举。当然,与变量库类似,常量库也可以映射为系统中的枚举。这样做的好处是我们可以避免手工输入,防止输入错误。创建常量库也比较简单,只需在“库”菜单下右击,选择“添加常量库”即可。创建常量库文件后,还会出现如下页面:3.2.3参数库文件参数库是URule规则中的一个临时变量,变量的类型和个数不固定。它可以被认为类似于地图。其实就是一个存储参数库的Map。同样的方法,直接在“库”菜单下右击,选择“添加参数库”。可以看出参数库中缺少左侧的类别。直接添加参数,选择类型,比较简单。“Name”栏我用的是英文,是Map中的key,“Title”栏是配置规则时显示的。中文更直观。当然还有一点要注意,定义的名称必须是唯一的,因为Map中的key是唯一的,否则会出现覆盖。3.2.4Action库文件action库可以映射spring中配置的bean方法,然后在规则中直接调用这些方法。平时的套路,在“库”菜单下右击,点击“添加动作库”。然后我在系统中添加了一个类Action,然后在类上打上@Component注解,将类交给springbean容器管理。在该类中添加一些方法,并在方法上标注@ExposeAction注解,由URule定义,表示标注的方法将被动作库读取。包com.bstek.urule.cicada;导入com.bstek.urule.action.ActionId;导入com.bstek.urule.model.ExposeAction;导入org.springframework.stereotype.Component;导入java.text.SimpleDateFormat;导入java。util.Date;/***@author往事如风*@version1.0*@date2023/3/1013:59*@description*/@Component("action")publicclassAction{@ActionId("Hello")publicStringhello(){返回“你好”;}@ExposeAction(value="方法1")publicbooleanevalTest(Stringusername){if(username==null){returnfalse;}elseif(username.equals("张三")){returntrue;}返回假;}@ExposeAction(value="测试Int")publicinttestInt(inta,intb){returna+b;}@ExposeAction(value="打印内容")publicvoidprintContent(Stringusername,Datebirthday){SimpleDateFormatsd=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");if(birthday!=null){System.out.println(username+"+sd.format(birthday)+"岁!");}else{System.out.println("你好"+用户名+"");}}@ExposeAction(value="PrintStu")publicvoidprintUser(Stum){System.out.println("Hello"+m.getName()+",isage:"+m.getAge());}}最后,在action库页面添加一个bean,在“BeanId”栏输入对应的springbean的名称,这里输入action点击操作栏的小手按钮,会弹出刚才的Action类用ExposeAction注解标记方法。选择一个指定的方法添加进去,最后看到方法对应的参数会被自动加载进去。最后定义好变量库、参数库、动作库、常量库等库文件后,分别将它们可以在配置规则文件的时候导入。但是一旦这些库文件被一个规则文件使用了,就不要随意修改库文件了。3.3规则集说到规则集,顾名思义就是配置规则。上面定义的库文件需要导入到规则集中进行配置和使用。它是最常用的业务规则实现。规则集是指规则的集合,它由三部分组成:if,then,otherwise。在规则集的定义方式上,URule有向导式和脚本式两种;向导式规则集:用鼠标点击页面,高度可视化配置,不是所有开发者都能看懂,这也是这个规则引擎的亮点。Script-styleRuleset:看名字就知道,这东西是需要脚本的。它提高了配置门槛,需要懂一些编码的人来编写它。3.3.1向导规则集还是老样子,先新建一个。这次是在“决策集”菜单上右击,点击“添加向导决策集”创建规则集。在配置规则之前,您可以导入之前定义的库文件。我这里导入变量库文件,在页面点击“变量库”,然后选择指定的变量库文件。如图所示;终于可以愉快的配置规则了,向导式的没什么好讲的,全是可视化界面,点击即可。下面是我配置的一个简单的规则集;你可以看到它由三部分组成:if,then,otherwise;if:配置规则的条件;then:配置满足条件后要执行的动作,一般配置变量赋值比较多;otherwise:配置不满足条件的action执行最后添加规则后,通过代码执行规则;包com.cicada;导入cn.hutool.core.bean.BeanUtil;导入com.Result;导入com.bstek.urule。实用程序;导入com.bstek.urule.runtime.KnowledgePackage;导入com.bstek.urule.runtime.KnowledgeSession;导入com.bstek.urule.runtime.KnowledgeSessionFactory;导入com.bstek.urule.runtime.service.KnowledgeService;导入com.cicada.req.StuReq;导入org.springframework.web.bind.annotation.PostMapping;导入org.springframework.web.bind.annotation.RequestBody;导入org.springframework.web.bind.annotation.RequestMapping;导入org.springframework.web.bind.annotation.RestController;importjava.io.IOException;/***@author往事如风*@version1.0*@date2023/3/1016:47*@description*/@RestController@RequestMapping("/rule")公共类RuleDataController{@PostMapping("/stu")公共结果规则(@RequestBodyStuReqstuReq)抛出IOException{KnowledgeServiceknowledgeService=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);KnowledgePackageknowledgePackage=knowledgeService.getKnowledge("xxx/xxx");KnowledgeSessionknowledgeSession=KnowledgeSessionFactory.newKnowledgeSession=UtilsSessionFactory.newKnowledgeSessionPack(.copyProperties(stuReq,Stu.class);knowledgeSession.insert(stu);knowledgeSession.fireRules();returnResult.success(stu.getTeacher());}}请求接口,最终参数满足配置条件,返回"then3.3.2Script-basedrulesetScript-basedruleset,script-basedruleset的各种原理和wizard-style完全一样,无非就是提高3.4决策表先说决策表,其实它是规则集的另一种展示形式,与规则集相比,我更喜欢用decisiontable来配置规则,因为这样更直观,也更容易理解。但是本质上和ruleset没有什么区别。我不想讲太多,这里放一个配置好的decision桌子;3.5其他当然还有其他的概念和功能,这里就不一一介绍了,因为上面说的已经是最常用的了,如果你想知道,你可以自己去了解。其他功能包括:交叉决策表、记分卡、复杂记分卡、决策树、规则流;当然,其中一些是专业版的功能。4.应用场景最近在开发一个大版本的需求,有一个场景,如下;参与采购订单的用户都会有自己的等级,也可以说是角色。每个用户都会有三个职位:普通用户、会员、精英会员。然后,在每个月的月初,将对用户进行推广。普通用户满足条件将升级为会员,会员满足条件将升级为精英会员。当然,普通用户晋升为会员和会员晋升为精英会员会有不同的规则。普通用户->会员:3个月内注册会员达到3人;10,000;个人接单率超过80%。会员->精英会员:3个月内注册会员达到6人;3个月内,自己和团队下人的订单量突破5万;个人接单率超过90%。不能跨级晋升。普通用户最多只能成为会员,成为会员后才能升级为精英会员。当然,这只是要求的简化部分。我做了一些改变。真正的需求场景并没有这么简单。接下来,我将为这个需求配置一个规则。这里我使用决策表进行配置;在配置规则之前,我添加了一个变量库文件和一个常量库;最后,添加决策表并配置规则;可以看到,表格一共有五列,前四列是规则,最后一列是满足规则后的输出信息。这样一来,看着就非常清晰,即使不是技术人员,也能轻松看懂规则。5.总结规则引擎是否适用于我们的系统。它可以锦上添花,帮助我们分离出业务中需要大量判断的场景。但是,这种规则的剥离,需要我们的开发人员去理解需求,在理解的基础上,我们要把抽象的概念具体化。这也是整个编程的必由之路。6.参考源码编程文档:https://gitee.com/cicadasmile/butte-java-note应用仓库:https://gitee.com/cicadasmile/butte-flyer-parent
