目录
一、Flowable介绍
二、Flowable基础
2.1、创建ProcessEngine
2.2、部署流程定义
2.3、启动流程实例
2.4、查看任务
2.5、完成任务
2.6、查看历史信息
三、Flowable流程设计器
3.1、Eclipse Designer
3.2、Flowable UI应用
3.2.1、安装部署
3.2.2、启动服务
3.2.3、用户管理
3.2.4、绘制流程
3.2.5、部署流程
3.2.6、FlowableUI 演示
部署流程
启动流程
一、Flowable介绍
Flowable是BPMN的一个基于java的软件实现,不过Flowable不仅仅包括BPMN,还有DMN决策表和CMMN Case管理引擎,并且有自己的用户管理、微服务API等一系列功能,是一个服务平台。
二、Flowable基础
2.1、创建ProcessEngine
第一步:创建一个基本的maven工程,然后添加两个依赖
-
Flowable流程引擎。使我们可以创建一个ProcessEngine流程引擎对象,并访问Flowable API。
-
一个是MySQL的数据库驱动。
在pom.xml文件中添加下列行:
<dependencies><dependency><groupId>org.flowable</groupId><artifactId>flowable-engine</artifactId><version>6.3.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.21</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency>
</dependencies>
第二步:创建一个普通的Java测试类
首先要做的是初始化ProcessEngine流程引擎实例。这是一个线程安全的对象,因此通常只需要在一个应用中初始化一次。 ProcessEngine由ProcessEngineConfiguration实例创建。该实例可以配置与调整流程引擎的设置。 通常使用一个配置XML文件创建ProcessEngineConfiguration,但是(像在这里做的一样)也可以编程方式创建它。 ProcessEngineConfiguration所需的最小配置,是数据库JDBC连接:
public class Test01 {/*** 获取流程引擎对象*/@Testpublic void testProcessEngine(){ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration().setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC").setJdbcUsername("root").setJdbcPassword("root").setJdbcDriver("com.mysql.cj.jdbc.Driver").setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);ProcessEngine processEngine = cfg.buildProcessEngine();System.out.println(processEngine);}}
注意:我们右键运行测试方法,发现报错,信息如下:
出现这种情况只需要在mysql的连接字符串中添加上nullCatalogMeansCurrent=true,设置为只查当前连接的schema库即可。
第三步:查看数据库
自动创建了很多的表,这都是用来做工作流的。
第四步:增加日志log4j
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.21</version>
</dependency>
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.21</version>
</dependency>
Log4j需要一个配置文件。在src/main/resources文件夹下添加log4j.properties文件,并写入下列内容:
log4j.rootLogger=DEBUG, CAlog4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
重新运行应用。应该可以看到关于引擎启动与创建数据库表结构的提示日志:
2.2、部署流程定义
接下来我们构建一个非常简单的请假流程,Flowable引擎需要流程定义为BPMN 2.0格式,这是一个业界广泛接受的XML标准。 在Flowable术语中,我们将其称为一个流程定义(process definition)。一个流程定义可以启动多个流程实例(process instance)。流程定义可以看做是重复执行流程的蓝图。 在这个例子中,流程定义定义了请假的各个步骤,而一个流程实例对应某个雇员提出的一个请假申请。
BPMN 2.0存储为XML,并包含可视化的部分:使用标准方式定义了每个步骤类型(人工任务,自动服务调用,等等)如何呈现,以及如何互相连接。这样BPMN 2.0标准使技术人员与业务人员能用双方都能理解的方式交流业务流程。
我们要使用的流程定义为:
流程定义说明:
-
我们假定启动流程需要提供一些信息,例如雇员名字、请假时长以及说明。当然,这些可以单独建模为流程中的第一步。 但是如果将它们作为流程的“输入信息”,就能保证只有在实际请求时才会建立一个流程实例。否则(将提交作为流程的第一步),用户可能在提交之前改变主意并取消,但流程实例已经创建了。 在某些场景中,就可能影响重要的指标(例如启动了多少申请,但还未完成),取决于业务目标。
-
左侧的圆圈叫做启动事件(start event)。这是一个流程实例的起点。
-
第一个矩形是一个用户任务(user task)。这是流程中用户操作的步骤。在这个例子中,经理需要批准或驳回申请
-
取决于经理的决定,排他网关(exclusive gateway) (带叉的菱形)会将流程实例路由至批准或驳回路径
-
如果批准,则需要将申请注册至某个外部系统,并跟着另一个用户任务,将经理的决定通知给申请人。当然也可以改为发送邮件。
-
如果驳回,则为雇员发送一封邮件通知他。
一般来说,这样的流程定义使用可视化建模工具建立,如Flowable Designer(Eclipse)或Flowable Web Modeler(Web应用)。但在这里我们直接撰写XML,以熟悉BPMN 2.0及其概念。
与上面展示的流程图对应的BPMN 2.0 XML在下面显示。请注意这只包含了“流程部分”。如果使用图形化建模工具,实际的XML文件还将包含“可视化部分”,用于描述图形信息,如流程定义中各个元素的坐标(所有的图形化信息包含在XML的BPMNDiagram标签中,作为definitions标签的子元素)。
将下面的XML保存在src/main/resources文件夹下名为holiday-request.bpmn20.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: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"xmlns:flowable="http://flowable.org/bpmn"typeLanguage="http://www.w3.org/2001/XMLSchema"expressionLanguage="http://www.w3.org/1999/XPath"targetNamespace="http://www.flowable.org/processdef"><process id="holidayRequest" name="Holiday Request" isExecutable="true"><startEvent id="startEvent"/><sequenceFlow sourceRef="startEvent" targetRef="approveTask"/><userTask id="approveTask" name="Approve or reject request"/><sequenceFlow sourceRef="approveTask" targetRef="decision"/><exclusiveGateway id="decision"/><sequenceFlow sourceRef="decision" targetRef="externalSystemCall"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved}]]></conditionExpression></sequenceFlow><sequenceFlow sourceRef="decision" targetRef="sendRejectionMail"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approved}]]></conditionExpression></sequenceFlow><serviceTask id="externalSystemCall" name="Enter holidays in external system"flowable:class="org.flowable.CallExternalSystemDelegate"/><sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/><userTask id="holidayApprovedTask" name="Holiday approved"/><sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/><serviceTask id="sendRejectionMail" name="Send out rejection email"flowable:class="org.flowable.SendRejectionMail"/><sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/><endEvent id="approveEnd"/><endEvent id="rejectEnd"/></process></definitions>
现在我们已经有了流程BPMN 2.0 XML文件,下来需要将它*部署(deploy)*到引擎中。部署一个流程定义意味着:
-
流程引擎会将XML文件存储在数据库中,这样可以在需要的时候获取它。
-
流程定义转换为内部的、可执行的对象模型,这样使用它就可以启动流程实例。
将流程定义部署至Flowable引擎,需要使用RepositoryService,其可以从ProcessEngine对象获取。使用RepositoryService,可以通过XML文件的路径创建一个新的部署(Deployment),并调用deploy()方法实际执行:
/*** 部署流程*/
@Test
public void testDeploy(){// 配置数据库相关信息 获取 ProcessEngineConfigurationProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration().setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn2?serverTimezone=UTC&nullCatalogMeansCurrent=true").setJdbcUsername("root").setJdbcPassword("root").setJdbcDriver("com.mysql.cj.jdbc.Driver").setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);// 获取流程引擎对象ProcessEngine processEngine = cfg.buildProcessEngine();// 部署流程 获取RepositoryService对象RepositoryService repositoryService = processEngine.getRepositoryService();Deployment deployment = repositoryService.createDeployment()// 创建Deployment对象.addClasspathResource("holiday-request.bpmn20.xml") // 添加流程部署文件.name("请求流程") // 设置部署流程的名称.deploy(); // 执行部署操作System.out.println("deployment.getId() = " + deployment.getId());System.out.println("deployment.getName() = " + deployment.getName());
}
然后执行该方法日志操作成功:
在后台表结构也可以看到相关的信息。
act_re_deployment:流程定义部署表,每部署一次就增加一条记录。
act_re_procdef :流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
act_ge_bytearray :流程资源表,流程部署的 bpmn文件和png图片会保存在该表中
我们现在可以通过API查询验证流程定义已经部署在引擎中(并学习一些API)。通过RepositoryService创建的ProcessDefinitionQuery对象实现。
/*** 查看流程定义*/
@Test
public void testDeployQuery(){// 配置数据库相关信息 获取 ProcessEngineConfigurationProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration().setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC&nullCatalogMeansCurrent=true").setJdbcUsername("root").setJdbcPassword("root").setJdbcDriver("com.mysql.cj.jdbc.Driver").setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);// 获取流程引擎对象ProcessEngine processEngine = cfg.buildProcessEngine();// 部署流程 获取RepositoryService对象RepositoryService repositoryService = processEngine.getRepositoryService();// 获取流程定义对象ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId("1").singleResult();System.out.println("processDefinition.getId() = " + processDefinition.getId());System.out.println("processDefinition.getName() = " + processDefinition.getName());System.out.println("processDefinition.getDeploymentId() = " + processDefinition.getDeploymentId());System.out.println("processDefinition.getDescription() = " + processDefinition.getDescription());
}
输出结果为:
processDefinition.getId() = holidayRequest:1:3
processDefinition.getName() = Holiday Request
processDefinition.getDeploymentId() = 1
processDefinition.getDescription() = null
2.3、启动流程实例
现在已经在流程引擎中部署了流程定义,因此可以使用这个流程定义作为“模板”启动流程实例。
要启动流程实例,需要提供一些初始化“流程变量”。一般来说,可以通过呈现给用户的表单,或者在流程由其他系统自动触发时通过REST API,来获取这些变量。在这个例子里,我们简化直接在代码中定义了,我们使用“RuntimeService”启动一个“流程实例”。
/*** 启动流程实例*/
@Test
public void testRunProcess(){// 配置数据库相关信息 获取 ProcessEngineConfigurationProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration().setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC&nullCatalogMeansCurrent=true").setJdbcUsername("root").setJdbcPassword("root").setJdbcDriver("com.mysql.cj.jdbc.Driver").setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);// 获取流程引擎对象ProcessEngine processEngine = cfg.buildProcessEngine();// 启动流程实例通过 RuntimeService 对象RuntimeService runtimeService = processEngine.getRuntimeService();// 构建流程变量Map<String,Object> variables = new HashMap<>();variables.put("employee","张三") ;// 谁申请请假variables.put("nrOfHolidays",3); // 请几天假variables.put("description","工作累了,想出去玩玩"); // 请假的原因// 启动流程实例,第一个参数是流程定义的idProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayRequest", variables);// 启动流程实例// 输出相关的流程实例信息System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());System.out.println("流程实例的ID:" + processInstance.getId());System.out.println("当前活动的ID:" + processInstance.getActivityId());
}
启动成功,输出结果如下:
流程定义的ID:holidayRequest:1:3
流程实例的ID:2501
当前活动的ID:null
启动流程实例涉及到的表结构:
-
act_hi_actinst 流程实例执行历史
-
act_hi_identitylink 流程的参与用户的历史信息
-
act_hi_procinst 流程实例历史信息
-
act_hi_taskinst 流程任务历史信息
-
act_ru_execution 流程执行信息
-
act_ru_identitylink 流程的参与用户信息
-
act_ru_task 任务信息
2.4、查看任务
上面员工发起了一个请假流程,接下来就会流转到总经理这儿来处理,之前我们没有指定经理这的处理人,我们可以加一个:
注意:因为这个审批人zhangsan是我们后加的,所以我们需要先将之前的流程删除掉,再重新部署,删除流程的代码如下:
@Test
public void testDeleteDeploy() {// 配置数据库相关信息 获取 ProcessEngineConfigurationProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration().setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC&nullCatalogMeansCurrent=true").setJdbcUsername("root").setJdbcPassword("root").setJdbcDriver("com.mysql.cj.jdbc.Driver").setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);// 获取流程引擎对象ProcessEngine processEngine = cfg.buildProcessEngine();// 获取RepositoryService对象RepositoryService repositoryService = processEngine.getRepositoryService();repositoryService.deleteDeployment("1", true); // 1对应的是act_re_deployment表的ID主键
}
删除成功后我们先部署再启动。
然后我们来查看下zhangsan的任务:
/*** 查看任务*/
@Test
public void testQueryTask(){// 配置数据库相关信息 获取 ProcessEngineConfigurationProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration().setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC&nullCatalogMeansCurrent=true").setJdbcUsername("root").setJdbcPassword("root").setJdbcDriver("com.mysql.cj.jdbc.Driver").setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);// 获取流程引擎对象ProcessEngine processEngine = cfg.buildProcessEngine();TaskService taskService = processEngine.getTaskService();List<Task> list = taskService.createTaskQuery().processDefinitionKey("holidayRequest").taskAssignee("zhangsan") // 查询这个任务的处理人.list();for (Task task : list) {System.out.println("task.getProcessDefinitionId() = " + task.getProcessDefinitionId());System.out.println("task.getId() = " + task.getId());System.out.println("task.getAssignee() = " + task.getAssignee());System.out.println("task.getName() = " + task.getName());}
}
输出结果为:
task.getProcessDefinitionId() = holidayRequest:1:5003
task.getId() = 7508
task.getAssignee() = zhangsan
task.getName() = Approve or reject request
2.5、完成任务
现在zhangsan这个角色可以来完成当前的任务了。
在此处我们直接解决掉这个请假,然后会走发送拒绝邮件的流程,这块我们需要用到JavaDelegate来触发。
我们定义这样一个Java类:
package org.flowable;import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;public class SendRejectionMail implements JavaDelegate {/*** 这是一个flowable中的触发器* @param delegateExecution*/@Overridepublic void execute(DelegateExecution delegateExecution) {// 模拟一下发邮件,这里我们用打印即可System.out.println("不好意思,你的请假申请被拒绝了!");}
}
然后来完成任务:
/*** 完成任务*/
@Test
public void testCompleteTask(){// 配置数据库相关信息 获取 ProcessEngineConfigurationProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration().setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC&nullCatalogMeansCurrent=true").setJdbcUsername("root").setJdbcPassword("root").setJdbcDriver("com.mysql.cj.jdbc.Driver").setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);// 获取流程引擎对象ProcessEngine processEngine = cfg.buildProcessEngine();TaskService taskService = processEngine.getTaskService();Task task = taskService.createTaskQuery().processDefinitionKey("holidayRequest").taskAssignee("zhangsan").singleResult();// 添加流程变量Map<String,Object> variables = new HashMap<>();variables.put("approved",false); // 拒绝请假// 完成任务taskService.complete(task.getId(),variables);
}
这时我们看act_ru_task表,发现数据已经没了,因为已经走完了。
2.6、查看历史信息
选择使用Flowable这样的流程引擎的原因之一,是它可以自动存储所有流程实例的审计数据或历史数据。这些数据可以用于创建报告,深入展现组织运行的情况,瓶颈在哪里,等等。
例如:如果希望显示流程实例已经执行的时间,就可以从ProcessEngine获取HistoryService,并创建历史活动(historical activities)的查询。在下面的代码片段中,可以看到我们添加了一些额外的过滤条件:
-
只选择一个特定流程实例的活动
-
只选择已完成的活动
结果按照结束时间排序,代表其执行顺序。
/*** 查看历史*/
@Test
public void testQueryHistory(){// 配置数据库相关信息 获取 ProcessEngineConfigurationProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration().setJdbcUrl("jdbc:mysql://localhost:3306/flowable-learn?serverTimezone=UTC&nullCatalogMeansCurrent=true").setJdbcUsername("root").setJdbcPassword("root").setJdbcDriver("com.mysql.cj.jdbc.Driver").setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);// 获取流程引擎对象ProcessEngine processEngine = cfg.buildProcessEngine();HistoryService historyService = processEngine.getHistoryService();List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().processDefinitionId("holidayRequest:1:5003").finished().orderByHistoricActivityInstanceEndTime().asc().list();for (HistoricActivityInstance historicActivityInstance : list) {System.out.println(historicActivityInstance.getActivityId() + " took "+ historicActivityInstance.getDurationInMillis() + " milliseconds");}}
输出结果:
startEvent took 2 milliseconds
approveTask took 877484 milliseconds
decision took 14 milliseconds
sendRejectionMail took 2 milliseconds
rejectEnd took 1 milliseconds
好了~flowable的基本应用我们就先介绍到这里了。
三、Flowable流程设计器
3.1、Eclipse Designer
Flowable提供了名为Flowable Eclipse Designer的Eclipse插件,可以用于图形化地建模、测试与部署BPMN 2.0流程。
由于我们现在都用的事IDEA,所以这里不介绍这种方式。
3.2、Flowable UI应用
Flowable提供了几个web应用,用于演示及介绍Flowable项目提供的功能:
-
Flowable IDM: 身份管理应用。为所有Flowable UI应用提供单点登录认证功能,并且为拥有IDM管理员权限的用户提供了管理用户、组与权限的功能。
-
Flowable Modeler: 让具有建模权限的用户可以创建流程模型、表单、选择表与应用定义。
-
Flowable Task: 运行时任务应用。提供了启动流程实例、编辑任务表单、完成任务,以及查询流程实例与任务的功能。
-
Flowable Admin: 管理应用。让具有管理员权限的用户可以查询BPMN、DMN、Form及Content引擎,并提供了许多选项用于修改流程实例、任务、作业等。管理应用通过REST API连接至引擎,并与Flowable Task应用及Flowable REST应用一同部署。
所有其他的应用都需要Flowable IDM提供认证。每个应用的WAR文件可以部署在相同的servlet容器(如Apache Tomcat)中,也可以部署在不同的容器中。由于每个应用使用相同的cookie进行认证,因此应用需要运行在相同的域名下。
3.2.1、安装部署
下载Tomcat:Apache Tomcat® - Apache Tomcat 8 Software Downloads 官网下载后解压缩到非中文目录即可,然后是下载FlowableUI的文件,在Flowable6.6之后把FlowableUI中提供的四个功能合并到了一起。
然后把解压缩后的两个war包拷贝到Tomcat的解压缩的webapps目录下:
Tomcat目录:
3.2.2、启动服务
启动Tomcat服务,执行startup.bat文件
如果启动中出现乱码修改Tomcat的conf目录下的 logging.properties 文件中的编码,添加如下这行代码:
如果一闪而过则检查jdk的环境变量配置。启动成功后,在浏览器中访问 http://localhost:8080/flowable-ui, 默认的账号密码是 admin/test
3.2.3、用户管理
第一步:我们先在 身份管理应用程序
中创建用户并授权
第二步:创建用户
第三步:填写详细信息
第四步:授权管理
选择【权限控制】,点击左侧菜单【访问workflow应用】,添加刚才创建的两个用户。
3.2.4、绘制流程
第一步:创建新的流程
第二步:流程图页面
第三步:创建流程,分配处理人
这个操作的意思是此用户任务只能由user1来审批或者驳回。
第四步:继续完成流程图的创建
记得点击保存
3.2.5、部署流程
绘制好的流程图,我们只需要一键导出即可
下载下来后拷贝到项目的resource目录下即可:
然后就是正常的操作流程了。
3.2.6、FlowableUI 演示
部署流程
在FlowableUI中提供了演示程序
创建一个新的应用程序,并指定相关的信息
创建应用后需要指定对应的流程图
发布应用程序
启动流程
发布了应用程序后我们就可以来启动流程了
点击右上角【显示图】,可以发现现在已经走到哪步了。
也就是可以看到当前是user1来处理,user1登录后可以看到要处理的流程,user2登录是看不到的。
现在我们登录user1账号:
这时再通过user2登录,就可以看到对应的待办的信息:
然后点击完成,那么整个流程就结束了。