JDK21和 Flowable 7.0.0

news2024/11/19 17:50:09

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": []
		}
	]
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL】视图,15道常见面试题---含考核思路详细讲解

目录 一 视图 1.1视图是什么 1.2 创建视图 1.3 查看视图(两种) 1.4 修改视图(两种) 1.5 删除视图 二 外连接&内连接&子查询介绍 2.1 外连接 2.2 内连接 2.3 子查询 三 外连接&内连接&子查询案例 3.1 了解表结构与数据 3.2 15道常见面试题 四 思…

Java中内存模型

参数传递图解与结论 通过上面的两段程序可以得出如下结论&#xff1a;根据内存去解释&#xff0c;局部变量随方法栈的弹出而消失&#xff1b; 当调用方法时&#xff0c;如果传入的数值为基本数据类型&#xff08;包含String类型&#xff09;&#xff0c;形式参数的改变对实际…

虚拟机Ubuntu网络配置

电脑有两个系统&#xff0c;windows系统和ubuntu系统&#xff0c;那网卡到底给哪一个用呢&#xff0c;所以要选择桥接模式&#xff0c;就可以共用网卡 但是我们电脑网卡&#xff0c;有线网卡&#xff0c;无线网卡&#xff0c;到底使用哪个网卡&#xff0c;所以选择桥接到自动或…

IP地址的网络安全防护和预防

网络安全对于保护个人和组织的信息资产至关重要&#xff0c;而IP地址是网络通信的基础。在这篇文章中&#xff0c;IP数据云将探讨IP地址的网络安全防护和预防措施&#xff0c;以确保网络的安全性和可靠性。 IP地址是互联网上每个设备在网络中的唯一标识符。有IPv4和IPv6两种类…

苹果手机怎么设置提醒事项?详细方法在这,记得收藏!

无论是学生党还是上班族&#xff0c;大家每天都需要处理许多任务和事项。为了更好地管理这些事项&#xff0c;苹果手机为用户提供了提醒功能。 通过设置提醒事项&#xff0c;我们可以减少忘记重要任务的可能性。那么&#xff0c;苹果手机怎么设置提醒事项呢&#xff1f;在本文…

阳光宝宝的视界:新生儿补充叶黄素的小心指南

引言&#xff1a; 叶黄素&#xff0c;作为视觉健康的重要组成部分&#xff0c;对新生儿的眼睛发育和视觉保护具有重要意义。本文将深入探讨叶黄素的功能、补充时机&#xff0c;以及在给新生儿补充叶黄素时应该注意的事项&#xff0c;为小天使们提供最贴心的呵护。 第一部分&am…

【SQL】对表中的记录通过时间维度分组,统计出每组的记录条数

场景&#xff1a;一般用作数据统计&#xff0c;比如统计一个淘宝用户在年、月、日的维度上的订单数。 业务&#xff1a;一个集合&#xff0c;以时间维度来进行分组求和。 准备一张订单表order&#xff0c;有一些常规属性&#xff0c;比如创建时间&#xff0c;订单号。 DDL语句如…

Vue面试之虚拟DOM

Vue面试之虚拟DOM 什么是虚拟dom&#xff1f;虚拟dom是如何产生的&#xff1f;编写模板template模板编译Complie挂载Mounting 如何进行新旧Dom对比&#xff1f; 最近在整理一些前端面试中经常被问到的问题&#xff0c;分为vue相关、react相关、js相关、react相关等等专题&#…

【C语言】指针——从底层原理到应用

C语言指针-从底层原理到花式技巧&#xff0c;用图文和代码帮你讲解透彻 目录 一、前言二、变量与指针的本质 1. 内存地址2. 32位与64位系统3. 变量4. 指针变量5. 操作指针变量 5.1 指针变量自身的值5.2 获取指针变量所指向的数据5.3 以什么样的数据类型来使用/解释指针变量所指…

【研究僧毕业总结】第1024个创作日

目录 前言1. 机缘2. 收获3. 憧憬 前言 收到这封来信&#xff0c;代表从创作至今刚好满足1024天 1024&#xff0c;程序员的记忆 1. 机缘 从学生到社会&#xff0c;都在需求一个记录笔记的软件&#xff0c;而作为程序员&#xff0c;CSDN可云同步又可直接在云平台上看到 选择了…

【算法分析与设计】三数之和

题目 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 示例…

ERD助力研发资产沉淀研发提效

一、从痛点中思考答案 痛点一&#xff1a;复杂系统的设计和逻辑碎片化散落&#xff0c;缺少沉淀导致系统后期维护、迭代以及架构升级都非常困难。 痛点二&#xff1a;由于新需求或新项目导致的系统的老旧逻辑梳理往往耗费大量人力&#xff0c;甚至造成人才的流失。 痛点三&a…

华为云AI:轻松实现图像识别调用

文章目录 前言一、环境配置关键步骤 二、图像识别实例媒资图像标签名人识别 总结 前言 基于华为云AI服务和java使用SDK实现图像识别&#xff0c;主要以媒资图像标签和名人识别为例。 一、环境配置 Maven&#xff08;没有直接下载华为的SDK包&#xff0c;而是使用Maven安装依赖…

CPT203-Software Engineering 笔记

Week 1 -- Introduction failure reason professional software development*** maintain, security, efficiency, acceptability two kinds***: generic, customized software deterioration 软件退化 reduce changes/ side effects after changes software engineering …

Python豆瓣爬虫详解

有没有过周末为看什么电影焦虑&#xff0c;今天手把手教学爬取豆瓣电影评分&#xff01; 0&#xff0c;当我们打开一个网站的时候这个时候网站给到我们一个cookies&#xff0c;这个cokkies可能是多个参数或一个参数&#xff0c;然后我们再浏览其他的页面的时候网站会校验cooki…

理解接雨水算法

一、IDEA注释显示图片 在做题时&#xff0c;需要对照这图片&#xff0c;才能更好的梳理思路。 首先&#xff0c;注释里添加<img/>标签 之后&#xff0c;将鼠标光标放置在需要以阅读模式预览注释的地方&#xff0c;然后按快捷键CtrlAltQ即可 二、接雨水算法 先看接雨水…

文件批量重命名:在原文件名上插入随机字母,高效命名文件的方法

在处理大量文件时&#xff0c;高效的文件命名系统可以大大提高工作效率。下面来看云炫文件管理器如何用简单的方法&#xff0c;轻松的在原文件名上批量插入随机字母&#xff0c;实现高效的文件命名。 原文件名插入随机字母前后的对比效果。 在原文件名上插入随机字母的操作&am…

机器学习 —— 自用整理期末复习笔记

一、绪论 机器学习术语 假设空间 p5 监督学习&#xff08;supervised learning&#xff09;的任务是学习一个模型&#xff0c;使模型能够对任意给定的输入&#xff0c;对其相应的输出做出一个好的预测。模型属于由输入空间到输出空间的映射的集合&#xff0c;这个集合就是假设空…

Nginx实战 | 高性能HTTP和反向代理神器Nginx前世今生,以及它的“繁花之境”

专栏集锦&#xff0c;大佬们可以收藏以备不时之需&#xff1a; Spring Cloud 专栏&#xff1a;http://t.csdnimg.cn/WDmJ9 Python 专栏&#xff1a;http://t.csdnimg.cn/hMwPR Redis 专栏&#xff1a;http://t.csdnimg.cn/Qq0Xc TensorFlow 专栏&#xff1a;http://t.csdni…

基于ssm的图书管理系统设计与实现论文

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自古…