Activiti6---Withdrawal of the process

1. Description

Scenario: Based on my case 4 , add such a Chinese-style requirement. For example, after the applicant submits the leave form, he finds that he has written a mistake and needs to withdraw the submission operation and fill in the submission again. So what should be done. This is the withdrawal of the process, the following is the operation of the withdrawal of the test process.

2. The idea of ​​realizing process withdrawal

1、获取当前任务所在的节点
2、获取所在节点的流出方向
3、记录所在节点的流出方向,并将所在节点的流出方向清空
4、获取目标节点
5、创建新的方向
6、将新的方向set到所在节点的流出方向
7、完成当前任务
8、还原所在节点的流出方向

3. Testing

Design flow chart:
Insert picture description here
Bpmn file:

<?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="m1597282397003" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="withdraw" isClosed="false" isExecutable="true" processType="None">
    <startEvent id="_2" name="开始"/>
    <userTask activiti:assignee="zhangsan" activiti:exclusive="true" id="_3" name="原始任务A"/>
    <userTask activiti:assignee="lisi" activiti:exclusive="true" id="_4" name="原始任务B"/>
    <endEvent id="_5" name="结束"/>
    <sequenceFlow id="_6" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="_7" sourceRef="_3" targetRef="_4"/>
    <sequenceFlow id="_8" sourceRef="_4" targetRef="_5"/>
  </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="withdraw">
      <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
        <omgdc:Bounds height="32.0" width="32.0" x="-5.0" y="325.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="140.0" y="310.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="55.0" width="85.0" x="350.0" y="310.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
        <omgdc:Bounds height="32.0" width="32.0" x="545.0" y="320.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_2" targetElement="_3">
        <omgdi:waypoint x="27.0" y="341.0"/>
        <omgdi:waypoint x="140.0" y="337.5"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_3" targetElement="_4">
        <omgdi:waypoint x="225.0" y="337.5"/>
        <omgdi:waypoint x="350.0" y="337.5"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_4" targetElement="_5">
        <omgdi:waypoint x="435.0" y="337.5"/>
        <omgdi:waypoint x="545.0" y="336.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>

Test category:

package com.yb;

import org.activiti.bpmn.model.BpmnModel;
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.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.hibernate.service.spi.ServiceException;
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 javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

/**
 * @author [email protected]
 * @version 1.0
 * @date 2020/8/5
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class ActivitiJpaTest08 {
    
    

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

    @Resource
    private HttpServletRequest request;
    /**
     * 部署流程,测试撤回
     */
    @Test
    public void repositoryDeploy(){
    
    
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("processes/BackOff.bpmn")
                .addClasspathResource("processes/BackOff.png")
                .name("测试撤回-1")
                .deploy();
        System.out.println("部署ID:"+deploy.getId());
        System.out.println("部署名称"+deploy.getName());
    }

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

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

    /**
     * 查询及完成任务
     */
    @Test
    public void taskQueryComplete(){
    
    
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("zhangsan")
                .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 taskQueryComplete2() throws Exception{
    
    
        //当前任务
        Task task = taskService.createTaskQuery()
                .taskAssignee("lisi")
                .singleResult();
        if(task==null) {
    
    
            throw new ServiceException("流程未启动或已执行完成,无法撤回");
        }
        List<HistoricTaskInstance> htlist = historyService.createHistoricTaskInstanceQuery()
                .processDefinitionId(task.getProcessDefinitionId())
                .list();

        String myTaskId = null;
        HistoricTaskInstance myTask = null;

        for (HistoricTaskInstance hti : htlist) {
    
    
            //回退到zhangsan也就是任务A,业务中这里就是当前登录的用户名
            if(hti.getAssignee().equals("zhangsan")){
    
    
                myTaskId=hti.getId();

                myTask=hti;
                break;
            }
        }

        if(myTask==null){
    
    
            throw new ServiceException("该任务非当前用户提交,无法撤回");
        }

        String processDefinitionId = myTask.getProcessDefinitionId();
        

        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);


        String myActivityId = null;

        List<HistoricActivityInstance> haiList =
                historyService
                        .createHistoricActivityInstanceQuery()
                        .executionId(myTask.getExecutionId())
                        .finished()
                        .list();

        for (HistoricActivityInstance hai : haiList) {
    
    
            if(myTaskId.equals(hai.getTaskId())) {
    
    
                myActivityId = hai.getActivityId();
                break;
            }
        }

        FlowNode myFlowNode =
                (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);

        Execution execution = runtimeService.createExecutionQuery()
                .executionId(task.getExecutionId()).singleResult();

        String activityId = execution.getActivityId();

        System.out.println(activityId);

        FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess()
                .getFlowElement(activityId);

        //记录原活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
        oriSequenceFlows.addAll(flowNode.getOutgoingFlows());

        //清理活动方向
        flowNode.getOutgoingFlows().clear();

        //建立新方向
        List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(flowNode);
        newSequenceFlow.setTargetFlowElement(myFlowNode);
        newSequenceFlowList.add(newSequenceFlow);
        flowNode.setOutgoingFlows(newSequenceFlowList);

        Authentication.setAuthenticatedUserId("zhangsan");
        taskService.addComment(task.getId(), task.getProcessInstanceId(), "撤回");

        //完成任务
        taskService.complete(task.getId());
        //恢复原方向
        flowNode.setOutgoingFlows(oriSequenceFlows);
    }
}

database:
Insert picture description here

Guess you like

Origin blog.csdn.net/Lv_vI/article/details/107997446