|
|
package com.example.mathsystemtogether;
|
|
|
|
|
|
import javafx.fxml.FXML;
|
|
|
import javafx.fxml.FXMLLoader;
|
|
|
import javafx.scene.Scene;
|
|
|
import javafx.scene.control.*;
|
|
|
import javafx.stage.Stage;
|
|
|
import javafx.collections.FXCollections;
|
|
|
import javafx.collections.ObservableList;
|
|
|
import java.util.*;
|
|
|
import java.util.regex.Pattern;
|
|
|
import java.io.*;
|
|
|
import java.nio.file.Files;
|
|
|
import java.nio.file.Paths;
|
|
|
import java.nio.file.StandardOpenOption;
|
|
|
|
|
|
/**
|
|
|
* 注册控制器
|
|
|
*/
|
|
|
public class RegisterController {
|
|
|
|
|
|
@FXML private TextField usernameField;
|
|
|
@FXML private PasswordField passwordField;
|
|
|
@FXML private PasswordField confirmPasswordField;
|
|
|
@FXML private TextField emailField;
|
|
|
@FXML private TextField verificationCodeField;
|
|
|
@FXML private Button sendCodeButton;
|
|
|
@FXML private Button registerButton;
|
|
|
@FXML private Button backToLoginButton;
|
|
|
@FXML private ComboBox<String> levelComboBox;
|
|
|
@FXML private Label statusLabel;
|
|
|
|
|
|
// 验证码相关
|
|
|
private String generatedCode;
|
|
|
private long codeGenerationTime;
|
|
|
private static final int CODE_VALIDITY_MINUTES = 5;
|
|
|
private static final String EMAIL_REGEX = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";
|
|
|
private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX);
|
|
|
|
|
|
// 密码强度验证正则表达式
|
|
|
private static final String PASSWORD_REGEX = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$";
|
|
|
private static final Pattern PASSWORD_PATTERN = Pattern.compile(PASSWORD_REGEX);
|
|
|
|
|
|
// 用户数据文件路径
|
|
|
private static final String USER_DATA_FILE = "user_data.txt";
|
|
|
|
|
|
// 邮件服务
|
|
|
private EmailService emailService;
|
|
|
|
|
|
@FXML
|
|
|
public void initialize() {
|
|
|
setupLevelComboBox();
|
|
|
// 初始化时禁用注册按钮
|
|
|
registerButton.setDisable(true);
|
|
|
// 初始化邮件服务
|
|
|
emailService = new EmailService();
|
|
|
|
|
|
// 检查邮件服务配置
|
|
|
if (!emailService.isAvailable()) {
|
|
|
showStatus("⚠️ 邮件服务未配置,请检查mail.properties文件", true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void setupLevelComboBox() {
|
|
|
ObservableList<String> levels = FXCollections.observableArrayList("小学", "初中", "高中");
|
|
|
levelComboBox.setItems(levels);
|
|
|
levelComboBox.setValue("小学");
|
|
|
}
|
|
|
|
|
|
@FXML
|
|
|
private void handleSendCode() {
|
|
|
String email = emailField.getText().trim();
|
|
|
|
|
|
if (email.isEmpty()) {
|
|
|
showStatus("请输入邮箱地址", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (!isValidEmail(email)) {
|
|
|
showStatus("请输入有效的邮箱地址", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 检查邮件服务是否可用
|
|
|
if (!emailService.isAvailable()) {
|
|
|
showStatus("❌ 邮件服务不可用,请检查配置", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 生成6位数字验证码
|
|
|
generatedCode = emailService.generateVerificationCode();
|
|
|
codeGenerationTime = System.currentTimeMillis();
|
|
|
|
|
|
// 发送真实邮件
|
|
|
showStatus("📤 正在发送验证码到邮箱:" + email + ",请稍候...", false);
|
|
|
|
|
|
// 在后台线程中发送邮件
|
|
|
new Thread(() -> {
|
|
|
boolean success = emailService.sendVerificationCode(email, generatedCode);
|
|
|
|
|
|
javafx.application.Platform.runLater(() -> {
|
|
|
if (success) {
|
|
|
showStatus("✅ 验证码已发送到邮箱:" + email + ",请查收邮件", false);
|
|
|
// 启用注册按钮
|
|
|
registerButton.setDisable(false);
|
|
|
} else {
|
|
|
showStatus("❌ 邮件发送失败,请检查邮箱地址或稍后重试", true);
|
|
|
// 显示验证码用于调试(仅开发环境)
|
|
|
showStatus("❌ 邮件发送失败,请检查邮箱地址或稍后重试。验证码:" + generatedCode + "(调试用)", true);
|
|
|
}
|
|
|
});
|
|
|
}).start();
|
|
|
|
|
|
// 禁用发送按钮60秒
|
|
|
sendCodeButton.setDisable(true);
|
|
|
sendCodeButton.setText("60秒后可重发");
|
|
|
|
|
|
// 启动倒计时
|
|
|
startCountdown();
|
|
|
}
|
|
|
|
|
|
@FXML
|
|
|
private void handleRegister() {
|
|
|
String username = usernameField.getText().trim();
|
|
|
String password = passwordField.getText();
|
|
|
String confirmPassword = confirmPasswordField.getText();
|
|
|
String email = emailField.getText().trim();
|
|
|
String verificationCode = verificationCodeField.getText().trim();
|
|
|
String level = levelComboBox.getValue();
|
|
|
|
|
|
// 验证输入
|
|
|
if (username.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() ||
|
|
|
email.isEmpty() || verificationCode.isEmpty()) {
|
|
|
showStatus("请填写所有字段", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (username.length() < 3) {
|
|
|
showStatus("用户名至少需要3个字符", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (password.length() < 6) {
|
|
|
showStatus("密码至少需要6个字符", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (!isValidPassword(password)) {
|
|
|
showStatus("密码必须包含大小写字母和数字", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (!password.equals(confirmPassword)) {
|
|
|
showStatus("两次输入的密码不一致", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (!isValidEmail(email)) {
|
|
|
showStatus("请输入有效的邮箱地址", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 验证验证码
|
|
|
if (!verifyCode(verificationCode)) {
|
|
|
showStatus("验证码错误或已过期", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 检查用户名是否已存在
|
|
|
if (isUserExists(username)) {
|
|
|
showStatus("用户名已存在,请选择其他用户名", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 检查邮箱是否已注册
|
|
|
if (isEmailExists(email)) {
|
|
|
showStatus("该邮箱已被注册", true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 保存用户信息
|
|
|
if (saveUserData(username, password, email, level)) {
|
|
|
showStatus("注册成功!正在跳转到登录界面...", false);
|
|
|
|
|
|
// 延迟跳转到登录界面
|
|
|
Timer timer = new Timer();
|
|
|
timer.schedule(new TimerTask() {
|
|
|
@Override
|
|
|
public void run() {
|
|
|
javafx.application.Platform.runLater(() -> {
|
|
|
handleBackToLogin();
|
|
|
});
|
|
|
}
|
|
|
}, 2000);
|
|
|
} else {
|
|
|
showStatus("注册失败,请重试", true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@FXML
|
|
|
private void handleBackToLogin() {
|
|
|
try {
|
|
|
// 关闭当前窗口
|
|
|
Stage currentStage = (Stage) backToLoginButton.getScene().getWindow();
|
|
|
currentStage.close();
|
|
|
|
|
|
// 打开登录界面
|
|
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("exam-view.fxml"));
|
|
|
Scene scene = new Scene(loader.load(), 900, 900);
|
|
|
Stage loginStage = new Stage();
|
|
|
loginStage.setTitle("数学考试系统 - 登录");
|
|
|
loginStage.setScene(scene);
|
|
|
loginStage.setResizable(true);
|
|
|
loginStage.show();
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
showStatus("跳转失败:" + e.getMessage(), true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
private boolean verifyCode(String inputCode) {
|
|
|
if (generatedCode == null) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 检查验证码是否过期
|
|
|
long currentTime = System.currentTimeMillis();
|
|
|
long elapsedMinutes = (currentTime - codeGenerationTime) / (1000 * 60);
|
|
|
|
|
|
if (elapsedMinutes > CODE_VALIDITY_MINUTES) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
return generatedCode.equals(inputCode);
|
|
|
}
|
|
|
|
|
|
private boolean isValidEmail(String email) {
|
|
|
return EMAIL_PATTERN.matcher(email).matches();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 验证密码强度
|
|
|
* 密码必须包含至少一个小写字母、一个大写字母和一个数字
|
|
|
*/
|
|
|
private boolean isValidPassword(String password) {
|
|
|
return PASSWORD_PATTERN.matcher(password).matches();
|
|
|
}
|
|
|
|
|
|
private boolean isUserExists(String username) {
|
|
|
try {
|
|
|
if (!Files.exists(Paths.get(USER_DATA_FILE))) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
List<String> lines = Files.readAllLines(Paths.get(USER_DATA_FILE));
|
|
|
for (String line : lines) {
|
|
|
if (line.startsWith(username + "|")) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
} catch (IOException e) {
|
|
|
System.err.println("检查用户存在性时出错:" + e.getMessage());
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
private boolean isEmailExists(String email) {
|
|
|
try {
|
|
|
if (!Files.exists(Paths.get(USER_DATA_FILE))) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
List<String> lines = Files.readAllLines(Paths.get(USER_DATA_FILE));
|
|
|
for (String line : lines) {
|
|
|
String[] parts = line.split("\\|");
|
|
|
if (parts.length >= 3 && parts[2].equals(email)) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
} catch (IOException e) {
|
|
|
System.err.println("检查邮箱存在性时出错:" + e.getMessage());
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
private boolean saveUserData(String username, String password, String email, String level) {
|
|
|
try {
|
|
|
String userData = username + "|" + password + "|" + email + "|" + level + "|" + System.currentTimeMillis() + "\n";
|
|
|
Files.write(Paths.get(USER_DATA_FILE), userData.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
|
|
|
return true;
|
|
|
} catch (IOException e) {
|
|
|
System.err.println("保存用户数据时出错:" + e.getMessage());
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void showStatus(String message, boolean isError) {
|
|
|
statusLabel.setText(message);
|
|
|
if (isError) {
|
|
|
statusLabel.setStyle("-fx-text-fill: #DC143C; -fx-font-weight: bold; -fx-font-size: 14;");
|
|
|
} else {
|
|
|
statusLabel.setStyle("-fx-text-fill: #228B22; -fx-font-weight: bold; -fx-font-size: 14;");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void startCountdown() {
|
|
|
Timer timer = new Timer();
|
|
|
timer.schedule(new TimerTask() {
|
|
|
int countdown = 60;
|
|
|
|
|
|
@Override
|
|
|
public void run() {
|
|
|
javafx.application.Platform.runLater(() -> {
|
|
|
if (countdown > 0) {
|
|
|
sendCodeButton.setText(countdown + "秒后可重发");
|
|
|
countdown--;
|
|
|
} else {
|
|
|
sendCodeButton.setDisable(false);
|
|
|
sendCodeButton.setText("📤 发送验证码");
|
|
|
timer.cancel();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}, 0, 1000);
|
|
|
}
|
|
|
}
|