Activiti开发案例之代码生成工作流图片
图例

环境
| 软件 | 版本 | 
|---|---|
| SpringBoot | 1.5.10 | 
| activiti-spring-boot-starter-basic | 6.0 | 
生成代码
以下是简化代码:
    /**
	 * 查看实例流程图,根据流程实例ID获取流程图
	 */
	@RequestMapping(value="traceprocess/{instanceId}",method=RequestMethod.GET)
	public void traceprocess(HttpServletResponse response,@PathVariable("instanceId")String instanceId) throws Exception{
		InputStream in = flowUtils.getResourceDiagramInputStream(instanceId);
		ServletOutputStream output = response.getOutputStream();
		IOUtils.copy(in, output);
	}
Flow 工具类:
/**
 * Flow 工具类
 * @author zhipeng.zhang
 */
@Component
public class FlowUtils {
	@Autowired
	RuntimeService runservice;
	@Autowired
    private HistoryService historyService;
	@Autowired
	private RepositoryService repositoryService;
	@Autowired
	private ProcessEngineFactoryBean processEngine;
	/**
	 * 获取历史节点流程图
	 * @param id
	 * @return
	 */
	public  InputStream getResourceDiagramInputStream(String id) {
        try {
            // 获取历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
            // 获取流程中已经执行的节点,按照执行先后顺序排序
            List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(id).orderByHistoricActivityInstanceId().asc().list();
            // 构造已执行的节点ID集合
            List<String> executedActivityIdList = new ArrayList<String>();
            for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
                executedActivityIdList.add(activityInstance.getActivityId());
            }
            // 获取bpmnModel
            BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
            // 获取流程已发生流转的线ID集合
            List<String> flowIds = this.getExecutedFlows(bpmnModel, historicActivityInstanceList);
            // 使用默认配置获得流程图表生成器,并生成追踪图片字符流
            ProcessDiagramGenerator processDiagramGenerator = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
            //你也可以 new 一个
            //DefaultProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
            InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", null, 2.0);
            return imageStream;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
	private List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 流转线ID集合
        List<String> flowIdList = new ArrayList<String>();
        // 全部活动实例
        List<FlowNode> historicFlowNodeList = new LinkedList<FlowNode>();
        // 已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstanceList = new LinkedList<HistoricActivityInstance>();
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            historicFlowNodeList.add((FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true));
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstanceList.add(historicActivityInstance);
            }
        }
        // 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
        FlowNode currentFlowNode = null;
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstanceList) {
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlowList = currentFlowNode.getOutgoingFlows();
            /**
             * 遍历outgoingFlows并找到已已流转的
             * 满足如下条件认为已已流转:
             * 1.当前节点是并行网关或包含网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
             * 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最近的流转节点视为有效流转
             */
            FlowNode targetFlowNode = null;
            if (BpmsActivityTypeEnum.PARALLEL_GATEWAY.getType().equals(currentActivityInstance.getActivityType())
                    || BpmsActivityTypeEnum.INCLUSIVE_GATEWAY.getType().equals(currentActivityInstance.getActivityType())) {
                // 遍历历史活动节点,找到匹配Flow目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlowList) {
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicFlowNodeList.contains(targetFlowNode)) {
                        flowIdList.add(sequenceFlow.getId());
                    }
                }
            } else {
                List<Map<String, String>> tempMapList = new LinkedList<Map<String,String>>();
                // 遍历历史活动节点,找到匹配Flow目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlowList) {
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                            tempMapList.add(UtilMisc.toMap("flowId", sequenceFlow.getId(), "activityStartTime", String.valueOf(historicActivityInstance.getStartTime().getTime())));
                        }
                    }
                }
                // 遍历匹配的集合,取得开始时间最早的一个
                long earliestStamp = 0L;
                String flowId = null;
                for (Map<String, String> map : tempMapList) {
                    long activityStartTime = Long.valueOf(map.get("activityStartTime"));
                    if (earliestStamp == 0 || earliestStamp >= activityStartTime) {
                        earliestStamp = activityStartTime;
                        flowId = map.get("flowId");
                    }
                }
                flowIdList.add(flowId);
            }
        }
        return flowIdList;
    }
}
UtilMisc 工具类:
public class UtilMisc {
    public static <V, V1 extends V, V2 extends V> Map<String, V> toMap(String name1, V1 value1, String name2, V2 value2) {
        return populateMap(new HashMap<String, V>(), name1, value1, name2, value2);
    }
    @SuppressWarnings("unchecked")
    private static <K, V> Map<String, V> populateMap(Map<String, V> map, Object... data) {
        for (int i = 0; i < data.length;) {
            map.put((String) data[i++], (V) data[i++]);
        }
        return map;
    }
}
工作流枚举类:
/**
 * 工作流枚举类
 * @author zhipeng.zhang
 */
public enum BpmsActivityTypeEnum {
    START_EVENT("startEvent", "开始事件"),
    END_EVENT("endEvent", "结束事件"),
    USER_TASK("userTask", "用户任务"),
    EXCLUSIVE_GATEWAY("exclusiveGateway", "排他网关"),
    PARALLEL_GATEWAY("parallelGateway", "并行网关"),
    INCLUSIVE_GATEWAY("inclusiveGateway", "包含网关");
    private String type;
    private String name;
    private BpmsActivityTypeEnum(String type, String name) {
        this.type = type;
        this.name = name;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
浏览器直接发送以下格式请求,就可以查看实时流程图:
# traceprocess 后面追加流程ID
http://localhost:8080/traceprocess/20190214
Activiti开发案例之代码生成工作流图片的更多相关文章
- Activiti开发案例之activiti-app工作流导出图片
		前言 自从 Activiti 和 JBPM4 分家以后,Activiti 目前已经发展到了版本7,本着稳定性原则我们最终选择了6,之前还有一个版本5. 问题 在开发使用的过程中发现 Activiti ... 
- Activiti 开发案例之动态指派任务
		流程图 以上是一个请假的流程图,以下为流程任务节点描述: 员工发起请假流程 部门经理审批 同意则进入人事审批 拒绝则调整申请或者直接结束流程 人事审批通过则进入销假环节 人事审批拒绝则调整申请或者直接 ... 
- Activiti开发案例之activiti-app更换数据源
		前言 由于Activiti 默认使用的数据库是H2数据库,重启服务后相关数据会丢失.为了永久保存,所以要配置关系型数据库,这里我们选择 SqlServer ,有钱任性. 环境 Activiti6,Sq ... 
- 百度UEditor开发案例(JSP)
		本案例的开发环境:MyEclipse+tomcat+jdk 本案例的开发内容: 用百度编辑器发布新闻(UEditor的初始化开发部署) 编辑已发过的新闻(UEditor的应用——编辑旧文章) ... 
- webpack打包多html开发案例新
		闲来无事在原来简单打包案例的基础上,参考vue-cli的打包代码,改为多文件打包. 区别于上篇文章<webpack打包多html开发案例>,此次打包根据开发的不同环节进行打包,也就是有开发 ... 
- 《实战突击:PHP项目开发案例整合(第2版)(含DVD光盘1张)》
		<实战突击:PHP项目开发案例整合(第2版)(含DVD光盘1张)> 基本信息 作者: 徐康明 辛洪郁 出版社:电子工业出版社 ISBN:9787121221378 上架时间:2014 ... 
- 前端到后台ThinkPHP开发整站--php开发案例
		前端到后台ThinkPHP开发整站--php开发案例 总结 还是需要做几个案例,一天一个为佳,那样才能做得快. 从需求分析着手,任务体系要构建好,这样才能非常高效. 转自: 前端到后台ThinkPHP ... 
- Bootstrap手机网站开发案例
		Bootstrap手机网站开发案例 一.总结 一句话总结:Bootstrap手机网站开发注意事项(3点):a.引入viewpoint声明,b.通过屏幕宽动态控制元素显隐 c.图片添加自适应 1.Boo ... 
- iOS开发系列--无限循环的图片浏览器
		--UIKit之UIScrollView 概述 UIKit框架中有大量的控件供开发者使用,在iOS开发中不仅可以直接使用这些控件还可以在这些控件的基础上进行扩展打造自己的控件.在这个系列中如果每个控件 ... 
随机推荐
- 基于Cisco packet tracer的AAA认证
			---恢复内容开始--- 1.Topology Diagram拓扑图 2.配置ip地址 3.路由互通 pc1--pc2 pc1--pc3 pc2--pc3 4.指令文件 R1: R1(config)# ... 
- Ubuntu 安装php+mysql+nginx
			0x01 安装PHP https://blog.csdn.net/Msmile_my/article/details/73647809 1.添加php的仓库 sudo apt-get instal ... 
- 【python3基础】相对路径,‘/’,‘./’,‘../’
			python3相对路径 “/” 前有没有 “.” ,有几个“.”,意思完全不一样. “/”:表示根目录,在windows系统下表示某个盘的根目录,如“E:\”: “./”:表示当前目录:(表示当前目录 ... 
- Centos扩容swap分区
			今天在vps上装了个msf,装了一半竟然被kill掉了,因为RAM只有512M,swap也只有130M.遂到网上寻求解决办法,下面是我的笔记: 查看当前swap大小: free -h 修改swap大小 ... 
- Python爬虫入门教程 47-100 mitmproxy安装与安卓模拟器的配合使用-手机APP爬虫部分
			1. 准备下载软件 介绍一款爬虫辅助工具mitmproxy ,mitmproxy 就是用于MITM的proxy,MITM中间人攻击.说白了就是服务器和客户机中间通讯多增加了一层.跟Fiddler和Ch ... 
- 【Android Studio安装部署系列】九、Android Studio常用配置以及快捷键
			版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 整理Android Studio的常用配置和快捷键. 常用配置 显示行号 临时显示 永久显示 File——Settings——Edi ... 
- VLayoutDemo【VLayout的简单使用demo(基于V1.2.8版本)】
			版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 VirtualLayout是一个针对RecyclerView的LayoutManager扩展, 主要提供一整套布局方案和布局间的组件 ... 
- asp.net三层架构增删改查
			数据库 use master if exists (select * from sysdatabases where name='bond') drop database bond create da ... 
- Raptor入门与安装
			作为计算机导论的一部分,Raptor的图形化界面可以让编程的初学者更加容易深入理解算法,可以作为一个简单入门的学习工具. 1.Raptor是什么? Raptor( the Rapid Algorith ... 
- JFreeChart画图+jsp页面显示实现统计图
			1 开发环境: 1.eclipse(可替换) 2.jfreechart-1.0.19 2 说明: (1) source目录:为 jfreechart的源码目录:不会的主要看这里.因为他的文档是收费的. ... 
