SpringBoot使用activiti自定义流程demo解析
环境搭建【这里直接讲解自定义流程】
集成 Activiti Modeler
下载源码
我这里选用的是 Activiti 5.23.0 版本的页面,下载 zip,解压
Activiti 5.23.0 源码
pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.activiti6</groupId>
<artifactId>activiti6-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<druid.version>1.1.6</druid.version>
<activiti.version>6.0.0</activiti.version>
<apache.xmlgraphics.version>1.7</apache.xmlgraphics.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- mysql 驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Druid DataSource -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- activiti -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
<exclusions>
<exclusion>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>${apache.xmlgraphics.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-css</artifactId>
<version>${apache.xmlgraphics.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svg-dom</artifactId>
<version>${apache.xmlgraphics.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>${apache.xmlgraphics.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
代码集成
前端代码集成
在项目中的 resource 文件夹下新建一个 static 文件夹
SpringBoot 能自动读取 static 目录下的静态文件,因此文件夹名称不可随意更改
找到 activiti-webapp-explorer2 包
将 webapp 下的 diagram-viewer 文件夹,editor-app 文件夹,modeler.html 文件复制到 static 下
diagram-viewer:流程图跟踪组件
editor-app:目录中包含设计器里面所有的资源:angular.js、oryx.js以及配套的插件及css
modeler.html:流程设计器的主页面,用来引入各种web资源
路径:Activiti-5.23.0\modules\activiti-webapp-explorer2\src\main\webapp
后端代码集成
将 Activit 5.23.0 项目中 resource 文件夹下的 stencilset.json 放到自己项目的 resource 目录下
路径:Activiti-5.23.0\modules\activiti-webapp-explorer2\src\main\resources
在我提供的demo中有汉化版本,这里只有国际版本
找到 activiti-modeler 包
将里面的 StencilsetRestResource.java,ModelEditorJsonRestResource.java,ModelSaveRestResource.java 文件放到自己的项目里
- StencilsetRestResource.java:用于读取 stencilset.json 文件
- ModelEditorJsonRestResource.java:用户获取流程数据
- ModelSaveRestResource.java:用于保存流程数据
路径:Activiti-5.23.0\modules\activiti-modeler\src\main\java\org\activiti\rest\editor
项目结构
创建流程与部署流程
package com.activiti6.controller;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.runtime.ProcessInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* 流程控制器
* zhudunfrng
*/
@Controller
public class ModelerController{
private static final Logger logger = LoggerFactory.getLogger(ModelerController.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private HistoryService historyService;
@Autowired
private RuntimeService runtimeService;
@RequestMapping("index")
public ModelAndView index(ModelAndView modelAndView) {
modelAndView.setViewName("index");
modelAndView.addObject("modelList", repositoryService.createModelQuery().list());
return modelAndView;
}
/**
* 跳转编辑器页面
* @return
*/
@GetMapping("editor")
public String editor(){
return "modeler";
}
/**
* 创建模型
* @param response
* @param name 模型名称
* @param key 模型key
*/
@RequestMapping("/create")
public void create(HttpServletResponse response,String name,String key) throws IOException {
logger.info("创建模型入参name:{},key:{}",name,key);
Model model = repositoryService.newModel();
ObjectNode modelNode = objectMapper.createObjectNode();
modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, "");
modelNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
model.setName(name);
model.setKey(key);
model.setMetaInfo(modelNode.toString());
//存入表act_re_model
repositoryService.saveModel(model);
createObjectNode(model.getId());
response.sendRedirect("/editor?modelId="+ model.getId());
logger.info("创建模型结束,返回模型ID:{}",model.getId());
}
/**
* 创建模型时完善ModelEditorSource,这里是对画布的相关设置
* @param modelId
*/
@SuppressWarnings("deprecation")
private void createObjectNode(String modelId){
logger.info("创建模型完善ModelEditorSource入参模型ID:{}",modelId);
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "canvas");
editorNode.put("resourceId", "canvas");
ObjectNode stencilSetNode = objectMapper.createObjectNode();
stencilSetNode.put("namespace","http://b3mn.org/stencilset/bpmn2.0#");
editorNode.put("stencilset", stencilSetNode);
try {
repositoryService.addModelEditorSource(modelId,editorNode.toString().getBytes("utf-8"));
} catch (Exception e) {
logger.info("创建模型时完善ModelEditorSource服务异常:{}",e);
}
logger.info("创建模型完善ModelEditorSource结束");
}
/**
* 发布流程///流程部署
* @param modelId 模型ID
* @return
*/
@ResponseBody
@RequestMapping("/publish")
public Object publish(String modelId){
logger.info("流程部署入参modelId:{}",modelId);
Map<String, String> map = new HashMap<String, String>();
try {
Model modelData = repositoryService.getModel(modelId);//获取相应的模型信息,act_re_model
byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());//获取相应的流程文件信息,act_ge_bytearray
if (bytes == null) {//没有相应的流程文件
logger.info("部署ID:{}的模型数据为空,请先设计流程并成功保存,再进行发布",modelId);
map.put("code", "FAILURE");
return map;
}
JsonNode modelNode = new ObjectMapper().readTree(bytes);//解析相应的流程文件
BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
//部署
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName())//模型名称
.addBpmnModel(modelData.getKey()+".bpmn20.xml", model)
.deploy();//部署相应的流程
modelData.setDeploymentId(deployment.getId());//获取流程部署后的流程id
repositoryService.saveModel(modelData);//保存到act_re_model表中
map.put("code", "SUCCESS");
} catch (Exception e) {
logger.info("部署modelId:{}模型服务异常:{}",modelId,e);
map.put("code", "FAILURE");
}
logger.info("流程部署出参map:{}",map);
return map;
}
/**
* 撤销流程定义
* @param modelId 模型ID
* @return
*/
@ResponseBody
@RequestMapping("/revokePublish")
public Object revokePublish(String modelId){
logger.info("撤销发布流程入参modelId:{}",modelId);
Map<String, String> map = new HashMap<String, String>();
Model modelData = repositoryService.getModel(modelId);//获取相应的模型信息,act_re_model
if(null != modelData){
try {
/**
* 参数不加true:为普通删除,如果当前规则下有正在执行的流程,则抛异常
* 参数加true:为级联删除,会删除和当前规则相关的所有信息,包括历史
*/
//根据流程的部署ID删除act_re_procdef、act_re_deployment表中的数据,后面的true是将还有未完成任务的流程强制删除
repositoryService.deleteDeployment(modelData.getDeploymentId(),true);
map.put("code", "SUCCESS");
} catch (Exception e) {
logger.error("撤销已部署流程服务异常:{}",e);
map.put("code", "FAILURE");
}
}
logger.info("撤销发布流程出参map:{}",map);
return map;
}
/**
* 删除流程实例 流程正在运行中
* @param modelId 模型ID
* @return
*/
@ResponseBody
@RequestMapping("/delete")
public Object deleteProcessInstance(String modelId){
logger.info("删除流程实例入参modelId:{}",modelId);
Map<String, String> map = new HashMap<String, String>();
Model modelData = repositoryService.getModel(modelId);//获取相应的模型信息,act_re_model
if(null != modelData){
try {
//流程启动后会在act_ru_execution表中查到,根据流程定义的key进行查询
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processDefinitionKey(modelData.getKey()).singleResult();
if(null != pi) {
//根据流程实例ID进行删除,act_ru_execution
runtimeService.deleteProcessInstance(pi.getId(), "");
//删除历史流程实例act_hi_procinst
historyService.deleteHistoricProcessInstance(pi.getId());
}
map.put("code", "SUCCESS");
} catch (Exception e) {
logger.error("删除流程实例服务异常:{}",e);
map.put("code", "FAILURE");
}
}
logger.info("删除流程实例出参map:{}",map);
return map;
}
/**
* 启动流程
*/
@RequestMapping("/start/{id}")
public void startProcess(@PathVariable("id")String processDefinitionId){
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId);
//从act_ru_exection中查询
System.out.println("流程启动成功:\n"+"执行实例id:"+processInstance.getId()
+"\n流程定义id:"+processInstance.getProcessDefinitionId()
+"\n流程实例id:"+processInstance.getProcessInstanceId());
}
}
SpringBoot使用activiti自定义流程demo解析的更多相关文章
- 关于我上传的activiti自定义流程demo的说明
最近又收到了一些询问activiti的问题,其中好几个都是向我索要我上传的这个activiti自定义流程demo的数据库设计. 索要的多了,而我早就把这个库给删掉了,所以我便觉得有必要做一个说明: 我 ...
- activiti自定义流程之自定义表单(二):创建表单
注:环境配置:activiti自定义流程之自定义表单(一):环境配置 在上一节自定义表单环境搭建好以后,我就正式开始尝试自己创建表单,在后台的处理就比较常规,主要是针对ueditor插件的功能在前端进 ...
- activiti自定义流程之整合(二):使用angular js整合ueditor创建表单
注:整体环境搭建:activiti自定义流程之整合(一):整体环境配置 基础环境搭建完毕,接下来就该正式着手代码编写了,在说代码之前,我觉得有必要先说明一下activit自定义流程的操作. 抛开自定义 ...
- activiti自定义流程之自定义表单(三):表单列表及预览和删除
注:(1)环境配置:activiti自定义流程之自定义表单(一):环境配置 (2)创建表单:activiti自定义流程之自定义表单(二):创建表单 自定义表单创建成功,要拿到activiti中使用,自 ...
- activiti自定义流程之整合(一):整体环境配置
结合之前所说的自定义流程的思路,分别是后台.前台.整合,之前的内容也分别进行了相关的练习和尝试,现在就该到了最后的整合了,依旧是以实现功能为目的,细节暂且不去管他. 因为我们实际项目后端用的是spri ...
- activiti自定义流程之自定义表单(一):环境配置
先补充说一下自定义流程整个的思路,自定义流程的目的就是为了让一套代码解决多种业务流程,比如请假单.报销单.采购单.协作单等等,用户自己来设计流程图. 这里要涉及到这样几个基本问题,一是不同的业务需求, ...
- activiti自定义流程之整合(五):启动流程时获取自定义表单
流程定义部署之后,自然就是流程定义列表了,但和前一节一样的是,这里也是和之前单独的activiti没什么区别,因此也不多说.我们先看看列表页面以及对应的代码,然后在一步步说明点击启动按钮时如何调用自定 ...
- activiti自定义流程之整合(四):整合自定义表单部署流程定义
综合前几篇博文内容,我想在整合这一部分中应该会有很多模块会跳过不讲,就如自定义表单的表单列表那一块,因为这些模块在整合的过程中都几乎没有什么改动,再多讲也是重复无用功. 正因为如此,在创建了流程模型之 ...
- activiti自定义流程之整合(七):完成我的申请任务
在上一篇的获得我的申请中,可以看到js代码中还包含了预览和完成任务的代码,既然上一篇已经罗列了相关代码,这里也就不重复. 那么需要补充的是,在上边的完成任务的js代码中,我们还调用了getTaskFo ...
随机推荐
- 浅谈HASH长度拓展攻击
前言 最近在做CTF题的时候遇到这个考点,想起来自己之前在做实验吧的入门CTF题的时候遇到过这个点,当时觉得难如看天书一般,现在回头望去,仔细琢磨一番感觉也不是那么难,这里就写篇文章记录一下自己的学习 ...
- 剑指 Offer 55 - II. 平衡二叉树
题目描述 输入一棵二叉树的根节点,判断该树是不是平衡二叉树.如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树. 示例1: 给定二叉树 [3,9,20,null,null,1 ...
- JVM学习第三天(JVM的执行子系统)之类加载机制
好几天没有学习了,前几天因为导出的事情,一直在忙,今天继续学习, 其实今天我也遇到了一个问题,如果有会的兄弟可以评论留给我谢谢; 问题:fastJSON中JSONObject.parseObject做 ...
- [Failed]Tomcat cluster方案共享session配置出错,sigh....
后继发展:https://www.cnblogs.com/xiandedanteng/p/12134300.html 参考网文一:多个Tomcat之间实现Session共享 参考网文二:Tomcat官 ...
- Python中自己不熟悉的知识点记录
重点笔记: Python 它是动态语言 动态语言的定义:动态编程语言 是 高级程序设计语言 的一个类别,在计算机科学领域已被广泛应用.它是一类 在 运行时可以改变其结构的语言 : ...
- TouchAction实现连续滑动设置手势密码
首先使用工具获取到元素坐标位置,可以看到起始位置是[144,462],终点位置是[576,894] 分析: 该图形可以横竖划分六等分 那么第一个圆中心点的坐标: x=144+(576-144)/6 y ...
- 3896. 【NOIP2014模拟10.26】战争游戏
鉴于如此一道恶心的题,作者还花了一个晚上草草学了tarjan. 于是乎,这道题就是道tarjan 具体怎么实现呢?正解上有个什么树形DP,看的我一脸懵逼. 这道题可以运用到tarjan一个高科技的算法 ...
- 记一次 node 项目重构改进
摘要:经常听到有祖传的代码一说,就是一些项目经过了很长时间的维护,经过了很多人之手,业务逻辑堆叠的越来越多,然后就变成了一个越来越难以维护. 经常听到有祖传的代码一说,就是一些项目经过了很长时间的维护 ...
- react项目结合echarts,百度地图实现热力图
一.最近在一个react项目(antd pro)中需要展示一个热力地图.需求是: 1.热力地图可缩放: 2.鼠标点击可以展示该点地理坐标,及热力值. 3.初始化时候自适应展示所有的热力点. 4.展示热 ...
- ECMAScript 6新特性简介
目录 简介 ECMAScript和JavaScript的关系 let和const 解构赋值 数组的扩展 函数的扩展 简介 ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言 ...