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开发中不仅可以直接使用这些控件还可以在这些控件的基础上进行扩展打造自己的控件.在这个系列中如果每个控件 ...
随机推荐
- 使用JDBC连接操作数据库
JDBC简介 Java数据库连接(Java Database Connectivity,JDBC),是一种用于执行SQL语句的Java API,它由一组用Java编程语言编写的类和接口组成. JDBC ...
- 10-Flink集群的高可用(搭建篇补充)
戳更多文章: 1-Flink入门 2-本地环境搭建&构建第一个Flink应用 3-DataSet API 4-DataSteam API 5-集群部署 6-分布式缓存 7-重启策略 8-Fli ...
- .NetCore采取JWT方式进行身份认证
验证与授权 Authentication(身份认证) 认证是系统对请求的用户进行身份识别的过程. Authorization (授权) 授权是对认证通过后的用户进行权限分配的过程.授权简单理解就是:识 ...
- Linux通配符应用详解
一.强大的“*” “*”在通配符中是最常用的一种,代表0个.一个或多个字符.在使用中有三种情况,表示三种不同的含义. 1.单独的“*” 这里指的是只有“*”出现的情况,默认为单独的一个,当然连续敲两个 ...
- Autofac 和 Quartz.Net 自动注入的整合
一:问题场景 在一次项目开发中,项目中已使用了Autofac.在新需求中要用到Quatrz.Net.在任务中使用注入方法,确始终无法使用注入的方法,经过千百次的度娘,终于找到了解决办法!吐槽下度娘真心 ...
- springMVC报404,没有显示地址
正常报404会显示你的错误地址信息,而针对本问题 如果你使用的是springMVC框架,这就代表你的请求被拦截了
- CentOS下MySQL的安装
MySQL数据库是一款比较常用的数据库,大家在练习安装时,可能会遇到各种各样的问题,请大家参考在CentOS系统下MySQL数据库的安装方式.如有任何问题,欢迎留言,本人随时解答. MySQL安装步骤 ...
- Java笔记(day11)
异常:是在运行时期发生的不正常情况. 异常类:在java中用类的形式对不正常情况进行了描述和封装对象,描述不正常的情况的类. 异常就是java通过面向对象的思想将问题封装成了对象.用异常类对其进行描述 ...
- 中文代码示例之5分钟入门TypeScript
"中文编程"知乎专栏原文 Typescript官方文档起的这个噱头名字: TypeScript in 5 minutes, 虽然光看完文章就不止5分钟...走完整个文档流水账如下( ...
- 适合精致女孩使用的APP软件 不容错过的精彩人生
阳光下灿烂,风雨中奔跑,每个人都会遇见美丽的缘分,或深或浅,或浓或淡.所以人生不管遇到什么难题,都要勇往直前.今天分享的软件也是十分精彩的,非常适合精彩的你哦! 薄荷健康 薄荷健康APP是专为想要减肥 ...