Flowable工作流——基础篇

1. 介绍

Flowable是BPMN的一个基于Java的软件实现,但是不仅仅限于BPMN,还有DMN决策表和CMMN Case管理引擎,并且有自己的用户管理,微服务API的功能,是一个服务平台。
是由开发了Acitivity6的开发人员,再次升级开发的。

2. 项目搭建

2.1 初始化

  • 构建一个普通maven项目。
  • 引入依赖:
    <dependencies>
        <dependency>
            <groupId>org.flowablegroupId>
            <artifactId>flowable-engineartifactId>
            <version>6.3.0version>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.49version>
        dependency>

        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.13.2version>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-apiartifactId>
            <version>1.7.21version>
        dependency>

        <dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-classicartifactId>
            <version>1.1.7version>
        dependency>

        <dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-coreartifactId>
            <version>1.1.7version>
        dependency>
    dependencies>
  • 新建测试类。
 @Test
    public void testProcessEngine(){
        // 流程配置对象
        ProcessEngineConfiguration configuration = new StandaloneProcessEngineConfiguration();

        // 数据库配置
        configuration.setJdbcDriver("com.mysql.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8");
        // 配置如果表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);


        // 构造核心流程对象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        System.err.println(processEngine);
    }
  • 耐心等待运行(创建表结构)
    Flowable工作流——基础篇_第1张图片
  • 配置slf4j,在resource下新建配置文件log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/logFile.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
  • 配置logback,resource下新建logback.xml

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <property name="APP_NAME" value="MY_APP_NAME" />
    <property name="LOG_DIR" value="logs" />
    <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS}  %-5level [%thread] %logger{15} - %msg%n" />
    
    <property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %boldYellow([%thread])  %cyan(%logger{15}) %msg%n"/>

    <contextName>${APP_NAME}contextName>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}pattern>
        encoder>
    appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${LOG_DIR}/logFile.logfile>
        <append>trueappend>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}pattern>
        encoder>
    appender>

    <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_DIR}/dayLogFile.%d{yyyy-MM-dd}.logfileNamePattern>
            <maxHistory>30maxHistory>
        rollingPolicy>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}pattern>
        encoder>
    appender>

    
    <logger name="com.example.Logger1" level="DEBUG" additivity="true">
    logger>

    
    <logger name="com.example.Logger2" level="DEBUG" additivity="false">
    logger>

    <logger name="com.example.Logger3" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
    logger>

    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <appender-ref ref="RollingFile" />
    root>
configuration>
  • 再次启动测试,可以看到是彩色的日志
    Flowable工作流——基础篇_第2张图片

3. 部署流程测试

使用官方给的请假测试流程。

3.1 流程图

  • 流程图如下:
    Flowable工作流——基础篇_第3张图片

    1. 发起请求之后由管理员去审批:通过或者拒绝
    2. 通过/拒绝,之后触发不同的流程。
  • 流程图说明

    1. 左侧的圆圈叫做启动事件(start event)。这是一个流程实例的起点。
    2. 第一个矩形是一个用户任务(user task)。这是流程中用户操作的步骤。在这个例子中,管理员需要批准或驳回申请。
    3. 取决于管理员的决定,排他网关(exclusive gateway) (带叉的菱形)会将流程实例路由至批准或驳回路径。
    4. 如果批准,则需要将申请注册至某个外部系统,并跟着另一个用户任务,将经理的决定通知给申请人。当然也可以改为发送邮件。
    5. 如果驳回,则为雇员发送一封邮件通知他。

3.2 流程文件

  • 对应的xml文件:将xml放到resource文件夹下,并命名为holiday-request.bpmn20.xml

<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">
                
            conditionExpression>
        sequenceFlow>


        <sequenceFlow  sourceRef="decision" targetRef="sendRejectionMail">
            <conditionExpression xsi:type="tFormalExpression">
                
            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>

3.3 部署文件

  • 新建单元测试文件
public class DoFlowable {

    ProcessEngineConfiguration configuration = null;

    /**
     * 获取flowable流引擎对象
     */
    @Before
    public void testProcessEngine(){
        // 流程配置对象
        configuration = new StandaloneProcessEngineConfiguration();
        // 数据库配置
        configuration.setJdbcDriver("com.mysql.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8");
        // 配置如果表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
    }

    /**
     * 部署流程
     */
    @Test
    public void testDeploy(){
        // 获取 processEngine 对象
        ProcessEngine processEngine = configuration.buildProcessEngine();

        // 获取 repositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 获取 deployment 对象
        Deployment deployment = repositoryService.createDeployment()// 创建Deployment对象
                .addClasspathResource("holiday-request.bpmn20.xml") // 添加流程部署文件
                .name("请求流程") // 设置部署流程的名称
                .deploy(); // 执行

        System.err.println("deployment.getId() = " + deployment.getId());
        System.out.println("deployment.getName() = " + deployment.getName());
    }

}
  • 执行之后:
    Flowable工作流——基础篇_第4张图片
  • 查看数据库:可以看到数据
    Flowable工作流——基础篇_第5张图片

4. 流程API

  • 先封装一个工具类,方便用来获取ProcessEngine对象
public class ProcessEngineUtils {

    /**
     * 获取flowable流引擎对象
     */
    public static ProcessEngine getProcessEngine(){
        // 流程配置对象
        ProcessEngineConfiguration configuration = new StandaloneProcessEngineConfiguration();
        // 数据库配置
        configuration.setJdbcDriver("com.mysql.jdbc.Driver");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("root");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC&nullCatalogMeansCurrent=true&useSSL=true");
        // 配置如果表结构不存在就新建
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

        // 构造核心流程对象
        return configuration.buildProcessEngine();
    }
}

4.1 查询操作

    /**
     * 查询流程定义信息
     */
    @Test
    public void deploymentQuery(){
        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        // 获取查询对象
        ProcessDefinitionQuery processQuery = repositoryService.createProcessDefinitionQuery();
        // 定义查询信息(单个查询)
        ProcessDefinition definition = processQuery.deploymentId("2501").singleResult();
    }

4.2 删除操作

    /**
     * 删除流程定义
     */
    @Test
    public void deployDel(){
        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        repositoryService.deleteDeployment("1"); // 删除对应id的流程,启动中不可以删除
//        repositoryService.deleteDeployment("1", true); // 删除对应id的流程(包含其中的过程数据),不考虑启动状态。
    }

4.3 启动流程

    /**
     * 启动流程
     */
    @Test
    public void deployRun(){
        // 配置传递的参数
        Map<String, Object> params = new HashMap<>();
        params.put("employee","王五");
        params.put("days",3);
        params.put("description","累了");

        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        // 1:这里的 key 对应配置文件中的 process 的 id
        // 2:传递的参数
        ProcessInstance holidayRequest = runtimeService.startProcessInstanceByKey("holidayRequest", params);
    }
  • 查看传入的数据
    Flowable工作流——基础篇_第6张图片

  • 查看启动的流程
    Flowable工作流——基础篇_第7张图片

  • 查看执行进度
    Flowable工作流——基础篇_第8张图片

4.4 管理员查询

  • 修改xml中的审批处理人

        <userTask id="approveTask" name="Approve or reject request" flowable:assignee="Ekko"/>

        <sequenceFlow sourceRef="approveTask" targetRef="decision"/>
  • 使用删除api,第二个参数传入true,级联删除任务数据。
  • 重新部署流程。
  • 再次重新启动流程。可以看到我们指定的审批人为:Ekko。
    Flowable工作流——基础篇_第9张图片
  • 查询代码
    /**
     * 任务查询
     */
    @Test
    public void taskQuery(){
        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("holidayRequest") // 指定流程key
                .taskAssignee("Ekko") // 指定处理人
                .list();
        for (Task task : list) {
            System.out.println(task.getProcessDefinitionId());
            System.out.println(task.getName());
            System.out.println(task.getAssignee());
            System.out.println(task.getDescription());
            System.out.println(task.getId());
        }
    }

4.5 管理员审批

  • 修改配置文件中排他网关的两种处理走的类这里的配置修改之后需要重新部署一下流程。
<!--        同意流程节点-->
        <serviceTask id="externalSystemCall" name="Enter holidays in external system"
                     flowable:class="com.yy.flowable.service.AppService"/>
        <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>
<!--        拒绝流程让任务节点-->
        <serviceTask id="sendRejectionMail" name="Send out rejection email"
                     flowable:class="com.yy.flowable.service.RejectService"/>
<!--        拒绝-指向结束-->
        <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>
  • 新增流程处理类
    Flowable工作流——基础篇_第10张图片
  • 请假通过接口
/**
 * @author : JinMing Zhou
 * @description: 通过类
 * @date : 2023/1/9 17:48
 */
public class AppService implements JavaDelegate {

    /**
     * 触发器,流程走到这里执行该方法
     * @param delegateExecution
     */
    @Override
    public void execute(DelegateExecution delegateExecution) {
        // 通过,执行逻辑
        System.out.println("通过。");
    }
}
  • 请假拒绝接口
/**
 * @author : JinMing Zhou
 * @description: 拒绝类
 * @date : 2023/1/9 17:48
 */
public class RejectService implements JavaDelegate {

    /**
     * 请假流程拒绝触发器
     * @param delegateExecution
     */
    @Override
    public void execute(DelegateExecution delegateExecution) {
        System.err.println("不给通过!");
    }
}

  • 处理操作
    /**
     * 处理流程
     */
    @Test
    public void completeTask(){
        // 指定审批参数
        Map<String, Object> params = new HashMap<>();
        params.put("approved",false);

        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        // 查询任务

        Task task = taskService.createTaskQuery()
                .processDefinitionKey("holidayRequest") // 指定流程key
                .taskAssignee("Ekko") // 指定处理人
                .singleResult();
        // 处理操作
        taskService.complete(task.getId(), params);
    }

Flowable工作流——基础篇_第11张图片

4.6 历史记录查询

    /**
     * 历史信息查询
     */
    @Test
    public void historyQuery(){
        ProcessEngine processEngine = ProcessEngineUtils.getProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processDefinitionId("holidayRequest:1:17503")
                .finished()
                .orderByHistoricActivityInstanceEndTime().asc()
                .list();
        for (HistoricActivityInstance historicActivityInstance : list) {
            System.err.println(historicActivityInstance.getActivityId() + " took "
                    + historicActivityInstance.getDurationInMillis() + " milliseconds");
        }
    }

Flowable工作流——基础篇_第12张图片

你可能感兴趣的:(2023,java,工作流)