|
|
package com.mathgenerator.service;
|
|
|
|
|
|
import com.google.gson.Gson;
|
|
|
import java.util.Objects;
|
|
|
import com.google.gson.GsonBuilder;
|
|
|
import com.google.gson.reflect.TypeToken;
|
|
|
import com.mathgenerator.model.User;
|
|
|
import java.io.FileReader;
|
|
|
import java.io.FileWriter;
|
|
|
import java.io.IOException;
|
|
|
import java.lang.reflect.Type;
|
|
|
import java.nio.file.Files;
|
|
|
import java.nio.file.Path;
|
|
|
import java.nio.file.Paths;
|
|
|
import java.util.Map;
|
|
|
import java.util.Optional;
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
import java.util.concurrent.ThreadLocalRandom;
|
|
|
import java.util.regex.Pattern;
|
|
|
import org.apache.commons.mail.Email;
|
|
|
import org.apache.commons.mail.EmailException;
|
|
|
import org.apache.commons.mail.SimpleEmail;
|
|
|
|
|
|
|
|
|
public class UserService {
|
|
|
private static final Path USER_FILE_PATH = Paths.get("users.json");
|
|
|
// 密码策略: 6-10位, 必须包含大小写字母和数字
|
|
|
private static final Pattern PASSWORD_PATTERN =
|
|
|
Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{6,10}$");
|
|
|
private Map<String, User> userDatabase;
|
|
|
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
|
|
|
|
|
public UserService() {
|
|
|
this.userDatabase = loadUsersFromFile();
|
|
|
}
|
|
|
|
|
|
private Map<String, User> loadUsersFromFile() {
|
|
|
try {
|
|
|
if (Files.exists(USER_FILE_PATH) && Files.size(USER_FILE_PATH) > 0) {
|
|
|
try (FileReader reader = new FileReader(USER_FILE_PATH.toFile())) {
|
|
|
Type type = new TypeToken<Map<String, User>>() {}.getType();
|
|
|
Map<String, User> loadedUsers = gson.fromJson(reader, type);
|
|
|
return loadedUsers != null ? new ConcurrentHashMap<>(loadedUsers) : new ConcurrentHashMap<>();
|
|
|
}
|
|
|
}
|
|
|
} catch (IOException e) {
|
|
|
System.err.println("错误:加载用户文件失败 - " + e.getMessage());
|
|
|
}
|
|
|
return new ConcurrentHashMap<>();
|
|
|
}
|
|
|
|
|
|
private void saveUsers() {
|
|
|
try (FileWriter writer = new FileWriter(USER_FILE_PATH.toFile())) {
|
|
|
gson.toJson(this.userDatabase, writer);
|
|
|
} catch (IOException e) {
|
|
|
System.err.println("错误:保存用户文件失败 - " + e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public Optional<User> findUserByUsername(String username) {
|
|
|
return Optional.ofNullable(this.userDatabase.get(username));
|
|
|
}
|
|
|
|
|
|
public Optional<User> login(String username, String password) {
|
|
|
return findUserByUsername(username)
|
|
|
.filter(user -> user.password().equals(password));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* (已更新) 发送真实的邮件验证码。
|
|
|
* @param email 用户的邮箱
|
|
|
* @return 成功发送则返回生成的6位验证码, 失败则返回null
|
|
|
*/
|
|
|
public String sendVerificationCode(String email) {
|
|
|
String code = String.format("%06d", ThreadLocalRandom.current().nextInt(100000, 1000000));
|
|
|
|
|
|
try {
|
|
|
Email mail = new SimpleEmail();
|
|
|
|
|
|
// 1. 设置SMTP服务器信息
|
|
|
mail.setHostName(EmailConfig.getHost());
|
|
|
mail.setSmtpPort(EmailConfig.getPort());
|
|
|
mail.setAuthentication(EmailConfig.getUsername(), EmailConfig.getPassword());
|
|
|
mail.setSSLOnConnect(true); // 开启SSL加密
|
|
|
|
|
|
// 2. 设置邮件内容
|
|
|
mail.setFrom(EmailConfig.getUsername()); // 发件人
|
|
|
mail.setSubject("【数学学习软件】您的注册验证码"); // 邮件主题
|
|
|
mail.setMsg("您好!\n\n感谢您注册数学学习软件。您的验证码是:" + code + "\n\n请在5分钟内使用。"); // 邮件正文
|
|
|
mail.addTo(email); // 收件人
|
|
|
|
|
|
// 3. 发送邮件
|
|
|
mail.send();
|
|
|
|
|
|
System.out.println("验证码邮件已成功发送至: " + email);
|
|
|
return code;
|
|
|
|
|
|
} catch (EmailException e) {
|
|
|
System.err.println("错误:发送验证码邮件失败!请检查您的 config.properties 配置或网络连接。");
|
|
|
e.printStackTrace();
|
|
|
return null; // 发送失败
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 注册新用户。
|
|
|
* @return 成功返回true, 否则返回false
|
|
|
*/
|
|
|
public boolean register(String username, String email, String password) {
|
|
|
// 1. 基础校验:防止 null 或空白输入
|
|
|
if (username == null || email == null || password == null ||
|
|
|
username.trim().isEmpty() || email.trim().isEmpty() || password.trim().isEmpty()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 2. 检查用户名或邮箱是否已存在(使用 Objects.equals 安全比较)
|
|
|
boolean usernameExists = userDatabase.containsKey(username);
|
|
|
boolean emailExists = userDatabase.values().stream()
|
|
|
.anyMatch(u -> Objects.equals(u.email(), email));
|
|
|
|
|
|
if (usernameExists || emailExists) {
|
|
|
return false; // 用户名或邮箱已存在
|
|
|
}
|
|
|
|
|
|
// 3. 创建新用户并保存
|
|
|
User newUser = new User(username, email, password);
|
|
|
userDatabase.put(username, newUser);
|
|
|
saveUsers();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 验证密码是否符合复杂度要求。
|
|
|
* @param password 待验证的密码
|
|
|
* @return true如果符合要求
|
|
|
*/
|
|
|
public static boolean isPasswordValid(String password) {
|
|
|
return password != null && PASSWORD_PATTERN.matcher(password).matches();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 修改密码。
|
|
|
* @return 成功返回true
|
|
|
*/
|
|
|
public boolean changePassword(String username, String oldPassword, String newPassword) {
|
|
|
return findUserByUsername(username)
|
|
|
.filter(user -> user.password().equals(oldPassword))
|
|
|
.map(user -> {
|
|
|
User updatedUser = new User(user.username(), user.email(), newPassword);
|
|
|
userDatabase.put(username, updatedUser);
|
|
|
saveUsers();
|
|
|
return true;
|
|
|
}).orElse(false);
|
|
|
}
|
|
|
} |