工作流flowable任务退回(任务跳转)、任务重新触发、任务删除的实现
在进行flowable工作流的运用中,会涉及到任务的的一些特殊操作,如:退回,跳转,删除,重新触发等。退回和重新触发 主要借助changeActivityState,删除主要用ExecutionEntityManager.deleteExecutionAndRelatedData()方法。
主要实现方法如下。
退回(任务跳转到指定节点)
退回功能,主要是找到当前节点(可能多个)的ExecutionId和目标节点的ActivityId,然后通过 moveExecutionsToSingleActivityId().changeState()实现。
这个功能也适用于任务的前进,比如: 流程是 A ->B->C->D->E , 当前在C节点, 可以通过这个功能将流程 退回到 B ,也可以 前进到E, 只要流程图线路是可达的。
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.iccboy.framework.flowable.core.FlowableConstant;
import com.iccboy.framework.flowable.core.util.FlowableUtils;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.delegate.ActivityBehavior;
import org.flowable.engine.impl.persistence.entity.ActivityInstanceEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import com.google.common.collect.Sets;
/**
* 任务退回(跳到指定节点)
* @Author iccboy
*/
public class BackTaskCmd implements Command<String>, Serializable {
public static final long serialVersionUID = 1L;
private final transient RuntimeService runtimeService;
/**
* 当前运行的任务,流程节点id
*/
protected String taskIdOrActivityIdOrExecutionId;
/**
* 需要跳转的任务节点定义ID
*/
protected String targetActivityId;
public BackTaskCmd(RuntimeService runtimeService, String taskId, String targetActivityId) {
this.runtimeService = runtimeService;
this.taskIdOrActivityIdOrExecutionId = taskId;
this.targetActivityId = targetActivityId;
}
@Override
public String execute(CommandContext commandContext) {
if (targetActivityId == null || targetActivityId.length() == 0) {
throw new FlowableException("TargetActivityId cannot be empty");
}
TaskEntity task = CommandContextUtil.getProcessEngineConfiguration().getTaskServiceConfiguration().getTaskService().getTask(taskIdOrActivityIdOrExecutionId);
String sourceActivityId = null;
String processInstanceId = null;
String processDefinitionId = null;
String executionId = null;
if (task != null) {
sourceActivityId = task.getTaskDefinitionKey();
processInstanceId = task.getProcessInstanceId();
processDefinitionId = task.getProcessDefinitionId();
executionId = task.getExecutionId();
} else {
ActivityInstanceEntity instanceEntity = CommandContextUtil.getActivityInstanceEntityManager().findById(taskIdOrActivityIdOrExecutionId);
if(instanceEntity != null) {
sourceActivityId = instanceEntity.getProcessInstanceId();
processInstanceId = instanceEntity.getActivityId();
processDefinitionId = instanceEntity.getProcessDefinitionId();
executionId = instanceEntity.getExecutionId();
} else {
ExecutionEntity executionEntity = CommandContextUtil.getExecutionEntityManager().findById(taskIdOrActivityIdOrExecutionId);
if(executionEntity != null) {
sourceActivityId = executionEntity.getActivityId();
processInstanceId = executionEntity.getProcessInstanceId();
processDefinitionId = executionEntity.getProcessDefinitionId();
executionId = executionEntity.getId();
}
}
}
if(sourceActivityId == null) {
throw new FlowableObjectNotFoundException("task " + taskIdOrActivityIdOrExecutionId + " doesn't exist", Task.class);
}
Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
FlowNode sourceFlowElement = (FlowNode) process.getFlowElement(sourceActivityId, true);
// 只支持从用户任务退回
if (!(sourceFlowElement instanceof UserTask)) {
//throw new FlowableException("Task with id:" + taskId + " is not a UserTask");
}
FlowNode targetFlowElement = (FlowNode) process.getFlowElement(targetActivityId, true);
// 退回节点到当前节点不可达到,不允许退回
if (!FlowableUtils.isReachable(process, targetFlowElement, sourceFlowElement)) {
throw new FlowableException("Cannot back to [" + targetActivityId + "]");
}
// ps:目标节点如果相对当前节点是在子流程内部,则无法直接退回,目前处理是只能退回到子流程开始节点
String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement,
targetFlowElement);
// 实际应操作的当前节点ID
String sourceRealActivityId = sourceAndTargetRealActivityId[0];
// 实际应操作的目标节点ID
String targetRealActivityId = sourceAndTargetRealActivityId[1];
Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);
// 当前节点处在的并行网关list
List<String> sourceInSpecialGatewayList = new ArrayList<>();
// 目标节点处在的并行网关list
List<String> targetInSpecialGatewayList = new ArrayList<>();
setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes,
sourceInSpecialGatewayList, targetInSpecialGatewayList);
// 实际应筛选的节点ID
Set<String> sourceRealAcitivtyIds = null;
// 若退回目标节点相对当前节点在并行网关中,则要找到相对当前节点最近的这个并行网关,后续做特殊处理
String targetRealSpecialGateway = null;
// 1.目标节点和当前节点都不在并行网关中
if (targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
}
// 2.目标节点不在并行网关中、当前节点在并行网关中
else if (targetInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(0));
}
// 3.目标节点在并行网关中、当前节点不在并行网关中
else if (sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
targetRealSpecialGateway = targetInSpecialGatewayList.get(0);
}
// 4.目标节点和当前节点都在并行网关中
else {
int diffSpecialGatewayLevel = FlowableUtils.getDiffLevel(sourceInSpecialGatewayList,
targetInSpecialGatewayList);
// 在并行网关同一层且在同一分支
if (diffSpecialGatewayLevel == -1) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
} else {
// 当前节点最内层并行网关不被目标节点最内层并行网关包含
// 或理解为当前节点相对目标节点在并行网关外
// 只筛选当前节点的execution
if (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
}
// 当前节点相对目标节点在并行网关内,应筛选相对目标节点最近的并行网关的所有节点的execution
else {
sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));
}
// 目标节点最内层并行网关包含当前节点最内层并行网关
// 或理解为目标节点相对当前节点在并行网关外
// 不做处理
if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
}
// 目标节点相对当前节点在并行网关内
else {
targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);
}
}
}
// 筛选需要处理的execution
List<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId,
executionId, sourceRealActivityId, sourceRealAcitivtyIds);
// 执行退回,直接跳转到实际的 targetRealActivityId
List<String> realExecutionIds =
realExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toList());
runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
.moveExecutionsToSingleActivityId(realExecutionIds, targetRealActivityId).changeState();
// 目标节点相对当前节点处于并行网关内,需要特殊处理,需要手动生成并行网关汇聚节点(_end)的execution数据
if (targetRealSpecialGateway != null) {
createTargetInSpecialGatewayEndExecutions(commandContext, realExecutions, process,
targetInSpecialGatewayList, targetRealSpecialGateway);
}
return targetRealActivityId;
}
private void setSpecialGatewayList(String sourceActivityId, String targetActivityId,
Map<String, Set<String>> specialGatewayNodes,
List<String> sourceInSpecialGatewayList,
List<String> targetInSpecialGatewayList) {
for (Map.Entry<String, Set<String>> entry : specialGatewayNodes.entrySet()) {
if (entry.getValue().contains(sourceActivityId)) {
sourceInSpecialGatewayList.add(entry.getKey());
}
if (entry.getValue().contains(targetActivityId)) {
targetInSpecialGatewayList.add(entry.getKey());
}
}
}
private void createTargetInSpecialGatewayEndExecutions(CommandContext commandContext,
List<ExecutionEntity> excutionEntitys, Process process,
List<String> targetInSpecialGatewayList,
String targetRealSpecialGateway) {
// 目标节点相对当前节点处于并行网关,需要手动生成并行网关汇聚节点(_end)的execution数据
String parentExecutionId = excutionEntitys.iterator().next().getParentId();
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity parentExecutionEntity = executionEntityManager.findById(parentExecutionId);
int index = targetInSpecialGatewayList.indexOf(targetRealSpecialGateway);
for (; index < targetInSpecialGatewayList.size(); index++) {
String targetInSpecialGateway = targetInSpecialGatewayList.get(index);
String targetInSpecialGatewayEndId = targetInSpecialGateway + FlowableConstant.SPECIAL_GATEWAY_END_SUFFIX;
FlowNode targetInSpecialGatewayEnd = (FlowNode) process.getFlowElement(targetInSpecialGatewayEndId, true);
int nbrOfExecutionsToJoin = targetInSpecialGatewayEnd.getIncomingFlows().size();
// 处理目标节点所处的分支以外的分支,即 总分枝数-1 = nbrOfExecutionsToJoin - 1
for (int i = 0; i < nbrOfExecutionsToJoin - 1; i++) {
ExecutionEntity childExecution = executionEntityManager.createChildExecution(parentExecutionEntity);
childExecution.setCurrentFlowElement(targetInSpecialGatewayEnd);
ActivityBehavior activityBehavior = (ActivityBehavior) targetInSpecialGatewayEnd.getBehavior();
activityBehavior.execute(childExecution);
}
}
}
private List<ExecutionEntity> getRealExecutions(CommandContext commandContext, String processInstanceId,
String taskExecutionId, String sourceRealActivityId,
Set<String> activityIds) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity taskExecution = executionEntityManager.findById(taskExecutionId);
List<ExecutionEntity> executions =
executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
Set<String> parentExecutionIds = FlowableUtils.getParentExecutionIdsByActivityId(executions,
sourceRealActivityId);
String realParentExecutionId = FlowableUtils.getParentExecutionIdFromParentIds(taskExecution,
parentExecutionIds);
return executionEntityManager.findExecutionsByParentExecutionAndActivityIds(realParentExecutionId,
activityIds);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
上面方法的调用如下:
@Autowired
private ManagementService managementService;
//.....
public void backTask(String taskId, String activityId) {
String targetRealActivityId = managementService.executeCommand(new BackTaskCmd(runtimeService,
taskId, activityId));
log.info("TaskId:{},流程跳到到:{}", taskId, targetRealActivityId);
}
1
2
3
4
5
6
7
8
9
10
任务重新触发
任务重新触发,主要用于当前节点是 ServiceTask的情况,比如:当ServiceTask是异步任务时,然后没有进行边界异常事件的处理,如果当任务执行异常时,会阻碍流程的继续,此时就需要重新触发任务的执行,让流程重新推进下去。主要实现思路与上面的任务跳转类似。就是当前节点跳当前节点。使用的方法还是moveExecutionsToSingleActivityId().changeState().
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Sets;
import com.iccboy.framework.flowable.core.FlowableConstant;
import com.iccboy.framework.flowable.core.util.FlowableUtils;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.delegate.ActivityBehavior;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.ProcessInstance;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
/**
* 重新加载任务
* @author iccboy
* @date 2023年1月8日
*/
public class ReloadTaskCmd implements Command<String>, Serializable {
public static final long serialVersionUID = 1L;
protected RuntimeService runtimeService;
protected String businessKey;
protected String targetActivityId;
public ReloadTaskCmd(RuntimeService runtimeService, String businessKey, String targetActivityId) {
this.runtimeService = runtimeService;
this.businessKey = businessKey;
this.targetActivityId = targetActivityId;
}
@Override
public String execute(CommandContext commandContext) {
if (StrUtil.isBlank(targetActivityId)) {
throw new FlowableException("TargetActivityId cannot be empty");
}
if (StrUtil.isBlank(businessKey)) {
throw new FlowableException("BusinessKey cannot be empty");
}
String sourceActivityId = null;
String processInstanceId = null;
String processDefinitionId = null;
String executionId = null;
ActivityInstance activityInstance = null;
List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey).list();
for (ProcessInstance processInstance : processInstanceList) {
List<ActivityInstance> activityInstances = runtimeService.createActivityInstanceQuery().processInstanceId(processInstance.getProcessInstanceId())
.activityId(targetActivityId).orderByActivityInstanceStartTime().desc().list();
if(CollUtil.isNotEmpty(activityInstances)) {
activityInstance = activityInstances.get(0);
sourceActivityId = activityInstance.getActivityId();
processInstanceId = activityInstance.getProcessInstanceId();
processDefinitionId = activityInstance.getProcessDefinitionId();
executionId = activityInstance.getExecutionId();
break;
}
}
if (activityInstance == null) {
for (ProcessInstance processInstance : processInstanceList) {
List<ExecutionEntity> executionEntitys = CommandContextUtil.getExecutionEntityManager()
.findExecutionsByParentExecutionAndActivityIds(processInstance.getProcessInstanceId(), Collections.singleton(targetActivityId));
if(CollUtil.isNotEmpty(executionEntitys)) {
ExecutionEntity executionEntity = executionEntitys.stream().max(Comparator.comparing(ExecutionEntity::getStartTime)).orElse(null);
sourceActivityId = executionEntity.getActivityId();
processInstanceId = executionEntity.getProcessInstanceId();
processDefinitionId = executionEntity.getProcessDefinitionId();
executionId = executionEntity.getId();
break;
}
}
}
if(sourceActivityId == null) {
throw new FlowableObjectNotFoundException("targetActivity: " + targetActivityId + " does not exist");
}
Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
FlowNode sourceFlowElement = (FlowNode) process.getFlowElement(sourceActivityId, true);
FlowNode targetFlowElement = (FlowNode) process.getFlowElement(targetActivityId, true);
// 退回节点到当前节点不可达到,不允许退回
if (!FlowableUtils.isReachable(process, targetFlowElement, sourceFlowElement)) {
throw new FlowableException("Cannot back to [" + targetActivityId + "]");
}
// ps:目标节点如果相对当前节点是在子流程内部,则无法直接退回,目前处理是只能退回到子流程开始节点
String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement,
targetFlowElement);
// 实际应操作的当前节点ID
String sourceRealActivityId = sourceAndTargetRealActivityId[0];
// 实际应操作的目标节点ID
String targetRealActivityId = sourceAndTargetRealActivityId[1];
Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);
// 当前节点处在的并行网关list
List<String> sourceInSpecialGatewayList = new ArrayList<>();
// 目标节点处在的并行网关list
List<String> targetInSpecialGatewayList = new ArrayList<>();
setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes,
sourceInSpecialGatewayList, targetInSpecialGatewayList);
// 实际应筛选的节点ID
Set<String> sourceRealAcitivtyIds = null;
// 若退回目标节点相对当前节点在并行网关中,则要找到相对当前节点最近的这个并行网关,后续做特殊处理
String targetRealSpecialGateway = null;
// 1.目标节点和当前节点都不在并行网关中
if (targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
}
// 2.目标节点不在并行网关中、当前节点在并行网关中
else if (targetInSpecialGatewayList.isEmpty() && !sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(0));
}
// 3.目标节点在并行网关中、当前节点不在并行网关中
else if (!targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
targetRealSpecialGateway = targetInSpecialGatewayList.get(0);
}
// 4.目标节点和当前节点都在并行网关中
else {
int diffSpecialGatewayLevel = FlowableUtils.getDiffLevel(sourceInSpecialGatewayList,
targetInSpecialGatewayList);
// 在并行网关同一层且在同一分支
if (diffSpecialGatewayLevel == -1) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
} else {
// 当前节点最内层并行网关不被目标节点最内层并行网关包含
// 或理解为当前节点相对目标节点在并行网关外
// 只筛选当前节点的execution
if (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
}
// 当前节点相对目标节点在并行网关内,应筛选相对目标节点最近的并行网关的所有节点的execution
else {
sourceRealAcitivtyIds =
specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));
}
// 目标节点最内层并行网关包含当前节点最内层并行网关
// 或理解为目标节点相对当前节点在并行网关外
// 不做处理
if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
} else {
// 目标节点相对当前节点在并行网关内
targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);
}
}
}
// 筛选需要处理的execution
List<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId,
executionId, sourceRealActivityId, sourceRealAcitivtyIds);
// 执行退回,直接跳转到实际的 targetRealActivityId
List<String> realExecutionIds =
realExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toList());
runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
.moveExecutionsToSingleActivityId(realExecutionIds, targetRealActivityId).changeState();
// 目标节点相对当前节点处于并行网关内,需要特殊处理,需要手动生成并行网关汇聚节点(_end)的execution数据
if (targetRealSpecialGateway != null) {
createTargetInSpecialGatewayEndExecutions(commandContext, realExecutions, process,
targetInSpecialGatewayList, targetRealSpecialGateway);
}
return targetRealActivityId;
}
private void setSpecialGatewayList(String sourceActivityId, String targetActivityId,
Map<String, Set<String>> specialGatewayNodes,
List<String> sourceInSpecialGatewayList,
List<String> targetInSpecialGatewayList) {
for (Map.Entry<String, Set<String>> entry : specialGatewayNodes.entrySet()) {
if (entry.getValue().contains(sourceActivityId)) {
sourceInSpecialGatewayList.add(entry.getKey());
}
if (entry.getValue().contains(targetActivityId)) {
targetInSpecialGatewayList.add(entry.getKey());
}
}
}
private void createTargetInSpecialGatewayEndExecutions(CommandContext commandContext,
List<ExecutionEntity> excutionEntitys, Process process,
List<String> targetInSpecialGatewayList,
String targetRealSpecialGateway) {
// 目标节点相对当前节点处于并行网关,需要手动生成并行网关汇聚节点(_end)的execution数据
String parentExecutionId = excutionEntitys.iterator().next().getParentId();
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity parentExecutionEntity = executionEntityManager.findById(parentExecutionId);
int index = targetInSpecialGatewayList.indexOf(targetRealSpecialGateway);
for (; index < targetInSpecialGatewayList.size(); index++) {
String targetInSpecialGateway = targetInSpecialGatewayList.get(index);
String targetInSpecialGatewayEndId = targetInSpecialGateway + FlowableConstant.SPECIAL_GATEWAY_END_SUFFIX;
FlowNode targetInSpecialGatewayEnd = (FlowNode) process.getFlowElement(targetInSpecialGatewayEndId, true);
int nbrOfExecutionsToJoin = targetInSpecialGatewayEnd.getIncomingFlows().size();
// 处理目标节点所处的分支以外的分支,即 总分枝数-1 = nbrOfExecutionsToJoin - 1
for (int i = 0; i < nbrOfExecutionsToJoin - 1; i++) {
ExecutionEntity childExecution = executionEntityManager.createChildExecution(parentExecutionEntity);
childExecution.setCurrentFlowElement(targetInSpecialGatewayEnd);
ActivityBehavior activityBehavior = (ActivityBehavior) targetInSpecialGatewayEnd.getBehavior();
activityBehavior.execute(childExecution);
}
}
}
private List<ExecutionEntity> getRealExecutions(CommandContext commandContext, String processInstanceId,
String taskExecutionId, String sourceRealActivityId,
Set<String> activityIds) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity taskExecution = executionEntityManager.findById(taskExecutionId);
List<ExecutionEntity> executions =
executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
Set<String> parentExecutionIds = FlowableUtils.getParentExecutionIdsByActivityId(executions,
sourceRealActivityId);
String realParentExecutionId = FlowableUtils.getParentExecutionIdFromParentIds(taskExecution,
parentExecutionIds);
return executionEntityManager.findExecutionsByParentExecutionAndActivityIds(realParentExecutionId,
activityIds);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
Cmd调用
public String reloadTask(String orderNo, String targetActivityId) {
String targetRealActivityId = managementService.executeCommand(new ReloadTaskCmd(runtimeService,
orderNo, targetActivityId));
log.info("orderNo:{},重新加载:{}", orderNo, targetRealActivityId);
return targetRealActivityId;
}
1
2
3
4
5
6
上面的方法大多和任务跳转代码相似,还可以继续简化。
上面的Cmd中,有个businessKey, 这里主要是通过业务key来定位的ProcessInstance, 也可以直接传参 ProcessInstanceId 进来,这样更简单。
任务(节点)删除
直接调用 taskService.deleteTask()方式时会报错:The task cannot be deleted because is part of a running process
通过ExecutionEntityManager.deleteExecutionAndRelatedData实现删除则没可以。
import cn.hutool.core.lang.Assert;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import java.io.Serializable;
/**
* 删除任务
* @author itchenchao
* @date 2023年1月8日
*/
public class DeleteTaskCmd implements Command<String>, Serializable {
public static final long serialVersionUID = 1L;
protected String executionId;
protected String deleteReason;
public DeleteTaskCmd(String executionId, String deleteReason) {
this.executionId = executionId;
this.deleteReason = deleteReason;
}
@Override
public String execute(CommandContext commandContext) {
if (StrUtil.isBlank(executionId)) {
throw new FlowableException("executionId cannot be empty");
}
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
ExecutionEntity executionEntity = CommandContextUtil.getExecutionEntityManager().findById(executionId);
Assert.notNull(executionEntity, "ExecutionEntity:{}不存在", executionId);
executionEntityManager.deleteExecutionAndRelatedData(executionEntity, deleteReason, false, false);
return executionId;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
调用示例:
managementService.executeCommand(new DeleteTaskCmd(task.getExecutionId(), req.getDeleteReason()));
1
其他
FlowableUtils工具类
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Flowable 相关的工具方法
*
*/
public class FlowableUtils {
public final static String SPECIAL_GATEWAY_BEGIN_SUFFIX = "_begin";
public final static String SPECIAL_GATEWAY_END_SUFFIX = "_end";
public final static String FLOWABLE_NAMESPACE = "http://flowable.org/bpmn";
// ========== User 相关的工具方法 ==========
public static void setAuthenticatedUserId(Long userId) {
Authentication.setAuthenticatedUserId(String.valueOf(userId));
}
public static void setAuthenticatedUserId(String bpmUserId) {
Authentication.setAuthenticatedUserId(bpmUserId);
}
public static void clearAuthenticatedUserId() {
Authentication.setAuthenticatedUserId(null);
}
// ========== BPMN 相关的工具方法 ==========
/**
* 获得 BPMN 流程中,指定的元素们
*
* @param model
* @param clazz 指定元素。例如说,{@link org.flowable.bpmn.model.UserTask}、{@link org.flowable.bpmn.model.Gateway} 等等
* @return 元素们
*/
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
List<T> result = new ArrayList<>();
model.getProcesses().forEach(process -> {
process.getFlowElements().forEach(flowElement -> {
if (flowElement.getClass().isAssignableFrom(clazz)) {
result.add((T) flowElement);
}
});
});
return result;
}
/**
* 比较 两个bpmnModel 是否相同
* @param oldModel 老的bpmn model
* @param newModel 新的bpmn model
*/
public static boolean equals(BpmnModel oldModel, BpmnModel newModel) {
// 由于 BpmnModel 未提供 equals 方法,所以只能转成字节数组,进行比较
return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
}
/**
* 把 bpmnModel 转换成 byte[]
* @param model bpmnModel
*/
public static byte[] getBpmnBytes(BpmnModel model) {
if (model == null) {
return new byte[0];
}
BpmnXMLConverter converter = new BpmnXMLConverter();
return converter.convertToXML(model);
}
// ========== Execution 相关的工具方法 ==========
public static String formatCollectionVariable(String activityId) {
return activityId + "_assignees";
}
public static String formatCollectionElementVariable(String activityId) {
return activityId + "_assignee";
}
public static <T> Map<String, List<T>> groupListContentBy(List<T> source, Function<T, String> classifier) {
return source.stream().collect(Collectors.groupingBy(classifier));
}
public static Map<String, FlowNode> getCanReachTo(FlowNode toFlowNode) {
return getCanReachTo(toFlowNode, null);
}
public static Map<String, FlowNode> getCanReachTo(FlowNode toFlowNode, Map<String, FlowNode> canReachToNodes) {
if (canReachToNodes == null) {
canReachToNodes = new HashMap<>(16);
}
List<SequenceFlow> flows = toFlowNode.getIncomingFlows();
if (flows != null && flows.size() > 0) {
for (SequenceFlow sequenceFlow : flows) {
FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
if (sourceFlowElement instanceof FlowNode) {
canReachToNodes.put(sourceFlowElement.getId(), (FlowNode) sourceFlowElement);
if (sourceFlowElement instanceof SubProcess) {
for (Map.Entry<String, FlowElement> entry :
((SubProcess) sourceFlowElement).getFlowElementMap().entrySet()) {
if (entry.getValue() instanceof FlowNode) {
FlowNode flowNodeV = (FlowNode) entry.getValue();
canReachToNodes.put(entry.getKey(), flowNodeV);
}
}
}
getCanReachTo((FlowNode) sourceFlowElement, canReachToNodes);
}
}
}
if (toFlowNode.getSubProcess() != null) {
getCanReachTo(toFlowNode.getSubProcess(), canReachToNodes);
}
return canReachToNodes;
}
public static Map<String, FlowNode> getCanReachFrom(FlowNode fromFlowNode) {
return getCanReachFrom(fromFlowNode, null);
}
public static Map<String, FlowNode> getCanReachFrom(FlowNode fromFlowNode,
Map<String, FlowNode> canReachFromNodes) {
if (canReachFromNodes == null) {
canReachFromNodes = new HashMap<>(16);
}
List<SequenceFlow> flows = fromFlowNode.getOutgoingFlows();
if (flows != null && flows.size() > 0) {
for (SequenceFlow sequenceFlow : flows) {
FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
if (targetFlowElement instanceof FlowNode) {
canReachFromNodes.put(targetFlowElement.getId(), (FlowNode) targetFlowElement);
if (targetFlowElement instanceof SubProcess) {
for (Map.Entry<String, FlowElement> entry :
((SubProcess) targetFlowElement).getFlowElementMap().entrySet()) {
if (entry.getValue() instanceof FlowNode) {
FlowNode flowNodeV = (FlowNode) entry.getValue();
canReachFromNodes.put(entry.getKey(), flowNodeV);
}
}
}
getCanReachFrom((FlowNode) targetFlowElement, canReachFromNodes);
}
}
}
if (fromFlowNode.getSubProcess() != null) {
getCanReachFrom(fromFlowNode.getSubProcess(), canReachFromNodes);
}
return canReachFromNodes;
}
public static Map<String, Set<String>> getSpecialGatewayElements(FlowElementsContainer container) {
return getSpecialGatewayElements(container, null);
}
public static Map<String, Set<String>> getSpecialGatewayElements(FlowElementsContainer container, Map<String,
Set<String>> specialGatewayElements) {
if (specialGatewayElements == null) {
specialGatewayElements = new HashMap<>(16);
}
Collection<FlowElement> flowelements = container.getFlowElements();
for (FlowElement flowElement : flowelements) {
boolean isBeginSpecialGateway =
flowElement.getId().endsWith(SPECIAL_GATEWAY_BEGIN_SUFFIX) && (flowElement instanceof ParallelGateway || flowElement instanceof InclusiveGateway || flowElement instanceof ComplexGateway);
if (isBeginSpecialGateway) {
String gatewayBeginRealId = flowElement.getId();
String gatewayId = gatewayBeginRealId.substring(0, gatewayBeginRealId.length() - 6);
Set<String> gatewayIdContainFlowelements = specialGatewayElements.computeIfAbsent(gatewayId,
k -> new HashSet<>());
findElementsBetweenSpecialGateway(flowElement,
gatewayId + SPECIAL_GATEWAY_END_SUFFIX, gatewayIdContainFlowelements);
} else if (flowElement instanceof SubProcess) {
getSpecialGatewayElements((SubProcess) flowElement, specialGatewayElements);
}
}
// 外层到里层排序
Map<String, Set<String>> specialGatewayNodesSort = new LinkedHashMap<>();
specialGatewayElements.entrySet().stream().sorted((o1, o2) -> o2.getValue().size() - o1.getValue().size()).forEach(entry -> specialGatewayNodesSort.put(entry.getKey(), entry.getValue()));
return specialGatewayNodesSort;
}
public static void findElementsBetweenSpecialGateway(FlowElement specialGatewayBegin, String specialGatewayEndId,
Set<String> elements) {
elements.add(specialGatewayBegin.getId());
List<SequenceFlow> sequenceFlows = ((FlowNode) specialGatewayBegin).getOutgoingFlows();
if (sequenceFlows != null && sequenceFlows.size() > 0) {
for (SequenceFlow sequenceFlow : sequenceFlows) {
FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
String targetFlowElementId = targetFlowElement.getId();
elements.add(specialGatewayEndId);
if (targetFlowElementId.equals(specialGatewayEndId)) {
continue;
} else {
findElementsBetweenSpecialGateway(targetFlowElement, specialGatewayEndId, elements);
}
}
}
}
/**
* Verifies if the element with the given source identifier can reach the element with the target identifier through
* following sequence flow.
*/
public static boolean isReachable(String processDefinitionId, String sourceElementId, String targetElementId) {
// Fetch source and target elements
org.flowable.bpmn.model.Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
FlowElement sourceFlowElement = process.getFlowElement(sourceElementId, true);
FlowNode sourceElement = null;
if (sourceFlowElement instanceof FlowNode) {
sourceElement = (FlowNode) sourceFlowElement;
} else if (sourceFlowElement instanceof SequenceFlow) {
sourceElement = (FlowNode) ((SequenceFlow) sourceFlowElement).getTargetFlowElement();
}
FlowElement targetFlowElement = process.getFlowElement(targetElementId, true);
FlowNode targetElement = null;
if (targetFlowElement instanceof FlowNode) {
targetElement = (FlowNode) targetFlowElement;
} else if (targetFlowElement instanceof SequenceFlow) {
targetElement = (FlowNode) ((SequenceFlow) targetFlowElement).getTargetFlowElement();
}
if (sourceElement == null) {
throw new FlowableException("Invalid sourceElementId '" + sourceElementId + "': no element found for " +
"this" + " id n process definition '" + processDefinitionId + "'");
}
if (targetElement == null) {
throw new FlowableException("Invalid targetElementId '" + targetElementId + "': no element found for " +
"this" + " id n process definition '" + processDefinitionId + "'");
}
Set<String> visitedElements = new HashSet<>();
return isReachable(process, sourceElement, targetElement, visitedElements);
}
public static boolean isReachable(org.flowable.bpmn.model.Process process, FlowNode sourceElement, FlowNode targetElement) {
return isReachable(process, sourceElement, targetElement, new HashSet());
}
public static boolean isReachable(org.flowable.bpmn.model.Process process, FlowNode sourceElement, FlowNode targetElement,
Set<String> visitedElements) {
// Special case: start events in an event subprocess might exist as an execution and are most likely be able to
// reach the target
// when the target is in the event subprocess, but should be ignored as they are not 'real' runtime executions
// (but rather waiting for a
// trigger)
if (sourceElement instanceof StartEvent && isInEventSubprocess(sourceElement)) {
return false;
}
// No outgoing seq flow: could be the end of eg . the process or an embedded subprocess
if (sourceElement.getOutgoingFlows().size() == 0) {
visitedElements.add(sourceElement.getId());
FlowElementsContainer parentElement = process.findParent(sourceElement);
if (parentElement instanceof SubProcess) {
sourceElement = (SubProcess) parentElement;
// by zjm begin
// 子流程的结束节点,若目标节点在该子流程中,说明无法到达,返回false
if (((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
return false;
}
// by zjm end
} else {
return false;
}
}
if (sourceElement.getId().equals(targetElement.getId())) {
return true;
}
// To avoid infinite looping, we must capture every node we visit
// and check before going further in the graph if we have already
// visited the node.
visitedElements.add(sourceElement.getId());
// by zjm begin
// 当前节点能够到达子流程,且目标节点在子流程中,说明可以到达,返回true
if (sourceElement instanceof SubProcess && ((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
return true;
}
// by zjm end
List<SequenceFlow> sequenceFlows = sourceElement.getOutgoingFlows();
if (sequenceFlows != null && sequenceFlows.size() > 0) {
for (SequenceFlow sequenceFlow : sequenceFlows) {
String targetRef = sequenceFlow.getTargetRef();
FlowNode sequenceFlowTarget = (FlowNode) process.getFlowElement(targetRef, true);
if (sequenceFlowTarget != null && !visitedElements.contains(sequenceFlowTarget.getId())) {
boolean reachable = isReachable(process, sequenceFlowTarget, targetElement, visitedElements);
if (reachable) {
return true;
}
}
}
}
return false;
}
protected static boolean isInEventSubprocess(FlowNode flowNode) {
FlowElementsContainer flowElementsContainer = flowNode.getParentContainer();
while (flowElementsContainer != null) {
if (flowElementsContainer instanceof EventSubProcess) {
return true;
}
if (flowElementsContainer instanceof FlowElement) {
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
} else {
flowElementsContainer = null;
}
}
return false;
}
public static List<String> getParentProcessIds(FlowNode flowNode) {
List<String> result = new ArrayList<>();
FlowElementsContainer flowElementsContainer = flowNode.getParentContainer();
while (flowElementsContainer != null) {
if (flowElementsContainer instanceof SubProcess) {
SubProcess flowElement = (SubProcess) flowElementsContainer;
result.add(flowElement.getId());
flowElementsContainer = flowElement.getParentContainer();
} else if (flowElementsContainer instanceof org.flowable.bpmn.model.Process) {
org.flowable.bpmn.model.Process flowElement = (org.flowable.bpmn.model.Process) flowElementsContainer;
result.add(flowElement.getId());
flowElementsContainer = null;
}
}
// 第一层Process为第0个
Collections.reverse(result);
return result;
}
/**
* 查询不同层级
*
* @param sourceList
* @param targetList
* @return 返回不同的层级,如果其中一个层级较深,则返回层级小的+1,从第0层开始,请注意判断是否会出现下标越界异常;返回 -1 表示在同一层
*/
public static Integer getDiffLevel(List<String> sourceList, List<String> targetList) {
if (sourceList == null || sourceList.isEmpty() || targetList == null || targetList.isEmpty()) {
throw new FlowableException("sourceList and targetList cannot be empty");
}
if (sourceList.size() == 1 && targetList.size() == 1) {
// 都在第0层且不相等
if (!sourceList.get(0).equals(targetList.get(0))) {
return 0;
} else {// 都在第0层且相等
return -1;
}
}
int minSize = sourceList.size() < targetList.size() ? sourceList.size() : targetList.size();
Integer targetLevel = null;
for (int i = 0; i < minSize; i++) {
if (!sourceList.get(i).equals(targetList.get(i))) {
targetLevel = i;
break;
}
}
if (targetLevel == null) {
if (sourceList.size() == targetList.size()) {
targetLevel = -1;
} else {
targetLevel = minSize;
}
}
return targetLevel;
}
public static Set<String> getParentExecutionIdsByActivityId(List<ExecutionEntity> executions, String activityId) {
List<ExecutionEntity> activityIdExecutions =
executions.stream().filter(e -> activityId.equals(e.getActivityId())).collect(Collectors.toList());
if (activityIdExecutions.isEmpty()) {
throw new FlowableException("Active execution could not be found with activity id " + activityId);
}
// check for a multi instance root execution
ExecutionEntity miExecution = null;
boolean isInsideMultiInstance = false;
for (ExecutionEntity possibleMiExecution : activityIdExecutions) {
if (possibleMiExecution.isMultiInstanceRoot()) {
miExecution = possibleMiExecution;
isInsideMultiInstance = true;
break;
}
if (isExecutionInsideMultiInstance(possibleMiExecution)) {
isInsideMultiInstance = true;
}
}
Set<String> parentExecutionIds = new HashSet<>();
if (isInsideMultiInstance) {
Stream<ExecutionEntity> executionEntitiesStream = activityIdExecutions.stream();
if (miExecution != null) {
executionEntitiesStream = executionEntitiesStream.filter(ExecutionEntity::isMultiInstanceRoot);
}
executionEntitiesStream.forEach(childExecution -> {
parentExecutionIds.add(childExecution.getParentId());
});
} else {
ExecutionEntity execution = activityIdExecutions.iterator().next();
parentExecutionIds.add(execution.getParentId());
}
return parentExecutionIds;
}
public static boolean isExecutionInsideMultiInstance(ExecutionEntity execution) {
return getFlowElementMultiInstanceParentId(execution.getCurrentFlowElement()).isPresent();
}
public static Optional<String> getFlowElementMultiInstanceParentId(FlowElement flowElement) {
FlowElementsContainer parentContainer = flowElement.getParentContainer();
while (parentContainer instanceof Activity) {
if (isFlowElementMultiInstance((Activity) parentContainer)) {
return Optional.of(((Activity) parentContainer).getId());
}
parentContainer = ((Activity) parentContainer).getParentContainer();
}
return Optional.empty();
}
public static boolean isFlowElementMultiInstance(FlowElement flowElement) {
if (flowElement instanceof Activity) {
return ((Activity) flowElement).getLoopCharacteristics() != null;
}
return false;
}
public static String getParentExecutionIdFromParentIds(ExecutionEntity execution, Set<String> parentExecutionIds) {
ExecutionEntity taskParentExecution = execution.getParent();
String realParentExecutionId = null;
while (taskParentExecution != null) {
if (parentExecutionIds.contains(taskParentExecution.getId())) {
realParentExecutionId = taskParentExecution.getId();
break;
}
taskParentExecution = taskParentExecution.getParent();
}
if (realParentExecutionId == null || realParentExecutionId.length() == 0) {
throw new FlowableException("Parent execution could not be found with executionId id " + execution.getId());
}
return realParentExecutionId;
}
public static String[] getSourceAndTargetRealActivityId(FlowNode sourceFlowElement, FlowNode targetFlowElement) {
// 实际应操作的当前节点ID
String sourceRealActivityId = sourceFlowElement.getId();
// 实际应操作的目标节点ID
String targetRealActivityId = targetFlowElement.getId();
List<String> sourceParentProcesss = FlowableUtils.getParentProcessIds(sourceFlowElement);
List<String> targetParentProcesss = FlowableUtils.getParentProcessIds(targetFlowElement);
int diffParentLevel = getDiffLevel(sourceParentProcesss, targetParentProcesss);
if (diffParentLevel != -1) {
sourceRealActivityId = sourceParentProcesss.size() == diffParentLevel ? sourceRealActivityId :
sourceParentProcesss.get(diffParentLevel);
targetRealActivityId = targetParentProcesss.size() == diffParentLevel ? targetRealActivityId :
targetParentProcesss.get(diffParentLevel);
}
return new String[]{sourceRealActivityId, targetRealActivityId};
}
public static String getAttributeValue(BaseElement element, String namespace, String name) {
return element.getAttributeValue(namespace, name);
}
public static String getFlowableAttributeValue(BaseElement element, String name) {
return element.getAttributeValue(FLOWABLE_NAMESPACE, name);
}
public static List<ExtensionElement> getExtensionElements(BaseElement element, String name) {
return element.getExtensionElements().get(name);
}
public static FlowElement getFlowElement(RepositoryService repositoryService, String processDefinitionId,
String flowElementId, boolean searchRecursive) {
Process process = repositoryService.getBpmnModel(processDefinitionId).getMainProcess();
FlowElement flowElement = process.getFlowElement(flowElementId, searchRecursive);
return flowElement;
}
public static FlowElement getFlowElement(RepositoryService repositoryService, String processDefinitionId,
String flowElementId) {
return getFlowElement(repositoryService, processDefinitionId, flowElementId, true);
}
}