From b471ab5edf72439cf6e37c4635c07e7a5fc08a4f Mon Sep 17 00:00:00 2001
From: xiaoyi <908147524@qq.com>
Date: 星期二, 03 十一月 2026 19:10:14 +0800
Subject: [PATCH] fix(excel): Excel 导入报错统一转为可读中文提示,不再出现「系统异常」500
---
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 160 insertions(+), 11 deletions(-)
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
index 84e3bb6..9b98bb5 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
@@ -14,17 +14,22 @@
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportExcelVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserSyncReqDTO;
+import cn.iocoder.yudao.module.system.api.user.event.AdminUserDeletedEvent;
+import cn.iocoder.yudao.module.system.api.user.event.AdminUserUpdatedEvent;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
+import cn.iocoder.yudao.module.system.mq.producer.user.AdminUserProducer;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.dept.PostService;
import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
@@ -37,6 +42,7 @@
import jakarta.annotation.Resource;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@@ -54,7 +60,7 @@
/**
* 鍚庡彴鐢ㄦ埛 Service 瀹炵幇绫�
*
- * @author 鑺嬮亾婧愮爜
+ * @author 瓒呯骇绠$悊鍛�
*/
@Service("adminUserService")
@Slf4j
@@ -88,6 +94,12 @@
@Resource
private ConfigApi configApi;
+ @Resource
+ private AdminUserProducer adminUserProducer;
+
+ @Resource
+ private ApplicationEventPublisher eventPublisher;
+
@Override
@Transactional(rollbackFor = Exception.class)
@LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}",
@@ -112,6 +124,10 @@
if (CollectionUtil.isNotEmpty(user.getPostIds())) {
userPostMapper.insertBatch(convertList(user.getPostIds(),
postId -> new UserPostDO().setUserId(user.getId()).setPostId(postId)));
+ }
+ // 2.3 鍒嗛厤鐢ㄦ埛瑙掕壊
+ if (CollUtil.isNotEmpty(createReqVO.getRoleIds())) {
+ permissionService.assignUserRole(user.getId(), createReqVO.getRoleIds());
}
// 3. 璁板綍鎿嶄綔鏃ュ織涓婁笅鏂�
@@ -158,10 +174,17 @@
userMapper.updateById(updateObj);
// 2.2 鏇存柊宀椾綅
updateUserPost(updateReqVO, updateObj);
+ // 2.3 鏄电О / 澶村儚鍙樺寲鏃讹紝鍙戦�佹秷鎭緵涓嬫父璁㈤槄锛堝 IM 妯″潡鎺� FRIEND_INFO_UPDATED锛�
+ publishUserProfileUpdatedIfChanged(oldUser, updateReqVO.getNickname(), updateReqVO.getAvatar());
// 3. 璁板綍鎿嶄綔鏃ュ織涓婁笅鏂�
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldUser, UserSaveReqVO.class));
LogRecordContext.putVariable("user", oldUser);
+
+ // 4. 閫氱煡 hrm 妯″潡鍚屾鍛樺伐妗f锛堟病鏈夊憳宸ユ。妗堢殑璐﹀彿浼氳鐩戝惉鍣ㄧ洿鎺ヨ烦杩囷級
+ eventPublisher.publishEvent(new AdminUserUpdatedEvent(updateReqVO.getId(),
+ updateReqVO.getNickname(), updateReqVO.getMobile(), updateReqVO.getDeptId(),
+ updateReqVO.getSex(), updateReqVO.getEmail()));
}
private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) {
@@ -187,13 +210,36 @@
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void updateUserProfile(Long id, UserProfileUpdateReqVO reqVO) {
- // 鏍¢獙姝g‘鎬�
- validateUserExists(id);
+ // 1. 鏍¢獙姝g‘鎬�
+ AdminUserDO oldUser = validateUserExists(id);
validateEmailUnique(id, reqVO.getEmail());
validateMobileUnique(id, reqVO.getMobile());
- // 鎵ц鏇存柊
+
+ // 2. 鎵ц鏇存柊
userMapper.updateById(BeanUtils.toBean(reqVO, AdminUserDO.class).setId(id));
+
+ // 3. 鏄电О / 澶村儚鍙樺寲鏃讹紝鍙戦�佹秷鎭緵涓嬫父璁㈤槄锛堝 IM 妯″潡鎺� FRIEND_INFO_UPDATED锛�
+ publishUserProfileUpdatedIfChanged(oldUser, reqVO.getNickname(), reqVO.getAvatar());
+
+ // 4. 閫氱煡 hrm 妯″潡鍚屾鍛樺伐妗f锛堟病鏈夊憳宸ユ。妗堢殑璐﹀彿浼氳鐩戝惉鍣ㄧ洿鎺ヨ烦杩囷級
+ eventPublisher.publishEvent(new AdminUserUpdatedEvent(id,
+ reqVO.getNickname(), reqVO.getMobile(), null, reqVO.getSex(), reqVO.getEmail()));
+ }
+
+ /**
+ * 浠呭綋 nickname 鎴� avatar 璺熸棫鍊间笉涓�鑷存椂锛屽彂閫� AdminUserProfileUpdateMessage
+ */
+ private void publishUserProfileUpdatedIfChanged(AdminUserDO oldUser, String newNickname, String newAvatar) {
+ boolean nicknameChanged = newNickname != null && !ObjUtil.equal(oldUser.getNickname(), newNickname);
+ boolean avatarChanged = newAvatar != null && !ObjUtil.equal(oldUser.getAvatar(), newAvatar);
+ if (!nicknameChanged && !avatarChanged) {
+ return;
+ }
+ adminUserProducer.sendUserProfileUpdateMessage(oldUser.getId(),
+ nicknameChanged ? newNickname : null,
+ avatarChanged ? newAvatar : null);
}
@Override
@@ -245,6 +291,18 @@
@LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
success = SYSTEM_USER_DELETE_SUCCESS)
public void deleteUser(Long id) {
+ doDeleteUser(id);
+ // 閫氱煡 hrm 妯″潡鑱斿姩鍒犻櫎鍛樺伐妗f锛堟病鏈夊憳宸ユ。妗堢殑璐﹀彿浼氳鐩戝惉鍣ㄧ洿鎺ヨ烦杩囷級
+ eventPublisher.publishEvent(new AdminUserDeletedEvent(id));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteUserSilently(Long id) {
+ doDeleteUser(id);
+ }
+
+ private void doDeleteUser(Long id) {
// 1. 鏍¢獙鐢ㄦ埛瀛樺湪
AdminUserDO user = validateUserExists(id);
@@ -270,6 +328,44 @@
permissionService.processUserDeleted(id);
userPostMapper.deleteByUserId(id);
});
+
+ // 3. 閫氱煡 hrm 妯″潡鑱斿姩鍒犻櫎鍛樺伐妗f
+ ids.forEach(id -> eventPublisher.publishEvent(new AdminUserDeletedEvent(id)));
+ }
+
+ @Override
+ public void updateUserForEmployee(AdminUserSyncReqDTO reqDTO) {
+ if (reqDTO == null || reqDTO.getUserId() == null) {
+ return;
+ }
+ // 1. 鏍¢獙鐢ㄦ埛瀛樺湪
+ AdminUserDO user = validateUserExists(reqDTO.getUserId());
+ // 2. 鏍¢獙鎵嬫満鍙� / 閭鍞竴锛堜笌鏃у�间笉鍚屾椂鎵嶆牎楠岋級
+ if (StrUtil.isNotBlank(reqDTO.getMobile()) && !ObjUtil.equal(user.getMobile(), reqDTO.getMobile())) {
+ validateMobileUnique(reqDTO.getUserId(), reqDTO.getMobile());
+ }
+ if (StrUtil.isNotBlank(reqDTO.getEmail()) && !ObjUtil.equal(user.getEmail(), reqDTO.getEmail())) {
+ validateEmailUnique(reqDTO.getUserId(), reqDTO.getEmail());
+ }
+ // 3. 鍙洿鏂伴潪绌哄瓧娈�
+ AdminUserDO updateObj = new AdminUserDO();
+ updateObj.setId(reqDTO.getUserId());
+ if (StrUtil.isNotBlank(reqDTO.getNickname())) {
+ updateObj.setNickname(reqDTO.getNickname());
+ }
+ if (StrUtil.isNotBlank(reqDTO.getMobile())) {
+ updateObj.setMobile(reqDTO.getMobile());
+ }
+ if (reqDTO.getDeptId() != null) {
+ updateObj.setDeptId(reqDTO.getDeptId());
+ }
+ if (reqDTO.getSex() != null) {
+ updateObj.setSex(reqDTO.getSex());
+ }
+ if (StrUtil.isNotBlank(reqDTO.getEmail())) {
+ updateObj.setEmail(reqDTO.getEmail());
+ }
+ userMapper.updateById(updateObj);
}
@Override
@@ -329,6 +425,11 @@
}
return userMapper.selectByIds(ids);
}
+
+ public List<AdminUserDO> getUserListAll() {
+ return userMapper.selectList();
+ }
+
@Override
public void validateUserList(Collection<Long> ids) {
@@ -479,10 +580,13 @@
if (CollUtil.isEmpty(importUsers)) {
throw exception(USER_IMPORT_LIST_IS_EMPTY);
}
- // 1.2 鍒濆鍖栧瘑鐮佷笉鑳戒负绌�
+ // 1.2 绯荤粺鍙傛暟涓殑鍒濆瀵嗙爜锛屼綔涓� Excel 鏈~鍐欏瘑鐮佹椂鐨勫厹搴�
String initPassword = configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY);
- if (StrUtil.isEmpty(initPassword)) {
- throw exception(USER_IMPORT_INIT_PASSWORD);
+ // 1.3 棰勫姞杞介儴闂細鍚嶇О -> 閮ㄩ棬锛岀敤浜庢寜鍚嶇О瑙f瀽锛堟ā鏉胯姹傚~閮ㄩ棬鍚嶇О锛岃�岄潪鏁版嵁搴撲富閿� ID锛�
+ List<DeptDO> allDepts = deptService.getDeptList(new DeptListReqVO());
+ Map<String, DeptDO> deptByName = new HashMap<>(Math.max(allDepts.size(), 1));
+ for (DeptDO dept : allDepts) {
+ deptByName.put(dept.getName().trim(), dept);
}
// 2. 閬嶅巻锛岄�愪釜鍒涘缓 or 鏇存柊
@@ -491,18 +595,36 @@
AtomicInteger index = new AtomicInteger(1);
importUsers.forEach(importUser -> {
int currentIndex = index.getAndIncrement();
+ String key = StrUtil.blankToDefault(importUser.getUsername(), "绗� " + currentIndex + " 琛�");
+ // 2.0 瑙f瀽閮ㄩ棬锛氭寜閮ㄩ棬鍚嶇О绮剧‘鍖归厤锛屽尮閰嶄笉鍒扮粰鍑烘槑纭彁绀猴紙涓嶅啀瑕佹眰濉暟鎹簱涓婚敭 ID锛�
+ DeptDO importDept = resolveImportDept(importUser.getDeptName(), deptByName);
+ if (importDept == null && StrUtil.isNotBlank(importUser.getDeptName())) {
+ respVO.getFailureUsernames().put(key,
+ "閮ㄩ棬銆�" + importUser.getDeptName().trim() + "銆嶄笉瀛樺湪锛岃濉啓閮ㄩ棬绠$悊涓殑瀹屾暣閮ㄩ棬鍚嶇О");
+ return;
+ }
+ Long importDeptId = importDept != null ? importDept.getId() : null;
+ // 2.1.0 瑙f瀽璇ヨ鍒濆瀵嗙爜锛欵xcel 濉啓浜嗗氨鐢ㄥ~鍐欑殑锛屽惁鍒欒鍙栫郴缁熷弬鏁�
+ String effectivePassword = StrUtil.isNotBlank(importUser.getPassword())
+ ? importUser.getPassword() : initPassword;
+ if (StrUtil.isBlank(effectivePassword)) {
+ respVO.getFailureUsernames().put(key,
+ "鏈~鍐欏垵濮嬪瘑鐮侊紝涓旂郴缁熷弬鏁� system.user.init-password 鏈厤缃��"
+ + "璇峰湪 Excel 涓~鍐欒琛屽垵濮嬪瘑鐮侊紝鎴栧厛鍦ㄥ弬鏁拌缃腑閰嶇疆 system.user.init-password");
+ return;
+ }
// 2.1.1 鏍¢獙瀛楁鏄惁绗﹀悎瑕佹眰
try {
- ValidationUtils.validate(BeanUtils.toBean(importUser, UserSaveReqVO.class).setPassword(initPassword));
+ ValidationUtils.validate(BeanUtils.toBean(importUser, UserSaveReqVO.class)
+ .setPassword(effectivePassword).setDeptId(importDeptId));
} catch (ConstraintViolationException ex) {
- String key = StrUtil.blankToDefault(importUser.getUsername(), "绗� " + currentIndex + " 琛�");
respVO.getFailureUsernames().put(key, ex.getMessage());
return;
}
// 2.1.2 鏍¢獙锛屽垽鏂槸鍚︽湁涓嶇鍚堢殑鍘熷洜
try {
validateUserForCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(),
- importUser.getDeptId(), null);
+ importDeptId, null);
} catch (ServiceException ex) {
respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
return;
@@ -512,7 +634,8 @@
AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername());
if (existUser == null) {
userMapper.insert(BeanUtils.toBean(importUser, AdminUserDO.class)
- .setPassword(encodePassword(initPassword)).setPostIds(new HashSet<>())); // 璁剧疆榛樿瀵嗙爜鍙婄┖宀椾綅缂栧彿鏁扮粍
+ .setDeptId(importDeptId) // 閮ㄩ棬宸叉寜鍚嶇О瑙f瀽锛岄渶鏄惧紡鍥炲~ ID
+ .setPassword(encodePassword(effectivePassword)).setPostIds(new HashSet<>())); // 璁剧疆鍒濆瀵嗙爜鍙婄┖宀椾綅缂栧彿鏁扮粍
respVO.getCreateUsernames().add(importUser.getUsername());
return;
}
@@ -523,18 +646,44 @@
}
AdminUserDO updateUser = BeanUtils.toBean(importUser, AdminUserDO.class);
updateUser.setId(existUser.getId());
+ updateUser.setDeptId(importDeptId); // 閮ㄩ棬宸叉寜鍚嶇О瑙f瀽锛岄渶鏄惧紡鍥炲~ ID
+ // 瀵嗙爜鍗曠嫭澶勭悊锛欵xcel 濉啓浜嗘墠鏇存柊锛涙湭濉啓鍒欑疆绌猴紝淇濇寔鍘熷瘑鐮佷笉鍙橈紙閬垮厤鎶婃槑鏂囧啓杩涘簱锛�
+ updateUser.setPassword(StrUtil.isNotBlank(importUser.getPassword())
+ ? encodePassword(importUser.getPassword()) : null);
userMapper.updateById(updateUser);
respVO.getUpdateUsernames().add(importUser.getUsername());
});
return respVO;
}
+ /**
+ * 瑙f瀽瀵煎叆琛岄噷鐨勯儴闂ㄥ悕绉�
+ *
+ * <p>鍙寜閮ㄩ棬鍚嶇О绮剧‘鍖归厤锛堜笉鍐嶈姹傚~鍐欐暟鎹簱涓婚敭 ID锛夛紝
+ * 閬垮厤浣跨敤鑰呮棤浠庡緱鐭ラ儴闂� ID 瀵艰嚧瀵煎叆蹇呯劧澶辫触銆�</p>
+ *
+ * @param raw 瀵煎叆濉啓鐨勯儴闂ㄥ悕绉�
+ * @param byName 閮ㄩ棬鍚嶇О -> 閮ㄩ棬
+ * @return 鍖归厤鍒扮殑閮ㄩ棬锛涙湭濉啓鎴栧尮閰嶄笉鍒拌繑鍥� null
+ */
+ private DeptDO resolveImportDept(String raw, Map<String, DeptDO> byName) {
+ if (StrUtil.isBlank(raw)) {
+ return null;
+ }
+ return byName.get(raw.trim());
+ }
+
@Override
public List<AdminUserDO> getUserListByStatus(Integer status) {
return userMapper.selectListByStatus(status);
}
@Override
+ public List<AdminUserDO> getDeptUsers(Collection<Long> deptIds) {
+ return userMapper.selectListByDeptIds(deptIds);
+ }
+
+ @Override
public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
return passwordEncoder.matches(rawPassword, encodedPassword);
}
--
Gitblit v1.9.3