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开发中不仅可以直接使用这些控件还可以在这些控件的基础上进行扩展打造自己的控件.在这个系列中如果每个控件 ...
随机推荐
- idapython import 'site' failed
打开IDA出现这个错误提示,问题根源是IDA找不到python,安装2.7版本的python并设置%PYTHONHOME%变量为python安装目录就可以了 如果已经安装了python可以在命令行下用 ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十二 || 三种跨域方式比较,DTOs(数据传输对象)初探
更新反馈 1.博友@落幕残情童鞋说到了,Nginx反向代理实现跨域,因为我目前还没有使用到,给忽略了,这次记录下,为下次补充.此坑已填 2.提示:跨域的姊妹篇——<三十三║ ⅖ 种方法实现完美跨 ...
- 【深度学习】--GAN从入门到初始
一.前述 GAN,生成对抗网络,在2016年基本火爆深度学习,所有有必要学习一下.生成对抗网络直观的应用可以帮我们生成数据,图片. 二.具体 1.生活案例 比如假设真钱 r 坏人定义为G 我们通过 ...
- 2.Flask jinjia2模板
1.1.模板传参 (1)主程序 from flask import Flask,render_template app = Flask(__name__) @app.route('/') def he ...
- 图像检索(4):IF-IDF,RootSift,VLAD
TF-IDF RootSift VLAD TF-IDF TF-IDF是一种用于信息检索的常用加权技术,在文本检索中,用以评估词语对于一个文件数据库中的其中一份文件的重要程度.词语的重要性随着它在文件中 ...
- SLAM+语音机器人DIY系列:(一)Linux基础——2.安装Linux发行版ubuntu系统
摘要 由于机器人SLAM.自动导航.语音交互这一系列算法都在机器人操作系统ROS中有很好的支持,所以后续的章节中都会使用ROS来组织构建代码:而ROS又是安装在Linux发行版ubuntu系统之上的, ...
- go并发调度原理学习
aaarticlea/jpeg;base64,/9j/4AAQSkZJRgABAQAAkACQAAD/4QB0RXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAA
- 前后端分离密码登陆加密RSA方案(java后端)
前言:密码加密有很多种方案,这里不做过多讨论,本篇文章是基于RSA加密实现. 首先在前端工程中需要引入加密js: "jsencrypt": "2.3.1",(注 ...
- Eclipse目录实解
从左1图中可以看到,其中的src/main/java存放java文件,src/main/resources存放项目用到的资源(js,css,图片,文件等).下面的两个文件夹是用来存放测试文件和资源的( ...
- 零基础学Python--------第9章 异常处理及程序调试
第9章 异常处理及程序调试 9.1 异常概述 在程序运行过程中,经常会遇到各种各样的错误,这些错误统称为“异常”.这些异常有的是由于开发者将关键字敲错导致的,这类错误多数产生的是SyntaxError ...