4.规则集

概述

规则集也叫决策集,在URule Pro当中是由一组普通规则和循环规则构成的规则集合,是使用频率最高的一种业务规则实现方式。

在URule Pro中规则有两种类型:一种是普通规则;一种是循环规则。所谓的普通规则是指一种由如果、那么、否则三个部分构成的规则;而循环规则顾名思义就是可循环的规则,它允许指定一个集合类型的对象,对这个集合中每个对象进行循环迭代,在循环体中则是若干个由如果、那么、否则构成的普通规则。

在定义方式上,URule Pro提供了向导式规则集以及脚本式规则集定义两种。

所谓向导式规则集是指利用引擎提供的设计器,一步一步通过鼠标点击就可以完成其中的普通规则与循环规则的配置,配合高度可视化的向导式规则设计器,可以最大限度将业务规则可视化,降低规则配置的复杂度;而脚本式规则集顾名思义就是通过在规则集文件当中按URule Pro的脚本语法规范来书写脚本实现普通规则与循环规则的定义工作。

向导式规则集因为是图形化,向导方式构建规则,所以适合业务人员使用;而脚本式规则集通过书写脚本构成规则,与传统代码编写类似,所以适合技术人员来编写规则;从功能上看,向导式规则集和脚本式规则集能实现的功能是完全相同的,也就是说向导式规则集中能实现的功能在脚本式规则集也完全可以实现,反之亦然。

在URule Pro当中,虽然说脚本式规则能实现向导式规则中提供的所有功能,但我们还是推荐大家采用向导式规则集文件来定义我们的业务规则,原因很简单,向导式规则构建规则方式是可视化的,构建好的规则可读性更好,这样构建业务规则中出错的机率就会更小。

打开一个URule Pro的项目,在“决策集”节点右键选择创建一个向导式决策集文件,创建好的文件通过URule Pro向导式决策集设计器打开后的效果如下:

在向导式决策集的设计器中,通过顶部的工具栏,点击“添加规则”按钮可以添加一个普通的由如果、那么、否则构成的普通规则;点击“添加循环规则”按钮可以添加一个可以循环的规则。之前介绍的变量、常量、参数、动作四种类型的库文件,在向导式规则里就可以导入使用了。

普通规则

点击“添加规则”按钮就在下面的工作区里添加了一个普通的规则,如下图所示:

一个普通规则主体是由如果、那么、否则构成,点击规则名可以对规则名进行修改,修改完成后鼠标点击页签处离开焦点就完成了规则名的修改确认;点击“添加属性”链接可以为当前规则定义相关属性。无论是普通规则还是循环规则都支持下面这些属性。

中文属性名 英文属性名 值类型 描述
优先级 salience 数字 当有多个规则满足条件时,这个值用来决定这些满足条件规则中动作的执行顺序,值越大,执行顺序越靠前。如不设置这个值,那按条件满足的顺序执行,也就是说如果不配置优先级属性规则的执行顺序是不确定的。
生效日期 effective-date 字符串 当规则设置了生效日期,表示这个规则只有在当前系统日期为大于等于生效日期时才会生效,否则即使条件满足也不会触发当前规则,如不设置,则不会对规则执行产生影响。该属性的值要求是一个日期格式的字符串,格式为:yyyy-MM-dd HH:mm:ss
失效日期 expires-date 字符串 与生效日期对应,当规则设置失效日期时,一旦当前系统日期大于或等于失效日期,即使条件满足规则也不会触发执行,如不设置,则不会对规则执行产生影响。属性的值要求是一个日期格式的字符串,格式为:yyyy-MM-dd HH:mm:ss
是否启用 enabled 布尔值 默认值为true,也就是启用当前规则;如设置为false,即使条件满足规则也不会触发执行,如不设置,则不会对规则执行产生影响。
允许调试信息输出 debug 布尔值 默认为false,表示不输出调试信息,设置为true后,规则在执行时会在控制台输出规则条件的匹配信息、规则动作的执行信息;如果规则计算过程出现异常,还会在控制台输出计算出现异常的位置,以便于我们快速定义规则错误位置。需要注意的是,如果项目的urule.debug属性设置为false时,规则的这个属性会被覆盖,也就是说即使设置为true也不会输出任何信息。
互斥组 mutex-group 字符串 系统会自动将此属性相同的规则划为一组,且这个组中只有一个规则会执行,待执行的规则如设置了优先级,则优先级最高的规则执行,否则随机;需要注意的是,互斥组属性仅在当前规则集文件默认模式下有用,在顺序模式下互斥组属性将不起作用。
执行组 pended-group 字符串 系统会自动将此属性相同的规则划为一组,默认情况下,引擎不会执行这个组里的规则,需要我们在定义规则动作时利用系统内置的函数显示的指定要激活执行的执行组名,这样系统才会尝试匹配并执行组里的规则。
允许循环触发 loop 布尔值 当执行“更新工作区对象”动作时,某些规则可以会再次满足条件,这时这个属性就是用来决定这种类型的规则是否允许再次触发执行。关于“更新工作区对象”请参考“更新工作区“章节介绍

在使用过程当中,如果一个规则配置了执行组,也配置了互斥组属性,那么这个规则对应的执行组属性被激活后,其中配置了互斥组属性的规则将按互斥组属性执行原则执行。

在向导式规则文件工具栏上名为配置的按钮菜单下有一个用于控制当前文件中所有向导式规则日志输出的开关按钮,它的默认状态为“禁用调试日志输出”,如果我们希望打开当前文件中所有向导式规则的日志输出功能,那么只需要在这里 将“禁用调试日志输出”改为“允许调试日志输出”即可。

规则名及属性定义完成后,接下来就可以开始配置规则的主体部分,首先是“如果”部分,在如果部分当中可以添加若干条件,添加完条件后,就可以通过鼠标点击以向导方式设置条件,如下图:

对于一个具体的条件来说,我们可以将其分为三个部分,分别是条件左边部分、比较操作符以及条件右边部分。条件左边部分,如上图所示我们可选择的有变量、参数或者方法或函数,当然这相应的需要我们导入相关的变量库、参数库以及方法库;对于操作符目前URule Pro当中提供了下面这些操作比较符,如下图所示:

这些操作比较符基本已涵盖我们业务当中所有类型的比较操作。

大部分的比较操作符比较简单,这里挑几个看起来复杂一些的比较操作符介绍一下使用方式:

比较操作符名称 描述
在集合中 判断条件左值是不是在条件右值的对应的集合当中,条件右值可以是一个Collection类型的集合对象,也可以是一个由,分隔的字符串,比如aaa,bbb,ccc
不在集合中 用法与“在集合中”刚好相反
匹配正则表达式 要求条件右值是一个标准的正则表达式,这里需要指出的是条件中采用的正则匹配方式是字符串全匹配方式,不是部分匹配方式。比如条件左值为字符串“我是中国人”,条件右值中输入字符串“中国”,运行时你会发现条件无法满足,这是因为条件右值中只是部分匹配条件左值,改成“.*中国.*”后即变成全字符串匹配,表示左值中只要包含“中国”字符串即可以匹配,具体使用大家可参照标准的正则表达式语法
不匹配正则表达式 与“匹配正则表达式”作用相反
在区间值中 要求条件右值是一个由,分隔的两个值,目前支持数字和日期两种类型,如:“30,40”表示条件左值大于等于30且小于等于40,如果条件左值是个日期,那么条件右值可以写成这样:“2010-12-11,2020-12-11”,那就表示左值的日期要大于等于2010-12-11,同时要小于等于2020-12-11(日期格式支持yyyy-MM-dd或yyyy-MM-dd HH:mm:ss两种类型);定义区间值时还可以使用[]和()来确定是否包含起始值,其中[]是包含起始值,与直接写区间值效果相同,()为不包含区间值。比如[30,40)表示条件左值大于等于30,同时小于40;比如(30,40)表示大于30,且小于40。[]和()符号用在日期区间上也能起到同样的效果
不在区间值中 与“在区间值中”作用相反

选择完比较操作符后,我们就可以来设置条件右边部分。条件右边部分相比左边,可选择的值类型会更多一些,如下图:

一旦选择某种类型值之后,我们就可以进入下一步操作,同时在URule Pro当中无论条件左值还是条件右值都可以进行无限多级简单的加、减、乘、除运算操作,如下图所示:

在配置条件时,需要我们把之前定义好的变量库文件、参数库文件、常量库文件、动作库文件导入进来。对于条件来说,可以是多个条件,也可以是多个复合条件组合,这样都是通过鼠标点击操作完成,这里就不再赘述。

条件配置完成后,可以为“那么”或“否则”部分添加动作,那么部分的动作只有条件满足时执行,反之,否则部分的动作只在条件不满足时执行,不加动作意味着什么也不干,动作可以有多个,多个动作添加完成后可以通过拖曳改变顺序。

目前在URule Pro当中支持的动作类型有三种,分别是:打印内容到控制台、变量赋值以及执行方法或函数,如下图所示:

打印内容到控制台其实就是将我们需要的信息打印输出到日志当中,内容可以是一个普通的输入值,也可以是一些复杂的值类型或它们的加、减、乘、除组合,及添加括号定义算术运行优先级;在规则配置好“允许日志输出”选项时,就可以看到这个动作对应的输出内容。

变量赋值也就是给当前导入的变量库或参数库的值进行赋值,值类型可以是一个普通的输入值,也可以是一些复杂的值类型或它们的加、减、乘、除组合,及添加括号定义算术运行优先级。

在变量赋值操作里,如果赋值对象的属性是一个对象类型,赋值时可以直接写个JSON为这个对象的部分属性进行赋值。

如Employee对象有个名为company的属性,该属性是个对象类型,对应的Company对象有四个字段,分别是id,name,address,post,那么在进行变量赋值操作时,如果想一次性为这个company属性的id,name,post三个属性赋值,我们可以写下面这样的JSON值:

{"id":"bstek","name":"锐道","post":1000815}

将上面的JSON值赋给这个company属性,JSON中的id,name,post三个属性值将会被写到company对象中的id,name,post三个对应子属性当中。

最后一种类型的动作是执行方法或函数,要选择执行的方法或函数,前提是我们必须方法所在的动作库文件导入到当前规则文件当中,否则就看不到要执行的方法,一旦选择执行方法后,如果当前方法当中包含参数,那么我们也需要选择相应的值为参数赋值,同样参数的值可以是一个普通的输入值,也可以是一些复杂的值类型或它们的加、减、乘、除组合。动作的具体配置都是基于鼠标点击操作的,比较简单,这里不再赘述。

在向导式规则编辑器中,可以通过鼠标拖曳来改变那么或否则部分的动作顺序,同时,对于多个规则文件,也可以通过拖曳来改变它们的显示顺序。

示例

下面是一个包含两个普通规则的决策集文件,导入了我们之前配置的那个包含Customer的变量库文件,在第一个规则当中条件满足时执行两个动作,不满足执行一个动作;第二个规则条件满足的话执行两个动作,不满足什么也不做,如下图所示:

可以看到这两个规则条件都比较简单,并且他们是互斥的,同一时刻最多只会满足一个规则。

接下来需要对这个决策集进行测试,测试方法比较简单,我们可以直接点击当前规则文件工具栏上的“测试”按钮针对当前文件进行快速测试, 也可以点击项目的“知识包”节点,在打开的编辑器中添加一个知识包,并将这个做好的规则集文件放到这个包中进行打包测试,如下图所示:

知识包是URule Pro中提供的一种用于将一个或多个规则集、决策表、交叉决策表、决策树、评分卡、复杂评分卡、评分流文件打包的工具,知识包的编码属性比较重要,是这个包在当前项目中的ID。 定义好知识包及这个包中包含的资源文件后,可以点击知识包菜单项中的“快速测试”按钮对当前知识包进行测试,如下图所示。

快速测试是URule Pro当中提供的一种针对知识包的测试工具,通过它可对定义好的知识包预先进行测试。 快速测试中,我们可以把相关的输入选择并添加进来,输入需要的值,同时把关注的输出选择好,这样就可以点击工具栏上的测试按钮对当前知识包中的规则文件或规则流文件进行测试(如果当前知识包中含有规则流, 那么点击测试按钮时就会开启这个规则流,如果有多个规则流,这里也只会触发一个;如果没有规则流,那么就触发知识包里的所有规则文件里的规则)。

实际上,如果我们需要对单个规则文件进行快速测试,那么可以直接在相关的规则文件中完成,不需要把规则文件加到知识包中进行, 方法上点击规则文件工具栏上的“快速测试”按钮即可弹出针对当前规则文件的快速测试窗口(向导式规则文件、评分卡、决策表、决策流等文件里都在工具栏里提供了快速测试的按钮)。

测试窗口中的“查看规则树”按钮是用来通过图形化展示当前知识包所对应的规则树结构,如上面的示例,点示此按钮看到的规则树效果如下:

可以看到,在这棵树中,把规则定义用到的所有条件节点都以树节点的形式表现出来,条件之间如果是并且关系,那么这些条件节点在树中就以串行的形式连接,这样在实际执行时只有前一个节点条件满足才会流转到下一节点;条件之间如果是或者关系,可以看到规则树会通过一个"OR"节点将若干个条件节点连接,这样在实际执行时,只要有一边连线到达这个“OR”节点,那么与这个“OR”节点连接的其它连线上的条件节点就不再计算;如果总的条件使用并且连接,但分支上有并且和或者连接的条件,那么在构建规则树时就会将分支上的并且条件以串行的形式连接,同时将分支上的或者条件使用“OR”节点连接,最后再使用“AND”节点将所有条件连接起来,如上图所示。

如果规则中有两个相同的条件,那么在构建规则树时会智能地将这些相同节点合并为一个节点,这样在树上只会显示一个节点,通过这种相同条件节点的共享机制可以减小规则树尺寸,从而减少内存占用。

树的最底端,就是具体的规则名。在规则树运行时,所有的业务对象通过顶端名为入口的节点插入到规则树,根据连线评估条件节点是否满足,如果能到达最底端规则节点, 则说明与当前规则相关的所有条件满足,对应规则里定义的动作就进入待执行状态。

利用树的特点,再配合相关的短路计算方式,可以最大程度上保证规则条件计算性能,减少不必要的条件匹配尝试,从而使得规则的计算性能可以与普通硬编码相媲美,实现业务计算的毫秒级响应。

循环规则

循环规则,它是一种可以对集合对象进行循环执行的规则。

循环规则它的名称与属性与普通规则一样,定义循环规则,首先需要定义它的“循环对象”属性,它要求我们指定一个集合类型的对象,目前URule Pro提供了两种类型的集合对象来源:一种是指定的集合对象, 这个对象可以是个参数或变量;另一种是指定类型的所有变量对象,选择这种类型后,当我们选择某个变量时,运行时引擎会自动从当前工作区中找到所有这种类型的变量并组装成一个集合返回; 循环规则在运行时会将这里选择的集合对象进行迭代。

接下来定义“开始前动作”属性,顾名思义,就是在循环规则执行前做的一些动作,也可以理解为对集合对象进行迭代前要执行的动作;通常我们会在这个地方做一些初始化的动作,比如临时参数的初始化赋值等, 同样这里的动作可以是0~n个,如果不定义那么就不执行。

开始前动作定义完成后就可以定义循环规则的循环单元部分,对于一个循环规则来说可以有一个或多个循环单元,每个循环单元都是一个普通规则的规则体,也就是一个由如果、那么、否则三部分构成的普通,定义方式与普通规则完全相同。

在循环规则执行时,每迭代一次“循环对象”,就会将当前迭代的对象插入到工作区,尝试匹配循环单元里的每一个规则体,如果满足条件就执行。

最后是“结束后动作”部分,它在循环执行完成后执行,动作可以是0~n个,不定义就不执行。

在决策集设计器中,点击工具栏上的“添加循环规则”按钮就可以添加一个循环规则,如下图所示:

我们来看一个例子,通过这个例子来学习循环规则的使用方法。

示例

我们要实际的业务是统计用户对象里订单金额小于1000的数量以及订单金额大于等于1000的数量。

首先我们需要在Customer实体类里添加一个新的属性orders,如下代码所示:

@Label("订单")
private List<Order> orders;

Order类源码如下:

package com.bstek.entity;
import com.bstek.urule.model.Label;
/**
 * @author Jacky.gao
 * @since 2016年9月30日
 */
public class Order {
    @Label("名称")
    private String name;
    @Label("价格")
    private float price;
    @Label("数量")
    private int amount;
    //省略getter和setter方法
}

打开我们的customer变量库文件,在"会员"分类下添加orders变量,同时将新的Order对象添加到库中,如下图:

这样,“会员”与“订单”之间就形成了一个一对多的关系。接下来我们需要利用循环规则实现一个简单的小需求,那就是统计当前会员的符合条件的订单价格的总额, 我们目前定义的两个BOM对象中,没有哪个属性可用于存储订单数量,所以需要添加两个临时的参数值,用来存储小于1000的订单数和大于或等于1000的订单数,如下图所示。

库文件准备好了之后,接下来我们就可以来定义一个循环规则来实现统计总价的需求了,定义好的规则如下:

我们定义的这个循环规则中,循环对象类型为指定的集合对象,这里指定对会员对象的订单属性进行循环; 循环体中有循环单元,分别用于统计金额小于1000的订单数量和金额大于或等于1000的订单数量;最后在循环结束后的动作里添加了将两个统计数量的参数输出到控制台的动作。

要测试这个循环规则我们可以在“知识包”节点,添加这个包含循环规则的决策集文件,然后针对这个知识包进行快速测试;当然也可以在这个规则文件里点工具栏上的快速测试按钮对这个循环规则进行测试, 测试方法是一样的。

点击规则文件工具栏上的“快速测试”按钮,添加一个会员对象作为输入,并选择其下的订单属性,并构造订单的JSON格式数据,如下图所示:

在上图当中,测试时,我们构造的订单数据共有三条,以标准的JSON格式呈现, 关于URule Pro测试时所需要的JSON格式详细描述,见知识包测试介绍,这里不再赘述。

点击“开始测试按钮”,就完成了订单总计统计功能,点击日志输出链接,就可以看到规则执行信息,如下图所示:

从上图的日志结果可以看出,规则已正确计算出订单数量。

在循环规则当中,有些时候我们可能需要从子对象中找出一个符合条件的就可以了,这样后面的就不需要循环了,针对这种情况, URule里还提供了一个名为“跳出循环”的方法,我们只需要在循环规则体的那么中添加这个方法即可,如下图所示:

在我们这里的循环规则中,针对用于统计数量的两个参数,在循环规则开始前并没有对它们进行了初始化,这是因为在URule Pro当中,对于参数的值引擎默认会进行初始化,比如Integer类型的参数它的默认值就是0, 下表中罗列了参数中会被引擎初始化的数据类型的默认值。

数量类型 初始化后的默认值
Integer 0
Long 0
Double 0
Float 0
Boolean false
List 一个不含任何元素的ArrayList对象
Set 一个不含任何元素的HashSet对象
Map 一个不含任何元素的HashMap对象

规则集中提供了规则模版导入功能。在业务规则的编写过程中,可能存在一些功能类似的普通规则或循环规则,比如某些规则只是条件部分有一点变化, 或者动作部分不太一样,其它都是相同的,对于这种类型的我们没有必要再重新定义一次,只需要把功能类似的普通规则或循环规则保存为模版,然后在需要时将其导入即可。利用这种特性可以大大提高业务规则的定义效率,节省开发时间。

在规则集中,每个普通规则和循环规则名前都有一个复选框,我们可以根据需要勾选后点击工具栏上的“保存为模版按钮”,如下图所示:

在弹出的窗口中输入模版名称及相关备注信息后即可,如果当前没有模版分类可以先创建一个分类(模版分类是为了更好的管理规则模版),然后选中后即可保存。

有了规则模版后,接下来就可以打开需要导入模版的规则集文件,选择工具栏上的“从模版导入”按钮,就可以将选择的规则模版导入到当前规则集文件中。

规则模版的适用范围是当前项目,也就是说当前项目中定义的模版在当前项目中的任何一个规则集文件中都可以导入使用,有了规则模版可大幅减少重复劳动,提高开发效率。

对象名与属性名的分隔

从2.1.1版本开始,在向导式规则中引用变量或常量时,分类名与具体属性名之间的分隔符支持用户自定义,其中变量默认为“的”;常量默认为空(分隔符为空时常量只显示常量值而不显示分类),如下图所示:

在使用过程中,如果觉得默认提供的分隔方式看起来不满足要求,那么可以通过修改下面两个属性来实现对变量以及常量分隔符的自定义功能:

属性名 默认值 描述
urule.variable.link 用于定义向导式规则中变量分类与属性之间的分隔符
urule.constant.link 用于定义向导式规则中常量分类与属性之间的分隔符,因为该属性默认值为空,所以我们看到默认向导式规则中常量不显示分类,只显示值,如果给该属性定义具体值,那么常量的分类就会显示出来

条件模版

从2.1.9版本开始,可以在“规则集”分类中创建“条件模版”文件,条件模版的作用就是把一些通用条件进行归类,并定义好有意义的名称,这样在向导式的规则集文件里就可以引用条件模版文件并在规则条件中使用条件模版,一个创建好的条件模版文件效果如下图所示:

条件模版创建完成后,就可以在向导式规则文件里引用并使用了,引用方式与导入库文件的方法一致,点击工具栏上的“模版”按钮,选择其下的“条件模版”菜单项,然后选择并添加相应的条件模版文件即可,如下图所示:

使用导入的条件模版,只需要在规则条件里选择“添加条件模版”即可,如下图所示:

从2.1.9版本开始,对于向导式规则,在规则名边,还添加了一个可以用于直接查看当前规则在编译后条件树的按钮,这对于采用了条件模版的规则来说非常有意义,通过查看编译后的条件树,就可以明确在添加了条件模版后对当前规则的条件组合产生的影响, 从而可以帮助我们更好的理解规则中配置的条件。

动作模版

从2.2.1版本开始,向导式规则集中开始支持动作模版。可以在“规则集”分类下创建“动作模版”文件,在动作模版文件中,可以把一些通过的动作定义出来,并赋予有意义的名称,这样在向导式规则集文件中就可以引用动作模版文件并在规则的动作部分使用它, 动作模版文件如下图所示:

定义好动作模版文件后,就可以在向导式规则文件里引用并使用了,引用方式与导入库文件的方法一致,点击工具栏上的“模版”按钮,选择其下的“动作模版”菜单项,然后选择并添加相应的动作模版文件即可;具体使用方法与条件模版相同,这里就不再赘述。

规则集的运行模式

在2.1.7版本以前,向导式规则集在默认模式下会把当前文件中所有所有规则编译成一棵规则树,根据输入对象先进行条件匹配计算,计算好后再根据规则设定的优先级来执行所有匹配规则的动作部分。

如果存在两个规则A、B,A规则的优先级高于B,A规则根据输入默认对象属性值条件可以满足,而B规则根据输入对象条件不满足,这时执行A规则动作部分,在动作中将输入对象某属性改为另外一个值,经过这个修改, B规则此时条件满足了,但在默认模式下,所有规则的条件匹配动作已经完成了,所以在A规则的动作部分将输入对象某属性改为B规则满足的值后,B规则也不会尝试重新匹配条件以执行B规则条件满足后的动作部分。

要实现这一功能,在2.1.7之前的版本当中,我们只能在A规则条件满足后的修改输入对象某属性值的动作后添加一个“更新工作区”的函数,更新这个修改后的输入对象,以使这个对象能重新匹配所有的规则, 看看有没有满足条件的,这样B规则才会被重新激活;或者使用“执行组”属性也能达到同样的目的,也就是给B规则添加一个“执行组”属性,在A规则条件满足后的修改输入对象某属性值的动作后加上激活B规则对应的执行组。

可以看到,在默认模式下,对于存在大量相互依赖的向导式规则来说,上述两种方式用起来还是很麻烦的,为此从2.1.7版本开始,对于向导式规则集文件添加了一种新的“顺序”运行模式, 通过向导式规则集的工具栏配置菜单按钮下的运行模式菜单项,即可实现将默认模式修改为“顺序模式”,如下图所示:

在这种模式下,当前规则集里的所有规则将不会被编译成一整棵规则树,而是每个规则都会独立的编译成一个规则树,运行的时候,会根据规则定义的优先级属性依次运行这些规则树, 这样,对于存在上述逻辑情况的业务来说,我们只需要修改运行模式为“顺序模式”,同时为各个规则定义好优先级,那么它们就会逐个执行,优先级较高的规则先执行, 执行后的动作部分如果存在对业务数据的修改,就会直接影响到后面规则条件的匹配,不再需要使用“更新工作区”以使规则重新尝试匹配或者使用“执行组”属性对规则执行顺序进行编排,这样可以大大简化业务规则定义的复杂度。

需要指出的是“顺序模式”下,因为规则是一条条匹配,所以性能上相比“默认模式”要差一些,如没有特殊需要不建议采用“顺序模式”。

results matching ""

    No results matching ""