最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

工作流flowable任務(wù)退回(任務(wù)跳轉(zhuǎn))、任務(wù)重新觸發(fā)、任務(wù)刪除的實現(xiàn)

2023-02-25 18:56 作者:Java編程Java  | 我要投稿

在進行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);

? ? }


}


工作流flowable任務(wù)退回(任務(wù)跳轉(zhuǎn))、任務(wù)重新觸發(fā)、任務(wù)刪除的實現(xiàn)的評論 (共 條)

分享到微博請遵守國家法律
石河子市| 博兴县| 双峰县| 聂拉木县| 长治市| 武威市| 达拉特旗| 宁波市| 灌南县| 英德市| 南平市| 黄浦区| 巨野县| 武邑县| 韩城市| 海丰县| 股票| 长顺县| 余江县| 沧源| 高青县| 呼图壁县| 怀安县| 老河口市| 曲水县| 江孜县| 南陵县| 平遥县| 夏津县| 留坝县| 崇明县| 潜江市| 陵水| 锦州市| 蓬莱市| 梁河县| 商都县| 张家港市| 长寿区| 轮台县| 江山市|