|
|
@@ -2,6 +2,8 @@ package cn.seecoder.seecoderportalserver.service;
|
|
|
|
|
|
import cn.seecoder.seecoderportalcommon.vo.UserRole;
|
|
|
import cn.seecoder.seecoderportalcommon.vo.UserVO;
|
|
|
+import cn.seecoder.seecoderportalcommon.vo.dto.BatchRegisterItemDTO;
|
|
|
+import cn.seecoder.seecoderportalcommon.vo.dto.BatchRegisterResultDTO;
|
|
|
import cn.seecoder.seecoderportalserver.domain.User;
|
|
|
import cn.seecoder.seecoderportalserver.mapper.UserMapper;
|
|
|
import cn.seecoder.seecoderportalserver.repository.UserRepository;
|
|
|
@@ -17,7 +19,9 @@ import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
+import java.util.Set;
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
/**
|
|
|
@@ -113,6 +117,105 @@ public class UserService {
|
|
|
@Transactional(rollbackFor = RuntimeException.class)
|
|
|
public User registerUser(UserVO userVO, String password, String phoneCode) {
|
|
|
checkPhoneMessageOrThrowError(userVO.getPhone(), phoneCode);
|
|
|
+ return registerUserWithoutPhoneCode(userVO, password, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = RuntimeException.class)
|
|
|
+ public BatchRegisterResultDTO registerUsersWithoutPhoneCode(List<BatchRegisterItemDTO> users) {
|
|
|
+ BatchRegisterResultDTO result = new BatchRegisterResultDTO();
|
|
|
+ if (users == null) {
|
|
|
+ users = new ArrayList<>();
|
|
|
+ }
|
|
|
+ result.setTotal(users.size());
|
|
|
+
|
|
|
+ int successCount = 0;
|
|
|
+ int skippedCount = 0;
|
|
|
+ int failedCount = 0;
|
|
|
+
|
|
|
+ Set<String> batchUsernames = new HashSet<>();
|
|
|
+ Set<String> batchPhones = new HashSet<>();
|
|
|
+
|
|
|
+ for (int i = 0; i < users.size(); i++) {
|
|
|
+ BatchRegisterItemDTO item = users.get(i);
|
|
|
+ Integer rowIndex = item.getRowIndex() == null ? i + 1 : item.getRowIndex();
|
|
|
+ String username = trimToEmpty(item.getUsername());
|
|
|
+ String phone = trimToEmpty(item.getPhone());
|
|
|
+ String name = trimToEmpty(item.getName());
|
|
|
+
|
|
|
+ cn.seecoder.seecoderportalcommon.vo.dto.BatchRegisterResultItemDTO detail =
|
|
|
+ new cn.seecoder.seecoderportalcommon.vo.dto.BatchRegisterResultItemDTO();
|
|
|
+ detail.setRowIndex(rowIndex);
|
|
|
+ detail.setUsername(username);
|
|
|
+ detail.setPhone(phone);
|
|
|
+
|
|
|
+ if (username.isEmpty() || phone.isEmpty() || name.isEmpty()) {
|
|
|
+ detail.setStatus("FAILED");
|
|
|
+ detail.setMessage("必填字段缺失:学号/姓名/手机号不能为空");
|
|
|
+ failedCount++;
|
|
|
+ result.getDetails().add(detail);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (batchUsernames.contains(username)) {
|
|
|
+ detail.setStatus("SKIPPED");
|
|
|
+ detail.setMessage("导入文件中学号重复");
|
|
|
+ skippedCount++;
|
|
|
+ result.getDetails().add(detail);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (batchPhones.contains(phone)) {
|
|
|
+ detail.setStatus("SKIPPED");
|
|
|
+ detail.setMessage("导入文件中手机号重复");
|
|
|
+ skippedCount++;
|
|
|
+ result.getDetails().add(detail);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ String defaultPassword;
|
|
|
+ try {
|
|
|
+ defaultPassword = generateDefaultPassword(username);
|
|
|
+ } catch (CustomErrorException e) {
|
|
|
+ detail.setStatus("FAILED");
|
|
|
+ detail.setMessage(e.getMessage());
|
|
|
+ failedCount++;
|
|
|
+ result.getDetails().add(detail);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ UserVO userVO = new UserVO();
|
|
|
+ userVO.setUsername(username);
|
|
|
+ userVO.setPhone(phone);
|
|
|
+ userVO.setName(name);
|
|
|
+ userVO.setEmail(trimToNull(item.getEmail()));
|
|
|
+ userVO.setRole(parseRole(item.getRole()));
|
|
|
+
|
|
|
+ try {
|
|
|
+ User savedUser = registerUserWithoutPhoneCode(userVO, defaultPassword, false);
|
|
|
+ detail.setUserId(savedUser.getId());
|
|
|
+ detail.setStatus("SUCCESS");
|
|
|
+ detail.setMessage("注册成功");
|
|
|
+ successCount++;
|
|
|
+ batchUsernames.add(username);
|
|
|
+ batchPhones.add(phone);
|
|
|
+ } catch (CustomErrorException e) {
|
|
|
+ detail.setStatus("SKIPPED");
|
|
|
+ detail.setMessage(e.getMessage());
|
|
|
+ skippedCount++;
|
|
|
+ } catch (Exception e) {
|
|
|
+ detail.setStatus("FAILED");
|
|
|
+ detail.setMessage("注册失败:" + e.getMessage());
|
|
|
+ failedCount++;
|
|
|
+ }
|
|
|
+ result.getDetails().add(detail);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.setSuccessCount(successCount);
|
|
|
+ result.setSkippedCount(skippedCount);
|
|
|
+ result.setFailedCount(failedCount);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private User registerUserWithoutPhoneCode(UserVO userVO, String password, boolean syncGitlab) {
|
|
|
userVO.setUsername(userVO.getUsername());
|
|
|
userRepository.findByPhone(userVO.getPhone()).ifPresent(user -> {
|
|
|
throw CustomErrorException.phoneAlreadyExist();
|
|
|
@@ -120,23 +223,61 @@ public class UserService {
|
|
|
userRepository.findByUsername(userVO.getUsername()).ifPresent(user -> {
|
|
|
throw CustomErrorException.usernameAlreadyExist();
|
|
|
});
|
|
|
- userRepository.findByEmail(userVO.getEmail()).ifPresent(user -> {
|
|
|
- throw CustomErrorException.emailAlreadyExist();
|
|
|
- });
|
|
|
+ if (userVO.getEmail() != null && !userVO.getEmail().trim().isEmpty()) {
|
|
|
+ userRepository.findByEmail(userVO.getEmail()).ifPresent(user -> {
|
|
|
+ throw CustomErrorException.emailAlreadyExist();
|
|
|
+ });
|
|
|
+ }
|
|
|
User user = userMapper.userVOToUser(userVO);
|
|
|
String encodedPassword = passwordEncoder.encode(password);
|
|
|
user.setPassword(encodedPassword);
|
|
|
User savedUser = userRepository.save(user);
|
|
|
- try {
|
|
|
- gitlabService.createGitlabUser(Math.toIntExact(savedUser.getId()), savedUser.getUsername(), password, savedUser.getEmail());
|
|
|
- } catch (SeecoderGitlabException e) {
|
|
|
- userRepository.delete(savedUser);
|
|
|
- throw new CustomErrorException("创建gitlab账号失败,请联系管理人员!");
|
|
|
+ if (syncGitlab) {
|
|
|
+ try {
|
|
|
+ gitlabService.createGitlabUser(Math.toIntExact(savedUser.getId()), savedUser.getUsername(), password, savedUser.getEmail());
|
|
|
+ } catch (SeecoderGitlabException e) {
|
|
|
+ userRepository.delete(savedUser);
|
|
|
+ throw new CustomErrorException("创建gitlab账号失败,请联系管理人员!");
|
|
|
+ }
|
|
|
}
|
|
|
log.debug("Created Information for User: {}", savedUser);
|
|
|
return savedUser;
|
|
|
}
|
|
|
|
|
|
+ private String generateDefaultPassword(String username) {
|
|
|
+ if (username == null || username.length() < 6) {
|
|
|
+ throw new CustomErrorException("学号长度不足6位,无法生成默认密码");
|
|
|
+ }
|
|
|
+ String suffix = username.substring(username.length() - 6);
|
|
|
+ if (!Pattern.matches("^\\d{6}$", suffix)) {
|
|
|
+ throw new CustomErrorException("学号后6位必须为数字");
|
|
|
+ }
|
|
|
+ return "Stu" + suffix;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String trimToEmpty(String value) {
|
|
|
+ return value == null ? "" : value.trim();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String trimToNull(String value) {
|
|
|
+ if (value == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String trimmed = value.trim();
|
|
|
+ return trimmed.isEmpty() ? null : trimmed;
|
|
|
+ }
|
|
|
+
|
|
|
+ private UserRole parseRole(String role) {
|
|
|
+ if (role == null || role.trim().isEmpty()) {
|
|
|
+ return UserRole.STUDENT;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return UserRole.valueOf(role.trim().toUpperCase());
|
|
|
+ } catch (IllegalArgumentException ex) {
|
|
|
+ return UserRole.STUDENT;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@Transactional(rollbackFor = RuntimeException.class)
|
|
|
public void resetPassword(String phone, String phoneCode, String password) {
|
|
|
checkPhoneMessageOrThrowError(phone, phoneCode);
|