Commit 0d738fa3976c61fdd840f695af5f51f3991a7832
1 parent
d4306846
【功能新增】工作流:支持审批人为空时,根据配置进行自动通过、自动拒绝、指定人审批的效果
Showing
13 changed files
with
197 additions
and
22 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.enums.definition; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | |
| 4 | +import lombok.Getter; | |
| 5 | +import lombok.RequiredArgsConstructor; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * BPM 用户任务的审批人为空时,处理类型枚举 | |
| 9 | + * | |
| 10 | + * @author 芋道源码 | |
| 11 | + */ | |
| 12 | +@RequiredArgsConstructor | |
| 13 | +@Getter | |
| 14 | +public enum BpmUserTaskAssignEmptyHandlerTypeEnum implements IntArrayValuable { | |
| 15 | + | |
| 16 | + APPROVE(1), // 自动通过 | |
| 17 | + REJECT(2), // 自动拒绝 | |
| 18 | + ASSIGN_USER(3), // 指定人员审批 | |
| 19 | + ASSIGN_ADMIN(4), // 转交给流程管理员 | |
| 20 | + ; | |
| 21 | + | |
| 22 | + private final Integer type; | |
| 23 | + | |
| 24 | + @Override | |
| 25 | + public int[] array() { | |
| 26 | + return new int[0]; | |
| 27 | + } | |
| 28 | + | |
| 29 | +} | ... | ... |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java
| ... | ... | @@ -28,6 +28,8 @@ public enum BpmReasonEnum { |
| 28 | 28 | ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), |
| 29 | 29 | ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), |
| 30 | 30 | ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), |
| 31 | + ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), | |
| 32 | + ASSIGN_EMPTY_REJECT("审批人为空,自动不通过"), | |
| 31 | 33 | ; |
| 32 | 34 | |
| 33 | 35 | private final String reason; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
| ... | ... | @@ -86,6 +86,11 @@ public class BpmSimpleModelNodeVO { |
| 86 | 86 | @InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class) |
| 87 | 87 | private Integer assignStartUserHandlerType; |
| 88 | 88 | |
| 89 | + /** | |
| 90 | + * 空处理策略 | |
| 91 | + */ | |
| 92 | + private AssignEmptyHandler assignEmptyHandler; | |
| 93 | + | |
| 89 | 94 | @Data |
| 90 | 95 | @Schema(description = "审批节点拒绝处理策略") |
| 91 | 96 | public static class RejectHandler { |
| ... | ... | @@ -122,6 +127,21 @@ public class BpmSimpleModelNodeVO { |
| 122 | 127 | } |
| 123 | 128 | |
| 124 | 129 | @Data |
| 130 | + @Schema(description = "空处理策略") | |
| 131 | + @Valid | |
| 132 | + public static class AssignEmptyHandler { | |
| 133 | + | |
| 134 | + @Schema(description = "空处理类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 135 | + @NotNull(message = "空处理类型不能为空") | |
| 136 | + @InEnum(BpmUserTaskAssignEmptyHandlerTypeEnum.class) | |
| 137 | + private Integer type; | |
| 138 | + | |
| 139 | + @Schema(description = "指定人员审批的用户编号数组", example = "1") | |
| 140 | + private List<Long> userIds; | |
| 141 | + | |
| 142 | + } | |
| 143 | + | |
| 144 | + @Data | |
| 125 | 145 | @Schema(description = "操作按钮设置") |
| 126 | 146 | public static class OperationButtonSetting { |
| 127 | 147 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | |
| 3 | 5 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| 4 | 6 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; |
| 5 | 7 | import lombok.Setter; |
| ... | ... | @@ -49,6 +51,10 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav |
| 49 | 51 | |
| 50 | 52 | // 第二步,获取任务的所有处理人 |
| 51 | 53 | Set<Long> assigneeUserIds = taskCandidateInvoker.calculateUsers(execution); |
| 54 | + if (CollUtil.isEmpty(assigneeUserIds)) { | |
| 55 | + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务,避免自动通过! | |
| 56 | + assigneeUserIds = SetUtils.asSet((Long) null); | |
| 57 | + } | |
| 52 | 58 | execution.setVariable(super.collectionVariable, assigneeUserIds); |
| 53 | 59 | return assigneeUserIds.size(); |
| 54 | 60 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | |
| 3 | 5 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| 4 | 6 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; |
| 5 | 7 | import lombok.Setter; |
| ... | ... | @@ -43,6 +45,10 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB |
| 43 | 45 | |
| 44 | 46 | // 第二步,获取任务的所有处理人 |
| 45 | 47 | Set<Long> assigneeUserIds = new LinkedHashSet<>(taskCandidateInvoker.calculateUsers(execution)); // 保证有序!!! |
| 48 | + if (CollUtil.isEmpty(assigneeUserIds)) { | |
| 49 | + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务,避免自动通过! | |
| 50 | + assigneeUserIds = SetUtils.asSet((Long) null); | |
| 51 | + } | |
| 46 | 52 | execution.setVariable(super.collectionVariable, assigneeUserIds); |
| 47 | 53 | return assigneeUserIds.size(); |
| 48 | 54 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.lang.Assert; | |
| 4 | +import cn.hutool.core.util.ObjectUtil; | |
| 5 | 5 | import cn.hutool.core.util.RandomUtil; |
| 6 | +import cn.hutool.extra.spring.SpringUtil; | |
| 7 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO; | |
| 8 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRejectReqVO; | |
| 9 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; | |
| 10 | +import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; | |
| 6 | 11 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; |
| 12 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 13 | +import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; | |
| 7 | 14 | import lombok.Setter; |
| 8 | 15 | import lombok.extern.slf4j.Slf4j; |
| 9 | 16 | import org.flowable.bpmn.model.UserTask; |
| ... | ... | @@ -14,6 +21,8 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; |
| 14 | 21 | import org.flowable.engine.impl.util.TaskHelper; |
| 15 | 22 | import org.flowable.task.service.TaskService; |
| 16 | 23 | import org.flowable.task.service.impl.persistence.entity.TaskEntity; |
| 24 | +import org.springframework.transaction.support.TransactionSynchronization; | |
| 25 | +import org.springframework.transaction.support.TransactionSynchronizationManager; | |
| 17 | 26 | |
| 18 | 27 | import java.util.List; |
| 19 | 28 | import java.util.Set; |
| ... | ... | @@ -41,9 +50,29 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { |
| 41 | 50 | DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { |
| 42 | 51 | // 第一步,获得任务的候选用户 |
| 43 | 52 | Long assigneeUserId = calculateTaskCandidateUsers(execution); |
| 44 | - Assert.notNull(assigneeUserId, "任务处理人不能为空"); | |
| 45 | 53 | // 第二步,设置作为负责人 |
| 46 | - TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); | |
| 54 | + if (assigneeUserId != null) { | |
| 55 | + TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); | |
| 56 | + return; | |
| 57 | + } | |
| 58 | + | |
| 59 | + // 特殊:审批人为空,根据配置是否要自动通过、自动拒绝 | |
| 60 | + Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(userTask); | |
| 61 | + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { | |
| 62 | + | |
| 63 | + @Override | |
| 64 | + public void afterCommit() { | |
| 65 | + if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) { | |
| 66 | + SpringUtil.getBean(BpmTaskService.class).approveTask(null, new BpmTaskApproveReqVO() | |
| 67 | + .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_APPROVE.getReason())); | |
| 68 | + } else if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.REJECT.getType())) { | |
| 69 | + SpringUtil.getBean(BpmTaskService.class).rejectTask(null, new BpmTaskRejectReqVO() | |
| 70 | + .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_REJECT.getReason())); | |
| 71 | + } | |
| 72 | + | |
| 73 | + } | |
| 74 | + | |
| 75 | + }); | |
| 47 | 76 | } |
| 48 | 77 | |
| 49 | 78 | private Long calculateTaskCandidateUsers(DelegateExecution execution) { |
| ... | ... | @@ -56,6 +85,9 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { |
| 56 | 85 | // 情况二,如果非多实例的任务,则计算任务处理人 |
| 57 | 86 | // 第一步,先计算可处理该任务的处理人们 |
| 58 | 87 | Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsers(execution); |
| 88 | + if (CollUtil.isEmpty(candidateUserIds)) { | |
| 89 | + return null; | |
| 90 | + } | |
| 59 | 91 | // 第二步,后随机选择一个任务的处理人 |
| 60 | 92 | // 疑问:为什么一定要选择一个任务处理人? |
| 61 | 93 | // 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
| ... | ... | @@ -27,7 +27,6 @@ import java.util.Set; |
| 27 | 27 | |
| 28 | 28 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 29 | 29 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG; |
| 30 | -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER; | |
| 31 | 30 | |
| 32 | 31 | /** |
| 33 | 32 | * {@link BpmTaskCandidateStrategy} 的调用者,用于调用对应的策略,实现任务的候选人的计算 |
| ... | ... | @@ -89,17 +88,16 @@ public class BpmTaskCandidateInvoker { |
| 89 | 88 | String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement()); |
| 90 | 89 | // 1.1 计算任务的候选人 |
| 91 | 90 | Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param); |
| 92 | - // 1.2 移除被禁用的用户 | |
| 93 | - removeDisableUsers(userIds); | |
| 91 | + // 1.2 候选人为空时,根据“审批人为空”的配置补充 | |
| 92 | + if (CollUtil.isEmpty(userIds)) { | |
| 93 | + userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy()) | |
| 94 | + .calculateUsers(execution, param); | |
| 95 | + } | |
| 94 | 96 | // 1.3 移除发起人的用户 |
| 95 | 97 | removeStartUserIfSkip(execution, userIds); |
| 96 | 98 | |
| 97 | - // 2. 校验是否有候选人 | |
| 98 | - if (CollUtil.isEmpty(userIds)) { | |
| 99 | - log.error("[calculateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(), | |
| 100 | - execution.getProcessDefinitionId(), execution.getCurrentActivityId(), strategy, param); | |
| 101 | - throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); | |
| 102 | - } | |
| 99 | + // 2. 移除被禁用的用户 | |
| 100 | + removeDisableUsers(userIds); | |
| 103 | 101 | return userIds; |
| 104 | 102 | } |
| 105 | 103 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAssignEmptyStrategy.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; | |
| 4 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; | |
| 5 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; | |
| 6 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 7 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 8 | +import jakarta.annotation.Resource; | |
| 9 | +import org.flowable.engine.delegate.DelegateExecution; | |
| 10 | +import org.springframework.stereotype.Component; | |
| 11 | + | |
| 12 | +import java.util.HashSet; | |
| 13 | +import java.util.Objects; | |
| 14 | +import java.util.Set; | |
| 15 | + | |
| 16 | +/** | |
| 17 | + * 审批人为空 {@link BpmTaskCandidateStrategy} 实现类 | |
| 18 | + * | |
| 19 | + * @author kyle | |
| 20 | + */ | |
| 21 | +@Component | |
| 22 | +public class BpmTaskCandidateAssignEmptyStrategy implements BpmTaskCandidateStrategy { | |
| 23 | + | |
| 24 | + @Resource | |
| 25 | + private AdminUserApi adminUserApi; | |
| 26 | + | |
| 27 | + @Override | |
| 28 | + public BpmTaskCandidateStrategyEnum getStrategy() { | |
| 29 | + return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY; | |
| 30 | + } | |
| 31 | + | |
| 32 | + @Override | |
| 33 | + public void validateParam(String param) { | |
| 34 | + } | |
| 35 | + | |
| 36 | + @Override | |
| 37 | + public Set<Long> calculateUsers(DelegateExecution execution, String param) { | |
| 38 | + // 情况一:指定人员审批 | |
| 39 | + Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(execution.getCurrentFlowElement()); | |
| 40 | + if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType())) { | |
| 41 | + return new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(execution.getCurrentFlowElement())); | |
| 42 | + } | |
| 43 | + | |
| 44 | + // 情况二:流程管理员 | |
| 45 | + if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) { | |
| 46 | + // TODO 芋艿:需要等待流程实例的管理员支持 | |
| 47 | + throw new UnsupportedOperationException("暂时实现!!!"); | |
| 48 | + } | |
| 49 | + | |
| 50 | + // 都不满足,还是返回空 | |
| 51 | + return new HashSet<>(); | |
| 52 | + } | |
| 53 | + | |
| 54 | +} | |
| 0 | 55 | \ No newline at end of file | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java
| ... | ... | @@ -30,6 +30,7 @@ public enum BpmTaskCandidateStrategyEnum implements IntArrayValuable { |
| 30 | 30 | START_USER_MULTI_LEVEL_DEPT_LEADER(38, "发起人连续多级部门的负责人"), |
| 31 | 31 | USER_GROUP(40, "用户组"), |
| 32 | 32 | EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager |
| 33 | + ASSIGN_EMPTY(1, "审批人为空"), | |
| 33 | 34 | ; |
| 34 | 35 | |
| 35 | 36 | public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmTaskCandidateStrategyEnum::getStrategy).toArray(); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
| ... | ... | @@ -39,6 +39,15 @@ public interface BpmnModelConstants { |
| 39 | 39 | String USER_TASK_ASSIGN_START_USER_HANDLER_TYPE = "assignStartUserHandlerType"; |
| 40 | 40 | |
| 41 | 41 | /** |
| 42 | + * BPMN ExtensionElement 的扩展属性,用于标记用户任务的空处理类型 | |
| 43 | + */ | |
| 44 | + String USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE = "assignEmptyHandlerType"; | |
| 45 | + /** | |
| 46 | + * BPMN ExtensionElement 的扩展属性,用于标记用户任务的空处理的指定用户编号数组 | |
| 47 | + */ | |
| 48 | + String USER_TASK_ASSIGN_USER_IDS = "assignEmptyUserIds"; | |
| 49 | + | |
| 50 | + /** | |
| 42 | 51 | * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型 |
| 43 | 52 | */ |
| 44 | 53 | String USER_TASK_REJECT_HANDLER_TYPE = "rejectHandlerType"; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; |
| 5 | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 8 | +import cn.iocoder.yudao.framework.common.util.string.StrUtils; | |
| 8 | 9 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; |
| 9 | 10 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; |
| 10 | 11 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| ... | ... | @@ -45,16 +46,24 @@ public class BpmnModelUtils { |
| 45 | 46 | } |
| 46 | 47 | |
| 47 | 48 | public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) { |
| 48 | - Integer rejectHandlerType = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE)); | |
| 49 | + Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE)); | |
| 49 | 50 | return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); |
| 50 | 51 | } |
| 51 | 52 | |
| 52 | 53 | public static String parseReturnTaskId(FlowElement flowElement) { |
| 53 | - return BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); | |
| 54 | + return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); | |
| 54 | 55 | } |
| 55 | 56 | |
| 56 | 57 | public static Integer parseAssignStartUserHandlerType(FlowElement userTask) { |
| 57 | - return NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE)); | |
| 58 | + return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE)); | |
| 59 | + } | |
| 60 | + | |
| 61 | + public static Integer parseAssignEmptyHandlerType(FlowElement userTask) { | |
| 62 | + return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE)); | |
| 63 | + } | |
| 64 | + | |
| 65 | + public static List<Long> parseAssignEmptyHandlerUserIds(FlowElement userTask) { | |
| 66 | + return StrUtils.splitToLong(parseExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS), ","); | |
| 58 | 67 | } |
| 59 | 68 | |
| 60 | 69 | public static String parseExtensionElement(FlowElement flowElement, String elementName) { | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| ... | ... | @@ -19,10 +19,7 @@ import org.flowable.bpmn.BpmnAutoLayout; |
| 19 | 19 | import org.flowable.bpmn.model.Process; |
| 20 | 20 | import org.flowable.bpmn.model.*; |
| 21 | 21 | |
| 22 | -import java.util.ArrayList; | |
| 23 | -import java.util.List; | |
| 24 | -import java.util.Map; | |
| 25 | -import java.util.Objects; | |
| 22 | +import java.util.*; | |
| 26 | 23 | |
| 27 | 24 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting; |
| 28 | 25 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; |
| ... | ... | @@ -456,7 +453,6 @@ public class SimpleModelUtils { |
| 456 | 453 | userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); |
| 457 | 454 | } |
| 458 | 455 | |
| 459 | - // TODO @jason:addCandidateElements、processMultiInstanceLoopCharacteristics 建议一起搞哈? | |
| 460 | 456 | // 添加候选人元素 |
| 461 | 457 | addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask); |
| 462 | 458 | // 添加表单字段权限属性元素 |
| ... | ... | @@ -469,6 +465,8 @@ public class SimpleModelUtils { |
| 469 | 465 | addTaskRejectElements(node.getRejectHandler(), userTask); |
| 470 | 466 | // 添加用户任务的审批人与发起人相同时的处理元素 |
| 471 | 467 | addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask); |
| 468 | + // 添加用户任务的空处理元素 | |
| 469 | + addAssignEmptyHandlerType(node.getAssignEmptyHandler(), userTask); | |
| 472 | 470 | return userTask; |
| 473 | 471 | } |
| 474 | 472 | |
| ... | ... | @@ -487,6 +485,14 @@ public class SimpleModelUtils { |
| 487 | 485 | addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString()); |
| 488 | 486 | } |
| 489 | 487 | |
| 488 | + private static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { | |
| 489 | + if (emptyHandler == null) { | |
| 490 | + return; | |
| 491 | + } | |
| 492 | + addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType())); | |
| 493 | + addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(",", emptyHandler.getUserIds())); | |
| 494 | + } | |
| 495 | + | |
| 490 | 496 | private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) { |
| 491 | 497 | BpmApproveMethodEnum bpmApproveMethodEnum = BpmApproveMethodEnum.valueOf(approveMethod); |
| 492 | 498 | if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.RANDOM) { |
| ... | ... | @@ -496,7 +502,7 @@ public class SimpleModelUtils { |
| 496 | 502 | addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_METHOD, |
| 497 | 503 | approveMethod == null ? null : approveMethod.toString()); |
| 498 | 504 | MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); |
| 499 | - // 设置 collectionVariable。本系统用不到。会在 仅仅为了校验。 | |
| 505 | + // 设置 collectionVariable。本系统用不到。仅仅为了 Flowable 校验不报错。 | |
| 500 | 506 | multiInstanceCharacteristics.setInputDataItem("${coll_userList}"); |
| 501 | 507 | if (bpmApproveMethodEnum == BpmApproveMethodEnum.ANY) { |
| 502 | 508 | multiInstanceCharacteristics.setCompletionCondition(ANY_OF_APPROVE_COMPLETE_EXPRESSION); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| ... | ... | @@ -191,7 +191,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 191 | 191 | */ |
| 192 | 192 | private Task validateTask(Long userId, String taskId) { |
| 193 | 193 | Task task = validateTaskExist(taskId); |
| 194 | - if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) { | |
| 194 | + // 为什么判断 assignee 非空的情况下? | |
| 195 | + // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 | |
| 196 | + if (StrUtil.isNotBlank(task.getAssignee()) | |
| 197 | + && ObjectUtil.notEqual(userId, NumberUtils.parseLong(task.getAssignee()))) { | |
| 195 | 198 | throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); |
| 196 | 199 | } |
| 197 | 200 | return task; | ... | ... |