一、背景

最近因为项目需求,我们需要引入一个流程引擎框架。基于以前做办公系统的经验,我就选择了activiti流程引擎框架,不过版本已经更新了好几个了,前后端技术也更新了,集成方式也不同了。于是我开始找资料,发现大多都是后端集成的方式,包括前端代码也喜欢放在后端resources中,这是五年前的模式了,我感觉这种方式很不友好。况且,目前都采用前后端分离的架构模式,前端框架也比较成熟和流行,我们还把html放在后端,从前端、后端、部署三个层面都显的很鸡肋,后面零零散散找了一些资料,踩了很多坑,奋斗了几个晚上,终于集成完毕。因此写个博客记录一下,有描述不详细的地方,欢迎留言,我目前还留存一点记忆,可以解答,哈哈。

二、前端集成

2.1、搭建一个vue项目,将activiti6的前端代码放在public目录下(前端代码可以去官网下载)

2.2、集成模型设计。新建一个vue文件,内容如下

<template>
  <div style="position:relative;height: 100%;">
    <iframe
      id="iframe"
      :src="modelerUrl"
      frameborder="0"
      width="100%"
      height="720px"
      scrolling="auto"
    />
    <Spin v-if="modelerLoading" fix size="large" />
  </div>
</template>

<script>
import { getToken } from '@/utils/auth'
export default {
  name: 'ModelDefine',
  components: {},

  data() {
    return {
      modelerLoading: true,
      modelerUrl: '/static/modeler.html?modelId=' + this.$route.query.id + '&time=' + new Date().getTime()
    }
  },
  computed: {
    token() {
      return 'Bearer ' + getToken()
    }
  },
  created() {},
  mounted() {
    window.getMyVue = this
  },
  methods: {}
}
</script>
<style lang="scss" scoped>
.iframe {
  width: 100%;
  height: calc(100vh - 154px);
}
</style>

结果如图:

2.3、对接token。每个系统都有自己的登录逻辑,activiti前端支持对接项目的token。打开static/modeler.html添加一下代码

  <script>
    (
      function (open) {
        XMLHttpRequest.prototype.open = function (method, url, async, user, pass) {
          open.call(this, method, url, async, user, pass);  //this指XMLHttpRequest
          this.setRequestHeader("Authorization", window.parent.getMyVue.token); //mounted时传入的token
        };
      }
    )(XMLHttpRequest.prototype.open);
  </script>

2.4、修改接口配置。activiti默认的api配置也是支持修改的,可以对应我们自己的业务系统的路由。打开static/editor-app/app-cfg.js

完成上述几步,前端集成基本告一段落了,需要我们重写后台接口,完成模型的新建和保存。

三、后端集成

3.1、搭建一个springboot工程,本次使用的springboot2.4.2

3.2、集成jar包

        <!-- activiti 流程引擎 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>6.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>6.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>6.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-codec</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-css</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svg-dom</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svggen</artifactId>
            <version>1.7</version>
        </dependency>

3.3、配置activiti(springboot项目的yml文件)

spring:
  activiti:
    check-process-definitions: false  # 自动检查、部署流程定义文件
    database-schema-update: true  # 自动更新数据库结构
    process-definition-location-prefix: classpath:/processes  # 流程定义文件存储目录

3.4、重写新增模型接口

    /**
     * 新增模型
     * */
    @PostMapping("/add")
    public AjaxResult addModel(@RequestBody ModelEntityForAdd newModel){
        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(MODEL_NAME, newModel.getName());
        modelNode.put(MODEL_DESCRIPTION, newModel.getDescription());
        modelNode.put(MODEL_REVISION, "1");

        Model model = repositoryService.newModel();
        model.setName(newModel.getName());
        model.setKey(newModel.getKey());
        model.setCategory(newModel.getCategory());
        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.putPOJO("stencilset", stencilSetNode);
        repositoryService.addModelEditorSource(id, editorNode.toString().getBytes(StandardCharsets.UTF_8));
        return AjaxResult.success(id);
    }

3.5、重写获取流程定义接口

/**
     * 获取流程定义json数据
     *
     * @param modelId
     * @return
     */
    @GetMapping(value = "/{modelId}/json")
    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());
                byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
                ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(new String(modelEditorSource, StandardCharsets.UTF_8));
                modelNode.putPOJO("model", editorJsonNode);

            } catch (Exception e) {
                throw new ActivitiException("Error creating model JSON", e);
            }
        }
        return modelNode;
    }

3.6、重写保存模型接口

/**
     * 保存流程定义数据
     */
    @PutMapping(value = "/{modelId}/save")
    public void saveModel(@PathVariable String modelId, @RequestParam("name") String name, @RequestParam("json_xml") String json_xml,
                          @RequestParam("svg_xml") String svg_xml, @RequestParam("description") String description) {
        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);
            model.setVersion(model.getVersion() + 1);
            repositoryService.saveModel(model);

            repositoryService.addModelEditorSource(model.getId(), Objects.requireNonNull(json_xml.getBytes(StandardCharsets.UTF_8)));

            InputStream svgStream = new ByteArrayInputStream(Objects.requireNonNull(svg_xml.getBytes(StandardCharsets.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) {
            throw new ActivitiException("Error saving model", e);
        }
    }

完成上述三个接口,再结合前端,就基本完成了模型的创建、编辑、保存了。至于后续的模型部署、流程实例等等,网上都有很多资料了。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐