环境:Springboot2.3.12.RELEASE + Activiti7.1.0.M6
本篇主要演示复杂的审批流程的应用:排他网关路由的使用(Exclusive Gateway)
流程设计
审批说明:当员工请假天数小于等于3天时,部门经理审批,当部门经理同意flag==1流程结束,不同意flag==0流程返回到"员工请假"节点;当请假天数大于3天时,先由部门经理审批,再由总经理审批,当总经理同意flag==1流程结束,不同意flag==0流程返回到"员工请假"节点。
- <?xml version="1.0" encoding="UTF-8"?>
- <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.xg.com">
- <process id="holiday3" name="holiday3" isExecutable="true">
- <startEvent id="startevent1" name="Start"></startEvent>
- <userTask id="usertask1" name="员工请假" activiti:assignee="${userId}"></userTask>
- <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
- <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
- <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="exclusivegateway1"></sequenceFlow>
- <userTask id="usertask2" name="部门经理审批" activiti:assignee="${d_mgr}"></userTask>
- <sequenceFlow id="flow3" name="${days <=3}" sourceRef="exclusivegateway1" targetRef="usertask2">
- <conditionExpression xsi:type="tFormalExpression"><![CDATA[${days <=3}]]></conditionExpression>
- </sequenceFlow>
- <userTask id="usertask3" name="部门经理审批" activiti:assignee="${d_mgr}"></userTask>
- <userTask id="usertask4" name="总经理审批" activiti:assignee="${g_mgr}"></userTask>
- <sequenceFlow id="flow4" name="${days > 3}" sourceRef="exclusivegateway1" targetRef="usertask3">
- <conditionExpression xsi:type="tFormalExpression"><![CDATA[${days > 3}]]></conditionExpression>
- </sequenceFlow>
- <sequenceFlow id="flow5" sourceRef="usertask3" targetRef="usertask4"></sequenceFlow>
- <endEvent id="endevent1" name="End"></endEvent>
- <sequenceFlow id="flow6" name="${flag==1}" sourceRef="usertask2" targetRef="endevent1">
- <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag==1}]]></conditionExpression>
- </sequenceFlow>
- <sequenceFlow id="flow7" name="${flag==1}" sourceRef="usertask4" targetRef="endevent1">
- <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag==1}]]></conditionExpression>
- </sequenceFlow>
- <sequenceFlow id="flow8" name="${flag==0}" sourceRef="usertask2" targetRef="usertask1">
- <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag==0}]]></conditionExpression>
- </sequenceFlow>
- <sequenceFlow id="flow9" name="${flag==0}" sourceRef="usertask4" targetRef="usertask1">
- <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag==0}]]></conditionExpression>
- </sequenceFlow>
- </process>
- <bpmndi:BPMNDiagram id="BPMNDiagram_holiday3">
- <bpmndi:BPMNPlane bpmnElement="holiday3" id="BPMNPlane_holiday3">
- <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
- <omgdc:Bounds height="35.0" width="35.0" x="470.0" y="40.0"></omgdc:Bounds>
- </bpmndi:BPMNShape>
- <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
- <omgdc:Bounds height="55.0" width="105.0" x="435.0" y="120.0"></omgdc:Bounds>
- </bpmndi:BPMNShape>
- <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
- <omgdc:Bounds height="40.0" width="40.0" x="467.0" y="260.0"></omgdc:Bounds>
- </bpmndi:BPMNShape>
- <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
- <omgdc:Bounds height="55.0" width="105.0" x="220.0" y="253.0"></omgdc:Bounds>
- </bpmndi:BPMNShape>
- <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
- <omgdc:Bounds height="55.0" width="105.0" x="640.0" y="253.0"></omgdc:Bounds>
- </bpmndi:BPMNShape>
- <bpmndi:BPMNShape bpmnElement="usertask4" id="BPMNShape_usertask4">
- <omgdc:Bounds height="55.0" width="105.0" x="880.0" y="253.0"></omgdc:Bounds>
- </bpmndi:BPMNShape>
- <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
- <omgdc:Bounds height="35.0" width="35.0" x="470.0" y="430.0"></omgdc:Bounds>
- </bpmndi:BPMNShape>
- <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
- <omgdi:waypoint x="487.0" y="75.0"></omgdi:waypoint>
- <omgdi:waypoint x="487.0" y="120.0"></omgdi:waypoint>
- </bpmndi:BPMNEdge>
- <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
- <omgdi:waypoint x="487.0" y="175.0"></omgdi:waypoint>
- <omgdi:waypoint x="487.0" y="260.0"></omgdi:waypoint>
- </bpmndi:BPMNEdge>
- <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
- <omgdi:waypoint x="467.0" y="280.0"></omgdi:waypoint>
- <omgdi:waypoint x="325.0" y="280.0"></omgdi:waypoint>
- <bpmndi:BPMNLabel>
- <omgdc:Bounds height="14.0" width="100.0" x="361.0" y="280.0"></omgdc:Bounds>
- </bpmndi:BPMNLabel>
- </bpmndi:BPMNEdge>
- <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
- <omgdi:waypoint x="507.0" y="280.0"></omgdi:waypoint>
- <omgdi:waypoint x="640.0" y="280.0"></omgdi:waypoint>
- <bpmndi:BPMNLabel>
- <omgdc:Bounds height="14.0" width="100.0" x="538.0" y="280.0"></omgdc:Bounds>
- </bpmndi:BPMNLabel>
- </bpmndi:BPMNEdge>
- <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
- <omgdi:waypoint x="745.0" y="280.0"></omgdi:waypoint>
- <omgdi:waypoint x="880.0" y="280.0"></omgdi:waypoint>
- </bpmndi:BPMNEdge>
- <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
- <omgdi:waypoint x="272.0" y="308.0"></omgdi:waypoint>
- <omgdi:waypoint x="272.0" y="447.0"></omgdi:waypoint>
- <omgdi:waypoint x="470.0" y="447.0"></omgdi:waypoint>
- <bpmndi:BPMNLabel>
- <omgdc:Bounds height="14.0" width="100.0" x="300.0" y="429.0"></omgdc:Bounds>
- </bpmndi:BPMNLabel>
- </bpmndi:BPMNEdge>
- <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
- <omgdi:waypoint x="932.0" y="308.0"></omgdi:waypoint>
- <omgdi:waypoint x="932.0" y="447.0"></omgdi:waypoint>
- <omgdi:waypoint x="505.0" y="447.0"></omgdi:waypoint>
- <bpmndi:BPMNLabel>
- <omgdc:Bounds height="14.0" width="100.0" x="800.0" y="429.0"></omgdc:Bounds>
- </bpmndi:BPMNLabel>
- </bpmndi:BPMNEdge>
- <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
- <omgdi:waypoint x="272.0" y="253.0"></omgdi:waypoint>
- <omgdi:waypoint x="272.0" y="147.0"></omgdi:waypoint>
- <omgdi:waypoint x="435.0" y="147.0"></omgdi:waypoint>
- <bpmndi:BPMNLabel>
- <omgdc:Bounds height="14.0" width="100.0" x="290.0" y="151.0"></omgdc:Bounds>
- </bpmndi:BPMNLabel>
- </bpmndi:BPMNEdge>
- <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
- <omgdi:waypoint x="932.0" y="253.0"></omgdi:waypoint>
- <omgdi:waypoint x="932.0" y="147.0"></omgdi:waypoint>
- <omgdi:waypoint x="540.0" y="147.0"></omgdi:waypoint>
- <bpmndi:BPMNLabel>
- <omgdc:Bounds height="14.0" width="100.0" x="790.0" y="151.0"></omgdc:Bounds>
- </bpmndi:BPMNLabel>
- </bpmndi:BPMNEdge>
- </bpmndi:BPMNPlane>
- </bpmndi:BPMNDiagram>
- </definitions>
到此流程设计完成。
Service接口
- @Service
- public class HolidayService {
- private static final Logger logger = LoggerFactory.getLogger(HolidayService2.class);
- @Resource
- private RuntimeService runtimeService ;
- @Resource
- private TaskService taskService ;
- /**
- * <p>
- * 设置assignee的取值,用户可以在界面上设置流程的执行人
- * </p>
- * <p>时间:2021年1月22日-下午8:30:39</p>
- * @author 爷爷
- * @param processDefinitionId
- * @return ProcessInstance
- */
- public ProcessInstance startProcessInstanceAssignVariables(String processDefinitionId, Map<String, Object> variables) {
- Authentication.setAuthenticatedUserId((String)variables.get("assignee")) ;
- ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, variables);
- logger.info("流程定义ID: {}", processInstance.getProcessDefinitionId());
- logger.info("流程实例ID: {}", processInstance.getId());
- logger.info("BussinessKey: {}", processInstance.getBusinessKey()) ;
- return processInstance ;
- }
- /**
- * <p>执行任务</p>
- * <p>时间:2021年1月24日-上午10:40:39</p>
- * @author 爷爷
- * @param variables
- * @param instanceId
- * @return void
- */
- public void executionTask(Map<String, Object> variables, String instanceId) {
- Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult() ;
- if (task == null) {
- logger.error("任务【{}】不存在", instanceId) ;
- throw new RuntimeException("任务【" + instanceId + "】不存在") ;
- }
- // 注意这里一定要设置变量,否则流程中设置的表达式无法获取,如下图
- taskService.setVariables(task.getId(), variables);
- taskService.complete(task.getId(), variables) ;
- }
- }
方法的含义已经在代码中说明了。
Controller接口
- package com.pack.controller;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.stream.Collectors;
- import javax.annotation.Resource;
- import org.activiti.engine.history.HistoricActivityInstance;
- import org.activiti.engine.runtime.ProcessInstance;
- import org.activiti.engine.task.Task;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
- import com.pack.dto.ProcessDefinitionDTO;
- import com.pack.dto.ProcessInstanceDTO;
- import com.pack.dto.R;
- import com.pack.task.HolidayService2;
- @RestController
- @RequestMapping("/holidays2")
- public class HolidayController2 {
- @Resource
- private HolidayService holidayService ;
- /**
- * <p>启动请假审批流程</p>
- * <p>时间:2021年1月23日-上午10:32:55</p>
- * @author 爷爷
- * @param userId
- * @param processDefinitionId 流程定义Id
- * @return R
- */
- @GetMapping("/start")
- public R startProcess(String userId, String processDefinitionId) {
- Map<String, Object> variables = new HashMap<>() ;
- variables.put("userId", userId) ;
- ProcessInstance instance = holidayService.startProcessInstanceAssignVariables(processDefinitionId, variables) ;
- ProcessInstanceDTO dto = new ProcessInstanceDTO() ;
- dto.setInstanceId(instance.getId()) ;
- return R.success(dto) ;
- }
- /**
- * <p>
- * 填写审批单
- * </p>
- * <p>时间:2021年1月23日-上午11:57:30</p>
- * @author 爷爷
- * @param Map取值如下
- * @param days 请假天数
- * @param explain 审批单说明
- * @param instanceId 流程实例ID
- * @param assignee 指定下一个流程执行人
- * @return R
- */
- @GetMapping("/apply")
- public R fillApply(@RequestParam Map<String, Object> variables) {
- String instanceId = (String) variables.remove("instanceId") ;
- if (StringUtils.isEmpty(instanceId)) {
- return R.failure("未知任务") ;
- }
- holidayService.executionTask(variables, instanceId);
- return R.success() ;
- }
- }
测试
启动流程
请假人处理
d_mgr:部门经理ID
days:3 (小于3天)
部门经理处理
flag:0 (不同意,驳回到员工申请)
查看act_hi_actinst表数据:
流程又进入到了“员工申请”节点
再次重复上面的流程,当走到部门经理时,将flag=1。
到此流程结束,当请求天数大于3天的处理过程与上面基本相同。
原文链接:https://www.toutiao.com/a7038841229978747422/