Camunda执行监听器与任务监听器

💖专栏简介

✔️本专栏将从Camunda(卡蒙达) 7中的关键概念到实现中国式工作流相关功能。

✔️文章中只包含演示核心代码及测试数据,完整代码可查看作者的开源项目snail-camunda

✔️请给snail-camunda 点颗星吧😘

💖Execution Listener

执行监听器可以在流程执行期间发生某些事件时执行额外的Java代码或者计算表达式,这些事件包含:

  • 流程的开始与结束
  • 活动的开始与结束
  • 网关的开始与结束
  • 中间事件的开始与结束

执行监听器的触发事件有:

  • start
  • end
  • take

其中节点有start、end两种事件,而连线则有take事件。连线也称之为过渡,take事件也就是在进行过渡

针对监听器类型首先演示一下Expression,在Start Event节点设置执行监听器。

与其他表达式一样,执行变量可以被解析使用。因为执行实现对象有一个公开事件名称的属性,所以可以使用execution. eventName将事件名称传递给方法。

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1z0li53" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.19.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.20.0">
  <bpmn:process id="Process_0ic6df5" isExecutable="true" camunda:historyTimeToLive="180">
    <bpmn:extensionElements />
    <bpmn:startEvent id="StartEvent_1">
      <bpmn:extensionElements>
        <camunda:executionListener expression="${StartEvent.hello(execution.eventName)}" event="start" />
      </bpmn:extensionElements>
      <bpmn:outgoing>Flow_16zen96</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:sequenceFlow id="Flow_16zen96" sourceRef="StartEvent_1" targetRef="Activity_0im4bve">
      <bpmn:extensionElements />
    </bpmn:sequenceFlow>
    <bpmn:endEvent id="Event_1w6waod">
      <bpmn:extensionElements />
      <bpmn:incoming>Flow_1yfgw6n</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_1yfgw6n" sourceRef="Activity_0im4bve" targetRef="Event_1w6waod" />
    <bpmn:userTask id="Activity_0im4bve" name="审批人" camunda:assignee="${assignee}">
      <bpmn:extensionElements />
      <bpmn:incoming>Flow_16zen96</bpmn:incoming>
      <bpmn:outgoing>Flow_1yfgw6n</bpmn:outgoing>
    </bpmn:userTask>
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0ic6df5">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="179" y="99" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1w6waod_di" bpmnElement="Event_1w6waod">
        <dc:Bounds x="432" y="99" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_02axx51_di" bpmnElement="Activity_0im4bve">
        <dc:Bounds x="270" y="77" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_16zen96_di" bpmnElement="Flow_16zen96">
        <di:waypoint x="215" y="117" />
        <di:waypoint x="270" y="117" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1yfgw6n_di" bpmnElement="Flow_1yfgw6n">
        <di:waypoint x="370" y="117" />
        <di:waypoint x="432" y="117" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

在代码中如下处理

@Slf4j
@Component("StartEvent")
public class StartEvent {
    public void hello(String eventName){
        log.info("挑战Camunda,eventName--{}",eventName);
    }
}

发起流程实力后:

💖Task Listener

任务监听器的触发事件类型如下:

  • create :在create事件之前不会触发其他与任务相关的事件
  • assignment:专门跟踪assignee 属性的更改
  • complete:在任务从运行时数据中删除之前,发生complete事件,该事件的成功执行代表任务事件生命周期的结束。
  • delete:删除事件发生在从运行时数据中删除任务之前,在delete事件之后不会触发其他事件,因为它也代表任务事件生命周期的结束。这意味着delete事件与complete事件是互斥的
  • update:当修改任务属性时触发,任务属性包含分配人,所有者,注释,任务局部变量等
  • timeout:当与此任务监听器关联的计时器到期时,将发生超时事件。注意,这需要定义一个Timer。

设计流程定义,在用户任务节点设置任务监听器的六种事件

这里比较特殊的是Timeout,此处的ID就是唯一标识符,仅在事件设置为timeout时才需要

选择不同的Type就要设置不同格式的值:

  • Date: 格式为ISO 8601格式的固定时间和日期 比如 2024-03-11T12:13:14Z
  • Duration:两种格式分别为PnYnMnDTnHnMnS、PnW。比如P10D 【间隔10天】
  • Cycle:两种格式分别为ISO 8601重复间隔标准规定的重复持续时间格式,比如R3/PT10H【3 次重复间隔,每次持续 10 小时】、第二种就是cron表达式

注意下面流程定义中的任务监听器 Java class路径 一定要根据自己的项目做调整

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1s60m5d" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.19.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.20.0">
  <bpmn:process id="Process_144a42e" isExecutable="true" camunda:historyTimeToLive="180">
    <bpmn:startEvent id="StartEvent_1">
      <bpmn:extensionElements />
      <bpmn:outgoing>Flow_1qgw3o5</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:sequenceFlow id="Flow_1qgw3o5" sourceRef="StartEvent_1" targetRef="Activity_0gpxgwy">
      <bpmn:extensionElements />
    </bpmn:sequenceFlow>
    <bpmn:userTask id="Activity_0gpxgwy" name="测试任务监听器" camunda:assignee="${assignee}">
      <bpmn:extensionElements>
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="create" />
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="assignment" />
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="complete" />
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="delete" />
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="update" />
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="timeout" id="listenerOne">
          <bpmn:timerEventDefinition id="TimerEventDefinition_1kyckfx">
            <bpmn:timeCycle xsi:type="bpmn:tFormalExpression">5 * * * * ?</bpmn:timeCycle>
          </bpmn:timerEventDefinition>
        </camunda:taskListener>
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_1qgw3o5</bpmn:incoming>
      <bpmn:outgoing>Flow_0589xmv</bpmn:outgoing>
      <bpmn:multiInstanceLoopCharacteristics camunda:collection="${assigneeList}" camunda:elementVariable="assignee">
        <bpmn:completionCondition xsi:type="bpmn:tFormalExpression">${nrOfCompletedInstances == 1}</bpmn:completionCondition>
      </bpmn:multiInstanceLoopCharacteristics>
    </bpmn:userTask>
    <bpmn:endEvent id="Event_0ybej7n">
      <bpmn:incoming>Flow_0589xmv</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_0589xmv" sourceRef="Activity_0gpxgwy" targetRef="Event_0ybej7n" />
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_144a42e">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="179" y="99" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_087xqfi_di" bpmnElement="Activity_0gpxgwy">
        <dc:Bounds x="270" y="77" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_0ybej7n_di" bpmnElement="Event_0ybej7n">
        <dc:Bounds x="432" y="99" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_1qgw3o5_di" bpmnElement="Flow_1qgw3o5">
        <di:waypoint x="215" y="117" />
        <di:waypoint x="270" y="117" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0589xmv_di" bpmnElement="Flow_0589xmv">
        <di:waypoint x="370" y="117" />
        <di:waypoint x="432" y="117" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

相关代码:

/**
 * 流程实例相关接口
 *
 * @author lonewalker
 */
@RequestMapping("/process/instance")
@RequiredArgsConstructor
@RestController
public class ProcessInstanceController {

    private final RuntimeService runtimeService;

    private final TaskService taskService;

    /**
     * 根据流程定义key发起流程实例
     *
     * @param requestParam 请求参数
     * @return 流程实例id
     */
    @PostMapping("/startProcessInstanceByKey")
    public String startProcessInstanceByKey(@RequestBody StartProcessRequest requestParam) {
        Map<String, Object> paramMap = new HashMap<>(8);
        List<String> assigneeList = new ArrayList<>();
        assigneeList.add("10086");
        assigneeList.add("10087");
        assigneeList.add("10088");
        paramMap.put("assigneeList", assigneeList);

        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(requestParam.getProcessDefinitionKey(), requestParam.getBusinessKey(), paramMap);
        return processInstance.getProcessInstanceId();
    }

    /**
     * 完成单个任务
     *
     * @param requestParam 请求参数
     * @return {@code true 成功}
     */
    @PostMapping("/completeSingleTask")
    public Boolean completeSingleTask(@RequestBody @Validated CompleteTaskRequest requestParam) {
        taskService.complete(requestParam.getTaskId());
        return true;
    }

    /**
     * 转交任务
     *
     * @param requestParam 请求参数
     * @return {@code true 成功}
     */
    @PostMapping("/transferTask")
    public Boolean transferTask(@RequestBody TransferTaskRequest requestParam){
        taskService.setAssignee(requestParam.getTaskId(), requestParam.getUserId());
        return true;
    }
}

各位按顺序看过来的,所以部分前文已有的参数类就不贴代码了

@Data
public class TransferTaskRequest {

    private String taskId;

    private String userId;
}

自定义任务监听器:

@Slf4j
@Component
public class CustomTaskListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        log.info("Event Type--{}",delegateTask.getEventName());
    }
}

发起流程实例后,等待超过设置的时间,控制台打印如下:

调用新增的【转交任务】接口,可以看到又执行了update和assignment,符合预期。

由于我们设置的是三个人或签,当调用完成任务接口后:第一个任务触发complete,另外两个触发delete

💖动态获取审批人

实际项目中可能会设置节点审批人是角色,那这个角色对应的人是动态变化的,假如流程被驳回到中间的某个节点再回到这个节点,需要保证获取角色对应的人员是最新的。

前文的演示中都是在发起流程实例时直接传入审批人参数,这里演示一下如何通过执行监听器 take获取审批人,介绍Execution Listener时也提到take是在连线【过渡】上设置的。当然也有朋友疑惑为何不在Task Listener上设置审批人,首先我们知道assignment事件是在assignee属性变更时触发,此时我们要设置assignee肯定要在它之前就要拿到值,那还剩个create事件,我们测试一下:

在执行监听器完成后就报错了

也就是一定要在创建任务之前就拿到审批人数据,流程引擎是根据传入的数据以及配置来判断创建几个任务

修改流程定义,在用户任务节点前的连线【过渡】上设置执行监听器take事件

我的路径是com.lonewalker.demo.listener.CustomExecutionListener,各位根据自己的项目修改

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1s60m5d" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.19.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.20.0">
  <bpmn:process id="Process_144a42e" isExecutable="true" camunda:historyTimeToLive="180">
    <bpmn:startEvent id="StartEvent_1">
      <bpmn:extensionElements />
      <bpmn:outgoing>Flow_1qgw3o5</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:sequenceFlow id="Flow_1qgw3o5" sourceRef="StartEvent_1" targetRef="Activity_0gpxgwy">
      <bpmn:extensionElements>
        <camunda:executionListener class="com.lonewalker.demo.listener.CustomExecutionListener" event="take" />
      </bpmn:extensionElements>
    </bpmn:sequenceFlow>
    <bpmn:userTask id="Activity_0gpxgwy" name="测试任务监听器" camunda:assignee="${assignee}">
      <bpmn:extensionElements>
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="create" />
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="assignment" />
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="complete" />
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="delete" />
        <camunda:taskListener class="com.lonewalker.demo.listener.CustomTaskListener" event="update" />
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_1qgw3o5</bpmn:incoming>
      <bpmn:outgoing>Flow_0589xmv</bpmn:outgoing>
      <bpmn:multiInstanceLoopCharacteristics camunda:collection="${assigneeList}" camunda:elementVariable="assignee">
        <bpmn:completionCondition xsi:type="bpmn:tFormalExpression">${nrOfCompletedInstances == 1}</bpmn:completionCondition>
      </bpmn:multiInstanceLoopCharacteristics>
    </bpmn:userTask>
    <bpmn:endEvent id="Event_0ybej7n">
      <bpmn:incoming>Flow_0589xmv</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_0589xmv" sourceRef="Activity_0gpxgwy" targetRef="Event_0ybej7n" />
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_144a42e">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="179" y="99" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_087xqfi_di" bpmnElement="Activity_0gpxgwy">
        <dc:Bounds x="270" y="77" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_0ybej7n_di" bpmnElement="Event_0ybej7n">
        <dc:Bounds x="432" y="99" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_1qgw3o5_di" bpmnElement="Flow_1qgw3o5">
        <di:waypoint x="215" y="117" />
        <di:waypoint x="270" y="117" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0589xmv_di" bpmnElement="Flow_0589xmv">
        <di:waypoint x="370" y="117" />
        <di:waypoint x="432" y="117" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

代码中将发起流程实例时传入的审批人参数转到执行监听器中:

@Slf4j
@Component
public class CustomExecutionListener implements ExecutionListener {
    @Override
    public void notify(DelegateExecution execution) {
        log.info("【执行监听器】Event Type--{}", execution.getEventName());
        List<String> assigneeList = new ArrayList<>();
        assigneeList.add("10086");
        assigneeList.add("10087");
        assigneeList.add("10088");
        execution.setVariable("assigneeList", assigneeList);
    }
}

重新部署、发起流程实例

💖扩展

网上很多人说Task Linstener的assignment事件是在create之前,根据我们的案例演示很明显不是

Camunda BPM 7是一个流程自动化平台,它Spring Boot集成可以帮助简化企业级工作流管理。整合Spring Boot后的全局任务监听器(Global Task Listener)允许你在处理所有任务实例执行过程的不同阶段,如开始、完成或失败时执行自定义操作。 要在Camunda 7和Spring Boot项目中添加全局任务监听器,通常需要做以下步骤: 1. 添加依赖:首先,在你的`pom.xml`或`build.gradle`文件中添加Camunda Spring Boot Starter的依赖。 ```xml <dependency> <groupId>org.camunda.bpm.springboot</groupId> <artifactId>camunda-bpm-spring-boot-starter</artifactId> <version>版本号</version> </dependency> ``` 2. 创建监听器:创建一个实现了`org.camunda.bpm.engine.delegate.TaskListener`接口的Spring组件,例如`MyTaskEventListener.java`。 ```java @Component public class MyTaskEventListener implements TaskListener { @Override public void notify(DelegateTask delegateTask) { // 在这里编写你的业务逻辑,比如日志记录或通知系统 log.info("Task [{}] started by [{}]", delegateTask.getTaskDefinitionKey(), delegateTask.getExecution().getProcessInstanceId()); } } ``` 3. 注册监听器:在Spring配置类中注册监听器,让它成为全局生效。 ```java @Configuration @EnableProcessApplication public class AppConfig extends ProcessApplication { @Autowired private TaskService taskService; @Bean public GlobalTaskListener taskCompleteListener() { return new MyTaskEventListener(); } // 其他Spring Boot配置... } ``` 4. 启动应用:现在,每当有任务实例被执行,你的全局监听器就会自动触发相应的回调。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LoneWalker、

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值