Flowable 简单介绍及使用

Flowable

一、工作流简介

​ 较早的⼯作流是 jBPM , 这是⼀个由 Java 实现的企业级 流程 引擎 , 是 JBoss 公司开发的产品之⼀ 。 jBPM 的创建者是 Tom Baeyens , 这个⼤佬后来离开了 JBoss , 并加⼊到 Alfresco , 并推出了基于 jBPM4 的开源⼯作流系统 Activiti , ⽽ j BPM 则在 后续的代码中完全放弃了 jBPM4 的代码 。 从这个过程中也能看出来 , jBPM 在发展 过程中 , 由于意⻅相左 , 后来变成了两个 jBPM 和 Activiti 。 然⽽戏剧的是 , Activiti5 没搞多久 , 从 Activiti 中⼜分出来⼀个 Camunda , Activiti 继续发展 , ⼜从中分出来⼀个 Flowable 。

​ 现在市⾯上主流的 流程 引擎就⼀共有三个 : Activiti Flowable Camunda 这三个各有特点 :

	1. Activiti ⽬前是侧重云 , 他⽬前的设计会向 Spring Cloud 、 Docker 这些去靠 拢 。
	2. Flowable 核⼼思想还是在做⼀个功能丰富的 流程 引擎⼯具 , 除了最最基础的⼯作 流 , 他还提供了很多其他的扩展点 , 我们可以基于 Flowable 实现出许多我们想要的功能 。 
	3. Camunda 相对于前两个⽽⾔⽐较轻量级 , Camunda 有⼀个⽐较有特⾊的功能就是他提 供了⼀个⼩巧的编辑器 , 基于 [bpmn.io](http://bpmn.io) 来实现的  。 如果 你的项⽬需求是做⼀个轻巧的 、 灵活的 、 定制性强的编辑器 , ⼯作流是嵌⼊式的 , 那么可 以选择 Camunda 。 

二、相关概念

以简单的请假流程为例:

在这里插入图片描述

一个流程图主要包含四个方面:

  • 事件
  • 连线
  • 任务
  • 网关

事件

在一个流程图中肯定要有的是开始事件和结束事件,就是上图中的圆圈,此外还有一些中间事件,边界事件等。

连线

连线是将事件,任务,网关等连在一起的线条,一般情况下都是普通连线,有的会有一些条件,比如上图中从网关出来的连线需要根据请假申请的审批结果确定走那一条连线

任务

  • 接收任务

请添加图片描述

这个任务并不需要处理额外的事,流程到这一步就自动停下来了,需要人工点一下,推动流程继续向下执行。

  • 发送任务

请添加图片描述

一般用来吧消息发送给外部参与者

  • 服务任务

请添加图片描述

一般由系统自动完成,我们需要自定义一个类,可以在这个自定义的类里面完成自己想要做的事,比较灵活

  • 脚本任务

请添加图片描述

一个自动化活动,当流程执行到脚本任务时,自动执行相应的脚本

  • 用户任务

请添加图片描述

用于为那些需要人工参与者完成的工作建模

服务主要分为两大类

  1. 用户任务:一般表示人工需要介入的事情,比如是否同意,或者一些表单输入,需要让人工完成,一般用户任务和表单结合在一起,用户任务需要用户向引擎提交一个完成任务的动作,否则流程会暂停在这里等待。
  2. 服务任务:服务任务会自动执行,调用服务任务的时候,可以是一个javabean,也可以是一个类的全限定名,也可以是一个远程rest服务,流程会自动执行服务任务。

网关

  • 互斥网关

请添加图片描述

也叫排他性网关,这种网关只有一个有效出口

  • 相容网关

请添加图片描述

这种网关有多个出口,只要满足条件,都会执行

  • 事件网关

请添加图片描述

事件网关是通过中间事件驱动,它在等待的事件发生后才会触发决策,基于事件的网关允许基于事件做出决策

  • 并行网关

请添加图片描述

并行网关一般是成对出现的,如果有一些任务可以并行执行,那么可以用并行网关

三、SpringBoot集成Flowable工作流

3.1 创建Spring Boot 项目并引入依赖

idea 创建spring boot 项目相信大家都很熟练了,就不再赘述了,因为flowable需要用到数据库,所以需要引入mysql和mybatis依赖

引入flowable依赖

        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.7.2</version>
        </dependency>

默认情况下,位于resources/processes目录下的历程都会被自动部署

配置数据库连接信息,项目启动会自动初始化数据库(建表,初始化流程信息),流程引擎运行时候的数据也会被保存到数据库中。

spring:
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://ip:port/process?useSSL=false&useUnicode=true&characterEncoding=utf-8
    username: username
    password: pw

3.2 画流程图

可以集成flowableUI画,也可以使用idea插件

本文使用Flowable BPMN visualizer插件画流程图

请添加图片描述

在processes目录下新建流程文件

请添加图片描述

本文新建的为ask_for_leave.bpmn20.xml .bpmn20.xml是固定后缀

在文件上右击,选择View BPMN(Flowable) Diagram就可以可视化的绘制流程图了

如果需要新添加一个流程图元素,则需要右键,根据需要选择即可

请添加图片描述

我画的流程图如下:

请添加图片描述

  1. 首先根据业务流程把各个流程画出并链接起来,开始事件–>员工的用户任务(员工发起请假流程)–>组长用户任务(组长审批是否同意)–>组长审批网关(如果不同意,则发送失败提示并结束流程,如果同意进入经理审批任务)–> 经理审批任务(经理审批是否同意)–> 组长审批网关(如果不同意,则发送失败提示并结束流程,如果同意结束流程)

  2. 员工用户任务配置:需要注意的点在于需要配置Assignee(办理人)属性,该属性一般为员工id
    请添加图片描述

  3. 组长审批 用户任务 和 经理审批用户任务的配置和员工的类似,办理人都需要指定为用户ID

  4. 组长审批网关和经理审批网关我们使用互斥网关,因为在审批的时候只能是同意或者不同意,其他没有特殊配置
    请添加图片描述

  5. 组长审批通过连线:需要配置条件表达式,用来判断什么情况下走这个连线,我们配置表达式为 ${var:equals(checkResult,“通过”)},这个地方的表达式是el表达式,会自动计算,checkResult是在组长审批的用户任务中,表单提交的数据
    请添加图片描述

  6. 发送失败提示 服务任务:服务任务会自动执行,所需要指定class 这个类需要自己实现接口
    请添加图片描述

自动生成的流程定义文件

<?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="ask_for_leave" name="ask_for_leave" isExecutable="true">
    <startEvent id="sid-3cb4e010-243b-401b-9ad3-4a1f8ecf35d7"/>
    <userTask id="sid-c60eebb9-3a5b-48f9-963c-d0b064344fad" name="请假" flowable:assignee="#{leaveTask}">
      <documentation>员工请假</documentation>
    </userTask>
    <sequenceFlow id="sid-ff0c2afe-1c1c-4e2f-b2eb-5083c5fadb41" sourceRef="sid-3cb4e010-243b-401b-9ad3-4a1f8ecf35d7" targetRef="sid-c60eebb9-3a5b-48f9-963c-d0b064344fad"/>
    <userTask id="sid-dafc35fa-fa0f-49af-9f46-7074fa5a60ab" name="组长审批" flowable:assignee="#{zuzhangTask}"/>
    <sequenceFlow id="sid-32a2a643-9ce6-44f3-bb94-f9df3d97c49b" sourceRef="sid-c60eebb9-3a5b-48f9-963c-d0b064344fad" targetRef="sid-dafc35fa-fa0f-49af-9f46-7074fa5a60ab"/>
    <exclusiveGateway id="sid-c03f8946-a6cd-4e38-881a-70d450e32748" name="组长审批网关"/>
    <sequenceFlow id="sid-bbed6467-cc35-41d2-ad08-2c3b17b8ca83" sourceRef="sid-dafc35fa-fa0f-49af-9f46-7074fa5a60ab" targetRef="sid-c03f8946-a6cd-4e38-881a-70d450e32748"/>
    <sequenceFlow id="sid-afe09a37-7634-4166-8617-68d5f35edef7" sourceRef="sid-c03f8946-a6cd-4e38-881a-70d450e32748" targetRef="sid-c9544983-0465-43e0-b6fc-1dbd0f1e0687" name="组长审批通过">
      <conditionExpression xsi:type="tFormalExpression">${var:equals(checkResult,"通过")}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-df2ad71d-6aa1-4d43-a9f9-bbdad8687e3a" sourceRef="sid-c03f8946-a6cd-4e38-881a-70d450e32748" targetRef="sid-e720a425-9f57-4a42-9833-b5f4f0175840" name="组长审批拒绝">
      <conditionExpression xsi:type="tFormalExpression">${var:equals(checkResult,"拒绝")}</conditionExpression>
    </sequenceFlow>
    <userTask id="sid-c9544983-0465-43e0-b6fc-1dbd0f1e0687" name="经理审批" flowable:assignee="#{manageTask}"/>
    <exclusiveGateway id="sid-970d87f2-84d0-4603-ae25-163a0ae7ca53" name="经理审批网关"/>
    <sequenceFlow id="sid-ffa6dd8b-ae8d-47a5-a2ff-45d25e58dcac" sourceRef="sid-c9544983-0465-43e0-b6fc-1dbd0f1e0687" targetRef="sid-970d87f2-84d0-4603-ae25-163a0ae7ca53"/>
    <serviceTask id="sid-e720a425-9f57-4a42-9833-b5f4f0175840" flowable:exclusive="true" name="发送失败提示" isForCompensation="true" flowable:class="com.xx.process.service.LeaveFailService"/>
    <endEvent id="sid-f158c31d-d760-4a06-bb4a-6ff8b156d2fb"/>
    <sequenceFlow id="sid-6e2fc1e3-43be-4768-96f5-d9c7861cbce7" sourceRef="sid-e720a425-9f57-4a42-9833-b5f4f0175840" targetRef="sid-f158c31d-d760-4a06-bb4a-6ff8b156d2fb"/>
    <sequenceFlow id="sid-c90c5e3e-e5c2-40f0-8324-44d548de0ef2" sourceRef="sid-970d87f2-84d0-4603-ae25-163a0ae7ca53" targetRef="sid-e720a425-9f57-4a42-9833-b5f4f0175840" name="经理审批拒绝">
      <conditionExpression xsi:type="tFormalExpression">${var:equals(checkResult,"拒绝")}</conditionExpression>
    </sequenceFlow>
    <endEvent id="sid-ea363fd0-f4ad-4537-9faf-a9349aac16f1"/>
    <sequenceFlow id="sid-5cad8e7b-8ee3-4e34-b94f-920a362f578d" sourceRef="sid-970d87f2-84d0-4603-ae25-163a0ae7ca53" targetRef="sid-ea363fd0-f4ad-4537-9faf-a9349aac16f1" name="经理审批通过">
      <conditionExpression xsi:type="tFormalExpression">${var:equals(checkResult,"通过")}</conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_ask_for_leave">
    <bpmndi:BPMNPlane bpmnElement="ask_for_leave" id="BPMNPlane_ask_for_leave">
      <bpmndi:BPMNShape id="shape-013aa23c-13e6-466e-942b-6a1a308eeda7" bpmnElement="sid-3cb4e010-243b-401b-9ad3-4a1f8ecf35d7">
        <omgdc:Bounds x="-2235.0" y="-1200.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-10dbd0c8-a3b1-4a9e-b214-5c506e80fd1d" bpmnElement="sid-c60eebb9-3a5b-48f9-963c-d0b064344fad">
        <omgdc:Bounds x="-2145.0" y="-1224.9999" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-97bf72f7-edcb-4813-9e03-73bc597d1492" bpmnElement="sid-ff0c2afe-1c1c-4e2f-b2eb-5083c5fadb41">
        <omgdi:waypoint x="-2205.0" y="-1185.0"/>
        <omgdi:waypoint x="-2145.0" y="-1184.9999"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-df2f6c22-1d44-4180-94fb-87753f871822" bpmnElement="sid-dafc35fa-fa0f-49af-9f46-7074fa5a60ab">
        <omgdc:Bounds x="-1945.0" y="-1225.0001" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-e0d1746e-e6b5-481a-a492-83881d2a9c22" bpmnElement="sid-32a2a643-9ce6-44f3-bb94-f9df3d97c49b">
        <omgdi:waypoint x="-2045.0" y="-1184.9999"/>
        <omgdi:waypoint x="-1945.0" y="-1185.0001"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-6e0e9d80-5884-4ca5-847d-d3e555005c51" bpmnElement="sid-c03f8946-a6cd-4e38-881a-70d450e32748">
        <omgdc:Bounds x="-1735.0" y="-1205.0" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-9dbfbe2b-c839-44eb-b74b-fceee48631b7" bpmnElement="sid-bbed6467-cc35-41d2-ad08-2c3b17b8ca83">
        <omgdi:waypoint x="-1845.0" y="-1185.0001"/>
        <omgdi:waypoint x="-1735.0" y="-1185.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-1ff96c95-d96d-4241-9d0f-85414bbb3859" bpmnElement="sid-c9544983-0465-43e0-b6fc-1dbd0f1e0687">
        <omgdc:Bounds x="-1570.0" y="-1224.9999" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-f8f6488f-354a-490c-9d22-1f57e9a654d4" bpmnElement="sid-afe09a37-7634-4166-8617-68d5f35edef7">
        <omgdi:waypoint x="-1695.0" y="-1185.0"/>
        <omgdi:waypoint x="-1632.5" y="-1185.0"/>
        <omgdi:waypoint x="-1570.0" y="-1184.9999"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-bb52e23c-ee53-4073-9b74-c5dca8459100" bpmnElement="sid-970d87f2-84d0-4603-ae25-163a0ae7ca53">
        <omgdc:Bounds x="-1395.0" y="-1205.0" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-6e5e32bc-5b44-41f5-97e6-458c1aa7b7f3" bpmnElement="sid-ffa6dd8b-ae8d-47a5-a2ff-45d25e58dcac">
        <omgdi:waypoint x="-1470.0" y="-1184.9999"/>
        <omgdi:waypoint x="-1395.0" y="-1185.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-26269b4b-a620-4e2f-a463-145fdfd8c0b1" bpmnElement="sid-e720a425-9f57-4a42-9833-b5f4f0175840">
        <omgdc:Bounds x="-1764.9999" y="-1055.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-47431d28-2f8b-4b2c-81e0-19dc832d1880" bpmnElement="sid-f158c31d-d760-4a06-bb4a-6ff8b156d2fb">
        <omgdc:Bounds x="-1910.0" y="-1030.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-cfc9460b-eaf6-44ee-9baa-300e89b18f4d" bpmnElement="sid-6e2fc1e3-43be-4768-96f5-d9c7861cbce7">
        <omgdi:waypoint x="-1764.9999" y="-1015.0"/>
        <omgdi:waypoint x="-1880.0" y="-1015.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-46d26639-0c9b-40da-b5ec-10678783920b" bpmnElement="sid-c90c5e3e-e5c2-40f0-8324-44d548de0ef2">
        <omgdi:waypoint x="-1375.0" y="-1165.0"/>
        <omgdi:waypoint x="-1375.0" y="-1015.0"/>
        <omgdi:waypoint x="-1664.9998" y="-1015.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-cfc8c0f0-f529-4023-83a6-e8635533be1b" bpmnElement="sid-ea363fd0-f4ad-4537-9faf-a9349aac16f1">
        <omgdc:Bounds x="-1200.0" y="-1200.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-5c22485e-bc92-4af7-b814-6f384c98ba5d" bpmnElement="sid-5cad8e7b-8ee3-4e34-b94f-920a362f578d">
        <omgdi:waypoint x="-1355.0" y="-1185.0"/>
        <omgdi:waypoint x="-1200.0" y="-1185.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-eb5ecc69-91e8-4e50-81e2-14123d77ee2d" bpmnElement="sid-df2ad71d-6aa1-4d43-a9f9-bbdad8687e3a">
        <omgdi:waypoint x="-1715.0" y="-1165.0"/>
        <omgdi:waypoint x="-1714.9999" y="-1055.0"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

3.3 服务任务实现类

@Slf4j
@Component
public class LeaveFailService implements JavaDelegate {
    @Override
    public void execute(DelegateExecution delegateExecution) {
        log.info("审批不通过{}", delegateExecution.getCurrentActivityId());
    }
}

需要实现 JavaDelegate接口;在执行到服务任务的时候,就会自动调用这个方法,在这个方法中我们可以做一些自定义的业务逻辑

3.4 流程图查看接口

为了方便查看流程执行到那一步了,故增加该接口查看

开启流程的时候会生成一个流程id,使用该流程ID可查询流程进行到那一步了

/**
 * LeaveController
 *
 * @Description:
 * @Author: wangyubiao
 * 标签属性 <a href="https://blog.csdn.net/zhongzk69/article/details/91489038">...</a>
 * 网关:<a href="https://blog.csdn.net/BASK2311/article/details/128073967">...</a>
 * @Date 2023/5/12 13:26
 * @since 1.0.0
 */
@RestController
public class LeaveController {

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    ProcessEngine processEngine;

    @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();
            }
        }
    }

}

查询结果:

请添加图片描述

3.5 单元测试

import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;

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

@SpringBootTest
@Slf4j
@ActiveProfiles("dev")
class ProcessApplicationTests {

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    TaskService taskService;

    // 员工id
    public static final String yuangongId = "yuangongID_3";


    // 员工id
    public static final String zuzhangId = "zuzhangId_3";

    // 员工id
    public static final String manageId = "manageId_3";

    @Test
    void contextLoads() {
    }


    /**
     * 开启一个请假流程
     */
    @Test
    void askForLeave() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("leaveTask", yuangongId);
        // 开启流程的key,就是流程定义文件里 process 标签的id
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("ask_for_leave", map);
        // 设置一些参数
        runtimeService.setVariable(processInstance.getId(), "name", "javaboy");
        runtimeService.setVariable(processInstance.getId(), "reason", "休息一下");
        runtimeService.setVariable(processInstance.getId(), "days", 10);
        log.info("创建请假流程 processId:{}", processInstance.getId());

    }

    /**
     * 员工提交请假
     */
    @Test
    void submitToZuZhang() {
        // 员工查找到自己的任务,然后提交给组长审批
        List<Task> list = taskService.createTaskQuery().taskAssignee(yuangongId).orderByTaskId().desc().list();
        for (Task task: list) {
            log.info("任务 ID:{};任务处理人:{};任务是否挂起:{}", task.getId(), task.getAssignee(), task.isSuspended());
            Map<String, Object> map = new HashMap<>();
            //提交给组长的时候,需要指定组长的 id
            map.put("zuzhangTask", zuzhangId);
            taskService.complete(task.getId(), map);
        }

    }

    /**
     * 组长批准请假
     */
    @Test
    void zuZhangApprove() {
        List<Task> list = taskService.createTaskQuery().taskAssignee(zuzhangId).orderByTaskId().desc().list();
        for (Task task: list) {
            if ("组长审批".equals(task.getName())) {
                log.info("任务 ID:{};任务处理人:{};任务是否挂起:{}", task.getId(), task.getAssignee(), task.isSuspended());
                Map<String, Object> map = new HashMap<>();
                //提交给组长的时候,需要指定组长的 id
                map.put("manageTask", manageId);
                map.put("checkResult", "通过");
                map.put("zuzhangTask", zuzhangId);

                try {
                    taskService.complete(task.getId(), map);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("组长审批失败{} {}", task.getId(), task.getAssignee());
                }

            }
        }

    }

    /**
     * 经理审批通过
     */
    @Test
    void managerApprove() {
        List<Task> list = taskService.createTaskQuery().taskAssignee(manageId).orderByTaskId().desc().list();
        for (Task task: list) {
            log.info("经理 {} 在审批 {} 任务", task.getAssignee(), task.getId());
            Map<String, Object> map = new HashMap<>();
            map.put("checkResult", "通过");
            taskService.complete(task.getId(), map);
        }
    }

    /**
     * 经理审批不通过
     */
    @Test
    void managerNotApprove() {
        List<Task> list = taskService.createTaskQuery().taskAssignee(manageId).orderByTaskId().desc().list();
        for (Task task: list) {
            log.info("经理 {} 在审批 {} 任务", task.getAssignee(), task.getId());
            Map<String, Object> map = new HashMap<>();
            map.put("checkResult", "拒绝");
            taskService.complete(task.getId(), map);
        }
    }


}

四、遇到的一些问题

4.1 生成的流程图文字显示为”口口口“

这是因为本地没有默认的字体,安装字体或者修改配置解决

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

/**
 * FlowableConfig
 *
 * @Description:
 * @Author: wangyubiao
 * @Date 2023/5/15 15:16
 * @since 1.0.0
 */
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {

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

4.2 一个流程已经开始了,修改了流程定义文件,已经开始的流程并没有更新

流程开始后,流程配置信息已经持久化了,修改流程定义文件,只会影响还没开始的流程,不会对已经开始的流程造成影响

五、附录

  1. flowable BPMN 用户手册: https://tkjohn.github.io/flowable-userguide/#chDeployment

  2. 工作流概念入门:https://mp.weixin.qq.com/s?__biz=MzI1NDY0MTkzNQ==&mid=2247501196&idx=1&sn=ab8f8a50002033e6033ab86e7e92b580&scene=21#wechat_redirect

  3. SpringBoot集成Flowable:https://blog.csdn.net/pengming97/article/details/127027694

  4. Flowable 79张表都是干嘛的?:https://wangsong.blog.csdn.net/article/details/126755364

  • 40
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Flowable是一个开源的Java工作流引擎,用于处理和管理业务流程。它提供了一套强大且灵活的工具和API,使开发人员能够定义、执行和监控复杂的业务流程。 使用Flowable可以实现以下功能: 1. 定义流程模型:使用Flowable的图形建模器,可以以可视化方式设计流程模型,包括用户任务、服务任务、网关和事件等。 2. 执行流程:Flowable引擎可以执行已定义的流程模型,并自动处理流程中的各个任务节点。 3. 任务管理:Flowable提供了对任务的管理功能,包括任务的分配、处理、委派和审批等。 4. 监控和追踪:Flowable支持对流程实例和任务进行监控和跟踪,可以实时查看流程的执行状态和各个任务的进展情况。 5. 异步执行:Flowable引擎支持异步执行任务,可以在流程执行过程中处理大量的并发任务。 要开始使用Flowable,你可以按照以下步骤进行: 1. 引入Flowable依赖:在你的Java项目中添加Flowable的依赖,可以通过Maven或Gradle进行添加。 2. 定义流程模型:使用Flowable提供的建模器,以图形化方式设计你的流程模型,并保存为BPMN文件。 3. 配置Flowable引擎:在你的项目中配置Flowable引擎,包括数据库连接、任务分配策略等。 4. 执行流程:使用Flowable的API,加载并执行你的流程模型,处理各个任务节点。 5. 监控和追踪:使用Flowable提供的监控功能,查看流程实例和任务的执行状态。 Flowable提供了丰富的文档和示例代码,你可以通过访问Flowable官方网站或GitHub仓库来获取更多的学习资源和帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值