流程设计模块实现
程序员文章站
2022-05-06 18:17:34
...
流程模型创建
设计流程图
查看流程图
流程模型管理接口
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yangzc.studentboot.workflow.model.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.pagehelper.Page;
import com.yangzc.studentboot.common.annotation.Log;
import com.yangzc.studentboot.common.controller.BaseController;
import com.yangzc.studentboot.common.domain.AjaxResult;
import com.yangzc.studentboot.common.domain.PageDomain;
import com.yangzc.studentboot.common.domain.TableDataInfo;
import com.yangzc.studentboot.common.domain.TableSupport;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.impl.persistence.entity.ModelEntityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author Tijs Rademakers
*/
@Controller
public class ModelEditorJsonRestResource extends BaseController implements ModelDataJsonConstants {
protected static final Logger logger = LoggerFactory.getLogger(ModelEditorJsonRestResource.class);
private static final String PREFIX = "/model";
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
@RequestMapping(value="/modeler/model/{modelId}/json", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public ObjectNode getEditorJson(@PathVariable String modelId) {
ObjectNode modelNode = null;
Model model = repositoryService.getModel(modelId);
if (model != null) {
try {
if (StringUtils.isNotEmpty(model.getMetaInfo())) {
modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
} else {
modelNode = objectMapper.createObjectNode();
modelNode.put(MODEL_NAME, model.getName());
}
modelNode.put(MODEL_ID, model.getId());
ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"));
modelNode.put("model", editorJsonNode);
} catch (Exception e) {
logger.error("Error creating model JSON", e);
throw new ActivitiException("Error creating model JSON", e);
}
}
return modelNode;
}
/**
* 模型列表
*/
// @RequestMapping("/process/modeler/modelList")
// public ModelAndView modelList(HttpServletRequest request) {
// return new ModelAndView("/process" + PREFIX + "/modelList");
// }
@RequestMapping("/workflow/model/list")
public String modelList(HttpServletRequest request) {
return "workflow" + PREFIX + "/modelList";
}
@PostMapping("/workflow/model/data")
@ResponseBody
public TableDataInfo list(ModelEntityImpl modelEntity) {
ModelQuery modelQuery = repositoryService.createModelQuery();
modelQuery.orderByLastUpdateTime().desc();
// 条件过滤
if (com.yangzc.studentboot.common.utils.StringUtils.isNotBlank(modelEntity.getKey())) {
modelQuery.modelKey(modelEntity.getKey());
}
if (com.yangzc.studentboot.common.utils.StringUtils.isNotBlank(modelEntity.getName())) {
modelQuery.modelNameLike("%" + modelEntity.getName() + "%");
}
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
List<Model> resultList = modelQuery.listPage((pageNum - 1) * pageSize, pageSize);
Page<Model> list = new Page<>();
list.addAll(resultList);
list.setTotal(modelQuery.count());
list.setPageNum(pageNum);
list.setPageSize(pageSize);
return getDataTable(list);
}
// @GetMapping("/process/modeler/addModal")
// public ModelAndView addModal() {
// return new ModelAndView("/process" + PREFIX + "/modelModal");
// }
@GetMapping("/workflow/model/addModal")
public String addModal() {
return "workflow" + PREFIX + "/modelModal";
}
/**
* 创建模型
*/
@RequestMapping(value = "/workflow/model/create")
@ResponseBody
public AjaxResult create(@RequestParam("name") String name, @RequestParam("key") String key,
@RequestParam(value = "description", required = false) String description) {
try {
ObjectMapper objectMapper = new ObjectMapper();
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);
ObjectNode modelObjectNode = objectMapper.createObjectNode();
modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, name);
modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
description = StringUtils.defaultString(description);
modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
Model newModel = repositoryService.newModel();
newModel.setMetaInfo(modelObjectNode.toString());
newModel.setName(name);
newModel.setKey(StringUtils.defaultString(key));
repositoryService.saveModel(newModel);
repositoryService.addModelEditorSource(newModel.getId(), editorNode.toString().getBytes("utf-8"));
return new AjaxResult(AjaxResult.Type.SUCCESS, "创建模型成功", newModel.getId());
} catch (Exception e) {
logger.error("创建模型失败:", e);
}
return error();
}
/**
* 根据Model部署流程
*/
@RequestMapping(value = "/workflow/model/deploy/{modelId}")
@ResponseBody
public AjaxResult deploy(@PathVariable("modelId") String modelId, RedirectAttributes redirectAttributes) {
try {
Model modelData = repositoryService.getModel(modelId);
ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
byte[] bpmnBytes = null;
BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
bpmnBytes = new BpmnXMLConverter().convertToXML(model);
String processName = modelData.getName() + ".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addString(processName, new String(bpmnBytes)).deploy();
logger.info("部署成功,部署ID=" + deployment.getId());
return success("部署成功");
} catch (Exception e) {
logger.error("根据模型部署流程失败:modelId={}", modelId, e);
}
return error("部署失败");
}
/**
* 导出model的xml文件
*/
@RequestMapping(value = "/workflow/model/export/{modelId}")
public void export(@PathVariable("modelId") String modelId, HttpServletResponse response) {
try {
Model modelData = repositoryService.getModel(modelId);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
// 流程非空判断
if (!CollectionUtils.isEmpty(bpmnModel.getProcesses())) {
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
String filename = bpmnModel.getMainProcess().getId() + ".bpmn";
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
IOUtils.copy(in, response.getOutputStream());
response.flushBuffer();
} else {
try {
response.sendRedirect("/workflow/model/list");
} catch (IOException ex) {
ex.printStackTrace();
}
}
} catch (Exception e) {
logger.error("导出model的xml文件失败:modelId={}", modelId, e);
try {
response.sendRedirect("/workflow/model/list");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
@Log("删除流程模型")
@PostMapping("/workflow/model/remove")
@ResponseBody
public AjaxResult remove(String ids) {
try {
repositoryService.deleteModel(ids);
return toAjax(true);
}
catch (Exception e) {
return error(e.getMessage());
}
}
@RequestMapping(value="/modeler/model/{modelId}/save", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void saveModel(@PathVariable String modelId, @RequestBody MultiValueMap<String, String> values) {
try {
Model model = repositoryService.getModel(modelId);
ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
modelJson.put(MODEL_NAME, values.getFirst("name"));
modelJson.put(MODEL_DESCRIPTION, values.getFirst("description"));
model.setMetaInfo(modelJson.toString());
model.setName(values.getFirst("name"));
repositoryService.saveModel(model);
repositoryService.addModelEditorSource(model.getId(), values.getFirst("json_xml").getBytes("utf-8"));
InputStream svgStream = new ByteArrayInputStream(values.getFirst("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);
}
}
}
流程设计的模板
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yangzc.studentboot.workflow.model.controller;
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;
import java.io.InputStream;
/**
* @author Tijs Rademakers
*/
@RequestMapping("/modeler")
@RestController
public class StencilsetRestResource {
@RequestMapping(value="/editor/stencilset", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
public @ResponseBody
String getStencilset() {
InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("stencilset.json");
try {
return IOUtils.toString(stencilsetStream, "utf-8");
} catch (Exception e) {
throw new ActivitiException("Error while loading stencil set", e);
}
}
}
流程模型列表页面
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="formId">
<div class="select-list">
<ul>
<li>
<p>KEY:</p>
<input type="text" name="key"/>
</li>
<li>
<p>名称:</p>
<input type="text" name="name"/>
</li>
<!--<li class="select-time">
<p>创建时间:</p>
<input type="text" class="time-input" id="craateStartTime" placeholder="开始时间" name="params[craateStartTime]"/>
<span>-</span>
<input type="text" class="time-input" id="createEndTime" placeholder="结束时间" name="params[createEndTime]"/>
</li>
<li class="select-time">
<p>最后更新时间:</p>
<input type="text" class="time-input" id="lastUpdateStartTime" placeholder="开始时间" name="params[lastUpdateStartTime]"/>
<span>-</span>
<input type="text" class="time-input" id="lastUpdateEndHandleTime" placeholder="结束时间" name="params[lastUpdateEndHandleTime]"/>
</li>-->
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i> 搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i> 重置</a>
</li>
</ul>
</div>
</form>
</div>
<div class="btn-group-sm" id="toolbar" role="group">
<a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="workflow:model:list">
<i class="fa fa-plus"></i> 创建新模型
</a>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var prefix = ctx + "workflow/model";
$(function() {
var options = {
url: prefix + "/data",
createUrl: prefix + "/addModal",
updateUrl: prefix + "/edit/{id}",
removeUrl: prefix + "/remove",
exportUrl: prefix + "/export",
modalName: "流程模型",
columns: [{
checkbox: true
},
{
field : 'id',
title : 'ID'
},
{
field : 'key',
title : 'KEY'
},
{
field : 'name',
title : '名称'
},
{
field : 'version',
title : '版本'
},
{
field : 'createTime',
title : '创建时间'
},
{
field : 'lastUpdateTime',
title : '最后更新时间'
},
{
field : 'metaInfo',
title : '元数据'
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var actions = [];
actions.push('<a class="btn btn-success btn-xs" href="javascript:void(0)" οnclick="successCallback(\'' + row.id + '\')"><i class="fa fa-edit"></i> 编辑</a> ');
actions.push('<a class="btn btn-primary btn-xs" href="javascript:void(0)" οnclick="deployModel(\'' + row.id + '\')"><i class="fa fa-cloud-upload"></i> 部署</a> ');
actions.push('<a class="btn btn-warning btn-xs" href="javascript:void(0)" οnclick="export2Bpmn(\'' + row.id + '\')"><i class="fa fa-download"></i> 导出</a> ');
actions.push('<a class="btn btn-danger btn-xs" href="javascript:void(0)" οnclick="$.operate.remove(\'' + row.id + '\')"><i class="fa fa-remove"></i> 删除</a> ');
return actions.join('');
}
}]
};
$.table.init(options);
});
function export2Bpmn(modelId) {
// $.operate.submit(prefix + "/export/" + modelId, "get", "", "");
window.location.href = prefix + "/export/" + modelId;
// $.modal.loading("正在导出数据,请稍后...");
// $.post(prefix + "/export/" + modelId, "", function(result) {
// if (result.code == web_status.SUCCESS) {
// window.location.href = ctx + "common/download?fileName=" + encodeURI(result.msg) + "&delete=" + true;
// } else if (result.code == web_status.WARNING) {
// $.modal.alertWarning(result.msg)
// } else {
// $.modal.alertError(result.msg);
// }
// $.modal.closeLoading();
// });
}
function successCallback(modelId) {
createMyMenuItem("/modeler/modeler.html?modelId=" + modelId, "模型在线设计");
}
function createMyMenuItem(dataUrl, menuName) {
var panelUrl = window.frameElement.getAttribute('data-id');
dataIndex = $.common.random(1,100), flag = true;
if (dataUrl == undefined || $.trim(dataUrl).length == 0) return false;
var topWindow = $(window.parent.document);
// 选项卡菜单不存在
if (flag) {
var str = '<a href="javascript:;" class="active J_menuTab" data-id="' + dataUrl + '" data-panel="' + panelUrl + '">' + menuName + ' <i class="fa fa-times-circle"></i></a>';
$('.J_menuTab', topWindow).removeClass('active');
// 添加选项卡对应的iframe
var str1 = '<iframe class="J_iframe" name="iframe' + dataIndex + '" width="100%" height="100%" src="' + dataUrl + '" frameborder="0" data-id="' + dataUrl + '" data-panel="' + panelUrl + '" seamless></iframe>';
$('.J_mainContent', topWindow).find('iframe.J_iframe').hide().parents('.J_mainContent').append(str1);
// 添加选项卡
$('.J_menuTabs .page-tabs-content', topWindow).append(str);
}
return false;
}
function deployModel(modelId) {
$.modal.confirm("确认要部署至流程定义吗?", function() {
$.post(prefix + "/deploy/" + modelId, "", function(result) {
$.modal.msgSuccess(result.msg);
});
});
}
</script>
</body>
</html>
创建流程模型的页面
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header" />
<th:block th:include="include :: select2-css" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-model-edit">
<div class="form-group">
<label class="col-sm-3 control-label"><span style="color: red; ">*</span>名称:</label>
<div class="col-sm-8">
<input name="name" class="form-control" type="text" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label"><span style="color: red; ">*</span>KEY:</label>
<div class="col-sm-8">
<input name="key" class="form-control" type="text" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">描述:</label>
<div class="col-sm-8">
<textarea name="description" class="form-control"></textarea>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: select2-js" />
<script type="text/javascript">
var prefix = ctx + "workflow/model";
$("#form-group-edit").validate({
focusCleanup: true
});
function submitHandler() {
if ($.validate.form()) {
var data = $('#form-model-edit').serializeArray();
var userIds = $.form.selectSelects("user");
$.operate.save(prefix + "/create", data, successCallback);
}
}
function successCallback(result) {
// $.modal.openTab("模型在线设计", prefix + "/modeler.html?modelId=" + result.data);
createMyMenuItem("/modeler/modeler.html?modelId=" + result.data, "模型在线设计");
}
function createMyMenuItem(dataUrl, menuName) {
var panelUrl = window.parent.frameElement.getAttribute('data-id');
dataIndex = $.common.random(1,100), flag = true;
if (dataUrl == undefined || $.trim(dataUrl).length == 0) return false;
var topWindow = $(window.parent.parent.document);
// 选项卡菜单不存在
if (flag) {
var str = '<a href="javascript:;" class="active J_menuTab" data-id="' + dataUrl + '" data-panel="' + panelUrl + '">' + menuName + ' <i class="fa fa-times-circle"></i></a>';
$('.J_menuTab', topWindow).removeClass('active');
// 添加选项卡对应的iframe
var str1 = '<iframe class="J_iframe" name="iframe' + dataIndex + '" width="100%" height="100%" src="' + dataUrl + '" frameborder="0" data-id="' + dataUrl + '" data-panel="' + panelUrl + '" seamless></iframe>';
$('.J_mainContent', topWindow).find('iframe.J_iframe').hide().parents('.J_mainContent').append(str1);
// 添加选项卡
$('.J_menuTabs .page-tabs-content', topWindow).append(str);
}
return false;
}
</script>
</body>
</html>
github项目地址
https://github.com/yangzc23/studentboot
参考资料
微信扫一扫关注公众号
点击链接加入群聊
https://jq.qq.com/?_wv=1027&k=5eVEhfN
软件测试学习交流QQ群号:511619105
上一篇: PHP打包下载整个文件夹或多文件