activiti6节点跳转设计

简介:工作流开发过程中,因为国内的需求变态原因,会有流程图节点任意跳转的需求

一.普通的流程跳转设计

因为普通流程只需要根据连线进行节点跳转就行直接贴代码:


        //获取流程定义
        Process process = repositoryService.getBpmnModel(processDefinitionId).getMainProcess();
        //获取目标节点定义
        FlowNode targetNode = (FlowNode) process.getFlowElement(flowElementId);
        List<Execution> executionList = runtimeService.createExecutionQuery().parentId(taskList.get(0).getProcessInstanceId()).list();
        Execution newExecution = executionList.remove(0);
        //流程执行到来源节点
        managementService.executeCommand(new SetNodeAndGoCmd(targetNode, newExecution.getId()));
        if (CollectionUtil.isNotEmpty(executionList)) {
            List<String> ids = executionList.stream().map(Execution::getId).collect(Collectors.toList());
            managementService.executeCommand(new DeleteExecutionCmd(ids));
        }
        //删除当前运行任务
        for (Task currentTask : taskList) {
            managementService.executeCommand(new DeleteTaskCmd(currentTask.getId()));
        }
@Slf4j
public class SetNodeAndGoCmd implements Command<Void> {

    private FlowNode flowElement;
    private String executionId;
    private String executionName;
    private String startUserId;

    public SetNodeAndGoCmd(FlowNode flowElement, String executionId) {
        this.flowElement = flowElement;
        this.executionId = executionId;
    }

    public SetNodeAndGoCmd(FlowNode flowElement, String executionId, String executionName, String startUserId) {
        this.flowElement = flowElement;
        this.executionId = executionId;
        this.executionName = executionName;
        this.startUserId = startUserId;
    }

    @Override
    public Void execute(CommandContext commandContext) {
        //获取目标节点的来源连线
        List<SequenceFlow> flows = flowElement.getIncomingFlows();
        if (flows == null || flows.size() < 1) {
            throw new ActivitiException("回退错误,目标节点没有来源连线");
        }

        //随便流程当前的主线,时当前执行计划为,从连线流转到目标节点,实现跳转
        ExecutionEntity executionEntity = commandContext.getExecutionEntityManager().findById(executionId);
        executionEntity.setCurrentFlowElement(flows.get(0));
        if (StrUtil.isNotEmpty(executionName)) {
            executionEntity.setName(executionName);
        }
        if (StrUtil.isNotEmpty(startUserId)) {
            executionEntity.setStartUserId(startUserId);
        }
        log.info("SetNodeAndGoCmd执行节点跳转:processInstanceId:{},来源:{},出线:{}", executionEntity.getProcessInstanceId(), flows.get(0).getSourceRef(), flows.get(0).getTargetRef());
        commandContext.getAgenda().planTakeOutgoingSequenceFlowsOperation(executionEntity, true);
        return null;
    }
}
@Slf4j
public class DeleteExecutionCmd implements Command<Void>, Serializable {

    private List<String> executionIds;

    public DeleteExecutionCmd(List<String> executionIds) {
        super();
        this.executionIds = executionIds;
    }

    /**
     * @param
     * @param
     * @return
     */
    @Override
    public Void execute(CommandContext commandContext) {
        //删除当前任务,来源任务
        ExecutionEntityManager executionEntityManager = (ExecutionEntityManagerImpl) commandContext.getExecutionEntityManager();
        for (String id : executionIds) {
            executionEntityManager.delete(id);
        }
        return null;
    }

}
@Slf4j
public class DeleteTaskCmd extends NeedsActiveTaskCmd<String> {
    public DeleteTaskCmd(String taskId) {
        super(taskId);
    }

    /**
     * 这里继承了NeedsActiveTaskCmd,主要时很多跳转业务场景下,要求不能时挂起任务。可以直接继承Command即可
     *
     * @param commandContext
     * @param currentTask
     * @return
     */
    @Override
    public String execute(CommandContext commandContext, TaskEntity currentTask) {
        log.info("DeleteTaskCmd执行任务删除:ID:{},name:{}", currentTask.getId(), currentTask.getName());
        //获取所需服务
        TaskEntityManagerImpl taskEntityManager = (TaskEntityManagerImpl) commandContext.getTaskEntityManager();
        //获取当前任务的来源任务及来源节点信息
        ExecutionEntity executionEntity = currentTask.getExecution();
        //删除当前任务,来源任务
        taskEntityManager.deleteTask(currentTask, "", false, false);
        HistoricTaskInstanceEntityManager historicTaskInstanceEntityManager = (HistoricTaskInstanceEntityManagerImpl) commandContext.getHistoricTaskInstanceEntityManager();
        historicTaskInstanceEntityManager.delete(currentTask.getId());
        return executionEntity == null ? "" : executionEntity.getId();
    }

二.并行网关跳转

因为并行网关每条支线execution都会创建,所以按照上面的方式直接跳转到其中一个节点是不可以的,并行网关不完整。有两个解决方案:

1.获取跳转节点所在的并行网关,或者网关开始的outgoing出线,每一条出现都新增一条任务和execution记录,这样网关就可以继续执行汇聚

2.业务限制网关内的节点不允许驳回,我们采用第二种方案

方案二要解决的问题:如果判断一个节点是否被并行网关包围?

代码如下:

这边代码可以优化,因为递归会有很多重复节点被扫描,达不到O(n)时间复杂度,如果用备忘录去剪枝优化需要注意一个问题,stack是全局的,如果已经判断的节点直接返回会导致stack数据不对,要做更多的判断,因为我们流程图不会有几百个节点,没有必要去优化

   @Override
    public Map<String, Boolean> isSurroundedWithParallelGateway(String processDefinitionId) {
        Map<String, Boolean> results = MapUtil.newHashMap(16);
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        Process process = bpmnModel.getProcesses().get(0);
        StartEvent startEvent = process.findFlowElementsOfType(org.activiti.bpmn.model.StartEvent.class).get(0);
        //从开始节点开始遍历流程图,如果遇到并行网关入栈,遍历所有节点入栈直到遇到并行网关出栈,并切进行判断是否存在目标节点
        //时间复杂度O(n)只需要遍历一遍,空间复杂度lgO(n)
        recursionParallelGateway(startEvent, new Stack<>(), results);
        return results;
    }

    private void recursionParallelGateway(FlowNode currentNode, Stack<FlowNode> stack, Map<String, Boolean> result) {
        if (currentNode instanceof EndEvent) {
            return;
        }
        List<SequenceFlow> outComing = currentNode.getOutgoingFlows();
        if (CollectionUtil.isEmpty(outComing)) {
            return;
        }
        //并行网关开始
        if (currentNode.getIncomingFlows().size() == 1 && currentNode instanceof ParallelGateway) {
            for (int i = 0; i < currentNode.getOutgoingFlows().size(); i++) {
                stack.push(currentNode);
            }
        }
        if (currentNode instanceof ParallelGateway) {
            log.info("并行网关参数:{},{},{}", currentNode.getId(), currentNode.getIncomingFlows().size(), currentNode.getOutgoingFlows());
        }
        //并行网关结束
        if (currentNode.getIncomingFlows().size() > 1 && currentNode instanceof ParallelGateway && !stack.isEmpty()) {
            stack.pop();
        }
        if (currentNode instanceof UserTask) {
            if (!stack.isEmpty()) {
                result.put(currentNode.getId(), true);
            } else {
                result.put(currentNode.getId(), false);
            }
        }
        for (SequenceFlow flow : outComing) {
            FlowNode flowElement = (FlowNode) flow.getTargetFlowElement();
            recursionParallelGateway(flowElement, stack, result);
        }
    }

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值