小工具知识网

当前位置: 首页 > 行业知识 > SpringBoot集成Flowable工作流

行业知识

SpringBoot集成Flowable工作流

2026-10-31 20:16:57 来源:互联网转载

文章目录

  • 一、了解Flowable
    • 1. 什么是Flowable
    • 2. Flowable基本流程
    • 3. Flowable主要几张表介绍
    • 二、SpringBoot集成Flowable
      • 1. 在idea中安装Flowable插件
      • 2. SpringBoot集成Flowable
      • 3. SpringBoot集成Flowable前端页面
      • 三、创建流程模版(以请假为例)

        提示:以下是本篇文章正文内容,下面案例可供参考

        一、了解Flowable

        1. 什么是Flowable

        Flowable下载

        Flowable源码GitHub下载

        Flowable用户使用手册

        Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据,等等。

        具体介绍请看Flowable介绍

        2. Flowable基本流程

        参考知乎Flowable工作流引擎

        对于业务建模,我们需要一种通用的语言来描绘,这样在沟通上和实现上会降低难度,就像中文、英文一样,BPMN2.0便是一种国际通用的建模语言,他能让自然人轻松阅读,更能被计算机所解析。

        协议中元素的主要分类为,事件-任务-连线-网关

        一个流程必须包含一个事件(如:开始事件)和至少一个结束(事件)。其中网关的作用是流程流转逻辑的控制。任务则分很多类型,他们各司其职,所有节点均由连线联系起来。

        BPMN2.0协议

        不同的图标代表不同的含义

        1. 网关

        互斥网关(Exclusive Gateway) 又称排他网关,他有且仅有一个有效出口,可以理解为if…else if… else if…else,就和我们平时写代码的一样。

        并行网关(Parallel Gateway) 他的所有出口都会被执行,可以理解为开多线程同时执行多个任务。

        包容性网关(Inclusive Gateway)

        只要满足条件的出口都会执行,可以理解为 if(…) do, if (…) do, if (…)

        do,所有的条件判断都是同级别的。

        1. 任务

        人工任务(User Task)

        它是使用得做多的一种任务类型,他自带有一些人工任务的变量,例如签收人(Assignee),签收人就代表该任务交由谁处理,我们也可以通过某个特定或一系列特定的签收人来查找待办任务。利用上面的行为解释便是,当到达User

        Task节点的时候,节点设置Assignee变量或等待设置Assignee变量,当任务被完成的时候,我们使用Trigger来要求流程引擎退出该任务,继续流转。

        服务任务(Service Task)

        该任务会在到达的时候执行一段自动的逻辑并自动流转。从“到达自动执行一段逻辑”这里我们就可以发现,服务任务的想象空间就可以非常大,我们可以执行一段计算,执行发送邮件,执行RPC调用,而使用最广泛的则为HTTP调用,因为HTTP是使用最广泛的协议之一,它可以解决大部分第三方调用问题,在我们的使用中,HTTP服务任务也被我们单独剥离出来作为一个特殊任务节点。

        接受任务(Receive Task) 该任务的名字让人费解,但它又是最简单的一种任务,当该任务到达的时候,它不做任何逻辑,而是被动地等待Trigger,它的适用场景往往是一些不明确的阻塞,比如:一个复杂的计算需要等待很多条件,这些条件是需要人为来判断是否可以执行,而不是直接执行,这个时候,工作人员如果判断可以继续了,那么就Trigger一下使其流转。

        1. 结构

        调用活动(Call Activity)

        调用活动可以理解为函数调用,它会引用另外一个流程使之作为子流程运行,调用活动跟函数调用的功能一样,使流程模块化,增加复用的可能性。

        3. Flowable主要几张表介绍

        Flowable在项目启动的时候会自动创建表,以下是主要几张表介绍

        • ACT_RU_TASK: 每次启动的流程都会再这张表中,表示代办项, 流程结束会删除该流程数据
        • ACT_RU_EXECUTION: 流程执行过程表, 会存该流程正在执行的过程数据, 流程结束会删除该流程数据
        • ACT_RU_VARIABLE: 流程变量表, 流程中传的参数都会再该表存储, 流程结束会删除该流程数据
        • ACT_HI_PROCINST: 历史运行流程, 当流程处理完了, 在ACT_RU_* 表中就不会有数据, 可以在该表中查询历史
        • ACT_HI_TASKINST: 历史运行的task信息,
        • ACT_RE_PROCDEF: 流程模板记录,同一个key多次发布version_字段会递增
        • ACT_RE_DEPLOYMENT: 部署的流程模板, 可以启动流程使用的

          二、SpringBoot集成Flowable

          1. 在idea中安装Flowable插件

          File-->settings 搜索:flowable

          安装完成,重启idea

          2. SpringBoot集成Flowable

          1. 添加依赖
               /**       * 引入数据库依赖,Flowable在项目启动的时候会自动创建表      */                    org.springframework.boot            spring-boot-starter-jdbc                            mysql            mysql-connector-java            runtime                 /**       * lombok      */                     org.projectlombok            lombok            1.16.16            compile                 /**       * 添加flowable工作流依赖      */                    org.flowable            flowable-spring-boot-starter            6.7.2                 /**       * 添加flowable工作流前端页面依赖,如果项目没有集成页面,无需添加此依赖      */                    org.flowable            flowable-ui-modeler-rest            6.7.2                 /**       * 添加flowable-ui-modeler配置依赖项      */                    org.flowable            flowable-ui-modeler-conf            6.7.2        
          1. 设置application.yml
          server:  port: 8080  #tomcat端口  servlet:    context-path: /spring:  #数据库链接配置  datasource:    url: jdbc:mysql://127.0.0.1:3306/flowable?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF8    driver-class-name: com.mysql.cj.jdbc.Driver    username: root    password: rootflowable:  #异步执行  async-executor-activate: true  #自动更新数据库  database-schema-update: true  #校验流程文件,默认校验resources下的processes文件夹里的流程文件  process-definition-location-prefix: classpath*:/processes/  process-definition-location-suffixes: "**.bpmn20.xml, **.bpmn"    #该配置只是防止页面报错,没有实际意义  common:    app:      idm-admin:        password: test        user: test      #没有实际意义      idm-url: http://localhost:8080/flowable-demo
          1. 创建 【*.bpmn20.xml】

            通过【application.yml】可知,需要在resources下的processes文件夹里的创建流程文件

            这是ask_for_leave.bpmn20.xml我自己的流程文件

            ask_for_leave.bpmn20.xml

                          员工提交申请                                      ${outcome=='驳回'}                  ${outcome=='通过'}                          ${outcome=='驳回'}                  ${outcome=='通过'}                                                                                                                                                                                                                                                                                                                                                                                                                                      
          1. 设置配置文件
          import org.flowable.spring.SpringProcessEngineConfiguration;import org.flowable.spring.boot.EngineConfigurationConfigurer;import org.springframework.context.annotation.Configuration;@Configurationpublic class FlowableConfig implements EngineConfigurationConfigurer {    /**     * 防止生成的流程图中文乱码     * @param springProcessEngineConfiguration     */    @Override    public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {        springProcessEngineConfiguration.setActivityFontName("宋体");        springProcessEngineConfiguration.setLabelFontName("宋体");        springProcessEngineConfiguration.setAnnotationFontName("宋体");    }}

          如果醒目没有集成页面,【SecurityConfiguration.java】无需配置

          参考简书springboot集成flowable

          import org.flowable.ui.common.security.SecurityConstants;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.Order;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;/** * 绕过flowable的登录验证 */@Configurationpublic class SecurityConfiguration {    @Configuration(proxyBeanMethods = false)    //Order配置说明    // 这个地方相同会报错    //这个地方如果大于则该配置在FlowableUiSecurityAutoConfiguratio中对应项后加载,不能起到绕过授权作用    //所以这个地方-1让该配置项在FlowableUiSecurityAutoConfiguratio中对应配置项前加载,以跳过授权    @Order(SecurityConstants.FORM_LOGIN_SECURITY_ORDER - 1)    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {        @Override        protected void configure(HttpSecurity http) throws Exception {            http.headers().frameOptions().disable();            http                    //必须要将csrf设置为disable,不然后面发送POST请求时会报403错误                    .csrf().disable()                    //为了简单起见,简单粗暴方式直接放行modeler下面所有请求                    .authorizeRequests().antMatchers("/modeler/**").permitAll();        }    }}
          1. 书写逻辑
          import lombok.extern.slf4j.Slf4j;import org.flowable.bpmn.model.BpmnModel;import org.flowable.engine.*;import org.flowable.engine.runtime.Execution;import org.flowable.engine.runtime.ProcessInstance;import org.flowable.image.ProcessDiagramGenerator;import org.flowable.task.api.Task;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;import java.io.InputStream;import java.io.OutputStream;import java.util.*;@Slf4j@RestController@RequestMapping("askForLeave")public class AskForLeaveFlowableController {    @Autowired    private RuntimeService runtimeService;    @Autowired    private TaskService taskService;    @Autowired    private RepositoryService repositoryService;    @Autowired    private ProcessEngine processEngine;    /**     * 员工提交请假申请     *     * @param employeeNo 员工工号     * @param name       姓名     * @param reason     原因     * @param days       天数     * @return     */    @GetMapping("employeeSubmit")    public String employeeSubmitAskForLeave(            @RequestParam(value = "employeeNo") String employeeNo,            @RequestParam(value = "name") String name,            @RequestParam(value = "reason") String reason,            @RequestParam(value = "days") Integer days) {        HashMap map = new HashMap();        /**         * 员工编号字段来自于配置文件         */        map.put("employeeNo", employeeNo);        map.put("name", name);        map.put("reason", reason);        map.put("days", days);        /**         *      key:配置文件中的下个处理流程id         *      value:默认领导工号为002         */        map.put("leaderNo", "002");        /**         * askForLeave:为开启流程的id  与配置文件中的一致         */        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("askForLeave", map);        log.info("{},提交请假申请,流程id:{}", name, processInstance.getId());        return "提交成功,流程id:"+processInstance.getId();    }    /**     * 领导审核通过     * @param employeeNo  员工工号     * @return     */    @GetMapping("leaderExaminePass")    public String leaderExamine(@RequestParam(value = "employeeNo") String employeeNo) {        List taskList = taskService.createTaskQuery().taskAssignee(employeeNo).orderByTaskId().desc().list();        if (null == taskList) {            throw  new RuntimeException("当前员工没有任何申请");        }        for (Task task : taskList) {            if (task == null) {                log.info("任务不存在 ID:{};", task.getId());                continue;            }            log.info("任务 ID:{};任务处理人:{};任务是否挂起:{}", task.getId(), task.getAssignee(), task.isSuspended());            Map map = new HashMap();            /**             *      key:配置文件中的下个处理流程id             *      value:默认老板工号为001             */            map.put("bossNo", "001");            /**             *      key:指定配置文件中的条件判断id             *      value:指定配置文件中的审核条件             */            map.put("outcome", "通过");            taskService.complete(task.getId(), map);        }        return "领导审核通过";    }    /**     * 老板审核通过     * @param leaderNo  领导工号     * @return     */    @GetMapping("bossExaminePass")    public String bossExamine(@RequestParam(value = "leaderNo") String leaderNo) {        List taskList = taskService.createTaskQuery().taskAssignee(leaderNo).orderByTaskId().desc().list();        if (null == taskList) {            throw  new RuntimeException("当前员工没有任何申请");        }        for (Task task : taskList) {            if (task == null) {                log.info("任务不存在 ID:{};", task.getId());                continue;            }            log.info("任务 ID:{};任务处理人:{};任务是否挂起:{}", task.getId(), task.getAssignee(), task.isSuspended());            Map map = new HashMap();            /**             *     老板是最后的审批人   无需指定下个流程             *///            map.put("boss", "001");            /**             *      key:指定配置文件中的条件判断id             *      value:指定配置文件中的审核条件             */            map.put("outcome", "通过");            taskService.complete(task.getId(), map);        }        return "领导审核通过";    }    /**     * 驳回     *     * @param employeeNo  员工工号     * @return     */    @GetMapping("reject")    public String reject(@RequestParam(value = "employeeNo") String employeeNo) {        List taskList = taskService.createTaskQuery().taskAssignee(employeeNo).orderByTaskId().desc().list();        if (null == taskList) {            throw  new RuntimeException("当前员工没有任何申请");        }        for (Task task : taskList) {            if (task == null) {                log.info("任务不存在 ID:{};", task.getId());                continue;            }            log.info("任务 ID:{};任务处理人:{};任务是否挂起:{}", task.getId(), task.getAssignee(), task.isSuspended());            Map map = new HashMap();            /**             *      key:指定配置文件中的领导id             *      value:指定配置文件中的审核条件             */            map.put("outcome", "驳回");            taskService.complete(task.getId(), map);        }        return "申请被驳回";    }    /**     * 生成流程图     *     * @param processId 任务ID     */    @GetMapping(value = "processDiagram")    public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();        //流程走完的不显示图        if (pi == null) {            return;        }        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象        String InstanceId = task.getProcessInstanceId();        List executions = runtimeService                .createExecutionQuery()                .processInstanceId(InstanceId)                .list();        //得到正在执行的Activity的Id        List activityIds = new ArrayList();        List flows = new ArrayList();        for (Execution exe : executions) {            List ids = runtimeService.getActiveActivityIds(exe.getId());            activityIds.addAll(ids);        }        //获取流程图        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows,                engconf.getActivityFontName(), engconf.getLabelFontName(),                engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);        OutputStream out = null;        byte[] buf = new byte[1024];        int legth = 0;        try {            out = httpServletResponse.getOutputStream();            while ((legth = in.read(buf)) != -1) {                out.write(buf, 0, legth);            }        } finally {            if (in != null) {                in.close();            }            if (out != null) {                out.close();            }        }    }}
          1. 测试
          • 员工提交请假申请,如下图:

            http://localhost:8080/askForLeave/employeeSubmit?name=Jerry&reason=去旅游&days=30&employeeNo=006

          • 查看员工提交申请的流程图

            http://localhost:8080/askForLeave/processDiagram?processId=2cd67ca3-c32e-11ee-a344-d41b812ff10e

          • 领导进行审批

            http://localhost:8080/askForLeave/leaderExaminePass?employeeNo=006

          • 查看领导审批后的流程图

            http://localhost:8080/askForLeave/processDiagram?processId=2cd67ca3-c32e-11ee-a344-d41b812ff10e

          • 老板审批

            http://localhost:8080/askForLeave/bossExaminePass?leaderNo=002

          • 查看老板审批后的流程图

            http://localhost:8080/askForLeave/processDiagram?processId=2cd67ca3-c32e-11ee-a344-d41b812ff10e

            3. SpringBoot集成Flowable前端页面

            参考简书毛于晏

            1. 下载源码包,此处使用的是6.7.2版本的

              源码下载地址: https://github.com/flowable/flowable-engine/tree/flowable-6.7.2

              或

              链接:https://pan.baidu.com/s/1w-iuynskMuREBlYcpbDFAg

              提取码:9gs7

            2. 将以下路径的代码复制到自己工程

              /flowable-6.7.2/modules/flowable-ui/flowable-ui-modeler-frontend/src/main/resources/static/modeler

            3. 跳过验证

            import org.flowable.ui.common.security.SecurityConstants;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.Order;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;/** * 绕过flowable的登录验证 */@Configurationpublic class SecurityConfiguration {    @Configuration(proxyBeanMethods = false)    //Order配置说明    // 这个地方相同会报错    //这个地方如果大于则该配置在FlowableUiSecurityAutoConfiguratio中对应项后加载,不能起到绕过授权作用    //所以这个地方-1让该配置项在FlowableUiSecurityAutoConfiguratio中对应配置项前加载,以跳过授权    @Order(SecurityConstants.FORM_LOGIN_SECURITY_ORDER - 1)    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {        @Override        protected void configure(HttpSecurity http) throws Exception {            http.headers().frameOptions().disable();            http                    //必须要将csrf设置为disable,不然后面发送POST请求时会报403错误                    .csrf().disable()                    //为了简单起见,简单粗暴方式直接放行modeler下面所有请求                    .authorizeRequests().antMatchers("/modeler/**").permitAll();        }    }}
            1. 启动项目,访问:http://localhost:8080/modeler/#/processes

            三、创建流程模版(以请假为例)

            1. 确保已在idea中安装Flowable插件

            2. 在resources下创建processes文件夹,右键,创建一个【*.bpmn20.xml】文件

            3. 选中创建好的【*.bpmn20.xml】文件,右键;

            4. 开始画流程图 任意位置右键–>启动一个流程

            5. 设置员工提交申请

            6. 设置领导

            7. 设置网关,用来判断领导是否同意

            8. 网关条件判断条件–驳回

            9. 网关条件判断条件–通过

            10. 设置老板

            11. 设置网关,用来判断老板是否同意

            12. 网关条件判断条件–驳回

            13. 网关条件判断条件–通过

            14. 当我们画好图后,【*.bpmn20.xml】流程文件也已经创建成功了

            15. 可以书写针对流程的逻辑了

上一篇:多个算式怎么列式计算?

下一篇:独家揭秘赌场豪杰任务攻略大解密,轻松掌握入口与玩法,成为赌场霸主!