本人最近在研究SpringBoot使用Drools规则引擎实现自动流程审批功能,期间遇到过很多坑,最终选定Drools决策表的功能,决策表方便业务人员进行规则的添加就修改操作,清晰明了,以下纯属个人学习记录,欢迎学习交流。
决策表是drl规则文件的变形,以excel 格式完成对规则的匹配,通俗的讲,决策表就是向电子表格输入特点的值,并加载到Drools规则库中,可以用特定的类对表格进行转换,转换为drl文件,从而用程序进行解析。废话不多说,先来一张做完的Drools决策表如下图,Drools决策表大体包含两部分,灰色部分是决策表部分,浅蓝色部分为规则部分,下面详细说明每个关键字的作用
关键字 | 是否必填 | 值 | 说明 |
---|---|---|---|
RuleSet | 是 | String | 在这个单元格右侧包含RuleSet的名称,与drl文件中的package一样的 |
Sequential | 否 | true/false | true则代表表格从上到下顺序执行,false 代表乱序执行 |
Import | 否 | String | 导入所需的引用的类或方法,多个类用逗号隔开 |
Functions | 否 | String | 功能与标准的drl 文件中函数相同,多个函数在每个函数后用逗号隔开,有无返回值都可以 |
Variables | 否 | String | 全局变量,多个变量用逗号隔开 |
Queries | 否 | String | 定义查询函数,多个用逗号隔开 |
RuleTable | 是 | String | 表示规则名称,在RuleTable 后面直接写规则名称,不要另起一列,规则名以逗号隔开 |
关键字 | 是否必填 | 值 | 说明 |
---|---|---|---|
CONDITION | 是 | String | 指明该列为判断条件,相当于drl文件中的when 部分,每个规则至少有一个CONDITION |
ACTION | 是 | String | 指明该列为结果,每行如果符合前面的所有CONDITION,既执行此行的ACTION,相当于drl文件中的then |
PRIORITY | 否 | int | 指明该列的值将被设置为该规则的‘lalience’(规则执行先后顺序,值越大执行顺序越靠前),但注意,若在ruleSet 下设置了sequential 为true,则PRIORITY不生效,设置为false或不设置,则PRIORITY生效 |
NAME | 否 | String | 指明该列的值将会被设置为从那行产生的规则名称 |
NO-LOOP | 否 | true/false | 指明这个规则不允许循环,与drl文件中no-loop功能一样 |
ACTIOVATION-GROUP | 否 | String | 在这个列中单元格的值,指出该规则行属于特定的活动分组。一个活动分组以为这在命名组中的规则只有一条会被引用(首条规则触发哦,中止其他规则活动)与drl中含义一样 |
AGENDA-GROUP | 否 | String | 在这个列中单元格的值,指出该规则行属于特定的议程组,可以理解成获取焦点(这是一种在规则组之间控制流的方法),与drl文件中含义一样 |
RULEFLOW-GROUP | 否 | String | 在这个列中单元格的值,指出该规则行属于特定的规则流组 |
决策表必不可少的俩部分RuleSet 和RuleTable,RuleSet 后面填写转换为drl 文件的包名,一般和实体类包名一致,Import需要注意的是,在Java开发中,String,Boolean这些类型是不需要在类中Import,但是在决策表中需要Import,否则会找不到类;股则RuleTable部分主要是CONDITION和ACTION两个关键字,相当于 when … then … 当条件CONDITION满足时就执行ACTION后的动作。
我目前使用的是通过Drools工具类KieHelper来调用决策表;大体分为以下几步:
1.pom引入所需Drools Mvn库
2.验证决策表转换drl文件是否正确
3.利用KieHelper执行drl文件
引入drools版本配置,我使用的是7.5.0Final,引入以下6个基本上满足开发需求了
<properties>
<drools.version>7.5.0.Final</drools.version>
</properties>
<dependencies>
<!-- drools -->
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>${drools.version}</version>
</dependency>
<!-- compiler 必须引用的包,用于对规则的编译、构建 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-templates</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-ci</artifactId>
<version>${drools.version}</version>
</dependency>
</dependencies>
/**
* 验证决策表格式是否正确
* 正确返回DRL文件
* 错误返回 NULL字符串
* @param filePath 文件存储地址
* @param fileType 文件类型 XLS 和 CSV 两种
* @return 返回DRL文件内容
*/
public static String ValidationecisionTable(String filePath,String fileType) {
String drl =null;
try {
File file = new File(filePath);
InputStream is = new FileInputStream(file);
// DRL 解析类 :compile 方法解析文件 可以解析 XLS 和 CSV 两种文件格式
SpreadsheetCompiler conCompiler = new SpreadsheetCompiler();
if("XLS".equals(fileType)) {
drl = conCompiler.compile(is, InputType.XLS);
}else if("CSV".equals(fileType)){
drl = conCompiler.compile(is, InputType.CSV);
}
} catch (Exception e) {
e.printStackTrace();
}
return drl;
}
/**
* 执行drl文件并返回结果集
* @param drl drl文件内容
* @param obj 所需执行的对象
* @return 返回对象执行后的结果
*/
public static Object ExecuteDRLFile(String drl,Object obj) {
if(drl!=null) {
KieHelper helper = new KieHelper();
helper.addContent(drl, ResourceType.DRL);
KieSession kieSession = helper.build().newKieSession();
kieSession.insert(obj);
int i = kieSession.fireAllRules();
System.out.println("规则执行了"+i+"次");
}else {
System.out.println("文件解析错误,请检查决策表");
}
return obj;
}
只需以上三步即可完成初步的实现,下面来调用一下试试!代码如下
决策表中用到的实体类
/**
*
* @Author Miracle hezp
* @Date 2020/5/7 13:49:50
* @Version 1.0
*/
@Data
public class LockApproval implements java.io.Serializable {
static final long serialVersionUID = 1L;
@org.kie.api.definition.type.Label("锁ID")
@JSONField(name = "lock_id")
private String lockId;
@org.kie.api.definition.type.Label("锁类型")
@JSONField(name = "lock_type")
private String lockType;
@org.kie.api.definition.type.Label("锁模式")
@JSONField(name = "lock_mode")
private String lockMode;
@org.kie.api.definition.type.Label("业务类型")
@JSONField(name = "business_type")
private String businessType;
@org.kie.api.definition.type.Label("分支")
@JSONField(name = "org_name")
private String orgName;
@org.kie.api.definition.type.Label("基础包月数")
@JSONField(name = "base_package_months")
private int basePackageMonths;
@org.kie.api.definition.type.Label("不可独立购买")
@JSONField(name = "category")
private List<String> category;
@org.kie.api.definition.type.Label("本分支产品")
@JSONField(name = "is_own_org_product")
private String isOwnOrgProduct;
@org.kie.api.definition.type.Label("是否跳过节点")
@JSONField(name = "result")
private Boolean result = false;
}
根据实际业务编写,我这个例子主要是用于判断是否可以自动审批流程,根据传入的实体类信息,返回是可以自动审批结果,结果存入result字段中,决策表中ACTION执行的动作如下;
Service 方法中直接调用DRL验证方法ValidationecisionTable,执行DRL文件即可
LockApprovalVo lockApprovalVo = new LockApprovalVo();
String drl = DRLUtil.ValidationecisionTable("E:\\LockApproval.xlsx", "XLS");
System.out.println(drl);
if (drl != null) {
lockApproval = (LockApproval) DRLUtil.ExecuteDRLFile(drl, lockApproval);
} else {
System.out.println("文件解析错误,请检查决策表");
}
执行后打印出drl转换结果如下示例
可以看到规则被执行了,获取类中的值,getResult()结果成功被修改为true.
到此SpringBoot结合Drl决策表配置及简单开发示例已完成,欢迎交流指正!