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,6 +28,8 @@ public enum BpmReasonEnum { | ||
| 28 | ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), | 28 | ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), |
| 29 | ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), | 29 | ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), |
| 30 | ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), | 30 | ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), |
| 31 | + ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), | ||
| 32 | + ASSIGN_EMPTY_REJECT("审批人为空,自动不通过"), | ||
| 31 | ; | 33 | ; |
| 32 | 34 | ||
| 33 | private final String reason; | 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,6 +86,11 @@ public class BpmSimpleModelNodeVO { | ||
| 86 | @InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class) | 86 | @InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class) |
| 87 | private Integer assignStartUserHandlerType; | 87 | private Integer assignStartUserHandlerType; |
| 88 | 88 | ||
| 89 | + /** | ||
| 90 | + * 空处理策略 | ||
| 91 | + */ | ||
| 92 | + private AssignEmptyHandler assignEmptyHandler; | ||
| 93 | + | ||
| 89 | @Data | 94 | @Data |
| 90 | @Schema(description = "审批节点拒绝处理策略") | 95 | @Schema(description = "审批节点拒绝处理策略") |
| 91 | public static class RejectHandler { | 96 | public static class RejectHandler { |
| @@ -122,6 +127,21 @@ public class BpmSimpleModelNodeVO { | @@ -122,6 +127,21 @@ public class BpmSimpleModelNodeVO { | ||
| 122 | } | 127 | } |
| 123 | 128 | ||
| 124 | @Data | 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 | @Schema(description = "操作按钮设置") | 145 | @Schema(description = "操作按钮设置") |
| 126 | public static class OperationButtonSetting { | 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 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | 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 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; | 5 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| 4 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; | 6 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; |
| 5 | import lombok.Setter; | 7 | import lombok.Setter; |
| @@ -49,6 +51,10 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav | @@ -49,6 +51,10 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav | ||
| 49 | 51 | ||
| 50 | // 第二步,获取任务的所有处理人 | 52 | // 第二步,获取任务的所有处理人 |
| 51 | Set<Long> assigneeUserIds = taskCandidateInvoker.calculateUsers(execution); | 53 | Set<Long> assigneeUserIds = taskCandidateInvoker.calculateUsers(execution); |
| 54 | + if (CollUtil.isEmpty(assigneeUserIds)) { | ||
| 55 | + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务,避免自动通过! | ||
| 56 | + assigneeUserIds = SetUtils.asSet((Long) null); | ||
| 57 | + } | ||
| 52 | execution.setVariable(super.collectionVariable, assigneeUserIds); | 58 | execution.setVariable(super.collectionVariable, assigneeUserIds); |
| 53 | return assigneeUserIds.size(); | 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 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | 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 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; | 5 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| 4 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; | 6 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; |
| 5 | import lombok.Setter; | 7 | import lombok.Setter; |
| @@ -43,6 +45,10 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB | @@ -43,6 +45,10 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB | ||
| 43 | 45 | ||
| 44 | // 第二步,获取任务的所有处理人 | 46 | // 第二步,获取任务的所有处理人 |
| 45 | Set<Long> assigneeUserIds = new LinkedHashSet<>(taskCandidateInvoker.calculateUsers(execution)); // 保证有序!!! | 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 | execution.setVariable(super.collectionVariable, assigneeUserIds); | 52 | execution.setVariable(super.collectionVariable, assigneeUserIds); |
| 47 | return assigneeUserIds.size(); | 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 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.lang.Assert; | 4 | +import cn.hutool.core.util.ObjectUtil; |
| 5 | import cn.hutool.core.util.RandomUtil; | 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 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; | 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 | import lombok.Setter; | 14 | import lombok.Setter; |
| 8 | import lombok.extern.slf4j.Slf4j; | 15 | import lombok.extern.slf4j.Slf4j; |
| 9 | import org.flowable.bpmn.model.UserTask; | 16 | import org.flowable.bpmn.model.UserTask; |
| @@ -14,6 +21,8 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; | @@ -14,6 +21,8 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; | ||
| 14 | import org.flowable.engine.impl.util.TaskHelper; | 21 | import org.flowable.engine.impl.util.TaskHelper; |
| 15 | import org.flowable.task.service.TaskService; | 22 | import org.flowable.task.service.TaskService; |
| 16 | import org.flowable.task.service.impl.persistence.entity.TaskEntity; | 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 | import java.util.List; | 27 | import java.util.List; |
| 19 | import java.util.Set; | 28 | import java.util.Set; |
| @@ -41,9 +50,29 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | @@ -41,9 +50,29 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | ||
| 41 | DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { | 50 | DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { |
| 42 | // 第一步,获得任务的候选用户 | 51 | // 第一步,获得任务的候选用户 |
| 43 | Long assigneeUserId = calculateTaskCandidateUsers(execution); | 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 | private Long calculateTaskCandidateUsers(DelegateExecution execution) { | 78 | private Long calculateTaskCandidateUsers(DelegateExecution execution) { |
| @@ -56,6 +85,9 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | @@ -56,6 +85,9 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | ||
| 56 | // 情况二,如果非多实例的任务,则计算任务处理人 | 85 | // 情况二,如果非多实例的任务,则计算任务处理人 |
| 57 | // 第一步,先计算可处理该任务的处理人们 | 86 | // 第一步,先计算可处理该任务的处理人们 |
| 58 | Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsers(execution); | 87 | Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsers(execution); |
| 88 | + if (CollUtil.isEmpty(candidateUserIds)) { | ||
| 89 | + return null; | ||
| 90 | + } | ||
| 59 | // 第二步,后随机选择一个任务的处理人 | 91 | // 第二步,后随机选择一个任务的处理人 |
| 60 | // 疑问:为什么一定要选择一个任务处理人? | 92 | // 疑问:为什么一定要选择一个任务处理人? |
| 61 | // 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。 | 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,7 +27,6 @@ import java.util.Set; | ||
| 27 | 27 | ||
| 28 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 28 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 29 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG; | 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 | * {@link BpmTaskCandidateStrategy} 的调用者,用于调用对应的策略,实现任务的候选人的计算 | 32 | * {@link BpmTaskCandidateStrategy} 的调用者,用于调用对应的策略,实现任务的候选人的计算 |
| @@ -89,17 +88,16 @@ public class BpmTaskCandidateInvoker { | @@ -89,17 +88,16 @@ public class BpmTaskCandidateInvoker { | ||
| 89 | String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement()); | 88 | String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement()); |
| 90 | // 1.1 计算任务的候选人 | 89 | // 1.1 计算任务的候选人 |
| 91 | Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param); | 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 | // 1.3 移除发起人的用户 | 96 | // 1.3 移除发起人的用户 |
| 95 | removeStartUserIfSkip(execution, userIds); | 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 | return userIds; | 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 | \ No newline at end of file | 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,6 +30,7 @@ public enum BpmTaskCandidateStrategyEnum implements IntArrayValuable { | ||
| 30 | START_USER_MULTI_LEVEL_DEPT_LEADER(38, "发起人连续多级部门的负责人"), | 30 | START_USER_MULTI_LEVEL_DEPT_LEADER(38, "发起人连续多级部门的负责人"), |
| 31 | USER_GROUP(40, "用户组"), | 31 | USER_GROUP(40, "用户组"), |
| 32 | EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager | 32 | EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager |
| 33 | + ASSIGN_EMPTY(1, "审批人为空"), | ||
| 33 | ; | 34 | ; |
| 34 | 35 | ||
| 35 | public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmTaskCandidateStrategyEnum::getStrategy).toArray(); | 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,6 +39,15 @@ public interface BpmnModelConstants { | ||
| 39 | String USER_TASK_ASSIGN_START_USER_HANDLER_TYPE = "assignStartUserHandlerType"; | 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 | * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型 | 51 | * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型 |
| 43 | */ | 52 | */ |
| 44 | String USER_TASK_REJECT_HANDLER_TYPE = "rejectHandlerType"; | 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,6 +5,7 @@ import cn.hutool.core.map.MapUtil; | ||
| 5 | import cn.hutool.core.util.ArrayUtil; | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | import cn.hutool.core.util.StrUtil; | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 7 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 8 | +import cn.iocoder.yudao.framework.common.util.string.StrUtils; | ||
| 8 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; | 9 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; |
| 9 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; | 10 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; |
| 10 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | 11 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| @@ -45,16 +46,24 @@ public class BpmnModelUtils { | @@ -45,16 +46,24 @@ public class BpmnModelUtils { | ||
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) { | 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 | return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); | 50 | return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | public static String parseReturnTaskId(FlowElement flowElement) { | 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 | public static Integer parseAssignStartUserHandlerType(FlowElement userTask) { | 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 | public static String parseExtensionElement(FlowElement flowElement, String elementName) { | 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,10 +19,7 @@ import org.flowable.bpmn.BpmnAutoLayout; | ||
| 19 | import org.flowable.bpmn.model.Process; | 19 | import org.flowable.bpmn.model.Process; |
| 20 | import org.flowable.bpmn.model.*; | 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 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting; | 24 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting; |
| 28 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; | 25 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; |
| @@ -456,7 +453,6 @@ public class SimpleModelUtils { | @@ -456,7 +453,6 @@ public class SimpleModelUtils { | ||
| 456 | userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); | 453 | userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); |
| 457 | } | 454 | } |
| 458 | 455 | ||
| 459 | - // TODO @jason:addCandidateElements、processMultiInstanceLoopCharacteristics 建议一起搞哈? | ||
| 460 | // 添加候选人元素 | 456 | // 添加候选人元素 |
| 461 | addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask); | 457 | addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask); |
| 462 | // 添加表单字段权限属性元素 | 458 | // 添加表单字段权限属性元素 |
| @@ -469,6 +465,8 @@ public class SimpleModelUtils { | @@ -469,6 +465,8 @@ public class SimpleModelUtils { | ||
| 469 | addTaskRejectElements(node.getRejectHandler(), userTask); | 465 | addTaskRejectElements(node.getRejectHandler(), userTask); |
| 470 | // 添加用户任务的审批人与发起人相同时的处理元素 | 466 | // 添加用户任务的审批人与发起人相同时的处理元素 |
| 471 | addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask); | 467 | addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask); |
| 468 | + // 添加用户任务的空处理元素 | ||
| 469 | + addAssignEmptyHandlerType(node.getAssignEmptyHandler(), userTask); | ||
| 472 | return userTask; | 470 | return userTask; |
| 473 | } | 471 | } |
| 474 | 472 | ||
| @@ -487,6 +485,14 @@ public class SimpleModelUtils { | @@ -487,6 +485,14 @@ public class SimpleModelUtils { | ||
| 487 | addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString()); | 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 | private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) { | 496 | private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) { |
| 491 | BpmApproveMethodEnum bpmApproveMethodEnum = BpmApproveMethodEnum.valueOf(approveMethod); | 497 | BpmApproveMethodEnum bpmApproveMethodEnum = BpmApproveMethodEnum.valueOf(approveMethod); |
| 492 | if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.RANDOM) { | 498 | if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.RANDOM) { |
| @@ -496,7 +502,7 @@ public class SimpleModelUtils { | @@ -496,7 +502,7 @@ public class SimpleModelUtils { | ||
| 496 | addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_METHOD, | 502 | addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_METHOD, |
| 497 | approveMethod == null ? null : approveMethod.toString()); | 503 | approveMethod == null ? null : approveMethod.toString()); |
| 498 | MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); | 504 | MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); |
| 499 | - // 设置 collectionVariable。本系统用不到。会在 仅仅为了校验。 | 505 | + // 设置 collectionVariable。本系统用不到。仅仅为了 Flowable 校验不报错。 |
| 500 | multiInstanceCharacteristics.setInputDataItem("${coll_userList}"); | 506 | multiInstanceCharacteristics.setInputDataItem("${coll_userList}"); |
| 501 | if (bpmApproveMethodEnum == BpmApproveMethodEnum.ANY) { | 507 | if (bpmApproveMethodEnum == BpmApproveMethodEnum.ANY) { |
| 502 | multiInstanceCharacteristics.setCompletionCondition(ANY_OF_APPROVE_COMPLETE_EXPRESSION); | 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,7 +191,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 191 | */ | 191 | */ |
| 192 | private Task validateTask(Long userId, String taskId) { | 192 | private Task validateTask(Long userId, String taskId) { |
| 193 | Task task = validateTaskExist(taskId); | 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 | throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); | 198 | throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); |
| 196 | } | 199 | } |
| 197 | return task; | 200 | return task; |