Flowable lightweight business process engine

Article directory

    • Introduction
    • SpringBoot build
      • Introduce dependencies
      • install plugin
      • Create a new BPMN file
        • file parsing
      • Process Engine API
        • test class
      • database
        • the whole frame
        • Common table

This article refers to the blog:

https://zhuanlan.zhihu.com/p/457996810

https://www.51cto.com/article/708150.html

Introduction

Flowable is a lightweight orchestration engine written in Java. The Flowable process engine can be used to deploy BPMN2.0 process definitions (an industry XML standard for defining processes), create process instances of these process definitions, perform queries, access running or historical process instances and related data, and more.

SpringBoot construction

Introducing dependencies

Import the flowable dependencies supported by boot

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

Install plugin

Tools to visualize the installation process

New BPMN file

  1. Create a new processes directory under the resources directory, and the process files in this directory will be automatically deployed in the future
  2. In the processes directory, create a new BPMN file (this option is available after the plug-in is installed)

File parsing

The flow chart is as follows

The xml file parsed according to the flowchart:

  • : Indicates a complete workflow.
  • : The starting position (circle) in the workflow.
  • : End position (circle) in the workflow.
  • : Represents a task node (rectangle).
  • : Logical judgment node (diamond).
  • : Lines linking each node.
  • : This is a service task. In a specific implementation, this task can do anything.
<?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="leave_approval" name="leave approval" isExecutable="true">
        <!--startEvent endEvent start and end nodes (circle) -->
        <!--userTask worker node (rectangle) -->
        <!--exclusiveGateway judgment node (diamond) -->
        <!--flowable: candidateGroups candidate operation group; flowable: assignee candidate operator -->
        <startEvent id="start" name="start" flowable:initiator="startuser" flowable:formFieldValidation="true"></startEvent>
        <userTask id="stu_task" name="student" flowable:candidateGroups="stu_group" flowable:formFieldValidation="true"></userTask>
        <userTask id="te_task" name="teacher" flowable:candidateGroups="te_group" flowable:formFieldValidation="true"></userTask>
        <exclusiveGateway id="getway1" name="Gateway1"></exclusiveGateway>
        <userTask id="mte_task" name="Principal" flowable:candidateGroups="mte_group" flowable:formFieldValidation="true"></userTask>
        <exclusiveGateway id="getway2" name="Gateway2"></exclusiveGateway>
        <endEvent id="end" name="end"></endEvent>

        <!--Streamline: sourceRef start position targetRef end position-->
        <sequenceFlow id="flow1" sourceRef="start" targetRef="stu_task"></sequenceFlow>
        <sequenceFlow id="flow2" name="leave" sourceRef="stu_task" targetRef="te_task"></sequenceFlow>
        <sequenceFlow id="flow3" name="approval" sourceRef="te_task" targetRef="getway1"></sequenceFlow>
        <sequenceFlow id="flow3_1" name="agree" sourceRef="getway1" targetRef="mte_task">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${command=='agree'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="flow3_2" name="deny" sourceRef="getway1" targetRef="stu_task">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${command=='refuse'}]]></conditionExpression>
        </sequenceFlow>

        <sequenceFlow id="flow4" name="approval" sourceRef="mte_task" targetRef="getway2"></sequenceFlow>
        <sequenceFlow id="flow4_1" name="agree" sourceRef="getway2" targetRef="end">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${command=='agree'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="flow4_2" name="deny" sourceRef="getway2" targetRef="stu_task">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${command=='refuse'}]]></conditionExpression>
        </sequenceFlow>

    </process>
</definitions>

Process Engine API

Interpretation of related concepts:

  • RepositoryService is likely to be the first service to use with the Flowable engine. This service provides operations for managing and controlling deployments and process definitions. manage static information,
  • RuntimeService is used to start a new process instance of a process definition.
  • IdentityService is simple. It is used to manage (create, update, delete, query…) groups and users.
  • FormService is an optional service. That said, Flowable can function just fine without it, without having to sacrifice any functionality.
  • HistoryService exposes all historical data collected by the Flowable engine. To provide the ability to query historical data.
  • ManagementService is usually not needed when writing user applications with Flowable. It can read the information of the database table and the original data of the table, and also provide the query and management operation of the job.
  • DynamicBpmnService can be used to modify part of the process definition without redeploying it. For example, you can modify the administrator setting of a user task in the process definition, or modify the class name in a service task.

Test class

package com.njcn.harmonic.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok. AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

/**
 * @author qijian
 * @version 1.0.0
 * @createTime 2022/11/7 - 14:33
 */
@Slf4j
@RestController
@RequestMapping("/flow")
@Api(tags = "flowchart test")
@AllArgsConstructor
public class FlowTestController {<!-- -->
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private IdentityService identityService;

    @PostMapping("/leaveApprovalFlow")
    @ApiOperation("running process")
    public void leaveApprovalFlow() {<!-- -->

        /*
         * Step 1: Query the process definition of the deployment
         */
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave_approval").list();
        List<ProcessDefinition> pages = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave_approval").listPage(1, 30);

        /*
         * Step 2: Start the process and create an instance
         */
        //The key of the process definition, corresponding to the flow chart of asking for leave
        String processDefinitionKey = "leave_approval";
        //Business code, use it according to your own business
        String businessKey = "schoolleave";
        //Process variables can be customized and expanded
        Map<String, Object> variablesDefinition = new HashMap<>();
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variablesDefinition);
        log.info("Start successfully: {}", processInstance.getId());

        /*
         * Step 3: Query the list of all started instances of the specified process
         * list, or pagination delete
         */
        List<Execution> executions = runtimeService.createExecutionQuery().processDefinitionKey("leave_approval").list();
        List<Execution> executionPages = runtimeService.createExecutionQuery().processDefinitionKey("leave_approval").listPage(1, 30);
// runtimeService.deleteProcessInstance(processInstanceId, deleteReason); //delete instance

        /*
         * Step 4: Students query the tasks that can be operated and complete the tasks
         */

        //flowable:assignee in the candidate xml file
        //String stu = "stu";
        //List<Task> taskList = taskService.createTaskQuery().taskAssignee(stu).orderByTaskId().desc().list();

        //flowable:candidateGroups in the candidate group xml file
        String candidateGroup = "stu_group";
        List<Task> taskList = taskService.createTaskQuery().taskCandidateGroup(candidateGroup).orderByTaskId().desc().list();
        for (Task task : taskList) {<!-- -->
            // Apply for a task and specify a user
            taskService. claim(task. getId(), "my");
            // Finish
            taskService.complete(task.getId());
        }

        /*
         * Step 5: The teacher queries the tasks that can be operated and completes the tasks
         */
        //flowable:candidateGroups="te_group" in the candidate group xml file
        String candidateGroupTe = "te_group";
        List<Task> taskListTe = taskService.createTaskQuery().taskCandidateGroup(candidateGroupTe).orderByTaskCreateTime().desc().list();
        for (Task task : taskListTe) {<!-- -->
            // Apply for a task and specify a user
            taskService. claim(task. getId(), "myte");
            // Finish
            Map<String, Object> variables = new HashMap<>();
            //Carry variables for conditional judgment of the gateway process, where the condition is to agree (xml configuration)
            variables. put("command","agree");
// variables. put("command","refuse");
            taskService.complete(task.getId(), variables);
        }

        /*
         * Step 6: Historical query, because once the process is executed, the data of the activity will be cleared, and the above query interface cannot find the data, but the historical query interface is provided
         */
        // historical process instance
        List<HistoricProcessInstance> historicProcessList = historyService.createHistoricProcessInstanceQuery().processDefinitionKey("leave_approval").list();
        // historical tasks
        List<HistoricTaskInstance> historicTaskList = historyService.createHistoricTaskInstanceQuery().processDefinitionKey("leave_approval").list();


        // instance history variable, task history variable
        // historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId);
        // historyService.createHistoricVariableInstanceQuery().taskId(taskId);

        // *************************************************** *****Separator******************************************** *************************
        // *************************************************** *****Separator******************************************** *************************
        // APIs that may still be needed
        // Mobile task, artificial jump task
        // runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
        // .moveActivityIdTo(currentActivityTaskId, newActivityTaskId).changeState();

        // If 2 groups and users are configured in the database, it will also be used
// List<User> users = identityService.createUserQuery().list(); //User query, user id corresponds to the user configured in xml
// List<Group> groups = identityService.createGroupQuery().list(); //Group query, the group id corresponds to the group configured in xml, such as stu_group, te_group is the value of id in the table

        // In addition, conditions can be spelled after each query, and there are many built-in queries, including fuzzy queries, and there are size comparisons
    }


}

Database

After the project starts, about 50 default tables will be created in the configured database: all database tables of Flowable start with ACT_.

Overall structure

  • ACT_RE_: ‘RE’ stands for repository. Tables with this prefix contain “static” information such as process definitions and process resources (images, rules, etc.).
  • ACT_RU_: ‘RU’ stands for runtime. These tables store runtime information such as process instances, user tasks, variables, jobs, etc. Flowable only saves runtime data while the process instance is running, and deletes the records when the process instance ends. This keeps the table small and fast at runtime.
  • ACT_HI_: ‘HI’ stands for history. These tables store historical data such as completed process instances, variables, tasks, etc.
  • ACT_GE_: General data. Used in multiple places.

Common table

  • General Data Sheet (2)

    • act_ge_bytearray: binary data table, such as process definition, process template, byte stream file of flow chart;
    • act_ge_property: attribute data table (not commonly used);
  • History table (8, HistoryService interface operation)

    • act_hi_actinst: historical node table, which stores the information of each node running the process instance (including non-task nodes such as start and end);
    • act_hi_attachment: Historical attachment table, which stores attachment information uploaded by historical nodes (not commonly used);
    • act_hi_comment: historical comment form;
    • act_hi_detail: History details table, which stores some information about node operation (not commonly used);
    • act_hi_identitylink: Historical process personnel table, which stores the information of candidates and personnel in each node of the process, and is often used to query the completed tasks of a person or department;
    • act_hi_procinst: historical process instance table, storing process instance historical data (including running process instances);
    • act_hi_taskinst: Historical process task table, storing historical task nodes;
    • act_hi_varinst: Process history variable table, storing variable information of process history nodes;
  • User related tables (4, IdentityService interface operation)

    • act_id_group: user group information table, which corresponds to the node selected candidate group information;
    • act_id_info: user extension information table, storing user extension information;
    • act_id_membership: user and user group relationship table;
    • act_id_user: user information table, which corresponds to the information of the person or candidate selected by the node;
  • Process definition, process template related tables (3, RepositoryService interface operation)

    • act_re_deployment: Deployment information table, storing process definition and template deployment information;
    • act_re_procdef: process definition information table, which stores process definition related description information, but its real content is stored in the act_ge_bytearray table, which is stored in bytes;
    • act_re_model: process template information table, which stores description information related to the process template, but its real content is stored in the act_ge_bytearray table, which is stored in bytes;
  • Process runtime table (6, RuntimeService interface operation)

    • act_ru_task: The runtime process task node table, which stores the task node information of the running process, is important, and is often used when querying the pending tasks of personnel or departments;
    • act_ru_event_subscr: monitoring information table, not commonly used;
    • act_ru_execution: the runtime process execution instance table, which records the information of each branch of the running process (when there is no sub-process, its data corresponds to the act_ru_task table data);
    • act_ru_identitylink: Runtime process personnel table, important, often used when querying the to-do tasks of personnel or departments;
    • act_ru_job: Runtime scheduled task data table, which stores the scheduled task information of the process;
    • act_ru_variable: Runtime process variable data table, which stores the variable information of each node of the running process;