获取下一节点任务,这个看起来非常的简单,但在开发过程中遇到了许多的坑,在这里进行记录,这个任务看起来非常的简单,但在开发过程中遇到了许多的坑,在这里进行记录。
- 首先,就是获取下一节点的任务信息,这里说简单也简单,说难也难,初期,我查阅了大量的资料,那些资料中都是在代码层面获取下一节点任务信息,我尝试了一下,非常的复杂,后来,我仔细研究了activiti流程变化与数据库变化之间的联系,发现act_ru_task表的执行特性,它会在当前节点任务完成后自动加载下一节点任务,根据这一点,我们就能能够获取到下一节点的相关信息。
1 2 3 4 5 6 7 8 9 10 11 12
| * @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)代码的第一个版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| * 发送待办提醒的通用入口( * @param task 当前节点的任务信息 */ @Override public void sendTaskNotice(Task 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++) { if(task.getExecutionId() == executionList.get(i).getParentId()){ String assignee = nextNodeTask.getAssignee(); if (assignee == null || "".equals(assignee)){ log.error("下一节点任务参与者为空"); return; } SysUser assigneeUser = sysUserService.selectUserByUserName(assignee); GatherTaskProperty gatherTaskProperty = gatherTaskPropertyMapper.selectGatherTaskPropertyByProcessInstanceId(task.getProcessInstanceId()); SysUser initialUser = sysUserService.selectUserByUserName(gatherTaskProperty.getCreateBy());
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)代码的第二个版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| * 发送待办提醒的通用入口(包含短信+邮箱提醒) * @param task 当前节点的任务信息 */ @Override public void sendTaskNotice(Task task){ List<Task> nextNodeUserTask = taskQueryService.nextAllNodeTaskList(task); if(nextNodeUserTask == null || nextNodeUserTask.size() <= 0){ return; }
if (nextNodeUserTask.size() == 1) { String assignee =null; List<Execution> executionList = runtimeService.createExecutionQuery().processInstanceId(nextNodeUserTask.get(0).getProcessInstanceId()).list(); if(executionList.size()==1){ assignee = nextNodeUserTask.get(0).getAssignee(); if (assignee == null || "".equals(assignee)){ log.error("下一节点任务参与者为空"); return; } SysUser assigneeUser = sysUserService.selectUserByUserName(assignee); GatherTaskProperty gatherTaskProperty = gatherTaskPropertyMapper.selectGatherTaskPropertyByProcessInstanceId(task.getProcessInstanceId()); SysUser initialUser = sysUserService.selectUserByUserName(gatherTaskProperty.getCreateBy());
sendTaskSMNotice(nextNodeUserTask.get(0), assigneeUser, initialUser); sendTaskMailNotice(nextNodeUserTask.get(0), assigneeUser, initialUser); sendTask(task,nextNodeUserTask.get(0)); }else{ return; }
}
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) { taskArraySet.add(nextNodeUserTask.get(i)); } else { taskArraySet.add(nextNodeUserTask.get(i + 1)); } }
} for (Task nextNodeTask : taskArraySet) { sendTask(task, nextNodeTask) ; } }
|
在这一个版本的代码,我加入了大量的判断,在后期发现这里的逻辑只能适用于两条并行的流程,三条并行会有重复发送的情况,而且在单线流程与并行流程的判断处有逻辑错误,不适合下图流程
在2、3、4任务完成后不会向5发送信息,这是严重的逻辑性错误
(3)代码的最终版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| * @param task 当前节点的任务信息 * @author : ADun * 发送待办提醒的通用入口 */ public void sendTaskNotice(Task 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); } }
}
|
在这一版本中我还是沿用上一版本代码的思想,在时间中做文章,这个思想适用于网络延时非常小,几乎不计,最后我是通过判断当前任务的完成时间是否下一任务的开始时间相同,相同就向下一任务审批人发送信息,我认为这里还是存在漏洞,可能时间会存在秒级的不准确,大家有什么好的想法可以在评论区留言。