|
|
|
|
@ -0,0 +1,228 @@
|
|
|
|
|
package com.student.mathquiz.user;
|
|
|
|
|
|
|
|
|
|
import com.google.gson.reflect.TypeToken;
|
|
|
|
|
import com.student.mathquiz.constant.Constants;
|
|
|
|
|
import com.student.mathquiz.constant.UserType;
|
|
|
|
|
import com.student.mathquiz.persistence.JsonUtils;
|
|
|
|
|
|
|
|
|
|
import java.lang.reflect.Type;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Random;
|
|
|
|
|
import java.util.Base64;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用户服务接口:定义用户操作规范,解耦实现
|
|
|
|
|
*/
|
|
|
|
|
public class UserFileService implements UserService {
|
|
|
|
|
// 存储用户数据的JSON文件路径(从常量类获取)
|
|
|
|
|
private static final String USER_DATA_PATH = Constants.USER_DATA_PATH;
|
|
|
|
|
// 存储用户列表的类型(Gson反序列化需要)
|
|
|
|
|
private static final Type USER_LIST_TYPE = new TypeToken<List<User>>() {}.getType();
|
|
|
|
|
// 验证码缓存(模拟:实际项目可用Redis,这里用内存缓存)
|
|
|
|
|
private final List<VerifyCodeCache> verifyCodeCache = new ArrayList<>();
|
|
|
|
|
// 随机数生成器(用于生成验证码)
|
|
|
|
|
private final Random random = new Random();
|
|
|
|
|
|
|
|
|
|
// ------------------------------ 1. 发送验证码 ------------------------------
|
|
|
|
|
@Override
|
|
|
|
|
public String sendVerifyCode(String email) {
|
|
|
|
|
// 1. 生成6位随机验证码(数字)
|
|
|
|
|
StringBuilder code = new StringBuilder();
|
|
|
|
|
for (int i = 0; i < Constants.VERIFY_CODE_LENGTH; i++) {
|
|
|
|
|
code.append(random.nextInt(10));
|
|
|
|
|
}
|
|
|
|
|
String verifyCode = code.toString();
|
|
|
|
|
|
|
|
|
|
// 2. 缓存验证码(有效期5分钟,避免重复使用)
|
|
|
|
|
verifyCodeCache.add(new VerifyCodeCache(email, verifyCode, System.currentTimeMillis() + 5 * 60 * 1000));
|
|
|
|
|
|
|
|
|
|
// 3. 模拟发送(控制台输出,实际项目替换为邮件发送逻辑)
|
|
|
|
|
System.out.println("[验证码发送] 邮箱:" + email + ",验证码:" + verifyCode + "(有效期5分钟)");
|
|
|
|
|
|
|
|
|
|
return verifyCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------ 2. 验证邮箱格式 ------------------------------
|
|
|
|
|
@Override
|
|
|
|
|
public boolean isValidEmail(String email) {
|
|
|
|
|
if (email == null || email.isEmpty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 邮箱格式正则(简单校验:包含@和.)
|
|
|
|
|
String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
|
|
|
|
|
return email.matches(emailRegex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------ 3. 用户注册 ------------------------------
|
|
|
|
|
@Override
|
|
|
|
|
public boolean register(String email, String verifyCode, String password, UserType userType) {
|
|
|
|
|
// 步骤1:验证邮箱格式
|
|
|
|
|
if (!isValidEmail(email)) {
|
|
|
|
|
System.out.println("[注册失败] 邮箱格式不正确!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤2:检查邮箱是否已注册
|
|
|
|
|
if (isEmailRegistered(email)) {
|
|
|
|
|
System.out.println("[注册失败] 该邮箱已注册!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤3:验证验证码(是否存在、未过期、匹配)
|
|
|
|
|
VerifyCodeCache validCache = verifyCodeCache.stream()
|
|
|
|
|
.filter(cache -> cache.getEmail().equals(email)
|
|
|
|
|
&& cache.getExpireTime() > System.currentTimeMillis()
|
|
|
|
|
&& cache.getVerifyCode().equals(verifyCode))
|
|
|
|
|
.findFirst()
|
|
|
|
|
.orElse(null);
|
|
|
|
|
if (validCache == null) {
|
|
|
|
|
System.out.println("[注册失败] 验证码无效或已过期!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤4:验证密码规则(6-10位,含大小写+数字)
|
|
|
|
|
if (!password.matches(Constants.PASSWORD_REGEX)) {
|
|
|
|
|
System.out.println("[注册失败] 密码需6-10位,含大小写字母和数字!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤5:加密密码(Base64简单加密,实际项目可用MD5+盐值)
|
|
|
|
|
String encryptedPwd = Base64.getEncoder().encodeToString(password.getBytes());
|
|
|
|
|
|
|
|
|
|
// 步骤6:读取现有用户列表
|
|
|
|
|
List<User> userList = JsonUtils.readFromJson(USER_DATA_PATH, USER_LIST_TYPE);
|
|
|
|
|
if (userList == null) {
|
|
|
|
|
userList = new ArrayList<>(); // 首次注册,创建空列表
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤7:添加新用户
|
|
|
|
|
userList.add(new User(email, encryptedPwd, userType));
|
|
|
|
|
|
|
|
|
|
// 步骤8:写入JSON文件
|
|
|
|
|
boolean writeSuccess = JsonUtils.writeToJson(USER_DATA_PATH, userList);
|
|
|
|
|
if (writeSuccess) {
|
|
|
|
|
System.out.println("[注册成功] 邮箱:" + email + ",学段:" + userType.getDisplayName());
|
|
|
|
|
// 移除已使用的验证码(避免重复注册)
|
|
|
|
|
verifyCodeCache.remove(validCache);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
System.out.println("[注册失败] 数据存储失败!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------ 4. 用户登录 ------------------------------
|
|
|
|
|
@Override
|
|
|
|
|
public User login(String email, String password) {
|
|
|
|
|
// 步骤1:验证邮箱格式
|
|
|
|
|
if (!isValidEmail(email)) {
|
|
|
|
|
System.out.println("[登录失败] 邮箱格式不正确!");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤2:读取用户列表
|
|
|
|
|
List<User> userList = JsonUtils.readFromJson(USER_DATA_PATH, USER_LIST_TYPE);
|
|
|
|
|
if (userList == null || userList.isEmpty()) {
|
|
|
|
|
System.out.println("[登录失败] 暂无注册用户!");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤3:加密输入的密码(与存储的加密方式一致)
|
|
|
|
|
String encryptedInputPwd = Base64.getEncoder().encodeToString(password.getBytes());
|
|
|
|
|
|
|
|
|
|
// 步骤4:匹配用户(邮箱+密码)
|
|
|
|
|
for (User user : userList) {
|
|
|
|
|
if (user.getEmail().equals(email) && user.getEncryptedPwd().equals(encryptedInputPwd)) {
|
|
|
|
|
System.out.println("[登录成功] 欢迎:" + email + "(" + user.getUserType().getDisplayName() + ")");
|
|
|
|
|
return user; // 登录成功,返回用户信息
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤5:匹配失败
|
|
|
|
|
System.out.println("[登录失败] 邮箱或密码错误!");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------ 5. 修改密码 ------------------------------
|
|
|
|
|
@Override
|
|
|
|
|
public boolean changePassword(String email, String oldPassword, String newPassword) {
|
|
|
|
|
// 步骤1:验证原密码(先登录逻辑)
|
|
|
|
|
User user = login(email, oldPassword);
|
|
|
|
|
if (user == null) {
|
|
|
|
|
System.out.println("[修改失败] 原密码错误!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤2:验证新密码规则
|
|
|
|
|
if (!newPassword.matches(Constants.PASSWORD_REGEX)) {
|
|
|
|
|
System.out.println("[修改失败] 新密码需6-10位,含大小写字母和数字!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤3:读取用户列表
|
|
|
|
|
List<User> userList = JsonUtils.readFromJson(USER_DATA_PATH, USER_LIST_TYPE);
|
|
|
|
|
if (userList == null) {
|
|
|
|
|
System.out.println("[修改失败] 用户数据不存在!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤4:更新用户密码
|
|
|
|
|
for (User u : userList) {
|
|
|
|
|
if (u.getEmail().equals(email)) {
|
|
|
|
|
// 加密新密码
|
|
|
|
|
String newEncryptedPwd = Base64.getEncoder().encodeToString(newPassword.getBytes());
|
|
|
|
|
u.setEncryptedPwd(newEncryptedPwd);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 步骤5:写入更新后的用户列表
|
|
|
|
|
boolean writeSuccess = JsonUtils.writeToJson(USER_DATA_PATH, userList);
|
|
|
|
|
if (writeSuccess) {
|
|
|
|
|
System.out.println("[修改成功] 密码已更新,请重新登录!");
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
System.out.println("[修改失败] 数据存储失败!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------ 6. 检查邮箱是否已注册 ------------------------------
|
|
|
|
|
@Override
|
|
|
|
|
public boolean isEmailRegistered(String email) {
|
|
|
|
|
List<User> userList = JsonUtils.readFromJson(USER_DATA_PATH, USER_LIST_TYPE);
|
|
|
|
|
if (userList == null) {
|
|
|
|
|
return false; // 无用户数据,未注册
|
|
|
|
|
}
|
|
|
|
|
// 遍历用户列表,检查邮箱是否存在
|
|
|
|
|
return userList.stream().anyMatch(user -> user.getEmail().equals(email));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------ 内部类:验证码缓存 ------------------------------
|
|
|
|
|
private static class VerifyCodeCache {
|
|
|
|
|
private String email; // 关联的邮箱
|
|
|
|
|
private String verifyCode; // 验证码
|
|
|
|
|
private long expireTime; // 过期时间(时间戳)
|
|
|
|
|
|
|
|
|
|
public VerifyCodeCache(String email, String verifyCode, long expireTime) {
|
|
|
|
|
this.email = email;
|
|
|
|
|
this.verifyCode = verifyCode;
|
|
|
|
|
this.expireTime = expireTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Getter(用于过滤缓存)
|
|
|
|
|
public String getEmail() {
|
|
|
|
|
return email;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String getVerifyCode() {
|
|
|
|
|
return verifyCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public long getExpireTime() {
|
|
|
|
|
return expireTime;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|