乐趣区

关于activiti:SpringBoot-Activiti6系列教程九流程回退实现

介绍

回退 操作是指,将流程退回到上一个节点,基本思路是通过审批历史服务 HistoryService 找到审批审批的上一节点,而后跟通用回绝操作相似,将流程拨回到该节点,要留神的一个问题是,如果碰到并行审批,在并行线上回退应该回退到哪里呢?

如图,如果审批程序为 主管审批 -> 上级领导审批 -> 董事长审批,这时候总监审批执行回退操作,应该回退到哪个节点呢,显然不是董事长,因为这是两个并行互不烦扰的审批,失常应该回退到主管审批这里,所以回退操作应该是基于 execution 的回退。如果你对 ecxecution 不理解,你能够查看之前的文章 SpringBoot Activiti6 系列教程(六)-Execution 阐明

因而,计划就是找到该 execution 的上个审批节点将流程回退到该节点。

实现

同样,咱们能够编写一个 command 的类实现回退

FlowToPreNodeCmd

import com.definesys.mpaas.common.exception.MpaasBusinessException;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.task.Task;

import java.util.List;

/**
 * @Copyright: Shanghai Definesys Company.All rights reserved.
 * @Description:
 * @author: jianfeng.zheng
 * @since: 2019/9/24 6:11 PM
 * @history: 1.2019/9/24 created by jianfeng.zheng
 */
public class FlowToPreNodeCmd implements Command<String> {
    private Task task;

    public FlowToPreNodeCmd(Task task) {this.task = task;}

    @Override
    public String execute(CommandContext context) {FlowElement element = this.getPreNode(this.task, context);
        if (element == null) {throw new MpaasBusinessException("该节点不能进行退回");
        }
        SequenceFlow flow = this.findAcessSequenceFlow((FlowNode) element);
        ExecutionEntity executionEntity = context.getExecutionEntityManager().findById(task.getExecutionId());
        executionEntity.setCurrentFlowElement(flow);
        context.getAgenda().planTakeOutgoingSequenceFlowsOperation(executionEntity, true);
        return executionEntity.getId();}

    private FlowElement getPreNode(Task task, CommandContext context) {HistoryService historyService = context.getProcessEngineConfiguration().getHistoryService();
        List<HistoricActivityInstance> items = historyService.createHistoricActivityInstanceQuery()
                .executionId(task.getExecutionId())
                .activityType("userTask")
                .orderByHistoricActivityInstanceStartTime()
                .desc()
                .list();
        if (items == null || items.size() == 0) {throw new MpaasBusinessException("未找到上一节点");
        }
        String currentAct = task.getTaskDefinitionKey();
        String preAct = null;
        for (int i = 0; i < items.size(); ++i) {HistoricActivityInstance item = items.get(i);
            if (currentAct.equals(item.getActivityId())) {continue;}
            preAct = item.getActivityId();
            break;
        }
        if (preAct == null) {return null;}
        RepositoryService repositoryService = context.getProcessEngineConfiguration().getRepositoryService();
        org.activiti.bpmn.model.Process process = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getMainProcess();
        FlowElement node = process.getFlowElement(preAct);
        return node;
    }

    private SequenceFlow findAcessSequenceFlow(FlowNode node) {List<SequenceFlow> flows = node.getIncomingFlows();
        if (flows == null || flows.size() == 0) {throw new MpaasBusinessException("上一节点找不到入口");
        }
        // 找没有加条件的连线
        for (SequenceFlow flow : flows) {if (flow.getConditionExpression() == null) {return flow;}
        }
        // 如果都没有抉择第一条
        return flows.get(0);
    }

}

有两个中央须要留神

  • 基于 execution 来进行查找,代码外面条件为.executionId(task.getExecutionId())
  • activiti 从新执行一个流须要有一条“线”,无限思考没有加条件的线,保障流程可能流进咱们心愿的节点

下面仅仅只是流程回退的操作,要实现回退成果,还须要删除变量和以后的 task。

@Override
public TaskResponse backwardTask(BPMContextInfo context, TaskResponse request, String user) {Task task = taskService.createTaskQuery().taskId(request.getTaskId()).singleResult();
    managementService.executeCommand(new ExecutionVariableDeleteCmd(task.getExecutionId()));
    managementService.executeCommand(new FlowToPreNodeCmd(task));
    managementService.executeCommand(new TaskDeleteCmd(request.getTaskId()));
    return this.taskResponse(task,task.getProcessInstanceId());
}

这三个步骤程序不能扭转,第一步须要删除 execution 变量免得对新节点造成影响,最初一步才删除 task,因为计算上一节点须要用到 task,如果提前删除会造成 task 找不到。

退出移动版