Flowable工作流引擎

Flowable工作流引擎

Flowable-基础篇(根据BV1Pb4y1p7Ku整理)

一、简介

Flowable是BPMN的一个基于java的软件实现,不过Flowable不仅仅包括BPMN,还有DMN决策表和CMMN Case管理引擎,并且有自己的用户管理、微服务API等一系列功能,是一个服务平台。
在这里插入图片描述

官方手册:https://tkjohn.github.io/flowable-userguide/# introduction

二、数据库解析(数据库为代码创建,先看第三点:实例前的示例(官方文档的例子))

在这里插入图片描述
1、act_evt_log:日志表
在这里插入图片描述
2、act_ge_bytearray:二进制资源表
在这里插入图片描述
3、act_ge_bytearray:引擎属性表
在这里插入图片描述
4、act_hi_actinst:历史活动信息表
在这里插入图片描述
5、act_hi_attachment:流程历史附件表
在这里插入图片描述
6、act_hi_comment:历史审批意见表
在这里插入图片描述
7、act_hi_detail:历史详情表
在这里插入图片描述
8、act_hi_identitylink:历史参与的人员表
在这里插入图片描述
9、act_hi_procinst:历史流程记录表
在这里插入图片描述
10、act_hi_taskinst:历史任务表
在这里插入图片描述
11、act_hi_varinst:历史流程变量表
在这里插入图片描述
12、act_id_bytearray:用户组部署内容表
用户组的部署内容
13、act_id_group:所有用户组信息表
所有用户组的信息
14、act_id_info:所有用户信息表
所有用户的信息,账号密码
15、act_id_membership:用户和用户组关系表
用户和用户组的关系
16、act_id_priv:权限表
17、act_id_priv_mapping:用户权限关系表
18、act_id_property:用户变量表
用户的变量,存了版本号
19、act_id_token:用户访问记录表
20、ct_id_user:用户基本信息表
用户基本信息,包括邮箱等
21、act_procdef_info:流程定义信息表
在这里插入图片描述
22、act_re_deployment:流程部署表
在这里插入图片描述
23、act_re_model:流程模型表
在这里插入图片描述
24、act_re_procdef:流程定义表
在这里插入图片描述
25、act_ru_deadletter_job:运行时死信作业表
在这里插入图片描述
26、act_ru_event_subscr:运行时事件订阅表
在这里插入图片描述
27、act_ru_execution:运行时流程执行实例表
在这里插入图片描述
28、atc_ru_history_job:运行时历史信息表
29、act_ru_identitylink:运行时身份连接表
在这里插入图片描述
30、act_ru_job:运行时作业表
在这里插入图片描述
31、act_ru_suspended_job:运行时挂起的定时作业表
在这里插入图片描述
32、act_ru_task:运行时任务表
在这里插入图片描述
33、act_ru_timer_job:运行时定时作业表
在这里插入图片描述
34、act_ru_variable:运行时流程变量表
在这里插入图片描述

三、实例前的示例(官方文档的例子)

1、创建项目

创建一个maven项目
在这里插入图片描述
在这里插入图片描述
填写pom依赖
在这里插入图片描述

2、配置依赖
<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-engine</artifactId>
            <version>6.3.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
3、Test01

先创建数据库
在这里插入图片描述
创建Test01
在这里插入图片描述

public class Test01 {

    /*获取流程引擎对象*/
    @Test
    public void testProcessEngine(){
        // 获取ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = new StandaloneInMemProcessEngineConfiguration();

        // 配置相关数据库连接信息(做演示就不封装了)
        configuration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable_learn?serverTimezone=UTC");

        // 如果数据库中的表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        // 通过ProcessEngineConfiguration构建我们需要的processEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
    }
}

运行程序
在这里插入图片描述
红色提示是由于没配置日志文件,运行完成后到数据库查看
在这里插入图片描述
可以看到已经创建了数据库表,这里使用的是5.7版本的数据库,值得注意的是,如果使用8.0版本的数据库,可能出现如下问题
在这里插入图片描述
添加日志配置
先添加pom依赖

<dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>

再添加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

再次运行就可以看到已经有日志信息的输出
在这里插入图片描述

4、流程部署

流程部署定义
在这里插入图片描述
在这里插入图片描述
流程部署布置:配置文件:holiday-request.bpmn20.xml
将下面的XML保存在src/main/resources文件夹下名为holiday-request.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"/>
    <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="org.flowable.SendRejectionMail"/>
    <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>

    <endEvent id="approveEnd"/>

    <endEvent id="rejectEnd"/>

  </process>

</definitions>

我们来添加些备注以便看懂整个逻辑

<?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:流程主键,name:名称,可以修改,比如改为"请假流程"-->
    <process id="holidayRequest" name="Holiday Request" isExecutable="true">

        <startEvent id="startEvent"/>
        <!--targetRef:目标引用,走到下面的approveTask-->
        <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>

        <!--name="同意或者拒绝请假"-->
        <userTask id="approveTask" name="Approve or reject request"/>
        <!--targetRef目标引用为下面的排他网关decision-->
        <sequenceFlow sourceRef="approveTask" targetRef="decision"/>

        <!--排他网关(id:decision),有两个流程-->
        <exclusiveGateway id="decision"/>
        <!--流程1:targetRef="外部系统调用",就是走到管理员审批的流程中-->
        <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
            <!--条件表达式:${approved}如果为真就走这里-->
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
          ${approved}
        ]]>
            </conditionExpression>
        </sequenceFlow>
        <!--targetRef="发送拒绝邮件",就是直接走拒绝策略,就跳转到下面id="sendRejectionMail"-->
        <sequenceFlow  sourceRef="decision" targetRef="sendRejectionMail">
            <!--条件表达式:${!approved}!表示取反,如果参数不为真就走这里-->
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
          ${!approved}
        ]]>
            </conditionExpression>
        </sequenceFlow>

        <!--name="在外部系统中输入假日"-->
        <serviceTask id="externalSystemCall" name="Enter holidays in external system"
                     flowable:class="org.flowable.CallExternalSystemDelegate"/>
        <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>

        <!--name="假期获批",targetRef="approveEnd"就是走到最下面结束-->
        <userTask id="holidayApprovedTask" name="Holiday approved"/>
        <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>

        <!--name="发送拒绝邮件",flowable:class就是用来处理发送邮件的操作,targetRef="rejectEnd"就是走到最下面结束-->
        <serviceTask id="sendRejectionMail" name="Send out rejection email"
                     flowable:class="org.flowable.SendRejectionMail"/>
        <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>

        <endEvent id="approveEnd"/>

        <endEvent id="rejectEnd"/>

    </process>

</definitions>

我们再次对照下图来看,xml中从process id="holidayRequest"开始,就是对应下图的发起请假申请;然后走到approveTask,approveTask走到排他网关,排他网关有两个分支,根据$ {approved}和$ {!approved}来判断走哪个分支,就是对应下图的网关同意和拒绝两个分支:拒绝的话走到sendRejectionMail发送拒绝邮件,再跳转到rejectEnd结束;同意的话就是走到externalSystemCall,对应下图进入人事系统,然后走到holidayApprovedTask假期获批,最后跳转到approveEnd结束流程。
在这里插入图片描述
流程部署布置:代码实现
回到test01中
在这里插入图片描述

	ProcessEngineConfiguration configuration = null;

    @Before
    public void before(){
        /*配置数据库设置*/
        configuration = new StandaloneInMemProcessEngineConfiguration();
        configuration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable_learn?serverTimezone=UTC");
        // 如果数据库中的表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
    }

    /**
     * 部署流程
     * */
    @Test
    public void testDeploy(){
        // 1.获取ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 2.获取RepositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
        // 3.完成流程的部署操作
        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());
    }

运行测试程序
在这里插入图片描述
到数据库中查看
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
还需要注意ProcessEngine和四个Service之间的关系
在这里插入图片描述

流程部署布置:查询流程定义的信息
在test01中添加如下代码并运行
在这里插入图片描述

	 /**
     * 查询流程定义的信息
     * */
    @Test
    public void testDeployQuery(){
        // 获取ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 获取RepositoryService资源管理服务
        RepositoryService repositoryService = processEngine.getRepositoryService();


        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                // deploymentId要查找现有的id(可以去数据库中查看deployment中查看),否则会报空指针异常
                .deploymentId("2501")
                .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.getId():" + processDefinition.getId());
    }

运行结果如下
在这里插入图片描述

若查询不存在的id
在这里插入图片描述

流程部署布置:删除流程定义
在test01中添加如下代码并运行
在这里插入图片描述

	/**
     * 删除流程定义
     * */
    @Test
    public void testDeleteDeploy(){
        // 获取ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 获取RepositoryService资源管理服务
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 删除部署的流程(第一个参数是id,如果部署的流程启动了就不允许删除了;第二是参数是级联删除,若为true,则若流程启动了,相关的任务会一并被删除)
        repositoryService.deleteDeployment("1", false);
    }

运行后,查看日志信息可以得知数据库数据已被删除
在这里插入图片描述

四、启动流程实例(官方文档的例子)

在test01中继续添加代码
在这里插入图片描述

	/**
     * 启动流程实例
     * */
    @Test
    public void testRunProcess(){
        ProcessEngine processEngine = configuration.buildProcessEngine();

        // 我们需要通过RuntimeService来启动流程实例
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //构建流程变量
        Map<String, Object> variables = new HashMap<>();
        variables.put("employee", "张三");
        variables.put("nrOfHolidays", "3");
        variables.put("description", "工作累了,出去玩玩");

        // 启动流程实例
        ProcessInstance holidayRequest = runtimeService.startProcessInstanceByKey("holidayRequest", variables);
        System.out.println("holidayRequest.getProcessDefinitionId():" + holidayRequest.getProcessDefinitionId());
        System.out.println("holidayRequest.getActivityId():" + holidayRequest.getActivityId()); // 当前活跃
        System.out.println("holidayRequest.getId():" + holidayRequest.getId());
    }

运行结果如下图所示(第二个参数:当前活跃的为0)
在这里插入图片描述
查询任务(查询自己需要审批的业务)
上面一步中员工发起了一个请假流程,接下里就会流转到经理这里来,我们现在指定这个经理为处理人
在这里插入图片描述
然后我们先删除掉之前的,即用“删除流程定义”方法把表act_ge_bytearray里面的数据都删除掉
删除完之后再执行一遍"部署流程"方法
在这里插入图片描述
在这里插入图片描述
然后运行"启动流程实例"方法
在这里插入图片描述
在这里插入图片描述
最后我们来添加一个查询任务方法,指定查询manager需要审批的任务
在这里插入图片描述

	/**
     * 查询任务
     * */
    @Test
    public void testQueryTask(){
        ProcessEngine processEngine = configuration.buildProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("holidayRequest") // 指定查询的流程编程
                .taskAssignee("manager") // 查询这个任务的处理人
                .list();

        for (Task task: list) {
            System.out.println("task.getProcessDefinitionId():" + task.getProcessDefinitionId());
            System.out.println("task.getName():" + task.getName());
            System.out.println("task.getAssignee():" + task.getAssignee());
            System.out.println("task.getDescription():" + task.getDescription());
            System.out.println("task.getId():" + task.getId());
        }
    }

在这里插入图片描述
执行任务(拒绝)
根据sendRejectionMail中的flowable:class创建类
在这里插入图片描述
在这里插入图片描述
写入拒绝流程
在这里插入图片描述

public class SendRejectionMail implements JavaDelegate {

    /**
     * 这是一个flowable的触发器
     * */
    @Override
    public void execute(DelegateExecution execution) {
        // 触发执行的逻辑,按照我们在流程中的定义应该给被拒绝的员工发送通知的邮件
        // 这里面可以通过邮件发送之类的,这里做模拟就只是写一个sout
        System.out.println("请假申请不通过,请安心工作");
    }
}

回到test01中书写"执行任务"方法
在这里插入图片描述

	/**
     * 执行任务
     * */
    @Test
    public void testCompleteTask(){
        ProcessEngine processEngine = configuration.buildProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("holidayRequest")
                .taskAssignee("manager") //指定是经理
                .singleResult();

        // holiday-request.bpmn20.xml中${!approved}表示走拒绝策略,我们这里传一个${!approved}
        Map<String, Object> map = new HashMap<>();
        map.put("approved", false);

        // 完成任务
        taskService.complete(task.getId(), map);
    }

运行发现已经出现拒绝提示
在这里插入图片描述
回到数据库中查看,发现只剩历史记录有数据了,因为已经执行完一个流程了
在这里插入图片描述
在这里插入图片描述
历史任务查询
在这里插入图片描述

 	/**
     * 获取流程任务的历史数据
     * */
    @Test
    public void testHistory(){
        ProcessEngine processEngine = configuration.buildProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();

        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processDefinitionId("holidayRequest:1:17503") // 从act_hi_actinst中获取
                .finished() // 查询的历史记录的状态是已完成
                .orderByHistoricActivityInstanceEndTime().asc() //指定排序的字段和顺序
                .list();

        for (HistoricActivityInstance history: list) {
            System.out.println(history.getActivityName() + ":" + history.getAssignee() + "--" + history.getActivityId() + ":" + history.getDurationInMillis() + "毫秒");
        }
    }

在这里插入图片描述
运行代码,结果如下
在这里插入图片描述
到此一个完整流程结束
完整的Test01如下:

package com.ghost.flowable.test;

import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.HistoricActivityInstanceQueryImpl;
import org.flowable.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.junit.Before;
import org.junit.Test;

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

/**
 * flowable测试01
 *
 * @author ghost
 * @date 2022-09-06
 */
public class Test01 {

    /*获取流程引擎对象*/
    @Test
    public void testProcessEngine(){
        // 获取ProcessEngineConfiguration对象
        ProcessEngineConfiguration configuration = new StandaloneInMemProcessEngineConfiguration();

        // 配置相关数据库连接信息(做演示就不封装了)
        configuration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable_learn?serverTimezone=UTC");

        // 如果数据库中的表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        // 通过ProcessEngineConfiguration构建我们需要的processEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        System.out.println("processEngine" + processEngine);
    }

    ProcessEngineConfiguration configuration = null;

    @Before
    public void before(){
        /*配置数据库设置*/
        configuration = new StandaloneInMemProcessEngineConfiguration();
        configuration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable_learn?serverTimezone=UTC");
        // 如果数据库中的表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
    }

    /**
     * 部署流程
     * */
    @Test
    public void testDeploy(){
        // 1.获取ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 2.获取RepositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
        // 3.完成流程的部署操作
        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());
    }

    /**
     * 查询流程定义的信息
     * */
    @Test
    public void testDeployQuery(){
        // 获取ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 获取RepositoryService资源管理服务
        RepositoryService repositoryService = processEngine.getRepositoryService();


        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                // deploymentId要查找现有的id(可以去数据库中查看deployment中查看),否则会报空指针异常
                .deploymentId("15001")
                .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.getId():" + processDefinition.getId());
    }

    /**
     * 删除流程定义
     * */
    @Test
    public void testDeleteDeploy(){
        // 获取ProcessEngine对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 获取RepositoryService资源管理服务
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 删除部署的流程(第一个参数是id,如果部署的流程启动了就不允许删除了;第二是参数是级联删除,若为true,则若流程启动了,相关的任务会一并被删除)
        repositoryService.deleteDeployment("5001", true);
    }

    /**
     * 启动流程实例
     * */
    @Test
    public void testRunProcess(){
        ProcessEngine processEngine = configuration.buildProcessEngine();

        // 我们需要通过RuntimeService来启动流程实例
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //构建流程变量
        Map<String, Object> variables = new HashMap<>();
        variables.put("employee", "张三");
        variables.put("nrOfHolidays", "3");
        variables.put("description", "工作累了,出去玩玩");

        // 启动流程实例
        ProcessInstance holidayRequest = runtimeService.startProcessInstanceByKey("holidayRequest", variables);
        System.out.println("holidayRequest.getProcessDefinitionId():" + holidayRequest.getProcessDefinitionId());
        System.out.println("holidayRequest.getActivityId():" + holidayRequest.getActivityId()); // 当前活跃
        System.out.println("holidayRequest.getId():" + holidayRequest.getId());
    }

    /**
     * 查询任务
     * */
    @Test
    public void testQueryTask(){
        ProcessEngine processEngine = configuration.buildProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("holidayRequest") // 指定查询的流程编程
                .taskAssignee("manager") // 查询这个任务的处理人
                .list();

        for (Task task: list) {
            System.out.println("task.getProcessDefinitionId():" + task.getProcessDefinitionId());
            System.out.println("task.getName():" + task.getName());
            System.out.println("task.getAssignee():" + task.getAssignee());
            System.out.println("task.getDescription():" + task.getDescription());
            System.out.println("task.getId():" + task.getId());
        }
    }

    /**
     * 执行任务
     * */
    @Test
    public void testCompleteTask(){
        ProcessEngine processEngine = configuration.buildProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("holidayRequest")
                .taskAssignee("manager") //指定是经理
                .singleResult();

        // holiday-request.bpmn20.xml中${!approved}表示走拒绝策略,我们这里传一个${!approved}
        Map<String, Object> map = new HashMap<>();
        map.put("approved", false);

        // 完成任务
        taskService.complete(task.getId(), map);
    }

    /**
     * 获取流程任务的历史数据
     * */
    @Test
    public void testHistory(){
        ProcessEngine processEngine = configuration.buildProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();

        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processDefinitionId("holidayRequest:1:17503") // 从act_hi_actinst中获取
                .finished() // 查询的历史记录的状态是已完成
                .orderByHistoricActivityInstanceEndTime().asc() //指定排序的字段和顺序
                .list();

        for (HistoricActivityInstance history: list) {
            System.out.println(history.getActivityName() + ":" + history.getAssignee() + "--" + history.getActivityId() + ":" + history.getDurationInMillis() + "毫秒");
        }
    }

}

五、Flowable UI应用

1、安装

查看官网,可以看到有各种工具安装地址
在这里插入图片描述
由于官网的Flowable 6显示30天试用,就从百度找了flowable-6.7.2:https://blog.csdn.net/newposte/article/details/124329777,解压完如下
在这里插入图片描述
下载一个tomcat,我们把flowable-6.7.2里面的两个war包丢到tomcat的webapps里面去
在这里插入图片描述
在这里插入图片描述
我们找到startup.bat,运行它
在这里插入图片描述
然后发现有乱码
在这里插入图片描述
这时我们需要修改一下配置文件
在这里插入图片描述
在这里插入图片描述

java.util.logging.ConsoleHandler.encoding = GBK

再次启动startup.bat,发现乱码问题解决了
在这里插入图片描述
回到tomcat的webapp中查看,发现多出两个flowable文件
在这里插入图片描述
到这里我们就可以运行了,输入网址http://localhost:8080/flowable-ui
在这里插入图片描述
初始密码为 admin test,我们登录进去查看
在这里插入图片描述
这里就对应简介里面的几种应用
在这里插入图片描述

2、任务应用程序

创建流程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
部署流程
点击右上角的一键导出按钮
在这里插入图片描述
然后文件就会被下载下来
在这里插入图片描述
然后我们复制这个文件到idea里,可以看到代码已经生成好了
在这里插入图片描述
代码
然后我们根据这个xml再书写一次代码,我们把Test01的代码全部复制,修改名称为TestMyHolidayUI
修改如下地方
在这里插入图片描述
然后我们运行部署流程方法,发现报错找不到我们新加进来的xml,我们删掉target再运行部署流程方法,让他生成新的target
在这里插入图片描述

如上图,在成功运行的同时就说明新的target生成好了,然后我们到数据库中查看
在这里插入图片描述

在这里插入图片描述
我们发现task表里面还没有东西,我们来运行testRunProcess方法,记得修改如下地方为procdef表中的数据且修改方法为ById
在这里插入图片描述
在这里插入图片描述
回到表中发现有数据了
在这里插入图片描述

3、建模器应用程序
4、管理员应用程序
5、身份管理应用程序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
还可以添加、设置分组
在这里插入图片描述

六级标题