工作流:一文让你学会使用flowable工作流

  • Post author:
  • Post category:其他




1.请假流程图

下图是 一个请假申请的简单流程图

(1)申请人通过发起流程进行请假申请,给经理发送一个待审批事项;

(2)经理在待办列表选择事项,进行审批,approved同意或者rejected驳回操作,并触发不同的事件;

(3)如果经理approved,则触发Enter holidays in external system事件,并给流程发起人发送一个待办事项;

(4)如果经理rejected,则触发Send out rejection email事件,给申请人发送一个请假不批的邮件,流程结束;

(5)如果是(3),申请人需要处理待办任务进行休假,流程结束。

在这里插入图片描述



2.工作流文件

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" flowable:candidateGroups="managers"/>
        <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="com.example.flowable.holiday.CallExternalSystemDelegate"/>
        <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>

        <userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/>
        <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>

        <serviceTask id="sendRejectionMail" name="Send out rejection email" flowable:class="com.example.flowable.holiday.SendRejectionMail"/>
        <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>

        <endEvent id="approveEnd"/>

        <endEvent id="rejectEnd"/>

    </process>

</definitions>



3.pom文件

依赖的jar包如下:

<dependency>
	<groupId>org.flowable</groupId>
	<artifactId>flowable-engine</artifactId>
	<version>6.4.2</version>
</dependency>
<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
	<scope>runtime</scope>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>
<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>



2.源码

(1)HolidayRequest类为测试主类


package com.example.flowable.holiday;

import org.flowable.engine.*;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;

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

/**
 1. 此类描述的是:测试demo
 2. 参考flowable手册https://tkjohn.github.io/flowable-userguide/
 3. @author juge
 4. @version 2021/9/9 9:54
*/
public class HolidayRequest {
    public static void main(String[] args) {
        //1.创建一个独立(standalone)配置
        ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
                /*.setJdbcUrl("jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1")
                .setJdbcUsername("sa")
                .setJdbcPassword("")
                .setJdbcDriver("org.h2.Driver")*/
                .setJdbcUrl("jdbc:mysql://127.0.0.1:3306/flowable?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true")
                .setJdbcUsername("root")
                .setJdbcPassword("123456")
                .setJdbcDriver("com.mysql.jdbc.Driver")
                //如果数据表不存在的时候,自动创建数据表
                .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        //2.创建流程引擎
        ProcessEngine processEngine = cfg.buildProcessEngine();
        // 使用BPMN 2.0定义process。存储为XML,同时也是可以可视化的。NPMN 2.0标准可以让技术人员与业务人员都参与讨论业务流程中来

        //3.利用流程引擎部署流程
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("holiday-request.bpmn20.xml")
                .deploy();
        //4.根据流程部署实例id获取流程定义实例
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();
        System.out.println("Found process definition : " + processDefinition.getName());

        //5.启动process实例
        //5.1需要一些初始化的变量,这里我们简单的从Scanner中获取,一般在线上会通过接口传递过来(发起流程的表单)
        //5.1.1 scanner输入类似于web前端表单输入
        Scanner scanner= new Scanner(System.in);

        System.out.println("Who are you?");
        String employee = scanner.nextLine();

        System.out.println("How many holidays do you want to request?");
        Integer nrOfHolidays = Integer.valueOf(scanner.nextLine());

        System.out.println("Why do you need them?");
        String description = scanner.nextLine();
        //5.1.2 此处代码类似web后端获取前端表单传来的字段
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("employee", employee);
        variables.put("nrOfHolidays", nrOfHolidays);
        variables.put("description", description);

        //6.发起流程
        RuntimeService runtimeService = processEngine.getRuntimeService();
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayRequest", variables);

        //7.经理查询待办任务
        TaskService taskService = processEngine.getTaskService();
        /*将第一个任务指派给"经理(managers)"组,而第二个用户任务指派给请假申请的提交人。因此需要为第一个任务添加candidateGroups属性:
            <userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>
          这里的candidateGroups,中文一般成为候选组,实际是一组用户的带号,在实际使用中可以写用户的角色id,或者组织机构(岗位、职位)id等
          当用户查看待办列表时,后台根据用户的id查询角色id,然后用角色id替换managers即可查询到对应的任务列表
         */
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
        //8.经理处理审批任务
        //实际一般为用户在web前端页面的待办任务列表,此处演示为控制台输入输出人机交互
        System.out.println("You have " + tasks.size() + " tasks:");
        for (int i=0; i<tasks.size(); i++) {
            System.out.println((i+1) + ") " + tasks.get(i).getName());
        }
        System.out.println("Which task would you like to complete?");
        //8.1 选择要处理的任务
        //此处一般为web前端页面,点击“同意”或“驳回”的按钮,同意为"y",驳回为"n"
        int taskIndex = Integer.valueOf(scanner.nextLine());
        Task task = tasks.get(taskIndex - 1);
        //使用任务Id获取特定流程实例的变量
        Map<String, Object> processVariables = taskService.getVariables(task.getId());
        System.out.println(processVariables.get("employee") + " wants " +
                processVariables.get("nrOfHolidays") + " of holidays. Do you approve this?");
        //8.2 编写任务处理意见,并进行approved or rejected的操作
        //此处一般为web前端页面,点击“同意”或“驳回”的按钮,同意为"y",驳回为"n"
        boolean approved = scanner.nextLine().toLowerCase().equals("y");
        //8.3 完成任务
        //此处代码一般写在web后端,接收到前端的同意或驳回的参数,完成任务
        variables = new HashMap<String, Object>();
        variables.put("approved", approved);
        //注意先保存意见,再完成任务,否则任务完成后找不到
        if (approved){
            Comment comment = taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), "同意休假");
            comment.setUserId("manager");
            taskService.saveComment(comment);
        }else{
            Comment comment = taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), "不允许休假");
            comment.setUserId("manager");
            taskService.saveComment(comment);
        }
        taskService.complete(task.getId(), variables);

        //9.申请人查询待办事务
        /*并如下所示为第二个任务添加assignee属性。请注意我们没有像上面的’managers’一样使用静态值,而是使用一个流程变量动态指派。这个流程变量是在流程实例启动时传递的:
            <userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/>
         */
        //注意发起流程时的employee,最好为申请人或者审批人的id或其他唯一字段
        tasks = taskService.createTaskQuery().taskAssignee(employee).list();
        //10.申请人处理休假任务
        System.out.println("You have " + tasks.size() + " tasks:");
        for (int i=0; i<tasks.size(); i++) {
            System.out.println((i+1) + ") " + tasks.get(i).getName());
        }
        if (tasks.size() > 0){
            System.out.println("Which task would you like to complete?");
            //10.1 选择要处理的任务
            taskIndex = Integer.valueOf(scanner.nextLine());
            task = tasks.get(taskIndex - 1);
            //使用任务Id获取特定流程实例的变量
            processVariables = taskService.getVariables(task.getId());
            System.out.println("Hi " + processVariables.get("employee") + "please use your holidays !");
            //10.2 完成任务
            variables = new HashMap<String, Object>();
            Comment comment1 = taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), processVariables.get("employee") + "休假中");
            comment1.setUserId("employe");
            taskService.saveComment(comment1);
            taskService.complete(task.getId(), variables);
        }
        //展示审批意见
        List<Comment> taskComments = taskService.getProcessInstanceComments(processInstance.getProcessInstanceId());
        for (Comment comment:taskComments){
            System.out.println("审批人:"+comment.getUserId()
             + ",审批意见:" + comment.getFullMessage() + ",审批时间:" + comment.getTime());
        }

        //展示流程流转的历史记录
        /*HistoryService historyService = processEngine.getHistoryService();
        List<HistoricActivityInstance> activities =
                historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(processInstance.getId())
                        .finished()
                        .orderByHistoricActivityInstanceEndTime().asc()
                        .list();

        for (HistoricActivityInstance activity : activities) {
            System.out.println(activity.getActivityId() + " took "
                    + activity.getDurationInMillis() + " milliseconds");
        }*/
    }
}

(2)CallExternalSystemDelegate类为Enter holidays in external system事件的执行类

package com.example.flowable.holiday;

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

public class CallExternalSystemDelegate implements JavaDelegate {
    @Override
    public void execute(DelegateExecution delegateExecution) {
        System.out.println("Calling the external system for employee "
                + delegateExecution.getVariable("employee"));
    }
}

(3)SendRejectionMail类为Send out rejection email事件的执行类

ackage com.example.flowable.holiday;

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

public class SendRejectionMail implements JavaDelegate {

    @Override
    public void execute(DelegateExecution delegateExecution) {
        System.out.println("send e-mail to employee "
                + delegateExecution.getVariable("employee"));
    }
}

源码下载地址:

https://download.csdn.net/download/juligang320/22333758?spm=1001.2014.3001.5501



3.测试

启动HolidayRequest类进行测试,控制台日志如下:

Found process definition : Holiday Request
Who are you?
john
How many holidays do you want to request?
2
Why do you need them?
go to ...

You have 3 tasks:
1) Approve or reject request
2) Approve or reject request
3) Approve or reject request
Which task would you like to complete?
3

john wants 2 of holidays. Do you approve this?
y

Calling the external system for employee john

You have 1 tasks:
1) Holiday approved
Which task would you like to complete?
1

Hi johnplease use your holidays !

审批人:employe,审批意见:john休假中,审批时间:Thu Sep 09 15:13:51 CST 2021
审批人:manager,审批意见:同意休假,审批时间:Thu Sep 09 15:13:47 CST 2021



4 说明

一般工作流使用的是web方式,web前端页面输入,后端进行逻辑处理并输出到前端页面展示。HolidayRequest类中的demo,输入采用scanner,控制台代替web前端页面。

以上列子主要参考一下文档:

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

欢迎交流!



版权声明:本文为juligang320原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。