mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 02:08:43 +08:00 
			
		
		
		
	重构 bpmn 任务的处理人的逻辑,收拢到 BpmTaskAssignRuleService
This commit is contained in:
		| @@ -29,7 +29,7 @@ public class BpmTaskExtDO extends BaseDO { | ||||
|     private Long id; | ||||
|  | ||||
|     /** | ||||
|      * 流程任务key | ||||
|      * 流程任务key TODO 芋艿,看看这个字段的作用 | ||||
|      */ | ||||
|     private String taskDefKey; | ||||
|     /** | ||||
|   | ||||
| @@ -17,8 +17,6 @@ public enum BpmTaskAssignRuleTypeEnum { | ||||
|     DEPT_LEADER(21, "部门的负责人"), | ||||
|     POST(22, "岗位"), | ||||
|     USER(30, "用户"), | ||||
|     USER_SIGN(31, "用户---会签"), | ||||
|     USER_OR_SIGN(32, "用户---或签"), | ||||
|     USER_GROUP(40, "用户组"), | ||||
|     SCRIPT(50, "自定义脚本"), // 例如说,发起人所在部门的领导、发起人所在部门的领导的领导 | ||||
|     ; | ||||
|   | ||||
| @@ -2,12 +2,7 @@ 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.framework.flowable.core.behavior.script.BpmTaskAssignScript; | ||||
| import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService; | ||||
| 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.permission.PermissionApi; | ||||
| 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; | ||||
| @@ -15,8 +10,6 @@ import org.springframework.beans.factory.ObjectProvider; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * BPM 模块的 Flowable 配置类 | ||||
|  * | ||||
| @@ -44,19 +37,10 @@ public class BpmFlowableConfiguration { | ||||
|     } | ||||
|  | ||||
|     @Bean | ||||
|     public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskAssignRuleService taskRuleService, | ||||
|                                                                  BpmUserGroupService userGroupService, | ||||
|                                                                  PermissionApi permissionApi, | ||||
|                                                                  DeptApi deptApi, | ||||
|                                                                  AdminUserApi adminUserApi, | ||||
|                                                                  List<BpmTaskAssignScript> scripts) { | ||||
|     public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskAssignRuleService taskRuleService) { | ||||
|         BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory(); | ||||
|         bpmActivityBehaviorFactory.setBpmTaskRuleService(taskRuleService); | ||||
|         bpmActivityBehaviorFactory.setUserGroupService(userGroupService); | ||||
|         bpmActivityBehaviorFactory.setAdminUserApi(adminUserApi); | ||||
|         bpmActivityBehaviorFactory.setPermissionApi(permissionApi); | ||||
|         bpmActivityBehaviorFactory.setDeptApi(deptApi); | ||||
|         bpmActivityBehaviorFactory.setScripts(scripts); | ||||
|         return bpmActivityBehaviorFactory; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,6 @@ | ||||
| package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | ||||
|  | ||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; | ||||
| import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService; | ||||
| 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.permission.PermissionApi; | ||||
| import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.Setter; | ||||
| @@ -17,8 +12,6 @@ import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; | ||||
| import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; | ||||
| import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * 自定义的 ActivityBehaviorFactory 实现类,目的如下: | ||||
|  * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配 | ||||
| @@ -32,41 +25,18 @@ public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory { | ||||
|  | ||||
|     @Setter | ||||
|     private BpmTaskAssignRuleService bpmTaskRuleService; | ||||
|     @Setter | ||||
|     private BpmUserGroupService userGroupService; | ||||
|  | ||||
|     @Setter | ||||
|     private PermissionApi permissionApi; | ||||
|     @Setter | ||||
|     private DeptApi deptApi; | ||||
|     @Setter | ||||
|     private AdminUserApi adminUserApi; | ||||
|     @Setter | ||||
|     private List<BpmTaskAssignScript> scripts; | ||||
|  | ||||
|     @Override | ||||
|     public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) { | ||||
|         BpmUserTaskActivityBehavior userTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask); | ||||
|         userTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService); | ||||
|         userTaskActivityBehavior.setPermissionApi(permissionApi); | ||||
|         userTaskActivityBehavior.setDeptApi(deptApi); | ||||
|         userTaskActivityBehavior.setUserGroupService(userGroupService); | ||||
|         userTaskActivityBehavior.setAdminUserApi(adminUserApi); | ||||
|         userTaskActivityBehavior.setScripts(scripts); | ||||
|         return userTaskActivityBehavior; | ||||
|         return new BpmUserTaskActivityBehavior(userTask) | ||||
|                 .setBpmTaskRuleService(bpmTaskRuleService); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity, | ||||
|                                                                              AbstractBpmnActivityBehavior innerActivityBehavior) { | ||||
|         BpmParallelMultiInstanceActivityBehavior bpmParallelMultiInstanceActivityBehavior = | ||||
|             new BpmParallelMultiInstanceActivityBehavior(activity, innerActivityBehavior); | ||||
|         bpmParallelMultiInstanceActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService); | ||||
|         bpmParallelMultiInstanceActivityBehavior.setPermissionApi(permissionApi); | ||||
|         bpmParallelMultiInstanceActivityBehavior.setDeptApi(deptApi); | ||||
|         bpmParallelMultiInstanceActivityBehavior.setUserGroupService(userGroupService); | ||||
|         bpmParallelMultiInstanceActivityBehavior.setAdminUserApi(adminUserApi); | ||||
|         bpmParallelMultiInstanceActivityBehavior.setScripts(scripts); | ||||
|         return bpmParallelMultiInstanceActivityBehavior; | ||||
|         return new BpmParallelMultiInstanceBehavior(activity, innerActivityBehavior) | ||||
|                 .setBpmTaskRuleService(bpmTaskRuleService); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,194 +0,0 @@ | ||||
| package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; | ||||
| import cn.iocoder.yudao.module.bpm.domain.enums.definition.BpmTaskAssignRuleTypeEnum; | ||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; | ||||
| import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService; | ||||
| 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.permission.PermissionApi; | ||||
| 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.Setter; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.flowable.bpmn.model.Activity; | ||||
| import org.flowable.common.engine.api.FlowableException; | ||||
| import org.flowable.engine.delegate.DelegateExecution; | ||||
| import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; | ||||
| import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; | ||||
| import org.flowable.engine.impl.util.CommandContextUtil; | ||||
|  | ||||
| import java.util.*; | ||||
|  | ||||
| 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.json.JsonUtils.toJsonString; | ||||
| import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER; | ||||
|  | ||||
| /** | ||||
|  * @author kemengkai | ||||
|  * @create 2022-04-21 16:57 | ||||
|  */ | ||||
| @Slf4j | ||||
| public class BpmParallelMultiInstanceActivityBehavior extends ParallelMultiInstanceBehavior { | ||||
|  | ||||
|     @Setter | ||||
|     private BpmTaskAssignRuleService bpmTaskRuleService; | ||||
|     @Setter | ||||
|     private BpmUserGroupService userGroupService; | ||||
|     @Setter | ||||
|     private DeptApi deptApi; | ||||
|     @Setter | ||||
|     private AdminUserApi adminUserApi; | ||||
|     @Setter | ||||
|     private PermissionApi permissionApi; | ||||
|     /** | ||||
|      * EL表达式集合模板 | ||||
|      */ | ||||
|     private final static String EXPRESSION_TEXT_TEMPLATE = "${coll_userList}"; | ||||
|  | ||||
|     /** | ||||
|      * 任务分配脚本 | ||||
|      */ | ||||
|     private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap(); | ||||
|  | ||||
|     public BpmParallelMultiInstanceActivityBehavior(Activity activity, | ||||
|         AbstractBpmnActivityBehavior innerActivityBehavior) { | ||||
|         super(activity, innerActivityBehavior); | ||||
|     } | ||||
|  | ||||
|     public void setScripts(List<BpmTaskAssignScript> scripts) { | ||||
|         this.scriptMap = convertMap(scripts, script -> script.getEnum().getId()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 创建并行任务 | ||||
|      * | ||||
|      * @param multiInstanceRootExecution 并行任务入参 | ||||
|      * | ||||
|      * @return 返回结果 | ||||
|      */ | ||||
|     @Override | ||||
|     protected int createInstances(DelegateExecution multiInstanceRootExecution) { | ||||
|         // 查找任务信息 | ||||
|         BpmTaskAssignRuleDO taskRule = getTaskRule(multiInstanceRootExecution); | ||||
|         // 获取任务用户 | ||||
|         Set<Long> assigneeUserIds = calculateTaskCandidateUsers(multiInstanceRootExecution, taskRule); | ||||
|         // 设置任务集合变量 | ||||
|         String expressionText = String.format("%s_userList", taskRule.getTaskDefinitionKey()); | ||||
|         // 设置任务集合变量与任务关系 | ||||
|         multiInstanceRootExecution.setVariable(expressionText, assigneeUserIds); | ||||
|         // 设置任务集合EL表达式 | ||||
|         this.collectionExpression = CommandContextUtil.getProcessEngineConfiguration().getExpressionManager() | ||||
|             .createExpression(String.format("${%s}", expressionText)); | ||||
|         // 根据会签,或签类型,设置会签,或签条件 | ||||
|         if (BpmTaskAssignRuleTypeEnum.USER_SIGN.getType().equals(taskRule.getType())) { | ||||
|             // 会签 | ||||
|             this.completionCondition = "${ nrOfInstances == nrOfCompletedInstances }"; | ||||
|         } else { | ||||
|             // 或签 | ||||
|             this.completionCondition = "${ nrOfCompletedInstances == 1 }"; | ||||
|         } | ||||
|         // 设置取出集合变量 | ||||
|         this.collectionElementVariable = "user"; | ||||
|         return super.createInstances(multiInstanceRootExecution); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Object resolveCollection(DelegateExecution execution) { | ||||
|         Object collection = null; | ||||
|         if (EXPRESSION_TEXT_TEMPLATE.equals(this.collectionExpression.getExpressionText())) { | ||||
|             // 查找任务信息 | ||||
|             BpmTaskAssignRuleDO taskRule = getTaskRule(execution); | ||||
|             // 设置任务集合变量 | ||||
|             String expressionText = String.format("%s_userList", execution.getCurrentActivityId()); | ||||
|             // 获取任务用户 | ||||
|             Set<Long> assigneeUserIds = calculateTaskCandidateUsers(execution, taskRule); | ||||
|             // 设置任务集合变量与任务关系 | ||||
|             execution.setVariable(expressionText, assigneeUserIds); | ||||
|             // 设置任务集合EL表达式 | ||||
|             this.collectionExpression = CommandContextUtil.getProcessEngineConfiguration().getExpressionManager() | ||||
|                 .createExpression(String.format("${%s}", expressionText)); | ||||
|         } | ||||
|         if (this.collectionExpression != null) { | ||||
|             collection = this.collectionExpression.getValue(execution); | ||||
|         } else if (this.collectionVariable != null) { | ||||
|             collection = execution.getVariable(this.collectionVariable); | ||||
|         } else if (this.collectionString != null) { | ||||
|             collection = this.collectionString; | ||||
|         } | ||||
|  | ||||
|         return collection; | ||||
|     } | ||||
|  | ||||
|     private BpmTaskAssignRuleDO getTaskRule(DelegateExecution task) { | ||||
|         List<BpmTaskAssignRuleDO> taskRules = | ||||
|             bpmTaskRuleService.getTaskAssignRuleListByProcessDefinitionId(task.getProcessDefinitionId(), | ||||
|                 task.getCurrentActivityId()); | ||||
|         if (CollUtil.isEmpty(taskRules)) { | ||||
|             throw new FlowableException( | ||||
|                 StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则", task.getId(), task.getProcessDefinitionId(), | ||||
|                     task.getCurrentActivityId())); | ||||
|         } | ||||
|         if (taskRules.size() > 1) { | ||||
|             throw new FlowableException( | ||||
|                 StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})", task.getId(), task.getProcessDefinitionId(), | ||||
|                     task.getCurrentActivityId(), taskRules.size())); | ||||
|         } | ||||
|         return taskRules.get(0); | ||||
|     } | ||||
|  | ||||
|     Set<Long> calculateTaskCandidateUsers(DelegateExecution task, BpmTaskAssignRuleDO rule) { | ||||
|         Set<Long> assigneeUserIds = null; | ||||
|         //        if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) { | ||||
|         //            assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule); | ||||
|         //        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) { | ||||
|         //            assigneeUserIds = calculateTaskCandidateUsersByDeptMember(task, rule); | ||||
|         //        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) { | ||||
|         //            assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(task, rule); | ||||
|         //        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) { | ||||
|         //            assigneeUserIds = calculateTaskCandidateUsersByPost(task, rule); | ||||
|         //        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) { | ||||
|         //            assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); | ||||
|         //        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) { | ||||
|         //            assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule); | ||||
|         //        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) { | ||||
|         //            assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule); | ||||
|         if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_SIGN.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersSignByUser(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersSignByUser(task, rule); | ||||
|         } | ||||
|  | ||||
|         // 移除被禁用的用户 | ||||
|         removeDisableUsers(assigneeUserIds); | ||||
|         // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 | ||||
|         if (CollUtil.isEmpty(assigneeUserIds)) { | ||||
|             log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", task.getId(), | ||||
|                 task.getProcessDefinitionId(), task.getCurrentActivityId(), toJsonString(rule)); | ||||
|             throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); | ||||
|         } | ||||
|         return assigneeUserIds; | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersSignByUser(DelegateExecution task, BpmTaskAssignRuleDO rule) { | ||||
|         return rule.getOptions(); | ||||
|     } | ||||
|  | ||||
|     @VisibleForTesting | ||||
|     void removeDisableUsers(Set<Long> assigneeUserIds) { | ||||
|         if (CollUtil.isEmpty(assigneeUserIds)) { | ||||
|             return; | ||||
|         } | ||||
|         //TODO 芋艿 这里有数据权限的问题。默认会加上数据权限 dept_id IN (deptId). 导致查询不到数据 | ||||
|         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,0 +1,116 @@ | ||||
| package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | ||||
| import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; | ||||
| import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService; | ||||
| import lombok.Setter; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.flowable.bpmn.model.Activity; | ||||
| import org.flowable.engine.delegate.DelegateExecution; | ||||
| import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; | ||||
| import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; | ||||
| import org.flowable.engine.impl.util.CommandContextUtil; | ||||
|  | ||||
| import java.util.Set; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||
| import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | ||||
| import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER; | ||||
|  | ||||
| /** | ||||
|  * @author kemengkai | ||||
|  * @create 2022-04-21 16:57 | ||||
|  */ | ||||
| @Slf4j | ||||
| public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior { | ||||
|  | ||||
|     /** | ||||
|      * EL表达式集合模板 | ||||
|      */ | ||||
|     private final static String EXPRESSION_TEXT_TEMPLATE = "${coll_userList}"; | ||||
|  | ||||
|     @Setter | ||||
|     private BpmTaskAssignRuleService bpmTaskRuleService; | ||||
|  | ||||
|     public BpmParallelMultiInstanceBehavior(Activity activity, | ||||
|                                             AbstractBpmnActivityBehavior innerActivityBehavior) { | ||||
|         super(activity, innerActivityBehavior); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 创建并行任务 | ||||
|      * | ||||
|      * @param multiInstanceRootExecution 并行任务入参 | ||||
|      * | ||||
|      * @return 返回结果 | ||||
|      */ | ||||
|     @Override | ||||
|     protected int createInstances(DelegateExecution multiInstanceRootExecution) { | ||||
|         // 查找任务信息 | ||||
| //        BpmTaskAssignRuleDO taskRule = getTaskRule(multiInstanceRootExecution); | ||||
|         BpmTaskAssignRuleDO taskRule = null; | ||||
|         // 获取任务用户 | ||||
|         Set<Long> assigneeUserIds = calculateTaskCandidateUsers(multiInstanceRootExecution, taskRule); | ||||
|         // 设置任务集合变量 | ||||
|         String expressionText = String.format("%s_userList", taskRule.getTaskDefinitionKey()); | ||||
|         // 设置任务集合变量与任务关系 | ||||
|         multiInstanceRootExecution.setVariable(expressionText, assigneeUserIds); | ||||
|         // 设置任务集合EL表达式 | ||||
|         this.collectionExpression = CommandContextUtil.getProcessEngineConfiguration().getExpressionManager() | ||||
|             .createExpression(String.format("${%s}", expressionText)); | ||||
|         // 根据会签,或签类型,设置会签,或签条件 | ||||
| //        if (BpmTaskAssignRuleTypeEnum.USER_SIGN.getType().equals(taskRule.getType())) { | ||||
| //            // 会签 | ||||
| //            this.completionCondition = "${ nrOfInstances == nrOfCompletedInstances }"; | ||||
| //        } else { | ||||
| //            // 或签 | ||||
| //            this.completionCondition = "${ nrOfCompletedInstances == 1 }"; | ||||
| //        } | ||||
|         // 设置取出集合变量 | ||||
|         this.collectionElementVariable = "user"; | ||||
|         return super.createInstances(multiInstanceRootExecution); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Object resolveCollection(DelegateExecution execution) { | ||||
|         Object collection = null; | ||||
|         if (EXPRESSION_TEXT_TEMPLATE.equals(this.collectionExpression.getExpressionText())) { | ||||
|             // 查找任务信息 | ||||
| //            BpmTaskAssignRuleDO taskRule = getTaskRule(execution); | ||||
|             BpmTaskAssignRuleDO taskRule = null; | ||||
|             // 设置任务集合变量 | ||||
|             String expressionText = String.format("%s_userList", execution.getCurrentActivityId()); | ||||
|             // 获取任务用户 | ||||
|             Set<Long> assigneeUserIds = calculateTaskCandidateUsers(execution, taskRule); | ||||
|             // 设置任务集合变量与任务关系 | ||||
|             execution.setVariable(expressionText, assigneeUserIds); | ||||
|             // 设置任务集合EL表达式 | ||||
|             this.collectionExpression = CommandContextUtil.getProcessEngineConfiguration().getExpressionManager() | ||||
|                 .createExpression(String.format("${%s}", expressionText)); | ||||
|         } | ||||
|         if (this.collectionExpression != null) { | ||||
|             collection = this.collectionExpression.getValue(execution); | ||||
|         } else if (this.collectionVariable != null) { | ||||
|             collection = execution.getVariable(this.collectionVariable); | ||||
|         } else if (this.collectionString != null) { | ||||
|             collection = this.collectionString; | ||||
|         } | ||||
|  | ||||
|         return collection; | ||||
|     } | ||||
|  | ||||
|     Set<Long> calculateTaskCandidateUsers(DelegateExecution task, BpmTaskAssignRuleDO rule) { | ||||
|         Set<Long> assigneeUserIds = SetUtils.asSet(1L, 104L); | ||||
|  | ||||
|         // 移除被禁用的用户 | ||||
|         // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 | ||||
|         if (CollUtil.isEmpty(assigneeUserIds)) { | ||||
|             log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", task.getId(), | ||||
|                 task.getProcessDefinitionId(), task.getCurrentActivityId(), toJsonString(rule)); | ||||
|             throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); | ||||
|         } | ||||
|         return assigneeUserIds; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -2,25 +2,11 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.util.RandomUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; | ||||
| 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.domain.enums.definition.BpmTaskAssignRuleTypeEnum; | ||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; | ||||
| import cn.iocoder.yudao.module.bpm.service.definition.BpmTaskAssignRuleService; | ||||
| 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.dto.DeptRespDTO; | ||||
| import cn.iocoder.yudao.module.system.api.permission.PermissionApi; | ||||
| 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.Setter; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.flowable.bpmn.model.UserTask; | ||||
| import org.flowable.common.engine.api.FlowableException; | ||||
| import org.flowable.common.engine.impl.el.ExpressionManager; | ||||
| import org.flowable.engine.delegate.DelegateExecution; | ||||
| import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; | ||||
| @@ -29,14 +15,9 @@ import org.flowable.engine.impl.util.TaskHelper; | ||||
| import org.flowable.task.service.TaskService; | ||||
| import org.flowable.task.service.impl.persistence.entity.TaskEntity; | ||||
|  | ||||
| import java.util.*; | ||||
|  | ||||
| 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.framework.common.util.json.JsonUtils.toJsonString; | ||||
| import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_ASSIGN_SCRIPT_NOT_EXISTS; | ||||
| import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
|  | ||||
| /** | ||||
|  * 自定义的流程任务的 assignee 负责人的分配 | ||||
| @@ -51,152 +32,23 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | ||||
|  | ||||
|     @Setter | ||||
|     private BpmTaskAssignRuleService bpmTaskRuleService; | ||||
|     @Setter | ||||
|     private BpmUserGroupService userGroupService; | ||||
|     @Setter | ||||
|     private DeptApi deptApi; | ||||
|     @Setter | ||||
|     private AdminUserApi adminUserApi; | ||||
|     @Setter | ||||
|     private PermissionApi permissionApi; | ||||
|  | ||||
|     /** | ||||
|      * 任务分配脚本 | ||||
|      */ | ||||
|     private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap(); | ||||
|  | ||||
|     public BpmUserTaskActivityBehavior(UserTask userTask) { | ||||
|         super(userTask); | ||||
|     } | ||||
|  | ||||
|     public void setScripts(List<BpmTaskAssignScript> scripts) { | ||||
|         this.scriptMap = convertMap(scripts, script -> script.getEnum().getId()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @DataPermission(enable = false) // 不需要处理数据权限, 不然会有问题,查询不到数据 | ||||
|     protected void handleAssignments(TaskService taskService, String assignee, String owner, | ||||
|         List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, | ||||
|         DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { | ||||
|         /*boolean isMultiInstance = hasMultiInstanceCharacteristics(); | ||||
|         if (isMultiInstance) { | ||||
|             //多实例 会签/或签,执行多次每个人 待办人都在execution里面获取 | ||||
|             Integer assigneeUserId = execution.getVariableLocal("user", Integer.class); | ||||
|             TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); | ||||
|         } else { | ||||
|             // 第一步,获得任务的规则 | ||||
|             BpmTaskAssignRuleDO rule = getTaskRule(task); | ||||
|             // 第二步,获得任务的候选用户们 | ||||
|             Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule); | ||||
|             // 第三步,设置一个作为负责人 | ||||
|         // 第一步,获得任务的候选用户们 | ||||
|         Set<Long> candidateUserIds = bpmTaskRuleService.calculateTaskCandidateUsers(task); | ||||
|         // 第二步,选择一个作为候选人 | ||||
|         Long assigneeUserId = chooseTaskAssignee(execution, candidateUserIds); | ||||
|         // 第三步,设置作为负责人 | ||||
|         TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); | ||||
|     } | ||||
|         */ | ||||
|         // 第一步,获得任务的规则 | ||||
|         BpmTaskAssignRuleDO rule = getTaskRule(task); | ||||
|         // 第二步,获得任务的候选用户们 | ||||
|         Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule); | ||||
|         // 第三步,设置一个作为负责人 | ||||
|         Long assigneeUserId = chooseTaskAssignee(execution, candidateUserIds); | ||||
|         TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); | ||||
|     } | ||||
|  | ||||
|     private BpmTaskAssignRuleDO getTaskRule(TaskEntity task) { | ||||
|         List<BpmTaskAssignRuleDO> taskRules = | ||||
|             bpmTaskRuleService.getTaskAssignRuleListByProcessDefinitionId(task.getProcessDefinitionId(), | ||||
|                 task.getTaskDefinitionKey()); | ||||
|         if (CollUtil.isEmpty(taskRules)) { | ||||
|             throw new FlowableException( | ||||
|                 StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则", task.getId(), task.getProcessDefinitionId(), | ||||
|                     task.getTaskDefinitionKey())); | ||||
|         } | ||||
|         if (taskRules.size() > 1) { | ||||
|             throw new FlowableException( | ||||
|                 StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})", task.getId(), task.getProcessDefinitionId(), | ||||
|                     task.getTaskDefinitionKey(), taskRules.size())); | ||||
|         } | ||||
|         return taskRules.get(0); | ||||
|     } | ||||
|  | ||||
|     Set<Long> calculateTaskCandidateUsers(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         Set<Long> assigneeUserIds = null; | ||||
|         if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByDeptMember(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByPost(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_SIGN.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); | ||||
|         } | ||||
|  | ||||
|         // 移除被禁用的用户 | ||||
|         removeDisableUsers(assigneeUserIds); | ||||
|         // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 | ||||
|         if (CollUtil.isEmpty(assigneeUserIds)) { | ||||
|             log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", task.getId(), | ||||
|                 task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule)); | ||||
|             throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); | ||||
|         } | ||||
|         return assigneeUserIds; | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByRole(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions()); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByDeptMember(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         List<AdminUserRespDTO> users = adminUserApi.getUsersByDeptIds(rule.getOptions()); | ||||
|         return convertSet(users, AdminUserRespDTO::getId); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         List<DeptRespDTO> depts = deptApi.getDepts(rule.getOptions()); | ||||
|         return convertSet(depts, DeptRespDTO::getLeaderUserId); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByPost(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         List<AdminUserRespDTO> users = adminUserApi.getUsersByPostIds(rule.getOptions()); | ||||
|         return convertSet(users, AdminUserRespDTO::getId); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         return rule.getOptions(); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions()); | ||||
|         Set<Long> userIds = new HashSet<>(); | ||||
|         userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds())); | ||||
|         return userIds; | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         // 获得对应的脚本 | ||||
|         List<BpmTaskAssignScript> scripts = new ArrayList<>(rule.getOptions().size()); | ||||
|         rule.getOptions().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(task))); | ||||
|         return userIds; | ||||
|     } | ||||
|  | ||||
|     private Long chooseTaskAssignee(DelegateExecution execution, Set<Long> candidateUserIds) { | ||||
|         // 获取任务变量 | ||||
| @@ -214,16 +66,4 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { | ||||
|         return CollUtil.get(candidateUserIds, index); | ||||
|     } | ||||
|  | ||||
|     @VisibleForTesting | ||||
|     void removeDisableUsers(Set<Long> assigneeUserIds) { | ||||
|         if (CollUtil.isEmpty(assigneeUserIds)) { | ||||
|             return; | ||||
|         } | ||||
|         //TODO 芋艿 这里有数据权限的问题。默认会加上数据权限 dept_id IN (deptId). 导致查询不到数据 | ||||
|         Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds); | ||||
|         assigneeUserIds.removeIf(id -> { | ||||
|             AdminUserRespDTO user = userMap.get(id); | ||||
|             return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,10 +4,12 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAs | ||||
| import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO; | ||||
| import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; | ||||
| import org.flowable.task.service.impl.persistence.entity.TaskEntity; | ||||
| import org.springframework.lang.Nullable; | ||||
|  | ||||
| import javax.validation.Valid; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| /** | ||||
|  * BPM 任务分配规则 Service 接口 | ||||
| @@ -84,4 +86,6 @@ public interface BpmTaskAssignRuleService { | ||||
|      */ | ||||
|     void checkTaskAssignRuleAllConfig(String id); | ||||
|  | ||||
|     Set<Long> calculateTaskCandidateUsers(TaskEntity task); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -3,25 +3,35 @@ 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.object.ObjectUtils; | ||||
| import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; | ||||
| import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; | ||||
| import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO; | ||||
| import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO; | ||||
| import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.bpm.convert.definition.BpmTaskAssignRuleConvert; | ||||
| 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.dal.mysql.definition.BpmTaskAssignRuleMapper; | ||||
| import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants; | ||||
| import cn.iocoder.yudao.module.bpm.domain.enums.definition.BpmTaskAssignRuleTypeEnum; | ||||
| import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants; | ||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; | ||||
| 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 lombok.extern.slf4j.Slf4j; | ||||
| import org.flowable.bpmn.model.BpmnModel; | ||||
| import org.flowable.bpmn.model.UserTask; | ||||
| import org.flowable.common.engine.api.FlowableException; | ||||
| import org.flowable.task.service.impl.persistence.entity.TaskEntity; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
| @@ -31,6 +41,9 @@ import javax.validation.Valid; | ||||
| import java.util.*; | ||||
|  | ||||
| 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.framework.common.util.json.JsonUtils.toJsonString; | ||||
| import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; | ||||
|  | ||||
| /** | ||||
| @@ -61,6 +74,17 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { | ||||
|     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 List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId, | ||||
| @@ -198,10 +222,6 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { | ||||
|             adminUserApi.validUsers(options); | ||||
|         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_GROUP.getType())) { | ||||
|             userGroupService.validUserGroups(options); | ||||
|         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_SIGN.getType())) { | ||||
|             adminUserApi.validUsers(options); | ||||
|         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType())) { | ||||
|             adminUserApi.validUsers(options); | ||||
|         } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) { | ||||
|             dictDataApi.validDictDatas(DictTypeConstants.TASK_ASSIGN_SCRIPT, | ||||
|                 CollectionUtils.convertSet(options, String::valueOf)); | ||||
| @@ -209,4 +229,117 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { | ||||
|             throw new IllegalArgumentException(StrUtil.format("未知的规则类型({})", type)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @DataPermission(enable = false) // 忽略数据权限,不然分配会存在问题 | ||||
|     public Set<Long> calculateTaskCandidateUsers(TaskEntity task) { | ||||
|         BpmTaskAssignRuleDO rule = getTaskRule(task); | ||||
|         return calculateTaskCandidateUsers(task, rule); | ||||
|     } | ||||
|  | ||||
|     @VisibleForTesting | ||||
|     BpmTaskAssignRuleDO getTaskRule(TaskEntity task) { | ||||
|         List<BpmTaskAssignRuleDO> taskRules = getTaskAssignRuleListByProcessDefinitionId( | ||||
|                 task.getProcessDefinitionId(), task.getTaskDefinitionKey()); | ||||
|         if (CollUtil.isEmpty(taskRules)) { | ||||
|             throw new FlowableException( | ||||
|                     StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则", task.getId(), task.getProcessDefinitionId(), | ||||
|                             task.getTaskDefinitionKey())); | ||||
|         } | ||||
|         if (taskRules.size() > 1) { | ||||
|             throw new FlowableException( | ||||
|                     StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})", task.getId(), task.getProcessDefinitionId(), | ||||
|                             task.getTaskDefinitionKey(), taskRules.size())); | ||||
|         } | ||||
|         return taskRules.get(0); | ||||
|     } | ||||
|  | ||||
|     @VisibleForTesting | ||||
|     Set<Long> calculateTaskCandidateUsers(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         Set<Long> assigneeUserIds = null; | ||||
|         if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByDeptMember(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByPost(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule); | ||||
|         } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) { | ||||
|             assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule); | ||||
|         } | ||||
|  | ||||
|         // 移除被禁用的用户 | ||||
|         removeDisableUsers(assigneeUserIds); | ||||
|         // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 | ||||
|         if (CollUtil.isEmpty(assigneeUserIds)) { | ||||
|             log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", task.getId(), | ||||
|                     task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule)); | ||||
|             throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); | ||||
|         } | ||||
|         return assigneeUserIds; | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByRole(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions()); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByDeptMember(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         List<AdminUserRespDTO> users = adminUserApi.getUsersByDeptIds(rule.getOptions()); | ||||
|         return convertSet(users, AdminUserRespDTO::getId); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         List<DeptRespDTO> depts = deptApi.getDepts(rule.getOptions()); | ||||
|         return convertSet(depts, DeptRespDTO::getLeaderUserId); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByPost(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         List<AdminUserRespDTO> users = adminUserApi.getUsersByPostIds(rule.getOptions()); | ||||
|         return convertSet(users, AdminUserRespDTO::getId); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         return rule.getOptions(); | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions()); | ||||
|         Set<Long> userIds = new HashSet<>(); | ||||
|         userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds())); | ||||
|         return userIds; | ||||
|     } | ||||
|  | ||||
|     private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskAssignRuleDO rule) { | ||||
|         // 获得对应的脚本 | ||||
|         List<BpmTaskAssignScript> scripts = new ArrayList<>(rule.getOptions().size()); | ||||
|         rule.getOptions().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(task))); | ||||
|         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()); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -15,7 +15,6 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; | ||||
| import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper; | ||||
| import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmActivityMapper; | ||||
| import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; | ||||
| import cn.iocoder.yudao.module.bpm.domain.enums.definition.BpmTaskAssignRuleTypeEnum; | ||||
| import cn.iocoder.yudao.module.bpm.domain.enums.task.BpmProcessInstanceResultEnum; | ||||
| import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; | ||||
| import cn.iocoder.yudao.module.business.hi.task.inst.service.HiTaskInstService; | ||||
| @@ -214,11 +213,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||||
|             taskAssignRuleMapper.selectListByProcessDefinitionId(task.getProcessDefinitionId(), | ||||
|                 task.getTaskDefinitionKey()); | ||||
|         if (CollUtil.isNotEmpty(bpmTaskAssignRuleList) && bpmTaskAssignRuleList.size() > 0) { | ||||
|             if (BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType().equals(bpmTaskAssignRuleList.get(0).getType())) { | ||||
|                 taskExtMapper.delTaskByProcInstIdAndTaskIdAndTaskDefKey( | ||||
|                     new BpmTaskExtDO().setTaskId(task.getId()).setTaskDefKey(task.getTaskDefinitionKey()) | ||||
|                         .setProcessInstanceId(task.getProcessInstanceId())); | ||||
|             } | ||||
|             // edit by 芋艿 | ||||
| //            if (BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType().equals(bpmTaskAssignRuleList.get(0).getType())) { | ||||
| //                taskExtMapper.delTaskByProcInstIdAndTaskIdAndTaskDefKey( | ||||
| //                    new BpmTaskExtDO().setTaskId(task.getId()).setTaskDefKey(task.getTaskDefinitionKey()) | ||||
| //                        .setProcessInstanceId(task.getProcessInstanceId())); | ||||
| //            } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPage | ||||
| import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; | ||||
| import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; | ||||
| import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper; | ||||
| import cn.iocoder.yudao.module.bpm.domain.enums.definition.BpmTaskAssignRuleTypeEnum; | ||||
| import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; | ||||
| import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||||
| @@ -21,14 +20,18 @@ import org.flowable.common.engine.impl.de.odysseus.el.ExpressionFactoryImpl; | ||||
| import org.flowable.common.engine.impl.de.odysseus.el.util.SimpleContext; | ||||
| import org.flowable.common.engine.impl.javax.el.ExpressionFactory; | ||||
| import org.flowable.common.engine.impl.javax.el.ValueExpression; | ||||
| import org.flowable.engine.*; | ||||
| import org.flowable.engine.HistoryService; | ||||
| import org.flowable.engine.RepositoryService; | ||||
| import org.flowable.engine.history.HistoricProcessInstance; | ||||
| import org.flowable.task.api.history.HistoricTaskInstance; | ||||
| import org.flowable.variable.api.history.HistoricVariableInstance; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.*; | ||||
| import java.util.HashMap; | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.function.Function; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| @@ -259,10 +262,11 @@ public class HiTaskInstService { | ||||
|                     DeptDO deptDO = deptMap.get(adminUserDO.getDeptId()); | ||||
|                     bpmTaskRespVO.setAssigneeUser(setUser(adminUserDO)); | ||||
|                     bpmTaskRespVO.getAssigneeUser().setDeptName(deptDO.getName()); | ||||
|                     if (!bpmTaskAssignRuleDO.getType().equals(BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType()) | ||||
|                         && !bpmTaskAssignRuleDO.getType().equals(BpmTaskAssignRuleTypeEnum.USER_SIGN.getType())) { | ||||
|                         break; | ||||
|                     } | ||||
|                     // edit by 芋艿 | ||||
| //                    if (!bpmTaskAssignRuleDO.getType().equals(BpmTaskAssignRuleTypeEnum.USER_OR_SIGN.getType()) | ||||
| //                        && !bpmTaskAssignRuleDO.getType().equals(BpmTaskAssignRuleTypeEnum.USER_SIGN.getType())) { | ||||
| //                        break; | ||||
| //                    } | ||||
|                 } | ||||
|             } | ||||
|             getFlow(bpmnModel, nextTaskDefKey, tmpBpmTaskAssignRuleDOList, bpmTaskRespVOList, userDoMap, deptMap, | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
|         SELECT * | ||||
|         FROM act_hi_taskinst; | ||||
|     </select> | ||||
|  | ||||
|     <select id="listAllByProcInstIdAndDelete" | ||||
|             resultType="cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmActivityDO"> | ||||
|         SELECT id_                AS `id`, | ||||
|   | ||||
| @@ -0,0 +1,96 @@ | ||||
| package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.impl; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||||
| import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | ||||
| import cn.iocoder.yudao.module.system.api.dept.DeptApi; | ||||
| import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | ||||
| import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | ||||
| import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | ||||
| import org.flowable.task.service.impl.persistence.entity.TaskEntity; | ||||
| import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; | ||||
| 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 org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest { | ||||
|  | ||||
|     @InjectMocks | ||||
|     private BpmTaskAssignLeaderX2Script script; | ||||
|  | ||||
|     @Mock | ||||
|     private AdminUserApi adminUserApi; | ||||
|     @Mock | ||||
|     private DeptApi deptApi; | ||||
|     @Mock | ||||
|     private BpmProcessInstanceService bpmProcessInstanceService; | ||||
|  | ||||
|     @Test | ||||
|     public void testCalculateTaskCandidateUsers_noDept() { | ||||
|         // 准备参数 | ||||
|         TaskEntity task = buildTaskEntity(1L); | ||||
|         // mock 方法(startUser) | ||||
|         AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L)); | ||||
|         when(adminUserApi.getUser(eq(1L))).thenReturn(startUser); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> result = script.calculateTaskCandidateUsers(task); | ||||
|         // 断言 | ||||
|         assertEquals(0, result.size()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCalculateTaskCandidateUsers_noParentDept() { | ||||
|         // 准备参数 | ||||
|         TaskEntity task = buildTaskEntity(1L); | ||||
|         // mock 方法(startUser) | ||||
|         AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L)); | ||||
|         when(adminUserApi.getUser(eq(1L))).thenReturn(startUser); | ||||
|         DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) | ||||
|                 .setLeaderUserId(20L)); | ||||
|         when(deptApi.getDept(eq(10L))).thenReturn(startUserDept); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> result = script.calculateTaskCandidateUsers(task); | ||||
|         // 断言 | ||||
|         assertEquals(asSet(20L), result); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCalculateTaskCandidateUsers_existParentDept() { | ||||
|         // 准备参数 | ||||
|         TaskEntity task = buildTaskEntity(1L); | ||||
|         // mock 方法(startUser) | ||||
|         AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L)); | ||||
|         when(adminUserApi.getUser(eq(1L))).thenReturn(startUser); | ||||
|         DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) | ||||
|                 .setLeaderUserId(20L)); | ||||
|         when(deptApi.getDept(eq(10L))).thenReturn(startUserDept); | ||||
|         // mock 方法(父 dept) | ||||
|         DeptRespDTO parentDept = randomPojo(DeptRespDTO.class, o -> o.setId(100L).setParentId(1000L) | ||||
|                 .setLeaderUserId(200L)); | ||||
|         when(deptApi.getDept(eq(100L))).thenReturn(parentDept); | ||||
|  | ||||
|         // 调用 | ||||
|         Set<Long> result = script.calculateTaskCandidateUsers(task); | ||||
|         // 断言 | ||||
|         assertEquals(asSet(200L), result); | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("SameParameterValue") | ||||
|     private TaskEntity buildTaskEntity(Long startUserId) { | ||||
|         TaskEntityImpl task = new TaskEntityImpl(); | ||||
| //        task.setProcessInstance(new ExecutionEntityImpl()); | ||||
| //        task.getProcessInstance().setStartUserId(String.valueOf(startUserId)); | ||||
|         // TODO | ||||
|         return task; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,217 @@ | ||||
| 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.domain.enums.definition.BpmTaskAssignRuleTypeEnum; | ||||
| import cn.iocoder.yudao.module.bpm.domain.enums.definition.BpmTaskRuleScriptEnum; | ||||
| import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; | ||||
| 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.permission.PermissionApi; | ||||
| import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | ||||
| import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | ||||
| import org.flowable.task.service.impl.persistence.entity.TaskEntity; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.boot.test.mock.mockito.MockBean; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.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) | ||||
| 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; | ||||
|  | ||||
|     @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.getUsersByDeptIds(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.getDepts(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.getUsersByPostIds(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(TaskEntity task) { | ||||
|                 return singleton(11L); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public BpmTaskRuleScriptEnum getEnum() { | ||||
|                 return BpmTaskRuleScriptEnum.LEADER_X1; | ||||
|             } | ||||
|         }; | ||||
|         BpmTaskAssignScript script2 = new BpmTaskAssignScript() { | ||||
|  | ||||
|             @Override | ||||
|             public Set<Long> calculateTaskCandidateUsers(TaskEntity 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); | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV