Skip to content

流程表达式与监听 ​

功能定位 ​

流程引擎中「表达式」与「监听」分别承担不同的职责,二者都采用「后台维护库 + 引擎运行时消费」的模式:

  • 流程表达式库:集中维护可复用的表达式片段(候选人表达式、条件表达式等)。设计器在配置节点时可以从库中挑选并把表达式文本复制到节点配置里,避免重复编写。
  • 流程监听器:与引擎事件总线对接的扩展钩子,用于在某类流程事件发生时执行自定义逻辑(如通知、业务回写、埋点等),执行失败不影响主流程推进。

流程表达式库 ​

image-20260930172211706

数据结构 ​

表达式库对应数据表 bpm_process_expression,记录非常简洁:

  • 名字 name:便于检索的标识
  • 状态 status:0 开启 / 1 关闭
  • 表达式 expression:表达式文本(最长 512 字符)

它本质上是一个「表达式片段仓库」。引擎在运行时并不会按编号引用它,而是设计器在节点配置中选择某条记录后,把其 expression 文本拷贝到节点自身的配置字段(如候选人表达式、跳过表达式、条件表达式)。因此维护库的目的是统一沉淀常用表达式、减少出错。

表达式在引擎中的使用场景 ​

表达式真正参与流程计算的地方主要有四处,语法规则各不相同:

候选人表达式 ​

在审批人节点的候选人策略选择「流程表达式」(EXPRESSION = 60)时,引擎通过 resolve_expression_users 解析该表达式,得到候选用户编号列表。支持以下格式:

  • startUser:流程发起人
  • ${字段名}:引用流程变量中的用户编号或编号数组(如表单用户字段、内置变量 PROCESS_START_USER_ID)
  • 字面量:1、1,2 或 [1, 2]

解析过程会去重并保持顺序,空值自动过滤;格式不合法会抛出配置错误提示。

条件表达式(安全求值) ​

条件分支节点、触发器节点的条件均支持「条件表达式」方式,由 eval_expression 执行。为保证安全,它基于 Python ast 白名单解析,只允许变量引用、比较运算、逻辑运算,禁止属性访问、函数调用、下标等任意代码执行。

支持的运算符与写法:

  • 比较:==、!=、>、>=、<、<=,以及 in / not in / is / is not
  • 逻辑:and / or / not,并兼容前端习惯写法 &&(转 and)、||(转 or)
  • 内置变量:PROCESS_START_USER_ID(发起人编号),其余变量来自流程变量

求值规则(容错优先,避免单条表达式错误中断流程):

  • 变量缺失、类型不匹配时表达式判 False
  • 语法错误或使用了白名单外的语法,抛出配置异常(属于配置错误,需修正)

条件规则(规则组) ​

除表达式外,条件还支持「条件规则」(conditionGroups):由「表单字段 + 运算符 + 值」组成的规则,按「组间 / 组内」的且 / 或关系组合。支持的运算符包含 ==、!=、大小比较,以及 contain / !contain(包含 / 不包含)。其中右值统一为字符串输入,引擎会在比较前做类型协调(如数字字符串按数值比较)。

跳过表达式 ​

审批人节点可配置「跳过表达式」——当表达式命中时,该节点在不满足条件的情况下被自动跳过,直接进入后续链路。

管理接口 ​

表达式库提供标准 CRUD,路由前缀 /bpm/process-expression:

  • 分页查询 /page、详情 /get
  • 新增 /create、修改 /update、删除 /delete
  • 所需权限:bpm:process-expression:query / create / update / delete

前端:表达式库与节点配置 ​

后台菜单「工作流 → 流程表达式」为表格页(views/bpm/processExpression),支持新增 / 编辑 / 删除与按名字、状态检索。

在简易设计器的审批人节点配置中,候选人策略选「流程表达式」时,可通过「选择」按钮打开 ProcessExpressionSelectModal 弹窗,从库中挑选一条记录,其表达式文本会被填入节点配置:

ts
function handleExpressionSelected(row: any) {
  configForm.value.expression = row?.expression ?? '';
}

审批人节点还提供「跳过表达式」配置项,书写规则与条件表达式一致。

流程监听器 ​

image-20260930172400705

监听器类型与事件 ​

监听器对应数据表 bpm_process_listener,核心字段:

  • 类型 type:execution(流程实例维度)/ task(任务维度)
  • 事件 event:监听的具体事件
  • 值类型 value_type:class(类路径)/ expression(表达式)/ delegateExpression(暂不支持)
  • 值 value:类路径或表达式文本
  • 状态 status:0 开启 / 1 关闭

引擎事件与「类型 + 事件」的映射关系如下:

引擎事件监听器类型 type监听事件 event
流程实例创建executionstart
流程实例结束executionend
任务创建taskcreate
任务通过taskcomplete

需要注意:task 类型的 complete 事件仅在任务审批通过(APPROVE)时触发;驳回、取消、退回等终态不会触发 complete。前端在 type = execution 时事件下拉仅展示「开始 / 结束」,在 type = task 时展示「创建 / 指派 / 完成 / 删除 / 更新 / 超时」,但实际引擎消费以上四组映射。

执行机制 ​

引擎启动时(framework/engine.py)会调用 process_listener.register() 注册事件订阅,实现与引擎核心解耦。当某类事件发生时:

  1. 按「类型 + 事件 + 状态为开启」从 bpm_process_listener 过滤出匹配的监听器;
  2. 注入上下文:流程实例变量 + 内置变量 PROCESS_START_USER_ID;
  3. 逐个执行,执行失败仅记录日志,不会阻断主流程推进。

三种值类型的执行方式 ​

  • expression:调用 eval_expression 对 value 求值,结果仅记录日志(不用于控制流程走向),适合做校验或日志埋点。
  • class:按 module.path:attr 或 module.path.attr 动态导入处理函数,调用签名统一为 handler(**payload),payload 包含事件载荷(如 instance、task、db 等)。处理函数可以是同步或异步(async)函数。
  • delegateExpression:当前版本暂不处理,会输出告警日志。

管理接口 ​

监听器提供标准 CRUD,路由前缀 /bpm/process-listener:

  • 分页查询 /page、详情 /get
  • 新增 /create、修改 /update、删除 /delete
  • 所需权限:bpm:process-listener:query / create / update / delete

前端 ​

后台菜单「工作流 → 流程监听器」为表格页(views/bpm/processListener),支持新增 / 编辑 / 删除与按名字、类型检索。表单中「类型」选择后会联动切换「事件」可选项,「值类型」会联动切换「值」输入框的占位提示(类路径 / 表达式)。同时提供 select-modal 选择弹窗,便于在其它配置处引用已有监听器。

实战示例 ​

候选人表达式 ​

在「报销审批」节点,希望审批人动态取自表单字段 financeLeader:

text
${financeLeader}

或在通用场景直接指定发起人:

text
startUser

条件表达式分支 ​

请假流程中,按请假天数走不同分支(兼容前端 && 写法):

text
dayCount > 3 && dayCount <= 7

内置变量可直接使用:

text
PROCESS_START_USER_ID == 100

编写 class 监听器 ​

若要在流程实例创建时同步写入业务系统,可在 value 中填写:

text
app.bpm.listeners.on_instance_start:handle

并实现处理函数:

python
async def handle(**payload):
    instance = payload.get("instance")
    # 基于 instance.variables 做业务回写
    ...

只要函数在 module:attr 路径下可导入,引擎便会自动调用;执行异常只记录日志,流程继续推进。