提供的Service方法如下:
Java
/** * 获取当前任务流程图 * * @param processInstanceId * @return */ @Override public InputStream generateDiagram(String processInstanceId) { //方法中用到的参数是流程实例ID,其实TaskId也可以转为这个。调用taskService查询即可。 Command<InputStream> cmd = new ProcessInstanceDiagramCmd(processInstanceId, runtimeService, repositoryService, processEngine, historyService); return managementService.executeCommand(cmd); }
而ProcessInstanceDiagramCmd采用了Activiti的命令模式。就是继承Command接口,其实这个地方完全可以使用纯Service方法去搞定,我之所以这么写,还是受到了国内临远大师的影响。
本次绘制的流程图分为两种情况:1、流程实例还未执行完毕,也就是流程还没有结束,还有运行的任务。2、已经执行完毕的流程,流程已经进入了流程历史。不管属于以上哪种情况的流程图都会绘制流程走向。
具体代码如下:
Java
import com.google.common.collect.Lists; import org.activiti.bpmn.model.BpmnModel; import org.activiti.engine.HistoryService; import org.activiti.engine.RepositoryService; import org.activiti.engine.RuntimeService; import org.activiti.engine.history.HistoricActivityInstance; import org.activiti.engine.history.HistoricProcessInstance; import org.activiti.engine.impl.context.Context; import org.activiti.engine.impl.interceptor.Command; import org.activiti.engine.impl.interceptor.CommandContext; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.impl.pvm.PvmTransition; import org.activiti.engine.impl.pvm.process.ActivityImpl; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.image.impl.DefaultProcessDiagramGenerator; import org.activiti.spring.ProcessEngineFactoryBean; import java.io.InputStream; import java.util.*; /** * 根据流程实例ID生成流程图 * * @author Chen Zhiguo */ public class ProcessInstanceDiagramCmd implements Command<InputStream> { protected String processInstanceId; private RuntimeService runtimeService; private RepositoryService repositoryService; private ProcessEngineFactoryBean processEngine; private HistoryService historyService; public ProcessInstanceDiagramCmd(String processInstanceId, RuntimeService runtimeService, RepositoryService repositoryService, ProcessEngineFactoryBean processEngine, HistoryService historyService) { this.processInstanceId = processInstanceId; this.runtimeService = runtimeService; this.repositoryService = repositoryService; this.processEngine = processEngine; this.historyService = historyService; } public InputStream execute(CommandContext commandContext) { ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId).singleResult(); HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); if (processInstance == null && historicProcessInstance == null) { return null; } ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) repositoryService .getProcessDefinition(processInstance == null ? historicProcessInstance.getProcessDefinitionId() : processInstance.getProcessDefinitionId()); BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); List<String> activeActivityIds = Lists.newArrayList(); if (processInstance != null) { activeActivityIds = runtimeService.getActiveActivityIds(processInstance.getProcessInstanceId()); } else { activeActivityIds.add(historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).activityType("endEvent") .singleResult().getActivityId()); } // 使用spring注入引擎请使用下面的这行代码 Context.setProcessEngineConfiguration(processEngine.getProcessEngineConfiguration()); List<String> highLightedFlows = getHighLightedFlows(processDefinition, processInstanceId); InputStream imageStream = new DefaultProcessDiagramGenerator().generateDiagram(bpmnModel, "png", activeActivityIds, highLightedFlows); return imageStream; } /** * getHighLightedFlows * * @param processDefinition * @param processInstanceId * @return */ private List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinition, String processInstanceId) { List<String> highLightedFlows = new ArrayList<String>(); // List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery() // .processInstanceId(processInstanceId) // //order by startime asc is not correct. use default order is correct. // //.orderByHistoricActivityInstanceStartTime().asc()/*.orderByActivityId().asc()*/ // .list(); //上面注释掉的代码是官方的rest方法中提供的方案,可是我在实际测试中有Bug出现,所以做了一下修改。注意下面List内容的排序会影响流程走向红线丢失的问题 List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId) .orderByHistoricActivityInstanceStartTime().orderByHistoricActivityInstanceEndTime().asc().list(); LinkedList<HistoricActivityInstance> hisActInstList = new LinkedList<HistoricActivityInstance>(); hisActInstList.addAll(historicActivityInstances); getHighlightedFlows(processDefinition.getActivities(), hisActInstList, highLightedFlows); return highLightedFlows; } /** * getHighlightedFlows * <p/> * code logic: * 1. Loop all activities by id asc order; * 2. Check each activity's outgoing transitions and eventBoundery outgoing transitions, if outgoing transitions's destination.id is in other executed activityIds, add this transition to highLightedFlows List; * 3. But if activity is not a parallelGateway or inclusiveGateway, only choose the earliest flow. * * @param activityList * @param hisActInstList * @param highLightedFlows */ private void getHighlightedFlows(List<ActivityImpl> activityList, LinkedList<HistoricActivityInstance> hisActInstList, List<String> highLightedFlows) { //check out startEvents in activityList List<ActivityImpl> startEventActList = new ArrayList<ActivityImpl>(); Map<String, ActivityImpl> activityMap = new HashMap<String, ActivityImpl>(activityList.size()); for (ActivityImpl activity : activityList) { activityMap.put(activity.getId(), activity); String actType = (String) activity.getProperty("type"); if (actType != null && actType.toLowerCase().indexOf("startevent") >= 0) { startEventActList.add(activity); } } //These codes is used to avoid a bug: //ACT-1728 If the process instance was started by a callActivity, it will be not have the startEvent activity in ACT_HI_ACTINST table //Code logic: //Check the first activity if it is a startEvent, if not check out the startEvent's highlight outgoing flow. HistoricActivityInstance firstHistActInst = hisActInstList.getFirst(); String firstActType = (String) firstHistActInst.getActivityType(); if (firstActType != null && firstActType.toLowerCase().indexOf("startevent") < 0) { PvmTransition startTrans = getStartTransaction(startEventActList, firstHistActInst); if (startTrans != null) { highLightedFlows.add(startTrans.getId()); } } while (!hisActInstList.isEmpty()) { HistoricActivityInstance histActInst = hisActInstList.removeFirst(); ActivityImpl activity = activityMap.get(histActInst.getActivityId()); if (activity != null) { boolean isParallel = false; String type = histActInst.getActivityType(); if ("parallelGateway".equals(type) || "inclusiveGateway".equals(type)) { isParallel = true; } else if ("subProcess".equals(histActInst.getActivityType())) { getHighlightedFlows(activity.getActivities(), hisActInstList, highLightedFlows); } List<PvmTransition> allOutgoingTrans = new ArrayList<PvmTransition>(); allOutgoingTrans.addAll(activity.getOutgoingTransitions()); allOutgoingTrans.addAll(getBoundaryEventOutgoingTransitions(activity)); List<String> activityHighLightedFlowIds = getHighlightedFlows(allOutgoingTrans, hisActInstList, isParallel); highLightedFlows.addAll(activityHighLightedFlowIds); } } } /** * Check out the outgoing transition connected to firstActInst from startEventActList * * @param startEventActList * @param firstActInst * @return */ private PvmTransition getStartTransaction(List<ActivityImpl> startEventActList, HistoricActivityInstance firstActInst) { for (ActivityImpl startEventAct : startEventActList) { for (PvmTransition trans : startEventAct.getOutgoingTransitions()) { if (trans.getDestination().getId().equals(firstActInst.getActivityId())) { return trans; } } } return null; } /** * getBoundaryEventOutgoingTransitions * * @param activity * @return */ private List<PvmTransition> getBoundaryEventOutgoingTransitions(ActivityImpl activity) { List<PvmTransition> boundaryTrans = new ArrayList<PvmTransition>(); for (ActivityImpl subActivity : activity.getActivities()) { String type = (String) subActivity.getProperty("type"); if (type != null && type.toLowerCase().indexOf("boundary") >= 0) { boundaryTrans.addAll(subActivity.getOutgoingTransitions()); } } return boundaryTrans; } /** * find out single activity's highlighted flowIds * * @param pvmTransitionList * @param hisActInstList * @param isParallel * @return */ private List<String> getHighlightedFlows(List<PvmTransition> pvmTransitionList, LinkedList<HistoricActivityInstance> hisActInstList, boolean isParallel) { List<String> highLightedFlowIds = new ArrayList<String>(); PvmTransition earliestTrans = null; HistoricActivityInstance earliestHisActInst = null; for (PvmTransition pvmTransition : pvmTransitionList) { String destActId = pvmTransition.getDestination().getId(); HistoricActivityInstance destHisActInst = findHisActInst(hisActInstList, destActId); if (destHisActInst != null) { if (isParallel) { highLightedFlowIds.add(pvmTransition.getId()); } else if (earliestHisActInst == null || (earliestHisActInst.getId().compareTo(destHisActInst.getId()) > 0)) { earliestTrans = pvmTransition; earliestHisActInst = destHisActInst; } } } if ((!isParallel) && earliestTrans != null) { highLightedFlowIds.add(earliestTrans.getId()); } return highLightedFlowIds; } private HistoricActivityInstance findHisActInst(LinkedList<HistoricActivityInstance> hisActInstList, String actId) { for (HistoricActivityInstance hisActInst : hisActInstList) { if (hisActInst.getActivityId().equals(actId)) { return hisActInst; } } return null; } }
最重要的是在控制层,通过流程实例id获取到流程图的文件流时,通过以下方法可以展示在jsp页面上:
public String viewImage(){
InputStream in = repositoryService.getResourceAsStream.getImageStream(deploymentId,imageName);
//此处方法实际项目应该放在service里面
HttpServletResponse resp = ServletActionContext.getResponse();
try { OutputStream out = resp.getOutputStream(); // 把图片的输入流程写入resp的输出流中
byte[] b = new byte[1024];
for (int len = -1; (len= in.read(b))!=-1; ) {
out.write(b, 0, len);
} // 关闭流
out.close();
in.close(); }
catch (IOException e) {
e.printStackTrace();
}
return null;
}
上面这个方法是控制层的代码,在jsp页面上,使用一个<img>标签来展示流程图
<img src = ""> <img>标签的src属性为上面这个方法的路径