前言
上篇文章介绍了如何在Spring Boot2集成activiti6
接下来,小编会一步一步的介绍如何集成在线设计器
Activiti Modoler 介绍
什么是 Activiti Modoler 呢,简单的说就是一个在线画流程图功能模块,它是 Activiti 实现的
虽然说可以通过eclipse、idea等插件去设计画流程图
但是有时候是需要客户动态创建并设计流程图,所以这里就需要把在线设计器集成进自己项目中
所以说,我们要做的就是把 Activiti Modoler 相关模块集成到自己项目中
注意,Activiti 并没有提供接口,这里我们只能通过源码去一步一步集成进来,废话不多说,下面开始
引入文件
首先,maven需要引入新的包(多了很多)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter-actuator</artifactId> <version>6.0.0</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-modeler</artifactId> <version>5.23.0</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-json-converter</artifactId> <version>6.0.0</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-diagram-rest</artifactId> <version>5.23.0</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-rest</artifactId> <version>5.23.0</version> </dependency> <dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>batik-codec</artifactId> <version>1.12</version> <exclusions> <exclusion> <artifactId>commons-io</artifactId> <groupId>commons-io</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>batik-css</artifactId> <version>1.12</version> </dependency> <dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>batik-svg-dom</artifactId> <version>1.12</version> </dependency> <dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>batik-svggen</artifactId> <version>1.12</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-explorer</artifactId> <version>5.23.0</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-simple-workflow</artifactId> <version>5.23.0</version> </dependency> |
然后,我们需要下载 Activiti 开源包:
github:地址https://github.com/Activiti/Activiti/tree/5.x.
下载完解压后,第一步先引入前端文件,位置分别在
\modules\activiti-webapp-explorer2\src\main\webapp
\modules\activiti-webapp-explorer2\src\main\resources
圈中的都复制到项目中:
第二步开始引入后端文件,位置分别在
\modules\activiti-modeler\src\main\java\org\activiti\rest\editor\model
\modules\activiti-modeler\src\main\java\org\activiti\rest\editor\main
也都复制到项目中:
代码编写
首先修改前端的代码,找到 \editor-app\app-cfg.js 文件,把 contextRoot 对应属性路径删了变成空字符
1 2 3 4 5 | var ACTIVITI = ACTIVITI || {}; ACTIVITI.CONFIG = { 'contextRoot' : '', }; |
编写控制层类跳转方法,由于在线流程设计器需要 Model 才能初始化,所以在跳转到该页面去需要创建 Model,代码如下:
ModelerController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | package com.ssactiviti.activiti.controller; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.activiti.editor.constants.ModelDataJsonConstants; import org.activiti.engine.RepositoryService; import org.activiti.engine.repository.Model; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; @Controller @RequestMapping("/modeler") public class ModelerController { @Resource private RepositoryService repositoryService; @Resource private ObjectMapper objectMapper; /** * 添加模型 */ @GetMapping("/save") public void save(HttpServletResponse response) throws IOException { //创建模型 Model modelData = repositoryService.newModel(); ObjectNode modelObjectNode = objectMapper.createObjectNode(); //模型名称 modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, "请假"); //模型版本 modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1); //模型详情 modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, "请假详情"); //以字符串信息存储进信息属性中 modelData.setMetaInfo(modelObjectNode.toString()); //模型名称 modelData.setName("请假"); //模型key modelData.setKey("leave"); //完善ModelEditorSource,这里固定的 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.set("stencilset", stencilSetNode); //添加模型 repositoryService.saveModel(modelData); repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes(StandardCharsets.UTF_8)); response.sendRedirect("/static/activiti/modeler.html?modelId=" + modelData.getId()); } } |
这里因为 Springmvc 会拦截静态资源,所以需要配置静态资源不拦截
MvcConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package com.ssactiviti.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebMvc class MvcConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { //定义一个convert转换消息的对象 FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); //添加fastjson的配置信息 FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setCharset(StandardCharsets.UTF_8); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); //处理中文乱码问题 List<MediaType> fastMediaTypes = new ArrayList<>(); fastMediaTypes.add(MediaType.APPLICATION_JSON); //在convert中添加配置信息. fastConverter.setSupportedMediaTypes(fastMediaTypes); fastConverter.setFastJsonConfig(fastJsonConfig); //将convert添加到converters中 converters.add(fastConverter); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/"); } /** * 接收PUT、DELETE等请求参数 * @return */ @Bean public FormContentFilter formContentFilter() { return new FormContentFilter(); } } |
开始运行程序,访问 http://127.0.0.1:8080/modeler/save,弹出该页面证明整合成功:
踩坑记
坑点1: js前台报这个错
TypeError: Cannot read property ‘namespace’ of undefined
小编第一次也踩过这个坑,网上相关描述也非常少
这里Spring Boot2默认采用jackson,当项目使用fastjson的时候,就会出现json转换异常的问题
也就是 ModelEditorJsonRestResource.java 里面的 getEditorJson() 方法返回了字符串,而不是 json 格式的数据导致的
解决办法就是把返回结果转换成 json 返回,方法返回改成Object,这里小编用的是 fastjson,所以对应方法是:
1 | return JSONObject.parseObject(modelNode.toString()); |
坑点2: js前台报这个错
TypeError: Cannot read property ‘split’ of undefined
小编也踩过这个坑…导致这个坑的原因有两个
第一检查是否引入了 stencilset.json 文件,并且文件路径是否正确,默认存放resources下级目录
第二和坑点一同理,只需要在 StencilsetRestResource.java 里面的 getStencilset() 方法返回转成 json 即可
1 | return JSONObject.parseObject(IOUtils.toString(stencilsetStream, "utf-8")); |
坑点3: 保存模型的时候报错:
org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘application/x-www-form-urlencoded;charset=UTF-8’ not supported
这里只需要在ModelSaveRestResource.java里面,把方法参数改成如下即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | @RequestMapping(value="/model/{modelId}/save", method = RequestMethod.PUT) @ResponseStatus(value = HttpStatus.OK) public void saveModel(@PathVariable String modelId, String name, String description, String json_xml, String svg_xml) { try { Model model = repositoryService.getModel(modelId); ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo()); modelJson.put(MODEL_NAME, name); modelJson.put(MODEL_DESCRIPTION, description); model.setMetaInfo(modelJson.toString()); model.setName(name); repositoryService.saveModel(model); repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes("utf-8")); InputStream svgStream = new ByteArrayInputStream(svg_xml.getBytes("utf-8")); TranscoderInput input = new TranscoderInput(svgStream); PNGTranscoder transcoder = new PNGTranscoder(); // Setup output ByteArrayOutputStream outStream = new ByteArrayOutputStream(); TranscoderOutput output = new TranscoderOutput(outStream); // Do the transformation transcoder.transcode(input, output); final byte[] result = outStream.toByteArray(); repositoryService.addModelEditorSourceExtra(model.getId(), result); outStream.close(); } catch (Exception e) { LOGGER.error("Error saving model", e); throw new ActivitiException("Error saving model", e); } } |
这几个问题出现的比较多,一般解决了就基本没有问题了
汉化
汉化的过程非常简单,只需要分别用中文版的 stencilset.json 和 en.json 替换下即可:
汉化之后的效果:
那么基本代码和效果也演示完毕了
demo也已经放到码云上,获取方式在文章的Spring Boot2 + Activiti6 系列搭建教程开头篇(1) 结尾处
谢谢大家~