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、概念 通过计算机对业务流程的自动化管理。工作流是建立在业务流程的基础上,一个软件的系统核心根本上还是系统的业务流程,工作流只是协助进行业务流程管理。 解决的是:在多个参与者之间按照某种预定义的规则自动进行传递…...

大模型从零开始——预训练之分词 Tokenization
文章目录 一、概念介绍 二、单词级别分词 三、字符级别分词 四、子词级别分词 BPE/BBPE分词 BPE:Byte-Pair Encoding (BPE) BBPE:Byte-level BPE (BBPE) WordPiece分词 Unigram分词 五、总结 词元化(Tokenization)是数据预处…...

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

每日一练 | 时延和抖动
01 真题题目 关于时延和抖动,下面描述正确的是(多选): A. 端到端时延等于处理时延与队列时延之和 B. 抖动是因为每个包的端到端时延不相等造成的 C. 抖动的大小跟时延的大小相关,时延小则抖动的范围也小,时…...

嵌入式开发之使用 FileZilla 在 Windows 和 Ubuntu 之间传文件
01-FileZilla简介 FileZilla 是一个常用的文件传输工具,它支持多种文件传输协议,包括以下主要协议: FTP (File Transfer Protocol) 这是 FileZilla 最基本支持的协议。FTP 是一种明文传输协议,不加密数据(包括用户名和…...

腾势D9风光不再?中期改款能否及时“救火”
文/王俣祺 导语:腾势D9销量下滑了,这背后是MPV市场的整体没落还是众多新车的围追堵截?如今2025款腾势D9也来了,“加量不加价”的新车又能否逆转乾坤,重夺MPV市场霸主的地位? 腾势D9销量下滑的“真相” 回…...

OpenCV-Python实战(11)——边缘检测
一、Sobel 算子 通过 X 梯度核与 Y 梯度核求得图像在,水平与垂直方向的梯度。 img cv2.Sobel(src*,ddepth*,dx*,dy*,ksize*,scale*,delta*,borderType*)img:目标图像。 src:原始图像。 ddepth:目标图像深度,-1 代表…...

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

机器学习随机森林回归时间序列预模型中时间滑动窗口作用以及参数设置
一、时间序列模型中时间滑动窗口作用 在时间序列模型中,时间滑动窗口(Sliding Window)起到了至关重要的作用。它是一种常见且有效的数据表示技术,通过将时间序列数据分割成多个固定大小的窗口,来捕捉和分析数据中的模式…...

【Rust自学】7.5. use关键字 Pt.2 :重导入与换国内镜像源教程
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 7.5.1. 使用pub use重新导入名称 使用use将路径导入作用域内后。该名称在词作用域内是私有的。 以上一篇文章的代码为例: m…...

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

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自动化测试(二)
模拟器的设置 打开“夜神模拟器”的系统设置,切换到“手机与网络”页,选中网络设置下的“开启网络连接”和“开启网络桥接模式”复选框,而后选择“静态IP”单选框,在IP地址中输入“192.168.0.105”,网关等内容不再赘述…...

数据结构之线性表之链表(附加一个考研题)
链表的定义 链表的结构: 单链表-初始化 代码实现: 单链表-头插法 代码实现: 这里我给大家分析一下 我们每创建一个新的节点都要插在头节点的后面,我们一定要注意顺序 一定要先让新节点指向头节点指向的下一个节点,…...

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

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

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

理解生成协同促进?华为诺亚提出ILLUME,15M数据实现多模态理解生成一体化
多模态理解与生成一体化模型,致力于将视觉理解与生成能力融入同一框架,不仅推动了任务协同与泛化能力的突破,更重要的是,它代表着对类人智能(AGI)的一种深层探索。通过在单一模型中统一理解与生成ÿ…...

[文献阅读]ReAct: Synergizing Reasoning and Acting in Language Models
文章目录 摘要Abstract:思考与行为协同化Reason(Chain of thought)ReAct ReAct如何协同推理 响应Action(动作空间)协同推理 结果总结 摘要 ReAct: Synergizing Reasoning and Acting in Language Models [2210.03629] ReAct: Synergizing Reasoning an…...

摄像头监视脚本
摄像头监视脚本,若检测到摄像头画面有变化,保存这一段视频 一、使用方法 1.运行脚本 默认参数Threshold3, Period3, path./recordings python cam.py --threshold30 --period3 --path./recordings 2.参数说明 threshold:摄像头捕获到的画面变化量阈值…...

FreeRTOS的内存管理(选择heap4.c文件的理由)
目录 1. 了解FreeRTOS内存管理 2. 了解内存碎片 3.了解各个heap.c的内存分配方法 1.heap1.c 2.heap2.c 3.heap3.c 4.heap4.c 5.heap5.c 总结: 内存管理是一个系统基本组成部分,FreeRTOS 中大量使用到了内存管理,比如创建任务、信号量…...

SQL-leetcode-183. 从不订购的客户
183. 从不订购的客户 Customers 表: -------------------- | Column Name | Type | -------------------- | id | int | | name | varchar | -------------------- 在 SQL 中,id 是该表的主键。 该表的每一行都表示客户的 ID 和名称。 Orders 表&#…...

苹果系统MacOS下ObjectC建立的App程序访问opencv加载图片程序
前言 苹果系统下使用opencv感觉还是有些不太方便,总是感觉有点受到限制。本博客描述的是在MacOS下建立App程序然后调用opencv显示图片时出现的一些问题并最后解决的一个过程。 一、程序的建立 选择程序的类型: 选择界面模式和编程语言: 其余…...

《代码随想录》Day21打卡!
写在前面:祝大家新年快乐!!!2025年快乐,2024年拜拜~~~ 《代码随想录》二叉树:修剪二叉搜索树 本题的完整题目如下: 本题的完整思路如下: 1.本题使用递归进行求解,所以分…...

Dell服务器升级ubuntu 22.04失败解决
ubuntu系统原版本20.04,服务器dell T40. 执行apt update后,再执行apt upgrade。 apt update执行成功,但apt upgrade执行中断,提示如下: Checking package manager Reading package lists... Done Building dependen…...

构建全志 T113 Tina SDK
1、环境配置: 准备一个 Ubuntu 系统,可以是 WSL,虚拟机等,建议版本是 20.04。 1.1、安装必要的软件 进入系统后,输入下方命令安装需要的工具 : sudo apt update -y sudo apt full-upgrade -y sudo apt i…...

(推荐)【通用业务分发架构】1.业务分发 2.rpc调用 3.Event事件系统
一.Reflections和SpringUtil完成扫描包的(反射缓存) 二.id与class的映射泛型上下文(玩家是否登录,rpc调用SeqId,class类名)反射调用 1.netty层的 AccountMsgParam // 登录前 OnlineMsgParam // 登录后 SceneMsgParam // 发到场景层的 2.跨进程rpc调用的…...

最近的一些事情
正义不会缺席 这家公司违法辞退不给工资乱开离职证明。严重影响个人发展。 今天终于收到法院的判决书。 警醒自身发展与社会之间密切交流,敲响警钟。 虽然最终得到的法院的支持,但过程举步维艰。 这其中的过程,也让我对律师、法院和中国…...