Drools规则案例实战(消费积分案例)

目录

一、需求说明与测试要点

1.1 需求说明

1.2 测试内容及要点

二、需求实现

2.1 创建工程

2.1.1 引入maven依赖

2.1.2 规则引擎自动配置类

2.1.3 SpringBoot 启动类

2.1.4 SpringBoot 测试启动类

2.2 业务需求实现

2.2.1 创建实体对象

2.2.1.1 创建用户实体

2.2.1.2 创建地址实体

2.2.1.3 地址检查状态实体

2.2.1.4 订单实体

2.2.2 创建规则文件

2.2.2.1 创建地址检查规则文件

2.2.2.2 创建订单积分规则文件

2.2.3 编写测试类

2.2.3.1 编写地址测试类

2.2.3.2 编写订单积分规则测试类

2.2.3.3 编写积分规则生成及积分测试类

2.2.4 测试结果

2.2.4.1 地址测试类结果(AddressDroolsTests)

2.2.4.2 订单积分规则测试类结果(OrderDroolsTests)

2.2.4.3 积分规则生成及积分测试类结果(DroolsTests)


一、需求说明与测试要点

1.1 需求说明

模拟消费赠送积需求,具体需求点如下:

用户下单原价金额:

  • 100元以下, 不加分
  • 100-500元加100分
  • 500-1000元加500分
  • 1000 以上元加1000分

1.2 测试内容及要点

测试要点:

  1. 检查测试地址是否正确
  2. 模拟用户下单金额,测试规则积分计算

二、需求实现

2.1 创建工程

2.1.1 引入maven依赖

<?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-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/>
    </parent>

    <groupId>com.ningzhaosheng.drools</groupId>
    <artifactId>drools-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.31</version>
        </dependency>

        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-spring</artifactId>
            <version>7.74.1.Final</version>
            <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>
        </dependency>

    </dependencies>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

2.1.2 规则引擎自动配置类

主要是实现规则文件加载和kie核心类装载、配置。

package com.ningzhaosheng.drools.config;

import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;

/**
 * @author ningzhaosheng
 * @date 2024/1/17 17:40:41
 * @description 规则引擎自动配置类
 */
@Configuration
public class DroolsAutoConfiguration {
    private static final String RULES_PATH = "rules/";

    @Bean
    @ConditionalOnMissingBean(KieServices.class)
    public KieServices getKieServices() {
        return KieServices.Factory.get();
    }

    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        for (Resource file : getRuleFiles()) {
            kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
        }
        return kieFileSystem;
    }

    private Resource[] getRuleFiles() throws IOException {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
    }

    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException {
        final KieRepository kieRepository = getKieServices().getRepository();

        kieRepository.addKieModule(() -> kieRepository.getDefaultReleaseId());

        KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();

        KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());

        return kieContainer;
    }

    @Bean
    @ConditionalOnMissingBean(KieBase.class)
    public KieBase kieBase() throws IOException {
        return kieContainer().getKieBase();
    }
}

2.1.3 SpringBoot 启动类

package com.ningzhaosheng.drools;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author ningzhaosheng
 * @date 2024/1/17 17:42:56
 * @description SpringBoot 启动类
 */
@SpringBootApplication
public class DroolsApplication {
    public static void main(String[] args) {
        SpringApplication.run(DroolsApplication.class, args);
        System.out.println("启动成功");
    }
}

2.1.4 SpringBoot 测试启动类

package com.ningzhaosheng.drools;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @author ningzhaosheng
 * @date 2024/1/17 17:15:44
 * @description 启动测试
 */
@SpringBootTest
public class DroolsApplicationTests {
    @Test
    void contextLoads() {
    }

}

2.2 业务需求实现

2.2.1 创建实体对象

2.2.1.1 创建用户实体
package com.ningzhaosheng.drools.integral.entity;

import lombok.Data;
import lombok.experimental.Accessors;

/**
 * @author ningzhaosheng
 * @date 2024/1/20 17:04:07
 * @description 用户实体
 */
@Data
@Accessors(chain = true)
public class User {

    // 用户名
    private String name;

    // 等级
    private int level;

}

2.2.1.2 创建地址实体
package com.ningzhaosheng.drools.integral.entity;

import lombok.Data;
import lombok.experimental.Accessors;

/**
 * @author ningzhaosheng
 * @date 2024/1/20 17:05:27
 * @description 地址实体
 */
@Data
@Accessors(chain = true)
public class Address {
    private String postcode;
    private String street;
}

2.2.1.3 地址检查状态实体
package com.ningzhaosheng.drools.integral.entity;

import lombok.Data;
import lombok.experimental.Accessors;

/**
 * @author ningzhaosheng
 * @date 2024/1/20 17:06:15
 * @description 地址检查状态实体
 */
@Data
@Accessors(chain = true)

public class AddressCheckResult {
    private boolean postCodeResult = false;
}

2.2.1.4 订单实体
package com.ningzhaosheng.drools.integral.entity;

import lombok.Data;
import lombok.experimental.Accessors;

import java.util.Date;

/**
 * @author ningzhaosheng
 * @date 2024/1/20 17:02:41
 * @description 订单实体
 */
@Data
@Accessors(chain = true)
public class Order {
    /**
     * 订单原价金额
     */
    private int price;
    /**
     *下单人
     */
    private User user;
    /**
     *积分
     */
    private int score;
    /**
     * 下单日期
     */
    private Date bookingDate;

}

2.2.2 创建规则文件

2.2.2.1 创建地址检查规则文件

在/resources/rules下创建规则文件address.drl,具体内容如下:

/*区域/战区 省市区的规则*/
package rules

import com.ningzhaosheng.drools.integral.entity.Address
import com.ningzhaosheng.drools.integral.entity.AddressCheckResult;

/*
  地址检查规则
*/
rule "Postcode should be filled with exactly 5 numbers"

    when
        address : Address(postcode != null, postcode matches "([0-9]{5})")
        checkResult : AddressCheckResult();
    then
        checkResult.setPostCodeResult(true);
		System.out.println("规则中打印日志:校验通过1!");
end

rule "TEST"

    when
        address : Address(street != null, street=="测试")
        checkResult : AddressCheckResult();
    then
        checkResult.setPostCodeResult(true);
		System.out.println("规则中打印日志:校验通过2!");
end

2.2.2.2 创建订单积分规则文件

在/resources/rules下创建规则文件point-rules.drl,具体内容如下:

package rules

import com.ningzhaosheng.drools.integral.entity.Order
import com.ningzhaosheng.drools.integral.entity.User

/*
  积分规则
*/
rule "zero"
    no-loop true
    lock-on-active true
    salience 1
    when
        $order : Order(price <= 100)
    then
        $order.setScore(0);
        update($order);
end

rule "add100"
    no-loop true
    lock-on-active true
    salience 1
    when
        $order : Order(price > 100 && price <= 500)
//        and $user : User(level>3)
    then
        System.out.println($order.getUser());
        $order.setScore(100);
        update($order);
end


rule "add500"
    no-loop true
    lock-on-active true
    salience 1
    when
        $order : Order(price > 500 && price <= 1000)
    then
        $order.setScore(500);
        update($order);
end

rule "add1000"
    no-loop true
    lock-on-active true
    salience 1
    when
        $order : Order(price > 1000)
    then
        $order.setScore(1000);
        update($order);
end

2.2.3 编写测试类

2.2.3.1 编写地址测试类
package com.ningzhaosheng.drools.integral.client;

import com.ningzhaosheng.drools.DroolsApplicationTests;
import com.ningzhaosheng.drools.integral.entity.Address;
import com.ningzhaosheng.drools.integral.entity.AddressCheckResult;
import org.junit.jupiter.api.Test;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;

import javax.annotation.Resource;

/**
 * @author ningzhaosheng
 * @date 2024/1/20 17:16:19
 * @description 地址测试类
 * 需求
 * 模拟生成地址数据
 * 触发地址规则校验
 */
public class AddressDroolsTests extends DroolsApplicationTests {
    @Resource
    public KieBase kieBase;

    @Test
    public void droolsOrderTest() {
        KieSession kieSession = kieBase.newKieSession();
        Address address = new Address();
        address.setPostcode(generateRandom(5));
        address.setStreet("测试1");
        AddressCheckResult result = new AddressCheckResult();
        kieSession.insert(address);
        kieSession.insert(result);

        int ruleFiredCount = kieSession.fireAllRules();
        System.out.println("触发了" + ruleFiredCount + "条规则");

        if (result.isPostCodeResult()) {
            System.out.println("规则校验通过");
        }
    }

    /**
     * 生成随机数
     *
     * @param num
     * @return
     */
    public String generateRandom(int num) {
        String chars = "0123456789";
        StringBuffer number = new StringBuffer();
        for (int i = 0; i < num; i++) {
            int rand = (int) (Math.random() * 10);
            number = number.append(chars.charAt(rand));
        }
        return number.toString();
    }
}

2.2.3.2 编写订单积分规则测试类
package com.ningzhaosheng.drools.integral.client;

import com.ningzhaosheng.drools.DroolsApplicationTests;
import com.ningzhaosheng.drools.integral.entity.Order;
import com.ningzhaosheng.drools.integral.entity.User;
import org.drools.core.impl.InternalKnowledgeBase;
import org.drools.core.impl.KnowledgeBaseFactory;
import org.junit.jupiter.api.Test;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.io.ResourceFactory;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ningzhaosheng
 * @date 2024/1/20 17:23:41
 * @description 测试订单积分规则
 * 计算额外积分金额 规则如下: 订单原价金额
 * 100以下, 不加分
 * 100-500 加100分
 * 500-1000 加500分
 * 1000 以上 加1000分
 */
public class OrderDroolsTests extends DroolsApplicationTests {
    public KieSession getKieSession() {
        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newClassPathResource("rules/point-rules.drl"), ResourceType.DRL);
        //BUILD RULEBASE
        InternalKnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addPackages(kbuilder.getKnowledgePackages());
        //NEW WORKING MEMORY
        final KieSession session = kbase.newKieSession();
        return session;
    }

    /**
     * if -else
     *
     * @throws Exception
     */
    /*@Test
    public void Test() throws Exception {
        List<Order> orderList = getInitData();
        for (Order order : orderList) {
            if (order.getPrice() <= 100) {
                order.setScore(0);
                addScore(order);
            } else if (order.getPrice() > 100 && order.getPrice() <= 500) {
                order.setScore(100);
                addScore(order);
            } else if (order.getPrice() > 500 && order.getPrice() <= 1000) {
                order.setScore(500);
                addScore(order);
            } else {
                order.setScore(1000);
                addScore(order);
            }
        }
    }*/

    @Test
    public void droolsOrderTest() throws Exception {
        KieSession kieSession = getKieSession();
        List<Order> orderList = getInitData();
        for (Order order : orderList) {
            // 1-规则引擎处理逻辑
            kieSession.insert(order);
            kieSession.fireAllRules();
            // 2-执行完规则后, 执行相关的逻辑
            addScore(order);
        }
        kieSession.dispose();
    }


    private static void addScore(Order o) {
        System.out.println("用户" + o.getUser().getName() + "享受额外增加积分: " + o.getScore());
    }

    private static List<Order> getInitData() throws Exception {
        List<Order> orderList = new ArrayList<>();
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        {
            Order order = new Order();
            order.setPrice(80);
            order.setBookingDate(df.parse("2015-07-01"));
            User user = new User();
            user.setLevel(1);
            user.setName("Name1");
            order.setUser(user);
            order.setScore(111);
            orderList.add(order);
        }
        {
            Order order = new Order();
            order.setPrice(200);
            order.setBookingDate(df.parse("2015-07-02"));
            User user = new User();
            user.setLevel(2);
            user.setName("Name2");
            order.setUser(user);
            orderList.add(order);
        }
        {
            Order order = new Order();
            order.setPrice(800);
            order.setBookingDate(df.parse("2015-07-03"));
            User user = new User();
            user.setLevel(3);
            user.setName("Name3");
            order.setUser(user);
            orderList.add(order);
        }
        {
            Order order = new Order();
            order.setPrice(1500);
            order.setBookingDate(df.parse("2015-07-04"));
            User user = new User();
            user.setLevel(4);
            user.setName("Name4");
            order.setUser(user);
            orderList.add(order);
        }
        return orderList;
    }
}


2.2.3.3 编写积分规则生成及积分测试类

改类新生成规则,并插入到规则文件point-rules.drl,同时测试规则积分。

package com.ningzhaosheng.drools.integral.client;

import com.ningzhaosheng.drools.DroolsApplicationTests;
import com.ningzhaosheng.drools.integral.entity.Order;
import com.ningzhaosheng.drools.integral.entity.User;
import org.drools.core.impl.InternalKnowledgeBase;
import org.drools.core.impl.KnowledgeBaseFactory;
import org.junit.jupiter.api.Test;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.io.ResourceFactory;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ningzhaosheng
 * @date 2024/1/20 17:20:27
 * @description 测试订单积分规则,测试手动修改规则文件,并测试规则
 * 计算额外积分金额 规则如下: 订单原价金额
 * 100以下, 不加分
 * 100-500且用户等级大于3 加100分
 * 500-1000 加500分
 * 1000 以上 加1000分
 */
public class DroolsTests extends DroolsApplicationTests {
    public KieSession getKieSessionFromClassPathResource() {
        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newClassPathResource("rules/point-rules.drl"), ResourceType.DRL);
        //BUILD RULEBASE
        InternalKnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addPackages(kbuilder.getKnowledgePackages());
        //NEW WORKING MEMORY
        final KieSession session = kbase.newKieSession();
        return session;
    }

    public KieSession getKieSessionFromByte() {
        String rulsStr = "package rules\n" +
                "\n" +
                "import com.ningzhaosheng.drools.integral.entity.Order\n" +
                "import com.ningzhaosheng.drools.integral.entity.User\n" +
                "\n" +
                "rule \"zero\"\n" +
                "    no-loop true\n" +
                "    lock-on-active true\n" +
                "    salience 1\n" +
                "    when\n" +
                "        $order : Order(price <= 100)\n" +
                "    then\n" +
                "        $order.setScore(0);\n" +
                "        update($order);\n" +
                "end\n" +
                "\n" +
                "rule \"add100\"\n" +
                "    no-loop true\n" +
                "    lock-on-active true\n" +
                "    salience 1\n" +
                "    when\n" +
                "        $order : Order(price > 100 && price <= 500)\n" +
                "        and $user : User(level>2)\n" +
                "    then\n" +
                "        System.out.println($order.getUser());\n" +
                "        $order.setScore(100);\n" +
                "        update($order);\n" +
                "end\n" +
                "\n" +
                "\n" +
                "rule \"add500\"\n" +
                "    no-loop true\n" +
                "    lock-on-active true\n" +
                "    salience 1\n" +
                "    when\n" +
                "        $order : Order(price > 500 && price <= 1000)\n" +
                "    then\n" +
                "        $order.setScore(500);\n" +
                "        update($order);\n" +
                "end\n" +
                "\n" +
                "rule \"add1000\"\n" +
                "    no-loop true\n" +
                "    lock-on-active true\n" +
                "    salience 1\n" +
                "    when\n" +
                "        $order : Order(price > 1000)\n" +
                "    then\n" +
                "        $order.setScore(1000);\n" +
                "        update($order);\n" +
                "end";
        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        //kbuilder.add(ResourceFactory.newClassPathResource("rules/point-rules.drl"), ResourceType.DRL);
        kbuilder.add(ResourceFactory.newByteArrayResource(rulsStr.getBytes()), ResourceType.DRL);
        //BUILD RULEBASE
        InternalKnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addPackages(kbuilder.getKnowledgePackages());
        //NEW WORKING MEMORY
        final KieSession session = kbase.newKieSession();
        return session;
    }

    @Test
    public void droolsOrderTest() throws Exception {
        KieSession kieSession = getKieSessionFromByte();
        List<Order> orderList = getInitData();
        for (Order order : orderList) {
            // 1-规则引擎处理逻辑
            kieSession.insert(order);
            kieSession.fireAllRules();
            // 2-执行完规则后, 执行相关的逻辑
            addScore(order);
        }
        kieSession.dispose();
    }

    private static void addScore(Order o) {
        System.out.println("用户" + o.getUser().getName() + "享受额外增加积分: " + o.getScore());
    }

    private static List<Order> getInitData() throws Exception {
        List<Order> orderList = new ArrayList<>();
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        {
            Order order = new Order();
            order.setPrice(80);
            order.setBookingDate(df.parse("2015-07-01"));
            User user = new User();
            user.setLevel(1);
            user.setName("Name1");
            order.setUser(user);
            order.setScore(111);
            orderList.add(order);
        }
        {
            Order order = new Order();
            order.setPrice(200);
            order.setBookingDate(df.parse("2015-07-02"));
            User user = new User();
            user.setLevel(2);
            user.setName("Name2");
            order.setUser(user);
            orderList.add(order);
        }
        {
            Order order = new Order();
            order.setPrice(800);
            order.setBookingDate(df.parse("2015-07-03"));
            User user = new User();
            user.setLevel(3);
            user.setName("Name3");
            order.setUser(user);
            orderList.add(order);
        }
        {
            Order order = new Order();
            order.setPrice(1500);
            order.setBookingDate(df.parse("2015-07-04"));
            User user = new User();
            user.setLevel(4);
            user.setName("Name4");
            order.setUser(user);
            orderList.add(order);
        }
        return orderList;
    }
}



2.2.4 测试结果

2.2.4.1 地址测试类结果(AddressDroolsTests)

2.2.4.2 订单积分规则测试类结果(OrderDroolsTests)

2.2.4.3 积分规则生成及积分测试类结果(DroolsTests)

好了,本次分享就到这里,欢迎大家继续阅读《Drools》专栏内容,Drools专栏内容暂时分享到这里,后续会上传源码到CSDN供大家下载,如果有帮助到大家,欢迎大家点赞+关注+收藏,有疑问也欢迎大家评论留言!

  • 31
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值