第1章:工作流引擎核心概念

大家好,我是你们的老朋友。今天咱们正式开始聊工作流引擎。

说实话,我第一次接触工作流引擎时,脑子里全是问号。这东西到底在干什么?为什么要有它?后来做了几个项目,踩了不少坑,才慢慢摸清楚门道。

这一章,我会把工作流引擎最核心的几个概念讲透。你理解透了,后面学起来就轻松了。

1.1 流程定义

流程定义是什么?说白了,就是一张「蓝图」。

你想想看,一个请假流程,需要经过哪些步骤?员工提交申请 -> 直属领导审批 -> 部门经理审批 -> 人事备案。这个步骤顺序,就是流程定义。

我在项目中遇到过一种情况:客户说「我们的审批流程很简单,就三步」。结果一调研,发现每个部门的三步都不一样,有的还要加会签、加条件分支。嗯,流程定义就是用来描述这些「不一样」的。

一个典型的流程定义包含以下要素:

  • 节点(Node):流程中的每个步骤,比如「提交申请」节点、「领导审批」节点
  • 连线(Sequence Flow):节点之间的流转方向
  • 网关(Gateway):用来做分支判断,比如金额大于5000需要总经理审批
  • 事件(Event):流程开始、结束、超时等触发点

重点记住:流程定义是「静态的」,它就像一张地图,画好了就不动了。

1.2 流程实例

流程实例,就是「跑起来」的流程定义。

举个例子:你定义了请假流程的蓝图。张三今天请假,系统就为他创建一个流程实例。李四明天请假,系统再创建一个新的流程实例。

每个流程实例都是独立的。张三的请假流程卡在领导审批环节,不影响李四的流程走到人事备案。

我个人习惯用「订单号」来理解流程实例。你想想看,电商系统里每个订单都是独立的,有自己的状态、创建时间、处理人。流程实例也一样,它有自己的:

  • 实例ID:唯一标识
  • 当前节点:流程走到哪一步了
  • 变量数据:比如请假天数、审批意见
  • 状态:运行中、已完成、已终止

小技巧:调试时,我习惯把流程实例ID打印到日志里。这样出问题了,直接搜ID就能定位到具体流程,省时省力。

1.3 任务节点

任务节点,是流程实例中的「动作单元」。

流程定义里的每个步骤,最终都会变成一个任务节点。比如「领导审批」这个步骤,在流程实例里就是一个待办任务。

任务节点分几种类型:

类型 说明 例子
用户任务 需要人来操作 审批、填写表单
服务任务 系统自动执行 发送邮件、调用API
脚本任务 执行一段脚本 计算金额、校验数据
子流程 嵌套另一个流程 采购流程里嵌套付款流程

我曾经犯过一个错:把所有任务都设计成用户任务。结果一个流程跑下来,需要人工点十几次。后来改成服务任务自动处理中间环节,效率提升了好几倍。

1.4 流转条件

流转条件,就是「什么时候走这条路」。

没有条件的流程,就像一条直线,从起点走到终点。但现实中的流程往往有分支:

  • 请假天数 ≤ 3天,部门经理审批即可
  • 请假天数 > 3天,需要总经理审批

这个「≤ 3天」和「> 3天」,就是流转条件。

在代码里,流转条件通常写成表达式:

// 伪代码示例
if (leaveDays <= 3) {
    goTo("部门经理审批");
} else {
    goTo("总经理审批");
}

注意:流转条件一定要考虑边界情况。我曾经遇到过「等于3天」时两个分支都走不通的bug,就是因为条件写成了 < 3 和 > 3,漏掉了 = 3 的情况。

1.5 工作流引擎架构

聊完概念,咱们看看工作流引擎的整体架构。

我习惯把工作流引擎分成三层:

  1. 定义层:负责流程定义的解析、校验、存储
  2. 运行时层:负责流程实例的创建、推进、状态管理
  3. 交互层:负责待办任务、表单展示、操作入口

这三层的关系,你可以这样理解:

  • 定义层是「图纸」
  • 运行时层是「施工队」
  • 交互层是「现场指挥」

图纸画好了,施工队按图纸干活,现场指挥协调各方。缺一不可。

一个简化的工作流引擎核心流程如下:

// 核心流程伪代码
public class WorkflowEngine {
    
    // 1. 解析流程定义
    public ProcessDefinition parseDefinition(String bpmnXml) {
        // 解析BPMN文件,构建节点和连线
    }
    
    // 2. 启动流程实例
    public ProcessInstance startProcess(String defId, Map<String, Object> variables) {
        // 根据定义创建实例,设置初始变量
    }
    
    // 3. 推进流程
    public void completeTask(String taskId, Map<String, Object> result) {
        // 完成任务,计算下一个节点
        // 检查流转条件,决定走哪条分支
    }
}

嗯,这里要注意:实际引擎比这个复杂得多。要考虑并发、事务、持久化、历史记录等等。但核心骨架就是这三步。

小结

这一章我们讲了五个核心概念:

  • 流程定义:静态蓝图
  • 流程实例:运行中的流程
  • 任务节点:流程中的动作单元
  • 流转条件:分支判断逻辑
  • 引擎架构:定义层、运行时层、交互层

你把这些概念搞清楚了,后面学具体实现时就不会晕。下一章,咱们开始动手搭建一个最小可用的工作流引擎。

一句话总结:流程定义是「画」出来的,流程实例是「跑」出来的,任务节点是「做」出来的,流转条件是「判」出来的。