Activiti 流程監控 流程圖

設計思路:前端

(1)一個頁面,兩個tab標籤:A和B。java

(2)A標籤加載流程圖,B標籤加載流程數據。數據庫

        流程圖的做用:顯示全局流程佈局,高亮顯示當前進行的環節。svg

        流程數據的做用:顯示當前人,處理人,處理時間等須要的信息。佈局

(3)加載流程圖:性能

        頁面傳回businessKey,後臺實現查詢。查詢須要用到的對象:HistoricProcessInstance 或者 ProcessInstance,二者有什麼區別?this

        HistoricProcessInstance:既能夠查詢歷史流程實例(結束的流程),也能夠查詢運行中的流程實例。(調用getHistoricService()方法實現業務處理)spa

         ProcessInstance :只查詢運行中的流程實例。(調用getRuntimeService()方法實現業務處理)設計

        結論:須要效率而且不關注結束流程的狀況選擇 ProcessInstance,而針對流程監控若是結束的流程也須要監控,應該選擇 HistoricProcessInstance code

 重要代碼:

/*取歷史流程實例,既能取到歷史實例又能取到運行中的流程實例*/
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方法*/
/**
 * 根據流程businessKey查詢歷史流程實例
 * @param processId
 * @return
 */
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<String> getHighLightedFlows(ProcessDefinitionEntity processDefinitionEntity,
	List<HistoricActivityInstance> 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 獲取歷史數據的前提是:須要配置歷史數據的記錄級別,與此同時,在配置中也能夠處理流程圖中文字的亂碼。

<!-- ProcessEngineConfiguration
	ProcessEngineConfiguration:用於建立ProcessEngine
-->
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
	<!-- 數據源 -->
	<property name="dataSource" ref="dataSource" />
	<!-- activiti數據庫表處理策略 -->
	<property name="databaseSchemaUpdate" value="true"/>
	<!--
		Activiti默認提供4種歷史級別:
		一、none: 不保存任何歷史記錄,能夠提升系統的性能;
		二、activity:保存全部的流程實例、任務、活動信息;
		三、audit:也是Activiti的默認級別,保存全部的流程實例、任務、活動、表單屬性;
		四、full:最完整的歷史記錄,除了包含Audit級別的信息以外還能保存詳細信息,例如:流程變量。
	-->
	<!-- 歷史數據記錄級別 -->
	<property name="history" value="full"/>
	<!-- 中文亂碼問題 -->
	<property name="activityFontName" value="宋體"/>  
	<property name="labelFontName" value="宋體"/>  
</bean>

流程圖效果:

附:完善後的流程圖貼出來啦:

注意:須要流程圖展現得美觀,畫流程圖的時候就得注意美觀。

說得這裏就完了嗎?沒有,流程數據的展現還沒說呢。

(4)加載流程數據:

流程數據爲何能和流程圖保持一致呢?由於頁面傳回的businessKey是同一個嘛。這種低級的問題我都很差意回答。

切入正題:從流程圖中咱們能夠看出,紅色標註能夠追蹤到一系列環節,也就意味着這一系列環節的數據有章可循。恰好,有個叫HistoricActivityInstance的對象。

/*查詢流程歷史記錄*/
List<HistoricActivityInstance> history = historyService.createHistoricActivityInstanceQuery()
	/*過濾條件*/
	.processInstanceId(processId)
	/*執行查詢*/
	.list();

而後呢?把這個history扔個前端去循環遍歷,有什麼屬性本身翻翻,至於如何發請求拿到後臺的數據,呵呵,差一點又回答低級問題。

注意:

(1)須要區別businessKey和processId。HistoricProcessInstance.getId()就是processId了,但他沒有getProcessId()方法哦。

(2)怎麼樣知道這些「未知」的對象中都有哪些變量或者方法呢?對象之間又怎麼聯繫呢?答案是反覆試驗幾遍,試驗的方法就是傳說的中的「斷點」。

總結:

爲何要總結?經歷的時候只是開闊眼界,總結和反思才能開始成長。

有人會問,爲何不在流程圖上顯示更多信息呢?好比鼠標通過時,顯示時間,處理人等等。我想說,試了你就知道了。還有人會問,流程圖好醜,爲何不用vml或者svg依賴數據畫圖呢?我想說,你不光有錢,還有勢,還有時間,還有精力,說不定還臉大。

設計和代碼一樣重要,缺一不可。

若有問題,隨時聯繫我,網名即QQ。聯繫不到我,就說明文章沒看完。

相關文章
相關標籤/搜索