| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.convert.Convert; |
| | | import cn.hutool.core.io.FileUtil; |
| | | import cn.hutool.core.lang.Assert; |
| | | import cn.hutool.core.map.MapUtil; |
| | | import cn.hutool.core.util.*; |
| | | import cn.hutool.extra.spring.SpringUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | |
| | | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| | | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| | | import cn.iocoder.yudao.module.bpm.enums.definition.*; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.*; |
| | | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; |
| | | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; |
| | | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; |
| | |
| | | import org.flowable.engine.runtime.ActivityInstance; |
| | | import org.flowable.engine.runtime.Execution; |
| | | import org.flowable.engine.runtime.ProcessInstance; |
| | | import org.flowable.engine.task.Attachment; |
| | | import org.flowable.task.api.DelegationState; |
| | | import org.flowable.task.api.Task; |
| | | import org.flowable.task.api.TaskInfo; |
| | |
| | | .orderByHistoricTaskInstanceStartTime().asc().list(); |
| | | } |
| | | |
| | | @Override |
| | | public List<Attachment> getAttachments(String processInstanceId, Set<String> taskIds, BpmAttachmentTypeEnum attachmentType) { |
| | | List<Attachment> result = taskService.getProcessInstanceAttachments(processInstanceId); |
| | | if (CollUtil.isNotEmpty(taskIds)) { |
| | | result = filterList(result, attachment -> taskIds.contains(attachment.getTaskId())); |
| | | } |
| | | if (attachmentType != null) { |
| | | result = filterList(result, attachment -> attachmentType.getType().equals(attachment.getType())); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 判断指定用户,是否是当前任务的审批人 |
| | | * |
| | |
| | | // 2.2 添加评论 |
| | | taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), |
| | | BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); |
| | | // 2.3 添加附件 |
| | | if (CollUtil.isNotEmpty(reqVO.getAttachments())) { |
| | | reqVO.getAttachments().forEach(attachment -> taskService.createAttachment(BpmAttachmentTypeEnum.TASK_ATTACHMENT.getType(), |
| | | task.getId(), task.getProcessInstanceId(), FileUtil.getName(URLUtil.getPath(attachment)), null, attachment)); |
| | | } |
| | | |
| | | // 3. 设置流程变量。如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时 variables 一定会为空 |
| | | // 场景一:A 节点发起,B 节点表单无可编辑字段,审批通过时,C 节点需要流程变量获取下一个执行节点,但因为 B 节点无可编辑的字段,variables 为空,流程可能出现问题。 |
| | |
| | | } |
| | | |
| | | // 4. 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 |
| | | Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables, |
| | | validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables, |
| | | bpmnModel, reqVO.getNextAssignees(), instance); |
| | | runtimeService.setVariables(task.getProcessInstanceId(), variables); |
| | | runtimeService.setVariables(task.getProcessInstanceId(), processVariables); |
| | | |
| | | // 5. 如果当前节点 Id 存在于需要预测的流程节点中,从中移除。 流程变量在回退操作中设置 |
| | | Object needSimulateTaskIds = runtimeService.getVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS); |
| | |
| | | runtimeService.setVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskIdsByReturn); |
| | | } |
| | | |
| | | // 6. 调用 BPM complete 去完成任务 |
| | | taskService.complete(task.getId(), variables, true); |
| | | // 6. 清理退回设置的不自动通过的变量。仅在该标记存在时才删除,避免每次完成任务都产生无谓的 DB delete |
| | | String returnFlagKey = String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()); |
| | | if (runtimeService.hasVariable(task.getProcessInstanceId(), returnFlagKey)) { |
| | | log.info("[approveTask][taskId({}) 清理退回标记变量({})]", task.getId(), returnFlagKey); |
| | | runtimeService.removeVariable(task.getProcessInstanceId(), returnFlagKey); |
| | | } |
| | | |
| | | // 7. 调用 BPM complete 去完成任务 |
| | | Map<String,Object> taskVariables = MapUtil.emptyIfNull(reqVO.getVariables()); // task local variables. 一般用于任务内嵌流程表单 |
| | | taskService.complete(task.getId(), taskVariables, true); |
| | | |
| | | // 【加签专属】处理加签任务 |
| | | handleParentTaskIfSign(task.getParentTaskId()); |
| | |
| | | } |
| | | // 1. 获取下一个将要执行的节点集合 |
| | | FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); |
| | | List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); |
| | | List<UserTask> nextFlowNodes = getNextUserTasks(flowElement, bpmnModel, variables); |
| | | |
| | | // 2. 校验选择的下一个节点的审批人,是否合法 |
| | | for (FlowNode nextFlowNode : nextFlowNodes) { |
| | | for (UserTask nextFlowNode : nextFlowNodes) { |
| | | Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); |
| | | // 2.1 情况一:如果节点中的审批人策略为 发起人自选 |
| | | if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { |
| | |
| | | |
| | | // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 |
| | | if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { |
| | | // 如果节点存在,但未配置审批人 |
| | | // 特殊:如果当前节点已经存在审批人,则不允许覆盖。 例如并行节点后,设置的审批人自选节点。 https://t.zsxq.com/daxv1 |
| | | Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); |
| | | if (approveUserSelectAssignees != null && CollUtil.isNotEmpty(approveUserSelectAssignees.get(nextFlowNode.getId()))) { |
| | | continue; |
| | | } |
| | | // 如果节点存在,但未配置审批人 |
| | | List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; |
| | | if (CollUtil.isEmpty(assignees)) { |
| | | throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); |
| | |
| | | // 2.2 添加流程评论 |
| | | taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), |
| | | BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); |
| | | // 2.3 如果当前任务时被加签的,则加它的根任务也标记成未通过 |
| | | // 2.3 添加附件 |
| | | if (CollUtil.isNotEmpty(reqVO.getAttachments())) { |
| | | reqVO.getAttachments().forEach(attachment -> taskService.createAttachment(BpmAttachmentTypeEnum.TASK_ATTACHMENT.getType(), |
| | | task.getId(), task.getProcessInstanceId(), FileUtil.getName(URLUtil.getPath(attachment)), null, attachment)); |
| | | } |
| | | |
| | | // 2.4 如果当前任务时被加签的,则加它的根任务也标记成未通过 |
| | | // 疑问:为什么要标记未通过呢? |
| | | // 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 yudao-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。 |
| | | if (task.getParentTaskId() != null) { |
| | |
| | | // 为什么不直接使用 runTaskKeyList 呢?因为可能存在多个审批分支,例如说:A -> B -> C 和 D -> F,而只要 C 撤回到 A,需要排除掉 F |
| | | List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); |
| | | List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId); |
| | | |
| | | // 2. 给当前要被退回的 task 数组,设置退回意见 |
| | | taskList.forEach(task -> { |
| | | // 需要排除掉,不需要设置退回意见的任务 |
| | | if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { |
| | | return; |
| | | } |
| | | |
| | | // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 |
| | | if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 |
| | | // 2.1.1 添加评论 |
| | |
| | | // 相关 issue: https://github.com/flowable/flowable-engine/issues/3944 |
| | | // ② flowable 7.2.0 版本后,继续使用 moveActivityIdsToSingleActivityId 方法。原因:flowable 7.2.0 版本修复了该问题。 |
| | | // 相关 issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/1018 |
| | | // ③ moveActivityIdsToSingleActivityId 使用遇到问题, 相关 issue https://gitee.com/zhijiantianya/yudao-cloud/issues/IJM8MS |
| | | // 改成 moveExecutionsToSingleActivityId 好像并没有遇到 ② 提到的超时提醒失效的问题。暂时先改回 moveExecutionsToSingleActivityId |
| | | // ④ moveExecutionsToSingleActivityId 回退多实例的时候不会去删除多实例根, 应改成 moveActivityIdsToSingleActivityId |
| | | // flowable 8.0.0 修复上面相关问题, 还修复了并行分支回退的问题 https://t.zsxq.com/z4d9i。 |
| | | // ⑤ 使用 moveExecutionsToSingleActivityId 方法进行回退操作时,如果是多实例的用户任务【芋道用户任务默认为多实例】,不会删除多实例任务的根数据 ACT_RU_EXECUTION |
| | | // 会导致有一些问题,所以使用 moveActivityIdsToSingleActivityId。 但是该方法在 flowable 6.8.1 ~ 7.1.0 的版本会有 bug 阻塞回退功能 |
| | | // 相关 issue: https://github.com/flowable/flowable-engine/issues/3944 |
| | | runtimeService.createChangeActivityStateBuilder() |
| | | .processInstanceId(currentTask.getProcessInstanceId()) |
| | | .moveActivityIdsToSingleActivityId(returnTaskKeyList, reqVO.getTargetTaskDefinitionKey()) |
| | | // 设置需要预测的任务 ids 的流程变量,用于辅助预测 |
| | | .processVariable(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskDefinitionKeys) |
| | | // 设置流程变量(local)节点退回标记, 用于退回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略,导致自动通过 |
| | | .localVariable(reqVO.getTargetTaskDefinitionKey(), |
| | | String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE) |
| | | // 设置流程变量节点退回标记, 用于退回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略,导致自动通过 |
| | | .processVariable(String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE) |
| | | .changeState(); |
| | | } |
| | | |
| | |
| | | return; |
| | | } |
| | | |
| | | // 自动去重,通过自动审批的方式 |
| | | BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId()); |
| | | if (processDefinitionInfo == null) { |
| | | log.error("[processTaskAssigned][taskId({}) 没有找到流程定义({})]", task.getId(), task.getProcessDefinitionId()); |
| | | return; |
| | | } |
| | | if (processDefinitionInfo.getAutoApprovalType() != null) { |
| | | HistoricTaskInstanceQuery sameAssigneeQuery = historyService.createHistoricTaskInstanceQuery() |
| | | .processInstanceId(task.getProcessInstanceId()) |
| | | .taskAssignee(task.getAssignee()) // 相同审批人 |
| | | .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus()) |
| | | .finished(); |
| | | if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType()) |
| | | && sameAssigneeQuery.count() > 0) { |
| | | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) |
| | | .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName())); |
| | | // 需要基于 instance 设置租户编号,避免 Flowable 内部异步执行时【例如:超时自动通过】 丢失租户编号 |
| | | FlowableUtils.execute(processInstance.getTenantId(), () -> { |
| | | // 自动去重,通过自动审批的方式 |
| | | BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId()); |
| | | if (processDefinitionInfo == null) { |
| | | log.error("[processTaskAssigned][taskId({}) 没有找到流程定义({})]", task.getId(), task.getProcessDefinitionId()); |
| | | return; |
| | | } |
| | | if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) { |
| | | BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); |
| | | if (bpmnModel == null) { |
| | | log.error("[processTaskAssigned][taskId({}) 没有找到流程模型({})]", task.getId(), task.getProcessDefinitionId()); |
| | | return; |
| | | } |
| | | List<String> sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingFlows( // 获取所有上一个节点 |
| | | BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())), |
| | | SequenceFlow::getSourceRef); |
| | | if (sameAssigneeQuery.taskDefinitionKeys(sourceTaskIds).count() > 0) { |
| | | if (processDefinitionInfo.getAutoApprovalType() != null) { |
| | | HistoricTaskInstanceQuery approvedTaskQuery = historyService.createHistoricTaskInstanceQuery() |
| | | .processInstanceId(task.getProcessInstanceId()) |
| | | .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus()) |
| | | .finished(); |
| | | if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType()) |
| | | && approvedTaskQuery.taskAssignee(task.getAssignee()).count() > 0) { |
| | | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) |
| | | .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName())); |
| | | .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName())); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 获取发起人节点 |
| | | BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); |
| | | if (bpmnModel == null) { |
| | | log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); |
| | | return; |
| | | } |
| | | FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); |
| | | // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略(使用 local variable) |
| | | Boolean returnTaskFlag = runtimeService.getVariableLocal(task.getExecutionId(), |
| | | String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); |
| | | Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), |
| | | BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); |
| | | if (userTaskElement.getId().equals(START_USER_NODE_ID) |
| | | && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 |
| | | || BooleanUtil.isTrue(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 |
| | | && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { |
| | | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) |
| | | .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); |
| | | return; |
| | | } |
| | | // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 |
| | | if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID) |
| | | && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { |
| | | if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { |
| | | Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); |
| | | |
| | | // 情况一:自动跳过 |
| | | if (ObjectUtils.equalsAny(assignStartUserHandlerType, |
| | | BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { |
| | | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) |
| | | .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason())); |
| | | return; |
| | | } |
| | | // 情况二:转交给部门负责人审批 |
| | | if (ObjectUtils.equalsAny(assignStartUserHandlerType, |
| | | BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) { |
| | | AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); |
| | | Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId()); |
| | | DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null; |
| | | Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), startUser.getDeptId()); |
| | | // 找不到部门负责人的情况下,自动审批通过 |
| | | // noinspection DataFlowIssue |
| | | if (dept.getLeaderUserId() == null) { |
| | | // 连续审批的节点自动通过 |
| | | if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) { |
| | | BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); |
| | | if (bpmnModel == null) { |
| | | log.error("[processTaskAssigned][taskId({}) 没有找到流程模型({})]", task.getId(), task.getProcessDefinitionId()); |
| | | return; |
| | | } |
| | | List<String> sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingUserTaskFlows( // 获取所有的上一个 UserTask 节点连线 |
| | | BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())), |
| | | SequenceFlow::getSourceRef); |
| | | approvedTaskQuery.taskDefinitionKeys(sourceTaskIds).orderByTaskCreateTime().desc(); // 设置 taskIds, 并按创建时间倒序排序 |
| | | HistoricTaskInstance firstHisTask = CollUtil.getFirst(approvedTaskQuery.list()); |
| | | if (firstHisTask != null && StrUtil.equals(firstHisTask.getAssignee(), task.getAssignee())) { |
| | | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) |
| | | .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason())); |
| | | .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName())); |
| | | return; |
| | | } |
| | | // 找得到部门负责人的情况下,修改负责人 |
| | | if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) { |
| | | getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO() |
| | | .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId()) |
| | | .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason())); |
| | | return; |
| | | } |
| | | // 如果部门负责人是自己,还是自己审批吧~ |
| | | } |
| | | } |
| | | } |
| | | // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 |
| | | FlowableUtils.execute(processInstance.getTenantId(), () -> { |
| | | |
| | | // 获取发起人节点 |
| | | BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); |
| | | if (bpmnModel == null) { |
| | | log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); |
| | | return; |
| | | } |
| | | FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); |
| | | // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 |
| | | Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), |
| | | String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); |
| | | Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), |
| | | BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); |
| | | if (userTaskElement.getId().equals(START_USER_NODE_ID) |
| | | && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 |
| | | || BooleanUtil.isTrue(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 |
| | | && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { |
| | | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) |
| | | .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); |
| | | return; |
| | | } |
| | | // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 |
| | | if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID) |
| | | && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { |
| | | if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { |
| | | Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); |
| | | |
| | | // 情况一:自动跳过 |
| | | if (ObjectUtils.equalsAny(assignStartUserHandlerType, |
| | | BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { |
| | | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) |
| | | .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason())); |
| | | return; |
| | | } |
| | | // 情况二:转交给部门负责人审批 |
| | | if (ObjectUtils.equalsAny(assignStartUserHandlerType, |
| | | BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) { |
| | | AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); |
| | | Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId()); |
| | | DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null; |
| | | Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), startUser.getDeptId()); |
| | | // 找不到部门负责人的情况下,自动审批通过 |
| | | // noinspection DataFlowIssue |
| | | if (dept.getLeaderUserId() == null) { |
| | | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) |
| | | .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason())); |
| | | return; |
| | | } |
| | | // 找得到部门负责人的情况下,修改负责人 |
| | | if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) { |
| | | getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO() |
| | | .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId()) |
| | | .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason())); |
| | | return; |
| | | } |
| | | // 如果部门负责人是自己,还是自己审批吧~ |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 发送消息 |
| | | AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); |
| | | messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); |
| | | }); |