天天看點

Activiti 流程監控 流程圖

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 擷取曆史資料的前提是:需要配置曆史資料的記錄級别,與此同時,在配置中也可以處理流程圖中文字的亂碼。