Commit 0d738fa3976c61fdd840f695af5f51f3991a7832

Authored by YunaiV
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;