Drools基础语法详解

60 阅读8分钟

Drools基础语法完全指南:从零到精通

前言

Drools规则引擎使用DRL(Drools Rule Language)语言编写业务规则。掌握DRL语法是使用Drools的基础。本文将系统介绍Drools的基础语法,包括规则结构、条件表达式、动作语句等,并结合生产实例进行详细讲解。

一、DRL文件基础结构

1.1 完整DRL文件结构

+--------------------------------------------------+
|                DRL File Structure                 |
+--------------------------------------------------+
|                                                  |
|  1. package 声明                                  |
|     package com.example.rules                    |
|                                                  |
|  2. import 导入                                   |
|     import com.example.model.Order               |
|     import java.math.BigDecimal                  |
|                                                  |
|  3. global 全局变量                               |
|     global java.util.List resultList             |
|                                                  |
|  4. function 函数定义                             |
|     function double calculateTax(double amount)  |
|                                                  |
|  5. query 查询定义                                |
|     query "findOrders"                           |
|                                                  |
|  6. rule 规则定义                                 |
|     rule "Rule Name"                             |
|         when ... then ... end                    |
|                                                  |
+--------------------------------------------------+

1.2 基本DRL示例

// src/main/resources/rules/basic-syntax.drl
package rules

import com.example.model.Order
import com.example.model.Customer
import java.math.BigDecimal
import java.util.List

// 全局变量声明
global List messages

// 函数定义
function BigDecimal calculateDiscount(BigDecimal amount, double rate) {
    return amount.multiply(BigDecimal.valueOf(rate));
}

// 规则定义
rule "Basic Rule Example"
    when
        $order: Order(totalAmount > 100)
    then
        System.out.println("订单金额大于100: " + $order.getOrderId());
end

二、规则(Rule)基础语法

2.1 规则完整结构

rule "规则名称"
    // 规则属性
    salience 100
    agenda-group "group1"
    no-loop true
    date-effective "2024-01-01"
    date-expires "2024-12-31"
    enabled true

    when
        // LHS: 条件部分 (Left Hand Side)
        // 模式匹配
    then
        // RHS: 动作部分 (Right Hand Side)
        // 执行动作
end

2.2 Java实体类

package com.example.model;

import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;

@Data
public class Order {
    private Long orderId;
    private String customerName;
    private BigDecimal totalAmount;
    private String status;
    private String priority;
    private Integer itemCount;
    private LocalDateTime orderTime;
    private boolean isVipOrder;
    private String category;
}

@Data
public class Customer {
    private Long customerId;
    private String name;
    private String level; // VIP, GOLD, SILVER, NORMAL
    private Integer age;
    private String city;
    private BigDecimal balance;
    private boolean active;
}

@Data
public class Product {
    private Long productId;
    private String name;
    private String category;
    private BigDecimal price;
    private Integer stock;
    private boolean onSale;
}

三、条件部分(When)语法详解

3.1 基本模式匹配

package rules

import com.example.model.Order
import java.math.BigDecimal

// 1. 简单匹配 - 匹配类型
rule "Simple Pattern Match"
    when
        Order()  // 只要有Order对象就匹配
    then
        System.out.println("找到一个订单");
end

// 2. 带约束的匹配
rule "Pattern with Constraints"
    when
        Order(totalAmount > 1000)
    then
        System.out.println("找到大额订单");
end

// 3. 多个约束(AND关系)
rule "Multiple Constraints"
    when
        Order(
            totalAmount > 1000,
            status == "PENDING",
            itemCount >= 5
        )
    then
        System.out.println("待处理的大额多件订单");
end

// 4. 变量绑定
rule "Variable Binding"
    when
        $order: Order(totalAmount > 1000)
    then
        System.out.println("订单ID: " + $order.getOrderId());
        System.out.println("订单金额: " + $order.getTotalAmount());
end

// 5. 字段绑定
rule "Field Binding"
    when
        Order($amount: totalAmount, $amount > 1000)
    then
        System.out.println("绑定的金额: " + $amount);
end

3.2 比较运算符

package rules

import com.example.model.Order
import com.example.model.Customer
import java.math.BigDecimal

// 1. 相等和不等
rule "Equality Operators"
    when
        Order(status == "CONFIRMED")        // 等于
        Customer(level != "VIP")            // 不等于
    then
        System.out.println("已确认的非VIP订单");
end

// 2. 大小比较
rule "Comparison Operators"
    when
        Order(
            totalAmount > 100,              // 大于
            totalAmount >= 100,             // 大于等于
            itemCount < 10,                 // 小于
            itemCount <= 10                 // 小于等于
        )
    then
        System.out.println("比较运算符示例");
end

// 3. 包含运算符
rule "Contains Operator"
    when
        Order(status in ("PENDING", "PROCESSING", "CONFIRMED"))
    then
        System.out.println("订单处于活动状态");
end

// 4. 不包含运算符
rule "Not Contains Operator"
    when
        Order(status not in ("CANCELLED", "REFUNDED"))
    then
        System.out.println("订单未取消或退款");
end

// 5. 匹配运算符(正则表达式)
rule "Matches Operator"
    when
        Order(customerName matches "张.*")   // 姓张的客户
    then
        System.out.println("张姓客户订单");
end

// 6. 不匹配运算符
rule "Not Matches Operator"
    when
        Order(customerName not matches ".*test.*")
    then
        System.out.println("非测试订单");
end

3.3 复合条件

package rules

import com.example.model.Order
import com.example.model.Customer

// 1. 多个对象匹配(隐式AND)
rule "Multiple Objects AND"
    when
        $customer: Customer(level == "VIP")
        $order: Order(customerName == $customer.name, totalAmount > 1000)
    then
        System.out.println("VIP客户的大额订单");
end

// 2. OR条件
rule "OR Condition"
    when
        Order(status == "URGENT") or Order(totalAmount > 5000)
    then
        System.out.println("紧急订单或大额订单");
end

// 3. NOT条件(不存在)
rule "NOT Condition"
    when
        $order: Order(status == "PENDING")
        not Customer(name == $order.customerName, level == "VIP")
    then
        System.out.println("非VIP客户的待处理订单");
end

// 4. EXISTS条件(至少存在一个)
rule "EXISTS Condition"
    when
        $customer: Customer()
        exists Order(customerName == $customer.name, status == "PENDING")
    then
        System.out.println("客户有待处理订单");
end

// 5. FORALL条件(所有都满足)
rule "FORALL Condition"
    when
        forall(
            $order: Order(customerName == "张三")
            Order(this == $order, status == "CONFIRMED")
        )
    then
        System.out.println("张三的所有订单都已确认");
end

3.4 集合操作

package rules

import com.example.model.Order
import com.example.model.Product
import java.util.List

// 1. from子句 - 从集合中提取
rule "From Collection"
    when
        $order: Order()
        $product: Product(price > 100) from $order.getProducts()
    then
        System.out.println("订单包含高价商品: " + $product.getName());
end

// 2. collect收集
rule "Collect Elements"
    when
        $customer: Customer()
        $orders: List() from collect(
            Order(customerName == $customer.name)
        )
    then
        System.out.println("客户订单数: " + $orders.size());
end

// 3. accumulate聚合
rule "Accumulate Sum"
    when
        $customer: Customer()
        $total: Number() from accumulate(
            Order($amount: totalAmount, customerName == $customer.name),
            sum($amount)
        )
    then
        System.out.println("客户总消费: " + $total);
end

// 4. accumulate其他函数
rule "Accumulate Functions"
    when
        // average - 平均值
        $avg: Number() from accumulate(
            Order($amount: totalAmount),
            average($amount)
        )

        // min - 最小值
        $min: Number() from accumulate(
            Order($amount: totalAmount),
            min($amount)
        )

        // max - 最大值
        $max: Number() from accumulate(
            Order($amount: totalAmount),
            max($amount)
        )

        // count - 计数
        $count: Number() from accumulate(
            Order(),
            count(1)
        )
    then
        System.out.println("统计结果");
end

四、动作部分(Then)语法详解

4.1 基本动作语句

package rules

import com.example.model.Order
import com.example.model.Customer
import java.math.BigDecimal

global java.util.List results
global org.slf4j.Logger logger

// 1. 修改对象属性
rule "Modify Object"
    when
        $order: Order(status == "PENDING")
    then
        $order.setStatus("PROCESSING");
        $order.setPriority("HIGH");
        System.out.println("订单状态已更新");
end

// 2. update通知更新(触发重新匹配)
rule "Update Fact"
    when
        $order: Order(status == "PENDING", totalAmount > 1000)
    then
        $order.setStatus("CONFIRMED");
        update($order);  // 通知规则引擎对象已更新
        System.out.println("订单已确认并更新");
end

// 3. insert插入新对象
rule "Insert New Fact"
    when
        $order: Order(totalAmount > 5000, isVipOrder == false)
    then
        $order.setVipOrder(true);
        update($order);

        Customer vipCustomer = new Customer();
        vipCustomer.setName($order.getCustomerName());
        vipCustomer.setLevel("VIP");
        insert(vipCustomer);  // 插入新对象到工作内存

        System.out.println("创建VIP客户");
end

// 4. retract删除对象
rule "Retract Fact"
    when
        $order: Order(status == "CANCELLED")
    then
        retract($order);  // 从工作内存移除
        System.out.println("已删除取消的订单");
end

// 5. modify原子性修改
rule "Modify Statement"
    when
        $order: Order(status == "PENDING")
    then
        modify($order) {
            setStatus("CONFIRMED"),
            setPriority("NORMAL"),
            setOrderTime(java.time.LocalDateTime.now())
        }
        System.out.println("订单已原子性修改");
end

4.2 调用Java方法

package rules

import com.example.model.Order
import com.example.service.OrderService
import com.example.service.NotificationService
import java.math.BigDecimal

global OrderService orderService
global NotificationService notificationService

// 调用服务方法
rule "Call Service Method"
    when
        $order: Order(totalAmount > 1000, status == "PENDING")
    then
        // 调用业务服务
        orderService.processOrder($order);

        // 发送通知
        notificationService.sendEmail(
            $order.getCustomerName(),
            "订单确认",
            "您的订单已确认,金额: " + $order.getTotalAmount()
        );

        $order.setStatus("CONFIRMED");
        update($order);
end

4.3 使用全局变量

package rules

import com.example.model.Order
import java.util.List

global List<String> messages
global List<Order> processedOrders

rule "Use Global Variables"
    when
        $order: Order(totalAmount > 1000)
    then
        // 添加消息到全局列表
        messages.add("处理订单: " + $order.getOrderId());

        // 记录已处理的订单
        processedOrders.add($order);

        System.out.println("使用全局变量记录");
end

五、规则属性详解

5.1 常用规则属性

package rules

import com.example.model.Order
import java.math.BigDecimal

// 1. salience: 优先级(数值越大越先执行)
rule "High Priority Rule"
    salience 100
    when
        $order: Order(priority == "URGENT")
    then
        System.out.println("紧急订单优先处理");
end

rule "Normal Priority Rule"
    salience 50
    when
        $order: Order(priority == "NORMAL")
    then
        System.out.println("普通订单处理");
end

// 2. no-loop: 防止死循环(防止规则触发自己)
rule "No Loop Rule"
    no-loop true
    when
        $order: Order(status == "PENDING")
    then
        $order.setStatus("PROCESSING");
        update($order);  // 不会再次触发此规则
end

// 3. lock-on-active: 同一激活组只执行一次
rule "Lock On Active Rule"
    lock-on-active true
    agenda-group "processing"
    when
        $order: Order()
    then
        System.out.println("同一激活组只执行一次");
end

// 4. agenda-group: 规则分组
rule "Group 1 Rule"
    agenda-group "group1"
    when
        Order()
    then
        System.out.println("分组1的规则");
end

rule "Group 2 Rule"
    agenda-group "group2"
    when
        Order()
    then
        System.out.println("分组2的规则");
end

// 5. activation-group: 互斥执行组(只执行一个)
rule "Discount 20%"
    activation-group "discount"
    salience 100
    when
        Order(totalAmount > 1000)
    then
        System.out.println("应用20%折扣");
end

rule "Discount 10%"
    activation-group "discount"
    salience 50
    when
        Order(totalAmount > 500)
    then
        System.out.println("应用10%折扣");
end

// 6. duration: 延迟执行(毫秒)
rule "Delayed Rule"
    duration 5000
    when
        $order: Order(status == "PENDING")
    then
        System.out.println("5秒后执行");
end

// 7. date-effective: 生效日期
rule "Effective Date Rule"
    date-effective "2024-01-01"
    when
        Order()
    then
        System.out.println("2024年1月1日起生效");
end

// 8. date-expires: 失效日期
rule "Expires Date Rule"
    date-expires "2024-12-31"
    when
        Order()
    then
        System.out.println("2024年12月31日前有效");
end

// 9. enabled: 启用/禁用
rule "Disabled Rule"
    enabled false
    when
        Order()
    then
        System.out.println("此规则已禁用");
end

// 10. dialect: 方言(java或mvel)
rule "Java Dialect Rule"
    dialect "java"
    when
        $order: Order()
    then
        System.out.println("使用Java语法");
end

rule "MVEL Dialect Rule"
    dialect "mvel"
    when
        $order: Order()
    then
        System.out.println("使用MVEL语法");
end

六、函数(Function)定义

package rules

import com.example.model.Order
import java.math.BigDecimal

// 定义函数
function BigDecimal calculateDiscount(BigDecimal amount, double rate) {
    return amount.multiply(BigDecimal.valueOf(rate));
}

function boolean isWeekend() {
    java.time.DayOfWeek day = java.time.LocalDate.now().getDayOfWeek();
    return day == java.time.DayOfWeek.SATURDAY ||
           day == java.time.DayOfWeek.SUNDAY;
}

function String formatMoney(BigDecimal amount) {
    return "¥" + amount.toString();
}

function int calculatePoints(BigDecimal amount) {
    return amount.intValue() / 10;  // 每10元1积分
}

// 使用函数
rule "Use Functions"
    when
        $order: Order(totalAmount > 100)
    then
        BigDecimal discount = calculateDiscount($order.getTotalAmount(), 0.1);
        System.out.println("折扣金额: " + formatMoney(discount));

        if (isWeekend()) {
            System.out.println("周末额外优惠");
        }

        int points = calculatePoints($order.getTotalAmount());
        System.out.println("获得积分: " + points);
end

七、查询(Query)定义

package rules

import com.example.model.Order
import com.example.model.Customer
import java.math.BigDecimal

// 1. 简单查询
query "findAllOrders"
    order: Order()
end

// 2. 带参数的查询
query "findOrdersByStatus"(String status)
    order: Order(status == status)
end

// 3. 带多个参数的查询
query "findOrdersByCustomerAndAmount"(String customerName, BigDecimal minAmount)
    order: Order(
        customerName == customerName,
        totalAmount >= minAmount
    )
end

// 4. 复杂查询
query "findVipCustomerOrders"
    $customer: Customer(level == "VIP")
    $order: Order(customerName == $customer.name)
end

// 5. 聚合查询
query "findHighValueOrders"
    accumulate(
        $order: Order($amount: totalAmount),
        $total: sum($amount),
        $avg: average($amount);
        $total > 10000
    )
end

7.1 Java中使用Query

package com.example.service;

import com.example.model.Order;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.QueryResults;
import org.kie.api.runtime.rule.QueryResultsRow;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Service
public class QueryService {

    private final KieSession kieSession;

    public QueryService(KieSession kieSession) {
        this.kieSession = kieSession;
    }

    /**
     * 查询所有订单
     */
    public List<Order> findAllOrders() {
        List<Order> orders = new ArrayList<>();
        QueryResults results = kieSession.getQueryResults("findAllOrders");

        for (QueryResultsRow row : results) {
            Order order = (Order) row.get("order");
            orders.add(order);
        }

        return orders;
    }

    /**
     * 按状态查询订单
     */
    public List<Order> findOrdersByStatus(String status) {
        List<Order> orders = new ArrayList<>();
        QueryResults results = kieSession.getQueryResults(
            "findOrdersByStatus",
            status
        );

        for (QueryResultsRow row : results) {
            Order order = (Order) row.get("order");
            orders.add(order);
        }

        return orders;
    }

    /**
     * 按客户和金额查询
     */
    public List<Order> findOrdersByCustomerAndAmount(
            String customerName,
            BigDecimal minAmount) {
        List<Order> orders = new ArrayList<>();
        QueryResults results = kieSession.getQueryResults(
            "findOrdersByCustomerAndAmount",
            customerName,
            minAmount
        );

        for (QueryResultsRow row : results) {
            Order order = (Order) row.get("order");
            orders.add(order);
        }

        return orders;
    }
}

八、条件元素(Conditional Elements)

8.1 eval表达式

package rules

import com.example.model.Order
import java.time.LocalDateTime
import java.time.LocalTime

// 使用eval执行任意Java表达式
rule "Eval Expression"
    when
        $order: Order()
        eval(LocalTime.now().getHour() >= 9 && LocalTime.now().getHour() < 17)
    then
        System.out.println("工作时间内的订单");
end

rule "Complex Eval"
    when
        $order: Order($amount: totalAmount)
        eval($amount.compareTo(new java.math.BigDecimal("1000")) > 0)
    then
        System.out.println("复杂条件判断");
end

8.2 accumulate高级用法

package rules

import com.example.model.Order
import com.example.model.Customer
import java.math.BigDecimal

// 1. 自定义accumulate
rule "Custom Accumulate"
    when
        $customer: Customer()
        $result: BigDecimal() from accumulate(
            Order($amount: totalAmount, customerName == $customer.name),
            init(BigDecimal total = BigDecimal.ZERO;),
            action(total = total.add($amount);),
            reverse(total = total.subtract($amount);),
            result(total)
        )
    then
        System.out.println("客户总金额: " + $result);
end

// 2. 多返回值accumulate
rule "Multi Result Accumulate"
    when
        accumulate(
            Order($amount: totalAmount, status == "CONFIRMED"),
            $total: sum($amount),
            $count: count($amount),
            $avg: average($amount)
        )
    then
        System.out.println("总额: " + $total);
        System.out.println("数量: " + $count);
        System.out.println("平均: " + $avg);
end

// 3. 带条件的accumulate
rule "Conditional Accumulate"
    when
        $customer: Customer()
        $vipOrders: Number() from accumulate(
            Order(
                customerName == $customer.name,
                totalAmount > 1000
            ),
            count(1)
        )
        eval($vipOrders.intValue() >= 3)
    then
        $customer.setLevel("VIP");
        update($customer);
        System.out.println("升级为VIP客户");
end

九、生产实战案例

9.1 电商订单处理规则

package rules

import com.example.model.Order
import com.example.model.Customer
import java.math.BigDecimal
import java.time.LocalDateTime

global java.util.List notifications

// 规则1: 订单自动确认
rule "Auto Confirm Order"
    salience 100
    when
        $order: Order(
            status == "PENDING",
            totalAmount < 500,
            totalAmount >= 10
        )
    then
        modify($order) {
            setStatus("CONFIRMED"),
            setOrderTime(LocalDateTime.now())
        }
        notifications.add("订单 " + $order.getOrderId() + " 已自动确认");
end

// 规则2: 大额订单人工审核
rule "Manual Review for Large Orders"
    salience 90
    when
        $order: Order(
            status == "PENDING",
            totalAmount >= 5000
        )
    then
        modify($order) {
            setStatus("REVIEW_REQUIRED"),
            setPriority("HIGH")
        }
        notifications.add("订单 " + $order.getOrderId() + " 需要人工审核");
end

// 规则3: VIP客户优先处理
rule "VIP Priority Processing"
    salience 95
    when
        $customer: Customer(level == "VIP")
        $order: Order(
            customerName == $customer.name,
            status == "PENDING"
        )
    then
        modify($order) {
            setPriority("URGENT"),
            setVipOrder(true)
        }
        notifications.add("VIP客户订单优先处理");
end

// 规则4: 库存预警
rule "Stock Warning"
    when
        $order: Order(itemCount > 100)
    then
        notifications.add("大批量订单,请检查库存: " + $order.getOrderId());
end

// 规则5: 异常订单检测
rule "Abnormal Order Detection"
    when
        $order: Order(
            totalAmount > 10000,
            orderTime after[0s, 1m] LocalDateTime.now().minusMinutes(1)
        )
        $count: Number(intValue > 5) from accumulate(
            Order(
                customerName == $order.customerName,
                orderTime after[0s, 1h] LocalDateTime.now().minusHours(1)
            ),
            count(1)
        )
    then
        modify($order) {
            setStatus("SUSPICIOUS"),
            setPriority("URGENT")
        }
        notifications.add("检测到异常订单模式,可能存在风险");
end

// 规则6: 积分计算
rule "Calculate Loyalty Points"
    when
        $order: Order(status == "CONFIRMED")
        $customer: Customer(name == $order.customerName)
    then
        int points = $order.getTotalAmount().intValue() / 10;
        if ($customer.getLevel().equals("VIP")) {
            points = points * 2;  // VIP双倍积分
        }
        notifications.add("客户获得积分: " + points);
end

// 规则7: 自动取消超时订单
rule "Auto Cancel Timeout Orders"
    when
        $order: Order(
            status == "PENDING",
            orderTime before[30m] LocalDateTime.now().minusMinutes(30)
        )
    then
        modify($order) {
            setStatus("CANCELLED")
        }
        notifications.add("订单 " + $order.getOrderId() + " 超时自动取消");
end

9.2 风控规则引擎

package rules

import com.example.model.Order
import com.example.model.Customer
import java.math.BigDecimal

global java.util.List riskAlerts

// 规则1: 单笔大额交易
rule "Large Single Transaction"
    salience 100
    when
        $order: Order(totalAmount > 20000)
    then
        riskAlerts.add("高风险: 单笔交易金额过大 - " + $order.getOrderId());
        $order.setStatus("RISK_REVIEW");
        update($order);
end

// 规则2: 频繁交易
rule "Frequent Transactions"
    salience 90
    when
        $customer: Customer()
        $count: Number(intValue >= 10) from accumulate(
            Order(
                customerName == $customer.name,
                orderTime after[0s, 1h] java.time.LocalDateTime.now().minusHours(1)
            ),
            count(1)
        )
    then
        riskAlerts.add("中风险: 客户 " + $customer.getName() + " 1小时内交易" + $count + "次");
end

// 规则3: 跨区域交易
rule "Cross Region Transaction"
    when
        $customer: Customer($city: city)
        $order: Order(
            customerName == $customer.name,
            category not in ($city)
        )
        exists Order(
            customerName == $customer.name,
            category == $city,
            orderTime after[0s, 24h] java.time.LocalDateTime.now().minusHours(24)
        )
    then
        riskAlerts.add("中风险: 检测到跨区域交易");
end

// 规则4: 新用户大额交易
rule "New User Large Transaction"
    when
        $customer: Customer(
            active == true,
            balance == 0
        )
        $order: Order(
            customerName == $customer.name,
            totalAmount > 5000
        )
        not Order(
            customerName == $customer.name,
            status == "CONFIRMED"
        )
    then
        riskAlerts.add("高风险: 新用户首笔大额交易");
        $order.setStatus("RISK_REVIEW");
        update($order);
end

// 规则5: 综合风险评分
rule "Comprehensive Risk Score"
    when
        $order: Order()
        $customer: Customer(name == $order.customerName)

        // 计算风险分数
        accumulate(
            Order(customerName == $customer.name, status == "CANCELLED"),
            $cancelCount: count(1)
        )

        eval($order.getTotalAmount().compareTo(new BigDecimal("10000")) > 0 ||
             $cancelCount.intValue() > 5)
    then
        int riskScore = 0;
        if ($order.getTotalAmount().compareTo(new BigDecimal("10000")) > 0) {
            riskScore += 50;
        }
        if ($cancelCount.intValue() > 5) {
            riskScore += 30;
        }

        if (riskScore >= 60) {
            riskAlerts.add("综合风险评分: " + riskScore + " - 需要审核");
        }
end

十、规则测试

10.1 单元测试

package com.example.drools;

import com.example.model.Customer;
import com.example.model.Order;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

class DroolsSyntaxTest {

    private KieSession kieSession;
    private List<String> notifications;

    @BeforeEach
    void setUp() {
        KieServices kieServices = KieServices.Factory.get();
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        kieSession = kieContainer.newKieSession("ksession-rules");

        notifications = new ArrayList<>();
        kieSession.setGlobal("notifications", notifications);
    }

    @Test
    void testAutoConfirmOrder() {
        Order order = new Order();
        order.setOrderId(1L);
        order.setStatus("PENDING");
        order.setTotalAmount(new BigDecimal("300"));
        order.setOrderTime(LocalDateTime.now());

        kieSession.insert(order);
        int rulesFired = kieSession.fireAllRules();

        assertTrue(rulesFired > 0, "应该触发规则");
        assertEquals("CONFIRMED", order.getStatus(), "订单应该被确认");
    }

    @Test
    void testManualReviewForLargeOrders() {
        Order order = new Order();
        order.setOrderId(2L);
        order.setStatus("PENDING");
        order.setTotalAmount(new BigDecimal("6000"));
        order.setOrderTime(LocalDateTime.now());

        kieSession.insert(order);
        kieSession.fireAllRules();

        assertEquals("REVIEW_REQUIRED", order.getStatus(), "大额订单需要审核");
        assertEquals("HIGH", order.getPriority(), "优先级应为HIGH");
    }

    @Test
    void testVipPriorityProcessing() {
        Customer vipCustomer = new Customer();
        vipCustomer.setCustomerId(1L);
        vipCustomer.setName("张三");
        vipCustomer.setLevel("VIP");

        Order order = new Order();
        order.setOrderId(3L);
        order.setCustomerName("张三");
        order.setStatus("PENDING");
        order.setTotalAmount(new BigDecimal("1000"));

        kieSession.insert(vipCustomer);
        kieSession.insert(order);
        kieSession.fireAllRules();

        assertEquals("URGENT", order.getPriority(), "VIP订单优先级为URGENT");
        assertTrue(order.isVipOrder(), "应标记为VIP订单");
    }

    @Test
    void testAbnormalOrderDetection() {
        Customer customer = new Customer();
        customer.setName("李四");

        // 创建多个订单模拟频繁交易
        for (int i = 0; i < 6; i++) {
            Order order = new Order();
            order.setOrderId((long) i);
            order.setCustomerName("李四");
            order.setTotalAmount(new BigDecimal("10000"));
            order.setOrderTime(LocalDateTime.now());
            kieSession.insert(order);
        }

        kieSession.insert(customer);
        kieSession.fireAllRules();

        boolean hasSuspiciousOrder = notifications.stream()
                .anyMatch(msg -> msg.contains("异常订单"));
        assertTrue(hasSuspiciousOrder, "应该检测到异常订单");
    }

    @Test
    void testRiskRules() {
        List<String> riskAlerts = new ArrayList<>();
        kieSession.setGlobal("riskAlerts", riskAlerts);

        Order largeOrder = new Order();
        largeOrder.setOrderId(100L);
        largeOrder.setTotalAmount(new BigDecimal("25000"));

        kieSession.insert(largeOrder);
        kieSession.fireAllRules();

        assertFalse(riskAlerts.isEmpty(), "应该有风险警报");
        assertEquals("RISK_REVIEW", largeOrder.getStatus(), "应该进入风险审核");
    }
}

十一、性能优化建议

11.1 规则编写最佳实践

// ❌ 不好的写法 - 过于复杂
rule "Bad Rule"
    when
        $order: Order()
        $customer: Customer() from $order.getCustomer()
        eval($customer.getLevel().equals("VIP") &&
             $order.getTotalAmount().compareTo(new BigDecimal("1000")) > 0)
    then
        // ...
end

// ✅ 好的写法 - 简洁高效
rule "Good Rule"
    when
        $order: Order(
            customer.level == "VIP",
            totalAmount > 1000
        )
    then
        // ...
end

11.2 优化技巧

规则优化建议:

1. 使用salience合理设置优先级
   +------------------+
   | 高优先级规则      |  salience 100
   +------------------+
   | 中优先级规则      |  salience 50
   +------------------+
   | 低优先级规则      |  salience 10
   +------------------+

2. 避免死循环
   - 使用 no-loop true
   - 避免不必要的update

3. 合理使用规则分组
   - agenda-group 按功能分组
   - activation-group 互斥规则

4. 优化条件顺序
   - 将最具区分度的条件放在前面
   - 先判断简单条件,后判断复杂条件

5. 避免过度使用eval
   - 尽量用约束代替eval
   - eval应该只用于无法用约束表达的情况

十二、总结

本文详细介绍了Drools的基础语法:

  1. DRL文件结构 - package、import、global、function、rule
  2. 条件语法 - 模式匹配、比较运算符、复合条件、集合操作
  3. 动作语法 - update、insert、retract、modify
  4. 规则属性 - salience、no-loop、agenda-group等
  5. 函数定义 - 自定义函数复用逻辑
  6. 查询定义 - query查询工作内存
  7. 条件元素 - eval、accumulate高级用法
  8. 实战案例 - 订单处理、风控规则
  9. 单元测试 - 完整的测试示例
  10. 性能优化 - 规则编写最佳实践

掌握这些基础语法,可以编写出高效、可维护的业务规则。