工作流引擎 SpringBoot+flowable完美结合,快速实现工作流,so easy

flowable 起源

在2016.7~2017.5期间activiti团队内部已经产生了重大的分歧。关于新的activiti新团队与原有的团队重要开发人员我们罗列一下,细节如下:

img

上图是Tijs Rademakers,算是activiti5以及6比较核心的leader了。现在是flowable框架的leader。

img

Joram Barrez 算是activiti5以及6比较核心的leader了。目前从事flowable框架开发。

img

Salaboy Activiti Cloud BPM leader(Activiti Cloud BPM 也就是目前的activiti7框架)

Tijs Rademakers以及Salaboy目前是两个框架的leader。

特此强调一点:activiti5以及activiti6、flowable是Tijs Rademakers团队开发的。

Activiti7是 Salaboy团队开发的。activiti6以及activiti5代码目前有 Salaboy团队进行维护。因为Tijs Rademakers团队去开发flowable框架了,所以activiti6以及activiti5代码已经交接给了 Salaboy团队(可以理解为离职之前工作交接)。目前的activiti5以及activiti6代码还是原Tijs Rademakers原有团队开发的。Salaboy团队目前在开发activiti7框架。对于activiti6以及activiti5的代码官方已经宣称暂停维护了。activiti7就是噱头 内核使用的还是activiti6。并没有为引擎注入更多的新特性,只是在activiti之外的上层封装了一些应用。

Flowable 和 activiti6 对比

目前Flowable已经修复了activiti6很多的bug,可以实现零成本从activiti迁移到flowable。

flowable目前已经支持加签、动态增加实例中的节点、支持cmmn、dmn规范。这些都是activiti6目前版本没有的。

1、flowable已经支持所有的历史数据使用mongdb存储,activiti没有。

2、flowable支持事务子流程,activiti没有。

3、flowable支持多实例加签、减签,activiti没有。

4、flowable支持httpTask等新的类型节点,activiti没有。

5、flowable支持在流程中动态添加任务节点,activiti没有。

6、flowable支持历史任务数据通过消息中间件发送,activiti没有。

7、flowable支持java11,activiti没有。

8、flowable支持动态脚本,,activiti没有。

9、flowable支持条件表达式中自定义juel函数,activiti没有。

10、flowable支持cmmn规范,activiti没有。

11、flowable修复了dmn规范设计器,activit用的dmn设计器还是旧的框架,bug太多。

12、flowable屏蔽了pvm,activiti6也屏蔽了pvm(因为6版本官方提供了加签功能,发现pvm设计的过于臃肿,索性直接移除,这样加签实现起来更简洁、事实确实如此,如果需要获取节点、连线等信息可以使用bpmnmodel替代)。

13、flowable与activiti提供了新的事务监听器。activiti5版本只有事件监听器、任务监听器、执行监听器。

14、flowable对activiti的代码大量的进行了重构。

15、activiti以及flowable支持的数据库有h2、hsql、mysql、oracle、postgres、mssql、db2。其他数据库不支持的。使用国产数据库的可能有点失望了,需要修改源码了。

16、flowable支持jms、rabbitmq、mongodb方式处理历史数据,activiti没有。

几个框架前景对比
1、2019年6月中旬 salboy已从alfresco公司离职。activiti7/8开发动向不明确。

2、flowable以6.4.1版本为分水岭,大力发展其商业版产品。开源版本维护不及时。部分功能已经不再开源版发布,比如表单生成器(表单引擎)、历史数据同步至其他数据源、es等等。dmn目前是个半成品,没有camunda稳定和好用,对于dmn规范支持薄弱。部分商业版的组件被商业化,因此开源版不再维护。Mongdb目前也放到商业产品中了,开源版的几乎不能用。

Flowable 使用

Flowable 提供了一个 Web UI 应用程序来演示和利用 Flowable 项目提供的功能。此应用程序包含四个主要应用程序组件:

  • Flowable IDM:一个身份管理应用程序,为所有 Flowable UI 应用程序提供单点登录身份验证功能,并且对于具有 IDM 管理权限的用户,它还提供管理用户、组和权限的功能。
  • Flowable Modeler:允许具有建模者权限的用户对流程、表单、决策表和应用程序定义进行建模的应用程序。
  • Flowable Task:一个运行时任务应用程序,提供启动流程实例、编辑任务表单、完成任务以及查询任务和流程实例的功能。
  • Flowable Admin:一个管理应用程序,允许具有管理员权限的用户查询 BPMN、DMN、表单和内容引擎,并提供多个选项来更改流程实例、任务、作业等。管理应用程序通过与 Flowable UI 应用程序和 Flowable REST 应用程序一起部署的 REST API 连接到引擎。

Web UI 应用程序以单个 WAR 文件的形式提供,该文件可以放在任何 Web 服务器中或使用其嵌入式服务器启动。

Flowable UI 应用程序安装

UI 应用程序可以部署在 Tomcat 服务器上,并且在开始使用附加配置设置时,这可能是最简单的方法。对于本安装指南,我们将描述应用程序在 Tomcat 服务器中的安装。

  1. 下载Apache Tomcat的最新稳定版本。
  2. 下载最新的稳定Flowable 6 版本
  3. 将 flowable-ui.war 文件从 Flowable 发行版wars文件夹复制到 Tomcat webapps 文件夹。
  4. 通过运行 bin/startup.sh(Mac OS 和 Linux)或 bin/startup.bat (Windows) 脚本来启动 Tomcat 服务器。
  5. 打开网络浏览器并转到 http://localhost:8080/flowable-ui。

Flowable UI 应用程序现在应该与 H2 内存数据库一起运行,并且您的 Web 浏览器中应该显示以下登录屏幕:

image-20220112100610569

默认情况下,Flowable IDM 组件将创建一个拥有所有 Flowable UI 应用程序权限的管理员用户。您可以使用 admin/test 登录,浏览器应该会转到 Flowable 登录页面:

image-20220112101907634

通常,您需要将默认的 H2 内存数据库配置更改为 MySQL 或 Postgres(或其他持久性数据库)配置。您可以通过更改应用程序的*WEB-INF/classes/*目录中的 application.properties文件来做到这一点 。可以在Github上找到示例配置 要将默认配置更改为 MySQL,需要对属性文件进行以下更改:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/flowable?characterEncoding=UTF-8
spring.datasource.username=flowable
spring.datasource.password=flowable

注意:需要将java驱动jar(mysql-connector-java-5.1.45.jar)复制到 apache-tomcat-9.0.37\webapps\flowable-rest\WEB-INF\lib

当然你也可以直接使用docker,来预览一下 效果

docker run -p8080:8080 flowable/flowable-ui

绘制流程图

最终效果图预览

图片

根据业务需要在 flowable-ui>APP.MODELER里面绘制流程图,示例如上图。先解释一些概念。

  • 事件(event) 通常用于为流程生命周期中发生的事情建模,图里是【开始、结束】两个圈。
  • 顺序流(sequence flow) 是流程中两个元素间的连接器。图里是【箭头线段】。
  • 网关(gateway) 用于控制执行的流向。图里是【菱形(中间有X)】
  • 用户任务(user task) 用于对需要人工执行的任务进行建模。图里是【矩形】。

简单的工作流大概就这些元素(还有很多这里就不扩展了)。下面描述一下工作流是如何流动的。

首先启动了工作流后,由【开始】节点自动流向【学生】节点,等待该任务执行。任务被分配的学生用户执行后流向 【老师】节点,再次等待该任务执行。被分配的老师用户执行后流向 【网关】,网关以此检查每个出口,流向符合条件的任务,比如这里老师执行任务时是同意,就流向【校长】节点,等待该任务执行。执行后跟老师类似,同意后就流向【结束】节点,整个流程到此结束。

绘图步骤:

  1. 进入 flowable-ui>APP.MODELER 点击 创建流程

image-20220112103709179

image-20220112103126955

  1. 点击保存,将显示 BPMN 建模画布的可视化编辑器。Flowable 引擎支持的所有 BPMN 元素都可用于流程模型的设计。

    image-20220112104602179

BPMN 可视化编辑器分为 4 个部分:

  • Palette(调色板):可用于设计流程模型的 BPMN 元素的调色板
  • Toolbar(工具栏):更改模型画布的操作,例如缩放、布局和保存模型
  • Modeling cancas( 模型画布):用于拖放 BPMN 元素和设计流程模型的建模画布
  • Properties panel(属性面板):如果没有选择任何元素,则为主流程模型的属性,否则为所选 BPMN 元素的属性

3.BPMN 可视化编辑器使用非常方便直接将 Palette(调色板)内模型拖入,并关联即可。

4.顺序流可以设置流条件来限制流动,比如上面的网关出口就设置了条件

图片

5、任务需要分配任务的执行用户,可以分配到候选组,也可以直接分配到候选人

图片

最后导出工作流文件

image-20220112131938347

文件内容

<?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-insmtece" 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" name="请假审批" isExecutable="true">
    <startEvent id="start" name="开始" flowable:initiator="startuser" flowable:formFieldValidation="true"></startEvent>
    <userTask id="stu_task" name="学生" flowable:candidateGroups="stu_group" flowable:formFieldValidation="true"></userTask>
    <sequenceFlow id="flow1" sourceRef="start" targetRef="stu_task"></sequenceFlow>
    <userTask id="te_task" name="老师" flowable:candidateGroups="te_group" flowable:formFieldValidation="true"></userTask>
    <exclusiveGateway id="getway1" name="网关1"></exclusiveGateway>
    <userTask id="mte_task" name="校长" flowable:candidateGroups="mte_group" flowable:formFieldValidation="true"></userTask>
    <exclusiveGateway id="getway2" name="网关2"></exclusiveGateway>
    <endEvent id="end" name="结束"></endEvent>
    <sequenceFlow id="flow1" name="请假" sourceRef="stu_task" targetRef="te_task" skipExpression="${command=='agree'}"></sequenceFlow>
    <sequenceFlow id="flow3_1" name="同意" sourceRef="getway1" targetRef="mte_task">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${command=='agree'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow2" name="审批" sourceRef="te_task" targetRef="getway1"></sequenceFlow>
    <sequenceFlow id="flow3_2" name="拒绝" sourceRef="getway1" targetRef="stu_task">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${command=='refuse'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow4" name="审批" sourceRef="mte_task" targetRef="getway2"></sequenceFlow>
    <sequenceFlow id="flow4_1" name="同意" sourceRef="getway2" targetRef="end" skipExpression="${command=='free'}">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${command=='agree'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow4_2" name="拒绝" sourceRef="getway2" targetRef="stu_task">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${command=='refuse'}]]></conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_leave_approval">
    这里先省略
  </bpmndi:BPMNDiagram>
</definitions>

4、bpmn文件导入

如果需要,可以把这个流程文件下载下来,直接导入使用

image-20220112132008630

三、后台项目搭建

后台项目基于jdk8,使用springboot框架

spring 版本

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

项目依赖pom.xml

<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.6.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.45</version>
</dependency>

项目配置application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/flowable?useSSL=false&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456

四、数据库

1、Flowable的所有数据库表都以ACT_开头。第二部分是说明表用途的两字符标示符。服务API的命名也大略符合这个规则。

2、ACT_RE_: 'RE’代表repository。带有这个前缀的表包含“静态”信息,例如流程定义与流程资源(图片、规则等)。

3、ACT_RU_: 'RU’代表runtime。这些表存储运行时信息,例如流程实例(process instance)、用户任务(user task)、变量(variable)、作业(job)等。Flowable只在流程实例运行中保存运行时数据,并在流程实例结束时删除记录。这样保证运行时表小和快。

4、ACT_HI_: 'HI’代表history。这些表存储历史数据,例如已完成的流程实例、变量、任务等。

5、ACT_GE_: 通用数据。在多处使用。

1)通用数据表(2个)
  • act_ge_bytearray:二进制数据表,如流程定义、流程模板、流程图的字节流文件;
  • act_ge_property:属性数据表(不常用);
2)历史表(8个,HistoryService接口操作的表)
  • act_hi_actinst:历史节点表,存放流程实例运转的各个节点信息(包含开始、结束等非任务节点);
  • act_hi_attachment:历史附件表,存放历史节点上传的附件信息(不常用);
  • act_hi_comment:历史意见表;
  • act_hi_detail:历史详情表,存储节点运转的一些信息(不常用);
  • act_hi_identitylink:历史流程人员表,存储流程各节点候选、办理人员信息,常用于查询某人或部门的已办任务;
  • act_hi_procinst:历史流程实例表,存储流程实例历史数据(包含正在运行的流程实例);
  • act_hi_taskinst:历史流程任务表,存储历史任务节点;
  • act_hi_varinst:流程历史变量表,存储流程历史节点的变量信息;
3)用户相关表(4个,IdentityService接口操作的表)
  • act_id_group:用户组信息表,对应节点选定候选组信息;
  • act_id_info:用户扩展信息表,存储用户扩展信息;
  • act_id_membership:用户与用户组关系表;
  • act_id_user:用户信息表,对应节点选定办理人或候选人信息;
4)流程定义、流程模板相关表(3个,RepositoryService接口操作的表)
  • act_re_deployment:部属信息表,存储流程定义、模板部署信息;
  • act_re_procdef:流程定义信息表,存储流程定义相关描述信息,但其真正内容存储在act_ge_bytearray表中,以字节形式存储;
  • act_re_model:流程模板信息表,存储流程模板相关描述信息,但其真正内容存储在act_ge_bytearray表中,以字节形式存储;

5)流程运行时表(6个,RuntimeService接口操作的表)

  • act_ru_task:运行时流程任务节点表,存储运行中流程的任务节点信息,重要,常用于查询人员或部门的待办任务时使用;
  • act_ru_event_subscr:监听信息表,不常用;
  • act_ru_execution:运行时流程执行实例表,记录运行中流程运行的各个分支信息(当没有子流程时,其数据与act_ru_task表数据是一一对应的);
  • act_ru_identitylink:运行时流程人员表,重要,常用于查询人员或部门的待办任务时使用;
  • act_ru_job:运行时定时任务数据表,存储流程的定时任务信息;
  • act_ru_variable:运行时流程变量数据表,存储运行中的流程各节点的变量信息;

五、流程引擎API与服务

引擎API是与Flowable交互的最常用手段。总入口点是ProcessEngine。

image-20220112132045307

1、RepositoryService很可能是使用Flowable引擎要用的第一个服务。这个服务提供了管理与控制部署(deployments)与流程定义(process definitions)的操作。管理静态信息,

2、RuntimeService用于启动流程定义的新流程实例。

3、IdentityService很简单。它用于管理(创建,更新,删除,查询……)组与用户。

4、FormService是可选服务。也就是说Flowable没有它也能很好地运行,而不必牺牲任何功能。

5、HistoryService暴露Flowable引擎收集的所有历史数据。要提供查询历史数据的能力。

6、ManagementService通常在用Flowable编写用户应用时不需要使用。它可以读取数据库表与表原始数据的信息,也提供了对作业(job)的查询与管理操作。

7、DynamicBpmnService可用于修改流程定义中的部分内容,而不需要重新部署它。例如可以修改流程定义中一个用户任务的办理人设置,或者修改一个服务任务中的类名。

接下来使用之前的请假流程图,上代码

代码
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.idm.api.Group;
import org.flowable.idm.api.User;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * TestFlowable
 *
 * @Author 
 * @Date: 2021/10/17 23:35
 * @Version 1.0
 */
@Slf4j
public class TestFlowable {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private org.flowable.engine.TaskService taskService;

    @Autowired
    private org.flowable.engine.IdentityService identityService;

    public void createDeploymentZip() {

        /*
         * @Date: 2021/10/17 23:38
         * Step 1: 部署xml(压缩到zip形式,直接xml需要配置相对路径,麻烦,暂不用)
         */
        try {
            File zipTemp = new File("f:/leave_approval.bpmn20.zip");
            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipTemp));
            Deployment deployment = repositoryService
                    .createDeployment()
                    .addZipInputStream(zipInputStream)
                    .deploy();
            log.info("部署成功:{}", deployment.getId());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        /*
         * @Date: 2021/10/17 23:40
         * Step 2: 查询部署的流程定义
         */
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave_approval").list();
        List<ProcessDefinition> pages = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave_approval").listPage(1, 30);

        /*
         * @Date: 2021/10/17 23:40
         * Step 3: 启动流程,创建实例
         */
        String processDefinitionKey = "leave_approval";//流程定义的key,对应请假的流程图
        String businessKey = "schoolleave";//业务代码,根据自己的业务用
        Map<String, Object> variablesDefinition = new HashMap<>();//流程变量,可以自定义扩充
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variablesDefinition);
        log.info("启动成功:{}", processInstance.getId());

        /*
         * @Date: 2021/10/17 23:40
         * Step 4: 查询指定流程所有启动的实例列表
         * 列表,或 分页 删除
         */
        List<Execution> executions = runtimeService.createExecutionQuery().processDefinitionKey("leave_approval").list();
        List<Execution> executionPages = runtimeService.createExecutionQuery().processDefinitionKey("leave_approval").listPage(1, 30);
//        runtimeService.deleteProcessInstance(processInstanceId, deleteReason); //删除实例

        /*
         * @Date: 2021/10/17 23:40
         * Step 5: 学生查询可以操作的任务,并完成任务
         */
        String candidateGroup = "stu_group"; //候选组 xml文件里面的 flowable:candidateGroups="stu_group"
        List<Task> taskList = taskService.createTaskQuery().taskCandidateGroup(candidateGroup).orderByTaskCreateTime().desc().list();
        for (Task task : taskList) {
            // 申领任务
            taskService.claim(task.getId(), "my");
            // 完成
            taskService.complete(task.getId());
        }

        /*
         * @Date: 2021/10/17 23:40
         * Step 6: 老师查询可以操作的任务,并完成任务
         */
        String candidateGroupTe = "te_group"; //候选组 xml文件里面的 flowable:candidateGroups="te_group"
        List<Task> taskListTe = taskService.createTaskQuery().taskCandidateGroup(candidateGroupTe).orderByTaskCreateTime().desc().list();
        for (Task task : taskListTe) {
            // 申领任务
            taskService.claim(task.getId(), "myte");
            // 完成
            Map<String, Object> variables = new HashMap<>();
            variables.put("command","agree"); //携带变量,用于网关流程的条件判定,这里的条件是同意
            taskService.complete(task.getId(), variables);
        }

        /*
         * @Date: 2021/10/18 0:17
         * Step 7: 历史查询,因为一旦流程执行完毕,活动的数据都会被清空,上面查询的接口都查不到数据,但是提供历史查询接口
         */
        // 历史流程实例
        List<HistoricProcessInstance> historicProcessList = historyService.createHistoricProcessInstanceQuery().processDefinitionKey("leave_approval").list();
        // 历史任务
        List<HistoricTaskInstance> historicTaskList = historyService.createHistoricTaskInstanceQuery().processDefinitionKey("leave_approval").list();
        // 实例历史变量 , 任务历史变量
        // historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId);
        // historyService.createHistoricVariableInstanceQuery().taskId(taskId);

        // *****************************************************分隔符********************************************************************
        // *****************************************************分隔符********************************************************************
        // 可能还需要的API
        // 移动任务,人为跳转任务
        // runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
        //       .moveActivityIdTo(currentActivityTaskId, newActivityTaskId).changeState();

        // 如果在数据库配置了分组和用户,还会用到
        List<User> users = identityService.createUserQuery().list();    //用户查询,用户id对应xml 里面配置的用户
        List<Group> groups = identityService.createGroupQuery().list(); //分组查询,分组id对应xml 里面配置的分组 如 stu_group,te_group 在表里是id的值

        // 另外,每个查询后面都可以拼条件,内置恁多查询,包括模糊查询,大小比较都有
    }
}

五、参考资料

https://www.flowable.com/open-source/docs/bpmn/ch14-Applications/#flowable-modeler-app
分享牛Flowable文档汉化:https://github.com/qiudaoke/flowable-userguide
https://www.cnblogs.com/xianz666/category/1787602.html
官网地址:https://www.flowable.org/
Flowable6.3中文教程:https://tkjohn.github.io/flowable-userguide/#_introduction

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值