4、规则引擎集成:Drools 规则引擎入门、决策表与决策树、规则与流程的联动

规则引擎这东西,说白了就是把业务决策从代码里抽出来,放到一个可维护、可热更新的地方。我见过太多项目,业务逻辑散落在 if-else 的海洋里,改一个促销规则要发版本、要重启,运维同学都快哭了。

Drools 是 Java 生态里最成熟的规则引擎之一。它基于 Rete 算法,能高效地匹配大量规则。今天我们就把它拆开看看,怎么用、怎么跟流程联动。

核心观点:规则引擎不是银弹,但它能解决「频繁变化的业务决策」和「复杂多条件组合判断」这两类痛点。用对了,事半功倍;用错了,反而增加复杂度。

4.1 Drools 规则引擎入门

先看一个最简单的例子。假设我们要给电商订单计算折扣:

  • 订单金额满 100 元,打 9 折
  • 订单金额满 200 元,打 8 折
  • 会员额外再打 9.5 折

如果用 Java 写,大概是一堆 if-else。用 Drools 呢?

// Order.java
public class Order {
    private double amount;
    private boolean isMember;
    private double discount;
    // getters/setters...
}

// discount.drl
package com.example.rules

import com.example.Order

rule "满100打9折"
    when
        $o: Order(amount >= 100 && amount < 200)
    then
        $o.setDiscount(0.9);
        System.out.println("应用规则:满100打9折");
end

rule "满200打8折"
    when
        $o: Order(amount >= 200)
    then
        $o.setDiscount(0.8);
        System.out.println("应用规则:满200打8折");
end

rule "会员额外折扣"
    when
        $o: Order(isMember == true)
    then
        $o.setDiscount($o.getDiscount() * 0.95);
        System.out.println("应用规则:会员额外折扣");
end

调用起来也很简单:

KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession();

Order order = new Order(250.0, true);
kSession.insert(order);
kSession.fireAllRules();
kSession.dispose();

System.out.println("最终折扣:" + order.getDiscount());
// 输出:0.8 * 0.95 = 0.76

我个人习惯把 .drl 文件放在 resources/rules/ 目录下,按业务模块分文件夹。这样项目大了也好找。

小技巧:Drools 的规则文件支持中文注释,建议在每条规则前写清楚「业务场景」和「预期效果」。我见过一个项目,规则文件里全是英文注释,后来业务人员看不懂,又得重新翻译一遍。

4.2 决策表与决策树

当规则数量超过 20 条,或者规则之间有复杂的优先级关系时,纯文本的 .drl 文件就不太好维护了。这时候,决策表和决策树就派上用场了。

4.2.1 决策表(Decision Table)

决策表本质上是把规则以表格形式呈现。Drools 支持 Excel 格式的决策表,业务人员可以直接编辑。

举个例子,运费计算规则:

条件:订单金额 条件:会员等级 条件:配送区域 动作:运费
< 50 普通 同城 10
< 50 普通 跨省 20
< 50 VIP 同城 5
< 50 VIP 跨省 10
>= 50 * * 0

这张表翻译成 Drools 规则,就是 5 条 rule。但业务人员看表格比看代码直观多了。我曾经在一个物流项目中,把 80 多条运费规则全部用决策表管理,业务方自己就能维护,再也不用排队等开发改代码了。

注意:决策表虽然方便,但不要把所有逻辑都塞进去。我见过有人把计算逻辑也写进决策表,结果表格里出现了公式嵌套,比代码还难调试。决策表只负责「条件→结果」的映射,复杂的计算还是交给 Java 方法。

4.2.2 决策树(Decision Tree)

决策树是另一种可视化规则的方式。它把条件判断组织成树形结构,每个分支代表一个决策路径。

Drools 本身不直接提供决策树编辑器,但我们可以用决策表模拟决策树的效果。或者,用第三方工具(如 SPL、DMN 编辑器)生成决策树模型,再导入 Drools。

我个人更推荐用 DMN(Decision Model and Notation)标准。Drools 7.x 开始支持 DMN,可以直接加载 .dmn 文件。DMN 决策树长这样:

<!-- 简化的 DMN 决策树片段 -->
<decision id="shippingCost" name="运费计算">
  <variable name="shippingCost" typeRef="number"/>
  <informationRequirement>
    <requiredInput href="#orderAmount"/>
    <requiredInput href="#memberLevel"/>
    <requiredInput href="#region"/>
  </informationRequirement>
  <decisionTable>
    <rule id="r1">
      <inputEntry><text>< 50</text></inputEntry>
      <inputEntry><text>"普通"</text></inputEntry>
      <inputEntry><text>"同城"</text></inputEntry>
      <outputEntry><text>10</text></outputEntry>
    </rule>
    <!-- 更多规则... -->
  </decisionTable>
</decision>

决策树的优势在于「可解释性」。你可以沿着树的分支,一步步解释为什么得出这个结论。这在金融风控、医疗诊断等场景下特别重要。

4.3 规则与流程的联动

光有规则还不够,规则需要嵌入到业务流程中。比如:

  • 订单提交后,先执行风控规则
  • 风控通过后,再执行定价规则
  • 定价完成后,执行库存扣减规则

这就是规则与流程的联动。Drools 提供了两种方式:

4.3.1 在流程中调用规则

Drools 的 BPMN 引擎(jBPM)支持在流程节点中嵌入规则任务。你可以在流程设计器里拖一个「规则任务」节点,指定要执行的规则文件或规则流。

<!-- BPMN 流程片段 -->
<bpmn2:process id="orderProcess" name="订单处理流程">
  <bpmn2:startEvent id="start"/>
  <bpmn2:businessRuleTask id="riskCheck" name="风控检查"
    drools:ruleFlowGroup="risk-rules"/>
  <bpmn2:businessRuleTask id="pricing" name="定价计算"
    drools:ruleFlowGroup="pricing-rules"/>
  <bpmn2:endEvent id="end"/>
  <bpmn2:sequenceFlow sourceRef="start" targetRef="riskCheck"/>
  <bpmn2:sequenceFlow sourceRef="riskCheck" targetRef="pricing"/>
  <bpmn2:sequenceFlow sourceRef="pricing" targetRef="end"/>
</bpmn2:process>

对应的 .drl 文件里,用 ruleflow-group 来分组:

rule "风控-黑名单检查"
    ruleflow-group "risk-rules"
    when
        $o: Order(blacklisted == true)
    then
        $o.setRiskResult("REJECT");
        System.out.println("风控不通过:黑名单用户");
end

rule "定价-满减规则"
    ruleflow-group "pricing-rules"
    when
        $o: Order(amount >= 200)
    then
        $o.setDiscount(0.8);
        System.out.println("应用满200减规则");
end

这样,流程引擎会按顺序执行 risk-rules 和 pricing-rules 两个规则组。互不干扰,顺序可控。

4.3.2 在规则中触发流程

反过来,规则也可以触发流程。比如:风控规则判定订单为高风险,自动发起一个「人工审核」流程。

rule "高风险订单-触发人工审核"
    when
        $o: Order(riskScore > 80)
    then
        // 启动一个 BPMN 流程实例
        ProcessInstance process = kcontext.getKieRuntime()
            .startProcess("manualReviewProcess", 
                Collections.singletonMap("order", $o));
        System.out.println("触发人工审核流程,订单ID:" + $o.getId());
end

这种双向联动,让规则和流程不再是孤岛。我做过一个供应链项目,采购订单的审批流程里嵌入了 30 多条规则,包括价格校验、供应商评级、库存预警等。规则引擎负责「判断」,流程引擎负责「流转」,各司其职。

最佳实践:规则和流程的边界怎么划?我的经验是:
规则负责:「是什么」「该不该」—— 条件判断、决策输出
流程负责:「怎么做」「谁来做」—— 任务分配、状态流转、超时处理
如果某个逻辑既涉及判断又涉及流转,那就拆成「规则+流程节点」的组合。

4.4 本章小结

我们聊了 Drools 的基本用法、决策表和决策树的应用场景,以及规则与流程的联动方式。嗯,这里要注意:规则引擎不是万能的,它最适合「多条件组合判断」和「频繁变化的业务规则」。如果你的规则只有三五条,或者变化频率极低,用 if-else 反而更简单。

最后,送大家一张我整理的规则引擎集成架构图,方便理解整体脉络:

规则引擎集成架构图 业务系统(订单/风控/审批) BPMN 流程引擎(jBPM) Drools 规则引擎 DRL 规则文件 决策表(Excel) DMN 决策模型 规则引擎接收流程引擎的调用,根据规则类型选择不同的规则源执行 执行结果返回流程引擎,驱动流程继续流转

这张图展示了从业务系统到流程引擎,再到规则引擎的调用链路。规则引擎内部又根据规则复杂度,选择 DRL 文件、决策表或 DMN 模型来执行。结果返回流程引擎,驱动下一步操作。