houzhongyi
2024-07-11 e7c1260db32209a078a962aaa0ad5492c35774fb
提交 | 用户 | 时间
e7c126 1 package com.iailab.module.system.service.user;
H 2
3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil;
5 import cn.hutool.core.io.IoUtil;
6 import cn.hutool.core.util.StrUtil;
7 import com.iailab.framework.common.enums.CommonStatusEnum;
8 import com.iailab.framework.common.exception.ServiceException;
9 import com.iailab.framework.common.pojo.PageResult;
10 import com.iailab.framework.common.util.collection.CollectionUtils;
11 import com.iailab.framework.common.util.object.BeanUtils;
12 import com.iailab.framework.datapermission.core.util.DataPermissionUtils;
13 import com.iailab.module.infra.api.file.FileApi;
14 import com.iailab.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
15 import com.iailab.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
16 import com.iailab.module.system.controller.admin.user.vo.user.UserImportExcelVO;
17 import com.iailab.module.system.controller.admin.user.vo.user.UserImportRespVO;
18 import com.iailab.module.system.controller.admin.user.vo.user.UserPageReqVO;
19 import com.iailab.module.system.controller.admin.user.vo.user.UserSaveReqVO;
20 import com.iailab.module.system.dal.dataobject.dept.DeptDO;
21 import com.iailab.module.system.dal.dataobject.dept.UserPostDO;
22 import com.iailab.module.system.dal.dataobject.user.AdminUserDO;
23 import com.iailab.module.system.dal.mysql.dept.UserPostMapper;
24 import com.iailab.module.system.dal.mysql.user.AdminUserMapper;
25 import com.iailab.module.system.service.dept.DeptService;
26 import com.iailab.module.system.service.dept.PostService;
27 import com.iailab.module.system.service.permission.PermissionService;
28 import com.iailab.module.system.service.tenant.TenantService;
29 import com.google.common.annotations.VisibleForTesting;
30 import com.mzt.logapi.context.LogRecordContext;
31 import com.mzt.logapi.service.impl.DiffParseFunction;
32 import com.mzt.logapi.starter.annotation.LogRecord;
33 import lombok.extern.slf4j.Slf4j;
34 import org.springframework.beans.factory.annotation.Value;
35 import org.springframework.context.annotation.Lazy;
36 import org.springframework.security.crypto.password.PasswordEncoder;
37 import org.springframework.stereotype.Service;
38 import org.springframework.transaction.annotation.Transactional;
39
40 import javax.annotation.Resource;
41 import java.io.InputStream;
42 import java.time.LocalDateTime;
43 import java.util.*;
44
45 import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
46 import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
47 import static com.iailab.framework.common.util.collection.CollectionUtils.convertSet;
48 import static com.iailab.module.system.enums.ErrorCodeConstants.*;
49 import static com.iailab.module.system.enums.LogRecordConstants.*;
50
51 /**
52  * 后台用户 Service 实现类
53  *
54  * @author iailab
55  */
56 @Service("adminUserService")
57 @Slf4j
58 public class AdminUserServiceImpl implements AdminUserService {
59
60     @Value("${sys.user.init-password:iailabyuanma}")
61     private String userInitPassword;
62
63     @Resource
64     private AdminUserMapper userMapper;
65
66     @Resource
67     private DeptService deptService;
68     @Resource
69     private PostService postService;
70     @Resource
71     private PermissionService permissionService;
72     @Resource
73     private PasswordEncoder passwordEncoder;
74     @Resource
75     @Lazy // 延迟,避免循环依赖报错
76     private TenantService tenantService;
77
78     @Resource
79     private UserPostMapper userPostMapper;
80
81     @Resource
82     private FileApi fileApi;
83
84     @Override
85     @Transactional(rollbackFor = Exception.class)
86     @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}",
87             success = SYSTEM_USER_CREATE_SUCCESS)
88     public Long createUser(UserSaveReqVO createReqVO) {
89         // 1.1 校验账户配合
90         tenantService.handleTenantInfo(tenant -> {
91             long count = userMapper.selectCount();
92             if (count >= tenant.getAccountCount()) {
93                 throw exception(USER_COUNT_MAX, tenant.getAccountCount());
94             }
95         });
96         // 1.2 校验正确性
97         validateUserForCreateOrUpdate(null, createReqVO.getUsername(),
98                 createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptId(), createReqVO.getPostIds());
99         // 2.1 插入用户
100         AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class);
101         user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
102         user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码
103         userMapper.insert(user);
104         // 2.2 插入关联岗位
105         if (CollectionUtil.isNotEmpty(user.getPostIds())) {
106             userPostMapper.insertBatch(convertList(user.getPostIds(),
107                     postId -> new UserPostDO().setUserId(user.getId()).setPostId(postId)));
108         }
109
110         // 3. 记录操作日志上下文
111         LogRecordContext.putVariable("user", user);
112         return user.getId();
113     }
114
115     @Override
116     @Transactional(rollbackFor = Exception.class)
117     @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
118             success = SYSTEM_USER_UPDATE_SUCCESS)
119     public void updateUser(UserSaveReqVO updateReqVO) {
120         updateReqVO.setPassword(null); // 特殊:此处不更新密码
121         // 1. 校验正确性
122         AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(),
123                 updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds());
124
125         // 2.1 更新用户
126         AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class);
127         userMapper.updateById(updateObj);
128         // 2.2 更新岗位
129         updateUserPost(updateReqVO, updateObj);
130
131         // 3. 记录操作日志上下文
132         LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldUser, UserSaveReqVO.class));
133         LogRecordContext.putVariable("user", oldUser);
134     }
135
136     private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) {
137         Long userId = reqVO.getId();
138         Set<Long> dbPostIds = convertSet(userPostMapper.selectListByUserId(userId), UserPostDO::getPostId);
139         // 计算新增和删除的岗位编号
140         Set<Long> postIds = CollUtil.emptyIfNull(updateObj.getPostIds());
141         Collection<Long> createPostIds = CollUtil.subtract(postIds, dbPostIds);
142         Collection<Long> deletePostIds = CollUtil.subtract(dbPostIds, postIds);
143         // 执行新增和删除。对于已经授权的岗位,不用做任何处理
144         if (!CollectionUtil.isEmpty(createPostIds)) {
145             userPostMapper.insertBatch(convertList(createPostIds,
146                     postId -> new UserPostDO().setUserId(userId).setPostId(postId)));
147         }
148         if (!CollectionUtil.isEmpty(deletePostIds)) {
149             userPostMapper.deleteByUserIdAndPostId(userId, deletePostIds);
150         }
151     }
152
153     @Override
154     public void updateUserLogin(Long id, String loginIp) {
155         userMapper.updateById(new AdminUserDO().setId(id).setLoginIp(loginIp).setLoginDate(LocalDateTime.now()));
156     }
157
158     @Override
159     public void updateUserProfile(Long id, UserProfileUpdateReqVO reqVO) {
160         // 校验正确性
161         validateUserExists(id);
162         validateEmailUnique(id, reqVO.getEmail());
163         validateMobileUnique(id, reqVO.getMobile());
164         // 执行更新
165         userMapper.updateById(BeanUtils.toBean(reqVO, AdminUserDO.class).setId(id));
166     }
167
168     @Override
169     public void updateUserPassword(Long id, UserProfileUpdatePasswordReqVO reqVO) {
170         // 校验旧密码密码
171         validateOldPassword(id, reqVO.getOldPassword());
172         // 执行更新
173         AdminUserDO updateObj = new AdminUserDO().setId(id);
174         updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码
175         userMapper.updateById(updateObj);
176     }
177
178     @Override
179     public String updateUserAvatar(Long id, InputStream avatarFile) {
180         validateUserExists(id);
181         // 存储文件
182         String avatar = fileApi.createFile(IoUtil.readBytes(avatarFile));
183         // 更新路径
184         AdminUserDO sysUserDO = new AdminUserDO();
185         sysUserDO.setId(id);
186         sysUserDO.setAvatar(avatar);
187         userMapper.updateById(sysUserDO);
188         return avatar;
189     }
190
191     @Override
192     @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE, bizNo = "{{#id}}",
193             success = SYSTEM_USER_UPDATE_PASSWORD_SUCCESS)
194     public void updateUserPassword(Long id, String password) {
195         // 1. 校验用户存在
196         AdminUserDO user = validateUserExists(id);
197
198         // 2. 更新密码
199         AdminUserDO updateObj = new AdminUserDO();
200         updateObj.setId(id);
201         updateObj.setPassword(encodePassword(password)); // 加密密码
202         userMapper.updateById(updateObj);
203
204         // 3. 记录操作日志上下文
205         LogRecordContext.putVariable("user", user);
206         LogRecordContext.putVariable("newPassword", updateObj.getPassword());
207     }
208
209     @Override
210     public void updateUserStatus(Long id, Integer status) {
211         // 校验用户存在
212         validateUserExists(id);
213         // 更新状态
214         AdminUserDO updateObj = new AdminUserDO();
215         updateObj.setId(id);
216         updateObj.setStatus(status);
217         userMapper.updateById(updateObj);
218     }
219
220     @Override
221     @Transactional(rollbackFor = Exception.class)
222     @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
223             success = SYSTEM_USER_DELETE_SUCCESS)
224     public void deleteUser(Long id) {
225         // 1. 校验用户存在
226         AdminUserDO user = validateUserExists(id);
227
228         // 2.1 删除用户
229         userMapper.deleteById(id);
230         // 2.2 删除用户关联数据
231         permissionService.processUserDeleted(id);
232         // 2.2 删除用户岗位
233         userPostMapper.deleteByUserId(id);
234
235         // 3. 记录操作日志上下文
236         LogRecordContext.putVariable("user", user);
237     }
238
239     @Override
240     public AdminUserDO getUserByUsername(String username) {
241         return userMapper.selectByUsername(username);
242     }
243
244     @Override
245     public AdminUserDO getUserByMobile(String mobile) {
246         return userMapper.selectByMobile(mobile);
247     }
248
249     @Override
250     public PageResult<AdminUserDO> getUserPage(UserPageReqVO reqVO) {
251         return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()));
252     }
253
254     @Override
255     public AdminUserDO getUser(Long id) {
256         return userMapper.selectById(id);
257     }
258
259     @Override
260     public List<AdminUserDO> getUserListByDeptIds(Collection<Long> deptIds) {
261         if (CollUtil.isEmpty(deptIds)) {
262             return Collections.emptyList();
263         }
264         return userMapper.selectListByDeptIds(deptIds);
265     }
266
267     @Override
268     public List<AdminUserDO> getUserListByPostIds(Collection<Long> postIds) {
269         if (CollUtil.isEmpty(postIds)) {
270             return Collections.emptyList();
271         }
272         Set<Long> userIds = convertSet(userPostMapper.selectListByPostIds(postIds), UserPostDO::getUserId);
273         if (CollUtil.isEmpty(userIds)) {
274             return Collections.emptyList();
275         }
276         return userMapper.selectBatchIds(userIds);
277     }
278
279     @Override
280     public List<AdminUserDO> getUserList(Collection<Long> ids) {
281         if (CollUtil.isEmpty(ids)) {
282             return Collections.emptyList();
283         }
284         return userMapper.selectBatchIds(ids);
285     }
286
287     @Override
288     public void validateUserList(Collection<Long> ids) {
289         if (CollUtil.isEmpty(ids)) {
290             return;
291         }
292         // 获得岗位信息
293         List<AdminUserDO> users = userMapper.selectBatchIds(ids);
294         Map<Long, AdminUserDO> userMap = CollectionUtils.convertMap(users, AdminUserDO::getId);
295         // 校验
296         ids.forEach(id -> {
297             AdminUserDO user = userMap.get(id);
298             if (user == null) {
299                 throw exception(USER_NOT_EXISTS);
300             }
301             if (!CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus())) {
302                 throw exception(USER_IS_DISABLE, user.getNickname());
303             }
304         });
305     }
306
307     @Override
308     public List<AdminUserDO> getUserListByNickname(String nickname) {
309         return userMapper.selectListByNickname(nickname);
310     }
311
312     /**
313      * 获得部门条件:查询指定部门的子部门编号们,包括自身
314      * @param deptId 部门编号
315      * @return 部门编号集合
316      */
317     private Set<Long> getDeptCondition(Long deptId) {
318         if (deptId == null) {
319             return Collections.emptySet();
320         }
321         Set<Long> deptIds = convertSet(deptService.getChildDeptList(deptId), DeptDO::getId);
322         deptIds.add(deptId); // 包括自身
323         return deptIds;
324     }
325
326     private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
327                                                Long deptId, Set<Long> postIds) {
328         // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
329         return DataPermissionUtils.executeIgnore(() -> {
330             // 校验用户存在
331             AdminUserDO user = validateUserExists(id);
332             // 校验用户名唯一
333             validateUsernameUnique(id, username);
334             // 校验手机号唯一
335             validateMobileUnique(id, mobile);
336             // 校验邮箱唯一
337             validateEmailUnique(id, email);
338             // 校验部门处于开启状态
339             deptService.validateDeptList(CollectionUtils.singleton(deptId));
340             // 校验岗位处于开启状态
341             postService.validatePostList(postIds);
342             return user;
343         });
344     }
345
346     @VisibleForTesting
347     AdminUserDO validateUserExists(Long id) {
348         if (id == null) {
349             return null;
350         }
351         AdminUserDO user = userMapper.selectById(id);
352         if (user == null) {
353             throw exception(USER_NOT_EXISTS);
354         }
355         return user;
356     }
357
358     @VisibleForTesting
359     void validateUsernameUnique(Long id, String username) {
360         if (StrUtil.isBlank(username)) {
361             return;
362         }
363         AdminUserDO user = userMapper.selectByUsername(username);
364         if (user == null) {
365             return;
366         }
367         // 如果 id 为空,说明不用比较是否为相同 id 的用户
368         if (id == null) {
369             throw exception(USER_USERNAME_EXISTS);
370         }
371         if (!user.getId().equals(id)) {
372             throw exception(USER_USERNAME_EXISTS);
373         }
374     }
375
376     @VisibleForTesting
377     void validateEmailUnique(Long id, String email) {
378         if (StrUtil.isBlank(email)) {
379             return;
380         }
381         AdminUserDO user = userMapper.selectByEmail(email);
382         if (user == null) {
383             return;
384         }
385         // 如果 id 为空,说明不用比较是否为相同 id 的用户
386         if (id == null) {
387             throw exception(USER_EMAIL_EXISTS);
388         }
389         if (!user.getId().equals(id)) {
390             throw exception(USER_EMAIL_EXISTS);
391         }
392     }
393
394     @VisibleForTesting
395     void validateMobileUnique(Long id, String mobile) {
396         if (StrUtil.isBlank(mobile)) {
397             return;
398         }
399         AdminUserDO user = userMapper.selectByMobile(mobile);
400         if (user == null) {
401             return;
402         }
403         // 如果 id 为空,说明不用比较是否为相同 id 的用户
404         if (id == null) {
405             throw exception(USER_MOBILE_EXISTS);
406         }
407         if (!user.getId().equals(id)) {
408             throw exception(USER_MOBILE_EXISTS);
409         }
410     }
411
412     /**
413      * 校验旧密码
414      * @param id          用户 id
415      * @param oldPassword 旧密码
416      */
417     @VisibleForTesting
418     void validateOldPassword(Long id, String oldPassword) {
419         AdminUserDO user = userMapper.selectById(id);
420         if (user == null) {
421             throw exception(USER_NOT_EXISTS);
422         }
423         if (!isPasswordMatch(oldPassword, user.getPassword())) {
424             throw exception(USER_PASSWORD_FAILED);
425         }
426     }
427
428     @Override
429     @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
430     public UserImportRespVO importUserList(List<UserImportExcelVO> importUsers, boolean isUpdateSupport) {
431         if (CollUtil.isEmpty(importUsers)) {
432             throw exception(USER_IMPORT_LIST_IS_EMPTY);
433         }
434         UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>())
435                 .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build();
436         importUsers.forEach(importUser -> {
437             // 校验,判断是否有不符合的原因
438             try {
439                 validateUserForCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(),
440                         importUser.getDeptId(), null);
441             } catch (ServiceException ex) {
442                 respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
443                 return;
444             }
445             // 判断如果不存在,在进行插入
446             AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername());
447             if (existUser == null) {
448                 userMapper.insert(BeanUtils.toBean(importUser, AdminUserDO.class)
449                         .setPassword(encodePassword(userInitPassword)).setPostIds(new HashSet<>())); // 设置默认密码及空岗位编号数组
450                 respVO.getCreateUsernames().add(importUser.getUsername());
451                 return;
452             }
453             // 如果存在,判断是否允许更新
454             if (!isUpdateSupport) {
455                 respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg());
456                 return;
457             }
458             AdminUserDO updateUser = BeanUtils.toBean(importUser, AdminUserDO.class);
459             updateUser.setId(existUser.getId());
460             userMapper.updateById(updateUser);
461             respVO.getUpdateUsernames().add(importUser.getUsername());
462         });
463         return respVO;
464     }
465
466     @Override
467     public List<AdminUserDO> getUserListByStatus(Integer status) {
468         return userMapper.selectListByStatus(status);
469     }
470
471     @Override
472     public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
473         return passwordEncoder.matches(rawPassword, encodedPassword);
474     }
475
476     /**
477      * 对密码进行加密
478      *
479      * @param password 密码
480      * @return 加密后的密码
481      */
482     private String encodePassword(String password) {
483         return passwordEncoder.encode(password);
484     }
485
486 }