前言

电商促销场景中,满减、折扣、优惠券、会员等级、限时活动等规则频繁变更,传统硬编码方式不仅需要频繁发版,还会让代码充斥大量if-else,维护成本极高。

使用 Drools 8 规则引擎,可将促销逻辑从业务代码中剥离,实现规则动态配置、实时生效、无需重启服务,大幅提升迭代效率,让复杂的优惠叠加逻辑清晰可维护。

本文基于 SpringBoot + Drools 8,从零搭建一套可直接落地的电商促销规则引擎,包含完整代码与实战经验。


二、Drools 到底是什么?

2.1 什么是 Drools?

Drools 是 开源业务规则管理系统(BRMS),基于Java开发,核心是RETE算法(高效规则匹配算法),能快速处理复杂的多条件规则推理。

简单说:Drools 就是一个“规则计算器”

  • 你输入业务数据(Fact)(如订单金额、用户等级)
  • 它加载规则文件(DRL)(如满减、折扣逻辑)
  • 自动匹配规则并执行,输出决策结果(如最终折扣、积分)

2.2 三大核心概念

1)Fact(事实)

Fact 是传入引擎的业务数据对象,就是我们的Java实体类(如Order、Customer),用于匹配规则条件。

// 订单Fact
public class Order {
    private double amount; // 订单金额
    private int vipLevel;   // VIP等级 0=普通 1=银卡 2=金卡
    private double discount;// 折扣金额
    
}
2)DRL(规则文件)

DRL 是 Drools 规则文件,后缀.drl,用 when-then 语法编写,接近自然语言,一个文件可写多条规则

// 规则:金卡用户满500减100
rule "gold_vip_full_reduce"
when
    $order : Order(amount >= 500, vipLevel == 2)
then
    $order.setDiscount($order.getDiscount() + 100);
end
3)Kie 容器

Kie 是 Drools 的核心容器,负责加载规则文件、创建会话、执行规则,Spring Boot 集成后可自动管理。


三、Spring Boot 集成 Drools 8 实战

3.1 场景需求

我们要实现一套叠加式促销规则,需求如下:

  1. 基础满减:订单金额≥300减50,≥500减100,≥1000减200
  2. VIP折扣:银卡(1级)9折,金卡(2级)8折(满减后金额计算折扣)
  3. 新用户优惠:用户注册≤7天,额外减30(可叠加所有规则)

3.2 步骤1:引入依赖

<!-- Drools 核心依赖 -->
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-core</artifactId>
    <version>8.41.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>8.41.0.Final</version>
</dependency>

3.3 步骤2:编写 Fact 实体类

Order.java(订单)
public class Order {
    // 订单金额(原始)
    private double amount;
    // VIP等级:0=普通 1=银卡 2=金卡
    private int vipLevel;
    // 是否新用户(注册≤7天)
    private boolean isNewUser;
    // 折扣金额(最终计算结果)
    private double discount;
    // 最终支付金额
    private double finalAmount;

    // 无参构造,必须,Drools 反射用
    public Order() {}

 
    public double getAmount() { return amount; }
    public void setAmount(double amount) { this.amount = amount; }
    public int getVipLevel() { return vipLevel; }
    public void setVipLevel(int vipLevel) { this.vipLevel = vipLevel; }
    public boolean isNewUser() { return isNewUser; }
    public void setNewUser(boolean newUser) { isNewUser = newUser; }
    public double getDiscount() { return discount; }
    public void setDiscount(double discount) { this.discount = discount; }
    public double getFinalAmount() { return finalAmount; }
    public void setFinalAmount(double finalAmount) { this.finalAmount = finalAmount; }
}

3.4 步骤3:编写促销规则文件(promotion.drl)

src/main/resources/rules 下创建 promotion.drl所有规则写在一个文件,清晰易维护

// 包名(必须和Java类一致)
package com.ecommerce.promotion

// 导入Fact类
import com.ecommerce.model.Order

// 全局变量(可选,用于返回结果)
global com.ecommerce.model.Order orderResult

// 规则1:基础满减 - 满300减50
rule "base_full_reduce_300"
salience 10 // 优先级:数字越大越先执行
when
    $order : Order(amount >= 300, amount < 500)
then
    $order.setDiscount(50);
    System.out.println("执行规则:满300减50");
end

// 规则2:基础满减 - 满500减100
rule "base_full_reduce_500"
salience 10
when
    $order : Order(amount >= 500, amount < 1000)
then
    $order.setDiscount(100);
    System.out.println("执行规则:满500减100");
end

// 规则3:基础满减 - 满1000减200
rule "base_full_reduce_1000"
salience 10
when
    $order : Order(amount >= 1000)
then
    $order.setDiscount(200);
    System.out.println("执行规则:满1000减200");
end

// 规则4:VIP银卡9折(满减后)
rule "vip_silver_discount"
salience 9
when
    $order : Order(vipLevel == 1)
then
    double afterReduce = $order.getAmount() - $order.getDiscount();
    double vipDiscount = afterReduce * 0.1; // 9折=减10%
    $order.setDiscount($order.getDiscount() + vipDiscount);
    System.out.println("执行规则:银卡9折,额外减" + vipDiscount);
end

// 规则5:VIP金卡8折(满减后)
rule "vip_gold_discount"
salience 9
when
    $order : Order(vipLevel == 2)
then
    double afterReduce = $order.getAmount() - $order.getDiscount();
    double vipDiscount = afterReduce * 0.2; // 8折=减20%
    $order.setDiscount($order.getDiscount() + vipDiscount);
    System.out.println("执行规则:金卡8折,额外减" + vipDiscount);
end

// 规则6:新用户额外减30
rule "new_user_discount"
salience 9
when
    $order : Order(isNewUser == true)
then
    $order.setDiscount($order.getDiscount() + 30);
    System.out.println("执行规则:新用户额外减30");
end

// 规则7:计算最终金额
rule "calculate_final_amount"
salience 8 // 最后执行
when
    $order : Order()
then
    double finalAmt = $order.getAmount() - $order.getDiscount();
    $order.setFinalAmount(finalAmt > 0 ? finalAmt : 0);
    System.out.println("执行规则:最终金额计算完成");
end

关键说明

  • salience:规则优先级,数字越大越先执行,确保满减→VIP折扣→新用户→最终计算的顺序。
  • when:条件部分,匹配Fact的属性。
  • then:动作部分,满足条件后执行的逻辑。

3.5 步骤4:Spring Boot 配置 Drools

@Configuration
public class DroolsConfig {
    // 配置Kie容器,加载规则文件
    @Bean
    public KieContainer kieContainer() {
        // 加载resources/rules下的所有drl文件
        KieServices kieServices = KieServices.Factory.get();
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        // 加载规则文件
        kieFileSystem.write(ResourceFactory.newClassPathResource("rules/promotion.drl"));
        // 构建Kie容器
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        kieBuilder.buildAll();
        return kieServices.newKieContainer(kieBuilder.getKieModule().getReleaseId());
    }
}

3.6 步骤5:编写促销服务(调用规则引擎)

@Service
public class PromotionService {
    @Autowired
    private KieContainer kieContainer;

    // 计算促销折扣
    public Order calculatePromotion(Order order) {
    		// 每次请求创建一个新的 session,线程安全
        KieSession kieSession = kieContainer.newKieSession();

         try {
            // 1. 把订单对象插入到规则引擎
            kieSession.insert(order);

            // 2. 执行所有规则
            kieSession.fireAllRules();

            // 3. 规则执行完,order 本身已经被修改了,直接返回
            return order;
        } finally {
            // 4. 关闭 session,避免内存泄漏
            kieSession.dispose();
        }
    }
}

3.7 步骤6:测试

    @Test
    void calculatePromotion() {
        Order order = new Order();
        order.setDiscount(0L);
        order.setAmount(1000);
        order.setNewUser(true);
        order.setVipLevel(2);

        Order result = promotionService.calculatePromotion(order);
        log.info("计算后的最终价格是:" + result.getFinalAmount());
    }

3.8 测试效果

在这里插入图片描述

完美实现规则叠加:满减200 + 金卡8折160 + 新用户30 = 总折扣390,最终支付610元。


四、Drools 核心优势

1)规则与代码解耦

  • 规则写在独立的 .drl 文件中,修改规则无需改Java代码
  • 运营可直接修改规则文件,无需开发介入,沟通成本降为0。

2)动态生效,无需重启

  • 配合 Kie Workbench(Drools可视化控制台),可在线编辑规则、实时发布生效,无需重启服务。
  • 大促期间紧急改规则,分钟级上线,告别部署等待。

3)复杂规则易维护

  • when-then 语法,接近自然语言,可读性极强。
  • 支持优先级、条件嵌套、规则叠加,复杂促销逻辑也能清晰拆分。

4)高性能规则匹配

  • 基于 RETE算法多条件匹配效率极高,万级规则也能快速执行。
  • 适合高并发大促场景,支撑每秒千级订单计算。

五、生产优化

1)KieSession 池化,避免频繁创建销毁

  • 问题:每次请求创建/销毁KieSession,性能低、易内存泄漏。
  • 优化:使用 Apache Commons Pool2 实现KieSession池化,复用会话,提升吞吐量。

2)规则文件热更新

  • 问题:修改drl文件后,需重启服务生效。
  • 优化:基于 KieScanner 实现规则文件热更新,监听文件变化自动加载,秒级生效

3)规则优先级与冲突解决

  • salience 控制执行顺序,避免规则冲突。
  • 复杂规则可拆分多个drl文件,按模块管理(满减规则、VIP规则、新用户规则)。

4)监控与日志

  • 记录规则执行日志(哪个规则执行、执行结果),便于问题排查。
  • 监控 KieSession 池状态、规则执行耗时,及时发现性能瓶颈。

六、总结

✅ 推荐使用场景

  • 电商促销:满减、折扣、优惠券、积分叠加(本文案例)。
  • 金融风控:反欺诈、黑名单、交易限额、风险评级。
  • 审批流程:多级审批、条件分支、自动审核。
  • 保险定价:保费计算、保额规则、理赔条件。

❌ 不推荐使用场景

  • 规则简单、变更极少(如固定的状态判断),硬编码更轻量。
  • 性能要求极高(每秒万级以上),且规则简单,可考虑自研简易规则引擎。



← 上一篇 基于 RocketMQ 延时消息的实现订单超时自动关闭场景
记得点赞、关注、收藏哦!
下一篇 别再死磕面试题了!Java 面试拼的从来不是背诵 →
Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐