activiti动态生成流程图

来源:互联网 发布:淘宝杂货铺推荐 编辑:程序博客网 时间:2024/05/18 14:15

转自:http://www.tuicool.com/articles/FJzMzmJ

最近又开始忙活工作流的相关工作了,第一次接触工作流也就是我在埃森哲的第一个项目,也是最后一个项目。那时候用的是日本的一整套解决方案好像叫-iMart。而进入金山工作后第二个项目也是和工作流密切相关的项目,那时候才接触到了这个开源工作流引擎-Activiti。那时候也是第一次真正了解这些东西。只是从金山离职后回到现在的公司,没想到还要用这些东西,好像又回到了去年的这个时间。

这一次需要重新集成这个引擎,以插件包的方式。其实工作量不大,因为Activiti已经自己封装的很好了,完全可以在日常开发中直接引用它的Service。闲话少说,这次解决工作流流程图问题。简而言之,在任何一条已启动的流程实例查看流程状态,用流程图片的形式展示一下。

提供的Service方法如下:

/*** 获取当前任务流程图** @param processInstanceId* @return*/@Overridepublic 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、已经执行完毕的流程,流程已经进入了流程历史。不管属于以上哪种情况的流程图都会绘制流程走向。

具体代码如下:

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;    }}

我遇到的坑:因为测试使用的是JUNIT跑的测试用例,所以真个流程在同一秒就结束了,所以造成流程历史节点开始时间都一样,排序错乱,流程图绘制出现短线的情况。

原创粉丝点击