SpringBoot+Flowable 完美结合,优雅实现工作流!

您所在的位置:网站首页 快速入门springboot SpringBoot+Flowable 完美结合,优雅实现工作流!

SpringBoot+Flowable 完美结合,优雅实现工作流!

2024-07-11 18:14| 来源: 网络整理| 查看: 265

点击关注公众号,Java干货及时送达👇

工作流Flowable学习,入门,实战

1工作流介绍 1.1 为什么使用工作流

在程序员工作中,或多或少都会遇见审批流程类型的业务需求。一个审批流程可能包含开始->申请->领导审批->老板审批->结束等多个阶段,如果我们用字段去定义每一个流程阶段(0->开始 1->申请 2->领导审批 3->老板审批 4->结束),虽然可以实现流程运行逻辑,但这样业务代码逻辑复杂。如果审批流程还有驳回操作,则还需要加一个是否驳回字段,显然这样实现,成本太大,且不利于维护。

为了解决上述用代码逻辑硬写审批流程而导致的成本大,不利用维护的缺点,工作流因此而生。

1.2 工作流是什么

工作流,是把业务之间的各个步骤以及规则进行抽象和概括性的描述。使用特定的语言为业务流程建模,让其运行在计算机上,并让计算机进行计算和推动。工作流是复杂版本的状态机。

简单状态

37c625fd45bbae67904aadfb3b967e77.png

上图为工作流退化为基础状态机的例子,路人乙的状态非常简单,站起来->走起来->跑起来->慢下来->站起来,无限循环,如果让我们实现路人乙的状态切换,那么我们只需要用一个字段来记录路人乙当前的状态就好了。

而对于复杂的状态或者状态维度增加且状态流转的条件极为复杂,可能单纯用字段记录状态的实现方式就会不那么理想。如下图:

复杂状态

525718064520d837cfa3168bc61274ba.png

现在交给路人甲的选择就多了起来,当路人甲获发完工资后的时候,他会根据余额的大小来判断接下来该如何行动,如果数额小于等于5000,那么他决定买一个平板,如果数额小于等于10万,那么路人甲就决定去学习一下购买理财产品,但如果路人甲获得的余额数量超过了30万,他就决定购买一辆宝马,但购买宝马的流程是复杂的,路人甲决定同时完成交首付和贷款的手续。

其实这个流程还不算特别复杂,但到目前为止,单纯用一个字段来表明状态已经无法满足要求了。

工作流解决的痛点在于,解除业务宏观流程和微观逻辑的耦合,让熟悉宏观业务流程的人去制定整套流转逻辑,而让专业的人只需要关心他们应当关心的流程节点,就好比大家要一起修建一座超级体育场,路人甲只需要关心他身边的这一堆砖是怎么堆砌而非整座建筑。

1.3 工作流不能解决的问题

工作流无法解决毫无关系(没有前后关联)的事务。

ef62108930a6b9bd665dd919887bc6d8.png

工作流是一个固定好的框架,大家就按照这个框架来执行流程就行了,但某些情况他本身没有流转顺序的。

比如:路人丙每天需要学习,谈恋爱以及玩游戏,它们之间没有关联性无法建立流程,但可以根据每一项完成的状态决定今天的任务是否完结,这种情况我们需要使用CMMN来建模,它就是专门针对这种情况而设计的,但今天我们不讲这个,而是讲讲BPMN协议。

2BPMN2.0协议 2.1 什么是BPMN2.0协议 bee825c10dbbe9a87fd56f8d3ffb5af9.png

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

2.2 BPMN2.0协议元素介绍

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

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

下面我就以每种类型的节点简单地概括一下其作用。

网关:

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

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

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

任务:

BPMN2.0协议的所有任务其实是从一个抽象任务派生而来的,抽象任务会有如下行为:

当流程流转到该任务时,应该做些什么?

当该任务获得信号(signal)的时候,它是否可以继续向下流转,而任务获得信号的这个动作我们称为Trigger。

利用如上的抽象行为,我们来解释一些比较常见且具有代表性的任务类型。

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

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

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

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

上面大概介绍了一下常用的节点,下面的图就展示了一个以BPMN2.0为基础的流程模型,尽量覆盖到所介绍的所有节点。

f32f6eff416207878e29fdea60fc7a75.png

这里是一个生产手机的流程,从“手机设计”节点到“批准生产”节点是一个串行的任务,而审批的结果会遇到一个互斥网关,上面讲过,互斥网关只需要满足其中一个条件就会流转,而这里表达的意义就是审批是否通过。

“载入图纸”是一个服务任务,它是自动执行的,之后会卡在“等待原材料”这个节点,因为这个节点是需要人为去判断(比如原材料涨价,原材料不足等因素),所以需要在一种自定义的条件下Trigger,而该图的条件应该为“原材料足够”,原材料足够之后,我们会开始并行生产手机零件。

需要注意的是,并行网关在图中是成对出现的,他的作用是开始一系列并行任务和等待并行任务一起完成。

2.3 如何使用BPMN2.0协议

首先,从用户的角度来看,使用者其实只需要关心三件事

我如何把我的业务逻辑转化为流程图-即容易理解的绘图工具。

我如何使流程流转-即开箱即用的API。

我需要引擎告诉我,我现在该处理什么节点-即丰富且鲜明的事件机制。

图中是流程图的整个生命周期,从画图到部署,然后启动流程,流程经过人工或自动的方式流转,最后结束。

e95791b51f83b88a4613a463805a390b.png 3Flowable简介 3.1 Flowable是什么

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

Flowable可以嵌入Java应用程序中运行,也可以作为服务器、集群运行,更可以提供云服务。

3.2 Flowable与Activiti

Flowable是基于Activiti-6.0.0.Beta4分支开发的。目前Flowable已经修复了Activiti6很多的bug,可以实现零成本从Activiti迁移到Flowable。

4Flowable实战

假设公司产品有一个新需求,需要你设计一个员工请假流程的功能。你该如何设计?

我会用flowable来实现

创建springboot项目(flowable-ui),该项目是启动flowable-ui网站,在本地进行流程画图设计。安装flowable-ui,启动服务进入folwable-ui网站画图,导出图对应的xml文件 (画图)

创建springboot项目(flowable),该项目是启动流程应用,将对应的xml文件复制到resources下的processes文件夹中(processes文件夹需自己创建) (部署)

启动springboot项目 (启动)

通过接口调用,启动流程 (流转)

4.1 本地安装,启动flowable-ui

创建 springboot(flowable-ui)项目 项目目录

2c1c8d3b0420730047e0356ee2f6b3ca.png

JavaSkillPointApplication文件

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

application.yml文件

server:   port: 8088 flowable:   idm:     app:       admin:         # 登录的用户名         user-id: admin         # 登录的密码         password: admin         # 用户的名字         first-name: wei         last-name: kai spring:   # mysql连接信息   datasource:     # mysql8之后     driver-class-name: com.mysql.cj.jdbc.Driver     # mysql8之前 #    driver-class-name: com.mysql.jdbc.Driver     url: jdbc:mysql://43.143.132.109:3306/flowable?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT&nullCatalogMeansCurrent=true     username: root     password: ******   jpa:     properties:       hibernate:         hbm2ddl:           auto: update         dialect: org.hibernate.dialect.MySQL5InnoDBDialect     open-in-view: true

注意:

&nullCatalogMeansCurrent=true 这个一定要加在url后,不然flowable自动创建表失败

在数据库中创建flowable数据库,启动flowable-ui项目时,flowable服务会自动创建对应的表

启动项目后 网站的登录用户 密码 user-id: admin password: admin

01b1b7fed4fdc1fa4b919316e707b656.png

pom.xml文件

                  org.springframework.boot         spring-boot-starter                   org.springframework.boot         spring-boot-starter-test         test                        org.springframework.boot         spring-boot-starter-web                        org.projectlombok         lombok         1.18.22                        org.flowable         flowable-spring-boot-starter-ui-idm         6.7.1                        org.flowable         flowable-spring-boot-starter-ui-modeler         6.7.1                        org.springframework.boot         spring-boot-starter-data-jpa                        mysql         mysql-connector-java                        org.flowable         flowable-spring-boot-starter         6.7.1     

启动项目

49240691ce1cf3ea8c04ff7515974dd9.png

访问http://localhost:8088/

87a91f541a8dd4d2d3211719a9960c4a.png 12383a3f49e38371952bd0a6bbb66252.png d72094930cb4002c77e6bfc7e6e62444.png b6a78fa03a7c0807bf4b081dbc566b8f.png 9a491afa0c149abd3b0a4da6c93decec.png

这个时候就可以开始设计员工请假流程图 我先画一个完整的出来 将每个步骤对应的信息都截下来 供大家参考

ac48c5db017e525a2de4966ee02a134c.png a72b0976baadbc022d681810bedc4f0f.png 40e73cf90a44d733f78a3b35c2d574a0.png ad1e683e6a2ebe31d711282fa2630200.png e3bfea65e107b314f81325713cbeb5bc.png 056b46970b3a8868a6a1007d92cc7cad.png 0b754e05fca724bde538f1a86f165f06.png e56f33345e3d97f93ed499ae85314350.png b941354ef5a0aea6bec9dee08103cb6f.png 1ece19efc9aefe3d1d057423e91a7c90.png cf01d0de0e4c9ae8903c50c9312c625d.png 90892249ed863ca653d87958535a4284.png 4c31634c94eeaabe8b6520fd54cb398a.png 1bb81ad5309a0342561e77b4031428dd.png

推荐大家画一遍 这里我会上传请假流程的xml文件,大家直接导入即可,通过链接下载流程xml文件

链接: https://pan.baidu.com/s/10FAFBiM3SYUj9FcSoPadtw 提取码: rqku

b7960d7722d3ded5697f8a3c3ae0f8f3.png 4.2 创建flowable应用项目 部署并启动

创建springboot项目

项目目录

205aebfe6e412a45bf71140dc34dabcc.png

JavaSkillPointApplication文件

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

公司员工请假流程.bpmn20.xml 在resources下创建processes文件夹 复制xml文件到该目录即可

       公司员工请假流程                                                                                                                                                    2}]]>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

application.yml

server:   port: 8081 spring:   # mysql连接信息   datasource:     # mysql8之后 #    driver-class-name: com.mysql.cj.jdbc.Driver     # mysql8之前     driver-class-name: com.mysql.jdbc.Driver     url: jdbc:mysql://43.143.132.109:3306/flowable?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT     username: root     password: ******

注意:这里的数据库url链接应与flowable-ui项目保持一致

JavaSkillPointApplicationTests测试文件

package com.weige.javaskillpoint; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.history.HistoricActivityInstance; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.util.CollectionUtils; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @SpringBootTest class JavaSkillPointApplicationTests {     @Autowired     private RuntimeService runtimeService;     @Autowired     private TaskService taskService;     @Autowired     private HistoryService historyService;     @Test     void sub() {         // 员工提交请假申请         Map map = new HashMap();         map.put("day", 5);         map.put("studentUser", "小明");         // leave为员工请假流程xml文件中的id         ProcessInstance a1 = runtimeService.startProcessInstanceByKey("leave", map);         Task task = taskService.createTaskQuery().processInstanceId(a1.getId()).singleResult();         taskService.complete(task.getId());     }     @Test     void queryLeadTask() {         // 查询领导分组的任务         List teacher = taskService.createTaskQuery().taskCandidateGroup("a").list();         for (Task task : teacher) {             // 根据任务id查询当前任务参数             Map variables = taskService.getVariables(task.getId());             System.out.println(variables.get("day") + "," + variables.get("studentUser"));         }     }     @Test     void queryBossTask() {         // 查询老板分组的任务         List teacher = taskService.createTaskQuery().taskCandidateGroup("b").list();         for (Task task : teacher) {             // 根据任务id查询当前任务参数             Map variables = taskService.getVariables(task.getId());             System.out.println(variables.get("day") + "," + variables.get("studentUser"));         }     }     @Test     void LeadApprovalTask() {         // 领导审批         List teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();         Map teacherMap = new HashMap();         teacherMap.put("outcome", "通过");         for (Task teacherTask : teacherTaskList) {             taskService.complete(teacherTask.getId(), teacherMap);         }     }     @Test     void boosApprovalTask() {         // 老板审批         List teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();         Map teacherMap = new HashMap();         teacherMap.put("outcome", "通过");         for (Task teacherTask : teacherTaskList) {             taskService.complete(teacherTask.getId(), teacherMap);         }     }     @Test     void queryHistory() {         List processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").orderByStartTime().desc().list();         if (CollectionUtils.isEmpty(processInstance)) {             System.out.println("------------------------------------------");         }         // 获取最近的一个流程         List activities = historyService.createHistoricActivityInstanceQuery()                 .processInstanceId(processInstance.get(0).getId())                 // 只查询已经完成的活动                 .finished()                 // 按照结束时间排序                 .orderByHistoricActivityInstanceEndTime().desc()                 .list();         List collect = activities.stream().map(a -> "活动名称:" + a.getActivityName() + ";活动执行时间:" + a.getDurationInMillis() + "毫秒").collect(Collectors.toList());         for (String s : collect) {             System.out.println(s);         }     }     @Test     void Test() {         // 发起请假         Map map = new HashMap();         map.put("day", 5);         map.put("studentUser", "小明");         ProcessInstance studentLeave = runtimeService.startProcessInstanceByKey("leave", map);         Task task = taskService.createTaskQuery().processInstanceId(studentLeave.getId()).singleResult();         taskService.complete(task.getId());         // 领导审批         List teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();         Map teacherMap = new HashMap();         teacherMap.put("outcome", "通过");         for (Task teacherTask : teacherTaskList) {             taskService.complete(teacherTask.getId(), teacherMap);         }         // 老板审批         List principalTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();         Map principalMap = new HashMap();         principalMap.put("outcome", "通过");         for (Task principalTask : principalTaskList) {             taskService.complete(principalTask.getId(), principalMap);         }         // 查看历史         List activities = historyService.createHistoricActivityInstanceQuery()                 .processInstanceId(studentLeave.getId())                 .finished()                 .orderByHistoricActivityInstanceEndTime().asc()                 .list();         for (HistoricActivityInstance activity : activities) {             System.out.println(activity.getActivityName());         }     } } 4.3 启动流程代码详解

流程实例

现在已经在流程引擎中部署了流程定义,因此可以使用这个流程定义作为“蓝图”启动流程实例。

我们使用RuntimeService启动一个流程实例。

收集的数据作为一个java.util.Map实例传递,其中的键就是之后用于获取变量的标识符。

这个流程实例使用key启动。这个key就是BPMN 2.0 XML文件中设置的id属性。

在这个例子里是StudentLeave。

这两个就是在XML文件钟定义的变量

flowable:assignee="${studentUser}" ${outcome==‘通过’} ${day       

领导对应的BPMN 2.0 XML属性 通过candidateGroups可以查询领导下可以审批的任务

老板对应的BPMN 2.0 XML属性 通过candidateGroups可以查询老板下可以审批的任务

员工发起请假流程代码:

@SpringBootTest class JavaSkillPointApplicationTests {     @Autowired     private RuntimeService runtimeService;     @Autowired     private TaskService taskService;     @Autowired     private HistoryService historyService;     @Test     void sub() {         // 员工提交请假申请         Map map = new HashMap();         map.put("day", 5);         map.put("studentUser", "小明");         // leave为员工请假流程xml文件中的id         ProcessInstance leave= runtimeService.startProcessInstanceByKey("leave", map);            Task task = taskService.createTaskQuery().processInstanceId(leave.getId()).singleResult();         // 员工提交请假申请         taskService.complete(task.getId());     } }

查询领导审批任务代码:

@SpringBootTest class JavaSkillPointApplicationTests {     @Autowired     private RuntimeService runtimeService;     @Autowired     private TaskService taskService;     @Autowired     private HistoryService historyService;     @Test     void queryLeadTask() {         // 查询领导分组的任务         List teacher = taskService.createTaskQuery().taskCandidateGroup("a").list();         for (Task task : teacher) {             // 根据任务id查询当前任务参数             Map variables = taskService.getVariables(task.getId());             System.out.println(variables.get("day") + "," + variables.get("studentUser"));         }     } }

查询老板审批任务代码:

@SpringBootTest class JavaSkillPointApplicationTests {     @Autowired     private RuntimeService runtimeService;     @Autowired     private TaskService taskService;     @Autowired     private HistoryService historyService;     @Test     void queryBossTask() {         // 查询老板分组的任务         List teacher = taskService.createTaskQuery().taskCandidateGroup("b").list();         for (Task task : teacher) {             // 根据任务id查询当前任务参数             Map variables = taskService.getVariables(task.getId());             System.out.println(variables.get("day") + "," + variables.get("studentUser"));         }     } }

领导进行审批任务代码(通过或驳回):

@SpringBootTest class JavaSkillPointApplicationTests {     @Autowired     private RuntimeService runtimeService;     @Autowired     private TaskService taskService;     @Autowired     private HistoryService historyService;   @Test     void LeadApprovalTask() {         // 领导审批         List teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();         Map teacherMap = new HashMap();         teacherMap.put("outcome", "通过");         for (Task teacherTask : teacherTaskList) {             taskService.complete(teacherTask.getId(), teacherMap);         }     } }

老板进行审批任务代码(通过或驳回):

@SpringBootTest class JavaSkillPointApplicationTests {     @Autowired     private RuntimeService runtimeService;     @Autowired     private TaskService taskService;     @Autowired     private HistoryService historyService;   @Test     void boosApprovalTask() {         // 老板审批         List teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();         Map teacherMap = new HashMap();         teacherMap.put("outcome", "通过");         for (Task teacherTask : teacherTaskList) {             taskService.complete(teacherTask.getId(), teacherMap);         }     } }

查询最进一个流程所走的节点时间(这里的流程不能走完,如果day=5,我们先执行领导审批,则开始查询流程开始到领导审批的节点信息,如果我们进行了老板审批,则该流程结束,此时查询为流程节点信息为空,因为该流程已执行完。):

@SpringBootTest class JavaSkillPointApplicationTests {     @Autowired     private RuntimeService runtimeService;     @Autowired     private TaskService taskService;     @Autowired     private HistoryService historyService;   @Test     void queryHistory() {         List processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").orderByStartTime().desc().list();         if (CollectionUtils.isEmpty(processInstance)) {             System.out.println("------------------------------------------");         }         // 获取最近的一个流程         List activities = historyService.createHistoricActivityInstanceQuery()                 .processInstanceId(processInstance.get(0).getId())                 // 只查询已经完成的活动                 .finished()                 // 按照结束时间排序                 .orderByHistoricActivityInstanceEndTime().desc()                 .list();         List collect = activities.stream().map(a -> "活动名称:" + a.getActivityName() + ";活动执行时间:" + a.getDurationInMillis() + "毫秒").collect(Collectors.toList());         for (String s : collect) {             System.out.println(s);         }     } }

员工请假天数小于等于2时

@SpringBootTest class JavaSkillPointApplicationTests {     @Autowired     private RuntimeService runtimeService;     @Autowired     private TaskService taskService;     @Autowired     private HistoryService historyService;   @Test     void Test() {         // 发起请假         Map map = new HashMap();         map.put("day", 2);         map.put("studentUser", "小明");         ProcessInstance studentLeave = runtimeService.startProcessInstanceByKey("leave", map);         Task task = taskService.createTaskQuery().processInstanceId(studentLeave.getId()).singleResult();         taskService.complete(task.getId());         // 领导审批         List teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();         Map teacherMap = new HashMap();         teacherMap.put("outcome", "通过");         for (Task teacherTask : teacherTaskList) {             taskService.complete(teacherTask.getId(), teacherMap);         }         // 老板审批         List principalTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();         Map principalMap = new HashMap();         principalMap.put("outcome", "通过");         for (Task principalTask : principalTaskList) {             taskService.complete(principalTask.getId(), principalMap);         }         // 查看历史         List activities = historyService.createHistoricActivityInstanceQuery()                 .processInstanceId(studentLeave.getId())                 .finished()                 .orderByHistoricActivityInstanceEndTime().asc()                 .list();         for (HistoricActivityInstance activity : activities) {             System.out.println(activity.getActivityName());         }     } } 808bfddae305bdec9abd6a58618e5d00.png

员工请假天数大于2时

@SpringBootTest class JavaSkillPointApplicationTests {     @Autowired     private RuntimeService runtimeService;     @Autowired     private TaskService taskService;     @Autowired     private HistoryService historyService;   @Test     void Test() {         // 发起请假         Map map = new HashMap();         map.put("day", 5);         map.put("studentUser", "小明");         ProcessInstance studentLeave = runtimeService.startProcessInstanceByKey("leave", map);         Task task = taskService.createTaskQuery().processInstanceId(studentLeave.getId()).singleResult();         taskService.complete(task.getId());         // 领导审批         List teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();         Map teacherMap = new HashMap();         teacherMap.put("outcome", "通过");         for (Task teacherTask : teacherTaskList) {             taskService.complete(teacherTask.getId(), teacherMap);         }         // 老板审批         List principalTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();         Map principalMap = new HashMap();         principalMap.put("outcome", "通过");         for (Task principalTask : principalTaskList) {             taskService.complete(principalTask.getId(), principalMap);         }         // 查看历史         List activities = historyService.createHistoricActivityInstanceQuery()                 .processInstanceId(studentLeave.getId())                 .finished()                 .orderByHistoricActivityInstanceEndTime().asc()                 .list();         for (HistoricActivityInstance activity : activities) {             System.out.println(activity.getActivityName());         }     } } 0ef36ee12c50981794b76bbe45fa182e.png 4.4 flowable数据库说明

Flowable的所有数据库表都以ACT_开头。第二部分是说明表用途的两字符标示符。服务API的命名也大略符合这个规则。

ACT_RE_* :’ RE ’表示repository(存储)。RepositoryService接口操作的表。带此前缀的表包含的是静态信息,如,流程定义,流程的资源(图片,规则等)。

ACT_RU_* :’ RU ’表示runtime。这是运行时的表存储着流程变量,用户任务,变量,职责(job)等运行时的数据。flowable只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。

ACT_ID_* : ’ ID ’表示identity(组织机构)。这些表包含标识的信息,如用户,用户组,等等。

ACT_HI_* : ’ HI ’表示history。就是这些表包含着历史的相关数据,如结束的流程实例,变量,任务,等等。

ACT_GE_* : 普通数据,各种情况都使用的数据。

一般数据对应表

cb47a2557e9b85615dd0b0fd486604b5.png

流程历史记录表

c8618235ea281ff461f737557d492870.png

用户用户组表

346ca09ee41bfbd5c9589f1faa739d7d.png

流程定义表

04df3a81d46a779e837dd54561c00851.png

运行实例表

d781859c06e489eec6f522807e5b46fa.png

其他表

b5474368ba253a465c958e97e1c01a45.png

来源:blog.csdn.net/qq_43372633/

article/details/130598488

c6d8cd9a1486839f076f2bb91b30d158.gif

SpringBoot+Redis BitMap 实现签到与统计功能

高逼格的 SQL 写法:行行比较

今年面试好激烈。

停止在 SpringBoot 中使用字段注入!

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。 获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3