场景:本章主要描述 需要经过多重网关 如何进行回退操作。
上一章:flowable 上一节点任务撤回-(3)会签任务(正在执行中)
下一章:[flowable 上一节点任务撤回-(5)嵌入式子流程](https://www.jianshu.com/p/9e7e49c2a8aa)环境:
springboot:2.2.0.RELEASE
flowable:6.4.2git地址:https://github.com/oldguys/flowable-modeler-demo/tree/branch_with_flowable_examples
所有章节:
- flowable 上一节点任务撤回-(1)普通节点撤回
- flowable 上一节点任务撤回-(2)会签任务(已完成)
- flowable 上一节点任务撤回-(3)会签任务(正在执行中)
- flowable 上一节点任务撤回-(4)多重网关
- flowable 上一节点任务撤回-(5)嵌入式子流程
- flowable 上一节点任务撤回-(6)调用式子流程
如上图进行,经过了并行网关,任务分成了b-1 ,b-2,再过包容网关,任务可能又变成了 c-1 ,c-2, c-3,b-1,此时数据会变成怎样?
Test-1: 从 a -> b
ProcessInstance pi = runtimeService.startProcessInstanceByKey(key02);
System.out.println("pi:" + pi.getProcessInstanceId());
Map variables = new HashMap<>();
variables.put("a", 2);
taskService.createTaskQuery()
.processInstanceId(pi.getProcessInstanceId())
.list().forEach(obj -> taskService.complete(obj.getId(), variables));
效果: 并行网关 没有 条件过滤的能力
官方文档: 并行网关:https://flowable.com/open-source/docs/bpmn/ch07b-BPMN-Constructs/#parallel-gateway
其中 Execution 效果:
Test-2: 从 b-2 -> c
包容网关具有线条流判断的能力
Test-3: b-1 ,c-1 完成。 c -3 未完成
Test-4: 从任务走到 d 的时候
结论:
- 不同于 普通节点 和 会签, 出现并行 分支的时候, 也会出现 多个 execution,但是父级节点 直接就是流程。
- 在线条汇总的时候,网关会出现一个 execution , Active = false。完成任务之后 execution 又会消失掉,所以进行网关回退的时候,需要模拟 汇总网关 的 完成任务状态,不然会导致撤回后的任务 无法往前走。
- 同理,跟之前会签一样,由于有造模拟完成的线条流,所以当其他线条中的其他任务进行再次回退的时候,需要移除已经创建的线条流。
com.example.oldguy.modules.examples.cmd.rollback.impl.DefaultTaskNextGatewayRollbackOperateStrategy:普通任务节点在多重网关间的策略
基于模板方法模式,构建编写通用 RollbackOperateStrategy.process() 操作
@Override
public void process(CommandContext commandContext, String assignee, Map variables) {
this.commandContext = commandContext;
this.assignee = assignee;
this.variables = variables;
log.info("处理 existNextFinishedTask");
existNextFinishedTask();
log.info("配置任务执行人 setAssignee");
setAssignee();
log.info("处理 createExecution");
createExecution();
log.info("处理 deleteRuntimeTasks");
deleteRuntimeTasks();
log.info("处理 deleteHisActInstance");
deleteHisActInstance();
}
createExecution()
@Override
public void createExecution() {
HistoricTaskInstance hisTask = paramsTemplate.getHisTask();
// 获取正在执行 execution
ExecutionEntity executionEntity = getExecutionEntity();
ExecutionEntity newExecution = CommandContextUtil.getExecutionEntityManager(commandContext)
.createChildExecution(executionEntity.getParent());
// 创建新任务
createExecution(newExecution);
// 特殊处理并行网关
processGateway(executionEntity.getParent());
// 移除历史任务
removeHisTask(hisTask);
}
processGateway(ExecutionEntity parent)
/**
* 使用并行网关进行 线条流汇总时候,会出现 特殊bug
*
* @param parent
*/
protected void processGateway(ExecutionEntity parent) {
// 当前正在运行所以任务
List taskEntityList = CommandContextUtil.getTaskService(commandContext)
.findTasksByProcessInstanceId(parent.getProcessInstanceId());
boolean isExistPassGatewayTask = false;
// 下一节点任务
List nextTaskList = taskEntityList.stream()
.filter(obj -> paramsTemplate.getNextFlowIdList().contains(obj.getTaskDefinitionKey()))
.collect(Collectors.toList());
if (!nextTaskList.isEmpty()) {
log.info("已经生成过网关任务");
isExistPassGatewayTask = true;
// 网关的连线
Map sqFlowMap = new HashMap<>();
paramsTemplate.getGatewayMap().values().forEach(obj -> {
obj.getIncomingFlows().forEach(item -> {
if (null != paramsTemplate.getGatewayMap().get(item.getSourceRef())) {
log.info("跳过gateway 间连线:" + item);
return;
}
if (paramsTemplate.getHisTask().getTaskDefinitionKey().equals(item.getSourceRef())) {
log.info("跳过当前回退历史任务:" + item);
return;
}
sqFlowMap.put(item.getSourceRef(), item);
});
});
// 创建网关相关连线 execution
createCompleteGatewayExecution(parent, sqFlowMap);
// 删除当前正在执行任务
Set nestTaskIdSet = new HashSet<>();
nextTaskList.forEach(obj -> {
removeRuntimeTaskOperate(obj);
nestTaskIdSet.add(obj.getId());
});
// 移除正在执行下一节点历史任务
List historicTaskInstanceList = CommandContextUtil.getHistoricTaskService(commandContext)
.findHistoricTasksByProcessInstanceId(parent.getProcessInstanceId());
historicTaskInstanceList.forEach(obj -> {
if (nestTaskIdSet.contains(obj.getId())) {
CommandContextUtil.getHistoricTaskService(commandContext).deleteHistoricTask(obj);
}
});
}
if (isExistPassGatewayTask) {
} else {
log.info("移除网关连线");
List executionEntityList = CommandContextUtil.getExecutionEntityManager(commandContext)
.findExecutionsByParentExecutionAndActivityIds(parent.getProcessInstanceId(), paramsTemplate.getGatewayMap().keySet());
Map targetGatewayMap = paramsTemplate.getCurrentTaskElement()
.getOutgoingFlows().stream().filter(obj -> obj.getTargetFlowElement() instanceof Gateway)
.map(obj -> obj.getTargetFlowElement())
.collect(Collectors.toMap(FlowElement::getId, obj -> obj));
List toRemoveList = new ArrayList<>();
executionEntityList.forEach(obj -> {
if (null != targetGatewayMap.get(obj.getActivityId())) {
toRemoveList.add(obj);
targetGatewayMap.put(obj.getActivityId(), null);
}
});
if (!toRemoveList.isEmpty()) {
toRemoveList.forEach(obj -> {
log.info("移除连线:" + obj);
CommandContextUtil.getExecutionEntityManager(commandContext).delete(obj);
});
}
}
}
/**
* 创建 Gateway 相关连线
*
* @param parent
* @param sqFlowMap
*/
protected void createCompleteGatewayExecution(ExecutionEntity parent, Map sqFlowMap) {
sqFlowMap.values().forEach(obj -> {
ExecutionEntity newExecution = CommandContextUtil.getExecutionEntityManager(commandContext)
.createChildExecution(parent);
newExecution.setCurrentFlowElement(obj.getTargetFlowElement());
newExecution.setActive(false);
log.debug("创建 gateway 连线 execution");
CommandContextUtil.getAgenda(commandContext).planContinueProcessInCompensation(newExecution);
});
}
PS: 注意连线间的任务操作,如多重网关的时候,网关连线应该跳过,已办任务也需要跳过。
其他重载 方法
@Override
public void existNextFinishedTask() {
HistoricTaskInstance hisTask = paramsTemplate.getHisTask();
List historicTaskInstanceList = CommandContextUtil.getHistoricTaskService(commandContext)
.findHistoricTaskInstancesByQueryCriteria(
(HistoricTaskInstanceQueryImpl) new HistoricTaskInstanceQueryImpl()
.finished()
.processInstanceId(hisTask.getProcessInstanceId())
.taskCompletedAfter(hisTask.getEndTime())
);
if (!historicTaskInstanceList
.stream()
.filter(obj -> paramsTemplate.getNextFlowIdList().contains(obj.getTaskDefinitionKey()))
.collect(Collectors.toList())
.isEmpty()) {
String msg = "存在已完成下一节点任务";
throw new FlowableRuntimeException(msg);
}
}
@Override
public void deleteRuntimeTasks() {
HistoricTaskInstance hisTask = paramsTemplate.getHisTask();
// 删除正在执行任务
List taskList = CommandContextUtil.getTaskService(commandContext)
.createTaskQuery()
.processInstanceId(hisTask.getProcessInstanceId())
.taskCreatedAfter(hisTask.getEndTime())
.list();
taskList.stream()
.filter(obj -> paramsTemplate.getNextFlowIdList().contains(obj.getTaskDefinitionKey()))
.forEach(obj -> {
log.info("删除运行时任务:" + obj);
removeRuntimeTaskOperate((TaskEntity) obj);
});
// 删除历史任务
List historicTaskInstances = CommandContextUtil.getHistoricTaskService(commandContext)
.findHistoricTasksByProcessInstanceId(hisTask.getProcessInstanceId());
historicTaskInstances.forEach(obj -> {
if (obj.getCreateTime().getTime() <= hisTask.getEndTime().getTime() && paramsTemplate.getNextFlowIdList().contains(obj.getTaskDefinitionKey())) {
log.info("删除历史任务:" + obj);
CommandContextUtil.getHistoricTaskService(commandContext).deleteHistoricTask(obj);
}
});
}
以上 完成对 上一节点任务撤回 架构设计 及 第 4 种情况 解决方案讲述。
git地址:https://github.com/oldguys/flowable-modeler-demo/tree/branch_with_flowable_examples