一 Flowable介绍
- BPMN
- 一个软件协议,此协议用于约定了流程框架的开发规范。比如说,我们去绘制流程图的图标的规范。
- 基于这个规范,很方便地学习基于此规范的其它的流程框架。
- flowable是一个服务平台
- 等一系列功能
- BPMN
- DMN决策表(树)
- CMMN Case管理引擎
- 用户管理
- 微服务API
- 功能比acitviti更加强大。如果有activiti的基础,那么可以很容易地学习与使用flowable。
- 如果会使用开源版本的flowable,那么商业版的flowable就无缝衔接了。
二 Flowable类关系图
- ProcessEnginConfiguration:配置类,用于配置流程引擎
- JtaProcessEngineConfiguration
- MultiSchemaMultiTenantProcessEngineConfiguration
- ProcessEngineConfigurationImpl
- StandaloneInMemProcessEngineConfiguration:基于内存的
- StandaloneProcessEngineConfiguration:单体的
- 作用:
- 同activiti一样,它是操作flowable流程的核心对象,类似于JDBC中的Connnection对象,或者说类似于做mybatis操作时的SqlSessionFactory对象。
- 在编码时,主要用于获取多个XxxService服务,如下图源码:
- 特点:
- 在一个程序中可以有多个流程引擎对象ProcessEngin,而且可以给这些对象取名。
- 获取方式:
- 第一步:获取配置流程引擎对象的配置类:ProcessEnginConfiguration
- 第二步:获取流程引擎对象:ProcessEngin
- 配置文件使用方式:
- 方式1:默认配置文件是resources目录下的flowable.cfg.xml
- ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
- 方式2:加载自定义名称的配置文件:
- @Test public void test2() throws Exception{ ProcessEngineConfiguration configuration = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("flowable.cfg.xml"); ProcessEngine processEngine = configuration.buildProcessEngine(); System.out.println("processEngine = " + processEngine); }
- ProcessEngineConfigurationImpl:抽象类
- 配置对象,是ProcessEngine的配置对象的默认实现。这是因为有如下代码来创建ProcessEngine:
public abstract class ProcessEngineConfigurationImpl extends ProcessEngineConfiguration implements ScriptingEngineAwareEngineConfiguration {
public ProcessEngine buildProcessEngine() {
// this表示绑定当前对象,即绑定ProcessEngineConfigurationImpl
this.init();
ProcessEngineImpl processEngine = new ProcessEngineImpl(this);
this.postProcessEngineInitialisation();
return processEngine;
}
}
- this.init()初始化了很多东西,使用了模板模式(设计模式) this.initConfigurators(); this.configuratorsBeforeInit(); this.initProcessDiagramGenerator(); this.initHistoryLevel(); this.initFunctionDelegates(); this.initDelegateInterceptor(); this.initExpressionManager(); this.initAgendaFactory(); if (this.usingRelationalDatabase) { this.initDataSource(); //如何关联数据为的? this.initDbSchemaManagers(); } this.initHelpers(); this.initVariableTypes(); this.initBeans(); this.initFormEngines(); this.initFormTypes(); this.initScriptingEngines(); this.initClock(); this.initBusinessCalendarManager(); this.initCommandContextFactory(); this.initTransactionContextFactory(); this.initCommandExecutors(); this.initServices(); this.initIdGenerator(); this.initWsdlImporterFactory(); this.initBehaviorFactory(); this.initListenerFactory(); this.initBpmnParser(); this.initProcessDefinitionCache(); this.initProcessDefinitionInfoCache(); this.initAppResourceCache(); this.initKnowledgeBaseCache(); this.initJobHandlers(); this.initHistoryJobHandlers(); this.initTransactionFactory(); if (this.usingRelationalDatabase) { this.initSqlSessionFactory(); //初始化SqlSessionFactory } this.initSessionFactories(); this.initDataManagers(); //初始化好多个管理器 this.initEntityManagers(); this.initCandidateManager(); this.initHistoryManager(); this.initDynamicStateManager(); this.initJpa(); this.initDeployers(); this.initEventHandlers(); this.initFailedJobCommandFactory(); this.initEventDispatcher(); this.initProcessValidator(); this.initFormFieldHandler(); this.initDatabaseEventLogging(); this.initFlowable5CompatibilityHandler(); this.initVariableServiceConfiguration(); this.initIdentityLinkServiceConfiguration(); this.initTaskServiceConfiguration(); this.initJobServiceConfiguration(); this.initAsyncExecutor(); this.initAsyncHistoryExecutor(); this.configuratorsAfterInit(); this.afterInitTaskServiceConfiguration();
- RepositoryService:资源(流程定义、流程部署等)管理服务
- RuntimeService:流程运行时服务
- TaskService:流程任务处理服务
- ManagementService:Activiti的引擎管理类,提供了对Flowable 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于 Flowable 系统的日常维护。
- HistoryService:流程历史信息
-------------------------------------------------------------------------------------------------------------------------------
- 规则:
- XxxService,在flowable中用来专门处理流程的各种操作。
- 关系:
- ProcessEngin 与 RepositoryService、RuntimeService、TaskService、ManagementService、HistoryService ProcessEngin相当于公司的老板,RepositoryService、RuntimeService、TaskService、ManagementService、HistoryService这几个相当于公司下各个部门(从事、财务、开发、运维)的主管。XxxService,在flowable中用来专门处理流程的各种操作。老板要做什么事情,都会去找相关的部门的主管说一下,主管会全权负责完成。
三 Flowable基础表结构(30张)
- 基础表
- 建表语句:jar源码包:
- 建表时机:
- 当我们去获取ProcessEngine对象的时候,flowable会在数据库中创建这5个类型的表(ACT_RE、ACT_RU、ACT_HI、ACT_GE、ACT_ID)。
- 随着业务复杂度的提高,后面去做业务处理或使用flowable UI的时候,还会创建一些其它前缀的数据表。
- 分类:
- ACT_RE :'RE'表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
- ACT_RU:'RU'表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Flowable只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
- ACT_HI:'HI'表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
- ACT_GE: GE 表示 general。 通用数据, 用于不同场景下
- ACT_ID: ’ID’表示identity(组织机构)。这些表包含标识的信息,如用户,用户组,等等。
- 基础表结构
- 一般数据表
- ACT_GE_BYTEARRAY:数据源表,保存通用的流程定义和流程资源
- 保存流程部署的相关信息,比如保存着流程定义xml文件
- ACT_GE_PROPERTY:系统相关属性
- 流程历史记录表
- ACT_HI_ACTINST:历史的流程实例
- ACT_HI_ATTACHMENT:历史的流程附件
- ACT_HI_COMMENT:历史的说明性信息
- ACT_HI_DETAIL:历史的流程运行中的细节信息
- ACT_HI_IDENTITYLINK:历史的流程运行过程中用户关系
- ACT_HI_PROCINST:历史的流程实例 【PRO是process流程的简写】
- ACT_HI_TASKINST:历史的任务实例 【TASK是task任务】
- ACT_HI_VARINST:历史的流程运行中的变量信息【VAR是valiable流程变量的简写】
- 流程定义表
- ACT_RE_DEPLOYMENT:部署单元信息 。 每部署一个流程,就会在此表产生一条记录。
- ACT_RE_MODEL :模型信息
- ACT_RE_PROCDEF:流程定义表,保存已部署的流程定义【PROC是process流程的简写,而DEF是definition定义的简写】
- 运行实例表
- ACT_RU_EVENT_SUBSCR:跟执事件相关,运行时事件
- ACT_RU_EXECUTION:跟执行器相关,运行时流程执行实例
- ACT_RU_IDENTITYLINK:跟用户相关,运行时用户关系信息,存储任务节点与参与者的相关信息
- ACT_RU_JOB:运行时 作业
- ACT_RU_TASK:任务表,运行时 任务
- ACT_RU_VARIABLE:运行时 变量 表,即流程变量的信息, 即提交参数(表单数据)到流程
- 用户用户组表
- ACT_ID_BYTEARRAY:相关的一些资源,二进制数据表
- ACT_ID_GROUP:用户 组 信息表
- ACT_ID_INFO: 用户详细信息 详情表
- ACT_ID_MEMBERSHIP :人与组关系表
- ACT_ID_PRIV:权限表
- ACT_ID_PRIV_MAPPING:用户或组权限关系表
- ACT_ID_PROPERTY:属性表
- ACT_ID_TOKEN:跟认证相关,记录用户的token信息
- ACT_ID_USER:用户表
- 规则:
- 成功部署并一个请假流程以后,会在表: ACT_GE_BYTEARRAY、 ACT_RE_DEPLOYMENT、 ACT_RE_PROCDEF 中创建一条新的记录,用于保存请假流程相关的信息。
- 删除已经部署的请假流程以后,会把表: ACT_GE_BYTEARRAY、 ACT_RE_DEPLOYMENT、 ACT_RE_PROCDEF 中对应的一条记录删除去。
- 启动一个流程以后:
- ACT_RU_VARIABLE会保存流程变量的信息。
- ACT_RU_TASK会保存第一步的任务,如请假条会流转到总经理审批(Approve or reject request)这一步任务,总经理要决策操作:是审批通过 或者 是拒绝。
- ACT_RU_EXECUTION会保存成功启动的流程的相关信息。
- 每走一步任务:
- ACT_RU_TASK:中的记录的审批者都会变成下一个要审批的人
- 最完所有任务:
- ACT_RU_TASK:与此流程相关的一条记录会被删除掉。
- 关系:
四 Flowable基础:基于BPMN 2.0
- 官方手册:Flowable BPMN 用户手册 (v 6.3.0) (tkjohn.github.io)
- 流程设计
- eclipse Designer
- flowable UI
- 基本应用、简单案例:不去结合流程设计,完成一个流程的完整的工作走向(流程)
- 流程部署
- 流程实例创建
- 启动流程
- 查询任务
- 处理任务
- 简单案例演示详细步骤
- 第一步:创建流程引擎对象ProcessEngine
- 第1步:创建maven项目
- 第2步:引入依赖
- flowable-engine 6.3.0
- mysql-connector-java 8.0.21
- junit 4.13.2
- 第3步:测试类(src/test/java/com.bobo.flowable.test/Test01.java)
- 配置流程引擎对象的配置类:ProcessEnginConfiguration
- 获取流程引擎对象:ProcessEngin
- 新建30张表:
package com.bobo.flowable.test;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
public class Test01 {
/**
* 获取流程引擎对象
*/
public static void testProcessEngine(){
// 获取流程引擎配置类对象:ProcessEngineConfiguration
ProcessEngineConfiguration configuration = new StandaloneProcessEngineConfiguration();
// 配置数据库连接信息
configuration.setJdbcUrl("jdbc:mysql://10.203.5.185:3306/fLearn?serverTimezone=UTC&nullCatalogMeansCurrent=true")
.setJdbcUsername("root")
.setJdbcPassword("MySQL#567890")
.setJdbcDriver("com.mysql.cj.jdbc.Driver")
// 如果数据库中的表结构不存在就新建
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
// 通过ProcessEngineConfiguration构建我们需要的流程引擎对象ProcessEngine
ProcessEngine ProcessEngine = configuration.buildProcessEngine();
System.out.println("ProcessEngine:"+ProcessEngine);
}
public static void main(String[] args) {
testProcessEngine();
}
}
- 第4步:加入日志功能
- 第a步:引入依赖
- slf4j-api
- slf4j-log4j12
- 第b步: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
- 第c步:重新运行单元测试方法
- 第d步:在控制台中看到多了很多日志。如果之前没有创建对应的表结构(30张),那么就会看到创建这30张表的建表语句。
- 第二步:部署流程定义 ps:创建流程,一般第1步:通过流程设计器绘制流程图。但为了把流程操作的完整流程演示出来,在此先不用流程设计器,而是使用一个写好的xml格式的流程文件(官网案例)。
- 流程图:请假流程
- 第1步:部署一个流程
- 第2步:发起一个流程
- 提供的参数:姓名、请假天数、请假理由、请假开始和结束时间等
- 第3步:管理员进行决策:同意 或者 拒绝
- 第4步:网关
- 第a步:如果管理员同意了,继续走下一步审批(如:人事审批等,直至审批环节走完、结束)。
- 第b步:如果管理员拒绝了,触发事件,发一封拒绝的邮件。
- 部署流程
- 概述:这里部署流程,是基于BPMN 2.0的格式来部署的。这就要求去创建一个xml格式的流程文件,在这个xml格式的流程文件中定义了请假流程的整个过程。
- 详细步骤
- 第1步:将下面的XML保存在src/main/resources文件夹下名为holiday-request.bpmn20.xml的文件中。(注:其中后缀“.bpmn20.xml”是固定不变的,而前缀“holiday-request”是流程名称。)
- <definitions>中定义了一些约束在里面。
- <process>是一个流程 id:唯一标识(主键) name:流程名称。可修改。
- <startEvent id="startEvent"/>:编号是"startEvent"
- <sequenceFlow>:顺序流 sourceRef="startEvent":顺序流的来源是"startEvent"。targetRef="approveTask":目标的引用是"approveTask(同意的任务)"。
- <conditionExpression>:条件表达式 xsi:type="tFormalExpression":去到哪个表达式
- <![CDATA[]]>:表达式写在里面
- ${!approved}:条件表达式,包含参数运算
- <userTask>:用户的任务 id="approveTask(同意的任务)" name="Approve or reject request":同意或者拒绝
- 排他网关<exclusiveGateway id="decision"/>
- <serviceTask id="sendRejectionMail" name="Send out rejection email" flowable:class="org.flowable.SendRejectionMail"/> flowable:class="org.flowable.SendRejectionMail"用来处理发送拒绝请假邮件的操作。
- <endEvent id="approveEnd"/>:某个分支的流程终止的节点。
- 总结:
- 第a步:<startEvent id="startEvent"/>表示开始
- 第b步:走到:用户任务<userTask id="approveTask(同意的任务)" name="Approve or reject request(同意或者拒绝)"/>
- 第c步:走到:排他网关<exclusiveGateway id="decision"/>
- 条件:<sequenceFlow sourceRef="decision" targetRef="externalSystemCall"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[ ${approved} ]]> </conditionExpression> </sequenceFlow>
- 排他网关的第1个流程,分支1是targetRef="externalSystemCall"
- 排他网关的第2个流程,分支2是targetRef="sendRejectionMail"
- 人事
- 第d步:人事处理请假的2个分支,如扣钱 和 发邮件处理拒绝请假:<serviceTask id="Xxx" name="Xxx"/>
- 第e步:人事处理请假的2个分支都走完终止流程:<endEvent id="Xxx"/>
- 第2步:使用代码实现流程部署
package com.bobo.flowable.test;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.junit.Before;
import org.junit.Test;
public class Test01 {
ProcessEngineConfiguration configuration = null;
/**
* 初始化流程引擎配置对象
*/
@Before
public void before(){
// 获取流程引擎配置类对象:ProcessEngineConfiguration
configuration = new StandaloneProcessEngineConfiguration();
// 配置数据库连接信息
configuration.setJdbcUrl("jdbc:mysql://10.203.5.185:3306/fLearn?serverTimezone=UTC&nullCatalogMeansCurrent=true")
.setJdbcUsername("root")
.setJdbcPassword("MySQL#567890")
.setJdbcDriver("com.mysql.cj.jdbc.Driver")
// 如果数据库中的表结构不存在就新建
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
}
/**
* 部署流程
*/
@Test
public void testProcessEngine(){
// 创建流程引擎对象:通过ProcessEngineConfiguration构建我们需要的流程引擎对象ProcessEngine
ProcessEngine processEngine = configuration.buildProcessEngine();
// 创建RepositoryService对象
// 类似地还有:RepositoryService、RuntimeService、TaskService、ManagementService、HistoryService,都是flowable提供的关于流程操作的核心服务类(service))
RepositoryService repositoryService = processEngine.getRepositoryService();
// 创建流程部署对象Deployment:
// (1)添加流程部署文件
// (2)设置部署流程的名称
// (3)执行部署操作
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("holiday-request.bpmn20.xml")
.name("请假流程")
.deploy();
System.out.println("deployment.getId() = " + deployment.getId());
System.out.println("deployment.getName() = " + deployment.getName());
}
}
- 第3步:运行测试方法
- 第4步:验证
- 方式1:日志打印出流程id与名称:
- 方式2:查看数据库表
- 第三步:操作CRUD流程
- 查看完成部署的流程定义的信息:// 获取流程定义对象 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .deploymentId("2501") .singleResult();
- 删除完成部署的流程实例: // 删除完成部署的流程实例 // 删除部署的流程 // (1)第一个参数是id。如果部署的流程启动了就不允许删除了 // (2)第二个参数是级联删除,如果为true,那么删除流程的同时启动了相关的任务也一并会被删除外 repositoryService.deleteDeployment("1");
- 第四步:启动流程实例
- 概述:经过上面的3个步骤,请假流程已经成功部署了。张三和李四都要请假,因此他们都需要发起一个流程。 张三和李四请假时,要填写请假表单(请假的相关信息,如请假人、请假时长、请假理由等),然后把请假表单数据提交给审批人。
- 详细步骤
- 代码实现: // 启动流程实例 // (1)第一个参数是流程定义的id(主键) // (2)第二个参数是提交的表单数据 ProcessInstance 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
- 第五步:查看任务
- 第1步:修改配置文件,指定每一步任务的一个审批人 或者 一个审批小组。 上面员工发起了一个请假流程,接下来就会流转到总经理这儿来处理,之前我们没有指定候选人经理这的处理人,我们可以加一个:
- 此时,如果任务流转到总经理(lisi)这里,那么lisi登录进来就可以看到zhansan请假的审批在这里,等待审批。
- 第2步:删除旧的流程:repositoryService.deleteDeployment("1",true);
- 验证:查看数据库表
- 第3步:重新启部署流程:Deployment deployment = repositoryService.createDeployment()// 创建Deployment对象 .addClasspathResource("holiday-request.bpmn20.xml") // 添加流程部署文件 .name("请假流程") // 设置部署流程的名称 .deploy(); // 执行部署操作 deployment.getId() = 5001 //流程编号 deployment.getName() = 请假流程 //流程名称
- 第4步:重启启动流程实例(张三请假)
- 第5步:代码实现,任务查看。即在实际开发中,张三的司令登录进来后会在前端页面看到流转到他的流程审批任务。
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("holidayRequestNew") //指定查询的流程编号
.taskAssignee("张三的司令")//查询这个任务的处理人(流转到)
.list();
- 第6步:验证: task.getProcessDefinitionId() = holidayRequest:1:5003 task.getId() = 10008 task.getAssignee() = 张三的司令 task.getName() = 同意或者拒绝请假 task.getProcessDefinitionId() = holidayRequest:1:5003 task.getId() = 7508 task.getAssignee() = 张三的司令 task.getName() = 同意或者拒绝请假
- 第六步:(审批人)处理任务(流转到)不管是放行还是拒绝请假,流程任务都会流转到排他网关。网关要对任务做一个操作,即根据总经理的处理(放行或拒绝),网关要做出路由。
- 第1步:配置文件 在此处我们直接解决掉这个请假,然后会走发送拒绝邮件的流程,这块我们需要用到JavaDelegate来触发。
- 第2步:添加Java委托类
package org.flowable;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
public class SendRejectionMail implements JavaDelegate {
/**
* 这是一个Flowable中的触发器
* @param delegateExecution
*/
@Override
public void execute(DelegateExecution delegateExecution) {
// 触发执行的逻辑,按照我们在流程中的定义,应该给被拒绝的员工发送邮件
// 扩展:短信、邮件、订单回退、删除订单等
System.out.println("请假被拒绝,,,安心工作吧");
}
}
- 第3步:单元测试,完成任务
@Test
public void testCompleteTask(){
// 获取流程引擎对象
ProcessEngine processEngine = configuration.buildProcessEngine();
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
.processDefinitionKey("holidayRequest2")
.taskAssignee("张司令")
.singleResult();
// 添加流程变量
Map<String,Object> variables = new HashMap<String,Object>();
variables.put("approved",false); // 拒绝请假
// 完成任务
taskService.complete(task.getId(),variables);
}
- 第4步: 验证,查询数据库
- 第七步:查看历史信息
- 代码实现: /** * 查看历史 */ @Test public void testQueryHistory(){ // 获取流程引擎对象 ProcessEngine processEngine = configuration.buildProcessEngine(); HistoryService historyService = processEngine.getHistoryService(); List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery() .processDefinitionId("holidayRequest2:1:2503") //从数据库表ACT_RE_PROCDEF中获取定义id .finished() //查询的历史记录的状态是已经完成 .orderByHistoricActivityInstanceEndTime().asc()//结束时间排序 .list(); for (HistoricActivityInstance historicActivityInstance : list) { System.out.println(historicActivityInstance.getActivityId() + " took " + historicActivityInstance.getDurationInMillis() + " milliseconds"); } }
五 Flowable流程设计器
1 纲:Eclipse Designer插件
2 Flowable UI应用
- 概述:flowable提供了flowable UI,是一个前端web应用
- 格式:war包
- 操作:部署
- 功能:绘制流程、演示、测试
- 使用详细步骤
- 部署flowable UI
- 第一步:官网下载:flowable-6.7.2.zip
- 第二步:解压,flowable-6.7.2\wars
- flowable-rest.war:
- flowable-ui.war:
- 注:6.4.0会把4个模块(Flowable IDM、Flowable Modeler 、Flowable Task 、Flowable Admin )分别做成1个war包,即一共4个war包。而在6.4.0以后,会把4个模块合并,并形成2个war包而已,这样更友好。
- 第三步:tomcat
- 第1步:把flowable-rest.war、flowable-ui.war放到tomcat的webapps目录中
- 第2步:启动tomcat服务器,startup.bat
- tomcat服务器会自动帮我们解压缩flowable-rest.war、flowable-ui.war
- 注:如果出现乱码问题的话,那么修改logging.propertie文件末尾加入java.util.logging.ConsoleHandler.encoding = GBK
- 第四步:浏览器中访问 http://localhost:8080/flowable-ui, 默认的账号密码是 admin/test
- 四个模块:任务应用程序、建模器应用程序、管理员应用程式、身份管理应用程序
- 注:你们第一次访问时,FirstApplication功能模块是没有的。为什么老师的页面上会有呢?这是因为,老师之前操作的时候创建了一个应用(FirstApplication)在里面。
- 身份管理应用程序:用户(组)的创建与权限控制
- 创建用户组
- 创建用户
- 权限控制
- 访问权限:用户可以访问哪些应用(功能)
- 建模器应用程序:主要用于绘制流程图
- 用户任务(选中)
- 表单引用:就是请假流程时,要把表单参数(如请假时间、请假理由、请假时长等信息)。
- Variable aggregations (Multi-instance):流程变量
- 分配用户:分配处理这个任务的用户(组)
- 部署流程
- 第一步:导出成bpmn文件
- 注:<bpmndi:BPMNDiagram id="BPMNDiagram_MyHolidayUI">标签中包含的是流程图所对应的元数据信息,有了元数据信息flowable要以把流程变成可视化的图片展示给我们看。
- 第二步:然后就是正常的操作流程了
- 演示应用程序:模拟演示的程序,可以帮助我们直观地看到流程流转的整个过程。
- 第一步:创建演示应用程序
- 第二步:指定关联的流程的流程图,并保存
- 第三步:发布演示应用程序(注:相当于我们部署并启动了一个应用)
- 第四步:在“任务应用程序”中,启动流程
- 第五步:在“任务应用程序”中,一个个任务走完流程
- 注:切换用户登录,进行审批(点击完成)
六 流程部署原理
- 环境:
- Eclipse
- 1个.bar中包含两个bpmn文件,即将一次部署两个流程
- 效果:
- 流程部署表:act_re_deployment
- 只会有1条部署的记录,此记录不关联相关的流程文件
- 流程定义表:act_re_procdef
- .bar会被解压出2个bpmn文件,在act_re_procdef分别生成1条流程定义文件记录,即2条记录。
- 流程部署资源文件表:act_ge_bytearray
- 每个流程会在此表中产生2条记录,即此次部署会在这张表中产生4条记录。2条记录分别记录了2个流程的流程文件,另外2条记录分别记录了2个流程的流程图文件。
- 总结:
- act_re_deployment和act_re_procdef、act_ge_bytearray是一对多的关系
- act_re_deployment的id,在act_re_procdef、act_ge_bytearray中都存在外键
七 流程启动的原理
- ACT_RU_Xxx是流程在运行过程中会涉及到的
- 流程启动操作涉及的表:
- ACT_RU_EXECUTION 运行时流程执行实例
- BUSINESS_KEY_:绑定的业务主键,如订单号
- PARENT_ID_:空代表流程实例的第一个任务
- ROOT_PROC_INST_ID_:当前流程实例根节点的编号
- Xxx_COUNT_:统计信息
- 注意事项:
- 同一个流程会有多条数据,每条数据用ROOT_PROC_INST_ID_进行关联
- ACT_RU_IDENTITYLINK 运行时用户关系信息
- ACT_RU_TASK 运行时任务表:记录当前流程实例所处的流程节点信息,比如现在到张三审批了;张三审批完,又记录现在是李四审批了。
- ASSIGNEE_:指派谁去处理
- FORM_KEY_:在flowable中是可以绑定表单的,FORM_KEY_就是用于记录绑定的表单
- ACT_RU_VARIABLE 运行时变量信息表
- ACT_HI_ACTINST 历史的流程实例
- ACT_HI_PROCINST 历史的流程定义信息
- ACT_HI_TASKINST 历史的任务实例信息
- ACT_HI_VARINST 历史的流程运行中的变量信息
八 流程挂起(暂停)与激活原理
- 挂起(暂停)
- 状态描述:act_re_procdef的SUSPENSION_STATE_,2代表挂起、1代表激活。
- 激活
- 状态描述:act_re_procdef的SUSPENSION_STATE_,2代表挂起、1代表激活。
- 注意事项:
- 挂起的流程,先激活,才能重新启动。
九 处理流程的原理ACT_RU_EXECUTION 运行时流程执行实例
- 处理流程过程中不会变
- ACT_RU_IDENTITYLINK 运行时用户关系信息,IDENTITYLINK中会记录每次流程操作的信息
- 每对流程做一次处理,ACT_RU_IDENTITYLINK 就会多1条记录
- ACT_RU_TASK 运行时任务表,记录了当前流程实例所运行的节点。
- 审批完成进入下一个环节,ACT_RU_TASK 会删除1条记录(旧任务),新增1条新记录(新任务)。
- ACT_RU_VARIABLE 运行时变量表:Map集合
- 如果之前没有绑定流程变量,那么就多1条记录,是处理流程时绑定的流程变量。
- 如果之前有绑定过流程变量,那么就把这条数据中的数据更新为最新数据。
十 完成(结束)一个流程
首先我们会发现
- ACT_RU_EXECUTION 运行时流程执行实例
- ACT_RU_IDENTITYLINK 运行时用户关系信息
- ACT_RU_TASK 运行时任务表
- ACT_RU_VARIABLE 运行时变量表
这四张表中对应的数据都没有了,也就是这个流程已经不是运行中的流程了。然后在对应的历史表中我们可以看到相关的信息:
- ACT_HI_ACTINST 历史的流程实例
- ACT_HI_ATTACHMENT 历史的流程附件
- ACT_HI_COMMENT 历史的说明性信息
- ACT_HI_DETAIL 历史的流程运行中的细节信息
- ACT_HI_IDENTITYLINK 历史的流程运行过程中用户关系
- ACT_HI_PROCINST 历史的流程实例
- ACT_HI_TASKINST 历史的任务实例
- ACT_HI_VARINST 历史的流程运行中的变量信息
在我们上面的处理流程的过程中设计到的历史表有
十一 任务分配和流程变量
- 任务分配(变更、赋值)
- 硬件编码分配:
- 表达式分配
- 值表达式1:${myVar}
- eclipse + UEL-value:
- idea + flowable ui+ UEL-value::选择“固定值”,并在“”分配处写上UEL-value表达式
- 验证:
- 第一步:绘制流程图
- 第二步:部署流程
- 第三步:启动流程实例
- 第1步:给每一个任务(节点)的“用户”变量赋值:
- 第2步:启动成功
- 第3步:打开表ACT _RU_ VARIABLE:
- 第4步:同时在Task表中,可以看到流程当前的分配人是张三,说明UEL表达式被解析了:
- 第四步:处理流程(任务1)
- Task表,处理人变成“李四”
- 值表达式2:${myBean.myProperty}
- 首先,和spring(springboot)整合。
- 其次,从spring容器中获取相应的bean对象。
- 最后,通过bean对象获取属性值。
- 方法表达式:
- 首先,和spring(springboot)整合。
- 其次,从spring容器中获取相应的bean对象。
- 最后,通过bean对象调用方法获取值。
- 监听器分配
- 原理
- 监听器可以捕获我们的很多行为和事件
- 监听类型
- create:任务创建后触发
- assignment:任务分配后触发
- Delete:任务完成后触发
- All:所有事件(行为)都触发
- eclipse + 监听器:
- idea + flowable UI + 监听器:
- 第一步:创建监听器类: /** * 自定义的监听器 */ public class MyTaskListener implements TaskListener { /** * 监听器触发的方法 * @param delegateTask */ @Override public void notify(DelegateTask delegateTask) { System.out.println("MyTaskListener监听器触发了,触发的事件是:" + delegateTask.getName()); // 满足触发条件的事件,那么我们就来设置assignee if("提交请假".equals(delegateTask.getName()) && "create".equals(delegateTask.getEventName())){ // 指定任务的负责人 delegateTask.setAssignee("小明"); }else { delegateTask.setAssignee("小张"); } } }
- 第二步:给任务配置监听器:
- 第三步:部署流程
- 第四步:启动流程
- 第五步:验证
- 查看日志:
- task表:
- 第六步:小明去处理任务
- 查看日志:事件是,审批
- task表:成变小李
- 总结
- 可以给多个节点(任务)指定同一个监听器,也可以分别指定监听器
- 流程变量
- 如下图所示:
- 流程定义:即绘制流程图
- 流程实例ProcessInstance:首先,部署流程定义。其次,才能启动流程创建出一个流程实例。如,张三发起一个请假流程。又如,李四也发起一个请假流程。
- 流程定义与流程实例的关系:
- 1个流程定义可以产生多个流程实例。
- 流程任务Task:比如,请假流程:张三发起一个请假流程,所以有一个节点需要张三处理,即张三有一个任务Task。当张三提交请假流程完成,流程到下一个节点李四时,即李四有一个任务Task。总结,在一个流程实例中有多少个节点,就对应有多少个任务Task。
- 流程实例与任务Task的关系:
- 1个流程实例中有很多个任务Task。
- 执行实例与流程实例的关系:
- 当我们去定义1个流程实例的时候,act_ru_execution表:
- 上图第3行表示,我们有一个流程定义。
- 当你创建一个流程实例,那么对应地在act_ru_execution表中就会有2条信息。1条是我们流程定义的信息,与之对应的是1条执行实例的信息。
- 简单业务的流程中:流程实例和执行实例是1对1的关系。
- 复杂业务的流程中(如有主流程和子流程,如嵌入流程,如包含流程):流程实例和执行实例是1对多的关系。
- 如何指定流程变量?
- 表达式
- 分类
- 全局变量:
- 作用域:流程实例的创建到结束,所有节点(任务Task)都是可见的。
- 生命周期:流程实例的创建到结束。
- 特点:
- 不能重名,重名会覆盖。
- 局部变量:
- 作用域:
- 任务和执行实例,即仅仅是针对一个任务和一个执行实例范围内。范围没有流程实例大, 称为 local 变量。
- 只在单个task(节点 或 任务)中生效
- 特点:
- 在不同任务或执行实例中,作用域是互不影响的,变量名可以相同。
- Local 变量名也可以和 global 变量名相同,没有影响。
- 案例演示
- 员工出差申请单流程图:
- 上图有4个用户任务:
- 用户任务1:创建出差申请单
- 用户任务2:部门经理审批(根据出差天数走不同的任务)
- 用户任务3:总经理审批
- 用户任务4:财务审批
- 开发步骤
- 第一步:绘制流程图,保存,导出,放到idea中
- UEL-VALUE表达式分配用户:${user1}、${user2}、${user3}、${user4}
- 点击“线条”,设置“流条件”:num>3,num<3
- 第二步:部署流程
- 第三步:启动流程,创建流程实例
- 对UEL-VALUE中的数据做一个赋值,即分配用户,${user1}、${user2}、${user3}、${user4}
- 第四步:验证
- ACT_RU_VARIABLE表数据,对于每一个流程变量都会对应表中的一条记录。如下图:
- tack表:
- 第五步:张三处理任务
- 新增(也可更新)全局变量——请假天数,这个流程变量的设置num=2
- 第六步:验证
- valibale表:多了全局变量(绑定了流程实例)num=2
- 第七步:设置局部变量
- 第1步:编码 /** * 演示:局部变量 * 根据Task编号来更新流程变量 * 比如,原先我申请出差2天,但由于疫情原因只能多请3天,就是说要出差5天。但流程已经在跑了,通过此方法可以在审批完成之前把出差天数更新了。 */ @Test public void updateVariableLocal(){ // 获取流程引擎对象 ProcessEngine processEngine = configuration.buildProcessEngine(); TaskService taskService = processEngine.getTaskService(); Task task = taskService.createTaskQuery() .processDefinitionId("40001") .taskAssignee("李四") .singleResult(); // 获取所有的流程变量 Map<String, Object> processVariables = task.getProcessVariables(); // 新增/更新num processVariables.put("num",5); // 设置局部变量 taskService.setVariablesLocal(task.getId(),processVariables); }
- 第2步:验证
- Variable表:
- 全局变量绑定的是流程实例
- 局部变量绑定的是执行实例,还绑定了任务
- 第3步:再次设置(修改)局部变量(执行上面的方法,把数值调整为6)
- 第4步:验证
- Variable表:版本号递增,局部变量修改为6
- 第八步:再次设置(修改)全局变量
- 代码: // 再次设置(更新)全局变量 @Test public void updateVariable(){ // 获取流程引擎对象 ProcessEngine processEngine = configuration.buildProcessEngine(); TaskService taskService = processEngine.getTaskService(); Task task = taskService.createTaskQuery() .processDefinitionId("40001") .taskAssignee("李四") .singleResult(); // 获取所有的流程变量 // 在局部变量和全局变量都有(名称相同)的情况下,这儿取出来的是局部变量 Map<String, Object> processVariables = task.getProcessVariables(); // 新增/更新num processVariables.put("num",7); // 设置全局变量 taskService.setVariables(task.getId(),processVariables); }
- 第2步:验证
- Variable表:为什么还是修改的是局部变量?因为在局部变量和全局变量都有(名称相同)的情况下,这儿取出来的是局部变量
- 第九步:处理任务(情况:全局变量和局部变量名称都是num,而数值分别是1和6)
- 编码:
- 结果
- 走的是全局变量num=2的审批,因为局部变量num=6在绑定的任务外(即进入下一个任务后)是失效的。
- 因为局部变量失效,所以在Variable表中会被删除掉。但要以在历史信息表act_hi_varinst表中查询到局部变量的信息。
- 第十步:网关处理原理
十二 候选人
- 候选人与分配用户的区别
- 分配用户指的是给某个任务Task指派处理人。
- 候选人指的是,公司所有人(如张三、李四、王五)或仅限于某个部门有权去办理出差申请审批业务。
- 候选人与处理人的区别
- 候选人不是真实的task处理人(assignee_),如下图所示:
- 环境:
- 有一个任务Task。
- 任务Task有一个指派人Assignee,指派人Assignee用于处理任务。
- 有3个候选人,张三、李四、王五。
- 指派人Assignee:
- 指派人Assignee要在候选人(3个)中去做一个选择,选择一个人出来处理任务Task。
- 案例演示
- 第1步:绘制请假流程图
- 第a步:设置候选人${candidate1}、${candidate2}、${candidate3}
- 第2步:流程部署
- 第3步:创建(启动)流程实例
- variables.put("candidate1","候选1") ; variables.put("candidate2","候选2"); variables.put("candidate3","候选3") ;
- 第4步:验证
- act_ru_variable表:
- act_ru_task表:
- assignee_字段没有数据,说明当前任务task并没有指派对应的处理人。为什么会这样呢?这是因为我们现在呢,有多个候选人。
- 注意:候选人不是真实的task处理人(assignee_)。
- act_ru_identitylink表:
- 第二步:查询候选人可拾取任务列表
- 第1步:场景分析
- 现在task表的assignee_字段为空,即此任务task没有指派处理人。
- 但有3个候选人。
- 实际开发过程中,所有的候选人登录系统以后,都可以看到一个可以“拾取”的任务,即可以把任务拉过来并进行处理的任务。
- 第2步:查询当前登录人可“拾取"的任务列表:
/**
* 根据登录的用户查询对应的可以拾取的任务
*
*/
@Test
public void queryTaskCandidate(){
// 获取流程引擎对象
ProcessEngine processEngine = configuration.buildProcessEngine();
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery()
.processInstanceId("50001")
//.processDefinitionId("houXuanRen:1:47504")
.taskCandidateUser("候选1")
.list();
for (Task task : list) {
System.out.println("task.getId() = " + task.getId());
System.out.println("task.getName() = " + task.getName());
}
}
- 第三步:候选人对任务的拾取、归还、交接、完成等操作
- 拾取
- 编码:
/**
* 拾取任务
* 一个候选人拾取了这个任务之后其他的用户就没有办法拾取这个任务了
* 所以如果一个用户拾取了任务之后又不想处理了,那么可以退还
*/
@Test
public void claimTaskCandidate(){
// 获取流程引擎对象
ProcessEngine processEngine = configuration.buildProcessEngine();
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
.processInstanceId("50001")
//.processDefinitionId("houXuanRen:1:47504")
.taskCandidateUser("候选1")
.singleResult();
if(task != null){
// 拾取对应的任务
taskService.claim(task.getId(),"候选1");
System.out.println("任务拾取成功");
}
}
- 效果:
- act_ru_task表中assignee_字段终于不为空,而是”候选1“了:
- 这回我们就可以使用用户”候选1“登录系统,并且处理这个任务task了。但”候选2“登录系统,看不见这个任务task。
- 拾取规则:
- 1个任务被某个候选人拾取以后,其它的所有人都不能再去拾取(看到)这个任务了。
- 1个候选人拾取了这个任务之后 ,其他的用户就没有办法拾取(看到)这个任务了。
- 如果1个用户拾取了任务之后,又不想处理了,那么可以归还。
- 归还
- 编码:
/**
* 退还任务
* 一个候选人拾取了这个任务之后其他的用户就没有办法拾取这个任务了
* 所以如果一个用户拾取了任务之后又不想处理了,那么可以退还
*/
@Test
public void unclaimTaskCandidate(){
// 获取流程引擎对象
ProcessEngine processEngine = configuration.buildProcessEngine();
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
.processInstanceId("50001")
//.processDefinitionId("houXuanRen:1:47504")
.taskAssignee("候选1")
.singleResult();
if(task != null){
// 归还对应的任务
taskService.unclaim(task.getId());
System.out.println("归还拾取成功");
}
}
- 效果:
- act_ru_task表中assignee_字段又变成空了:
- 这回我们就可以使用用户”候选1、候选2、候选3......“登录系统,都能看见任务,并都有机会拾取。
- 归还规则:
- 拾取以后,是可以归还的
- 交接(其中一个候选人必须先拾取任务,这样他才会有交换的权限)
- 编码:
/**
* 任务的交接:
* 如果我获取了任务,但是不想执行,那么我可以把这个任务交接给其他的用户
*/
@Test
public void taskCandidate(){
// 获取流程引擎对象
ProcessEngine processEngine = configuration.buildProcessEngine();
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
.processInstanceId("50001")
//.processDefinitionId("houXuanRen:1:47504")
.taskAssignee("候选1")
.singleResult();
if(task != null){
// 任务的交接
taskService.setAssignee(task.getId(),"候选2");
System.out.println("任务交接给了'候选2'");
}
}
- 效果:
- act_ru_task表中assignee_字段,从”候选1“变成了”候选2“。
- 候选2可以登录系统,并处理这个任务了。
- 交换规则:先拾取,才有权交接出去
- 完全任务
- 编码:
/**
* 完成任务
*/
@Test
public void completeTaskHouXuan(){
// 获取流程引擎对象
ProcessEngine processEngine = configuration.buildProcessEngine();
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
.processInstanceId("50001")
//.processDefinitionId("houXuanRen:1:47504")
.taskAssignee("候选2")
.singleResult();
if(task != null){
// 完成任务
taskService.complete(task.getId());
System.out.println("完成Task");
}
}
- 效果:
- 任务流转到下一个审批环节了
- act_ru_task表中assignee_字段,从”候选2“变成了”ww“
十二 候选人组
- 应用场景
- 当某个任务Task候选人比较多的情况下,我们可以做一个分组处理
- 方式1:在UI界面中的操作
- 第一步:中,创建多个用户:
- 第二步:在UI界面中,创建组:
- 第三步:在UI界面中,把用户分配到这个组中(即把用户跟组做一个关联):
- 第四步:在UI界面中,编辑流程图,把某个任务Task的“分配用户”指定为一个组
- 注:act_id_user和系统用户表T_USER表如何同步?只需要在T_USER表中创建用户的时候,同步到act_id_user表中即可。
- 方式2:编码
- 第一步:管理用户和组
- 第1步:用户管理
- 第a步:创建多个用户
- 第b步:act_id_user表中此用户可见:
- 注:act_id_user和系统用户表T_USER表如何同步?只需要在T_USER表中创建用户的时候,同步到act_id_user表中即可。
- 第2步:Group管理
- 第a步:创建1个或多个用户组。
- 第b步:act_id_group表中此组可见:
- 第3步:把用户分配到组
- 第a步:将用户分配给对应的Group。
- 第b步:查看n对n关系表(用户与组关联关系表)act_id_membership:
- 第二步:候选人组应用,即把任务Task分配到某个组的所有成员(用户)
- 第1步:在UI界面中,创建流程图
- 第a步:创建流程图
- 第b步:给任务Task分配用户组:
- 第c步:保存,并导出为.xml文件
- 第2步:在idea中,流程的部署运行
- 第a步:流程部署
- 第b步:启动流程,即创建一个流程实例
- 首先,查询所有的组
- 其次,遍历所有组,找到合适的一个组
- 接着,给流程定义中的UEL表达式赋值: variables.put("group1",group.getId()); // 给流程定义中的UEL表达式赋值
- 最后,启动流程
- 第c步:验证
- act_ru_variable:多了group1的流程变量
- act_ru_task:多一个任务task:
- 注意:这里ASSIGNEE_为空,即没有处理人。为什么呢?因为这里分配的不是指定的一个候选人,我们分配的是一个组。
- act_ru_identitylink:任务与候选组的关系
- 第3步:任务的查询、拾取、处理
- 第a步:根据登录的用户查询对应的可以拾取的任务
- 第b步:拾取任务
task表变了:注意:一个候选人拾取了这个任务之后其他的用户就没有办法拾取这个任务了。所以如果一个用户拾取了任务之后又不想处理了,那么可以退还。
- 第c步:退还任务
- 第d步:交接任务
- 第d步:处理任务
- task表变了:
十三 网关
- 概述
- 作用:通过网关来控制流程(审批)的方向,绘制复杂流程。
- 排他网关:多条分支只会走其中的一条分支
- 案例演示:
- 第一步:UI,绘制流程图:
- 第1步:任务”创建请假单“简单点,固定候选人”zhangsan“。
- 第2步:排他网关
- 名称:判断出差申请的天数
- 第3步:流条件(连线条件)1:${num<3}。
- 第4步:流条件(连线条件)2:${num>3}。
- 第5步:任务”部门经理审批“简单点,固定候选人”lisi“。
- 第6步:任务”总经理审批“简单点,固定候选人”wangwu“。
- 第7步:任务”人事审批“简单点,固定候选人”zhaoliu“。
- 第8步:保存,下载,idea
- 第二步:idea,编码
- 第1步:部署流程
- act_re_procdef:获取流程id,后面通过流程id启动流程
- 第2步:创建(启动)流程实例
- 第a步:设置流程变量num=2
- act_ru_variable:有流程变量num=2
- act_ru_task:处理人“zhangsan”
- 第3步:处理任务
- act_ru_task:因为num<3所以走“部门经理审批”
- 对比:
- 没有设置网关的情况下,如果出现条件都不满足的情况,整个流程就异常结束了。
- 设置了网关,如果出现条件都不满足的情况时,任务没有完成,还是原来的任务,我们可以重置流程变量。
- 设置了网关,如果有多个条件满足时,流程会走编号较小 或 最先设置的那条路,它会自己去选择。
- 并行网关
- 概述
- 应用场景:多个用户同时去审批。比如开发人员请假,同时要“项目经理”和“技术经理”同时审批完成了,你才能休假。
- 众多分支里面每个都走。
- 案例演示:
- 第一步:UI,绘制流程图:
- 第1步:任务Task,创建请假单,固定给zhangsan
- 第2步:分支 —— 开始(入口)并行网关
- 第3步:任务Task,技术经理审批,固定给lisi
- 第4步:任务Task,项目经理审批,固定给wangwu
- 第5步:汇聚 —— 结束(出口)并行网关
- 第6步:排他网关
- 第a步:流条件(连线条件)1:${num>3}。
- 第b步:流条件(连线条件)2:${num<3}。
- 第7步:任务Task,总经理审批,固定给zhaoliu
- 第8步:结束
- 第9步:保存,下载,idea
- 第二步:idea,编码
- 第1步:部署流程
- act_re_procdef:获取流程定义ID,qj-BingXing:1:27504
- 第2步:创建(启动)流程实例
- act_ru_task:创建请假申请 zhangsan
- act_ru_variable:num=4
- act_ru_execution:可获取执行实例id
- 第3步:处理任务“创建请假申请”,zhangsan
- act_ru_task:产生并行的两条任务Task。当我们执行了“创建请假单”后,到并行网关的位置的时候,在ACT_RU_TASK表中就有两条记录,即两条并行的任务Task我们要去处理。
- act_ru_execution:有3条执行实例:
- 注意:1个任务可以包含多个流程实例。
- 从上图可以看出,1个任务里面有两个执行实例。也就是说,在ACT_RU_EXECUTION中会同时有三条记录,一个任务对应的有两个执行实例。
- 第4步:处理任务“技术经理审批”,lisi
- act_ru_task:lisi的任务被删除了,wangwu(项目经理审批)还在。
- act_ru_execution:3条执行实例都还在。
- 第5步:处理任务“项目经理审批”,wangwu
- act_ru_variable:num=4
- act_ru_task:wangwu(项目经理审批)的任务也删除了,因为num=4>3,所以在排他网关这里走的是总经理审批:
- act_ru_execution:3条执行实例都还在。
- 第6步:处理任务“总经理审批”,zhaoliu
- 第7步:流程结束
- 包含网关
- 概述
- 应用场景:众多分支里面,有些分支必须要去执行的,有些分支可以不去执行。比如,开发人员请假时,根据条件判断,技术经理和项目经理只会有1个去做审批,但人事经理是必须要去做审批的。
- 示例
- 第一步:UI,绘制流程图:
- 第1步:任务Task,创建请假单,固定给zhangsan
- 第2步:包容网关
- 第a步:流条件(连线条件)1:${num>3}。
- 第b步:流条件(连线条件)2:无
- 第a步:流条件(连线条件)3:${num<=3}。
- 第3步:任务Task,项目经理审批,固定给U1
- 第4步:任务Task,从事审批,固定给U2
- 第5步:任务Task,技术经理审批,固定给U3
- 第6步:排他网关
- 第a步:流条件(连线条件)1:${num>3}。
- 第a步:流条件(连线条件)3:${num<=3}。
- 第7步:任务Task,总经理审批,固定给wangzong
- 第8步:结束
- 第9步:保存,下载,idea
- 第二步:idea,编码
- 第1步:部署流程
- act_re_procdef:获取流程定义ID,qj-baorong:1:27504
- 第2步:创建(启动)流程实例
- act_ru_task:创建请假单 zhangsan
- act_ru_variable:num=4
- act_ru_execution:可获取执行实例id
- 第3步:处理任务“创建请假单”,zhangsan
- 第a步:包容网关
- 必须走,人事审批
- 因为num=4,所以还要走项目经理审批
- 条件不满足,所以不走技术经理审批
- act_ru_task:2条任务,”项目经理审批“和”人事审批“
- act_ru_execution:3条执行实例
- 第4步:处理任务“项目经理审批”,U1
- act_ru_task:只剩1条任务,人事审批
- 第5步:处理任务“项目经理审批”,U2
- act_ru_task:删除任务,从事审批
- 因为num=4>3,所以在排他网关这里走的是总经理审批
- 第6步:处理任务“总经理审批”,wangzong
- 第7步:流程结束
- 事件网关
- 概述: 事件网关允许根据事件判断流向。网关的每个外出顺序流都要连接到一个中间捕获事件。 当流程到达一个基于事件网关,网关会进入等待状态:会暂停执行。与此同时,会为每个外出顺序流创建相对的事件订阅。 事件网关的外出顺序流和普通顺序流不同,这些顺序流不会真的"执行", 相反它们让流程引擎去决定执行到事件网关的流程需要订阅哪些事件。 要考虑以下条件:
- 事件网关必须有两条或以上外出顺序流;
- 事件网关后,只能使用intermediateCatchEvent类型(activiti不支持基于事件网关后连接ReceiveTask)
- 连接到事件网关的中间捕获事件必须只有一个入口顺序流。
- ps:事件网关肯定是跟相关的事件有关联的,在事件章节里再具体去学习它。
十四 flowable整合springboot
- 概述
- 可参考flowable官网的用户手册。
- Flowable Actuator Endpoint:flowable的健康检查、指标监测。
- 详细步骤
- 第一步:数据库(24张表)的初始化
- 第1创建maven、springboot、spring web项目
- 第2步:pom.xml中添加依赖
- flowable-spring-boot-starter:flowable的起步依赖
- mysql-connector-java:连接mysql
- druid:连接池
- junit:单元测试
- 第3步:application.yml配置文件 spring: datasource:#数据源(库)配置 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url:jdbc:mysql://10.203.5.185:3306/fLearn?serverTimezone=UTC&nullCatalogMeansCurrent=true username: root password: 123456 hikari:#下面是数据库连接池的一些配置 minimum-idle: 5 idle-timeout: 600000 maximum-pool-size: 10 auto-commit: true pool-name: MyHikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1 flowable: async-executor-activate: false #关闭定时任务JOB # 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。 database-schema-update: true server: port: 8082
- 第4步:验证
- 第a步:启动项目,自动创建24张表。
- 第b步:从容器中获取ProcessEngine对象:
@SpringBootTest
class FlowableSpringbootApplicationTests {
/*
springboot程序在启动的时候,会自动完成流程引擎ProcessEngine processEngine的注入。
ProcessEngine processEngine相关的XxxService,也会自动被注入。
因此XxxService可以直接从spring容器中获取,或者通过ProcessEngine processEngine点(".")出来。
*/
@Autowired
private ProcessEngine processEngine;
@Test
void contextLoads() {
System.out.println("processEngine="+processEngine);
}
}
- 第二步:UI,绘制流程中图
- 第1步:任务task,创建请假单,${us1}
- 第2步:任务task,总经理审批 ,${us2}
- 第二步:流程的部署
- 方案1:自动部署
- 第1步:在resource目录下创建processes 目录
- 第2步:把请假流程spingboot.bpmn20.xml文件放进processes 目录
- 第3步:启动项目(或者运行单元测试),记得把target目录删除
- 第4步:验证
- ACT_RE_DEPLOYMENT:有1条部署信息
- act_re_procdef:有1条流程实例信息
- 第5步:重启项目(先关闭)
- 不会重复部署
- 附:官网提出的自动部署方式:
- processes目录下的任何BPMN 2.0流程定义都会被自动部署。创建processes目录,并在其中创建示例流程定义(命名为one-task-process.bpmn20.xml)。
- cases目录下的任何CMMN 1.1事例都会被自动部署。
- forms目录下的任何Form定义都会被自动部署。
- 方案2:手动部署(建议使用)
- 第1步:把Xxx.bpmn20.xml文件移出processes目录
- 第2步:编码:
@SpringBootTest
class FlowableSpringBoot28ApplicationTests {
@Autowired
private ProcessEngine processEngine;
@Autowired
private RepositoryService repositoryService;
@Autowired
private TaskService taskService;
@Autowired
private RuntimeService runtimeService;
/**
* Deploy
*/
@Test
void testDeploy() {
//RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("请假流程.bpmn20.xml")
.name("holiday")
.deploy();
System.out.println("deploy.getId() = " + deploy.getId());
System.out.println("deploy.getName() = " + deploy.getName());
}
}
- 同理:.bar 或 .zip
- 第三步:启动流程
- 启动流程和前面介绍的就没什么差异了,通过RuntimeService来实现:
/**
* start process
*/
@Test
void startFlow(){
Map<String,Object> map = new HashMap();
map.put("assignee0","zhangsan");
map.put("assignee1","zhangsan");
runtimeService.startProcessInstanceById("holiday28:2:3653a34e-ae45-11ec-969d-c03c59ad2248",map);
}
- 第四步:完成流程
- 处理流程和前面介绍的也一样,通过TaskService来处理:
/**
* complete Task
*/
@Test
void completeTask(){
Task task = taskService.createTaskQuery()
.processInstanceId("fb166cd8-ae45-11ec-92c4-c03c59ad2248")
.taskAssignee("zhangsan")
.singleResult();
if(task != null){
taskService.complete(task.getId());
System.out.println("complete ....");
}
}