工作流flowable任務(wù)退回(任務(wù)跳轉(zhuǎn))、任務(wù)重新觸發(fā)、任務(wù)刪除的實現(xiàn)
在進行flowable工作流的運用中,會涉及到任務(wù)的的一些特殊操作,如:退回,跳轉(zhuǎn),刪除,重新觸發(fā)等。退回和重新觸發(fā) 主要借助changeActivityState,刪除主要用ExecutionEntityManager.deleteExecutionAndRelatedData()方法。
主要實現(xiàn)方法如下。
退回(任務(wù)跳轉(zhuǎn)到指定節(jié)點)
退回功能,主要是找到當前節(jié)點(可能多個)的ExecutionId和目標節(jié)點的ActivityId,然后通過 moveExecutionsToSingleActivityId().changeState()實現(xiàn)。
這個功能也適用于任務(wù)的前進,比如: 流程是 A ->B->C->D->E , 當前在C節(jié)點, 可以通過這個功能將流程 退回到 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;
/**
?* 任務(wù)退回(跳到指定節(jié)點)
?* @Author iccboy
?*/
public class BackTaskCmd implements Command<String>, Serializable {
? ? public static final long serialVersionUID = 1L;
? ? private final transient RuntimeService runtimeService;
? ? /**
? ? ?* 當前運行的任務(wù),流程節(jié)點id
? ? ?*/
? ? protected String taskIdOrActivityIdOrExecutionId;
? ? /**
? ? ?* 需要跳轉(zhuǎn)的任務(wù)節(jié)點定義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);
? ? ? ? // 只支持從用戶任務(wù)退回
? ? ? ? if (!(sourceFlowElement instanceof UserTask)) {
? ? ? ? ? ? //throw new FlowableException("Task with id:" + taskId + " is not a UserTask");
? ? ? ? }
? ? ? ? FlowNode targetFlowElement = (FlowNode) process.getFlowElement(targetActivityId, true);
? ? ? ? // 退回節(jié)點到當前節(jié)點不可達到,不允許退回
? ? ? ? if (!FlowableUtils.isReachable(process, targetFlowElement, sourceFlowElement)) {
? ? ? ? ? ? throw new FlowableException("Cannot back to [" + targetActivityId + "]");
? ? ? ? }
? ? ? ? // ps:目標節(jié)點如果相對當前節(jié)點是在子流程內(nèi)部,則無法直接退回,目前處理是只能退回到子流程開始節(jié)點
? ? ? ? String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement,
? ? ? ? ? ? ? ? targetFlowElement);
? ? ? ? // 實際應(yīng)操作的當前節(jié)點ID
? ? ? ? String sourceRealActivityId = sourceAndTargetRealActivityId[0];
? ? ? ? // 實際應(yīng)操作的目標節(jié)點ID
? ? ? ? String targetRealActivityId = sourceAndTargetRealActivityId[1];
? ? ? ? Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);
? ? ? ? // 當前節(jié)點處在的并行網(wǎng)關(guān)list
? ? ? ? List<String> sourceInSpecialGatewayList = new ArrayList<>();
? ? ? ? // 目標節(jié)點處在的并行網(wǎng)關(guān)list
? ? ? ? List<String> targetInSpecialGatewayList = new ArrayList<>();
? ? ? ? setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes,
? ? ? ? ? ? ? ? sourceInSpecialGatewayList, targetInSpecialGatewayList);
? ? ? ? // 實際應(yīng)篩選的節(jié)點ID
? ? ? ? Set<String> sourceRealAcitivtyIds = null;
? ? ? ? // 若退回目標節(jié)點相對當前節(jié)點在并行網(wǎng)關(guān)中,則要找到相對當前節(jié)點最近的這個并行網(wǎng)關(guān),后續(xù)做特殊處理
? ? ? ? String targetRealSpecialGateway = null;
? ? ? ? // 1.目標節(jié)點和當前節(jié)點都不在并行網(wǎng)關(guān)中
? ? ? ? if (targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
? ? ? ? ? ? sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
? ? ? ? }
? ? ? ? // 2.目標節(jié)點不在并行網(wǎng)關(guān)中、當前節(jié)點在并行網(wǎng)關(guān)中
? ? ? ? else if (targetInSpecialGatewayList.isEmpty()) {
? ? ? ? ? ? sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(0));
? ? ? ? }
? ? ? ? // 3.目標節(jié)點在并行網(wǎng)關(guān)中、當前節(jié)點不在并行網(wǎng)關(guān)中
? ? ? ? else if (sourceInSpecialGatewayList.isEmpty()) {
? ? ? ? ? ? sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
? ? ? ? ? ? targetRealSpecialGateway = targetInSpecialGatewayList.get(0);
? ? ? ? }
? ? ? ? // 4.目標節(jié)點和當前節(jié)點都在并行網(wǎng)關(guān)中
? ? ? ? else {
? ? ? ? ? ? int diffSpecialGatewayLevel = FlowableUtils.getDiffLevel(sourceInSpecialGatewayList,
? ? ? ? ? ? ? ? ? ? targetInSpecialGatewayList);
? ? ? ? ? ? // 在并行網(wǎng)關(guān)同一層且在同一分支
? ? ? ? ? ? if (diffSpecialGatewayLevel == -1) {
? ? ? ? ? ? ? ? sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? // 當前節(jié)點最內(nèi)層并行網(wǎng)關(guān)不被目標節(jié)點最內(nèi)層并行網(wǎng)關(guān)包含
? ? ? ? ? ? ? ? // 或理解為當前節(jié)點相對目標節(jié)點在并行網(wǎng)關(guān)外
? ? ? ? ? ? ? ? // 只篩選當前節(jié)點的execution
? ? ? ? ? ? ? ? if (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
? ? ? ? ? ? ? ? ? ? sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 當前節(jié)點相對目標節(jié)點在并行網(wǎng)關(guān)內(nèi),應(yīng)篩選相對目標節(jié)點最近的并行網(wǎng)關(guān)的所有節(jié)點的execution
? ? ? ? ? ? ? ? else {
? ? ? ? ? ? ? ? ? ? sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 目標節(jié)點最內(nèi)層并行網(wǎng)關(guān)包含當前節(jié)點最內(nèi)層并行網(wǎng)關(guān)
? ? ? ? ? ? ? ? // 或理解為目標節(jié)點相對當前節(jié)點在并行網(wǎng)關(guān)外
? ? ? ? ? ? ? ? // 不做處理
? ? ? ? ? ? ? ? if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 目標節(jié)點相對當前節(jié)點在并行網(wǎng)關(guān)內(nèi)
? ? ? ? ? ? ? ? else {
? ? ? ? ? ? ? ? ? ? targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 篩選需要處理的execution
? ? ? ? List<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId,
? ? ? ? ? ? ? ? executionId, sourceRealActivityId, sourceRealAcitivtyIds);
? ? ? ? // 執(zhí)行退回,直接跳轉(zhuǎn)到實際的 targetRealActivityId
? ? ? ? List<String> realExecutionIds =
? ? ? ? ? ? ? ? realExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toList());
? ? ? ? runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
? ? ? ? ? ? ? ? .moveExecutionsToSingleActivityId(realExecutionIds, targetRealActivityId).changeState();
? ? ? ? // 目標節(jié)點相對當前節(jié)點處于并行網(wǎng)關(guān)內(nèi),需要特殊處理,需要手動生成并行網(wǎng)關(guān)匯聚節(jié)點(_end)的execution數(shù)據(jù)
? ? ? ? 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) {
? ? ? ? // 目標節(jié)點相對當前節(jié)點處于并行網(wǎng)關(guān),需要手動生成并行網(wǎng)關(guān)匯聚節(jié)點(_end)的execution數(shù)據(jù)
? ? ? ? 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();
? ? ? ? ? ? // 處理目標節(jié)點所處的分支以外的分支,即 總分枝數(shù)-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
上面方法的調(diào)用如下:
@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
任務(wù)重新觸發(fā)
任務(wù)重新觸發(fā),主要用于當前節(jié)點是 ServiceTask的情況,比如:當ServiceTask是異步任務(wù)時,然后沒有進行邊界異常事件的處理,如果當任務(wù)執(zhí)行異常時,會阻礙流程的繼續(xù),此時就需要重新觸發(fā)任務(wù)的執(zhí)行,讓流程重新推進下去。主要實現(xiàn)思路與上面的任務(wù)跳轉(zhuǎn)類似。就是當前節(jié)點跳當前節(jié)點。使用的方法還是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;
/**
?* 重新加載任務(wù)
* @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);
? ? ? ? // 退回節(jié)點到當前節(jié)點不可達到,不允許退回
? ? ? ? if (!FlowableUtils.isReachable(process, targetFlowElement, sourceFlowElement)) {
? ? ? ? ? ? throw new FlowableException("Cannot back to [" + targetActivityId + "]");
? ? ? ? }
? ? ? ? // ps:目標節(jié)點如果相對當前節(jié)點是在子流程內(nèi)部,則無法直接退回,目前處理是只能退回到子流程開始節(jié)點
? ? ? ? String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement,
? ? ? ? ? ? ? ? targetFlowElement);
? ? ? ? // 實際應(yīng)操作的當前節(jié)點ID
? ? ? ? String sourceRealActivityId = sourceAndTargetRealActivityId[0];
? ? ? ? // 實際應(yīng)操作的目標節(jié)點ID
? ? ? ? String targetRealActivityId = sourceAndTargetRealActivityId[1];
? ? ? ? Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);
? ? ? ? // 當前節(jié)點處在的并行網(wǎng)關(guān)list
? ? ? ? List<String> sourceInSpecialGatewayList = new ArrayList<>();
? ? ? ? // 目標節(jié)點處在的并行網(wǎng)關(guān)list
? ? ? ? List<String> targetInSpecialGatewayList = new ArrayList<>();
? ? ? ? setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes,
? ? ? ? ? ? ? ? sourceInSpecialGatewayList, targetInSpecialGatewayList);
? ? ? ? // 實際應(yīng)篩選的節(jié)點ID
? ? ? ? Set<String> sourceRealAcitivtyIds = null;
? ? ? ? // 若退回目標節(jié)點相對當前節(jié)點在并行網(wǎng)關(guān)中,則要找到相對當前節(jié)點最近的這個并行網(wǎng)關(guān),后續(xù)做特殊處理
? ? ? ? String targetRealSpecialGateway = null;
? ? ? ? // 1.目標節(jié)點和當前節(jié)點都不在并行網(wǎng)關(guān)中
? ? ? ? if (targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
? ? ? ? ? ? sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
? ? ? ? }
? ? ? ? // 2.目標節(jié)點不在并行網(wǎng)關(guān)中、當前節(jié)點在并行網(wǎng)關(guān)中
? ? ? ? else if (targetInSpecialGatewayList.isEmpty() && !sourceInSpecialGatewayList.isEmpty()) {
? ? ? ? ? ? sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(0));
? ? ? ? }
? ? ? ? // 3.目標節(jié)點在并行網(wǎng)關(guān)中、當前節(jié)點不在并行網(wǎng)關(guān)中
? ? ? ? else if (!targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
? ? ? ? ? ? sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
? ? ? ? ? ? targetRealSpecialGateway = targetInSpecialGatewayList.get(0);
? ? ? ? }
? ? ? ? // 4.目標節(jié)點和當前節(jié)點都在并行網(wǎng)關(guān)中
? ? ? ? else {
? ? ? ? ? ? int diffSpecialGatewayLevel = FlowableUtils.getDiffLevel(sourceInSpecialGatewayList,
? ? ? ? ? ? ? ? ? ? targetInSpecialGatewayList);
? ? ? ? ? ? // 在并行網(wǎng)關(guān)同一層且在同一分支
? ? ? ? ? ? if (diffSpecialGatewayLevel == -1) {
? ? ? ? ? ? ? ? sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? // 當前節(jié)點最內(nèi)層并行網(wǎng)關(guān)不被目標節(jié)點最內(nèi)層并行網(wǎng)關(guān)包含
? ? ? ? ? ? ? ? // 或理解為當前節(jié)點相對目標節(jié)點在并行網(wǎng)關(guān)外
? ? ? ? ? ? ? ? // 只篩選當前節(jié)點的execution
? ? ? ? ? ? ? ? if (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
? ? ? ? ? ? ? ? ? ? sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 當前節(jié)點相對目標節(jié)點在并行網(wǎng)關(guān)內(nèi),應(yīng)篩選相對目標節(jié)點最近的并行網(wǎng)關(guān)的所有節(jié)點的execution
? ? ? ? ? ? ? ? else {
? ? ? ? ? ? ? ? ? ? sourceRealAcitivtyIds =
? ? ? ? ? ? ? ? ? ? ? ? ? ? specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 目標節(jié)點最內(nèi)層并行網(wǎng)關(guān)包含當前節(jié)點最內(nèi)層并行網(wǎng)關(guān)
? ? ? ? ? ? ? ? // 或理解為目標節(jié)點相對當前節(jié)點在并行網(wǎng)關(guān)外
? ? ? ? ? ? ? ? // 不做處理
? ? ? ? ? ? ? ? if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? // 目標節(jié)點相對當前節(jié)點在并行網(wǎng)關(guān)內(nèi)
? ? ? ? ? ? ? ? ? ? targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 篩選需要處理的execution
? ? ? ? List<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId,
? ? ? ? ? ? ? ? executionId, sourceRealActivityId, sourceRealAcitivtyIds);
? ? ? ? // 執(zhí)行退回,直接跳轉(zhuǎn)到實際的 targetRealActivityId
? ? ? ? List<String> realExecutionIds =
? ? ? ? ? ? ? ? realExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toList());
? ? ? ? runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
? ? ? ? ? ? ? ? .moveExecutionsToSingleActivityId(realExecutionIds, targetRealActivityId).changeState();
? ? ? ? // 目標節(jié)點相對當前節(jié)點處于并行網(wǎng)關(guān)內(nèi),需要特殊處理,需要手動生成并行網(wǎng)關(guān)匯聚節(jié)點(_end)的execution數(shù)據(jù)
? ? ? ? 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) {
? ? ? ? // 目標節(jié)點相對當前節(jié)點處于并行網(wǎng)關(guān),需要手動生成并行網(wǎng)關(guān)匯聚節(jié)點(_end)的execution數(shù)據(jù)
? ? ? ? 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();
? ? ? ? ? ? // 處理目標節(jié)點所處的分支以外的分支,即 總分枝數(shù)-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調(diào)用
? ? 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
上面的方法大多和任務(wù)跳轉(zhuǎn)代碼相似,還可以繼續(xù)簡化。
上面的Cmd中,有個businessKey, 這里主要是通過業(yè)務(wù)key來定位的ProcessInstance, 也可以直接傳參 ProcessInstanceId 進來,這樣更簡單。
任務(wù)(節(jié)點)刪除
直接調(diào)用 taskService.deleteTask()方式時會報錯:The task cannot be deleted because is part of a running process
通過ExecutionEntityManager.deleteExecutionAndRelatedData實現(xiàn)刪除則沒可以。
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;
/**
?* 刪除任務(wù)
* @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
調(diào)用示例:
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 相關(guān)的工具方法
?*
?*/
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 相關(guān)的工具方法 ==========
? ? 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 相關(guān)的工具方法 ==========
? ? /**
? ? ?* 獲得 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 方法,所以只能轉(zhuǎn)成字節(jié)數(shù)組,進行比較
? ? ? ? return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
? ? }
? ? /**
? ? ?* 把 bpmnModel 轉(zhuǎn)換成 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 相關(guān)的工具方法 ==========
? ? 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
? ? ? ? ? ? ? ? // 子流程的結(jié)束節(jié)點,若目標節(jié)點在該子流程中,說明無法到達,返回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
? ? ? ? // 當前節(jié)點能夠到達子流程,且目標節(jié)點在子流程中,說明可以到達,返回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層開始,請注意判斷是否會出現(xiàn)下標越界異常;返回 -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) {
? ? ? ? // 實際應(yīng)操作的當前節(jié)點ID
? ? ? ? String sourceRealActivityId = sourceFlowElement.getId();
? ? ? ? // 實際應(yīng)操作的目標節(jié)點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);
? ? }
}