当前位置: 首页 > news >正文

SpringBoot集成Flowable工作流

文章目录

  • 一、了解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在项目启动的时候会自动创建表*/<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>/*** lombok*/<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.16</version><scope>compile</scope></dependency>/*** 添加flowable工作流依赖*/<dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.7.2</version></dependency>/*** 添加flowable工作流前端页面依赖,如果项目没有集成页面,无需添加此依赖*/<dependency><groupId>org.flowable</groupId><artifactId>flowable-ui-modeler-rest</artifactId><version>6.7.2</version></dependency>/*** 添加flowable-ui-modeler配置依赖项*/<dependency><groupId>org.flowable</groupId><artifactId>flowable-ui-modeler-conf</artifactId><version>6.7.2</version></dependency>
  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=UTF8driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 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: testuser: 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
<?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="askForLeave" name="ask_for_leave_process" isExecutable="true"><startEvent id="sid-b3fcb298-124a-4dc3-b603-1397b392e1a5"/><userTask id="employee" name="员工" flowable:assignee="#{employeeNo}" flowable:formFieldValidation="true"><documentation>员工提交申请</documentation></userTask><sequenceFlow id="sid-9a4f7da0-7df8-422c-8d93-fcfe6eed6454" sourceRef="sid-b3fcb298-124a-4dc3-b603-1397b392e1a5" targetRef="employee"/><userTask id="leader" name="领导" flowable:assignee="#{leaderNo}" flowable:formFieldValidation="true"/><sequenceFlow id="sid-cda53600-1fdd-4556-b1f4-434cdef4b44c" sourceRef="employee" targetRef="leader"/><exclusiveGateway id="sid-53b678ee-c126-46be-9bb7-70efe235451c"/><sequenceFlow id="sid-c18a730f-6932-4036-b105-a840204bbd1f" sourceRef="leader" targetRef="sid-53b678ee-c126-46be-9bb7-70efe235451c"/><endEvent id="sid-dc057597-34a0-4835-bbbb-1b12f0e8e407"/><sequenceFlow id="leaderExamine" sourceRef="sid-53b678ee-c126-46be-9bb7-70efe235451c" targetRef="sid-dc057597-34a0-4835-bbbb-1b12f0e8e407" name="领导审核不通过"><conditionExpression xsi:type="tFormalExpression">${outcome=='驳回'}</conditionExpression></sequenceFlow><userTask id="boss" name="老板" flowable:formFieldValidation="true" flowable:assignee="#{bossNo}"/><sequenceFlow id="leaderExaminePass" sourceRef="sid-53b678ee-c126-46be-9bb7-70efe235451c" targetRef="boss" name="领导审核通过"><conditionExpression xsi:type="tFormalExpression">${outcome=='通过'}</conditionExpression></sequenceFlow><exclusiveGateway id="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046"/><sequenceFlow id="sid-e4dd8e02-acc7-4e51-95d4-5a1ade5eb2fd" sourceRef="boss" targetRef="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046"/><endEvent id="sid-311b83fa-5c04-48af-8491-4e2f9417c49c"/><sequenceFlow id="bossExamine" sourceRef="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046" targetRef="sid-311b83fa-5c04-48af-8491-4e2f9417c49c" name="老板审核不通过"><conditionExpression xsi:type="tFormalExpression">${outcome=='驳回'}</conditionExpression></sequenceFlow><endEvent id="sid-3f6150ad-4b8c-47e9-a726-39f5dffd5e0a"/><sequenceFlow id="bossExaminePass" sourceRef="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046" targetRef="sid-3f6150ad-4b8c-47e9-a726-39f5dffd5e0a" name="老板审核通过"><conditionExpression xsi:type="tFormalExpression">${outcome=='通过'}</conditionExpression></sequenceFlow></process><bpmndi:BPMNDiagram id="BPMNDiagram_ask_for_leave"><bpmndi:BPMNPlane bpmnElement="askForLeave" id="BPMNPlane_ask_for_leave"><bpmndi:BPMNShape id="shape-c5da47a1-57a1-465c-a7f8-2aad63d19b47" bpmnElement="sid-b3fcb298-124a-4dc3-b603-1397b392e1a5"><omgdc:Bounds x="-203.0" y="-10.75" width="30.0" height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNShape id="shape-71f351aa-e8f4-4656-9fa0-7979ddc1d916" bpmnElement="employee"><omgdc:Bounds x="-140.0" y="-10.5" width="61.0" height="29.5"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-a0cd4bce-af0a-40ce-8128-97629a5f4a23" bpmnElement="sid-9a4f7da0-7df8-422c-8d93-fcfe6eed6454"><omgdi:waypoint x="-173.0" y="4.25"/><omgdi:waypoint x="-140.0" y="4.25"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="shape-6a814a53-e6e2-4453-bb4a-43a1167e5559" bpmnElement="leader"><omgdc:Bounds x="-46.5" y="-11.0" width="63.0" height="30.5"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-3a2944ef-43d6-4409-920d-9d3d1acd422f" bpmnElement="sid-cda53600-1fdd-4556-b1f4-434cdef4b44c"><omgdi:waypoint x="-79.0" y="4.25"/><omgdi:waypoint x="-46.5" y="4.25"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="shape-bef28649-6416-4428-a3bd-43edb5c1b9c6" bpmnElement="sid-53b678ee-c126-46be-9bb7-70efe235451c"><omgdc:Bounds x="42.36" y="-15.75" width="40.0" height="40.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-fa69c266-5b3c-4e26-a4e2-3f58c271ebb4" bpmnElement="sid-c18a730f-6932-4036-b105-a840204bbd1f"><omgdi:waypoint x="16.5" y="4.25"/><omgdi:waypoint x="42.36" y="4.25"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="shape-9d577a21-ca61-4e33-bf3e-c892557f1638" bpmnElement="sid-dc057597-34a0-4835-bbbb-1b12f0e8e407"><omgdc:Bounds x="47.36" y="54.97" width="30.0" height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-5651dbf2-4118-4668-bbd6-0259282cc084" bpmnElement="leaderExamine"><omgdi:waypoint x="62.36" y="24.25"/><omgdi:waypoint x="62.36" y="54.97"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="shape-1398abfb-fefc-4a16-881c-84a6c4b7b7f7" bpmnElement="boss"><omgdc:Bounds x="108.86" y="-12.75" width="68.0" height="34.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-3f5df081-8086-492f-a2c7-90aba1c1e4d7" bpmnElement="leaderExaminePass"><omgdi:waypoint x="82.36" y="4.25"/><omgdi:waypoint x="108.86" y="4.25"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="shape-4932221b-736b-4e85-a319-0859dbeb3e6b" bpmnElement="sid-17d3919b-99f0-4a2e-8914-3dfb7bc74046"><omgdc:Bounds x="203.35999" y="-15.75" width="40.0" height="40.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-3daa1ef4-119f-4b98-a40e-0b9cb3b205ce" bpmnElement="sid-e4dd8e02-acc7-4e51-95d4-5a1ade5eb2fd"><omgdi:waypoint x="176.86" y="4.25"/><omgdi:waypoint x="203.35999" y="4.25"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="shape-6becf929-e7e7-4153-a1d1-6175677742ca" bpmnElement="sid-311b83fa-5c04-48af-8491-4e2f9417c49c"><omgdc:Bounds x="208.35999" y="54.97" width="30.0" height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-1f1a1414-f4f9-442a-97af-676878899415" bpmnElement="bossExamine"><omgdi:waypoint x="223.35999" y="24.25"/><omgdi:waypoint x="223.35999" y="54.97"/></bpmndi:BPMNEdge><bpmndi:BPMNShape id="shape-919e70dd-5004-4066-8103-427901b5c1fd" bpmnElement="sid-3f6150ad-4b8c-47e9-a726-39f5dffd5e0a"><omgdc:Bounds x="275.86" y="-10.75" width="30.0" height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-542631d7-42d7-4b2a-b467-da8fdba298a1" bpmnElement="bossExaminePass"><omgdi:waypoint x="243.35999" y="4.25"/><omgdi:waypoint x="275.86" y="4.25"/></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</definitions>
  1. 设置配置文件
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {/*** 防止生成的流程图中文乱码* @param springProcessEngineConfiguration*/@Overridepublic 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的登录验证*/
@Configuration
public class SecurityConfiguration {@Configuration(proxyBeanMethods = false)//Order配置说明// 这个地方相同会报错//这个地方如果大于则该配置在FlowableUiSecurityAutoConfiguratio中对应项后加载,不能起到绕过授权作用//所以这个地方-1让该配置项在FlowableUiSecurityAutoConfiguratio中对应配置项前加载,以跳过授权@Order(SecurityConstants.FORM_LOGIN_SECURITY_ORDER - 1)public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {@Overrideprotected 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 {@Autowiredprivate RuntimeService runtimeService;@Autowiredprivate TaskService taskService;@Autowiredprivate RepositoryService repositoryService;@Autowiredprivate 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<String, Object> 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<Task> 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<String, Object> 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<Task> 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<String, Object> 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<Task> 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<String, Object> 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<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(InstanceId).list();//得到正在执行的Activity的IdList<String> activityIds = new ArrayList<>();List<String> flows = new ArrayList<>();for (Execution exe : executions) {List<String> 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的登录验证*/
@Configuration
public class SecurityConfiguration {@Configuration(proxyBeanMethods = false)//Order配置说明// 这个地方相同会报错//这个地方如果大于则该配置在FlowableUiSecurityAutoConfiguratio中对应项后加载,不能起到绕过授权作用//所以这个地方-1让该配置项在FlowableUiSecurityAutoConfiguratio中对应配置项前加载,以跳过授权@Order(SecurityConstants.FORM_LOGIN_SECURITY_ORDER - 1)public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {@Overrideprotected 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. 可以书写针对流程的逻辑了

相关文章:

SpringBoot集成Flowable工作流

文章目录 一、了解Flowable1. 什么是Flowable2. Flowable基本流程3. Flowable主要几张表介绍 二、SpringBoot集成Flowable1. 在idea中安装Flowable插件2. SpringBoot集成Flowable3. SpringBoot集成Flowable前端页面 三、创建流程模版(以请假为例) 提示&#xff1a;以下是本篇文…...

try-with-resources 语法详解

目录 一、介绍 二、用法对比 三、优势 四、原理分析 一、介绍 在Java 7中&#xff0c;引入了一项重要的语法糖——try-with-resources&#xff0c;这项特性的目的是为了更有效地处理资源的管理。资源指的是需要在代码执行完毕后手动关闭的对象&#xff0c;比如文件流、网络…...

【Java程序设计】【C00207】基于(JavaWeb+SSM)的宠物领养管理系统(论文+PPT)

基于&#xff08;JavaWebSSM&#xff09;的宠物领养管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的宠物领养系统 本系统分为前台系统、管理员、收养者和寄养者4个功能模块。 前台系统&#xff1a;游客打开系统…...

2024-2-4-复习作业

源代码&#xff1a; #include <stdio.h> #include <stdlib.h> typedef int datatype; typedef struct Node {datatype data;struct Node *next;struct Node *prev; }*DoubleLinkList;DoubleLinkList create() {DoubleLinkList s(DoubleLinkList)malloc(sizeof(st…...

【Linux】解决:为什么重复创建同一个【进程pid会变化,而ppid父进程id不变?】

前言 大家好吖&#xff0c;欢迎来到 YY 滴Linux 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…...

【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的API网关设计实现

盘点本年度探索对外服务的百万请求量的API网关设计实现 背景介绍高性能API网关API网关架构优化多级缓存架构设计多级缓存富客户端漏斗模型数据读取架构 异步刷新过期缓存网关异步化调用模型高性能批量API调用&#xff08;减少对于网关的交互和通信&#xff09;并行调用和请求合…...

Python算法题集_环形链表

Python算法题集_环形链表 题234&#xff1a;环形链表1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【集合检索】2) 改进版一【字典检测】3) 改进版二【双指针】 4. 最优算法 本文为Python算法题集之一的代码示例 题234&#xff1a;环形链表 …...

【51单片机】开发板&开发软件(Keil5&STC-ISP)简介&下载安装破译传送门(1)

前言 大家好吖&#xff0c;欢迎来到 YY 滴单片机系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过单片机的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…...

#vu3# element plus表格的序号字段

在表格中添加序号字段&#xff0c;可以使用以下几种方式来实现 1. 利用索引 在<el-table>组件的<el-table-column>中使用插槽来显示序号。示例&#xff1a; <el-table :data"tableData"><el-table-column label"序号" type"i…...

华为配置OSPF与BFD联动示例

配置OSPF与BFD联动示例 组网图形 图1 配置OSPF与BFD联动组网图 OSPF与BFD联动简介配置注意事项组网需求配置思路操作步骤配置文件 OSPF与BFD联动简介 双向转发检测BFD&#xff08;Bidirectional Forwarding Detection&#xff09;是一种用于检测转发引擎之间通信故障的检测…...

Git 常用命令详解及如何在IDEA中操作

文章目录 前言发现宝藏一、初识Git1.Git概述2. Git的功能3. Git运行图示 二、Git下载安装三、Git 代码托管服务1.常用的 Git 代码托管服务2.使用码云代码托管服务 四、Git 常用命令1.Git 全局设置2.获取Git 仓库3.工作区、暂存区、版本库 概念4.Git 工作区中文件的两种状态5.本…...

linux+rv1126/imx6ull:opencv静态库交叉编译(手把手百分百成功)

目录 1.下载 2.准备工作 2.1安装依赖环境 2.2安装Cmake 2.3 解压opencv 3.Cmake设置...

Python使用回调函数或async/await关键字、协程实现异步编程

异步编程是一种编程模式,它允许程序在执行某个任务时,能够同时执行其他任务而不需要等待当前任务完成。在传统的同步编程中,程序执行一个任务后必须等待该任务完成后才能继续执行下一个任务。而在异步编程中,程序可以发起一个任务后立即执行其他任务,当原先的任务完成后,…...

异地办公必不可缺的远程控制软件,原理到底是什么?

目录 引言远程桌面连接软件的作用与重要性 基本概念与架构客户端-服务器模型网络通信协议 核心技术组件图形界面捕获与传输输入转发会话管理 性能优化策略带宽优化延迟优化 引言 远程桌面连接软件的作用与重要性 在当今这个高度数字化和网络化的时代&#xff0c;远程桌面连接软…...

docker更换镜像源

添加的镜像源 {"registry-mirrors": ["https://registry.cn-hangzhou.aliyuncs.com", "https://reg-mirror.qiniu.com/", "https://docker.mirrors.ustc.edu.cn"] }docker更换镜像源之后一定要重启守卫 systemctl daemon-reloaddock…...

SaaS 电商设计 (八) 直接就能用的一套商品池完整的设计方案(建议收藏)

目录 一.前言1.1 在哪些业务场景里使用1.2 一些名词搞懂他1.3 结合业务思考一下-业务or产品的意图 二.方案设计2.1 业务主流程2.2 一步步带你分析B端如何配置2.3 数据流2.3.1 ES 数据表建设2.3.2 核心商品池流程2.3.2.1 商品池B端维护流程2.3.2.2 商品池版本更新逻辑 2.4 核心代…...

【Spring连载】使用Spring Data访问Redis(八)----发布/订阅消息

【Spring连载】使用Spring Data访问Redis&#xff08;八&#xff09;----发布/订阅消息Pub/Sub Messaging 一、发布消息Publishing (Sending Messages)二、订阅消息Subscribing (Receiving Messages)2.1 消息监听容器Message Listener Containers2.2 消息监听适配器The Message…...

list基本使用

list基本使用 构造迭代器容量访问修改 list容器底层是带头双向链表结构&#xff0c;可以在常数范围内在任意位置进行输入和删除&#xff0c;但不支持任意位置的随机访问&#xff08;如不支持[ ]下标访问&#xff09;&#xff0c;下面介绍list容器的基本使用接口。 template <…...

网络原理TCP/IP(5)

文章目录 IP协议IP协议报头地址管理网段划分特殊的IP地址路由选择以太网认识MAC地址对比理解MAC地址和IP地址DNS&#xff08;域名服务器&#xff09; IP协议 IP协议主要完成的工作是两方面&#xff1a; 地址管理&#xff0c;使用一套地址体系&#xff0c;来描述互联网上每个设…...

前端JavaScript篇之JavaScript为什么要进行变量提升,它导致了什么问题?什么是尾调用,使用尾调用有什么好处?

目录 JavaScript为什么要进行变量提升&#xff0c;它导致了什么问题&#xff1f;总结 什么是尾调用&#xff0c;使用尾调用有什么好处&#xff1f;总结 JavaScript为什么要进行变量提升&#xff0c;它导致了什么问题&#xff1f; 变量提升是JavaScript在代码执行之前对变量和函…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...