2023最新版本Activiti7系列-事件篇

事件篇

在这里插入图片描述

事件(event)通常用于为流程生命周期中发生的事情建模。事件总是图形化为圆圈。在BPMN 2.0中,有两种主要的事件分类:*捕获(catching)抛出(throwing)*事件。

  • 捕获: 当流程执行到达这个事件时,会等待直到触发器动作。触发器的类型由其中的图标,或者说XML中的类型声明而定义。捕获事件与抛出事件显示上的区别,是其内部的图标没有填充(即是白色的)。
  • 抛出: 当流程执行到达这个事件时,会触发一个触发器。触发器的类型,由其中的图标,或者说XML中的类型声明而定义。抛出事件与捕获事件显示上的区别,是其内部的图标填充为黑色。

1. 定时器事件

  定时器事件是一种在特定时间触发的事件。在Activiti中,可以通过定时器事件来实现定时执行某个任务或者触发某个流程实例,具体包括定时器启动事件,定时器捕获中间件事件,定时器边界事件,在很多的业务场景中。

1.1 定时器开始事件

  定时器启动事件(timer start event)在指定时间创建流程实例。在流程只需要启动一次,或者流程需要在特定的时间间隔重复启动时,都可以使用。在使用时我们需要注意如下几个点:

  1. 子流程不能有定时器启动事件。
  2. 定时器启动事件,在流程部署的同时就开始计时。不需要调用startProcessInstanceByXXX就会在时间启动。调用startProcessInstanceByXXX时会在定时启动之外额外启动一个流程。
  3. 当部署带有定时器启动事件的流程的更新版本时,上一版本的定时器作业会被移除。这是因为通常并不希望旧版本的流程仍然自动启动新的流程实例。
  4. asyncExecutorActivate:需要设置为true,否则定时器不会生效,因为这块需要开启异步任务。

定时器启动事件,用其中有一个钟表图标的圆圈来表示。我们通过具体案例来介绍

在这里插入图片描述

部署流程后会在我们设置的时间开启一个流程实例,在没有到达定时时间的时候在act_ru_timer_job可以看到我们的定时任务信息

在这里插入图片描述

时间到达后会触发定时开启事件。

在这里插入图片描述

定时器开始事件除了上面的指定固定时间启动外我们还可以通过循环和持续时间来处理

  • timeDate:指定一个具体的日期和时间,例如2022-01-01T00:00:00
  • timeCycle:指定一个重复周期,例如R/PT1H表示每隔1小时触发一次。
  • timeDuration:指定一个持续时间,例如PT2H30M表示持续2小时30分钟。

然后我们增加一个重复周期的案例。这块我们可以通过自动任务来演示案例

在这里插入图片描述

在自动任务这块绑定了一个JavaDelegate来处理

public class MyJavaDelegate implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("自动任务执行了..." + LocalDateTime.now());
    }
}

在这里插入图片描述

然后部署流程测试,在act_ru_timer_job查看定义信息

在这里插入图片描述

在这里插入图片描述

可以看到执行了3次。都间隔了30秒

1.2 定时器中间事件

  在开始事件和结束事件之间发生的事件称为中间事件,定时器中间捕获事件指在流程中将一个定时器作为独立的节点来运行,是一个捕获事件。当流程流转到定时器中间捕获事件时,会启动一个定时器,并一直等待触发,只有到达指定时间定时器才被触发。

在这里插入图片描述

  当我们审批通过申请出库后,等待一分钟触发定时器。然后会进入到出库处理。同时在触发前在act_ru_timer_job中可以查询到对应的任务信息。

1.3 定时器边界事件

  当某个用户任务或者子流程在规定的时间后还没有执行。那么我们就可以通过定时器边界事件来触发执行特定的处理流程。

  注意在定时器边界事件配置了cancelActivity属性,用于说明该事件是否为中断事件。cancelActivity属性值默认为true,表示它是边界中断事件,当该边界事件触发时,它所依附的活动实例被终止,原有的执行流会被中断,流程将沿边界事件的外出顺序流继续流转。如果将其设置为false,表示它是边界非中断事件,当边界事件触发时,则原来的执行流仍然存在,所依附的活动实例继续执行,同时也执行边界事件的外出顺序流。

在这里插入图片描述

部署后启动流程。那么会进入到合同审批-总经理审判的这个节点。同时在act_ru_timer_job中可以看到这个边界事件的定义
在这里插入图片描述

等待了一分钟定时器边界事件触发。我们可以在控制台中看到JavaDelegate任务的执行。

在这里插入图片描述

因为这块的边界事件我们定义的是非中断。所以用户任务还在,只是在边界事件中触发了服务任务。来通知用户审批处理。

在这里插入图片描述

然后总经理审批通过。后会进入到财务审批的节点

在这里插入图片描述

同时会开启我们的中间边界事件。act_ru_timer_job中会生成对应的记录。

在这里插入图片描述

同时act_ru_task中的审批是财务审核

在这里插入图片描述

等待一分钟后。因为边界事件设置的是中断类型。所以触发后财务审核终止。只剩下触发后的新的出口中的财务实习审批
在这里插入图片描述

在这里插入图片描述

2.消息事件

  消息事件(message event),是指引用具名消息的事件。消息具有名字与载荷。与信号不同,消息事件只有一个接收者

2.1 开始事件

消息开始事件,也就是我们通过接收到某些消息后来启动流程实例,比如接收到了一封邮件,一条短信等,具体通过案例来讲解.
在这里插入图片描述

做消息的定义

在这里插入图片描述

在消息开始事件中我们需要绑定上面定义的消息

在这里插入图片描述

然后就可以部署流程

    /**
     * 流程部署操作
     */
    @Test
    public void test1(){
        // 1.获取ProcessEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 2.完成流程的部署操作 需要通过RepositoryService来完成
        RepositoryService repositoryService = processEngine.getRepositoryService();
        // 3.完成部署操作
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("flow/event-message-start.bpmn20.xml")
                .name("消息启动事件")
                .deploy(); // 是一个流程部署的行为 可以部署多个流程定义的
        System.out.println(deploy.getId());
        System.out.println(deploy.getName());

    }

部署完流程后。消息启动事件会在act_ru_event_subscr中记录我们的定义信息。

在这里插入图片描述

然后就可以发送相关的消息。来激活该流程实例,注意:消息的名称我们不要使用驼峰命名法来定义
在这里插入图片描述

当我们发送消息后

/**
 * 发送消息。触发流程
 */
@Test
public void test3() throws InterruptedException {
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    RuntimeService runtimeService = engine.getRuntimeService();
    // 发送消息 发送的消息应该是具体的消息的名称而不应该是id
    runtimeService.startProcessInstanceByMessage("msg01");
    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}

可以看到消息开始事件触发了。

在这里插入图片描述

2.2 中间事件

  消息中间事件就是在流程运作中需要消息来触发的场景,案例演示,自动流程1处理完成后,需要接收特定的消息之后才能进入到自动流程2

在这里插入图片描述

然后在消息中间事件的图标中我们需要绑定刚刚定义的消息

在这里插入图片描述

部署启动和审批流程后进入到消息中间事件的节点

在这里插入图片描述

然后发送消息触发消息中间事件

/**
 * 触发消息中间事件
 */
@Test
public void test5(){
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    RuntimeService runtimeService = engine.getRuntimeService();
    // 查询出当前的 执行实例的 编号
    Execution execution = runtimeService.createExecutionQuery()
            .processInstanceId("110001")
            .onlyChildExecutions()
            .singleResult();
    runtimeService.messageEventReceived("msg02",execution.getId());
}

然后进入到了用户任务2的审批。说明触发了

在这里插入图片描述

2.3 边界事件

  消息边界事件同样的针对是用户节点在消息触发前如果还没有审批。就会触发消息事件的处理逻辑。同样我们通过具体的案例来介绍。

在这里插入图片描述

定义两个消息

在这里插入图片描述

部署流程、启动流程后进入到用户任务1后。在act_ru_event_subscr表中就可以看到对应的消息事件,这时我们就可以发送相关的消息。

在这里插入图片描述

public void test5(){
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    RuntimeService runtimeService = engine.getRuntimeService();
    runtimeService.messageEventReceived("msg03","170005");
}

然后在控制台就可以看到JavaDelegate被执行调用了

在这里插入图片描述

这里我们需要注意当前的边界事件是非中断的。所以还是需要用户任务1来审批推进,审批后会绑定msg04

在这里插入图片描述

在这里插入图片描述

当我们触发了第二个消息边界事件。那么任务会进入到用户任务3。同时用户任务2被中断了。然后msg04的任务也结束了。

3.错误事件

  错误事件可以用做一个流程的开始事件或者作为一个任务或者子流程的边界事件,错误事件没有提供作用中间事件的功能,这一点和前面介绍的定时器事件和消息事件还有区别的。在错误事件中提供了错误结束事件。我们在案例中会详细的讲解。

3.1 开始事件

  错误开始事件(error start event)可以触发一个事件子流程,且总是在另外一个流程异常结束时触发。BPMN 2.0规定了错误开始事件只能在事件子流程中被触发,不能在其他流程中被触发,包括顶级流程、嵌套子流程和调用活动。错误启动事件不能用于启动流程实例

  错误启动事件总是中断。我们通过案例来介绍

在这里插入图片描述

在对应的自动任务1中我们需要显示的抛出异常信息

/**
 * 自定义的委托类
 */
public class MyFirstDelegate implements JavaDelegate {
    /**
     * 回调方法
     * @param execution
     */
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("服务任务执行了..." + LocalDateTime.now().toString());
        // 抛出错误 触发 子流程中的错误开始事件
        throw new BpmnError("error01");
    }
}

  那么部署完流程后。然后发起一个新的流程就会走事件子流程中的逻辑了。错误开始事件可以在如下的场景中使用:

  1. 输入验证失败:当用户提交工作流启动请求时,需要对输入的数据进行验证。如果数据不符合预期的格式或规则,可以使用错误开始事件来捕获并处理验证失败的情况。

  2. 权限验证失败:在某些情况下,只有特定的用户或用户组才能启动某个工作流。当非授权用户尝试启动工作流时,可以使用错误开始事件来捕获并处理权限验证失败的情况。

  3. 前置条件不满足:在工作流启动之前,可能需要满足一些前置条件,例如某个数据已经存在或某个服务可用。如果前置条件不满足,可以使用错误开始事件来捕获并处理这种情况。

  4. 数据源异常:在工作流启动过程中,可能需要从外部数据源获取数据。如果数据源出现异常导致无法获取数据,可以使用错误开始事件来捕获并处理数据源异常的情况。

总的来说,错误开始事件可以用于捕获工作流启动时可能出现的各种错误情况,并根据具体的业务需求进行相应的处理。

3.2 边界事件

  当某个任务发生错误时,可以通过错误边界事件来捕获并处理该错误,以保证流程的正常执行。

  错误边界事件可以在流程中的任务节点上定义,并与该任务节点关联。当任务节点执行过程中发生错误时,错误边界事件会被触发,并执行相应的处理逻辑,如发送错误通知、重新分配任务、跳转到其他节点等。

  错误边界事件可以捕获多种类型的错误,如异常、超时、网络故障等。通过使用错误边界事件,可以增加流程的容错性,并提供更好的错误处理机制,保证流程的稳定性和可靠性。

  需要注意的是,错误边界事件只能与任务节点关联,而不能与其他类型的节点(如网关、开始节点、结束节点)关联。此外,在设计流程时,需要准确定义错误边界事件的触发条件和处理逻辑,以确保错误能够被正确捕获和处理。具体我们通过案例来演示。

在这里插入图片描述

  案例中我们把错误边界事件绑定在了普通的用户任何和一个子流程上。如果对应的节点抛出的相关的错误。对应的边界事件就可以被触发。

错误边界事件可能的应用场景:

  1. 任务执行失败:当某个任务执行失败时,可以使用错误边界事件来捕获该异常,并执行一些恢复操作,例如重新分配任务给其他用户或记录错误信息。

  2. 子流程异常:当子流程执行过程中发生异常时,可以使用错误边界事件捕获该异常,并执行一些补救措施,例如回退到上一个节点或重新启动子流程。

  3. 超时处理:当某个任务或子流程在规定的时间内没有完成时,可以使用错误边界事件来捕获超时异常,并执行相应的超时处理逻辑,例如发送提醒邮件或自动终止流程。

  4. 数据校验失败:在某些场景下,需要对流程中的数据进行校验,如果校验失败,则可以使用错误边界事件来捕获校验异常,并进行相应的处理,例如返回错误信息给用户或中止流程。

总之,错误边界事件可以帮助我们在流程执行过程中及时捕获并处理异常情况,提高流程的可靠性和稳定性。

3.3 结束事件

  在Activiti中,错误结束事件(Error End Event)是一个用于标记流程实例在特定错误条件下结束的节点。当流程实例执行到错误结束事件时,流程实例将立即终止执行,并且流程实例的状态将被标记为“错误结束”。

  错误结束事件可以与错误边界事件(Error Boundary Event)结合使用,用于在流程中捕获和处理特定的错误。当错误边界事件触发时,流程会跳转到与错误边界事件关联的错误结束事件,从而使流程实例结束。

  错误结束事件可以配置一个错误代码,用于标识特定的错误类型。在流程定义中,可以定义多个错误结束事件,每个事件可以有不同的错误代码。当流程实例执行到错误结束事件时,可以根据错误代码进行相应的处理,例如记录日志、发送通知等。

  错误结束事件可以用于处理各种错误情况,例如系统异常、业务规则异常等。通过使用错误结束事件,可以使流程能够在错误发生时进行合理的处理,提高系统的可靠性和稳定性。

总之,错误结束事件是Activiti中的一个节点,用于标记流程实例在特定错误条件下结束。它可以与错误边界事件结合使用,用于捕获和处理特定的错误。通过使用错误结束事件,可以实现对流程中各种错误情况的处理和管理。
在这里插入图片描述

当子流程中的支付失败的情况下会触发错误结束事件。该事件会被错误边界事件捕获。错误边界事件捕获后会重新发起支付的流程。这就是我们介绍的案例流程。

4. 信号事件

  信号事件是Activiti中的一种事件类型,用于在流程执行过程中通知其他流程实例或任务实例。

  信号事件是一种全局事件,可以在任何流程实例或任务实例中触发和捕获。当一个流程实例或任务实例触发了一个信号事件,其他等待捕获相同信号的流程实例或任务实例将被唤醒并继续执行。

信号事件可以用于以下场景:

  1. 并行流程实例之间的协作:当一个流程实例需要与其他并行流程实例进行协作时,可以触发一个信号事件来通知其他流程实例执行相应的任务。

  2. 动态流程控制:当流程的执行需要根据外部条件进行动态调整时,可以使用信号事件来触发相应的流程变化。

  3. 异常处理:当发生异常情况时,可以触发一个信号事件来通知其他流程实例或任务实例进行异常处理。

使用信号事件需要以下几个步骤:

  1. 定义信号事件:在流程定义中定义一个信号事件,指定信号的名称和其他属性。

  2. 触发信号事件:在流程实例或任务实例中触发一个信号事件。

  3. 捕获信号事件:在其他流程实例或任务实例中捕获相同名称的信号事件。

  4. 响应信号事件:在捕获的信号事件中定义相应的处理逻辑,例如执行任务或流程变化。

  信号事件我们可以分为开始事件中间捕获事件中间抛出事件边界事件,具体的介绍如下

4.1 开始事件

  • 启动事件是一个特殊的信号事件,用于在流程启动时触发。
  • 当流程启动时,如果存在一个启动事件,并且该事件匹配到了被触发的信号,流程将会被启动。
  • 启动事件可以用于实现流程启动前的条件判断,例如当某个条件满足时,才允许启动流程。

具体的案例如下:

在这里插入图片描述

定义信号信息:

在这里插入图片描述

在定义信号的时候有一个Scope属性可以设置为Global或processInstance

  • Global:全局范围的信号定义,表示可以在任何流程实例中触发和捕获信号。当一个信号事件被触发时,所有等待捕获该信号的节点都会被唤醒。
  • processInstance:流程实例范围的信号定义,表示只能在当前流程实例中触发和捕获信号。当一个信号事件被触发时,只有等待在当前流程实例中捕获该信号的节点会被唤醒。

  而当前的启动事件是在流程实例启动时触发的事件,用于执行一些初始化操作。启动事件可以在流程定义的开始节点上定义,并在开始节点上设置事件类型为start。启动事件只有一个全局范围的信号定义,即scope属性只能设置为Global。当一个启动事件被触发时,所有等待捕获该信号的节点都会被唤醒。

然后在信号开始节点中绑定刚刚定义的信号:

在这里插入图片描述

接下就可以部署流程。然后通过信号来启动对应的流程实例了。

/**
 * 通过信号启动一个新的流程
 */
@Test
public void test2() throws InterruptedException {
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    // 发起流程 需要通过 runtimeService来实现
    RuntimeService runtimeService = engine.getRuntimeService();
    // 通过发送信号。触发对应订阅了该信号的流程
    runtimeService.signalEventReceived("signal1");
    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}

执行上面的方法就可以看到act_ru_task中对应的就有了一条用户任务的待办信息

在这里插入图片描述

同时对应的信号事件存储在了act_ru_event_subscr中。
在这里插入图片描述

当然触发该事件的方式并不仅仅只有这一种方案还有:

  • 由流程中的信号中间抛出事件抛出信号,所有订阅了该信号的信号开始事件所在的流程定义都会被启动;
  • 作为普通开始事件,启动流程。

事件抛出我们在后面的案例中讲解。而作为普通的开始事件。直接执行下面的启动代码即可

// 通过流程定义ID来启动流程  返回的是流程实例对象
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceById("event-signal-start1:1:232503");

4.2 中间事件

  信号中间事件分为捕获事件抛出事件.当流程流转到信号中间捕获事件时会中断并等待触发,直到接收到相应的信号后沿信号中间捕获事件的外出顺序流继续流转。信号事件默认是全局的,与其他事件(如错误事件)不同,其信号不会在捕获之后被消费。如果存在多个引用了相同信号的事件被激活,即使它们不在同一个流程实例中,当接收到该信号时,这些事件也会被一并触发。具体我们通过案例来讲解
在这里插入图片描述

消息定义我们用的scope是 processInstance。也就是只在当前流程实例生效。部署运行后可以看具体的效果

在这里插入图片描述

启动流程后在act_ru_event_subscr中记录了信号事件的相关信息。同时记录了作用域信息

在这里插入图片描述

然后我们审批用户节点进入到抛出信号事件的节点。

在这里插入图片描述

审批任务完成

/**
 * 任务审批
 */
@Test
public void test7() throws Exception{
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = engine.getTaskService();
    taskService.complete("245007");
    Thread.sleep(100000000);
}

可以看到自动任务2执行了

在这里插入图片描述

同时因为scopeprocessInstanceact_ru_event_subscr中记录的信号事件也被消费了。如果是global则该信号事件还是继续监听。

在这里插入图片描述

4.3 边界事件

  信号边界事件会捕获与其信号事件定义引用的信号具有相同信号名称的信号。当流程流转到信号边界事件依附的流程活动(如用户任务、子流程等)时,工作流引擎会创建一个捕获事件,在其依附的流程活动的生命周期内等待一个抛出信号。该信号可以由信号中间抛出事件抛出或由API触发。信号边界事件被触发后流程会沿其外出顺序流继续流转。如果该边界事件设置为中断,则依附的流程活动将被终止。

在这里插入图片描述

部署流程后启动流程那么具有的相关的数据act_ru_event_subscr表中记录的信号事件
在这里插入图片描述

然后流程会进入到用户任务1节点。当然可以正常的审批。还有就是可以发布相关的信号事件。在当前的环境下我们可以通过runtimeService的API来触发

/**
 * 通过信号启动事件
 * 发起一个流程
 * 1.通过runtimeService中提供的API来发送信号
 * 2.通过其他流程实例中的信号中间抛出事件来触发
 * 3.作为普通的流程实例来启动即可
 */
@Test
public void test2() throws InterruptedException {
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    // 发起流程 需要通过 runtimeService来实现
    RuntimeService runtimeService = engine.getRuntimeService();
    // 通过runtimeService的API来发布信号
    runtimeService.signalEventReceived("signal02");
    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}

在这里插入图片描述

同时因为是非中断的,所以用户任务1还在。接下来我们就需要做审批操作。审批通过就会进入到用户任务2

在这里插入图片描述

进入到用户任务2后。继续审批就会触发信号抛出事件,然后被信号边界事件捕获。
在这里插入图片描述

5. 其他事件

5.1 终止结束事件

  终止结束事件也称为中断结束事件,主要是对流程进行终止的事件,可以在一个复杂的流程中,如果某方想要提前中断这个流程,可以采用这个事件来处理,可以在并行处理任务中。如果你是在流程实例层处理,整个流程都会被中断,如果是在子流程中使用,那么当前作用和作用域内的所有的内部流程都会被终止。具体还是通过两个案例来给大家介绍:

第一个案例:终止结束事件是在主流程中触发的场景

在这里插入图片描述

设置终止结束事件。里面有一个terminateAll默认为false。含义是当终止结束事件在多实例或者嵌套的子流程中。那么不会终止整个流程。如果设置为true那么不管是否嵌套都会终止整个的流程实例。

在这里插入图片描述

通过案例的演示。我们发下在用户任务1用户任何2没有审批的情况下当用户任务3审批通过后同时flag设置为false的情况下触发了终止结束事件那么整个流程实例都被终止了。

另一个流程案例:在子流程中触发终止结束事件

在这里插入图片描述

在本案例中我们可以通过terminateAll属性非常方便的控制终止的范围。

5.2 取消结束事件

  取消结束事件(cancel end event)只能与BPMN事务子流程(BPMN transaction subprocess)一起使用。当到达取消结束事件时,会抛出取消事件,且必须由取消边界事件(cancel boundary event)捕获。取消边界事件将取消事务,并触发补偿(compensation)。

具体通过案例来讲解:

在这里插入图片描述

注意:结束取消事件我们只能在事务子流程中使用.

在这里插入图片描述

在流程设计器中没有直接提供事务子流程的图标,我们需要通过普通的子流程来设置事务的属性即可
在这里插入图片描述

然后就是补偿的任务我们需要勾选可补偿的选项
在这里插入图片描述

部署任务后我们再继续启动流程实例的时候。出现了如下的错误
在这里插入图片描述

检查xml文件中发现少了该属性。那么我们需要收到的加上

在这里插入图片描述

然后做正常的审批。触发取消结束事件,结合上面的流程图我们可以看到如下的效果

在这里插入图片描述

补充任务触发。可以看到控制台的日志信息

在这里插入图片描述

用户任务4在act_ru_task中可以看到对应的记录

在这里插入图片描述

5.3 补偿事件

  在Activiti中,补偿事件(Compensation Event)是一种用于处理流程中发生异常或错误的特殊事件。当流程中的某个任务或活动发生错误或无法继续执行时,补偿事件可以被触发来回滚或修复之前已经完成的任务或活动。

  补偿事件通常与错误边界事件(Error Boundary Event)结合使用。错误边界事件是在流程中的任务或活动周围设置的捕获异常的事件。当任务或活动发生异常时,错误边界事件将被触发,进而触发相应的补偿事件。

  补偿事件可以执行一系列的补偿操作,包括撤销之前已经完成的任务、还原数据、发送通知等。补偿操作的具体步骤和逻辑可以在流程定义中定义,并且可以使用Java代码或脚本来实现。

  补偿事件的触发和执行是自动完成的,无需人工干预。一旦补偿事件被触发,Activiti引擎会自动查找相应的补偿事件,并按照定义的补偿操作进行执行。

  通过使用补偿事件,可以有效地处理流程中的异常情况,提高流程的稳定性和容错性。补偿事件可以帮助流程在发生错误时自动进行修复,确保流程能够正常完成。

在这里插入图片描述

<?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:activiti="http://activiti.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.activiti.org/test">
    <error id="payFail" errorCode="payFail" ></error>
    <process id="myProcess" name="My process" isExecutable="true">
        <startEvent id="startevent1" name="开始事件"></startEvent>
        <parallelGateway id="parallelgateway1" name="并行网关"></parallelGateway>
        <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="parallelgateway1"></sequenceFlow>
        <serviceTask id="servicetask1" name="预订机票" activiti:class="com.bobo.delegate.MyTwoDelegate"></serviceTask>
        <serviceTask id="servicetask2" name="微信支付" activiti:class="com.bobo.delegate.MyOneDelegate"></serviceTask>
        <userTask id="usertask1" name="人工出票" activiti:assignee="zhangsan"></userTask>
        <sequenceFlow id="flow2" sourceRef="servicetask1" targetRef="usertask1"></sequenceFlow>
        <parallelGateway id="parallelgateway2" name="Parallel Gateway"></parallelGateway>
        <sequenceFlow id="flow3" sourceRef="usertask1" targetRef="parallelgateway2"></sequenceFlow>
        <sequenceFlow id="flow4" sourceRef="parallelgateway1" targetRef="servicetask1"></sequenceFlow>
        <sequenceFlow id="flow5" sourceRef="parallelgateway1" targetRef="servicetask2"></sequenceFlow>
        <sequenceFlow id="flow6" sourceRef="servicetask2" targetRef="parallelgateway2"></sequenceFlow>
        <serviceTask id="servicetask3" name="取消预订" isForCompensation="true" activiti:class="com.bobo.delegate.MyThreeDelegate"></serviceTask>
        <boundaryEvent id="boundarycompensation1" name="补偿边界事件" attachedToRef="servicetask1" cancelActivity="true">
            <compensateEventDefinition></compensateEventDefinition>
        </boundaryEvent>
        <boundaryEvent id="boundaryerror1" name="错误边界事件" attachedToRef="servicetask2">
            <errorEventDefinition errorRef="payFail"></errorEventDefinition>
        </boundaryEvent>
        <intermediateThrowEvent id="compensationintermediatethrowevent1" name="补偿抛出中间事件">
            <compensateEventDefinition></compensateEventDefinition>
        </intermediateThrowEvent>
        <sequenceFlow id="flow7" sourceRef="boundaryerror1" targetRef="compensationintermediatethrowevent1"></sequenceFlow>
        <endEvent id="endevent1" name="End"></endEvent>
        <sequenceFlow id="flow8" sourceRef="compensationintermediatethrowevent1" targetRef="endevent1"></sequenceFlow>
        <endEvent id="endevent2" name="End"></endEvent>
        <sequenceFlow id="flow9" sourceRef="parallelgateway2" targetRef="endevent2"></sequenceFlow>
        <association id="association1" sourceRef="boundarycompensation1" targetRef="servicetask3" associationDirection="None"></association>
    </process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
        <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
            <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
                <omgdc:Bounds height="35.0" width="35.0" x="160.0" y="360.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="parallelgateway1" id="BPMNShape_parallelgateway1">
                <omgdc:Bounds height="40.0" width="40.0" x="380.0" y="357.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1">
                <omgdc:Bounds height="55.0" width="105.0" x="580.0" y="220.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="boundarycompensation1" id="BPMNShape_boundarycompensation1">
                <omgdc:Bounds height="30.0" width="30.0" x="650.0" y="270.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="servicetask2" id="BPMNShape_servicetask2">
                <omgdc:Bounds height="55.0" width="105.0" x="580.0" y="450.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="boundaryerror1" id="BPMNShape_boundaryerror1">
                <omgdc:Bounds height="30.0" width="30.0" x="650.0" y="490.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
                <omgdc:Bounds height="55.0" width="105.0" x="820.0" y="220.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="parallelgateway2" id="BPMNShape_parallelgateway2">
                <omgdc:Bounds height="40.0" width="40.0" x="1140.0" y="336.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="servicetask3" id="BPMNShape_servicetask3">
                <omgdc:Bounds height="55.0" width="105.0" x="830.0" y="336.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="compensationintermediatethrowevent1" id="BPMNShape_compensationintermediatethrowevent1">
                <omgdc:Bounds height="35.0" width="35.0" x="740.0" y="590.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
                <omgdc:Bounds height="35.0" width="35.0" x="820.0" y="590.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="endevent2" id="BPMNShape_endevent2">
                <omgdc:Bounds height="35.0" width="35.0" x="1225.0" y="339.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
                <omgdi:waypoint x="195.0" y="377.0"></omgdi:waypoint>
                <omgdi:waypoint x="380.0" y="377.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
                <omgdi:waypoint x="685.0" y="247.0"></omgdi:waypoint>
                <omgdi:waypoint x="820.0" y="247.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
                <omgdi:waypoint x="925.0" y="247.0"></omgdi:waypoint>
                <omgdi:waypoint x="1160.0" y="247.0"></omgdi:waypoint>
                <omgdi:waypoint x="1160.0" y="336.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
                <omgdi:waypoint x="400.0" y="357.0"></omgdi:waypoint>
                <omgdi:waypoint x="400.0" y="247.0"></omgdi:waypoint>
                <omgdi:waypoint x="580.0" y="247.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
                <omgdi:waypoint x="400.0" y="397.0"></omgdi:waypoint>
                <omgdi:waypoint x="400.0" y="477.0"></omgdi:waypoint>
                <omgdi:waypoint x="580.0" y="477.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
                <omgdi:waypoint x="685.0" y="477.0"></omgdi:waypoint>
                <omgdi:waypoint x="1160.0" y="477.0"></omgdi:waypoint>
                <omgdi:waypoint x="1160.0" y="376.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
                <omgdi:waypoint x="665.0" y="520.0"></omgdi:waypoint>
                <omgdi:waypoint x="664.0" y="607.0"></omgdi:waypoint>
                <omgdi:waypoint x="740.0" y="607.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
                <omgdi:waypoint x="775.0" y="607.0"></omgdi:waypoint>
                <omgdi:waypoint x="820.0" y="607.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
                <omgdi:waypoint x="1180.0" y="356.0"></omgdi:waypoint>
                <omgdi:waypoint x="1225.0" y="356.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="association1" id="BPMNEdge_association1">
                <omgdi:waypoint x="665.0" y="300.0"></omgdi:waypoint>
                <omgdi:waypoint x="664.0" y="363.0"></omgdi:waypoint>
                <omgdi:waypoint x="830.0" y="363.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
</definitions>

然后部署流程和启动流程实例。通过控制台的输出可以看到微信支付失败后触发了补偿中间事件。然后补偿边界事件触发。触发了补偿自动任务
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/41529.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1haclon 简单操作

文章目录 *读取图片 read_image(Image,claudia) *转换为灰度 rgb1_to_gray(Image,GrayImage)阈值分割 区域连接 获取最衣服 *读取图片 read_image(Image,claudia) *转换为灰度 select_shape (Connection, SelectedRegions, area, and, 40963.3, 44724.8) rgb1_to_gray(Image,Gr…

TCP实现FTP功能

目录 server client makefile 运行顺序 FTP&#xff08;File Transfer Protocol&#xff09;是一种用于在计算机网络上传输文件的标准协议。 它允许用户通过网络将文件从一个计算机&#xff08;称为FTP服务器&#xff09;传输到另一个计算机&#xff08;称为FTP客户端&…

orbslam3 生成标定板rosrun kalibr kalibr_create_target_pdf --type

rosrun kalibr kalibr_create_target_pdf --type apriltag --nx 6 --ny 6 --tsize 0.08 --tspace 0.3小师妹要做相机视觉标定&#xff0c;需要制作棋盘格&#xff0c;无奈其电脑有些卡&#xff0c;对此毫无经验的博主从头开始安装&#xff08;此前博主已经安装了ROS环境&#x…

grpc --- protoc生成的pb.go文件的位置

目录 一、环境相关版本二、go_package配置为当前目录下三、go_package配置为指定目录四、结论 一、环境相关版本 go v1.20.5 protoc v4.24.0 protoc-gen-go v1.26.0protoc-gen-go版本过高时需要指定包名&#xff0c;即go_package 二、go_package配置为…

git rebase 合并提交

一. 合并提交步骤 git log --oneline 查看当前提交记录 git rebase -i HEAD~2 选择最后提交的2条记录进行合并进入编辑界面,将c865404的pick改为f, 表示向前合并也就是向cc5a54合并 编辑完之后:wq 保存并退出git rebase --continuegit push --force origin feature/v1.2 推送…

WEB阶段_CSSJS篇(附代码笔记)

&#xff08;一&#xff09;、使用DIVCSS布局首页 1、HTML的块标记 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><div>div1</div><div>div2</div><…

青岛大学_王卓老师【数据结构与算法】Week05_06_栈的顺序表示_学习笔记

本文是个人学习笔记&#xff0c;素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享&#xff0c; 另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权&#xff0c;请留言作删文处理。 课程视频链接&#xff1a; 数据结构与算法基础…

No.2(3)——双指针算法实现平方数组排序

双指针算法指的是&#xff0c;从数组的两侧开辟指针变量进行查找&#xff0c;这类问题往往通过暴力&#xff08;双循环&#xff09;可以解出&#xff0c;而采用双指针相当于用空间换取时间&#xff0c;省略双层循环中重复的部分。 对于一个含有负数的有序数组&#xff0c;要求保…

一本通1910:【00NOIP普及组】计算器的改良题解

今天是编程集训的第二天&#xff0c;也是我来到CSDN整整1年。感谢所有阅读过我的文章的人&#xff0c;谢谢。 今天的比赛难度略低于昨天&#xff0c;但这道题也卡了我好久。 进入正题 题目&#xff1a; 题目描述&#xff1a; NCL是一家专门从事计算器改良与升级的实验室&a…

项目名称:智能家居边缘网关项目

一&#xff0c;项目介绍 软件环境: C语言 硬件环境: STM32G030C8TX单片机开发板 开发工具: Linux平台GCC交叉编译环境以及ukeil (1)边缘网关概念 边缘网关是部署在网络边缘侧的网关&#xff0c;通过网络联接、协议转换等功能联接物理和数字世界&#xff0c;提供轻量化的联接管…

C#基础--进程和线程的认识

C#基础–进程和线程的认识 一、基础概念 1. 什么是进程&#xff1f; 进程并不是物理的东西&#xff0c;是虚拟出来的&#xff0c;是一种概念。当一个程序开始运行时&#xff0c;它就是一个进程&#xff0c;进程包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又…

小白入门C#编写MVC登录小案例

一、C#编写MVC登录小案例 &#x1f680;1. 新建MVC项目。 &#x1f680;2. 在Models文件夹下创建一个User类&#xff0c;包含登录所需要的用户名和密码属性。 namespace MvcLogin.Models {public class User{public string UserName{get; set;}public string Password{get;se…

unity01 界面布局

布局 坐标系 遵循左手定则&#xff0c;中指是y轴、食指是x轴、大拇指是z轴。 可以理解为x轴代表东西方向&#xff0c;z轴代表南北方向&#xff0c;y轴代表上下方向。 常用快捷键 鼠标中键&#xff1a;移动地图 右键&#xff1a;移动视角 shift鼠标左键单击gimo导航器的小方…

【C++】设计模式-单例模式

目录 一、单例模式 单例模式的三个要点 针对上述三要点的解决方案 常用的两类单例模式 二、懒汉模式实现 1.基本实现 2.锁静态成员析构单例 3.双层检查锁定优化 4.双层检查锁定智能指针 三、饿汉模式实现 1.基础实现 2.嵌套内部类解决内存泄漏 3.智能指针解决内存泄…

linux 系统修改已经打好jar包的yml配置文件

工作中可能回遇到&#xff0c;jar包已经打好&#xff0c;并且文件已经上传了&#xff0c;但是突然发现配置文件中的某一个参数写错了&#xff0c;怎么办&#xff1f;重新打包&#xff1f;如果重新打包再上传的话太影响效率了。那么我们可以通过以下方法&#xff0c;修改已经上传…

SuperMap iServer新增支持FlatGeobuf数据格式,查询渲染性能提升2-3倍

导语 FlatGeobuf是一种地理数据存储格式&#xff0c;采用了二进制编码&#xff0c;相比其他文本或XML格式更高效&#xff0c;可以显著减小文件大小&#xff0c;这使得数据的传输和存储更加快速和高效。 SuperMap iServer 11i(2023) &#xff08;以下简称SuperMap iServer11.1&a…

Pandas Groupby:在Python中汇总、聚合和分组数据

GroupBy是一个非常简单的概念。我们可以创建一个类别分组&#xff0c;并对这些类别应用一个函数。这是一个简单的概念&#xff0c;但它是一种在数据科学中广泛使用的非常有价值的技术。在真实的的数据科学项目中&#xff0c;您将处理大量数据并一遍又一遍地尝试&#xff0c;因此…

elementUI el-radio 无法点击的问题

<el-form-item label"B端客户类型" prop"user_type"><template slot"label"><span>B端客户类型</span><el-tooltip effect"dark" placement"top" content"B端大客户账期有效,只有设置该类型…

【Go】实现一个代理Kerberos环境部分组件控制台的Web服务

实现一个代理Kerberos环境部分组件控制台的Web服务 背景安全措施引入的问题SSO单点登录 过程整体设计路由反向代理登录会话组件代理YarnHbase 结果 背景 首先要说明下我们目前有部分集群的环境使用的是HDP-3.1.5.0的大数据集群&#xff0c;除了集成了一些自定义的服务以外&…

寻找下一个生成式 AI 独角兽,亚马逊云科技创业加速器火热招募中!

生成式AI让人工智能技术又一次破圈&#xff0c;带来了机器学习被大规模采用的历史转折点。它正在掀起新一轮的科技革命&#xff0c;为人类带来前所未有的颠覆性的影响&#xff0c;而诸多创业者也应势而上&#xff0c;寻求创新机遇。生成式AI可以创造全新的客户体验、提高企业内…