工作流引擎之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
    评论
<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、付费专栏及课程。

余额充值