欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

流程设计模块实现

程序员文章站 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>&nbsp;搜索</a>
                                <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</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

参考资料

[01] stencil set 模板集

微信扫一扫关注公众号
流程设计模块实现
点击链接加入群聊

https://jq.qq.com/?_wv=1027&k=5eVEhfN
软件测试学习交流QQ群号:511619105