• 基于jeecg-boot的flowable流程自定义业务驳回到发起人的一种处理方式


           有些粉丝,希望对自定义业务中,驳回到发起人进行处理,比如可以重新进行发起流程,下面就给出一种方式,当然不一定是最好的方式,只是提供一种参考而已,以后可以考虑动态根据流程状态或节点信息进行更加好的处理。

         这种方式目前前端不做修改,只做后端的一种处理。

        主要是增加两个逻辑:

    1、增加一个判断是发起人节点,isFirstInitiator  ,以后可以考虑增加驳回与退回的处理

    2、对于驳回里对于驳回到发起人后进行流程删除与关联删除,以便进行重新发起流程

    1. /**
    2. * 驳回任务 for自定义业务
    3. *
    4. * @param flowTaskVo
    5. */
    6. @Override
    7. public void taskRejectForDataId(FlowTaskVo flowTaskVo) {
    8. if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) {
    9. throw new CustomException("任务处于挂起状态");
    10. }
    11. // 当前任务 task
    12. Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
    13. // 获取流程定义信息
    14. ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
    15. // 获取所有节点信息
    16. Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
    17. // 获取全部节点列表,包含子节点
    18. Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
    19. // 获取当前任务节点元素
    20. FlowElement source = null;
    21. if (allElements != null) {
    22. for (FlowElement flowElement : allElements) {
    23. // 类型为用户节点
    24. if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
    25. // 获取节点信息
    26. source = flowElement;
    27. }
    28. }
    29. }
    30. // 目的获取所有跳转到的节点 targetIds
    31. // 获取当前节点的所有父级用户任务节点
    32. // 深度优先算法思想:延边迭代深入
    33. List parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
    34. if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
    35. throw new CustomException("当前节点为初始任务节点,不能驳回");
    36. }
    37. // 获取活动 ID 即节点 Key
    38. List parentUserTaskKeyList = new ArrayList<>();
    39. parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
    40. // 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序
    41. List historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list();
    42. // 数据清洗,将回滚导致的脏数据清洗掉
    43. List lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList);
    44. // 此时历史任务实例为倒序,获取最后走的节点
    45. List targetIds = new ArrayList<>();
    46. // 循环结束标识,遇到当前目标节点的次数
    47. int number = 0;
    48. StringBuilder parentHistoricTaskKey = new StringBuilder();
    49. for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
    50. // 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过
    51. if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
    52. continue;
    53. }
    54. parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
    55. if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
    56. number++;
    57. }
    58. // 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次
    59. // 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环
    60. // number == 1,第一次遇到当前节点
    61. // number == 2,第二次遇到,代表最后一次的循环范围
    62. if (number == 2) {
    63. break;
    64. }
    65. // 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点
    66. if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
    67. targetIds.add(historicTaskInstanceKey);
    68. }
    69. }
    70. // 目的获取所有需要被跳转的节点 currentIds
    71. // 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路
    72. UserTask oneUserTask = parentUserTaskList.get(0);
    73. // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务
    74. List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
    75. List runTaskKeyList = new ArrayList<>();
    76. runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
    77. // 需驳回任务列表
    78. List currentIds = new ArrayList<>();
    79. // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务
    80. List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
    81. currentUserTaskList.forEach(item -> currentIds.add(item.getId()));
    82. // 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况
    83. if (targetIds.size() > 1 && currentIds.size() > 1) {
    84. throw new CustomException("任务出现多对多情况,无法撤回");
    85. }
    86. // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因
    87. List currentTaskIds = new ArrayList<>();
    88. currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
    89. if (currentId.equals(runTask.getTaskDefinitionKey())) {
    90. currentTaskIds.add(runTask.getId());
    91. }
    92. }));
    93. // 设置驳回意见
    94. currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment()));
    95. SysUser loginUser = iFlowThirdService.getLoginUser();
    96. try {
    97. // 设置处理人
    98. taskService.setAssignee(task.getId(), loginUser.getUsername());
    99. // 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况
    100. if (targetIds.size() > 1) {
    101. // 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多)
    102. runtimeService.createChangeActivityStateBuilder()
    103. .processInstanceId(task.getProcessInstanceId()).
    104. moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
    105. }
    106. // 如果父级任务只有一个,因此当前任务可能为网关中的任务
    107. if (targetIds.size() == 1) {
    108. // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1)
    109. runtimeService.createChangeActivityStateBuilder()
    110. .processInstanceId(task.getProcessInstanceId())
    111. .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
    112. }
    113. /*======================驳回 回调以及关键数据保存======================*/
    114. //业务数据id
    115. String dataId = flowTaskVo.getDataId();
    116. if (dataId==null) return;
    117. //如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
    118. FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
    119. // 驳回到了上一个节点等待处理
    120. Task targetTask = taskService.createTaskQuery().processInstanceId(business.getProcessInstanceId()).active().singleResult();
    121. //spring容器类名
    122. String serviceImplName = business.getServiceImplName();
    123. FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
    124. Map values = flowTaskVo.getValues();
    125. if (values ==null){
    126. values = MapUtil.newHashMap();
    127. values.put("dataId",dataId);
    128. } else {
    129. values.put("dataId",dataId);
    130. }
    131. List beforeParamsCandidateUsernames = flowCallBackService.flowCandidateUsernamesOfTask(targetTask.getTaskDefinitionKey(), values);
    132. //设置数据
    133. String doneUsers = business.getDoneUsers();
    134. // 处理过流程的人
    135. JSONArray doneUserList = new JSONArray();
    136. if (StrUtil.isNotBlank(doneUsers)){
    137. doneUserList = JSON.parseArray(doneUsers);
    138. }
    139. if (!doneUserList.contains(loginUser.getUsername())){
    140. doneUserList.add(loginUser.getUsername());
    141. }
    142. business.setActStatus(ActStatus.reject)
    143. .setTaskId(targetTask.getId())
    144. .setTaskNameId(targetTask.getTaskDefinitionKey())
    145. .setTaskName(targetTask.getName())
    146. .setDoneUsers(doneUserList.toJSONString())
    147. ;
    148. FlowElement targetElement = null;
    149. if (allElements != null) {
    150. for (FlowElement flowElement : allElements) {
    151. // 类型为用户节点
    152. if (flowElement.getId().equals(targetTask.getTaskDefinitionKey())) {
    153. // 获取节点信息
    154. targetElement = flowElement;
    155. }
    156. }
    157. }
    158. ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(targetTask.getProcessInstanceId()).singleResult();
    159. String startUserId = processInstance.getStartUserId();
    160. if (targetElement!=null){
    161. UserTask targetUserTask = (UserTask) targetElement;
    162. business.setPriority(targetUserTask.getPriority());
    163. if (StrUtil.equals(targetUserTask.getIncomingFlows().get(0).getSourceRef(),"startNode1")) {//是否为发起人节点
    164. // 开始节点。设置处理人为申请人
    165. business.setTodoUsers(JSON.toJSONString(Lists.newArrayList(business.getProposer())));
    166. taskService.setAssignee(business.getTaskId(),business.getProposer());
    167. } else {
    168. List sysUserFromTask = getSysUserFromTask(targetUserTask,startUserId);
    169. List collect_username = sysUserFromTask.stream().map(SysUser::getUsername).collect(Collectors.toList());
    170. //collect_username转换成realname
    171. List newusername = new ArrayList();
    172. for (String oldUser : collect_username) {
    173. if(StringUtils.equalsAnyIgnoreCase(oldUser, "${INITIATOR}")) {//对发起人做特殊处理
    174. SysUser sysUser = iFlowThirdService.getUserByUsername(startUserId);
    175. newusername.add(sysUser.getRealname());
    176. }
    177. else {
    178. SysUser sysUser = iFlowThirdService.getUserByUsername(oldUser);
    179. newusername.add(sysUser.getRealname());
    180. }
    181. }
    182. business.setTodoUsers(JSON.toJSONString(newusername));
    183. // 删除后重写
    184. for (String oldUser : collect_username) {
    185. taskService.deleteCandidateUser(targetTask.getId(),oldUser);
    186. }
    187. if (CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
    188. // 业务层有指定候选人,覆盖
    189. for (String newUser : beforeParamsCandidateUsernames) {
    190. taskService.addCandidateUser(targetTask.getId(),newUser);
    191. }
    192. business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
    193. } else {
    194. for (String oldUser : collect_username) {
    195. taskService.addCandidateUser(targetTask.getId(),oldUser);
    196. }
    197. }
    198. if(collect_username.size() ==1) {
    199. targetTask.setAssignee(newusername.get(0).toString());
    200. taskService.addUserIdentityLink(targetTask.getId(), collect_username.get(0).toString(), IdentityLinkType.ASSIGNEE);
    201. }else if(collect_username.size() > 1){
    202. List list = historyService
    203. .createHistoricActivityInstanceQuery()
    204. .activityId(targetTask.getTaskDefinitionKey())
    205. .orderByHistoricActivityInstanceStartTime()
    206. .desc().list();
    207. for (HistoricActivityInstance historicActivityInstance : list) {
    208. if (StrUtil.isNotBlank(historicActivityInstance.getAssignee())) {
    209. targetTask.setAssignee(historicActivityInstance.getAssignee());
    210. taskService.addUserIdentityLink(targetTask.getId(), historicActivityInstance.getAssignee(), IdentityLinkType.ASSIGNEE);
    211. break;
    212. }
    213. }
    214. }
    215. }
    216. }
    217. // 重新查询当前任务
    218. Task currentTask = taskService.createTaskQuery().processInstanceId(targetTask.getProcessInstanceId()).singleResult();
    219. //判断是否是发起人节点,恢复自定义业务表单重新提交
    220. if(isFirstInitiator(currentTask)) {
    221. //删除自定义业务任务关联表与流程历史表,以便可以重新发起流程。
    222. //(要是需要重新进行提交的话,那就要保留第一个发起人历史信息,自定义业务表单最好增加一个再次发起按钮来处理这种情况
    223. if (business != null) {
    224. flowMyBusinessService.removeById(business);
    225. // 对自定义业务,删除运行和历史的节点信息
    226. this.deleteActivity(targetTask.getTaskDefinitionKey(), targetTask.getProcessInstanceId(), dataId);
    227. }
    228. }
    229. else {
    230. flowMyBusinessService.updateById(business);
    231. // 流程处理完后,进行回调业务层
    232. business.setValues(values);
    233. if (flowCallBackService!=null) flowCallBackService.afterFlowHandle(business);
    234. }
    235. } catch (FlowableObjectNotFoundException e) {
    236. throw new CustomException("未找到流程实例,流程可能已发生变化");
    237. } catch (FlowableException e) {
    238. throw new CustomException("无法取消或开始活动");
    239. }
    240. }
    241. /**
    242. * 判断当前节点是否是第一个发起人节点
    243. *
    244. * @param flowTaskVo 请求实体参数
    245. */
    246. boolean isFirstInitiator(Task task) {
    247. BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
    248. // 获取当前活动节点
    249. FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
    250. // 输入连线
    251. List inFlows = currentFlowNode.getIncomingFlows();
    252. for (SequenceFlow sequenceFlow : inFlows) {
    253. FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
    254. // 如果上个节点为开始节点
    255. if (sourceFlowElement instanceof StartEvent) {
    256. log.info("当前节点为发起人节点,上个节点为开始节点:id=" + sourceFlowElement.getId() + ",name=" + sourceFlowElement.getName());
    257. return true;
    258. }
    259. }
    260. return false;
    261. }

         

  • 相关阅读:
    Perl区分文件换行符类型
    leetcode451:根据字符出现频率排序
    Spring容器启动流程
    Java Spring 通过 AOP 实现方法参数的重新赋值、修改方法参数的取值
    嵌入式图像处理机器视觉库YMCV使用
    2022.07.19 随机数字python
    C语言文件操作总结
    Glide 源码解析与原理总结——Glide.with
    mysql 8.0 版本innodb 内存管理锁 buffer pool mutex 变化
    理解 Vue.js 中的 immediate: true
  • 原文地址:https://blog.csdn.net/qq_40032778/article/details/132652886