[TOC]
*****
> 本節我們介紹Activiti提供的 7大Service接口。先了解這7個service的用途,有利于我們更好的學習activiti。
我們不會專門介紹這幾個Service接口的所有API方法,而是通過實例的方式直接使用這些API,在實踐中熟悉這些API。此處貼出官方的javadocs文檔地址:[https://www.activiti.org/javadocs/](https://www.activiti.org/javadocs/)

## 1、流程模型
我們通過使用下面這個請假流程模型來練習這7個Activiti Service。
### 1.1 流程圖
流程節點的具體內容,本章節我們不介紹如何畫流程圖,而是先以一個實例來熟悉一下Activiti的使用過程。

簡要介紹一下,以上流程圖的使用場景,這是一個請假流程:
* 請假當事人發起流程后,流程流轉到`部門經理審批`節點,部門經理選擇同意,則流轉到`人事審批`節點,否則流轉到`調整申請`節點,當事人可以在`調整申請`節點修改請假信息,重新申請流程或者結束流程。
* 當部門經理同意后,流程到達`人事審批`節點,這個節點的操作和`部門經理審批`節點邏輯是一樣的。
* 當人事經理審批同意后,流程流轉到銷假節點,當事人在這個節點處理銷假信息,然后結束流程。
### 1.2 流程定義
以下是上面流程圖對應的定義文件。(省略部分位置信息,初學看不懂沒關系,了解工作流程就行)
```
<?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.kafeitu.me/activiti/leave">
<process id="leave" name="請假流程-普通表單" isExecutable="true">
<documentation>請假流程演示</documentation>
<startEvent id="startevent1" name="Start" activiti:initiator="applyUserId"></startEvent>
<userTask id="deptLeaderVerify" name="部門領導審批" activiti:candidateGroups="deptLeader"></userTask>
<exclusiveGateway id="exclusivegateway5" name="Exclusive Gateway"></exclusiveGateway>
<userTask id="modifyApply" name="調整申請" activiti:assignee="${applyUserId}"></userTask>
<userTask id="hrVerify" name="人事審批" activiti:candidateGroups="hr"></userTask>
<exclusiveGateway id="exclusivegateway6" name="Exclusive Gateway"></exclusiveGateway>
<userTask id="reportBack" name="銷假" activiti:assignee="${applyUserId}">
<extensionElements>
<activiti:taskListener event="complete" delegateExpression="${reportBackEndProcessor}"></activiti:taskListener>
</extensionElements>
</userTask>
<endEvent id="endevent1" name="End"></endEvent>
<exclusiveGateway id="exclusivegateway7" name="Exclusive Gateway"></exclusiveGateway>
<sequenceFlow id="flow2" sourceRef="startevent1" targetRef="deptLeaderVerify"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="deptLeaderVerify" targetRef="exclusivegateway5"></sequenceFlow>
<sequenceFlow id="flow4" name="不同意" sourceRef="exclusivegateway5" targetRef="modifyApply">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!deptLeaderApproved}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" name="同意" sourceRef="exclusivegateway5" targetRef="hrVerify">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptLeaderApproved}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow6" sourceRef="hrVerify" targetRef="exclusivegateway6"></sequenceFlow>
<sequenceFlow id="flow7" name="同意" sourceRef="exclusivegateway6" targetRef="reportBack">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrApproved}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow8" sourceRef="reportBack" targetRef="endevent1"></sequenceFlow>
<sequenceFlow id="flow9" name="不同意" sourceRef="exclusivegateway6" targetRef="modifyApply">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!hrApproved}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow10" name="重新申請" sourceRef="exclusivegateway7" targetRef="deptLeaderVerify">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${reApply}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow11" sourceRef="modifyApply" targetRef="exclusivegateway7"></sequenceFlow>
<sequenceFlow id="flow12" name="結束流程" sourceRef="exclusivegateway7" targetRef="endevent1">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!reApply}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane bpmnElement="leave" id="BPMNPlane_leave">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
//........................
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
```
### 1.3 節點審批人員約定
| 用戶名 | ACT\_ID\_USER(表) |ACT\_ID\_GROUP(表)|
| --- | --- |--- |
| 發起人(startmen) | 任意 | 任意 |
| 部門領導 | deptmen | deptLeader |
| 人事領導 | hrmen | hr |
## 2、單元測試
我們以單元測試的形式演示這個請假流程的工作過程。
### 2.1 獲取流程引擎及各個Service接口
只有先獲取了流程引擎,才能獲取7大Service接口。
```
package com.sxdx.workflow.activiti.rest;
import org.activiti.engine.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertNotNull;
@SpringBootTest
@RunWith(SpringRunner.class)
public class ActivitiServiceTest {
private ProcessEngine processEngine;
private IdentityService identityService;
private RepositoryService repositoryService;
private RuntimeService runtimeService;
private TaskService taskService;
private HistoryService historyService;
@Test
public void before(){
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration
.createStandaloneProcessEngineConfiguration();
processEngineConfiguration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
processEngineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activiti-demo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true");
processEngineConfiguration.setJdbcUsername("root");
processEngineConfiguration.setJdbcPassword("xxxxxxx");
//如果表不存在,則自動創建表
processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
processEngine = processEngineConfiguration.buildProcessEngine();
System.out.println(processEngine.toString());
repositoryService = processEngine.getRepositoryService();
identityService = processEngine.getIdentityService();
runtimeService = processEngine.getRuntimeService();
taskService = processEngine.getTaskService();
historyService = processEngine.getHistoryService();
assertNotNull(processEngine);
}
}
```
測試結果:查看 activiti-demo 數據庫(單元測試庫),可以看到生成了28張表。

### 2.2 初始化審批人
此處我們通過代碼方式,向數據庫中插入流程所需的用戶信息。
~~~
package com.sxdx.workflow.activiti.rest;
import org.activiti.engine.*;
import org.activiti.engine.identity.User;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@SpringBootTest
@RunWith(SpringRunner.class)
public class ActivitiServiceTest {
private ProcessEngine processEngine;
private IdentityService identityService;
private RepositoryService repositoryService;
private RuntimeService runtimeService;
private TaskService taskService;
private HistoryService historyService;
/**
* 獲取流程引擎及各個Service
*/
@Before
public void before(){
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration
.createStandaloneProcessEngineConfiguration();
processEngineConfiguration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
processEngineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activiti-demo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true");
processEngineConfiguration.setJdbcUsername("root");
processEngineConfiguration.setJdbcPassword("gaoyipeng");
//如果表不存在,則自動創建表
processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
processEngine = processEngineConfiguration.buildProcessEngine();
System.out.println(processEngine.toString());
repositoryService = processEngine.getRepositoryService();
identityService = processEngine.getIdentityService();
runtimeService = processEngine.getRuntimeService();
taskService = processEngine.getTaskService();
historyService = processEngine.getHistoryService();
assertNotNull(processEngine);
}
/**
* 初始化審批人 act_id_user: deptmen, hrmen
*/
@Test
public void initUser(){
User deptmen = identityService.newUser("deptmen");
deptmen.setFirstName("部門領導");
identityService.saveUser(deptmen);
User hrmen = identityService.newUser("hrmen");
hrmen.setFirstName("人事領導");
identityService.saveUser(hrmen);
assertEquals(2,identityService.createUserQuery().count());
}
}
~~~
執行結果:

### 2.3 初始化組
此處我們通過代碼方式,向數據庫中插入流程所需的用戶組信息。
~~~
/**
* 初始化組 act_id_group: deptLeader, hr
* 在Activiti中組分為2種:
* assignment:普通的崗位角色,是用戶分配業務中的功能權限
* security-role: 安全角色,全局管理用戶組織及整個流程的狀態
* 如果使用Activiti提供的Explorer,需要security-role才能看到manage頁簽,需要assignment才能claim任務
*/
@Test
public void initGroup(){
Group deptLeader = identityService.newGroup("deptLeader");
deptLeader.setName("deptLeader");
//擴展字段
deptLeader.setType("assignment");
identityService.saveGroup(deptLeader);
Group hr = identityService.newGroup("hr");
hr.setName("hr");
hr.setType("assignment");
identityService.saveGroup(hr);
assertEquals(2,identityService.createGroupQuery().count());
}
~~~
執行結果:

### 2.4 初始化人員、組的關系
```
/**
* 初始化人員、組的關系
*/
@Test
public void initMemberShip(){
identityService.createMembership("deptmen","deptLeader");
identityService.createMembership("hrmen","hr");
}
~
```
執行結果:

API 補充:
```
//刪除user
identityService.deleteUser(userId);
//刪除group
identityService.deleteGroup(groupId);
//刪除user、group關聯關系
identityService.deleteMembership( userId, groupId);
```
### 2.5 部署流程定義
流程圖準備好后,需要部署流程才能進行后續操作。
將下載好的leave.bpmn放到`src/main/resources`目錄下,github代碼中已經包含了

~~~
/**
* 部署流程定義
*/
@Test
public void deployTest(){
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("leave.bpmn")
.deploy();
assertNotNull(deployment);
}
~~~
執行結果:


數據表中已經生成了數據,說明流程已經部署成功。
### 2.6 發起審批
```
/**
* 發起流程
*/
@Test
public void submitApplyTest(){
String applyUserId = "startmen";
//設置流程啟動發起人,在流程開始之前設置,會自動在表ACT_HI_PROCINST 中的START_USER_ID_中設置用戶ID
identityService.setAuthenticatedUserId(applyUserId);
runtimeService.startProcessInstanceByKey("leave");
}
```
執行結果如下(說明流程已經發起成功):


### 2.7 部門領導獲取待辦
```
/**
* 獲取待辦
*/
@Test
public void getTaskTodo(){
//根據當前人id查詢待辦任務
List<Task> taskList = taskService.createTaskQuery()
.processDefinitionKey("leave")
.taskAssignee("deptmen")
.active().list();
//根據當前人未簽收的任務
List<Task> taskList1 = taskService.createTaskQuery()
.processDefinitionKey("leave")
.taskCandidateUser("deptmen")
.active().list();
List<Task> list = new ArrayList<>();
list.addAll(taskList);
list.addAll(taskList1);
System.out.println("-------"+list.get(0).toString()+"----"+list.get(0).getName());
assertEquals(1,list.size());
Task task = list.get(0);
}
```
斷點截圖(顯示了部門領導節點需要處理的審批記錄):

### 2.8 部門領導審批通過流程
接下來添加審批代碼
~~~
/**
* 獲取待辦并通過
*/
@Test
public void getTaskTodo(){
//根據當前人id查詢待辦任務
List<Task> taskList = taskService.createTaskQuery()
.processDefinitionKey("leave")
.taskAssignee("deptmen")
.active().list();
//根據當前人未簽收的任務
List<Task> taskList1 = taskService.createTaskQuery()
.processDefinitionKey("leave")
.taskCandidateUser("deptmen")
.active().list();
List<Task> list = new ArrayList<>();
list.addAll(taskList);
list.addAll(taskList1);
System.out.println("-------"+list.get(0).toString()+"----"+list.get(0).getName());
assertEquals(1,list.size());
Task task = list.get(0);
//審批流程
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processDefinitionKey("leave")
.singleResult();
// 添加批注
identityService.setAuthenticatedUserId("deptmen");
taskService.addComment(task.getId(), processInstance.getId(), "deptmen【同意】了");
Map<String, Object> variables = new HashMap<>();
variables.put("deptLeaderApproved", true);
// 只有簽收任務,act_hi_taskinst 表的 assignee 字段才不為 null
taskService.claim(task.getId(), "deptmen");
taskService.complete(task.getId(), variables);
}
~~~
執行結果:可以看到審批意見了,部門領導已經審批通過了。

### 2.9 獲取已辦
~~~
/**
* 獲取已完成的流程
*/
@Test
public void getCompileTask(){
List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery()
.processDefinitionKey("leave")
.taskAssignee("deptmen")
.finished().list();
for (HistoricTaskInstance instance :taskInstanceList) {
System.out.println(instance.getProcessInstanceId()+"--"+instance.getName()+"--"+instance.getAssignee());
}
}
~~~
執行結果:可以看到 5001 這條流程,部門經理已經審批過了。

### 2.10 獲取審批意見
~~~
/**
* 獲取審批意見
*/
@Test
public void getHistoryComment(){
//獲取流程實例對象
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processDefinitionKey("leave").singleResult();
//獲取歷史活動集合
List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstance.getId())
.activityType("userTask")
.finished()
.list();
for (HistoricActivityInstance historicActivityInstance:historicActivityInstanceList ) {
List<Comment> commentList = taskService.getTaskComments(historicActivityInstance.getTaskId(), "comment");
for (int i = 0; i < commentList.size(); i++) {
System.out.println(commentList.get(i).getProcessInstanceId()+"---"+ commentList.get(i).getUserId() +"批復內容:" + commentList.get(i).getFullMessage());
}
}
}
~~~

**請記住這個5001,獲取流程圖時會用到。**
## 3、獲取流程圖
這個是比較固定的代碼,不做詳解介紹。只做演示。添加代碼如下

訪問API:[http://localhost:8080/process/read-resource/5001](http://localhost:8080/process/read-resource/5001),pProcessInstanceId是流程實例:5001(**act\_hi\_procinst**表ID\_)
~~~
@RequestMapping(value = "/read-resource/{pProcessInstanceId}")
public void readResource(@PathVariable("pProcessInstanceId")String pProcessInstanceId, HttpServletResponse response)
throws Exception {
// 設置頁面不緩存
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
String processDefinitionId = "";
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(pProcessInstanceId).singleResult();
if(processInstance == null) {
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(pProcessInstanceId).singleResult();
processDefinitionId = historicProcessInstance.getProcessDefinitionId();
} else {
processDefinitionId = processInstance.getProcessDefinitionId();
}
ProcessDefinitionQuery pdq = repositoryService.createProcessDefinitionQuery();
ProcessDefinition pd = pdq.processDefinitionId(processDefinitionId).singleResult();
String resourceName = pd.getDiagramResourceName();
if(resourceName.endsWith(".png") && StringUtils.isEmpty(pProcessInstanceId) == false)
{
getActivitiProccessImage(pProcessInstanceId,response);
//ProcessDiagramGenerator.generateDiagram(pde, "png", getRuntimeService().getActiveActivityIds(processInstanceId));
}
else
{
// 通過接口讀取
InputStream resourceAsStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), resourceName);
// 輸出資源內容到相應對象
byte[] b = new byte[1024];
int len = -1;
while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}
}
~~~
~~~
/**
* 獲取流程圖像,已執行節點和流程線高亮顯示
*/
public void getActivitiProccessImage(String pProcessInstanceId, HttpServletResponse response) {
//logger.info("[開始]-獲取流程圖圖像");
try {
// 獲取歷史流程實例
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(pProcessInstanceId).singleResult();
if (historicProcessInstance == null) {
//throw new BusinessException("獲取流程實例ID[" + pProcessInstanceId + "]對應的歷史流程實例失敗!");
}
else {
// 獲取流程定義
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
.getDeployedProcessDefinition(historicProcessInstance.getProcessDefinitionId());
// 獲取流程歷史中已執行節點,并按照節點在流程中執行先后順序排序
List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(pProcessInstanceId).orderByHistoricActivityInstanceId().asc().list();
// 已執行的節點ID集合
List<String> executedActivityIdList = new ArrayList<String>();
int index = 1;
//logger.info("獲取已經執行的節點ID");
for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
executedActivityIdList.add(activityInstance.getActivityId());
//logger.info("第[" + index + "]個已執行節點=" + activityInstance.getActivityId() + " : " +activityInstance.getActivityName());
index++;
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
// 已執行的線集合
List<String> flowIds = new ArrayList<String>();
// 獲取流程走過的線 (getHighLightedFlows是下面的方法)
flowIds = getHighLightedFlows(bpmnModel,processDefinition, historicActivityInstanceList);
// // 獲取流程圖圖像字符流
// ProcessDiagramGenerator pec = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
// //配置字體
// InputStream imageStream = pec.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds,"宋體","微軟雅黑","黑體",null,2.0);
Set<String> currIds = runtimeService.createExecutionQuery().processInstanceId(pProcessInstanceId).list()
.stream().map(e->e.getActivityId()).collect(Collectors.toSet());
ICustomProcessDiagramGenerator diagramGenerator = (ICustomProcessDiagramGenerator) processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList,
flowIds, "宋體", "宋體", "宋體", null, 1.0, new Color[] { WorkflowConstants.COLOR_NORMAL, WorkflowConstants.COLOR_CURRENT }, currIds);
response.setContentType("image/png");
OutputStream os = response.getOutputStream();
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
imageStream.close();
}
//logger.info("[完成]-獲取流程圖圖像");
} catch (Exception e) {
System.out.println(e.getMessage());
//logger.error("【異常】-獲取流程圖失敗!" + e.getMessage());
//throw new BusinessException("獲取流程圖失敗!" + e.getMessage());
}
}
~~~
~~~
private List<String> getHighLightedFlows(BpmnModel bpmnModel, ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) {
// 高亮流程已發生流轉的線id集合
List<String> highLightedFlowIds = new ArrayList<>();
// 全部活動節點
List<FlowNode> historicActivityNodes = new ArrayList<>();
// 已完成的歷史活動節點
List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
historicActivityNodes.add(flowNode);
if (historicActivityInstance.getEndTime() != null) {
finishedActivityInstances.add(historicActivityInstance);
}
}
FlowNode currentFlowNode = null;
FlowNode targetFlowNode = null;
// 遍歷已完成的活動實例,從每個實例的outgoingFlows中找到已執行的
for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
// 獲得當前活動對應的節點信息及outgoingFlows信息
currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
/**
* 遍歷outgoingFlows并找到已已流轉的 滿足如下條件認為已已流轉: 1.當前節點是并行網關或兼容網關,則通過outgoingFlows能夠在歷史活動中找到的全部節點均為已流轉 2.當前節點是以上兩種類型之外的,通過outgoingFlows查找到的時間最早的流轉節點視為有效流轉
*/
if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
// 遍歷歷史活動節點,找到匹配流程目標節點的
for (SequenceFlow sequenceFlow : sequenceFlows) {
targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
if (historicActivityNodes.contains(targetFlowNode)) {
highLightedFlowIds.add(targetFlowNode.getId());
}
}
} else {
List<Map<String, Object>> tempMapList = new ArrayList<>();
for (SequenceFlow sequenceFlow : sequenceFlows) {
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
Map<String, Object> map = new HashMap<>();
map.put("highLightedFlowId", sequenceFlow.getId());
map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
tempMapList.add(map);
}
}
}
if (!CollectionUtils.isEmpty(tempMapList)) {
// 遍歷匹配的集合,取得開始時間最早的一個
long earliestStamp = 0L;
String highLightedFlowId = null;
for (Map<String, Object> map : tempMapList) {
long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {
highLightedFlowId = map.get("highLightedFlowId").toString();
earliestStamp = highLightedFlowStartTime;
}
}
highLightedFlowIds.add(highLightedFlowId);
}
}
}
return highLightedFlowIds;
}
~~~
獲取的流程圖如下:

本節到此結束。
- 使用教程
- 1、環境說明
- 2、導入教程
- 3、系統展示
- 4、更新歷史
- 搭建教程
- 第一章:Activiti模塊
- 1、基本概念
- 2、資料下載
- 3、環境搭建
- 4、集成Activiti-Modeler流程設計器
- 5、七大Service接口
- 6、流程定義文件—流程定義—流程模型 的相互轉化
- 7、用戶和用戶組
- 8、任務表單
- 8.1、表單分類
- 8.2 、動態表單實戰、集成Swagger、Logback
- 8.3、外置表單實戰
- 8.4、普通表單實戰,集成Thymeleaf,Mybatis-Plus
- 8.5、表單模式選型
- 9、多實例(會簽)
- 10、子流程和調用活動
- 10.1、子流程
- 10.2、事件子流程
- 10.3、調用活動
- 10.4、事務子流程
- 11、流程歷史管理、補充獲取流程定義列表接口
- 12、Activiti事件
- 12.1、 事件類別
- 12.2、 Activiti啟動事件
- 12.3、Activiti結束事件
- 12.4、邊界事件(一)
- 12.5、邊界事件(二)
- 12.6、中間事件
- 13、網關
- 14、Activiti審批意見管理
- 15、Activiti流程駁回、流程回退
- 16、Activiti任務委托
- 17、Activiti流程的掛起、激活
- 第二章:基礎架構完善
- 1、Spring-Security-OAuth2簡介
- 2、搭建認證服務器
- 3、搭建資源服務器
- 4、Activiti自帶的Rest接口
- 5、添加JWT支持
- 6、數據庫存儲授權碼Code,Client信息
- 第三章、集成RBAC權限管理
- 1、RBAC-基于角色的訪問控制
- 2、替換Activiti用戶和用戶組
- 3、Spring-Security獲取當前操作人信息
- 4、OauthUserDetailService改造
- 第四章、使用Swagger生成靜態接口文檔