Activiti5入门--HelloWorld程序演示流程执行

通过学习上一篇的内容,我们已经完成了项目的初始化。Activiti流程引擎所需要的23张数据表已经在数据库中创建成功,在这一篇中,我们将通过HelloWorld示例模拟流程的运行过程,初步体会流程的执行过程和框架核心API的使用。

本篇将分为两个部分,第一部分是绘制流程图,第二部分通过代码模拟一个完整流程的运行过程。

绘制流程图

创建bpmn文件

继续上一篇创建的工程,选中项目src/main/resources目录下的包diagrams,右键New->Other…->Activiti,选择创建Activiti Diagram,将文件命名为helloworld,然后,按照一般步骤点击直到结束。

这样我们就创建好了一个helloworld.bpmn文件。

绘制流程

打开helloworld.bpmn文件,可以在IDE右侧看到如下面板,遵循如下步骤创建流程图

Activiti面板

  1. 拖拽Start event下的StartEvent,创建开始节点

  2. 拖拽End event下的EndEvent,创建结束节点

  3. 拖拽Task下的UserTask,创建任务节点

  4. 选择IDE顶部菜单Window->Show View->Properties,此时会打开一个界面,这个界面有助于我们编辑这些节点的属性,对于不同节点,Properties面板里的内容会有一定的差异,这是未选中节点时流程图的全局Propertis配置项界面

    properties面板

  5. 选择任务节点时的界面

    任务节点配置

  6. 编辑任务节点,设置任务节点名称

    • 选择刚才创建的任务节点,将Name改为提交申请
    • 再创建一个任务节点,将Name改为审批【部门经理】
    • 最后创建一个任务节点,将Name改为审批【总经理】
  7. 找到右侧Activiti面板里Connection->SequenceFlow,将各个节点从开始节点到结束节点依次连接起来,如下图所示

    流程图

  8. 编辑任务节点,设置每个任务节点的办理人,在Properties界面里的Main config->Assignee里设置

    • 提交申请任务节点的Assignee为张三
    • 审批【部门经理】任务节点的Assignee为李四
    • 审批【总经理】任务节点的Assignee为王五
  9. 点击空白处,此时出现全局Propertis配置项界面

    • 设置Process->Idhelloworld
    • 设置Process->NamehelloworldProcess
  10. 点击保存,即会生成对应流程图的图片文件

使用XML方式打开bpmn文件,可以看到如下代码

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
<?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="helloworld" name="helloworldProcess" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<endEvent id="endevent1" name="End"></endEvent>
<userTask id="usertask1" name="提交申请" activiti:assignee="张三"></userTask>
<userTask id="usertask2" name="审批【部门经理】" activiti:assignee="李四"></userTask>
<userTask id="usertask3" name="审批【总经理】" activiti:assignee="王五"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow>
<sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_helloworld">
<bpmndi:BPMNPlane bpmnElement="helloworld" id="BPMNPlane_helloworld">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="390.0" y="50.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="390.0" y="380.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="355.0" y="120.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0" x="355.0" y="200.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
<omgdc:Bounds height="55.0" width="105.0" x="355.0" y="290.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="407.0" y="85.0"></omgdi:waypoint>
<omgdi:waypoint x="407.0" y="120.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="407.0" y="175.0"></omgdi:waypoint>
<omgdi:waypoint x="407.0" y="200.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="407.0" y="255.0"></omgdi:waypoint>
<omgdi:waypoint x="407.0" y="290.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="407.0" y="345.0"></omgdi:waypoint>
<omgdi:waypoint x="407.0" y="380.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>

可以看到,该XML代码描述的过程和bpmn文件所图示的内容是一致的

模拟流程执行

在绘画完流程图后,此时,可以写代码模拟流程运行了。在src/main/java下新建包cn.demo.helloworld,并创建类HelloWorld

该流程执行的主要步骤有

  1. 部署流程定义
  2. 启动流程实例
  3. 查询当前人的个人任务
  4. 完成个人的任务

下面来看一下这些步骤的相关代码

部署流程定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 部署流程定义
*/
@Test
public void deploymentProcessDefinition() {
// 存在于act_re_deployment表
Deployment deploy = processEngine.getRepositoryService() // 与流程定义和部署对象相关的Service
.createDeployment() // 创建一个部署对象
.name("helloworld入门程序") // 添加部署的名称
.addClasspathResource("diagrams/helloworld.bpmn") // 从classpath的资源中加载,一次只能加载一个文件
.addClasspathResource("diagrams/helloworld.png").deploy(); // 完成部署
System.out.println("部署ID: " + deploy.getId());
System.out.println("部署名称: " + deploy.getName());
}

对于一个流程来说,首先需要做的就是部署流程定义,而流程部署和定义需要使用repositoryService,通过该服务,我们可以获得一个部署对象,获取流程定义相关信息

启动流程实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 启动流程实例
*/
@Test
public void startProcessInstance() {
// 流程定义的key
String processDefinitionKey = "helloworld";
ProcessInstance processInstance = processEngine.getRuntimeService() // 与正在执行的流程实例和执行对象相关的Service
.startProcessInstanceByKey(processDefinitionKey); // 使用流程定义的key启动实例,key对应helloworld.bpmn文件中Id的属性值,默认是按照最新版本的流程定义启动
System.out.println("流程实例ID: " + processInstance.getId()); // 流程实例Id
// 对应act_ru_execution表中的ID
// 101
System.out.println("流程定义ID: " + processInstance.getProcessDefinitionId()); // 流程定义Id
// 对应act_re_procdef表中的ID / // helloworld:1:4
}

部署完流程之后,就可以使用runtimeService启动一个具体流程实例

查询当前人的个人任务

流程在不断的执行,当流程执行到当前人的时候,就可以查询到当前人的个人任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 查询当前人的个人任务
*/
@Test
public void findMyPersonalTask() {
String assignee = "张三";
List<Task> list = processEngine.getTaskService() // 与正在执行的任务管理相关的Service
.createTaskQuery() // 创建任务对象
.taskAssignee(assignee) // 指定个人任务,指定办理人
.list();
// 存储在act_ru_task表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("任务的创建时间: " + task.getCreateTime());
System.out.println("任务的办理人: " + task.getAssignee());
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("执行对象ID: " + task.getExecutionId());
System.out.println("流程定义ID: " + task.getProcessDefinitionId());
System.out.println("#######################################");
}
}
}

完成个人的任务

1
2
3
4
5
6
7
8
9
/**
* 完成我的任务
*/
@Test
public void completeMyPersonalTask() {
String taskId = "104";
processEngine.getTaskService().complete(taskId);
System.out.println("完成任务:任务Id:" + taskId);
}

当我们查询到当前我们有任务时,可以对该任务进行处理。当处理完毕之后,流程就执行到下一步了。当流程全部步骤执行完成后,会存储到历史表中

完整代码

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
72
73
74
75
76
77
78
79
80
81
82
83
package cn.demo.helloworld;
import java.util.List;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
public class HelloWorld {
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
/**
* 部署流程定义
*/
@Test
public void deploymentProcessDefinition() {
// 存在于act_re_deployment表
Deployment deploy = processEngine.getRepositoryService() // 与流程定义和部署对象相关的Service
.createDeployment() // 创建一个部署对象
.name("helloworld入门程序") // 添加部署的名称
.addClasspathResource("diagrams/helloworld.bpmn") // 从classpath的资源中加载,一次只能加载一个文件
.addClasspathResource("diagrams/helloworld.png").deploy(); // 完成部署
System.out.println("部署ID: " + deploy.getId());
System.out.println("部署名称: " + deploy.getName());
}
/**
* 启动流程实例
*/
@Test
public void startProcessInstance() {
// 流程定义的key
String processDefinitionKey = "helloworld";
ProcessInstance processInstance = processEngine.getRuntimeService() // 与正在执行的流程实例和执行对象相关的Service
.startProcessInstanceByKey(processDefinitionKey); // 使用流程定义的key启动实例,key对应helloworld.bpmn文件中Id的属性值,默认是按照最新版本的流程定义启动
System.out.println("流程实例ID: " + processInstance.getId()); // 流程实例Id
// 对应act_ru_execution表中的ID
// 101
System.out.println("流程定义ID: " + processInstance.getProcessDefinitionId()); // 流程定义Id
// 对应act_re_procdef表中的ID //helloworld:1:4
}
/**
* 查询当前人的个人任务
*/
@Test
public void findMyPersonalTask() {
String assignee = "张三";
List<Task> list = processEngine.getTaskService() // 与正在执行的任务管理相关的Service
.createTaskQuery() // 创建任务对象
.taskAssignee(assignee) // 指定个人任务,指定办理人
.list();
// 存储在act_ru_task表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("任务的创建时间: " + task.getCreateTime());
System.out.println("任务的办理人: " + task.getAssignee());
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("执行对象ID: " + task.getExecutionId());
System.out.println("流程定义ID: " + task.getProcessDefinitionId());
System.out.println("#######################################");
}
}
}
/**
* 完成我的任务
*/
@Test
public void completeMyPersonalTask() {
String taskId = "104";
processEngine.getTaskService().complete(taskId);
System.out.println("完成任务:任务Id:" + taskId);
}
}

演示流程任务执行

根据上面所绘制的流程图步骤,如下操作

  1. 首先,设置当前人的任务的assignee为张三并执行查询当前人的任务方法,得到张三的任务ID
  2. 将当前人的任务ID带入并执行完成个人任务方法
  3. 然后将步骤1中的assignee设置为李四,得到李四的任务ID,将李四的任务ID带入并执行步骤2
  4. 最后将assignee设置为王五,得到王五的任务ID,将王五的任务ID带入并执行步骤2
  5. 当以上步骤都完成时,就完成了模拟流程任务执行的过程
当前人 任务ID
张三 104
李四 202
王五 302

总结

通过上面的例子,可以总结出一般流程执行的主要步骤

  1. 部署流程定义
  2. 启动流程实例
  3. 查询当前人的个人任务
  4. 完成个人的任务
  5. 重复执行3、4步,直到满足条件结束