快速上手Activiti6.0工作流引擎
概念啥的就不扯了,网上大把资料.话不多说,直接切入正题,预完成一个员工请假流程,1天部门经理审核,3天以内(含3天)副总审核,3天到7天(含7天)总经理审核,7天以上其他渠道审核.
- demo结构图如上图所示,springboot+maven项目,整合Activiti6.0,pom依赖如下图所示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.dale</groupId>
<artifactId>activiti-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Activiti6.0已经整合到springboot了,所以上手起来顺畅的多.
-
在application.properties文件中做些许配置:
``
##spring database configuration spring.datasource.url=jdbc:mysql://172.10.3.150:3306/activiti?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull spring.datasource.username=root spring.datasource.password=Mysql_108 spring.datasource.driver-class-name=com.mysql.jdbc.Driver #activiti #自动检查、部署流程定义文件 spring.activiti.check-process-definitions=true #自动更新数据库结构 spring.activiti.database-schema-update=true #流程定义文件存放目录 spring.activiti.process-definition-location-prefix=classpath:/processes/
-
接下来运行一段代码,生成工作流所需的表,你得先在数据库里建好数据库activiti:
``
package cn.dale.activitidemo; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngineConfiguration; import org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration; /** * @Author: dale * @Date: 2019/11/5 17:26 */ public class ActivitiTable { public static void main(String[] args) { // 引擎配置 ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration() .setJdbcUrl("jdbc:mysql://172.10.3.150:3306/activiti?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull") .setJdbcUsername("root") .setJdbcPassword("Mysql_108") .setJdbcDriver("com.mysql.jdbc.Driver") .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); // 获取流程引擎对象 ProcessEngine processEngine = cfg.buildProcessEngine(); } }
-
run main()方法,表就建好了,接下来在idea添加一个叫actiBPM的插件,直接下载安装就好了,重启idea
-
在resources目录下新建process目录,右键新建bpmn文件—>leave_process.bpmn,通过拖拽右边的图形元素绘图,将鼠标停留在元素中部,出现黑点后可以画连线.
画图时要注意id不能重复,Name要想好怎么命名,一个项目会有很多图形元素需要命名,一定要起名有规律,后面要用到的.哪个红色中间带个×的叫排他网关,达到某种条件才能进入这个网关,需要人为设定,如下图所示:
这个也不好描述,直接上这个图对应的xml文件:
``
<?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="m1573013492978" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="start" isClosed="false" isExecutable="true" name="leave_process" processType="None">
<startEvent id="processstart" name="StartEvent"/>
<userTask activiti:assignee="${Applier}" activiti:exclusive="true" id="applier" name="Applier"/>
<userTask activiti:assignee="${DeptMng}" activiti:exclusive="true" id="dept_mng" name="DeptMng"/>
<sequenceFlow id="_5" sourceRef="processstart" targetRef="applier"/>
<sequenceFlow id="_6" sourceRef="applier" targetRef="dept_mng"/>
<exclusiveGateway gatewayDirection="Unspecified" id="exclusive_gateway1" name="ExclusiveGateway"/>
<userTask activiti:assignee="${ViceMng}" activiti:exclusive="true" id="vice_mng" name="ViceMng"/>
<userTask activiti:assignee="${GnlMng}" activiti:exclusive="true" id="gnl_mng" name="GnlMng"/>
<sequenceFlow id="_2" sourceRef="dept_mng" targetRef="exclusive_gateway1"/>
<sequenceFlow id="_3" name="tovicemng" sourceRef="exclusive_gateway1" targetRef="vice_mng">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptMngResult==1&&1<day&&day<=3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_4" name="tognlmng" sourceRef="exclusive_gateway1" targetRef="gnl_mng">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptMngResult==1&&3<day&&day<=7}]]></conditionExpression>
</sequenceFlow>
<userTask activiti:assignee="${special}" activiti:exclusive="true" id="special" name="special"/>
<sequenceFlow id="_8" name="tospecial" sourceRef="exclusive_gateway1" targetRef="special">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptMngResult==1&&day>7}]]></conditionExpression>
</sequenceFlow>
<exclusiveGateway gatewayDirection="Unspecified" id="_9" name="ExclusiveGateway"/>
<sequenceFlow id="_10" sourceRef="special" targetRef="_9"/>
<sequenceFlow id="_11" sourceRef="vice_mng" targetRef="_9"/>
<sequenceFlow id="_12" sourceRef="gnl_mng" targetRef="_9"/>
<endEvent id="end" name="EndEvent"/>
<sequenceFlow id="_14" name="success" sourceRef="_9" targetRef="end">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${result==1}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_7" name="faliure" sourceRef="_9" targetRef="applier">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${result==0}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_13" name="deptMngRejected" sourceRef="exclusive_gateway1" targetRef="applier">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptMngResult==0}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram documentation="background=#FFFFFF;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="start">
<bpmndi:BPMNShape bpmnElement="processstart" id="Shape-processstart">
<omgdc:Bounds height="32.0" width="32.0" x="265.0" y="10.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="applier" id="Shape-applier">
<omgdc:Bounds height="55.0" width="85.0" x="240.0" y="85.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="dept_mng" id="Shape-dept_mng">
<omgdc:Bounds height="55.0" width="85.0" x="245.0" y="180.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="exclusive_gateway1" id="Shape-exclusive_gateway1" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="265.0" y="270.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="vice_mng" id="Shape-vice_mng">
<omgdc:Bounds height="55.0" width="85.0" x="-10.0" y="365.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="gnl_mng" id="Shape-gnl_mng">
<omgdc:Bounds height="55.0" width="85.0" x="480.0" y="365.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="special" id="Shape-special">
<omgdc:Bounds height="55.0" width="85.0" x="245.0" y="460.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_9" id="Shape-_9" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="265.0" y="615.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="end" id="Shape-end">
<omgdc:Bounds height="32.0" width="32.0" x="265.0" y="710.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_13" id="BPMNEdge__13" sourceElement="exclusive_gateway1" targetElement="applier">
<omgdi:waypoint x="281.0" y="270.0"/>
<omgdi:waypoint x="281.0" y="140.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="gnl_mng" targetElement="_9">
<omgdi:waypoint x="480.0" y="392.5"/>
<omgdi:waypoint x="297.0" y="631.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_14" id="BPMNEdge__14" sourceElement="_9" targetElement="end">
<omgdi:waypoint x="281.0" y="647.0"/>
<omgdi:waypoint x="281.0" y="710.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_2" id="BPMNEdge__2" sourceElement="dept_mng" targetElement="exclusive_gateway1">
<omgdi:waypoint x="281.0" y="235.0"/>
<omgdi:waypoint x="281.0" y="270.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_3" id="BPMNEdge__3" sourceElement="exclusive_gateway1" targetElement="vice_mng">
<omgdi:waypoint x="265.0" y="286.0"/>
<omgdi:waypoint x="75.0" y="392.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_4" id="BPMNEdge__4" sourceElement="exclusive_gateway1" targetElement="gnl_mng">
<omgdi:waypoint x="297.0" y="286.0"/>
<omgdi:waypoint x="480.0" y="392.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_5" id="BPMNEdge__5" sourceElement="start" targetElement="applier">
<omgdi:waypoint x="281.0" y="42.0"/>
<omgdi:waypoint x="281.0" y="85.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="applier" targetElement="dept_mng">
<omgdi:waypoint x="285.0" y="140.0"/>
<omgdi:waypoint x="285.0" y="180.0"/>
<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="_9" targetElement="applier">
<omgdi:waypoint x="281.0" y="615.0"/>
<omgdi:waypoint x="281.0" y="140.0"/>
<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="exclusive_gateway1" targetElement="special">
<omgdi:waypoint x="281.0" y="302.0"/>
<omgdi:waypoint x="281.0" y="460.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="vice_mng" targetElement="_9">
<omgdi:waypoint x="75.0" y="392.5"/>
<omgdi:waypoint x="265.0" y="631.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="special" targetElement="_9">
<omgdi:waypoint x="281.0" y="515.0"/>
<omgdi:waypoint x="281.0" y="615.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>
要用的使用,把后缀名改成bpmn就行了.
-
跑一个测试用例,看能不能按照预先设定的逻辑执行:
``
package cn.dale.activitidemo; import cn.dale.activitidemo.entity.Employee; import cn.dale.activitidemo.task.ActivitiTask; import org.activiti.engine.*; import org.activiti.engine.impl.persistence.entity.ExecutionEntity; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.transaction.annotation.Transactional; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Author: dale * @Date: 2019/11/5 17:59 */ @RunWith(SpringRunner.class) @WebAppConfiguration @SpringBootTest @Transactional @Rollback(false) //@Ignore public class ActiTest { @Autowired private RuntimeService runtimeService; @Autowired private IdentityService identityService; @Autowired private TaskService taskService; @Autowired private RepositoryService repositoryService; //初始化一个要请假的员工 private static final Employee emp = new Employee("zs", 8); @Test public void testInitProcess() { System.out.println("method startActivityDemo begin...."); System.out.println("调用流程存储服务,已部署流程数量:" + repositoryService.createDeploymentQuery().count()); Map<String, Object> map = new HashMap<String, Object>(); // 流程图里写的${user} ,这里传进去user map.put("Applier", emp.getEmployeeName()); map.put("DeptMng", "ls"); map.put("ViceMng", "ww"); map.put("GnlMng", "zl"); map.put("special", "sq"); // 指定流程的发起者 不指定发起者的字段就为空,注意跟审核人分开 identityService.setAuthenticatedUserId(emp.getEmployeeName()); //流程启动 ExecutionEntity pi = (ExecutionEntity) runtimeService.startProcessInstanceByKey("start", map); String processInstanceId = pi.getId(); System.out.println("流程开启成功,流程实例id:" + processInstanceId); ActivitiTask at = new ActivitiTask(taskService); //开始进行流程 while (runtimeService .createProcessInstanceQuery()//获取查询对象 .processInstanceId(processInstanceId)//根据id查询流程实例 .singleResult()//获取查询结果,如果为空,说明这个流程已经执行完毕,否则,获取任务并执行 != null) { Task task = taskService.createTaskQuery()//创建查询对象 .processInstanceId(processInstanceId)//通过流程实例id来查询当前任务 .singleResult();//获取单个查询结果 String taskName = task.getName(); System.out.println("任务ID: " + task.getId()); System.out.println("任务的办理人: " + task.getAssignee()); System.out.println("任务名称: " + taskName); System.out.println("任务的创建时间: " + task.getCreateTime()); System.out.println("流程实例ID: " + task.getProcessInstanceId()); switch (taskName) { case "Applier": //职员节点 at.completeEmployeeTask(task, emp); break; case "DeptMng": //部门经理节点 at.completeLeaderTask(task, emp); break; default: //经理节点 at.completeJingliTask(task, emp); break; } System.out.println(emp.getApplyStatus().getName()); } System.out.println("method startActivityDemo end...."); } }
ActivitiTask代码:
package cn.dale.activitidemo.task;
import cn.dale.activitidemo.entity.Employee;
import cn.dale.activitidemo.enums.ApplyStatusEnum;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: dale
* @Date: 2019/11/6 15:38
*/
public class ActivitiTask {
private TaskService taskService;
public ActivitiTask(TaskService taskService) {
this.taskService = taskService;
}
//职员提交申请
public void completeEmployeeTask(Task task, Employee emp){
//获取任务id
String taskId = task.getId();
//完成任务
taskService.complete(taskId);
emp.setApplyStatus(ApplyStatusEnum.AUDITING);
System.out.println("职员已经提交申请.......");
}
//领导审批
public void completeLeaderTask(Task task, Employee emp){
//获取任务id
String taskId = task.getId();
//领导意见
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("day",emp.getDay());
variables.put("deptMngResult", 1);
emp.setApplyStatus(ApplyStatusEnum.AUDITING);
//完成任务
taskService.complete(taskId, variables);
System.out.println("领导审核完毕........");
}
//经理审批
public void completeJingliTask(Task task, Employee emp){
//获取任务id
String taskId = task.getId();
String name = task.getName();
//经理意见
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("day",emp.getDay());
// variables.put("result", 0);
// emp.setApplyStatus(ApplyStatusEnum.NO);
variables.put("result", 1);
emp.setApplyStatus(ApplyStatusEnum.YES);
//完成任务
taskService.complete(taskId, variables);
System.out.println("经理审核完毕........,审核经理:"+name);
}
}
请假被sq拒绝后的运行结果:
可以看到流程一直循环执行,因为到最后一环被拒绝了,现在我让sq同意请假申请,结果如下图:
这样,一个8天的请假流程就顺利走完了.简单的一个案例,当然Activiti还有满足不同场景需求的更多的Api,这里就不一一列举,大家根据自己项目的实际需要去学习.谢谢观看,如有不合理之处,还请指正.