https://cloud.tencent.com/developer/article/1165460
https://blog.csdn.net/m0_37222746/article/details/73321680
springboot 與 activiti 參考 另一個blog
有接觸過activiti的朋友可能知道,在activiti5.x的追蹤流程節點查找,可以用
ActivityImpl這個類來實作,可惜在activiti6版本,pvm包整個類包都被删除,再也沒有ActivityImpl這個類。這給流程圖追蹤造成一定的麻煩,不過沒關系,本章節就是教大家如何用activiti6來生成實時流程圖追蹤。
生成流程圖核心類
主要是利用ProcessDiagramGenerator這個接口實作類,去實作generateDiagram這個方法。預設可以采用ProcessDiagramGenerator的子類
DefaultProcessDiagramGenerator調用generateDiagram實作流程圖。當然也可以自定義相應的子類CustomProcessDiagramGenerator去定制化實作
生成流程圖核心方法
public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows,
String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor;
看這個方法,我們顯然要關注的是bpmnmodel,highLightedActivities,highLightedFlows
這三個字段值
bpmnmodel:bpmn模型,這個簡單,調用
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
調用預設api就可以擷取。相對有點繁瑣是
highLightedActivities(需要高亮的執行流程節點集合的擷取)以及
highLightedFlows(需要高亮流程連接配接線集合的擷取)。
上代碼
package com.zte.activi.controller;
import io.swagger.annotations.Api;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author zcy
* @version 2019/9/23
* Created by zcy on 2019/9/23.
*/
@Controller
@RequestMapping("/draw")
@Api("draw")
public class DrawController {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@Autowired
ProcessEngineFactoryBean processEngine;
@Autowired
ProcessEngineConfiguration processEngineConfiguration;
@RequestMapping("queryProPlan.html")
public void queryProPlan(HttpServletRequest request, HttpServletResponse response) throws Exception {
String processInstanceId = "50023"; //57510 65037 50023
//擷取曆史流程執行個體
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
//擷取流程圖
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
processEngineConfiguration = processEngine.getProcessEngineConfiguration();
Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);
ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());
List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
//高亮環節id集合
List<String> highLightedActivitis = new ArrayList<String>();
for(HistoricActivityInstance tempActivity : highLightedActivitList){
String activityId = tempActivity.getActivityId();
highLightedActivitis.add(activityId);
}
//高亮線路id集合
//List<String> highLightedFlows = getHighLightedFlows(bpmnModel,highLightedActivitList);
List<String> highLightedFlows = getHighLightedFlows(bpmnModel,definitionEntity,highLightedActivitList);
//中文顯示的是口口口,設定字型就好了
InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel,"png",highLightedActivitis,highLightedFlows,"宋體","微軟雅黑","黑體",null,2.0);
//單獨傳回流程圖,不高亮顯示
//InputStream imageStream = diagramGenerator.generatePngDiagram(bpmnModel);
// 輸出資源内容到相應對象
byte[] b = new byte[1024];
int len;
while ((len = imageStream.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}
/**
* 擷取需要高亮的線
*
* @param historicActivityInstances
* @return
*/
private static List<String> getHighLightedFlows(BpmnModel bpmnModel, 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;
}
public List<String> getHighLightedFlows(BpmnModel bpmnModel,ProcessDefinitionEntity processDefinitionEntity,List<HistoricActivityInstance> historicActivityInstances)
{
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //24小時制
List<String> highFlows = new ArrayList<String>();// 用以儲存高亮的線flowId
for (int i = 0; i < historicActivityInstances.size() - 1; i++)
{
// 對曆史流程節點進行周遊
// 得到節點定義的詳細資訊
FlowNode activityImpl = (FlowNode)bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());
List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();// 用以儲存後續開始時間相同的節點
FlowNode sameActivityImpl1 = null;
HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);// 第一個節點
HistoricActivityInstance activityImp2_ ;
for(int k = i + 1 ; k <= historicActivityInstances.size() - 1; k++)
{
activityImp2_ = historicActivityInstances.get(k);// 後續第1個節點
if ( activityImpl_.getActivityType().equals("userTask") && activityImp2_.getActivityType().equals("userTask") &&
df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime())) ) //都是usertask,且主節點與後續節點的開始時間相同,說明不是真實的後繼節點
{
}
else
{
sameActivityImpl1 = (FlowNode)bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());//找到緊跟在後面的一個節點
break;
}
}
sameStartTimeNodes.add(sameActivityImpl1); // 将後面第一個節點放在時間相同節點的集合裡
for (int j = i + 1; j < historicActivityInstances.size() - 1; j++)
{
HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 後續第一個節點
HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 後續第二個節點
if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime())) )
{// 如果第一個節點和第二個節點開始時間相同儲存
FlowNode sameActivityImpl2 = (FlowNode)bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
sameStartTimeNodes.add(sameActivityImpl2);
}
else
{// 有不相同跳出循環
break;
}
}
List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows() ; // 取出節點的所有出去的線
for (SequenceFlow pvmTransition : pvmTransitions)
{// 對所有的線進行周遊
FlowNode pvmActivityImpl = (FlowNode)bpmnModel.getMainProcess().getFlowElement( pvmTransition.getTargetRef());// 如果取出的線的目标節點存在時間相同的節點裡,儲存該線的id,進行高亮顯示
if (sameStartTimeNodes.contains(pvmActivityImpl)) {
highFlows.add(pvmTransition.getId());
}
}
}
return highFlows;
}
}