Commit e21c262bd7e3ed687b5aa1f640ecdec7066dd352

Authored by kyle
1 parent 5ae1d0ab

refactor: 重构查找候选人的逻辑

Showing 12 changed files with 797 additions and 0 deletions
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/candidate/vo/BpmTaskCandidateVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleBaseVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +import java.util.Set;
  9 +
  10 +/**
  11 + * 流程任务分配规则 Base VO,提供给添加、修改、详细的子 VO 使用
  12 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  13 + *
  14 + * @see BpmTaskAssignRuleBaseVO
  15 + */
  16 +@Data
  17 +public class BpmTaskCandidateVO {
  18 +
  19 + @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "bpm_task_assign_rule_type")
  20 + @NotNull(message = "规则类型不能为空")
  21 + private Integer type;
  22 +
  23 + @Schema(description = "规则值数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
  24 + @NotNull(message = "规则值数组不能为空")
  25 + private Set<Long> options;
  26 +
  27 +}
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmCandidateProcessorConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.framework.bpm.config;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
  4 +import cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor.*;
  5 +import org.springframework.beans.factory.ObjectProvider;
  6 +import org.springframework.context.annotation.Bean;
  7 +import org.springframework.context.annotation.Configuration;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * BPM 通用的 Configuration 配置类,提供给 Activiti 和 Flowable
  13 + * @author kyle
  14 + */
  15 +@Configuration(proxyBeanMethods = false)
  16 +public class BpmCandidateProcessorConfiguration {
  17 + @Bean
  18 + public BpmCandidateAdminUserApiSourceInfoProcessor bpmCandidateAdminUserApiSourceInfoProcessor() {
  19 + return new BpmCandidateAdminUserApiSourceInfoProcessor();
  20 + }
  21 +
  22 + @Bean
  23 + public BpmCandidateDeptApiSourceInfoProcessor bpmCandidateDeptApiSourceInfoProcessor() {
  24 + return new BpmCandidateDeptApiSourceInfoProcessor();
  25 + }
  26 +
  27 + @Bean
  28 + public BpmCandidatePostApiSourceInfoProcessor bpmCandidatePostApiSourceInfoProcessor() {
  29 + return new BpmCandidatePostApiSourceInfoProcessor();
  30 + }
  31 +
  32 + @Bean
  33 + public BpmCandidateRoleApiSourceInfoProcessor bpmCandidateRoleApiSourceInfoProcessor() {
  34 + return new BpmCandidateRoleApiSourceInfoProcessor();
  35 + }
  36 +
  37 + @Bean
  38 + public BpmCandidateUserGroupApiSourceInfoProcessor bpmCandidateUserGroupApiSourceInfoProcessor() {
  39 + return new BpmCandidateUserGroupApiSourceInfoProcessor();
  40 + }
  41 +
  42 + /**
  43 + * 可以自己定制脚本,然后通过这里设置到处理器里面去
  44 + * @param scriptsOp
  45 + * @return
  46 + */
  47 + @Bean
  48 + public BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor(ObjectProvider<BpmTaskAssignScript> scriptsOp) {
  49 + BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor = new BpmCandidateScriptApiSourceInfoProcessor();
  50 + bpmCandidateScriptApiSourceInfoProcessor.setScripts(scriptsOp);
  51 + return bpmCandidateScriptApiSourceInfoProcessor;
  52 + }
  53 +}
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfo.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleBaseVO;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.AllArgsConstructor;
  7 +import lombok.Data;
  8 +import lombok.NoArgsConstructor;
  9 +import org.flowable.engine.delegate.DelegateExecution;
  10 +
  11 +import java.util.HashSet;
  12 +import java.util.Set;
  13 +
  14 +/**
  15 + * 获取候选人信息
  16 + */
  17 +@AllArgsConstructor
  18 +@NoArgsConstructor
  19 +@Data
  20 +public class BpmCandidateSourceInfo {
  21 +
  22 + @Schema(description = "流程id")
  23 + private String processInstanceId;
  24 +
  25 + @Schema(description = "当前任务ID")
  26 + private String taskId;
  27 +
  28 + /**
  29 + * 通过这些规则,生成最终需要生成的用户
  30 + */
  31 + @Schema(description = "当前任务预选规则")
  32 + private Set<BpmTaskCandidateVO> rules;
  33 +
  34 + @Schema(description = "源执行流程")
  35 + private DelegateExecution execution;
  36 +
  37 + public void addRule(BpmTaskCandidateVO vo) {
  38 + assert vo != null;
  39 + if (rules == null) {
  40 + rules = new HashSet<>();
  41 + }
  42 + rules.add(vo);
  43 + }
  44 +}
0 45 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +
  7 +import java.util.Collections;
  8 +import java.util.HashSet;
  9 +import java.util.Set;
  10 +
  11 +public interface BpmCandidateSourceInfoProcessor {
  12 + /**
  13 + * 获取该处理器支持的类型
  14 + * 来自 {@link BpmTaskAssignRuleTypeEnum}
  15 + *
  16 + * @return
  17 + */
  18 + Set<Integer> getSupportedTypes();
  19 +
  20 + /**
  21 + * 对规则和人员做校验
  22 + *
  23 + * @param type 规则
  24 + * @param options 人员id
  25 + */
  26 + void validRuleOptions(Integer type, Set<Long> options);
  27 +
  28 + /**
  29 + * 默认的处理
  30 + * 如果想去操作所有的规则,则可以覆盖此方法
  31 + *
  32 + * @param request
  33 + * @param chain
  34 + * @return 必须包含的是用户ID,而不是其他的ID
  35 + * @throws Exception
  36 + */
  37 + default Set<Long> process(BpmCandidateSourceInfo request, BpmCandidateSourceInfoProcessorChain chain) throws Exception {
  38 + Set<BpmTaskCandidateVO> rules = request.getRules();
  39 + Set<Long> results = new HashSet<>();
  40 + for (BpmTaskCandidateVO rule : rules) {
  41 + // 每个处理器都有机会处理自己支持的事件
  42 + if (CollUtil.contains(getSupportedTypes(), rule.getType())) {
  43 + results.addAll(doProcess(request, rule));
  44 + }
  45 + }
  46 + return results;
  47 + }
  48 +
  49 + default Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateVO rule) {
  50 + return Collections.emptySet();
  51 + }
  52 +}
0 53 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChain.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  5 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  6 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  7 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  8 +import org.flowable.engine.delegate.DelegateExecution;
  9 +import org.springframework.beans.factory.ObjectProvider;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import javax.annotation.Resource;
  13 +import java.util.*;
  14 +import java.util.stream.Collectors;
  15 +
  16 +@Service
  17 +public class BpmCandidateSourceInfoProcessorChain {
  18 +
  19 + // 保存处理节点
  20 +
  21 + private List<BpmCandidateSourceInfoProcessor> processorList;
  22 + @Resource
  23 + private AdminUserApi adminUserApi;
  24 +
  25 + /**
  26 + * 可添加其他处理器
  27 + *
  28 + * @param processorOp
  29 + * @return
  30 + */
  31 + @Resource
  32 + // 动态扩展处理节点
  33 + public BpmCandidateSourceInfoProcessorChain addProcessor(ObjectProvider<BpmCandidateSourceInfoProcessor> processorOp) {
  34 + List<BpmCandidateSourceInfoProcessor> processor = processorOp.orderedStream().collect(Collectors.toList());
  35 + if (null == processorList) {
  36 + processorList = new ArrayList<>(processor.size());
  37 + }
  38 + processorList.addAll(processor);
  39 + return this;
  40 + }
  41 +
  42 + // 获取处理器处理
  43 + public Set<Long> process(BpmCandidateSourceInfo sourceInfo) throws Exception {
  44 + // Verify our parameters
  45 + if (sourceInfo == null) {
  46 + throw new IllegalArgumentException();
  47 + }
  48 + for (BpmCandidateSourceInfoProcessor processor : processorList) {
  49 + try {
  50 + for (BpmTaskCandidateVO vo : sourceInfo.getRules()) {
  51 + processor.validRuleOptions(vo.getType(), vo.getOptions());
  52 + }
  53 + } catch (Exception e) {
  54 + e.printStackTrace();
  55 + throw e;
  56 + }
  57 + }
  58 + Set<Long> saveResult = Collections.emptySet();
  59 + Exception saveException = null;
  60 + for (BpmCandidateSourceInfoProcessor processor : processorList) {
  61 + try {
  62 + saveResult = processor.process(sourceInfo, this);
  63 + if (CollUtil.isNotEmpty(saveResult)) {
  64 + removeDisableUsers(saveResult);
  65 + break;
  66 + }
  67 + } catch (Exception e) {
  68 + saveException = e;
  69 + break;
  70 + }
  71 + }
  72 + // Return the exception or result state from the last execute()
  73 + if ((saveException != null)) {
  74 + throw saveException;
  75 + } else {
  76 + return (saveResult);
  77 + }
  78 + }
  79 +
  80 + public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution, BpmCandidateSourceInfo sourceInfo) {
  81 + sourceInfo.setExecution(execution);
  82 + Set<Long> results = Collections.emptySet();
  83 + try {
  84 + results = process(sourceInfo);
  85 + } catch (Exception e) {
  86 + e.printStackTrace();
  87 + }
  88 + return results;
  89 + }
  90 +
  91 + /**
  92 + * 移除禁用用户
  93 + * @param assigneeUserIds
  94 + */
  95 + public void removeDisableUsers(Set<Long> assigneeUserIds) {
  96 + if (CollUtil.isEmpty(assigneeUserIds)) {
  97 + return;
  98 + }
  99 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
  100 + assigneeUserIds.removeIf(id -> {
  101 + AdminUserRespDTO user = userMap.get(id);
  102 + return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
  103 + });
  104 + }
  105 +}
0 106 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateAdminUserApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  8 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  9 +
  10 +import javax.annotation.Resource;
  11 +import java.util.Set;
  12 +
  13 +public class BpmCandidateAdminUserApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  14 + @Resource
  15 + private AdminUserApi api;
  16 +
  17 + @Override
  18 + public Set<Integer> getSupportedTypes() {
  19 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER.getType());
  20 + }
  21 +
  22 + @Override
  23 + public void validRuleOptions(Integer type, Set<Long> options) {
  24 + api.validateUserList(options);
  25 + }
  26 +
  27 + @Override
  28 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateVO rule) {
  29 + return rule.getOptions();
  30 + }
  31 +}
0 32 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateDeptApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  8 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  9 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  10 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  11 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  12 +
  13 +import javax.annotation.Resource;
  14 +import java.util.Collections;
  15 +import java.util.List;
  16 +import java.util.Objects;
  17 +import java.util.Set;
  18 +
  19 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  20 +
  21 +public class BpmCandidateDeptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  22 + @Resource
  23 + private DeptApi api;
  24 + @Resource
  25 + private AdminUserApi adminUserApi;
  26 +
  27 + @Override
  28 + public Set<Integer> getSupportedTypes() {
  29 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
  30 + BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType());
  31 + }
  32 +
  33 + @Override
  34 + public void validRuleOptions(Integer type, Set<Long> options) {
  35 + api.validateDeptList(options);
  36 + }
  37 +
  38 + @Override
  39 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateVO rule) {
  40 + if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
  41 + List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(rule.getOptions());
  42 + return convertSet(users, AdminUserRespDTO::getId);
  43 + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
  44 + List<DeptRespDTO> depts = api.getDeptList(rule.getOptions());
  45 + return convertSet(depts, DeptRespDTO::getLeaderUserId);
  46 + }
  47 + return Collections.emptySet();
  48 + }
  49 +}
0 50 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidatePostApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  8 +import cn.iocoder.yudao.module.system.api.dept.PostApi;
  9 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  10 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  11 +
  12 +import javax.annotation.Resource;
  13 +import java.util.List;
  14 +import java.util.Set;
  15 +
  16 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  17 +
  18 +public class BpmCandidatePostApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  19 + @Resource
  20 + private PostApi api;
  21 + @Resource
  22 + private AdminUserApi adminUserApi;
  23 +
  24 + @Override
  25 + public Set<Integer> getSupportedTypes() {
  26 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.POST.getType());
  27 + }
  28 +
  29 + @Override
  30 + public void validRuleOptions(Integer type, Set<Long> options) {
  31 + api.validPostList(options);
  32 + }
  33 +
  34 + @Override
  35 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateVO rule) {
  36 + List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(rule.getOptions());
  37 + return convertSet(users, AdminUserRespDTO::getId);
  38 + }
  39 +}
0 40 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateRoleApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  6 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  8 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  9 +import cn.iocoder.yudao.module.system.api.permission.RoleApi;
  10 +
  11 +import javax.annotation.Resource;
  12 +import java.util.Set;
  13 +
  14 +public class BpmCandidateRoleApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  15 + @Resource
  16 + private RoleApi api;
  17 +
  18 + @Resource
  19 + private PermissionApi permissionApi;
  20 +
  21 + @Override
  22 + public Set<Integer> getSupportedTypes() {
  23 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.ROLE.getType());
  24 + }
  25 +
  26 + @Override
  27 + public void validRuleOptions(Integer type, Set<Long> options) {
  28 + api.validRoleList(options);
  29 + }
  30 +
  31 + @Override
  32 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateVO rule) {
  33 + return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
  34 + }
  35 +
  36 +}
0 37 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateScriptApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  5 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  6 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  7 +import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
  8 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  9 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
  10 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  11 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  12 +import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
  13 +import org.flowable.engine.delegate.DelegateExecution;
  14 +import org.springframework.beans.factory.ObjectProvider;
  15 +
  16 +import javax.annotation.Resource;
  17 +import java.util.*;
  18 +import java.util.stream.Collectors;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  22 +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_ASSIGN_SCRIPT_NOT_EXISTS;
  23 +
  24 +public class BpmCandidateScriptApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  25 + @Resource
  26 + private DictDataApi dictDataApi;
  27 +
  28 + /**
  29 + * 任务分配脚本
  30 + */
  31 + private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
  32 +
  33 + public void setScripts(ObjectProvider<BpmTaskAssignScript> scriptsOp) {
  34 + List<BpmTaskAssignScript> scripts = scriptsOp.orderedStream().collect(Collectors.toList());
  35 + setScripts(scripts);
  36 + }
  37 +
  38 + public void setScripts(List<BpmTaskAssignScript> scripts) {
  39 + this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
  40 + }
  41 +
  42 + @Override
  43 + public Set<Integer> getSupportedTypes() {
  44 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.SCRIPT.getType());
  45 + }
  46 +
  47 + @Override
  48 + public void validRuleOptions(Integer type, Set<Long> options) {
  49 + dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
  50 + CollectionUtils.convertSet(options, String::valueOf));
  51 + }
  52 +
  53 + @Override
  54 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateVO rule) {
  55 + return calculateTaskCandidateUsersByScript(request.getExecution(), rule.getOptions());
  56 + }
  57 +
  58 + private Set<Long> calculateTaskCandidateUsersByScript(DelegateExecution execution, Set<Long> options) {
  59 + // 获得对应的脚本
  60 + List<BpmTaskAssignScript> scripts = new ArrayList<>(options.size());
  61 + options.forEach(id -> {
  62 + BpmTaskAssignScript script = scriptMap.get(id);
  63 + if (script == null) {
  64 + throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id);
  65 + }
  66 + scripts.add(script);
  67 + });
  68 + // 逐个计算任务
  69 + Set<Long> userIds = new HashSet<>();
  70 + scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(execution)));
  71 + return userIds;
  72 + }
  73 +}
0 74 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/candidate/sourceInfoProcessor/BpmCandidateUserGroupApiSourceInfoProcessor.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  5 +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
  6 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  7 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  8 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessor;
  9 +import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
  10 +
  11 +import javax.annotation.Resource;
  12 +import java.util.HashSet;
  13 +import java.util.List;
  14 +import java.util.Set;
  15 +
  16 +public class BpmCandidateUserGroupApiSourceInfoProcessor implements BpmCandidateSourceInfoProcessor {
  17 + @Resource
  18 + private BpmUserGroupService api;
  19 + @Resource
  20 + private BpmUserGroupService userGroupService;
  21 +
  22 + @Override
  23 + public Set<Integer> getSupportedTypes() {
  24 + return SetUtils.asSet(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType());
  25 + }
  26 +
  27 + @Override
  28 + public void validRuleOptions(Integer type, Set<Long> options) {
  29 + api.validUserGroups(options);
  30 + }
  31 +
  32 + @Override
  33 + public Set<Long> doProcess(BpmCandidateSourceInfo request, BpmTaskCandidateVO rule) {
  34 + List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
  35 + Set<Long> userIds = new HashSet<>();
  36 + userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
  37 + return userIds;
  38 + }
  39 +
  40 +}
0 41 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/candidate/BpmCandidateSourceInfoProcessorChainTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.service.candidate;
  2 +
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  7 +import cn.iocoder.yudao.module.bpm.controller.admin.candidate.vo.BpmTaskCandidateVO;
  8 +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
  9 +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
  10 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  11 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskRuleScriptEnum;
  12 +import cn.iocoder.yudao.module.bpm.framework.bpm.config.BpmCandidateProcessorConfiguration;
  13 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
  14 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl.BpmTaskAssignLeaderX1Script;
  15 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl.BpmTaskAssignLeaderX2Script;
  16 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfo;
  17 +import cn.iocoder.yudao.module.bpm.service.candidate.BpmCandidateSourceInfoProcessorChain;
  18 +import cn.iocoder.yudao.module.bpm.service.candidate.sourceInfoProcessor.BpmCandidateScriptApiSourceInfoProcessor;
  19 +import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleServiceImpl;
  20 +import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
  21 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  22 +import cn.iocoder.yudao.module.system.api.dept.PostApi;
  23 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  24 +import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
  25 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  26 +import cn.iocoder.yudao.module.system.api.permission.RoleApi;
  27 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  28 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  29 +import org.flowable.engine.delegate.DelegateExecution;
  30 +import org.junit.jupiter.api.Test;
  31 +import org.springframework.beans.factory.ObjectProvider;
  32 +import org.springframework.boot.test.mock.mockito.MockBean;
  33 +import org.springframework.context.annotation.Import;
  34 +
  35 +import javax.annotation.Resource;
  36 +import java.util.Arrays;
  37 +import java.util.List;
  38 +import java.util.Map;
  39 +import java.util.Set;
  40 +
  41 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  42 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  43 +import static java.util.Collections.singleton;
  44 +import static org.junit.jupiter.api.Assertions.assertEquals;
  45 +import static org.mockito.ArgumentMatchers.eq;
  46 +import static org.mockito.Mockito.when;
  47 +
  48 +@Import({BpmCandidateSourceInfoProcessorChain.class, BpmCandidateProcessorConfiguration.class,
  49 + BpmCandidateScriptApiSourceInfoProcessor.class, BpmTaskAssignLeaderX1Script.class,
  50 + BpmTaskAssignLeaderX2Script.class})
  51 +public class BpmCandidateSourceInfoProcessorChainTest extends BaseDbUnitTest {
  52 + @Resource
  53 + private BpmCandidateSourceInfoProcessorChain processorChain;
  54 +
  55 + @MockBean
  56 + private BpmUserGroupService userGroupService;
  57 + @MockBean
  58 + private DeptApi deptApi;
  59 + @MockBean
  60 + private AdminUserApi adminUserApi;
  61 + @MockBean
  62 + private PermissionApi permissionApi;
  63 + @MockBean
  64 + private RoleApi roleApi;
  65 + @MockBean
  66 + private PostApi postApi;
  67 + @MockBean
  68 + private DictDataApi dictDataApi;
  69 + @Resource
  70 + private BpmCandidateScriptApiSourceInfoProcessor bpmCandidateScriptApiSourceInfoProcessor;
  71 +
  72 + @Test
  73 + public void testCalculateTaskCandidateUsers_Role() {
  74 + // 准备参数
  75 + BpmTaskCandidateVO rule = new BpmTaskCandidateVO().setOptions(asSet(1L, 2L))
  76 + .setType(BpmTaskAssignRuleTypeEnum.ROLE.getType());
  77 + // mock 方法
  78 + when(permissionApi.getUserRoleIdListByRoleIds(eq(rule.getOptions())))
  79 + .thenReturn(asSet(11L, 22L));
  80 + mockGetUserMap(asSet(11L, 22L));
  81 +
  82 + // 调用
  83 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  84 + sourceInfo.addRule(rule);
  85 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  86 + // 断言
  87 + assertEquals(asSet(11L, 22L), results);
  88 + }
  89 +
  90 + @Test
  91 + public void testCalculateTaskCandidateUsers_DeptMember() {
  92 + // 准备参数
  93 + BpmTaskCandidateVO rule = new BpmTaskCandidateVO().setOptions(asSet(1L, 2L))
  94 + .setType(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType());
  95 + // mock 方法
  96 + List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
  97 + id -> new AdminUserRespDTO().setId(id));
  98 + when(adminUserApi.getUserListByDeptIds(eq(rule.getOptions()))).thenReturn(users);
  99 + mockGetUserMap(asSet(11L, 22L));
  100 +
  101 + // 调用
  102 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  103 + sourceInfo.addRule(rule);
  104 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  105 + // 断言
  106 + assertEquals(asSet(11L, 22L), results);
  107 + }
  108 +
  109 + @Test
  110 + public void testCalculateTaskCandidateUsers_DeptLeader() {
  111 + // 准备参数
  112 + BpmTaskCandidateVO rule = new BpmTaskCandidateVO().setOptions(asSet(1L, 2L))
  113 + .setType(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType());
  114 + // mock 方法
  115 + DeptRespDTO dept1 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(11L));
  116 + DeptRespDTO dept2 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(22L));
  117 + when(deptApi.getDeptList(eq(rule.getOptions()))).thenReturn(Arrays.asList(dept1, dept2));
  118 + mockGetUserMap(asSet(11L, 22L));
  119 +
  120 + // 调用
  121 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  122 + sourceInfo.addRule(rule);
  123 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  124 + // 断言
  125 + assertEquals(asSet(11L, 22L), results);
  126 + }
  127 +
  128 + @Test
  129 + public void testCalculateTaskCandidateUsers_Post() {
  130 + // 准备参数
  131 + BpmTaskCandidateVO rule = new BpmTaskCandidateVO().setOptions(asSet(1L, 2L))
  132 + .setType(BpmTaskAssignRuleTypeEnum.POST.getType());
  133 + // mock 方法
  134 + List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
  135 + id -> new AdminUserRespDTO().setId(id));
  136 + when(adminUserApi.getUserListByPostIds(eq(rule.getOptions()))).thenReturn(users);
  137 + mockGetUserMap(asSet(11L, 22L));
  138 +
  139 + // 调用
  140 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  141 + sourceInfo.addRule(rule);
  142 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  143 + // 断言
  144 + assertEquals(asSet(11L, 22L), results);
  145 + }
  146 +
  147 + @Test
  148 + public void testCalculateTaskCandidateUsers_User() {
  149 + // 准备参数
  150 + BpmTaskCandidateVO rule = new BpmTaskCandidateVO().setOptions(asSet(1L, 2L))
  151 + .setType(BpmTaskAssignRuleTypeEnum.USER.getType());
  152 + // mock 方法
  153 + mockGetUserMap(asSet(1L, 2L));
  154 +
  155 + // 调用
  156 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  157 + sourceInfo.addRule(rule);
  158 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  159 + // 断言
  160 + assertEquals(asSet(1L, 2L), results);
  161 + }
  162 +
  163 + @Test
  164 + public void testCalculateTaskCandidateUsers_UserGroup() {
  165 + // 准备参数
  166 + BpmTaskCandidateVO rule = new BpmTaskCandidateVO().setOptions(asSet(1L, 2L))
  167 + .setType(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType());
  168 + // mock 方法
  169 + BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(11L, 12L)));
  170 + BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setMemberUserIds(asSet(21L, 22L)));
  171 + when(userGroupService.getUserGroupList(eq(rule.getOptions()))).thenReturn(Arrays.asList(userGroup1, userGroup2));
  172 + mockGetUserMap(asSet(11L, 12L, 21L, 22L));
  173 +
  174 + // 调用
  175 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  176 + sourceInfo.addRule(rule);
  177 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  178 + // 断言
  179 + assertEquals(asSet(11L, 12L, 21L, 22L), results);
  180 + }
  181 +
  182 + private void mockGetUserMap(Set<Long> assigneeUserIds) {
  183 + Map<Long, AdminUserRespDTO> userMap = CollectionUtils.convertMap(assigneeUserIds, id -> id,
  184 + id -> new AdminUserRespDTO().setId(id).setStatus(CommonStatusEnum.ENABLE.getStatus()));
  185 + when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
  186 + }
  187 +
  188 + @Test
  189 + public void testCalculateTaskCandidateUsers_Script() {
  190 + // 准备参数
  191 + BpmTaskCandidateVO rule = new BpmTaskCandidateVO().setOptions(asSet(20L, 21L))
  192 + .setType(BpmTaskAssignRuleTypeEnum.SCRIPT.getType());
  193 + // mock 方法
  194 + BpmTaskAssignScript script1 = new BpmTaskAssignScript() {
  195 +
  196 + @Override
  197 + public Set<Long> calculateTaskCandidateUsers(DelegateExecution task) {
  198 + return singleton(11L);
  199 + }
  200 +
  201 + @Override
  202 + public BpmTaskRuleScriptEnum getEnum() {
  203 + return BpmTaskRuleScriptEnum.LEADER_X1;
  204 + }
  205 + };
  206 + BpmTaskAssignScript script2 = new BpmTaskAssignScript() {
  207 +
  208 + @Override
  209 + public Set<Long> calculateTaskCandidateUsers(DelegateExecution task) {
  210 + return singleton(22L);
  211 + }
  212 +
  213 + @Override
  214 + public BpmTaskRuleScriptEnum getEnum() {
  215 + return BpmTaskRuleScriptEnum.LEADER_X2;
  216 + }
  217 + };
  218 + bpmCandidateScriptApiSourceInfoProcessor.setScripts(Arrays.asList(script1, script2));
  219 + mockGetUserMap(asSet(11L, 22L));
  220 +
  221 + // 调用
  222 + BpmCandidateSourceInfo sourceInfo = new BpmCandidateSourceInfo();
  223 + sourceInfo.addRule(rule);
  224 + Set<Long> results = processorChain.calculateTaskCandidateUsers(null, sourceInfo);
  225 + // 断言
  226 + assertEquals(asSet(11L, 22L), results);
  227 + }
  228 +
  229 + @Test
  230 + public void testRemoveDisableUsers() {
  231 + // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到
  232 + Set<Long> assigneeUserIds = asSet(1L, 2L, 3L);
  233 + // mock 方法
  234 + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
  235 + .setStatus(CommonStatusEnum.ENABLE.getStatus()));
  236 + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
  237 + .setStatus(CommonStatusEnum.DISABLE.getStatus()));
  238 + Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
  239 + .put(user2.getId(), user2).build();
  240 + when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
  241 +
  242 + // 调用
  243 + processorChain.removeDisableUsers(assigneeUserIds);
  244 + // 断言
  245 + assertEquals(asSet(1L), assigneeUserIds);
  246 + }
  247 +
  248 +}
0 249 \ No newline at end of file
... ...