当前位置: 首页 > 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…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...

FOPLP vs CoWoS

以下是 FOPLP&#xff08;Fan-out panel-level packaging 扇出型面板级封装&#xff09;与 CoWoS&#xff08;Chip on Wafer on Substrate&#xff09;两种先进封装技术的详细对比分析&#xff0c;涵盖技术原理、性能、成本、应用场景及市场趋势等维度&#xff1a; 一、技术原…...

免费批量Markdown转Word工具

免费批量Markdown转Word工具 一款简单易用的批量Markdown文档转换工具&#xff0c;支持将多个Markdown文件一键转换为Word文档。完全免费&#xff0c;无需安装&#xff0c;解压即用&#xff01; 官方网站 访问官方展示页面了解更多信息&#xff1a;http://mutou888.com/pro…...