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多级审批的博客.
希望本篇博客对你有帮助;