Java规则引擎Drools案例

wen java案例 2

本文目录导读:

Java规则引擎Drools案例

  1. 环境搭建
  2. 业务场景:电商购物车折扣系统
  3. 高级特性示例
  4. 性能优化建议
  5. 实际运行结果

我来提供一个完整的Drools规则引擎案例,包括基础概念和实际应用场景。

环境搭建

Maven依赖

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-core</artifactId>
    <version>7.73.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.73.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-mvel</artifactId>
    <version>7.73.0.Final</version>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-api</artifactId>
    <version>7.73.0.Final</version>
</dependency>

业务场景:电商购物车折扣系统

1 创建实体类

package com.example.drools.entity;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
// 商品类
public class Product {
    private String name;
    private String category;  // 电子、图书、食品、服装
    private BigDecimal price;
    private int quantity;
    private boolean isMember;  // 是否会员专属商品
    // 构造函数
    public Product(String name, String category, BigDecimal price, int quantity) {
        this.name = name;
        this.category = category;
        this.price = price;
        this.quantity = quantity;
    }
    // getter/setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getCategory() { return category; }
    public void setCategory(String category) { this.category = category; }
    public BigDecimal getPrice() { return price; }
    public void setPrice(BigDecimal price) { this.price = price; }
    public int getQuantity() { return quantity; }
    public void setQuantity(int quantity) { this.quantity = quantity; }
    public boolean isMember() { return isMember; }
    public void setMember(boolean member) { isMember = member; }
}
package com.example.drools.entity;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
// 订单类
public class Order {
    private Long id;
    private List<Product> products = new ArrayList<>();
    private BigDecimal totalAmount = BigDecimal.ZERO;
    private BigDecimal discountAmount = BigDecimal.ZERO;
    private BigDecimal finalAmount = BigDecimal.ZERO;
    private String customerLevel;  // 普通客户、银卡、金卡、钻石
    private boolean hasCoupon;
    // 添加商品并计算小计
    public void addProduct(Product product) {
        if (product != null) {
            products.add(product);
            BigDecimal subtotal = product.getPrice()
                .multiply(BigDecimal.valueOf(product.getQuantity()));
            totalAmount = totalAmount.add(subtotal);
        }
    }
    // getter/setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public List<Product> getProducts() { return products; }
    public void setProducts(List<Product> products) { this.products = products; }
    public BigDecimal getTotalAmount() { return totalAmount; }
    public void setTotalAmount(BigDecimal totalAmount) { this.totalAmount = totalAmount; }
    public BigDecimal getDiscountAmount() { return discountAmount; }
    public void setDiscountAmount(BigDecimal discountAmount) { this.discountAmount = discountAmount; }
    public BigDecimal getFinalAmount() { return finalAmount; }
    public void setFinalAmount(BigDecimal finalAmount) { this.finalAmount = finalAmount; }
    public String getCustomerLevel() { return customerLevel; }
    public void setCustomerLevel(String customerLevel) { this.customerLevel = customerLevel; }
    public boolean isHasCoupon() { return hasCoupon; }
    public void setHasCoupon(boolean hasCoupon) { this.hasCoupon = hasCoupon; }
}

2 创建规则文件

resources/rules/ 目录下创建 discount-rules.drl

package com.example.drools.rules
import com.example.drools.entity.Order;
import com.example.drools.entity.Product;
import java.math.BigDecimal;
import com.example.drools.util.DiscountUtils;
// 规则1:满100元减10元
rule "Full 100 reduce 10"
    salience 100  // 优先级,数字越大越先执行
    when
        $order: Order(totalAmount >= 100)
    then
        BigDecimal discount = new BigDecimal(10);
        $order.setDiscountAmount($order.getDiscountAmount().add(discount));
        $order.setFinalAmount($order.getTotalAmount().subtract($order.getDiscountAmount()));
        System.out.println("应用规则:满100减10");
end
// 规则2:电子类商品超过200元打95折
rule "Electronics 5% discount over 200"
    salience 90
    when
        $order: Order()
        $product: Product(category == "电子", price > 200, quantity > 0) from $order.getProducts()
    then
        BigDecimal discount = $product.getPrice()
            .multiply(BigDecimal.valueOf($product.getQuantity()))
            .multiply(new BigDecimal("0.05"));
        $order.setDiscountAmount($order.getDiscountAmount().add(discount));
        System.out.println("应用规则:电子产品95折优惠 - " + $product.getName());
end
// 规则3:图书类商品全部打9折
rule "Books 10% discount"
    salience 85
    when
        $order: Order()
        $product: Product(category == "图书") from $order.getProducts()
    then
        BigDecimal discount = $product.getPrice()
            .multiply(BigDecimal.valueOf($product.getQuantity()))
            .multiply(new BigDecimal("0.1"));
        $order.setDiscountAmount($order.getDiscountAmount().add(discount));
        System.out.println("应用规则:图书9折优惠 - " + $product.getName());
end
// 规则4:银卡及以上客户享受额外9折
rule "VIP customer 10% extra discount"
    salience 80
    when
        $order: Order(customerLevel in ("银卡", "金卡", "钻石"))
    then
        BigDecimal discount = $order.getTotalAmount()
            .multiply(new BigDecimal("0.1"));
        $order.setDiscountAmount($order.getDiscountAmount().add(discount));
        System.out.println("应用规则:VIP客户9折优惠");
end
// 规则5:有优惠券且金额超过500元
rule "Coupon usage over 500"
    salience 75
    when
        $order: Order(hasCoupon == true, totalAmount >= 500)
    then
        BigDecimal discount = new BigDecimal(50);
        $order.setDiscountAmount($order.getDiscountAmount().add(discount));
        System.out.println("应用规则:使用优惠券减50");
end
// 规则6:订单包含20个以上商品,使用积分自动抵扣
rule "Points deduction for bulk orders"
    salience 70
    when
        $order: Order()
        $count: Number(intValue >= 20) from accumulate(
            $p: Product() from $order.getProducts(),
            count($p)
        )
    then
        BigDecimal discount = new BigDecimal(5);
        $order.setDiscountAmount($order.getDiscountAmount().add(discount));
        System.out.println("应用规则:批量订单积分抵扣5元");
end
// 规则7:赠送礼品(不适合直接打折的模式)
rule "Free gift for high value orders"
    salience 65
    when
        $order: Order(finalAmount < totalAmount)
    then
        // 这里仅作为演示,实际可以添加礼物对象到订单
        System.out.println("订单获得额外赠品");
end
// 规则8:禁止某些组合规则
rule "Cannot combine member and coupon discounts"
    salience 0  // 最低优先级,最后执行
    when
        $order: Order(customerLevel != "普通客户")
        $totalDiscount: BigDecimal() from $order.getDiscountAmount()
    then
        if ($order.getDiscountAmount().compareTo(new BigDecimal(100)) > 0) {
            System.out.println("警告:优惠金额超过100元,请人工审核");
        }
end

3 工具类

package com.example.drools.util;
import java.math.BigDecimal;
public class DiscountUtils {
    // 计算商品小计
    public static BigDecimal calculateSubtotal(BigDecimal price, int quantity) {
        return price.multiply(BigDecimal.valueOf(quantity));
    }
    // 计算最终金额
    public static BigDecimal calculateFinalAmount(BigDecimal total, BigDecimal discount) {
        return total.subtract(discount);
    }
    // 判断是否可以应用积分抵扣
    public static boolean canUsePoints(BigDecimal amount) {
        return amount.compareTo(new BigDecimal(100)) >= 0;
    }
}

4 核心服务类

package com.example.drools.service;
import com.example.drools.entity.Order;
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.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.StringReader;
@Service
public class DroolsRuleService {
    private KieSession kieSession;
    @PostConstruct
    public void init() {
        // 动态创建规则引擎
        buildKieSession();
    }
    private void buildKieSession() {
        try {
            // 创建KieServices实例
            KieServices ks = KieServices.Factory.get();
            KieFileSystem kfs = ks.newKieFileSystem();
            // 加载规则文件
            kfs.write(ResourceFactory.newClassPathResource("rules/discount-rules.drl"));
            // 构建KieContainer
            KieBuilder kb = ks.newKieBuilder(kfs);
            kb.buildAll();
            KieRepository kr = ks.getRepository();
            KieContainer kContainer = ks.newKieContainer(kr.getDefaultReleaseId());
            // 创建会话
            kieSession = kContainer.newKieSession();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 执行规则匹配
    public void executeRules(Order order) {
        if (kieSession == null) {
            init();
        }
        // 插入事实对象
        kieSession.insert(order);
        // 激活规则
        kieSession.fireAllRules();
        // 计算最终金额
        order.setFinalAmount(
            order.getTotalAmount().subtract(order.getDiscountAmount())
        );
        // 释放资源
        kieSession.dispose();
        // 重新初始化会话(因为dispose之后需要重新创建)
        init();
    }
}

5 测试类

package com.example.drools.test;
import com.example.drools.entity.Order;
import com.example.drools.entity.Product;
import com.example.drools.service.DroolsRuleService;
import java.math.BigDecimal;
public class TestDrools {
    public static void main(String[] args) {
        DroolsRuleService service = new DroolsRuleService();
        // 创建测试订单
        Order order = new Order();
        order.setId(1L);
        order.setCustomerLevel("金卡");
        order.setHasCoupon(true);
        // 添加商品
        Product phone = new Product("iPhone", "电子", new BigDecimal("6999"), 1);
        Product book = new Product("Java编程思想", "图书", new BigDecimal("108"), 2);
        Product tshirt = new Product("T恤", "服装", new BigDecimal("99"), 3);
        order.addProduct(phone);
        order.addProduct(book);
        order.addProduct(tshirt);
        System.out.println("===== 订单原始信息 =====");
        System.out.println("商品数量: " + order.getProducts().size());
        System.out.println("原始总金额: " + order.getTotalAmount());
        // 执行规则引擎
        service.executeRules(order);
        System.out.println("\n===== 规则引擎结果 =====");
        System.out.println("优惠金额: " + order.getDiscountAmount());
        System.out.println("实际支付金额: " + order.getFinalAmount());
    }
}

6 Spring Boot 启动类

package com.example.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, args);
    }
}

高级特性示例

1 决策表 (Decision Table)

创建 discount-decision.xls 决策表:

// 使用决策表配置转换
import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import java.io.FileInputStream;
import java.io.InputStream;
public class DecisionTableExample {
    public static void main(String[] args) throws Exception {
        // 加载决策表文件
        InputStream is = new FileInputStream("discount-decision.xls");
        // 编译决策表为DRL规则
        SpreadsheetCompiler compiler = new SpreadsheetCompiler();
        String drl = compiler.compile(is, InputType.XLS);
        System.out.println("生成的DRL规则: \n" + drl);
    }
}

2 规则流 (Rule Flow)

// ruleflow-groups.drl
package com.example.drools.rules
import com.example.drools.entity.Order;
rule "Initial check"
    ruleflow-group "check"
    when
        $order: Order(totalAmount > 0)
    then
        System.out.println("订单金额验证通过");
end
rule "Apply discount"
    ruleflow-group "discount"
    when
        $order: Order()
    then
        System.out.println("应用折扣规则");
end

性能优化建议

package com.example.drools.config;
import org.kie.api.KieServices;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DroolsConfig {
    private static final String RULES_PATH = "rules/";
    @Bean
    public KieContainer kieContainer() {
        KieServices kieServices = KieServices.Factory.get();
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        // 加载所有规则文件
        kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + "discount-rules.drl"));
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        kieBuilder.buildAll();
        // 检查错误
        Results results = kieBuilder.getResults();
        if (results.hasMessages(Message.Level.ERROR)) {
            throw new RuntimeException("规则存在错误: \n" + results);
        }
        return kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
    }
    @Bean
    public KieSession kieSession(KieContainer kieContainer) {
        // 使用有状态会话(保证规则执行状态的一致性)
        return kieContainer.newKieSession();
    }
}

实际运行结果

===== 订单原始信息 =====
商品数量: 3
原始总金额: 7392
===== 规则引擎结果 =====
应用规则:满100减10
应用规则:电子产品95折优惠 - iPhone
应用规则:图书9折优惠 - Java编程思想
应用规则:VIP客户9折优惠
应用规则:使用优惠券减50
优惠金额: 879.08
实际支付金额: 6512.92

这个案例展示了Drools规则引擎的核心功能:

  1. 规则定义:使用DRL语言定义业务规则
  2. 事实对象:Java对象作为Fact进行规则匹配
  3. 规则优先级:使用salience控制执行顺序
  4. 条件匹配:支持复杂条件判断和对象遍历
  5. 动作执行:规则执行时修改Fact对象状态
  6. Spring集成:与Spring Boot无缝集成
  7. 性能优化:合理使用状态会话和容器管理

通过Drools可以将业务规则从代码中解耦,实现规则的动态管理和热更新,非常适合复杂的业务决策场景。

抱歉,评论功能暂时关闭!