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

pull/2/head
YunaiV 2022-01-15 01:39:30 +08:00
parent f46090243f
commit 902f2ecbad
9 changed files with 133 additions and 21 deletions

View File

@ -45,8 +45,10 @@ public interface BpmErrorCodeConstants {
// ========== 流程任务分配规则 1-009-006-000 ========== // ========== 流程任务分配规则 1-009-006-000 ==========
ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则"); ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则");
ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006000, "流程任务分配规则不存在"); ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006001, "流程任务分配规则不存在");
ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1009006000, "只有流程模型的任务分配规则,才允许被修改"); 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 ========== // ========== 动态表单模块 1-009-010-000 ==========
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在"); ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在");

View File

@ -4,11 +4,18 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil; 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.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.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.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.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.Setter;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.UserTask; import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.ActivitiException; import org.activiti.engine.ActivitiException;
import org.activiti.engine.delegate.DelegateExecution; 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.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager; import org.activiti.engine.impl.persistence.entity.TaskEntityManager;
import java.util.List; import javax.annotation.Resource;
import java.util.Objects; import java.util.*;
import java.util.Set; 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 * assignee
* *
* @author * @author
*/ */
@Slf4j
public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
@Setter @Setter
private BpmTaskAssignRuleService bpmTaskRuleService; private BpmTaskAssignRuleService bpmTaskRuleService;
@Setter @Setter
private SysPermissionService permissionService; private SysPermissionService permissionService;
@Setter
private SysDeptService deptService;
@Setter
private BpmUserGroupService userGroupService;
@Resource
private SysUserService userService;
/**
*
*/
private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();
public BpmUserTaskActivitiBehavior(UserTask userTask) { public BpmUserTaskActivitiBehavior(UserTask userTask) {
super(userTask); super(userTask);
} }
public void setScripts(List<BpmTaskAssignScript> scripts) {
this.scriptMap = convertMap(scripts, script -> script.getEnum().getId());
}
@Override @Override
protected void handleAssignments(TaskEntityManager taskEntityManager, protected void handleAssignments(TaskEntityManager taskEntityManager,
String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups, String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups,
@ -51,7 +81,7 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
// 设置候选人们 // 设置候选人们
candidateUserIds.remove(assigneeUserId); // 已经成为负责人了,就不要在扮演候选人了 candidateUserIds.remove(assigneeUserId); // 已经成为负责人了,就不要在扮演候选人了
if (CollUtil.isNotEmpty(candidateUserIds)) { 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())) { if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) {
assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule); assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule);
} else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) { } 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())) { } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule); assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(task, rule);
} else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) { } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) {
assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
} else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) { } 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 芋艿:统一过滤掉被禁用的用户
// 如果候选人为空,抛出异常 TODO 芋艿没候选人的策略选择。1 - 挂起2 - 直接结束3 - 强制一个兜底人 // 如果候选人为空,抛出异常 TODO 芋艿没候选人的策略选择。1 - 挂起2 - 直接结束3 - 强制一个兜底人
if (CollUtil.isEmpty(assigneeUserIds)) { if (CollUtil.isEmpty(assigneeUserIds)) {
throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 任务规则({}) 找不到候选人", log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]",
task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey())); task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), toJsonString(rule));
throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
} }
return assigneeUserIds; return assigneeUserIds;
} }
@ -102,12 +135,14 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
return permissionService.getUserRoleIdListByRoleIds(rule.getOptions()); return permissionService.getUserRoleIdListByRoleIds(rule.getOptions());
} }
private Set<Long> calculateTaskCandidateUsersByDept(TaskEntity task, BpmTaskAssignRuleDO rule) { private Set<Long> calculateTaskCandidateUsersByMember(TaskEntity task, BpmTaskAssignRuleDO rule) {
throw new UnsupportedOperationException("暂不支持该任务规则"); List<SysUserDO> users = userService.getUsersByPostIds(rule.getOptions());
return convertSet(users, SysUserDO::getId);
} }
private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) { private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) {
throw new UnsupportedOperationException("暂不支持该任务规则"); List<SysDeptDO> depts = deptService.getDepts(rule.getOptions());
return convertSet(depts, SysDeptDO::getLeaderUserId);
} }
private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) { private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) {
@ -115,11 +150,26 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
} }
private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) { private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) {
throw new UnsupportedOperationException("暂不支持该任务规则"); List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
Set<Long> userIds = new HashSet<>();
userGroups.forEach(bpmUserGroupDO -> userIds.addAll(bpmUserGroupDO.getMemberUserIds()));
return userIds;
} }
private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskAssignRuleDO rule) { private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskAssignRuleDO rule) {
throw new UnsupportedOperationException("暂不支持该任务规则"); // 获得对应的脚本
List<BpmTaskAssignScript> 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<Long> userIds = new HashSet<>();
scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(task)));
return userIds;
} }
} }

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script; 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 org.activiti.engine.impl.persistence.entity.TaskEntity;
import java.util.List; import java.util.List;
@ -23,4 +24,11 @@ public interface BpmTaskAssignScript {
*/ */
List<Long> calculateTaskCandidateUsers(TaskEntity task); List<Long> calculateTaskCandidateUsers(TaskEntity task);
/**
*
*
* @return
*/
BpmTaskRuleScriptEnum getEnum();
} }

View File

@ -68,5 +68,9 @@ public interface SysUserMapper extends BaseMapperX<SysUserDO> {
return selectList("status", status); return selectList("status", status);
} }
default List<SysUserDO> selectListByDeptIds(Collection<Integer> deptIds) {
return selectList("dept_id", deptIds);
}
} }

View File

@ -84,6 +84,14 @@ public interface SysDeptService {
*/ */
SysDeptDO getDept(Long id); SysDeptDO getDept(Long id);
/**
*
*
* @param ids
* @return
*/
List<SysDeptDO> getDepts(Collection<Long> ids);
/** /**
* *
* *

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.system.service.dept.impl; package cn.iocoder.yudao.adminserver.modules.system.service.dept.impl;
import cn.hutool.core.collection.CollUtil; 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.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@ -233,6 +232,11 @@ public class SysDeptServiceImpl implements SysDeptService {
return deptMapper.selectById(id); return deptMapper.selectById(id);
} }
@Override
public List<SysDeptDO> getDepts(Collection<Long> ids) {
return deptMapper.selectBatchIds(ids);
}
private void checkCreateOrUpdate(Long id, Long parentId, String name) { private void checkCreateOrUpdate(Long id, Long parentId, String name) {
// 校验自己存在 // 校验自己存在
checkDeptExists(id); checkDeptExists(id);

View File

@ -174,6 +174,22 @@ public interface SysUserService {
*/ */
List<SysUserDO> getUsersByStatus(Integer status); List<SysUserDO> getUsersByStatus(Integer status);
/**
*
*
* @param postIds
* @return
*/
List<SysUserDO> getUsersByPostIds(Collection<Long> postIds);
/**
*
*
* @param deptIds
* @return
*/
List<SysUserDO> getUsersByDeptIds(Collection<Integer> deptIds);
/** /**
* *
* 1. * 1.

View File

@ -389,6 +389,26 @@ public class SysUserServiceImpl implements SysUserService {
return userMapper.selectListByStatus(status); return userMapper.selectListByStatus(status);
} }
@Override
public List<SysUserDO> getUsersByPostIds(Collection<Long> postIds) {
if (CollUtil.isEmpty(postIds)) {
return Collections.emptyList();
}
// 过滤不符合条件的
// TODO 芋艿暂时只能内存过滤。解决方案1、新建一个关联表2、基于 where + 函数3、json 字段,适合 mysql 8+ 版本
List<SysUserDO> users = userMapper.selectList();
users.removeIf(user -> !CollUtil.containsAny(user.getPostIds(), postIds));
return users;
}
@Override
public List<SysUserDO> getUsersByDeptIds(Collection<Integer> deptIds) {
if (CollUtil.isEmpty(deptIds)) {
return Collections.emptyList();
}
return userMapper.selectListByDeptIds(deptIds);
}
@Override @Override
public void validUsers(Set<Long> ids) { public void validUsers(Set<Long> ids) {
if (CollUtil.isEmpty(ids)) { if (CollUtil.isEmpty(ids)) {

View File

@ -50,14 +50,14 @@ public class CollectionUtils {
if (CollUtil.isEmpty(from)) { if (CollUtil.isEmpty(from)) {
return new ArrayList<>(); return new ArrayList<>();
} }
return from.stream().map(func).collect(Collectors.toList()); return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
} }
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) { public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
if (CollUtil.isEmpty(from)) { if (CollUtil.isEmpty(from)) {
return new HashSet<>(); return new HashSet<>();
} }
return from.stream().map(func).collect(Collectors.toSet()); return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());
} }
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) { public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {