场景:本章主要描述 已完成会签任务 如何进行回退操作。
上一章:flowable 上一节点任务撤回-(1)普通节点撤回
下一章:[flowable 上一节点任务撤回-(3)会签任务(正在执行中)](https://www.jianshu.com/p/6daf767b1084)ree/branch_with_flowable_examples]环境:
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)调用式子流程
前置知识:
- 会签分为 并行 串行 两种。
并行会签:会同时创建所有的任务,依赖于变量进行操作。如图所示
串行会签:按照变量顺序逐次创建任务,依赖于变量进行操作。如图所示
根据上面的图可以分析出 关系:
execution 层次结构:
execution[流程实例] < Execution[会签顶级实例] < Execution[实际会签任务]
并行
1:1:3(设置变量数决定)
串行
1:1:1
其中可以看到 flowable 本身的变量
父级流程 execution变量:
nrOfActiveInstances:未完成任务个数
nrOfCompletedInstances:已完成任务个数
nrOfInstances:总任务个数任务变量 execution
loopCounter:任务计数器
上面是执行前的情况,当所有会签任务完成之后
结论1: 从上面图片分析出,当会签任务走完,原本的变量会被清理掉。所以要 从 普通节点 退回到 会签节点,就必须模拟出这些变量。
结论2: 由于Execution也消失了,所以需要模拟出 2个层级的 execution。并且历史execution变量将不可用
CompletedMultiInstanceTaskAndNextDefaultTaskRollbackOperateStrategy: 回退已完成会签任务
前置知识参考 第一章:
d-1 回退到 并行 c-1
步骤:
判断 d-1 是否已经完成,如果完成,则无法进行回退。如果未完成,则到下一步骤
构建 并行 c-1 的 父级 Execution,并构建并构建 任务 execution(简称:c-1任务),此时需要根据并行 , 串行 对变量进行修改。(PS:串行的时候 还需要判断 回退历史任务的执行人是否为 启动会签任务时候入参 集合 的最后一个参数,如果不是的话,则不应该回退。)
配置 刚刚构建的 并行 c-1 的 父级 Execution的变量:
nrOfActiveInstances = 1
nrOfCompletedInstances = 会签总人数-1
nrOfInstances = 会签总人数
execution.multiInstanceRoot = true配置任务变量
loopCounter = 会签总人数最后一个集合下标( 即:List.size()-1 )
execution.active = true如果是并行会签,则需要补全 其他已完成任务的 execution,即创建 N-1 个 与 c-1 任务 同级的 exection ,但是 active = false,不生成任务。(不生成完成会签任务execution会导致计数器无法执行计满,会导致无法到下一流程节点)。 串行会签由于使用的是同一个 execution,所以不需要补充。
构建任务的时候使用语句与构建普通任务的语句不一样:(使用同一个会出现直接构建整个节点)
CommandContextUtil.getAgenda(commandContext) .planContinueMultiInstanceOperation(newExecution, parentExecution, assignees.size() - 1);
- 之后的步骤与之前的操作一样,删除当前执行任务,清除回退的历史任务,清除痕迹及关联,此处就不过多赘述。 可以参考第一章:
基于模板方法模式,构建编写通用 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();
}
创建 父级 会签 execution
@Override
public void createExecution() {
if (paramsTemplate.getCurrentTaskElement().getBehavior() instanceof SequentialMultiInstanceBehavior) {
isSequence = true;
}
// 获取 execution
ExecutionEntity executionEntity = getExecutionEntity();
VariableInstanceEntity obj = CommandContextUtil.getVariableService(commandContext)
.findVariableInstanceByExecutionAndName(executionEntity.getProcessInstanceId(), assigneeListExpr);
if (obj == null || !(obj.getValue() instanceof Collection)) {
throw new FlowableRuntimeException("没有可用会签参数:" + assigneeListExpr);
}
// 会签执行人变量
Collection assignees = (Collection) obj.getValue();
List historicVariableInstances = CommandContextUtil.getHistoricVariableService()
.findHistoricVariableInstancesByQueryCriteria(
new HistoricVariableInstanceQueryImpl()
.executionId(paramsTemplate.getHisTask().getExecutionId())
);
if (historicVariableInstances.isEmpty()) {
throw new FlowableRuntimeException("没有可用会签任务参数");
}
// 历史变量
Map hisVarMap = historicVariableInstances.stream().collect(Collectors.toMap(HistoricVariableInstance::getVariableName, item -> item.getValue()));
if (hisVarMap.containsKey(assigneeExpr) && hisVarMap.containsKey(RollbackConstants.MultiInstanceConstants.LOOP_COUNTER)) {
log.info("变量有效");
} else {
throw new FlowableRuntimeException("缺少会签任务变量");
}
/**
* 串行 最终的 loopCounter assignee 都会是最后一个人
*/
if (isSequence) {
List assigneeList = (List) runtimeService.getVariableLocal(paramsTemplate.getHisTask().getProcessInstanceId(), assigneeListExpr);
if (!assigneeList.get(assigneeList.size() - 1).equals(paramsTemplate.getHisTask().getAssignee())) {
String msg = "不是串行最后一个节点,无法进行回退 ";
throw new FlowableRuntimeException(msg);
}
// 替换任务执行变量
assigneeList.set(assigneeList.size() - 1, assignee);
runtimeService.setVariableLocal(paramsTemplate.getHisTask().getProcessInstanceId(), assigneeListExpr, assigneeList);
}
// 流程执行变量
Integer loopCounter = (Integer) hisVarMap.get(RollbackConstants.MultiInstanceConstants.LOOP_COUNTER);
// 会签主任务
ExecutionEntity parentExecution = CommandContextUtil.getExecutionEntityManager(commandContext)
.createChildExecution(executionEntity.getParent());
parentExecution.setCurrentFlowElement(paramsTemplate.getCurrentTaskElement());
parentExecution.setActive(false);
// 配置 会签 root execution
parentExecution.setMultiInstanceRoot(true);
// 配置主 execution 变量
Map parentVarMap = new HashMap<>();
parentVarMap.put(RollbackConstants.MultiInstanceConstants.NR_OF_ACTIVE_INSTANCES, 1);
parentVarMap.put(RollbackConstants.MultiInstanceConstants.NR_OF_COMPLETE_INSTANCES, assignees.size() - 1);
parentVarMap.put(RollbackConstants.MultiInstanceConstants.NR_OF_INSTANCE, assignees.size());
parentExecution.setVariablesLocal(parentVarMap);
if (isSequence) {
log.info("创建 串行 会签任务");
createSequenceMultiInstance(parentExecution, assignees);
} else {
log.info("创建 并行 会签任务");
createParallelMultiInstance(parentExecution, assignees, loopCounter);
}
removeHisTask(paramsTemplate.getHisTask());
}
创建 串行会签任务:createSequenceMultiInstance
/**
* 创建并行会签任务
*
* @param parentExecution
* @param loopCounter
*/
private void createParallelMultiInstance(ExecutionEntity parentExecution, Collection assignees, Integer loopCounter) {
for (int i = 0; i < assignees.size(); i++) {
if (i != loopCounter) {
Map varMap = new HashMap<>();
varMap.put(RollbackConstants.MultiInstanceConstants.LOOP_COUNTER, i);
varMap.put(assigneeExpr, "已完成任务");
// // 创建 新执行任务
ExecutionEntity newExecution = newExecution = CommandContextUtil.getExecutionEntityManager(commandContext)
.createChildExecution(parentExecution);
newExecution.setCurrentFlowElement(paramsTemplate.getCurrentTaskElement());
newExecution.setActive(false);
newExecution.setVariablesLocal(varMap);
//
// CommandContextUtil.getExecutionEntityManager(commandContext)
// .update(newExecution);
} else {
ExecutionEntity newExecution = CommandContextUtil.getExecutionEntityManager(commandContext)
.createChildExecution(parentExecution);
newExecution.setCurrentFlowElement(paramsTemplate.getCurrentTaskElement());
Map varMap = new HashMap<>();
varMap.put(assigneeExpr, assignee);
varMap.put(RollbackConstants.MultiInstanceConstants.LOOP_COUNTER, i);
newExecution.setVariablesLocal(varMap);
newExecution.setActive(true);
CommandContextUtil.getAgenda(commandContext).planContinueMultiInstanceOperation(newExecution, parentExecution, loopCounter);
}
}
}
}
创建 并行会签任务:createSequenceMultiInstance
private void createSequenceMultiInstance(ExecutionEntity parentExecution, Collection assignees) {
ExecutionEntity newExecution = CommandContextUtil.getExecutionEntityManager(commandContext)
.createChildExecution(parentExecution);
Map varMap = new HashMap<>();
varMap.put(assigneeExpr, assignee);
varMap.put(RollbackConstants.MultiInstanceConstants.LOOP_COUNTER, assignees.size() - 1);
newExecution.setCurrentFlowElement(paramsTemplate.getCurrentTaskElement());
newExecution.setVariablesLocal(varMap);
newExecution.setActive(true);
CommandContextUtil.getAgenda(commandContext)
.planContinueMultiInstanceOperation(newExecution, parentExecution, assignees.size() - 1);
}
以上 完成对 上一节点任务撤回 架构设计 及 第 2 种情况 解决方案讲述。
git地址:https://github.com/oldguys/flowable-modeler-demo/tree/branch_with_flowable_examples