diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java index 7181d2d3f..62b924517 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java @@ -45,8 +45,10 @@ public interface BpmErrorCodeConstants { // ========== 流程任务分配规则 1-009-006-000 ========== ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则"); - ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006000, "流程任务分配规则不存在"); - ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1009006000, "只有流程模型的任务分配规则,才允许被修改"); + ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006001, "流程任务分配规则不存在"); + ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1009006002, "只有流程模型的任务分配规则,才允许被修改"); + ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1009006003, "操作失败,原因:找不到任务的审批人!"); + ErrorCode TASK_ASSIGN_SCRIPT_NOT_EXISTS = new ErrorCode(1009006004, "操作失败,原因:任务分配脚本({}) 不存在"); // ========== 动态表单模块 1-009-010-000 ========== ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在"); diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java index 701d729ea..c79f76d7e 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/BpmUserTaskActivitiBehavior.java +++ b/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; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; +import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmUserGroupDO; import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; +import cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script.BpmTaskAssignScript; import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskAssignRuleService; +import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmUserGroupService; +import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO; +import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService; import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService; +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.activiti.bpmn.model.UserTask; import org.activiti.engine.ActivitiException; import org.activiti.engine.delegate.DelegateExecution; @@ -17,26 +24,49 @@ import org.activiti.engine.impl.el.ExpressionManager; import org.activiti.engine.impl.persistence.entity.TaskEntity; import org.activiti.engine.impl.persistence.entity.TaskEntityManager; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import javax.annotation.Resource; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; + +import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.TASK_ASSIGN_SCRIPT_NOT_EXISTS; +import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*; +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; /** * 自定义的流程任务的 assignee 负责人的分配 * * @author 芋道源码 */ +@Slf4j public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { @Setter private BpmTaskAssignRuleService bpmTaskRuleService; @Setter private SysPermissionService permissionService; + @Setter + private SysDeptService deptService; + @Setter + private BpmUserGroupService userGroupService; + @Resource + private SysUserService userService; + /** + * 任务分配脚本 + */ + private Map scriptMap = Collections.emptyMap(); public BpmUserTaskActivitiBehavior(UserTask userTask) { super(userTask); } + public void setScripts(List scripts) { + this.scriptMap = convertMap(scripts, script -> script.getEnum().getId()); + } + @Override protected void handleAssignments(TaskEntityManager taskEntityManager, String assignee, String owner, List candidateUsers, List candidateGroups, @@ -51,7 +81,7 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { // 设置候选人们 candidateUserIds.remove(assigneeUserId); // 已经成为负责人了,就不要在扮演候选人了 if (CollUtil.isNotEmpty(candidateUserIds)) { - task.addCandidateUsers(CollectionUtils.convertSet(candidateUserIds, String::valueOf)); + task.addCandidateUsers(convertSet(candidateUserIds, String::valueOf)); } } @@ -80,20 +110,23 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) { assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule); } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) { - assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule); + assigneeUserIds = calculateTaskCandidateUsersByMember(task, rule); } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) { - assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule); - } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(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 = calculateTaskCandidateUsersByUser(task, rule); + assigneeUserIds = calculateTaskCandidateUsersByUserGroup(task, rule); + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule); } // TODO 芋艿:统一过滤掉被禁用的用户 // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 if (CollUtil.isEmpty(assigneeUserIds)) { - throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 任务规则({}) 找不到候选人", - task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey())); + log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", + task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule)); + throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); } return assigneeUserIds; } @@ -102,12 +135,14 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { return permissionService.getUserRoleIdListByRoleIds(rule.getOptions()); } - private Set calculateTaskCandidateUsersByDept(TaskEntity task, BpmTaskAssignRuleDO rule) { - throw new UnsupportedOperationException("暂不支持该任务规则"); + private Set calculateTaskCandidateUsersByMember(TaskEntity task, BpmTaskAssignRuleDO rule) { + List users = userService.getUsersByPostIds(rule.getOptions()); + return convertSet(users, SysUserDO::getId); } private Set calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) { - throw new UnsupportedOperationException("暂不支持该任务规则"); + List depts = deptService.getDepts(rule.getOptions()); + return convertSet(depts, SysDeptDO::getLeaderUserId); } private Set calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) { @@ -115,11 +150,26 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { } private Set calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) { - throw new UnsupportedOperationException("暂不支持该任务规则"); + List userGroups = userGroupService.getUserGroupList(rule.getOptions()); + Set userIds = new HashSet<>(); + userGroups.forEach(bpmUserGroupDO -> userIds.addAll(bpmUserGroupDO.getMemberUserIds())); + return userIds; } private Set calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskAssignRuleDO rule) { - throw new UnsupportedOperationException("暂不支持该任务规则"); + // 获得对应的脚本 + List 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 userIds = new HashSet<>(); + scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(task))); + return userIds; } } diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java index cf3e03519..0e881e703 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script; +import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskRuleScriptEnum; import org.activiti.engine.impl.persistence.entity.TaskEntity; import java.util.List; @@ -23,4 +24,11 @@ public interface BpmTaskAssignScript { */ List calculateTaskCandidateUsers(TaskEntity task); + /** + * 获得枚举值 + * + * @return 枚举值 + */ + BpmTaskRuleScriptEnum getEnum(); + } diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/user/SysUserMapper.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/user/SysUserMapper.java index a278b79f3..55a073ce5 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/user/SysUserMapper.java +++ b/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 { return selectList("status", status); } + default List selectListByDeptIds(Collection deptIds) { + return selectList("dept_id", deptIds); + } + } diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/SysDeptService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/SysDeptService.java index f1dc1c9a1..bf3ce317f 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/SysDeptService.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/SysDeptService.java @@ -84,6 +84,14 @@ public interface SysDeptService { */ SysDeptDO getDept(Long id); + /** + * 获得部门信息数组 + * + * @param ids 部门编号数组 + * @return 部门信息数组 + */ + List getDepts(Collection ids); + /** * 获得所有子部门,从缓存中 * diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java index 33da2d6e5..60b33431b 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.adminserver.modules.system.service.dept.impl; import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.SysRoleDO; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; @@ -233,6 +232,11 @@ public class SysDeptServiceImpl implements SysDeptService { return deptMapper.selectById(id); } + @Override + public List getDepts(Collection ids) { + return deptMapper.selectBatchIds(ids); + } + private void checkCreateOrUpdate(Long id, Long parentId, String name) { // 校验自己存在 checkDeptExists(id); diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserService.java index 10fef200e..076dd8471 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserService.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserService.java @@ -174,6 +174,22 @@ public interface SysUserService { */ List getUsersByStatus(Integer status); + /** + * 获得指定岗位的用户数组 + * + * @param postIds 岗位数组 + * @return 用户数组 + */ + List getUsersByPostIds(Collection postIds); + + /** + * 获得指定部门的用户数组 + * + * @param deptIds 部门数组 + * @return 用户数组 + */ + List getUsersByDeptIds(Collection deptIds); + /** * 校验用户们是否有效。如下情况,视为无效: * 1. 用户编号不存在 diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java index 3b7fe1c10..cf627df5f 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java @@ -389,6 +389,26 @@ public class SysUserServiceImpl implements SysUserService { return userMapper.selectListByStatus(status); } + @Override + public List getUsersByPostIds(Collection postIds) { + if (CollUtil.isEmpty(postIds)) { + return Collections.emptyList(); + } + // 过滤不符合条件的 + // TODO 芋艿:暂时只能内存过滤。解决方案:1、新建一个关联表;2、基于 where + 函数;3、json 字段,适合 mysql 8+ 版本 + List users = userMapper.selectList(); + users.removeIf(user -> !CollUtil.containsAny(user.getPostIds(), postIds)); + return users; + } + + @Override + public List getUsersByDeptIds(Collection deptIds) { + if (CollUtil.isEmpty(deptIds)) { + return Collections.emptyList(); + } + return userMapper.selectListByDeptIds(deptIds); + } + @Override public void validUsers(Set ids) { if (CollUtil.isEmpty(ids)) { diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index 44691c088..cbbfb5233 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java @@ -50,14 +50,14 @@ public class CollectionUtils { if (CollUtil.isEmpty(from)) { return new ArrayList<>(); } - return from.stream().map(func).collect(Collectors.toList()); + return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList()); } public static Set convertSet(Collection from, Function func) { if (CollUtil.isEmpty(from)) { return new HashSet<>(); } - return from.stream().map(func).collect(Collectors.toSet()); + return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet()); } public static Map convertMap(Collection from, Function keyFunc) {