工作流Flowable入门教程:flowableUI的安装使用,RepositoryService、RuntimeService、TaskService、HistoryService的使用

Flowable入门教程

  • Flowable是什么
  • 下载使用flowableUI
    • 百度网盘下载
    • 启动flowableUI
    • flowableUI使用(只操作创建bpmn20.xml文件)
  • Flowable项目使用
    • 创建工程
    • 引入依赖
    • 如果控制台没有详细的日志的话,可以使用log4j作为slf4j的实现
    • RepositoryService、RuntimeService、TaskService、HistoryService的使用
      • 创建流程引擎
      • 部署流程定义
      • 启动流程实例
      • 查询任务
      • 完成任务
      • 历史任务
      • 删除任务
      • 删除流程
      • 流程挂起与激活

Flowable是什么

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

  • Flowable可以十分灵活地加入你的应用/服务/构架。可以将JAR形式发布的Flowable库加入应用或服务,来嵌入引擎。 以JAR形式发布使Flowable可以轻易加入任何Java环境:Java SE;Tomcat、Jetty或Spring之类的servlet容器;JBoss或WebSphere之类的Java EE服务器,等等。 另外,也可以使用Flowable REST API进行HTTP调用。也有许多Flowable应用(Flowable Modeler, Flowable Admin, Flowable IDM 与 Flowable Task),提供了直接可用的UI示例,可以使用流程与任务。

  • 所有使用Flowable方法的共同点是核心引擎。核心引擎是一组服务的集合,并提供管理与执行业务流程的API。 下面的教程从设置与使用核心引擎的介绍开始。后续章节都建立在之前章节中获取的知识之上。

  • 官网使用手册

    链接: https://tkjohn.github.io/flowable-userguide/#_getting_started_2

下载使用flowableUI

目的:在flowableUI中画bpmn20图,生成bpmn20.xml文件

百度网盘下载

链接:https://pan.baidu.com/s/1EKOSIl9ZVUpF0VGmte0utw?pwd=wd0s

提取码:wd0s

启动flowableUI

解压开后复制flowable-6.7.2\wars目录下的两个war包

在这里插入图片描述

将war包放入tomcat的webapps目录下

在这里插入图片描述

启动tomcat:在bin目录下点击startup.bat

在这里插入图片描述

flowableUI使用(只操作创建bpmn20.xml文件)

  • 进入建模器应用程序—>创建流程

    在这里插入图片描述

  • 创建流程

    在这里插入图片描述

  • 分配用户

    在这里插入图片描述

  • 点击固定值–>分配填入用户–>例如张三–>另一个usertask以此类推

    在这里插入图片描述

    在这里插入图片描述

  • 保存

    在这里插入图片描述

  • 下载bpmn20.xml,点击显示详情信息–>导出bomn2–>结束

    在这里插入图片描述

    在这里插入图片描述

Flowable项目使用

创建工程

通过idea创建或者eclipse创建一个maven工程

引入依赖

  • 需要添加两个依赖:

    Flowable流程引擎。使我们可以创建一个ProcessEngine流程引擎对象,并访问Flowable API。

    数据库驱动依赖。生成flowable流程表

		            org.flowable            flowable-engine            6.3.0                            mysql            mysql-connector-java            8.0.21        

引入测试包

		
            junit
            junit
            4.13.2
        

如果控制台没有详细的日志的话,可以使用log4j作为slf4j的实现

  • 在pom添加依赖:
		
            org.slf4j
            slf4j-api
            1.7.21
        
        
            org.slf4j
            slf4j-log4j12
            1.7.21
        
  • 在src/main/resources文件夹下添加log4j.properties文件,并写入下列内容:
log4j.rootLogger=DEBUG, CA

log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n

RepositoryService、RuntimeService、TaskService、HistoryService的使用

创建流程引擎

  • 首先要做的就是初始化ProcessEngine流程引擎实例。这是一个线程安全的对象,因此通常只需要在一个应用中初始化一次。ProcessEngine由ProcessEngineConfiguration实例创建。该实例可以配置与调整流程引擎的设置。通常使用一个配置xml文件创建ProcessEngineConfiguration,也可以通过编程方式创建它。
  • 新建Test方法:
	/**
     * 获取流程引擎对象
     */
    @Test
    public void testProcessEngine() {
        //获取ProcessEngineConfiguration对象
        ProcessEngineConfiguration processEngineConfiguration = new StandaloneProcessEngineConfiguration();
        //配置相关的数据库信息
        processEngineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC")
                .setJdbcUsername("root")
                .setJdbcPassword("123456")
                .setJdbcDriver("com.mysql.cj.jdbc.Driver");
        //如果数据库中表结构不存在就新建
        processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        //通过ProcessEngineConfiguration 构建我们需要的ProcessEngine对象
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
    }
  • 执行该程序,控制台不出错的话,就是操作完成了。查看下数据库,生成了34张act表。

    在这里插入图片描述

部署流程定义

  • 我们要构建的流程是一个非常简单的请假流程。Flowable引擎需要流程定义为BPMN 2.0格式,这是一个业界广泛接受的XML标准。 在Flowable术语中,我们将其称为一个流程定义(process definition)。一个流程定义可以启动多个流程实例(process instance)。流程定义可以看做是重复执行流程的蓝图。 在这个例子中,流程定义定义了请假的各个步骤,而一个流程实例对应某个雇员提出的一个请假申请。
  • BPMN 2.0存储为XML,并包含可视化的部分:使用标准方式定义了每个步骤类型(人工任务,自动服务调用,等等)如何呈现,以及如何互相连接。这样BPMN 2.0标准使技术人员与业务人员能用双方都能理解的方式交流业务流程。
  • 我们要使用的流程定义为:

    来自官网文档图

    在这里插入图片描述

  • 简单的说明一下这个流程:
    • 假定启动流程需要提供一些信息,例如雇员名字、请假时长以及说明。当然,这些可以单独建模为流程中的第一步。但是如果将他们作为流程的”输入信息”,就能保证只有在实际请求时才会建立一个流程实例。否则将提交作为流程的第一步,用户可能在提交之前改变主意并取消,但流程实例已经创建了。在某些场景中,就可能影响重要的指标(例如提交了多少申请,但未完成),取决于业务目标。
    • 图中左侧的圆圈叫做启动事件(start event)。这是一个流程实例的起点。

      第一个矩形是一个用户任务(user task)。这是流程中用户操作的步骤。在这里例子中,后续经理需要批准或者驳回用户的申请。

    • 带叉的菱形是一个排他网关(exclusive gateway)。后面还有其他的网关介绍。这里的排他网关取决于经理的操作,然后将流程实例转至批准或者驳回路径。
    • 如果批准,则通过另一个用户任务(user task),将经理的决定通知给申请人。当然也可以是别的通知方式(发邮件等)。
    • 如果驳回,可以直接发邮件通知申请人。
  • 这样的流程定义使用可视化建模工具建立,如Flowable Designer(Eclipse)或Web应用(flowableUI)。
  • 与上面展示的流程图对应的BPMN 2.0 XML在下面显示。请注意这只包含了“流程部分”。如果使用图形化建模工具,实际的XML文件还将包含“可视化部分”,用于描述图形信息,如流程定义中各个元素的坐标(所有的图形化信息包含在XML的BPMNDiagram标签中,作为definitions标签的子元素)。
  • 在src/main/resources文件夹下创建名为holiday-request.bpmn20.xml的文件,这个名字随意,这里当前需要设计的流程定义的,请假流程申请
                                                                                        <![CDATA[          ${approved}        ]]>                                                        <![CDATA[          ${!approved}        ]]>                                                                                        
  • xml文件介绍

    • 每一个步骤(在BPMN 2.0术语中称作活动(activity))都有一个id属性,为其提供一个在XML文件中唯一的标识符。所有的活动都可以设置一个名字,以提高流程图的可读性。
    • 活动之间通过**顺序流(sequence flow)**连接,在流程图中是一个有向箭头。在执行流程实例时,执行(execution)会从启动事件沿着顺序流流向下一个活动。
    • 离开排他网关(带有X的菱形)的顺序流很特别:都以表达式(execution)的形式定义了条件(condition)。当流程实例的执行到达这个网关时,会计算条件,并使用第一个计算为true的顺序流。这就是排他的含义:只选择一个。当然如果有需要不同的策略,也可以使用其他类型的网格。
    • 这里用作条件的表达式为a p p r o v e d ∗ ,这是 ∗ {approved},这是*approved∗,这是∗{approved == true}*的简写。变量approved被称作流程变量(process variable)。流程变量是持久化的数据,与流程实例存储在一起并可以在流程实例的生命周期中使用。在这个例子中,我们需要在特定的地方(当经理用户任务提交时,或者以Flowable的术语来说,完成(complete)时)设置这个流程变量,因为这不是流程实例启动时就能获取的数据。
  • 现在我们已经有了流程BPMN 2.0 XML文件,下来需要将它***部署(deploy)***到引擎中。部署一个流程定义意味着:

    • 流程引擎会将XML文件存储在数据库中,这样可以在需要的时候获取它。
    • 流程定义转换为内部的、可执行的对象模型,这样使用它就可以启动流程实例。
  • 将流程定义部署至Flowable引擎,需要使用*RepositoryService,其可以从ProcessEngine对象获取。使用RepositoryService,可以通过XML文件的路径创建一个新的部署(Deployment),并调用deploy()*方法实际执行:

    • 测试类中添加测试部署方式:
	private ProcessEngineConfiguration processEngineConfiguration;

    /**
     * 初始化封装下,不然每个测试方法都要写,很麻烦
     */
    @Before
    public void init() {
        //获取ProcessEngineConfiguration对象
        processEngineConfiguration = new StandaloneProcessEngineConfiguration();
        //配置相关的数据库信息
        processEngineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC")
                .setJdbcUsername("root")
                .setJdbcPassword("123456")
                .setJdbcDriver("com.mysql.cj.jdbc.Driver");
        //如果数据库中表结构不存在就新建
        processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
    }

    /**
     * 部署流程
     * RepositoryService介绍:
     * 查询引擎现有的部署与流程定义。
     * 暂停或激活部署中的某些流程,或整个部署。暂停意味着不能再对它进行操作,激活刚好相反,重新使它可以操作。
     * 获取各种资源,比如部署中保存的文件,或者引擎自动生成的流程图。
     * 获取POJO版本的流程定义。它可以用Java而不是XML的方式查看流程。
     */
    @Test
    public void deployment() {
        //获取ProcessEngine对象
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();

        //获取部署接口
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("holiday-request.bpmn20.xml")
                .name("请假流程")
                .deploy();
        System.out.println("deploy.getId():" + deploy.getId());
        System.out.println("deploy.getName():" + deploy.getName());
        System.out.println("deploy.getKey():" + deploy.getKey());
        System.out.println("deploy.getCategory():" + deploy.getCategory());
        System.out.println("deploy.getTenantId():" + deploy.getTenantId());
    }
  • key,name自己根据业务定义
deploy.getId():70001
deploy.getName():请假流程
deploy.getKey():null
deploy.getCategory():null
deploy.getTenantId():
  • 查看下数据库:

    • act_re_deployment:流程定义部署表,没部署一次就会增加一条记录
    • 这里的ID=67501,就是我们代码部署生成的记录

      在这里插入图片描述

    • act_re_procdef:流程定义表,部署新的流程定义就会新增一条记录,表中DELOYMENT_ID就是act_re_deployment的主键ID

      在这里插入图片描述

    • act_ge_bytearray:流程资源表,流程部署的bpmn文件和png文件都会存在该表

      在这里插入图片描述

  • 现在可以通过API查询验证流程定义已经部署在引擎中(并学习一些API)。通过RepositoryService创建的ProcessDefinitionQuery对象实现。

//查询流程定义的信息
    @Test
    public void queryDeployment() {
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId("67501")
                .singleResult();
        System.out.println("processDefinition.getDeploymentId() = " + processDefinition.getDeploymentId());
        System.out.println("processDefinition.getName() = " + processDefinition.getName());
        System.out.println("processDefinition.getDescription() = " + processDefinition.getDescription());
        System.out.println("processDefinition.getKey() = " + processDefinition.getKey());
        System.out.println("processDefinition.getId() = " + processDefinition.getId());
    }
processDefinition.getDeploymentId() = 70001
processDefinition.getName() = Holiday Request
processDefinition.getDescription() = null
processDefinition.getKey() = holidayRequest
processDefinition.getId() = holidayRequest:1:70003

启动流程实例

  • 现在已经在流程引擎中部署了流程定义,因此可以使用这个流程定义作为“蓝图”启动流程实例。

  • 一个部署成功的流程定义可以启动多个流程实例,就好比请假申请单,是可以多个员工都可以去填写的。

  • 要启动流程实例,通常需要提供一些初始化流程变量。一般来说,可以通过呈现给用户的表单,或者在流程由其他系统自动触发时通过REST API,来获取这些变量。这样才能达到一个比较全面的数据交互。

  • 接下来,我们使用RuntimeService启动一个流程实例。收集的数据作为一个java.util.Map实例传递,其中的键就是之后用于获取变量的标识符。这个流程实例使用key启动。这个key就是BPMN 2.0 XML文件中设置的id属性,在这个例子里是holidayRequest,也就是act_re_procdef表中的KEY_字段

    在这里插入图片描述

    在这里插入图片描述

	//启动流程实例
    @Test
    public void startProcess() {
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
        //获取启动对象
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //初始化流程变量,这些变量实际是从页面表单传过来的,员工填写请假申请单的一些相关表单数据
        Map var = new HashMap();
        var.put("employee", "张三");
        var.put("description", "累了想请假");
        //启动流程实例
        //holidayRequest就是流程定义的ID,在xml或者`act_re_procdef`的`KEY_`字段都能查到
        ProcessInstance holidayRequest = runtimeService.startProcessInstanceByKey("holidayRequest", var);
        System.out.println("holidayRequest.getProcessDefinitionId() = " + holidayRequest.getProcessDefinitionId());
    }
  • 执行成功后,观察数据库表:

    act_ru_task:任务信息表,会新增一条任务记录

    在这里插入图片描述

  • act_ru_variable:流程实例的流程变量信息

    可以看到代码中,map存放的变量数据

    在这里插入图片描述

  • act_ru_execution:流程执行的过程信息

    在这里插入图片描述

  • act_hi_procinst:流程实例历史信息

  • act_hi_taskinst:流程任务历史信息

  • act_hi_actinst:流程实例执行历史

查询任务

  • 在实际的应用中,会为用户提供界面化操作,让他们可以登录并查看任务列表。可以看到作为流程变量存储的流程实例数据,并决定后续如何操作。现在通过API的方式调用查询任务列表。

  • 在开始的创建的holiday-request.bpmn20.xml中并没有为用户任务配置处理人,即某个员工发起了请假流程申请,但是后续并没有领导去审核。我们在这里需要加上任务的处理人



  • 流程实例执行完成后,去act_ru_task任务表去查看ASSIGNEE_字段,可以看到当前任务的处理人就是刚刚flowable:assignee设置的

    在这里插入图片描述

  • 查询lisi的任务:
	/**     * 查询任务     */    @Test    public void queryTask() {        //先获取ProcessEngine对象        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();        //获取TaskService对象        TaskService taskService = processEngine.getTaskService();        // 查询任务        List taskList = taskService                //创建查询                .createTaskQuery()                //根据流程定义查询                .processDefinitionKey("holidayRequest")                //根据任务人查询                .taskAssignee("lisi")                .list();        for (Task task : taskList) {            System.out.println("task.getProcessDefinitionId() = " + task.getProcessDefinitionId());            System.out.println("task.getId() = " + task.getId());            System.out.println("task.getAssignee() = " + task.getAssignee());            System.out.println("task.getName() = " + task.getName());        }    }
  • 查询结果
task.getProcessDefinitionId() = holidayRequest:1:70003task.getId() = 72507task.getAssignee() = lisitask.getName() = Approve or reject request

完成任务

  • 上面流程已经发起成功了,任务成功流转到lisi来处理,现在通过lisi来完成任务,确定任务的下一步走向

    在这里插入图片描述

    • 按照事先定好的流程图,下一步操作是通过排他网关处理,这里暂时不用管网关具体是什么,我们只需要这一步经理lisi的操作需要通过网关来流转到不同的走向,这里目前有两个走向,通过和拒绝,我们先拒绝这个任务

    • 在流程的xml文件可以看到serviceTask中flowable:class=com.flowable.demo.SendRejectionMail,这个意思是,当我们拒绝后会触发Send out rejection email 这个任务标签的实现类,这个类需要我们自己定义然后去实现JavaDelegate

      在这里插入图片描述

    • 定义一个这样的类:flowable:class的路径及类根据自己的创建的来设置,这里就按照com.flowable.demo.SendRejectionMail来创建了

package com.flowable.demo;

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

public class SendRejectionMail implements JavaDelegate {
    /**
     * flowable中的触发器
     * @param delegateExecution
     */
    @Override
    public void execute(DelegateExecution delegateExecution) {
        //触发执行的逻辑 发送邮件
        System.out.println("SendRejectionMail:被拒绝了");
    }
}
  • list完成任务:
	/**
     * 完成任务
     */
    @Test
    public void completeTask() {
        //获取ProcessEngine对象
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();

        //再获取lisi的任务
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery().processDefinitionKey("holidayRequest").taskAssignee("lisi").singleResult();
        //完成该任务,同时添加该任务的流程变量信息
        Map variables = new HashMap();
        //这个approved变量就是流程里面排他网关节点设置的,通过给这个流程变量值后判断流程的下一步走向
        variables.put("approved", false);//这里false是拒绝

        //完成任务,complete需要的参数:任务ID,流程节点中的流程变量
        taskService.complete(task.getId(), variables);
    }
  • 执行代码,可以看到拒绝流程自定义的JavaDelegate触发了

    在这里插入图片描述

  • 按照当前的流程设计,拒绝之后,流程也就结束了,再去查看act_ru_task表已经没有该任务数据了

历史任务

  • 当流程处于执行过程中,或者已经执行结束了,我们都可以查询到该流程实例的历史数据

  • 可以通过ProcessEngine获取HistoryService创建历史活动查询。

/**
     * 流程实例的历史任务
     */
    @Test
    public void historyTask() {
        //获取ProcessEngine对象
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();

        //获取historyService
        HistoryService historyService = processEngine.getHistoryService();
        List activityInstances = historyService
                .createHistoricActivityInstanceQuery()
                //按流程实例ID查询,这个流程定义是启动流程后有的
                .processDefinitionId("holidayRequest:1:70003")
                //查询已完成的
                .finished()
                //按照结束时间排序
                .orderByHistoricActivityInstanceEndTime().asc()
                .list();

        for (HistoricActivityInstance activityInstance : activityInstances) {
    		System.out.println(historicActivityInstance.getActivityId() + ":" + historicActivityInstance.getActivityName() +
                    ":" + historicActivityInstance.getAssignee() + ":" + historicActivityInstance.getDurationInMillis() + "毫秒");
        }
    }
startEvent:null:null:2毫秒
approveTask:Approve or reject request:lisi:9388974毫秒
decision:null:null:15毫秒
sendRejectionMail:Send out rejection email:null:10毫秒
rejectEnd:null:null:2毫秒

删除任务

	/** 
     * 删除任务
     */
    @Test
    public void deleteTask() {
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
        
        TaskService taskService = processEngine.getTaskService();
     
        taskService.deleteTask("xxx");
    }

删除流程

  • 当流程不需要的时候可以将其删除
	/**
     * 删除流程定义
     */
    @Test
    public void deleteProcess() {
        //获取ProcessEngine对象
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
        //获取RepositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //删除流程定义
        //根据流程部署ID删除,如果流程未启动可以直接通过流程部署ID删除,否则流程启动后该删除会报异常
        //根据流程部署ID删除,同时设置级联删除,true就是表示级联删除,即使流程已经启动也会删除成功及所关联的数据
        repositoryService.deleteDeployment("xxx", true);
    }

流程挂起与激活

	//流程的挂起和激活
    @Test
    public void suspendedProcess() {
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //获取对应的流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId("holidayRequest:1:70003")
                .singleResult();
        //获取当前的流程定义的 状态信息
        boolean suspended = processDefinition.isSuspended();
        if (suspended) {
            //当前流程被挂起了
            //激活流程
            System.out.println("激活流程" + processDefinition.getId() + ":" + processDefinition.getName());
            repositoryService.activateProcessDefinitionById("holidayRequest:1:70003");
        } else {
            //当前流程是激活状态
            //挂起当前流程
            repositoryService.suspendProcessDefinitionById("holidayRequest:1:70003");
            System.out.println("挂起流程" + processDefinition.getId() + ":" + processDefinition.getName());
        }
    }

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/0bd7db4855.html