最近要把Activiti6集成到系統中,遇到了生成追蹤流程圖片的問題,在5.x版本中可以使用歷史流程節點查找ActivityImpl,升級到6.0版本后,pvm包被移除,所以要實現新的圖片生成辦法。本文靈感來自:Activiti6.0.0 跟蹤流程執行情況用紅色框在流程圖上標識路線跟節點
生成追蹤流程圖要完成兩件事情:已執行的Activity高亮和已流轉的Flow高亮。
以下方法是生成完整的追蹤圖片:
public InputStream getResourceDiagramInputStream(String id) { try { // 獲取歷史流程實例 HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); // 獲取流程中已經執行的節點,按照執行先后順序排序 List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(id).orderByHistoricActivityInstanceId().asc().list(); // 構造已執行的節點ID集合 List<String> executedActivityIdList = new ArrayList<String>(); for (HistoricActivityInstance activityInstance : historicActivityInstanceList) { executedActivityIdList.add(activityInstance.getActivityId()); } // 獲取bpmnModel BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId()); // 獲取流程已發生流轉的線ID集合 List<String> flowIds = this.getExecutedFlows(bpmnModel, historicActivityInstanceList); // 使用默認配置獲得流程圖表生成器,並生成追蹤圖片字符流 ProcessDiagramGenerator processDiagramGenerator = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator(); InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "宋體", "微軟雅黑", "黑體", null, 2.0); return imageStream; } catch (Exception e) { e.printStackTrace(); return null; } }
以下方法獲取所有已流轉的線,由於並行網關與包含網關可能存在多個流轉,所以要找到全部符合要求的Flow,其它類型的節點只查找歷史活動實例中的第一個節點:
private List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) { // 流轉線ID集合 List<String> flowIdList = new ArrayList<String>(); // 全部活動實例 List<FlowNode> historicFlowNodeList = new LinkedList<FlowNode>(); // 已完成的歷史活動節點 List<HistoricActivityInstance> finishedActivityInstanceList = new LinkedList<HistoricActivityInstance>(); for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) { historicFlowNodeList.add((FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true)); if (historicActivityInstance.getEndTime() != null) { finishedActivityInstanceList.add(historicActivityInstance); } } // 遍歷已完成的活動實例,從每個實例的outgoingFlows中找到已執行的 FlowNode currentFlowNode = null; for (HistoricActivityInstance currentActivityInstance : finishedActivityInstanceList) { // 獲得當前活動對應的節點信息及outgoingFlows信息 currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true); List<SequenceFlow> sequenceFlowList = currentFlowNode.getOutgoingFlows(); /** * 遍歷outgoingFlows並找到已已流轉的 * 滿足如下條件認為已已流轉: * 1.當前節點是並行網關或包含網關,則通過outgoingFlows能夠在歷史活動中找到的全部節點均為已流轉 * 2.當前節點是以上兩種類型之外的,通過outgoingFlows查找到的時間最近的流轉節點視為有效流轉 */ FlowNode targetFlowNode = null; if (BpmsActivityTypeEnum.PARALLEL_GATEWAY.getType().equals(currentActivityInstance.getActivityType()) || BpmsActivityTypeEnum.INCLUSIVE_GATEWAY.getType().equals(currentActivityInstance.getActivityType())) { // 遍歷歷史活動節點,找到匹配Flow目標節點的 for (SequenceFlow sequenceFlow : sequenceFlowList) { targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true); if (historicFlowNodeList.contains(targetFlowNode)) { flowIdList.add(sequenceFlow.getId()); } } } else { List<Map<String, String>> tempMapList = new LinkedList<Map<String,String>>(); // 遍歷歷史活動節點,找到匹配Flow目標節點的 for (SequenceFlow sequenceFlow : sequenceFlowList) { for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) { if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) { tempMapList.add(UtilMisc.toMap("flowId", sequenceFlow.getId(), "activityStartTime", String.valueOf(historicActivityInstance.getStartTime().getTime()))); } } } // 遍歷匹配的集合,取得開始時間最早的一個 long earliestStamp = 0L; String flowId = null; for (Map<String, String> map : tempMapList) { long activityStartTime = Long.valueOf(map.get("activityStartTime")); if (earliestStamp == 0 || earliestStamp >= activityStartTime) { earliestStamp = activityStartTime; flowId = map.get("flowId"); } } flowIdList.add(flowId); } } return flowIdList; }
枚舉說明:
public enum BpmsActivityTypeEnum { START_EVENT("startEvent", "開始事件"), END_EVENT("endEvent", "結束事件"), USER_TASK("userTask", "用戶任務"), EXCLUSIVE_GATEWAY("exclusiveGateway", "排他網關"), PARALLEL_GATEWAY("parallelGateway", "並行網關"), INCLUSIVE_GATEWAY("inclusiveGateway", "包含網關"); private String type; private String name; private BpmsActivityTypeEnum(String type, String name) { this.type = type; this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
工具方法:
public class UtilMisc { public static <V, V1 extends V, V2 extends V> Map<String, V> toMap(String name1, V1 value1, String name2, V2 value2) { return populateMap(new HashMap<String, V>(), name1, value1, name2, value2); } @SuppressWarnings("unchecked") private static <K, V> Map<String, V> populateMap(Map<String, V> map, Object... data) { for (int i = 0; i < data.length;) { map.put((String) data[i++], (V) data[i++]); } return map; } }
看一個簡單的生成效果:

在按照流程節點類型做區分處理上,考慮的不是特別周全,希望有用到的朋友遇到了問題能夠與我分享,一起探討更好的解決方式~
