How to quickly implement the leave application process (OA)? : Activiti workflow

Hello everyone,

In the company, everyone needs to create or approve some processes frequently, such as regular application , leave process , business trip application , etc.

So, have you ever thought about, if you, how would you implement the control logic of these processes?

For example, to apply for leave, you first need to define a process template in advance, including rules such as company-level leadership approval when the number of leave days is greater than 3 days

Then when an employee initiates a leave application, according to the employee's department and the number of days of leave, etc., the specific process information can be determined, including the approver of each node

At any approval node, you need to see the historical approval information of this process and the process to be followed in the future, so you need to save the completed and to-be-completed tasks

It should be easy for you to think that we can control the logic of the process through the field status of the table

However, a simple process can still be implemented. Once the process is more complicated, not only the code to be implemented will increase, but also the number of tables to be operated will be particularly large, and the logic will only become more complicated.

So, is there a ready-made workflow framework that can be used directly to easily implement these logics and realize automatic control of the process?

The answer is yes, today I will introduce you to a framework dedicated to managing workflow: Activiti

1 theory

1 implement the logic

Activiti uses BPMN 2.0 for process modeling and process execution management, so what is BPMN ?

BPMN (Business Process Model AndNotation) - Business Process Model and Notation is a set of standard business process modeling notations developed by BPMI (BusinessProcess Management Initiative). Business processes can be created using the notations provided by BPMN

The defined process template will eventually be stored in the database table, and its implementation logic is actually to encapsulate the operations of many process control tables, providing developers with some quick operation APIs for quick workflow management

2 Implementation steps

Using Activiti as a workflow framework is roughly divided into the following steps:

  • process definition

  • Deployment process definition

  • Start process instance

  • Query the current user's to-do tasks

  • mission accomplished

illustrate:

The process definition is equivalent to each process template , such as the pre-defined leave process and business trip process template, while the process instance is a real process initiated by each employee. Once the process instance is created, what does the process need to go through? The approval link, which (or which) person is responsible for the approval of each link, is completely determined

The relationship between a process definition and a process instance can also be understood as the relationship between a class and an object instance . One is static , and the other is actually used, relatively dynamic

3 Service

Activiti provides several Service classes to manage workflows. The following four are commonly used:

  • 1) RepositoryService: Provides functions such as process definition and deployment . For example, realize the deployment, deletion, suspension and activation of the process, and the query of the process

  • 2) RuntimeService: Provides the structure and behavior of different steps of the processing process instance . Includes functions such as starting process instances, suspending and activating process instances

  • 3) TaskService: Provides services for task-related functions . Including task query, deletion and completion functions

  • 4) HistoryService: Provides the historical record information service collected by the Activiti engine . Mainly used for the query function of historical information

    There are two more:

  • **1)**ManagementService: job task query and database operation

  • **2)**DynamicBpmnService: Process definition content can be modified without redeployment

4 Understanding the table structure

Two Actual Combat

The following enters the actual combat link, from generating 25 tables and running the first Activiti process instance, to quickly understand the simplicity and ease of use of this workflow framework

These two examples, using the Activiti7 version, need to pre-introduce the following dependencies:

<dependencies>

    <dependency>
         <groupId>org.activiti</groupId>
         <artifactId>activiti-engine</artifactId>
         <version>7.1.0.M6</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring</artifactId>
        <version>7.1.0.M6</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-model</artifactId>
        <version>7.1.0.M6</version>
    </dependency>

    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-converter</artifactId>
        <version>7.1.0.M6</version>
    </dependency>

    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-json-converter</artifactId>
        <version>7.1.0.M6</version>
    </dependency>

    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-layout</artifactId>
        <version>7.1.0.M6</version>
    </dependency>

    <dependency>
        <groupId>org.activiti.cloud</groupId>
        <artifactId>activiti-cloud-services-api</artifactId>
        <version>7.0.0.Beta1</version>
    </dependency>
    <!-- mysql 连接-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.29</version>
    </dependency>

    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>

    <!--lombok 工具 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.17.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>

复制代码

Then let's see how to generate 25 tables

(1) Generate 25 tables

1 Code writing

1) activiti.cfg.xml file configuration

We create a file activiti.cfg.xml in the resources directory, which is mainly used to configure the database information

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置数据库 dataSouce 信息-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        
        <property name="maxActive" value="3"/>
        <property name="maxIdle" value="1"/>
    </bean>


    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置模式 true : 自动创建和更新表 -->
        <property name="databaseSchemaUpdate" value="true" />
    </bean>

</beans>

复制代码

Activiti7 supports 7 databases including h2, mssql, mysql, db2, oracle hsql and postgres . We use mysql database here

Configure the url of the mysql database , database driver , user name and password and other information, and then define the object processEngineConfiguration of the type StandaloneProcessEngineConfiguration, and specify its configuration mode as databaseSchemaUpdate, which means that every time the project is started, the tables in the database will be checked. exists, create it, otherwise check for updates

2) Code writing

Through the ProcessEngines.getDefaultProcessEngine() method, realize the automatic creation of 25 tables

@Slf4j
public class TestCreat {
    /**
     * 数据库表结构的创建
     */
    @Test
    public void testCreateDbTable(){
        /**
         * 创建 processEngine 时,会自动加载 /resources 目录下的 activiti.cfg.xml 文件
         * 进行 mysql 数据库中表的创建
         */
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        log.info(processEngine.getName());
    }

复制代码

When creating processEngine , Activiti will automatically load the activiti.cfg.xml file in the /resources directory to create tables in the mysql database

If you are interested, you can click all the way to see the source code of this method.

Until the init() method of ProcessEngines (when the system judges that the table creation action has not been performed in the database), you will find that it loads the activiti.cfg.xml file in the resource directory

Of course, if it is a Spring project, the activiti-context.xml file is loaded . The integration of Spring and Activiti will not be read in this article, but will be discussed in the next article

2 running

Start the operation and find that the logs in the console are printed as follows

By executing the sql file org/activiti/db/create/activiti.mysql.create.engine.sql, the creation of 25 tables required by Activiti is completed

Check the database, you will find that 25 tables are generated in the database

(2) The first activiti process instance

Activiti has process-defined plug-ins in both eclipse and IDEA . The plug-ins that need to be installed in IDEA are as follows

After the plug-in is installed, restart IDEA , and then create a folder bpmn in the resources directory (specially used to store Activiti process files)

1 Process definition

1) Process file creation

Create our first bpmn file as shown below

Right click, New -> New Activiti 6.x BPMN 2.0 file -> fill in the file name , after creating it, it is found that the file is actually an xml file

Right-click the file, click: View BPMN (Activiti) Diagram, and you can design the process

2) Process file design

In an empty space, right click

You can see that there are process start events , activities , architecture , gateways , boundary events , emergency capture , emergency throw events , and end events , etc. Click the corresponding right triangle to create the corresponding event or activity

for example

By dragging the arrow in the upper right corner of each event , a connection line can be drawn. Here we have created a relatively simple leave process

It should be noted that the name and id of the process definition can be updated by clicking on the blank space. Here, our process definition id is leaveApplication , and the process definition name is leave process definition

Then, specify the name and person in charge of the two activities respectively . Of course, the person in charge of each work node of a process template cannot be fixed, so here we use the UEL expression supported by Activiti to define the person in charge as dynamic parameters: assignee0 and assignee1

- create application

- Approval application

Ok, so we have defined a simple process. Next, we need to deploy the process, that is to say, we need to associate the process definition file we created with the table in the database.

2 Process deployment

/**
 * 部署流程 --RepositoryService
 */
@Test
public void testDeploy(){

    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    RepositoryService repositoryService = processEngine.getRepositoryService();

    Deployment deploy = repositoryService.createDeployment()
            .name("请假申请流程定义")
            .addClasspathResource("bpmn/leaveApplication.bpmn20.xml")
            .deploy();
    log.info("流程部署id:{}",deploy.getId());
    log.info("流程部署名称:{}",deploy.getName());
}

复制代码

Because the process itself needs to be deployed, and the process itself is a static resource that needs to be stored, the API of RepositoryService is used for  deployment.

Run the test method and find that the console has inserted records into the following three tables

Right now

act_re_procdef(流程定义数据表)
act_re_deployment(部署信息表)
act_ge_bytearray(二进制数据表,存储流程定义时的资源文件信息,如这里的 bpmn/leaveApplication.bpmn20.xml 文件)

复制代码

That is to say, we store the information in this process definition file in the table about the process definition in the database.

3 Start the process instance

/**
 * 启动流程 --  RuntimeService
 *
 */
@Test
public void testStartProcess(){

    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    RuntimeService runtimeService = processEngine.getRuntimeService();
    Map<String,Object> map = new HashMap<>();
    map.put("assignee0","张三");
    map.put("assignee1","王经理");
    ProcessInstance instance = runtimeService.startProcessInstanceByKey(key,map);

    System.out.println("流程定义id:"+instance.getProcessDefinitionId());
    System.out.println("流程实例 id:"+instance.getId());
    System.out.println("当前活动的id:"+instance.getActivityId());

}

复制代码

When starting the process, designate the person in charge of each task. The above code indicates that the leave request process was initiated by Zhang San , and the approver is Manager Wang

Since starting a process means that the process instance is running, then use the startProcessInstanceByKey() method of RuntimeService, where the parameter key indicates the specified process definition to be started, and map is to assign values ​​to the process instance after it is started

To start a process instance, our expectation is to complete the Start event event, and the process enters Zhang San's "create application" step

Then the Start event task node will be executed, and the corresponding information will be stored in the history information table , and then the information of the task node " creation application " to be executed in the next step will be stored in the runtime information table . What is it like?

We run the startup method, observe the console, and find that the table being operated is as follows:

1) 历史表
  
ACT_HI_VARINST    历史变量表
ACT_HI_TASKINST   历史任务实例表
ACT_HI_PROCINST   历史流程实例表
ACT_HI_ACTINST    历史节点表
ACT_HI_IDENTITYLINK 历史人员参与表
  
2) 运行时表
  
ACT_RU_EXECUTION   运行时流程执行实例
ACT_RU_TASK        运行时任务节点表
ACT_RU_IDENTITYLINK  运行时人员参与表
ACT_RU_VARIABLE      运行时变量表

复制代码

By looking at the changes in these tables, the results are in line with the above expectations. For example, if you look at the ACT_RU_TASK table, you will find that the task node information has been stored in the table, indicating that the current task to be performed is to create an application

4 Query representative tasks

/**
 * 查询个人待执行的任务 -- TaskService
 */
@Test
public void testFindPersonalTaskList(){
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = processEngine.getTaskService();

    List<Task> taskList = taskService.createTaskQuery()
            .processDefinitionKey(key)
            .taskAssignee("张三")
            .list();
    for (Task task: taskList) {
        System.out.println("流程实例id:"+task.getProcessInstanceId());
        System.out.println("任务id:"+task.getId());
        System.out.println("任务负责人:"+task.getAssignee());
        System.out.println("任务名称:"+task.getName());

    }
}

复制代码

Query through TaskService , and query all tasks under the user name through the key and user name of the leave process respectively

5 Complete tasks

@Test
    public void completeProcess(){
        
       String assignee = "张三";

        /**
         * 完成任务
         */

        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assignee)
                .singleResult();
        
        taskService.complete(task.getId());

    }

复制代码

First query the task that needs to be completed, and then complete the task through the complete() method of TaskService (of course, in actual business, you need to perform the specific operation of the task first, such as adding your own approval comments, etc. before clicking the completion of the task , go to the next step)

When the task is completed, by checking the log, it is found that it is similar to the table operated by the task node that started the task, that is, the task completed by the current node is transferred to the historical information table, and then the task that needs to be completed in the next step is stored in the runtime information sheet

Here, after we complete the task of creating an application, the current task that needs to be performed in the table becomes "approval application"

It shows that the task has been successfully executed. Next, use the same method to complete the task of approving the application. We can guess how the data in the table will change

/**
     * 完成任务 -- TaskService
     */
    @Test
    public void completeTask(){
//        String assignee = "张三";
        String assignee = "王经理";
        /**
         * 完成任务
         */

        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assignee)
                .singleResult();

        taskService.complete(task.getId());


    }

复制代码

Yes, it is still the same operation, the task completed by the current node is transferred to the historical information table, but this is the last step of the task, so after the execution is completed, the End event will also be executed directly

Therefore, after running this step, we will find that the act_ru_task table becomes empty, and in the act_hi_taskinst table, we can see all historically executed task instances, including start events, creation applications, approval applications, and end events

Summarize

Well, at this point, we have easily completed all the operations of process definition , process definition deployment , process instance startup , and task completion for a leave request process

You will find that, in fact, the logic of Activiti is actually to perform specific task state operations through the control table. Each step of the operation needs to be controlled by updating several tables according to a certain logical relationship

And these operations are encapsulated by the Activiti framework for us. What we provide is only the API of each service , which greatly simplifies the operation of the workflow, so it is not easy to use it?

Article demo code address:

github.com/helemile/Sp…

that is it. Learn a little every day, time will witness your strength~

Guess you like

Origin blog.csdn.net/m0_48922996/article/details/125817186