简单来说,升级事件是一种用于在流程内部进行**“向上汇报”或“请求关注”的通信机制。它的核心特点是默认不中断主流程的执行**。这与错误事件(通常会中断流程)和取消事件(总是中断并回滚事务)有本质区别。
你可以把它想象成在工作中遇到的一个情况:你正在处理一个任务,但遇到一个不确定但又没到完全卡死地步的问题,于是你发了一封邮件给你的经理寻求建议,同时你继续进行任务中其他可以处理的部分。这个“发邮件给经理”的动作,就是一个“升级”。
1. 抛出升级事件 (Throwing Escalation Event)
这个事件用来发送一个升级信号。
图标: 一个带有空心向上箭头的粗边圆圈(如果是结束事件)或双边圆圈(如果是中间事件)。
位置: 可以是中间抛出事件 (Intermediate Throw Event) 或结束事件 (End Event)。
作用: 当流程执行到这个事件时,它会根据定义的“升级码”(Escalation Code)广播一个信号。这个信号可以被同一流程或父流程中的捕获事件监听到。发送信号后,流程会继续沿着其路径执行(如果是中间事件)或结束(如果是结束事件)。
2. 捕获升级事件 (Catching Escalation Event)
这个事件用来接收升级信号并触发相应的处理逻辑。
图标: 一个附着在活动边框上的、带有空心向上箭头的双边圆圈。
位置: 通常有两种形式:
边界事件 (Boundary Event): 附着在某个活动(如用户任务、子流程)的边界上。
事件子流程的启动事件 (Event Sub-process Start Event): 作为一个可以接收整个流程范围内升级信号的“全局监听器”。
关键特性: 升级边界事件可以是中断的 (Interrupting) 或非中断的 (Non-interrupting)。
非中断(默认且最常用): 当它捕获到升级信号后,会启动一条并行的分支来执行处理逻辑(例如通知经理),但原有的活动(如“经理审批”)并不会被中断,仍然可以继续执行。这是升级事件最核心和最独特的用途。
中断: 行为类似于错误事件,会中断当前活动并改变流程路径。这种情况用得相对较少。
典型的企业业务需求场景:费用报销审批超时
一个员工提交了费用报销申请,需要其直线经理审批。
业务规则:
经理需要在3天内完成审批。
如果超过3天经理还未审批,系统需要自动发送一封邮件通知该经理的上级(例如部门总监),提醒他关注此事。
重要: 发送提醒邮件后,原来的审批任务依然有效,直线经理仍然可以随时进行审批。
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:flowable="http://flowable.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.flowable.org/processdef">
<!-- 定义一个全局的升级信号 -->
<escalation id="managerApprovalTimeoutEscalation" name="经理审批超时升级" escalationCode="ESC-MANAGER-TIMEOUT"/>
<process id="expenseApprovalWithEscalation" name="费用报销审批(含升级)" isExecutable="true">
<startEvent id="start" name="员工提交报销单"/>
<!-- 经理审批任务 -->
<userTask id="managerApprovalTask" name="经理审批">
<documentation>请在3天内审批该报销单。</documentation>
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>${manager}</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
<!-- 附着在审批任务上的非中断边界事件 -->
<!-- 它由一个计时器触发,然后抛出一个升级信号 -->
<boundaryEvent id="approvalTimer" attachedToRef="managerApprovalTask" cancelActivity="false">
<timerEventDefinition>
<timeDuration>P3D</timeDuration> <!-- 3天后触发 -->
</timerEventDefinition>
</boundaryEvent>
<!-- 计时器触发后,抛出升级事件 -->
<intermediateThrowEvent id="throwEscalationEvent">
<escalationEventDefinition escalationRef="managerApprovalTimeoutEscalation"/>
</intermediateThrowEvent>
<exclusiveGateway id="decision" name="审批结果?"/>
<endEvent id="endApproved" name="审批通过"/>
<endEvent id="endRejected" name="审批驳回"/>
<!-- 主流程流转 -->
<sequenceFlow sourceRef="start" targetRef="managerApprovalTask"/>
<sequenceFlow sourceRef="managerApprovalTask" targetRef="decision"/>
<sequenceFlow sourceRef="decision" targetRef="endApproved">
<conditionExpression xsi:type="tFormalExpression">${approved == true}</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="decision" targetRef="endRejected">
<conditionExpression xsi:type="tFormalExpression">${approved == false}</conditionExpression>
</sequenceFlow>
<!-- 升级路径 -->
<sequenceFlow sourceRef="approvalTimer" targetRef="throwEscalationEvent"/>
<!-- 事件子流程:用于捕获并处理升级信号 -->
<subProcess id="escalationHandlerSubProcess" name="升级处理程序" triggeredByEvent="true">
<!-- 启动事件,捕获特定的升级信号,并且是非中断的 -->
<startEvent id="catchEscalationStart" isInterrupting="false">
<escalationEventDefinition escalationRef="managerApprovalTimeoutEscalation"/>
</startEvent>
<serviceTask id="notifySupervisorTask" name="通知上级总监"
flowable:class="com.example.NotifySupervisorDelegate"/>
<endEvent id="escalationHandledEnd"/>
<sequenceFlow sourceRef="catchEscalationStart" targetRef="notifySupervisorTask"/>
<sequenceFlow sourceRef="notifySupervisorTask" targetRef="escalationHandledEnd"/>
</subProcess>
</process>
<!-- BPMN图定义 (省略) -->
<bpmndi:BPMNDiagram id="BPMNDiagram_expenseApprovalWithEscalation">
<!-- ... -->
</bpmndi:BPMNDiagram>
</definitions>
流程关键节点解析
<escalation id=“managerApprovalTimeoutEscalation” … />
在流程外部或顶层定义了一个“升级”信号模板,包含一个唯一的escalationCode。抛出和捕获事件通过这个id或code进行匹配。
这是一个附着在“经理审批”任务上的非中断计时器边界事件。
cancelActivity=“false” 是关键,它确保当计时器触发时,managerApprovalTask 不会被取消。
P3D 表示在任务创建3天后触发。
当计时器触发后,流程会走到这里,它会抛出我们之前定义的 managerApprovalTimeoutEscalation 信号。
(事件子流程)
triggeredByEvent=“true” 表明这是一个事件子流程,它没有常规的流入顺序流,而是由其内部的启动事件触发。它像一个流程范围内的“监听器”。
这是事件子流程的入口。它监听 managerApprovalTimeoutEscalation 信号。
isInterrupting=“false” 再次强调了处理逻辑不会中断主流程。
模拟执行流程
员工提交报销单,流程启动,一个“经理审批”任务被创建并分配给经理。
场景一:经理在3天内审批
经理完成任务,设置 approved 变量。
流程经过 decision 网关,正常结束。
附着在任务上的计时器被自动销毁,升级逻辑永远不会被触发。
场景二:经理超过3天未审批
“经理审批”任务创建3天后,approvalTimer 边界事件被触发。
因为 cancelActivity=“false”,所以“经理审批”任务仍然存在且有效。
流程从 approvalTimer 分叉出一条新路径,执行 throwEscalationEvent,抛出升级信号。
在流程某处等待的 escalationHandlerSubProcess 的 catchEscalationStart 事件捕获到此信号。
事件子流程被激活,开始执行 notifySupervisorTask(发送邮件给总监)。
通知任务完成后,这个并行的升级分支结束。
此时,主流程的“经理审批”任务依然在等待经理处理。当经理最终处理时,流程会从 managerApprovalTask 继续往下走。
关键点与区别
升级 vs. 错误 (Escalation vs. Error):
错误 (Error) 是为了处理异常、中断性的问题。它意味着“出错了,停止当前操作,走另一条路”。
升级 (Escalation) 是为了处理需要关注、非中断性的问题。它意味着“继续当前操作,但同时通知某人”。