activiti5.22整合官方编辑器

本文详细介绍了如何在SpringBoot项目中整合Activiti 5.22.0的官方编辑器,包括前端组件部署、后端接口实现、界面汉化步骤,并提供模型管理的RESTful接口示例。适合希望在实际项目中使用在线流程编辑功能的开发者。
摘要由CSDN通过智能技术生成

环境

activiti:5.22.0
springboot:2.5.1

前言

默认已经Springboot2整合activiti5完毕;
可以参考:springboot2整合activiti5.22 maven版

这篇文章是接着上面文章写的。

activiti5.22官方编辑器

网上很多都是使用idea或者eclipse插件来画图,这种根据就没法用,代码部署上线后,肯定需要在线编辑器才能满足业务的使用;

期望如下效果:

在这里插入图片描述

思路

我们已经把activiti整合完毕;官方其实提供了编辑器,我们可以思考下,编辑器需要哪些东西:

  1. 前端代码:渲染画图、增删改查的组件显示;
  2. 后端代码:对应画图页面中的保存、查询等接口;

前端代码

根据网上的资料,我们需要在activiti官方源码中找到如下东西:
在这里插入图片描述

前端代码描述
editor-app编辑器
modeler.html编辑器的入口页面
diagram-viewer流程跟踪插件,虽然这次用不着,但之后会用到
stencilset.json本身是英文的,可以通过替换它来达到汉化的效果。
favicon.ico一个logo

先下载源码

https://github.com/Activiti/Activiti/tree/activiti-5.22.0

源码路径:activiti-webapp-explorer2模块中webappresource中。
在这里插入图片描述

放入自己的项目中:

在这里插入图片描述

说明:

  1. diagram-viewereditor-appfavicon.icomodeler.htmlstencilset.json移动到自己项目static文件夹中。
  2. model-list.html不是官方的,是新建流程的入口页面,自己编写的前端代码。

editor-app/app-cfg.js中配置一下项目url。这个url是编辑器相关的后台服务的url。

ACTIVITI.CONFIG = {
    'contextRoot' : '/service',
};

后端代码

先引入两个activiti的模块,因为编辑器会用到这两个模块。

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-modeler</artifactId>
    <version>${activiti.version}</version>
</dependency>

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-diagram-rest</artifactId>
    <version>${activiti.version}</version>
</dependency>

其中需要将源码中activiti-modeler模块的源代码放到src中,因为需要在其中做部分修改,主要是url的映射。

其中有3个类,都是Controller:

#获取编辑器组件及配置项信息。
StencilsetRestResource 
#根据modelId获取model的节点信息,编辑器根据返回的json进行绘图。
ModelEditorJsonRestResource 
#编辑器制图之后,将节点信息以json的形式提交给这个Controller,然后由其进行持久化操作。
ModelSaveRestResource 

在这里插入图片描述
个人项目存放的位置:

在这里插入图片描述

需要修改的地方就三个,在每个Controller类上加上@RequestMapping注解,并指定值为"service"(对应前台app-cfg.js中配置的url)。

···
@RequestMapping("service")
public class StencilsetRestResource {
···
···
@RequestMapping("service")
public class ModelEditorJsonRestResource implements ModelDataJsonConstants {
···
···
@RequestMapping("service")
public class ModelSaveRestResource implements ModelDataJsonConstants {
···

最后别忘了添加包扫描,扫描activiti提供的这些controller

如果启动类和上面放的类都在一个包下,就不用添加。

这样整合部分就基本结束了,此时编辑器已经可以使用了。

至于界面的汉化,界面上各个组件,各个标签上的文字都是在resource下的stencilset.json文件中设置的,可以在网上找一个汉化后的stencilset.json文件替换掉,就能看到中文界面了。

但是入口页面得我们自己写,效果如下:

在这里插入图片描述

接下来,我们就要自己去设计这个页面,下面贴出后端代码,前端代码参考文章最后的源码链接;

modeler相关方法的封装

主要需要封装4个方法:

  1. 新建一个空的模型;
  2. 所有模型列表;
  3. 发布模型;
  4. 删除模型;

(activiti已提供了保存和获取模型节点信息的方法,就是上面的那3个类)
由于这里涉及前后端交互,实现方式随意,主要是activiti的api的调用。

下面只贴出 参考代码,demo,请参考文章最后的链接。

模型管理类

package com.sgy.flowactiviti.admin.controller;

import org.springframework.web.bind.annotation.*;

import java.io.Serializable;

/**
 * restful的基本规范
 */
public interface RestServiceController<T, ID extends Serializable> {
    /**
     * 根据id查询资源
     * @param id 资源的唯一标识
     * @return
     */
    @GetMapping("{id}")
    Object getOne(@PathVariable("id") ID id);

    /**
     * 列表分页
     * @param rowSize 一页数据大小
     * @param page 当前页码
     * @return
     */
    @GetMapping
    Object getList(@RequestParam(value = "rowSize", defaultValue = "1000", required = false) Integer rowSize
            , @RequestParam(value = "page", defaultValue = "1", required = false) Integer page);

    /**
     * 提交一个资源
     * @param entity 资源实体
     * @return
     */
    @PostMapping
    Object postOne(@RequestBody T entity);

    /**
     * 提交一个资源,并给出标识
     * @param id 标识
     * @param entity 资源实体
     * @return
     */
    @PutMapping("{id}")
    Object putOne(@PathVariable("id") ID id, @RequestBody T entity);

    /**
     * 提交一个资源的一部分,不处理null值
     * @param id 标识
     * @param entity 资源实体
     * @return
     */
    @PatchMapping("{id}")
    Object patchOne(@PathVariable("id") ID id, @RequestBody T entity);

    /**
     * 根据id删除一个资源
     * @param id 标识
     * @return
     */
    @DeleteMapping("{id}")
    Object deleteOne(@PathVariable("id") ID id);
}

具体实现:

package com.sgy.flowactiviti.admin.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.sgy.flowactiviti.admin.util.Status;
import com.sgy.flowactiviti.admin.util.ToWeb;
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.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.UnsupportedEncodingException;
import java.util.List;

/**
 * 模型管理
 */
@RestController
@RequestMapping("models")
public class ModelerController implements RestServiceController<Model, String> {

    @Autowired
    RepositoryService repositoryService;
    @Autowired
    ObjectMapper objectMapper;

    /**
     * 新建一个空模型
     * @return
     * @throws UnsupportedEncodingException
     */
    @PostMapping("newModel")
    public Object newModel() throws UnsupportedEncodingException {
        //初始化一个空模型
        Model model = repositoryService.newModel();

        //设置一些默认信息
        String name = "new-process";
        String description = "";
        int revision = 1;
        String key = "process";

        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);

        model.setName(name);
        model.setKey(key);
        model.setMetaInfo(modelNode.toString());

        repositoryService.saveModel(model);
        String id = model.getId();

        //完善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.put("stencilset", stencilSetNode);
        repositoryService.addModelEditorSource(id,editorNode.toString().getBytes("utf-8"));
        return ToWeb.buildResult().redirectUrl("/editor?modelId="+id);
    }


    /**
     * 发布模型为流程定义
     * @param id
     * @return
     * @throws Exception
     */
    @PostMapping("{id}/deployment")
    public Object deploy(@PathVariable("id")String id) throws Exception {

        //获取模型
        Model modelData = repositoryService.getModel(id);
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());

        if (bytes == null) {
            return ToWeb.buildResult().status(Status.FAIL)
                    .msg("模型数据为空,请先设计流程并成功保存,再进行发布。");
        }

        JsonNode modelNode = new ObjectMapper().readTree(bytes);

        BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        if(model.getProcesses().size()==0){
            return ToWeb.buildResult().status(Status.FAIL)
                    .msg("数据模型不符要求,请至少设计一条主线流程。");
        }
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

        //发布流程
        String processName = modelData.getName() + ".bpmn20.xml";
        Deployment deployment = repositoryService.createDeployment()
                .name(modelData.getName())
                .addString(processName, new String(bpmnBytes, "UTF-8"))
                .deploy();
        modelData.setDeploymentId(deployment.getId());
        repositoryService.saveModel(modelData);

        return ToWeb.buildResult().refresh();
    }

    @Override
    public Object getOne(@PathVariable("id") String id) {
        Model model = repositoryService.createModelQuery().modelId(id).singleResult();
        return ToWeb.buildResult().setObjData(model);
    }

    @Override
    public Object getList(@RequestParam(value = "rowSize", defaultValue = "1000", required = false) Integer rowSize, @RequestParam(value = "page", defaultValue = "1", required = false) Integer page) {
        List<Model> list = repositoryService.createModelQuery().listPage(rowSize * (page - 1)
                , rowSize);
        long count = repositoryService.createModelQuery().count();

        return ToWeb.buildResult().setRows(
                ToWeb.Rows.buildRows().setCurrent(page)
                        .setTotalPages((int) (count/rowSize+1))
                        .setTotalRows(count)
                        .setList(list)
                        .setRowSize(rowSize)
        );
    }

    public Object deleteOne(@PathVariable("id")String id){
        repositoryService.deleteModel(id);
        return ToWeb.buildResult().refresh();
    }

    @Override
    public Object postOne(@RequestBody Model entity) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Object putOne(@PathVariable("id") String s, @RequestBody Model entity) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Object patchOne(@PathVariable("id") String s, @RequestBody Model entity) {
        throw new UnsupportedOperationException();
    }
}

前端代码,你们就参考链接吧:

https://github.com/b3601993/spring-boot-with-activiti

总结

总体最麻烦的可能就是模型列表前端页面相关的代码,因为这块得我们自己去实现设计。
对于我后端人员来说,真的太难受了。

参考地址:

【activiti】整合官方编辑器插件

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山鬼谣me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值