3. 规则定义与配置:规则的结构(条件+动作)、规则配置化设计、规则文件格式(DRL、JSON、YAML)

好,咱们进入第三章。这一章聊的是规则引擎的“骨架”——规则的定义与配置。

说实话,很多团队一开始做规则引擎,都栽在同一个坑里:规则写死了。业务一改,就得改代码、重新部署。这哪叫规则引擎?这叫硬编码。

所以,这一章的核心就三个字:配置化。把规则从代码里抽出来,变成可配置、可维护的独立资产。

3.1 规则的结构:条件 + 动作

规则的本质是什么?说白了就是:如果满足某个条件,就执行某个动作。

你想想看,不管是风控系统、优惠券发放,还是审批流程,底层逻辑都是这个。我习惯把规则拆成三个部分:

  • 元数据:规则名称、版本号、生效时间、优先级。这些是给运维看的。
  • 条件(Condition):触发规则的前提。可以是简单的字段比较,也可以是复杂的组合逻辑。
  • 动作(Action):条件满足后执行的操作。比如“拒绝订单”、“发送短信”、“计算折扣”。

一个典型的规则结构(伪代码):

规则 "订单风控-高频交易" {
    优先级: 10
    条件: 当 用户.近1小时交易次数 > 5 且 用户.账户等级 == "普通"
    动作: 标记订单为 "高风险",触发人工审核
}

我在项目中遇到过一种情况:业务方说“规则很简单,就两个条件”。结果一上线,发现条件之间还有隐含的优先级关系,导致误判率飙升。嗯,这里要注意:条件之间的逻辑关系(AND/OR/NOT)一定要显式定义,别靠猜。

3.2 规则配置化设计

规则配置化,不是简单地把规则塞进数据库就完事了。我见过最糟糕的设计:规则存在一个超大 JSON 字段里,业务方改规则得找开发写 SQL。这哪是配置化?这是换了个地方硬编码。

我个人习惯的配置化设计,要满足三个原则:

  1. 可读性:业务人员能看懂,甚至能自己改。别整一堆技术符号。
  2. 版本化:每次修改都要留痕,能回滚。我曾经因为没做版本管理,一次误操作导致线上规则全乱,回滚花了两个小时。
  3. 热加载:改完规则,不用重启服务就能生效。这是配置化的底线。

我的配置化设计经验:

我建议把规则拆成“规则头”和“规则体”。规则头存元数据(名称、版本、状态),规则体存具体的条件和动作。这样,查询规则列表时不用加载全部内容,性能好很多。

举个例子,一个简单的规则配置表设计:

字段 类型 说明
rule_id varchar(32) 规则唯一标识
rule_name varchar(128) 规则名称,业务方用
version int 版本号,每次修改递增
status tinyint 0-禁用,1-启用
condition_json text 条件表达式(JSON格式)
action_json text 动作定义(JSON格式)
priority int 优先级,数字越小越优先
effective_time datetime 生效时间
expire_time datetime 失效时间

3.3 规则文件格式:DRL、JSON、YAML

规则写在哪?常见的有三种格式。我分别说说我的看法。

3.3.1 DRL(Drools Rule Language)

DRL 是 Drools 规则引擎的原生语言。如果你用 Drools,那 DRL 是首选。它的语法贴近自然语言,表达能力很强。

举个例子:

package com.example.rules

import com.example.model.Order

rule "大额订单折扣"
    when
        $order: Order(amount > 1000)
    then
        $order.setDiscount(0.1);
        System.out.println("大额订单,享受9折优惠");
end

DRL 的好处是:与 Drools 引擎深度集成,性能好,支持复杂的推理。但缺点也很明显:学习曲线陡,业务人员基本看不懂。我一般只在后端团队内部使用 DRL,不会让业务方直接碰。

3.3.2 JSON

JSON 是通用格式,前后端都熟悉。我建议用 JSON 作为规则配置的存储格式,尤其是需要和前端规则编辑器配合时。

一个 JSON 格式的规则示例:

{
    "ruleId": "R001",
    "ruleName": "高频交易风控",
    "priority": 10,
    "condition": {
        "operator": "AND",
        "conditions": [
            { "field": "user.tradeCountLastHour", "operator": ">", "value": 5 },
            { "field": "user.level", "operator": "==", "value": "normal" }
        ]
    },
    "action": {
        "type": "markOrder",
        "params": {
            "riskLevel": "high",
            "needReview": true
        }
    }
}

JSON 的优点是:结构清晰,解析方便。但要注意,JSON 不支持注释,所以字段含义得靠文档说明。我习惯在 JSON 里加一个 description 字段,写清楚这条规则是干嘛的。

3.3.3 YAML

YAML 比 JSON 更简洁,可读性更好。如果你想让规则文件看起来像配置文件,YAML 是很好的选择。

同样的规则,用 YAML 写:

ruleId: R001
ruleName: 高频交易风控
priority: 10
condition:
  operator: AND
  conditions:
    - field: user.tradeCountLastHour
      operator: ">"
      value: 5
    - field: user.level
      operator: "=="
      value: normal
action:
  type: markOrder
  params:
    riskLevel: high
    needReview: true

YAML 支持注释,这是它比 JSON 强的地方。但 YAML 对缩进敏感,一不小心就会解析失败。我曾经因为一个空格问题,排查了半小时。所以,如果用 YAML,一定要加校验。

避坑指南:

我曾经在项目中混合使用 JSON 和 YAML 两种格式,结果运维搞混了,解析器报错。后来我统一了格式:存储用 JSON,展示用 YAML。JSON 作为标准格式,YAML 只用于人工查看和编辑。

3.4 知识体系结构图

下面这张图,帮你理清规则定义与配置的核心脉络:

规则定义与配置知识体系 规则引擎配置 规则结构:条件+动作 配置化设计原则 规则文件格式 元数据(名称/版本) 条件(AND/OR/NOT) 动作(标记/计算/通知) 可读性(业务可编辑) 版本化(可回滚) 热加载(无需重启) DRL(Drools原生) JSON(通用存储) YAML(可读性好) 核心目标:将规则从代码中解耦,实现可配置、可维护

好了,这一章的内容就这些。规则的结构、配置化设计、文件格式,这三块是规则引擎的基石。下一章我们会聊规则引擎的执行流程,到时候你会看到这些规则是怎么跑起来的。

本章小结:

  • 规则的本质是“条件+动作”,但别忘了元数据
  • 配置化设计要满足可读性、版本化、热加载
  • DRL 适合后端内部,JSON 适合存储和传输,YAML 适合人工编辑
  • 统一格式,避免混用

可可精品资料库