JDK21和 Flowable 7.0.0

JDK21和 Flowable 7.0.0

  • 一.Flowable
  • 二.项目搭建
    • 1.依赖包
    • 2.数据库
    • 3.资源文件
      • 1.YML配置文件
      • 2.Drools kbase
      • 3.Drools rule
      • 4.DMN 决策表
      • 5.BPMN 流文件
    • 4.BPMN 流程图绘制插件
    • 5.测试代码
      • 1.启动类
      • 2.Flowable 配置
      • 3.Camel 配置
        • 1.Camel 配置
        • 2.Camel Router 定义
      • 4.扩展类监听
        • 1.外部工作类
        • 2.普通Java服务扩展
        • 3.Camel Listener
        • 4.DmnEndListener
        • 5.DmnStartListener
        • 6.ExternalWorkerListener
        • 7.HttpListener
        • 8.MailListener
        • 9.ManualTaskListener
        • 10.ReceiveTaskListener
        • 11.ScriptListener
        • 12.ShellListener
        • 13.UserTaskListener
      • 5.测试接口类
  • 三.测试
    • 1.测试
    • 2.导出流进度
    • 3.Postman 配置文件

一.Flowable

Java语言实现的轻量级工作流框架

二.项目搭建

项目结构

在这里插入图片描述

1.依赖包

本测试项目包含了 drools 、dmn 等依赖

<?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.example</groupId>
        <artifactId>flowable-test</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>flowable</artifactId>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.boot.version>3.1.3</spring.boot.version>
        <flowable.version>7.0.0</flowable.version>
        <drools.version>9.44.0.Final</drools.version>
    </properties>
    <!--    drools-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.drools</groupId>
                <artifactId>drools-bom</artifactId>
                <type>pom</type>
                <scope>import</scope>
                <version>${drools.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
        <!-- flowable -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>${flowable.version}</version>
        </dependency>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-actuator</artifactId>
            <version>${flowable.version}</version>
        </dependency>
        <!--        camel-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-camel</artifactId>
            <version>${flowable.version}</version>
        </dependency>
        <!--        dmn-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-dmn-engine</artifactId>
            <version>${flowable.version}</version>
        </dependency>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-dmn-engine-configurator</artifactId>
            <version>${flowable.version}</version>
        </dependency>
        <!--        mq jms-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-jms-spring-executor</artifactId>
            <version>${flowable.version}.M1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.flowable</groupId>
                    <artifactId>flowable-spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-pool</artifactId>
            <version>6.0.1</version>
        </dependency>
        <!--        httpclient flowable http-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.14</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel.springboot</groupId>
            <artifactId>camel-spring-boot-starter</artifactId>
            <version>4.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- pgsql -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.6.0</version>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.44</version>
        </dependency>
        <!--java script execution-->
        <dependency>
            <groupId>org.openjdk.nashorn</groupId>
            <artifactId>nashorn-core</artifactId>
            <version>15.4</version>
        </dependency>
        <!--        drools-->
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-engine</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-mvel</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-model-compiler</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-xml-support</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>
</project>

2.数据库

Flowable 默认使用 H2 数据库,但是可以通过修改配置文件来指定,本文使用的是 Postgres
创建一个名为 flowable 的库
Flowable 首次启动时默认会自动创建表
也可以通过官方SQL脚本手动创建表

在这里插入图片描述

3.资源文件

1.YML配置文件

server:
  port: 8086
  servlet.context-path: /flowable-rest

## 暴露 SpringBoot 除 shutdown 外的所有端点
management:
  server.port: 7291
  endpoints:
    web:
      exposure:
        include: "*"
    health:
      show-details: when_authorized
      roles: access-admin

spring:
  activemq:
    broker-url: tcp://localhost:61616
  application:
    name: flowable-rest
  banner:
    location: classpath:/org/flowable/spring/boot/flowable-banner.txt
  jmx:
    default-domain: ${spring.application.name}
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://127.0.0.1:5432/flowable
    username: postgres
    password: 123456
    ## 连接池
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      poolName: ${spring.application.name}
      maxLifetime: 600000
      idleTimeout: 300000
      minimumIdle: 10
      maximumPoolSize: 50
      connection-test-query: select 1

## java object serializable
rest:
  variables:
    allow:
      serializable=true:

flowable:
  process-definition-location-prefix: classpath*:/processes/
  process-definition-location-suffixes: "**.bpmn20.xml,**.bpmn,drools/**.drl,dmn/**.dmn.xml"
  database-schema-update: true
  async-executor-activate: true
  history-level: full
  rest:
    app:
      swagger-docs-enabled: true
      create-demo-definitions: true
      authentication-mode: verify-privilege
      admin:
        user-id: rest-admin
        password: test
        firstname: Rest
        lastname: Admin
  process:
    servlet:
      path: /service
  mail.server:
    default-from: xxxxxx@qq.com
    host: smtp.qq.com
    username: xxxxxx@qq.com
    password: xxxxxx
    s-s-l-port: 465
    use-ssl: true
    use-tls: false

logging:
  level:
    org.example.*: debug

2.Drools kbase

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">

    <!--
        name: kbase 名称全局唯一
        packages: 规则文件包目录
        default: 指定为默认 kbase
    -->
    <kbase name="my_kbase" packages="processes.drools.*" default="true">
        <!--
            name: ksession 名称 kbase 下唯一
            default: 指定为默认 ksession
            clockType: 时钟类型 系统时钟或测试的伪时钟
        -->
        <ksession name="my_ks" clockType="realtime" default="true"/>
    </kbase>
</kmodule>

3.Drools rule

MyBusinessRule.drl

package drools

rule "myBusiness_1"
    when
    then
        System.out.println("Step4_1 drools 9.44.0.Final");
end

rule "myBusiness_2"
    when
    then
        System.out.println("Step4_2 drools 9.44.0.Final");
end

rule "myBusiness_3"
    when
    then
        System.out.println("Step4_3 drools 9.44.0.Final");
end

4.DMN 决策表

MyDecision.dmn.xml

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/DMN/20180521/MODEL/"
             id="simple" name="Simple" namespace="http://activiti.org/dmn">
  <decision id="myDecision" name="Simple decision">
    <decisionTable id="myDecisionTest" hitPolicy="UNIQUE">
      <input id="inputId">
        <inputExpression id="inputExpression1" typeRef="number">
          <text>age</text>
        </inputExpression>
      </input>
      <output id="outputId" label="myResult" name="myResult" typeRef="string" />
      <rule>
        <inputEntry id="inputEntry_1">
          <text>&gt;18</text>
        </inputEntry>
        <outputEntry id="outputEntry_1">
          <text>'成年'</text>
        </outputEntry>
      </rule>
      <rule>
        <inputEntry id="inputEntry_2">
          <text>&lt;=18</text>
        </inputEntry>
        <outputEntry id="outputEntry_2">
          <text>'未成年'</text>
        </outputEntry>
      </rule>
    </decisionTable>
  </decision>
</definitions>

5.BPMN 流文件

leave_approval_process.bpmn20.xml

可通过下面 IDEA 插件查看流节点

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
  <process id="leave_approval_process" name="leave_approval_process" isExecutable="true">
    <startEvent id="sid-a76e7b82-39bd-4838-b819-2628292d2c2c" name="Start"/>
    <userTask id="sid-604145ee-174b-4864-a973-934078a7d043" name="LineManager" flowable:assignee="admin">
      <extensionElements>
        <flowable:taskListener event="create" class="org.example.exp.listener.UserTaskListener"/>
      </extensionElements>
    </userTask>
    <sequenceFlow id="sid-4fcf06a4-ba36-486e-a058-d993513403d0" sourceRef="sid-a76e7b82-39bd-4838-b819-2628292d2c2c" targetRef="sid-604145ee-174b-4864-a973-934078a7d043"/>
    <serviceTask id="sid-501c9238-5da1-47f0-8f95-2f458010a970" name="QueryRemainingDays" flowable:delegateExpression="${queryRemainingDays}"/>
    <sequenceFlow id="sid-6a8c97f8-acc2-4a3e-9051-6c78f161365b" sourceRef="sid-604145ee-174b-4864-a973-934078a7d043" targetRef="sid-501c9238-5da1-47f0-8f95-2f458010a970"/>
    <endEvent id="sid-e6b42855-57d6-4e8d-96a7-f5b1a004ed3d"/>
    <scriptTask id="sid-354c035b-2e49-4f5f-88ca-ca1cfd0e27ff" name="VerifyScript" scriptFormat="JavaScript" flowable:autoStoreVariables="false">
      <extensionElements>
        <flowable:executionListener class="org.example.exp.listener.ScriptListener" event="end"/>
      </extensionElements>
      <script><![CDATA[execution.setVariable("sum",90);]]></script>
    </scriptTask>
    <sequenceFlow id="sid-0da818a4-ea4f-4f85-b9ad-e23e943fc2f0" sourceRef="sid-501c9238-5da1-47f0-8f95-2f458010a970" targetRef="sid-354c035b-2e49-4f5f-88ca-ca1cfd0e27ff"/>
    <businessRuleTask id="sid-42d7188c-58e7-46c1-994c-3004b8d44b6b" name="MyBusiness" flowable:rules="myBusiness_1,myBusiness_3"/>
    <sequenceFlow id="sid-1ec2477c-054e-4611-9c01-fcae871faee5" sourceRef="sid-354c035b-2e49-4f5f-88ca-ca1cfd0e27ff" targetRef="sid-42d7188c-58e7-46c1-994c-3004b8d44b6b"/>
    <receiveTask id="sid-myReceive" name="myReceive">
      <extensionElements>
        <flowable:executionListener event="end" class="org.example.exp.listener.ReceiveTaskListener"/>
      </extensionElements>
    </receiveTask>
    <sequenceFlow id="sid-2b9661b6-f462-41f9-98d2-556cd0a2c064" sourceRef="sid-42d7188c-58e7-46c1-994c-3004b8d44b6b" targetRef="sid-myReceive"/>
    <manualTask id="sid-5d7dd741-0f16-4100-a21f-3fc2b5808653" flowable:exclusive="true" name="myManualTask">
      <extensionElements>
        <flowable:executionListener event="start" class="org.example.exp.listener.ManualTaskListener"/>
      </extensionElements>
    </manualTask>
    <sequenceFlow id="sid-2bc20ebd-7970-44d9-b6c0-a8448cb91db3" sourceRef="sid-myReceive" targetRef="sid-5d7dd741-0f16-4100-a21f-3fc2b5808653"/>
    <!-- flowable 单独定义的节点类型 支持按 apache camel 路由规则进行调用 -->
    <serviceTask flowable:type="camel" id="sid-bb78a1a1-3bd8-411a-920b-63ae2eb06949" flowable:exclusive="true" name="CamelServiceTask">
      <extensionElements>
        <flowable:field name="camelContext">
          <flowable:string>camelContext</flowable:string>
        </flowable:field>
        <flowable:executionListener event="end" class="org.example.exp.listener.CamelListener"/>
      </extensionElements>
    </serviceTask>
    <sequenceFlow id="sid-e44ef6ef-5e78-4d6b-9fef-45a793579236" sourceRef="sid-5d7dd741-0f16-4100-a21f-3fc2b5808653" targetRef="sid-bb78a1a1-3bd8-411a-920b-63ae2eb06949"/>
    <serviceTask flowable:type="http" id="sid-fe09b981-5d42-4cca-a222-9b1256dea9f6" flowable:exclusive="true" name="HttpServiceTask">
      <extensionElements>
        <flowable:field name="requestUrl">
          <flowable:string>http://127.0.0.1:8088/view/hello</flowable:string>
        </flowable:field>
        <flowable:executionListener event="end" class="org.example.exp.listener.HttpListener"/>
        <flowable:field name="requestMethod">
          <flowable:string>GET</flowable:string>
        </flowable:field>
        <flowable:field name="saveResponseParameters">
          <flowable:string>true</flowable:string>
        </flowable:field>
        <flowable:field name="responseVariableName">
          <flowable:string>result</flowable:string>
        </flowable:field>
      </extensionElements>
    </serviceTask>
    <sequenceFlow id="sid-7ed37a45-da59-43d3-ae84-83f57ed56025" sourceRef="sid-bb78a1a1-3bd8-411a-920b-63ae2eb06949" targetRef="sid-fe09b981-5d42-4cca-a222-9b1256dea9f6"/>
    <serviceTask flowable:type="mail" id="sid-5796c07d-53d8-4bd4-a56a-ddff642c06e6" name="MailserviceTask">
      <extensionElements>
        <flowable:executionListener event="end" class="org.example.exp.listener.MailListener"/>
        <flowable:field name="to">
          <flowable:string>hongxu_1234@163.com</flowable:string>
        </flowable:field>
        <flowable:field name="subject">
          <flowable:string>【Flowable 测试】</flowable:string>
        </flowable:field>
        <flowable:field name="text">
          <flowable:string>Hello</flowable:string>
        </flowable:field>
      </extensionElements>
    </serviceTask>
    <sequenceFlow id="sid-d65dc071-03b3-44b1-b757-e78fc175cb50" sourceRef="sid-fe09b981-5d42-4cca-a222-9b1256dea9f6" targetRef="sid-5796c07d-53d8-4bd4-a56a-ddff642c06e6"/>
    <serviceTask flowable:type="dmn" id="sid-2f1cea1d-b3de-451e-8612-0247f27b3d30" flowable:exclusive="true" name="DmnServiceTask">
      <extensionElements>
        <flowable:executionListener event="start" class="org.example.exp.listener.DmnStartListener"/>
        <flowable:executionListener event="end" class="org.example.exp.listener.DmnEndListener"/>
        <flowable:field name="decisionTableReferenceKey">
          <flowable:string>myDecision</flowable:string>
        </flowable:field>
      </extensionElements>
    </serviceTask>
    <sequenceFlow id="sid-389b9aea-15c7-41ed-b509-80c7b90ef6a5" sourceRef="sid-5796c07d-53d8-4bd4-a56a-ddff642c06e6" targetRef="sid-2f1cea1d-b3de-451e-8612-0247f27b3d30"/>
    <serviceTask flowable:type="shell" id="sid-968bc676-cb2a-4e38-ab51-037f174addfb" flowable:exclusive="true" name="ShellServiceTask">
      <extensionElements>
        <flowable:executionListener event="end" class="org.example.exp.listener.ShellListener"/>
        <flowable:field name="outputVariable">
          <flowable:string>shellOutput</flowable:string>
        </flowable:field>
        <flowable:field name="command">
          <flowable:string>ls</flowable:string>
        </flowable:field>
      </extensionElements>
    </serviceTask>
    <sequenceFlow id="sid-285f6ce8-8b6c-4aa0-8a47-ffe9ddcd535f" sourceRef="sid-2f1cea1d-b3de-451e-8612-0247f27b3d30" targetRef="sid-968bc676-cb2a-4e38-ab51-037f174addfb"/>
    <serviceTask flowable:type="external-worker" id="sid-e3e82c89-44df-4beb-89d6-29a5819386d9" flowable:exclusive="true" name="ExternalWorkerTask" flowable:topic="my-external-worker" flowable:async="true">
      <extensionElements>
        <flowable:executionListener event="end" class="org.example.exp.listener.ExternalWorkerListener"/>
      </extensionElements>
    </serviceTask>
    <sequenceFlow id="sid-e7db60c3-6b77-42b1-8921-7c5b45cf2e52" sourceRef="sid-968bc676-cb2a-4e38-ab51-037f174addfb" targetRef="sid-e3e82c89-44df-4beb-89d6-29a5819386d9"/>
    <sequenceFlow id="sid-fa098a45-db8a-415f-96b2-6b1dc1cd3c26" sourceRef="sid-e3e82c89-44df-4beb-89d6-29a5819386d9" targetRef="sid-e6b42855-57d6-4e8d-96a7-f5b1a004ed3d"/>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_leave_approval_process">
    <bpmndi:BPMNPlane bpmnElement="leave_approval_process" id="BPMNPlane_leave_approval_process">
      <bpmndi:BPMNShape id="shape-678b7128-9f1c-4ebd-a546-3c1337b0ec0d" bpmnElement="sid-a76e7b82-39bd-4838-b819-2628292d2c2c">
        <omgdc:Bounds x="-700.0" y="-420.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-bc8d7db7-870a-4b22-89b6-66f91f51be7f" bpmnElement="sid-604145ee-174b-4864-a973-934078a7d043">
        <omgdc:Bounds x="-735.0" y="-340.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-04bdc7f3-62dd-49d6-8e1d-bf9ad723c114" bpmnElement="sid-4fcf06a4-ba36-486e-a058-d993513403d0">
        <omgdi:waypoint x="-685.0" y="-390.0"/>
        <omgdi:waypoint x="-685.0" y="-340.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-1850c5f4-e021-4cfc-8f45-1ed25c94cf67" bpmnElement="sid-501c9238-5da1-47f0-8f95-2f458010a970">
        <omgdc:Bounds x="-560.0" y="-340.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-77901c58-dc34-403e-8bc1-ed63e71f442c" bpmnElement="sid-6a8c97f8-acc2-4a3e-9051-6c78f161365b">
        <omgdi:waypoint x="-635.0" y="-300.0"/>
        <omgdi:waypoint x="-560.0" y="-300.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-27dba26e-e25e-42c7-99b1-4a487c854aba" bpmnElement="sid-e6b42855-57d6-4e8d-96a7-f5b1a004ed3d">
        <omgdc:Bounds x="-165.0" y="115.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-fa3bf5cf-c5da-4626-8def-332c1a2f12e9" bpmnElement="sid-354c035b-2e49-4f5f-88ca-ca1cfd0e27ff">
        <omgdc:Bounds x="-375.0" y="-340.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-757b6ea6-bd95-4eec-b301-c897a784ef2e" bpmnElement="sid-0da818a4-ea4f-4f85-b9ad-e23e943fc2f0">
        <omgdi:waypoint x="-460.0" y="-300.0"/>
        <omgdi:waypoint x="-375.0" y="-300.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-3cf05ae4-6095-4ded-9107-577ddf81469d" bpmnElement="sid-42d7188c-58e7-46c1-994c-3004b8d44b6b">
        <omgdc:Bounds x="-205.0" y="-340.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-76ba8768-a486-4cb9-b9dc-1a12eaa0b600" bpmnElement="sid-1ec2477c-054e-4611-9c01-fcae871faee5">
        <omgdi:waypoint x="-275.0" y="-300.0"/>
        <omgdi:waypoint x="-205.0" y="-300.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-48a1535c-18c4-4749-8e7c-44c5861ba53c" bpmnElement="sid-myReceive">
        <omgdc:Bounds x="-205.0" y="-195.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-33bc9ca7-a4ff-4d01-8dcd-f82789df32c1" bpmnElement="sid-2b9661b6-f462-41f9-98d2-556cd0a2c064">
        <omgdi:waypoint x="-155.0" y="-260.0"/>
        <omgdi:waypoint x="-155.0" y="-195.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-9749617d-f42b-4300-8dd3-c7362c2fdcf0" bpmnElement="sid-5d7dd741-0f16-4100-a21f-3fc2b5808653">
        <omgdc:Bounds x="-375.0" y="-195.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-933e1c6d-abc2-4d32-8d86-66590f3a3fb5" bpmnElement="sid-2bc20ebd-7970-44d9-b6c0-a8448cb91db3">
        <omgdi:waypoint x="-205.0" y="-155.0"/>
        <omgdi:waypoint x="-275.0" y="-155.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-5b38760c-f447-4bc4-8d91-e1203260de7b" bpmnElement="sid-bb78a1a1-3bd8-411a-920b-63ae2eb06949">
        <omgdc:Bounds x="-560.0" y="-195.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-8c7b7240-feae-424a-8533-7310817ad8ac" bpmnElement="sid-e44ef6ef-5e78-4d6b-9fef-45a793579236">
        <omgdi:waypoint x="-375.0" y="-155.0"/>
        <omgdi:waypoint x="-460.0" y="-155.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-4999f21a-5c5a-4aeb-b3a1-d0970216e1ef" bpmnElement="sid-fe09b981-5d42-4cca-a222-9b1256dea9f6">
        <omgdc:Bounds x="-725.0" y="-195.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-c4376369-11d5-4a49-be3b-abc1d4664bb8" bpmnElement="sid-7ed37a45-da59-43d3-ae84-83f57ed56025">
        <omgdi:waypoint x="-560.0" y="-155.0"/>
        <omgdi:waypoint x="-625.0" y="-155.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-7e4436fd-c2f0-4f78-8f8c-95705b65ab65" bpmnElement="sid-5796c07d-53d8-4bd4-a56a-ddff642c06e6">
        <omgdc:Bounds x="-725.0" y="-50.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-0c6a4753-80bb-4b01-9dd1-07a9ecc2eb26" bpmnElement="sid-d65dc071-03b3-44b1-b757-e78fc175cb50">
        <omgdi:waypoint x="-675.0" y="-115.0"/>
        <omgdi:waypoint x="-675.0" y="-50.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-f1b0d350-1a0e-4db0-940c-0403056ad9ce" bpmnElement="sid-2f1cea1d-b3de-451e-8612-0247f27b3d30">
        <omgdc:Bounds x="-555.0" y="-50.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-e3edbe78-449d-4e20-8835-6e9d5923657f" bpmnElement="sid-389b9aea-15c7-41ed-b509-80c7b90ef6a5">
        <omgdi:waypoint x="-625.0" y="-10.0"/>
        <omgdi:waypoint x="-555.0" y="-10.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-85558274-3c5f-4251-91fa-c7929e0b848d" bpmnElement="sid-968bc676-cb2a-4e38-ab51-037f174addfb">
        <omgdc:Bounds x="-375.0" y="-50.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-847326d8-1d9d-439a-b5fc-fc04d53b7ca5" bpmnElement="sid-285f6ce8-8b6c-4aa0-8a47-ffe9ddcd535f">
        <omgdi:waypoint x="-455.0" y="-10.0"/>
        <omgdi:waypoint x="-375.0" y="-10.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-ac87169d-593d-4bfa-a0b7-370ae27e1181" bpmnElement="sid-e3e82c89-44df-4beb-89d6-29a5819386d9">
        <omgdc:Bounds x="-205.0" y="-50.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-d9f11d8b-1d81-43eb-b0ab-1c889868c2d2" bpmnElement="sid-e7db60c3-6b77-42b1-8921-7c5b45cf2e52">
        <omgdi:waypoint x="-275.0" y="-10.0"/>
        <omgdi:waypoint x="-205.0" y="-10.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-795e60ae-ca8d-4b57-97c1-44fd8d424451" bpmnElement="sid-ba0aefb1-778c-45d2-92f5-a448479b66cd">
        <omgdi:waypoint x="135.0" y="280.0"/>
        <omgdi:waypoint x="205.0" y="280.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-dda2b7be-0018-4e00-a39e-31244236942c" bpmnElement="sid-2acc642a-907d-47dd-bf42-07008e856a0e">
        <omgdi:waypoint x="255.0" y="320.0"/>
        <omgdi:waypoint x="255.0" y="420.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-52d51756-b205-4b41-8267-a8142e969843" bpmnElement="sid-fa098a45-db8a-415f-96b2-6b1dc1cd3c26">
        <omgdi:waypoint x="-155.0" y="30.0"/>
        <omgdi:waypoint x="-149.99998" y="114.99999"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

4.BPMN 流程图绘制插件

IDEA 插件:Flowable BPMN visualizer

安装好该插件后即可在 IDEA 中通过右键创建流程模板了

在这里插入图片描述

右键可以创建不同节点

在这里插入图片描述

右键用该插件打开上面 bpmn 配置文件,如下图,本配置包含了大部分 Flowable节点的简单测试实现

在这里插入图片描述

5.测试代码

1.启动类

package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class WflApp {

    public static void main(String[] args) {
        SpringApplication.run(WflApp.class,args);
        System.out.println("Hello world!");
    }
}

2.Flowable 配置

package org.example.config;

import org.example.exp.external.MyExternalWorker;
import org.flowable.common.engine.impl.EngineDeployer;
import org.flowable.common.engine.impl.history.HistoryLevel;
import org.flowable.dmn.engine.deployer.DmnDeployer;
import org.flowable.engine.impl.rules.RulesDeployer;
import org.flowable.job.service.JobServiceConfiguration;
import org.flowable.job.service.impl.asyncexecutor.DefaultJobManager;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-02 下午 6:18
 */
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {

    @Value("${flowable.async-executor-activate}")
    boolean asyncExecutorActivate;

    @Value("${flowable.history-level}")
    HistoryLevel historyLevel;

    @Override
    public void configure(SpringProcessEngineConfiguration engineConfiguration) {

        //设置字体
        engineConfiguration.setActivityFontName("宋体");
        engineConfiguration.setLabelFontName("宋体");
        engineConfiguration.setAnnotationFontName("宋体");


        //设置 RULE 依赖
        List<EngineDeployer> customPostDeployers = new ArrayList<>();
        customPostDeployers.add(new RulesDeployer());
        customPostDeployers.add(new DmnDeployer());
        engineConfiguration.setCustomPostDeployers(customPostDeployers);

        // JOB 执行器
        engineConfiguration.setAsyncExecutorActivate(asyncExecutorActivate);
        engineConfiguration.setHistoryLevel(historyLevel);

        //设置外部处理器
        engineConfiguration.addCustomJobHandler(new MyExternalWorker());

        //设置 JOB Manger
        String engineName = engineConfiguration.getEngineName();
        JobServiceConfiguration configuration = new JobServiceConfiguration(engineName);
        DefaultJobManager jobManager = new DefaultJobManager(configuration);
        engineConfiguration.setJobManager(jobManager);
    }
}

3.Camel 配置

1.Camel 配置
package org.example.config;

import jakarta.annotation.PostConstruct;
import org.apache.camel.CamelContext;
import org.example.router.CamelRouter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-08 下午 3:06
 */
@Component
public class MyCamelContext {

    @Autowired
    CamelContext camelContext;

    @PostConstruct
    public void init() throws Exception {
        camelContext.addRoutes(new CamelRouter());
        System.out.println();
    }
}

2.Camel Router 定义
package org.example.router;

import org.apache.camel.builder.RouteBuilder;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-08 下午 2:26
 */
public class CamelRouter extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("flowable://leave_approval_process:sid-bb78a1a1-3bd8-411a-920b-63ae2eb06949")
                .setBody(constant("hello world!"))
                .to("log:output");
    }
}

4.扩展类监听

1.外部工作类
package org.example.exp.external;

import org.flowable.common.engine.api.scope.ScopeTypes;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.bpmn.helper.ErrorPropagation;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.CountingEntityUtil;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
import org.flowable.variable.service.VariableService;
import org.flowable.variable.service.impl.persistence.entity.VariableInstanceEntity;

import java.util.List;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-09 上午 10:07
 */
public class MyExternalWorker implements JobHandler {

    /**
     * 自定义实现覆盖默认实现
     */
    private static final String type = "async-continuation";

    /**
     * key 用于获取唯一的处理器 JobHandlers
     * @return
     */
    @Override
    public String getType() {
        return type;
    }

    /**
     * 定义一个异步任务 用于在流程外执行一些处理逻辑 弱关联性
     * @param job
     * @param configuration
     * @param variableScope
     * @param commandContext
     */
    @Override
    public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {


        System.out.println("Step12 My External work take ...");

        ExecutionEntity executionEntity = (ExecutionEntity) variableScope;

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        VariableService variableService = processEngineConfiguration.getVariableServiceConfiguration().getVariableService();
        List<VariableInstanceEntity> jobVariables = variableService.findVariableInstanceBySubScopeIdAndScopeType(executionEntity.getId(), ScopeTypes.BPMN_EXTERNAL_WORKER);
        for (VariableInstanceEntity jobVariable : jobVariables) {
            executionEntity.setVariable(jobVariable.getName(), jobVariable.getValue());
            CountingEntityUtil.handleDeleteVariableInstanceEntityCount(jobVariable, false);
            variableService.deleteVariableInstance(jobVariable);
        }

        if (configuration != null && configuration.startsWith("error:")) {
            String errorCode;
            if (configuration.length() > 6) {
                errorCode = configuration.substring(6);
            } else {
                errorCode = null;
            }
            ErrorPropagation.propagateError(errorCode, executionEntity);
        } else {
            CommandContextUtil.getAgenda(commandContext).planTriggerExecutionOperation(executionEntity);
        }
    }
}

2.普通Java服务扩展
package org.example.exp.service;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-04 下午 10:04
 */
@Component("queryRemainingDays")
public class QueryRemainingDays implements JavaDelegate {

    public void execute(DelegateExecution execution) {

        System.out.println("Step2 QueryRemainingDays ...");
    }
}

3.Camel Listener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-08 下午 2:54
 */
public class CamelListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {
        System.out.println("Step7 ServiceTask Camel ...");
    }
}
4.DmnEndListener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-09 上午 2:08
 */
public class DmnEndListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {
        Object object = execution.getVariable("myResult");
        System.out.println(String.format("Step10 ServiceTask Dmn End ... I'm %s",object));
    }
}
5.DmnStartListener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-08 下午 11:42
 */
public class DmnStartListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {
        execution.setVariable("age",20);
        System.out.println("Step10 ServiceTask Dmn ... ");
    }
}

6.ExternalWorkerListener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

import java.util.List;
import java.util.Map;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-09 上午 10:31
 */
public class ExternalWorkerListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {

        System.out.println("Step12 ServiceTask ExternalWorker ...");
    }

}

7.HttpListener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-08 下午 5:10
 */
public class HttpListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {
        Object object = execution.getVariable("result");
        System.out.println(String.format("Step8 ServiceTask Http ... result : %s",object));
    }
}
8.MailListener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-08 下午 11:02
 */
public class MailListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {
        System.out.println("Step9 ServiceTask Mail ... ");
    }
}

9.ManualTaskListener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-08 下午 1:53
 */
public class ManualTaskListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {
        System.out.println("Step6 ManualTask ...");
    }
}
10.ReceiveTaskListener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-08 上午 11:59
 */
public class ReceiveTaskListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {
        System.out.println("Step5 ReceiveTask Trigger ...");
    }
}

11.ScriptListener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-05 下午 3:33
 */
public class ScriptListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {
        System.out.println(String.format("Step3 Script: Sum %s",execution.getVariable("sum")));
    }
}

12.ShellListener
package org.example.exp.listener;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-09 上午 9:42
 */
public class ShellListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution execution) {
        String output = (String) execution.getVariable("shellOutput");
        System.out.println(String.format("Step11 ServiceTask Shell ... output : %s",output.replace("\n"," ")));
    }
}

13.UserTaskListener
package org.example.exp.listener;

import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-05 上午 11:07
 */
public class UserTaskListener implements TaskListener {

    @Override
    public void notify(DelegateTask delegateTask) {
        System.out.println("Step1 Create ...");
    }
}

5.测试接口类

包含用户节点的查询和审批,接收节点的触发

package org.example.controller;

import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpServletResponse;
import org.example.exp.service.QueryRemainingDays;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author zhx & moon
 * @Since 1.8
 * @Date 2024-01-02 下午 6:21
 */
@RestController
@RequestMapping("/wfl")
public class ProcessController {

    @Autowired
    QueryRemainingDays queryRemainingDays;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    TaskService taskService;

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    ProcessEngine engine;

    /**
     * 查询流程定义
     * @return
     */
    @GetMapping("/queryProcessDefinition")
    public List<String> queryProcessDefinition(){
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();
        return list.stream().map(ProcessDefinition::getId).collect(Collectors.toList());
    }

    /**
     * 通过 ID 激活流程
     * @param id
     */
    @GetMapping("/active/{id}")
    public void active(@PathVariable("id") String id){

        if (!StringUtils.hasLength(id)){
            id = "leave_approval_process";
        }
        HashMap<String,Object> map = new HashMap<>();
        map.put("id","111");
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(id,map);
        System.out.println(String.format("instance id :%s ",instance.getId()));
    }

    /**
     * 获取任务
     * 可以根据候选人、组来查询
     * 认领 taskService.claim(t.getId(),"admin");
     * 转交 taskService.setAssignee(t.getId(),"admin");
     * 回退 taskService.setAssignee(t.getId(),null);
     */
    @GetMapping("/queryUserTask")
    public List<JSONObject> queryUserTask(){
        List<Task> list = taskService.createTaskQuery().list();
        List<JSONObject> result = new ArrayList<>(list.size());
        JSONObject object;
        for (Task t:list){
            object = new JSONObject();
            object.put("taskId",t.getId());
            object.put("userId",t.getAssignee());
            result.add(object);
        }
        return result;
    }

    /**
     * 认领和处理
     * @param taskId
     */
    @GetMapping("/claimAndComplete")
    public void claimAndComplete(@RequestParam("taskId") String taskId,@RequestParam("userCode") String userCode){

        //获取任务
        Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(userCode).singleResult();
        if (task!=null){
            //TODO 审批校验,通过或不通过
            //完成
            taskService.complete(taskId);
        }
    }

    /**
     * 等待任务:即任务执行到本节点会阻塞,如本节点前会进行一系列非常复杂的业务处理,需要人为或后台线程单独检测完成情况,并决定是否继续触发
     * @param receiveTaskId
     */
    @GetMapping("/receiveTaskHandler")
    public void receiveTaskHandler(@RequestParam("receiveTaskId") String receiveTaskId){

        //查找当前流实例
        List<Execution> list = runtimeService.createExecutionQuery().activityId(receiveTaskId).list();
        for (Execution execution:list){
            runtimeService.trigger(execution.getId());
        }
    }


    /**
     * 查询流实例
     * @return
     */
    @GetMapping("/queryProcessInstance/{key}")
    public List<JSONObject> queryProcessInstance(@PathVariable("key") String key){
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processDefinitionKey(key).list();
        List<JSONObject> result = new ArrayList<>(list.size());
        JSONObject object;
        for (ProcessInstance instance:list){
            object = new JSONObject();
            object.put("key",instance.getProcessDefinitionKey());
            object.put("instanceId",instance.getProcessInstanceId());
            result.add(object);
        }

        return result;
    }

    /**
     * 批量删除实例
     * @param key
     */
    @DeleteMapping("/deleteProcessInstance/{key}")
    public void deleteProcessInstance(@PathVariable("key") String key){
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processDefinitionKey(key).list();
        List<String> ids = list.stream().map(ProcessInstance::getId).collect(Collectors.toList());
        runtimeService.bulkDeleteProcessInstances(ids,"test");

    }

    /**
     * 绘制流程图
     * @param response
     * @param instanceId
     * @throws IOException
     */
    @GetMapping("/buildFlowChart")
    public void buildFlowChart(@RequestParam("instanceId") String instanceId,HttpServletResponse response) throws IOException {

        //查找实例
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        if (null == pi){
            return;
        }
        //查找已执行节点
        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(instanceId).list();
        List<String> act = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution execution:executions){
            List<String> ids = runtimeService.getActiveActivityIds(execution.getId());
            act.addAll(ids);
        }
        //获取 bpmn 模型并绘制流程图
        BpmnModel model = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engineConfiguration = engine.getProcessEngineConfiguration();
        ProcessDiagramGenerator generator = engineConfiguration.getProcessDiagramGenerator();
        InputStream in = generator.generateDiagram(model,"png",act,flows,engineConfiguration.getActivityFontName(),engineConfiguration.getLabelFontName(),engineConfiguration.getAnnotationFontName(),engineConfiguration.getClassLoader(),1.0,false);
        int length;
        byte[] bytes = new byte[4096];
        OutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            while ((length=in.read(bytes)) != -1){
                outputStream.write(bytes,0,length);
            }
            outputStream.flush();
            
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (null != in){
                in.close();
            }
            if (null != outputStream){
                outputStream.close();
            }
        }
    }
}

三.测试

1.测试

创建流实例、查询待审批节点、审批、触发接收节点

输出日志

在这里插入图片描述

2.导出流进度

接口:/flowable-rest/wfl/buildFlowChart?instanceId=0626e0cb-af11-11ee-9530-00155da1d269

在这里插入图片描述

3.Postman 配置文件

{
	"info": {
		"_postman_id": "a6e5c056-e06d-40d2-b499-6db72d51c413",
		"name": "Flowable",
		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
		"_exporter_id": "29860655"
	},
	"item": [
		{
			"name": "启动流",
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "127.0.0.1:8086/flowable-rest/wfl/active/leave_approval_process",
					"host": [
						"127",
						"0",
						"0",
						"1"
					],
					"port": "8086",
					"path": [
						"flowable-rest",
						"wfl",
						"active",
						"leave_approval_process"
					]
				}
			},
			"response": []
		},
		{
			"name": "查询用户任务",
			"event": [
				{
					"listen": "test",
					"script": {
						"exec": [
							"pm.test(\"set token\",function(){\r",
							"    var json = pm.response.json()\r",
							"    pm.environment.set(\"instanceId\",json.get(0).taskId)\r",
							"});"
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "http://127.0.0.1:8086/flowable-rest/wfl/queryUserTask",
					"protocol": "http",
					"host": [
						"127",
						"0",
						"0",
						"1"
					],
					"port": "8086",
					"path": [
						"flowable-rest",
						"wfl",
						"queryUserTask"
					]
				}
			},
			"response": []
		},
		{
			"name": "用户审批",
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "http://127.0.0.1:8086/flowable-rest/wfl/claimAndComplete?taskId={{instanceId}}&userCode=admin",
					"protocol": "http",
					"host": [
						"127",
						"0",
						"0",
						"1"
					],
					"port": "8086",
					"path": [
						"flowable-rest",
						"wfl",
						"claimAndComplete"
					],
					"query": [
						{
							"key": "taskId",
							"value": "{{instanceId}}"
						},
						{
							"key": "userCode",
							"value": "admin"
						}
					]
				}
			},
			"response": []
		},
		{
			"name": "等待触发",
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "http://127.0.0.1:8086/flowable-rest/wfl/receiveTaskHandler?instanceId={{instanceId}}&receiveTaskId=sid-myReceive",
					"protocol": "http",
					"host": [
						"127",
						"0",
						"0",
						"1"
					],
					"port": "8086",
					"path": [
						"flowable-rest",
						"wfl",
						"receiveTaskHandler"
					],
					"query": [
						{
							"key": "instanceId",
							"value": "{{instanceId}}"
						},
						{
							"key": "receiveTaskId",
							"value": "sid-myReceive"
						}
					]
				}
			},
			"response": []
		},
		{
			"name": "查询流实例",
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "http://127.0.0.1:8086/flowable-rest/wfl/queryProcessInstance/leave_approval_process",
					"protocol": "http",
					"host": [
						"127",
						"0",
						"0",
						"1"
					],
					"port": "8086",
					"path": [
						"flowable-rest",
						"wfl",
						"queryProcessInstance",
						"leave_approval_process"
					]
				}
			},
			"response": []
		},
		{
			"name": "删除流实例",
			"request": {
				"method": "DELETE",
				"header": [],
				"url": {
					"raw": "http://127.0.0.1:8086/flowable-rest/wfl/deleteProcessInstance/leave_approval_process",
					"protocol": "http",
					"host": [
						"127",
						"0",
						"0",
						"1"
					],
					"port": "8086",
					"path": [
						"flowable-rest",
						"wfl",
						"deleteProcessInstance",
						"leave_approval_process"
					]
				}
			},
			"response": []
		},
		{
			"name": "根据实例绘制流显示进度",
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "http://127.0.0.1:8086/flowable-rest/wfl/buildFlowChart?instanceId={{instanceId}}",
					"protocol": "http",
					"host": [
						"127",
						"0",
						"0",
						"1"
					],
					"port": "8086",
					"path": [
						"flowable-rest",
						"wfl",
						"buildFlowChart"
					],
					"query": [
						{
							"key": "instanceId",
							"value": "{{instanceId}}"
						}
					]
				}
			},
			"response": []
		},
		{
			"name": "MQ 发数测试",
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "127.0.0.1:8085/test/topic?msg=abc",
					"host": [
						"127",
						"0",
						"0",
						"1"
					],
					"port": "8085",
					"path": [
						"test",
						"topic"
					],
					"query": [
						{
							"key": "msg",
							"value": "abc"
						}
					]
				}
			},
			"response": []
		}
	]
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/306327.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot 创建定时任务(配合数据库动态执行)

相关参考文件如下&#xff1a; SpringBoot 创建定时任务&#xff08;配合数据库动态执行&#xff09; - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/88875287

css如何让两个元素在同一水平线上(文字和svg图片)

一开始写发现这两者不在同一水平线 起初用margin-top margin-bottom来协调 发现效果并不好 1&#xff1a;写法僵硬 2&#xff1a;margin会把div撑破&#xff0c;达不到预期效果&#xff08;padding也是&#xff09; 3. 加了flex布局 之后, 因为我这个是在表格里面,无法居中…

RK3568平台开发系列讲解(Linux系统篇)/dev/mem 物理内存映射到用户态

🚀返回专栏总目录 文章目录 一、/dev/mem 配置二、/dev/mem 设备的使用方法三、mmap 函数沉淀、分享、成长,让自己和他人都能有所收获!😄 一、/dev/mem 配置 /dev/mem 是 Linux 系统中的一个虚拟设备,通常与 mmap 结合使用,可以将设备的物理内存映射到用户态,以实现用…

QT DAY2 作业

1. 头文件 #ifndef MYWIDGET_H #define MYWIDGET_H#include <QWidget> #include <QMovie> #include <QTextEdit> #include <QDebug> namespace Ui { class MyWidget; }class MyWidget : public QWidget {Q_OBJECTpublic:explicit MyWidget(QWidget *p…

2024 Midjourney 基础教程(⼆):了解 Midjourney Bot 和AI绘画使用技巧进阶教学

在上⼀篇⽂章中&#xff0c;我们学到了如何注册 Midjourney &#xff0c;开通付费订阅&#xff0c;并画出了可能是⾃⼰的第⼀张 AI绘画。怎么样&#xff1f;这种将想象的画⾯&#xff0c;变为现实世界图⽚的感觉。 是否有种造物者的错觉&#xff0c;同时有种开盲盒的惊喜感&…

20230403读书笔记|《苏轼词集》——试问岭南应不好,此心安处是吾乡

20230403读书笔记|《苏轼词集》——试问岭南应不好&#xff0c;此心安处是吾乡 导读卷一卷二卷三 在书架里加了好几本苏轼词集&#xff0c;很多词是重复的&#xff0c;但这并不影响多读几本&#xff0c;苏轼的词豪迈大气&#xff0c;豁达爽朗&#xff0c;蛮值得一读的。有不少都…

图解Kubernetes的服务(Service)

pod 准备&#xff1a; 不要直接使用和管理Pods&#xff1a; 当使用ReplicaSet水平扩展scale时&#xff0c;Pods可能被terminated当使用Deployment时&#xff0c;去更新Docker Image Version&#xff0c;旧Pods会被terminated&#xff0c;然后创建新Pods 0 啥是服务&#xf…

【LeetCode:200. 岛屿数量 | DFS 】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Low Poly Cartoon House Interiors

400个独特的低多边形预制件的集合,可以轻松创建高质量的室内场景。所有模型都已准备好放入场景中,并使用一个纹理创建,以提高性能!包含演示场景! 模型分类: - 墙壁(79件) - 地板(28块) - 浴室(33个) - 厨房(36件) - 厨房道具(68件) - 房间道具(85件) - 灯具(…

Git LFS 大文件存储

Git 碰到大文件的困境 Git 是业界流行的分布式版本控制工具&#xff0c;本地仓库与远端仓库同样保存了全量的文件和变更历史&#xff0c;这样让代码协作变得简单和高效。但也正因为如此&#xff0c;Git针对大型文件&#xff08;例如图片、视频或其他二进制文件&#xff09;的版…

浅谈智慧路灯安全智能供电方案设计——安科瑞赵嘉敏

摘要: 智慧路灯&#xff0c;作为智慧城市、新基建、城市更新的主要组成部分&#xff0c;近些年在各大城市已得到很好的落地和 应用&#xff0c;但其与传统路灯相比集成大量异元异构电子设备&#xff0c;这些设备的供电电压、接口形式、权属单位各不相同&#xff0c; 如何设计一…

网络高清视频监控平台的告警联动(或报警联动)功能介绍

一、告警联动的定义和概念 视频监控领域的告警联动功能是指通过视频监控平台&#xff0c;将多个相关系统或设备进行集成&#xff0c;实现统一管理和自动化控制。当监控场景中出现异常情况时&#xff0c;系统能够自动触发相应的报警和响应&#xff0c;以保障安全和及时处理异常事…

[算法与数据结构][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝

C与Python中的赋值、浅拷贝与深拷贝 写在前面&#xff1a;Python和C中的赋值与深浅拷贝&#xff0c;由于其各自语言特性的问题&#xff0c;在概念和实现上稍微有点差异&#xff0c;本文将这C和Python中的拷贝与赋值放到一起&#xff0c;希望通过对比学习两语言实现上的异同点&a…

test fuzz-04-模糊测试 jazzer Coverage-guided, in-process fuzzing for the JVM

拓展阅读 开源 Auto generate mock data for java test.(便于 Java 测试自动生成对象信息) 开源 Junit performance rely on junit5 and jdk8.(java 性能测试框架。性能测试。压测。测试报告生成。) test fuzz-01-模糊测试&#xff08;Fuzz Testing&#xff09; test fuzz-…

【Helm 及 Chart 快速入门】02、Helm 基本使用

目录 一、Helm 基本使⽤ 1.1 搜索 chart 应⽤ 1.2 部署 chart 应⽤ 1.3 删除 chart 应⽤ 1.4 定制参数部署应⽤ 1.5 chart 应⽤升级 1.6 chart 应⽤回滚 一、Helm 基本使⽤ 1.1 搜索 chart 应⽤ 使⽤ helm search repo 关键字可以查看相关 charts&#xff1a; […

数据结构之二叉搜索树(Binary Search Tree)

数据结构可视化演示链接&#xff0c;也就是图片演示的网址 系列文章目录 数据结构之AVL Tree 数据结构之B树和B树 数据结构之Radix和Trie 文章目录 系列文章目录示例图定义二叉搜索树满足的条件应用场景 示例图 二叉 线形(顺序插入就变成了线性树&#xff0c;例如插入顺序为&…

安卓逆向某脚本-autojs逆向

引言 上次讲到,为了静态分析,逆向了所有加密字符串。来看下今天我们看下他的流程。 分析app 启动之后会出现一个更新按钮,如图:我们先看下一般autojs 项目是怎么打包的,打包的时候可以选择加密类型,选择dex 类型,然后就是把js 文件变成dex 。 我们先看下一般autojs 项目是…

用html和css实现一个加载页面【究极简单】

要创建一个简单的加载页面&#xff0c;你可以使用 HTML 和 CSS 来设计。以下是一个基本的加载页面示例&#xff1a; HTML 文件 (index.html): <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"…

scVI与MultiVI

scVI&#xff1a;https://docs.scvi-tools.org/en/stable/user_guide/models/scvi.html MultiVI&#xff1a;https://docs.scvi-tools.org/en/stable/user_guide/models/multivi.html 目录 scVI生成推理任务 MultiVI生成推理 scVI single cell variational inference提出了一个…

JVM工作原理与实战(十一):双亲委派机制

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、双亲委派机制 1.双亲委派机制详解 2.父类加载器 3.双亲委派机制的主要作用 二、双亲委派机制常见问题 总结 前言 ​JVM作为Java程序的运行环境&#xff0c;其负责解释和执行字…