Commit 620a0d8c2ce29b0e17ca541d676281e076a3ff0f
1 parent
ae7dbd3c
仿钉钉流程设计- 新增连续多级部门负责人审批策略
Showing
5 changed files
with
278 additions
and
0 deletions
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractDeptLeaderStrategy.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; | |
| 2 | + | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 4 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; | |
| 5 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 6 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 7 | + | |
| 8 | +import java.util.Collections; | |
| 9 | +import java.util.LinkedHashSet; | |
| 10 | +import java.util.Set; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * 部门的负责人 {@link BpmTaskCandidateStrategy} 抽象类 | |
| 14 | + * | |
| 15 | + * @author jason | |
| 16 | + */ | |
| 17 | +public abstract class BpmTaskCandidateAbstractDeptLeaderStrategy implements BpmTaskCandidateStrategy { | |
| 18 | + | |
| 19 | + protected DeptApi deptApi; | |
| 20 | + | |
| 21 | + public BpmTaskCandidateAbstractDeptLeaderStrategy(DeptApi deptApi) { | |
| 22 | + this.deptApi = deptApi; | |
| 23 | + } | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 获取上级部门的负责人 | |
| 27 | + * | |
| 28 | + * @param assignDept 指定部门 | |
| 29 | + * @param level 第几级 | |
| 30 | + * @return 部门负责人 Id | |
| 31 | + */ | |
| 32 | + protected Long getAssignLevelDeptLeaderId(DeptRespDTO assignDept, Integer level) { | |
| 33 | + Assert.isTrue(level > 0, "level 必须大于 0"); | |
| 34 | + if (assignDept == null) { | |
| 35 | + return null; | |
| 36 | + } | |
| 37 | + DeptRespDTO dept = assignDept; | |
| 38 | + for (int i = 1; i < level; i++) { | |
| 39 | + DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()); | |
| 40 | + if (parentDept == null) { // 找不到父级部门,到了最高级。返回最高级的部门负责人 | |
| 41 | + break; | |
| 42 | + } | |
| 43 | + dept = parentDept; | |
| 44 | + } | |
| 45 | + return dept.getLeaderUserId(); | |
| 46 | + } | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * 获取连续上级部门的负责人, 包含指定部门的负责人 | |
| 50 | + * | |
| 51 | + * @param assignDept 指定部门 | |
| 52 | + * @param level 第几级 | |
| 53 | + * @return 连续部门负责人 Id | |
| 54 | + */ | |
| 55 | + protected Set<Long> getMultiLevelDeptLeaderIds(DeptRespDTO assignDept, Integer level){ | |
| 56 | + Assert.isTrue(level > 0, "level 必须大于 0"); | |
| 57 | + if (assignDept == null) { | |
| 58 | + return Collections.emptySet(); | |
| 59 | + } | |
| 60 | + Set<Long> deptLeaderIds = new LinkedHashSet<>(); // 保证有序 | |
| 61 | + DeptRespDTO dept = assignDept; | |
| 62 | + for (int i = 0; i < level; i++) { | |
| 63 | + if (dept.getLeaderUserId() != null) { | |
| 64 | + deptLeaderIds.add(dept.getLeaderUserId()); | |
| 65 | + } | |
| 66 | + DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()); | |
| 67 | + if (parentDept == null) { // 找不到父级部门. 已经到了最高层级了 | |
| 68 | + break; | |
| 69 | + } | |
| 70 | + dept = parentDept; | |
| 71 | + } | |
| 72 | + return deptLeaderIds; | |
| 73 | + } | |
| 74 | + | |
| 75 | +} | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateMultiLevelDeptLeaderStrategy.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 5 | +import cn.iocoder.yudao.framework.common.util.string.StrUtils; | |
| 6 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; | |
| 7 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; | |
| 8 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 9 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 10 | +import org.flowable.engine.delegate.DelegateExecution; | |
| 11 | +import org.springframework.stereotype.Component; | |
| 12 | + | |
| 13 | +import java.util.List; | |
| 14 | +import java.util.Set; | |
| 15 | + | |
| 16 | +/** | |
| 17 | + * 连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 | |
| 18 | + * | |
| 19 | + * @author jason | |
| 20 | + */ | |
| 21 | +@Component | |
| 22 | +public class BpmTaskCandidateMultiLevelDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy { | |
| 23 | + | |
| 24 | + public BpmTaskCandidateMultiLevelDeptLeaderStrategy(DeptApi deptApi) { | |
| 25 | + super(deptApi); | |
| 26 | + } | |
| 27 | + | |
| 28 | + @Override | |
| 29 | + public BpmTaskCandidateStrategyEnum getStrategy() { | |
| 30 | + return BpmTaskCandidateStrategyEnum.MULTI_LEVEL_DEPT_LEADER; | |
| 31 | + } | |
| 32 | + | |
| 33 | + @Override | |
| 34 | + public void validateParam(String param) { | |
| 35 | + // 参数格式: ,分割。 前面一个指定指定部门Id, 后面一个是部门的层级 | |
| 36 | + List<Long> params = StrUtils.splitToLong(param, ","); | |
| 37 | + Assert.isTrue(params.size() == 2, "参数格式不匹配"); | |
| 38 | + deptApi.validateDeptList(CollUtil.toList(params.get(0))); | |
| 39 | + Assert.isTrue(params.get(1) > 0, "部门层级必须大于 0"); | |
| 40 | + } | |
| 41 | + | |
| 42 | + @Override | |
| 43 | + public Set<Long> calculateUsers(DelegateExecution execution, String param) { | |
| 44 | + // 参数格式: ,分割。 前面一个指定指定部门Id, 后面一个是审批的层级 | |
| 45 | + List<Long> params = StrUtils.splitToLong(param, ","); | |
| 46 | + // TODO @芋艿 是否要支持多个部门。 是不是这种场景,一个部门就可以了 | |
| 47 | + Long deptId = params.get(0); | |
| 48 | + Long level = params.get(1); | |
| 49 | + DeptRespDTO dept = deptApi.getDept(deptId); | |
| 50 | + return getMultiLevelDeptLeaderIds(dept, level.intValue()); | |
| 51 | + } | |
| 52 | + | |
| 53 | +} | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; | |
| 2 | + | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |
| 5 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; | |
| 6 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; | |
| 7 | +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | |
| 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 | +import jakarta.annotation.Resource; | |
| 13 | +import org.flowable.engine.delegate.DelegateExecution; | |
| 14 | +import org.flowable.engine.runtime.ProcessInstance; | |
| 15 | +import org.springframework.context.annotation.Lazy; | |
| 16 | +import org.springframework.stereotype.Component; | |
| 17 | + | |
| 18 | +import java.util.Set; | |
| 19 | + | |
| 20 | +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | |
| 21 | +import static java.util.Collections.emptySet; | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * 发起人的部门负责人, 可以是上级部门负责人 {@link BpmTaskCandidateStrategy} 实现类 | |
| 25 | + * | |
| 26 | + * @author jason | |
| 27 | + */ | |
| 28 | +@Component | |
| 29 | +public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy { | |
| 30 | + @Resource | |
| 31 | + @Lazy | |
| 32 | + private BpmProcessInstanceService processInstanceService; | |
| 33 | + @Resource | |
| 34 | + private AdminUserApi adminUserApi; | |
| 35 | + @Override | |
| 36 | + public BpmTaskCandidateStrategyEnum getStrategy() { | |
| 37 | + return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER; | |
| 38 | + } | |
| 39 | + | |
| 40 | + public BpmTaskCandidateStartUserDeptLeaderStrategy(DeptApi deptApi) { | |
| 41 | + super(deptApi); | |
| 42 | + } | |
| 43 | + | |
| 44 | + @Override | |
| 45 | + public void validateParam(String param) { | |
| 46 | + // 参数是部门的层级 | |
| 47 | + Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0"); | |
| 48 | + } | |
| 49 | + | |
| 50 | + @Override | |
| 51 | + public Set<Long> calculateUsers(DelegateExecution execution, String param) { | |
| 52 | + // 获得流程发起人 | |
| 53 | + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); | |
| 54 | + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); | |
| 55 | + | |
| 56 | + DeptRespDTO dept = getStartUserDept(startUserId); | |
| 57 | + Long deptLeaderId = getAssignLevelDeptLeaderId(dept, Integer.valueOf(param)); // 参数是部门的层级 | |
| 58 | + return deptLeaderId != null ? asSet(deptLeaderId) : emptySet(); | |
| 59 | + } | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * 获取发起人的部门 | |
| 63 | + * | |
| 64 | + * @param startUserId 发起人 Id | |
| 65 | + */ | |
| 66 | + protected DeptRespDTO getStartUserDept(Long startUserId) { | |
| 67 | + AdminUserRespDTO startUser = adminUserApi.getUser(startUserId); | |
| 68 | + if (startUser.getDeptId() == null) { // 找不到部门 | |
| 69 | + return null; | |
| 70 | + } | |
| 71 | + return deptApi.getDept(startUser.getDeptId()); | |
| 72 | + } | |
| 73 | + | |
| 74 | +} | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserMultiLevelDeptLeaderStrategy.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; | |
| 2 | + | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |
| 5 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; | |
| 6 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; | |
| 7 | +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | |
| 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 | +import jakarta.annotation.Resource; | |
| 13 | +import org.flowable.engine.delegate.DelegateExecution; | |
| 14 | +import org.flowable.engine.runtime.ProcessInstance; | |
| 15 | +import org.springframework.context.annotation.Lazy; | |
| 16 | +import org.springframework.stereotype.Component; | |
| 17 | + | |
| 18 | +import java.util.Set; | |
| 19 | + | |
| 20 | +/** | |
| 21 | + * 发起人连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 | |
| 22 | + * | |
| 23 | + * @author jason | |
| 24 | + */ | |
| 25 | +@Component | |
| 26 | +public class BpmTaskCandidateStartUserMultiLevelDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy { | |
| 27 | + | |
| 28 | + @Resource | |
| 29 | + @Lazy | |
| 30 | + private BpmProcessInstanceService processInstanceService; | |
| 31 | + | |
| 32 | + @Resource | |
| 33 | + private AdminUserApi adminUserApi; | |
| 34 | + | |
| 35 | + public BpmTaskCandidateStartUserMultiLevelDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) { | |
| 36 | + super(deptApi); | |
| 37 | + } | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public BpmTaskCandidateStrategyEnum getStrategy() { | |
| 41 | + return BpmTaskCandidateStrategyEnum.START_USER_MULTI_LEVEL_DEPT_LEADER; | |
| 42 | + } | |
| 43 | + | |
| 44 | + @Override | |
| 45 | + public void validateParam(String param) { | |
| 46 | + // 参数是部门的层级 | |
| 47 | + Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0"); | |
| 48 | + } | |
| 49 | + | |
| 50 | + @Override | |
| 51 | + public Set<Long> calculateUsers(DelegateExecution execution, String param) { | |
| 52 | + // 获得流程发起人 | |
| 53 | + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); | |
| 54 | + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); | |
| 55 | + | |
| 56 | + DeptRespDTO dept = getStartUserDept(startUserId); | |
| 57 | + return getMultiLevelDeptLeaderIds(dept, Integer.valueOf(param)); // 参数是部门的层级 | |
| 58 | + } | |
| 59 | + | |
| 60 | + /** | |
| 61 | + * 获取发起人的部门 | |
| 62 | + * | |
| 63 | + * @param startUserId 发起人 Id | |
| 64 | + */ | |
| 65 | + protected DeptRespDTO getStartUserDept(Long startUserId) { | |
| 66 | + AdminUserRespDTO startUser = adminUserApi.getUser(startUserId); | |
| 67 | + if (startUser.getDeptId() == null) { // 找不到部门 | |
| 68 | + return null; | |
| 69 | + } | |
| 70 | + return deptApi.getDept(startUser.getDeptId()); | |
| 71 | + } | |
| 72 | + | |
| 73 | +} | ... | ... |
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 { |
| 21 | 21 | ROLE(10, "角色"), |
| 22 | 22 | DEPT_MEMBER(20, "部门的成员"), // 包括负责人 |
| 23 | 23 | DEPT_LEADER(21, "部门的负责人"), |
| 24 | + MULTI_LEVEL_DEPT_LEADER(23, "连续多级部门的负责人"), | |
| 24 | 25 | POST(22, "岗位"), |
| 25 | 26 | USER(30, "用户"), |
| 26 | 27 | START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 |
| 27 | 28 | START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 |
| 29 | + START_USER_DEPT_LEADER(37, "发起人部门负责人"), // 可以是发起人上级部门负责人 | |
| 30 | + START_USER_MULTI_LEVEL_DEPT_LEADER(38, "发起人连续多级部门的负责人"), | |
| 28 | 31 | USER_GROUP(40, "用户组"), |
| 29 | 32 | EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager |
| 30 | 33 | ; | ... | ... |