소스 검색

eai那边批量注册

lzzz 5 달 전
부모
커밋
ceded72594

+ 20 - 0
seecoder-portal-common/src/main/java/cn/seecoder/seecoderportalcommon/vo/dto/BatchRegisterDTO.java

@@ -0,0 +1,20 @@
+package cn.seecoder.seecoderportalcommon.vo.dto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 批量注册请求体
+ */
+public class BatchRegisterDTO {
+
+    private List<BatchRegisterItemDTO> users = new ArrayList<>();
+
+    public List<BatchRegisterItemDTO> getUsers() {
+        return users;
+    }
+
+    public void setUsers(List<BatchRegisterItemDTO> users) {
+        this.users = users;
+    }
+}

+ 82 - 0
seecoder-portal-common/src/main/java/cn/seecoder/seecoderportalcommon/vo/dto/BatchRegisterItemDTO.java

@@ -0,0 +1,82 @@
+package cn.seecoder.seecoderportalcommon.vo.dto;
+
+/**
+ * 批量注册单行数据
+ */
+public class BatchRegisterItemDTO {
+
+    private Integer rowIndex;
+
+    /**
+     * 学号
+     */
+    private String username;
+
+    /**
+     * 姓名
+     */
+    private String name;
+
+    /**
+     * 手机号
+     */
+    private String phone;
+
+    /**
+     * 邮箱(可选)
+     */
+    private String email;
+
+    /**
+     * 角色(默认 STUDENT)
+     */
+    private String role = "STUDENT";
+
+    public Integer getRowIndex() {
+        return rowIndex;
+    }
+
+    public void setRowIndex(Integer rowIndex) {
+        this.rowIndex = rowIndex;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+}

+ 56 - 0
seecoder-portal-common/src/main/java/cn/seecoder/seecoderportalcommon/vo/dto/BatchRegisterResultDTO.java

@@ -0,0 +1,56 @@
+package cn.seecoder.seecoderportalcommon.vo.dto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 批量注册汇总结果
+ */
+public class BatchRegisterResultDTO {
+
+    private Integer total;
+    private Integer successCount;
+    private Integer skippedCount;
+    private Integer failedCount;
+    private List<BatchRegisterResultItemDTO> details = new ArrayList<>();
+
+    public Integer getTotal() {
+        return total;
+    }
+
+    public void setTotal(Integer total) {
+        this.total = total;
+    }
+
+    public Integer getSuccessCount() {
+        return successCount;
+    }
+
+    public void setSuccessCount(Integer successCount) {
+        this.successCount = successCount;
+    }
+
+    public Integer getSkippedCount() {
+        return skippedCount;
+    }
+
+    public void setSkippedCount(Integer skippedCount) {
+        this.skippedCount = skippedCount;
+    }
+
+    public Integer getFailedCount() {
+        return failedCount;
+    }
+
+    public void setFailedCount(Integer failedCount) {
+        this.failedCount = failedCount;
+    }
+
+    public List<BatchRegisterResultItemDTO> getDetails() {
+        return details;
+    }
+
+    public void setDetails(List<BatchRegisterResultItemDTO> details) {
+        this.details = details;
+    }
+}

+ 62 - 0
seecoder-portal-common/src/main/java/cn/seecoder/seecoderportalcommon/vo/dto/BatchRegisterResultItemDTO.java

@@ -0,0 +1,62 @@
+package cn.seecoder.seecoderportalcommon.vo.dto;
+
+/**
+ * 批量注册单行结果
+ */
+public class BatchRegisterResultItemDTO {
+
+    private Integer rowIndex;
+    private Long userId;
+    private String username;
+    private String phone;
+    private String status;
+    private String message;
+
+    public Integer getRowIndex() {
+        return rowIndex;
+    }
+
+    public void setRowIndex(Integer rowIndex) {
+        this.rowIndex = rowIndex;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+}

+ 1 - 0
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/config/SpringSecurityConfiguration.java

@@ -56,6 +56,7 @@ public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
                         "/api/user/login",
                         "/api/user/login/yuantu",
                         "/api/user/register",
+                        "/api/user/register/batch/internal",
                         "/api/user/register/yuantu",
                         "/api/user/resetPassword",
                         "/api/user/sendMessage",

+ 149 - 8
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/service/UserService.java

@@ -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);

+ 8 - 0
seecoder-portal-server/src/main/java/cn/seecoder/seecoderportalserver/web/rest/UserController.java

@@ -40,6 +40,14 @@ public class UserController {
         ));
     }
 
+    /**
+     * 内部批量注册接口:用于跨系统同步注册,不校验短信验证码。
+     */
+    @PostMapping("/register/batch/internal")
+    public OkResponse<BatchRegisterResultDTO> batchRegisterWithoutSms(@RequestBody BatchRegisterDTO batchRegisterDTO) {
+        return OkResponse.of(userService.registerUsersWithoutPhoneCode(batchRegisterDTO.getUsers()));
+    }
+
     @PostMapping("/sendMessage")
     public OkResponse<UserMessageVO> sendMessage(@RequestBody PhoneDTO phoneDTO) {
         aliService.sendMessage(phoneDTO.getPhone());

+ 1 - 1
seecoder-portal-server/src/main/resources/application.yml

@@ -3,7 +3,7 @@ seec:
   secret: UenM4KGb6DskRWUenM4KGb6DskRWUenM4KGb6DskRWUenM4KGb6DskRWUenM4KGb6DskRWUenM4KGb6DskRWUenM4KGb6DskRWUenM4KGb6DskRWUenM4KGb6DskRW
   gitlab:
     url: http://seecoder-gitlab-server.seec.seecoder.cn
-    host: http://environment-13-31.seec.svc.cluster.local:np8080
+    host: http://environment-13-31.seec.svc.cluster.local:8080
     token: wXDeETv2Kmt-JhiApJ9H
   coder:
     url: http://environment-11-16.seec.svc.cluster.local:8080