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的基础语法:
- DRL文件结构 - package、import、global、function、rule
- 条件语法 - 模式匹配、比较运算符、复合条件、集合操作
- 动作语法 - update、insert、retract、modify
- 规则属性 - salience、no-loop、agenda-group等
- 函数定义 - 自定义函数复用逻辑
- 查询定义 - query查询工作内存
- 条件元素 - eval、accumulate高级用法
- 实战案例 - 订单处理、风控规则
- 单元测试 - 完整的测试示例
- 性能优化 - 规则编写最佳实践
掌握这些基础语法,可以编写出高效、可维护的业务规则。