diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractDeptLeaderStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractDeptLeaderStrategy.java new file mode 100644 index 000000000..a6e0790c6 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractDeptLeaderStrategy.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * 部门的负责人 {@link BpmTaskCandidateStrategy} 抽象类 + * + * @author jason + */ +public abstract class BpmTaskCandidateAbstractDeptLeaderStrategy implements BpmTaskCandidateStrategy { + + protected DeptApi deptApi; + + public BpmTaskCandidateAbstractDeptLeaderStrategy(DeptApi deptApi) { + this.deptApi = deptApi; + } + + /** + * 获取上级部门的负责人 + * + * @param assignDept 指定部门 + * @param level 第几级 + * @return 部门负责人 Id + */ + protected Long getAssignLevelDeptLeaderId(DeptRespDTO assignDept, Integer level) { + Assert.isTrue(level > 0, "level 必须大于 0"); + if (assignDept == null) { + return null; + } + DeptRespDTO dept = assignDept; + for (int i = 1; i < level; i++) { + DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()); + if (parentDept == null) { // 找不到父级部门,到了最高级。返回最高级的部门负责人 + break; + } + dept = parentDept; + } + return dept.getLeaderUserId(); + } + + /** + * 获取连续上级部门的负责人, 包含指定部门的负责人 + * + * @param assignDept 指定部门 + * @param level 第几级 + * @return 连续部门负责人 Id + */ + protected Set getMultiLevelDeptLeaderIds(DeptRespDTO assignDept, Integer level){ + Assert.isTrue(level > 0, "level 必须大于 0"); + if (assignDept == null) { + return Collections.emptySet(); + } + Set deptLeaderIds = new LinkedHashSet<>(); // 保证有序 + DeptRespDTO dept = assignDept; + for (int i = 0; i < level; i++) { + if (dept.getLeaderUserId() != null) { + deptLeaderIds.add(dept.getLeaderUserId()); + } + DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()); + if (parentDept == null) { // 找不到父级部门. 已经到了最高层级了 + break; + } + dept = parentDept; + } + return deptLeaderIds; + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateMultiLevelDeptLeaderStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateMultiLevelDeptLeaderStrategy.java new file mode 100644 index 000000000..c3eb064e1 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateMultiLevelDeptLeaderStrategy.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +/** + * 连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateMultiLevelDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy { + + public BpmTaskCandidateMultiLevelDeptLeaderStrategy(DeptApi deptApi) { + super(deptApi); + } + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.MULTI_LEVEL_DEPT_LEADER; + } + + @Override + public void validateParam(String param) { + // 参数格式: ,分割。 前面一个指定指定部门Id, 后面一个是部门的层级 + List params = StrUtils.splitToLong(param, ","); + Assert.isTrue(params.size() == 2, "参数格式不匹配"); + deptApi.validateDeptList(CollUtil.toList(params.get(0))); + Assert.isTrue(params.get(1) > 0, "部门层级必须大于 0"); + } + + @Override + public Set calculateUsers(DelegateExecution execution, String param) { + // 参数格式: ,分割。 前面一个指定指定部门Id, 后面一个是审批的层级 + List params = StrUtils.splitToLong(param, ","); + // TODO @芋艿 是否要支持多个部门。 是不是这种场景,一个部门就可以了 + Long deptId = params.get(0); + Long level = params.get(1); + DeptRespDTO dept = deptApi.getDept(deptId); + return getMultiLevelDeptLeaderIds(dept, level.intValue()); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java new file mode 100644 index 000000000..972d2909d --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static java.util.Collections.emptySet; + +/** + * 发起人的部门负责人, 可以是上级部门负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy { + @Resource + @Lazy + private BpmProcessInstanceService processInstanceService; + @Resource + private AdminUserApi adminUserApi; + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER; + } + + public BpmTaskCandidateStartUserDeptLeaderStrategy(DeptApi deptApi) { + super(deptApi); + } + + @Override + public void validateParam(String param) { + // 参数是部门的层级 + Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0"); + } + + @Override + public Set calculateUsers(DelegateExecution execution, String param) { + // 获得流程发起人 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + + DeptRespDTO dept = getStartUserDept(startUserId); + Long deptLeaderId = getAssignLevelDeptLeaderId(dept, Integer.valueOf(param)); // 参数是部门的层级 + return deptLeaderId != null ? asSet(deptLeaderId) : emptySet(); + } + + /** + * 获取发起人的部门 + * + * @param startUserId 发起人 Id + */ + protected DeptRespDTO getStartUserDept(Long startUserId) { + AdminUserRespDTO startUser = adminUserApi.getUser(startUserId); + if (startUser.getDeptId() == null) { // 找不到部门 + return null; + } + return deptApi.getDept(startUser.getDeptId()); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserMultiLevelDeptLeaderStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserMultiLevelDeptLeaderStrategy.java new file mode 100644 index 000000000..326ea8870 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserMultiLevelDeptLeaderStrategy.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * 发起人连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateStartUserMultiLevelDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy { + + @Resource + @Lazy + private BpmProcessInstanceService processInstanceService; + + @Resource + private AdminUserApi adminUserApi; + + public BpmTaskCandidateStartUserMultiLevelDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) { + super(deptApi); + } + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER_MULTI_LEVEL_DEPT_LEADER; + } + + @Override + public void validateParam(String param) { + // 参数是部门的层级 + Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0"); + } + + @Override + public Set calculateUsers(DelegateExecution execution, String param) { + // 获得流程发起人 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + + DeptRespDTO dept = getStartUserDept(startUserId); + return getMultiLevelDeptLeaderIds(dept, Integer.valueOf(param)); // 参数是部门的层级 + } + + /** + * 获取发起人的部门 + * + * @param startUserId 发起人 Id + */ + protected DeptRespDTO getStartUserDept(Long startUserId) { + AdminUserRespDTO startUser = adminUserApi.getUser(startUserId); + if (startUser.getDeptId() == null) { // 找不到部门 + return null; + } + return deptApi.getDept(startUser.getDeptId()); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java index 78628d0da..6a37bc616 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -21,10 +21,13 @@ public enum BpmTaskCandidateStrategyEnum implements IntArrayValuable { ROLE(10, "角色"), DEPT_MEMBER(20, "部门的成员"), // 包括负责人 DEPT_LEADER(21, "部门的负责人"), + MULTI_LEVEL_DEPT_LEADER(23, "连续多级部门的负责人"), POST(22, "岗位"), USER(30, "用户"), START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 + START_USER_DEPT_LEADER(37, "发起人部门负责人"), // 可以是发起人上级部门负责人 + START_USER_MULTI_LEVEL_DEPT_LEADER(38, "发起人连续多级部门的负责人"), USER_GROUP(40, "用户组"), EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager ;