Activiti6.0 多級審批流程 代碼執行
背景:公司要求有流程審批系統以便于适應不同客戶的審批層級;确定以Activiti6.0作為流程引擎;
這裡給出一個簡單的多級審批流程:
- 建立流程圖,我這裡流程圖在eclipse中生成的,使用了Activiti Eclipse BPMN 2.0 Designer插件;流程圖為.bpmn格式,我們在解析的時候,可以重命名為.bpmn20.xml;
這裡給出一個多級審批的流程圖:
這個圖我加了一些文字說明,醜了點,将就着看吧;
然後我給出這個流程圖對應的源碼:檔案全名稱為ApprovalProcess.bpmn20.xml
<?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.activiti.org/test">
<process id="myProcess" name="流程審批" isExecutable="true">
<startEvent id="startEvent" name="開始"></startEvent>
<userTask id="Submit" name="填寫資訊送出校對">
<extensionElements>
<activiti:formProperty id="SubmitMessage" name="送出時填寫的資訊" type="string"
required="true"></activiti:formProperty>
<activiti:formProperty id="SubmitUserName" name="申請人" type="string"
required="true"></activiti:formProperty>
<activiti:formProperty id="SubmitTime" name="送出時間" type="date" datePattern="yyyy-MM-dd"
required="true"></activiti:formProperty>
<activiti:formProperty id="SubmitType" name="是否送出校對" type="string"
required="true"></activiti:formProperty>
</extensionElements>
</userTask>
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="Submit"></sequenceFlow>
<exclusiveGateway id="decideSubmit" name="送出校對或者取消"></exclusiveGateway>
<sequenceFlow id="flow2" sourceRef="Submit" targetRef="decideSubmit"></sequenceFlow>
<userTask id="Proofread" name="校對">
<extensionElements>
<activiti:formProperty id="ProofreadMessage" name="校對時填寫的資訊" type="string"
required="true"></activiti:formProperty>
<activiti:formProperty id="ProofreadUserName" name="校對人" type="string"
required="true"></activiti:formProperty>
<activiti:formProperty id="ProofreadTime" name="校對時間" type="date" datePattern="yyyy-MM-dd"
required="true"></activiti:formProperty>
<activiti:formProperty id="ProofreadType" name="是否校對通過" type="string"
required="true"></activiti:formProperty>
</extensionElements>
</userTask>
<sequenceFlow id="flow3" sourceRef="decideSubmit" targetRef="Proofread">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${SubmitType=="Y" || SubmitType=="y"}]]></conditionExpression>
</sequenceFlow>
<exclusiveGateway id="decideProofread" name="校對通過或者失敗"></exclusiveGateway>
<sequenceFlow id="flow4" sourceRef="Proofread" targetRef="decideProofread"></sequenceFlow>
<userTask id="Review" name="稽核">
<extensionElements>
<activiti:formProperty id="ReviewMesage" name="稽核時填寫的資訊" type="string"
required="true"></activiti:formProperty>
<activiti:formProperty id="ReviewUserName" name="稽核人" type="string"
required="true"></activiti:formProperty>
<activiti:formProperty id="ReviewTime" name="稽核時間" type="date" datePattern="yyyy-MM-dd"
required="true"></activiti:formProperty>
<activiti:formProperty id="ReviewType" name="是否稽核通過" type="string"
required="true"></activiti:formProperty>
</extensionElements>
</userTask>
<sequenceFlow id="flow5" sourceRef="decideProofread" targetRef="Review">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${ProofreadType=="Y" || ProofreadType=="y"}]]></conditionExpression>
</sequenceFlow>
<exclusiveGateway id="decideReview" name="稽核通過或者失敗"></exclusiveGateway>
<sequenceFlow id="flow6" sourceRef="Review" targetRef="decideReview"></sequenceFlow>
<endEvent id="endEvent1" name="結束"></endEvent>
<sequenceFlow id="flow7" sourceRef="decideReview" targetRef="endEvent1">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${ReviewType=="Y" || ReviewType=="y"}]]></conditionExpression>
</sequenceFlow>
<endEvent id="endEventCancel" name="取消"></endEvent>
<sequenceFlow id="flow8" sourceRef="decideSubmit" targetRef="endEventCancel">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${SubmitType=="N" || SubmitType=="n"}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow9" sourceRef="decideProofread" targetRef="Submit">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${ProofreadType=="N" || ProofreadType=="n"}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow10" sourceRef="decideReview" targetRef="Submit">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${ReviewType=="N" || ReviewType=="n"}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="startEvent" id="BPMNShape_startEvent">
<omgdc:Bounds height="35.0" width="35.0" x="130.0" y="260.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="Submit" id="BPMNShape_Submit">
<omgdc:Bounds height="55.0" width="105.0" x="210.0" y="250.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="decideSubmit" id="BPMNShape_decideSubmit">
<omgdc:Bounds height="40.0" width="40.0" x="360.0" y="258.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="Proofread" id="BPMNShape_Proofread">
<omgdc:Bounds height="55.0" width="105.0" x="445.0" y="251.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="decideProofread" id="BPMNShape_decideProofread">
<omgdc:Bounds height="40.0" width="40.0" x="595.0" y="259.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="Review" id="BPMNShape_Review">
<omgdc:Bounds height="55.0" width="105.0" x="680.0" y="252.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="decideReview" id="BPMNShape_decideReview">
<omgdc:Bounds height="40.0" width="40.0" x="830.0" y="260.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endEvent1" id="BPMNShape_endEvent1">
<omgdc:Bounds height="35.0" width="35.0" x="915.0" y="263.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endEventCancel" id="BPMNShape_endEventCancel">
<omgdc:Bounds height="35.0" width="35.0" x="440.0" y="350.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="165.0" y="277.0"></omgdi:waypoint>
<omgdi:waypoint x="210.0" y="277.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="315.0" y="277.0"></omgdi:waypoint>
<omgdi:waypoint x="360.0" y="278.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="400.0" y="278.0"></omgdi:waypoint>
<omgdi:waypoint x="445.0" y="278.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="550.0" y="278.0"></omgdi:waypoint>
<omgdi:waypoint x="595.0" y="279.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
<omgdi:waypoint x="635.0" y="279.0"></omgdi:waypoint>
<omgdi:waypoint x="680.0" y="279.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
<omgdi:waypoint x="785.0" y="279.0"></omgdi:waypoint>
<omgdi:waypoint x="830.0" y="280.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
<omgdi:waypoint x="870.0" y="280.0"></omgdi:waypoint>
<omgdi:waypoint x="915.0" y="280.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
<omgdi:waypoint x="380.0" y="298.0"></omgdi:waypoint>
<omgdi:waypoint x="380.0" y="367.0"></omgdi:waypoint>
<omgdi:waypoint x="440.0" y="367.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
<omgdi:waypoint x="615.0" y="299.0"></omgdi:waypoint>
<omgdi:waypoint x="614.0" y="401.0"></omgdi:waypoint>
<omgdi:waypoint x="262.0" y="398.0"></omgdi:waypoint>
<omgdi:waypoint x="262.0" y="305.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10">
<omgdi:waypoint x="850.0" y="260.0"></omgdi:waypoint>
<omgdi:waypoint x="850.0" y="87.0"></omgdi:waypoint>
<omgdi:waypoint x="565.0" y="87.0"></omgdi:waypoint>
<omgdi:waypoint x="262.0" y="87.0"></omgdi:waypoint>
<omgdi:waypoint x="262.0" y="250.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
- maven項目引入dependency依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.sd</groupId>
<artifactId>process</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
這裡說明一下因為是代碼測試,是以資料庫使用的是H2記憶體資料庫;
- 流程執行代碼
package org.sd.processs;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.*;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.impl.form.DateFormType;
import org.activiti.engine.impl.form.StringFormType;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.Executors;
/**
* @author sword_happy
*/
@Slf4j
public class ProcessMain {
public static void main(String[] args) throws ParseException {
log.info("啟動我們的程式!");
//1 建立流程引擎,基于記憶體的資料庫處理
ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
ProcessEngine processEngine = cfg.buildProcessEngine();
String name = processEngine.getName();
String version = ProcessEngine.VERSION;
log.info("流程引擎名稱:[{}] 版本:[{}]", name, version);
//2 部署流程定義檔案
RepositoryService repositoryService = processEngine.getRepositoryService();
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
deploymentBuilder.addClasspathResource("ApprovalProcess.bpmn20.xml");
Deployment deployment = deploymentBuilder.deploy();
String deploymentId = deployment.getId();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deploymentId)
.singleResult();
log.info("流程定義檔案 [{}],流程ID [{}]", processDefinition.getName(), processDefinition.getId());
//3 啟動運作流程
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());
log.info("啟動流程 [{}]", processInstance.getProcessDefinitionKey());
//4 處理流程任務
Scanner scanner = new Scanner(System.in);
while (processInstance != null && !processInstance.isEnded()) {
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery().list();
System.out.println("共計[" + list.size() + "]個任務");
for (Task task : list) {
log.info("待處理任務 [{}]", task.getName());
FormService formService = processEngine.getFormService();
TaskFormData taskFormData = formService.getTaskFormData(task.getId());
List<FormProperty> formProperties = taskFormData.getFormProperties();
Map<String, Object> variables = Maps.newHashMap();
for (FormProperty formProperty : formProperties) {
String line = null;
if (StringFormType.class.isInstance(formProperty.getType())) {
log.info("請輸入 {} ?", formProperty.getName());
line = scanner.nextLine();
variables.put(formProperty.getId(), line);
} else if (DateFormType.class.isInstance(formProperty.getType())) {
log.info("請輸入 {} ? 格式 (yyyy-MM-dd)", formProperty.getName());
line = scanner.nextLine();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = simpleDateFormat.parse(line);
variables.put(formProperty.getId(), date);
} else {
log.info("類型暫不支援 {}", formProperty.getType());
}
log.info("您輸入的内容是 [{}] ?", line);
}
taskService.complete(task.getId(), variables);
processInstance = processEngine.getRuntimeService()
.createProcessInstanceQuery()
.processInstanceId(processInstance.getId())
.singleResult();
}
}
log.info("結束我們的程式!");
}
}
OK,到此為止,大功告成;
提示一下,這裡是基于H2的記憶體資料庫,是以構造對象的時候使用了Memory的方法;
後面的一些對象都是通過ProcessEngine進行擷取或者間接擷取的;==>意味着在Spring中,我們使用Activiti各種service的時候,隻需要添加@Autowired注解就可以直接使用;
過段時間我會再寫一篇基于SpringBoot的activiti6.0多級審批的部落格.
希望本篇部落格對你有幫助;