drools決策表的簡單使用

2022-05-30 09:00:55

一、背景

在之前的文章中,我們的業務規則都是寫在了drl檔案中,這對開發人員來說是沒有什麼問題,如果是業務人員則不怎麼友好,這篇文章我們簡單學習一下drools中決策表的使用,規則是寫在excel檔案中。

二、一個簡單的決策表

在上面這個圖中ResultSetResultTable是必須的,而且同一個包中,我們最好只上傳一個決策表。

1、在同一個決策表中處理多個Sheet頁

2、RuleSet下方可以有哪些屬性

Label Value Usage
RuleSet The package name for the generated DRL file. Optional, the default is rule_table. Must be the first entry.
Sequential true or false. If true, then salience is used to ensure that rules fire from the top down. Optional, at most once. If omitted, no firing order is imposed.
SequentialMaxPriority Integer numeric value Optional, at most once. In sequential mode, this option is used to set the start value of the salience. If omitted, the default value is 65535.
SequentialMinPriority Integer numeric value Optional, at most once. In sequential mode, this option is used to check if this minimum salience value is not violated. If omitted, the default value is 0.
EscapeQuotes true or false. If true, then quotation marks are escaped so that they appear literally in the DRL. Optional, at most once. If omitted, quotation marks are escaped.
IgnoreNumericFormat true or false. If true, then the format for numeric values is ignored, for example, percent and currency. Optional, at most once. If omitted, DRL takes formatted values.
Import A comma-separated list of Java classes to import from another package. Optional, may be used repeatedly.
Variables Declarations of DRL globals (a type followed by a variable name). Multiple global definitions must be separated by commas. Optional, may be used repeatedly.
Functions One or more function definitions, according to DRL syntax. Optional, may be used repeatedly.
Queries One or more query definitions, according to DRL syntax. Optional, may be used repeatedly.
Declare One or more declarative types, according to DRL syntax. Optional, may be used repeatedly.
Unit The rule units that the rules generated from this decision table belong to. Optional, at most once. If omitted, the rules do not belong to any unit.
Dialect java or mvel. The dialect used in the actions of the decision table. Optional, at most once. If omitted, java is imposed.

ResultSet:區域只可有一個。

3、RuleTable下方可以有哪些屬性

Label Or custom label that begins with Value Usage
NAME N Provides the name for the rule generated from that row. The default is constructed from the text following the RuleTable tag and the row number. At most one column.
DESCRIPTION I Results in a comment within the generated rule. At most one column.
CONDITION C Code snippet and interpolated values for constructing a constraint within a pattern in a condition. At least one per rule table.
ACTION A Code snippet and interpolated values for constructing an action for the consequence of the rule. At least one per rule table.
METADATA @ Code snippet and interpolated values for constructing a metadata entry for the rule. Optional, any number of columns.

具體的使用可以見上方的圖

4、規則屬性的編寫

ResultSetResultTable這個地方都可以編寫規則屬性。ResultSet地方的規則屬性將影響同一個包下所有的規則,而ResultTable這個地方的規則屬性,隻影響這個規則。ResultTable的優先順序更高。

支援的規則屬性有:PRIORITYDATE-EFFECTIVEDATE-EXPIRESNO-LOOPAGENDA-GROUPACTIVATION-GROUPDURATIONTIMERCALENDARAUTO-FOCUSLOCK-ON-ACTIVERULEFLOW-GROUP

具體的用法:見上圖中ACTIVATION-GROUP的使用。

三、需求

我們需要根據學生的成績分數,給出相應的結果。規則如下:

特殊處理的規則:
規則一:只要名字是張三的,直接判定為 優
規則二:只要名字是李四的,如果分數在0,60之間,直接認為是一般

普通規則:
規則三:分數在0,60之間認為是不及格
規則四:分數在60,70之間認為是一般
規則五:分數在70,90之間認為是良好
規則六:分數在90,100之間認為是

從上方的規則中,我們可以看到姓名為張三李四的學生特殊處理了。

四、實現

1、專案實現結構圖

2、引入jar包

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-bom</artifactId>
            <type>pom</type>
            <version>7.69.0.Final</version>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-compiler</artifactId>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-mvel</artifactId>
    </dependency>
    <!-- 決策表 -->
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-decisiontables</artifactId>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.11</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
    </dependency>
</dependencies>

3、編寫kmodule.xml檔案

<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <kbase name="kabse" packages="rules.decision.tables" default="false">
        <ksession name="ksession" default="false" type="stateful"/>
    </kbase>
</kmodule>

4、編寫學生實體類

@Getter
@Setter
@ToString
public class Student {

    private String name;
    // 分數只能在 0-100 之間
    private Integer score;

    public Student(String name, Integer score) {
        this.name = name;
        if (null == score || score < 0 || score > 100) {
            throw new RuntimeException("分數只能在0-100之間");
        }
        this.score = score;
    }
}

5、編寫決策表

6、將決策錶轉換成drl檔案

這步主要是為了檢視我們的決策表編寫的是否正確,看看最終生成的drl檔案是什麼樣的

1、決策錶轉換成drl檔案程式碼

/**
* 決策錶轉換成 drl 檔案
*/
public static void decisionTable2Drl() throws IOException {
    Resource resource = ResourceFactory.newClassPathResource("rules/decision/tables/student-score.xlsx", "UTF-8");
    InputStream inputStream = resource.getInputStream();
    SpreadsheetCompiler compiler = new SpreadsheetCompiler();
    String drl = compiler.compile(inputStream, InputType.XLS);
    log.info("決策錶轉換的drl內容為:\r{}", drl);

    // 驗證一下 drl 檔案是否有問題
    KieHelper kieHelper = new KieHelper();
    Results results = kieHelper.addContent(drl, ResourceType.DRL).verify();
    List<Message> messages = results.getMessages(Message.Level.ERROR);
    if (null != messages && !messages.isEmpty()) {
        for (Message message : messages) {
            log.error(message.getText());
        }
    }
}

2、轉換成具體的drl檔案為

package rules.decision.tables;
//generated from Decision Table
import java.lang.StringBuilder;
import com.huan.drools.Student;
global java.lang.StringBuilder resultsInfo;



// rule values at B15, header at B10
rule "student-score-name-1"
/* 1、姓名為張三的特殊處理
2、自定義規則的名字 */
	salience 65535
	activation-group "score"
	when
		$stu: Student(name == "張三")
	then
		resultsInfo.append("張三特殊處理:");
System.out.println("規則:" + drools.getRule().getName() + " 執行了.");
		resultsInfo.append("優");
System.out.println("規則:" + drools.getRule().getName() + " 執行了.");
end

// rule values at B16, header at B10
rule "student-score_16"
	salience 65534
	activation-group "score"
	when
		$stu: Student(name == "李四", score > 0 && score < 60)
	then
		resultsInfo.append("李四部分特殊處理:");
System.out.println("規則:" + drools.getRule().getName() + " 執行了.");
		resultsInfo.append("一般");
System.out.println("規則:" + drools.getRule().getName() + " 執行了.");
end

// rule values at B17, header at B10
rule "student-score_17"
	salience 65533
	activation-group "score"
	when
		$stu: Student(score > 0 && score < 60)
	then
		resultsInfo.append("不及格");
System.out.println("規則:" + drools.getRule().getName() + " 執行了.");
end

// rule values at B18, header at B10
rule "student-score_18"
	salience 65532
	activation-group "score"
	when
		$stu: Student(score > 60 && score < 70)
	then
		resultsInfo.append("一般");
System.out.println("規則:" + drools.getRule().getName() + " 執行了.");
end

// rule values at B19, header at B10
rule "student-score_19"
	salience 65531
	activation-group "score"
	when
		$stu: Student(score > 70 && score < 90)
	then
		resultsInfo.append("良好");
System.out.println("規則:" + drools.getRule().getName() + " 執行了.");
end

// rule values at B20, header at B10
rule "student-score_20"
	salience 65530
	activation-group "score"
	when
		$stu: Student(score > 90 && score < 100)
	then
		resultsInfo.append("優");
System.out.println("規則:" + drools.getRule().getName() + " 執行了.");
end

從上方可以看出第一個規則規則名稱是不一樣的,而且存在一些描述資訊,這個是在決策表中特殊處理了。

7、測試

1、編寫測試程式碼

package com.huan.drools;

import lombok.extern.slf4j.Slf4j;
import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import org.kie.api.KieServices;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.io.Resource;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.kie.internal.utils.KieHelper;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * drools 決策表的使用
 */
@Slf4j
public class DroolsDecisionTableApplication {
    public static void main(String[] args) throws IOException {
        decisionTable2Drl();
        KieServices kieServices = KieServices.get();
        KieContainer kieContainer = kieServices.newKieClasspathContainer();
        // 張三雖然只得20分,但是根據規則判斷,結果應該是  優
        invokedDecisionTable(kieContainer, new Student("張三", 20));
        // 李四雖然只得20分,但是根據規則判斷,結果應該是  一般
        invokedDecisionTable(kieContainer, new Student("李四", 20));
        // 李四得75分,但是根據規則判斷,結果應該是  良好
        invokedDecisionTable(kieContainer, new Student("李四", 75));
        // 王五得59分,但是根據規則判斷,結果應該是  不及格
        invokedDecisionTable(kieContainer, new Student("王五", 59));
        // 趙六得20分,但是根據規則判斷,結果應該是  一般
        invokedDecisionTable(kieContainer, new Student("趙六", 65));
        // 錢七得20分,但是根據規則判斷,結果應該是  良好
        invokedDecisionTable(kieContainer, new Student("錢七", 75));
        // 李八得20分,但是根據規則判斷,結果應該是  優
        invokedDecisionTable(kieContainer, new Student("李八", 95));
    }

    public static void invokedDecisionTable(KieContainer kieContainer, Student student) {
        System.out.println("\r");
        KieSession kieSession = kieContainer.newKieSession("ksession");
        StringBuilder result = new StringBuilder();
        kieSession.setGlobal("resultsInfo", result);
        kieSession.insert(student);
        kieSession.fireAllRules();
        kieSession.dispose();
        System.out.println("規則執行結果:" + result);
    }

    /**
     * 決策錶轉換成 drl 檔案
     */
    public static void decisionTable2Drl() throws IOException {
        Resource resource = ResourceFactory.newClassPathResource("rules/decision/tables/student-score.xlsx", "UTF-8");
        InputStream inputStream = resource.getInputStream();
        SpreadsheetCompiler compiler = new SpreadsheetCompiler();
        String drl = compiler.compile(inputStream, InputType.XLS);
        log.info("決策錶轉換的drl內容為:\r{}", drl);
        // 驗證一下 drl 檔案是否有問題
        KieHelper kieHelper = new KieHelper();
        Results results = kieHelper.addContent(drl, ResourceType.DRL).verify();
        List<Message> messages = results.getMessages(Message.Level.ERROR);
        if (null != messages && !messages.isEmpty()) {
            for (Message message : messages) {
                log.error(message.getText());
            }
        }
    }
}

2、測試結果

從上圖中可知,我們的規則都正常執行了。

五、完整程式碼

https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-decision-table

六、參考檔案

1、https://docs.drools.org/7.69.0.Final/drools-docs/html_single/index.html