手机网站免费生成,wordpress 图片title,网站内的搜索是怎么做的,自媒体简单的Activiti Modoler 流程在线编辑器
1.需求
我们公司使用的流程是activiti5.22.0#xff0c;版本有些老了#xff0c;然后使用的编辑器都是eclipse的流程编辑器插件#xff0c;每次编辑流程需要打开eclipse进行编辑#xff0c;然后再导入到项目里面#xff0c;不是特…简单的Activiti Modoler 流程在线编辑器
1.需求
我们公司使用的流程是activiti5.22.0版本有些老了然后使用的编辑器都是eclipse的流程编辑器插件每次编辑流程需要打开eclipse进行编辑然后再导入到项目里面不是特别方便所以我们决定使用官方提供的 Activiti Modoler实现项目集成在线编辑器方便流程的发布编辑以及部署。
2.具体实现步骤
整体的实现步骤就是从官网下载对应的activiti5.22.0 或者版本相近的包然后找到对应的前端需要集成的文件以及controller等文件添加到自己的项目中我使用的springboot项目然后需要配置静态资源的对应的地址信息便可以进行简单的使用了。
2.1 添加pom需要jar包
我们此处就只列举了流程所需包其他包简略 !-- activiti 5.22.0启动器排除mybatis依赖 --dependencygroupIdorg.activiti/groupIdartifactIdactiviti-spring-boot-starter-basic/artifactIdversion5.22.0/versionexclusionsexclusiongroupIdorg.mybatis/groupIdartifactIdmybatis/artifactId/exclusion/exclusions/dependencydependencygroupIdorg.activiti/groupIdartifactIdactiviti-diagram-rest/artifactIdversion5.22.0/version/dependency!-- Activiti在线设计 --dependencygroupIdorg.activiti/groupIdartifactIdactiviti-modeler/artifactIdversion5.22.0/version/dependency2.2 下载包
我们下载的地址为 activiti5.x.x 官方包
2.3 解压复制文件到项目中
替换文件主要是两部分一部分为java文件一部分为静态资源文件首先替换java文件 activiti-webexplore\Activiti-5.x\modules\activiti-modeler\src\main\java\org\activiti\rest\editor 向下的所有java文件到自己项目中 注意ModelSaveRestResource 对应的请求方式修改为 POST。 在之后我们需要添加一个发起流程图编辑或新增的入口 ModelerController发起以及修改全部从此方法进入最后导入后台文件效果如下 最后添加的几个controller代码如下 ModelEditorJsonRestResource package cn.git.workflow.modeler;import cn.git.common.exception.ServiceException;
import cn.git.common.util.LogUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import java.nio.charset.StandardCharsets;/*** description: 通过modelId获取流程model json数据* program: bank-credit-sy* author: lixuchun* create: 2024-11-21*/
Api(value model流程设计器获取json数据, tags model流程设计器获取json数据)
Slf4j
RestController
public class ModelEditorJsonRestResource implements ModelDataJsonConstants {Autowiredprivate RepositoryService repositoryService;Autowiredprivate ObjectMapper objectMapper;/*** 获取流程图json数据** param modelId* return*/ApiOperation(value 获取流程图json数据, notes 获取流程图json数据)RequestMapping(value /model/{modelId}/json, method RequestMethod.GET, produces application/json)public Object getEditorJson(PathVariable String modelId) {// 最终响应数据ObjectNode modelNode null;Model model repositoryService.getModel(modelId);// 获取流程定义if (ObjectUtil.isNotNull(model)) {try {// 获取流程定义的JSON数据if (StrUtil.isNotBlank(model.getMetaInfo())) {modelNode (ObjectNode) objectMapper.readTree(model.getMetaInfo());} else {modelNode objectMapper.createObjectNode();modelNode.put(MODEL_NAME, model.getName());}// 设置流程定义的IDmodelNode.put(MODEL_ID, model.getId());// 获取流程定义的JSON数据ObjectNode editorJsonNode (ObjectNode) objectMapper.readTree(new String(repositoryService.getModelEditorSource(model.getId()), StandardCharsets.UTF_8));modelNode.put(model, editorJsonNode);} catch (Exception e) {log.error(StrUtil.format(通过modelId获取model对应json数据失败失败信息为[{}], LogUtil.getStackTraceInfo(e)));throw new ServiceException(通过modelId获取model对应json数据失败!);}}return JSONObject.parseObject(modelNode.toString());}
} ModelerController package cn.git.workflow.modeler;import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
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 org.springframework.web.bind.annotation.RequestParam;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.rmi.ServerException;/** * description: model流程设计器保存* program: bank-credit-sy* author: lixuchun* create: 2024-11-21*/
Api(value model流程设计器保存, tags model流程图画图模块保存)
Controller
RequestMapping(/modeler)
public class ModelerController {Resourceprivate RepositoryService repositoryService;Resourceprivate ObjectMapper objectMapper;/*** 初始化流程设计器方法跳转至流程设计器** param modelName 模型名称* param modelKey 模型key* param modelDescription 模型描述* param modelId 模型id* return*/ApiOperation(value 初始化流程设计器方法跳转至流程设计器, notes 初始化流程设计器方法跳转至流程设计器)GetMapping(/save)public void save(HttpServletResponse response,RequestParam(value modelName, required false) String modelName,RequestParam(value modelKey, required false) String modelKey,RequestParam(value modelDescription, required false) String modelDescription,RequestParam(value modelId, required false) String modelId) throws IOException {// 如果有modelId则直接跳转if (StrUtil.isNotBlank(modelId)) {Model model repositoryService.getModel(modelId);if (ObjectUtil.isNotNull(model)) {response.sendRedirect(/static/activiti/modeler.html?modelId model.getId());} else {throw new ServerException(StrUtil.format(模型不存在请检查modelId[{}], modelId));}} else {// modelId为空则表示请求为新建一个模型则进行必填参数校验if (StrUtil.isBlank(modelName) ||StrUtil.isBlank(modelKey) || StrUtil.isBlank(modelDescription)) {throw new ServerException(参数[modelName, modelKey, modelDescription]不能为空,请确认!);}// 创建模型Model modelData repositoryService.newModel();ObjectNode modelObjectNode objectMapper.createObjectNode();// 模型名称modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelName);// 模型版本modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);// 模型详情modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, modelDescription);// 以字符串信息存储进信息属性中modelData.setMetaInfo(modelObjectNode.toString());// 模型名称modelData.setName(modelName);// 模型keymodelData.setKey(modelKey);// 初始化json数据ObjectNode editorNode objectMapper.createObjectNode();editorNode.put(id, canvas);editorNode.put(resourceId, canvas);// 创建一个stencilset节点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());}}}ModelSaveRestResource 注意此方法请求方式修改为POST package cn.git.workflow.modeler;import cn.git.common.exception.ServiceException;
import cn.git.common.result.Result;
import cn.git.common.util.LogUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;/** * description: 控制器类用于保存activiti模型* program: bank-credit-sy* author: lixuchun* create: 2024-11-21*/
Api(value model流程设计器编辑保存, tags model流程设计器编辑保存)
RestController
public class ModelSaveRestResource implements ModelDataJsonConstants {protected static final Logger LOGGER LoggerFactory.getLogger(ModelSaveRestResource.class);Autowiredprivate RepositoryService repositoryService;/*** Jackson ObjectMapper*/Autowiredprivate ObjectMapper objectMapper;/*** 模型修改通过jsno格式保存信息** param modelId 模型 ID* param name 模型名称* param description 模型描述* param json_xml JSON 格式的模型数据* param svg_xml SVG 格式的模型数据*/ApiOperation(value 模型修改通过jsno格式保存信息, notes 模型修改通过jsno格式保存信息)RequestMapping(value /model/{modelId}/save, method RequestMethod.POST)ResponseStatus(value HttpStatus.OK)public ResultString saveModel(PathVariable String modelId, String name, String description, String json_xml, String svg_xml) {// 创建一个 ByteArrayOutputStream 对象用于存储转换后的 PNG 图像try (ByteArrayOutputStream outStream new ByteArrayOutputStream()) {// 获取指定 ID 的模型Model model repositoryService.getModel(modelId);// 将模型的元数据转换为 JSON 对象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);// 将 JSON 格式的模型数据保存到模型编辑源repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes(StandardCharsets.UTF_8));// 将 SVG 格式的模型数据转换为 PNG 图像InputStream svgStream new ByteArrayInputStream(svg_xml.getBytes(StandardCharsets.UTF_8));TranscoderInput input new TranscoderInput(svgStream);PNGTranscoder transcoder new PNGTranscoder();// 设置输出流TranscoderOutput output new TranscoderOutput(outStream);// 执行转换transcoder.transcode(input, output);final byte[] result outStream.toByteArray();// 将转换后的 PNG 图像保存到模型编辑源额外数据repositoryService.addModelEditorSourceExtra(model.getId(), result);return Result.ok(保存成功!);} catch (Exception e) {throw new ServiceException(StrUtil.format(模型保存失败具体失败原因为[{}], LogUtil.getStackTraceInfo(e)));}}
} StencilsetRestResource package cn.git.workflow.modeler;import java.io.InputStream;import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.activiti.engine.ActivitiException;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;/*** description: 在线模板中文转义文件获取* program: bank-credit-sy* author: lixuchun* create: 2024-11-20*/
Api(value model流程设计器加载中文模板文件, tags model流程设计器加载中文模板文件)
RestController
public class StencilsetRestResource {/*** 在线编辑器中文模板文件*/public static final String EDITOR_CH_FILE stencilset.json;/*** 获取在线编辑器中文模板文件** return*/ApiOperation(value 加载stencilset.json中文文件, notes 加载stencilset.json中文文件)RequestMapping(value /editor/stencilset, method RequestMethod.GET, produces application/json;charsetutf-8)public ResponseBody Object getStencilset() {InputStream inputStream this.getClass().getClassLoader().getResourceAsStream(EDITOR_CH_FILE);try {assert inputStream ! null;return JSONObject.parseObject(IOUtils.toString(inputStream, utf-8));} catch (Exception e) {throw new ActivitiException(获取流程在线编辑器中文模板失败!, e);}}
}
我们的项目是springboot前后端分离项目我们整合的流程在线编辑器与前端项目需要进行 iframe 嵌套所以有跨域问题。需要开放允许跨域。具体代码如下
GlobalCrossConfigpackage cn.git.workflow.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;/*** 通用跨域设置允许跨域访问本项目* program: bank-credit-sy* author: lixuchun* create: 2021-06-01*/
Configuration
public class GlobalCrossConfig {/*** 设置跨域配置信息*/Beanpublic CorsFilter corsFilter() {CorsConfiguration corsConfiguration new CorsConfiguration();corsConfiguration.addAllowedOrigin(*);corsConfiguration.setAllowCredentials(true);corsConfiguration.addAllowedMethod(*);corsConfiguration.addAllowedHeader(*);corsConfiguration.addExposedHeader(*);UrlBasedCorsConfigurationSource configSource new UrlBasedCorsConfigurationSource();configSource.registerCorsConfiguration(/**, corsConfiguration);return new CorsFilter(configSource);}}
除此之外还需要开启允许进行iframe嵌套需要禁用X-Frame-Options头。X-Frame-Options是一个HTTP响应头用于控制浏览器是否允许在一个 frame, iframe, 或 object 中加载页面。默认情况下这个头会设置为 DENY 或 SAMEORIGIN以防止点击劫持Clickjacking攻击。
调用 disable() 方法会禁用 X-Frame-Options 头即不在响应中发送这个头。这样页面可以在任何域的 frame 或 iframe 中加载具体操作代码如下
SecurityConfigpackage cn.git.workflow.config;import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;/*** description: 禁用activiti-rest引入后的security主动校验功能* program: bank-credit-sy* author: lixuchun* create: 2024-06-11*/
EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 禁用activiti-rest引入后的security主动校验功能* param http* throws Exception*/Overridepublic void configure(HttpSecurity http) throws Exception {// 关闭securityhttp.httpBasic().disable().csrf().disable()// 允许iframe嵌套.headers().frameOptions().disable();http.authorizeRequests().anyRequest().permitAll();}
}我们还需要设置静态资源信息提供我们在线编辑器的再跳转功能具体设置如下 WebMvcConfig package cn.git.workflow.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** description: 添加fastjson转换器* program: bank-credit-sy* author: lixuchun* create: 2024-11-21*/
Configuration
class WebMvcConfig implements WebMvcConfigurer {/*** 添加静态资源文件外部可以直接访问地址** param registry*/Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {// 添加外部静态资源registry.addResourceHandler(/static/**).addResourceLocations(classpath:/static/);// 添加模板信息registry.addResourceHandler(/templates/**).addResourceLocations(classpath:/templates/);}
}2.4 前端文件设置
具体替换文件 activiti-webexplore\Activiti-5.x\modules\activiti-webapp-explorer2\src\main\webapp下的是哪个文件 以及 activiti-webexplore\Activiti-5.x\modules\activiti-webapp-explorer2\src\main\resource目录下的文件标签文件 最终复制文件到项目中目录格式如下 注意我们也可以替换 stencilset.json将流程页面展示信息变更为中文提示信息此为中文文件地址 修改 app-cfg.js文件将contextRoot 修改为空
var ACTIVITI ACTIVITI || {};
ACTIVITI.CONFIG {contextRoot : ,
};修改 /editor-app/configuration/toolbar-default-actions.jsupdate方法修改请求方式为POST对应前面ModelSaveRestResource请求方式修改为POST
3. 进行测试
测试分为两个部分一个是流程服务自己测试一个是使用iframe嵌套到前端项目中进行测试
3.1 流程项目自己测试
启动服务我们便可以进行简单测试了访问 http://3.1.19.13:11203/modeler/save?modelName测试啦999modelKeyjackAoteManmodelDescription杰克奥特曼 进行新增 访问 http://3.1.19.13:11203/modeler/save?modelId9fb167fa2434422697090a8ea56467e8 可以进行修改编辑
3.2 前端嵌入iframe测试
我们公司则是将其嵌入到了公司的前端项目中我们通过公司前端进行访问访问我们新增的流程model信息 点击修改按钮进行简单修改 点击部署按钮进行部署之后此流程便可以正常使用了
除了部署修改还有移除下载xml文件等操作此处便不多赘述部署之后可以在已部署目录中查看已部署信息包含历史全部部署文件信息可以分版本下载查看 本文参考此篇博文