Flowable20补偿事件-------------持续更新中

想象一个生活中的场景:在线预订一次旅行。
这个流程包含三个步骤:
预订机票(成功)
预订酒店(成功)
预订租车(失败,比如信用卡额度不足)
现在,租车失败了,但机票和酒店已经订好了。整个旅行计划泡汤了,你不能只放着订好的机票和酒店不管。你必须执行“反向操作”来撤销之前的成功步骤:
取消机票预订
取消酒店预订
在 Flowable (以及 BPMN 2.0 规范) 中,这种为已经成功完成的活动执行“反向操作”的机制,就叫做补偿(Compensation)。补偿事件就是用来触发和处理这些补偿行为的。

补偿的核心目的不是处理当前活动的失败,而是为了撤销一个或多个过去已经成功完成的活动。
这和“错误事件(Error Event)”有本质区别:
错误事件:处理的是当前活动执行时发生的错误。比如“预订租车”这个任务本身出错了。
补偿事件:处理的是因为后续流程出现了问题,需要去撤销之前已经成功的活动。比如“预订租车”失败后,去触发“取消机票”和“取消酒店”这两个反向操作。

一个完整的补偿机制通常由以下三部分组成:

  1. 需要被补偿的活动 (Activity to be Compensated)
    这是流程中一个正常的、已经成功完成的活动。例如,“预订机票”这个用户任务或服务任务。要使其能够被补偿,你必须为它定义一个补偿处理器。
  2. 补偿处理器 (Compensation Handler)
    这是真正的“反向操作”逻辑。它定义了如何撤销原始活动。
    在BPMN图中,它通常是一个附加在原始活动边界上的补偿边界事件(Compensation Boundary Event),这个事件图标是一个向左的回退箭头符号。
    从这个边界事件引出的流程就是补偿逻辑,例如连接到一个叫“取消机票预订”的服务任务。
    重要:补偿处理器在原始活动成功完成时被“注册”或“关联”上,但并不执行。它只是静静地等待被将来的补偿事件触发。
    (上图中,“Book Hotel”活动有一个补偿边界事件,它连接到“Cancel Hotel”这个补偿处理器。)
  3. 触发补偿的事件 (Compensation Trigger)
    这是启动补偿流程的“扳机”。主要有两种方式:
    a. 补偿中间抛出事件 (Intermediate Throwing Compensation Event)
    这是一个明确的指令,告诉流程引擎:“开始执行补偿!”。
    你可以配置它来补偿所有在当前作用域(比如主流程或子流程)内已完成且定义了补偿处理器的活动。
    也可以指定只补偿某一个特定的活动。
    在我们的旅行预订例子中,当“预订租车”失败后,流程会走向一个“补偿中间抛出事件”,由它来触发“取消机票”和“取消酒店”这两个补偿处理器。
    b. 补偿结束事件 (Compensation End Event)
    如果一个子流程以“补偿结束事件”结尾,它会自动触发该子流程范围内所有已完成活动的补偿。
    c. (隐式触发) 事务被取消
    如果一个活动包含在一个**事务子流程(Transaction Sub-Process)**中,当这个事务因为错误或取消事件而被回滚时,Flowable会自动对该事务内所有已成功完成的活动执行补偿。这是补偿机制最强大的应用之一,常用于实现分布式事务中的SAGA模式。

业务场景描述

一个客户在线预订一个“机票 + 酒店”的旅游套餐。
系统首先尝试预订机票。
如果机票预订成功,系统接着尝试预订酒店。
如果酒店也预订成功,系统最后进行信用卡扣款。
核心问题:如果机票和酒店都预订成功了,但最后的信用卡扣款失败了(例如,额度不足、卡号无效等),那么整个订单是失败的。此时,必须自动取消之前已经成功预订的机票和酒店,以避免为客户和公司造成损失。
这个“取消”操作,就是补偿机制的完美应用场景。

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 
             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" 
             xmlns:flowable="http://flowable.org/bpmn" 
             targetNamespace="http://flowable.org/bpmn/examples">

  <process id="tripBookingCompensationProcess" name="旅游预订补偿流程" isExecutable="true">

    <!-- 1. 流程开始 -->
    <startEvent id="startEvent" name="开始预订"/>
    <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="bookFlightTask"/>

    <!-- 2. 预订机票 (可被补偿的活动) -->
    <serviceTask id="bookFlightTask" name="预订机票" flowable:class="org.flowable.example.BookFlightDelegate"/>
    <sequenceFlow id="flow2" sourceRef="bookFlightTask" targetRef="bookHotelTask"/>

    <!-- 3. 预订酒店 (可被补偿的活动) -->
    <serviceTask id="bookHotelTask" name="预订酒店" flowable:class="org.flowable.example.BookHotelDelegate"/>
    <sequenceFlow id="flow3" sourceRef="bookHotelTask" targetRef="chargeCreditCardTask"/>

    <!-- 4. 信用卡扣款 (关键决策点) -->
    <serviceTask id="chargeCreditCardTask" name="信用卡扣款" flowable:class="org.flowable.example.ChargeCardDelegate"/>
    <sequenceFlow id="flow4" sourceRef="chargeCreditCardTask" targetRef="paymentGateway"/>

    <!-- 5. 决策网关:判断扣款是否成功 -->
    <exclusiveGateway id="paymentGateway" name="支付成功?"/>
    <sequenceFlow id="flowSuccess" sourceRef="paymentGateway" targetRef="sendConfirmationTask">
      <conditionExpression xsi:type="tFormalExpression" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><![CDATA[${paymentSuccess == true}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flowFailure" sourceRef="paymentGateway" targetRef="throwCompensationEvent">
      <conditionExpression xsi:type="tFormalExpression" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><![CDATA[${paymentSuccess == false}]]></conditionExpression>
    </sequenceFlow>
    
    <!-- 6. 成功路径 -->
    <serviceTask id="sendConfirmationTask" name="发送预订成功确认" flowable:class="org.flowable.example.SendSuccessEmailDelegate"/>
    <sequenceFlow id="flowEndSuccess" sourceRef="sendConfirmationTask" targetRef="endEventSuccess"/>
    <endEvent id="endEventSuccess" name="预订成功"/>

    <!-- 7. 失败路径 -->
    <!-- 7a. 触发补偿的事件 -->
    <intermediateThrowEvent id="throwCompensationEvent" name="触发补偿">
      <compensateEventDefinition waitForCompletion="true"/>
    </intermediateThrowEvent>
    <sequenceFlow id="flowAfterCompensation" sourceRef="throwCompensationEvent" targetRef="sendFailureNotificationTask"/>

    <!-- 7b. 发送失败通知 -->
    <serviceTask id="sendFailureNotificationTask" name="发送预订失败通知" flowable:class="org.flowable.example.SendFailureEmailDelegate"/>
    <sequenceFlow id="flowEndFailure" sourceRef="sendFailureNotificationTask" targetRef="endEventFailure"/>
    <endEvent id="endEventFailure" name="预订失败"/>

    <!-- 8. 补偿处理器 (Compensation Handlers) -->
    <!-- 补偿处理器:取消机票 -->
    <boundaryEvent id="compensateBoundaryFlight" attachedToRef="bookFlightTask">
      <compensateEventDefinition/>
    </boundaryEvent>
    <serviceTask id="cancelFlightTask" name="取消机票预订" isForCompensation="true" flowable:class="org.flowable.example.CancelFlightDelegate"/>
    <association id="assoc1" sourceRef="compensateBoundaryFlight" targetRef="cancelFlightTask" />

    <!-- 补偿处理器:取消酒店 -->
    <boundaryEvent id="compensateBoundaryHotel" attachedToRef="bookHotelTask">
      <compensateEventDefinition/>
    </boundaryEvent>
    <serviceTask id="cancelHotelTask" name="取消酒店预订" isForCompensation="true" flowable:class="org.flowable.example.CancelHotelDelegate"/>
    <association id="assoc2" sourceRef="compensateBoundaryHotel" targetRef="cancelHotelTask" />
    
  </process>

  <!-- BPMN 图形定义部分 -->
  <bpmndi:BPMNDiagram id="BPMNDiagram_tripBookingCompensationProcess">
    <bpmndi:BPMNPlane bpmnElement="tripBookingCompensationProcess" id="BPMNPlane_tripBookingCompensationProcess">
      <bpmndi:BPMNShape bpmnElement="startEvent" id="BPMNShape_startEvent">
        <omgdc:Bounds height="36.0" width="36.0" x="100.0" y="200.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="bookFlightTask" id="BPMNShape_bookFlightTask">
        <omgdc:Bounds height="60.0" width="100.0" x="180.0" y="188.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="bookHotelTask" id="BPMNShape_bookHotelTask">
        <omgdc:Bounds height="60.0" width="100.0" x="330.0" y="188.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="chargeCreditCardTask" id="BPMNShape_chargeCreditCardTask">
        <omgdc:Bounds height="60.0" width="100.0" x="480.0" y="188.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="paymentGateway" id="BPMNShape_paymentGateway" isMarkerVisible="true">
        <omgdc:Bounds height="40.0" width="40.0" x="630.0" y="198.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sendConfirmationTask" id="BPMNShape_sendConfirmationTask">
        <omgdc:Bounds height="60.0" width="100.0" x="720.0" y="188.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endEventSuccess" id="BPMNShape_endEventSuccess">
        <omgdc:Bounds height="36.0" width="36.0" x="870.0" y="200.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="throwCompensationEvent" id="BPMNShape_throwCompensationEvent">
        <omgdc:Bounds height="36.0" width="36.0" x="632.0" y="300.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sendFailureNotificationTask" id="BPMNShape_sendFailureNotificationTask">
        <omgdc:Bounds height="60.0" width="100.0" x="720.0" y="288.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endEventFailure" id="BPMNShape_endEventFailure">
        <omgdc:Bounds height="36.0" width="36.0" x="870.0" y="300.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="compensateBoundaryFlight" id="BPMNShape_compensateBoundaryFlight">
        <omgdc:Bounds height="30.0" width="30.0" x="255.0" y="173.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="cancelFlightTask" id="BPMNShape_cancelFlightTask">
        <omgdc:Bounds height="60.0" width="100.0" x="290.0" y="90.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="compensateBoundaryHotel" id="BPMNShape_compensateBoundaryHotel">
        <omgdc:Bounds height="30.0" width="30.0" x="405.0" y="173.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="cancelHotelTask" id="BPMNShape_cancelHotelTask">
        <omgdc:Bounds height="60.0" width="100.0" x="440.0" y="90.0"/>
      </bpmndi:BPMNShape>

      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="136.0" y="218.0"/>
        <omgdi:waypoint x="180.0" y="218.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="280.0" y="218.0"/>
        <omgdi:waypoint x="330.0" y="218.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="430.0" y="218.0"/>
        <omgdi:waypoint x="480.0" y="218.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="580.0" y="218.0"/>
        <omgdi:waypoint x="630.0" y="218.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flowSuccess" id="BPMNEdge_flowSuccess">
        <omgdi:waypoint x="670.0" y="218.0"/>
        <omgdi:waypoint x="720.0" y="218.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flowFailure" id="BPMNEdge_flowFailure">
        <omgdi:waypoint x="650.0" y="238.0"/>
        <omgdi:waypoint x="650.0" y="300.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flowAfterCompensation" id="BPMNEdge_flowAfterCompensation">
        <omgdi:waypoint x="668.0" y="318.0"/>
        <omgdi:waypoint x="720.0" y="318.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flowEndSuccess" id="BPMNEdge_flowEndSuccess">
        <omgdi:waypoint x="820.0" y="218.0"/>
        <omgdi:waypoint x="870.0" y="218.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flowEndFailure" id="BPMNEdge_flowEndFailure">
        <omgdi:waypoint x="820.0" y="318.0"/>
        <omgdi:waypoint x="870.0" y="318.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="assoc1" id="BPMNEdge_assoc1">
        <omgdi:waypoint x="270.0" y="173.0"/>
        <omgdi:waypoint x="340.0" y="150.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="assoc2" id="BPMNEdge_assoc2">
        <omgdi:waypoint x="420.0" y="173.0"/>
        <omgdi:waypoint x="490.0" y="150.0"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

文件核心逻辑解读

可被补偿的活动 (Compensatable Activities)
bookFlightTask (预订机票) 和 bookHotelTask (预订酒店) 是两个普通的服务任务。
它们各自附加了一个补偿边界事件 (compensateBoundaryFlight 和 compensateBoundaryHotel)。这个图标是一个向左的回退箭头。这表明这两个任务如果成功完成了,是可以被补偿的。
补偿处理器 (Compensation Handlers)
cancelFlightTask (取消机票预订) 和 cancelHotelTask (取消酒店预订) 是补偿逻辑的实际执行者。
它们被一个虚线(在XML中是)连接到对应的边界事件上。
关键属性:isForCompensation=“true”,这明确告诉Flowable引擎,这不是一个常规任务,而是一个补偿任务。它不会通过常规的顺序流被触发。
补偿触发 (Compensation Trigger)
当流程执行到 chargeCreditCardTask (信用卡扣款) 后,会有一个业务逻辑(代码中ChargeCardDelegate)设置一个流程变量,例如 paymentSuccess (布尔值)。
paymentGateway (排他网关) 会根据 paymentSuccess 的值来决定流程走向。
如果失败 (paymentSuccess == false),流程会走向 throwCompensationEvent。这是一个补偿中间抛出事件,它的图标是实心的回退箭头。
这个事件被触发时,Flowable引擎会:
自动查找当前流程范围内所有已经成功完成、并且定义了补偿处理器的活动(即“预订机票”和“预订酒店”)。
并行执行它们的补偿处理器(即“取消机票预订”和“取消酒店预订”)。
waitForCompletion=“true” 属性确保主流程会等待所有补偿活动都完成后,才继续向下执行。
流程路径
成功路径:开始 -> 预订机票 -> 预订酒店 -> 信用卡扣款 -> [成功] -> 发送成功确认 -> 结束。
失败(补偿)路径:开始 -> 预订机票 -> 预订酒店 -> 信用卡扣款 -> [失败] -> 触发补偿 -> (引擎在此自动执行取消机票和取消酒店) -> 发送失败通知 -> 结束。
这个例子清晰地展示了如何使用补偿事件来处理一个需要“回滚”已完成步骤的复杂业务场景,是SAGA模式的一种典型实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值