flowable工作流看这一篇就够了(高级篇 下)

目录

三、候选人和候选人组

3.1、候选人

3.1.1、定义流程图

3.1.2、部署和启动流程实例

3.1.3、任务的查询

3.1.4、任务的拾取

3.1.5、任务的归还

3.1.6、任务的交接

3.1.7、任务的完成

3.2、候选人组

3.2.1、管理用户和组

用户管理

Group管理

为用户分配组

3.2.2、候选人组应用

创建流程图

流程的部署运行

任务的拾取和完成

四、网关

4.1、排他网关

4.2、并行网关

4.3、包含网关

4.4、事件网关

4.5、总结


三、候选人和候选人组

在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。针对这种情况可以给任务设置多个候选人或者候选人组,可以从候选人中选择参与者来完成任务。

3.1、候选人

3.1.1、定义流程图

定义流程图,同时指定候选人。

第一步:创建流程

第二步:为两个节点分配用户和候选人

第三步:流程下载复制到resources下

3.1.2、部署和启动流程实例

部署流程,并且在启动流程实例的时候对UEL表达式赋值。

/*** 部署流程*/
@Test
public void testDeploy(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService = processEngine.getRepositoryService();repositoryService.createDeployment().addClasspathResource("请假流程-候选人.bpmn20.xml").name("请假流程-候选人").deploy();
}/*** 启动流程实例*/
@Test
public void runProcess(){// 获取流程引擎对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 启动流程实例通过 RuntimeService 对象RuntimeService runtimeService = processEngine.getRuntimeService();// 设置流程变量Map<String,Object> variables = new HashMap<>();// 设置候选人的值variables.put("candidate1","苏格拉底");variables.put("candidate2","柏拉图");// 启动流程实例,第一个参数是流程定义的idruntimeService.startProcessInstanceById("holiday-candidate:1:4",variables); //启动流程实例
}

在对应的表结构中我们可以看到流程变量已经有了,但是对于的Task的Assignee还是为空。

3.1.3、任务的查询

根据当前登录的用户,查询对应的候选任务。

/*** 根据登录的用户查询对应的可以拾取的任务**/
@Test
public void queryTaskCandidate(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();TaskService taskService = processEngine.getTaskService();List<Task> 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());}
}

3.1.4、任务的拾取

知道了我有可拾取的任务后,拾取任务。

/*** 拾取任务*    一个候选人拾取了这个任务之后其他的用户就没有办法拾取这个任务了*    所以如果一个用户拾取了任务之后又不想处理了,那么可以退还*/
@Test
public 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("任务拾取成功");}
}

发现苏格拉底已经拾取了这个任务,以前“创建请假单”是个没人要的孩子,现在被苏格拉底拾取了。

3.1.5、任务的归还

拾取任务后不想操作那么就归还任务。

/*** 退还任务*    一个候选人拾取了这个任务之后其他的用户就没有办法拾取这个任务了*    所以如果一个用户拾取了任务之后又不想处理了,那么可以退还*/
@Test
public 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("归还拾取成功");}
}

3.1.6、任务的交接

拾取任务后如果不想操作也不想归还可以直接交接给另外一个人来处理。

/*** 任务的交接*    如果我获取了任务,但是不想执行,那么我可以把这个任务交接给其他的用户*/
@Test
public 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("任务交接给了柏拉图");}
}

3.1.7、任务的完成

正常的任务处理。

/*** 完成任务*/
@Test
public 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");}
}

3.2、候选人组

当候选人很多的情况下,我们可以分组来处理。先创建组,然后把用户分配到这个组中。

3.2.1、管理用户和组

用户管理

我们需要先单独维护用户信息。后台对应的表结构是ACT_ID_USER。

/*** 维护用户*/
@Test
public void createUser(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 通过 IdentityService 完成相关的用户和组的管理IdentityService identityService = processEngine.getIdentityService();User user = identityService.newUser("莫言");user.setFirstName("莫");user.setLastName("言");user.setEmail("moyan@qq.com");identityService.saveUser(user);
}

注意:我们在真实的开发中,肯定有我们自己的用户表,比如t_user,那么当用户注册的时候,我们不但要往t_user表里添加数据,同时也要往act_id_user表添加相同的数据,符合原子操作即可。

Group管理

维护对应的Group信息,后台对应的表结构是`ACT_ID_GROUP`。

/*** 创建用户组*/
@Test
public void createGroup(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();IdentityService identityService = processEngine.getIdentityService();// 创建Group对象并指定相关的信息Group group = identityService.newGroup("group1");group.setName("开发组");group.setType("type1");// 创建Group对应的表结构数据identityService.saveGroup(group);
}

为用户分配组

用户和组是一个多对多的关联关联,我们需要做相关的分配,后台对应的表结构是`ACT_ID_MEMBERSHIP`。

/*** 将用户分配给对应的Group*/
@Test
public void userGroup(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();IdentityService identityService = processEngine.getIdentityService();// 根据组的编号找到对应的Group对象Group group = identityService.createGroupQuery().groupId("group1").singleResult();List<User> list = identityService.createUserQuery().list();for (User user : list) {// 将用户分配给对应的组identityService.createMembership(user.getId(),group.getId());}
}

3.2.2、候选人组应用

搞清楚了用户和用户组的关系后我们就可以来使用候选人组的应用了。

创建流程图

流程的部署运行

然后我们把流程部署和运行,注意对UEL表达式赋值,关联上Group。

/*** 部署流程*/
@Test
public void deploy(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService = processEngine.getRepositoryService();Deployment deploy = repositoryService.createDeployment().addClasspathResource("请假流程-候选人组.bpmn20.xml").name("请求流程-候选人").deploy();
}/*** 启动流程实例*/
@Test
public void runProcess(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();IdentityService identityService = processEngine.getIdentityService();Group group = identityService.createGroupQuery().groupId("group1").singleResult();RuntimeService runtimeService = processEngine.getRuntimeService();// 给流程定义中的UEL表达式赋值Map<String,Object> variables = new HashMap<>();// variables.put("g1","group1");variables.put("g1",group.getId()); // 给流程定义中的UEL表达式赋值runtimeService.startProcessInstanceById("holiday-group:1:17504",variables);
}

对应表结构中就有对应的体现:

任务的拾取和完成

然后完成任务的查询拾取和处理操作。

/*** 根据登录的用户查询对应的可以拾取的任务**/
@Test
public void queryTaskCandidateGroup(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 根据当前登录的用户找到对应的组IdentityService identityService = processEngine.getIdentityService();// 当前用户所在的组Group group = identityService.createGroupQuery().groupMember("莫言").singleResult();TaskService taskService = processEngine.getTaskService();List<Task> 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());}
}/*** 拾取任务*    一个候选人拾取了这个任务之后其他的用户就没有办法拾取这个任务了*    所以如果一个用户拾取了任务之后又不想处理了,那么可以退还*/
@Test
public 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("任务拾取成功");}
}/*** 完成任务*/
@Test
public 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");}
}

四、网关

网关用来控制流程的流向。

4.1、排他网关

排他网关(exclusive gateway)(也叫异或网关 XOR gateway,或者更专业的,基于数据的排他网关 exclusive data-based gateway),用于对流程中的决策建模。当执行到达这个网关时,会按照所有出口顺序流定义的顺序对它们进行计算。选择第一个条件计算为true的顺序流(当没有设置条件时,认为顺序流为true)继续流程。

请注意这里出口顺序流的含义与BPMN 2.0中的一般情况不一样。一般情况下,会选择所有条件计算为true的顺序流,并行执行。而使用排他网关时,只会选择一条顺序流。当多条顺序流的条件都计算为true时,会且仅会选择在XML中最先定义的顺序流继续流程。如果没有可选的顺序流,会抛出异常。

图示

排他网关用内部带有’X’图标的标准网关(菱形)表示,'X’图标代表异或的含义。请注意内部没有图标的网关默认为排他网关。BPMN 2.0规范不允许在同一个流程中混合使用有及没有X的菱形标志。

案例:

设置条件:

部门经理审批和总经理审批分别是lisi和wangwu,人事审批是zhaoliu。

/*** 部署流程*/
@Test
public void deploy(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService = processEngine.getRepositoryService();Deployment deploy = repositoryService.createDeployment().addClasspathResource("请假流程-排他网关.bpmn20.xml").name("请求流程-排他网关").deploy();
}/*** 启动流程实例*/
@Test
public void runProcess(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService = processEngine.getRuntimeService();// 给流程定义中的UEL表达式赋值Map<String,Object> variables = new HashMap<>();// variables.put("g1","group1");variables.put("num",2); // 给流程定义中的UEL表达式赋值runtimeService.startProcessInstanceById("holiday-exclusive:1:4",variables);
}/*** 完成任务*/
@Test
public 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");}
}

当我们使用zhangsan完成任务时:

因为我们设置的num为2,满足num<3的条件,所以走部门经理审批。

注意:我们当时设置的条件是num<3和num>3,那么如果我设置num=3,会发现什么效果呢?

前面我们可以直接在连接线上定义条件,那为什么还要有排他网关呢?直接在线上的情况,如果条件都不满足,流程就结束了,是异常结束!!但是有了排他网关,尽管你条件都不满足,也只是给你抛出异常,并不会结束流程,你可以重新设置变量就行了。

4.2、并行网关

并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:

  • fork分支:并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。

  • join汇聚: 所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。

注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。

与其他网关的主要区别是,并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。

案例:

当我们执行了创建请假单后,到并行网关的位置的时候,在ACT_RU_TASK表中就有两条记录。

然后同时在ACT_RU_EXECUTION中有三条记录,一个任务对应的有两个执行实例。

如果我们使用lisi审批一下,task表中lisi的记录就没了,只剩下wangwu的。

4.3、包含网关

包含网关可以看做是排他网关和并行网关的结合体。 和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。 但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。

包含网关的功能是基于进入和外出顺序流的:

  • 分支: 所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行, 会为每个顺序流创建一个分支。

  • 汇聚:所有并行分支到达包含网关,会进入等待状态, 直到每个包含流程token的进入顺序流的分支都到达。 这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后,流程会穿过包含网关继续执行。

4.4、事件网关

事件网关允许根据事件判断流向。网关的每个外出顺序流都要连接到一个中间捕获事件。 当流程到达一个基于事件网关,网关会进入等待状态:会暂停执行。与此同时,会为每个外出顺序流创建相对的事件订阅。

事件网关的外出顺序流和普通顺序流不同,这些顺序流不会真的"执行", 相反它们让流程引擎去决定执行到事件网关的流程需要订阅哪些事件。 要考虑以下条件:

  1. 事件网关必须有两条或以上外出顺序流;

  2. 事件网关后,只能使用intermediateCatchEvent类型(activiti不支持基于事件网关后连接ReceiveTask)

  3. 连接到事件网关的中间捕获事件必须只有一个入口顺序流。

4.5、总结

排他:两条路径只取其中一条走。

并行:两条路径同时走。

包含:只要条件为true的都走。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/226913.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解网络 I/O:单 Group 混杂模式|多 Group 主从模式

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任 Java 开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、Java&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏&…

Linux 常用的操作命令

我们习惯的使用Windows,安装软件进行使用&#xff0c;比如 WPS&#xff0c;浏览器&#xff0c;一些工具&#xff0c;但是在Linux上就需要用命令去操作&#xff0c;也可以使用像Ubuntu 和 CentOS这类的可视化面板 Linux系统是开源的&#xff0c;所以开发人员可以反复的发现Bug以…

1231. 航班时间(整行字符串输入:getline(cin,line))

题目&#xff1a; 1231. 航班时间 - AcWing题库 输入样例&#xff1a; 3 17:48:19 21:57:24 11:05:18 15:14:23 17:21:07 00:31:46 (1) 23:02:41 16:13:20 (1) 10:19:19 20:41:24 22:19:04 16:41:09 (1)输出样例&#xff1a; 04:09:05 12:10:39 14:22:05 思路&#xff1a; …

selenium 做 Web 自动化,鼠标当然也要自动化!

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

docker安装Prometheus

docker安装Prometheus Docker搭建Prometheus监控系统 环境准备(这里的环境和版本是经过测试没有问题,并不是必须这个版本) 主机名IP配置系统说明localhost随意2核4gCentOS7或者Ubuntu20.0.4docker版本23.0.1或者24.0.5,docker-compose版本1.29 安装Docker Ubuntu20.0.4版本…

STM32——串口

串口发送/接收函数&#xff1a; HAL_UART_Transmit(); 串口发送数据&#xff0c;使用超时管理机制 HAL_UART_Receive(); 串口接收数据&#xff0c;使用超时管理机制 HAL_UART_Transmit_IT(); 串口中断模式发送 HAL_UART_Receive_IT(); 串口中断模式接收 HAL_UART_Tran…

Netty常见的设计模式

简介 设计模式在软件开发中起着至关重要的作用&#xff0c;它们是解决常见问题的经过验证的解决方案。而Netty作为一个优秀的网络应用程序框架&#xff0c;同样也采用了许多设计模式来提供高性能和可扩展性。在本文中&#xff0c;我们将探讨Netty中使用的一些关键设计模式&…

云开发微信小程序实战

随着移动互联网的快速发展&#xff0c;微信小程序作为一种轻量级的应用程序&#xff0c;逐渐成为了企业开展业务和提升用户体验的重要工具。而云开发则为企业提供了高效、安全、可靠的后台服务&#xff0c;使得小程序的开发和维护更加便捷。本文将详细介绍如何使用微信小程序与…

返回零长度的数组或集合,而不是null

返回零长度的数组或集合而不是 null 是一种良好的编程实践&#xff0c;可以提高代码的可靠性和可读性。以下是一个例子&#xff0c;展示了返回零长度的数组或集合的情况&#xff1a; import java.util.ArrayList; import java.util.List;public class StudentManager {private…

牛客第一期

1.创建动态数组 #include <iostream> using namespace std;int main() {int n; cin>>n; int *pnew int [n]; int i0; for(i0;i<n;i) {*(pi)ni; } int j0; for(j0;j<n;j) {printf("%d ",*(pj)); } } #include<bits/stdc.h> using namespace s…

网站提示“不安全”

当你在浏览网站时&#xff0c;有时可能会遇到浏览器提示网站不安全的情况。这通常是由于网站缺乏SSL证书所致。那么&#xff0c;从SSL证书的角度出发&#xff0c;我们应该如何解决这个问题呢&#xff1f; 首先&#xff0c;让我们简单了解一下SSL证书。SSL证书是一种用于保护网站…

python实战教学之python版“张万森,好久不见”

前言 WINTER IS COMING 最近《一闪一闪亮星星》的电影在火热预售中&#xff0c;家人们抢到票了嘛&#xff0c;前两天小编写了一篇“张万森&#xff0c;下雪了”的文章后&#xff0c;收到了不少小伙伴的反馈&#xff1a;“代码的运行结果只有文字&#xff0c;没有雪花啊”&#…

OceanBase数据库部署

文章目录 OceanBase基础概念集群、Zone和OB ServerRootService总控服务&#xff08;RS&#xff09;多租户机制&#xff1a;资源隔离&#xff0c;数据隔离每个租户拥有若干资源池&#xff08;Resource Pool&#xff09; 部署形式部署流程OceanBase客户端工具 学习体验部署实现 O…

VSCode配置代码片段,提升效率必备!

1.点击文件—> 首选项------>配置用户代码片段 2、新建用户代码片段 3、以js的控制台输出为例 {//片段名称"console.log": {"prefix": "cls",//呼出命令"body": ["console.log($1)"//具体片段],"descriptio…

netty-daxin-3(rpc远程调用)

文章目录 nettyRpcObjectEncoder 与 ObjectDecoderjdk动态代理回顾Rpc调用过程简析服务端客户端 nettyRpc ObjectEncoder 与 ObjectDecoder ObjectEncoder继承自MessageToByteEncoder<Serializable>&#xff0c;它内部使用ByteBufOutputStream包装ByteBuf对象&#xff…

vue3项目中使用vue-cropper实现截图效果

前言&#xff1a; 自用&#xff01;&#xff01;&#xff01; 插件文档链接&#xff1a; https://www.npmjs.com/package/vue-cropper 使用步骤&#xff1a; 1、安装 npm install vue-cropper 2、项目中引入&#xff08;全局引入&#xff09;&#xff1b;文档中有介绍如何局…

怎么解决bash: composer: command not found问题

是不是遇到过bash: composer: command not found问题&#xff0c;怎么解决呢&#xff1f;下面由composer教程栏目给大家来详细介绍该问题的解决方法。 1、先看报错 2、由于错误的原因&#xff0c;安装很多东西都失败了。网上有的说是环境变量的问题&#xff0c;又一个个找也没…

DS考研真题总结——客观题(1)

开始整理真题中的客观小题&#xff0c;至于和算法有关的大题统一最后整理~ 定义背诵&#xff1a;数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下&#xff0c;精心选择的数据结构可以带来更高的运行或者存储效…

spring面试:二、bean的生命周期和循环引入问题(三级缓存、@Lazy)

bean的生命周期 Spring容器在进行实例化时&#xff0c;会将xml配置的的信息封装成一个BeanDefinition对象&#xff0c;Spring根据BeanDefinition来创建Bean对象&#xff0c;里面有很多的属性用来描述Bean。 其中比较重要的是&#xff1a; beanClassName&#xff1a;bean 的类…

tcp连接全过程各种状态详解

文章目录 TCP的一些重要特性tcp连接全过程各种状态参考资料 TCP的一些重要特性 TCP是一种可靠、面向连接、全双工、流控制、拥塞控制、有序传输、无差错传输、无重复传输、无丢失传输等特点的协议。为了实现这些特点&#xff0c;TCP必须对上层应用程序发送的数据进行分段、重组…