6 Drools 规则引擎案例

303 阅读5分钟

6.1 概述

这一章主要介绍Drools规则引擎的实际应用案例,通过具体的案例,帮助读者了解Drools规则引擎的强大功能,以及在各行各业中的应用场景。这一章中将包括多个案例,包括金融、保险、电商等领域的应用案例。通过对案例的介绍,读者可以了解到Drools规则引擎在各个领域的应用价值,以及如何在自己的项目中使用Drools规则引擎。

Drools是一个规则引擎,通常用于实现业务规则管理和自动决策。下面是Drools常见的使用场景:

  • 客户端决策:Drools可以被用来处理客户端决策问题,如客户经理决策、客户评分卡、授信决策、风险评估等。
  • 规则管理:Drools可以作为一个独立的规则管理平台,帮助企业管理规则和流程。
  • 基于规则的业务流程管理:Drools可以集成到业务流程管理系统中,帮助企业管理规则驱动的业务流程。
  • 数据验证和清洗:Drools可以被用来对数据进行验证和清洗,以确保数据的准确性和一致性。
  • 基于事件的决策:Drools可以被用来实现基于事件的决策,如基于实时事件的促销决策、实时交易监测等。

通过本文,读者将学会如何使用Drools编写规则,并了解Drools在实际业务中的实际应用。

6.2 数据验证和清洗

Drools可以在数据验证和清洗场景中使用,通过使用规则来验证数据的有效性和一致性。具体来说,Drools可以用于:

  • 数据格式验证:可以使用Drools验证数据是否符合特定的格式要求,如邮件地址、电话号码等。
  • 数据范围验证:可以使用Drools验证数据是否在允许的范围内,如年龄范围、工资范围等。
  • 数据一致性验证:可以使用Drools验证不同数据项之间的一致性,如邮寄地址和电话号码的一致性等。
  • 数据清洗:可以使用Drools对数据进行清洗,如删除重复的数据、修正错误的数据等。

通过使用Drools进行数据验证和清洗,可以保证数据的准确性和一致性,从而提高数据的可靠性和可用性。

如下几个规则是关于Drools在数据清洗场景中的示例

删除重复的数据

rule "Remove duplicate records"
when
    $record1: Record( $id1: id, $name1: name, $age1: age, $email1: email )
    $record2: Record( id == $id1, name == $name1, age == $age1, email == $email1 )
    $record1 != $record2
then
    retract( $record2 );
end

修正错误的数据

rule "Correct invalid email addresses"
when
    $record: Record( email not matches "(\w+\.)*\w+@\w+\.\w+" )
then
    modify( $record ) { setEmail( "invalid" ) };
end

数据格式转换

使用Drools对数据进行格式转换,如将日期字段从 一种格式转换为另一种格式等。

package com.example.drools.rules;

import com.example.drools.DataCleaningExample.Record;

rule "Remove Records with Invalid Age"
when
    $record : Record(age < 0 || age > 120)
then
    retract($record);
end

上面的规则演示了标准化地址的功能(将所有地址字段转换为大写字母)。

去除噪声数据

使用Drools对数据进行预处理,如去除数据中的噪声等。

package com.example.drools.rules;

import com.example.drools.DataCleaningExample.Record;

rule "Remove Outliers - Age"
when
    $record : Record(age < 18 || age > 65)
then
    retract($record);
end

rule "Remove Outliers - Address"
when
    $record : Record(address matches ".*[0-9].*")
then
    retract($record);
end

这是两个Drools规则,分别演示了如何删除年龄的离群值(年龄小于18或大于65)和地址的离群值(地址中包含数字)。

填补缺失的数据

Drools对数据进行预处理,如填补缺失的数据等。

package com.example.drools.rules;

import com.example.drools.DataCleaningExample.Record;

rule "Fill Missing Name"
when
    $record : Record(name == null || name.isEmpty())
then
    $record.setName("N/A");
end

rule "Fill Missing Age"
when
    $record : Record(age == 0)
then
    $record.setAge(30);
end

rule "Fill Missing Address"
when
    $record : Record(address == null || address.isEmpty())
then
    $record.setAddress("N/A");
end

这是三个Drools规则,分别演示了如何填补缺失的名称,年龄和地址。

6.3 客户评分卡

Drools可以用于客户评分卡场景,以评估客户的信用评分。 它可以通过评估客户的财务历史、信用历史和个人信息等因素来评分客户。

package com.example.drools.rules;

import com.example.drools.CreditScoringExample.Customer;

rule "Good Credit History"
when
$customer : Customer(creditHistory == "Good")
then
$customer.setScore($customer.getScore() + 20);
end

rule "High Income"
when
$customer : Customer(income > 75000)
then
$customer.setScore($customer.getScore() + 10);
end

rule "Long Employment"
when
$customer : Customer(employmentLength > 5)
then
$customer.setScore($customer.getScore() + 15);
end

6.4 基于规则的业务流程管理

Drools可以用于帮助企业管理规则和流程。 它可以作为一种途径,帮助企业管理和自动化各种业务规则,如:

  • 权限管理:判断用户是否具有某种特定的权限,以访问特定的功能或数据。
  • 工作流管理:帮助企业管理复杂的业务流程,如请求审批或订单处理。
  • 报价管理:帮助企业根据特定的规则,计算客户的报价。
  • 优惠券管理:帮助企业判断特定的优惠券是否适用于特定的客户。

如下是一个简单的Drools规则示例,演示了如何管理报价:

package com.example.drools.rules;

import com.example.drools.PricingManagementExample.Order;

rule "Discount for High Volume Orders"
when
    $order : Order(quantity > 100)
then
    $order.setDiscount(0.10);
end

rule "Discount for Repeat Customers"
when
    $order : Order(customer.numberOfOrders > 10)
then
    $order.setDiscount(0.05);
end

rule "Discount for Large Orders"
when
    $order : Order(total > 1000)
then
    $order.setDiscount(0.15);
end

这是三个Drools规则,分别演示了如何为大量订单、重复顾客和大订单提供折扣。

6.5 基于事件的决策

Drools可以通过使用事件驱动的决策来实现基于事件的决策。 它通过监听事件并触发相应的规则来实现事件驱动的决策。 规则可以根据事件中包含的信息执行特定的操作,如:

  • 审批请求:当客户发起请求时,根据请求类型和客户信息执行相应的审批操作。
  • 发送通知:当特定事件发生时,发送通知到相关的人员或系统。
  • 执行操作:当特定事件发生时,执行特定的操作,如修改数据或发送请求。

如下是一个简单的Drools规则示例,演示了如何执行基于事件的决策:

package com.example.drools.rules;

import com.example.drools.EventDrivenDecisionMakingExample.OrderEvent;

rule "Approve High Volume Orders"
when
    $orderEvent : OrderEvent(order.quantity > 100)
then
    $orderEvent.getOrder().setApproved(true);
end

rule "Send Notification for Large Orders"
when
    $orderEvent : OrderEvent(order.total > 1000)
then
    // Send notification to relevant personnel or system
end

rule "Update Inventory for Approved Orders"
when
    $orderEvent : OrderEvent(order.isApproved() == true)
then
    // Update inventory information
end

这是三个Drools规则,分别演示了如何批准大量订单、发送通知和更新库存信息。

6.6 银行业务审批

在银行业务审批中,Drools可以应用于贷款审批和信用卡审批等场景。通过规则引擎,银行可以根据客户的贷款申请和信用历史等信息,自动化地判断是否批准贷款或信用卡。

下面是一个简单的银行业务审批案例,演示了Drools如何在银行业务审批中使用:

规则文件(loan-approval.drl)

package rules;

import org.dtt.entity.*;

rule "Reject Loan for Applicant with Bad Credit Score"
when
  $applicant:Applicant(creditScore < 600)
  $loanApplication:LoanApplication(applicant == $applicant)
then
  $loanApplication.setApproved(false);
  $loanApplication.setReason("Rejected due to low credit score");
  System.out.println("Rejected due to low credit score");
  retract($loanApplication);
end

rule "Approve Loan for Applicant with Good Credit Score"
when
  $applicant:Applicant(creditScore >= 600)
  $loanApplication:LoanApplication(applicant == $applicant)
then
  $loanApplication.setApproved(true);
  $loanApplication.setReason("Approved");
  System.out.println("Approve Loan for Applicant with Good Credit Score --> approved");
end

rule "Reject Loan for Applicant with Low Income"
when
  $applicant:Applicant(income < 5000)
  $loanApplication:LoanApplication(applicant == $applicant)
then
  $loanApplication.setApproved(false);
  $loanApplication.setReason("Rejected due to low income");
  retract($loanApplication);
  System.out.println("Reject Loan for Applicant with Low Income --> reject");
end


rule "print"
when
  $loanApplication: LoanApplication()
then
  System.out.println($loanApplication.getApplicant().getName());
end

Fact类

import java.io.Serializable;
public class Applicant implements Serializable {
    private String name;
    private int age;
    private int creditScore;
    private double income;
    public LoanApplication(Applicant applicant, Integer loanAmount) {
        this.applicant = applicant;
        this.loanAmount = loanAmount;
    }
//省略Getters/Setters方法
}
public class LoanApplication {
   private Applicant applicant;
    private Integer loanAmount;
    private boolean approved;
    private String reason;
	//省略Getters/Setters方法
}

测试示例

在我们运行TestLoanApproval代码时,它会触发Drools规则引擎,并使用LoanApplication和Applicant对象作为规则的输入。 Drools规则引擎将按照定义的规则来评估这些对象,并决定是否批准贷款申请。

如果所有的规则都被评估为true,那么贷款申请将被批准。在这种情况下,LoanApprovalTest中的断言语句将不会抛出任何异常,代表测试成功。

如果任何一个规则评估为false,那么贷款申请将不会被批准。在这种情况下,LoanApprovalTest中的断言语句将抛出异常,代表测试失败。

 @Test
public void testLoanApproval() {
    KieServices kieServices = KieServices.Factory.get();
    KieContainer kieContainer = kieServices.newKieClasspathContainer();
    KieSession kieSession = kieContainer.newKieSession();
    Applicant applicant = new Applicant("Joe", 23, 900, 10000);
    LoanApplication loanApplication = new LoanApplication(applicant);
    loanApplication.setLoanAmount(100000);
    kieSession.insert(applicant);
    kieSession.insert(loanApplication);
    kieSession.fireAllRules();
    kieSession.dispose();
    assertTrue(loanApplication.isApproved());
}

6.7 小明喝汽水问题

1元钱一瓶汽水,喝完后两个空瓶换一瓶汽水,问:小明有20元钱,最多可以喝到几瓶汽水?

规则拆分

规则1:1元钱一瓶汽水--> 有钱就买水,空瓶+1,钱-1,喝水+1

规则2:两个空瓶换一瓶汽水-->有空瓶就换钱,空瓶-2,钱+1

//规则1:1元钱一瓶汽水。有钱就买水,空瓶+1,钱-1,喝水+1;
        rule "rule1"
            salience 3
            when
                $m:XiaoMing(money>0);
            then
                System.out.println("有钱即可喝水,钱:"+$m.getMoney());
                $m.setBottle($m.getBottle()+1);
                $m.setMoney($m.getMoney()-1);
                $m.setDrink($m.getDrink()+1);
                update($m)
        end

需要注意的是,如果想要换水,需要再编写一个规则,描述空瓶的变化情况。

该规则描述了:当XiaoMing实例的bottle属性大于等于2时,空瓶减少2瓶,喝水量增加1瓶,并且更新XiaoMing实例。

//规则2:两个空瓶换一瓶汽水。有空瓶就换钱,空瓶-2,钱+1;
        rule "rule2"
            salience 2
            when
                $m:XiaoMing(bottle>=2);
            then
                System.out.println("有瓶子就换钱,瓶子:"+$m.getBottle());
                $m.setBottle($m.getBottle()-2);
                $m.setMoney($m.getMoney()+1);
                update($m)
        end

如果想要在Drools规则中打印喝水数量,可以在合适的地方添加一行代码,例如在最终状态打印喝水数量:

//规则3,当XiaoMing实例的money属性小于等于0时,打印喝水数量
        rule "rule3"
            salience 1
            when
                $m:XiaoMing(money<=0);
            then
                System.out.println("总共喝掉:"+$m.getDrink());
        end

Fact类

/**
 * Fact定义
 */
public class XiaoMing {

    //总共的钱
    private int money;
    //空瓶子数目
    private int bottle;
    //已经喝掉的汽水
    private int drink;
	//省略getters、setters方法.....
}

测试方法

@Test
public void test01() {
        KieContainer kc = KieServices.Factory.get().getKieClasspathContainer();
        System.out.println(kc.verify().getMessages().toString());
        KieSession ksession = kc.newKieSession("mingKS");
        XiaoMing xiaoMing=new XiaoMing();
        xiaoMing.setMoney(20);
        ksession.insert(xiaoMing);
        ksession.fireAllRules();
        ksession.dispose();
    }

6.8 高尔夫球员站位问题

问题分析

已知有四个高尔夫球员,他们的名字是Fred,Joe,Bob,Tom;今天他们分别穿着红色,蓝色,橙色,以及格子衣服,并且他们按照从左往右的顺序站成一排。 我们将最左边的位置定为1,最右边的位置定为4,中间依次是2,3位置。

现在我们了解的情况是:

  • 高尔夫球员Fred,目前不知道他的位置和衣服颜色
  • Fred右边紧挨着的球员穿蓝色衣服
  • Joe排在第2个位置
  • Bob穿着格子短裤
  • Tom没有排在第1位或第4位,也没有穿橙色衣服

请问,这四名球员的位置和衣服颜色。

规则

package com.xshe.drools

import com.xshe.drools.bean.Golfer;

rule "find solution"
    when
        //1.高尔夫球员Fred,目前不知道他的位置和衣服颜色
        $fred : Golfer( name == "Fred" )

        //3.Joe排在第2个位置
        $joe : Golfer( name == "Joe",
                position == 2,
                position != $fred.position,
                color != $fred.color )

        //4.Bob穿着格子短裤
        $bob : Golfer( name == "Bob",
                position != $fred.position,
                position != $joe.position,
                color == "plaid",
                color != $fred.color,
                color != $joe.color )

        //5.Tom没有排在第1位或第4位,也没有穿橙色衣服
        $tom : Golfer( name == "Tom",
                position != 1,
                position != 4,
                position != $fred.position,
                position != $joe.position,
                position != $bob.position,
                color != "orange",
                color != $fred.color,
                color != $joe.color,
                color != $bob.color )

        //2.Fred右边紧挨着的球员穿蓝色衣服
        Golfer( position == ( $fred.position + 1 ),
                      color == "blue",
                      this in ( $joe, $bob, $tom ) )

    then
        System.out.println( "Fred " + $fred.getPosition() + " " + $fred.getColor() );
        System.out.println( "Joe " + $joe.getPosition() + " " + $joe.getColor() );
        System.out.println( "Bob " + $bob.getPosition() + " " + $bob.getColor() );
        System.out.println( "Tom " + $tom.getPosition() + " " + $tom.getColor() );
end

测试

public static void main(final String[] args) {
        KieContainer kc = KieServices.Factory.get().getKieClasspathContainer();
        System.out.println(kc.verify().getMessages().toString());
        KieSession ksession = kc.newKieSession("golfKS");
        String[] names = new String[]{"Fred", "Joe", "Bob", "Tom"};
        String[] colors = new String[]{"red", "blue", "plaid", "orange"};
        int[] positions = new int[]{1, 2, 3, 4};

        for (String name : names) {
            for (String color : colors) {
                for (int position : positions) {
                    ksession.insert(new Golfer(name, color, position));
                }
            }
        }
        ksession.fireAllRules();
        ksession.dispose();
    }