使用activiti框架,首先要創建bpmn流程圖,這裏有兩種選擇,一種是ide自帶的插件(eclipse就不說了,網上各種說好用的;idea是真心難用,而且是好幾年沒更新的插件了),一種是用activiti官方提供的工具activiti-app(6.0的版本,我們只用它來畫圖),把activiti-app.war放在tomcat運行。
1、繪製流程圖
- 訪問地址:http://localhost:8080/activiti-app
- 賬號:admin
- 密碼:test
先創建一個流程
創建一個請假流程
流程圖如下:
關鍵屬性:
- 每個矩形框就是一個userTask,最關鍵的屬性:Assignments 任務指定人
因爲沒有使用activiti的identity(認證)部分,所有我選擇動態傳入一個參數進去(動態變量名不能重複): - 每個連接線就是一個sequenceFlow,最關鍵的屬性是:Flow condition 順序流條件;可以設置條件表達式;這裏分支的變量名必須一致,比如審覈通過爲“{audit==1”}
2、導入流程圖
將畫好的流程圖下載到本地,在processes目錄下新建文件(leave.bpmn20.xml),將流程圖用文本打開復制進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/processdef">
<process id="leave" name="請假流程" isExecutable="true">
<startEvent id="startEvent1" name="開始"></startEvent>
<userTask id="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA" name="填寫申請" activiti:assignee="${user}"><!--activiti:assignee 任務指定人-->
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-782B5EB0-C3E4-4EA8-B47C-001493E8CFAA" sourceRef="startEvent1" targetRef="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA"></sequenceFlow>
<userTask id="sid-F30892E7-AD40-44CE-8FE1-911564290536" name="領導批准" activiti:assignee="${approve}">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-9BAB7E50-7E04-4C87-8448-ECDF79CB4405" sourceRef="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA" targetRef="sid-F30892E7-AD40-44CE-8FE1-911564290536"></sequenceFlow>
<exclusiveGateway id="sid-C7396408-A226-4385-9E87-C63DA1295BEE"></exclusiveGateway>
<sequenceFlow id="sid-4570FB2C-F000-4EB9-961B-795DFD3CD037" sourceRef="sid-F30892E7-AD40-44CE-8FE1-911564290536" targetRef="sid-C7396408-A226-4385-9E87-C63DA1295BEE"></sequenceFlow>
<endEvent id="sid-E8EA0FBB-EAE1-4316-BAAE-1465DD35D4EA" name="結束"></endEvent>
<!--sequenceFlow 流程分支-->
<sequenceFlow id="sid-715DC950-61C4-4AA8-9662-A6FD71611EAA" name="審覈不通過" sourceRef="sid-C7396408-A226-4385-9E87-C63DA1295BEE" targetRef="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${audit==0}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="sid-EA1D4887-5497-43DE-9D57-A03D3B719681" name="審覈通過" sourceRef="sid-C7396408-A226-4385-9E87-C63DA1295BEE" targetRef="sid-E8EA0FBB-EAE1-4316-BAAE-1465DD35D4EA">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${audit==1}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane bpmnElement="leave" id="BPMNPlane_leave">
<bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
<omgdc:Bounds height="30.0" width="30.0" x="90.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA" id="BPMNShape_sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA">
<omgdc:Bounds height="80.0" width="100.0" x="165.0" y="125.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-F30892E7-AD40-44CE-8FE1-911564290536" id="BPMNShape_sid-F30892E7-AD40-44CE-8FE1-911564290536">
<omgdc:Bounds height="80.0" width="100.0" x="300.0" y="125.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-C7396408-A226-4385-9E87-C63DA1295BEE" id="BPMNShape_sid-C7396408-A226-4385-9E87-C63DA1295BEE">
<omgdc:Bounds height="40.0" width="40.0" x="450.0" y="145.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-E8EA0FBB-EAE1-4316-BAAE-1465DD35D4EA" id="BPMNShape_sid-E8EA0FBB-EAE1-4316-BAAE-1465DD35D4EA">
<omgdc:Bounds height="28.0" width="28.0" x="540.0" y="151.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sid-782B5EB0-C3E4-4EA8-B47C-001493E8CFAA" id="BPMNEdge_sid-782B5EB0-C3E4-4EA8-B47C-001493E8CFAA">
<omgdi:waypoint x="120.0" y="165.0"></omgdi:waypoint>
<omgdi:waypoint x="165.0" y="165.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-9BAB7E50-7E04-4C87-8448-ECDF79CB4405" id="BPMNEdge_sid-9BAB7E50-7E04-4C87-8448-ECDF79CB4405">
<omgdi:waypoint x="265.0" y="165.0"></omgdi:waypoint>
<omgdi:waypoint x="300.0" y="165.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-4570FB2C-F000-4EB9-961B-795DFD3CD037" id="BPMNEdge_sid-4570FB2C-F000-4EB9-961B-795DFD3CD037">
<omgdi:waypoint x="400.0" y="165.20746887966806"></omgdi:waypoint>
<omgdi:waypoint x="450.4166666666667" y="165.41666666666666"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-715DC950-61C4-4AA8-9662-A6FD71611EAA" id="BPMNEdge_sid-715DC950-61C4-4AA8-9662-A6FD71611EAA">
<omgdi:waypoint x="470.5" y="145.5"></omgdi:waypoint>
<omgdi:waypoint x="470.5" y="26.0"></omgdi:waypoint>
<omgdi:waypoint x="215.0" y="26.0"></omgdi:waypoint>
<omgdi:waypoint x="215.0" y="125.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-EA1D4887-5497-43DE-9D57-A03D3B719681" id="BPMNEdge_sid-EA1D4887-5497-43DE-9D57-A03D3B719681">
<omgdi:waypoint x="489.6144578313253" y="165.3855421686747"></omgdi:waypoint>
<omgdi:waypoint x="540.0002509882663" y="165.0838308324056"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
3、配置application.yml
spring:
datasource:
#數據源基本配置
username: root
password: root
url: jdbc:mysql://localhost/activiti?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&nullCatalogMeansCurrent=true&useSSL=false&useLegacyDatetimeCode=false
driver-class-name: com.mysql.cj.jdbc.Driver
activiti:
# 開啓歷史庫
db-history-used: true
history-level: audit
歷史信息級別可以配置成以下幾種(activiti7感覺默認的是none):
none: 忽略所有歷史存檔。這是流程執行時性能最好的狀態,但沒有任何歷史信息可用。
activity: 保存所有流程實例信息和活動實例信息。 在流程實例結束時, 最後一個流程實例中的最新的變量值將賦值給歷史變量。 不會保存過程中的詳細信息。
audit: 它保存所有流程實例信息, 活動信息, 保證所有的變量和提交的表單屬性保持同步 這樣所有用戶交互信息都是可追溯的,可以用來審計。
full: 這個是最高級別的歷史信息存檔,同樣也是最慢的。 這個級別存儲發生在審覈以及所有其它細節的信息, 主要是更新流程變量。
4.開啓一個任務
/**
* 開啓一個請假流程
* @param user 用戶key
* @param processDefinitionKey 流程圖key 每一個流程有對應的一個key這個是某一個流程內固定的寫在bpmn內的
*/
void startLeaveProcess(String user,String processDefinitionKey){
System.out.println(user+"開啓一個請假流程:"+ processDefinitionKey);
HashMap<String, Object> variables=new HashMap<>();
variables.put("user", user);//userKey在上文的流程變量中指定了
ProcessInstance instance = runtimeService
.startProcessInstanceByKey(processDefinitionKey,variables);
System.out.println("流程實例ID:"+instance.getId());
System.out.println("流程定義ID:"+instance.getProcessDefinitionId());
System.out.println("==================================================================");
}
運行結果:
張三開啓一個請假流程:leave
流程實例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6
流程定義ID:leave:1:32f7bc77-0d07-11ea-b319-9c5c8e7034f6
==================================================================
注意數據庫的變化:
mysql> select ID_,REV_,PROC_INST_ID_,NAME_,ASSIGNEE_ from act_ru_task;
+--------------------------------------+------+--------------------------------------+----------+-----------+
| ID_ | REV_ | PROC_INST_ID_ | NAME_ | ASSIGNEE_ |
+--------------------------------------+------+--------------------------------------+----------+-----------+
| 34f8958d-0d07-11ea-b319-9c5c8e7034f6 | 1 | 34f2f038-0d07-11ea-b319-9c5c8e7034f6 | 填寫申請 | 張三 |
+--------------------------------------+------+--------------------------------------+----------+-----------+
1 row in set
5、根據執行人查詢當前流程
/**
* 查詢當前任務流程
*/
void queryLeaveProcessING(String assignee){
System.out.println(assignee+"查詢自己當前的流程:");
List<Task> list = taskService.createTaskQuery()//創建任務查詢對象
.taskAssignee(assignee)//指定個人任務查詢
.list();
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());
Map<String, Object> map = task.getProcessVariables();
for (Map.Entry<String, Object> m : map.entrySet()) {
System.out.println("key:" + m.getKey() + " value:" + m.getValue());
}
for (Map.Entry<String, Object> m : task.getTaskLocalVariables().entrySet()) {
System.out.println("key:" + m.getKey() + " value:" + m.getValue());
}
}
}
System.out.println("==================================================================");
}
運行結果:
張三查詢自己當前的流程:
任務ID:34f8958d-0d07-11ea-b319-9c5c8e7034f6
任務名稱:填寫申請
任務的創建時間:Fri Nov 22 17:05:19 CST 2019
任務的辦理人:張三
流程實例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6
執行對象ID:34f3b38a-0d07-11ea-b319-9c5c8e7034f6
流程定義ID:leave:1:32f7bc77-0d07-11ea-b319-9c5c8e7034f6
==================================================================
6、提交給領導審覈
void completeTask(String approve,String taskId){
System.out.println(approve+":提交自己的流程:"+taskId);
//任務ID
HashMap<String, Object> variables=new HashMap<>();
variables.put("approve", approve);//userKey在上文的流程變量中指定了
taskService.complete(taskId,variables);
System.out.println("完成任務:任務ID:"+taskId);
System.out.println("==================================================================");
}
運行結果:
領導李四:提交自己的流程:34f8958d-0d07-11ea-b319-9c5c8e7034f6
完成任務:任務ID:34f8958d-0d07-11ea-b319-9c5c8e7034f6
==================================================================
數據庫:
mysql> select ID_,REV_,PROC_INST_ID_,NAME_,ASSIGNEE_ from act_ru_task;
+--------------------------------------+------+--------------------------------------+----------+-----------+
| ID_ | REV_ | PROC_INST_ID_ | NAME_ | ASSIGNEE_ |
+--------------------------------------+------+--------------------------------------+----------+-----------+
| e60702be-0d08-11ea-8a0a-9c5c8e7034f6 | 1 | 34f2f038-0d07-11ea-b319-9c5c8e7034f6 | 領導批准 | 領導李四 |
+--------------------------------------+------+--------------------------------------+----------+-----------+
1 row in set
7、領導審覈通過
void completeTask(String user,String taskId,int audit){
System.out.println(user+":提交自己的流程:"+taskId+" ;是否通過:"+audit);
//任務ID
HashMap<String, Object> variables=new HashMap<>();
variables.put("audit", audit);//userKey在上文的流程變量中指定了
taskService.complete(taskId,variables);
System.out.println("完成任務:任務ID:"+taskId);
System.out.println("==================================================================");
}
運行結果:
領導李四:提交自己的流程:e60702be-0d08-11ea-8a0a-9c5c8e7034f6 ;是否通過:1
完成任務:任務ID:e60702be-0d08-11ea-8a0a-9c5c8e7034f6
==================================================================
數據庫:
mysql> select ID_,REV_,PROC_INST_ID_,NAME_,ASSIGNEE_ from act_ru_task;
Empty set
8、查詢歷史數據
根據流程ID查詢:
List<HistoricTaskInstance> list=historyService // 歷史相關Service
.createHistoricTaskInstanceQuery() // 創建歷史活動實例查詢
.processInstanceId("34f2f038-0d07-11ea-b319-9c5c8e7034f6") // 執行流程實例id
.orderByTaskCreateTime()
.asc()
.list();
for(HistoricTaskInstance hai:list){
System.out.println("活動ID:"+hai.getId());
System.out.println("流程實例ID:"+hai.getProcessInstanceId());
System.out.println("活動名稱:"+hai.getName());
System.out.println("辦理人:"+hai.getAssignee());
System.out.println("開始時間:"+hai.getStartTime());
System.out.println("結束時間:"+hai.getEndTime());
System.out.println("==================================================================");
}
運行結果:
活動ID:34f8958d-0d07-11ea-b319-9c5c8e7034f6
流程實例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6
活動名稱:填寫申請
辦理人:張三
開始時間:Fri Nov 22 17:05:19 CST 2019
結束時間:Fri Nov 22 17:17:25 CST 2019
==================================================================
活動ID:e60702be-0d08-11ea-8a0a-9c5c8e7034f6
流程實例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6
活動名稱:領導批准
辦理人:領導李四
開始時間:Fri Nov 22 17:17:25 CST 2019
結束時間:Fri Nov 22 17:31:03 CST 2019
==================================================================
查詢個人歷史記錄:
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee("張三").orderByTaskCreateTime().asc().list();
for(HistoricTaskInstance hai:list){
System.out.println("活動ID:"+hai.getId());
System.out.println("流程實例ID:"+hai.getProcessInstanceId());
System.out.println("活動名稱:"+hai.getName());
System.out.println("辦理人:"+hai.getAssignee());
System.out.println("開始時間:"+hai.getStartTime());
System.out.println("結束時間:"+hai.getEndTime());
System.out.println("==================================================================");
}
運行結果:
活動ID:34f8958d-0d07-11ea-b319-9c5c8e7034f6
流程實例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6
活動名稱:填寫申請
辦理人:張三
開始時間:Fri Nov 22 17:05:19 CST 2019
結束時間:Fri Nov 22 17:17:25 CST 2019
==================================================================
數據庫:
mysql> select ID_,PROC_INST_ID_,NAME_,ASSIGNEE_ from act_hi_taskinst;
+--------------------------------------+--------------------------------------+----------+-----------+
| ID_ | PROC_INST_ID_ | NAME_ | ASSIGNEE_ |
+--------------------------------------+--------------------------------------+----------+-----------+
| 34f8958d-0d07-11ea-b319-9c5c8e7034f6 | 34f2f038-0d07-11ea-b319-9c5c8e7034f6 | 填寫申請 | 張三 |
| e60702be-0d08-11ea-8a0a-9c5c8e7034f6 | 34f2f038-0d07-11ea-b319-9c5c8e7034f6 | 領導批准 | 領導李四 |
+--------------------------------------+--------------------------------------+----------+-----------+
2 rows in set
9.完整的測試類代碼
package com.example.activitidemo2;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SpringBootTest
class ActivitiDemo2ApplicationTests {
@Resource
RepositoryService repositoryService;
@Resource
RuntimeService runtimeService;
@Resource
TaskService taskService;
@Resource
HistoryService historyService;
@Test
void contextLoads() {
System.out.println("Number of process definitions : "
+ repositoryService.createProcessDefinitionQuery().count());
System.out.println("Number of tasks : " + taskService.createTaskQuery().count());
runtimeService.startProcessInstanceByKey("oneTaskProcess");
System.out.println("Number of tasks after process start: " + taskService.createTaskQuery().count());
}
@Test
void testProcess(){
//張三開啓一個請假流程
String user = "張三";
String approve = "領導李四";
// startLeaveProcess(user,"leave");
//張三查詢自己流程
// queryLeaveProcessING(user);
// 提交給領導李四審覈
// String taskId = "34f8958d-0d07-11ea-b319-9c5c8e7034f6";
// completeTask(approve,taskId);
//領導李四查詢自己的流程
// queryLeaveProcessING(approve);
//李四提交自己的流程
completeTask(approve,"e60702be-0d08-11ea-8a0a-9c5c8e7034f6",1);
//張三查詢自己的歷史流程
// queryHistoryTask(userKey);
}
/**
* 開啓一個請假流程
* @param user 用戶key
* @param processDefinitionKey 流程圖key 每一個流程有對應的一個key這個是某一個流程內固定的寫在bpmn內的
*/
void startLeaveProcess(String user,String processDefinitionKey){
System.out.println(user+"開啓一個請假流程:"+ processDefinitionKey);
HashMap<String, Object> variables=new HashMap<>();
variables.put("user", user);//userKey在上文的流程變量中指定了
ProcessInstance instance = runtimeService
.startProcessInstanceByKey(processDefinitionKey,variables);
System.out.println("流程實例ID:"+instance.getId());
System.out.println("流程定義ID:"+instance.getProcessDefinitionId());
System.out.println("==================================================================");
}
/**
* 查詢當前任務流程
*/
void queryLeaveProcessING(String assignee){
System.out.println(assignee+"查詢自己當前的流程:");
List<Task> list = taskService.createTaskQuery()//創建任務查詢對象
.taskAssignee(assignee)//指定個人任務查詢
.list();
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());
Map<String, Object> map = task.getProcessVariables();
for (Map.Entry<String, Object> m : map.entrySet()) {
System.out.println("key:" + m.getKey() + " value:" + m.getValue());
}
for (Map.Entry<String, Object> m : task.getTaskLocalVariables().entrySet()) {
System.out.println("key:" + m.getKey() + " value:" + m.getValue());
}
}
}
System.out.println("==================================================================");
}
@Test
void completeTask(String approve,String taskId){
System.out.println(approve+":提交自己的流程:"+taskId);
//任務ID
HashMap<String, Object> variables=new HashMap<>();
variables.put("approve", approve);//userKey在上文的流程變量中指定了
taskService.complete(taskId,variables);
System.out.println("完成任務:任務ID:"+taskId);
System.out.println("==================================================================");
}
@Test
void completeTask(String user,String taskId,int audit){
System.out.println(user+":提交自己的流程:"+taskId+" ;是否通過:"+audit);
//任務ID
HashMap<String, Object> variables=new HashMap<>();
variables.put("audit", audit);//userKey在上文的流程變量中指定了
taskService.complete(taskId,variables);
System.out.println("完成任務:任務ID:"+taskId);
System.out.println("==================================================================");
}
@Test
void queryHistoryTask(){
List<HistoricTaskInstance> list=historyService // 歷史相關Service
.createHistoricTaskInstanceQuery() // 創建歷史活動實例查詢
.processInstanceId("34f2f038-0d07-11ea-b319-9c5c8e7034f6") // 執行流程實例id
.orderByTaskCreateTime()
.asc()
.list();
for(HistoricTaskInstance hai:list){
System.out.println("活動ID:"+hai.getId());
System.out.println("流程實例ID:"+hai.getProcessInstanceId());
System.out.println("活動名稱:"+hai.getName());
System.out.println("辦理人:"+hai.getAssignee());
System.out.println("開始時間:"+hai.getStartTime());
System.out.println("結束時間:"+hai.getEndTime());
System.out.println("==================================================================");
}
// List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee("張三").orderByTaskCreateTime().asc().list();
// for(HistoricTaskInstance hai:list){
// System.out.println("活動ID:"+hai.getId());
// System.out.println("流程實例ID:"+hai.getProcessInstanceId());
// System.out.println("活動名稱:"+hai.getName());
// System.out.println("辦理人:"+hai.getAssignee());
// System.out.println("開始時間:"+hai.getStartTime());
// System.out.println("結束時間:"+hai.getEndTime());
// System.out.println("==================================================================");
// }
}
}