Activiti6.0获取下一节点任务的心路历程
在我的开发任务中,我被分配了一个像下一个节点审批人发送短信的任务,这个任务看起来非常的简单,但在开发过程中遇到了许多的坑,在这里进行记录,如果你想要快速知道结果,请看代码版本(3)。
- 首先,就是获取下一节点的任务信息,这里说简单也简单,说难也难,初期,我查阅了大量的资料,那些资料中都是在代码层面获取下一节点任务信息,我尝试了一下,非常的复杂,后来,我仔细研究了activiti流程变化与数据库变化之间的联系,发现act_ru_task表的执行特性,它会在当前节点任务完成后自动加载下一节点任务,根据这一点,我们就能能够获取到下一节点的相关信息。
/**
* @author : ADun
* 获取当前节点的下一节点所有节点信息
* 解释:当当前节点任务完成后,ac_ru_task表会删除当前任务保存下一待办任务,因此可以获得下一节点任务相关信息
* @param task
* @return
*/
public List<Task> nextAllNodeTaskList(Task task) {
List<Task> taskList = null;
taskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
return taskList;
}
- 接着就是判断流程是怎样执行的了,何时给下一节点发信息,何时不发,在这里我由于对项目流程图不够熟悉,出现了各种的逻辑漏洞
(1)代码的第一个版本
/**
* 发送待办提醒的通用入口(包含短信+邮箱提醒)
* @param task 当前节点的任务信息
*/
@Override
public void sendTaskNotice(Task task){
//1、获取下一节点所有任务信息,实际是查询act_ru_task表中的信息
List<Task> nextNodeUserTask = taskQueryService.nextAllNodeTaskList(task);
if(nextNodeUserTask == null || nextNodeUserTask.size() <= 0){//下一节点任务为空,即当前节点为最后一个节点
return;
}
for (Task nextNodeTask : nextNodeUserTask) {
List<Execution> executionList = runtimeService.createExecutionQuery().processInstanceId(nextNodeTask.getProcessInstanceId()).list();//查询出所有的执行实例
for (int i = 0; i < executionList.size(); i++) {
//如果当前任务的执行实例id是下一节点任务执行实例的父id,则进行下一步
if(task.getExecutionId() == executionList.get(i).getParentId()){
String assignee = nextNodeTask.getAssignee();//下一节点任务的参与者uid
if (assignee == null || "".equals(assignee)){
log.error("下一节点任务参与者为空");
return;
}
SysUser assigneeUser = sysUserService.selectUserByUserName(assignee);//下一节点任务参与者用户信息
//2、获取流程实例的任务属性
GatherTaskProperty gatherTaskProperty = gatherTaskPropertyMapper.selectGatherTaskPropertyByProcessInstanceId(task.getProcessInstanceId());
SysUser initialUser = sysUserService.selectUserByUserName(gatherTaskProperty.getCreateBy());//发起人用户信息
//3、发送邮件、短信提醒
sendTaskSMNotice(nextNodeTask, assigneeUser, initialUser);
sendTaskMailNotice(nextNodeTask, assigneeUser, initialUser);
}
}
}
}
在这一个版本的代码逻辑中,我的的逻辑是这样的,这里使用到了activiti流程引擎在执行过程中的一个特性,如果流程是单线流程,那么在执行过程中只会在act_ru_exection执行实例表【这个表中会保存流程实例与执行实例,它们有细微的区别,不明白的可以去了解一下】中保存一条流程实例信息,如果是并行流程,那么在act_ru_exection表中会同时保存流程实例与执行实例,并且执行实例属于流程实例,在表中可以发现,这里包含三条信息,第一条就是流程实例的信息,下面两条是执行实例的信息

由此我们也能够得出此时是一个下图这种类型的流程图

当初是这样想的,现在看起来非常的智障::当1号节点任务完成时,act_ru_task表会自动加载2与4任务节点的信息【并行网关的特性】,此时的2与3属于一个执行实例,4与5属于一个执行实例,当任务2完成时,act_ru_task表会自动删除2号节点,加载3号任务,此时3号执行实例的parentid我认为是2号节点的执行id,但后来发现,act_ru_exection表中一直会保存三条数据,变化的只是act_id,第一版本宣布失败
(2)代码的第二个版本
/**
* 发送待办提醒的通用入口(包含短信+邮箱提醒)
* @param task 当前节点的任务信息
*/
@Override
public void sendTaskNotice(Task task){
//1、获取下一节点所有任务信息,实际是查询act_ru_task表中的信息
List<Task> nextNodeUserTask = taskQueryService.nextAllNodeTaskList(task);
if(nextNodeUserTask == null || nextNodeUserTask.size() <= 0){//下一节点任务为空,即当前节点为最后一个节点
return;
}
//2、如果下一节点只有一个任务,act_ru_task中只有一条数据
if (nextNodeUserTask.size() == 1) {
String assignee =null;
//判断是单线流程还是并行流程,根据当前任务的执行流程实例id查询act_ru_exection,只有一条数据说明是单线流程,否则时多线
List<Execution> executionList = runtimeService.createExecutionQuery().processInstanceId(nextNodeUserTask.get(0).getProcessInstanceId()).list();
if(executionList.size()==1){
//单线流程,直接向下一节点任务负责人发送信息
assignee = nextNodeUserTask.get(0).getAssignee();//下一节点任务的参与者uid
if (assignee == null || "".equals(assignee)){
log.error("下一节点任务参与者为空");
return;
}
SysUser assigneeUser = sysUserService.selectUserByUserName(assignee);//下一节点任务参与者用户信息
//2、获取流程实例的任务属性
GatherTaskProperty gatherTaskProperty = gatherTaskPropertyMapper.selectGatherTaskPropertyByProcessInstanceId(task.getProcessInstanceId());
SysUser initialUser = sysUserService.selectUserByUserName(gatherTaskProperty.getCreateBy());//发起人用户信息
//3、发送邮件、短信提醒
sendTaskSMNotice(nextNodeUserTask.get(0), assigneeUser, initialUser);
sendTaskMailNotice(nextNodeUserTask.get(0), assigneeUser, initialUser);
sendTask(task,nextNodeUserTask.get(0));
}else{
//多线流程,只有一条执行实例完成时有可能出现一个任务节点,此时跳过
return;
}
}
//3、如果下一节点任务不只一个任务,act_ru_task中有多条数据
Set<Task> taskArraySet = new HashSet<>();//去重
if (nextNodeUserTask.size() > 1) {
for (int i = 0; i < nextNodeUserTask.size() - 1; i++) {
Date timeOne = nextNodeUserTask.get(i).getCreateTime();
Date timeTwo = nextNodeUserTask.get(i + 1).getCreateTime();
if (timeOne.compareTo(timeTwo) == 0) {
//如果当前任务的创建时间相同,都发送
taskArraySet.add(nextNodeUserTask.get(i));
taskArraySet.add(nextNodeUserTask.get(i + 1));
} else if (timeOne.compareTo(timeTwo) > 0) {
//如果当前任务的创建时间不同,timeOne在timeTwo时间之后
taskArraySet.add(nextNodeUserTask.get(i));//只发送最近时间创建的任务
} else {
//如果当前任务的创建时间不同,timeOne在timeTwo时间之前
taskArraySet.add(nextNodeUserTask.get(i + 1));//只发送最近时间创建的任务
}
}
}
for (Task nextNodeTask : taskArraySet) {
sendTask(task, nextNodeTask) ;
}
}
在这一个版本的代码,我加入了大量的判断,在后期发现这里的逻辑只能适用于两条并行的流程,三条并行会有重复发送的情况,而且在单线流程与并行流程的判断处有逻辑错误,不适合下图流程

在2、3、4任务完成后不会向5发送信息,这是严重的逻辑性错误
(3)代码的最终版本
/**
* @param task 当前节点的任务信息
* @author : ADun
* 发送待办提醒的通用入口(包含短信+邮箱提醒)
*/
public void sendTaskNotice(Task task) {
//1、获取下一节点所有任务信息,实际是查询act_ru_task表中的信息
List<Task> nextNodeUserTask = taskQueryService.nextAllNodeTaskList(task);
//下一节点任务为空,即当前节点为最后一个节点
if (nextNodeUserTask == null || nextNodeUserTask.size() <= 0) {
return;
}
for (Task nextNodeTask : nextNodeUserTask) {
String assignee = nextNodeTask.getAssignee();
if(assignee == null || "".equals(assignee)){
log.error("taskId为" + nextNodeTask.getId() + "的任务参与者不存在!");
continue;
}
//获取当前任务的结束时间
HistoricTaskInstance hisTask = historyService.createHistoricTaskInstanceQuery().taskId(task.getId()).singleResult();
Date hisTaskEndTime = hisTask.getEndTime();
//获取下一任务开始时间
Date nextNodeTaskCreateTime = nextNodeTask.getCreateTime();
//只有下一任务节点创建时间与当前任务完成时间相等时,向下一节点发送信息
if (nextNodeTaskCreateTime.compareTo(hisTaskEndTime) == 0) {
sendTask(task, nextNodeTask);
}
}
}
在这一版本中我还是沿用上一版本代码的思想,在时间中做文章,因为这是一个公司内部的OA系统,网络延时非常小,几乎不计,最后我是通过判断当前任务的完成时间是否下一任务的开始时间相同,相同就向下一任务审批人发送信息,我认为这里还是存在漏洞,可能时间会存在秒级的不准确,大家有什么好的想法可以在评论区留言。
Activiti6.0获取下一节点任务的心路历程的更多相关文章
- javascript 获取下一个节点
下一个节点: nextElementSibling 上一个节点 previousElementSibling <div> <select onchange="alert(t ...
- javascript 获取dom书的下一个节点。
利用javascript 写一个在页面点击加减按钮实现数字的累加.. 简略的html大概如此.看得懂就好不要在意这些细节啊 <input type="button" valu ...
- Activit的心路历程:获取当前节点的下一节点【可能存在多个】的nodeId
上一任务节点 在我的开发任务中,突然给我提出了一个待办任务需要获取当前任务节点下一任务节点的表单信息,刚开始搞得我有点措手不及,后来仔细是靠后,灵感一下,直接操作流程的bpmn信息就可以获取到节点信息 ...
- JavaScript获取HTML DOM节点元素详解(转)
在Web运用程序特别是Web 2.0程序开发中,经常要获取页面中某个元素,然后更新该元素的样式.内容等.如何获取要更新的元素,是首先要处理的疑问. 51CTO推荐阅读:JavaScript DOM文档 ...
- 原生JavaScript 获取下一个/上一个同胞元素
看JavaScript遇到的问题,研究了下 获取上一个或者下一个同胞元素,使用很多人会立马想到JQuery prev() 和 next() 的确非常方便.那么原生的JavaScript该怎么获取 上 ...
- js获取HTML DOM节点元素方法总结
1. 通过顶层document节点获取: (1)document.getElementById(elementId) :通过ID获得节点,如果页面上含有多个相同id的节点,那么只返回第一个节点. ...
- js总结:利用js获取下拉框的value值和文本值
select下拉框在项目开发中是经常用到的,特别是在联级菜单方面的应用更为广泛.但是,对于一些初学者来说,如何获取下拉框子节点option的value值和文本内容,还是有一点难度的. html代码: ...
- 461在全志r16平台tinav3.0系统下使用地磁计QMC5883L
461在全志r16平台tinav3.0系统下使用地磁计QMC5883L 2018/9/7 14:08 版本:V1.0 开发板:SC3817R SDK:tina v3.0 (基本确认全志tina v3. ...
- JS---DOM---节点的概念,属性,和获取相关的节点
回顾概念 文档: document 元素: 页面中所有的标签, 元素---element, 标签----元素---对象 节点: 页面中所有的内容(标签,属性,文本(文字,换行,空格,回车))---- ...
随机推荐
- List集合对象去重及按属性去重的8种方法-java基础总结系列第六篇
最近在写一些关于java基础的文章,但是我又不想按照教科书的方式去写知识点的文章,因为意义不大.基础知识太多了,如何将这些知识归纳总结,总结出优缺点或者是使用场景才是对知识的升华.所以我更想把java ...
- 在Oracle中十分钟内创建一张千万级别的表
小表不会产生性能问题,大表才会.要练习SQL调优,还非得有大表不可.但数据不会自然产生,没有数据时如何创建一张千万级别的大表呢? 之前,我想用Oracle的批量插入语法去插入数据,此语法如下: INS ...
- docker 容器中部署 Go 服务时,请求 https 文件时抛错
错误提示: Get https://res.ddkt365.com/ddktRes/imageRes/wx_headimg/0f1d9e55913c22bcaf7cca9b38048d29.jpeg: ...
- python基础五(函数、全局变量和局部变量)
一.全局变量和局部变量 全局变量,即可以在当前文件所有地方都可使用的变量,一次修改,整个文件都影响.如果函数体内部使用全局变量,要先声明global 局部变量,只能在函数体内部使用,除了函数体就不可使 ...
- 自定义 NSLog以便发版和调试
问题 开发中用了大量的 NSLog,但是发布时想取消这些 NSLog 开发中是否经常用过 NSLog(@"%s", __FUNCTION__); 解决问题 新建 ExtendNSL ...
- [算法]类似n sum个数的问题(DP)
题目 求和为target的数组元素组合数,含重复. 例: 输入 arr = { 1, 2, 3, 3, 4 } ,target = 6 输出 4 题解 dp[i][j]代表到数组第i-1个元素,目标和 ...
- 使用App Metrics实现性能监控
App Metrics监控需要安装InfluxDB时序数据库和Grafana可视化分析工具 1.安装InfluxDB 下载地址:https://portal.influxdata.com/downlo ...
- JDK13环境变量配置
第一步:下载JDK(开发工具包) JDK分为OracleJDK和OpenJDK下面简要说明 OracleJDK 部分代码闭源.商业收费 OpenJDK 开放源码.商业免费 两者大部分代码是共用的(除闭 ...
- Skywalking Php注册不上问题排查
Skywalking是一款分布式追踪应用,具体介绍可以参考 skywalking. 最近公司的一个Php应用在Skywalking后台查不到数据了: 登录到某台服务器上发现注册不上,启动时就报错了: ...
- java虚拟机5 字节码
java字节码本质是java程序的格式化表示,便于机器处理.所以他是java程序的另一种表示,java程序包含的信息他都包含并且更加结构化. java虚拟机字节码格式: magic 魔数,标识该文件是 ...