Activiti7 之实战应用(四)

1 基本流程

1.1 流程图

此处每个 userTask 均需设置执行人
在这里插入图片描述

1.2 BPMN 文件

<?xml version="1.0" encoding="UTF-8"?>
<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:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://www.activiti.org/testm1625990337688" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="m1625990337688" name="" targetNamespace="http://www.activiti.org/testm1625990337688">
  <process id="zhanleai_approve_base" name="测试出差申请流程" processType="None" isClosed="false" isExecutable="true">
    <documentation />
    <startEvent id="_2" name="StartEvent" />
    <userTask id="_3" name="创建出差申请" activiti:assignee="张三" activiti:exclusive="true">
      <documentation />
      <extensionElements>
        <activiti:taskListener event="create" class="com.gh.maintenance.controller.activiti_example.ActivitiApproveListener" />
      </extensionElements>
    </userTask>
    <userTask id="_4" name="经理审批" activiti:assignee="李四" activiti:exclusive="true">
      <documentation />
      <extensionElements />
    </userTask>
    <userTask id="_5" name="总经理审批" activiti:assignee="老马" activiti:exclusive="true">
      <documentation />
      <extensionElements />
    </userTask>
    <userTask id="_6" name="财务审批" activiti:assignee="小王" activiti:exclusive="true" />
    <endEvent id="_7" name="EndEvent" />
    <sequenceFlow id="_8" sourceRef="_2" targetRef="_3" />
    <sequenceFlow id="_9" sourceRef="_4" targetRef="_5" />
    <sequenceFlow id="_10" sourceRef="_5" targetRef="_6" />
    <sequenceFlow id="_11" sourceRef="_6" targetRef="_7" />
    <sequenceFlow id="_12" sourceRef="_3" targetRef="_4" />
  </process>
  <bpmndi:BPMNDiagram id="Diagram-_1" name="New Diagram" documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0">
    <bpmndi:BPMNPlane bpmnElement="zhanleai_approve_base">
      <bpmndi:BPMNEdge id="BPMNEdge__12" bpmnElement="_12" sourceElement="_3" targetElement="_4">
        <di:waypoint x="650" y="13" />
        <di:waypoint x="700" y="13" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge__11" bpmnElement="_11" sourceElement="_6" targetElement="_7">
        <di:waypoint x="1110" y="13" />
        <di:waypoint x="1162" y="13" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge__10" bpmnElement="_10" sourceElement="_5" targetElement="_6">
        <di:waypoint x="960" y="13" />
        <di:waypoint x="1010" y="13" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge__9" bpmnElement="_9" sourceElement="_4" targetElement="_5">
        <di:waypoint x="800" y="13" />
        <di:waypoint x="860" y="13" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge__8" bpmnElement="_8" sourceElement="_2" targetElement="_3">
        <di:waypoint x="501" y="13" />
        <di:waypoint x="550" y="13" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="Shape-_2" bpmnElement="_2">
        <dc:Bounds x="465" y="-5" width="32" height="32" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="393" y="6" width="53" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_3" bpmnElement="_3">
        <dc:Bounds x="550" y="-27" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_4" bpmnElement="_4">
        <dc:Bounds x="700" y="-27" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_5" bpmnElement="_5">
        <dc:Bounds x="860" y="-27" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_6" bpmnElement="_6">
        <dc:Bounds x="1010" y="-27" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_7" bpmnElement="_7">
        <dc:Bounds x="1162" y="-5" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="1215" y="6" width="49" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

1.3 启动流程实例

/**
 * 启动流程实例
 * `act_hi_actinst`    流程实例执行历史信息
 * `act_hi_identitylink` 流程参与用户的历史信息
 * `act_hi_procinst`     流程实例的历史信息
 * `act_hi_taskinst`     流程任务的历史信息
 * `act_ru_execution`    流程执行信息
 * `act_ru_identitylink`  流程的正在参与用户信息
 * `act_ru_task`         流程当前任务信息
 */
@RequestMapping("/startProcess")
public String startProcess(String key) {
    
    

    // 1、根据流程定义的id启动流程
    ProcessInstance instance = runtimeService.startProcessInstanceByKey(key);
    // 2、输出内容
    log.info("流程定义ID:" + instance.getProcessDefinitionId());
    log.info("流程实例ID:" + instance.getId());
    log.info("当前活动的ID:" + instance.getActivityId());

    return "出差申请流程启动成功!";
}

1.4 查询个人待执行任务

/**
 * 查询个人待执行的任务
 */
@RequestMapping("/getUnfinishTaskList")
public List<Map<String,String>> getUnfinishTaskList(String key, String taskAssignee) {
    
    

    // 1、根据流程key 和 任务的负责人 查询任务
    List<Task> taskList = taskService.createTaskQuery()
            .processDefinitionKey(key) //流程Key
            .taskAssignee(taskAssignee)  //要查询的负责人
            .list();

    List<Map<String,String>> taskMapList = new ArrayList<>();
    // 2、输出
    for (Task task : taskList) {
    
    
        Map<String,String> taskMap = new HashMap<>();
        taskMap.put("processInstanceId",task.getProcessInstanceId());               //流程实例
        taskMap.put("id",task.getId());                                             //任务Id
        taskMap.put("assignee",task.getAssignee());                                 //任务负责人
        taskMap.put("name",task.getName());                                         //任务名称

        //取流程中设置的变量值(基本类型)
//            taskMap.put("taskAssignee", Objects.nonNull(taskService.getVariable(task.getId(),"出差申请人"))?taskService.getVariable(task.getId(),"出差申请人").toString():"");           //出差申请人
//            taskMap.put("days", Objects.nonNull(taskService.getVariable(task.getId(),"请假天数"))?taskService.getVariable(task.getId(),"请假天数").toString():"");                      //出差天数
//            taskMap.put("evaluateFee", Objects.nonNull(taskService.getVariable(task.getId(),"出差预计费用"))?taskService.getVariable(task.getId(),"出差预计费用").toString():"");        //出差预计费用
//            taskMap.put("startDate", Objects.nonNull(taskService.getVariable(task.getId(),"出差开始日期"))?taskService.getVariable(task.getId(),"出差开始日期").toString():"");          //出差开始日期

        //取流程中设置的变量值(bean类型)
        taskMap.put("approveInfo",Objects.nonNull(taskService.getVariable(task.getId(),"出差信息"))?JSONObject.toJSONString(taskService.getVariable(task.getId(),"出差信息")):"");

        taskMapList.add(taskMap);
    }

    return taskMapList;
}

1.5 完成个人任务

/**
 * (以张三创建出差申请为例)
 * 完成个人任务 : 创建出差申请
 * `act_ru_task` 这里的 '创建出差申请' 会变为 '经理审批'
 * 也就是也是下一个任务:  经理审批
 * `act_hi_taskinst` 这里的'创建出差申请'这条记录会有开始时间和结束时间
 * 然后还会增加一条记录 '经理审批'
 */
@RequestMapping("/completeTask")
public String completeTask(String key,String taskAssignee,String days,String evaluateFee, String startDate) {
    
    

    // 查找该负责人下所有的任务
    Task task = taskService.createTaskQuery()
            .processDefinitionKey(key)
            .taskAssignee(taskAssignee)
            .singleResult();

    //设置流程变量(基本类型),前提是该流程实例必须未执行完成 —— 存在 act_ru_variable 表中,对应取值即可
//        taskService.setVariable(task.getId(), "请假人", taskAssignee);
//        taskService.setVariable(task.getId(), "出差天数",days);
//        taskService.setVariable(task.getId(), "出差预计费用", evaluateFee);
//        taskService.setVariable(task.getId(), "出差开始日期", startDate);

    //设置流程变量(JavaBean类型),前提是该流程实例必须未执行完成 —— 存在 act_ru_variable 表中,但是实际值存在 act_ge_bytearray 表中
    ApproveInfo approveInfo = ApproveInfo.builder()
            .taskAssignee(taskAssignee)
            .days(days)
            .evaluateFee(evaluateFee)
            .startDate(startDate)
            .build();

    taskService.setVariable(task.getId(),"出差信息",approveInfo);

    //执行流程
    taskService.complete(task.getId());

    return "当前," + task.getAssignee() + "已完成任务:" + task.getName() ;
}

1.6 根据任务ID完成任务

/**
 * 根据任务ID来完成任务
 */
@RequestMapping("/completeTaskByTaskId")
public String completeTaskByTaskId(String taskId) {
    
    
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
    taskService.complete(taskId);
    return "当前已完成任务:" + task.getName() ;
}

2 排他网关

部分代码跟前面简单流程的有重复,我这部分就只贴流程图、bpmn文件和核心代码了

2.1 流程图

排他网关需要配置出口的表达式,然后通过 activiti 参数传值的形式会自动识别到该值,自动进行程序流转。
在这里插入图片描述

2.2 BPMN 文件

<?xml version="1.0" encoding="UTF-8"?>
<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:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://www.activiti.org/testm1625990337688" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="m1625990337688" name="" targetNamespace="http://www.activiti.org/testm1625990337688">
  <process id="zhanleai_approve_reject" name="测试出差申请流程" processType="None" isClosed="false" isExecutable="true">
    <documentation />
    <startEvent id="_1" name="流程开始">
      <documentation />
      <extensionElements />
      <outgoing>Flow_11ejhh5</outgoing>
    </startEvent>
    <userTask id="_2" name="创建出差申请" activiti:assignee="张三" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_08ru13p</incoming>
      <incoming>Flow_11ejhh5</incoming>
      <outgoing>Flow_1b2032e</outgoing>
    </userTask>
    <userTask id="_4" name="经理审批" activiti:assignee="李四" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_1fc8hv7</incoming>
      <outgoing>Flow_13ro4z7</outgoing>
    </userTask>
    <userTask id="_5" name="总经理审批" activiti:assignee="老马" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_13ro4z7</incoming>
      <outgoing>Flow_1jpug20</outgoing>
    </userTask>
    <userTask id="_7" name="财务审批" activiti:assignee="小王" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_09owxd3</incoming>
      <outgoing>Flow_1m3z2le</outgoing>
    </userTask>
    <sequenceFlow id="_11" sourceRef="_7" targetRef="_9" />
    <userTask id="_8" name="大老板审批" activiti:assignee="老赵" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_0p8i7yo</incoming>
      <outgoing>Flow_149ikim</outgoing>
      <outgoing>Flow_1h2q7h5</outgoing>
    </userTask>
    <sequenceFlow id="Flow_149ikim" sourceRef="_8" targetRef="_9" />
    <exclusiveGateway id="_6">
      <documentation />
      <incoming>Flow_1jpug20</incoming>
      <outgoing>Flow_09owxd3</outgoing>
      <outgoing>Flow_08ru13p</outgoing>
    </exclusiveGateway>
    <sequenceFlow id="Flow_1jpug20" sourceRef="_5" targetRef="_6" />
    <sequenceFlow id="Flow_09owxd3" name="同意审批" sourceRef="_6" targetRef="_7">
      <documentation />
      <conditionExpression xsi:type="tFormalExpression">${status==1}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="Flow_08ru13p" name="驳回申请" sourceRef="_6" targetRef="_2">
      <documentation />
      <conditionExpression xsi:type="tFormalExpression">${status==0}</conditionExpression>
    </sequenceFlow>
    <exclusiveGateway id="_3" name="">
      <documentation />
      <incoming>Flow_1b2032e</incoming>
      <outgoing>Flow_1fc8hv7</outgoing>
      <outgoing>Flow_0p8i7yo</outgoing>
    </exclusiveGateway>
    <sequenceFlow id="Flow_11ejhh5" sourceRef="_1" targetRef="_2" />
    <sequenceFlow id="Flow_1b2032e" sourceRef="_2" targetRef="_3">
      <documentation />
    </sequenceFlow>
    <sequenceFlow id="Flow_1fc8hv7" name="非特殊情况" sourceRef="_3" targetRef="_4">
      <documentation />
      <conditionExpression xsi:type="tFormalExpression">${status==0}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="Flow_0p8i7yo" name="特殊情况" sourceRef="_3" targetRef="_8">
      <documentation />
      <conditionExpression xsi:type="tFormalExpression">${status==1}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="Flow_1h2q7h5" sourceRef="_8" targetRef="_9">
      <documentation />
    </sequenceFlow>
    <sequenceFlow id="Flow_1m3z2le" sourceRef="_7" targetRef="_9" />
    <sequenceFlow id="Flow_13ro4z7" sourceRef="_4" targetRef="_5" />
    <endEvent id="_9" name="EndEvent">
      <documentation />
      <incoming>Flow_1h2q7h5</incoming>
      <incoming>Flow_1m3z2le</incoming>
      <terminateEventDefinition id="TerminateEventDefinition_1q47fr4" />
    </endEvent>
  </process>
  <bpmndi:BPMNDiagram id="Diagram-_1" name="New Diagram" documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0">
    <bpmndi:BPMNPlane bpmnElement="zhanleai_approve_reject">
      <bpmndi:BPMNEdge id="Flow_13ro4z7_di" bpmnElement="Flow_13ro4z7">
        <di:waypoint x="485" y="310" />
        <di:waypoint x="485" y="340" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1m3z2le_di" bpmnElement="Flow_1m3z2le">
        <di:waypoint x="485" y="640" />
        <di:waypoint x="485" y="750" />
        <di:waypoint x="622" y="750" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1h2q7h5_di" bpmnElement="Flow_1h2q7h5">
        <di:waypoint x="800" y="330" />
        <di:waypoint x="800" y="750" />
        <di:waypoint x="658" y="750" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0p8i7yo_di" bpmnElement="Flow_0p8i7yo">
        <di:waypoint x="640" y="125" />
        <di:waypoint x="640" y="190" />
        <di:waypoint x="800" y="190" />
        <di:waypoint x="800" y="250" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="698" y="165" width="44" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1fc8hv7_di" bpmnElement="Flow_1fc8hv7">
        <di:waypoint x="640" y="125" />
        <di:waypoint x="640" y="190" />
        <di:waypoint x="485" y="190" />
        <di:waypoint x="485" y="230" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="535" y="165" width="55" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1b2032e_di" bpmnElement="Flow_1b2032e">
        <di:waypoint x="640" y="30" />
        <di:waypoint x="640" y="75" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_11ejhh5_di" bpmnElement="Flow_11ejhh5">
        <di:waypoint x="640" y="-82" />
        <di:waypoint x="640" y="-50" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_08ru13p_di" bpmnElement="Flow_08ru13p">
        <di:waypoint x="460" y="480" />
        <di:waypoint x="360" y="480" />
        <di:waypoint x="360" y="-10" />
        <di:waypoint x="590" y="-10" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="353" y="225" width="44" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_09owxd3_di" bpmnElement="Flow_09owxd3">
        <di:waypoint x="485" y="505" />
        <di:waypoint x="485" y="560" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="478" y="523" width="45" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1jpug20_di" bpmnElement="Flow_1jpug20">
        <di:waypoint x="485" y="420" />
        <di:waypoint x="485" y="455" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="Shape-_2" bpmnElement="_1">
        <dc:Bounds x="622" y="-118" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="618" y="-147" width="44" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_3" bpmnElement="_2">
        <dc:Bounds x="590" y="-50" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_4" bpmnElement="_4">
        <dc:Bounds x="435" y="230" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_5" bpmnElement="_5">
        <dc:Bounds x="435" y="340" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_6" bpmnElement="_7">
        <dc:Bounds x="435" y="560" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_19lsvak_di" bpmnElement="_8">
        <dc:Bounds x="750" y="250" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_06brwuq_di" bpmnElement="_6" isMarkerVisible="true">
        <dc:Bounds x="460" y="455" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_1izdxic_di" bpmnElement="_3" isMarkerVisible="true">
        <dc:Bounds x="615" y="75" width="50" height="50" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="607" y="125" width="66" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1y7rj7q_di" bpmnElement="_9">
        <dc:Bounds x="622" y="732" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="616" y="775" width="49" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

2.3 排他网关核心代码

其实也就是流程图配置参数,然后调用的时候从前端传进来,后端代码需要将这个放到 variableMap 对象中,activiti 会自动根据 status 的值来判断,从而自动流转。
在这里插入图片描述

3 指定任意节点

流程图和 bpmn 复用排他网关

3.1 核心流程点

用户在前端页面的操作如下:

  • 查询已执行的任务节点信息【前端】
  • 选择节点信息【前端】
  • 改变节点信息【前端】

3.2 根据流程实例ID查询已执行的任务节点信息

/**
 *  根据流程实例ID查询已执行的任务节点信息
 *
 *  act_hi_taskinst:历史流程任务表
 * 
 * @Date 2023/6/14 15:03
 * @Param [processInstanceId]
 * @return 
 **/
@RequestMapping("/getRunNodesByProcInstId")
public Map<String,String> getRunNodesByProcInstId(String processInstanceId) {
    
    
    Map<String,String> map = new LinkedHashMap();
    // 获取流程历史中已执行节点,并按照节点在流程中执行先后顺序排序
    List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
            .processInstanceId(processInstanceId)
            .activityType("userTask")   //用户任务
            .finished()                 //已经执行的任务节点
            .orderByHistoricActivityInstanceEndTime()
            .asc()
            .list();

    // 已执行的节点ID集合
    if(Objects.nonNull(historicActivityInstanceList) && historicActivityInstanceList.size()>0){
    
    
        for (HistoricActivityInstance historicActivityInstance:historicActivityInstanceList){
    
    
            if(!map.containsKey(historicActivityInstance.getActivityId())){
    
    
                map.put(historicActivityInstance.getActivityId(),historicActivityInstance.getActivityName());
            }
        }
    }
    return map;
}

3.3 改变节点到指定节点

/**
 * 改变节点到指定节点
 * @param rejectNodeId 指定节点ID
 * @param processInstanceId 流程实例ID
 * @param assignee 负责人
 * @return
 */
@RequestMapping("/changeRunNode")
public String changeRunNode(String rejectNodeId, String processInstanceId, String assignee) {
    
    
    // 查找该负责人下所有的任务
    Task task = taskService.createTaskQuery()
            .processInstanceId(processInstanceId)
            .singleResult();

    //如果当前节点处理人不是该用户,就无法进行改变节点操作
    if (!assignee.equals(task.getAssignee())) {
    
    
        return "当前用户无法改变流程节点";
    }

    //获取当前节点
    String currActivityId = task.getTaskDefinitionKey();
    String processDefinitionId = task.getProcessDefinitionId();
    BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
    FlowNode currFlow = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currActivityId);

    if (null == currFlow) {
    
    
        List<SubProcess> subProcessList = bpmnModel.getMainProcess().findFlowElementsOfType(SubProcess.class, true);
        for (SubProcess subProcess : subProcessList) {
    
    
            FlowElement flowElement = subProcess.getFlowElement(currActivityId);
            if (flowElement != null) {
    
    
                currFlow = (FlowNode) flowElement;
                break;
            }
        }
    }
    //获取目标节点
    FlowNode targetFlow = (FlowNode) bpmnModel.getFlowElement(rejectNodeId);

    //如果不是同一个流程(子流程)不能进行改变节点操作
    if (!(currFlow.getParentContainer().equals(targetFlow.getParentContainer()))) {
    
    
        return "不是同一个流程,无法进行改变节点操作";
    }

    //记录原活动方向
    List<SequenceFlow> oriSequenceFlows = Lists.newArrayList();
    oriSequenceFlows.addAll(currFlow.getOutgoingFlows());

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

    //建立新的方向
    List<SequenceFlow> newSequenceFlows = Lists.newArrayList();
    SequenceFlow newSequenceFlow = new SequenceFlow();
    String uuid = UUID.randomUUID().toString().replace("-", "");
    newSequenceFlow.setId(uuid);
    newSequenceFlow.setSourceFlowElement(currFlow);  //原节点
    newSequenceFlow.setTargetFlowElement(targetFlow);  //目标节点
    newSequenceFlows.add(newSequenceFlow);
    currFlow.setOutgoingFlows(newSequenceFlows);

    //审批意见叠加
    //variables 审批意见     act_ru_variable  变量表
    Map<String, Object> variables = task.getProcessVariables();
    variables.put("status","2");
    variables.put("msg","资料填写有误,请重新填写!");

    //完成节点任务
    taskService.complete(task.getId(), variables);
    //恢复原方向
    currFlow.setOutgoingFlows(oriSequenceFlows);

    return "改变流程到指定节点,已完成!";
}

4 并行网关

4.1 流程图

在这里插入图片描述

4.2 BPMN 文件

<?xml version="1.0" encoding="UTF-8"?>
<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:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://www.activiti.org/testm1625990337688" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="m1625990337688" name="" targetNamespace="http://www.activiti.org/testm1625990337688">
  <process id="zhanleai_approve_parallel" name="测试出差申请流程" processType="None" isClosed="false" isExecutable="true">
    <documentation />
    <startEvent id="_1" name="流程开始">
      <documentation />
      <extensionElements />
      <outgoing>Flow_11ejhh5</outgoing>
    </startEvent>
    <userTask id="_2" name="创建出差申请" activiti:assignee="张三" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_08ru13p</incoming>
      <incoming>Flow_11ejhh5</incoming>
      <outgoing>Flow_1b2032e</outgoing>
    </userTask>
    <userTask id="_4" name="经理审批" activiti:assignee="李四" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_1fc8hv7</incoming>
      <outgoing>Flow_13ro4z7</outgoing>
    </userTask>
    <userTask id="_5" name="总经理审批" activiti:assignee="老马" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_13ro4z7</incoming>
      <outgoing>Flow_1jpug20</outgoing>
    </userTask>
    <userTask id="_7" name="财务审批" activiti:assignee="小王" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_09owxd3</incoming>
      <outgoing>Flow_1m3z2le</outgoing>
    </userTask>
    <sequenceFlow id="_11" sourceRef="_7" targetRef="_9" />
    <userTask id="_8" name="大老板审批" activiti:assignee="老赵" activiti:exclusive="true">
      <documentation />
      <extensionElements />
      <incoming>Flow_0p8i7yo</incoming>
      <outgoing>Flow_149ikim</outgoing>
      <outgoing>Flow_1h2q7h5</outgoing>
    </userTask>
    <sequenceFlow id="Flow_149ikim" sourceRef="_8" targetRef="_9" />
    <exclusiveGateway id="_6">
      <documentation />
      <incoming>Flow_1jpug20</incoming>
      <outgoing>Flow_09owxd3</outgoing>
      <outgoing>Flow_08ru13p</outgoing>
    </exclusiveGateway>
    <sequenceFlow id="Flow_1jpug20" sourceRef="_5" targetRef="_6" />
    <sequenceFlow id="Flow_09owxd3" name="同意审批" sourceRef="_6" targetRef="_7">
      <documentation />
      <conditionExpression xsi:type="tFormalExpression">${status==1}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="Flow_08ru13p" name="驳回申请" sourceRef="_6" targetRef="_2">
      <documentation />
      <conditionExpression xsi:type="tFormalExpression">${status==0}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="Flow_11ejhh5" sourceRef="_1" targetRef="_2" />
    <sequenceFlow id="Flow_1b2032e" sourceRef="_2" targetRef="_3">
      <documentation />
    </sequenceFlow>
    <sequenceFlow id="Flow_1fc8hv7" name="" sourceRef="_3" targetRef="_4">
      <documentation />
    </sequenceFlow>
    <sequenceFlow id="Flow_0p8i7yo" name="" sourceRef="_3" targetRef="_8">
      <documentation />
    </sequenceFlow>
    <sequenceFlow id="Flow_1h2q7h5" sourceRef="_8" targetRef="_9">
      <documentation />
    </sequenceFlow>
    <sequenceFlow id="Flow_1m3z2le" sourceRef="_7" targetRef="_9" />
    <sequenceFlow id="Flow_13ro4z7" sourceRef="_4" targetRef="_5" />
    <parallelGateway id="_3" name="">
      <documentation />
      <incoming>Flow_1b2032e</incoming>
      <outgoing>Flow_1fc8hv7</outgoing>
      <outgoing>Flow_0p8i7yo</outgoing>
    </parallelGateway>
    <endEvent id="_9" name="结束任务">
      <documentation />
      <incoming>Flow_1h2q7h5</incoming>
      <incoming>Flow_1m3z2le</incoming>
      <terminateEventDefinition id="TerminateEventDefinition_1gxwlzk" />
    </endEvent>
  </process>
  <bpmndi:BPMNDiagram id="Diagram-_1" name="New Diagram" documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0">
    <bpmndi:BPMNPlane bpmnElement="zhanleai_approve_parallel">
      <bpmndi:BPMNEdge id="Flow_13ro4z7_di" bpmnElement="Flow_13ro4z7">
        <di:waypoint x="485" y="310" />
        <di:waypoint x="485" y="340" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1m3z2le_di" bpmnElement="Flow_1m3z2le">
        <di:waypoint x="485" y="640" />
        <di:waypoint x="485" y="750" />
        <di:waypoint x="622" y="750" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1h2q7h5_di" bpmnElement="Flow_1h2q7h5">
        <di:waypoint x="840" y="350" />
        <di:waypoint x="840" y="750" />
        <di:waypoint x="658" y="750" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0p8i7yo_di" bpmnElement="Flow_0p8i7yo">
        <di:waypoint x="640" y="125" />
        <di:waypoint x="640" y="190" />
        <di:waypoint x="840" y="190" />
        <di:waypoint x="840" y="270" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="718" y="165" width="44" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1fc8hv7_di" bpmnElement="Flow_1fc8hv7">
        <di:waypoint x="640" y="125" />
        <di:waypoint x="640" y="190" />
        <di:waypoint x="485" y="190" />
        <di:waypoint x="485" y="230" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="535" y="165" width="55" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1b2032e_di" bpmnElement="Flow_1b2032e">
        <di:waypoint x="640" y="30" />
        <di:waypoint x="640" y="75" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_11ejhh5_di" bpmnElement="Flow_11ejhh5">
        <di:waypoint x="640" y="-82" />
        <di:waypoint x="640" y="-50" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_08ru13p_di" bpmnElement="Flow_08ru13p">
        <di:waypoint x="460" y="480" />
        <di:waypoint x="360" y="480" />
        <di:waypoint x="360" y="-10" />
        <di:waypoint x="590" y="-10" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="353" y="225" width="44" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_09owxd3_di" bpmnElement="Flow_09owxd3">
        <di:waypoint x="485" y="505" />
        <di:waypoint x="485" y="560" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="478" y="523" width="45" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1jpug20_di" bpmnElement="Flow_1jpug20">
        <di:waypoint x="485" y="420" />
        <di:waypoint x="485" y="455" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="Shape-_2" bpmnElement="_1">
        <dc:Bounds x="622" y="-118" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="618" y="-147" width="44" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_3" bpmnElement="_2">
        <dc:Bounds x="590" y="-50" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_4" bpmnElement="_4">
        <dc:Bounds x="435" y="230" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_5" bpmnElement="_5">
        <dc:Bounds x="435" y="340" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_6" bpmnElement="_7">
        <dc:Bounds x="435" y="560" width="100" height="80" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_19lsvak_di" bpmnElement="_8">
        <dc:Bounds x="790" y="270" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_06brwuq_di" bpmnElement="_6" isMarkerVisible="true">
        <dc:Bounds x="460" y="455" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_02haxla_di" bpmnElement="_3">
        <dc:Bounds x="615" y="75" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1mmhxvi_di" bpmnElement="_9">
        <dc:Bounds x="622" y="732" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="619" y="775" width="44" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

4.3 核心动作

并行网关没有什么特别的设置,直接画流程图,正常流转就能结束。但是需要注意一点,必须将结束事件设置为:Terminate End Event,不然会出现整个流程无法结束的情况。
在这里插入图片描述

5 任务监听器

  • create:任务创建后触发
  • Assignment:任务分配后触发
  • Delete:任务完成后触发
  • All:所有任务都触发

5.1 流程图

针对某个 userTask 设置监听器,选择“创建后”,java类设置全路径为:com.gh.maintenance.controller.activiti_example.ActivitiApproveListener
在这里插入图片描述

5.2 BPMN 文件

<?xml version="1.0" encoding="UTF-8"?>
<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:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://www.activiti.org/testm1625990337688" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="m1625990337688" name="" targetNamespace="http://www.activiti.org/testm1625990337688">
  <process id="zhanleai_approve_listener" name="测试出差申请流程" processType="None" isClosed="false" isExecutable="true">
    <documentation />
    <startEvent id="_2" name="StartEvent" />
    <userTask id="_3" name="创建出差申请" activiti:assignee="张三" activiti:exclusive="true">
      <documentation />
      <extensionElements>
        <activiti:taskListener event="create" class="com.gh.maintenance.controller.activiti_example.ActivitiApproveListener" />
      </extensionElements>
    </userTask>
    <userTask id="_4" name="经理审批" activiti:assignee="李四" activiti:exclusive="true">
      <documentation />
      <extensionElements />
    </userTask>
    <userTask id="_5" name="总经理审批" activiti:assignee="老马" activiti:exclusive="true">
      <documentation></documentation>
      <extensionElements />
    </userTask>
    <userTask id="_6" name="财务审批" activiti:assignee="小王" activiti:exclusive="true">
      <documentation></documentation>
      <extensionElements />
    </userTask>
    <endEvent id="_7" name="EndEvent" />
    <sequenceFlow id="_8" sourceRef="_2" targetRef="_3" />
    <sequenceFlow id="_9" sourceRef="_4" targetRef="_5" />
    <sequenceFlow id="_10" sourceRef="_5" targetRef="_6" />
    <sequenceFlow id="_11" sourceRef="_6" targetRef="_7" />
    <sequenceFlow id="_12" sourceRef="_3" targetRef="_4" />
  </process>
  <bpmndi:BPMNDiagram id="Diagram-_1" name="New Diagram" documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0">
    <bpmndi:BPMNPlane bpmnElement="zhanleai_approve_listener">
      <bpmndi:BPMNEdge id="BPMNEdge__12" bpmnElement="_12" sourceElement="_3" targetElement="_4">
        <di:waypoint x="477.5" y="160" />
        <di:waypoint x="477.5" y="195" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge__11" bpmnElement="_11" sourceElement="_6" targetElement="_7">
        <di:waypoint x="481" y="440" />
        <di:waypoint x="481" y="510" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge__10" bpmnElement="_10" sourceElement="_5" targetElement="_6">
        <di:waypoint x="480" y="340" />
        <di:waypoint x="480" y="385" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge__9" bpmnElement="_9" sourceElement="_4" targetElement="_5">
        <di:waypoint x="477.5" y="250" />
        <di:waypoint x="477.5" y="285" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge__8" bpmnElement="_8" sourceElement="_2" targetElement="_3">
        <di:waypoint x="481" y="27" />
        <di:waypoint x="481" y="105" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="0" height="0" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="Shape-_2" bpmnElement="_2">
        <dc:Bounds x="465" y="-5" width="32" height="32" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="32" height="32" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_3" bpmnElement="_3">
        <dc:Bounds x="435" y="105" width="85" height="55" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_4" bpmnElement="_4">
        <dc:Bounds x="435" y="195" width="85" height="55" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_5" bpmnElement="_5">
        <dc:Bounds x="435" y="285" width="85" height="55" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_6" bpmnElement="_6">
        <dc:Bounds x="440" y="385" width="85" height="55" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="85" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-_7" bpmnElement="_7">
        <dc:Bounds x="465" y="510" width="32" height="32" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="0" y="0" width="32" height="32" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

5.3 任务监听器核心代码

import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;

/**
 * @Description: 监听器 【create-任务创建后触发  Assignment-任务分配后触发  Delete-任务完成后触发  All-所有任务都触发】
 *
 * @Author: zhanleai
 * @Date:2023/6/16 11:37
 */
@Slf4j
public class ActivitiApproveListener implements TaskListener {
    
    
    @Override
    public void notify(DelegateTask delegateTask) {
    
    
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>此监听器触发机制为:"+delegateTask.getEventName());
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>触发监听任务ID值为:"+delegateTask.getId());
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>负责人为:"+delegateTask.getAssignee());
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>任务名称"+delegateTask.getName());
    }
}

猜你喜欢

转载自blog.csdn.net/qq_23845083/article/details/131292700
今日推荐