Commit 902f2ecbadb7e637f28f12ec2e2656dc3565089c

Authored by YunaiV
1 parent f4609024

BPM 模型重构 7:任务分配规则的后端,全部实现完成

yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java
... ... @@ -45,8 +45,10 @@ public interface BpmErrorCodeConstants {
45 45  
46 46 // ========== 流程任务分配规则 1-009-006-000 ==========
47 47 ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则");
48   - ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006000, "流程任务分配规则不存在");
49   - ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1009006000, "只有流程模型的任务分配规则,才允许被修改");
  48 + ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006001, "流程任务分配规则不存在");
  49 + ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1009006002, "只有流程模型的任务分配规则,才允许被修改");
  50 + ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1009006003, "操作失败,原因:找不到任务的审批人!");
  51 + ErrorCode TASK_ASSIGN_SCRIPT_NOT_EXISTS = new ErrorCode(1009006004, "操作失败,原因:任务分配脚本({}) 不存在");
50 52  
51 53 // ========== 动态表单模块 1-009-010-000 ==========
52 54 ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在");
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java
... ... @@ -4,11 +4,18 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.RandomUtil;
5 5 import cn.hutool.core.util.StrUtil;
6 6 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
  7 +import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmUserGroupDO;
7 8 import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
  9 +import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.BpmTaskAssignScript;
8 10 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskAssignRuleService;
  11 +import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmUserGroupService;
  12 +import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO;
  13 +import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
9 14 import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
10   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  15 +import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
  16 +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
11 17 import lombok.Setter;
  18 +import lombok.extern.slf4j.Slf4j;
12 19 import org.activiti.bpmn.model.UserTask;
13 20 import org.activiti.engine.ActivitiException;
14 21 import org.activiti.engine.delegate.DelegateExecution;
... ... @@ -17,26 +24,49 @@ import org.activiti.engine.impl.el.ExpressionManager;
17 24 import org.activiti.engine.impl.persistence.entity.TaskEntity;
18 25 import org.activiti.engine.impl.persistence.entity.TaskEntityManager;
19 26  
20   -import java.util.List;
21   -import java.util.Objects;
22   -import java.util.Set;
  27 +import javax.annotation.Resource;
  28 +import java.util.*;
  29 +import java.util.function.Consumer;
  30 +import java.util.function.Function;
  31 +
  32 +import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.TASK_ASSIGN_SCRIPT_NOT_EXISTS;
  33 +import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER;
  34 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*;
  35 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  36 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  37 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
23 38  
24 39 /**
25 40 * 自定义的流程任务的 assignee 负责人的分配
26 41 *
27 42 * @author 芋道源码
28 43 */
  44 +@Slf4j
29 45 public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
30 46  
31 47 @Setter
32 48 private BpmTaskAssignRuleService bpmTaskRuleService;
33 49 @Setter
34 50 private SysPermissionService permissionService;
  51 + @Setter
  52 + private SysDeptService deptService;
  53 + @Setter
  54 + private BpmUserGroupService userGroupService;
  55 + @Resource
  56 + private SysUserService userService;
  57 + /**
  58 + * 任务分配脚本
  59 + */
  60 + private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
35 61  
36 62 public BpmUserTaskActivitiBehavior(UserTask userTask) {
37 63 super(userTask);
38 64 }
39 65  
  66 + public void setScripts(List<BpmTaskAssignScript> scripts) {
  67 + this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
  68 + }
  69 +
40 70 @Override
41 71 protected void handleAssignments(TaskEntityManager taskEntityManager,
42 72 String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups,
... ... @@ -51,7 +81,7 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
51 81 // 设置候选人们
52 82 candidateUserIds.remove(assigneeUserId); // 已经成为负责人了,就不要在扮演候选人了
53 83 if (CollUtil.isNotEmpty(candidateUserIds)) {
54   - task.addCandidateUsers(CollectionUtils.convertSet(candidateUserIds, String::valueOf));
  84 + task.addCandidateUsers(convertSet(candidateUserIds, String::valueOf));
55 85 }
56 86 }
57 87  
... ... @@ -80,20 +110,23 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
80 110 if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) {
81 111 assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule);
82 112 } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
83   - assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule);
  113 + assigneeUserIds = calculateTaskCandidateUsersByMember(task, rule);
84 114 } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
85   - assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule);
86   - } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) {
  115 + assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(task, rule);
  116 + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) {
87 117 assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
88 118 } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) {
89   - assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
  119 + assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule);
  120 + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) {
  121 + assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule);
90 122 }
91 123  
92 124 // TODO 芋艿:统一过滤掉被禁用的用户
93 125 // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人
94 126 if (CollUtil.isEmpty(assigneeUserIds)) {
95   - throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 任务规则({}) 找不到候选人",
96   - task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey()));
  127 + log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]",
  128 + task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule));
  129 + throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
97 130 }
98 131 return assigneeUserIds;
99 132 }
... ... @@ -102,12 +135,14 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
102 135 return permissionService.getUserRoleIdListByRoleIds(rule.getOptions());
103 136 }
104 137  
105   - private Set<Long> calculateTaskCandidateUsersByDept(TaskEntity task, BpmTaskAssignRuleDO rule) {
106   - throw new UnsupportedOperationException("暂不支持该任务规则");
  138 + private Set<Long> calculateTaskCandidateUsersByMember(TaskEntity task, BpmTaskAssignRuleDO rule) {
  139 + List<SysUserDO> users = userService.getUsersByPostIds(rule.getOptions());
  140 + return convertSet(users, SysUserDO::getId);
107 141 }
108 142  
109 143 private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) {
110   - throw new UnsupportedOperationException("暂不支持该任务规则");
  144 + List<SysDeptDO> depts = deptService.getDepts(rule.getOptions());
  145 + return convertSet(depts, SysDeptDO::getLeaderUserId);
111 146 }
112 147  
113 148 private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) {
... ... @@ -115,11 +150,26 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
115 150 }
116 151  
117 152 private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) {
118   - throw new UnsupportedOperationException("暂不支持该任务规则");
  153 + List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
  154 + Set<Long> userIds = new HashSet<>();
  155 + userGroups.forEach(bpmUserGroupDO -> userIds.addAll(bpmUserGroupDO.getMemberUserIds()));
  156 + return userIds;
119 157 }
120 158  
121 159 private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskAssignRuleDO rule) {
122   - throw new UnsupportedOperationException("暂不支持该任务规则");
  160 + // 获得对应的脚本
  161 + List<BpmTaskAssignScript> scripts = new ArrayList<>(rule.getOptions().size());
  162 + rule.getOptions().forEach(id -> {
  163 + BpmTaskAssignScript script = scriptMap.get(id);
  164 + if (script == null) {
  165 + throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id);
  166 + }
  167 + scripts.add(script);
  168 + });
  169 + // 逐个计算任务
  170 + Set<Long> userIds = new HashSet<>();
  171 + scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(task)));
  172 + return userIds;
123 173 }
124 174  
125 175 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java
1 1 package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script;
2 2  
  3 +import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum;
3 4 import org.activiti.engine.impl.persistence.entity.TaskEntity;
4 5  
5 6 import java.util.List;
... ... @@ -23,4 +24,11 @@ public interface BpmTaskAssignScript {
23 24 */
24 25 List<Long> calculateTaskCandidateUsers(TaskEntity task);
25 26  
  27 + /**
  28 + * 获得枚举值
  29 + *
  30 + * @return 枚举值
  31 + */
  32 + BpmTaskRuleScriptEnum getEnum();
  33 +
26 34 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/user/SysUserMapper.java
... ... @@ -68,5 +68,9 @@ public interface SysUserMapper extends BaseMapperX&lt;SysUserDO&gt; {
68 68 return selectList("status", status);
69 69 }
70 70  
  71 + default List<SysUserDO> selectListByDeptIds(Collection<Integer> deptIds) {
  72 + return selectList("dept_id", deptIds);
  73 + }
  74 +
71 75 }
72 76  
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/SysDeptService.java
... ... @@ -85,6 +85,14 @@ public interface SysDeptService {
85 85 SysDeptDO getDept(Long id);
86 86  
87 87 /**
  88 + * 获得部门信息数组
  89 + *
  90 + * @param ids 部门编号数组
  91 + * @return 部门信息数组
  92 + */
  93 + List<SysDeptDO> getDepts(Collection<Long> ids);
  94 +
  95 + /**
88 96 * 获得所有子部门,从缓存中
89 97 *
90 98 * @param parentId 部门编号
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java
1 1 package cn.iocoder.yudao.adminserver.modules.system.service.dept.impl;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.SysRoleDO;
5 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 5 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
7 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
... ... @@ -233,6 +232,11 @@ public class SysDeptServiceImpl implements SysDeptService {
233 232 return deptMapper.selectById(id);
234 233 }
235 234  
  235 + @Override
  236 + public List<SysDeptDO> getDepts(Collection<Long> ids) {
  237 + return deptMapper.selectBatchIds(ids);
  238 + }
  239 +
236 240 private void checkCreateOrUpdate(Long id, Long parentId, String name) {
237 241 // 校验自己存在
238 242 checkDeptExists(id);
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserService.java
... ... @@ -175,6 +175,22 @@ public interface SysUserService {
175 175 List<SysUserDO> getUsersByStatus(Integer status);
176 176  
177 177 /**
  178 + * 获得指定岗位的用户数组
  179 + *
  180 + * @param postIds 岗位数组
  181 + * @return 用户数组
  182 + */
  183 + List<SysUserDO> getUsersByPostIds(Collection<Long> postIds);
  184 +
  185 + /**
  186 + * 获得指定部门的用户数组
  187 + *
  188 + * @param deptIds 部门数组
  189 + * @return 用户数组
  190 + */
  191 + List<SysUserDO> getUsersByDeptIds(Collection<Integer> deptIds);
  192 +
  193 + /**
178 194 * 校验用户们是否有效。如下情况,视为无效:
179 195 * 1. 用户编号不存在
180 196 * 2. 用户被禁用
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java
... ... @@ -390,6 +390,26 @@ public class SysUserServiceImpl implements SysUserService {
390 390 }
391 391  
392 392 @Override
  393 + public List<SysUserDO> getUsersByPostIds(Collection<Long> postIds) {
  394 + if (CollUtil.isEmpty(postIds)) {
  395 + return Collections.emptyList();
  396 + }
  397 + // 过滤不符合条件的
  398 + // TODO 芋艿:暂时只能内存过滤。解决方案:1、新建一个关联表;2、基于 where + 函数;3、json 字段,适合 mysql 8+ 版本
  399 + List<SysUserDO> users = userMapper.selectList();
  400 + users.removeIf(user -> !CollUtil.containsAny(user.getPostIds(), postIds));
  401 + return users;
  402 + }
  403 +
  404 + @Override
  405 + public List<SysUserDO> getUsersByDeptIds(Collection<Integer> deptIds) {
  406 + if (CollUtil.isEmpty(deptIds)) {
  407 + return Collections.emptyList();
  408 + }
  409 + return userMapper.selectListByDeptIds(deptIds);
  410 + }
  411 +
  412 + @Override
393 413 public void validUsers(Set<Long> ids) {
394 414 if (CollUtil.isEmpty(ids)) {
395 415 return;
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
... ... @@ -50,14 +50,14 @@ public class CollectionUtils {
50 50 if (CollUtil.isEmpty(from)) {
51 51 return new ArrayList<>();
52 52 }
53   - return from.stream().map(func).collect(Collectors.toList());
  53 + return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
54 54 }
55 55  
56 56 public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
57 57 if (CollUtil.isEmpty(from)) {
58 58 return new HashSet<>();
59 59 }
60   - return from.stream().map(func).collect(Collectors.toSet());
  60 + return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());
61 61 }
62 62  
63 63 public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
... ...