瀏覽代碼

【代码重构】工作流:BpmTaskCandidateStrategy 拆分成 calculateUsers、calculateUsersByTask、calculateUsersByActivity,定位更明确

YunaiV 7 月之前
父節點
當前提交
c73d7fe32b
共有 41 個文件被更改,包括 813 次插入399 次删除
  1. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java
  2. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
  3. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
  4. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java
  5. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java
  6. 50 16
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
  7. 20 23
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java
  8. 0 37
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractStrategy.java
  9. 0 92
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java
  10. 20 7
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java
  11. 11 11
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java
  12. 5 9
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java
  13. 7 8
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java
  14. 70 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java
  15. 12 32
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java
  16. 20 24
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java
  17. 21 14
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java
  18. 13 11
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java
  19. 6 10
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java
  20. 7 8
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java
  21. 2 7
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java
  22. 10 15
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java
  23. 6 7
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java
  24. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java
  25. 7 13
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
  26. 34 8
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
  27. 0 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
  28. 4 4
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java
  29. 43 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java
  30. 10 9
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java
  31. 15 11
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java
  32. 82 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java
  33. 82 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java
  34. 68 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java
  35. 88 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java
  36. 24 4
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java
  37. 3 3
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java
  38. 3 3
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java
  39. 3 3
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java
  40. 56 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java
  41. 5 4
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java

@@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConver
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept.BpmTaskCandidateStartUserSelectStrategy;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java

@@ -17,7 +17,7 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConver
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
 import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceStatusEvent;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept.BpmTaskCandidateStartUserSelectStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java

@@ -53,7 +53,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
         @SuppressWarnings("unchecked")
         Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
         if (assigneeUserIds == null) {
-            assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
+            assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
             execution.setVariable(super.collectionVariable, assigneeUserIds);
             if (CollUtil.isEmpty(assigneeUserIds)) {
                 // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java

@@ -46,7 +46,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB
         @SuppressWarnings("unchecked")
         Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
         if (assigneeUserIds == null) {
-            assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
+            assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
             execution.setVariable(super.collectionVariable, assigneeUserIds);
             if (CollUtil.isEmpty(assigneeUserIds)) {
                 // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java

@@ -57,7 +57,7 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
 
         // 情况二,如果非多实例的任务,则计算任务处理人
         // 第一步,先计算可处理该任务的处理人们
-        Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsers(execution);
+        Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
         if (CollUtil.isEmpty(candidateUserIds)) {
             return null;
         }

+ 50 - 16
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java

@@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import com.google.common.annotations.VisibleForTesting;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowElement;
 import org.flowable.bpmn.model.UserTask;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.flowable.engine.runtime.ProcessInstance;
@@ -89,31 +90,66 @@ public class BpmTaskCandidateInvoker {
      * @return 用户编号集合
      */
     @DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人
-    public Set<Long> calculateUsers(DelegateExecution execution) {
+    public Set<Long> calculateUsersByTask(DelegateExecution execution) {
         // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过
-        Integer approveType = BpmnModelUtils.parseApproveType(execution.getCurrentFlowElement());
+        FlowElement flowElement = execution.getCurrentFlowElement();
+        Integer approveType = BpmnModelUtils.parseApproveType(flowElement);
         if (ObjectUtils.equalsAny(approveType,
                 BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),
                 BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
             return new HashSet<>();
         }
 
-        Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
-        String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
+        Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);
+        String param = BpmnModelUtils.parseCandidateParam(flowElement);
         // 1.1 计算任务的候选人
-        Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
-        // 1.2 移除被禁用的用户 TODO @芋艿 在 calculateUsers 方法中默认已经移除了被禁用的用户, 这里还需要移除被禁用的用户吗?
+        Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param);
+        // 1.2 移除被禁用的用户
         removeDisableUsers(userIds);
 
         // 2. 候选人为空时,根据“审批人为空”的配置补充
         if (CollUtil.isEmpty(userIds)) {
             userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())
-                    .calculateUsers(execution, param);
+                    .calculateUsersByTask(execution, param);
             // ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!!
         }
 
         // 3. 移除发起人的用户
-        removeStartUserIfSkip(execution, userIds);
+        ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
+                .getProcessInstance(execution.getProcessInstanceId());
+        Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
+        removeStartUserIfSkip(userIds, flowElement, Long.valueOf(processInstance.getStartUserId()));
+        return userIds;
+    }
+
+    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId,
+                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
+        // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过
+        FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);
+        Integer approveType = BpmnModelUtils.parseApproveType(flowElement);
+        if (ObjectUtils.equalsAny(approveType,
+                BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(),
+                BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
+            return new HashSet<>();
+        }
+
+        Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement);
+        String param = BpmnModelUtils.parseCandidateParam(flowElement);
+        // 1.1 计算任务的候选人
+        Set<Long> userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param,
+                startUserId, processDefinitionId, processVariables);
+        // 1.2 移除被禁用的用户
+        removeDisableUsers(userIds);
+
+        // 2. 候选人为空时,根据“审批人为空”的配置补充
+        if (CollUtil.isEmpty(userIds)) {
+            userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy())
+                    .calculateUsersByActivity(bpmnModel, activityId, param, startUserId, processDefinitionId, processVariables);
+            // ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!!
+        }
+
+        // 3. 移除发起人的用户
+        removeStartUserIfSkip(userIds, flowElement, startUserId);
         return userIds;
     }
 
@@ -134,25 +170,23 @@ public class BpmTaskCandidateInvoker {
      *
      * 注意:如果只有一个候选人,则不处理,避免无法审批
      *
-     * @param execution 执行中的任务
      * @param assigneeUserIds 当前分配的候选人
+     * @param flowElement 当前节点
+     * @param startUserId 发起人
      */
     @VisibleForTesting
-    void removeStartUserIfSkip(DelegateExecution execution, Set<Long> assigneeUserIds) {
+    void removeStartUserIfSkip(Set<Long> assigneeUserIds, FlowElement flowElement, Long startUserId) {
         if (CollUtil.size(assigneeUserIds) <= 1) {
             return;
         }
-        Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(execution.getCurrentFlowElement());
+        Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(flowElement);
         if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
             return;
         }
-        ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
-                .getProcessInstance(execution.getProcessInstanceId());
-        Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
-        assigneeUserIds.remove(Long.valueOf(processInstance.getStartUserId()));
+        assigneeUserIds.remove(startUserId);
     }
 
-    public BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
+    private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
         BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
         Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
         BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);

+ 20 - 23
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java

@@ -1,10 +1,10 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
 
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
+import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.engine.delegate.DelegateExecution;
-import org.flowable.engine.runtime.ProcessInstance;
 
-import java.util.Collections;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -42,47 +42,44 @@ public interface BpmTaskCandidateStrategy {
     /**
      * 基于候选人参数,获得任务的候选用户们
      *
+     * 注意:实现 calculateUsers 系列方法时,有两种选择:
+     * 1. 只重写 calculateUsers 默认方法
+     * 2. 都重写 calculateUsersByTask 和 calculateUsersByActivity 两个方法
+     *
      * @param param 执行任务
      * @return 用户编号集合
      */
     default Set<Long> calculateUsers(String param) {
-        return Collections.emptySet();
+        throw new UnsupportedOperationException("该分配方法未实现,请检查!");
     }
 
     /**
-     * 基于执行任务,获得任务的候选用户们
+     * 基于执行任务,获得任务的候选用户们
      *
      * @param execution 执行任务
      * @return 用户编号集合
      */
-    default Set<Long> calculateUsers(DelegateExecution execution, String param) {
-        Set<Long> users = calculateUsers(param);
-        removeDisableUsers(users);
-        return users;
+    default Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
+        return calculateUsers(param);
     }
 
     /**
-     * 基于流程实例,获得任务的候选用户们
+     * 基于【流程活动】,获得任务的候选用户们
      * <p>
      * 目的:用于获取未执行节点的候选用户们
      *
-     * @param startUserId  流程发起人编号
-     * @param processInstance 流程实例编号
-     * @param activityId 活动 Id (对应 Bpmn XML id)
+     * @param bpmnModel 流程图
+     * @param activityId 活动 ID (对应 Bpmn XML id)
      * @param param     节点的参数
+     * @param startUserId  流程发起人编号
+     * @param processDefinitionId 流程定义编号
+     * @param processVariables 流程变量
      * @return 用户编号集合
      */
-    default Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
-        Set<Long> users = calculateUsers(param);
-        removeDisableUsers(users);
-        return users;
+    @SuppressWarnings("unused")
+    default Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
+                                               Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
+        return calculateUsers(param);
     }
 
-    /**
-     * 移除被禁用的用户
-     *
-     * @param users 用户 Ids
-     */
-    void removeDisableUsers(Set<Long> users);
-
 }

+ 0 - 37
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractStrategy.java

@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * {@link BpmTaskCandidateStrategy} 抽象类
- *
- * @author jason
- */
-public abstract class BpmTaskCandidateAbstractStrategy implements BpmTaskCandidateStrategy {
-
-    protected AdminUserApi adminUserApi;
-
-    public BpmTaskCandidateAbstractStrategy(AdminUserApi adminUserApi) {
-        this.adminUserApi = adminUserApi;
-    }
-
-    @Override
-    public void removeDisableUsers(Set<Long> users) {
-        if (CollUtil.isEmpty(users)) {
-            return;
-        }
-        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(users);
-        users.removeIf(id -> {
-            AdminUserRespDTO user = userMap.get(id);
-            return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
-        });
-    }
-
-}

+ 0 - 92
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java

@@ -1,92 +0,0 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
-
-import cn.hutool.core.lang.Assert;
-import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
-import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
-import cn.iocoder.yudao.module.system.api.dept.DeptApi;
-import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import jakarta.annotation.Resource;
-import org.flowable.engine.delegate.DelegateExecution;
-import org.flowable.engine.runtime.ProcessInstance;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Component;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import static cn.hutool.core.collection.ListUtil.toList;
-
-/**
- * 发起人连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类
- *
- * @author jason
- */
-@Component
-public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy {
-
-    @Resource
-    @Lazy
-    private BpmProcessInstanceService processInstanceService;
-
-    public BpmTaskCandidateStartUserDeptLeaderMultiStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
-        super(adminUserApi, deptApi);
-    }
-
-    @Override
-    public BpmTaskCandidateStrategyEnum getStrategy() {
-        return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI;
-    }
-
-    @Override
-    public void validateParam(String param) {
-        // 参数是部门的层级
-        Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0");
-    }
-
-    @Override
-    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
-        // 获得流程发起人
-        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
-        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
-        // 获取发起人的 multi 部门负责人
-        DeptRespDTO dept = getStartUserDept(startUserId);
-        if (dept == null) {
-            return new HashSet<>();
-        }
-        Set<Long> users = getMultiLevelDeptLeaderIds(toList(dept.getId()), Integer.valueOf(param)); // 参数是部门的层级
-        // TODO @jason:这里 removeDisableUsers 的原因是啥呀?
-        // TODO @芋艿 calculateUsers(execution, param)  calculateUsers(startUserId, processInstance, activityId, param) 现在这两个方法, 默认都移除了被禁用的用户
-        // TODO @芋艿 因为被禁用的用户是不能审批任务。 在这里移除是不是好一点。 代码可以重用。
-        removeDisableUsers(users);
-        return users;
-    }
-
-    @Override
-    public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
-        DeptRespDTO dept = getStartUserDept(startUserId);
-        if (dept == null) {
-            return new HashSet<>();
-        }
-        Set<Long> users =  getMultiLevelDeptLeaderIds(toList(dept.getId()), Integer.valueOf(param)); // 参数是部门的层级
-        removeDisableUsers(users);
-        return users;
-    }
-
-    /**
-     * 获取发起人的部门
-     *
-     * @param startUserId 发起人 Id
-     */
-    protected DeptRespDTO getStartUserDept(Long startUserId) {
-        AdminUserRespDTO startUser = adminUserApi.getUser(startUserId);
-        if (startUser.getDeptId() == null) { // 找不到部门
-            return null;
-        }
-        return deptApi.getDept(startUser.getDeptId());
-    }
-
-}

+ 20 - 7
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractDeptLeaderStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
@@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCand
 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import jakarta.annotation.Resource;
 
 import java.util.*;
 
@@ -14,14 +16,12 @@ import java.util.*;
  *
  * @author jason
  */
-public abstract class BpmTaskCandidateAbstractDeptLeaderStrategy extends  BpmTaskCandidateAbstractStrategy {
+public abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {
 
+    @Resource
     protected DeptApi deptApi;
-
-    public BpmTaskCandidateAbstractDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
-        super(adminUserApi);
-        this.deptApi = deptApi;
-    }
+    @Resource
+    protected AdminUserApi adminUserApi;
 
     /**
      * 获得指定层级的部门负责人,只有第 level 的负责人
@@ -75,4 +75,17 @@ public abstract class BpmTaskCandidateAbstractDeptLeaderStrategy extends  BpmTas
         return deptLeaderIds;
     }
 
+    /**
+     * 获取发起人的部门
+     *
+     * @param startUserId 发起人 Id
+     */
+    protected DeptRespDTO getStartUserDept(Long startUserId) {
+        AdminUserRespDTO startUser = adminUserApi.getUser(startUserId);
+        if (startUser.getDeptId() == null) { // 找不到部门
+            return null;
+        }
+        return deptApi.getDept(startUser.getDeptId());
+    }
+
 }

+ 11 - 11
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderMultiStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java

@@ -1,13 +1,12 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
 
 import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
-import cn.iocoder.yudao.module.system.api.dept.DeptApi;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -16,11 +15,7 @@ import java.util.Set;
  * @author jason
  */
 @Component
-public class BpmTaskCandidateDeptLeaderMultiStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy {
-
-    public BpmTaskCandidateDeptLeaderMultiStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
-        super(adminUserApi, deptApi);
-    }
+public class BpmTaskCandidateDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
 
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {
@@ -32,14 +27,19 @@ public class BpmTaskCandidateDeptLeaderMultiStrategy extends BpmTaskCandidateAbs
         // 参数格式: | 分隔:1)左边为部门(多个部门用 , 分隔)。2)右边为部门层级
         String[] params = param.split("\\|");
         Assert.isTrue(params.length == 2, "参数格式不匹配");
-        deptApi.validateDeptList(StrUtils.splitToLong(params[0], ","));
-        Assert.isTrue(Integer.parseInt(params[1]) > 0, "部门层级必须大于 0");
+        List<Long> deptIds = StrUtils.splitToLong(params[0], ",");
+        int level = Integer.parseInt(params[1]);
+        // 校验部门存在
+        deptApi.validateDeptList(deptIds);
+        Assert.isTrue(level > 0, "部门层级必须大于 0");
     }
 
     @Override
     public Set<Long> calculateUsers(String param) {
         String[] params = param.split("\\|");
-        return getMultiLevelDeptLeaderIds(StrUtils.splitToLong(params[0], ","), Integer.valueOf(params[1]));
+        List<Long> deptIds = StrUtils.splitToLong(params[0], ",");
+        int level = Integer.parseInt(params[1]);
+        return super.getMultiLevelDeptLeaderIds(deptIds, level);
     }
 
 }

+ 5 - 9
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java

@@ -1,11 +1,11 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
 
 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import jakarta.annotation.Resource;
 import org.springframework.stereotype.Component;
 
 import java.util.List;
@@ -19,14 +19,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
  * @author kyle
  */
 @Component
-public class BpmTaskCandidateDeptLeaderStrategy extends BpmTaskCandidateAbstractStrategy {
+public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {
 
-    private final DeptApi deptApi;
-
-    public BpmTaskCandidateDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
-        super(adminUserApi);
-        this.deptApi = deptApi;
-    }
+    @Resource
+    private DeptApi deptApi;
 
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {

+ 7 - 8
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptMemberStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
 
 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidat
 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import jakarta.annotation.Resource;
 import org.springframework.stereotype.Component;
 
 import java.util.List;
@@ -19,14 +20,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
  * @author kyle
  */
 @Component
-public class BpmTaskCandidateDeptMemberStrategy extends BpmTaskCandidateAbstractStrategy {
+public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrategy {
 
-    private final DeptApi deptApi;
-
-    public BpmTaskCandidateDeptMemberStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
-        super(adminUserApi);
-        this.deptApi = deptApi;
-    }
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    private AdminUserApi adminUserApi;
 
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {

+ 70 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java

@@ -0,0 +1,70 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
+
+import cn.hutool.core.lang.Assert;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import jakarta.annotation.Resource;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static cn.hutool.core.collection.ListUtil.toList;
+
+/**
+ * 发起人连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类
+ *
+ * @author jason
+ */
+@Component
+public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
+
+    @Resource
+    @Lazy
+    private BpmProcessInstanceService processInstanceService;
+
+    @Override
+    public BpmTaskCandidateStrategyEnum getStrategy() {
+        return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI;
+    }
+
+    @Override
+    public void validateParam(String param) {
+        int level = Integer.parseInt(param); // 参数是部门的层级
+        Assert.isTrue(level > 0, "部门的层级必须大于 0");
+    }
+
+    @Override
+    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
+        int level = Integer.parseInt(param); // 参数是部门的层级
+        // 获得流程发起人
+        ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
+        Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
+        // 获取发起人的 multi 部门负责人
+        DeptRespDTO dept = super.getStartUserDept(startUserId);
+        if (dept == null) {
+            return new HashSet<>();
+        }
+        return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level);
+    }
+
+    @Override
+    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
+                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
+        int level = Integer.parseInt(param); // 参数是部门的层级
+        DeptRespDTO dept = super.getStartUserDept(startUserId);
+        if (dept == null) {
+            return new HashSet<>();
+        }
+        return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level);
+    }
+
+}

+ 12 - 32
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java

@@ -1,21 +1,20 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
 
 import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
-import cn.iocoder.yudao.module.system.api.dept.DeptApi;
 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import jakarta.annotation.Resource;
+import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
@@ -26,7 +25,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  * @author jason
  */
 @Component
-public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy {
+public class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
 
     @Resource
     @Lazy // 避免循环依赖
@@ -37,10 +36,6 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidat
         return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER;
     }
 
-    public BpmTaskCandidateStartUserDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) {
-        super(adminUserApi, deptApi);
-    }
-
     @Override
     public void validateParam(String param) {
         // 参数是部门的层级
@@ -48,44 +43,29 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidat
     }
 
     @Override
-    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
         // 获得流程发起人
         ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
         Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
         // 获取发起人的部门负责人
-        Set<Long> users = getStartUserDeptLeader(startUserId, param);
-        removeDisableUsers(users);
-        return users;
+        return getStartUserDeptLeader(startUserId, param);
     }
 
     @Override
-    public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
+    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
+                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
         // 获取发起人的部门负责人
-        Set<Long> users =  getStartUserDeptLeader(startUserId, param);
-        removeDisableUsers(users);
-        return users;
+        return getStartUserDeptLeader(startUserId, param);
     }
 
     private Set<Long> getStartUserDeptLeader(Long startUserId, String param) {
-        DeptRespDTO dept = getStartUserDept(startUserId);
+        int level = Integer.parseInt(param); // 参数是部门的层级
+        DeptRespDTO dept = super.getStartUserDept(startUserId);
         if (dept == null) {
             return new HashSet<>();
         }
-        Long deptLeaderId = getAssignLevelDeptLeaderId(dept, Integer.valueOf(param)); // 参数是部门的层级
+        Long deptLeaderId = super.getAssignLevelDeptLeaderId(dept, level);
         return deptLeaderId != null ? asSet(deptLeaderId) : new HashSet<>();
     }
 
-    /**
-     * 获取发起人的部门
-     *
-     * @param startUserId 发起人 Id
-     */
-    protected DeptRespDTO getStartUserDept(Long startUserId) {
-        AdminUserRespDTO startUser = adminUserApi.getUser(startUserId);
-        if (startUser.getDeptId() == null) { // 找不到部门
-            return null;
-        }
-        return deptApi.getDept(startUser.getDeptId());
-    }
-
 }

+ 20 - 24
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserSelectStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java

@@ -1,12 +1,13 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import com.google.common.collect.Sets;
 import jakarta.annotation.Resource;
 import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.bpmn.model.UserTask;
@@ -23,16 +24,12 @@ import java.util.*;
  * @author 芋道源码
  */
 @Component
-public class BpmTaskCandidateStartUserSelectStrategy extends BpmTaskCandidateAbstractStrategy {
+public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy {
 
     @Resource
     @Lazy // 延迟加载,避免循环依赖
     private BpmProcessInstanceService processInstanceService;
 
-    public BpmTaskCandidateStartUserSelectStrategy(AdminUserApi adminUserApi) {
-        super(adminUserApi);
-    }
-
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {
         return BpmTaskCandidateStrategyEnum.START_USER_SELECT;
@@ -42,7 +39,12 @@ public class BpmTaskCandidateStartUserSelectStrategy extends BpmTaskCandidateAbs
     public void validateParam(String param) {}
 
     @Override
-    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+    public boolean isParamRequired() {
+        return false;
+    }
+
+    @Override
+    public LinkedHashSet<Long> calculateUsersByTask(DelegateExecution execution, String param) {
         ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
         Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId());
         Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
@@ -50,28 +52,22 @@ public class BpmTaskCandidateStartUserSelectStrategy extends BpmTaskCandidateAbs
                 execution.getProcessInstanceId());
         // 获得审批人
         List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
-        Set<Long> users = new LinkedHashSet<>(assignees);
-        removeDisableUsers(users);
-        return users;
+        return new LinkedHashSet<>(assignees);
     }
 
     @Override
-    public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
-        if (processInstance == null) {
-            return Collections.emptySet();
+    public LinkedHashSet<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
+                                                        Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
+        if (processVariables == null) {
+            return Sets.newLinkedHashSet();
+        }
+        Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processVariables);
+        if (startUserSelectAssignees == null) {
+            return Sets.newLinkedHashSet();
         }
-        Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
-        Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", processInstance.getId());
         // 获得审批人
         List<Long> assignees = startUserSelectAssignees.get(activityId);
-        Set<Long> users = new LinkedHashSet<>(assignees);
-        removeDisableUsers(users);
-        return users;
-    }
-
-    @Override
-    public boolean isParamRequired() {
-        return false;
+        return new LinkedHashSet<>(assignees);
     }
 
     /**

+ 21 - 14
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAssignEmptyStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
 
 import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
@@ -7,13 +7,15 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCand
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import jakarta.annotation.Resource;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowElement;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -23,16 +25,12 @@ import java.util.Set;
  * @author kyle
  */
 @Component
-public class BpmTaskCandidateAssignEmptyStrategy extends BpmTaskCandidateAbstractStrategy {
+public class BpmTaskCandidateAssignEmptyStrategy implements BpmTaskCandidateStrategy {
 
     @Resource
     @Lazy // 延迟加载,避免循环依赖
     private BpmProcessDefinitionService processDefinitionService;
 
-    public BpmTaskCandidateAssignEmptyStrategy(AdminUserApi adminUserApi) {
-        super(adminUserApi);
-    }
-
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {
         return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY;
@@ -43,19 +41,28 @@ public class BpmTaskCandidateAssignEmptyStrategy extends BpmTaskCandidateAbstrac
     }
 
     @Override
-    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
+        return getCandidateUsers(execution.getProcessDefinitionId(), execution.getCurrentFlowElement());
+    }
+
+    @Override
+    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
+                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
+        FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId);
+        return getCandidateUsers(processDefinitionId, flowElement);
+    }
+
+    private Set<Long> getCandidateUsers(String processDefinitionId, FlowElement flowElement) {
         // 情况一:指定人员审批
-        Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(execution.getCurrentFlowElement());
+        Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(flowElement);
         if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType())) {
-            Set<Long> users = new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(execution.getCurrentFlowElement()));
-            removeDisableUsers(users);
-            return users;
+            return new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(flowElement));
         }
 
         // 情况二:流程管理员
         if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) {
-            BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId());
-            Assert.notNull(processDefinition, "流程定义({})不存在", execution.getProcessDefinitionId());
+            BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processDefinitionId);
+            Assert.notNull(processDefinition, "流程定义({})不存在", processDefinitionId);
             return new HashSet<>(processDefinition.getManagerUserIds());
         }
 

+ 13 - 11
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateExpressionStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java

@@ -1,13 +1,14 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
 
 import cn.hutool.core.convert.Convert;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.springframework.stereotype.Component;
 
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -16,11 +17,7 @@ import java.util.Set;
  * @author 芋道源码
  */
 @Component
-public class BpmTaskCandidateExpressionStrategy extends BpmTaskCandidateAbstractStrategy {
-
-    public BpmTaskCandidateExpressionStrategy(AdminUserApi adminUserApi) {
-        super(adminUserApi);
-    }
+public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy {
 
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {
@@ -33,11 +30,16 @@ public class BpmTaskCandidateExpressionStrategy extends BpmTaskCandidateAbstract
     }
 
     @Override
-    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
         Object result = FlowableUtils.getExpressionValue(execution, param);
-        Set<Long> users = Convert.toSet(Long.class, result);
-        removeDisableUsers(users);
-        return users;
+        return Convert.toSet(Long.class, result);
+    }
+
+    @Override
+    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
+                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
+        Object result = FlowableUtils.getExpressionValue(processVariables, param);
+        return Convert.toSet(Long.class, result);
     }
 
 }

+ 6 - 10
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateGroupStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java

@@ -1,11 +1,11 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
 
 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import jakarta.annotation.Resource;
 import org.springframework.stereotype.Component;
 
 import java.util.Collection;
@@ -20,14 +20,10 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
  * @author kyle
  */
 @Component
-public class BpmTaskCandidateGroupStrategy extends BpmTaskCandidateAbstractStrategy {
+public class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy {
 
-    private final BpmUserGroupService userGroupService;
-
-    public BpmTaskCandidateGroupStrategy(AdminUserApi adminUserApi, BpmUserGroupService userGroupService) {
-        super(adminUserApi);
-        this.userGroupService = userGroupService;
-    }
+    @Resource
+    private BpmUserGroupService userGroupService;
 
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {
@@ -37,7 +33,7 @@ public class BpmTaskCandidateGroupStrategy extends BpmTaskCandidateAbstractStrat
     @Override
     public void validateParam(String param) {
         Set<Long> groupIds = StrUtils.splitToLongSet(param);
-        userGroupService.getUserGroupList(groupIds);
+        userGroupService.validUserGroups(groupIds);
     }
 
     @Override

+ 7 - 8
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidatePostStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
 
 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidat
 import cn.iocoder.yudao.module.system.api.dept.PostApi;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import jakarta.annotation.Resource;
 import org.springframework.stereotype.Component;
 
 import java.util.List;
@@ -19,14 +20,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
  * @author kyle
  */
 @Component
-public class BpmTaskCandidatePostStrategy extends BpmTaskCandidateAbstractStrategy {
+public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy {
 
-    private final PostApi postApi;
-
-    public BpmTaskCandidatePostStrategy(AdminUserApi adminUserApi, PostApi postApi) {
-        super(adminUserApi);
-        this.postApi = postApi;
-    }
+    @Resource
+    private PostApi postApi;
+    @Resource
+    private AdminUserApi adminUserApi;
 
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {

+ 2 - 7
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java

@@ -1,11 +1,10 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
 
 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
 import cn.iocoder.yudao.module.system.api.permission.RoleApi;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Component;
 
@@ -17,17 +16,13 @@ import java.util.Set;
  * @author kyle
  */
 @Component
-public class BpmTaskCandidateRoleStrategy extends BpmTaskCandidateAbstractStrategy {
+public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy {
 
     @Resource
     private RoleApi roleApi;
     @Resource
     private PermissionApi permissionApi;
 
-    public BpmTaskCandidateRoleStrategy(AdminUserApi adminUserApi) {
-        super(adminUserApi);
-    }
-
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {
         return BpmTaskCandidateStrategyEnum.ROLE;

+ 10 - 15
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java

@@ -1,15 +1,17 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
 
 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import jakarta.annotation.Resource;
+import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -20,16 +22,12 @@ import java.util.Set;
  * @author jason
  */
 @Component
-public class BpmTaskCandidateStartUserStrategy extends BpmTaskCandidateAbstractStrategy {
+public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrategy {
 
     @Resource
     @Lazy // 延迟加载,避免循环依赖
     private BpmProcessInstanceService processInstanceService;
 
-    public BpmTaskCandidateStartUserStrategy(AdminUserApi adminUserApi) {
-        super(adminUserApi);
-    }
-
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {
         return BpmTaskCandidateStrategyEnum.START_USER;
@@ -45,18 +43,15 @@ public class BpmTaskCandidateStartUserStrategy extends BpmTaskCandidateAbstractS
     }
 
     @Override
-    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+    public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
         ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
-        Set<Long> users =  SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));
-        removeDisableUsers(users);
-        return users;
+        return SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));
     }
 
     @Override
-    public Set<Long> calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) {
-        Set<Long> users = SetUtils.asSet(startUserId);
-        removeDisableUsers(users);
-        return users;
+    public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
+                                              Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
+        return SetUtils.asSet(startUserId);
     }
 
 }

+ 6 - 7
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateUserStrategy.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java

@@ -1,14 +1,14 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
 
 import cn.hutool.core.text.StrPool;
 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import jakarta.annotation.Resource;
 import org.springframework.stereotype.Component;
 
 import java.util.LinkedHashSet;
-import java.util.Set;
 
 /**
  * 用户 {@link BpmTaskCandidateStrategy} 实现类
@@ -16,11 +16,10 @@ import java.util.Set;
  * @author kyle
  */
 @Component
-public class BpmTaskCandidateUserStrategy extends BpmTaskCandidateAbstractStrategy {
+public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy {
 
-    public BpmTaskCandidateUserStrategy(AdminUserApi adminUserApi) {
-        super(adminUserApi);
-    }
+    @Resource
+    private AdminUserApi adminUserApi;
 
     @Override
     public BpmTaskCandidateStrategyEnum getStrategy() {
@@ -33,7 +32,7 @@ public class BpmTaskCandidateUserStrategy extends BpmTaskCandidateAbstractStrate
     }
 
     @Override
-    public Set<Long> calculateUsers(String param) {
+    public LinkedHashSet<Long> calculateUsers(String param) {
         return new LinkedHashSet<>(StrUtils.splitToLong(param, StrPool.COMMA));
     }
 

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java

@@ -34,7 +34,7 @@ public class BpmCopyTaskDelegate implements JavaDelegate {
     @Override
     public void execute(DelegateExecution execution) {
         // 1. 获得抄送人
-        Set<Long> userIds = taskCandidateInvoker.calculateUsers(execution);
+        Set<Long> userIds = taskCandidateInvoker.calculateUsersByTask(execution);
         if (CollUtil.isEmpty(userIds)) {
             return;
         }

+ 7 - 13
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java

@@ -5,7 +5,6 @@ import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
@@ -23,7 +22,6 @@ import org.flowable.bpmn.model.Process;
 import org.flowable.bpmn.model.*;
 import org.flowable.common.engine.api.FlowableException;
 import org.flowable.common.engine.impl.util.io.BytesStreamSource;
-import org.flowable.engine.ManagementService;
 
 import java.util.*;
 
@@ -773,20 +771,16 @@ public class BpmnModelUtils {
      * @return 是否满足条件
      */
     public static boolean evalConditionExpress(Map<String, Object> variables, String express) {
-        ManagementService managementService = SpringUtil.getBean(ManagementService.class);
         if (express == null) {
             return Boolean.FALSE;
         }
-        // TODO @jason:疑问,为啥这里要在 managementService 里执行哈?
-        Object result = managementService.executeCommand(context -> {
-            try {
-                return FlowableUtils.getExpressionValue(variables, express);
-            } catch (FlowableException ex) {
-                log.error("[evalConditionExpress][条件表达式({}) 解析报错", express, ex);
-                return Boolean.FALSE;
-            }
-        });
-        return Boolean.TRUE.equals(result);
+        try {
+            Object result = FlowableUtils.getExpressionValue(variables, express);
+            return Boolean.TRUE.equals(result);
+        } catch (FlowableException ex) {
+            log.error("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错", express, variables, ex);
+            return Boolean.FALSE;
+        }
     }
 
     @SuppressWarnings("PatternVariableCanBeUsed")

+ 34 - 8
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
 
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
@@ -9,6 +10,7 @@ import org.flowable.common.engine.api.variable.VariableContainer;
 import org.flowable.common.engine.impl.el.ExpressionManager;
 import org.flowable.common.engine.impl.identity.Authentication;
 import org.flowable.common.engine.impl.variable.MapDelegateVariableContainer;
+import org.flowable.engine.ManagementService;
 import org.flowable.engine.ProcessEngineConfiguration;
 import org.flowable.engine.history.HistoricProcessInstance;
 import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
@@ -146,9 +148,22 @@ public class FlowableUtils {
      * @param processInstance 流程实例
      * @return 发起用户选择的审批人 Map
      */
-    @SuppressWarnings("unchecked")
     public static Map<String, List<Long>> getStartUserSelectAssignees(ProcessInstance processInstance) {
-        return (Map<String, List<Long>>) processInstance.getProcessVariables().get(
+        return processInstance != null ? getStartUserSelectAssignees(processInstance.getProcessVariables()) : null;
+    }
+
+    /**
+     * 获得流程实例的发起用户选择的审批人 Map
+     *
+     * @param processVariables 流程变量
+     * @return 发起用户选择的审批人 Map
+     */
+    @SuppressWarnings("unchecked")
+    public static Map<String, List<Long>> getStartUserSelectAssignees(Map<String, Object> processVariables) {
+        if (processVariables == null) {
+            return null;
+        }
+        return (Map<String, List<Long>>) processVariables.get(
                 BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
     }
 
@@ -202,18 +217,29 @@ public class FlowableUtils {
 
     // ========== Expression 相关的工具方法 ==========
 
-    public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {
-        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
-        assert processEngineConfiguration != null;
+    private static Object getExpressionValue(VariableContainer variableContainer, String expressionString,
+                                             ProcessEngineConfigurationImpl processEngineConfiguration) {
+        assert processEngineConfiguration!= null;
         ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
-        assert expressionManager != null;
+        assert expressionManager!= null;
         Expression expression = expressionManager.createExpression(expressionString);
         return expression.getValue(variableContainer);
     }
 
+    public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {
+        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
+        if (processEngineConfiguration != null) {
+            return getExpressionValue(variableContainer, expressionString, processEngineConfiguration);
+        }
+        // 如果 ProcessEngineConfigurationImpl 获取不到,则需要通过 ManagementService 来获取
+        ManagementService managementService = SpringUtil.getBean(ManagementService.class);
+        assert managementService != null;
+        return managementService.executeCommand(context ->
+                getExpressionValue(variableContainer, expressionString, CommandContextUtil.getProcessEngineConfiguration()));
+    }
+
     public static Object getExpressionValue(Map<String, Object> variable, String expressionString) {
-        VariableContainer variableContainer = new MapDelegateVariableContainer(variable,
-                VariableContainer.empty());
+        VariableContainer variableContainer = new MapDelegateVariableContainer(variable, VariableContainer.empty());
         return getExpressionValue(variableContainer, expressionString);
     }
 

文件差異過大導致無法顯示
+ 0 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java


+ 4 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
 import cn.hutool.core.map.MapUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateUserStrategy;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -47,13 +47,13 @@ public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {
 
     @BeforeEach
     public void setUp() {
-        strategy = new BpmTaskCandidateUserStrategy(adminUserApi); // 创建strategy实例
+        strategy = new BpmTaskCandidateUserStrategy(); // 创建strategy实例
         strategyList = Collections.singletonList(strategy); // 创建strategyList
         taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi);
     }
 
     @Test
-    public void testCalculateUsers() {
+    public void testCalculateUsersByTask() {
         // 准备参数
         String param = "1,2";
         DelegateExecution execution = mock(DelegateExecution.class);
@@ -74,7 +74,7 @@ public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {
         when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
 
         // 调用
-        Set<Long> results = taskCandidateInvoker.calculateUsers(execution);
+        Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);
         // 断言
         assertEquals(asSet(1L, 2L), results);
     }

+ 43 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java

@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import org.assertj.core.util.Sets;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.stubbing.Answer;
+
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidateDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateDeptLeaderMultiStrategy strategy;
+
+    @Mock
+    private DeptApi deptApi;
+
+    @Test
+    public void testCalculateUsers() {
+        // 准备参数
+        String param = "10,20|2";
+        // mock 方法
+        when(deptApi.getDept(any())).thenAnswer((Answer<DeptRespDTO>) invocationOnMock -> {
+            Long deptId = invocationOnMock.getArgument(0);
+            return randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1));
+        });
+
+        // 调用
+        Set<Long> userIds = strategy.calculateUsers(param);
+        // 断言结果
+        assertEquals(Sets.newLinkedHashSet(11L, 1001L, 21L, 2001L), userIds);
+    }
+
+}

+ 10 - 9
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderStrategyTest.java → yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java

@@ -1,15 +1,16 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
 
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import org.assertj.core.util.Sets;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 
 import java.util.Set;
 
-import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
 import static java.util.Arrays.asList;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -27,16 +28,16 @@ public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest
     @Test
     public void testCalculateUsers() {
         // 准备参数
-        String param = "1,2";
+        String param = "10,20";
         // mock 方法
-        DeptRespDTO dept1 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(11L));
-        DeptRespDTO dept2 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(22L));
-        when(deptApi.getDeptList(eq(asSet(1L, 2L)))).thenReturn(asList(dept1, dept2));
+        when(deptApi.getDeptList(eq(SetUtils.asSet(10L, 20L)))).thenReturn(asList(
+                randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(10L).setLeaderUserId(11L)),
+                randomPojo(DeptRespDTO.class, o -> o.setId(20L).setParentId(20L).setLeaderUserId(21L))));
 
         // 调用
-        Set<Long> results = strategy.calculateUsers(null, param);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
+        Set<Long> userIds = strategy.calculateUsers(param);
+        // 断言结果
+        assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds);
     }
 
 }

+ 15 - 11
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptMemberStrategyTest.java → yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java

@@ -1,17 +1,19 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
 
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import org.assertj.core.util.Sets;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 
-import java.util.List;
 import java.util.Set;
 
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static java.util.Arrays.asList;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
@@ -21,22 +23,24 @@ public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest
     @InjectMocks
     private BpmTaskCandidateDeptMemberStrategy strategy;
 
+    @Mock
+    private DeptApi deptApi;
     @Mock
     private AdminUserApi adminUserApi;
 
     @Test
     public void testCalculateUsers() {
         // 准备参数
-        String param = "11,22";
+        String param = "10,20";
         // mock 方法
-        List<AdminUserRespDTO> users = convertList(asSet(11L, 22L),
-                id -> new AdminUserRespDTO().setId(id));
-        when(adminUserApi.getUserListByDeptIds(eq(asSet(11L, 22L)))).thenReturn(users);
+        when(adminUserApi.getUserListByDeptIds(eq(SetUtils.asSet(10L, 20L)))).thenReturn(asList(
+                randomPojo(AdminUserRespDTO.class, o -> o.setId(11L)),
+                randomPojo(AdminUserRespDTO.class, o -> o.setId(21L))));
 
         // 调用
-        Set<Long> results = strategy.calculateUsers(null, param);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
+        Set<Long> userIds = strategy.calculateUsers(param);
+        // 断言结果
+        assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds);
     }
 
 }

+ 82 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java

@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import org.assertj.core.util.Sets;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.stubbing.Answer;
+
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateStartUserDeptLeaderMultiStrategy strategy;
+
+    @Mock
+    private BpmProcessInstanceService processInstanceService;
+
+    @Mock
+    private AdminUserApi adminUserApi;
+    @Mock
+    private DeptApi deptApi;
+
+    @Test
+    public void testCalculateUsersByTask() {
+        // 准备参数
+        String param = "2";
+        // mock 方法(获得流程发起人)
+        Long startUserId = 1L;
+        ProcessInstance processInstance = mock(ProcessInstance.class);
+        DelegateExecution execution = mock(DelegateExecution.class);
+        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
+        when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
+        // mock 方法(获取发起人的 multi 部门负责人)
+        mockGetStartUserDept(startUserId);
+
+        // 调用
+        Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
+        // 断言
+        assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);
+    }
+
+    @Test
+    public void testCalculateUsersByActivity() {
+        // 准备参数
+        String param = "2";
+        // mock 方法
+        Long startUserId = 1L;
+        mockGetStartUserDept(startUserId);
+
+        // 调用
+        Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
+                startUserId, null, null);
+        // 断言
+        assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);
+    }
+
+    private void mockGetStartUserDept(Long startUserId) {
+        when(adminUserApi.getUser(eq(startUserId))).thenReturn(
+                randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptId(10L)));
+        when(deptApi.getDept(any())).thenAnswer((Answer<DeptRespDTO>) invocationOnMock -> {
+            Long deptId = invocationOnMock.getArgument(0);
+            return randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1));
+        });
+    }
+
+}

+ 82 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java

@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import org.assertj.core.util.Sets;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.stubbing.Answer;
+
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateStartUserDeptLeaderStrategy strategy;
+
+    @Mock
+    private BpmProcessInstanceService processInstanceService;
+
+    @Mock
+    private AdminUserApi adminUserApi;
+    @Mock
+    private DeptApi deptApi;
+
+    @Test
+    public void testCalculateUsersByTask() {
+        // 准备参数
+        String param = "2";
+        // mock 方法(获得流程发起人)
+        Long startUserId = 1L;
+        ProcessInstance processInstance = mock(ProcessInstance.class);
+        DelegateExecution execution = mock(DelegateExecution.class);
+        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
+        when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
+        // mock 方法(获取发起人的部门负责人)
+        mockGetStartUserDeptLeader(startUserId);
+
+        // 调用
+        Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
+        // 断言
+        assertEquals(Sets.newLinkedHashSet(1001L), userIds);
+    }
+
+    @Test
+    public void testGetStartUserDeptLeader() {
+        // 准备参数
+        String param = "2";
+        // mock 方法
+        Long startUserId = 1L;
+        mockGetStartUserDeptLeader(startUserId);
+
+        // 调用
+        Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
+                startUserId, null, null);
+        // 断言
+        assertEquals(Sets.newLinkedHashSet(1001L), userIds);
+    }
+
+    private void mockGetStartUserDeptLeader(Long startUserId) {
+        when(adminUserApi.getUser(eq(startUserId))).thenReturn(
+                randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptId(10L)));
+        when(deptApi.getDept(any())).thenAnswer((Answer<DeptRespDTO>) invocationOnMock -> {
+            Long deptId = invocationOnMock.getArgument(0);
+            return randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1));
+        });
+    }
+
+}

+ 68 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java

@@ -0,0 +1,68 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
+
+import cn.hutool.core.map.MapUtil;
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import org.assertj.core.util.Sets;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidateStartUserSelectStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateStartUserSelectStrategy strategy;
+
+    @Mock
+    private BpmProcessInstanceService processInstanceService;
+
+    @Test
+    public void testCalculateUsersByTask() {
+        // 准备参数
+        String param = "2";
+        // mock 方法(获得流程发起人)
+        ProcessInstance processInstance = mock(ProcessInstance.class);
+        DelegateExecution execution = mock(DelegateExecution.class);
+        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
+        when(execution.getCurrentActivityId()).thenReturn("activity_001");
+        // mock 方法(FlowableUtils)
+        Map<String, Object> processVariables = new HashMap<>();
+        processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
+                MapUtil.of("activity_001", List.of(1L, 2L)));
+        when(processInstance.getProcessVariables()).thenReturn(processVariables);
+
+        // 调用
+        Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
+        // 断言
+        assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds);
+    }
+
+    @Test
+    public void testCalculateUsersByActivity() {
+        // 准备参数
+        String activityId = "activity_001";
+        Map<String, Object> processVariables = new HashMap<>();
+        processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
+                MapUtil.of("activity_001", List.of(1L, 2L)));
+
+        // 调用
+        Set<Long> userIds = strategy.calculateUsersByActivity(null, activityId, null,
+                null, null, processVariables);
+        // 断言
+        assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds);
+    }
+
+}

+ 88 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java

@@ -0,0 +1,88 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
+
+import cn.hutool.core.collection.ListUtil;
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.*;
+
+public class BpmTaskCandidateAssignEmptyStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateAssignEmptyStrategy strategy;
+
+    @Mock
+    private BpmProcessDefinitionService processDefinitionService;
+
+    @Test
+    public void testCalculateUsersByTask() {
+        try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class);
+             MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
+            // 准备参数
+            DelegateExecution execution = mock(DelegateExecution.class);
+            String param = randomString();
+            // mock 方法(execution)
+            String processDefinitionId = randomString();
+            when(execution.getProcessDefinitionId()).thenReturn(processDefinitionId);
+            FlowElement flowElement = mock(FlowElement.class);
+            when(execution.getCurrentFlowElement()).thenReturn(flowElement);
+            // mock 方法(parseAssignEmptyHandlerType)
+            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement)))
+                    .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType());
+            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerUserIds(same(flowElement)))
+                    .thenReturn(ListUtil.of(1L, 2L));
+
+            // 调用
+            Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
+            // 断言
+            assertEquals(SetUtils.asSet(1L, 2L), userIds);
+        }
+
+    }
+
+    @Test
+    public void testCalculateUsersByActivity() {
+        try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
+            // 准备参数
+            String processDefinitionId = randomString();
+            String activityId = randomString();
+            String param = randomString();
+            // mock 方法(getFlowElementById)
+            FlowElement flowElement = mock(FlowElement.class);
+            BpmnModel bpmnModel = mock(BpmnModel.class);
+            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId)))
+                    .thenReturn(flowElement);
+            // mock 方法(parseAssignEmptyHandlerType)
+            bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement)))
+                 .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType());
+            // mock 方法(getProcessDefinitionInfo)
+            BpmProcessDefinitionInfoDO processDefinition = randomPojo(BpmProcessDefinitionInfoDO.class,
+                    o -> o.setManagerUserIds(ListUtil.of(1L, 2L)));
+            when(processDefinitionService.getProcessDefinitionInfo(eq(processDefinitionId))).thenReturn(processDefinition);
+
+            // 调用
+            Set<Long> userIds = strategy.calculateUsersByActivity(bpmnModel, activityId, param,
+                    null, processDefinitionId, null);
+            // 断言
+            assertEquals(SetUtils.asSet(1L, 2L), userIds);
+        }
+    }
+
+}

+ 24 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateExpressionStrategyTest.java → yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java

@@ -1,12 +1,14 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
 
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.MockedStatic;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
@@ -20,7 +22,7 @@ public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest
     private BpmTaskCandidateExpressionStrategy strategy;
 
     @Test
-    public void testCalculateUsers() {
+    public void testCalculateUsersByTask() {
         try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {
             // 准备参数
             String param = "1,2";
@@ -30,7 +32,25 @@ public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest
                     .thenReturn(asSet(1L, 2L));
 
             // 调用
-            Set<Long> results = strategy.calculateUsers(execution, param);
+            Set<Long> results = strategy.calculateUsersByTask(execution, param);
+            // 断言
+            assertEquals(asSet(1L, 2L), results);
+        }
+    }
+
+    @Test
+    public void testCalculateUsersByActivity() {
+        try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {
+            // 准备参数
+            String param = "1,2";
+            Map<String, Object> processVariables = new HashMap<>();
+            // mock 方法
+            flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(processVariables), eq(param)))
+                    .thenReturn(asSet(1L, 2L));
+
+            // 调用
+            Set<Long> results = strategy.calculateUsersByActivity(null, null, param,
+                    null, null, processVariables);
             // 断言
             assertEquals(asSet(1L, 2L), results);
         }

+ 3 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateGroupStrategyTest.java → yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
 
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
@@ -34,9 +34,9 @@ public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest {
         when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2));
 
         // 调用
-        Set<Long> results = strategy.calculateUsers(null, param);
+        Set<Long> userIds = strategy.calculateUsersByTask(null, param);
         // 断言
-        assertEquals(asSet(11L, 12L, 21L, 22L), results);
+        assertEquals(asSet(11L, 12L, 21L, 22L), userIds);
     }
 
 }

+ 3 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidatePostStrategyTest.java → yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
 
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import cn.iocoder.yudao.module.system.api.dept.PostApi;
@@ -37,9 +37,9 @@ public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest {
         when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(users);
 
         // 调用
-        Set<Long> results = strategy.calculateUsers(null, param);
+        Set<Long> userIds = strategy.calculateUsersByTask(null, param);
         // 断言
-        assertEquals(asSet(11L, 22L), results);
+        assertEquals(asSet(11L, 22L), userIds);
     }
 
 }

+ 3 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategyTest.java → yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
 
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
@@ -33,9 +33,9 @@ public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest {
             .thenReturn(asSet(11L, 22L));
 
         // 调用
-        Set<Long> results = strategy.calculateUsers(null, param);
+        Set<Long> userIds = strategy.calculateUsersByTask(null, param);
         // 断言
-        assertEquals(asSet(11L, 22L), results);
+        assertEquals(asSet(11L, 22L), userIds);
     }
 
 }

+ 56 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java

@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import org.assertj.core.util.Sets;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidateStartUserStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateStartUserStrategy strategy;
+
+    @Mock
+    private BpmProcessInstanceService processInstanceService;
+
+    @Test
+    public void testCalculateUsersByTask() {
+        // 准备参数
+        String param = "2";
+        // mock 方法(获得流程发起人)
+        Long startUserId = 1L;
+        ProcessInstance processInstance = mock(ProcessInstance.class);
+        DelegateExecution execution = mock(DelegateExecution.class);
+        when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
+        when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
+
+        // 调用
+        Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
+        // 断言
+        assertEquals(Sets.newLinkedHashSet(startUserId), userIds);
+    }
+
+    @Test
+    public void testCalculateUsersByActivity() {
+        // 准备参数
+        Long startUserId = 1L;
+
+        // 调用
+        Set<Long> userIds = strategy.calculateUsersByActivity(null, null, null,
+                startUserId, null, null);
+        // 断言
+        assertEquals(Sets.newLinkedHashSet(startUserId), userIds);
+    }
+
+}

+ 5 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateUserStrategyTest.java → yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
 
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import org.junit.jupiter.api.Test;
@@ -15,14 +15,15 @@ public class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest {
     private BpmTaskCandidateUserStrategy strategy;
 
     @Test
-    public void testCalculateUsers() {
+    public void test() {
         // 准备参数
         String param = "1,2";
 
         // 调用
-        Set<Long> results = strategy.calculateUsers(null, param);
+        Set<Long> userIds = strategy.calculateUsersByTask(null, param);
         // 断言
-        assertEquals(asSet(1L, 2L), results);
+        assertEquals(asSet(1L, 2L), userIds);
     }
 
+
 }

部分文件因文件數量過多而無法顯示