mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 18:28:43 +08:00 
			
		
		
		
	【功能新增】审批节点的审批人与发起人相同时,对应的处理类型的配置
This commit is contained in:
		| @@ -4,7 +4,6 @@ import cn.hutool.core.util.ArrayUtil; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Getter; | ||||
|  | ||||
| // TODO @jason:这个是不是可以去掉了哈? | ||||
| /** | ||||
|  * BPM 边界事件 (boundary event) 自定义类型枚举 | ||||
|  * | ||||
| @@ -14,8 +13,7 @@ import lombok.Getter; | ||||
| @AllArgsConstructor | ||||
| public enum BpmBoundaryEventType { | ||||
|  | ||||
|     USER_TASK_TIMEOUT(1,"用户任务超时"), | ||||
|     USER_TASK_REJECT_POST_PROCESS(2, "用户任务拒绝后处理"); | ||||
|     USER_TASK_TIMEOUT(1,"用户任务超时"); | ||||
|  | ||||
|     private final Integer type; | ||||
|     private final String name; | ||||
| @@ -23,4 +21,5 @@ public enum BpmBoundaryEventType { | ||||
|     public static BpmBoundaryEventType typeOf(Integer type) { | ||||
|         return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,27 @@ | ||||
| package cn.iocoder.yudao.module.bpm.enums.definition; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| /** | ||||
|  * BPM 用户任务的审批人与发起人相同时,处理类型枚举 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @RequiredArgsConstructor | ||||
| @Getter | ||||
| public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements IntArrayValuable { | ||||
|  | ||||
|     START_USER_AUDIT(1), // 由发起人对自己审批 | ||||
|     SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过 | ||||
|     ASSIGN_DEPT_LEADER(3); // 转交给部门负责人审批 | ||||
|  | ||||
|     private final Integer type; | ||||
|  | ||||
|     @Override | ||||
|     public int[] array() { | ||||
|         return new int[0]; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,10 +1,7 @@ | ||||
| package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.validation.InEnum; | ||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum; | ||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType; | ||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; | ||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType; | ||||
| import cn.iocoder.yudao.module.bpm.enums.definition.*; | ||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; | ||||
| import com.fasterxml.jackson.annotation.JsonIgnore; | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| @@ -84,6 +81,10 @@ public class BpmSimpleModelNodeVO { | ||||
|      */ | ||||
|     private TimeoutHandler timeoutHandler; | ||||
|  | ||||
|     @Schema(description = "审批节点的审批人与发起人相同时,对应的处理类型", example = "1") | ||||
|     @InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class) | ||||
|     private Integer assignStartUserHandlerType; | ||||
|  | ||||
|     @Data | ||||
|     @Schema(description = "审批节点拒绝处理策略") | ||||
|     public static class RejectHandler { | ||||
| @@ -132,14 +133,7 @@ public class BpmSimpleModelNodeVO { | ||||
|     } | ||||
|  | ||||
|     // Map<String, Integer> formPermissions; 表单权限;仅发起、审批、抄送节点会使用 | ||||
|     // Integer approveMethod; 审批方式;仅审批节点会使用 | ||||
|     // TODO @jason 后面和前端一起调整一下;下面的 ①、②、③ 是优先级 | ||||
|     // TODO @芋艿:① 审批人的选择; | ||||
|     // TODO @芋艿:⑥ 没有人的策略? | ||||
|     // TODO @芋艿:② 审批拒绝的策略? | ||||
|     // TODO @芋艿:③ 配置的可操作列表?(操作权限) | ||||
|     // TODO @芋艿:④ 表单的权限列表? | ||||
|     // TODO @芋艿:⑨ 超时配置;要支持指定时间点、指定时间间隔; | ||||
|     // TODO @芋艿:条件;建议可以固化的一些选项;然后有个表达式兜底;要支持 | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -176,7 +176,6 @@ public interface BpmTaskConvert { | ||||
|         childTask.setParentTaskId(parentTask.getId()); | ||||
|         childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId()); | ||||
|         childTask.setProcessInstanceId(parentTask.getProcessInstanceId()); | ||||
| //        childTask.setExecutionId(parentTask.getExecutionId()); // TODO 芋艿:新加的,不太确定;尴尬,不加时,子任务不通过会失败(报错);加了,子任务审批通过会失败(报错) | ||||
|         childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey()); | ||||
|         childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId()); | ||||
|         childTask.setPriority(parentTask.getPriority()); | ||||
|   | ||||
| @@ -29,12 +29,17 @@ public interface BpmnModelConstants { | ||||
|     String BOUNDARY_EVENT_TYPE = "boundaryEventType"; | ||||
|  | ||||
|     // TODO @jason:这个命名,应该也要改哈 | ||||
|     // TODO @jason:1)是不是上面的 timeoutAction 改成 timeoutHandler; | ||||
|     /** | ||||
|      * BPMN ExtensionElement 的扩展属性,用于标记用户任务超时执行动作 | ||||
|      */ | ||||
|     String USER_TASK_TIMEOUT_HANDLER_ACTION = "timeoutAction"; | ||||
|  | ||||
|     // TODO @jason:1)是不是上面的 timeoutAction 改成 timeoutHandler;2)rejectHandlerType 改成 rejectHandler 哇? | ||||
|     /** | ||||
|      * BPMN ExtensionElement 的扩展属性,用于标记用户任务的审批人与发起人相同时,对应的处理类型 | ||||
|      */ | ||||
|     String USER_TASK_ASSIGN_START_USER_HANDLER_TYPE = "assignStartUserHandlerType"; | ||||
|  | ||||
|     /** | ||||
|      * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型 | ||||
|      */ | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||||
| import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; | ||||
| import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.RejectHandler; | ||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum; | ||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType; | ||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModeConditionType; | ||||
| import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType; | ||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | ||||
| @@ -25,7 +26,6 @@ import java.util.Objects; | ||||
|  | ||||
| import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting; | ||||
| import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; | ||||
| import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_TIMEOUT; | ||||
| import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*; | ||||
| import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType.REMINDER; | ||||
| import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; | ||||
| @@ -338,8 +338,9 @@ public class SimpleModelUtils { | ||||
|         List<FlowElement> flowElements = new ArrayList<>(); | ||||
|         UserTask userTask = buildBpmnUserTask(node); | ||||
|         flowElements.add(userTask); | ||||
|  | ||||
|         // 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理 | ||||
|         if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { | ||||
|             // 添加用户任务的 Timer Boundary Event, 用于任务的超时处理 | ||||
|             BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, node.getTimeoutHandler()); | ||||
|             flowElements.add(boundaryEvent); | ||||
|         } | ||||
| @@ -347,31 +348,31 @@ public class SimpleModelUtils { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 添加 UserTask 用户审批的 BoundaryEvent 超时事件 | ||||
|      * 添加 UserTask 用户的审批超时 BoundaryEvent 事件 | ||||
|      * | ||||
|      * @param userTask 审批任务 | ||||
|      * @param timeoutHandler 超时处理器 | ||||
|      * @return | ||||
|      * @return BoundaryEvent 超时事件 | ||||
|      */ | ||||
|     private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, TimeoutHandler timeoutHandler) { | ||||
|         // 定时器边界事件 | ||||
|         // 1.1 定时器边界事件 | ||||
|         BoundaryEvent boundaryEvent = new BoundaryEvent(); | ||||
|         boundaryEvent.setId("Event-" + IdUtil.fastUUID()); | ||||
|         // 设置关联的任务为不会被中断 | ||||
|         boundaryEvent.setCancelActivity(false); | ||||
|         boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断 | ||||
|         boundaryEvent.setAttachedToRef(userTask); | ||||
|         // 1.2 定义超时时间、最大提醒次数 | ||||
|         TimerEventDefinition eventDefinition = new TimerEventDefinition(); | ||||
|         eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration()); | ||||
|         if (Objects.equals(REMINDER.getAction(), timeoutHandler.getAction()) && | ||||
|                 timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) { | ||||
|             // 最大提醒次数 | ||||
|             eventDefinition.setTimeCycle(String.format("R%d/%s", timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration())); | ||||
|             eventDefinition.setTimeCycle(String.format("R%d/%s", | ||||
|                     timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration())); | ||||
|         } | ||||
|         boundaryEvent.addEventDefinition(eventDefinition); | ||||
|  | ||||
|         // 添加定时器边界事件类型 | ||||
|         addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, USER_TASK_TIMEOUT.getType().toString()); | ||||
|         // 添加超时执行动作元素 | ||||
|         // 2.1 添加定时器边界事件类型 | ||||
|         addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventType.USER_TASK_TIMEOUT.getType().toString()); | ||||
|         // 2.2 添加超时执行动作元素 | ||||
|         addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_ACTION, StrUtil.toStringOrNull(timeoutHandler.getAction())); | ||||
|         return boundaryEvent; | ||||
|     } | ||||
| @@ -455,8 +456,6 @@ public class SimpleModelUtils { | ||||
|             userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); | ||||
|         } | ||||
|  | ||||
|         // TODO 芋艿 + jason:要不要基于服务任务,实现或签下的审批不通过?或者说,按比例审批 | ||||
|  | ||||
|         // TODO @jason:addCandidateElements、processMultiInstanceLoopCharacteristics 建议一起搞哈? | ||||
|         // 添加候选人元素 | ||||
|         addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask); | ||||
| @@ -468,10 +467,11 @@ public class SimpleModelUtils { | ||||
|         processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask); | ||||
|         // 添加任务被拒绝的处理元素 | ||||
|         addTaskRejectElements(node.getRejectHandler(), userTask); | ||||
|         // 添加用户任务的审批人与发起人相同时的处理元素 | ||||
|         addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask); | ||||
|         return userTask; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private static void addTaskRejectElements(RejectHandler rejectHandler, UserTask userTask) { | ||||
|         if (rejectHandler == null) { | ||||
|             return; | ||||
| @@ -480,6 +480,13 @@ public class SimpleModelUtils { | ||||
|         addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId()); | ||||
|     } | ||||
|  | ||||
|     private static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) { | ||||
|         if (assignStartUserHandlerType == null) { | ||||
|             return; | ||||
|         } | ||||
|         addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString()); | ||||
|     } | ||||
|  | ||||
|     private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) { | ||||
|         BpmApproveMethodEnum bpmApproveMethodEnum = BpmApproveMethodEnum.valueOf(approveMethod); | ||||
|         if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.RANDOM) { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.task; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.util.*; | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||||
| import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | ||||
| @@ -79,7 +80,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||||
|     @Resource | ||||
|     private BpmProcessInstanceCopyService processInstanceCopyService; | ||||
|     @Resource | ||||
|     private BpmModelService bpmModelService; | ||||
|     private BpmModelService modelService; | ||||
|     @Resource | ||||
|     private BpmMessageService messageService; | ||||
|  | ||||
| @@ -228,7 +229,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||||
|         // 1.1 校验当前任务 task 存在 | ||||
|         Task task = validateTaskExist(id); | ||||
|         // 1.2 根据流程定义获取流程模型信息 | ||||
|         BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); | ||||
|         BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); | ||||
|         FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); | ||||
|         if (source == null) { | ||||
|             throw exception(TASK_NOT_EXISTS); | ||||
| @@ -498,7 +499,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||||
|         } | ||||
|  | ||||
|         // 3. 根据不同的 RejectHandler 处理策略 | ||||
|         BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); | ||||
|         BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); | ||||
|         FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); | ||||
|         // 3.1 情况一:驳回到指定的任务节点 | ||||
|         BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement); | ||||
| @@ -562,7 +563,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||||
|      */ | ||||
|     private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) { | ||||
|         // 1.1 获取流程模型信息 | ||||
|         BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId); | ||||
|         BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); | ||||
|         // 1.3 获取当前任务节点元素 | ||||
|         FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey); | ||||
|         // 1.3 获取跳转的节点元素 | ||||
| @@ -690,7 +691,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||||
|         }); | ||||
|  | ||||
|         // 2. 终止流程 | ||||
|         BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId()); | ||||
|         BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId()); | ||||
|         List<String> activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey)); | ||||
|         EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); | ||||
|         Assert.notNull(endEvent, "结束节点不能未空"); | ||||
| @@ -915,13 +916,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||||
|             @Override | ||||
|             public void afterCommit() { | ||||
|                 if (StrUtil.isEmpty(task.getAssignee())) { | ||||
|                     log.error("[processTaskAssigned][taskId({}) 没有分配到负责人]", task.getId()); | ||||
|                     return; | ||||
|                 } | ||||
|                 ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); | ||||
|                 if (processInstance != null) { | ||||
|                     AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); | ||||
|                     messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); | ||||
|                 if (processInstance == null) { | ||||
|                     log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId()); | ||||
|                     return; | ||||
|                 } | ||||
|                 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); | ||||
|                 if (bpmnModel == null) { | ||||
|                     log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 // 审批人与提交人为同一人时,根据策略进行处理 | ||||
|                 if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { | ||||
|                     getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO() | ||||
|                             .setId(task.getId()).setReason("审批人与提交人为同一人时,自动通过")); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); | ||||
|                 messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); | ||||
|             } | ||||
|  | ||||
|         }); | ||||
| @@ -966,4 +983,13 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获得自身的代理对象,解决 AOP 生效问题 | ||||
|      * | ||||
|      * @return 自己 | ||||
|      */ | ||||
|     private BpmTaskServiceImpl getSelf() { | ||||
|         return SpringUtil.getBean(getClass()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV