草庐IT

springboot集成Camunda,加前端bpmn页面流程编辑器

Yang_RR 2024-03-15 原文

1.后端搭建

搭建流程引擎后端的时候,主要注意springboot版本和Camunda版本之间的兼容性。此次springboot版本2.6.4,Camunda版本7.18.0。pom文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.4</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>camunda-demo2</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <springboot.version>2.6.4</springboot.version>
        <camunda.springboot.version>7.18.0</camunda.springboot.version>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
<!--            <scope>runtime</scope>-->
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-test</artifactId>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter</artifactId>
            <version>${camunda.springboot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
            <version>${camunda.springboot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.camunda.bpm.springboot</groupId>
            <artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
            <version>${camunda.springboot.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

pom文件配置好后,通过一般的注解入口@SpringBootApplication,即可启动Camunda流程引擎。

2.数据库改造

Camunda默认使用的数据库为自带的h2内存数据库,此时一般需要将其改成mysql数据库,在配置文件配置数据库即可,application.yaml如下:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cmd_dev?characterEncoding=UTF-8&useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456

camunda.bpm:
  admin-user:
    id: demo
    password: demo
    firstName: Tom
  filter:
    create: All tasks

一般Camunda会自动初始化mysql生成act_开头的49张表,如需手动初始化sql,可去官网找到对应版本(此例中Camunda版本为7.18.0)的sql脚本文件,https://camunda.com/download/

下载windows安装压缩包,./configuration/sql目录下即可找到sql文件,engine和identity都执行。

不需要增加java代码或配置,直接启动即成功切换数据库。

3.流程接口文档

流程接口可以直接使用rest-api,参考官方文档https://docs.camunda.org/manual/7.18/reference/rest/

a.部署流程

文档地址:https://docs.camunda.org/manual/7.18/reference/rest/deployment/post-deployment/

接口路径 POST /deployment/create

全路径:post http://localhost:8080/engine-rest/deployment/create

将流程文件 流程.bpmn 通过此接口即可完成流程部署,返回definitionId;

b.启动流程实例

文档地址https://docs.camunda.org/manual/7.18/reference/rest/process-definition/post-start-process-instance/

接口路径 POST /process-definition/{id}/start

其中id为上一步获取到的definitionId;

c.提交任务

接口路径 post /task/{id}/complete

d.获取待办任务

接口地址 get /task

参数assignee承办人,选用一对一模式(一个任务对应一个参与者)可通过此参数获取本人待办

参数candidateUser候选人,需要用到一对多模式,可用参数candidateUser获取本人待办

e.审批意见

接口地址 post /task/{id}/comment/create

当然,除了官方提供的rest-api,也可以自定义开发api,通过以下代码同样可以实现流程部署:

    @Autowired
    private RepositoryService repositoryService;

    InputStream in;
    try {
        in= new FileInputStream("D:/ab/a.bpmn");
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    }
    Deployment deploy = repositoryService.createDeployment()
                .addInputStream("b.bpmn", in)    //流程定义文件名称
                .name("nameA")                    //流程部署名称
                .source("sourceA")                //流程部署source
                .deploy();

所有class类官方文档https://docs.camunda.org/javadoc/camunda-bpm-platform/7.18/allclasses.html

其中:

RepositoryService: 对流程定义即bpm文件的相关操作;

RuntimeService: 通过流程定义产生的流程实例,由RuntimeService操作

TaskService: 由用户去执行的任务的相关操作

IdentityService: 管理组和用户

FormService: 可选的表单服务

HistoryService: 历史记录服务

4.流程编辑器bpmn

可参考开源项目https://gitcode.net/mirrors/PL-FE/bpmn-camunda?utm_source=csdn_github_accelerator

编辑流程图bpmn文件:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="sid-38422fae-e03e-43a3-bef4-bd33b32041b2" targetNamespace="http://bpmn.io/bpmn" exporter="bpmn-js (https://demo.bpmn.io)" exporterVersion="5.1.2">
  <process id="Process_7" name="流程7" isExecutable="true">
    <startEvent id="StartEvent_1y45yut" name="开始">
      <outgoing>Flow_0yq3fo1</outgoing>
    </startEvent>
    <userTask id="Activity_04i1hw0" name="审批7" camunda:assignee="${deptMajor}">
      <incoming>Flow_1ona6kc</incoming>
      <outgoing>Flow_0r1n32u</outgoing>
    </userTask>
    <userTask id="Activity_1x3adqc" name="结算7" camunda:assignee="${orgMajor}">
      <incoming>Flow_0yedygk</incoming>
      <outgoing>Flow_1kw655g</outgoing>
    </userTask>
    <endEvent id="Event_06xx78w">
      <incoming>Flow_0r1n32u</incoming>
      <incoming>Flow_1kw655g</incoming>
    </endEvent>
    <exclusiveGateway id="Gateway_19qnoe0">
      <incoming>Flow_0yq3fo1</incoming>
      <outgoing>Flow_1ona6kc</outgoing>
      <outgoing>Flow_0yedygk</outgoing>
    </exclusiveGateway>
    <sequenceFlow id="Flow_0yq3fo1" sourceRef="StartEvent_1y45yut" targetRef="Gateway_19qnoe0" />
    <sequenceFlow id="Flow_1ona6kc" sourceRef="Gateway_19qnoe0" targetRef="Activity_04i1hw0">
      <conditionExpression xsi:type="tFormalExpression">${flag==1}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="Flow_0yedygk" sourceRef="Gateway_19qnoe0" targetRef="Activity_1x3adqc">
      <conditionExpression xsi:type="tFormalExpression">${flag==2}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="Flow_0r1n32u" sourceRef="Activity_04i1hw0" targetRef="Event_06xx78w" />
    <sequenceFlow id="Flow_1kw655g" sourceRef="Activity_1x3adqc" targetRef="Event_06xx78w" />
  </process>
  <bpmndi:BPMNDiagram id="BpmnDiagram_1">
    <bpmndi:BPMNPlane id="BpmnPlane_1" bpmnElement="Process_7">
      <bpmndi:BPMNEdge id="Flow_1kw655g_di" bpmnElement="Flow_1kw655g">
        <omgdi:waypoint x="370" y="200" />
        <omgdi:waypoint x="500" y="200" />
        <omgdi:waypoint x="500" y="138" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0r1n32u_di" bpmnElement="Flow_0r1n32u">
        <omgdi:waypoint x="370" y="30" />
        <omgdi:waypoint x="500" y="30" />
        <omgdi:waypoint x="500" y="102" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0yedygk_di" bpmnElement="Flow_0yedygk">
        <omgdi:waypoint x="150" y="145" />
        <omgdi:waypoint x="150" y="200" />
        <omgdi:waypoint x="270" y="200" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1ona6kc_di" bpmnElement="Flow_1ona6kc">
        <omgdi:waypoint x="150" y="95" />
        <omgdi:waypoint x="150" y="30" />
        <omgdi:waypoint x="270" y="30" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0yq3fo1_di" bpmnElement="Flow_0yq3fo1">
        <omgdi:waypoint x="38" y="120" />
        <omgdi:waypoint x="125" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="StartEvent_1y45yut_di" bpmnElement="StartEvent_1y45yut">
        <omgdc:Bounds x="2" y="102" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <omgdc:Bounds x="10" y="145" width="22" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_04i1hw0_di" bpmnElement="Activity_04i1hw0">
        <omgdc:Bounds x="270" y="-10" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1x3adqc_di" bpmnElement="Activity_1x3adqc">
        <omgdc:Bounds x="270" y="160" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_06xx78w_di" bpmnElement="Event_06xx78w">
        <omgdc:Bounds x="482" y="102" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_19qnoe0_di" bpmnElement="Gateway_19qnoe0" isMarkerVisible="true">
        <omgdc:Bounds x="125" y="95" width="50" height="50" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

其中:”可执行文件“,这个选项需要勾选上,不然流程部署后,3.b.启动流程实例步骤获取不到definitionId

创建任务的时候,要选择用户任务

,而不是普通任务

默认的编辑器没有用户任务,需要在前端项目node_modules文件夹下找到

bpmn-js/lib/features/palette/paletteprovider.js

参照create.task添加create.user-task:

    'create.task': createAction(
      'bpmn:Task', 'activity', 'bpmn-icon-task',
      translate('Create Task')
    ),
    'create.user-task': createAction(
      'bpmn:UserTask', 'activity', 'bpmn-icon-user-task',
      translate('Create User Task')
    ),

a.设置参与者

assignee承办人对应用户任务的”代理人“属性,也可以选用一对多的candidateUser候选人或候选组

设置参与者的时候,可以直接写死参与者的id,也可以通过使用流程变量来动态设置参与者,格式如下:

${deptMajor}

其中流程变量deptMajor,在启动流程时候,需要定义数据类型(值可以先不定义)

参照接口3.b POST /process-definition/{id}/start

在请求体传入如下json数据,初始化流程变量:

{
  "variables": {
    "deptMajor" : {
        "value" : "123",
        "type": "String"
    },
    "orgMajor" : {
        "value" : null,
        "type": "String"
    },
    "flag" : {
        "value" : 2,
        "type": "Integer"
    }
  }
}

b.流程分支控制

设计流程图时,网关各分支增加条件,条件类型为表达式:${flag==1}

然后如上启动流程的时候给流程变量flag初始化和赋值,后续提交任务的时候,也可随时通过修改流程变量的值来控制流程分支的走向。

5.常用表

act_ge_bytearray 记录部署的bpmn流程文件

act_re_deployment 部署发布的流程会在此产生记录

act_re_procdef 已发布的流程定义(BPMN流程模型定义表)

act_hi_procinst 历史流程实例表

act_hi_actinst 历史活动实例表

act_hi_identitylink 历史任务执行人表

act_ru_task 任务表

act_ru_identitylink 任务执行人表

act_ru_execution BPMN流程运行时记录表

6.当前流程图高亮

基本思路为通过接口查出流程定义的xml内容,然后找到当前正在运行的节点,通过方法canvas.addMarker(actId, 'highlight'),修改其样式:

<template>
  <div class="containers">
    <div id="canvas" ref="canvas"></div>
  </div>
</template>

<script>
  import BpmnViewer from 'bpmn-js'
  import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'
  let bpmnViewer = null
  const webUrl = "http://localhost:8095/camunda"
  export default {
    name: 'bpmnView',
    data () {
      return {
        msg: 'Welcome to yjr Vue.js App',
        bpmnModeler: null
      }
    },
    mounted() {
      bpmnViewer = new BpmnViewer({
        container: '#canvas',
        width: '100%',
        height: '100%',
        additionalModules: [
          MoveCanvasModule // 移动整个画布
        ]
      })
      this.greet()
    },
    methods: {
      createBpmnViewer :async function (bpmnXML) {
        try {
          await bpmnViewer.importXML(bpmnXML);
        } catch (err) {
          console.error('error loading BPMN 2.0 XML', err);
        }
        let canvas = bpmnViewer.get('canvas')
        canvas.zoom('fit-viewport', 'auto')
      },
      greet: function () {
            const that = this
            const processDefinitionId = this.$route.query.processDefinitionId
            this.$http.get(webUrl + '/engine-rest/process-definition/' + processDefinitionId + '/xml')
            .then((response)=>{
                if(response.data.bpmn20Xml){
                  that.createBpmnViewer(response.data.bpmn20Xml)
                  that.getAct();
                }
            })
            .catch((response)=>{
                console.log(response);
            })
        },
        getAct: function () {
          const that = this
          const processInstanceId = this.$route.query.processInstanceId
          let canvas = bpmnViewer.get('canvas')
          this.$http.get(webUrl + '/web/processInstance/getAct?processInstanceId=' + processInstanceId)
            .then((response)=>{
                if(response.data.code == 200){
                  let list = response.data.data;
                  for(var actId in list){
                    canvas.addMarker(list[actId], 'highlight');
                  }
                }
            })
            .catch((response)=>{
                console.log(response);
            })
        }
    }
  }
</script>
<style>
.highlight .djs-visual > :nth-child(1) {
  stroke: green !important;
  fill: rgba(0, 80, 0, 0.4) !important;
}
.djs-container{
    height: 80vh !important;
  }
</style>

效果如下:

具体js中初始化及方法可参考bpmn官网:bpmn-js walkthrough | Toolkits | bpmn.io

有关springboot集成Camunda,加前端bpmn页面流程编辑器的更多相关文章

  1. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  2. ruby-on-rails - 如何使辅助方法在 Rails 集成测试中可用? - 2

    我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel

  3. ruby-on-rails - 我如何将 Hoptoad 与 DelayedJob 和 DaemonSpawn 集成? - 2

    我一直很高兴地使用DelayedJob习惯用法:foo.send_later(:bar)这会调用DelayedJob进程中对象foo的方法bar。我一直在使用DaemonSpawn在我的服务器上启动DelayedJob进程。但是...如果foo抛出异常,Hoptoad不会捕获它。这是任何这些包中的错误...还是我需要更改某些配置...或者我是否需要在DS或DJ中插入一些异常处理来调用Hoptoad通知程序?回应下面的第一条评论。classDelayedJobWorker 最佳答案 尝试monkeypatchingDelayed::W

  4. jenkins部署1--jenkins+gitee持续集成 - 2

    前置步骤我们都操作完了,这篇开始介绍jenkins的集成。话不多说,看操作1、登录进入jenkins后会让你选择安装插件,选择第一个默认的就行。安装完成后设置账号密码,重新登录。2、配置JDK和Git都需要执行路径,所以需要先把执行路径找到,先进入服务器的docker容器,2.1JDK的路径root@69eef9ee86cf:/usr/bin#echo$JAVA_HOME/usr/local/openjdk-82.2Git的路径root@69eef9ee86cf:/#whichgit/usr/bin/git3、先配置JDK和Git。点击:ManageJenkins>>GlobalToolCon

  5. ruby - 在 ASP 页面上 Mechanize 中断 - 2

    require'mechanize'agent=Mechanize.newlogin=agent.get('http://www.schoolnet.ch/DE/HomeDE.htm')agent.clicklogin.link_withtext:/Login/然后我得到Mechanize::UnsupportedSchemeError。 最佳答案 Mechanize不支持javascript但您可以将搜索字段添加到表单并为其分配搜索词并使用mechanize提交表单form=page.forms.firstform.add_fie

  6. ruby-on-rails - prawnto 显示新页面时不会中断的表格 - 2

    我有可变数量的表格和可变数量的行,我想让它们一个接一个地显示,但如果表格不适合当前页面,请将其放在下一页,然后继续。我已将表格放入事务中,以便我可以回滚然后打印它(如果高度适合当前页面),但我如何获得表格高度?我现在有这段代码pdf.transactiondopdf.table@data,:font_size=>12,:border_style=>:grid,:horizontal_padding=>10,:vertical_padding=>3,:border_width=>2,:position=>:left,:row_colors=>["FFFFFF","DDDDDD"]pdf.

  7. ruby - 每个页面上的 Jekyll 分页 - 2

    据我们所知,Jekyll默认分页仅支持index.html,我想创建blog.html并在那里包含分页。有什么解决办法吗? 最佳答案 如果您创建一个名为/blog的目录并在其中放置一个index.html文件,那么您可以向_config.yml表示paginate_path:"blog/page:num"。不是使用根文件夹中的默认index.html作为分页器模板,而是使用/blog/index.html。分页器将根据需要生成类似/blog/page2/和/blog/page3/的页面。这将使您到达yourwebsite.com/b

  8. ruby-on-rails - 尝试打开 .gitignore 以在文本编辑器中对其进行编辑,但在 OS X Mountain Lion 上找不到文件位置 - 2

    我使用“newapp_name”创建了一个新的Rails应用程序,我正在尝试编辑.gitignore文件,但在我的应用程序文件夹中找不到它。我在哪里可以找到它?我安装了Git。 最佳答案 .gitignore位于项目的root中,而不是app子目录中。首先打开终端并进入您的目录。您需要使用ls-a来显示stash文件。然后使用打开.gitignore 关于ruby-on-rails-尝试打开.gitignore以在文本编辑器中对其进行编辑,但在OSXMountainLion上找不到文件位

  9. ruby-on-rails - RoR && "coming soon"页面 - 2

    我正在寻找一种简单的方法来为我在RubyonRails上的项目实现简单的“即将推出”(预启动)页面。用户应该能够留下电子邮件以便在项目启动时收到通知。有没有这样的插件\gem?或者我应该自己做... 最佳答案 LaunchingSoon是一个Rails插件。它还集成了MailChimp或Campaignmonitor. 关于ruby-on-rails-RoR&&"comingsoon"页面,我们在StackOverflow上找到一个类似的问题: https:/

  10. ruby - 如何让 GitHub 页面使用 master 分支? - 2

    我有一个使用Jekyll托管在GitHub上的静态网站。问题是,我真的不需要master分支,因为存储库唯一包含的是网站。这样我就必须gitcheckoutgh-pages,然后gitmergemaster,然后gitpushorigingh-pages。有什么简单的方法可以摆脱gh-pages分支并直接从master推送? 最佳答案 Theproblemis,Idon'treallyneedthemasterbranch,astheonlythingtherepositorycontainsisthewebsite.Isthere

随机推荐