好的网站设计制作,网站主页排版,临沂建设网,京东企业门户Flowable基础篇 课程环境说明#xff1a;
JDK8Flowable6.7.2MySQL8
一、基础知识科普
1.工作流发展 BPM(BusinessProcessManagement)#xff0c;业务流程管理是一种管理原则#xff0c;通常也可以代指BPMS(BusinessProcessManagementSuite)#xff0c;是一个实现整合不同…Flowable基础篇 课程环境说明
JDK8Flowable6.7.2MySQL8
一、基础知识科普
1.工作流发展 BPM(BusinessProcessManagement)业务流程管理是一种管理原则通常也可以代指BPMS(BusinessProcessManagementSuite)是一个实现整合不同系统和数据的流程管理软件套件. BPMN(BusinessProcessModelandNotation)是基于流程图的通用可视化标准。该流程图被设计用于创建业务流程操作的图形化模型。业务流程模型就是图形化对象的网状图包括活动和用于定义这些活动执行顺序的流程设计器。BPMN2.0正式版本于2011年1月3日发布常见的工作流引擎如Activiti、Flowable、jBPM 都基于 BPMN 2.0 标准。 然后来看看BPM的发展历程 说明最新版本的Flowable7.0.0需要的环境https://tkjohn.github.io/flowable-userguide/#download
SpringBoot3Spring6Java 17
2.流程设计器
2.1 误区说明 为什么一定要纠结于找IDEA中安装绘制的插件呢我们需要的是一个能够帮助我们绘制流程图的工具。即使没有流程设计器我们也可以通过代码来定义流程的。
2.2 古老的Eclipse 在Eclipse中我们可以通过安装插件来绘制流程图。然后导出对应的bpmn文件。然后就可以做相关的部署操作了。 这个只做简单介绍了解即可。
2.3 官方的FlowableUI Flowable官方给我们提供了一个功能完备的基于web应用的流程设计器。可以用于流程相关的操作。具体提供了如下的功能
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进行认证因此应用需要运行在相同的域名下。
2.4 BPMN.js自定义 FlowableUI是官方提供的针对国内复杂的流程业务需求有时并不能很好的满足企业的工作流的需求。这时我们就可以基于bpmn.js来自定义流程设计器官网地址https://bpmn.io/toolkit/bpmn-js/walkthrough/ 开源的学习资料https://github.com/LinDaiDai/bpmn-chinese-document/tree/master/LinDaiDai
2.5 第三方的设计器 如何感觉完全基于bpmn.js来从零开发一个流程设计器太费时了。也可以找一些开源的别人写好的流程设计器比如
https://gitee.com/zhangjinlibra/workflow-engine?_fromgitee_searchhttps://gitee.com/MiyueSC/bpmn-process-designer?_fromgitee_search其他的可以自行在GitHub或者gitee上查找
最后我们可以不借助流程设计器来定义流程图。我们可以通过BpmnModle对象来自定义流程图
二、流程审批初体验
1.FlowableUI安装 官方提供的FlowableUI是集成在Flowable源码包中的。但是在最新的7.0.0中已经移除了。我们需要在6.7.2中获取。 https://github.com/flowable/flowable-engine/releases 对应的流程设计器在如下目录中 然后我们把这个flowable-ui.war扔到Tomcat容器中然后启动Tomcat服务即可不要部署在有中文的目录下 访问地址http://localhost:8080/flowable-ui 登录成功的效果 2.用户管理 流程审批是伴随着用户的审批处理的。所以肯定需要用户的参与。我们先通过身份管理应用程序来创建两个测试的用户。 点击进入后可以看到在FlowableUI中针对用户这块是从三个维度来管理的
用户组权限控制
我们先就创建普通的用户。并分配相关的权限 创建了zhangsan并设置了密码为123.然后分配相关的权限 然后我们就可以通过zhangsan来登录然后就可以看到相关的功能权限。 同样的操作我们再创建下lisi的账号。 3.流程定义 有了相关的用户信息。我们就可以来创建流程图了。这块我们需要通过建模应用程序来实现。 点击后进入看到如下的界面 第一次进入提示我们还没有创建流程模型我们可以点击右上角的创建流程按钮开始创建第一个流程案例。 然后会进入到具体的流程图的绘制界面。 创建第一个请假流程图 然后我们需要分配相关节点的审批人
人事审批-- zhangsan 审批经理审批 – lisi 审批
分配操作 选择分配给单个用户 并搜索到zhangsan. 相同的操作完成经理审批节点的配置然后就可以保存退出了 到此流程定义完成~
4.图标介绍 BPMN 2.0是业务流程建模符号2.0的缩写。它由Business Process Management Initiative这个非营利协会创建并不断发展。作为一种标识BPMN 2.0是使用一些符号来明确业务流程设计流程图的一整套符号规范它能增进业务建模时的沟通效率。目前BPMN2.0是最新的版本它用于在BPM上下文中进行布局和可视化的沟通。接下来我们先来了解在流程设计中常见的 符号。
BPMN2.0的基本符合主要包含
4.1 事件图标 在Flowable中的事件图标启动事件边界事件,中间事件和结束事件. 4.2 活动(任务)图标 活动是工作或任务的一个通用术语。一个活动可以是一个任务还可以是一个当前流程的子处理流程 其次你还可以为活动指定不同的类型。常见活动如下: 4.3 结构图标 结构图标可以看做是整个流程活动的结构一个流程中可以包括子流程。常见的结构有 4.4 网关图标 网关用来处理决策有几种常用网关需要了解 5.流程部署 有了流程定义后然后我们可以通过FlowableUI中提供的应用程序我们可以来完成部署的相关操作。 进入功能页面后我们可以创建应用程序。 然后在弹出的菜单中录入基本信息 点击选择需要绑定的流程定义 点击进入后我们可以点击右上角的发布操作来完成部署的行为 部署出现的时候出现了异常信息 同时在看控制台出现了如下的问题 原因是部署的项目不能放在有中文的路径下。所以我们调整下Tomcat的位置.然后再启动即可然后在发布就提示发布成功了 6.流程审批 流程部署完毕后我们就可以启动一个流程实例了。然后就可以走流程审批的操作。启动流程实例我们通过任务应用程序来处理。 点击启动新的流程 启动流程后进入到人事审批的阶段 点击显示流程可以看到当前的状态 然后我们可以切换到zhangsan账号登录后来审批操作 点击完成后点击流程可以看到对应的流程情况 然后切换到lisi登录来继续审批 点击完成后审批操作完成。到此FlowableUI的流程审批操作就演示完成。
三、流程审批操作
1.案例环境 创建一个基本的SpringBoot项目。相关的版本如下
SpringBoot2.6.13JDK8Flowable6.7.2
对应的依赖如下
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!--Flowable的核心依赖--dependencygroupIdorg.flowable/groupIdartifactIdflowable-spring-boot-starter/artifactIdversion6.7.2/version/dependency!-- MySQL的依赖 --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.1.14/version/dependency!-- 单元测试 --dependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.13.2/versionscopetest/scope/dependency!-- 日志相关 --dependencygroupIdorg.slf4j/groupIdartifactIdslf4j-api/artifactIdversion1.7.21/version/dependencydependencygroupIdorg.slf4j/groupIdartifactIdslf4j-log4j12/artifactIdversion1.7.21/version/dependency2.流程部署 流程部署我们可以在Spring的环境下部署。也可以在非Spring的环境下部署。下面先展示非Spring环境下的部署操作 /*** 不通过Spring容器。我们单独的构建ProcessEngine对象来实现部署的操作*/Testvoid contextLoads() {// 1.流程引擎的配置ProcessEngineConfiguration cfg new StandaloneProcessEngineConfiguration().setJdbcUrl(jdbc:mysql://localhost:3306/flowable-learn?serverTimezoneUTCnullCatalogMeansCurrenttrue).setJdbcUsername(root).setJdbcPassword(123456).setJdbcDriver(com.mysql.cj.jdbc.Driver).setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);// 2.构建流程引擎对象ProcessEngine processEngine cfg.buildProcessEngine();System.out.println(processEngine);Deployment deploy processEngine.getRepositoryService().createDeployment().addClasspathResource(process/HolidayDemo1.bpmn20.xml).name(第一个流程案例).deploy();System.out.println(deploy.getId());}方法执行成功后会在这三张表中记录相关的部署信息
act_ge_bytearray:记录流程定义的资源信息。xml和流程图的图片信息act_re_deployment:流程部署表记录这次的部署行为act_re_procdef:流程定义表记录这次部署动作对应的流程定义信息
databaseSchemaUpdate用于设置流程引擎启动关闭时使用的数据库表结构控制策略
false (默认): 当引擎启动时检查数据库表结构的版本是否匹配库文件版本。版本不匹配时抛出异常。true: 构建引擎时检查并在需要时更新表结构。表结构不存在则会创建。create-drop: 引擎创建时创建表结构并在引擎关闭时删除表结构。
注意:要注意区分流程部署和流程定义的关系
一次部署操作可以部署多个流程定义
// 2.构建流程引擎对象
ProcessEngine processEngine cfg.buildProcessEngine();
System.out.println(processEngine);
Deployment deploy processEngine.getRepositoryService().createDeployment().addClasspathResource(process/HolidayDemo1.bpmn20.xml) // 部署一个流程.addClasspathResource(process/消息中间事件.bpmn20.xml) // 部署第二个流程.name(第一个流程案例).deploy();当然上面的操作我们是自己定义ProcessEngine和ProcessEngineConfiguration来实现流程引擎对象的获取操作我们完全可以把这些初始化的操作交给Spring容器来管理。接下来我们看看在SpringBoot项目中怎么简化处理。首先依赖我们上面已经添加了。然后需要在application.yml中配置如下信息
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/flowable-learn?serverTimezoneUTCnullCatalogMeansCurrenttrueusername: rootpassword: 123456hikari:minimum-idle: 5idle-timeout: 600000maximum-pool-size: 10auto-commit: truepool-name: MyHikariCPmax-lifetime: 1800000connection-timeout: 30000connection-test-query: SELECT 1
flowable:async-executor-activate: true #关闭定时任务JOB# 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时会自动将数据库表结构升级至新版本。database-schema-update: true
server:port: 8082
logging:level:org:flowable: debug注意在url中需要添加nullCatalogMeansCurrenttrue属性。不然启动服务的时候会出现如下问题 具体部署流程的代码,就可以直接从Spring容器中获取ProcessEngine对象了。大大简化了相关的代码 Autowiredprivate ProcessEngine processEngine;/*** 流程部署*/Testpublic void deployFlow(){Deployment deploy processEngine.getRepositoryService().createDeployment()// 部署一个流程.addClasspathResource(process/HolidayUI.bpmn20.xml)// 部署第二个流程//.addClasspathResource(process/消息中间事件.bpmn20.xml).name(第一个流程案例).deploy();System.out.println(deploy.getId());}3.启动流程实例 流程部署完成后我们就可以发起一个流程实例。具体的操作如下 /*** 发起流程*/Testpublic void startProcess(){// 发起流程需要通过RuntimeService来实现RuntimeService runtimeService processEngine.getRuntimeService();//对应act_re_procdef的id,流程定义之后会生成id , key对应KEY// 一、流程实例id启动流程实例String processInstanceId HolidayUI:1:c8fa0f40-71a8-11ef-b3f4-287fcff7031e;runtimeService.startProcessInstanceById(processInstanceId);// 二、指定流程定义key启动流程实例 key对应定义流程时指定的key: HolidayUI//runtimeService.startProcessInstanceByKey(HolidayUI);//runtimeService.startProcessInstanceByKeyAndTenantId()}启动流程需要通过RuntimeService来实现。同时在启动流程的时候有两个方法可以调用
startProcessInstanceById: 对应于act_re_procdef 表中的idstartProcessInstanceByKey: 对应于act_re_procdef 表中的key
启动流程后根据我们的定义。流程会走到人事审批。 这时我们可以在act_ru_task表中看到对应的记录。act_ru_task记录的都是当前待办的记录信息 还有一个要注意的每启动一个流程实例那么就会在act_hi_procinst表中维护一条记录。然后在act_ru_execution会记录流程的分支
流程定义和流程实例的关系
流程定义Java中的类流程实例Java中的对象
4.流程审批 流程启动后会进入到相关的审批节点。上面的案例中就会进入到人事审批节点。审批人是zhangsan,也就是zhangsan有了一条待办信息。这时候我们可以查询下zhangsan有哪些待办任务。 /*** 待办任务查询*/Testvoid findTask(){// 任务查询这块我们可以通过 TaskService 来实现TaskService taskService processEngine.getTaskService();// 查询的其实就是 act_ru_task 中的记录ListTask list taskService.createTaskQuery().taskAssignee(zhangsan) // 根据审批人来查询.list();// 返回多条记录for (Task task : list) {System.out.println(task.getId());}}查询出了对应的两条记录 找到了需要审批的任务。我们就可以根据 taskId来完成审批的操作 /*** 完成任务的审批*/Testvoid completeTask(){TaskService taskService processEngine.getTaskService();// 需要审批的任务 IdString taskId 926b41f8-7a4c-11ee-97fd-c03c59ad2248;taskService.complete(taskId); // 通过complete方法完成审批}审批通过后就会进入到经理审批的节点。 相同的操作可以完成经理审批的待办处理。然后就完成了第一个流程的审批处理操作了。
5.各个Service服务 Service是工作流引擎提供用于进行工作流部署、执行、管理的服务接口我们使用这些接口可以就是操作服务对应的数据表 5.1 Service创建方式
通过ProcessEngine创建Service
方式如下
RuntimeService runtimeService processEngine.getRuntimeService();
RepositoryService repositoryService processEngine.getRepositoryService();
TaskService taskService processEngine.getTaskService();
// ...5.2 Service总览
service名称service作用RepositoryServiceFlowable的资源管理类RuntimeServiceFlowable的流程运行管理类TaskServiceFlowable的任务管理类HistoryServiceFlowable的历史管理类ManagerServiceFlowable的引擎管理类
简单介绍
RepositoryService 是activiti的资源管理类提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此service将流程定义文件的内容部署到计算机。
除了部署流程定义以外还可以查询引擎中的发布包和流程定义。 暂停或激活发布包对应全部和特定流程定义。 暂停意味着它们不能再执行任何操作了激活是对应的反向操作。获得多种资源像是包含在发布包里的文件 或引擎自动生成的流程图。 获得流程定义的pojo版本 可以用来通过java解析流程而不必通过xml。
RuntimeService Activiti的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息
TaskService Activiti的任务管理类。可以从这个类中获取任务的信息。
HistoryService Flowable的历史管理类可以查询历史信息执行流程时引擎会保存很多数据根据配置比如流程实例启动时间任务的参与者 完成任务的时间每个流程实例的执行路径等等。 这个服务主要通过查询功能来获得这些数据。
ManagementService Activiti的引擎管理类提供了对Flowable 流程引擎的管理和维护功能这些功能不在工作流驱动的应用程序中使用主要用于 Flowable 系统的日常维护。
四、相关表结构介绍 接下来我们结合上面演示的第一个审批流程来详细的介绍下Flowable中的相关表结构的含义和作用。
1.表结构介绍
支持的数据库有
Activiti数据库类型示例JDBC URL备注h2jdbc:h2:tcp://localhost/activiti默认配置的数据库mysqljdbc:mysql://localhost:3306/activiti?autoReconnecttrue已使用mysql-connector-java数据库驱动测试oraclejdbc:oracle:thin:localhost:1521:xepostgresjdbc:postgresql://localhost:5432/activitidb2jdbc:db2://localhost:50000/activitimssqljdbc:sqlserver://localhost:1433;databaseNameactiviti (jdbc.drivercom.microsoft.sqlserver.jdbc.SQLServerDriver) OR jdbc:jtds:sqlserver://localhost:1433/activiti (jdbc.drivernet.sourceforge.jtds.jdbc.Driver)已使用Microsoft JDBC Driver 4.0 (sqljdb 工作流程的相关操作都是操作存储在对应的表结构中为了能更好的弄清楚Flowable的实现原理和细节我们有必要先弄清楚Flowable的相关表结构及其作用。在Flowable中的表结构在初始化的时候会创建五类表结构具体如下
ACT_RE RE’表示 repository。 这个前缀的表包含了流程定义和流程静态资源 图片规则等等。ACT_RURU’表示 runtime。 这些运行时的表包含流程实例任务变量异步任务等运行中的数据。 Flowable只在流程实例执行过程中保存这些数据 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。ACT_HIHI’表示 history。 这些表包含历史数据比如历史流程实例 变量任务等等。ACT_GE GE 表示 general。 通用数据 用于不同场景下ACT_ID: ’ID’表示identity(组织机构)。这些表包含标识的信息如用户用户组等等。
具体的表结构的含义:
表分类表名解释一般数据[ACT_GE_BYTEARRAY]通用的流程定义和流程资源[ACT_GE_PROPERTY]系统相关属性流程历史记录[ACT_HI_ACTINST]历史的流程实例[ACT_HI_ATTACHMENT]历史的流程附件[ACT_HI_COMMENT]历史的说明性信息[ACT_HI_DETAIL]历史的流程运行中的细节信息[ACT_HI_IDENTITYLINK]历史的流程运行过程中用户关系[ACT_HI_PROCINST]历史的流程实例[ACT_HI_TASKINST]历史的任务实例[ACT_HI_VARINST]历史的流程运行中的变量信息流程定义表[ACT_RE_DEPLOYMENT]部署单元信息[ACT_RE_MODEL]模型信息[ACT_RE_PROCDEF]已部署的流程定义运行实例表[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]用户表
2.部署流程 我们来分析下部署流程涉及到了哪些表结构
部署资源表act_ge_bytearray
字段名称备注ID_主键REV_版本号NAME_名称部署的文件名称如holiday-request-new.bpmn20.xml、holiday-request-new.bpmn20.pngDEPLOYMENT_ID_部署IDBYTES_字节二进制数据GENERATED_是否系统生成0为用户上传1为系统自动生成 比如系统会 自动根据xml生 成png
部署ID表act_re_deployment
字段名称备注ID_主键NAME_名称CATEGORY_分类TENANT_ID_租户IDDEPLOY_TIME_部署时间DERIVED_FROM_来源于DERIVED_FROM_ROOT_来源于ENGINE_VERSION_流程引擎的版本
流程表act_re_procdef
字段名称备注ID_主键REV_版本号CATEGORY_分类流程定义的Namespace就是类别NAME_名称KEY_标识VERSION_版本DEPLOYMENT_ID_部署IDRESOURCE_NAME_资源名称流程bpmn文件名称DGRM_RESOURCE_NAME_图片资源名称DESCRIPTION_描述HAS_START_FORM_KEY_拥有开始表单标识start节点是否存在formKey 0否 1是HAS_GRAPHICAL_NOTATION_拥有图形信息SUSPENSION_STATE_挂起状态暂停状态 1激活 2暂停TENANT_ID_租户ID
注意
业务流程定义数据表。此表和ACT_RE_DEPLOYMENT是多对一的关系即一个部署的bar包里可能包含多个流程定义文件每个流程定义文件都会有一条记录在ACT_REPROCDEF表内每个流程定义的数据都会对于ACT_GE_BYTEARRAY表内的一个资源文件和PNG图片文件。和ACT_GE_BYTEARRAY的关联是通过程序用ACT_GE_BYTEARRAY.NAME与ACT_RE_PROCDEF.NAME_完成的
3.挂起和激活 部署的流程默认的状态为激活如果我们暂时不想使用该定义的流程那么可以挂起该流程。当然该流程定义下边所有的流程实例全部暂停。
流程定义为挂起状态该流程定义将不允许启动新的流程实例同时该流程定义下的所有的流程实例都将全部挂起暂停执行。
/*** 挂起流程*/Testpublic void test05(){RepositoryService repositoryService processEngine.getRepositoryService();ProcessDefinition processDefinition repositoryService.createProcessDefinitionQuery().processDefinitionId(holiday:1:4).singleResult();// 获取流程定义的状态boolean suspended processDefinition.isSuspended();System.out.println(suspended suspended);if(suspended){// 表示被挂起System.out.println(激活流程定义);repositoryService.activateProcessDefinitionById(holiday:1:4,true,null);}else{// 表示激活状态System.out.println(挂起流程);repositoryService.suspendProcessDefinitionById(holiday:1:4,true,null);}}/*** 挂起流程实例*/Testvoid suspendInstance(){// 挂起流程实例runtimeService.suspendProcessInstanceById(a7ae5680-7ba3-11ee-809a-c03c59ad2248);// 激活流程实例//runtimeService.activateProcessInstanceById(a7ae5680-7ba3-11ee-809a-c03c59ad2248);}具体的实现其实就是更新了流程定义表中的字段 而且通过REV_字段来控制数据安全也是一种乐观锁的体现了如果要启动一个已经挂起的流程就会出现如下的错误 4.启动流程 当我们启动了一个流程实例后会在ACT_RU_*对应的表结构中操作,运行时实例涉及的表结构共10张
ACT_RU_DEADLETTER_JOB 正在运行的任务表ACT_RU_EVENT_SUBSCR 运行时事件ACT_RU_EXECUTION 运行时流程执行实例ACT_RU_HISTORY_JOB 历史作业表ACT_RU_IDENTITYLINK 运行时用户关系信息ACT_RU_JOB 运行时作业表ACT_RU_SUSPENDED_JOB 暂停作业表ACT_RU_TASK 运行时任务表ACT_RU_TIMER_JOB 定时作业表ACT_RU_VARIABLE 运行时变量表 启动一个流程实例的时候涉及到的表有
ACT_RU_EXECUTION 运行时流程执行实例ACT_RU_IDENTITYLINK 运行时用户关系信息ACT_RU_TASK 运行时任务表ACT_RU_VARIABLE 运行时变量表
ACT_RU_EXECUTION表结构
字段名称备注ID_主键REV_版本号PROC_INST_ID_流程实例IDBUSINESS_KEY_业务主键IDPARENT_ID_父执行流的IDPROC_DEF_ID_流程定义的数据IDSUPER_EXEC_ROOT_PROC_INST_ID_流程实例的root流程idACT_ID_节点实例IDIS_ACTIVE_是否存活IS_CONCURRENT_执行流是否正在并行IS_SCOPE_IS_EVENT_SCOPE_IS_MI_ROOT_SUSPENSION_STATE_流程终端状态CACHED_ENT_STATE_TENANT_ID_租户编号NAME_START_TIME_开始时间START_USER_ID_开始的用户编号LOCK_TIME_锁定时间IS_COUNT_ENABLED_EVT_SUBSCR_COUNT_TASK_COUNT_JOB_COUNT_TIMER_JOB_COUNT_SUSP_JOB_COUNT_DEADLETTER_JOB_COUNT_VAR_COUNT_ID_LINK_COUNT_
创建流程实例后对应的表结构的数据 ACT_RU_TASK 运行时任务表
字段名称备注ID_主键REV_版本号EXECUTION_ID_任务所在的执行流IDPROC_INST_ID_流程实例IDPROC_DEF_ID_流程定义数据IDNAME_任务名称PARENT_TASK_ID_父任务IDDESCRIPTION_说明TASK_DEF_KEY_任务定义的ID值OWNER_任务拥有人ASSIGNEE_被指派执行该任务的人DELEGATION_委托人PRIORITY_优先级CREATE_TIME_创建时间DUE_DATE_耗时CATEGORY_类别SUSPENSION_STATE_是否挂起1代表激活 2代表挂起TENANT_ID_租户编号FORM_KEY_表单KeyCLAIM_TIME_拾取时间
创建流程实例后对应的表结构的数据 ACT_RU_VARIABLE 运行时变量表
字段名称备注ID_主键REV_版本号TYPE_参数类型可以是基本的类型也可以用户自行扩展NAME_参数名称EXECUTION_ID_参数执行IDPROC_INST_ID_流程实例IDTASK_ID_任务IDBYTEARRAY_ID_资源IDDOUBLE_参数为double则保存在该字段中LONG_参数为long则保存在该字段中TEXT_用户保存文本类型的参数值TEXT2_用户保存文本类型的参数值
创建流程实例后对应的表结构的数据 ACT_RU_IDENTITYLINK 运行时用户关系信息
字段名称备注ID_主键REV_版本号GROUP_ID_用户组IDTYPE_关系数据类型assignee支配人(组)、candidate候选人(组)、owner拥有人,participant参与者USER_ID_用户IDTASK_ID_任务IDPROC_INST_ID_流程定义IDPROC_DEF_ID_属性ID
创建流程实例后对应的表结构的数据: 5.流程审批 上面的流程已经流转到了zhangsan这个用户这里然后可以开始审批了
// 获取流程引擎对象ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();TaskService taskService processEngine.getTaskService();Task task taskService.createTaskQuery().processDefinitionId(holiday:1:4).taskAssignee(zhangsan).singleResult();// 添加流程变量MapString,Object variables new HashMap();variables.put(approved,false); // 拒绝请假// 完成任务taskService.complete(task.getId(),variables);在正常处理流程中涉及到的表结构
ACT_RU_EXECUTION 运行时流程执行实例ACT_RU_IDENTITYLINK 运行时用户关系信息ACT_RU_TASK 运行时任务表ACT_RU_VARIABLE 运行时变量表
ACT_RU_TASK 运行时任务表 :会新生成一条记录 6.流程完毕 一个具体的流程审批完成后相关的数据如下
首先我们会发现
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 历史的流程运行中的变量信息
在我们上面的处理流程的过程中设计到的历史表有
ACT_HI_ACTINST 历史的流程实例
字段名称备注ID_主键PROC_DEF_ID_流程定义IDPROC_INST_ID_流程实例IDEXECUTION_ID_执行IDACT_ID_节点实例IDTASK_ID_任务IDCALL_PROC_INST_ID_调用外部的流程实例IDACT_NAME_节点名称ACT_TYPE_节点类型ASSIGNEE_处理人START_TIME_开始时间END_TIME_结束时间DURATION_耗时DELETE_REASON_删除原因TENANT_ID_租户编号 ACT_HI_IDENTITYLINK 历史的流程运行过程中用户关系
字段名称备注ID_主键GROUP_ID_组编号TYPE_类型USER_ID_用户编号TASK_ID_任务编号CREATE_TIME_创建时间PROC_INST_ID_流程实例编号SCOPE_ID_SCOPE_TYPE_SCOPE_DEFINITION_ID_ ACT_HI_PROCINST 历史的流程实例
字段名称备注ID_主键PROC_INST_ID_流程实例IDBUSINESS_KEY_业务主键PROC_DEF_ID_属性IDSTART_TIME_开始时间END_TIME_结束时间DURATION_耗时START_USER_ID_起始人START_ACT_ID_起始节点END_ACT_ID_结束节点SUPER_PROCESS_INSTANCE_ID_父流程实例IDDELETE_REASON_删除原因TENANT_ID_租户编号NAME_名称 ACT_HI_TASKINST 历史的任务实例
字段名称备注ID_主键PROC_DEF_ID_流程定义IDTASK_DEF_KEY_任务定义的ID值PROC_INST_ID_流程实例IDEXECUTION_ID_执行IDPARENT_TASK_ID_父任务IDNAME_名称DESCRIPTION_说明OWNER_实际签收人 任务的拥有者签收人默认为空只有在委托时才有值ASSIGNEE_被指派执行该任务的人START_TIME_开始时间CLAIM_TIME_任务拾取时间END_TIME_结束时间DURATION_耗时DELETE_REASON_删除原因PRIORITY_优先级别DUE_DATE_过期时间FORM_KEY_节点定义的formkeyCATEGORY_类别TENANT_ID_租户 ACT_HI_VARINST 历史的流程运行中的变量信息流程变量虽然在任务完成后在流程实例表中会删除但是在历史表中还是会记录的
字段名称备注ID_主键PROC_INST_ID_流程实例IDEXECUTION_ID_指定IDTASK_ID_任务IDNAME_名称VAR_TYPE_参数类型REV_数据版本BYTEARRAY_ID_字节表IDDOUBLE_存储double类型数据LONG_存储long类型数据… 五、任务分配 在做流程定义的时候我们需要给相关的用户节点指派对应的处理人。在Flowable中提供了三种分配的方式我们来分别介绍
1.固定分配 固定分配就是我们前面介绍的在绘制流程图或者直接在流程文件中通过Assignee来指定的方式. 2.表达式 Flowable使用UEL进行表达式解析。UEL代表Unified Expression Language是EE6规范的一部分查看EE6规范了解更多信息。为了在所有环境上支持UEL标准的所有最新特性我们使用JUEL的修改版本。 表达式可以用于例如Java服务任务 Java Service tasks, 执行监听器 Execution Listeners, 任务监听器 Task Listeners 与 条件流 Conditional sequence flows。尽管有值表达式与方法表达式两种表达式通过Flowable的抽象使它们都可以在需要expression表达式的地方使用。 ${myVar} ${myBean.myProperty} 2.1 值表达式 我们在处理的位置通过UEL表达式来占位。 然后做流程的部署和启动操作
/*** 流程部署操作*/
Test
public void test1(){// 1.获取ProcessEngine对象// 2.完成流程的部署操作 需要通过RepositoryService来完成RepositoryService repositoryService processEngine.getRepositoryService();// 3.完成部署操作Deployment deploy repositoryService.createDeployment().addClasspathResource(flow/test2.bpmn20.xml).name(请假流程-流程变量).deploy(); // 是一个流程部署的行为 可以部署多个流程定义的System.out.println(deploy.getId());System.out.println(deploy.getName());
}然后我们发起请假流程
/*** 发起一个流程*/
Test
public void test3(){ProcessEngine engine ProcessEngines.getDefaultProcessEngine();// 发起流程 需要通过 runtimeService来实现RuntimeService runtimeService engine.getRuntimeService();// 通过流程定义ID来启动流程 返回的是流程实例对象ProcessInstance processInstance runtimeService.startProcessInstanceById(test01:1:12503);System.out.println(processInstance.getId() processInstance.getId());System.out.println(processInstance.getDeploymentId() processInstance.getDeploymentId());System.out.println(processInstance.getDescription() processInstance.getDescription());
}我们发起流程后。根据流程的设计应该需要进入到人事审批。但是呢。审批的用户是${assign1}是一个流程变量。那么还没有赋值的情况下。那么系统是没有办法识别的。 我们需要在进入该节点前对流程变量赋值
/*** 发起一个流程*/
Test
public void test3(){ProcessEngine engine ProcessEngines.getDefaultProcessEngine();// 发起流程 需要通过 runtimeService来实现RuntimeService runtimeService engine.getRuntimeService();// 对流程变量做赋值操作MapString,Object map new HashMap();map.put(assgin1,张三);map.put(assign2,李四);// 通过流程定义ID来启动流程 返回的是流程实例对象ProcessInstance processInstance runtimeService.startProcessInstanceById(test01:1:12503,map);System.out.println(processInstance.getId() processInstance.getId());System.out.println(processInstance.getDeploymentId() processInstance.getDeploymentId());System.out.println(processInstance.getDescription() processInstance.getDescription());
}然后我们就可以看到对应的表结构中的待办记录 同时需要了解 ACT_RU_VARIABLE 2.2 方法表达式 方法表达式 Method expression: 调用一个方法可以带或不带参数。**当调用不带参数的方法时要确保在方法名后添加空括号以避免与值表达式混淆。**传递的参数可以是字面值(literal value)也可以是表达式它们会被自动解析。例如
${printer.print()}
${myBean.getAssignee()}
${myBean.addNewOrder(orderName)}
${myBean.doSomething(myVar, execution)}myBean是Spring容器中的个Bean对象表示调用的是bean的addNewOrder方法.我们通过案例来演示下。我们先定义对应的Service
先定义Bean
Component
public class MyBean {public String getAssignee(){System.out.println(本方法执行了....);return wangwu;}
}然后在绘制流程图的时候就可以对应的指派了。 然后我们先部署流程
/*** 流程部署操作*/
Test
public void test1(){// 1.获取ProcessEngine对象ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();// 2.完成流程的部署操作 需要通过RepositoryService来完成RepositoryService repositoryService processEngine.getRepositoryService();// 3.完成部署操作Deployment deploy repositoryService.createDeployment().addClasspathResource(flow/test3.bpmn20.xml).name(请假流程-方法表达式).deploy(); // 是一个流程部署的行为 可以部署多个流程定义的System.out.println(deploy.getId());System.out.println(deploy.getName());
}然后我们发起新的流程。注意在这块我们可以不用添加流程变量信息了。因为人事审批节点的审批人是通过流程方法来赋值的
/*** 发起一个流程*/
Test
public void test3(){ProcessEngine engine ProcessEngines.getDefaultProcessEngine();// 发起流程 需要通过 runtimeService来实现RuntimeService runtimeService engine.getRuntimeService();// 通过流程定义ID来启动流程 返回的是流程实例对象ProcessInstance processInstance runtimeService.startProcessInstanceById(test01:2:27503);System.out.println(processInstance.getId() processInstance.getId());System.out.println(processInstance.getDeploymentId() processInstance.getDeploymentId());System.out.println(processInstance.getDescription() processInstance.getDescription());
}可以看到操作成功。方法表达式被执行了 同时待办中的审批人就是方法表达式返回的结果 3.监听器分配 可以使用监听器来完成很多Flowable的流程业务。我们在此处使用监听器来完成负责人的指定那么我们在流程设计的时候就不需要指定assignee。创建自定义监听器
public class MyFirstListener implements TaskListener {/*** 监听器触发的回调方法* param delegateTask*/Overridepublic void notify(DelegateTask delegateTask) {System.out.println(----自定义的监听器执行了);if(EVENTNAME_CREATE.equals(delegateTask.getEventName())){// 表示是Task的创建事件被触发了// 指定当前Task节点的处理人delegateTask.setAssignee(boge666);}}
}在配置流程的时候关联监听器。注意对应的事件。CREATE 然后我们部署和启动流程后。可以看到自定义的监听器触发了 而且待办中的任务的处理人就是监听器中设置的信息 六、流程变量 流程变量可以用将数据添加到流程的运行时状态中或者更具体地说变量作用域中。改变实体的各种API可以用来更新这些附加的变量。一般来说一个变量由一个名称和一个值组成。名称用于在整个流程中识别变量。例如如果一个活动activity设置了一个名为 var 的变量那么后续活动中可以通过使用这个名称来访问它。变量的值是一个 Java 对象。
1.运行时变量 流程实例运行时的变量存入act_ru_variable表中。在流程实例运行结束时此实例的变量在表中删除。在流程实例创建及启动时可设置流程变量。所有的startProcessInstanceXXX方法都有一个可选参数用于设置变量。例如RuntimeService中
ProcessInstance startProcessInstanceById(String processDefinitionId, MapString, Object variables);也可以在流程执行中加入变量。例如(RuntimeService): //设置全局变量void setVariable(String executionId, String variableName, Object value);void setVariables(String executionId, MapString, ? extends Object variables);//设置局部变量void setVariableLocal(String executionId, String variableName, Object value);void setVariablesLocal(String executionId, MapString, ? extends Object variables);读取变量方法: MapString, Object getVariables(String executionId);MapString, Object getVariablesLocal(String executionId);MapString, Object getVariables(String executionId, CollectionString variableNames);MapString, Object getVariablesLocal(String executionId, CollectionString variableNames);Object getVariable(String executionId, String variableName);T T getVariable(String executionId, String variableName, ClassT variableClass);注意由于流程实例结束时对应在运行时表的数据跟着被删除。所以查询一个已经完结流程实例的变量只能在历史变量表中查找。 当然运行时变量我们也可以根据对应的作用域把他分为全局变量和局部变量.
1.1 全局变量 流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时可以称为 global 变量
注意如 Global变量userId变量名、zhangsan变量值 global 变量中变量名不允许重复设置相同名称的变量后设置的值会覆盖前设置的变量值。
案例
定义监听器
public class MySecondListener implements TaskListener {Overridepublic void notify(DelegateTask delegateTask) {// 获取所有的流程变量MapString, Object variables delegateTask.getVariables();SetString keys variables.keySet();for (String key : keys) {Object obj variables.get(key);System.out.println(key obj);if(obj instanceof String){// 修改 流程变量的信息// variables.put(key,obj :boge3306); 直接修改Map中的数据 达不到修改流程变量的效果delegateTask.setVariable(key :boge3306);}}}
}设计流程 然后完成流程的部署操作
Test
public void test1(){// 1.获取ProcessEngine对象ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();// 2.完成流程的部署操作 需要通过RepositoryService来完成RepositoryService repositoryService processEngine.getRepositoryService();// 3.完成部署操作Deployment deploy repositoryService.createDeployment().addClasspathResource(flow/holiday1.bpmn20.xml).name(请假流程-流程变量).deploy(); // 是一个流程部署的行为 可以部署多个流程定义的System.out.println(deploy.getId());System.out.println(deploy.getName());
}然后启动流程实例。注意在启动流程实例时我们需要指定相关的流程变量
/*** 发起一个流程*/
Test
public void test3(){ProcessEngine engine ProcessEngines.getDefaultProcessEngine();// 发起流程 需要通过 runtimeService来实现RuntimeService runtimeService engine.getRuntimeService();// 定义流程变量信息MapString,Object map new HashMap();map.put(assignee1,张三);map.put(assignee2,李四);map.put(ass1,变量1);map.put(ass2,299);map.put(ass3,湖南长沙);map.put(ass4,波哥666);// 通过流程定义ID来启动流程 返回的是流程实例对象ProcessInstance processInstance runtimeService.startProcessInstanceById(holiday1:1:42503,map);System.out.println(processInstance.getId() processInstance.getId());System.out.println(processInstance.getDeploymentId() processInstance.getDeploymentId());System.out.println(processInstance.getDescription() processInstance.getDescription());
}启动流程和触发对应的监听器同时会在act_ru_variable中记录当前的变量信息 当然我们也可以通过RunTimeService来查询当前对应的流程实例的流程变量信息
/*** 查询当前的流程变量信息*/
Test
public void testVal(){ProcessEngine engine ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService engine.getRuntimeService();// 获取流程变量信息 获取某个流程实例的变量信息MapString, VariableInstance variableInstances runtimeService.getVariableInstances(50008);SetString keys variableInstances.keySet();for (String key : keys) {System.out.println(keyvariableInstances.get(key));}
}1.2 局部变量 任务和执行实例仅仅是针对一个任务和一个执行实例范围范围没有流程实例大 称为 local 变量。 Local 变量由于在不同的任务或不同的执行实例中作用域互不影响变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同没有影响。
我们通过RuntimeService 设置的Local变量绑定的是 executionId。在该流程中有效 Testpublic void test4(){ProcessEngine engine ProcessEngines.getDefaultProcessEngine();// 待办查询 执行中的任务处理通过 TaskService来实现TaskService taskService engine.getTaskService();RuntimeService runtimeService engine.getRuntimeService();runtimeService.setVariableLocal(60004,orderId,100001);runtimeService.setVariableLocal(60004,price,6666);}我们还可以通过TaskService来绑定本地流程变量。需要指定对应的taskId
Test
public void test41(){ProcessEngine engine ProcessEngines.getDefaultProcessEngine();// 待办查询 执行中的任务处理通过 TaskService来实现TaskService taskService engine.getTaskService();taskService.setVariableLocal(60007,wechat,boge3306);
}然后通过测试演示我们可以看到通过TaskService绑定的Local变量的作用域只是在当前的Task有效。而通过RuntimeService绑定的Local变量作用的访问是executionId。
需要注意executionId和processInstanceId的区别
2.历史变量 历史变量存入act_hi_varinst表中。在流程启动时流程变量会同时存入历史变量表中在流程结束时历史表中的变量仍然存在。可理解为“永久代”的流程变量。 获取已完成的、id为’XXX’的流程实例中所有的HistoricVariableInstances历史变量实例并以变量名排序。
historyService.createHistoricVariableInstanceQuery().processInstanceId(XXX).orderByVariableName.desc().list();七、身份服务 在流程定义中在任务结点的 assignee 固定设置任务负责人在流程定义时将参与者固定设置在.bpmn 文件中如果临时任务负责人变更则需要修改流程定义系统可扩展性差。针对这种情况可以给任务设置多个候选人或者候选人组可以从候选人中选择参与者来完成任务。
1.候选人
1.1 定义流程图 定义流程图同时指定候选人多个候选人会通过,连接 1.2 部署和启动流程实例 部署流程并且在启动流程实例的时候对UEL表达式赋值 /*** 部署流程*/Testpublic void deploy(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService processEngine.getRepositoryService();Deployment deploy repositoryService.createDeployment().addClasspathResource(请假流程-候选人.bpmn20.xml).name(请求流程-候选人).deploy();System.out.println(deploy.getId() deploy.getId());System.out.println(deploy.getName());}/*** 启动流程实例*/Testpublic void runProcess(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService processEngine.getRuntimeService();// 给流程定义中的UEL表达式赋值MapString,Object variables new HashMap();variables.put(candidate1,张三);variables.put(candidate2,李四);variables.put(candidate3,王五);runtimeService.startProcessInstanceById(holiday-candidate:1:4,variables);}在对应的表结构中我们可以看到流程变量已经有了但是对于的Task的Assignee还是为空。 1.3 任务的查询 根据当前登录的用户查询对应的候选任务 /*** 根据登录的用户查询对应的可以拾取的任务**/Testpublic void queryTaskCandidate(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();TaskService taskService processEngine.getTaskService();ListTask list taskService.createTaskQuery()//.processInstanceId(2501).processDefinitionId(holiday-candidate:1:4).taskCandidateUser(李四) # 注意.list();for (Task task : list) {System.out.println(task.getId() task.getId());System.out.println(task.getName() task.getName());}}1.4 任务的拾取 知道了我有可拾取的任务后拾取任务。
/*** 拾取任务* 一个候选人拾取了这个任务之后其他的用户就没有办法拾取这个任务了* 所以如果一个用户拾取了任务之后又不想处理了那么可以退还*/Testpublic void claimTaskCandidate(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();TaskService taskService processEngine.getTaskService();Task task taskService.createTaskQuery()//.processInstanceId(2501).processDefinitionId(holiday-candidate:1:4).taskCandidateUser(李四).singleResult();if(task ! null){// 拾取对应的任务taskService.claim(task.getId(),李四);System.out.println(任务拾取成功);}}1.5 任务的归还 拾取任务后不想操作那么就归还任务 /*** 退还任务* 一个候选人拾取了这个任务之后其他的用户就没有办法拾取这个任务了* 所以如果一个用户拾取了任务之后又不想处理了那么可以退还*/Testpublic void unclaimTaskCandidate(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();TaskService taskService processEngine.getTaskService();Task task taskService.createTaskQuery()//.processInstanceId(2501).processDefinitionId(holiday-candidate:1:4).taskAssignee(张三).singleResult();if(task ! null){// 拾取对应的任务taskService.unclaim(task.getId());System.out.println(归还拾取成功);}}1.6 任务的交接 拾取任务后如果不想操作也不想归还可以直接交接给另外一个人来处理 /*** 任务的交接* 如果我获取了任务但是不想执行那么我可以把这个任务交接给其他的用户*/Testpublic void taskCandidate(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();TaskService taskService processEngine.getTaskService();Task task taskService.createTaskQuery()//.processInstanceId(2501).processDefinitionId(holiday-candidate:1:4).taskAssignee(李四).singleResult();if(task ! null){// 任务的交接taskService.setAssignee(task.getId(),王五);System.out.println(任务交接给了王五);}}1.7 任务的完成 正常的任务处理 /*** 完成任务*/Testpublic void completeTask(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();TaskService taskService processEngine.getTaskService();Task task taskService.createTaskQuery()//.processInstanceId(2501).processDefinitionId(holiday-candidate:1:4).taskAssignee(王五).singleResult();if(task ! null){// 完成任务taskService.complete(task.getId());System.out.println(完成Task);}}
2.候选人组 当候选人很多的情况下我们可以分组来处理。先创建组然后把用户分配到这个组中。
2.1 管理用户和组
2.1.1 用户管理 我们需要先单独维护用户信息。后台对应的表结构是ACT_ID_USER. /*** 维护用户*/Testpublic void createUser(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();// 通过 IdentityService 完成相关的用户和组的管理IdentityService identityService processEngine.getIdentityService();User user identityService.newUser(田佳);user.setFirstName(田);user.setLastName(jia);user.setEmail(tianjiaqq.com);identityService.saveUser(user);}2.1.2 Group管理 维护对应的Group信息后台对应的表结构是ACT_ID_GROUP /*** 创建用户组*/Testpublic void createGroup(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();IdentityService identityService processEngine.getIdentityService();// 创建Group对象并指定相关的信息Group group identityService.newGroup(group2);group.setName(开发部);group.setType(type1);// 创建Group对应的表结构数据identityService.saveGroup(group);}2.1.3 用户分配组 用户和组是一个多对多的关联关联我们需要做相关的分配后台对应的表结构是ACT_ID_MEMBERSHIP /*** 将用户分配给对应的Group*/Testpublic void userGroup(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();IdentityService identityService processEngine.getIdentityService();// 根据组的编号找到对应的Group对象Group group identityService.createGroupQuery().groupId(group1).singleResult();ListUser list identityService.createUserQuery().list();for (User user : list) {// 将用户分配给对应的组identityService.createMembership(user.getId(),group.getId());}}2.2 候选人组应用 搞清楚了用户和用户组的关系后我们就可以来使用候选人组的应用了
2.2.1 创建流程图 2.2.2 流程的部署运行 然后我们把流程部署和运行注意对UEL表达式赋值关联上Group
/*** 部署流程*/Testpublic void deploy(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService processEngine.getRepositoryService();Deployment deploy repositoryService.createDeployment().addClasspathResource(请假流程-候选人组.bpmn20.xml).name(请求流程-候选人).deploy();System.out.println(deploy.getId() deploy.getId());System.out.println(deploy.getName());}/*** 启动流程实例*/Testpublic void runProcess(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();IdentityService identityService processEngine.getIdentityService();Group group identityService.createGroupQuery().groupId(group1).singleResult();RuntimeService runtimeService processEngine.getRuntimeService();// 给流程定义中的UEL表达式赋值MapString,Object variables new HashMap();// variables.put(g1,group1);variables.put(g1,group.getId()); // 给流程定义中的UEL表达式赋值runtimeService.startProcessInstanceById(holiday-group:1:17504,variables);}对应表结构中就有对应的体现 2.2.3 任务的拾取和完成 然后完成任务的查询拾取和处理操作
/*** 根据登录的用户查询对应的可以拾取的任务**/Testpublic void queryTaskCandidateGroup(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();// 根据当前登录的用户找到对应的组IdentityService identityService processEngine.getIdentityService();// 当前用户所在的组Group group identityService.createGroupQuery().groupMember(邓彪).singleResult();TaskService taskService processEngine.getTaskService();ListTask list taskService.createTaskQuery()//.processInstanceId(2501).processDefinitionId(holiday-group:1:17504).taskCandidateGroup(group.getId()).list();for (Task task : list) {System.out.println(task.getId() task.getId());System.out.println(task.getName() task.getName());}}/*** 拾取任务* 一个候选人拾取了这个任务之后其他的用户就没有办法拾取这个任务了* 所以如果一个用户拾取了任务之后又不想处理了那么可以退还*/Testpublic void claimTaskCandidate(){String userId 田佳;ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();// 根据当前登录的用户找到对应的组IdentityService identityService processEngine.getIdentityService();// 当前用户所在的组Group group identityService.createGroupQuery().groupMember(userId).singleResult();TaskService taskService processEngine.getTaskService();Task task taskService.createTaskQuery()//.processInstanceId(2501).processDefinitionId(holiday-group:1:17504).taskCandidateGroup(group.getId()).singleResult();if(task ! null) {// 任务拾取taskService.claim(task.getId(),userId);System.out.println(任务拾取成功);}} /*** 完成任务*/Testpublic void completeTask(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();TaskService taskService processEngine.getTaskService();Task task taskService.createTaskQuery()//.processInstanceId(2501).processDefinitionId(holiday-group:1:17504).taskAssignee(邓彪).singleResult();if(task ! null){// 完成任务taskService.complete(task.getId());System.out.println(完成Task);}}八、网关服务
网关用来控制流程的流向
1.排他网关 排他网关exclusive gateway也叫异或网关 XOR gateway或者更专业的基于数据的排他网关 exclusive data-based gateway用于对流程中的决策建模。当执行到达这个网关时会按照所有出口顺序流定义的顺序对它们进行计算。选择第一个条件计算为true的顺序流当没有设置条件时认为顺序流为true继续流程。 请注意这里出口顺序流的含义与BPMN 2.0中的一般情况不一样。一般情况下会选择所有条件计算为true的顺序流并行执行。而使用排他网关时只会选择一条顺序流。当多条顺序流的条件都计算为true时会且仅会选择在XML中最先定义的顺序流继续流程。如果没有可选的顺序流会抛出异常。
图示 排他网关用内部带有’X’图标的标准网关菱形表示X’图标代表异或的含义。请注意内部没有图标的网关默认为排他网关。BPMN 2.0规范不允许在同一个流程中混合使用有及没有X的菱形标志。 案例 /*** 部署流程*/Testpublic void deploy(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService processEngine.getRepositoryService();Deployment deploy repositoryService.createDeployment().addClasspathResource(请假流程-排他网关.bpmn20.xml).name(请求流程-排他网关).deploy();System.out.println(deploy.getId() deploy.getId());System.out.println(deploy.getName());}/*** 启动流程实例*/Testpublic void runProcess(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService processEngine.getRuntimeService();// 给流程定义中的UEL表达式赋值MapString,Object variables new HashMap();// variables.put(g1,group1);variables.put(num,3); // 给流程定义中的UEL表达式赋值runtimeService.startProcessInstanceById(holiday-exclusive:1:4,variables);}/*** 启动流程实例*/Testpublic void setVariables(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService processEngine.getRuntimeService();// 给流程定义中的UEL表达式赋值MapString,Object variables new HashMap();// variables.put(g1,group1);variables.put(num,4); // 给流程定义中的UEL表达式赋值runtimeService.setVariables(12503,variables);}/*** 完成任务*/Testpublic void completeTask(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();TaskService taskService processEngine.getTaskService();Task task taskService.createTaskQuery()//.processInstanceId(2501).processDefinitionId(holiday-exclusive:1:4).taskAssignee(zhangsan).singleResult();if(task ! null){// 完成任务taskService.complete(task.getId());System.out.println(完成Task);}}如果从网关出去的线所有条件都不满足的情况下会抛出系统异常 但是要注意任务没有介绍还是原来的任务我们可以重置流程变量 Testpublic void setVariables(){ProcessEngine processEngine ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService processEngine.getRuntimeService();// 给流程定义中的UEL表达式赋值MapString,Object variables new HashMap();// variables.put(g1,group1);variables.put(num,4); // 给流程定义中的UEL表达式赋值runtimeService.setVariables(12503,variables);}2.并行网关 并行网关允许将流程分成多条分支也可以把多条分支汇聚到一起并行网关的功能是基于进入和外出顺序流的 fork分支并行后的所有外出顺序流为每个顺序流都创建一个并发分支。 join汇聚 所有到达并行网关在此等待的进入分支 直到所有进入顺序流的分支都到达以后 流程就会通过汇聚网关。 注意如果同一个并行网关有多个进入和多个外出顺序流 它就同时具有分支和汇聚功能。 这时网关会先汇聚所有进入的顺序流然后再切分成多个并行分支。
与其他网关的主要区别是并行网关不会解析条件。 即使顺序流中定义了条件也会被忽略。
案例 当我们执行了创建请假单后到并行网关的位置的时候在ACT_RU_TASK表中就有两条记录 然后同时在ACT_RU_EXECUTION中有三条记录一个任务对应的有两个执行实例 /*** 流程部署*/Testpublic void deployFlow() {Deployment deploy processEngine.getRepositoryService().createDeployment()// 部署一个流程.addClasspathResource(process/HolidayDemo7.bpmn20.xml).name(并行网关案例).deploy();System.out.println(deploy.getId());}/*** 启动流程实例*/Testpublic void startProcess() {String processInstanceId HolidayDemo7:1:0e762fbc-76fb-11ef-8265-287fcff7031e;MapString, Object variables new HashMap();variables.put(apply, llp);runtimeService.startProcessInstanceById(processInstanceId, variables);}/*** 并行网关会同时分配任务给到多个分支* 多个分支审批通过之后再到并行网关汇聚进入下一个阶段的审批**/Testpublic void completeTask() {Task task taskService.createTaskQuery().taskAssignee(lisi).processInstanceId(21543400-76fb-11ef-bf18-287fcff7031e).singleResult();if (task ! null) {taskService.complete(task.getId());System.out.println(lisi完成Task);}Task task2 taskService.createTaskQuery().taskAssignee(wangwu).processInstanceId(21543400-76fb-11ef-bf18-287fcff7031e).singleResult();if (task2 ! null) {taskService.complete(task2.getId());System.out.println(wangwu完成Task);}}3.包含网关 包含网关可以看做是排他网关和并行网关的结合体。 和排他网关一样你可以在外出顺序流上定义条件包含网关会解析它们。 但是主要的区别是包含网关可以选择多于一条顺序流这和并行网关一样。
包含网关的功能是基于进入和外出顺序流的 分支 所有外出顺序流的条件都会被解析结果为true的顺序流会以并行方式继续执行 会为每个顺序流创建一个分支。 汇聚所有并行分支到达包含网关会进入等待状态 直到每个包含流程token的进入顺序流的分支都到达。 这是与并行网关的最大不同。换句话说包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后流程会穿过包含网关继续执行。 /*** 流程部署*/Testpublic void deployFlow() {Deployment deploy processEngine.getRepositoryService().createDeployment()// 部署一个流程.addClasspathResource(process/HolidayDemo8.bpmn20.xml).name(包含网关案例).deploy();System.out.println(deploy.getId());}/*** 启动流程实例*/Testpublic void startProcess() {String processInstanceId HolidayDemo8:1:1d3c334b-76fe-11ef-ad36-287fcff7031e;runtimeService.startProcessInstanceById(processInstanceId);}/*** day10总经理审批* day3项目经理审批* day3技术经理审批* 包含网关可以理解为是并行和排他的一个结合* 当满足条件的多个分支都审批通过后进入下一个分支*/Testpublic void completeTask() {String taskId 2d8fe3b1-76fe-11ef-9b4c-287fcff7031e;MapString,Object variables new HashMap();variables.put(day, 11);taskService.complete(taskId,variables);}4.事件网关 事件网关允许根据事件判断流向。网关的每个外出顺序流都要连接到一个中间捕获事件。 当流程到达一个基于事件网关网关会进入等待状态会暂停执行。与此同时会为每个外出顺序流创建相对的事件订阅。 事件网关的外出顺序流和普通顺序流不同这些顺序流不会真的执行 相反它们让流程引擎去决定执行到事件网关的流程需要订阅哪些事件。 要考虑以下条件
事件网关必须有两条或以上外出顺序流事件网关后只能使用intermediateCatchEvent类型activiti不支持基于事件网关后连接ReceiveTask连接到事件网关的中间捕获事件必须只有一个入口顺序流。