Activiti6---流程并行回退

文章目录

1. 说明

回退操作是指,将流程退回到上一个节点,基本思路是通过审批历史服务HistoryService找到审批审批的上一节点,然后跟通用拒绝操作类似,将流程拨回到该节点,要注意的一个问题是,如果碰到并行审批,在并行线上回退应该回退到哪里呢?
在这里插入图片描述
如图,如果审批顺序为主管审批->上级领导审批->董事长审批,这时候总监审批执行回退操作,应该回退到哪个节点呢,显然不是董事长,因为这是两个并行互不干扰的审批,正常应该回退到主管审批这里,所以回退操作应该是基于execution的回退。

2. 实现

Bpmn文件:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1597729482723" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="parallel" isClosed="false" isExecutable="true" processType="None">
    <startEvent id="_2" name="开始"/>
    <userTask activiti:assignee="user1" activiti:exclusive="true" id="_3" name="项目经理审批"/>
    <parallelGateway gatewayDirection="Unspecified" id="_4" name="ParallelGateway"/>
    <userTask activiti:assignee="user2" activiti:exclusive="true" id="_5" name="上级领导审批"/>
    <userTask activiti:assignee="user3" activiti:exclusive="true" id="_6" name="董事长审批"/>
    <userTask activiti:assignee="user4" activiti:exclusive="true" id="_7" name="主管审批"/>
    <userTask activiti:assignee="user5" activiti:exclusive="true" id="_8" name="总监审批"/>
    <parallelGateway gatewayDirection="Unspecified" id="_9" name="ParallelGateway"/>
    <endEvent id="_10" name="结束"/>
    <sequenceFlow id="_11" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="_12" sourceRef="_3" targetRef="_4"/>
    <sequenceFlow id="_13" sourceRef="_4" targetRef="_5"/>
    <sequenceFlow id="_14" sourceRef="_4" targetRef="_7"/>
    <sequenceFlow id="_15" sourceRef="_5" targetRef="_6"/>
    <sequenceFlow id="_16" sourceRef="_7" targetRef="_8"/>
    <sequenceFlow id="_17" sourceRef="_6" targetRef="_9"/>
    <sequenceFlow id="_18" sourceRef="_8" targetRef="_9"/>
    <sequenceFlow id="_19" sourceRef="_9" targetRef="_10"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#000000;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
    <bpmndi:BPMNPlane bpmnElement="parallel">
      <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
        <omgdc:Bounds height="32.0" width="32.0" x="285.0" y="-10.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
        <omgdc:Bounds height="55.0" width="85.0" x="260.0" y="60.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
        <omgdc:Bounds height="32.0" width="32.0" x="285.0" y="145.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
        <omgdc:Bounds height="55.0" width="85.0" x="190.0" y="265.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
        <omgdc:Bounds height="55.0" width="85.0" x="190.0" y="405.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7">
        <omgdc:Bounds height="55.0" width="85.0" x="335.0" y="265.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_8" id="Shape-_8">
        <omgdc:Bounds height="55.0" width="85.0" x="335.0" y="405.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_9" id="Shape-_9">
        <omgdc:Bounds height="32.0" width="32.0" x="290.0" y="530.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_10" id="Shape-_10">
        <omgdc:Bounds height="32.0" width="32.0" x="290.0" y="645.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_13" id="BPMNEdge__13" sourceElement="_4" targetElement="_5">
        <omgdi:waypoint x="285.0" y="161.0"/>
        <omgdi:waypoint x="235.0" y="230.0"/>
        <omgdi:waypoint x="235.0" y="265.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_3" targetElement="_4">
        <omgdi:waypoint x="301.0" y="115.0"/>
        <omgdi:waypoint x="301.0" y="145.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_15" id="BPMNEdge__15" sourceElement="_5" targetElement="_6">
        <omgdi:waypoint x="232.5" y="320.0"/>
        <omgdi:waypoint x="232.5" y="405.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_14" id="BPMNEdge__14" sourceElement="_4" targetElement="_7">
        <omgdi:waypoint x="317.0" y="161.0"/>
        <omgdi:waypoint x="375.0" y="225.0"/>
        <omgdi:waypoint x="375.0" y="265.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_17" id="BPMNEdge__17" sourceElement="_6" targetElement="_9">
        <omgdi:waypoint x="235.0" y="460.0"/>
        <omgdi:waypoint x="235.0" y="490.0"/>
        <omgdi:waypoint x="290.0" y="546.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_16" id="BPMNEdge__16" sourceElement="_7" targetElement="_8">
        <omgdi:waypoint x="377.5" y="320.0"/>
        <omgdi:waypoint x="377.5" y="405.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_19" id="BPMNEdge__19" sourceElement="_9" targetElement="_10">
        <omgdi:waypoint x="306.0" y="562.0"/>
        <omgdi:waypoint x="306.0" y="645.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_18" id="BPMNEdge__18" sourceElement="_8" targetElement="_9">
        <omgdi:waypoint x="380.0" y="460.0"/>
        <omgdi:waypoint x="380.0" y="490.0"/>
        <omgdi:waypoint x="322.0" y="546.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_2" targetElement="_3">
        <omgdi:waypoint x="301.0" y="22.0"/>
        <omgdi:waypoint x="301.0" y="60.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

测试类:

package com.yb;

import com.yb.utils.DeleteTaskCommand;
import com.yb.utils.JumpCommand;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author liangjie@itcast.cn
 * @version 1.0
 * @date 2020/8/5
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class ActivitiJpaTest10 {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    @Resource
    private ManagementService managementService;
    /**
     * 部署流程,测试征询
     */
    @Test
    public void repositoryDeploy(){
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("processes/activiti_ParallelFallback.bpmn")
                .addClasspathResource("processes/activiti_ParallelFallback.png")
                .name("测试并行流程回退-1")
                .deploy();
        System.out.println("部署ID:"+deploy.getId());
        System.out.println("部署名称"+deploy.getName());
    }

    /**
     * 发布流程
     */
    @Test
    public void runtimeRelease(){

        ProcessInstance pi = runtimeService.startProcessInstanceByKey("parallel");
        System.out.println("流程实例ID:"+pi.getId());
        System.out.println("流程定义ID:"+pi.getProcessDefinitionId());
    }

    /**
     * 查询及完成任务
     */
    @Test
    public void taskQueryComplete(){
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("user1")
                .list();
        for (Task task : list) {
            System.out.println("--------------------------------------------");
            System.out.println("任务ID:" + task.getId());
            System.out.println("任务名称:" + task.getName());
            System.out.println("任务创建时间:" + task.getCreateTime());
            System.out.println("任务委派人:" + task.getAssignee());
            System.out.println("流程实例ID:" + task.getProcessInstanceId());
            System.out.println("--------------------------------------------");
            taskService.complete(task.getId());
        }
    }

    /**
     * 流程回退
     */
    @Test
    public void taskBack(){
        Task task = taskService.createTaskQuery()
                .taskAssignee("user4")
                .singleResult();

        Process process = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getMainProcess();

        FlowNode flowNode = (FlowNode) process.getFlowElement("_3");

        String s = managementService.executeCommand(new DeleteTaskCommand(task.getId()));

        managementService.executeCommand(new JumpCommand(flowNode,s));

    }


    /**
     * 根据流程实例id获取上一个节点的信息
     */
    @Test
    public void queryUpOneNode(){
        Task task = taskService.createTaskQuery()
                .taskAssignee("user4")
                .singleResult();
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .orderByHistoricTaskInstanceEndTime()
                .desc()
                .list();

        HistoricTaskInstance taskInstance = null;

        if(!list.isEmpty()){
            if(list.get(0).getEndTime()!=null){
                taskInstance=list.get(0);
            }
        }
        System.out.println(list.get(0).getEndTime());
        System.out.println(taskInstance.getTaskDefinitionKey()+"==="+taskInstance.getName());
    }
}

测试结果:
当前测试流程:项目经理审批→(主管审批→撤回操作)→上级领导审批(不做审批)→(项目经理审批→提交,上级领导审批)→(上级领导审批,上级领导审批,主管审批))
错误:
并行流程,支线回退时,只删除了自己的任务,另外一个并行的任务依然存在,这样就会导致数据库中,act_ru_task表中数据会有多余的一条任务信息。
在这里插入图片描述
出现的问题:
并行流程回退操作时,支线回退到主线,所有并行支线需要删除,否则会出现重复任务。

解决:
并行支线回退时,直接回退到项目经理审批节点,进行重新审批。

	/**
     * 流程回退
     */
    @Test
    public void taskBack(){
        Task task = taskService.createTaskQuery()
                .taskAssignee("user5")
                .singleResult();

        List<Task> list = taskService.createTaskQuery()
                .processInstanceId(task.getProcessInstanceId())
                .list();
        Process process = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getMainProcess();
        
        //通过项目经理审批节点id获取到流程
        FlowNode flowNode = (FlowNode) process.getFlowElement("_3");
        //删除当前所有任务
        String s = null;
        for (Task task1 : list) {
            DeleteTaskCommand deleteTaskCommand = new DeleteTaskCommand(task1.getId());
            s = managementService.executeCommand(deleteTaskCommand);
        } 
//        String s = managementService.executeCommand(new DeleteTaskCommand(task.getProcessInstanceId()));
//        managementService.executeCommand()
        //跳转到项目经理审批节点
        managementService.executeCommand(new JumpCommand(flowNode,s));
    }

数据库:
在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值