工作流引擎之Flowable教程(整合SpringBoot)

简介

Flowable是什么,下面是官方文档介绍:

Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据,等等。这个章节将用一个可以在你自己的开发环境中使用的例子,逐步介绍各种概念与API。

说人话,无论是学生还是职场员工,都用过OA系统,其中的请假流程大家应该都用过,一般是:发起请假->辅导员审批->院长审批,类似这样的流程。如果自己去实现这种功能的话,我个人的想法是使用状态机模式,每个过程如何流转都需要自己去实现,比较麻烦。
而Flowable框架帮我解决了这个麻烦,只需要我们遵循BPMN 2.0(可以理解为一种流程规范)就可以帮我们自动完成。而且它提供了图形化页面,只需要使用拖拽的方式就可以完成流程的定义,我们只用关注业务逻辑即可。

快速开始

我们先用JavaSE简单体验一下,下面会带大家集成SpringBoot和使用图形化界面。
本节例子为:发起请求->经理审批,审批通过则结束,不通过则发送失败邮件。

目录结构

在这里插入图片描述

创建Maven工程

添加Flowable依赖和MySQL依赖

        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-engine</artifactId>
            <version>6.3.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

编写BPMN文件

注:.bpmn20.xml 是固定后缀名

<?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: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"
             xmlns:flowable="http://flowable.org/bpmn"
             typeLanguage="http://www.w3.org/2001/XMLSchema"
             expressionLanguage="http://www.w3.org/1999/XPath"
             targetNamespace="http://www.flowable.org/processdef">

    <process id="holidayRequest" name="Holiday Request" isExecutable="true">

        <startEvent id="startEvent"/>
        <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>

        <userTask id="approveTask" name="Approve or reject request" flowable:assignee="boss"/>
        <sequenceFlow sourceRef="approveTask" targetRef="decision"/>

        <exclusiveGateway id="decision"/>
        <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
          ${approved}
        ]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow  sourceRef="decision" targetRef="sendRejectionMail">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
          ${!approved}
        ]]>
            </conditionExpression>
        </sequenceFlow>

        <serviceTask id="externalSystemCall" name="Enter holidays in external system"
                     flowable:class="org.flowable.CallExternalSystemDelegate"/>
        <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>

        <userTask id="holidayApprovedTask" name="Holiday approved"/>
        <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>

        <serviceTask id="sendRejectionMail" name="Send out rejection email"
                     flowable:class="com.ego.SendRejectionMail"/>
        <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>

        <endEvent id="approveEnd"/>

        <endEvent id="rejectEnd"/>

    </process>

</definitions>

编写测试类

package com.ego;

import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.task.api.Task;

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

public class Main {

    static ProcessEngine processEngine = null;
    static String taskKey = "holidayRequest";
    static String boss = "boss";

    public static void main(String[] args) {

        init();
        deploy("holiday-request.bpmn20.xml");

        Map<String, Object> startVariables = new HashMap<>();
        startVariables.put("employee", "ego");
        startVariables.put("nrOfHolidays", "3");
        startVariables.put("description", "事假");

        startProcess(taskKey, startVariables);

        queryTask(taskKey, boss);


        Map<String, Object> completeVariables = new HashMap<>();
        completeVariables.put("approved", false);

        completeTask(taskKey, boss, completeVariables);

        history("holidayRequest:5:10003");

    }

    /**
     * 初始化
     */
    public static void init(){
        //获取配置类
        ProcessEngineConfiguration configuration = new StandaloneProcessEngineConfiguration();

        //配置数据库连接
        configuration
                .setJdbcDriver("com.mysql.cj.jdbc.Driver")
                .setJdbcUsername("root")
                .setJdbcPassword("root")
                .setJdbcUrl("jdbc:mysql://localhost:3306/flowable-demo?serverTimezone=UTC&nullCatalogMeansCurrent=true")
                //表结构不存在就自动创建
                .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

        //获取引擎类ProcessEngine
        processEngine = configuration.buildProcessEngine();
    }

    /**
     * 部署
     * @param path 配置文件
     */
    public static void deploy(String path){
        RepositoryService repositoryService = processEngine.getRepositoryService();
        repositoryService.createDeployment()
                .addClasspathResource(path)
                .name("请求流程")
                .deploy();

    }

    /**
     * 启动流程实例
     * @param key 流程id
     * @param variables 参数集合
     */
    public static void startProcess(String key, Map<String, Object> variables){
        RuntimeService runtimeService = processEngine.getRuntimeService();
        runtimeService.startProcessInstanceByKey(key, variables);
    }

    /**
     * 查询任务
     * @param key 流程id
     * @param assignee 处理人
     */
    public static void queryTask(String key, String assignee){
        TaskService taskService = processEngine.getTaskService();

        //查询任务列表
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assignee)
                .list();

        for (Task task : list) {
            System.out.println(task.getName());
        }
    }

    /**
     * 处理任务
     * @param key 流程id
     * @param assignee 处理人
     * @param variables 参数集合
     */
    public static void completeTask(String key, String assignee, Map<String, Object> variables){
        TaskService taskService = processEngine.getTaskService();

         List<Task> taskList = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assignee)
                 .list();
         Task task = taskList.get(0);

        taskService.complete(task.getId(), variables);
    }

    /**
     * 查询历史记录
     * @param definitionId
     */
    public static void history(String definitionId){
        HistoryService historyService = processEngine.getHistoryService();
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processDefinitionId(definitionId)
                .finished()
                .orderByHistoricActivityInstanceEndTime().asc()
                .list();
        for (HistoricActivityInstance history : list) {
            System.out.println(history.getActivityName() + ":" + history.getActivityId() + ":" + history.getDurationInMillis() + "毫秒");
        }

    }

}

package com.ego;

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

public class SendRejectionMail implements JavaDelegate {
    @Override
    public void execute(DelegateExecution delegateExecution) {
        System.out.println("不好意思,请假申请未通过...");
    }
}

使用Flowable-ui图形化界面

下载Flowable和Tomcat

Flowable网址:http://www.flowable.org/downloads.html
Tomcat网址:http://tomcat.apache.org/

部署

将Flowable的war包放到Tomcat的webapps目录下,然后启动Tomcat,
访问http://localhost:8080/flowable-ui,默认用户名和密码为 admin / test

绘制

在这里插入图片描述
我们此节和下一节用的例子比之前多一个环节,由组长和经理两个人审核。
在这里插入图片描述

使用建模器绘制,绘制后点击导出,生成xml文件。
在这里插入图片描述

整合SpringBoot

目录结构

在这里插入图片描述

创建Maven工程

添加依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.14</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ego</groupId>
    <artifactId>flowable</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>flowable</name>
    <description>flowable</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.7.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

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

</project>

编写配置文件

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/flowable-demo?serverTimezone=Asia/Shanghai&useSSL=false&nullCatalogMeansCurrent=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

flowable:
  database-schema-update: true


BPMN文件

我们直接将图形化导出的xml文件放到resources目录下即可。

<?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" exporter="Flowable Open Source Modeler" exporterVersion="6.7.2">
    <process id="askForLeave" name="请假流程" isExecutable="true">
        <startEvent id="startEvent1" flowable:formFieldValidation="true"/>
        <userTask id="sid-494B6EC2-AC32-4A6F-8A4F-8016190388FA" name="组长审批" flowable:assignee="#{mentor}" />
        <userTask id="sid-3D540C65-A644-4DF0-B8F5-332B4BD0C338" name="请假申请" flowable:assignee="#{employee}" />
        <sequenceFlow id="sid-6B958ED5-8CE1-451A-AF66-F4B22FBA621C" sourceRef="startEvent1" targetRef="sid-3D540C65-A644-4DF0-B8F5-332B4BD0C338"/>
        <sequenceFlow id="sid-AC31EA26-5738-499E-8634-3B0F88BAEDAC" sourceRef="sid-3D540C65-A644-4DF0-B8F5-332B4BD0C338" targetRef="sid-494B6EC2-AC32-4A6F-8A4F-8016190388FA"/>
        <exclusiveGateway id="sid-1C1F7CC6-1FA3-4F95-83BB-E75B3FDD330B"/>
        <sequenceFlow id="sid-AD16B066-863B-4ED5-8A89-6D4AFC57EAF1" sourceRef="sid-494B6EC2-AC32-4A6F-8A4F-8016190388FA" targetRef="sid-1C1F7CC6-1FA3-4F95-83BB-E75B3FDD330B"/>
        <userTask id="sid-89C5782A-6193-48B8-8891-5C36C46F73A4" name="经理审批" flowable:assignee="#{manager}" />
        <serviceTask id="sid-B3B3A401-C199-4F5E-8D96-2D0C21CE2BFB" name="发送失败邮箱" flowable:class="com.ego.flowable.task.FailTask"/>
        <exclusiveGateway id="sid-CB500461-17D2-4CEF-95E1-BF53CBC1116A"/>
        <endEvent id="sid-295CD660-15BC-45FE-8618-4CDCFE832FD7"/>
        <endEvent id="sid-22B062BD-ED1C-49DE-B97A-ADC092FB9F3B"/>
        <sequenceFlow id="sid-BC00842A-2187-47A3-8348-49629CA3C90C" sourceRef="sid-B3B3A401-C199-4F5E-8D96-2D0C21CE2BFB" targetRef="sid-22B062BD-ED1C-49DE-B97A-ADC092FB9F3B"/>
        <sequenceFlow id="sid-D29EA250-62A9-4976-B98F-6214DBD4D5A4" sourceRef="sid-89C5782A-6193-48B8-8891-5C36C46F73A4" targetRef="sid-CB500461-17D2-4CEF-95E1-BF53CBC1116A"/>
        <sequenceFlow id="sid-A5E01BEF-690A-4126-AD87-5DD05BBD57F7" sourceRef="sid-CB500461-17D2-4CEF-95E1-BF53CBC1116A" targetRef="sid-295CD660-15BC-45FE-8618-4CDCFE832FD7">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[ ${approved} ]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="sid-A93D00FB-8777-47FB-8E4E-FB51E487956B" sourceRef="sid-1C1F7CC6-1FA3-4F95-83BB-E75B3FDD330B" targetRef="sid-89C5782A-6193-48B8-8891-5C36C46F73A4">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[ ${approved} ]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="sid-41511EEA-AB96-474B-9A1D-A534F5A459DC" sourceRef="sid-CB500461-17D2-4CEF-95E1-BF53CBC1116A" targetRef="sid-B3B3A401-C199-4F5E-8D96-2D0C21CE2BFB">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[ ${!approved} ]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="sid-2836484B-86A0-473D-84D2-6BF40FDD652A" sourceRef="sid-1C1F7CC6-1FA3-4F95-83BB-E75B3FDD330B" targetRef="sid-B3B3A401-C199-4F5E-8D96-2D0C21CE2BFB">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[ ${!approved} ]]>
            </conditionExpression>
        </sequenceFlow>
    </process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_askForLeave">
        <bpmndi:BPMNPlane bpmnElement="askForLeave" id="BPMNPlane_askForLeave">
            <bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
                <omgdc:Bounds height="30.0" width="30.000000000000007" x="59.99999821186071" y="149.99999061226887"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-494B6EC2-AC32-4A6F-8A4F-8016190388FA" id="BPMNShape_sid-494B6EC2-AC32-4A6F-8A4F-8016190388FA">
                <omgdc:Bounds height="79.99999999999999" width="100.00000000000006" x="419.999987483025" y="124.99998688697896"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-3D540C65-A644-4DF0-B8F5-332B4BD0C338" id="BPMNShape_sid-3D540C65-A644-4DF0-B8F5-332B4BD0C338">
                <omgdc:Bounds height="80.0" width="100.0" x="215.99999207258247" y="124.99999508261695"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-1C1F7CC6-1FA3-4F95-83BB-E75B3FDD330B" id="BPMNShape_sid-1C1F7CC6-1FA3-4F95-83BB-E75B3FDD330B">
                <omgdc:Bounds height="40.0" width="40.0" x="564.999987483025" y="144.99998688697895"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-89C5782A-6193-48B8-8891-5C36C46F73A4" id="BPMNShape_sid-89C5782A-6193-48B8-8891-5C36C46F73A4">
                <omgdc:Bounds height="80.0" width="100.0" x="649.999987483025" y="124.99998688697895"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-B3B3A401-C199-4F5E-8D96-2D0C21CE2BFB" id="BPMNShape_sid-B3B3A401-C199-4F5E-8D96-2D0C21CE2BFB">
                <omgdc:Bounds height="80.0" width="100.0" x="534.9999715387834" y="269.9999678134942"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-CB500461-17D2-4CEF-95E1-BF53CBC1116A" id="BPMNShape_sid-CB500461-17D2-4CEF-95E1-BF53CBC1116A">
                <omgdc:Bounds height="40.0" width="40.0" x="794.999987483025" y="144.99998688697895"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-295CD660-15BC-45FE-8618-4CDCFE832FD7" id="BPMNShape_sid-295CD660-15BC-45FE-8618-4CDCFE832FD7">
                <omgdc:Bounds height="28.0" width="28.0" x="879.999987483025" y="150.99998688697895"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="sid-22B062BD-ED1C-49DE-B97A-ADC092FB9F3B" id="BPMNShape_sid-22B062BD-ED1C-49DE-B97A-ADC092FB9F3B">
                <omgdc:Bounds height="28.0" width="28.0" x="434.99998703599016" y="295.999958992008"/>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="sid-AC31EA26-5738-499E-8634-3B0F88BAEDAC" id="BPMNEdge_sid-AC31EA26-5738-499E-8634-3B0F88BAEDAC" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="50.00000000000003" flowable:targetDockerY="39.99999999999999">
                <omgdi:waypoint x="315.9499920725825" y="164.9999930738821"/>
                <omgdi:waypoint x="419.9999870473585" y="164.99998889370505"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-6B958ED5-8CE1-451A-AF66-F4B22FBA621C" id="BPMNEdge_sid-6B958ED5-8CE1-451A-AF66-F4B22FBA621C" flowable:sourceDockerX="15.0" flowable:sourceDockerY="15.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="89.94999771072398" y="164.9999909621731"/>
                <omgdi:waypoint x="215.99999203304566" y="164.99999391236872"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-2836484B-86A0-473D-84D2-6BF40FDD652A" id="BPMNEdge_sid-2836484B-86A0-473D-84D2-6BF40FDD652A" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="585.4340131410052" y="184.50916665252876"/>
                <omgdi:waypoint x="585.138211259262" y="269.9999678134942"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-AD16B066-863B-4ED5-8A89-6D4AFC57EAF1" id="BPMNEdge_sid-AD16B066-863B-4ED5-8A89-6D4AFC57EAF1" flowable:sourceDockerX="50.000000000000036" flowable:sourceDockerY="40.0" flowable:targetDockerX="20.5" flowable:targetDockerY="20.5">
                <omgdi:waypoint x="519.9499874830227" y="165.2162206532127"/>
                <omgdi:waypoint x="565.4130309612859" y="165.41303036523982"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-D29EA250-62A9-4976-B98F-6214DBD4D5A4" id="BPMNEdge_sid-D29EA250-62A9-4976-B98F-6214DBD4D5A4" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="20.5" flowable:targetDockerY="20.5">
                <omgdi:waypoint x="749.949987483023" y="165.21622065321273"/>
                <omgdi:waypoint x="795.4130309612859" y="165.41303036523982"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-BC00842A-2187-47A3-8348-49629CA3C90C" id="BPMNEdge_sid-BC00842A-2187-47A3-8348-49629CA3C90C" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="14.0" flowable:targetDockerY="14.0">
                <omgdi:waypoint x="534.9999715387834" y="309.9999645703004"/>
                <omgdi:waypoint x="462.9499152959249" y="309.9999598968591"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-A5E01BEF-690A-4126-AD87-5DD05BBD57F7" id="BPMNEdge_sid-A5E01BEF-690A-4126-AD87-5DD05BBD57F7" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="14.0" flowable:targetDockerY="14.0">
                <omgdi:waypoint x="834.5591744228417" y="165.37819201518406"/>
                <omgdi:waypoint x="880.0002630355089" y="165.08883877124302"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-41511EEA-AB96-474B-9A1D-A534F5A459DC" id="BPMNEdge_sid-41511EEA-AB96-474B-9A1D-A534F5A459DC" flowable:sourceDockerX="20.000012516974948" flowable:sourceDockerY="35.00001326203267" flowable:targetDockerX="99.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="812.0900320941328" y="182.06913118148157"/>
                <omgdi:waypoint x="634.9499715387833" y="309.28173606088245"/>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="sid-A93D00FB-8777-47FB-8E4E-FB51E487956B" id="BPMNEdge_sid-A93D00FB-8777-47FB-8E4E-FB51E487956B" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="604.5247245557606" y="165.4166535536456"/>
                <omgdi:waypoint x="649.999987483025" y="165.2181091577213"/>
            </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
</definitions>

编写Controller

为简化步骤,本案例将业务逻辑放在Controller里,实际开发请放到Service中。

package com.ego.flowable.controller;

import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

@RestController
public class AskForLeaveController {

    private static final Logger LOGGER = LoggerFactory.getLogger(AskForLeaveController.class);

    @Autowired
    RuntimeService runtimeService;
    @Autowired
    TaskService taskService;
    @Autowired
    RepositoryService repositoryService;
    @Autowired
    ProcessEngine processEngine;

    private static final String EMPLOYEE = "1001";
    private static final String MENTOR = "101";
    private static final String MANAGER = "10";

    /**
     * 查看流程图
     * @param resp
     * @param processId 流程id
     * @throws Exception
     */
    @GetMapping("/pic")
    public void showPic(HttpServletResponse resp, String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        if (pi == null) {
            return;
        }
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(processId)
                .list();

        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }

        /**
         * 生成流程图
         */
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, false);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = resp.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * 开启流程
     */
    @PostMapping("/start")
    public void start(){
        Map<String, Object> map = new HashMap<>();
        map.put("employee", EMPLOYEE);
        map.put("name", "ego");
        map.put("reason", "出去玩");
        map.put("days", 10);
        ProcessInstance instance = runtimeService.startProcessInstanceByKey("askForLeave", map);
        LOGGER.info("开启请假流程 processId:{}", instance.getId());
    }

    /**
     * 员工提交给组长
     */
    @PostMapping("/submitToMentor")
    public void submitToMentor(){
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee(EMPLOYEE)
                .orderByTaskId()
                .desc()
                .list();
        for (Task task : list) {
            Map<String, Object> variables = taskService.getVariables(task.getId());
            LOGGER.info("员工任务:任务id:{}, 请假人:{}, 请假原因:{}, 请假天数:{}", task.getId(), variables.get("name"), variables.get("reason"), variables.get("days"));
            Map<String, Object> map = new HashMap<>();
            map.put("mentor", MENTOR);
            taskService.complete(task.getId(), map);
        }
    }

    /**
     * 组长提交给经理
     * @param approved 是否通过
     */
    @PostMapping("/submitToManager")
    public void submitToManager(Boolean approved){
        System.out.println(approved);
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee(MENTOR)
                .orderByTaskId()
                .desc()
                .list();
        for (Task task : list) {
            Map<String, Object> variables = taskService.getVariables(task.getId());
            LOGGER.info("组长任务:任务id:{}, 请假人:{}, 请假原因:{}, 请假天数:{}", task.getId(), variables.get("name"), variables.get("reason"), variables.get("days"));
            Map<String, Object> map = new HashMap<>();
            map.put("approved", approved);
            if(approved){
                map.put("manager", MANAGER);
            }
            taskService.complete(task.getId(), map);
        }
    }

    /**
     * 经理审核
     * @param approved 是否通过
     */
    @PostMapping("/managerApprove")
    public void managerApprove(Boolean approved){
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee(MANAGER)
                .orderByTaskId()
                .desc()
                .list();
        for (Task task : list) {
            Map<String, Object> variables = taskService.getVariables(task.getId());
            LOGGER.info("经理任务:任务id:{}, 请假人:{}, 请假原因:{}, 请假天数:{}", task.getId(), variables.get("name"), variables.get("reason"), variables.get("days"));
            Map<String, Object> map = new HashMap<>();
            map.put("approved", approved);
            taskService.complete(task.getId(), map);
        }
    }
}

package com.ego.flowable.task;

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

public class FailTask implements JavaDelegate {
    @Override
    public void execute(DelegateExecution delegateExecution) {
        System.out.println("请假失败...");
    }
}

流程图中文显示方框问题

配置一下即可。

package com.ego.flowable.config;

import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;


@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {

    @Override
    public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
        springProcessEngineConfiguration.setActivityFontName("宋体");
        springProcessEngineConfiguration.setLabelFontName("宋体");
        springProcessEngineConfiguration.setAnnotationFontName("宋体");
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
从基础讲起,结合应用场景,由浅到深细化讲解BPMN和Flowable的相关组件,并结合具体实例,演示功能的使用和注意事项。最终结合Springboot搭建一套工作流系统,囊括一般项目中所需要的知识点,理论结合实际,让真正入门到熟练。 1 简介 2 学习指南 2.1 Flowable初体验 2.1.1 Flowable是什么? 2.1.2 Flowable 和 Activiti 2.1.3 构建命令行应用程序 2.1.3.1 创建一个流程引擎 2.1.3.2 部署一个流程定义 2.1.3.3 启动一个流程实例 2.1.3.4 查询和完成一个任务 2.1.3.5 写一个JavaDelegate 2.1.3.6 查询历史数据 2.2 Flowable整合Spring 2.3 Flowable整合SpringBoot 2.4 Flowable流程定义部署 2.4.1 使用xml部署 2.4.2 使用压缩文件部署 2.4.3 使用IO流部署 3 BPMN2.0简介 3.1 什么是BPMN2.0 3.2 创建一个BPMN 3.2.1 直接编写XML文件 3.2.2 使用插件编写 3.2.2.1 在线安装插件 3.2.2.2 离线安装 3.2.2.3 插件使用说明 4 BPMN2.0组成 4.1 事件 4.1.1 事件定义 4.1.2 计时器事件定义 4.1.2.1 timeDate 4.1.2.1.1 开始事件TimerStartEvent 4.1.2.1.2 中间事件TimerCatchingEvent 4.1.2.1.3 边界事件TimerBoundaryEvent 4.1.2.2 timeDuration 4.1.2.2.1 开始事件TimerStartEvent 4.1.2.1.2 中间事件TimerCatchingEvent 4.1.2.1.3 边界事件TimerBoundaryEvent 4.1.2.3 timeCycle 4.1.2.3.1 开始事件TimerStartEvent 4.1.2.3.2 中间事件TimerCatchingEvent 4.1.2.3.3 边界事件TimerBoundaryEvent 4.1.3 消息事件 4.1.3.1 开始事件MessageStartEvent 4.1.3.2 中间事件MessagecatchingEvent 4.1.3.3 边界事件MessageBoundaryEvent 4.1.4 错误事件 4.1.4.1 开始事件ErrorStartEvent 4.1.4.2 边界事件ErrorBoundaryEvent 4.1.5 信号事件 4.1.5.1 开始事件SignalStartEvent 4.1.5.2 中间事件 4.1.5.2.1 捕捉事件SignalCatchingEvent 4.1.5.2.2 抛出事件SignalThrowingEvent 4.1.5.3 边界事件SignalBoundaryEvent dream21st 4.1.6结束事件 4.1.6.1 错误结束事件ErrorEndEvent 4.1.6.2 中断结束事件TerminateEndEvent 4.1.6.2.1 中断结束事件案例一 4.1.6.2.2 中断结束事件案例二 4.1.6.3 取消结束事件 CancelEndEvent 4.1.7 补偿事件CompensationThrowing 4.1.8 网关 4.1.8.1 并行网关ParallelGateway 4.1.8.2 排他网关ExclusiveGateway 4.1.8.3 包容网关InclusiveGateWay 4.1.8.4 事件网关EventGateway 4.2 任务 4.2.1 用户任务UserTask 4.2.1.1 用户任务入门案例Assignee指定 4.2.1.2 CandidateUser和CandidateGroup指定 4.2.1.3 多人会签MultiInstance 4.2.1.4 动态表单 4.2.2 服务任务ServiceTask 4.2.3 手工任务ManualTask 4.2.4 接受任务ReceiveTask 4.2.5 调用流程CallActivity 4.2.5.1 固定子流程 4.2.5.2 动态子流程 4.3 容器 5 工作流实战案例 5.1 实战案例一 5.1.1 部署流程定义 5.1.2 启动流程实例 5.1.3 查询待办任务 5.1.4 提交任务 5.1.5 查询候选任务 5.1.6 获取候选任务 5.1.7 通过流程实例ID查询任务流转图 5.2 实战案例二
<h3>回答1:</h3><br/>Flowable是一个开源的工作流引擎,可以用于设计和执行各种类型的工作流。Spring Boot是一个开源的Java框架,可以快速构建基于Spring的应用程序。将FlowableSpring Boot整合可以使开发人员更轻松地构建和管理工作流应用程序。 要将Flowable整合Spring Boot中,可以使用Flowable Spring Boot Starter。该starter包含了Flowable引擎Spring Boot的集成,可以轻松地将Flowable引擎集成到Spring Boot应用程序中。在pom.xml文件中添加以下依赖项即可: ``` <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>6.6.</version> </dependency> ``` 然后,在Spring Boot应用程序中,可以使用Flowable的API来创建和管理工作流。例如,可以使用以下代码创建一个简单的工作流: ``` @Service public class MyWorkflowService { @Autowired private RuntimeService runtimeService; public void startWorkflow(String processDefinitionKey) { runtimeService.startProcessInstanceByKey(processDefinitionKey); } } ``` 在这个例子中,我们注入了Flowable的RuntimeService,并使用它来启动一个指定的流程定义。 总之,FlowableSpring Boot整合可以使开发人员更轻松地构建和管理工作流应用程序。通过使用Flowable Spring Boot Starter,可以轻松地将Flowable引擎集成到Spring Boot应用程序中,并使用Flowable的API来创建和管理工作流。 <h3>回答2:</h3><br/>flowable是一个开源的工作流引擎,它能够帮助开发人员快速的构建和部署自己的工作流应用。而springboot则是一种快速开发框架,它可以帮助开发人员快速搭建一个基于spring的web应用。在实际开发中,我们经常需要将flowablespringboot整合在一起使用,这可以让我们更加高效的开发工作流应用。 首先,我们需要在springboot的pom文件中引入flowable的依赖,以便在我们的代码中使用flowable的功能。同时,我们还需要在application.yml文件中进行配置,配置相关的数据库连接信息,例如数据库名称、数据库URL、数据库用户名和密码等。这些配置需要与流程引擎的数据库进行对应。 接下来,我们需要创建一个Java类来配置flowable引擎。在这个类中,我们可以配置许多flowable的参数,例如流程图的存储位置、流程实例的缓存设置等等。在配置完这些参数后,我们需要使用流程引擎的builder()方法创建一个flowable引擎实例并将其注入到springboot的容器中,以便后续代码中可以使用。 在整合flowablespringboot之后,我们可以在代码中使用所有flowable的功能。例如,我们可以使用flowable的TaskService来获取任务信息,使用RuntimeService来启动流程实例,使用RepositoryService来获取流程图等等。总之,整合flowablespringboot可以帮助我们更快、更便捷的开发工作流应用。 <h3>回答3:</h3><br/>Flowable是一个流程引擎框架,它提供了流程定义、流程实例、任务管理、工作流管理等一系列的功能模块。与此同时,Spring Boot是一个快速构建现代化基于Spring框架的应用程序的工具。利用这两种框架的优势,可以实现快速开发基于流程引擎的应用。 Flowable整合Spring Boot的步骤如下: 1. 创建Spring Boot项目 通过Spring Initializr创建一个基于Spring Boot的项目,选择需要的依赖项和应用程序配置。 2. 添加Flowable依赖 在pom.xml文件中添加以下依赖项: ``` <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>6.6.0</version> </dependency> ``` 3. 创建流程定义 Flowable支持BPMN2.0规范的流程定义,可以使用Flowable Modeler或者其他的BPMN2.0编辑器进行流程定义。 4. 配置Flowable 在application.yml文件中添加Flowable的配置,如下所示: ``` flowable: database-schema-update: true history-level: full ``` 5. 集成Flowable服务 在Spring Boot应用程序中使用Flowable的服务,可以通过注入以下服务来访问它们: - RepositoryService:管理部署的流程定义和流程模型。 - RuntimeService:管理流程实例和执行流程中的任务。 - TaskService:管理任务,例如签收、处理和完成任务。 - HistoryService:查询历史流程实例和历史任务信息。 6. 测试流程 通过编写测试代码,可以测试流程定义和流程实例的工作流程。 综上所述,FlowableSpring Boot整合使得开发基于流程引擎的应用变得更加容易。可以利用Spring Boot的便利快速构建应用程序,并利用Flowable流程引擎模块完成流程定义、流程实例和任务管理等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值