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

news2024/11/26 0:51:46

事件篇

在这里插入图片描述

事件(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>

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【java】【基础1】数据类型运算符

目录 一、数据类型&#xff08;4大类8种&#xff09; 1.1类型转换 1、自动类型转换 2、表达式的自动类转换 3、强制类型转换 二、运算符 2.1基本算术运算符 2.2自增自减运算符 2.3赋值运算 2.4关系运算符 2.5逻辑运算符 2.6三元运算符 2.7运算符的优先级 三、API文档&am…

中国人民大学与加拿大女王大学金融硕士——行而不辍,履践致远

理论和实践哪个重要呢&#xff0c;申克・阿伦斯曾说过&#xff1a;“如果没有理论的指导&#xff0c;实践就会变得盲目&#xff1b;而如果没有实践的充实&#xff0c;那么理论就会显得空洞”。学习不能停留在理论层面&#xff0c;要用于实践才能真正的消化吸收。学习的目的在于…

第一次实操Python+robotframework接口自动化测试

目前我们需要考虑的是如何实现关键字驱动实现接口自动化输出&#xff0c;通过关键字的封装实现一定意义上的脚本与用例的脱离&#xff01; robot framework 的安装不过多说明&#xff0c;网上资料比较太多~ 实例&#xff1a;&#xff01;&#xff01;&#xff01;&#xff01…

【数学建模】--典型相关分析

典型相关分析&#xff08;Canonical Correlation analysis&#xff09;研究两组变量&#xff08;每组变量中都可能有多个指标&#xff09;之间相关关系的一种多元统计方法。它能够揭示出两组变量之间的内在联系。 例子&#xff1a; 典型相关分析定义&#xff1a; 列题分析&…

visual studio中Git同步提交 报错:管道已结束

这里出现了在提交到远程的时候&#xff0c;vs提示管道已结束。 这里我的解决办法是使用git bash来手动提交 找到仓库的目录&#xff0c;右键找到gitbash的选项&#xff0c;输入add指令来添加文件到暂存区 git add .输入commit指令来将暂存区文件提交到本地仓库 git commit…

内核机制在以下方面发挥作用:

进程间通信&#xff1a;内核提供了特定的机制和接口&#xff0c;用于实现进程间的通信。这可以包括共享内存、管道、消息队列、信号量等。通过这些机制&#xff0c;进程可以在内核的帮助下进行数据传输和同步&#xff0c;实现进程间的协作和通信。 进程调度&#xff1a;内核负…

JAVA集成国密SM2

JAVA集成国密SM2加解密 一、pom配置二、代码集成2.1、目录结构2.2、源码2.3、测试 三、相关链接 国密算法概述&#xff1a;https://blog.csdn.net/qq_38254635/article/details/131801527 SM2椭圆曲线公钥密码算法 为非对称加密&#xff0c;基于ECC。该算法已公开。由于该算法…

详细解析张雪峰老师对计算机专业的评价“进可攻,退可守”--【职场篇】

文章目录 张雪峰的评价计算机行业类的总结性指示就业面宽进可攻&#xff0c;退可守另一个就业出口--培训 持续学习&#xff0c;技术过人 总结 张雪峰的评价 计算机行业类的总结性指示 “就业面宽&#xff0c;进可攻&#xff0c;退可守&#xff0c;各行各业其实对计算机专业都有…

确保安全:选择企业邮箱的五大关键因素!

为您的企业或组织选择正确的邮箱地址可以帮助您确保数据和通信的安全性。不安全的邮箱地址会使您容易受到垃圾邮件、病毒和网络钓鱼攻击。这里有一些小技巧可以帮助你选择一个安全的公司邮箱地址。 1.选择一个你可以信任的邮箱提供商 选择一个安全的公司邮箱地址的第一步是确保…

JavaScript入门——笔记用

JavaScript入门 变量数组常量模板字符串检测数据类型判断分支判断循环数组函数对象 与html和css不同的是js并不是一门标记语言&#xff0c;而是与java等相同&#xff0c;也是一门编程语言&#xff08;实现人机交互的运行在客户端即浏览器的编程语言&#xff09; JavaScript作用…

算法训练营第四十三天||● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

● 1049. 最后一块石头的重量 II 这道题和昨天的分割等和子集一样&#xff0c;只是最后返回值不一样 class Solution { public:int lastStoneWeightII(vector<int>& stones) {int sum 0;for(int i 0;i<stones.size();i){sum stones[i];}int target sum / 2;…

结构型模式 - 享元模式

概述 定义&#xff1a; 运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销&#xff0c;从而提高系统资源的利用率。 结构 享元&#xff08;Flyweight &#xff09;模式中存在以下两种状态&…

Python案例之新浪世界杯各国球队数据(德国VS日本)

目录标题 前言知识点:开发环境:实现代码:代码展示尾语 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 知识点: 动态数据抓包 requests发送请求 json数据解析 开发环境: python 3.8 运行代码 pycharm 2021.2 辅助敲代码 requests pip install requests 第三方模块…

linux系统上安装R语言并安装mclust包

请注意&#xff0c;如果你本来就是root账号&#xff0c;则所有的sudo都不用加。 x.1 安装R语言 安装R语言建议使用清华源&#xff0c;链接https://mirrors.tuna.tsinghua.edu.cn/CRAN/bin/linux/ubuntu/ 把下面这一串代码在terminal中全部输入便可&#xff0c;5000个R包也建…

分类管理业务开发 -- 手把手教你做ssm+springboot入门后端项目黑马程序员瑞吉外卖(四)

文章目录 前言一、开发功能预览二、公共字段自动填充1.问题分析2.代码实现3.功能测试4.功能完善 三、新增分类1.需求分析2.数据模型3.代码开发4.功能测试 四、分类信息分页查询1.需求分析2.代码开发3.功能测试 五、删除分类1.需求分析2.代码开发3.功能测试4.功能完善 六、修改分…

VS2013配置所有项目附加包含目录的办法

任意打开一个项目&#xff0c;点击视图->其他窗口->属性管理器 在属性管理器中打开Debug|Win32&#xff0c;找到Microsoft.Cpp.Win32.user&#xff0c;双击打开 直接配置VC的附加包含目录就可以了 之后所有项目都会继承这些目录 这样就不用为每个项目配置库包含目录了…

[微信小程序] 关于自定义字体的坑

报错&#xff1a; [渲染层网络层错误] Failed to load local font resource... the server responded with a status of 500 (HTTP/1.1 500 Internal Server Error) ... 原因&#xff1a;小程序不允许引用本地ttf等字体文件 解决办法&#xff1a;改成网络引用&#xff0c;或者…

通过xshell连接服务器后出现中文乱码的解决方法

先查看服务器环境变量中设置的是什么字符集&#xff0c;命令为&#xff1a;locale 然后在xshell工具中把字符集设置为上面查出来的 然后关掉xshell工具再重新打开。

Python SMTP发送邮件

如何使用Python发送QQ邮件&#xff1f;如何发送带附件的邮件&#xff1f;这篇文章将详细说明 目录 一、发送邮件 二、发送HTML格式的邮件 三、在HTML中添加图片 四、发送带附件的邮件 五、最终整合版 六、配置指引 一、发送邮件 import smtplib from email.mime.text im…

疑问:为什么我的手机不能同时放两张电信卡呢?联通移动可以

很多后台的小伙伴私信我&#xff1a;“为什么我的双卡双待手机不能用两张电信卡呢&#xff1f;”其实我一直在认真的去查证这个问题&#xff0c;因为现在普遍网上的大流量手机卡套餐&#xff0c;电信是主力&#xff0c;如果第一张卡是电信&#xff0c;第二张卡不能使用电信了&a…