当前位置:   article > 正文

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

工作流flowable

全新前后端微商城项目,手把手教学!

全新前后端微商城项目,手把手教学!

全新前后端微商城项目,手把手教学!

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

1工作流介绍

1.1 为什么使用工作流

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

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

1.2 工作流是什么

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

简单状态

75cf605e86843ad257028c1b6e1803b5.png

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

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

复杂状态

d9118afdeb385bacff0a8f695711bf92.png

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

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

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

1.3 工作流不能解决的问题

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

8af97c3a6d1e77e1cfa06bc394e7be48.png

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

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

2BPMN2.0协议

2.1 什么是BPMN2.0协议

3f958910abb646628146cd411b0bc8b2.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协议的所有任务其实是从一个抽象任务派生而来的,抽象任务会有如下行为:

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

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

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

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

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

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

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

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

66c9470aea676b9fa2a7be4ad8768fd8.png

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

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

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

2.3 如何使用BPMN2.0协议

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

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

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

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

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

f61a42c6f0a8b31c6120b78d43050575.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实战

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

  1. 我会用flowable来实现

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

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

  4. 启动springboot项目 (启动)

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

4.1 本地安装,启动flowable-ui

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

659963a915e430c5993ecbab272beb6b.png

JavaSkillPointApplication文件

  1. package com.weige.javaskillpoint;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class JavaSkillPointApplication {
  6.     public static void main(String[] args) {
  7.         SpringApplication.run(JavaSkillPointApplication.class, args);
  8.     }
  9. }

application.yml文件

  1. server:
  2.   port: 8088
  3. flowable:
  4.   idm:
  5.     app:
  6.       admin:
  7.         # 登录的用户名
  8.         user-id: admin
  9.         # 登录的密码
  10.         password: admin
  11.         # 用户的名字
  12.         first-name: wei
  13.         last-name: kai
  14. spring:
  15.   # mysql连接信息
  16.   datasource:
  17.     # mysql8之后
  18.     driver-class-name: com.mysql.cj.jdbc.Driver
  19.     # mysql8之前
  20. #    driver-class-name: com.mysql.jdbc.Driver
  21.     url: jdbc:mysql://43.143.132.109:3306/flowable?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT&nullCatalogMeansCurrent=true
  22.     username: root
  23.     password: ******
  24.   jpa:
  25.     properties:
  26.       hibernate:
  27.         hbm2ddl:
  28.           auto: update
  29.         dialect: org.hibernate.dialect.MySQL5InnoDBDialect
  30.     open-in-view: true

注意:

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

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

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

c86118acdd19578a4bc8849f83c0485a.png

pom.xml文件

  1. <dependencies>
  2.     <!--Springboot项目自带 -->
  3.     <dependency>
  4.         <groupId>org.springframework.boot</groupId>
  5.         <artifactId>spring-boot-starter</artifactId>
  6.     </dependency>
  7.     <dependency>
  8.         <groupId>org.springframework.boot</groupId>
  9.         <artifactId>spring-boot-starter-test</artifactId>
  10.         <scope>test</scope>
  11.     </dependency>
  12.     <!--Springboot Web项目 -->
  13.     <dependency>
  14.         <groupId>org.springframework.boot</groupId>
  15.         <artifactId>spring-boot-starter-web</artifactId>
  16.     </dependency>
  17.     <!--lombok -->
  18.     <dependency>
  19.         <groupId>org.projectlombok</groupId>
  20.         <artifactId>lombok</artifactId>
  21.         <version>1.18.22</version>
  22.     </dependency>
  23.     <!-- idm依赖提供身份认证 -->
  24.     <dependency>
  25.         <groupId>org.flowable</groupId>
  26.         <artifactId>flowable-spring-boot-starter-ui-idm</artifactId>
  27.         <version>6.7.1</version>
  28.     </dependency>
  29.     <!-- modeler绘制流程图 -->
  30.     <dependency>
  31.         <groupId>org.flowable</groupId>
  32.         <artifactId>flowable-spring-boot-starter-ui-modeler</artifactId>
  33.         <version>6.7.1</version>
  34.     </dependency>
  35.     <!-- jpa -->
  36.     <dependency>
  37.         <groupId>org.springframework.boot</groupId>
  38.         <artifactId>spring-boot-starter-data-jpa</artifactId>
  39.     </dependency>
  40.     <!-- mysql驱动 -->
  41.     <dependency>
  42.         <groupId>mysql</groupId>
  43.         <artifactId>mysql-connector-java</artifactId>
  44.     </dependency>
  45.     <!-- flowable -->
  46.     <dependency>
  47.         <groupId>org.flowable</groupId>
  48.         <artifactId>flowable-spring-boot-starter</artifactId>
  49.         <version>6.7.1</version>
  50.     </dependency>
  51. </dependencies>

启动项目

b7ed28a66e86f1c9e28f2fb057a921cb.png

访问http://localhost:8088/

0ec18e8df96c26f1224eb8b9f8ef7c05.png 8809b0a20e83f4b84a94a3b6f0e7ef22.png 8de7a9e604af9c111fa251d65b18591d.png 350e76bdc05ef9bd779b017516ddb098.png 99af2d7c4d85c3587fe98d67e0495d66.png

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

c257c8049d63c02a822135723ac3a871.png d6870ff659abfee9cc55cc61fab48575.png 3c5390501a03e5161121013b5dcfc2b3.png 661b28d917303956be4d73ac75da9d2e.png bf0a143c688e2489f829e3d590ff298c.png 0efc39abf2ffd1d92cefbf48fae88e83.png 5243ac706a2056a24bae1e530f225e0e.png c6c89b9dd5d02a170eadaba010fdb9ad.png 3764ec3335910dc1a60aa634e861c496.png bfc42f123c4b3cbed09ed33271d3e296.png 7d3564c95676673725b1cc00fa4ef749.png 7bcdf192038b5674b40e2ee465e97e79.png bf1be48b7c9aceffdc2cf69e150af4cb.png 1196756348c812b2955aaa11e1ced034.png

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

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

7014fd8b55ec3d5c3fa93d23fbc96d6d.png

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

创建springboot项目

项目目录

14b0c0cee5a90e06ff35e1b626588464.png

JavaSkillPointApplication文件

  1. package com.weige.javaskillpoint;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class JavaSkillPointApplication {
  6.     public static void main(String[] args) {
  7.         SpringApplication.run(JavaSkillPointApplication.class, args);
  8.     }
  9. }

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

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <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" exporter="Flowable Open Source Modeler" exporterVersion="6.7.1">
  3.   <process id="leave" name="公司员工请假流程" isExecutable="true">
  4.     <documentation>公司员工请假流程</documentation>
  5.     <startEvent id="startEvent1" name="开始 " flowable:formFieldValidation="true"></startEvent>
  6.     <userTask id="apply" name="请假申请 " flowable:assignee="${studentUser}" flowable:formFieldValidation="true">
  7.       <extensionElements>
  8.         <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
  9.       </extensionElements>
  10.     </userTask>
  11.     <userTask id="teacherPass" name="领导审批 " flowable:candidateGroups="a" flowable:formFieldValidation="true"></userTask>
  12.     <exclusiveGateway id="judgeTask" name="判断是否大于2天"></exclusiveGateway>
  13.     <sequenceFlow id="applyFlow" name="申请流程 " sourceRef="apply" targetRef="teacherPass"></sequenceFlow>
  14.     <userTask id="principalPass" name="老板审批" flowable:candidateGroups="b" flowable:formFieldValidation="true"></userTask>
  15.     <endEvent id="sid-EA94CD8C-3002-421E-BD64-B46EBDD8070F" name="结束"></endEvent>
  16.     <sequenceFlow id="principalCheck" name="通过" sourceRef="principalPass" targetRef="sid-EA94CD8C-3002-421E-BD64-B46EBDD8070F">
  17.       <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
  18.     </sequenceFlow>
  19.     <sequenceFlow id="sid-8BA4821E-F022-45C8-B86B-CD7052B9FB05" name="流程开始 " sourceRef="startEvent1" targetRef="apply"></sequenceFlow>
  20.     <sequenceFlow id="teacherPassFlow" name="通过 " sourceRef="teacherPass" targetRef="judgeTask">
  21.       <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
  22.     </sequenceFlow>
  23.     <sequenceFlow id="teacherNotPassFlow" name="驳回" sourceRef="teacherPass" targetRef="apply">
  24.       <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
  25.     </sequenceFlow>
  26.     <sequenceFlow id="judgeLess" name="小于2天" sourceRef="judgeTask" targetRef="sid-EA94CD8C-3002-421E-BD64-B46EBDD8070F">
  27.       <conditionExpression xsi:type="tFormalExpression"><![CDATA[${day<=2}]]></conditionExpression>
  28.     </sequenceFlow>
  29.     <sequenceFlow id="judgeMore" name="大于2天 " sourceRef="judgeTask" targetRef="principalPass">
  30.       <conditionExpression xsi:type="tFormalExpression"><![CDATA[${day>2}]]></conditionExpression>
  31.     </sequenceFlow>
  32.     <sequenceFlow id="principalNotPassFlow" name="驳回 " sourceRef="principalPass" targetRef="apply">
  33.       <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
  34.     </sequenceFlow>
  35.   </process>
  36.   <bpmndi:BPMNDiagram id="BPMNDiagram_leave">
  37.     <bpmndi:BPMNPlane bpmnElement="leave" id="BPMNPlane_leave">
  38.       <bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
  39.         <omgdc:Bounds height="30.0" width="30.0" x="74.99999776482586" y="159.99999329447763"></omgdc:Bounds>
  40.       </bpmndi:BPMNShape>
  41.       <bpmndi:BPMNShape bpmnElement="apply" id="BPMNShape_apply">
  42.         <omgdc:Bounds height="80.0" width="100.0" x="254.99999620020395" y="134.99999798834327"></omgdc:Bounds>
  43.       </bpmndi:BPMNShape>
  44.       <bpmndi:BPMNShape bpmnElement="teacherPass" id="BPMNShape_teacherPass">
  45.         <omgdc:Bounds height="79.99999999999994" width="100.0" x="494.9999926239253" y="134.99999798834327"></omgdc:Bounds>
  46.       </bpmndi:BPMNShape>
  47.       <bpmndi:BPMNShape bpmnElement="judgeTask" id="BPMNShape_judgeTask">
  48.         <omgdc:Bounds height="40.0" width="40.0" x="734.9999780952935" y="154.99999567866334"></omgdc:Bounds>
  49.       </bpmndi:BPMNShape>
  50.       <bpmndi:BPMNShape bpmnElement="principalPass" id="BPMNShape_principalPass">
  51.         <omgdc:Bounds height="80.0" width="100.0" x="704.9999785423283" y="434.999993517995"></omgdc:Bounds>
  52.       </bpmndi:BPMNShape>
  53.       <bpmndi:BPMNShape bpmnElement="sid-EA94CD8C-3002-421E-BD64-B46EBDD8070F" id="BPMNShape_sid-EA94CD8C-3002-421E-BD64-B46EBDD8070F">
  54.         <omgdc:Bounds height="28.0" width="28.0" x="914.9999863654377" y="160.99999089539082"></omgdc:Bounds>
  55.       </bpmndi:BPMNShape>
  56.       <bpmndi:BPMNEdge bpmnElement="judgeLess" id="BPMNEdge_judgeLess" flowable:sourceDockerX="20.0" flowable:sourceDockerY="20.0" flowable:targetDockerX="14.0" flowable:targetDockerY="14.0">
  57.         <omgdi:waypoint x="774.9442491776681" y="174.9999951288619"></omgdi:waypoint>
  58.         <omgdi:waypoint x="914.9999863654377" y="174.9999912788773"></omgdi:waypoint>
  59.       </bpmndi:BPMNEdge>
  60.       <bpmndi:BPMNEdge bpmnElement="judgeMore" id="BPMNEdge_judgeMore" flowable:sourceDockerX="20.0" flowable:sourceDockerY="20.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
  61.         <omgdi:waypoint x="754.9999781250214" y="194.94667175689042"></omgdi:waypoint>
  62.         <omgdi:waypoint x="754.9999784827237" y="434.999993517995"></omgdi:waypoint>
  63.       </bpmndi:BPMNEdge>
  64.       <bpmndi:BPMNEdge bpmnElement="principalNotPassFlow" id="BPMNEdge_principalNotPassFlow" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
  65.         <omgdi:waypoint x="704.9999785423213" y="474.88912948633504"></omgdi:waypoint>
  66.         <omgdi:waypoint x="304.0" y="474.0"></omgdi:waypoint>
  67.         <omgdi:waypoint x="304.86621744522387" y="214.94999798834328"></omgdi:waypoint>
  68.       </bpmndi:BPMNEdge>
  69.       <bpmndi:BPMNEdge bpmnElement="teacherNotPassFlow" id="BPMNEdge_teacherNotPassFlow" flowable:sourceDockerX="50.0" flowable:sourceDockerY="39.99999999999997" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
  70.         <omgdi:waypoint x="544.9999926239253" y="134.99999798834327"></omgdi:waypoint>
  71.         <omgdi:waypoint x="544.9999926239253" y="75.0"></omgdi:waypoint>
  72.         <omgdi:waypoint x="306.0" y="75.0"></omgdi:waypoint>
  73.         <omgdi:waypoint x="305.3994977262591" y="134.99999798834327"></omgdi:waypoint>
  74.       </bpmndi:BPMNEdge>
  75.       <bpmndi:BPMNEdge bpmnElement="applyFlow" id="BPMNEdge_applyFlow" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="50.0" flowable:targetDockerY="39.99999999999997">
  76.         <omgdi:waypoint x="354.94999620020394" y="174.99999798834324"></omgdi:waypoint>
  77.         <omgdi:waypoint x="494.9999926238438" y="174.99999798834324"></omgdi:waypoint>
  78.       </bpmndi:BPMNEdge>
  79.       <bpmndi:BPMNEdge bpmnElement="principalCheck" id="BPMNEdge_principalCheck" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="14.0" flowable:targetDockerY="14.0">
  80.         <omgdi:waypoint x="804.9499785423245" y="474.71097808621374"></omgdi:waypoint>
  81.         <omgdi:waypoint x="928.0" y="474.0"></omgdi:waypoint>
  82.         <omgdi:waypoint x="928.953164519099" y="188.9499145334794"></omgdi:waypoint>
  83.       </bpmndi:BPMNEdge>
  84.       <bpmndi:BPMNEdge bpmnElement="sid-8BA4821E-F022-45C8-B86B-CD7052B9FB05" id="BPMNEdge_sid-8BA4821E-F022-45C8-B86B-CD7052B9FB05" flowable:sourceDockerX="15.0" flowable:sourceDockerY="15.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
  85.         <omgdi:waypoint x="104.94999736929918" y="174.99999362086504"></omgdi:waypoint>
  86.         <omgdi:waypoint x="254.99999620020395" y="174.9999968967466"></omgdi:waypoint>
  87.       </bpmndi:BPMNEdge>
  88.       <bpmndi:BPMNEdge bpmnElement="teacherPassFlow" id="BPMNEdge_teacherPassFlow" flowable:sourceDockerX="50.0" flowable:sourceDockerY="39.99999999999997" flowable:targetDockerX="20.0" flowable:targetDockerY="20.0">
  89.         <omgdi:waypoint x="594.949992623855" y="174.9999974384194"></omgdi:waypoint>
  90.         <omgdi:waypoint x="734.9999775100968" y="174.99999589808294"></omgdi:waypoint>
  91.       </bpmndi:BPMNEdge>
  92.     </bpmndi:BPMNPlane>
  93.   </bpmndi:BPMNDiagram>
  94. </definitions>

application.yml

  1. server:
  2.   port: 8081
  3. spring:
  4.   # mysql连接信息
  5.   datasource:
  6.     # mysql8之后
  7. #    driver-class-name: com.mysql.cj.jdbc.Driver
  8.     # mysql8之前
  9.     driver-class-name: com.mysql.jdbc.Driver
  10.     url: jdbc:mysql://43.143.132.109:3306/flowable?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
  11.     username: root
  12.     password: ******

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

JavaSkillPointApplicationTests测试文件

  1. package com.weige.javaskillpoint;
  2. import org.flowable.engine.HistoryService;
  3. import org.flowable.engine.RuntimeService;
  4. import org.flowable.engine.TaskService;
  5. import org.flowable.engine.history.HistoricActivityInstance;
  6. import org.flowable.engine.runtime.ProcessInstance;
  7. import org.flowable.task.api.Task;
  8. import org.junit.jupiter.api.Test;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.boot.test.context.SpringBootTest;
  11. import org.springframework.util.CollectionUtils;
  12. import java.util.HashMap;
  13. import java.util.List;
  14. import java.util.Map;
  15. import java.util.stream.Collectors;
  16. @SpringBootTest
  17. class JavaSkillPointApplicationTests {
  18.     @Autowired
  19.     private RuntimeService runtimeService;
  20.     @Autowired
  21.     private TaskService taskService;
  22.     @Autowired
  23.     private HistoryService historyService;
  24.     @Test
  25.     void sub() {
  26.         // 员工提交请假申请
  27.         Map<String, Object> map = new HashMap<>();
  28.         map.put("day"5);
  29.         map.put("studentUser""小明");
  30.         // leave为员工请假流程xml文件中的id
  31.         ProcessInstance a1 = runtimeService.startProcessInstanceByKey("leave"map);
  32.         Task task = taskService.createTaskQuery().processInstanceId(a1.getId()).singleResult();
  33.         taskService.complete(task.getId());
  34.     }
  35.     @Test
  36.     void queryLeadTask() {
  37.         // 查询领导分组的任务
  38.         List<Task> teacher = taskService.createTaskQuery().taskCandidateGroup("a").list();
  39.         for (Task task : teacher) {
  40.             // 根据任务id查询当前任务参数
  41.             Map<String, Object> variables = taskService.getVariables(task.getId());
  42.             System.out.println(variables.get("day") + "," + variables.get("studentUser"));
  43.         }
  44.     }
  45.     @Test
  46.     void queryBossTask() {
  47.         // 查询老板分组的任务
  48.         List<Task> teacher = taskService.createTaskQuery().taskCandidateGroup("b").list();
  49.         for (Task task : teacher) {
  50.             // 根据任务id查询当前任务参数
  51.             Map<String, Object> variables = taskService.getVariables(task.getId());
  52.             System.out.println(variables.get("day") + "," + variables.get("studentUser"));
  53.         }
  54.     }
  55.     @Test
  56.     void LeadApprovalTask() {
  57.         // 领导审批
  58.         List<Task> teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();
  59.         Map<String, Object> teacherMap = new HashMap<>();
  60.         teacherMap.put("outcome""通过");
  61.         for (Task teacherTask : teacherTaskList) {
  62.             taskService.complete(teacherTask.getId(), teacherMap);
  63.         }
  64.     }
  65.     @Test
  66.     void boosApprovalTask() {
  67.         // 老板审批
  68.         List<Task> teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();
  69.         Map<String, Object> teacherMap = new HashMap<>();
  70.         teacherMap.put("outcome""通过");
  71.         for (Task teacherTask : teacherTaskList) {
  72.             taskService.complete(teacherTask.getId(), teacherMap);
  73.         }
  74.     }
  75.     @Test
  76.     void queryHistory() {
  77.         List<ProcessInstance> processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").orderByStartTime().desc().list();
  78.         if (CollectionUtils.isEmpty(processInstance)) {
  79.             System.out.println("------------------------------------------");
  80.         }
  81.         // 获取最近的一个流程
  82.         List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
  83.                 .processInstanceId(processInstance.get(0).getId())
  84.                 // 只查询已经完成的活动
  85.                 .finished()
  86.                 // 按照结束时间排序
  87.                 .orderByHistoricActivityInstanceEndTime().desc()
  88.                 .list();
  89.         List<String> collect = activities.stream().map(a -> "活动名称:" + a.getActivityName() + ";活动执行时间:" + a.getDurationInMillis() + "毫秒").collect(Collectors.toList());
  90.         for (String s : collect) {
  91.             System.out.println(s);
  92.         }
  93.     }
  94.     @Test
  95.     void Test() {
  96.         // 发起请假
  97.         Map<String, Object> map = new HashMap<>();
  98.         map.put("day"5);
  99.         map.put("studentUser""小明");
  100.         ProcessInstance studentLeave = runtimeService.startProcessInstanceByKey("leave"map);
  101.         Task task = taskService.createTaskQuery().processInstanceId(studentLeave.getId()).singleResult();
  102.         taskService.complete(task.getId());
  103.         // 领导审批
  104.         List<Task> teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();
  105.         Map<String, Object> teacherMap = new HashMap<>();
  106.         teacherMap.put("outcome""通过");
  107.         for (Task teacherTask : teacherTaskList) {
  108.             taskService.complete(teacherTask.getId(), teacherMap);
  109.         }
  110.         // 老板审批
  111.         List<Task> principalTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();
  112.         Map<String, Object> principalMap = new HashMap<>();
  113.         principalMap.put("outcome""通过");
  114.         for (Task principalTask : principalTaskList) {
  115.             taskService.complete(principalTask.getId(), principalMap);
  116.         }
  117.         // 查看历史
  118.         List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
  119.                 .processInstanceId(studentLeave.getId())
  120.                 .finished()
  121.                 .orderByHistoricActivityInstanceEndTime().asc()
  122.                 .list();
  123.         for (HistoricActivityInstance activity : activities) {
  124.             System.out.println(activity.getActivityName());
  125.         }
  126.     }
  127. }

4.3 启动流程代码详解

流程实例

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

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

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

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

  • 在这个例子里是StudentLeave。

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

  1. flowable:assignee="${studentUser}"
  2. ${outcome==‘通过’}
  3. ${day <= 2}

员工对应的BPMN 2.0 XML属性,我们通过id启动流程

<process id="leave" name="公司员工请假流程" isExecutable="true">

员工对应的BPMN 2.0 XML属性 通过assignee员工可以发起请假流程

  1. <userTask id="apply" name="请假申请 " flowable:assignee="${studentUser}" flowable:formFieldValidation="true">
  2.     <extensionElements>
  3.        <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
  4.      </extensionElements>
  5. </userTask>

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

<userTask id="teacherPass" name="领导审批 " flowable:candidateGroups="a" flowable:formFieldValidation="true"></userTask>

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

<userTask id="principalPass" name="老板审批" flowable:candidateGroups="b" flowable:formFieldValidation="true"></userTask>

员工发起请假流程代码:

  1. @SpringBootTest
  2. class JavaSkillPointApplicationTests {
  3.     @Autowired
  4.     private RuntimeService runtimeService;
  5.     @Autowired
  6.     private TaskService taskService;
  7.     @Autowired
  8.     private HistoryService historyService;
  9.     @Test
  10.     void sub() {
  11.         // 员工提交请假申请
  12.         Map<String, Object> map = new HashMap<>();
  13.         map.put("day"5);
  14.         map.put("studentUser""小明");
  15.         // leave为员工请假流程xml文件中的id
  16.         ProcessInstance leave= runtimeService.startProcessInstanceByKey("leave"map);
  17.   
  18.         Task task = taskService.createTaskQuery().processInstanceId(leave.getId()).singleResult();
  19.         // 员工提交请假申请
  20.         taskService.complete(task.getId());
  21.     }
  22. }

查询领导审批任务代码:

  1. @SpringBootTest
  2. class JavaSkillPointApplicationTests {
  3.     @Autowired
  4.     private RuntimeService runtimeService;
  5.     @Autowired
  6.     private TaskService taskService;
  7.     @Autowired
  8.     private HistoryService historyService;
  9.     @Test
  10.     void queryLeadTask() {
  11.         // 查询领导分组的任务
  12.         List<Task> teacher = taskService.createTaskQuery().taskCandidateGroup("a").list();
  13.         for (Task task : teacher) {
  14.             // 根据任务id查询当前任务参数
  15.             Map<String, Object> variables = taskService.getVariables(task.getId());
  16.             System.out.println(variables.get("day") + "," + variables.get("studentUser"));
  17.         }
  18.     }
  19. }

查询老板审批任务代码:

  1. @SpringBootTest
  2. class JavaSkillPointApplicationTests {
  3.     @Autowired
  4.     private RuntimeService runtimeService;
  5.     @Autowired
  6.     private TaskService taskService;
  7.     @Autowired
  8.     private HistoryService historyService;
  9.     @Test
  10.     void queryBossTask() {
  11.         // 查询老板分组的任务
  12.         List<Task> teacher = taskService.createTaskQuery().taskCandidateGroup("b").list();
  13.         for (Task task : teacher) {
  14.             // 根据任务id查询当前任务参数
  15.             Map<String, Object> variables = taskService.getVariables(task.getId());
  16.             System.out.println(variables.get("day") + "," + variables.get("studentUser"));
  17.         }
  18.     }
  19. }

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

  1. @SpringBootTest
  2. class JavaSkillPointApplicationTests {
  3.     @Autowired
  4.     private RuntimeService runtimeService;
  5.     @Autowired
  6.     private TaskService taskService;
  7.     @Autowired
  8.     private HistoryService historyService;
  9.   @Test
  10.     void LeadApprovalTask() {
  11.         // 领导审批
  12.         List<Task> teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();
  13.         Map<String, Object> teacherMap = new HashMap<>();
  14.         teacherMap.put("outcome""通过");
  15.         for (Task teacherTask : teacherTaskList) {
  16.             taskService.complete(teacherTask.getId(), teacherMap);
  17.         }
  18.     }
  19. }

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

  1. @SpringBootTest
  2. class JavaSkillPointApplicationTests {
  3.     @Autowired
  4.     private RuntimeService runtimeService;
  5.     @Autowired
  6.     private TaskService taskService;
  7.     @Autowired
  8.     private HistoryService historyService;
  9.   @Test
  10.     void boosApprovalTask() {
  11.         // 老板审批
  12.         List<Task> teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();
  13.         Map<String, Object> teacherMap = new HashMap<>();
  14.         teacherMap.put("outcome""通过");
  15.         for (Task teacherTask : teacherTaskList) {
  16.             taskService.complete(teacherTask.getId(), teacherMap);
  17.         }
  18.     }
  19. }

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

  1. @SpringBootTest
  2. class JavaSkillPointApplicationTests {
  3.     @Autowired
  4.     private RuntimeService runtimeService;
  5.     @Autowired
  6.     private TaskService taskService;
  7.     @Autowired
  8.     private HistoryService historyService;
  9.   @Test
  10.     void queryHistory() {
  11.         List<ProcessInstance> processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").orderByStartTime().desc().list();
  12.         if (CollectionUtils.isEmpty(processInstance)) {
  13.             System.out.println("------------------------------------------");
  14.         }
  15.         // 获取最近的一个流程
  16.         List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
  17.                 .processInstanceId(processInstance.get(0).getId())
  18.                 // 只查询已经完成的活动
  19.                 .finished()
  20.                 // 按照结束时间排序
  21.                 .orderByHistoricActivityInstanceEndTime().desc()
  22.                 .list();
  23.         List<String> collect = activities.stream().map(a -> "活动名称:" + a.getActivityName() + ";活动执行时间:" + a.getDurationInMillis() + "毫秒").collect(Collectors.toList());
  24.         for (String s : collect) {
  25.             System.out.println(s);
  26.         }
  27.     }
  28. }

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

  1. @SpringBootTest
  2. class JavaSkillPointApplicationTests {
  3.     @Autowired
  4.     private RuntimeService runtimeService;
  5.     @Autowired
  6.     private TaskService taskService;
  7.     @Autowired
  8.     private HistoryService historyService;
  9.   @Test
  10.     void Test() {
  11.         // 发起请假
  12.         Map<String, Object> map = new HashMap<>();
  13.         map.put("day"2);
  14.         map.put("studentUser""小明");
  15.         ProcessInstance studentLeave = runtimeService.startProcessInstanceByKey("leave"map);
  16.         Task task = taskService.createTaskQuery().processInstanceId(studentLeave.getId()).singleResult();
  17.         taskService.complete(task.getId());
  18.         // 领导审批
  19.         List<Task> teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();
  20.         Map<String, Object> teacherMap = new HashMap<>();
  21.         teacherMap.put("outcome""通过");
  22.         for (Task teacherTask : teacherTaskList) {
  23.             taskService.complete(teacherTask.getId(), teacherMap);
  24.         }
  25.         // 老板审批
  26.         List<Task> principalTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();
  27.         Map<String, Object> principalMap = new HashMap<>();
  28.         principalMap.put("outcome""通过");
  29.         for (Task principalTask : principalTaskList) {
  30.             taskService.complete(principalTask.getId(), principalMap);
  31.         }
  32.         // 查看历史
  33.         List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
  34.                 .processInstanceId(studentLeave.getId())
  35.                 .finished()
  36.                 .orderByHistoricActivityInstanceEndTime().asc()
  37.                 .list();
  38.         for (HistoricActivityInstance activity : activities) {
  39.             System.out.println(activity.getActivityName());
  40.         }
  41.     }
  42. }
d48a25cfdc7fb0a9a902d2807fbe45b3.png

员工请假天数大于2时

  1. @SpringBootTest
  2. class JavaSkillPointApplicationTests {
  3.     @Autowired
  4.     private RuntimeService runtimeService;
  5.     @Autowired
  6.     private TaskService taskService;
  7.     @Autowired
  8.     private HistoryService historyService;
  9.   @Test
  10.     void Test() {
  11.         // 发起请假
  12.         Map<String, Object> map = new HashMap<>();
  13.         map.put("day"5);
  14.         map.put("studentUser""小明");
  15.         ProcessInstance studentLeave = runtimeService.startProcessInstanceByKey("leave"map);
  16.         Task task = taskService.createTaskQuery().processInstanceId(studentLeave.getId()).singleResult();
  17.         taskService.complete(task.getId());
  18.         // 领导审批
  19.         List<Task> teacherTaskList = taskService.createTaskQuery().taskCandidateGroup("a").list();
  20.         Map<String, Object> teacherMap = new HashMap<>();
  21.         teacherMap.put("outcome""通过");
  22.         for (Task teacherTask : teacherTaskList) {
  23.             taskService.complete(teacherTask.getId(), teacherMap);
  24.         }
  25.         // 老板审批
  26.         List<Task> principalTaskList = taskService.createTaskQuery().taskCandidateGroup("b").list();
  27.         Map<String, Object> principalMap = new HashMap<>();
  28.         principalMap.put("outcome""通过");
  29.         for (Task principalTask : principalTaskList) {
  30.             taskService.complete(principalTask.getId(), principalMap);
  31.         }
  32.         // 查看历史
  33.         List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
  34.                 .processInstanceId(studentLeave.getId())
  35.                 .finished()
  36.                 .orderByHistoricActivityInstanceEndTime().asc()
  37.                 .list();
  38.         for (HistoricActivityInstance activity : activities) {
  39.             System.out.println(activity.getActivityName());
  40.         }
  41.     }
  42. }
ce45c0f67bdec7f639dab1d92ca7ed92.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_* : 普通数据,各种情况都使用的数据。

一般数据对应表

276aab8b96fdb1fba582b65fae32d8a5.png

流程历史记录表

823d97c3e67530721a1a77d92e61d205.png

用户用户组表

ecbbddb49a2fabf9517690930b83ca62.png

流程定义表

655abba42c501494ad5694cdd6dcf8be.png

运行实例表

4111f9ab23260d17ff8c7813a4a0fbc8.png

其他表

b6df73ad0b6801753736dd58707024b6.png

来源:blog.csdn.net/qq_43372633/

article/details/130598488

  1. 推荐全新学习项目全新基于springboot+vue+vant的前后端分离的微商城项目,包括手机端微商城项目和后台管理系统,整个电商购物流程已经能流畅支持,涵盖商品浏览、搜索、商品评论、商品规格选择、加入购物车、立即购买、下单、订单支付、后台发货、退货等。功能强大,主流技术栈,非常值得学习。线上演示:https://www.markerhub.com/vueshop从文档到视频、接口调试、学习看板等方面,让项目学习更加容易,内容更加沉淀。全套视频教程约35小时,共222期,讲解非常详细细腻。下面详细为大家介绍:
  2. 架构与业务使用主流的技术架构,真正手把手教你从01如何搭建项目手脚架、项目架构分析、建表逻辑、业务分析、实现等。涵盖SpringBoot、Mybatis Plus、Jwt、Redis、Lombok、Hutool、Shiro、Elasticsearch、RabbitMq、Docker、Jenkins等技术。
  3. vueshop微商城的整个购物流程已经完善,各个模块的业务都是已经实现,涵盖商品模块、搜索模块、购物车模块、订单模块、退款模块、后台权限模块、业务数据管理模块等
  4. 更多详情请查看:
  5. 手把手教学,从0开发前后端微商城项目,主流Java技术一网打尽!
  6. 手把手教学,从0开发前后端微商城项目,主流Java技术一网打尽!
  7. 手把手教学,从0开发前后端微商城项目,主流Java技术一网打尽!
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/681173
推荐阅读
相关标签
  

闽ICP备14008679号