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,8 +45,10 @@ public interface BpmErrorCodeConstants {
45 45
46 // ========== 流程任务分配规则 1-009-006-000 ========== 46 // ========== 流程任务分配规则 1-009-006-000 ==========
47 ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则"); 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 // ========== 动态表单模块 1-009-010-000 ========== 53 // ========== 动态表单模块 1-009-010-000 ==========
52 ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在"); 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,11 +4,18 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.RandomUtil; 4 import cn.hutool.core.util.RandomUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; 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 import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; 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 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskAssignRuleService; 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 import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService; 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 import lombok.Setter; 17 import lombok.Setter;
  18 +import lombok.extern.slf4j.Slf4j;
12 import org.activiti.bpmn.model.UserTask; 19 import org.activiti.bpmn.model.UserTask;
13 import org.activiti.engine.ActivitiException; 20 import org.activiti.engine.ActivitiException;
14 import org.activiti.engine.delegate.DelegateExecution; 21 import org.activiti.engine.delegate.DelegateExecution;
@@ -17,26 +24,49 @@ import org.activiti.engine.impl.el.ExpressionManager; @@ -17,26 +24,49 @@ import org.activiti.engine.impl.el.ExpressionManager;
17 import org.activiti.engine.impl.persistence.entity.TaskEntity; 24 import org.activiti.engine.impl.persistence.entity.TaskEntity;
18 import org.activiti.engine.impl.persistence.entity.TaskEntityManager; 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 * 自定义的流程任务的 assignee 负责人的分配 40 * 自定义的流程任务的 assignee 负责人的分配
26 * 41 *
27 * @author 芋道源码 42 * @author 芋道源码
28 */ 43 */
  44 +@Slf4j
29 public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { 45 public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
30 46
31 @Setter 47 @Setter
32 private BpmTaskAssignRuleService bpmTaskRuleService; 48 private BpmTaskAssignRuleService bpmTaskRuleService;
33 @Setter 49 @Setter
34 private SysPermissionService permissionService; 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 public BpmUserTaskActivitiBehavior(UserTask userTask) { 62 public BpmUserTaskActivitiBehavior(UserTask userTask) {
37 super(userTask); 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 @Override 70 @Override
41 protected void handleAssignments(TaskEntityManager taskEntityManager, 71 protected void handleAssignments(TaskEntityManager taskEntityManager,
42 String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups, 72 String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups,
@@ -51,7 +81,7 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { @@ -51,7 +81,7 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
51 // 设置候选人们 81 // 设置候选人们
52 candidateUserIds.remove(assigneeUserId); // 已经成为负责人了,就不要在扮演候选人了 82 candidateUserIds.remove(assigneeUserId); // 已经成为负责人了,就不要在扮演候选人了
53 if (CollUtil.isNotEmpty(candidateUserIds)) { 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,20 +110,23 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
80 if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) { 110 if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) {
81 assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule); 111 assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule);
82 } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) { 112 } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
83 - assigneeUserIds = calculateTaskCandidateUsersByDept(task, rule); 113 + assigneeUserIds = calculateTaskCandidateUsersByMember(task, rule);
84 } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) { 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 assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule); 117 assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
88 } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) { 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 // TODO 芋艿:统一过滤掉被禁用的用户 124 // TODO 芋艿:统一过滤掉被禁用的用户
93 // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人 125 // 如果候选人为空,抛出异常 TODO 芋艿:没候选人的策略选择。1 - 挂起;2 - 直接结束;3 - 强制一个兜底人
94 if (CollUtil.isEmpty(assigneeUserIds)) { 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 return assigneeUserIds; 131 return assigneeUserIds;
99 } 132 }
@@ -102,12 +135,14 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { @@ -102,12 +135,14 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
102 return permissionService.getUserRoleIdListByRoleIds(rule.getOptions()); 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 private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRuleDO rule) { 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 private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) { 148 private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRuleDO rule) {
@@ -115,11 +150,26 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior { @@ -115,11 +150,26 @@ public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {
115 } 150 }
116 151
117 private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRuleDO rule) { 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 private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskAssignRuleDO rule) { 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 package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script; 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 import org.activiti.engine.impl.persistence.entity.TaskEntity; 4 import org.activiti.engine.impl.persistence.entity.TaskEntity;
4 5
5 import java.util.List; 6 import java.util.List;
@@ -23,4 +24,11 @@ public interface BpmTaskAssignScript { @@ -23,4 +24,11 @@ public interface BpmTaskAssignScript {
23 */ 24 */
24 List<Long> calculateTaskCandidateUsers(TaskEntity task); 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,5 +68,9 @@ public interface SysUserMapper extends BaseMapperX&lt;SysUserDO&gt; {
68 return selectList("status", status); 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,6 +85,14 @@ public interface SysDeptService {
85 SysDeptDO getDept(Long id); 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 * @param parentId 部门编号 98 * @param parentId 部门编号
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysDeptServiceImpl.java
1 package cn.iocoder.yudao.adminserver.modules.system.service.dept.impl; 1 package cn.iocoder.yudao.adminserver.modules.system.service.dept.impl;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.SysRoleDO;  
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; 5 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@@ -233,6 +232,11 @@ public class SysDeptServiceImpl implements SysDeptService { @@ -233,6 +232,11 @@ public class SysDeptServiceImpl implements SysDeptService {
233 return deptMapper.selectById(id); 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 private void checkCreateOrUpdate(Long id, Long parentId, String name) { 240 private void checkCreateOrUpdate(Long id, Long parentId, String name) {
237 // 校验自己存在 241 // 校验自己存在
238 checkDeptExists(id); 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,6 +175,22 @@ public interface SysUserService {
175 List<SysUserDO> getUsersByStatus(Integer status); 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 * 1. 用户编号不存在 195 * 1. 用户编号不存在
180 * 2. 用户被禁用 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,6 +390,26 @@ public class SysUserServiceImpl implements SysUserService {
390 } 390 }
391 391
392 @Override 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 public void validUsers(Set<Long> ids) { 413 public void validUsers(Set<Long> ids) {
394 if (CollUtil.isEmpty(ids)) { 414 if (CollUtil.isEmpty(ids)) {
395 return; 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,14 +50,14 @@ public class CollectionUtils {
50 if (CollUtil.isEmpty(from)) { 50 if (CollUtil.isEmpty(from)) {
51 return new ArrayList<>(); 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 public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) { 56 public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
57 if (CollUtil.isEmpty(from)) { 57 if (CollUtil.isEmpty(from)) {
58 return new HashSet<>(); 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 public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) { 63 public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {