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

SpringBoot集成Flowable

一、工作流介绍

1、概念
通过计算机对业务流程的自动化管理。工作流是建立在业务流程的基础上,一个软件的系统核心根本上还是系统的业务流程,工作流只是协助进行业务流程管理。

解决的是:在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标

2、工作流系统
概念:具有工作流功能的系统
比如,OA、ERP系统,可能涉及工作流,都可以叫工作流系统

3、具体应用
关键业务流程:订单、报价处理、合同审核、客户电话处理、供应链管理等
行政管理类:出差申请、加班申请、请假申请、用车申请、各种办公用品申请、购买申请、日报周报等凡是原来手工流转处理的行政表单。
人事管理类:员工培训安排、绩效考评、职位变动处理、员工档案信息管理等。
财务相关类:付款请求、应收款处理、日常报销处理、出差报销、预算和计划申请等。
客户服务类:客户信息管理、客户投诉、请求处理、售后服务管理等。
特殊服务类:SO系列对应流程、质量管理对应流程、产品数据信息管理、贸易公司报关处理、物流公司货物跟踪处理等各种通过表单逐步手工流转完成的任务均可应用工作流软件自动规范地实施。
4、实现方式对比
原始方式:就是采用状态值来跟踪流程的变化,通过这个值去决定不同用户是否展示,耦合度高
新的工作流引擎,可以灵活调整,实现简单

二、Flowable概述

具体参考官网:https://tkjohn.github.io/flowable-userguide/#_introduction

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

2、BPM
业务流程管理,一种规范化的构造端到端的业务流程,提高组织业务效率。

3、BPMN
业务流程模型和符号,由BPMI 开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。

一般就是用来画我们需要的流程图。
常用符号

网关

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

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

**包容性网关(Inclusive Gateway)**只要满足条件的出口都会执行,可以理解为 if(…) do, if (…) do, if (…) do,所有的条件判断都是同级别的。

任务

人工任务(User Task)
它是使用的最多的一种任务类型,他自带有一些人工任务的变量,例如签收人(Assignee),签收人就代表该任务交由谁处理,我们也可以通过某个特定或一系列特定的签收人来查找待办任务。利用上面的行为解释便是,当到达User Task节点的时候,节点设置Assignee变量或等待设置Assignee变量,当任务被完成的时候,我们使用Trigger来要求流程引擎退出该任务,继续流转。

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

接受任务(Receive Task)

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

结构

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

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

4、为什么选择Flowable?
修复了activiti6很多的bug,可以实现零成本从activiti迁移到flowable。flowable目前已经支持加签、动态增加实例中的节点、支持cmmn、dmn规范。这些都是activiti6目前版本没有的。

flowable已经支持所有的历史数据使用mongdb存储,activiti没有。
flowable支持事务子流程,activiti没有。
flowable支持多实例加签、减签,activiti没有。
flowable支持httpTask等新的类型节点,activiti没有。
flowable支持在流程中动态添加任务节点,activiti没有。
flowable支持历史任务数据通过消息中间件发送,activiti没有。
flowable支持java11,activiti没有。
flowable支持动态脚本,,activiti没有。
flowable支持条件表达式中自定义juel函数,activiti没有。
flowable支持cmmn规范,activiti没有。
flowable修复了dmn规范设计器,activit用的dmn设计器还是旧的框架,bug太多。
flowable屏蔽了pvm,activiti6也屏蔽了pvm(因为6版本官方提供了加签功能,发现pvm设计的过于臃肿,索性直接移除,这样加签实现起来更简洁、事实确实如此,如果需要获取节点、连线等信息可以使用bpmnmodel替代)。
flowable与activiti提供了新的事务监听器。activiti5版本只有事件监听器、任务监听器、执行监听器。
flowable对activiti的代码大量的进行了重构。
activiti以及flowable支持的数据库有h2、hsql、mysql、oracle、postgres、mssql、db2。其他数据库不支持的。使用国产数据库的可能有点失望了,需要修改源码了。
flowable支持jms、rabbitmq、mongodb方式处理历史数据,activiti没有。

5、Flowable基础表结构

工作流程的相关操作都是操作存储在对应的表结构中,为了能更好的弄清楚Flowable的实现原理和细节,我们有必要先弄清楚Flowable的相关表结构及其作用。在Flowable中的表结构在初始化的时候会创建五类表结构,具体如下:

ACT_RE :'RE’表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
ACT_RU:'RU’表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Flowable只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
ACT_HI:'HI’表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
ACT_GE: GE 表示 general。 通用数据, 用于不同场景下
ACT_ID: ’ID’表示identity(组织机构)。这些表包含标识的信息,如用户,用户组,等等。

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:部署的流程模板,可以启动流程使用的

三、创建项目

创建一个maven项目,引入springboot、mysql、flowable

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.test</groupId><artifactId>flowable-test</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.0.RELEASE</version><relativePath/></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.38</version></dependency><!--flowable--><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.4.1</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.22</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.25</version></dependency></dependencies>
</project>

application.yml

注意mysql数据库驱动版本差异

  • com.mysql.jdbc.Driver:

这是 旧版 MySQL JDBC 驱动的类名。
该驱动属于 mysql-connector-java 5.x 或更早版本。
它是 MySQL 5.x 系列及之前的 JDBC 驱动程序的标准类名。
从 MySQL 5.1 到 MySQL 5.7 版本,使用的是这个驱动。

  • com.mysql.cj.jdbc.Driver:

这是 新版 MySQL JDBC 驱动的类名。
该驱动是 MySQL 8.x 及之后版本的标准 JDBC 驱动程序。
com.mysql.cj.jdbc.Driver 是从 mysql-connector-java 8.x 开始引入的。
com.mysql.cj 代表 “Connector/J” 的缩写,cj 指代的是 Connector/J 新的模块命名空间。

spring:datasource:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/flowable-test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8driver-class-name: com.mysql.jdbc.Driver
flowable:# 业务流程设计的表自动创建database-schema-update: true# 异步执行耗时操作,提升性能async-executor-activate: false

FlowableApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class FlowableApplication {public static void main(String[] args) {SpringApplication.run(FlowableApplication.class, args);}
}

运行并启动项目,会在数据库自动生成工作流所使用的表

四、画流程图

这里使用 IDEA 插件Flowable BPMN visualizer,如下图:

装好插件之后,在 resources 目录下新建 processes 目录,这个目录下的流程文件将来会被自动部署

新建一个ask_for_leave.bpmn20.xml文件(.bpmn20.xml是固定文件后缀,不需要填写)

文件创建出来之后,右键单击,选择 View BPMN(Flowable) Diagram,就打开了此文件的可视化页面了,然后就可以来绘制自己的流程图

然后在可视化页面空白处,右键点击,就可以添加不同的符号,来绘制流程图

绘制完成

我们来看下这个流程对应的 XML 文件,一些流程细节会在 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="ask_for_leave" name="ask_for_leave" isExecutable="true"><startEvent id="startLeave"/><userTask id="leaveTask" name="请假" flowable:assignee="#{leaveUser}"/><sequenceFlow id="flowStart" sourceRef="startLeave" targetRef="leaveTask"/><userTask id="zuZhangTask" name="组长审核" flowable:assignee="#{zuZhangUser}"/><sequenceFlow id="modeFlow" sourceRef="leaveTask" targetRef="zuZhangTask"/><exclusiveGateway id="zuZhangJudgeGateway"/><sequenceFlow id="zuZhang_go_judge" sourceRef="zuZhangTask" targetRef="zuZhangJudgeGateway"/><userTask id="managerTask" name="经理审核" flowable:assignee="#{managerUser}"/><sequenceFlow id="zuZhangPass" sourceRef="zuZhangJudgeGateway" targetRef="managerTask" name="组长审核通过"><conditionExpression xsi:type="tFormalExpression">${var:equals(zuZhangCheckResult,"通过")}</conditionExpression></sequenceFlow><endEvent id="endLeave"/><exclusiveGateway id="managerJudgeGateway"/><sequenceFlow id="manager_go_judge" sourceRef="managerTask" targetRef="managerJudgeGateway"/><sequenceFlow id="managerPass" sourceRef="managerJudgeGateway" targetRef="endLeave" name="经理审核通过"><conditionExpression xsi:type="tFormalExpression">${var:equals(managerCheckResult,"通过")}</conditionExpression></sequenceFlow><serviceTask id="sendFailMessage" flowable:exclusive="true" name="发送失败提示" isForCompensation="true" flowable:class="com.test.service.LeaveFailService"/><sequenceFlow id="zuZhangRefuse" sourceRef="zuZhangJudgeGateway" targetRef="sendFailMessage" name="组长审核拒绝"><conditionExpression xsi:type="tFormalExpression">${var:equals(zuZhangCheckResult,"拒绝")}</conditionExpression></sequenceFlow><endEvent id="failEndLeave"/><sequenceFlow id="go_fail_end" sourceRef="sendFailMessage" targetRef="failEndLeave"/><sequenceFlow id="managerRefuse" sourceRef="managerJudgeGateway" targetRef="sendFailMessage" name="经理审核拒绝"><conditionExpression xsi:type="tFormalExpression">${var:equals(managerCheckResult,"拒绝")}</conditionExpression></sequenceFlow></process><bpmndi:BPMNDiagram id="BPMNDiagram_ask_for_leave"><bpmndi:BPMNPlane bpmnElement="ask_for_leave" id="BPMNPlane_ask_for_leave"><bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-92e89192-ae6e-4c06-84ea-e3deb6d92402" bpmnElement="startLeave"><omgdc:Bounds x="-454.35" y="-60.0" width="30.0" height="30.0"/></bpmdi:BPMNShape><bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-9ab478cf-067b-4984-a12e-0b14d28d6bb5" bpmnElement="leaveTask"><omgdc:Bounds x="-381.4296" y="-85.0" width="100.0" height="80.0"/></bpmdi:BPMNShape><bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-5e4ce6f7-76af-4972-91a1-ce776b2ca0c7" bpmnElement="flowStart"><omgdi:waypoint x="-424.35" y="-45.0"/><omgdi:waypoint x="-381.4296" y="-45.0"/></bpmdi:BPMNEdge><bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-46866177-80d1-4db8-abd7-f0426459804a" bpmnElement="zuZhangTask"><omgdc:Bounds x="-234.44722" y="-85.0" width="100.0" height="80.0"/></bpmdi:BPMNShape><bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-c65d7d95-b9f8-44e1-89a3-0bf2a9e95738" bpmnElement="modeFlow"><omgdi:waypoint x="-281.4296" y="-45.0"/><omgdi:waypoint x="-234.44722" y="-45.0"/></bpmdi:BPMNEdge><bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-53989386-6942-4e52-8f37-9076c52057a6" bpmnElement="zuZhangJudgeGateway"><omgdc:Bounds x="-78.09332" y="-65.0" width="40.0" height="40.0"/></bpmdi:BPMNShape><bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-4b34e417-f0c0-40ef-8bb6-7690e136d67f" bpmnElement="zuZhang_go_judge"><omgdi:waypoint x="-134.44722" y="-45.0"/><omgdi:waypoint x="-78.09332" y="-45.0"/></bpmdi:BPMNEdge><bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-86d89ed3-dcb1-451c-b96c-ba3ef43baeba" bpmnElement="managerTask"><omgdc:Bounds x="26.607117" y="-85.0" width="100.0" height="80.0"/></bpmdi:BPMNShape><bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-c2ec3e89-62ab-4e90-af28-0cf551530ca0" bpmnElement="zuZhangPass"><omgdi:waypoint x="-38.093323" y="-45.0"/><omgdi:waypoint x="26.607117" y="-45.0"/></bpmdi:BPMNEdge><bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-dd6c6e8f-e05d-4e50-9852-672656a76494" bpmnElement="endLeave"><omgdc:Bounds x="284.48773" y="-59.999996" width="30.0" height="30.0"/></bpmdi:BPMNShape><bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-fa4fc0dc-9773-4944-9fde-d2741dc9386b" bpmnElement="managerJudgeGateway"><omgdc:Bounds x="164.3713" y="-65.0" width="40.0" height="40.0"/></bpmdi:BPMNShape><bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-4d924eb1-02a1-43d3-b884-d7a3e5918359" bpmnElement="manager_go_judge"><omgdi:waypoint x="126.60712" y="-45.0"/><omgdi:waypoint x="164.3713" y="-45.0"/></bpmdi:BPMNEdge><bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-9918da8c-b7e5-4ec1-866b-dbfae2c6ea05" bpmnElement="managerPass"><omgdi:waypoint x="204.3713" y="-45.0"/><omgdi:waypoint x="284.48773" y="-44.999996"/></bpmdi:BPMNEdge><bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-ccc89da3-1cf3-414b-8c39-3a90c8a42326" bpmnElement="sendFailMessage"><omgdc:Bounds x="-108.09332" y="41.54" width="100.0" height="80.0"/></bpmdi:BPMNShape><bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-98a4db5f-6ff9-48d0-a358-9470a03c8f1c" bpmnElement="zuZhangRefuse"><omgdi:waypoint x="-58.093323" y="-25.0"/><omgdi:waypoint x="-58.093323" y="41.54"/></bpmdi:BPMNEdge><bpmdi:BPMNShape xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="shape-48b0f47e-8768-4275-b013-575039f2cd20" bpmnElement="failEndLeave"><omgdc:Bounds x="-199.44724" y="66.54" width="30.0" height="30.0"/></bpmdi:BPMNShape><bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-08d8631a-3c62-486f-9a65-6f1d1a63c317" bpmnElement="go_fail_end"><omgdi:waypoint x="-108.09332" y="81.54"/><omgdi:waypoint x="-169.44724" y="81.54"/></bpmdi:BPMNEdge><bpmdi:BPMNEdge xmlns:bpmdi="http://www.omg.org/spec/BPMN/20100524/DI" id="edge-136a3062-a9f0-4c34-a275-0d6b51fc55d3" bpmnElement="managerRefuse"><omgdi:waypoint x="184.3713" y="-25.0"/><omgdi:waypoint x="184.3713" y="81.54"/><omgdi:waypoint x="-8.093322" y="81.53999"/></bpmdi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</definitions>
五、流程接口代码

AskForLeaveController.java

package com.test.controller;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.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@RestController
public class AskForLeaveController {@AutowiredRuntimeService runtimeService;@AutowiredRepositoryService repositoryService;@AutowiredTaskService taskService;@AutowiredProcessEngine processEngine;/*** 查看流程进度图* @param resp* @param processId 流程id* @throws Exception*/@GetMapping("/viewFlowChart")public void viewFlowChart(HttpServletResponse resp, String processId) throws Exception {ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();if (pi == null) {return;}List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processId).list();List<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, false);OutputStream out = null;byte[] buf = new byte[1024];int legth = 0;try {out = resp.getOutputStream();while ((legth = in.read(buf)) != -1) {out.write(buf, 0, legth);}} finally {if (in != null) {in.close();}if (out != null) {out.close();}}}/*** 开启一个请假流程*/@GetMapping("/askForLeave")public String askForLeave() {Map<String, Object> map = new HashMap<>();map.put("leaveUser", "张三");map.put("name", "ego");map.put("reason", "出去玩");map.put("days", 10);ProcessInstance instance = runtimeService.startProcessInstanceByKey("ask_for_leave", map);return "开启请假流程 processId:" + instance.getId();}/*** 员工请假提交给组长审核*/@GetMapping("/submitToZuZhang")public String submitToZuZhang() {// 员工查找到自己的任务,然后提交给组长审批List<Task> list = taskService.createTaskQuery().taskAssignee("张三").orderByTaskId().desc().list();String info = null;for (Task task: list) {info = "任务 ID:" + task.getId() + ",任务申请人:" + task.getAssignee() + ",任务是否挂起:" + task.isSuspended();//提交给组长的时候,需要指定组长的idMap<String, Object> map = new HashMap<>();map.put("zuZhangUser", "李四");taskService.complete(task.getId(), map);}return info;}/*** 组长审核请假*/@GetMapping("/zuZhangApprove")public String zuZhangApprove() {List<Task> list = taskService.createTaskQuery().taskAssignee("李四").orderByTaskId().desc().list();String info = null;for (Task task: list) {if ("组长审核".equals(task.getName())) {info = "任务 ID:" + task.getId() + ",组长审核人:" + task.getAssignee() + ",任务是否挂起:" + task.isSuspended();// 提交给经理的时候,需要指定经理的idMap<String, Object> map = new HashMap<>();map.put("managerUser", "王五");map.put("zuZhangCheckResult", "通过");map.put("zuZhangUser", "李四");try {taskService.complete(task.getId(), map);} catch (Exception e) {e.printStackTrace();info = "组长审核失败:" + task.getId() + " " + task.getAssignee();}}}return info;}/*** 经理审核通过*/@GetMapping("/managerApprove")public String managerApprove() {List<Task> list = taskService.createTaskQuery().taskAssignee("王五").orderByTaskId().desc().list();String info = null;for (Task task: list) {info = "经理:" + task.getAssignee() + ",审核通过:" + task.getId() + " 任务";Map<String, Object> map = new HashMap<>();map.put("managerCheckResult", "通过");taskService.complete(task.getId(), map);}return info;}/*** 经理审核拒绝*/@GetMapping("/managerRefuse")public String managerRefuse() {List<Task> list = taskService.createTaskQuery().taskAssignee("王五").orderByTaskId().desc().list();String info = null;for (Task task: list) {info = "经理:" + task.getAssignee() + ",审核拒绝:" + task.getId() + " 任务";Map<String, Object> map = new HashMap<>();map.put("managerCheckResult", "拒绝");taskService.complete(task.getId(), map);}return info;}
}

LeaveFailService.java

对应流程图中的

package com.test.service;import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;@Component
public class LeaveFailService implements JavaDelegate {@Overridepublic void execute(DelegateExecution delegateExecution) {System.out.println("审批不通过 " + delegateExecution.getCurrentActivityId());}
}

FlowableConfig.java,防止流程图出现中文乱码的配置类

package com.test.config;import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {@Overridepublic void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {springProcessEngineConfiguration.setActivityFontName("宋体");springProcessEngineConfiguration.setLabelFontName("宋体");springProcessEngineConfiguration.setAnnotationFontName("宋体");}
}
五、测试一下流程接口

1、开启一个请假流程,http://localhost:8080/askForLeave

查看流程进度图,参数是开启的流程的processId

http://localhost:8080/viewFlowChartprocessId=ef67cd51-af01-11ef-8fdb-005056c00001,

可以看到,请假这个userTask用红色的框框起来了,说明当前流程走到了这一步

2、员工请假提交给组长审核,http://localhost:8080/submitToZuZhang

查看流程进度图

3、组长审核请假,http://localhost:8080/zuZhangApprove

查看流程进度

4、经理审核拒绝,http://localhost:8080/managerRefuse

调用LeaveFailService,控制台打印

流程结束

5、重新开启一个流程,重复1-4,试一试“经理审核通过”

相关文章:

SpringBoot集成Flowable

一、工作流介绍 1、概念 通过计算机对业务流程的自动化管理。工作流是建立在业务流程的基础上&#xff0c;一个软件的系统核心根本上还是系统的业务流程&#xff0c;工作流只是协助进行业务流程管理。 解决的是&#xff1a;在多个参与者之间按照某种预定义的规则自动进行传递…...

大模型从零开始——预训练之分词 Tokenization

文章目录 一、概念介绍 二、单词级别分词 三、字符级别分词 四、子词级别分词 BPE/BBPE分词 BPE&#xff1a;Byte-Pair Encoding (BPE) BBPE&#xff1a;Byte-level BPE (BBPE) WordPiece分词 Unigram分词 五、总结 词元化&#xff08;Tokenization&#xff09;是数据预处…...

Python下载包缓慢时怎么修改镜像源

前言 在使用Python下载第三方包时&#xff0c;有时会比较缓慢&#xff0c;与Linux环境中下载软件一样可以替换软件源一样&#xff0c;Python下载源也可以进行修改&#xff0c;因为总是记不住镜像地址&#xff0c;特此记录一下。 常用镜像 豆瓣镜像&#xff1a; https://pypi.…...

每日一练 | 时延和抖动

01 真题题目 关于时延和抖动&#xff0c;下面描述正确的是&#xff08;多选&#xff09;&#xff1a; A. 端到端时延等于处理时延与队列时延之和 B. 抖动是因为每个包的端到端时延不相等造成的 C. 抖动的大小跟时延的大小相关&#xff0c;时延小则抖动的范围也小&#xff0c;时…...

嵌入式开发之使用 FileZilla 在 Windows 和 Ubuntu 之间传文件

01-FileZilla简介 FileZilla 是一个常用的文件传输工具&#xff0c;它支持多种文件传输协议&#xff0c;包括以下主要协议&#xff1a; FTP (File Transfer Protocol) 这是 FileZilla 最基本支持的协议。FTP 是一种明文传输协议&#xff0c;不加密数据&#xff08;包括用户名和…...

腾势D9风光不再?中期改款能否及时“救火”

文/王俣祺 导语&#xff1a;腾势D9销量下滑了&#xff0c;这背后是MPV市场的整体没落还是众多新车的围追堵截&#xff1f;如今2025款腾势D9也来了&#xff0c;“加量不加价”的新车又能否逆转乾坤&#xff0c;重夺MPV市场霸主的地位&#xff1f; 腾势D9销量下滑的“真相” 回…...

OpenCV-Python实战(11)——边缘检测

一、Sobel 算子 通过 X 梯度核与 Y 梯度核求得图像在&#xff0c;水平与垂直方向的梯度。 img cv2.Sobel(src*,ddepth*,dx*,dy*,ksize*,scale*,delta*,borderType*)img&#xff1a;目标图像。 src&#xff1a;原始图像。 ddepth&#xff1a;目标图像深度&#xff0c;-1 代表…...

【智行安全】基于Synaptics SL1680的AI疲劳驾驶检测方案

随著车载技术的快速进步&#xff0c;驾驶安全越来越受到重视&#xff0c;而疲劳驾驶是造成交通事故的重要原因之一。传统的驾驶监控技术因精度不足或反应迟缓&#xff0c;无法满足实时监测需求。因此&#xff0c;结合人工智能技术的疲劳驾驶检测系统成为行业新方向&#xff0c;…...

机器学习随机森林回归时间序列预模型中时间滑动窗口作用以及参数设置

一、时间序列模型中时间滑动窗口作用 在时间序列模型中&#xff0c;时间滑动窗口&#xff08;Sliding Window&#xff09;起到了至关重要的作用。它是一种常见且有效的数据表示技术&#xff0c;通过将时间序列数据分割成多个固定大小的窗口&#xff0c;来捕捉和分析数据中的模式…...

【Rust自学】7.5. use关键字 Pt.2 :重导入与换国内镜像源教程

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 7.5.1. 使用pub use重新导入名称 使用use将路径导入作用域内后。该名称在词作用域内是私有的。 以上一篇文章的代码为例&#xff1a; m…...

自定义luacheck校验规则

安装运行环境 安装环境及源码解析&#xff0c;参考&#xff1a;LuaCheck校验原理解析 自定义校验规则 从代码中可以看出&#xff0c;定义一条规则有以下关键点&#xff1a; 需要定义告警信息&#xff1a;由键值对组成&#xff0c;key为告警编码&#xff08;不一定为纯数字&…...

python钉钉机器人

上代码 #coding:utf-8 import sys import time import hmac import hashlib import base64 import urllib.parse import requeststimestamp str(round(time.time() * 1000)) secret 你的secret secret_enc secret.encode(utf-8) string_to_sign {}\n{}.format(timestamp, …...

汇编学习笔记

汇编 1. debug指令 -R命令(register) 查看、改变CPU寄存器的内容 r ax 修改AX中的内容 -D命令(display) 查看内存中的内容 -E命令(enter) 改写内存中的内容 -U命令(unassenble反汇编) 将内存中的机器指令翻译成汇编指令 -T命令(trace跟踪) 执行一条机器指令 -A命令…...

混合并行训练框架性能对比

混合并行训练框架性能对比 1. 框架类型 DeepSpeed、Megatron - LM、Colossal - AI、SageMaker、Merak、FasterMoE、Tutel、Whale、Alpa、DAPPLE、Mesh - TensorFlow 2. 可用并行性(Available parallelisms) DNN framework(深度神经网络框架)DP(数据并行,Data Parallelis…...

基于Docker+模拟器的Appium自动化测试(二)

模拟器的设置 打开“夜神模拟器”的系统设置&#xff0c;切换到“手机与网络”页&#xff0c;选中网络设置下的“开启网络连接”和“开启网络桥接模式”复选框&#xff0c;而后选择“静态IP”单选框&#xff0c;在IP地址中输入“192.168.0.105”&#xff0c;网关等内容不再赘述…...

数据结构之线性表之链表(附加一个考研题)

链表的定义 链表的结构&#xff1a; 单链表-初始化 代码实现&#xff1a; 单链表-头插法 代码实现&#xff1a; 这里我给大家分析一下 我们每创建一个新的节点都要插在头节点的后面&#xff0c;我们一定要注意顺序 一定要先让新节点指向头节点指向的下一个节点&#xff0c;…...

etmem

title: 聚焦 Etmem&#xff1a;高效内存管理的新引擎 date: ‘2024-12-31’ category: blog tags: Etmem内存管理性能优化系统资源 sig: storage archives: ‘2024-12’ author:way_back summary: Etmem 是一款专注于内存管理优化的创新工具&#xff0c;通过智能的内存分配、回…...

LangChain4j与Elasticsearch:构建高效的语义嵌入存储

LangChain4j与Elasticsearch&#xff1a;构建高效的语义嵌入存储 一、LangChain4j与Elasticsearch集成概述 1.1 LangChain4j简介 LangChain4j是一个为Java开发者设计的开源库&#xff0c;旨在简化大型语言模型&#xff08;LLM&#xff09;在Java应用程序中的集成。它提供了与…...

黄河小浪底水利枢纽泄洪预警广播系统正式上线

24小时站岗、危险自动报警、远程喊话驱离……近日&#xff0c;小浪底水利枢纽和西霞院水利枢纽的泄洪预警广播系统正式上线&#xff0c;通过数字化设施赋能管控水域日常监管&#xff0c;将危险水域各个角落“尽收眼底”&#xff0c;涉水危险行为“无处可藏”。 “前方船只请注意…...

理解生成协同促进?华为诺亚提出ILLUME,15M数据实现多模态理解生成一体化

多模态理解与生成一体化模型&#xff0c;致力于将视觉理解与生成能力融入同一框架&#xff0c;不仅推动了任务协同与泛化能力的突破&#xff0c;更重要的是&#xff0c;它代表着对类人智能&#xff08;AGI&#xff09;的一种深层探索。通过在单一模型中统一理解与生成&#xff…...

如何快速跳过FF14副本动画:终极ACT插件安装与使用指南

如何快速跳过FF14副本动画&#xff1a;终极ACT插件安装与使用指南 【免费下载链接】FFXIV_ACT_CutsceneSkip 项目地址: https://gitcode.com/gh_mirrors/ff/FFXIV_ACT_CutsceneSkip 还在为《最终幻想14》国服副本中那些无法跳过的冗长动画而烦恼吗&#xff1f;FFXIV_AC…...

Bilibili神奇弹幕机器人:打造智能直播间的完整免费解决方案

Bilibili神奇弹幕机器人&#xff1a;打造智能直播间的完整免费解决方案 【免费下载链接】MagicalDanmaku 本仓库及所有相关项目已永久停止开发、维护和任何形式的分发。 项目地址: https://gitcode.com/gh_mirrors/bi/MagicalDanmaku 想要让你的B站直播间实现自动化运营…...

告别命令行!用Offset Explorer(原Kafka Tool)图形化管理Kafka集群,5分钟上手

告别命令行恐惧&#xff1a;用Offset Explorer实现Kafka集群的可视化高效管理 对于许多开发者和运维人员来说&#xff0c;Kafka的命令行操作就像一道难以逾越的门槛。那些复杂的参数、冗长的命令和难以直观理解的输出&#xff0c;常常让人望而却步。而Offset Explorer&#xff…...

从开题到定稿,okbiye 如何让本科毕业论文写作告别 “通宵焦虑”

okbiye-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/AI PPT毕业论文 - Okbiye智能写作https://www.okbiye.com/ai/bylw 一、本科毕业论文的 “三座大山”&#xff0c;正在拖垮你的毕业季 对于大多数本科生而言&#xff0c;毕业论文写作早已不是 “写一篇文章”…...

别再只用默认配置了!GaussDB密码安全策略的这8个参数,DBA必须知道怎么调

GaussDB密码安全策略深度实战&#xff1a;8个关键参数配置指南 在数据库安全管理中&#xff0c;密码策略往往是最容易被忽视却又最常被攻击利用的薄弱环节。许多DBA习惯性地沿用数据库默认配置&#xff0c;殊不知这些默认值可能无法满足企业实际安全需求。GaussDB作为企业级分布…...

模板 ID 配置化: “公众号路由 + 模板消息发送” 封装成一个干净的业务 Service

文章目录 引言 I “公众号路由 + 模板消息发送” 多公众号 同模板不同 ID 公众号实例 公众号路由 模板消息发送 Service(业务层 ✅) 异步调用 II 公众号账号配置【升级版】 账号配置 启用配置 模板 ID 解析器 公众号 Router(升级版 ✅) III 路由(Redis 版本) WxRedisOps…...

【技术剖析】AI-RPA 的“眼睛”:详解 DOM 树精简与 OmniParser 屏幕解析技术

引言&#xff1a;当 RPA 遇上 AI&#xff0c;谁来做机器的“眼睛”&#xff1f; 2026 年&#xff0c;AI 与 RPA 的融合正在经历一场深刻的技术重构。根据市场研究数据&#xff0c;AIRPA 全球市场规模预计从 2025 年的 47.9 亿美元增长至 2026 年的 56 亿美元&#xff0c;复合年…...

高炉智变:12期实战带你玩转工业AI落地~系列文章11:可解释AI实践:SHAP+LIME打开高炉模型的“黑箱“

&#x1f3af; 高炉智变11&#xff5c;可解释AI实践&#xff1a;SHAPLIME打开高炉模型的"黑箱" &#x1f4c5; 本文目录 一、前言&#xff1a;AI可解释性的重要性二、SHAP可解释性框架三、LIME局部解释方法四、高炉模型可解释性实践五、实战代码实现六、总结与预告 一…...

Steam挂刀行情站:如何利用开源工具实现Steam饰品交易自动化监控

Steam挂刀行情站&#xff1a;如何利用开源工具实现Steam饰品交易自动化监控 【免费下载链接】SteamTradingSiteTracker Steam 挂刀行情站 —— 24小时更新的 BUFF & IGXE & C5 & UUYP & ECO 挂刀比例数据 | Track cheap Steam Community Market items on buff.…...

GNSS PPP宽窄巷模糊度固定算法全链路拆解

1. GNSS PPP宽窄巷模糊度固定算法概述 全球导航卫星系统&#xff08;GNSS&#xff09;精密单点定位&#xff08;PPP&#xff09;技术中&#xff0c;模糊度固定是提升定位精度的关键环节。我第一次接触这个领域时&#xff0c;被各种专业术语绕得头晕&#xff0c;直到把整个流程…...