Browse Source

BPM:重构流程分配人的实现,通过 BpmTaskCandidateStrategy 策略模式

YunaiV 1 year ago
parent
commit
f5f73adcbb
55 changed files with 981 additions and 1598 deletions
  1. 1 1
      pom.xml
  2. 5 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
  3. 1 1
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
  4. 0 35
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTaskAssignRuleTypeEnum.java
  5. 0 30
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTaskRuleScriptEnum.java
  6. 0 52
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmCandidateProcessorConfiguration.java
  7. 17 4
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java
  8. 5 11
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java
  9. 3 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
  10. 3 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java
  11. 0 36
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/BpmTaskAssignScript.java
  12. 0 28
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java
  13. 0 28
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java
  14. 134 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
  15. 39 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java
  16. 8 9
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java
  17. 5 16
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java
  18. 46 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderStrategy.java
  19. 49 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptMemberStrategy.java
  20. 36 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateExpressionStrategy.java
  21. 47 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateGroupStrategy.java
  22. 49 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidatePostStrategy.java
  23. 44 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategy.java
  24. 39 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateUserStrategy.java
  25. 40 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java
  26. 24 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
  27. 0 45
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java
  28. 0 53
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessor.java
  29. 0 107
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChain.java
  30. 0 32
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateAdminUserApiSourceInfoProcessor.java
  31. 0 50
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateDeptApiSourceInfoProcessor.java
  32. 0 40
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidatePostApiSourceInfoProcessor.java
  33. 0 37
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateRoleApiSourceInfoProcessor.java
  34. 0 73
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateScriptApiSourceInfoProcessor.java
  35. 0 41
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateUserGroupApiSourceInfoProcessor.java
  36. 4 2
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
  37. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
  38. 0 30
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleService.java
  39. 0 254
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
  40. 0 9
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyService.java
  41. 0 68
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyServiceImpl.java
  42. 93 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java
  43. 9 9
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java
  44. 42 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderStrategyTest.java
  45. 42 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptMemberStrategyTest.java
  46. 39 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateExpressionStrategyTest.java
  47. 42 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateGroupStrategyTest.java
  48. 45 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidatePostStrategyTest.java
  49. 41 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategyTest.java
  50. 28 0
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateUserStrategyTest.java
  51. 0 244
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChainTest.java
  52. 0 227
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImplTest.java
  53. 0 17
      yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/enums/BpmnModelConstants.java
  54. 0 1
      yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/package-info.java
  55. 0 1
      yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/package-info.java

+ 1 - 1
pom.xml

@@ -16,7 +16,7 @@
         <module>yudao-module-system</module>
         <module>yudao-module-infra</module>
 <!--        <module>yudao-module-member</module>-->
-<!--        <module>yudao-module-bpm</module>-->
+        <module>yudao-module-bpm</module>
 <!--        <module>yudao-module-report</module>-->
 <!--        <module>yudao-module-mp</module>-->
 <!--        <module>yudao-module-pay</module>-->

+ 5 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.framework.common.util.string;
 
+import cn.hutool.core.text.StrPool;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.StrUtil;
 
@@ -46,6 +47,10 @@ public class StrUtils {
         return Arrays.stream(longs).boxed().collect(Collectors.toList());
     }
 
+    public static Set<Long> splitToLongSet(String value) {
+        return splitToLongSet(value, StrPool.COMMA);
+    }
+
     public static Set<Long> splitToLongSet(String value, CharSequence separator) {
         long[] longs = StrUtil.splitToLong(value, separator);
         return Arrays.stream(longs).boxed().collect(Collectors.toSet());

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java

@@ -26,7 +26,7 @@ public interface ErrorCodeConstants {
     ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_009_002_001, "流程模型不存在");
     ErrorCode MODEL_KEY_VALID = new ErrorCode(1_009_002_002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
     ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置");
-    ErrorCode MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," +
+    ErrorCode MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," +
             "原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置");
     ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1_009_003_005, "流程定义部署失败,原因:信息未发生变化");
 

+ 0 - 35
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTaskAssignRuleTypeEnum.java

@@ -1,35 +0,0 @@
-package cn.iocoder.yudao.module.bpm.enums.definition;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * BPM 任务分配规则的类型枚举
- *
- * @author 芋道源码
- */
-@Getter
-@AllArgsConstructor
-public enum BpmTaskAssignRuleTypeEnum {
-
-    ROLE(10, "角色"),
-    DEPT_MEMBER(20, "部门的成员"), // 包括负责人
-    DEPT_LEADER(21, "部门的负责人"),
-    POST(22, "岗位"),
-    USER(30, "用户"),
-    USER_GROUP(40, "用户组"),
-    @Deprecated
-    SCRIPT(50, "自定义脚本"), // 例如说,发起人所在部门的领导、发起人所在部门的领导的领导
-    EXPRESS(60, "流程表达式"), // 表达式 ExpressionManager
-    ;
-
-    /**
-     * 类型
-     */
-    private final Integer type;
-    /**
-     * 描述
-     */
-    private final String desc;
-
-}

+ 0 - 30
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTaskRuleScriptEnum.java

@@ -1,30 +0,0 @@
-package cn.iocoder.yudao.module.bpm.enums.definition;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * BPM 任务规则的脚本枚举
- * 目前暂时通过 TODO 芋艿:硬编码,未来可以考虑 Groovy 动态脚本的方式
- *
- * @author 芋道源码
- */
-@Getter
-@AllArgsConstructor
-public enum BpmTaskRuleScriptEnum {
-
-    START_USER(10L, "流程发起人"),
-
-    LEADER_X1(20L, "流程发起人的一级领导"),
-    LEADER_X2(21L, "流程发起人的二级领导");
-
-    /**
-     * 脚本编号
-     */
-    private final Long id;
-    /**
-     * 脚本描述
-     */
-    private final String desc;
-
-}

+ 0 - 52
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmCandidateProcessorConfiguration.java

@@ -1,52 +0,0 @@
-package cn.iocoder.yudao.module.bpm.framework.bpm.config;
-
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
-import cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor.*;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-// TODO @芋艿:Candidate 相关还在完善中,用户可以暂时忽略,仅 yudao 开发的同学需要关注~计划是把 Candidate 和 Assign 融合成一套
-/**
- * BPM 通用的 Configuration 配置类,提供给 Activiti 和 Flowable
- * @author kyle
- */
-@Configuration(proxyBeanMethods = false)
-public class BpmCandidateProcessorConfiguration {
-    @Bean
-    public BpmCandidateAdminUserApiSourceInfoProcessor bpmCandidateAdminUserApiSourceInfoProcessor() {
-        return new BpmCandidateAdminUserApiSourceInfoProcessor();
-    }
-
-    @Bean
-    public BpmCandidateDeptApiSourceInfoProcessor bpmCandidateDeptApiSourceInfoProcessor() {
-        return new BpmCandidateDeptApiSourceInfoProcessor();
-    }
-
-    @Bean
-    public BpmCandidatePostApiSourceInfoProcessor bpmCandidatePostApiSourceInfoProcessor() {
-        return new BpmCandidatePostApiSourceInfoProcessor();
-    }
-
-    @Bean
-    public BpmCandidateRoleApiSourceInfoProcessor bpmCandidateRoleApiSourceInfoProcessor() {
-        return new BpmCandidateRoleApiSourceInfoProcessor();
-    }
-
-    @Bean
-    public BpmCandidateUserGroupApiSourceInfoProcessor bpmCandidateUserGroupApiSourceInfoProcessor() {
-        return new BpmCandidateUserGroupApiSourceInfoProcessor();
-    }
-
-    /**
-     * 可以自己定制脚本,然后通过这里设置到处理器里面去
-     * @param scriptsOp 脚本包装对象
-     * @return
-     */
-    @Bean
-    public BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor(ObjectProvider<BpmTaskAssignScript> scriptsOp) {
-        BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor = new BpmCandidateScriptApiSourceInfoProcessor();
-        bpmCandidateScriptApiSourceInfoProcessor.setScripts(scriptsOp);
-        return bpmCandidateScriptApiSourceInfoProcessor;
-    }
-}

+ 17 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java

@@ -2,7 +2,9 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.config;
 
 import cn.hutool.core.collection.ListUtil;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory;
-import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
 import org.flowable.spring.SpringProcessEngineConfiguration;
 import org.flowable.spring.boot.EngineConfigurationConfigurer;
@@ -10,6 +12,8 @@ import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+import java.util.List;
+
 /**
  * BPM 模块的 Flowable 配置类
  *
@@ -36,11 +40,20 @@ public class BpmFlowableConfiguration {
         };
     }
 
+    // =========== 审批人相关的 Bean ==========
+
     @Bean
-    public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskAssignRuleService taskRuleService) {
+    public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskCandidateInvoker bpmTaskCandidateInvoker) {
         BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory();
-        bpmActivityBehaviorFactory.setBpmTaskRuleService(taskRuleService);
+        bpmActivityBehaviorFactory.setTaskCandidateInvoker(bpmTaskCandidateInvoker);
         return bpmActivityBehaviorFactory;
     }
 
-}
+    @Bean
+    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") // adminUserApi 可以注入成功
+    public BpmTaskCandidateInvoker bpmTaskCandidateInvoker(List<BpmTaskCandidateStrategy> strategyList,
+                                                           AdminUserApi adminUserApi) {
+        return new BpmTaskCandidateInvoker(strategyList, adminUserApi);
+    }
+
+}

+ 5 - 11
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java

@@ -1,10 +1,7 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
 
-import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
 import lombok.Setter;
-import lombok.ToString;
 import org.flowable.bpmn.model.Activity;
 import org.flowable.bpmn.model.UserTask;
 import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
@@ -18,25 +15,22 @@ import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFacto
  *
  * @author 芋道源码
  */
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
+@Setter
 public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {
 
-    @Setter
-    private BpmTaskAssignRuleService bpmTaskRuleService;
+    private BpmTaskCandidateInvoker taskCandidateInvoker;
 
     @Override
     public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
         return new BpmUserTaskActivityBehavior(userTask)
-                .setBpmTaskRuleService(bpmTaskRuleService);
+                .setTaskCandidateInvoker(taskCandidateInvoker);
     }
 
     @Override
     public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity,
                                                                              AbstractBpmnActivityBehavior innerActivityBehavior) {
         return new BpmParallelMultiInstanceBehavior(activity, innerActivityBehavior)
-                .setBpmTaskRuleService(bpmTaskRuleService);
+                .setTaskCandidateInvoker(taskCandidateInvoker);
     }
 
     // TODO @ke:SequentialMultiInstanceBehavior 这个抽空也可以看看

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

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
 
 import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
-import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
 import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.bpmn.model.Activity;
@@ -23,7 +23,7 @@ import java.util.Set;
 public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior {
 
     @Setter
-    private BpmTaskAssignRuleService bpmTaskRuleService;
+    private BpmTaskCandidateInvoker taskCandidateInvoker;
 
     public BpmParallelMultiInstanceBehavior(Activity activity,
                                             AbstractBpmnActivityBehavior innerActivityBehavior) {
@@ -50,7 +50,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
         super.collectionElementVariable = FlowableUtils.formatCollectionElementVariable(execution.getCurrentActivityId());
 
         // 第二步,获取任务的所有处理人
-        Set<Long> assigneeUserIds = bpmTaskRuleService.calculateTaskCandidateUsers(execution);
+        Set<Long> assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
         execution.setVariable(super.collectionVariable, assigneeUserIds);
         return assigneeUserIds.size();
     }

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

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.RandomUtil;
-import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
 import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.bpmn.model.UserTask;
@@ -29,7 +29,7 @@ import java.util.Set;
 public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
 
     @Setter
-    private BpmTaskAssignRuleService bpmTaskRuleService;
+    private BpmTaskCandidateInvoker taskCandidateInvoker;
 
     public BpmUserTaskActivityBehavior(UserTask userTask) {
         super(userTask);
@@ -54,7 +54,7 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
 
         // 情况二,如果非多实例的任务,则计算任务处理人
         // 第一步,先计算可处理该任务的处理人们
-        Set<Long> candidateUserIds = bpmTaskRuleService.calculateTaskCandidateUsers(execution);
+        Set<Long> candidateUserIds = taskCandidateInvoker.calculateUsers(execution);
         // 第二步,后随机选择一个任务的处理人
         // 疑问:为什么一定要选择一个任务处理人?
         // 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。

+ 0 - 36
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/BpmTaskAssignScript.java

@@ -1,36 +0,0 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script;
-
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
-import org.flowable.engine.delegate.DelegateExecution;
-
-import java.util.Set;
-
-/**
- * Bpm 任务分配的自定义 Script 脚本
- * 使用场景:
- * 1. 设置审批人为发起人
- * 2. 设置审批人为发起人的 Leader
- * 3. 甚至审批人为发起人的 Leader 的 Leader
- *
- * @author 芋道源码
- */
-@Deprecated
-public interface BpmTaskAssignScript {
-
-    /**
-     * 基于执行任务,获得任务的候选用户们
-     *
-     * @param execution 执行任务
-     * @return 候选人用户的编号数组
-     */
-    Set<Long> calculateTaskCandidateUsers(DelegateExecution execution);
-
-    /**
-     * 获得枚举值
-     *
-     * @return 枚举值
-     */
-    BpmTaskRuleScriptEnum getEnum();
-
-}
-

+ 0 - 28
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java

@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
-
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
-import org.flowable.engine.delegate.DelegateExecution;
-import org.springframework.stereotype.Component;
-
-import java.util.Set;
-
-/**
- * 分配给发起人的一级 Leader 审批的 Script 实现类
- *
- * @author 芋道源码
- */
-@Deprecated
-@Component
-public class BpmTaskAssignLeaderX1Script extends BpmTaskAssignLeaderAbstractScript {
-
-    @Override
-    public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
-        return calculateTaskCandidateUsers(execution, 1);
-    }
-
-    @Override
-    public BpmTaskRuleScriptEnum getEnum() {
-        return BpmTaskRuleScriptEnum.LEADER_X1;
-    }
-
-}

+ 0 - 28
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java

@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
-
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
-import org.flowable.engine.delegate.DelegateExecution;
-import org.springframework.stereotype.Component;
-
-import java.util.Set;
-
-/**
- * 分配给发起人的二级 Leader 审批的 Script 实现类
- *
- * @author 芋道源码
- */
-@Deprecated
-@Component
-public class BpmTaskAssignLeaderX2Script extends BpmTaskAssignLeaderAbstractScript {
-
-    @Override
-    public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
-        return calculateTaskCandidateUsers(execution, 2);
-    }
-
-    @Override
-    public BpmTaskRuleScriptEnum getEnum() {
-        return BpmTaskRuleScriptEnum.LEADER_X2;
-    }
-
-}

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

@@ -0,0 +1,134 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
+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;
+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 java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER;
+
+/**
+ * {@link BpmTaskCandidateStrategy} 的调用者,用于调用对应的策略,实现任务的候选人的计算
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class BpmTaskCandidateInvoker {
+
+    private final Map<BpmTaskCandidateStrategyEnum, BpmTaskCandidateStrategy> strategyMap = new HashMap<>();
+
+    private final AdminUserApi adminUserApi;
+
+    public BpmTaskCandidateInvoker(List<BpmTaskCandidateStrategy> strategyList,
+                                   AdminUserApi adminUserApi) {
+        strategyList.forEach(strategy -> {
+            BpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy);
+            Assert.isNull(oldStrategy, "策略(%s) 重复", strategy.getStrategy());
+        });
+        this.adminUserApi = adminUserApi;
+    }
+
+    /**
+     * 校验流程模型的任务分配规则全部都配置了
+     * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去!
+     *
+     * @param bpmnBytes BPMN XML
+     */
+    public void validateBpmnConfig(byte[] bpmnBytes) {
+        BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
+        assert bpmnModel != null;
+        List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
+        // 遍历所有的 UserTask,校验审批人配置
+        userTaskList.forEach(userTask -> {
+            // 1. 非空校验
+            Integer strategy = parseCandidateStrategy(userTask);
+            String param = parseCandidateParam(userTask);
+            if (strategy == null || StrUtil.isBlank(param)) {
+                throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
+            }
+            // 2. 具体策略校验
+            getCandidateStrategy(strategy).validateParam(param);
+        });
+    }
+
+    /**
+     * 计算任务的候选人
+     *
+     * @param execution 执行任务
+     * @return 用户编号集合
+     */
+    public Set<Long> calculateUsers(DelegateExecution execution) {
+        // TODO 芋艿:这里需要重构
+//        // 1. 先从提前选好的审批人中获取
+//        List<Long> assignee = processInstanceService.getAssigneeByProcessInstanceIdAndTaskDefinitionKey(
+//                execution.getProcessInstanceId(), execution.getCurrentActivityId());
+//        if (CollUtil.isNotEmpty(assignee)) {
+//            // TODO @hai:new HashSet 即可
+//            return convertSet(assignee, Function.identity());
+//        }
+        Integer strategy = parseCandidateStrategy(execution.getCurrentFlowElement());
+        String param = parseCandidateParam(execution.getCurrentFlowElement());
+        // 1.1 计算任务的候选人
+        Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
+        // 1.2 移除被禁用的用户
+        removeDisableUsers(userIds);
+
+        // 2. 校验是否有候选人
+        if (CollUtil.isEmpty(userIds)) {
+            log.error("[calculateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(),
+                    execution.getProcessDefinitionId(), execution.getCurrentActivityId(), strategy, param);
+            throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
+        }
+        return userIds;
+    }
+
+    @VisibleForTesting
+    void removeDisableUsers(Set<Long> assigneeUserIds) {
+        if (CollUtil.isEmpty(assigneeUserIds)) {
+            return;
+        }
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
+        assigneeUserIds.removeIf(id -> {
+            AdminUserRespDTO user = userMap.get(id);
+            return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
+        });
+    }
+
+    private static Integer parseCandidateStrategy(FlowElement userTask) {
+        return NumberUtils.parseInt(userTask.getAttributeValue(
+                BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
+    }
+
+    private static String parseCandidateParam(FlowElement userTask) {
+        return userTask.getAttributeValue(
+                BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
+    }
+
+    private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
+        BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
+        Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
+        BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);
+        Assert.notNull(strategyObj, "策略(%s) 不存在", strategy);
+        return strategyObj;
+    }
+
+}

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

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
+
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
+import org.flowable.engine.delegate.DelegateExecution;
+
+import java.util.Set;
+
+/**
+ * BPM 任务的候选人的策略接口
+ *
+ * 例如说:分配审批人
+ *
+ * @author 芋道源码
+ */
+public interface BpmTaskCandidateStrategy {
+
+    /**
+     * 对应策略
+     *
+     * @return 策略
+     */
+    BpmTaskCandidateStrategyEnum getStrategy();
+
+    /**
+     * 校验参数
+     *
+     * @param param 参数
+     */
+    void validateParam(String param);
+
+    /**
+     * 基于执行任务,获得任务的候选用户们
+     *
+     * @param execution 执行任务
+     * @return 用户编号集合
+     */
+    Set<Long> calculateUsers(DelegateExecution execution, String param);
+
+}

+ 8 - 9
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java

@@ -1,7 +1,6 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression;
 
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
 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;
@@ -9,7 +8,7 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.flowable.engine.runtime.ProcessInstance;
-import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
 import org.springframework.util.Assert;
 
 import jakarta.annotation.Resource;
@@ -19,23 +18,23 @@ import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
 import static java.util.Collections.emptySet;
 
 /**
- * 分配给发起人的 Leader 审批的 Script 实现类
- * 目前 Leader 的定义是,
+ * 分配给发起人的 Leader 审批的 Expression 流程表达式
+ * 目前 Leader 的定义是,发起人所在部门的 Leader
  *
  * @author 芋道源码
  */
-@Deprecated
-public abstract class BpmTaskAssignLeaderAbstractScript implements BpmTaskAssignScript {
+@Component
+public class BpmTaskAssignLeaderExpression {
 
     @Resource
     private AdminUserApi adminUserApi;
     @Resource
     private DeptApi deptApi;
+
     @Resource
-    @Lazy // 解决循环依赖
     private BpmProcessInstanceService bpmProcessInstanceService;
 
-    protected Set<Long> calculateTaskCandidateUsers(DelegateExecution execution, int level) {
+    protected Set<Long> calculateUsers(DelegateExecution execution, int level) {
         Assert.isTrue(level > 0, "level 必须大于 0");
         // 获得发起人
         ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());

+ 5 - 16
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.java → yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java

@@ -1,41 +1,30 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression;
 
 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+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 jakarta.annotation.Resource;
 import java.util.Set;
 
 /**
- * 分配给发起人审批的 Script 实现类
+ * 分配给发起人审批的 Expression 流程表达式
  *
  * @author 芋道源码
  */
-@Deprecated
 @Component
-public class BpmTaskAssignStartUserScript implements BpmTaskAssignScript {
+public class BpmTaskAssignStartUserExpression {
 
     @Resource
-    @Lazy // 解决循环依赖
     private BpmProcessInstanceService bpmProcessInstanceService;
 
-    @Override
-    public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
+    public Set<Long> calculateUsers(DelegateExecution execution) {
         ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId());
         Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
         return SetUtils.asSet(startUserId);
     }
 
-    @Override
-    public BpmTaskRuleScriptEnum getEnum() {
-        return BpmTaskRuleScriptEnum.START_USER;
-    }
-
 }

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

@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+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 jakarta.annotation.Resource;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+
+/**
+ * 部门的负责人 {@link BpmTaskCandidateStrategy} 实现类
+ *
+ * @author kyle
+ */
+@Component
+public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {
+
+    @Resource
+    private DeptApi deptApi;
+
+    @Override
+    public BpmTaskCandidateStrategyEnum getStrategy() {
+        return BpmTaskCandidateStrategyEnum.DEPT_LEADER;
+    }
+
+    @Override
+    public void validateParam(String param) {
+        Set<Long> deptIds = StrUtils.splitToLongSet(param);
+        deptApi.validateDeptList(deptIds);
+    }
+
+    @Override
+    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        Set<Long> deptIds = StrUtils.splitToLongSet(param);
+        List<DeptRespDTO> depts = deptApi.getDeptList(deptIds);
+        return convertSet(depts, DeptRespDTO::getLeaderUserId);
+    }
+
+}

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

@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+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 cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import jakarta.annotation.Resource;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+
+/**
+ * 部门的成员 {@link BpmTaskCandidateStrategy} 实现类
+ *
+ * @author kyle
+ */
+@Component
+public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrategy {
+
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    private AdminUserApi adminUserApi;
+
+    @Override
+    public BpmTaskCandidateStrategyEnum getStrategy() {
+        return BpmTaskCandidateStrategyEnum.DEPT_MEMBER;
+    }
+
+    @Override
+    public void validateParam(String param) {
+        Set<Long> deptIds = StrUtils.splitToLongSet(param);
+        deptApi.validateDeptList(deptIds);
+    }
+
+    @Override
+    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        Set<Long> deptIds = StrUtils.splitToLongSet(param);
+        List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(deptIds);
+        return convertSet(users, AdminUserRespDTO::getId);
+    }
+
+}

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

@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
+import org.dromara.hutool.core.convert.Convert;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 流程表达式 {@link BpmTaskCandidateStrategy} 实现类
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy {
+
+    @Override
+    public BpmTaskCandidateStrategyEnum getStrategy() {
+        return BpmTaskCandidateStrategyEnum.EXPRESSION;
+    }
+
+    @Override
+    public void validateParam(String param) {
+        // do nothing 因为它基本做不了校验
+    }
+
+    @Override
+    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        Object result = FlowableUtils.getExpressionValue(execution, param);
+        return Convert.toSet(Long.class, result);
+    }
+
+}

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

@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+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 jakarta.annotation.Resource;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
+
+/**
+ * 用户组 {@link BpmTaskCandidateStrategy} 实现类
+ *
+ * @author kyle
+ */
+@Component
+public class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy {
+
+    @Resource
+    private BpmUserGroupService userGroupService;
+
+    @Override
+    public BpmTaskCandidateStrategyEnum getStrategy() {
+        return BpmTaskCandidateStrategyEnum.USER_GROUP;
+    }
+
+    @Override
+    public void validateParam(String param) {
+        Set<Long> groupIds = StrUtils.splitToLongSet(param);
+        userGroupService.getUserGroupList(groupIds);
+    }
+
+    @Override
+    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        Set<Long> groupIds = StrUtils.splitToLongSet(param);
+        List<BpmUserGroupDO> groups = userGroupService.getUserGroupList(groupIds);
+        return convertSetByFlatMap(groups, BpmUserGroupDO::getMemberUserIds, Collection::stream);
+    }
+
+}

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

@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+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.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.flowable.engine.delegate.DelegateExecution;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+
+/**
+ * 岗位 {@link BpmTaskCandidateStrategy} 实现类
+ *
+ * @author kyle
+ */
+@Component
+public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy {
+
+    @Resource
+    private PostApi postApi;
+    @Resource
+    private AdminUserApi adminUserApi;
+
+    @Override
+    public BpmTaskCandidateStrategyEnum getStrategy() {
+        return BpmTaskCandidateStrategyEnum.POST;
+    }
+
+    @Override
+    public void validateParam(String param) {
+        Set<Long> postIds = StrUtils.splitToLongSet(param);
+        postApi.validPostList(postIds);
+    }
+
+    @Override
+    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        Set<Long> postIds = StrUtils.splitToLongSet(param);
+        List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(postIds);
+        return convertSet(users, AdminUserRespDTO::getId);
+    }
+
+}

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

@@ -0,0 +1,44 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+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 jakarta.annotation.Resource;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 角色 {@link BpmTaskCandidateStrategy} 实现类
+ *
+ * @author kyle
+ */
+@Component
+public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy {
+
+    @Resource
+    private RoleApi roleApi;
+    @Resource
+    private PermissionApi permissionApi;
+
+    @Override
+    public BpmTaskCandidateStrategyEnum getStrategy() {
+        return BpmTaskCandidateStrategyEnum.ROLE;
+    }
+
+    @Override
+    public void validateParam(String param) {
+        Set<Long> roleIds = StrUtils.splitToLongSet(param);
+        roleApi.validRoleList(roleIds);
+    }
+
+    @Override
+    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        Set<Long> roleIds = StrUtils.splitToLongSet(param);
+        return permissionApi.getUserRoleIdListByRoleIds(roleIds);
+    }
+
+}

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

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+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.flowable.engine.delegate.DelegateExecution;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 用户 {@link BpmTaskCandidateStrategy} 实现类
+ *
+ * @author kyle
+ */
+@Component
+public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy {
+
+    @Resource
+    private AdminUserApi adminUserApi;
+
+    @Override
+    public BpmTaskCandidateStrategyEnum getStrategy() {
+        return BpmTaskCandidateStrategyEnum.USER;
+    }
+
+    @Override
+    public void validateParam(String param) {
+        adminUserApi.validateUserList(StrUtils.splitToLongSet(param));
+    }
+
+    @Override
+    public Set<Long> calculateUsers(DelegateExecution execution, String param) {
+        return StrUtils.splitToLongSet(param);
+    }
+
+}

+ 40 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
+
+import cn.hutool.core.util.ArrayUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * BPM 任务的候选人策略枚举
+ *
+ * 例如说:分配给指定人审批
+ *
+ * @author 芋道源码
+ */
+@Getter
+@AllArgsConstructor
+public enum BpmTaskCandidateStrategyEnum {
+
+    ROLE(10, "角色"),
+    DEPT_MEMBER(20, "部门的成员"), // 包括负责人
+    DEPT_LEADER(21, "部门的负责人"),
+    POST(22, "岗位"),
+    USER(30, "用户"),
+    USER_GROUP(40, "用户组"),
+    EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager
+    ;
+
+    /**
+     * 类型
+     */
+    private final Integer strategy;
+    /**
+     * 描述
+     */
+    private final String description;
+
+    public static BpmTaskCandidateStrategyEnum valueOf(Integer strategy) {
+        return ArrayUtil.firstMatch(o -> o.getStrategy().equals(strategy), values());
+    }
+
+}

+ 24 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
+
+/**
+ * 流程常量信息
+ */
+public interface BpmnModelConstants {
+
+    String BPMN_FILE_SUFFIX = ".bpmn";
+
+    /**
+     * BPMN 中的命名空间
+     */
+    String NAMESPACE = "http://flowable.org/bpmn";
+
+    /**
+     * BPMN UserTask 的扩展属性,用于标记候选人策略
+     */
+    String USER_TASK_CANDIDATE_STRATEGY = "candidateStrategy";
+    /**
+     * BPMN UserTask 的扩展属性,用于标记候选人参数
+     */
+    String USER_TASK_CANDIDATE_PARAM = "candidateParam";
+
+}

+ 0 - 45
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java

@@ -1,45 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate;
-
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * 获取候选人信息
- */
-@AllArgsConstructor
-@NoArgsConstructor
-@Data
-public class BpmCandidateSourceInfo {
-    @Schema(description = "当前任务ID")
-    @NotNull
-    private String taskId;
-    /**
-     * 通过这些规则,生成最终需要生成的用户
-     */
-    @Schema(description = "当前任务预选规则")
-    @NotEmpty(message = "不允许空规则")
-    private Set<BpmTaskCandidateRuleVO> rules;
-
-    @Schema(description = "发起抄送的用户")
-    private String creator;
-
-    @Schema(description = "抄送原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "请帮忙审查下!")
-    @NotEmpty(message = "抄送原因不能为空")
-    private String reason;
-
-    public void addRule(BpmTaskCandidateRuleVO vo) {
-        assert vo != null;
-        if (rules == null) {
-            rules = new HashSet<>();
-        }
-        rules.add(vo);
-    }
-}

+ 0 - 53
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessor.java

@@ -1,53 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import org.flowable.engine.delegate.DelegateExecution;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-public interface BpmCandidateSourceInfoProcessor {
-    /**
-     * 获取该处理器支持的类型
-     * 来自 {@link BpmTaskAssignRuleTypeEnum}
-     *
-     * @return
-     */
-    Set<Integer> getSupportedTypes();
-
-    /**
-     * 对规则和人员做校验
-     *
-     * @param type    规则
-     * @param options 人员id
-     */
-    void validRuleOptions(Integer type, Set<Long> options);
-
-    /**
-     * 默认的处理
-     * 如果想去操作所有的规则,则可以覆盖此方法
-     *
-     * @param request           原始请求
-     * @param delegateExecution 审批过程中的对象
-     * @return 必须包含的是用户ID,而不是其他的ID
-     * @throws Exception
-     */
-    default Set<Long> process(BpmCandidateSourceInfo request, DelegateExecution delegateExecution) throws Exception {
-        Set<BpmTaskCandidateRuleVO> rules = request.getRules();
-        Set<Long> results = new HashSet<>();
-        for (BpmTaskCandidateRuleVO rule : rules) {
-            // 每个处理器都有机会处理自己支持的事件
-            if (CollUtil.contains(getSupportedTypes(), rule.getType())) {
-                results.addAll(doProcess(request, rule, delegateExecution));
-            }
-        }
-        return results;
-    }
-
-    default Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
-        return Collections.emptySet();
-    }
-}

+ 0 - 107
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChain.java

@@ -1,107 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.collection.ListUtil;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import org.flowable.engine.delegate.DelegateExecution;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.stereotype.Service;
-
-import jakarta.annotation.Resource;
-import java.util.*;
-
-@Service
-public class BpmCandidateSourceInfoProcessorChain {
-
-    // 保存处理节点
-
-    private List<BpmCandidateSourceInfoProcessor> processorList;
-    @Resource
-    private AdminUserApi adminUserApi;
-
-    /**
-     * 可添加其他处理器
-     *
-     * @param processorOp
-     * @return
-     */
-    @Resource
-    // 动态扩展处理节点
-    public BpmCandidateSourceInfoProcessorChain addProcessor(ObjectProvider<BpmCandidateSourceInfoProcessor> processorOp) {
-        List<BpmCandidateSourceInfoProcessor> processor = ListUtil.toList(processorOp.iterator());
-        if (null == processorList) {
-            processorList = new ArrayList<>(processor.size());
-        }
-        processorList.addAll(processor);
-        return this;
-    }
-
-    // 获取处理器处理
-    public Set<Long> process(BpmCandidateSourceInfo sourceInfo, DelegateExecution execution) throws Exception {
-        // Verify our parameters
-        if (sourceInfo == null) {
-            throw new IllegalArgumentException();
-        }
-        for (BpmCandidateSourceInfoProcessor processor : processorList) {
-            try {
-                for (BpmTaskCandidateRuleVO vo : sourceInfo.getRules()) {
-                    if (CollUtil.contains(processor.getSupportedTypes(), vo.getType())) {
-                        processor.validRuleOptions(vo.getType(), vo.getOptions());
-                    }
-                }
-            } catch (Exception e) {
-                e.printStackTrace();
-                throw e;
-            }
-        }
-        Set<Long> saveResult = Collections.emptySet();
-        Exception saveException = null;
-        for (BpmCandidateSourceInfoProcessor processor : processorList) {
-            try {
-                saveResult = processor.process(sourceInfo, execution);
-                if (CollUtil.isNotEmpty(saveResult)) {
-                    removeDisableUsers(saveResult);
-                    break;
-                }
-            } catch (Exception e) {
-                saveException = e;
-                break;
-            }
-        }
-        // Return the exception or result state from the last execute()
-        if ((saveException != null)) {
-            throw saveException;
-        } else {
-            return (saveResult);
-        }
-    }
-
-    public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution, BpmCandidateSourceInfo sourceInfo) {
-        Set<Long> results = Collections.emptySet();
-        try {
-            results = process(sourceInfo, execution);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return results;
-    }
-
-    /**
-     * 移除禁用用户
-     *
-     * @param assigneeUserIds
-     */
-    public void removeDisableUsers(Set<Long> assigneeUserIds) {
-        if (CollUtil.isEmpty(assigneeUserIds)) {
-            return;
-        }
-        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
-        assigneeUserIds.removeIf(id -> {
-            AdminUserRespDTO user = userMap.get(id);
-            return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
-        });
-    }
-}

+ 0 - 32
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateAdminUserApiSourceInfoProcessor.java

@@ -1,32 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
-
-import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import org.flowable.engine.delegate.DelegateExecution;
-
-import jakarta.annotation.Resource;
-import java.util.Set;
-
-public class BpmCandidateAdminUserApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
-    @Resource
-    private AdminUserApi api;
-
-    @Override
-    public Set<Integer> getSupportedTypes() {
-        return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER.getType());
-    }
-
-    @Override
-    public void validRuleOptions(Integer type, Set<Long> options) {
-        api.validateUserList(options);
-    }
-
-    @Override
-    public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
-        return rule.getOptions();
-    }
-}

+ 0 - 50
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateDeptApiSourceInfoProcessor.java

@@ -1,50 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
-
-import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
-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.flowable.engine.delegate.DelegateExecution;
-
-import jakarta.annotation.Resource;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-
-public class BpmCandidateDeptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
-    @Resource
-    private DeptApi api;
-    @Resource
-    private AdminUserApi adminUserApi;
-
-    @Override
-    public Set<Integer> getSupportedTypes() {
-        return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
-                BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType());
-    }
-
-    @Override
-    public void validRuleOptions(Integer type, Set<Long> options) {
-        api.validateDeptList(options);
-    }
-
-    @Override
-    public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
-        if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
-            List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(rule.getOptions());
-            return convertSet(users, AdminUserRespDTO::getId);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
-            List<DeptRespDTO> depts = api.getDeptList(rule.getOptions());
-            return convertSet(depts, DeptRespDTO::getLeaderUserId);
-        }
-        return Collections.emptySet();
-    }
-}

+ 0 - 40
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidatePostApiSourceInfoProcessor.java

@@ -1,40 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
-
-import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
-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 org.flowable.engine.delegate.DelegateExecution;
-
-import jakarta.annotation.Resource;
-import java.util.List;
-import java.util.Set;
-
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-
-public class BpmCandidatePostApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
-    @Resource
-    private PostApi api;
-    @Resource
-    private AdminUserApi adminUserApi;
-
-    @Override
-    public Set<Integer> getSupportedTypes() {
-        return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.POST.getType());
-    }
-
-    @Override
-    public void validRuleOptions(Integer type, Set<Long> options) {
-        api.validPostList(options);
-    }
-
-    @Override
-    public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
-        List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(rule.getOptions());
-        return convertSet(users, AdminUserRespDTO::getId);
-    }
-}

+ 0 - 37
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateRoleApiSourceInfoProcessor.java

@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
-
-import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
-import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
-import cn.iocoder.yudao.module.system.api.permission.RoleApi;
-import org.flowable.engine.delegate.DelegateExecution;
-
-import jakarta.annotation.Resource;
-import java.util.Set;
-
-public class BpmCandidateRoleApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
-    @Resource
-    private RoleApi api;
-
-    @Resource
-    private PermissionApi permissionApi;
-
-    @Override
-    public Set<Integer> getSupportedTypes() {
-        return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.ROLE.getType());
-    }
-
-    @Override
-    public void validRuleOptions(Integer type, Set<Long> options) {
-        api.validRoleList(options);
-    }
-
-    @Override
-    public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
-        return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
-    }
-
-}

+ 0 - 73
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateScriptApiSourceInfoProcessor.java

@@ -1,73 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
-import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
-import org.flowable.engine.delegate.DelegateExecution;
-import org.springframework.beans.factory.ObjectProvider;
-
-import jakarta.annotation.Resource;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
-import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_ASSIGN_SCRIPT_NOT_EXISTS;
-
-public class BpmCandidateScriptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
-    @Resource
-    private DictDataApi dictDataApi;
-
-    /**
-     * 任务分配脚本
-     */
-    private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
-
-    public void setScripts(ObjectProvider<BpmTaskAssignScript> scriptsOp) {
-        List<BpmTaskAssignScript> scripts = scriptsOp.orderedStream().collect(Collectors.toList());
-        setScripts(scripts);
-    }
-
-    public void setScripts(List<BpmTaskAssignScript> scripts) {
-        this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
-    }
-
-    @Override
-    public Set<Integer> getSupportedTypes() {
-        return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.SCRIPT.getType());
-    }
-
-    @Override
-    public void validRuleOptions(Integer type, Set<Long> options) {
-        dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
-                CollectionUtils.convertSet(options, String::valueOf));
-    }
-
-    @Override
-    public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
-        return calculateTaskCandidateUsersByScript(delegateExecution, rule.getOptions());
-    }
-
-    private Set<Long> calculateTaskCandidateUsersByScript(DelegateExecution execution, Set<Long> options) {
-        // 获得对应的脚本
-        List<BpmTaskAssignScript> scripts = new ArrayList<>(options.size());
-        options.forEach(id -> {
-            BpmTaskAssignScript script = scriptMap.get(id);
-            if (script == null) {
-                throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id);
-            }
-            scripts.add(script);
-        });
-        // 逐个计算任务
-        Set<Long> userIds = new HashSet<>();
-        scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(execution)));
-        return userIds;
-    }
-}

+ 0 - 41
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateUserGroupApiSourceInfoProcessor.java

@@ -1,41 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
-
-import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
-import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
-import org.flowable.engine.delegate.DelegateExecution;
-
-import jakarta.annotation.Resource;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class BpmCandidateUserGroupApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
-    @Resource
-    private BpmUserGroupService api;
-    @Resource
-    private BpmUserGroupService userGroupService;
-
-    @Override
-    public Set<Integer> getSupportedTypes() {
-        return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType());
-    }
-
-    @Override
-    public void validRuleOptions(Integer type, Set<Long> options) {
-        api.validUserGroups(options);
-    }
-
-    @Override
-    public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateRuleVO rule, DelegateExecution delegateExecution) {
-        List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
-        Set<Long> userIds = new HashSet<>();
-        userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
-        return userIds;
-    }
-
-}

+ 4 - 2
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java

@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
 import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
 import jakarta.annotation.Resource;
@@ -54,8 +55,9 @@ public class BpmModelServiceImpl implements BpmModelService {
     private BpmProcessDefinitionService processDefinitionService;
     @Resource
     private BpmFormService bpmFormService;
+
     @Resource
-    private BpmTaskAssignRuleService taskAssignRuleService;
+    private BpmTaskCandidateInvoker taskCandidateInvoker;
 
     @Override
     public PageResult<BpmModelPageItemRespVO> getModelPage(BpmModelPageReqVO pageVO) {
@@ -166,7 +168,7 @@ public class BpmModelServiceImpl implements BpmModelService {
         // 1.3 校验表单已配
         BpmFormDO form = checkFormConfig(model.getMetaInfo());
         // 1.4 校验任务分配规则已配置
-        taskAssignRuleService.checkTaskAssignRuleAllConfig(bpmnBytes);
+        taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
 
         // 1.5 校验模型是否发生修改。如果未修改,则不允许创建
         BpmProcessDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model, form)

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java

@@ -5,7 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
-import cn.iocoder.yudao.framework.flowable.core.enums.BpmnModelConstants;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
 import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO;

+ 0 - 30
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleService.java

@@ -1,30 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.definition;
-
-import org.flowable.engine.delegate.DelegateExecution;
-
-import java.util.Set;
-
-/**
- * BPM 任务分配规则 Service 接口
- *
- * @author 芋道源码
- */
-public interface BpmTaskAssignRuleService {
-
-    /**
-     * 校验流程模型的任务分配规则全部都配置了
-     * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去!
-     *
-     * @param bpmnBytes BPMN XML
-     */
-    void checkTaskAssignRuleAllConfig(byte[] bpmnBytes);
-
-    /**
-     * 计算当前执行任务的处理人
-     *
-     * @param execution 执行任务
-     * @return 处理人的编号数组
-     */
-    Set<Long> calculateTaskCandidateUsers(DelegateExecution execution);
-
-}

+ 0 - 254
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java

@@ -1,254 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.definition;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-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.object.ObjectUtils;
-import cn.iocoder.yudao.framework.common.util.string.StrUtils;
-import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
-import cn.iocoder.yudao.framework.flowable.core.enums.BpmnModelConstants;
-import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
-import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
-import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
-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.PostApi;
-import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
-import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
-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 cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import com.google.common.annotations.VisibleForTesting;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.dromara.hutool.core.convert.Convert;
-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.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Service;
-import org.springframework.validation.annotation.Validated;
-
-import java.util.*;
-import java.util.function.Function;
-
-import static cn.hutool.core.text.CharSequenceUtil.format;
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
-
-/**
- * BPM 任务分配规则 Service 实现类
- */
-@Service
-@Validated
-@Slf4j
-public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
-
-    @Resource
-    private BpmUserGroupService userGroupService;
-    @Resource
-    @Lazy // 解决循环依赖
-    private BpmProcessInstanceService processInstanceService;
-
-//    @Resource
-//    private ExpressionManager expressionManager;
-
-    @Resource
-    private RoleApi roleApi;
-    @Resource
-    private DeptApi deptApi;
-    @Resource
-    private PostApi postApi;
-    @Resource
-    private AdminUserApi adminUserApi;
-    @Resource
-    private DictDataApi dictDataApi;
-    @Resource
-    private PermissionApi permissionApi;
-
-    /**
-     * 任务分配脚本
-     */
-    private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
-
-    @Resource
-    public void setScripts(List<BpmTaskAssignScript> scripts) {
-        this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
-    }
-
-    @Override
-    public void checkTaskAssignRuleAllConfig(byte[] bpmnBytes) {
-        BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
-        assert bpmnModel != null;
-        List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
-        // 遍历所有的 UserTask,校验审批人配置
-        userTaskList.forEach(userTask -> {
-            // TODO 芋艿:assignType/assignOptions/, 枚举
-            Integer type = NumberUtils.parseInt(userTask.getAttributeValue(BpmnModelConstants.NAMESPACE, "assignType"));
-            String options = userTask.getAttributeValue(BpmnModelConstants.NAMESPACE, "assignOptions");
-            if (type == null || StrUtil.isBlank(options)) {
-                throw exception(MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG, userTask.getName());
-            }
-            // TODO 芋艿:校验 options
-            if (ObjectUtil.equal(type, BpmTaskAssignRuleTypeEnum.EXPRESS.getType())) {
-                return;
-            }
-            validTaskAssignRuleOptions(type, StrUtils.splitToLong(options, ","));
-        });
-    }
-
-    private void validTaskAssignRuleOptions(Integer type, Collection<Long> options) {
-        if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) {
-            roleApi.validRoleList(options);
-        } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
-                BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
-            deptApi.validateDeptList(options);
-        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) {
-            postApi.validPostList(options);
-        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER.getType())) {
-            adminUserApi.validateUserList(options);
-        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_GROUP.getType())) {
-            userGroupService.validUserGroups(options);
-        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) {
-            dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
-                    CollectionUtils.convertSet(options, String::valueOf));
-        } else {
-            throw new IllegalArgumentException(format("未知的规则类型({})", type));
-        }
-    }
-
-    public Long test(DelegateExecution execution) {
-        return 1L;
-    }
-
-    public Long test2(DelegateExecution execution, Long id) {
-        return id;
-    }
-
-    @Override
-    @DataPermission(enable = false) // 忽略数据权限,不然分配会存在问题
-    public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
-        // 1. 先从提前选好的审批人中获取
-        List<Long> assignee = processInstanceService.getAssigneeByProcessInstanceIdAndTaskDefinitionKey(
-                execution.getProcessInstanceId(), execution.getCurrentActivityId());
-        if (CollUtil.isNotEmpty(assignee)) {
-            // TODO @hai:new HashSet 即可
-            return convertSet(assignee, Function.identity());
-        }
-        // 2. 通过分配规则,计算审批人
-        return calculateTaskCandidateUsers0(execution);
-    }
-
-    @VisibleForTesting
-    Set<Long> calculateTaskCandidateUsers0(DelegateExecution execution) {
-        // 获得审批人配置
-        // TODO 芋艿:assignType/assignOptions/, 枚举
-        FlowElement flowElement = execution.getCurrentFlowElement();
-        Integer type = NumberUtils.parseInt(flowElement.getAttributeValue(BpmnModelConstants.NAMESPACE, "assignType"));
-        String optionStr = flowElement.getAttributeValue(BpmnModelConstants.NAMESPACE, "assignOptions");
-        Set<Long> options = null;
-        if (ObjectUtil.notEqual(BpmTaskAssignRuleTypeEnum.EXPRESS.getType(), type)) {
-            options = StrUtils.splitToLongSet(optionStr, ",");
-        }
-
-        // 计算审批人
-        Set<Long> assigneeUserIds = null;
-        if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), type)) {
-            assigneeUserIds = calculateTaskCandidateUsersByRole(options);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), type)) {
-            assigneeUserIds = calculateTaskCandidateUsersByDeptMember(options);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), type)) {
-            assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(options);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), type)) {
-            assigneeUserIds = calculateTaskCandidateUsersByPost(options);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), type)) {
-            assigneeUserIds = calculateTaskCandidateUsersByUser(options);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), type)) {
-            assigneeUserIds = calculateTaskCandidateUsersByUserGroup(options);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), type)) {
-            assigneeUserIds = calculateTaskCandidateUsersByScript(execution, options);
-        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.EXPRESS.getType(), type)) {
-           Object result = FlowableUtils.getExpressionValue(execution, optionStr);
-           assigneeUserIds = Convert.toSet(Long.class, result);
-        }
-
-        // 移除被禁用的用户
-        removeDisableUsers(assigneeUserIds);
-        // 如果候选人为空,抛出异常
-        if (CollUtil.isEmpty(assigneeUserIds)) {
-            log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(),
-                    execution.getProcessDefinitionId(), execution.getCurrentActivityId(), type, options);
-            throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
-        }
-        return assigneeUserIds;
-    }
-
-    private Set<Long> calculateTaskCandidateUsersByRole(Set<Long> roleIds) {
-        return permissionApi.getUserRoleIdListByRoleIds(roleIds);
-    }
-
-    private Set<Long> calculateTaskCandidateUsersByDeptMember(Set<Long> deptIds) {
-        List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(deptIds);
-        return convertSet(users, AdminUserRespDTO::getId);
-    }
-
-    private Set<Long> calculateTaskCandidateUsersByDeptLeader(Set<Long> deptIds) {
-        List<DeptRespDTO> depts = deptApi.getDeptList(deptIds);
-        return convertSet(depts, DeptRespDTO::getLeaderUserId);
-    }
-
-    private Set<Long> calculateTaskCandidateUsersByPost(Set<Long> postIds) {
-        List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(postIds);
-        return convertSet(users, AdminUserRespDTO::getId);
-    }
-
-    private Set<Long> calculateTaskCandidateUsersByUser(Set<Long> userIds) {
-        return userIds;
-    }
-
-    private Set<Long> calculateTaskCandidateUsersByUserGroup(Set<Long> groupIds) {
-        List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(groupIds);
-        Set<Long> userIds = new HashSet<>();
-        userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
-        return userIds;
-    }
-
-    private Set<Long> calculateTaskCandidateUsersByScript(DelegateExecution execution, Set<Long> scriptIds) {
-        // 获得对应的脚本
-        List<BpmTaskAssignScript> scripts = new ArrayList<>(scriptIds.size());
-        scriptIds.forEach(scriptId -> {
-            BpmTaskAssignScript script = scriptMap.get(scriptId);
-            if (script == null) {
-                throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, scriptId);
-            }
-            scripts.add(script);
-        });
-        // 逐个计算任务
-        Set<Long> userIds = new HashSet<>();
-        scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(execution)));
-        return userIds;
-    }
-
-    @VisibleForTesting
-    void removeDisableUsers(Set<Long> assigneeUserIds) {
-        if (CollUtil.isEmpty(assigneeUserIds)) {
-            return;
-        }
-        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
-        assigneeUserIds.removeIf(id -> {
-            AdminUserRespDTO user = userMap.get(id);
-            return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
-        });
-    }
-
-}

+ 0 - 9
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyService.java

@@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyMyPageReqVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
 
 /**
  * 流程抄送 Service 接口
@@ -13,14 +12,6 @@ import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  */
 public interface BpmProcessInstanceCopyService {
 
-    // TODO 芋艿:这块要 review 下;思考下~~
-    /**
-     * 抄送
-     * @param sourceInfo 抄送源信息,方便抄送处理
-     * @return
-     */
-    boolean makeCopy(BpmCandidateSourceInfo sourceInfo);
-
     /**
      * 流程实例的抄送
      *

+ 0 - 68
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/cc/BpmProcessInstanceCopyServiceImpl.java

@@ -1,35 +1,22 @@
 package cn.iocoder.yudao.module.bpm.service.task.cc;
 
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyCreateReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyMyPageReqVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.cc.BpmProcessInstanceCopyDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.cc.BpmProcessInstanceCopyMapper;
 import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
-import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessorChain;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
-import cn.iocoder.yudao.module.bpm.service.task.cc.dto.BpmDelegateExecutionDTO;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
-import org.flowable.engine.RuntimeService;
-import org.flowable.engine.delegate.DelegateExecution;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.flowable.task.api.Task;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 
 /**
@@ -45,12 +32,6 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
     @Resource
     private BpmProcessInstanceCopyMapper processInstanceCopyMapper;
 
-    @Resource
-    private RuntimeService runtimeService;
-
-    @Resource
-    private BpmCandidateSourceInfoProcessorChain processorChain;
-
     @Resource
     @Lazy
     private BpmTaskService bpmTaskService;
@@ -58,55 +39,6 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
     @Lazy
     private BpmProcessInstanceService bpmProcessInstanceService;
 
-    @Override
-    public boolean makeCopy(BpmCandidateSourceInfo sourceInfo) {
-        if (null == sourceInfo) {
-            return false;
-        }
-
-        Task task = bpmTaskService.getTask(sourceInfo.getTaskId());
-        if (ObjectUtil.isNull(task)) {
-            return false;
-        }
-        String processInstanceId = task.getProcessInstanceId();
-        if (StrUtil.isBlank(processInstanceId)) {
-            return false;
-        }
-        DelegateExecution executionEntity = new BpmDelegateExecutionDTO(processInstanceId);
-        Set<Long> ccCandidates = processorChain.calculateTaskCandidateUsers(executionEntity, sourceInfo);
-        if (CollUtil.isEmpty(ccCandidates)) {
-            log.warn("相关抄送人不存在 {}", sourceInfo.getTaskId());
-            return false;
-        } else {
-            BpmProcessInstanceCopyDO copyDO = new BpmProcessInstanceCopyDO();
-            // 调用
-            // 设置任务id
-            copyDO.setTaskId(sourceInfo.getTaskId());
-            copyDO.setTaskName(task.getName());
-            copyDO.setProcessInstanceId(processInstanceId);
-            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
-                    .processInstanceId(processInstanceId)
-                    .singleResult();
-            if (null == processInstance) {
-                log.warn("相关流程实例不存在 {}", sourceInfo.getTaskId());
-                return false;
-            }
-            copyDO.setStartUserId(Long.parseLong(processInstance.getStartUserId()));
-            copyDO.setProcessInstanceName(processInstance.getName());
-            copyDO.setCategory(processInstance.getProcessDefinitionCategory());
-            copyDO.setReason(sourceInfo.getReason());
-            copyDO.setCreator(sourceInfo.getCreator());
-            copyDO.setCreateTime(LocalDateTime.now());
-            List<BpmProcessInstanceCopyDO> copyList = new ArrayList<>(ccCandidates.size());
-            for (Long userId : ccCandidates) {
-                BpmProcessInstanceCopyDO copy = BeanUtil.copyProperties(copyDO, BpmProcessInstanceCopyDO.class);
-                copy.setUserId(userId);
-                copyList.add(copy);
-            }
-            return processInstanceCopyMapper.insertBatch(copyList);
-        }
-    }
-
     @Override
     public void createProcessInstanceCopy(Long userId, BpmProcessInstanceCopyCreateReqVO reqVO) {
         // 1.1 校验任务存在

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

@@ -0,0 +1,93 @@
+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.enums.BpmTaskCandidateStrategyEnum;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+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 org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * {@link BpmTaskCandidateInvoker} 的单元测试
+ *
+ * @author 芋道源码
+ */
+public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateInvoker taskCandidateInvoker;
+
+    @Mock
+    private AdminUserApi adminUserApi;
+    @Spy
+    private BpmTaskCandidateStrategy strategy = new BpmTaskCandidateUserStrategy();
+    @Spy
+    private List<BpmTaskCandidateStrategy> strategyList = Collections.singletonList(strategy);
+
+    @Test
+    public void testCalculateUsers() {
+        // 准备参数
+        String param = "1,2";
+        DelegateExecution execution = mock(DelegateExecution.class);
+        // mock 方法(DelegateExecution)
+        UserTask userTask = mock(UserTask.class);
+        when(execution.getCurrentFlowElement()).thenReturn(userTask);
+        when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))
+                .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());
+        when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))
+                .thenReturn(param);
+        // mock 方法(adminUserApi)
+        AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
+                .setStatus(CommonStatusEnum.ENABLE.getStatus()));
+        AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
+                .setStatus(CommonStatusEnum.ENABLE.getStatus()));
+        Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
+                .put(user2.getId(), user2).build();
+        when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
+
+        // 调用
+        Set<Long> results = taskCandidateInvoker.calculateUsers(execution);
+        // 断言
+        assertEquals(asSet(1L, 2L), results);
+    }
+
+    @Test
+    public void testRemoveDisableUsers() {
+        // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到
+        Set<Long> assigneeUserIds = asSet(1L, 2L, 3L);
+        // mock 方法
+        AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
+                .setStatus(CommonStatusEnum.ENABLE.getStatus()));
+        AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
+                .setStatus(CommonStatusEnum.DISABLE.getStatus()));
+        Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
+                .put(user2.getId(), user2).build();
+        when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
+
+        // 调用
+        taskCandidateInvoker.removeDisableUsers(assigneeUserIds);
+        // 断言
+        assertEquals(asSet(1L), assigneeUserIds);
+    }
+
+}

+ 9 - 9
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java → yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl;
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression;
 
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
@@ -21,10 +21,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
-public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
+public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest {
 
     @InjectMocks
-    private BpmTaskAssignLeaderX2Script script;
+    private BpmTaskAssignLeaderExpression expression;
 
     @Mock
     private AdminUserApi adminUserApi;
@@ -34,7 +34,7 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
     private BpmProcessInstanceService bpmProcessInstanceService;
 
     @Test
-    public void testCalculateTaskCandidateUsers_noDept() {
+    public void testCalculateUsers_noDept() {
         // 准备参数
         DelegateExecution execution = mockDelegateExecution(1L);
         // mock 方法(startUser)
@@ -44,13 +44,13 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
         when(deptApi.getDept(eq(10L))).thenReturn(null);
 
         // 调用
-        Set<Long> result = script.calculateTaskCandidateUsers(execution);
+        Set<Long> result = expression.calculateUsers(execution, 1);
         // 断言
         assertEquals(0, result.size());
     }
 
     @Test
-    public void testCalculateTaskCandidateUsers_noParentDept() {
+    public void testCalculateUsers_noParentDept() {
         // 准备参数
         DelegateExecution execution = mockDelegateExecution(1L);
         // mock 方法(startUser)
@@ -63,13 +63,13 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
         when(deptApi.getDept(eq(100L))).thenReturn(null);
 
         // 调用
-        Set<Long> result = script.calculateTaskCandidateUsers(execution);
+        Set<Long> result = expression.calculateUsers(execution, 2);
         // 断言
         assertEquals(asSet(20L), result);
     }
 
     @Test
-    public void testCalculateTaskCandidateUsers_existParentDept() {
+    public void testCalculateUsers_existParentDept() {
         // 准备参数
         DelegateExecution execution = mockDelegateExecution(1L);
         // mock 方法(startUser)
@@ -84,7 +84,7 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
         when(deptApi.getDept(eq(100L))).thenReturn(parentDept);
 
         // 调用
-        Set<Long> result = script.calculateTaskCandidateUsers(execution);
+        Set<Long> result = expression.calculateUsers(execution, 2);
         // 断言
         assertEquals(asSet(200L), result);
     }

+ 42 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderStrategyTest.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+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.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;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateDeptLeaderStrategy strategy;
+
+    @Mock
+    private DeptApi deptApi;
+
+    @Test
+    public void testCalculateUsers() {
+        // 准备参数
+        String param = "1,2";
+        // 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));
+
+        // 调用
+        Set<Long> results = strategy.calculateUsers(null, param);
+        // 断言
+        assertEquals(asSet(11L, 22L), results);
+    }
+
+}

+ 42 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptMemberStrategyTest.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+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 org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateDeptMemberStrategy strategy;
+
+    @Mock
+    private AdminUserApi adminUserApi;
+
+    @Test
+    public void testCalculateUsers() {
+        // 准备参数
+        String param = "11,22";
+        // mock 方法
+        List<AdminUserRespDTO> users = convertList(asSet(11L, 22L),
+                id -> new AdminUserRespDTO().setId(id));
+        when(adminUserApi.getUserListByDeptIds(eq(asSet(11L, 22L)))).thenReturn(users);
+
+        // 调用
+        Set<Long> results = strategy.calculateUsers(null, param);
+        // 断言
+        assertEquals(asSet(11L, 22L), results);
+    }
+
+}

+ 39 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateExpressionStrategyTest.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.MockedStatic;
+
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateExpressionStrategy strategy;
+
+    @Test
+    public void testCalculateUsers() {
+        try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {
+            // 准备参数
+            String param = "1,2";
+            DelegateExecution execution = mock(DelegateExecution.class);
+            // mock 方法
+            flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(execution), eq(param)))
+                    .thenReturn(asSet(1L, 2L));
+
+            // 调用
+            Set<Long> results = strategy.calculateUsers(execution, param);
+            // 断言
+            assertEquals(asSet(1L, 2L), results);
+        }
+    }
+
+}

+ 42 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateGroupStrategyTest.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+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 org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateGroupStrategy strategy;
+
+    @Mock
+    private BpmUserGroupService userGroupService;
+
+    @Test
+    public void testCalculateUsers() {
+        // 准备参数
+        String param = "1,2";
+        // mock 方法
+        BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(11L, 12L)));
+        BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(21L, 22L)));
+        when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2));
+
+        // 调用
+        Set<Long> results = strategy.calculateUsers(null, param);
+        // 断言
+        assertEquals(asSet(11L, 12L, 21L, 22L), results);
+    }
+
+}

+ 45 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidatePostStrategyTest.java

@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+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 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 org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidatePostStrategy strategy;
+
+    @Mock
+    private PostApi postApi;
+    @Mock
+    private AdminUserApi adminUserApi;
+
+    @Test
+    public void testCalculateUsers() {
+        // 准备参数
+        String param = "1,2";
+        // mock 方法
+        List<AdminUserRespDTO> users = convertList(asSet(11L, 22L),
+                id -> new AdminUserRespDTO().setId(id));
+        when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(users);
+
+        // 调用
+        Set<Long> results = strategy.calculateUsers(null, param);
+        // 断言
+        assertEquals(asSet(11L, 22L), results);
+    }
+
+}

+ 41 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategyTest.java

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
+import cn.iocoder.yudao.module.system.api.permission.RoleApi;
+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 org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateRoleStrategy strategy;
+
+    @Mock
+    private RoleApi roleApi;
+    @Mock
+    private PermissionApi permissionApi;
+
+    @Test
+    public void testCalculateUsers() {
+        // 准备参数
+        String param = "1,2";
+        // mock 方法
+        when(permissionApi.getUserRoleIdListByRoleIds(eq(asSet(1L, 2L))))
+            .thenReturn(asSet(11L, 22L));
+
+        // 调用
+        Set<Long> results = strategy.calculateUsers(null, param);
+        // 断言
+        assertEquals(asSet(11L, 22L), results);
+    }
+
+}

+ 28 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateUserStrategyTest.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private BpmTaskCandidateUserStrategy strategy;
+
+    @Test
+    public void testCalculateUsers() {
+        // 准备参数
+        String param = "1,2";
+
+        // 调用
+        Set<Long> results = strategy.calculateUsers(null, param);
+        // 断言
+        assertEquals(asSet(1L, 2L), results);
+    }
+
+}

+ 0 - 244
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChainTest.java

@@ -1,244 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.candidate;
-
-import cn.hutool.core.map.MapUtil;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
-import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateRuleVO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl.BpmTaskAssignLeaderX1Script;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl.BpmTaskAssignLeaderX2Script;
-import cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor.BpmCandidateScriptApiSourceInfoProcessor;
-import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
-import cn.iocoder.yudao.module.system.api.dept.DeptApi;
-import cn.iocoder.yudao.module.system.api.dept.PostApi;
-import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
-import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
-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 cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import jakarta.annotation.Resource;
-import org.flowable.engine.delegate.DelegateExecution;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Import;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-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.Collections.singleton;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-@Disabled // TODO 芋艿:临时禁用,暂时不修复,等重构后解决
-@Import({BpmCandidateSourceInfoProcessorChain.class,
-        BpmCandidateScriptApiSourceInfoProcessor.class, BpmTaskAssignLeaderX1Script.class,
-        BpmTaskAssignLeaderX2Script.class})
-public class BpmCandidateSourceInfoProcessorChainTest extends BaseDbUnitTest {
-    @Resource
-    private BpmCandidateSourceInfoProcessorChain processorChain;
-
-    @MockBean
-    private BpmUserGroupService userGroupService;
-    @MockBean
-    private DeptApi deptApi;
-    @MockBean
-    private AdminUserApi adminUserApi;
-    @MockBean
-    private PermissionApi permissionApi;
-    @MockBean
-    private RoleApi roleApi;
-    @MockBean
-    private PostApi postApi;
-    @MockBean
-    private DictDataApi dictDataApi;
-    @Resource
-    private BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor;
-
-    @Test
-    public void testCalculateTaskCandidateUsers_Role() {
-        // 准备参数
-        BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.ROLE.getType());
-        // mock 方法
-        when(permissionApi.getUserRoleIdListByRoleIds(eq(rule.getOptions())))
-                .thenReturn(asSet(11L, 22L));
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
-        sourceInfo.addRule(rule);
-        Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_DeptMember() {
-        // 准备参数
-        BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType());
-        // mock 方法
-        List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
-                id -> new AdminUserRespDTO().setId(id));
-        when(adminUserApi.getUserListByDeptIds(eq(rule.getOptions()))).thenReturn(users);
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
-        sourceInfo.addRule(rule);
-        Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_DeptLeader() {
-        // 准备参数
-        BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType());
-        // mock 方法
-        DeptRespDTO dept1 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(11L));
-        DeptRespDTO dept2 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(22L));
-        when(deptApi.getDeptList(eq(rule.getOptions()))).thenReturn(Arrays.asList(dept1, dept2));
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
-        sourceInfo.addRule(rule);
-        Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_Post() {
-        // 准备参数
-        BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.POST.getType());
-        // mock 方法
-        List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
-                id -> new AdminUserRespDTO().setId(id));
-        when(adminUserApi.getUserListByPostIds(eq(rule.getOptions()))).thenReturn(users);
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
-        sourceInfo.addRule(rule);
-        Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_User() {
-        // 准备参数
-        BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.USER.getType());
-        // mock 方法
-        mockGetUserMap(asSet(1L, 2L));
-
-        // 调用
-        BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
-        sourceInfo.addRule(rule);
-        Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
-        // 断言
-        assertEquals(asSet(1L, 2L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_UserGroup() {
-        // 准备参数
-        BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType());
-        // mock 方法
-        BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(11L, 12L)));
-        BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(21L, 22L)));
-        when(userGroupService.getUserGroupList(eq(rule.getOptions()))).thenReturn(Arrays.asList(userGroup1, userGroup2));
-        mockGetUserMap(asSet(11L, 12L, 21L, 22L));
-
-        // 调用
-        BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
-        sourceInfo.addRule(rule);
-        Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
-        // 断言
-        assertEquals(asSet(11L, 12L, 21L, 22L), results);
-    }
-
-    private void mockGetUserMap(Set<Long> assigneeUserIds) {
-        Map<Long, AdminUserRespDTO> userMap = CollectionUtils.convertMap(assigneeUserIds, id -> id,
-                id -> new AdminUserRespDTO().setId(id).setStatus(CommonStatusEnum.ENABLE.getStatus()));
-        when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_Script() {
-        // 准备参数
-        BpmTaskCandidateRuleVO rule = new BpmTaskCandidateRuleVO().setOptions(asSet(20L, 21L))
-                .setType(BpmTaskAssignRuleTypeEnum.SCRIPT.getType());
-        // mock 方法
-        BpmTaskAssignScript script1 = new BpmTaskAssignScript() {
-
-            @Override
-            public Set<Long> calculateTaskCandidateUsers(DelegateExecution task) {
-                return singleton(11L);
-            }
-
-            @Override
-            public BpmTaskRuleScriptEnum getEnum() {
-                return BpmTaskRuleScriptEnum.LEADER_X1;
-            }
-        };
-        BpmTaskAssignScript script2 = new BpmTaskAssignScript() {
-
-            @Override
-            public Set<Long> calculateTaskCandidateUsers(DelegateExecution task) {
-                return singleton(22L);
-            }
-
-            @Override
-            public BpmTaskRuleScriptEnum getEnum() {
-                return BpmTaskRuleScriptEnum.LEADER_X2;
-            }
-        };
-        bpmCandidateScriptApiSourceInfoProcessor.setScripts(Arrays.asList(script1, script2));
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
-        sourceInfo.addRule(rule);
-        Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testRemoveDisableUsers() {
-        // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到
-        Set<Long> assigneeUserIds = asSet(1L, 2L, 3L);
-        // mock 方法
-        AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
-                .setStatus(CommonStatusEnum.ENABLE.getStatus()));
-        AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
-                .setStatus(CommonStatusEnum.DISABLE.getStatus()));
-        Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
-                .put(user2.getId(), user2).build();
-        when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
-
-        // 调用
-        processorChain.removeDisableUsers(assigneeUserIds);
-        // 断言
-        assertEquals(asSet(1L), assigneeUserIds);
-    }
-
-}

+ 0 - 227
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImplTest.java

@@ -1,227 +0,0 @@
-package cn.iocoder.yudao.module.bpm.service.definition;
-
-import cn.hutool.core.map.MapUtil;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
-import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
-import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl.BpmTaskAssignStartUserScript;
-import cn.iocoder.yudao.module.system.api.dept.DeptApi;
-import cn.iocoder.yudao.module.system.api.dept.PostApi;
-import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
-import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
-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 cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import org.flowable.engine.delegate.DelegateExecution;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.annotation.Import;
-
-import jakarta.annotation.Resource;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-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.Collections.singleton;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-/**
- * {@link BpmTaskAssignRuleService} 的单元测试
- *
- * @author 芋道源码
- */
-@Import({BpmTaskAssignRuleServiceImpl.class, BpmTaskAssignStartUserScript.class}) // Import 引入 BpmTaskAssignStartUserScript 目的是保证不报错
-public class BpmTaskAssignRuleServiceImplTest extends BaseDbUnitTest {
-
-    @Resource
-    private BpmTaskAssignRuleServiceImpl bpmTaskRuleService;
-
-    @MockBean
-    private BpmUserGroupService userGroupService;
-    @MockBean
-    private DeptApi deptApi;
-    @MockBean
-    private AdminUserApi adminUserApi;
-    @MockBean
-    private PermissionApi permissionApi;
-    @MockBean
-    private RoleApi roleApi;
-    @MockBean
-    private PostApi postApi;
-    @MockBean
-    private DictDataApi dictDataApi;
-
-    @Test
-    public void testCalculateTaskCandidateUsers_Role() {
-        // 准备参数
-        BpmTaskAssignRuleDO rule = new BpmTaskAssignRuleDO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.ROLE.getType());
-        // mock 方法
-        when(permissionApi.getUserRoleIdListByRoleIds(eq(rule.getOptions())))
-                .thenReturn(asSet(11L, 22L));
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        Set<Long> results = bpmTaskRuleService.calculateTaskCandidateUsers(null, rule);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_DeptMember() {
-        // 准备参数
-        BpmTaskAssignRuleDO rule = new BpmTaskAssignRuleDO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType());
-        // mock 方法
-        List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
-                id -> new AdminUserRespDTO().setId(id));
-        when(adminUserApi.getUserListByDeptIds(eq(rule.getOptions()))).thenReturn(users);
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        Set<Long> results = bpmTaskRuleService.calculateTaskCandidateUsers(null, rule);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_DeptLeader() {
-        // 准备参数
-        BpmTaskAssignRuleDO rule = new BpmTaskAssignRuleDO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType());
-        // mock 方法
-        DeptRespDTO dept1 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(11L));
-        DeptRespDTO dept2 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(22L));
-        when(deptApi.getDeptList(eq(rule.getOptions()))).thenReturn(Arrays.asList(dept1, dept2));
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        Set<Long> results = bpmTaskRuleService.calculateTaskCandidateUsers(null, rule);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_Post() {
-        // 准备参数
-        BpmTaskAssignRuleDO rule = new BpmTaskAssignRuleDO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.POST.getType());
-        // mock 方法
-        List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
-                id -> new AdminUserRespDTO().setId(id));
-        when(adminUserApi.getUserListByPostIds(eq(rule.getOptions()))).thenReturn(users);
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        Set<Long> results = bpmTaskRuleService.calculateTaskCandidateUsers(null, rule);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_User() {
-        // 准备参数
-        BpmTaskAssignRuleDO rule = new BpmTaskAssignRuleDO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.USER.getType());
-        // mock 方法
-        mockGetUserMap(asSet(1L, 2L));
-
-        // 调用
-        Set<Long> results = bpmTaskRuleService.calculateTaskCandidateUsers(null, rule);
-        // 断言
-        assertEquals(asSet(1L, 2L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_UserGroup() {
-        // 准备参数
-        BpmTaskAssignRuleDO rule = new BpmTaskAssignRuleDO().setOptions(asSet(1L, 2L))
-                .setType(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType());
-        // mock 方法
-        BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(11L, 12L)));
-        BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(21L, 22L)));
-        when(userGroupService.getUserGroupList(eq(rule.getOptions()))).thenReturn(Arrays.asList(userGroup1, userGroup2));
-        mockGetUserMap(asSet(11L, 12L, 21L, 22L));
-
-        // 调用
-        Set<Long> results = bpmTaskRuleService.calculateTaskCandidateUsers(null, rule);
-        // 断言
-        assertEquals(asSet(11L, 12L, 21L, 22L), results);
-    }
-
-    @Test
-    public void testCalculateTaskCandidateUsers_Script() {
-        // 准备参数
-        BpmTaskAssignRuleDO rule = new BpmTaskAssignRuleDO().setOptions(asSet(20L, 21L))
-                .setType(BpmTaskAssignRuleTypeEnum.SCRIPT.getType());
-        // mock 方法
-        BpmTaskAssignScript script1 = new BpmTaskAssignScript() {
-
-            @Override
-            public Set<Long> calculateTaskCandidateUsers(DelegateExecution task) {
-                return singleton(11L);
-            }
-
-            @Override
-            public BpmTaskRuleScriptEnum getEnum() {
-                return BpmTaskRuleScriptEnum.LEADER_X1;
-            }
-        };
-        BpmTaskAssignScript script2 = new BpmTaskAssignScript() {
-
-            @Override
-            public Set<Long> calculateTaskCandidateUsers(DelegateExecution task) {
-                return singleton(22L);
-            }
-
-            @Override
-            public BpmTaskRuleScriptEnum getEnum() {
-                return BpmTaskRuleScriptEnum.LEADER_X2;
-            }
-        };
-        bpmTaskRuleService.setScripts(Arrays.asList(script1, script2));
-        mockGetUserMap(asSet(11L, 22L));
-
-        // 调用
-        Set<Long> results = bpmTaskRuleService.calculateTaskCandidateUsers(null, rule);
-        // 断言
-        assertEquals(asSet(11L, 22L), results);
-    }
-
-    @Test
-    public void testRemoveDisableUsers() {
-        // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到
-        Set<Long> assigneeUserIds = asSet(1L, 2L, 3L);
-        // mock 方法
-        AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
-                .setStatus(CommonStatusEnum.ENABLE.getStatus()));
-        AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
-                .setStatus(CommonStatusEnum.DISABLE.getStatus()));
-        Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
-                .put(user2.getId(), user2).build();
-        when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
-
-        // 调用
-        bpmTaskRuleService.removeDisableUsers(assigneeUserIds);
-        // 断言
-        assertEquals(asSet(1L), assigneeUserIds);
-    }
-
-    private void mockGetUserMap(Set<Long> assigneeUserIds) {
-        Map<Long, AdminUserRespDTO> userMap = CollectionUtils.convertMap(assigneeUserIds, id -> id,
-                id -> new AdminUserRespDTO().setId(id).setStatus(CommonStatusEnum.ENABLE.getStatus()));
-        when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
-    }
-
-}

+ 0 - 17
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/enums/BpmnModelConstants.java

@@ -1,17 +0,0 @@
-package cn.iocoder.yudao.framework.flowable.core.enums;
-
-/**
- * 流程常量信息
- */
-public interface BpmnModelConstants {
-
-    String BPMN_FILE_SUFFIX = ".bpmn";
-
-    /**
-     * BPMN 中的命名空间
-     *
-     * 这个东西有可能导致无法切换工作流程的实现
-     */
-    String NAMESPACE = "http://flowable.org/bpmn";
-
-}

+ 0 - 1
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.yudao.framework.flowable.core;

+ 0 - 1
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.yudao.framework.flowable;