Commit 620a0d8c2ce29b0e17ca541d676281e076a3ff0f

Authored by jason
1 parent ae7dbd3c

仿钉钉流程设计- 新增连续多级部门负责人审批策略

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 ;
... ...