package mathlearning.service; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import mathlearning.model.User; import at.favre.lib.crypto.bcrypt.BCrypt; import java.io.File; import java.io.IOException; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class UserService { private static final String USERS_FILE = "data/users.json"; private static final long VERIFICATION_CODE_EXPIRY_MINUTES = 10; private Map users; private Map verificationCodeTimestamps; private ObjectMapper objectMapper; public UserService() { this.users = new ConcurrentHashMap<>(); this.verificationCodeTimestamps = new ConcurrentHashMap<>(); this.objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); loadUsers(); } private void loadUsers() { try { File file = new File(USERS_FILE); if (file.exists() && file.length() > 0) { User[] userArray = objectMapper.readValue(file, User[].class); for (User user : userArray) { users.put(user.getEmail(), user); } System.out.println("成功加载 " + userArray.length + " 个用户"); } else { saveUsers(); System.out.println("创建新的用户数据文件"); } } catch (IOException e) { System.err.println("加载用户数据失败: " + e.getMessage()); backupAndRecreateFile(); } } private void backupAndRecreateFile() { try { File file = new File(USERS_FILE); if (file.exists()) { File backup = new File(USERS_FILE + ".bak." + System.currentTimeMillis()); file.renameTo(backup); System.err.println("损坏的文件已备份为: " + backup.getName()); } saveUsers(); System.out.println("已重新创建用户数据文件"); } catch (Exception e) { System.err.println("备份文件失败: " + e.getMessage()); } } private void saveUsers() { try { File file = new File(USERS_FILE); file.getParentFile().mkdirs(); objectMapper.writerWithDefaultPrettyPrinter().writeValue(file, users.values().toArray(new User[0])); } catch (IOException e) { System.err.println("保存用户数据失败: " + e.getMessage()); e.printStackTrace(); } } public boolean registerUser(String email, String verificationCode) { if (users.containsKey(email)) { return false; // 用户已存在 } // 生成密码占位符,实际密码在验证后设置 String tempPasswordHash = BCrypt.withDefaults().hashToString(12, "temp".toCharArray()); User user = new User(email, tempPasswordHash, verificationCode); users.put(email, user); verificationCodeTimestamps.put(email, LocalDateTime.now()); saveUsers(); return true; } public boolean verifyUser(String username, String email, String verificationCode, String password) { User user = users.get(email); if (user == null || !user.getVerificationCode().equals(verificationCode)) { return false; } // 检查验证码是否过期 LocalDateTime codeTime = verificationCodeTimestamps.get(email); if (codeTime == null || codeTime.plusMinutes(VERIFICATION_CODE_EXPIRY_MINUTES).isBefore(LocalDateTime.now())) { return false; } // 验证密码格式 if (!validatePassword(password)) { return false; } // 设置实际密码 String passwordHash = BCrypt.withDefaults().hashToString(12, password.toCharArray()); user.setPasswordHash(passwordHash); user.setUsername(username); user.setVerified(true); verificationCodeTimestamps.remove(email); saveUsers(); return true; } public boolean login(String email, String password) { User user = users.get(email); if (user == null || !user.isVerified()) { return false; } BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), user.getPasswordHash()); return result.verified; } public boolean changePassword(String email, String oldPassword, String newPassword) { User user = users.get(email); if (user == null) { return false; } // 验证旧密码 BCrypt.Result result = BCrypt.verifyer().verify(oldPassword.toCharArray(), user.getPasswordHash()); if (!result.verified) { return false; } // 验证新密码格式 if (!validatePassword(newPassword)) { return false; } // 更新密码 String newPasswordHash = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray()); user.setPasswordHash(newPasswordHash); saveUsers(); return true; } public boolean setPasswordResetCode(String email, String verificationCode) { User user = users.get(email); if (user == null) { return false; // 用户不存在 } user.setVerificationCode(verificationCode); verificationCodeTimestamps.put(email, LocalDateTime.now()); saveUsers(); return true; } public boolean resetPasswordWithCode(String email, String verificationCode, String newPassword) { User user = users.get(email); if (user == null) { return false; } // 验证验证码 if (!user.getVerificationCode().equals(verificationCode)) { return false; } // 检查验证码是否过期 LocalDateTime codeTime = verificationCodeTimestamps.get(email); if (codeTime == null || codeTime.plusMinutes(VERIFICATION_CODE_EXPIRY_MINUTES).isBefore(LocalDateTime.now())) { return false; } // 验证新密码格式 if (!validatePassword(newPassword)) { return false; } // 更新密码 String newPasswordHash = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray()); user.setPasswordHash(newPasswordHash); verificationCodeTimestamps.remove(email); // 清除验证码 saveUsers(); return true; } public boolean validatePassword(String password) { if (password.length() < 6 || password.length() > 10) { return false; } boolean hasUpper = false; boolean hasLower = false; boolean hasDigit = false; for (char c : password.toCharArray()) { if (Character.isUpperCase(c)) hasUpper = true; if (Character.isLowerCase(c)) hasLower = true; if (Character.isDigit(c)) hasDigit = true; } return hasUpper && hasLower && hasDigit; } public boolean userExists(String email) { return users.containsKey(email); } public boolean isUserExistsAndVerified(String email) { User user = users.get(email); return user != null && user.isVerified(); } public boolean isValidEmail(String email) { return email.matches("^[A-Za-z0-9+_.-]+@(.+)$"); } public User getUser(String email ) { User user = users.get(email); return user; } }