前面我們編寫的規則檔案都是drl形式的檔案,Drools除了支援drl形式的檔案外還支援xls格式的檔案(即Excel檔案)。這種xls格式的檔案通常稱為決策表(decision table)。
決策表(decision table)是一個「精確而緊湊的」表示條件邏輯的方式,非常適合商業級別的規則。決策表與現有的drl檔案可以無縫替換。Drools提供了相應的API可以將xls檔案編譯為drl格式的字串。
一個決策表的例子如下:
決策表語法:
關鍵字 | 說明 | 是否必須 |
---|---|---|
RuleSet | 相當於drl檔案中的package | 必須,只能有一個。如果沒有設定RuleSet對應的值則使用預設值rule_table |
Sequential | 取值為Boolean型別。true表示規則按照表格自上到下的順序執行,false表示亂序 | 可選 |
Import | 相當於drl檔案中的import,如果引入多個類則類之間用逗號分隔 | 可選 |
Variables | 相當於drl檔案中的global,用於定義全域性變數,如果有多個全域性變數則中間用逗號分隔 | 可選 |
RuleTable | 它指示了後面將會有一批rule,RuleTable的名稱將會作為以後生成rule的字首 | 必須 |
CONDITION | 規則條件關鍵字,相當於drl檔案中的when。下面兩行則表示 LHS 部分,第三行則為註釋行,不計為規則部分,從第四行開始,每一行表示一條規則 | 每個規則表至少有一個 |
ACTION | 規則結果關鍵字,相當於drl檔案中的then | 每個規則表至少有一個 |
NO-LOOP | 相當於drl檔案中的no-loop | 可選 |
AGENDA-GROUP | 相當於drl檔案中的agenda-group | 可選 |
在決策表中還經常使用到預留位置,語法為$後面加數位,用於替換每條規則中設定的具體值。
上面的決策表例子轉換為drl格式的規則檔案內容如下:
package rules;
import com.itheima.drools.entity.PersonInfoEntity;
import java.util.List;
global java.util.List listRules;
rule "personCheck_10"
salience 65535
agenda-group "sign"
when
$person : PersonInfoEntity(sex != "男")
then
listRules.add("性別不對");
end
rule "personCheck_11"
salience 65534
agenda-group "sign"
when
$person : PersonInfoEntity(age < 22 || age > 25)
then
listRules.add("年齡不合適");
end
rule "personCheck_12"
salience 65533
agenda-group "sign"
when
$person : PersonInfoEntity(salary < 10000)
then
listRules.add("工資太低了");
end
要進行決策表相關操作,需要匯入如下maven座標:
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>7.10.0.Final</version>
</dependency>
通過下圖可以發現,由於maven的依賴傳遞特性在匯入drools-decisiontables座標後,drools-core和drools-compiler等座標也被傳遞了過來
Drools提供的將xls檔案編譯為drl格式字串的API如下:
String realPath = "C:\\testRule.xls";//指定決策表xls檔案的磁碟路徑
File file = new File(realPath);
InputStream is = new FileInputStream(file);
SpreadsheetCompiler compiler = new SpreadsheetCompiler();
String drl = compiler.compile(is, InputType.XLS);
Drools還提供了基於drl格式字串建立KieSession的API:
KieHelper kieHelper = new KieHelper();
kieHelper.addContent(drl, ResourceType.DRL);
KieSession session = kieHelper.build().newKieSession();
基於決策表的入門案例:
第一步:建立maven工程drools_decisiontable_demo並設定pom.xml檔案
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>7.10.0.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
第二步:建立實體類PersonInfoEntity
package com.itheima.drools.entity;
public class PersonInfoEntity {
private String sex;
private int age;
private double salary;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
第三步:建立xls規則檔案(可以直接使用資料中提供的testRule.xls檔案)
第四步:建立單元測試
@Test
public void test1() throws Exception{
String realPath = "d:\\testRule.xls";//指定決策表xls檔案的磁碟路徑
File file = new File(realPath);
InputStream is = new FileInputStream(file);
SpreadsheetCompiler compiler = new SpreadsheetCompiler();
String drl = compiler.compile(is, InputType.XLS);
System.out.println(drl);
KieHelper kieHelper = new KieHelper();
kieHelper.addContent(drl, ResourceType.DRL);
KieSession session = kieHelper.build().newKieSession();
PersonInfoEntity personInfoEntity = new PersonInfoEntity();
personInfoEntity.setSex("男");
personInfoEntity.setAge(35);
personInfoEntity.setSalary(1000);
List<String> list = new ArrayList<String>();
session.setGlobal("listRules",list);
session.insert(personInfoEntity);
session.getAgenda().getAgendaGroup("sign").setFocus();
session.fireAllRules();
for (String s : list) {
System.out.println(s);
}
session.dispose();
}
各保險公司針對人身、財產推出了不同的保險產品,作為商業保險公司,篩選出符合公司利益最大化的客戶是非常重要的,即各保險產品的准入人群是不同的,也就是說保險公司會針對不同的人群特徵,制定不同的產品繳費和賠付規則。
我們來看一下某保險產品准入規則的簡化版,當不滿足以下規則時,系統模組需要返回准入失敗標識和失敗原因
規則1: 保險公司是:PICC
規則2: 銷售區域是:北京、天津
規則3: 投保人年齡:0 ~ 17歲
規則4: 保險期間是:20年、25年、30年
規則5: 繳費方式是:躉交(一次性交清)或年交
規則6: 保險期與交費期規則一:保險期間為20年期交費期間最長10年交且不能選擇[躉交]
規則7: 保險期與交費期規則二:保險期間為25年期交費期間最長15年交且不能選擇[躉交]
規則8: 保險期與交費期規則三:保險期間為30年期交費期間最長20年交且不能選擇[躉交]
規則9: 被保人要求:(投保年齡+保險期間)不得大於40週歲
規則10: 保險金額規則:投保時約定,最低為5萬元,超過部分必須為1000元的整數倍
規則11: 出單基本保額限額規則:線上出單基本保額限額62.5萬元,超62.5萬元需配合契調轉線下出單
在本案例中規則檔案是一個Excel檔案,業務人員可以直接更改這個檔案中指標的值,系統不需要做任何變更。
本案例還是基於Spring Boot整合Drools的架構來實現。
第一步:建立maven工程insuranceInfoCheck並設定pom.xml檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>insuranceInfoCheck</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!--drools規則引擎-->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-templates</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
</exclusions>
<version>7.6.0.Final</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
第二步:建立/resources/application.yml檔案
server:
port: 8080
spring:
application:
name: insuranceInfoCheck
第三步:建立實體類InsuranceInfo
package com.itheima.drools.entity;
/**
* 保險資訊
*/
public class InsuranceInfo {
private String param1;//保險公司
private String param2;//方案程式碼
private String param3;//渠道號
private String param4;//銷售區域
private String param5;//投保年齡
private String param6;//保險期間
private String param7;//繳費期間
private String param8;//繳費方式
private String param9;//保障型別
private String param10;//等待期
private String param11;//猶豫期
private String param12;//職業型別
private String param13;//保額限制
private String param14;//免賠額
private String param15;//主險保額
private String param16;//主險保費
private String param17;//附加險保額
private String param18;//附加險保費
private String param19;//與投保人關係
private String param20;//與被保人關係
private String param21;//性別
private String param22;//證件
private String param23;//保費
private String param24;//保額
//getter setter省略
}
第四步:建立決策表檔案(也可以直接使用實戰資料中提供的insuranceInfoCheck.xls檔案)
第五步:封裝工具類KieSessionUtils
package com.itheima.drools.utils;
import com.itheima.drools.entity.InsuranceInfo;
import com.itheima.drools.entity.PersonInfoEntity;
import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.utils.KieHelper;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class KieSessionUtils {
private KieSessionUtils() {
}
// 把xls檔案解析為String
public static String getDRL (String realPath) throws FileNotFoundException {
File file = new File(realPath); // 例如:C:\\abc.xls
InputStream is = new FileInputStream(file);
SpreadsheetCompiler compiler = new SpreadsheetCompiler();
String drl = compiler.compile(is, InputType.XLS);
System.out.println(drl);
return drl;
}
// drl為含有內容的字串
public static KieSession createKieSessionFromDRL(String drl) throws Exception{
KieHelper kieHelper = new KieHelper();
kieHelper.addContent(drl, ResourceType.DRL);
Results results = kieHelper.verify();
if (results.hasMessages(Message.Level.WARNING, Message.Level.ERROR)) {
List<Message> messages = results.getMessages(Message.Level.WARNING, Message.Level.ERROR);
for (Message message : messages) {
System.out.println("Error: "+message.getText());
}
// throw new IllegalStateException("Compilation errors were found. Check the logs.");
}
return kieHelper.build().newKieSession();
}
// realPath為Excel檔案絕對路徑
public static KieSession getKieSessionFromXLS(String realPath) throws Exception {
return createKieSessionFromDRL(getDRL(realPath));
}
}
第六步:建立RuleService類
package com.itheima.drools.service;
import com.itheima.drools.entity.InsuranceInfo;
import com.itheima.drools.utils.KieSessionUtils;
import org.kie.api.runtime.KieSession;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class RuleService {
public List<String> insuranceInfoCheck(InsuranceInfo insuranceInfo) throws Exception{
KieSession session = KieSessionUtils.getKieSessionFromXLS("D:\\rules.xls");
session.getAgenda().getAgendaGroup("sign").setFocus();
session.insert(insuranceInfo);
List<String> listRules = new ArrayList<>();
session.setGlobal("listRules", listRules);
session.fireAllRules();
return listRules;
}
}
第七步:建立RuleController類
package com.itheima.drools.controller;
import com.itheima.drools.entity.InsuranceInfo;
import com.itheima.drools.service.RuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/rule")
public class RuleController {
@Autowired
private RuleService ruleService;
@RequestMapping("/insuranceInfoCheck")
public Map insuranceInfoCheck(){
Map map = new HashMap();
//模擬資料,實際應為頁面傳遞過來
InsuranceInfo insuranceInfo = new InsuranceInfo();
insuranceInfo.setParam1("picc");
insuranceInfo.setParam4("上海");
insuranceInfo.setParam5("101");
insuranceInfo.setParam6("12");
insuranceInfo.setParam7("222");
insuranceInfo.setParam8("1");
insuranceInfo.setParam13("3");
try {
List<String> list = ruleService.insuranceInfoCheck(insuranceInfo);
if(list != null && list.size() > 0){
map.put("checkResult",false);
map.put("msg","准入失敗");
map.put("detail",list);
}else{
map.put("checkResult",true);
map.put("msg","准入成功");
}
return map;
} catch (Exception e) {
e.printStackTrace();
map.put("checkResult",false);
map.put("msg","未知錯誤");
return map;
}
}
}
第八步:建立啟動類DroolsApplication
package com.itheima.drools;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DroolsApplication {
public static void main(String[] args) {
SpringApplication.run(DroolsApplication.class);
}
}