Activiti作為一款輕量級的流程引擎,基本滿足日常的工作需要。但涉及到流程監控時,就會顯得心有餘而力不足。如何不借助其他插件,又盡量避免改寫底層代碼以實作流程的追蹤呢?
設計思路:
(1)一個頁面,兩個tab标簽:A和B。
(2)A标簽加載流程圖,B标簽加載流程資料。
流程圖的作用:顯示全局流程布局,高亮顯示目前進行的環節。
流程資料的作用:顯示目前人,處理人,處理時間等需要的資訊。
(3)加載流程圖:
頁面傳回businessKey,背景實作查詢。查詢需要用到的對象:HistoricProcessInstance 或者 ProcessInstance,兩者有什麼差別?
HistoricProcessInstance:既可以查詢曆史流程執行個體(結束的流程),也可以查詢運作中的流程執行個體。(調用getHistoricService()方法實作業務處理)
ProcessInstance :隻查詢運作中的流程執行個體。(調用getRuntimeService()方法實作業務處理)
結論:需要效率并且不關注結束流程的情況選擇 ProcessInstance,而針對流程監控如果結束的流程也需要監控,應該選擇 HistoricProcessInstance 。
重要代碼:
/取曆史流程執行個體,既能取到曆史執行個體又能取到運作中的流程執行個體/
HistoricProcessInstance hpi = workFlowEngineServiceImpl.findHistoryProcessInstanceByBusKey(businessKey);
try {
if (hpi == null) {
throw new RuntimeException(“擷取流程圖異常!”);
} else {
InputStream imageStream = workFlowEngineServiceImpl.getFlowMap(hpi, hpi.getId(), flowType);
ServletOutputStream os = response.getOutputStream();
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = imageStream.read(buffer, 0, 1024)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.flush();
os.close();
imageStream.close();
}
} catch (Exception e) {
logger.error(e, e);
throw new RuntimeException(“擷取流程圖異常!”);
}
/findHistoryProcessInstanceByBusKey方法/
public HistoricProcessInstance findHistoryProcessInstanceByBusKey(String businessKey){
HistoryService historyService = this.getHistoryService();
return historyService.createHistoricProcessInstanceQuery()
.processInstanceBusinessKey(businessKey).singleResult();
}
/getFlowMap方法/
public InputStream getFlowMap(HistoricProcessInstance processInstance, String instanceId, String flowType) {
processEngine = getInstance();
// RuntimeService runtimeService = processEngine.getRuntimeService();
// DynamicBpmnService flowMoniService = processEngine.getDynamicBpmnService();
/*資源服務*/
RepositoryService repositoryService = processEngine.getRepositoryService();
/*曆史資料服務*/
HistoryService historyService = processEngine.getHistoryService();
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
/*為了流程監控圖顯示效果,替換多有未取到的變量,隻顯示節點的中文描述*/
List<ActivityImpl> activityList = processDefinition.getActivities();
for(ActivityImpl activity : activityList){
String name = bpmnModel.getFlowElement(activity.getId()).getName();
bpmnModel.getFlowElement(activity.getId())
.setName(name.replaceAll("[\\w{}$\\-+]", ""));
}
/*曆史節點,取出變量,設定為節點的名稱*/
List<ArkHistoricActivity> hisList = findProcessHistoryByPiid(instanceId);
for(ArkHistoricActivity hisActivity : hisList){
bpmnModel.getFlowElement(hisActivity.getActivityId())
.setName(hisActivity.getActivityName());
}
List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId).orderByHistoricActivityInstanceStartTime().asc().list();
List<String> activitiIds = new ArrayList<String>();
List<String> flowIds = new ArrayList<String>();
/*擷取流程走過的線*/
flowIds = flowServiceImpl.getHighLightedFlows(processDefinition, activityInstances);
/*擷取流程走過的節點*/
for (HistoricActivityInstance hai : activityInstances) {
activitiIds.add(hai.getActivityId());
}
Context.setProcessEngineConfiguration(
(ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration());
/**
* 從配置檔案中擷取中文配置資訊,避免中文亂碼
* processEngine.getProcessEngineConfiguration().getActivityFontName(),
* processEngine.getProcessEngineConfiguration().getLabelFontName(),
*/
InputStream imageStream = new DefaultProcessDiagramGenerator().generateDiagram(bpmnModel, "png", activitiIds,
flowIds, processEngine.getProcessEngineConfiguration().getActivityFontName(),
processEngine.getProcessEngineConfiguration().getLabelFontName(),
"", null, 1.0);
return imageStream;
}
/getHighLightedFlows方法/
public List getHighLightedFlows(ProcessDefinitionEntity processDefinitionEntity,
List historicActivityInstances) {
/*用以儲存高亮的線flowId*/
List<String> highFlows = new ArrayList<String>();
/*對曆史流程節點進行周遊*/
for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
/*得到節點定義的詳細資訊*/
ActivityImpl activityImpl = processDefinitionEntity.findActivity(historicActivityInstances.get(i).getActivityId());
/*用以儲存後需開始時間相同的節點*/
List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();
/*将後面第一個節點放在時間相同節點的集合裡*/
ActivityImpl sameActivityImpl1 = processDefinitionEntity.findActivity(historicActivityInstances.get(i + 1).getActivityId());
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 (activityImpl1.getStartTime().equals(activityImpl2.getStartTime())) {
ActivityImpl sameActivityImpl2 = processDefinitionEntity.findActivity(activityImpl2.getActivityId());
sameStartTimeNodes.add(sameActivityImpl2);
}
/*有不相同跳出循環*/
else {
break;
}
}
/*取出節點的所有出去的線*/
List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();
/*對所有的線進行周遊*/
for (PvmTransition pvmTransition : pvmTransitions) {
/*如果取出的線的目标節點存在時間相同的節點裡,儲存該線的id,進行高亮顯示*/
ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition.getDestination();
if (sameStartTimeNodes.contains(pvmActivityImpl)) {
highFlows.add(pvmTransition.getId());
}
}
}
return highFlows;
}
注意:
1)流程圖中可能需要處理${currName}類似的變量,代碼中已有寫出,但這些變量替換為真實資料的前提是環節已辦理,因為已辦理才會記錄Variables,未辦理的環節需要将這些變量替換為空字元串,這是一些細節處理。
2) HistoricProcessInstance 擷取曆史資料的前提是:需要配置曆史資料的記錄級别,與此同時,在配置中也可以處理流程圖中文字的亂碼。