简单来说,取消事件是专门用来处理事务性操作失败并进行回滚的机制。它总是与事务子流程 (Transaction Subprocess) 配合使用,是实现业务流程中“要么全部成功,要么全部回滚”这种原子性操作的关键。
该事件分类比较少只有两种。
1. 取消结束事件 (Cancel End Event)
这个事件用来触发一个取消动作。
图标: 一个带有实心“X”的粗边圆圈。
位置: 只能放置在事务子流程 (Transaction Subprocess) 的内部。(不然会失效!!!!!!!)
作用: 当流程执行到事务子流程内部的这个“取消结束事件”时,它会立即中断子流程当前的执行,并抛出一个“取消”信号。这个信号的目的是告诉外部“这个事务失败了,需要回滚”。
你可以把它想象成在代码块中抛出了一个RollbackException异常。
2. 取消边界事件 (Cancel Boundary Event)
这个事件用来捕获取消动作,并处理后续逻辑。
图标: 一个附着在活动边框上的、带有实心“X”的双边圆圈。
位置: 只能附着在事务子流程的边界上(不然会失效)。
作用: 它的唯一作用就是等待并捕获其所附着的事务子流程内部抛出的“取消”信号。一旦捕获到信号,它会执行以下两个核心动作:
回滚事务: 中断事务子流程中所有正在进行的活动,并**自动撤销(回滚)**所有在这个事务中已经成功完成的活动。Flowable通过补偿(Compensation)机制来隐式实现回滚。
改变流程路径: 流程将从这个边界事件引出的路径继续往下执行,通常这条路径会通向一些处理失败的逻辑,比如“通知用户订单失败”、“记录失败日志”等。
下面我将提供一个非常典型的企业级业务场景——在线下单流程——的BPMN 2.0文件示例。这个场景完美地诠释了取消事件的用途。
业务场景描述
一个客户在电商网站下单,系统需要在一个事务内完成以下三件事:
创建订单: 在数据库中生成一条订单记录。
锁定库存: 为该订单预留商品库存。
处理支付: 调用支付网关进行扣款。
业务规则: 这三个步骤必须构成一个原子操作。如果支付失败,那么之前创建的订单记录必须被删除,锁定的库存也必须被释放。整个下单操作就像从未发生过一样。
<?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:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:flowable="http://flowable.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.flowable.org/processdef">
<process id="orderProcessingWithCancel" name="在线下单流程(含取消回滚)" isExecutable="true">
<startEvent id="start" name="客户提交订单"/>
<!-- 事务子流程: 包含所有需要原子性操作的任务 -->
<transaction id="orderTransactionSubProcess" name="处理订单事务">
<startEvent id="subStart"/>
<!-- 步骤1: 创建订单 (可补偿) -->
<serviceTask id="createOrderTask" name="创建订单记录"
flowable:class="com.example.CreateOrderDelegate"
isForCompensation="true"/>
<!-- 步骤2: 锁定库存 (可补偿) -->
<serviceTask id="reserveInventoryTask" name="锁定商品库存"
flowable:class="com.example.ReserveInventoryDelegate"
isForCompensation="true"/>
<!-- 步骤3: 处理支付 -->
<serviceTask id="processPaymentTask" name="处理支付"
flowable:class="com.example.ProcessPaymentDelegate"/>
<!-- 支付结果判断 -->
<exclusiveGateway id="paymentGateway" name="支付是否成功?"/>
<!-- 支付成功路径 -->
<endEvent id="subEndSuccess" name="事务成功"/>
<!-- 支付失败路径 -> 触发取消 -->
<endEvent id="cancelOrderEndEvent" name="支付失败,取消事务">
<cancelEventDefinition/>
</endEvent>
<!-- 子流程内部流转 -->
<sequenceFlow sourceRef="subStart" targetRef="createOrderTask"/>
<sequenceFlow sourceRef="createOrderTask" targetRef="reserveInventoryTask"/>
<sequenceFlow sourceRef="reserveInventoryTask" targetRef="processPaymentTask"/>
<sequenceFlow sourceRef="processPaymentTask" targetRef="paymentGateway"/>
<sequenceFlow id="flowPaymentSuccess" sourceRef="paymentGateway" targetRef="subEndSuccess">
<conditionExpression xsi:type="tFormalExpression">${paymentSuccess == true}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flowPaymentFailed" sourceRef="paymentGateway" targetRef="cancelOrderEndEvent">
<conditionExpression xsi:type="tFormalExpression">${paymentSuccess == false}</conditionExpression>
</sequenceFlow>
<!-- 补偿任务定义 -->
<!-- 补偿"创建订单"的任务 -->
<serviceTask id="deleteOrderTask" name="补偿:删除订单记录"
isForCompensation="true"
flowable:class="com.example.DeleteOrderDelegate"/>
<association associationDirection="One" sourceRef="deleteOrderTask" targetRef="createOrderTask"/>
<!-- 补偿"锁定库存"的任务 -->
<serviceTask id="releaseInventoryTask" name="补偿:释放库存"
isForCompensation="true"
flowable:class="com.example.ReleaseInventoryDelegate"/>
<association associationDirection="One" sourceRef="releaseInventoryTask" targetRef="reserveInventoryTask"/>
</transaction>
<!-- 边界取消事件: 捕获事务失败信号 -->
<boundaryEvent id="cancelBoundaryEvent" attachedToRef="orderTransactionSubProcess">
<cancelEventDefinition/>
</boundaryEvent>
<!-- 正常成功路径 -->
<serviceTask id="sendOrderConfirmationTask" name="发送订单成功通知"
flowable:class="com.example.SendConfirmationDelegate"/>
<endEvent id="endSuccess" name="流程正常结束"/>
<!-- 失败处理路径 -->
<serviceTask id="notifyUserOfFailureTask" name="通知用户下单失败"
flowable:class="com.example.SendFailureNotificationDelegate"/>
<endEvent id="endFailure" name="流程异常结束"/>
<!-- 主流程流转 -->
<sequenceFlow sourceRef="start" targetRef="orderTransactionSubProcess"/>
<sequenceFlow sourceRef="orderTransactionSubProcess" targetRef="sendOrderConfirmationTask"/>
<sequenceFlow sourceRef="sendOrderConfirmationTask" targetRef="endSuccess"/>
<sequenceFlow sourceRef="cancelBoundaryEvent" targetRef="notifyUserOfFailureTask"/>
<sequenceFlow sourceRef="notifyUserOfFailureTask" targetRef="endFailure"/>
</process>
<!-- BPMN图定义 (为了在Modeler中正确显示) -->
<bpmndi:BPMNDiagram id="BPMNDiagram_orderProcessingWithCancel">
<!-- ... 此处省略了详细的坐标信息,但文件在工具中可以自动生成或解析 ... -->
</bpmndi:BPMNDiagram>
</definitions>
流程关键节点解析
(事务子流程)
这是核心,它将“创建订单”、“锁定库存”和“处理支付”捆绑成一个逻辑单元。Flowable会确保这个子流程的原子性。
(可补偿任务)
isForCompensation=“true” 这个属性非常重要,它标记这个任务是可以被撤销的。只有被标记的任务,在事务回滚时才会被补偿。
createOrderTask 和 reserveInventoryTask 都被标记了。
(取消结束事件)
当支付失败(paymentSuccess == false),流程会走到这里。这个事件的作用就是抛出一个“取消信号”,请求回滚整个事务。
(取消边界事件)
它像一个监听器,附着在事务子流程的边界上,专门等待内部抛出的“取消信号”。
(补偿任务)
这些任务是用来“反向操作”的。例如,deleteOrderTask 的逻辑就是删除订单。
标签将补偿任务(deleteOrderTask)与它要补偿的原始任务(createOrderTask)关联起来。
失败处理流程
一旦cancelBoundaryEvent被触发,流程会沿着它引出的路径继续,即执行 notifyUserOfFailureTask (通知用户下单失败)。
模拟执行流程
场景一:支付成功
流程进入 orderTransactionSubProcess。
createOrderTask 执行成功(订单已创建)。
reserveInventoryTask 执行成功(库存已锁定)。
processPaymentTask 执行成功,并设置流程变量 paymentSuccess = true。
支付网关判断条件为 true,流程走向 subEndSuccess。
事务子流程成功完成,所有操作被提交。
流程离开子流程,继续执行 sendOrderConfirmationTask (发送成功通知),然后正常结束。
场景二:支付失败(取消事件的核心)
流程进入 orderTransactionSubProcess。
createOrderTask 执行成功(订单已创建)。
reserveInventoryTask 执行成功(库存已锁定)。
processPaymentTask 执行失败,设置流程变量 paymentSuccess = false。
支付网关判断条件为 false,流程走向 cancelOrderEndEvent。
cancelOrderEndEvent 被触发,抛出取消信号。
cancelBoundaryEvent 捕获到该信号。
Flowable 引擎自动执行回滚操作:
它会查找事务内所有已完成且可补偿的任务。
它会按照完成顺序的逆序来执行补偿任务。
首先执行 releaseInventoryTask(释放库存)。
然后执行 deleteOrderTask(删除订单)。
回滚完成后,流程从 cancelBoundaryEvent 引出的路径继续执行,即 notifyUserOfFailureTask(通知用户下单失败),然后流程结束。
这个例子清晰地展示了如何使用取消事件来建模需要“全有或全无”的业务逻辑,而无需在代码中编写复杂的、易出错的状态管理和回滚代码。整个回滚过程由流程引擎根据模型声明自动完成。