部分简介摘抄自官方文档
因为Activiti流程设计器暂不支持补偿中间事件,所以该文实例参考杨大仙的博客
https://blog.csdn.net/boxiong86/article/details/78858563
推荐他的书籍《疯狂工作流讲义(第2版)》
*后台服务基于Springboot2 + Activiti6,整合文章请参考:https://blog.csdn.net/yy756127197/article/details/101211510 不需要流程设计器就排除3,4步骤 *
1. 简介
1.1 中间事件
中间事件分为两类:中间Cathing事件和中间Throwing事件,当流程到达中间Cathing事件时,它会一直等待知道接收到消息,才会触发。当流程到达中间Throwing事件时,该事件会自动触发并抛出结果。
BPMN2.0中定义的中间Cathing事件有:消息中间事件,定时器中间事件,条件中间事件,连接中间事件,信号中间事件,组合中间事件,并行中间事件等。
中间Throwing事件有:无指定中间事件,消息中间事件,补偿中间事件,连接中间事件,信号中间事件,组合中间事件等。
注意:补偿边界事件不支持内嵌子流程。
1.2 补偿中间事件
补偿中间事件属于中间Throwing事件,主要用来触发补偿边界事件。
注意:中间Cathing事件,流程设计器当前只支持消息中间事件和信号中间事件和定时器中间事件
注意:中间Throwing事件,流程设计器当前只支持无指定中间事件和信号中间事件
2. 补偿边界事件
2.1 简介
补偿边界事件与其他边界事件的策略不同。 其他边界事件(比如信号边界事件)当到达关联的节点就会被激活。 离开节点时,就会挂起,对应的事件订阅也会取消。 补偿边界事件则不同。补偿边界事件在关联的节点成功完成时激活。 当补偿事件触发或对应流程实例结束时,事件订阅才会删除。
特性
- 当补偿中间事件触发时,会触发当前执行流中的补偿Catching事件,如果补偿边界事件依附的节点是多实例的,那么补偿事件也会触发多次。
- 补偿的执行顺序会按照倒叙进行补偿。
- 只会补偿已经完成的任务,还没有完成的任务补偿边界事件是不会触发的。
注意:如果补偿被一个包含子流程的作用域触发,子流程还包含了关联补偿处理器的节点, 补偿只会传播到子流程,如果它已经成功完成了。 如果子流程中的节点也完成了,并关联了补偿处理器, 如果子流程包含的这些节点还没有完成,就不会执行补偿处理器。 参考下面实例:
2.2 流程设计
例如有如下场景,银行转账流程,流程启动后,A银行扣款,B银行收款,之后进入转行验证环节,如果成功流程结束,如果验证失败,AB银行执行补偿事件。
流程图
原图来自https://my.oschina.net/JavaLaw/blog/1589427
流程文件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: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">
<process id="cbProcess" name="cbProcess" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<serviceTask id="servicetask2" name="转入银行加款" activiti:class="com.it.cloud.activiti.boundary.delegate.TransferInDelegate"></serviceTask>
<boundaryEvent id="boundarysignal2" attachedToRef="servicetask2" cancelActivity="true">
<compensateEventDefinition></compensateEventDefinition>
</boundaryEvent>
<serviceTask id="servicetask4" name="转出银行扣款" activiti:class="com.it.cloud.activiti.boundary.delegate.TransferOutDelegate"></serviceTask>
<boundaryEvent id="boundarysignal1" attachedToRef="servicetask4" cancelActivity="true">
<compensateEventDefinition></compensateEventDefinition>
</boundaryEvent>
<serviceTask id="servicetask5" name="转出银行取消" isForCompensation="true" activiti:class="com.it.cloud.activiti.boundary.delegate.CancelTransferOutDelegate"></serviceTask>
<serviceTask id="servicetask6" name="转入银行取消" isForCompensation="true" activiti:class="com.it.cloud.activiti.boundary.delegate.CancelTransferInDelegate"></serviceTask>
<serviceTask id="servicetask7" name="验证转账结果" activiti:class="com.it.cloud.activiti.boundary.delegate.ValidateTransferDelegate"></serviceTask>
<boundaryEvent id="boundaryerror1" attachedToRef="servicetask7">
<errorEventDefinition errorRef="transferError"></errorEventDefinition>
</boundaryEvent>
<endEvent id="endevent1" name="End"></endEvent>
<endEvent id="endevent2" name="End"></endEvent>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="servicetask4"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="servicetask4" targetRef="servicetask2"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="servicetask2" targetRef="servicetask7"></sequenceFlow>
<sequenceFlow id="flow4" sourceRef="servicetask7" targetRef="endevent1"></sequenceFlow>
<intermediateThrowEvent id="noneintermediatethrowevent1" name="NoneThrowEvent">
<compensateEventDefinition></compensateEventDefinition>
</intermediateThrowEvent>
<sequenceFlow id="flow5" sourceRef="boundaryerror1" targetRef="noneintermediatethrowevent1"></sequenceFlow>
<sequenceFlow id="flow6" sourceRef="noneintermediatethrowevent1" targetRef="endevent2"></sequenceFlow>
<association id="a1" sourceRef="boundarysignal1" targetRef="servicetask5"></association>
<association id="a2" sourceRef="boundarysignal2" targetRef="servicetask6"></association>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_cbProcess">
<bpmndi:BPMNPlane bpmnElement="cbProcess" id="BPMNPlane_cbProcess">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="200.0" y="190.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask2" id="BPMNShape_servicetask2">
<omgdc:Bounds height="55.0" width="105.0" x="430.0" y="180.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="boundarysignal2" id="BPMNShape_boundarysignal2">
<omgdc:Bounds height="30.0" width="30.0" x="490.0" y="230.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask4" id="BPMNShape_servicetask4">
<omgdc:Bounds height="55.0" width="105.0" x="290.0" y="180.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="boundarysignal1" id="BPMNShape_boundarysignal1">
<omgdc:Bounds height="30.0" width="30.0" x="350.0" y="230.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask5" id="BPMNShape_servicetask5">
<omgdc:Bounds height="55.0" width="105.0" x="280.0" y="280.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask6" id="BPMNShape_servicetask6">
<omgdc:Bounds height="55.0" width="105.0" x="430.0" y="280.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask7" id="BPMNShape_servicetask7">
<omgdc:Bounds height="55.0" width="105.0" x="600.0" y="180.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="boundaryerror1" id="BPMNShape_boundaryerror1">
<omgdc:Bounds height="30.0" width="30.0" x="660.0" y="230.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="760.0" y="190.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent2" id="BPMNShape_endevent2">
<omgdc:Bounds height="35.0" width="35.0" x="760.0" y="300.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="noneintermediatethrowevent1" id="BPMNShape_noneintermediatethrowevent1">
<omgdc:Bounds height="35.0" width="35.0" x="670.0" y="300.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="235.0" y="207.0"></omgdi:waypoint>
<omgdi:waypoint x="290.0" y="207.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="395.0" y="207.0"></omgdi:waypoint>
<omgdi:waypoint x="430.0" y="207.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="535.0" y="207.0"></omgdi:waypoint>
<omgdi:waypoint x="600.0" y="207.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="705.0" y="207.0"></omgdi:waypoint>
<omgdi:waypoint x="760.0" y="207.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
<omgdi:waypoint x="675.0" y="260.0"></omgdi:waypoint>
<omgdi:waypoint x="687.0" y="300.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
<omgdi:waypoint x="705.0" y="317.0"></omgdi:waypoint>
<omgdi:waypoint x="760.0" y="317.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="a1" id="BPMNEdge_a1">
<omgdi:waypoint x="365.0" y="260.0"></omgdi:waypoint>
<omgdi:waypoint x="332.0" y="280.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="a2" id="BPMNEdge_a2">
<omgdi:waypoint x="505.0" y="260.0"></omgdi:waypoint>
<omgdi:waypoint x="482.0" y="280.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
2.3 测试类
CompensationBoundaryTest.java
import com.it.cloud.modules.activiti.service.IActReModelService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 补偿边界事件
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class CompensationBoundaryTest {
@Autowired
private IActReModelService actReModelService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 部署流程定义
*/
@Test
public void deploy() {
Deployment deployment = repositoryService.createDeployment() // 创建部署
.addClasspathResource("diagrams/compensationBoundary.bpmn20.xml") // 加载流程资源文件
.name("cbProcess流程") // 流程名称
.deploy(); // 部署
System.out.println("流程部署ID:" + deployment.getId());
System.out.println("流程部署Name:" + deployment.getName());
}
/**
* 启动流程实例
*/
@Test
public void start() throws InterruptedException {
// 参数
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("result", false);
ProcessInstance pi = runtimeService.startProcessInstanceByKey("cbProcess", vars); // 流程定义表的KEY字段值
System.out.println("流程实例ID:" + pi.getId());
System.out.println("流程定义ID:" + pi.getProcessDefinitionId());
}
}
2.4 运行
运行deploy()
2.4.1 启动任务
运行start()
效果:
当result=false时
当result=true时
源码地址
IT-CLOUD-ACTIVITI6
开源项目,持续更新中,喜欢请 Star~
项目推荐
IT-CLOUD :IT服务管理平台,集成基础服务,中间件服务,监控告警服务等。
开源项目,持续更新中,喜欢请 Star~