<Excerpt in index | 首页摘要>

最近做的一个项目中需要实现员工请假的功能,计划使用 activiti 来实现请假流程,所以去学习了一下相关的知识。并且完成了一个简单的请假实例。

<The rest of contents | 余下全文>

Activiti 相关

Activiti 介绍

Activiti 是基于 Apache 许可的开源 BPM 平台,创始人 Tom Baeyens 原是 JBPM 架构师,可以理解为与 JBPM 出自同一祖师爷。它提供了 Eclipse 插件,开发可以通过插件直接绘制业务流程图。基于 Spring,ibatis 等框架,并在此之上构建了非常清晰的开发框架。是由 Alfresco 软件发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理、工作流、服务协作等领域的一个开源的、灵活的、易扩展的可执行流程语言框架。 本文基于 Activiti7 的 Activiti Core,基于 Spring Boot 做简单学习总结。

Acticiti 核心类介绍

ProcessEngine

流程引擎的抽象,可以通过此类获取需要的所有服务。

Service 类

通过 ProcessEngine 获取,Activiti 将不同生命周期的服务封装在不同 Service 中,包括定义、部署、运行。通过服务类可获取相关生命周期中的服务信息。

taskService

流程运行过程中,每个任务节点的相关操作接口,如 complete,delete,delegate 等。

RepositoryService

流程定义和部署相关的存储服务

RuntimeService

流程运行时相关的服务,如根据流程好启动流程实例 startProcessInstanceByKey。

HistoryService

历史记录相关服务接口。

项目搭建

创建 SpringBoot 项目并且添加 maven 依赖

pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.0.0.Beta2</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

添加配置文件

application.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/test2?nullCatalogMeansCurrent=true
driver-class-name: com.mysql.cj.jdbc.Driver
devtools:
restart:
enabled: true #设置开启热部署
activiti:
database-schema-update: true
history-level: full
db-history-used: true


debug: true

创建流程图

在创建流程图之前首先需要安装 idea 支持 bpm 文件的插件

2

安装成功后在 resources 目录下创建 processes 文件夹并新建 holiday.bpmn 文件并且创建流程

对应 xml 文件代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?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="m1562573992349" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="myProcess_1" isClosed="false" isExecutable="true" processType="None">
<startEvent id="_2" name="StartEvent"/>
<userTask activiti:assignee="${userId}" activiti:exclusive="false" id="_3" name="填写申请单"/>
<userTask activiti:assignee="department" activiti:exclusive="true" id="_4" name="审核"/>
<endEvent id="_6" name="EndEvent"/>
<sequenceFlow id="_7" sourceRef="_2" targetRef="_3"/>
<sequenceFlow id="_8" sourceRef="_3" targetRef="_4"/>
<sequenceFlow id="_11" sourceRef="_4" targetRef="_6"/>
</process>
<bpmndi:BPMNDiagram 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" id="Diagram-_1" name="New Diagram">
<bpmndi:BPMNPlane bpmnElement="myProcess_1">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<omgdc:Bounds height="32.0" width="32.0" x="225.0" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
<omgdc:Bounds height="55.0" width="85.0" x="435.0" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
<omgdc:Bounds height="55.0" width="85.0" x="655.0" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
<omgdc:Bounds height="32.0" width="32.0" x="875.0" y="185.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_2" targetElement="_3">
<omgdi:waypoint x="257.0" y="191.0"/>
<omgdi:waypoint x="435.0" y="202.5"/>
<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="_3" targetElement="_4">
<omgdi:waypoint x="520.0" y="202.5"/>
<omgdi:waypoint x="655.0" y="202.5"/>
<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="_4" targetElement="_6">
<omgdi:waypoint x="740.0" y="202.5"/>
<omgdi:waypoint x="875.0" y="201.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>

运行 Application

启动后查看数据库发现 springboot 自动读取了 processes 下的 holiday.bpmn 文件并且部署任务流程到 activiti。数据库中生成了 25 张工作表。

并且在 ACT_GE_BYTEARRAY 表中也存在部署的信息

4

编写实例

开启请假流程

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void start() {
String instanceKey = "myProcess_1";
logger.info("开启请假流程...");
Map<String, Object> map = new HashMap<String, Object>();
//在holiday.bpmn中,填写请假单的任务办理人为动态传入的userId,此处模拟一个id
map.put("userId", "10001");
ProcessInstance instance = runtimeService.startProcessInstanceByKey(instanceKey, map);
logger.info("启动流程实例成功:{}", instance);
logger.info("流程实例ID:{}", instance.getId());
logger.info("流程定义ID:{}", instance.getProcessDefinitionId());

}

运行结果:

5

运行后成功开启了一个请假实例并且获取到 id 为:adc6a5f0-a2c3-11e9-bb83-00e04c83a7ff。

填写请假单

通过实例 id 可以查询到实例所对应的任务,模拟用户请假的内容存入 map 中添加为流程变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void employeeApply() {
String instanceId = "adc6a5f0-a2c3-11e9-bb83-00e04c83a7ff"; // 任务ID
String leaveDays = "10"; // 请假天数
String leaveReason = "回老家结婚"; // 请假原因
Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
if (task == null) {
logger.info("任务ID:{}查询到任务为空!", instanceId);
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("days", leaveDays);
map.put("date", new Date());
map.put("reason", leaveReason);
taskService.complete(task.getId(), map);
logger.info("执行【员工申请】环节,流程推动到【上级审核】环节");
}

运行结果如下:

6

流程变量查看

上级审核的时候需要查看到员工所提交的请假相关信息,可以通过 taskService.getVariable 的形式来获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void showTaskVariable (){
String instanceId = "adc6a5f0-a2c3-11e9-bb83-00e04c83a7ff"; // 任务ID

Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
String days = (String) taskService.getVariable(task.getId(), "days");
Date date = (Date) taskService.getVariable(task.getId(), "date");
String reason = (String) taskService.getVariable(task.getId(), "reason");
String userId = (String) taskService.getVariable(task.getId(), "userId");
System.out.println("请假天数: " + days);
System.out.println("请假理由: " + reason);
System.out.println("请假人id: " + userId);
System.out.println("请假日期: " + date.toString());
}

运行结果:

7

上级审批

上级审批并且添加意见

1
2
3
4
5
6
7
8
9
10
11
@Test
public void departmentAudit() {
String instanceId = "adc6a5f0-a2c3-11e9-bb83-00e04c83a7ff"; // 任务ID
String departmentalOpinion = "早去早回";
Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
Map<String, Object> map = new HashMap<String, Object>();
map.put("departmentalOpinion", departmentalOpinion);
taskService.complete(task.getId(), map);
logger.info("添加审批意见,请假流程结束");
}

运行结果:

8

总结

使用 Activiti 可以方便对流程进行控制。本文只是对简单的请假流程做了实例,没有涉及到多任务或多分支等情景。对于此类情况以后再做补充。