






















工作流引擎中的 Business Rule Task(规则任务) 需要把「业务规则」从流程图里抽离出来:规则可复用、可版本化、可独立测试,而 BPMN 只负责 选哪套规则 与 把流程变量交给规则。
本实践在 Slickflow 中落地 规则集(Rule Set),并集成开源规则引擎 NRules,同时支持 程序集规则(强类型 C# Rule) 与 JSON DSL 规则(声明式 when/outputs) 两种形态,统一落在数据库表 wf_rule_set,由 RuleSetExecutionManager 按模式调度执行。
| 层级 | 职责 |
|---|---|
这样 规则本体 与 流程定义 XML 解耦:同一套规则可被多个流程/节点引用;更新规则主要改表数据或发布流程版本,而不必反复改 BPMN 里的规则正文。
下面用 请假业务 把「规则集算出的结果如何驱动 BPMN」说清楚:流程里只有 一套规则集(例如绑定 LeaveApproveRule 或等价的 DSL),规则内部按 三种互斥情形 写出 ApprovalLevel;排他网关 再按该变量走 三条出线,分别对应三种审批路径——这与 LeaveApproveRule 中 Leader / Manager / HR 三档一致。
参与者与变量约定: 员工提交请假时,流程中已有(或经表单写入)LeaveDays(天数)、LeaveType(类型,如事假、病假等)。规则任务执行前,这些量作为规则输入;执行后,规则向流程变量写入 ApprovalLevel,供网关条件引用。

节点顺序(自上而下、与典型 BPMN 泳道图一致):
wf_variable 声明的 Input,供规则任务引用)。wf_rule_set 中的请假规则集(ruleSetCode)。引擎调用 RuleExecutor → RuleSetExecutionManager,根据 mode 执行 NRules 或 JSON DSL,唯一输出路由键 为 ApprovalLevel。ApprovalLevel 为唯一判别依据,三条出线互斥,同一实例只激活其中一条:
ApprovalLevel == "Leader" → 用户任务「直属领导审批」(短假、常规情形,由组长 / 主管处理)。ApprovalLevel == "Manager" → 用户任务「部门经理审批」(例如天数超过 3 天且未落入 HR 档)。ApprovalLevel == "HR" → 用户任务「人事审批」(例如超过 7 天或病假等需人事备案的情形)。要点: 网关上的三条条件不是再写三套独立「业务规则」,而是 消费规则任务已写好的同一个变量;「三种规则」体现在 规则集内部 对 LeaveDays、LeaveType 的分支判断(NRules 里 ApplyApproval 的三段 if / else if / else,或 DSL 里多条 when),流程图侧则 一规则任务 + 一网关三分支,职责清晰、易测易改。
ruleTypes:NRules 程序集规则rule_content 为 JSON,包含 ruleTypes 数组,元素为 程序集限定名(与 NRules Rule 派生类一致)。
运行时:
RuleRepository 加载类型并编译会话;RuleInputFact(变量字典)与 RuleOutputFact(输出字典);session.Fire() 执行规则,从输出字典取结果(如 ApprovalLevel)。适合:复杂分支、需强类型与单测、与现有 .NET 业务规则类复用。
下面是与「请假审批级别」对应的 NRules 规则类 完整示例(引擎通过 ruleTypes 中的程序集限定名加载该类;RuleInputFact / RuleOutputFact 由引擎注入,变量从 input.Vars 读取,结果写入 output):
源码位置: sfbpmn-project/core/Slickflow.Module.BusinessRule/Approval/LeaveApproveRule.cs
using NRules.Fluent.Dsl;
using Slickflow.Engine.Business.Entity;
using System.Globalization;
namespace Slickflow.Module.BusinessRule.Approval
{
public class LeaveApproveRule : Rule
{
public override void Define()
{
RuleInputFact input = null!;
RuleOutputFact output = null!;
When()
.Match(() => input)
.Match(() => output);
Then()
.Do(_ => ApplyApproval(input, output));
}
private static void ApplyApproval(RuleInputFact input, RuleOutputFact output)
{
var days = 0;
var leaveType = string.Empty;
if (input.Vars.TryGetValue("LeaveDays", out var d) && d != null)
{
var rawDays = Convert.ToString(d, CultureInfo.InvariantCulture);
if (!string.IsNullOrWhiteSpace(rawDays)
&& int.TryParse(rawDays, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedDays))
{
days = parsedDays;
}
}
if (input.Vars.TryGetValue("LeaveType", out var t) && t != null)
leaveType = t.ToString() ?? string.Empty;
if (days > 7 || leaveType.Equals("Sick", StringComparison.OrdinalIgnoreCase))
output.Set("ApprovalLevel", "HR");
else if (days > 3)
output.Set("ApprovalLevel", "Manager");
else
output.Set("ApprovalLevel", "Leader");
}
}
}
bindingsJson(表字段名,含义为 JSON 规则 DSL)此处 mode = bindingsJson 表示 rule_content 内是 JSON DSL,与早期 BPMN 上曾出现的同名「变量绑定」概念已区分;任务节点不再承载大块绑定 JSON,避免与 wf_rule_set.rule_content 混淆。
rule_content 形如:
stopOnFirstMatch 与 rules 数组;name、when(条件表达式)与 outputs(输出键值);stopOnFirstMatch: true 时 首条命中即停止。适合:规则变更频繁、希望少发版、由运营/实施直接改 JSON 的场景。
DSL 模式示例(入库到 wf_rule_set.rule_content,mode 为 bindingsJson;变量名 LeaveDays、LeaveType、ApprovalLevel 与流程/规则输入约定一致即可):
{
"stopOnFirstMatch": true,
"rules": [
{
"name": "ShortSickLeave",
"when": "LeaveDays <= 3 || LeaveType == \"Sick\"",
"outputs": {
"ApprovalLevel": "Leader"
}
},
{
"name": "LongLeave",
"when": "LeaveDays > 3",
"outputs": {
"ApprovalLevel": "Manager"
}
}
]
}
说明:上例与上一节 LeaveApproveRule.cs 的分档逻辑 不必完全一致——前者用声明式条件快速迭代,后者用 C# 实现更细的分支(例如病假、超过 7 天走 HR);产品可按同一业务域分别维护两套实现或逐步对齐语义。
RuleExecutor → RuleSetExecutionManagerRuleExecutor(Slickflow.Engine)在 RuleTask 执行时:
ruleSetCode 从 wf_rule_set 取实体;RuleSetExecutionManager.Execute;wf_process_variable(活动作用域),供后续网关、任务使用。RuleConfigDetail 除 BPMN 中的 ruleSetCode 外,可在解析流程时 MergeFrom 合并表字段(名称、描述、rule_content、mode 等),便于调试与 API 展示;执行仍以库表为准,避免 BPMN 与库不一致。
wf_variable 定义规则入参需要与流程数据对齐:
wf_process_variable 提供运行时值;wf_variable 描述当前活动上的变量定义:方向、是否引用前序节点、source_ref / source_variable_name 等。RuleTask 组装输入时:先尊重 wf_variable 中声明的 Input——引用型输入从前序活动实例的 wf_process_variable 按引用解析;未声明则回退为合并活动变量(兼容旧流程)。声明了输入但暂无值时写入空字符串,避免 JSON DSL 中 LeaveType == "Sick" 等表达式因缺键导致整条规则无法命中。
RuleSetModeEnum(ruleTypes / bindingsJson)与 RuleSetModeHelper 做持久化字符串与枚举互转,避免散落魔法字符串。RuleSetController 在保存时校验 mode 与 rule_content 形状,保证入库数据可被引擎执行。Modeling 菜单进入 业务规则设置,维护规则集列表与 rule_content;

RuleTask 属性里选择 规则集编码,与引擎读取路径一致。

wf_rule_set,BPMN 只存 ruleSetCode,降低模型体积与合并冲突。mode 明确分支,避免隐式猜测。Slickflow 规则集功能将 NRules 与 自研 JSON DSL 执行器 统一在 RuleSetExecutionManager 之下,通过 wf_rule_set.mode + rule_content 驱动;RuleTask 只负责绑定规则集与喂入流程变量,从而形成清晰的分层:产品(建模/API)→ 引擎(执行与持久化)→ 规则运行时(NRules 或 DSL)。
后续可在同一套扩展点上增加规则版本、审计日志、规则仿真接口等,而不必改动 BPMN 核心结构。
本文基于 Slickflow 仓库中 RuleSetExecutionManager、RuleExecutor、VariableManager、wf_rule_set 等相关实现整理,如有变更以当前代码与数据库脚本为准。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。