1.中间事件核心概念

  • 定位:位于开始事件与结束事件之间,影响流程路由但不启动/终止流程。

  • 两类中间事件:

    • 捕获事件(Catch):等待外部触发(如消息、信号到达)。

    • 抛出事件(Throw):主动触发事件(如发送信号、抛出异常)。

  • 与边界事件区别:边界事件附着在活动节点上,而中间事件独立存在于连线上。


1.1 中间抛出事件

1. 空中间抛出事件

  • 作用:流程状态标记点,通过监听器执行自定义逻辑。

  • XML示例:

    <process id="noneEventProcess">
      <intermediateThrowEvent id="statusMarker">
        <extensionElements>
          <flowable:executionListener 
            event="start"
            class="com.example.ProcessStatusListener"/> <!-- 监听状态变更 -->
        </extensionElements>
      </intermediateThrowEvent>
    </process>
  • 使用场景:记录流程进入关键节点(如"订单已审核"),触发数据库日志记录。

  • 定义创建了一个简单流程,其中包含一个无类型中间抛出事件,当流程执行到这个事件时,会触发指定的 Java 类(MyExecutionListener)中的逻辑。这种模式通常用于在流程执行过程中插入自定义业务逻辑、发送通知或与外部系统交互等场景。


2. 信号中间抛出事件

在流程执行过程中发送特定消息的机制

实例说明

具体实例

假设我们有一个订单处理流程,当订单确认后需要向支付系统发送支付请求,这就是消息中间抛出事件的典型应用场景。

BPMN XML 完整定义:

<?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"
             xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd"
             id="Definitions_1"
             targetNamespace="http://camunda.org/examples">

  <!-- 定义消息 -->
  <message id="paymentRequestMsg" name="订单支付请求" />
  <message id="paymentResponseMsg" name="支付结果响应" />

  <!-- 订单流程 - 包含消息中间抛出事件 -->
  <process id="orderProcess" name="订单处理流程" isExecutable="true">
    <startEvent id="start" />
    
    <sequenceFlow id="flow1" sourceRef="start" targetRef="confirmOrder" />
    
    <userTask id="confirmOrder" name="确认订单" />
    
    <sequenceFlow id="flow2" sourceRef="confirmOrder" targetRef="sendPaymentRequest" />
    
    <!-- 消息中间抛出事件:发送支付请求 -->
    <intermediateThrowEvent id="sendPaymentRequest">
      <messageEventDefinition messageRef="paymentRequestMsg" />
    </intermediateThrowEvent>
    
    <sequenceFlow id="flow3" sourceRef="sendPaymentRequest" targetRef="waitPaymentResponse" />
    
    <!-- 消息中间捕获事件:等待支付响应 -->
    <intermediateCatchEvent id="waitPaymentResponse">
      <messageEventDefinition messageRef="paymentResponseMsg" />
    </intermediateCatchEvent>
    
    <sequenceFlow id="flow4" sourceRef="waitPaymentResponse" targetRef="end" />
    
    <endEvent id="end" />
  </process>

  <!-- 支付流程 - 接收支付请求 -->
  <process id="paymentProcess" name="支付处理流程" isExecutable="true">
    <!-- 消息开始事件:接收支付请求 -->
    <startEvent id="paymentStart">
      <messageEventDefinition messageRef="paymentRequestMsg" />
    </startEvent>
    
    <sequenceFlow id="flow5" sourceRef="paymentStart" targetRef="processPayment" />
    
    <serviceTask id="processPayment" name="处理支付" />
    
    <sequenceFlow id="flow6" sourceRef="processPayment" targetRef="sendPaymentResponse" />
    
    <!-- 消息中间抛出事件:发送支付响应 -->
    <intermediateThrowEvent id="sendPaymentResponse">
      <messageEventDefinition messageRef="paymentResponseMsg" />
    </intermediateThrowEvent>
    
    <sequenceFlow id="flow7" sourceRef="sendPaymentResponse" targetRef="paymentEnd" />
    
    <endEvent id="paymentEnd" />
  </process>
</definitions>
// 1. 部署流程定义
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment()
    .addClasspathResource("order-payment-process.bpmn20.xml")
    .deploy();

// 2. 启动订单流程
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance orderProcessInstance = runtimeService.startProcessInstanceByKey("orderProcess");
System.out.println("订单流程已启动,ID: " + orderProcessInstance.getId());

// 3. 完成"确认订单"任务
TaskService taskService = processEngine.getTaskService();
Task confirmTask = taskService.createTaskQuery()
    .processInstanceId(orderProcessInstance.getId())
    .singleResult();
taskService.complete(confirmTask.getId());

// 此时流程会执行到sendPaymentRequest节点,发送支付请求消息
// 支付流程会自动启动并处理支付(因为有消息开始事件)

// 4. 模拟支付完成后,发送支付响应消息
// 查找等待支付响应的执行实例
Execution execution = runtimeService.createExecutionQuery()
    .processInstanceId(orderProcessInstance.getId())
    .messageEventSubscriptionName("paymentResponseMsg")
    .singleResult();

// 发送响应消息
runtimeService.messageEventReceived("paymentResponseMsg", execution.getId());
System.out.println("支付响应已发送,订单流程将继续执行");

  • 作用:全局广播信号,触发所有匹配的信号捕获事件(包括开始/边界/中间事件)。

  • XML定义:

    <signal id="alertSignal" name="紧急告警" /> <!-- 信号定义 -->
    <process id="signalProcess">
      <intermediateThrowEvent id="throwAlert">
        <signalEventDefinition signalRef="alertSignal" /> <!-- 抛出信号 -->
      </intermediateThrowEvent>
    </process>
  • 触发效果:

    • 并行流程中订阅了alertSignal的信号边界事件会中断当前任务。

    • 信号开始事件启动新流程实例。

  • 消息中间抛出事件

    • 是 BPMN 中的一种事件类型,用于在流程执行到特定节点时主动发送消息
    • 实现的是点对点通信模式,需要有明确的消息接收方
    • 通常用于不同流程之间或流程与外部系统之间的通信
  • XML 定义解析

    • <message> 标签:定义消息元数据,id是唯一标识,name是可读名称
    • <intermediateThrowEvent> 标签:定义中间抛出事件
    • <messageEventDefinition> 标签:将事件与特定消息关联,messageRef引用消息的id
  • 接收机制

    • 必须有对应的消息捕获事件(如消息中间捕获事件或消息边界事件)来接收此消息
    • 形成了 "发送 - 接收" 的消息传递模式
  • 整个流程包含两个子流程:订单处理流程和支付处理流程
  • 订单流程在确认订单后,通过 "消息中间抛出事件" 发送支付请求
  • 支付流程通过 "消息开始事件" 接收支付请求并处理
  • 支付完成后,支付流程通过另一个 "消息中间抛出事件" 发送支付响应
  • 订单流程通过 "消息中间捕获事件" 等待并接收支付响应,然后继续执行后续流程

实例说明

  1. 整个流程包含两个子流程:订单处理流程和支付处理流程
  2. 订单流程在确认订单后,通过 "消息中间抛出事件" 发送支付请求
  3. 支付流程通过 "消息开始事件" 接收支付请求并处理
  4. 支付完成后,支付流程通过另一个 "消息中间抛出事件" 发送支付响应
  5. 订单流程通过 "消息中间捕获事件" 等待并接收支付响应,然后继续执行后续流程

3. 消息中间抛出事件

  • 作用:向特定接收方发送消息(点对点通信)。

  • XML定义:

    <message id="paymentMsg" name="支付请求" /> <!-- 消息定义 -->
    <process id="paymentProcess">
      <intermediateThrowEvent id="sendPayment">
        <messageEventDefinition messageRef="paymentMsg" /> <!-- 抛出消息 -->
      </intermediateThrowEvent>
    </process>
  • 接收方:必须存在对应的消息捕获事件(如消息中间捕获事件)。

  • 实例:

    // 外部系统通过API触发消息
    runtimeService.messageEventReceived("paymentMsg", executionId); 

    流程在sendPayment节点暂停,直到外部系统调用此API。

  • xml示例

<!-- 消息中间抛出事件:发送支付请求 -->
    <intermediateThrowEvent id="sendPaymentRequest">
      <messageEventDefinition messageRef="paymentRequestMsg" />
    </intermediateThrowEvent>
    
    <sequenceFlow id="flow3" sourceRef="sendPaymentRequest" targetRef="waitPaymentResponse" />
    
    <!-- 消息中间捕获事件:等待支付响应 -->
    <intermediateCatchEvent id="waitPaymentResponse">
      <messageEventDefinition messageRef="paymentResponseMsg" />
    </intermediateCatchEvent>

4. 补偿中间抛出事件

  • 作用:用于在流程出现异常或需要取消操作时,触发已完成活动的补偿逻辑,恢复到之前的状态。

  • XML定义:

    <intermediateThrowEvent id="undoActions">
      <compensateEventDefinition /> <!-- 触发补偿 -->
    </intermediateThrowEvent>
  • XML 定义解析

    • <intermediateThrowEvent id="undoActions">:定义补偿中间抛出事件
    • <compensateEventDefinition />:标识这是一个补偿事件,用于触发补偿逻辑
  • 补偿规则:

  • 触发补偿:当流程执行到补偿中间抛出事件时,会触发已完成活动的补偿处理器
  • 事务回滚:用于撤销之前的操作,恢复系统状态
  • 补偿处理器:为需要补偿的活动定义的撤销逻辑(如取消订单、退款等)
  • 补偿规则详解

    • 反向触发:按活动完成顺序的逆序执行补偿(最后完成的活动最先补偿)
    • 作用域限制:仅补偿同一子流程层级中定义了补偿处理器的活动
    • 多实例活动:需等待所有实例都完成后才会触发补偿
  • 补偿处理器定义

    • 通过边界事件<boundaryEvent>定义,需指定attachedToRef关联到需要补偿的活动
    • 包含<compensateEventDefinition />标识这是一个补偿处理器
  • 实例:

    <?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"
                 xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd http://flowable.org/bpmn http://flowable.org/bpmn/current/flowable-bpmn.xsd"
                 id="Definitions_1"
                 targetNamespace="http://flowable.org/examples">
    
      <process id="travelBookingProcess" name="旅行预订流程" isExecutable="true">
        <startEvent id="start" />
        
        <sequenceFlow id="flow1" sourceRef="start" targetRef="bookFlight" />
        
        <!-- 预订航班服务任务 -->
        <serviceTask id="bookFlight" name="预订航班" flowable:class="com.example.BookFlightService">
          <extensionElements>
            <flowable:executionListener event="end" class="com.example.LogFlightBookingListener" />
          </extensionElements>
        </serviceTask>
        
        <!-- 航班预订的补偿处理器 -->
        <boundaryEvent id="compensateFlight" attachedToRef="bookFlight" cancelActivity="false">
          <compensateEventDefinition />
        </boundaryEvent>
        
        <!-- 补偿航班预订的服务任务 -->
        <serviceTask id="undoFlightBooking" name="取消航班预订" flowable:class="com.example.CancelFlightBookingService">
          <extensionElements>
            <flowable:executionListener event="end" class="com.example.LogFlightCancellationListener" />
          </extensionElements>
        </serviceTask>
        
        <sequenceFlow id="flow2" sourceRef="compensateFlight" targetRef="undoFlightBooking" />
        <sequenceFlow id="flow3" sourceRef="undoFlightBooking" targetRef="compensationEnd" />
        
        <sequenceFlow id="flow4" sourceRef="bookFlight" targetRef="bookHotel" />
        
        <!-- 预订酒店服务任务 -->
        <serviceTask id="bookHotel" name="预订酒店" flowable:class="com.example.BookHotelService">
          <extensionElements>
            <flowable:executionListener event="end" class="com.example.LogHotelBookingListener" />
          </extensionElements>
        </serviceTask>
        
        <!-- 酒店预订的补偿处理器 -->
        <boundaryEvent id="compensateHotel" attachedToRef="bookHotel" cancelActivity="false">
          <compensateEventDefinition />
        </boundaryEvent>
        
        <!-- 补偿酒店预订的服务任务 -->
        <serviceTask id="undoHotelBooking" name="取消酒店预订" flowable:class="com.example.CancelHotelBookingService">
          <extensionElements>
            <flowable:executionListener event="end" class="com.example.LogHotelCancellationListener" />
          </extensionElements>
        </serviceTask>
        
        <sequenceFlow id="flow5" sourceRef="compensateHotel" targetRef="undoHotelBooking" />
        <sequenceFlow id="flow6" sourceRef="undoHotelBooking" targetRef="compensationEnd" />
        
        <sequenceFlow id="flow7" sourceRef="bookHotel" targetRef="paymentCheck" />
        
        <!-- 检查支付状态网关 -->
        <exclusiveGateway id="paymentCheck" />
        
        <!-- 支付成功路径 -->
        <sequenceFlow id="flow8" sourceRef="paymentCheck" targetRef="bookingSuccess">
          <conditionExpression xsi:type="tFormalExpression">${paymentSuccessful == true}</conditionExpression>
        </sequenceFlow>
        
        <!-- 支付失败路径 - 触发补偿 -->
        <sequenceFlow id="flow9" sourceRef="paymentCheck" targetRef="undoActions">
          <conditionExpression xsi:type="tFormalExpression">${paymentSuccessful == false}</conditionExpression>
        </sequenceFlow>
        
        <!-- 补偿中间抛出事件 -->
        <intermediateThrowEvent id="undoActions">
          <compensateEventDefinition />
        </intermediateThrowEvent>
        
        <sequenceFlow id="flow10" sourceRef="undoActions" targetRef="compensationEnd" />
        
        <!-- 预订成功结束事件 -->
        <endEvent id="bookingSuccess" />
        
        <!-- 补偿完成结束事件 -->
        <endEvent id="compensationEnd" />
      </process>
    </definitions>
    // 部署流程
    repositoryService.createDeployment()
        .addClasspathResource("travel-booking-process.bpmn20.xml")
        .deploy();
    
    // 启动流程实例
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("travelBookingProcess");
    
    // 模拟支付失败的情况
    Task paymentTask = taskService.createTaskQuery()
        .processInstanceId(processInstance.getId())
        .taskName("检查支付状态")
        .singleResult();
    
    // 设置支付失败变量
    Map<String, Object> variables = new HashMap<>();
    variables.put("paymentSuccessful", false);
    taskService.complete(paymentTask.getId(), variables);
    
    // 此时流程会执行到undoActions节点,触发补偿
    // 补偿顺序:先取消酒店预订(后完成的活动),再取消航班预订(先完成的活动)

    实例执行流程说明

  • 正常流程:

    • 开始 → 预订航班 → 预订酒店 → 检查支付状态 → 支付成功 → 流程结束
  • 补偿触发流程(当支付失败时):

    • 开始 → 预订航班 → 预订酒店 → 检查支付状态 → 支付失败 → 触发补偿中间事件 (undoActions)
    • 补偿执行顺序(反向执行):
      1. 触发酒店预订的补偿处理器 → 执行取消酒店预订
      2. 触发航班预订的补偿处理器 → 执行取消航班预订
    • 补偿完成 → 流程结束


5. 升级中间抛出事件(Escalation)

  • 作用:向父流程传递异常(非错误),不中断当前流程。

  • 基本作用

    • 跨层级通知:向父流程传递特定事件信息
    • 非中断性:抛出事件后,当前流程(子流程)继续执行
    • 异常传递:用于传递需要上级处理的特殊情况(非错误)
  • XML 定义解析

    • <intermediateThrowEvent id="escalateIssue">:定义升级中间抛出事件
    • <escalationEventDefinition escalationRef="managerApproval" />:关联到特定升级定义,escalationRef引用升级事件的 ID
  • 工作机制

    • 子流程中定义升级抛出事件
    • 父流程中需定义对应的升级捕获事件(通常是边界事件)
    • 事件触发后,父流程执行相应处理,子流程继续原有路径
  • 与错误事件的关键区别

    • 错误事件:抛出后会终止当前流程路径,通常用于处理异常情况
    • 升级事件:仅向上传递信息,不影响当前流程执行,用于需要上级关注的业务情况
  • 详细实例:采购审批流程

  • <?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"
                 xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd http://flowable.org/bpmn http://flowable.org/bpmn/current/flowable-bpmn.xsd"
                 id="Definitions_1"
                 targetNamespace="http://flowable.org/examples">
    
      <!-- 定义升级事件 -->
      <escalation id="managerApproval" name="需要经理审批" />
      
      <!-- 父流程:采购管理流程 -->
      <process id="purchaseManagementProcess" name="采购管理流程" isExecutable="true">
        <startEvent id="parentStart" />
        
        <sequenceFlow id="flow1" sourceRef="parentStart" targetRef="subProcess" />
        
        <!-- 子流程:采购审批流程 -->
        <subProcess id="subProcess" name="采购审批流程">
          <startEvent id="subStart" />
          
          <sequenceFlow id="flow2" sourceRef="subStart" targetRef="checkAmount" />
          
          <!-- 检查采购金额 -->
          <serviceTask id="checkAmount" name="检查采购金额" flowable:class="com.example.CheckPurchaseAmountService" />
          
          <sequenceFlow id="flow3" sourceRef="checkAmount" targetRef="amountGateway" />
          
          <!-- 金额判断网关 -->
          <exclusiveGateway id="amountGateway" />
          
          <!-- 金额正常路径 -->
          <sequenceFlow id="flow4" sourceRef="amountGateway" targetRef="departmentApproval">
            <conditionExpression xsi:type="tFormalExpression">${amount <= 10000}</conditionExpression>
          </sequenceFlow>
          
          <!-- 金额超限路径 - 触发升级 -->
          <sequenceFlow id="flow5" sourceRef="amountGateway" targetRef="escalateIssue">
            <conditionExpression xsi:type="tFormalExpression">${amount > 10000}</conditionExpression>
          </sequenceFlow>
          
          <!-- 升级中间抛出事件 -->
          <intermediateThrowEvent id="escalateIssue">
            <escalationEventDefinition escalationRef="managerApproval" />
          </intermediateThrowEvent>
          
          <sequenceFlow id="flow6" sourceRef="escalateIssue" targetRef="departmentApproval" />
          
          <!-- 部门审批 -->
          <userTask id="departmentApproval" name="部门审批" />
          
          <sequenceFlow id="flow7" sourceRef="departmentApproval" targetRef="subEnd" />
          
          <endEvent id="subEnd" />
        </subProcess>
        
        <!-- 父流程中捕获升级事件的边界事件 -->
        <boundaryEvent id="catchEscalation" attachedToRef="subProcess">
          <escalationEventDefinition escalationRef="managerApproval" />
        </boundaryEvent>
        
        <sequenceFlow id="flow8" sourceRef="catchEscalation" targetRef="notifyManager" />
        
        <!-- 通知经理处理 -->
        <serviceTask id="notifyManager" name="通知经理" flowable:class="com.example.NotifyManagerService" />
        
        <sequenceFlow id="flow9" sourceRef="notifyManager" targetRef="parentEnd" />
        <sequenceFlow id="flow10" sourceRef="subProcess" targetRef="parentEnd" />
        
        <endEvent id="parentEnd" />
      </process>
    </definitions>

    Java 代码实现示例:

  • 检查采购金额的服务类:
  •  
    public class CheckPurchaseAmountService implements JavaDelegate {
        @Override
        public void execute(DelegateExecution execution) {
            // 获取采购金额(实际应用中从流程变量获取)
            double amount = (double) execution.getVariable("purchaseAmount");
            System.out.println("检查采购金额: " + amount);
            
            // 设置金额变量用于网关判断
            execution.setVariable("amount", amount);
        }
    }
    

  • 通知经理的服务类:
  •  
    public class NotifyManagerService implements JavaDelegate {
        @Override
        public void execute(DelegateExecution execution) {
            double amount = (double) execution.getVariable("amount");
            String purchaseId = (String) execution.getVariable("purchaseId");
            
            System.out.println("发送通知给经理: 采购单 " + purchaseId + 
                               " 金额为 " + amount + ",超过审批阈值需要关注");
            // 实际应用中这里会发送邮件、消息等
        }
    }

// 部署流程定义
repositoryService.createDeployment()
    .addClasspathResource("purchase-approval-process.bpmn20.xml")
    .deploy();

// 启动流程实例,设置采购金额为15000(超过10000阈值)
Map<String, Object> variables = new HashMap<>();
variables.put("purchaseId", "PO-" + System.currentTimeMillis());
variables.put("purchaseAmount", 15000.00);

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
    "purchaseManagementProcess", variables);

// 完成部门审批任务
Task departmentTask = taskService.createTaskQuery()
    .processInstanceId(processInstance.getId())
    .taskName("部门审批")
    .singleResult();

taskService.complete(departmentTask.getId());

实例执行流程说明

  1. 正常执行路径:

    • 父流程启动 → 进入子流程 → 检查采购金额(15000 元)
    • 金额超过阈值(10000 元)→ 触发升级中间抛出事件 (escalateIssue)
    • 子流程继续执行 → 部门审批 → 子流程结束 → 父流程结束
  2. 升级事件处理路径(并行发生):

    • 升级事件触发 → 父流程的边界事件 (catchEscalation) 捕获事件
    • 执行通知经理服务 → 完成经理通知流程
  3. 关键特性体现:

    • 升级事件不中断子流程,子流程继续完成部门审批
    • 父流程同时处理升级事件,发送通知给经理
    • 实现了 "异常情况上报" 但 "业务继续处理" 的业务需求

注:父子事件联系如下:

<!-- 父流程中的子流程节点 -->
<subProcess id="subProcess" name="采购审批流程">
  <!-- 子流程内部逻辑(包含升级抛出事件) -->
</subProcess>

<!-- 绑定到子流程的升级边界事件 -->
<boundaryEvent id="catchEscalation" attachedToRef="subProcess">
  <!-- 关联到子流程抛出的升级事件ID -->
  <escalationEventDefinition escalationRef="managerApproval" />
</boundaryEvent>

关键区别总结

事件类型方向触发方式作用范围
信号抛出抛出全局广播跨流程实例
消息抛出抛出点对点发送指定接收方
补偿抛出抛出触发补偿处理器当前作用域
信号/消息捕获捕获等待外部触发当前流程节点
计时器捕获捕获时间到达后自动触发当前节点

最佳实践建议

  1. 空事件:结合监听器实现审计日志或指标统计。

  2. 信号事件:跨流程协同(如主流程通知子流程)。

  3. 消息事件:与外部系统集成(如等待支付回调)。

  4. 补偿事件:实现SAGA事务模式,确保数据一致性。

  5. 升级事件:构建分层异常处理机制(子流程→父流程)。


1.2 中间捕获事件

1. 计时器中间捕获事件

  • 作用:延迟等待(如"3天后继续")。

  • <intermediateCatchEvent id="wait3Days">
      <timerEventDefinition>
        <timeDuration>PT72H</timeDuration> <!-- 等待72小时 -->
      </timerEventDefinition>
    </intermediateCatchEvent>
  • 实例展示
  • <?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"
                 xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd"
                 id="Definitions_1"
                 targetNamespace="http://example.org">
    
      <process id="leaveRequestProcess" name="假期申请流程" isExecutable="true">
        <startEvent id="start" />
        
        <sequenceFlow id="flow1" sourceRef="start" targetRef="submitRequest" />
        
        <!-- 员工提交假期申请 -->
        <userTask id="submitRequest" name="提交假期申请" />
        
        <sequenceFlow id="flow2" sourceRef="submitRequest" targetRef="managerApproval" />
        
        <!-- 经理审批 -->
        <userTask id="managerApproval" name="经理审批" />
        
        <sequenceFlow id="flow3" sourceRef="managerApproval" targetRef="end" />
        
        <!-- 绑定到经理审批的边界事件:如果超时未审批则触发 -->
        <boundaryEvent id="approvalTimeout" attachedToRef="managerApproval">
          <!-- 计时器中间捕获事件定义:等待3天(72小时) -->
          <timerEventDefinition>
            <timeDuration>PT72H</timeDuration> <!-- 时间格式:PT代表时间周期,72H表示72小时 -->
          </timerEventDefinition>
        </boundaryEvent>
        
        <sequenceFlow id="flow4" sourceRef="approvalTimeout" targetRef="sendReminder" />
        
        <!-- 发送审批提醒 -->
        <serviceTask id="sendReminder" name="发送审批提醒" />
        
        <sequenceFlow id="flow5" sourceRef="sendReminder" targetRef="managerApproval" />
        
        <endEvent id="end" />
      </process>
    </definitions>

    流程说明:

  • 员工提交假期申请后,流程进入 "经理审批" 环节
  • 同时,绑定在审批环节上的计时器开始计时(等待 72 小时)
  • 如果经理在 3 天内完成审批,流程直接结束
  • 如果 3 天内未审批,计时器事件触发,执行 "发送审批提醒"
  • 发送提醒后,流程回到 "经理审批" 环节,等待经理处理

2. 信号/消息中间捕获事件

信号 / 消息中间捕获事件是 BPMN 中用于接收外部信号或消息的机制,与对应的抛出事件配合使用,实现流程间或流程与外部系统的异步通信。其中:

  • 消息中间捕获事件:接收特定的点对点消息,通常用于两个明确流程之间的通信
  • 信号中间捕获事件:接收广播式信号,可被多个流程实例同时捕获

两者均会使流程暂停,等待对应的事件触发后再继续执行。

消息中间捕获事件实例:订单支付流程

以下是一个电商订单流程的示例,订单创建后会等待支付系统发送的支付完成消息,收到消息后才继续后续的发货流程。

<?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"
             xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd http://flowable.org/bpmn http://flowable.org/bpmn/current/flowable-bpmn.xsd"
             id="Definitions_1"
             targetNamespace="http://flowable.org/examples">

  <!-- 定义支付消息 -->
  <message id="paymentMsg" name="支付完成消息" />
  
  <!-- 订单处理流程 -->
  <process id="orderProcess" name="订单处理流程" isExecutable="true">
    <startEvent id="start" />
    
    <sequenceFlow id="flow1" sourceRef="start" targetRef="createOrder" />
    
    <!-- 创建订单 -->
    <serviceTask id="createOrder" name="创建订单" flowable:class="com.example.CreateOrderService" />
    
    <sequenceFlow id="flow2" sourceRef="createOrder" targetRef="waitPayment" />
    
    <!-- 消息中间捕获事件:等待支付完成消息 -->
    <intermediateCatchEvent id="waitPayment">
      <messageEventDefinition messageRef="paymentMsg" />
    </intermediateCatchEvent>
    
    <sequenceFlow id="flow3" sourceRef="waitPayment" targetRef="arrangeDelivery" />
    
    <!-- 安排发货 -->
    <serviceTask id="arrangeDelivery" name="安排发货" flowable:class="com.example.ArrangeDeliveryService" />
    
    <sequenceFlow id="flow4" sourceRef="arrangeDelivery" targetRef="end" />
    
    <endEvent id="end" />
  </process>
  
  <!-- 支付处理流程 -->
  <process id="paymentProcess" name="支付处理流程" isExecutable="true">
    <startEvent id="paymentStart" />
    
    <sequenceFlow id="flow5" sourceRef="paymentStart" targetRef="processPayment" />
    
    <!-- 处理支付 -->
    <serviceTask id="processPayment" name="处理支付" flowable:class="com.example.ProcessPaymentService" />
    
    <sequenceFlow id="flow6" sourceRef="processPayment" targetRef="sendPaymentMsg" />
    
    <!-- 消息中间抛出事件:发送支付完成消息 -->
    <intermediateThrowEvent id="sendPaymentMsg">
      <messageEventDefinition messageRef="paymentMsg" />
    </intermediateThrowEvent>
    
    <sequenceFlow id="flow7" sourceRef="sendPaymentMsg" targetRef="paymentEnd" />
    
    <endEvent id="paymentEnd" />
  </process>
</definitions>

Java 代码实现示例:

  1. 创建订单服务类:
public class CreateOrderService implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        String orderId = "ORDER-" + System.currentTimeMillis();
        execution.setVariable("orderId", orderId);
        System.out.println("订单创建成功,订单ID: " + orderId);
        // 实际业务中会保存订单信息到数据库
    }
}
  1. 处理支付服务类:
public class ProcessPaymentService implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        String orderId = (String) execution.getVariable("orderId");
        System.out.println("处理订单 " + orderId + " 的支付...");
        // 实际业务中会调用支付网关处理支付
        execution.setVariable("paymentStatus", "SUCCESS");
    }
}
  1. 流程交互代码:
// 1. 部署流程
repositoryService.createDeployment()
    .addClasspathResource("order-payment-process.bpmn20.xml")
    .deploy();

// 2. 启动订单流程
Map<String, Object> orderVariables = new HashMap<>();
ProcessInstance orderInstance = runtimeService.startProcessInstanceByKey("orderProcess", orderVariables);
String orderId = (String) runtimeService.getVariable(orderInstance.getId(), "orderId");
System.out.println("订单流程已启动,等待支付,订单ID: " + orderId);

// 3. 启动支付流程,传入订单ID
Map<String, Object> paymentVariables = new HashMap<>();
paymentVariables.put("orderId", orderId);
ProcessInstance paymentInstance = runtimeService.startProcessInstanceByKey("paymentProcess", paymentVariables);

// 4. 支付完成后,发送支付消息给订单流程
// 查找等待支付消息的执行实例
Execution waitingExecution = runtimeService.createExecutionQuery()
    .processInstanceId(orderInstance.getId())
    .messageEventSubscriptionName("paymentMsg")
    .singleResult();

// 发送消息触发订单流程继续执行
runtimeService.messageEventReceived("paymentMsg", waitingExecution.getId());
System.out.println("支付完成消息已发送,订单流程将继续执行");

实例执行流程说明

  1. 订单流程启动后,创建订单并执行到waitPayment节点
  2. 此时订单流程暂停,等待paymentMsg消息
  3. 支付流程启动并处理支付,完成后通过sendPaymentMsg节点抛出paymentMsg消息
  4. 调用runtimeService.messageEventReceived()方法将消息传递给订单流程
  5. 订单流程的waitPayment节点捕获到消息,继续执行后续的发货流程

关键特性总结

  • 消息捕获事件与抛出事件通过messageRef关联,必须引用同一个消息 ID
  • 流程会在消息捕获事件处暂停,直到收到对应的消息才继续
  • 消息是点对点的,通常用于两个特定流程之间的通信
  • 适用于需要异步等待外部系统响应的场景(如支付、审批等)

2.结束事件

2.1 空结束事件(None End Event)

2.1.1 解释

  • 定义:空结束事件是 Flowable 中最常见且最简单的结束事件。它用于标记流程或分支的结束,且不抛出任何特定结果。当流程实例或分支到达空结束事件时,流程引擎会终止该流程实例或分支。如果流程实例有多个并行分支,只有当所有分支都到达空结束事件时,整个流程实例才会结束。
  • 特点:
    • 不处理抛出结果,相当于流程或分支的“正常结束”。
    • 适用于流程或分支的常规终止场景,无需额外的操作。
  • 使用场景:通常用于流程的自然结束,例如订单处理完成后结束流程。

2.1.2 图形标记

  • 空结束事件在 BPMN 图中表现为一个粗边圆圈,内部没有图标,表示无特定结果类型。
  • 示例图形(文字描述):end-event(粗边圆圈,无内部图标)。

2.1.3 XML 表示

  • 空结束事件的 XML 定义非常简单,仅包含 <endEvent> 标签,无子元素。
  • 示例代码:

    <endEvent id="endEvent1" name="noneEndEvent"></endEvent>

    • id:事件的唯一标识符。
    • name:事件的显示名称(可选)。

2.1.4 使用示例

  • 场景:一个简单的订单处理流程,包含下单、支付和发货步骤,支付成功后到达空结束事件,流程结束。
  • 流程描述:用户下单后,流程进入支付节点,支付成功后到达空结束事件,流程实例终止。
  • XML 示例:
    <process id="simpleOrderProcess" name="简单订单流程" isExecutable="true">
    
    <startEvent id="startEvent" />
    
    <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="orderTask" />
    
    <userTask id="orderTask" name="下单" />
    
    <sequenceFlow id="flow2" sourceRef="orderTask" targetRef="paymentTask" />
    
    <userTask id="paymentTask" name="支付" />
    
    <sequenceFlow id="flow3" sourceRef="paymentTask" targetRef="endEvent" />
    
    <endEvent id="endEvent" name="noneEndEvent" />
    
    </process>
    • 说明:流程从 startEvent 开始,经过 orderTask(下单)和 paymentTask(支付),最后到达 endEvent,结束流程。

2.2 错误结束事件(Error End Event)

2.2.1 解释

  • 定义:错误结束事件在流程到达时抛出错误,并终止当前流程分支。它通常用于子流程中,抛出的错误会被父流程的错误边界事件捕获。错误结束事件需要定义一个错误码(errorRef),以便与错误边界事件匹配。
  • 特点:
    • 抛出特定错误码,触发对应的错误边界事件。
    • 仅适用于子流程,父流程通过错误边界事件处理抛出的错误。
  • 使用场景:常用于异常处理,例如支付失败时触发重新支付的逻辑。

2.2.2 图形标记

  • 错误结束事件表现为一个粗边圆圈,内部包含一个全黑错误图标(类似闪电形状),表示抛出错误。
  • 示例图形(文字描述):end-event(粗边圆圈,内部有全黑错误图标)。

2.2.3 XML 表示

  • 错误结束事件的 XML 定义包含 <endEvent> 标签和 <errorEventDefinition> 子元素,errorRef 属性引用流程中定义的错误。
  • 示例代码:

    <error id="theError" />

    <process id="errorEndEventProcess">

    <endEvent id="myErrorEndEvent">

    <errorEventDefinition errorRef="theError" />

    </endEvent>

    </process>

    • <error>:定义错误,id 为错误标识。
    • <errorEventDefinition>:指定抛出的错误,errorRef 引用错误标识。

2.2.4 使用示例

  • 场景:一个订单流程包含支付子流程。如果支付失败,子流程到达错误结束事件,抛出错误,触发父流程的错误边界事件,重新启动支付子流程;如果支付成功,子流程到达空结束事件,继续到发货节点。
  • 流程描述:
    1. 用户下单。
    2. 进入支付子流程。
    3. 支付成功:到达空结束事件,子流程结束,进入发货。
    4. 支付失败:到达错误结束事件,抛出错误,触发错误边界事件,重新进入支付子流程。
  • XML 示例(简化版,基于文档提供的内容):
    <error id="payError" errorCode="payErrorCode" />
    
    <process id="ErrorEndEventProcess" name="错误结束事件" isExecutable="true">
    
    <startEvent id="startEvent" />
    
    <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="orderTask" />
    
    <userTask id="orderTask" name="下单" />
    
    <sequenceFlow id="flow2" sourceRef="orderTask" targetRef="paymentSubProcess" />
    
    <subProcess id="paymentSubProcess" name="付款子流程">
    
    <startEvent id="subStartEvent" />
    
    <sequenceFlow id="subFlow1" sourceRef="subStartEvent" targetRef="paymentTask" />
    
    <userTask id="paymentTask" name="付款" />
    
    <sequenceFlow id="subFlow2" sourceRef="paymentTask" targetRef="gateway" />
    
    <exclusiveGateway id="gateway" />
    
    <sequenceFlow id="successFlow" name="支付成功" sourceRef="gateway" targetRef="noneEndEvent">
    
    <conditionExpression xsi:type="tFormalExpression">${payResult == true}</conditionExpression>
    
    </sequenceFlow>
    
    <endEvent id="noneEndEvent" />
    
    <sequenceFlow id="errorFlow" name="支付失败" sourceRef="gateway" targetRef="errorEndEvent">
    
    <conditionExpression xsi:type="tFormalExpression">${payResult == false}</conditionExpression>
    
    </sequenceFlow>
    
    <endEvent id="errorEndEvent">
    
    <errorEventDefinition errorRef="payError" />
    
    </endEvent>
    
    </subProcess>
    
    <sequenceFlow id="flow3" sourceRef="paymentSubProcess" targetRef="deliveryTask" />
    
    <userTask id="deliveryTask" name="发货" />
    
    <sequenceFlow id="flow4" sourceRef="deliveryTask" targetRef="endEvent" />
    
    <endEvent id="endEvent" />
    
    <boundaryEvent id="errorBoundaryEvent" attachedToRef="paymentSubProcess">
    
    <errorEventDefinition errorRef="payError" />
    
    </boundaryEvent>
    
    <sequenceFlow id="retryFlow" name="重新付款" sourceRef="errorBoundaryEvent" targetRef="paymentSubProcess" />
    
    </process>
    • 说明:支付子流程 (paymentSubProcess) 中,支付失败时到达 errorEndEvent,抛出 payError 错误,被父流程的 errorBoundaryEvent 捕获,重新流转到 paymentSubProcess。

2.3 取消结束事件(Cancel End Event)

2.3.1 解释

  • 定义:取消结束事件专用于 BPMN 事务子流程(Transaction Subprocess)。当到达取消结束事件时,抛出取消事件,必须由事务子流程上的取消边界事件捕获。捕获后,事务被取消,并触发补偿机制(compensation)来回滚相关操作。
  • 特点:
    • 仅在事务子流程中使用。
    • 抛出取消事件,触发取消边界事件和补偿机制。
    • 常与补偿事件一起使用,用于事务回滚场景。
  • 使用场景:适用于需要事务一致性的场景,例如订单取消后需要释放库存或退款。

2.3.2 图形标记

  • 取消结束事件表现为一个粗边圆圈,内部包含一个全黑取消图标(通常为“X”形状)。
  • 示例图形(文字描述):cancel-end-event(粗边圆圈,内部有全黑取消图标)。

2.3.3 XML 表示

  • 取消结束事件的 XML 定义包含 <endEvent> 标签和 <cancelEventDefinition> 子元素。
  • 示例代码:

    <process id="cancelEndEventProcess">

    <endEvent id="cancelEndEvent">

    <cancelEventDefinition />

    </endEvent>

    </process>

    • <cancelEventDefinition>:表示取消事件,无需额外属性。

2.3.4 使用示例

  • 场景:一个系统上线事务子流程,包含人工上线任务。如果上线失败,到达取消结束事件,触发取消边界事件,执行自动回滚补偿,并流转到用户排查任务。
  • 流程图描述(文字说明):
  • 流程启动 → 用户提交订单 → 事务子流程(锁定库存 → 支付)。
  • 支付成功 → 空结束事件 → 发货 → 结束。
  • 用户取消订单 → 取消结束事件 → 触发取消边界事件 → 补偿(释放库存、退款) → 自动取消订单 → 结束。
  • XML 示例:
    <process id="CompleteTransactionSubProcess" name="完整事务子流程" isExecutable="true">
      <!-- 错误定义 -->
      <error id="errorFlag" errorCode="500" />
      
      <!-- 主流程 -->
      <startEvent id="startEvent" />
      <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="submitOrderTask" />
      <userTask id="submitOrderTask" name="用户提交订单">
        <extensionElements>
          <flowable:formData />
          <flowable:assigneeType>static</flowable:assigneeType>
        </extensionElements>
      </userTask>
      <sequenceFlow id="flow2" sourceRef="submitOrderTask" targetRef="transactionSubProcess" />
    
      <!-- 事务子流程 -->
      <transaction id="transactionSubProcess" name="订单处理事务">
        <startEvent id="subStartEvent" />
        <sequenceFlow id="subFlow1" sourceRef="subStartEvent" targetRef="lockInventoryTask" />
        <serviceTask id="lockInventoryTask" name="锁定库存" flowable:class="com.example.LockInventoryService">
          <extensionElements>
            <flowable:formData />
            <flowable:assigneeType>static</flowable:assigneeType>
          </extensionElements>
        </serviceTask>
        <sequenceFlow id="subFlow2" sourceRef="lockInventoryTask" targetRef="paymentTask" />
        <userTask id="paymentTask" name="用户支付订单">
          <extensionElements>
            <flowable:formData />
            <flowable:assigneeType>static</flowable:assigneeType>
          </extensionElements>
        </userTask>
        <sequenceFlow id="subFlow3" sourceRef="paymentTask" targetRef="gateway" />
        <exclusiveGateway id="gateway" />
        
        <!-- 正常路径:支付成功 -->
        <sequenceFlow id="successFlow" name="支付成功" sourceRef="gateway" targetRef="deductInventoryTask">
          <conditionExpression xsi:type="tFormalExpression">${payResult == true}</conditionExpression>
        </sequenceFlow>
        <serviceTask id="deductInventoryTask" name="扣减库存" flowable:class="com.example.DeductInventoryService" />
        <sequenceFlow id="subFlow4" sourceRef="deductInventoryTask" targetRef="noneEndEvent" />
        <endEvent id="noneEndEvent" />
    
        <!-- 取消路径:用户取消订单 -->
        <sequenceFlow id="cancelFlow" name="用户取消" sourceRef="gateway" targetRef="cancelEndEvent">
          <conditionExpression xsi:type="tFormalExpression">${payResult == false}</conditionExpression>
        </sequenceFlow>
        <endEvent id="cancelEndEvent">
          <cancelEventDefinition />
        </endEvent>
    
        <!-- 补偿任务 -->
        <serviceTask id="releaseInventoryTask" name="释放库存" isForCompensation="true" flowable:class="com.example.ReleaseInventoryService" />
        <serviceTask id="refundTask" name="费用退回" isForCompensation="true" flowable:class="com.example.RefundService" />
        <boundaryEvent id="compensateInventoryEvent" attachedToRef="lockInventoryTask">
          <compensateEventDefinition />
        </boundaryEvent>
        <boundaryEvent id="compensatePaymentEvent" attachedToRef="paymentTask">
          <compensateEventDefinition />
        </boundaryEvent>
        <association id="assoc1" associationDirection="One" sourceRef="compensateInventoryEvent" targetRef="releaseInventoryTask" />
        <association id="assoc2" associationDirection="One" sourceRef="compensatePaymentEvent" targetRef="refundTask" />
      </transaction>
    
      <!-- 取消边界事件 -->
      <boundaryEvent id="cancelBoundaryEvent" attachedToRef="transactionSubProcess">
        <cancelEventDefinition />
      </boundaryEvent>
      <sequenceFlow id="flow3" sourceRef="cancelBoundaryEvent" targetRef="cancelOrderTask" />
      <serviceTask id="cancelOrderTask" name="自动取消订单" flowable:class="com.example.CancelOrderService">
        <extensionElements>
          <flowable:formData />
          <flowable:assigneeType>static</flowable:assigneeType>
        </extensionElements>
      </serviceTask>
      <sequenceFlow id="flow4" sourceRef="cancelOrderTask" targetRef="endEventCancel" />
      <endEvent id="endEventCancel" />
    
      <!-- 正常路径继续 -->
      <sequenceFlow id="flow5" sourceRef="transactionSubProcess" targetRef="deliveryTask" />
      <userTask id="deliveryTask" name="发货">
        <extensionElements>
          <flowable:formData />
          <flowable:assigneeType>static</flowable:assigneeType>
        </extensionElements>
      </userTask>
      <sequenceFlow id="flow6" sourceRef="deliveryTask" targetRef="endEventSuccess" />
      <endEvent id="endEventSuccess" />
    </process>
  • 正常路径:
    • 从 subStartEvent 开始,执行 lockInventoryTask(锁定库存)和 paymentTask(支付)。
    • 支付成功(payResult == true),通过 gateway 流向 deductInventoryTask(扣减库存),到达 noneEndEvent,事务子流程结束,进入主流程的 deliveryTask(发货)。
  • 取消路径:
    • 用户取消订单(payResult == false),通过 gateway 流向 cancelEndEvent,抛出取消事件。
    • cancelBoundaryEvent 捕获取消事件,触发补偿任务(releaseInventoryTask 和 refundTask),释放库存并退款。
    • 流程流向 cancelOrderTask(自动取消订单),最终到达 endEventCancel。
  • 补偿机制:
    • releaseInventoryTask 和 refundTask 标记为 isForCompensation="true",通过 compensateEventDefinition 关联到对应的任务,确保取消时回滚操作。
  • 图示
[主流程]
   |
   ▼
[Start Event] ----> [UserTask: 用户提交订单]
   |                    name="submitOrderTask"
   |                    id="submitOrderTask"
   ▼
[Transaction Subprocess: 订单处理事务] ----> [UserTask: 发货] ----> [End Event]
   id="transactionSubProcess"                    name="deliveryTask"    id="endEventSuccess"
   |                                            id="deliveryTask"
   ▼
   [Start Event] ----> [ServiceTask: 锁定库存] ----> [UserTask: 用户支付订单] ----> [Exclusive Gateway]
      id="subStartEvent"    name="lockInventoryTask"     name="paymentTask"          id="gateway"
                            id="lockInventoryTask"       id="paymentTask"
                            flowable:class="com.example.LockInventoryService"
                                                         输出参数: payResult (boolean)
                                                         |
                                                         | payResult == true
                                                         ▼
                                                       [ServiceTask: 扣减库存] ----> [None End Event]
                                                         name="deductInventoryTask"    id="noneEndEvent"
                                                         id="deductInventoryTask"
                                                         flowable:class="com.example.DeductInventoryService"
                                                         |
                                                         | payResult == false
                                                         ▼
                                                       [Cancel End Event] ----> [触发 Cancel Boundary Event]
                                                         id="cancelEndEvent"        id="cancelBoundaryEvent"
                                                         <cancelEventDefinition />  attachedTo="transactionSubProcess"
   |
   | [Compensate Boundary Event] ----> [ServiceTask: 释放库存]
   |   id="compensateInventoryEvent"    name="releaseInventoryTask"
   |   attachedTo="lockInventoryTask"   isForCompensation="true"
   |                                    flowable:class="com.example.ReleaseInventoryService"
   |
   | [Compensate Boundary Event] ----> [ServiceTask: 费用退回]
   |   id="compensatePaymentEvent"      name="refundTask"
   |   attachedTo="paymentTask"         isForCompensation="true"
   |                                    flowable:class="com.example.RefundService"
   |
   ▼
[Cancel Boundary Event] ----> [ServiceTask: 自动取消订单] ----> [End Event]
   id="cancelBoundaryEvent"     name="cancelOrderTask"         id="endEventCancel"
   attachedTo="transactionSubProcess" id="cancelOrderTask"
   <cancelEventDefinition />    flowable:class="com.example.CancelOrderService"

2.3.5 注意事项

  1. 取消结束事件只能用于事务子流程。
  2. 必须有取消边界事件捕获取消事件。
  3. 取消事件需要配合补偿机制,否则会抛出异常(如 FlowableException: No execution found for sub process of boundary cancel event)。

2.4 终止结束事件(Terminate End Event)

2.4.1 解释

  • 定义:终止结束事件用于强制终止整个流程或其所在的范围(scope,如子流程或主流程)。当流程到达终止结束事件时,流程引擎会终止当前范围内的所有活动,包括并行分支。如果在子流程中,终止范围由 terminateAll 属性决定。
  • 特点:
    • 可终止整个流程或仅当前子流程。
    • terminateAll="true":终止整个根流程实例,无论事件位置。
    • terminateAll="false"(默认):仅终止当前子流程或实例。
  • 使用场景:适用于需要立即停止所有流程活动的场景,例如紧急取消订单。

2.4.2 图形标记

  • 终止结束事件表现为一个粗边圆圈,内部包含一个全黑实心圆图标。
  • 示例图形(文字描述):terminate-end-event(粗边圆圈,内部有全黑实心圆)。

2.4.3 XML 表示

  • 终止结束事件的 XML 定义包含 <endEvent> 标签和 <terminateEventDefinition> 子元素,flowable:terminateAll 属性控制终止范围。
  • 示例代码:

    <endEvent id="terminateEndEvent">

    <terminateEventDefinition flowable:terminateAll="true" />

    </endEvent>

    • flowable:terminateAll:可选,true 表示终止整个流程,false 表示仅终止当前范围。

2.4.4 使用示例

  • 场景:一个订单流程包含并行分支(支付和库存锁定)。如果用户取消订单,到达终止结束事件,立即终止所有分支,流程结束。
  • 流程描述:
    1. 流程启动,进入并行网关,分为支付和库存锁定两个分支。
    2. 用户取消订单,到达终止结束事件,终止所有分支,流程结束。
  • XML 示例:
    <process id="terminateEndEventProcess" name="终止结束事件" isExecutable="true">
    
    <startEvent id="startEvent" />
    
    <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="orderTask" />
    
    <userTask id="orderTask" name="下单" />
    
    <sequenceFlow id="flow2" sourceRef="orderTask" targetRef="parallelGateway" />
    
    <parallelGateway id="parallelGateway" />
    
    <sequenceFlow id="flow3" sourceRef="parallelGateway" targetRef="paymentTask" />
    
    <userTask id="paymentTask" name="支付" />
    
    <sequenceFlow id="flow4" sourceRef="paymentTask" targetRef="noneEndEvent" />
    
    <endEvent id="noneEndEvent" />
    
    <sequenceFlow id="flow5" sourceRef="parallelGateway" targetRef="lockTask" />
    
    <serviceTask id="lockTask" name="锁定库存" flowable:class="com.example.LockService" />
    
    <sequenceFlow id="flow6" sourceRef="lockTask" targetRef="cancelTask" />
    
    <userTask id="cancelTask" name="取消订单" />
    
    <sequenceFlow id="flow7" sourceRef="cancelTask" targetRef="terminateEndEvent" />
    
    <endEvent id="terminateEndEvent">
    
    <terminateEventDefinition flowable:terminateAll="true" />
    
    </endEvent>
    
    </process>
    • 说明:流程通过 parallelGateway 分出支付和库存锁定分支。如果用户在 cancelTask 取消订单,到达 terminateEndEvent,设置 terminateAll="true",整个流程(包括支付分支)立即终止。

2.4.5 注意事项

  1. terminateAll="true" 会终止整个流程实例,适用于需要强制停止所有活动的场景。
  2. terminateAll="false" 仅终止当前子流程,适合局部终止场景。

总结

  • 空结束事件:最简单的结束事件,用于流程或分支的正常结束,无需抛出结果。
  • 错误结束事件:用于子流程抛出错误,触发父流程的错误边界事件,适合异常处理。
  • 取消结束事件:专用于事务子流程,触发取消边界事件和补偿机制,适合事务回滚。
  • 终止结束事件:强制终止流程或子流程,适合紧急停止场景,范围由 terminateAll 属性控制。
  • 共同点:所有结束事件均为“抛出”事件,区别在于抛出结果的类型和处理方式。
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐