diff --git a/src/main/java/com/example/mathsystemtogether/EmailService.java b/src/main/java/com/example/mathsystemtogether/EmailService.java new file mode 100644 index 0000000..b5f5155 --- /dev/null +++ b/src/main/java/com/example/mathsystemtogether/EmailService.java @@ -0,0 +1,186 @@ +package com.example.mathsystemtogether; + +import jakarta.mail.*; +import jakarta.mail.internet.*; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.Random; + +/** + * 邮件服务类 + * 负责发送验证码邮件 + */ +public class EmailService { + + private String smtpHost; + private String smtpPort; + private String fromEmail; + private String fromPassword; + private Session session; + private boolean isConfigured = false; + + public EmailService() { + loadConfiguration(); + if (isConfigured) { + initializeSession(); + } + } + + /** + * 从配置文件加载邮件配置 + */ + private void loadConfiguration() { + try (InputStream input = getClass().getClassLoader().getResourceAsStream("mail.properties")) { + if (input == null) { + return; + } + + Properties props = new Properties(); + props.load(input); + + smtpHost = props.getProperty("mail.smtp.host"); + smtpPort = props.getProperty("mail.smtp.port"); + fromEmail = props.getProperty("mail.from.email"); + fromPassword = props.getProperty("mail.from.password"); + + // 检查配置是否完整 + if (smtpHost != null && smtpPort != null && fromEmail != null && fromPassword != null) { + isConfigured = true; + } + + } catch (IOException e) { + // 配置加载失败,静默处理 + } + } + + /** + * 初始化邮件会话 + */ + private void initializeSession() { + if (!isConfigured) { + return; + } + + Properties props = new Properties(); + props.put("mail.smtp.host", smtpHost); + props.put("mail.smtp.port", smtpPort); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.starttls.enable", "true"); + props.put("mail.smtp.ssl.trust", smtpHost); + + // 创建认证器 + Authenticator authenticator = new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(fromEmail, fromPassword); + } + }; + + session = Session.getInstance(props, authenticator); + } + + /** + * 生成6位数字验证码 + */ + public String generateVerificationCode() { + Random random = new Random(); + return String.format("%06d", random.nextInt(1000000)); + } + + /** + * 发送验证码邮件 + * @param toEmail 接收方邮箱 + * @param verificationCode 验证码 + * @return 发送是否成功 + */ + public boolean sendVerificationCode(String toEmail, String verificationCode) { + if (!isConfigured) { + return false; + } + + try { + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(fromEmail)); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail)); + message.setSubject("数学考试系统 - 邮箱验证码"); + + // 创建邮件内容 + String htmlContent = createEmailContent(verificationCode); + message.setContent(htmlContent, "text/html; charset=UTF-8"); + + // 发送邮件 + Transport.send(message); + return true; + + } catch (MessagingException e) { + return false; + } + } + + /** + * 创建邮件HTML内容 + */ + private String createEmailContent(String verificationCode) { + StringBuilder html = new StringBuilder(); + html.append(""); + html.append(""); + html.append(""); + html.append(""); + html.append("邮箱验证码"); + html.append(""); + html.append(""); + html.append(""); + html.append("
"); + html.append("
"); + html.append("

数学考试系统

"); + html.append("

邮箱验证码

"); + html.append("
"); + html.append("
"); + html.append("

您好!

"); + html.append("

您正在注册数学考试系统,请使用以下验证码完成注册:

"); + html.append("
"); + html.append("

验证码:

"); + html.append("
").append(verificationCode).append("
"); + html.append("
"); + html.append("
"); + html.append("

重要提示:

"); + html.append("
    "); + html.append("
  • 验证码有效期为 5分钟
  • "); + html.append("
  • 请勿将验证码泄露给他人
  • "); + html.append("
  • 如非本人操作,请忽略此邮件
  • "); + html.append("
"); + html.append("
"); + html.append("

如果您没有注册数学考试系统,请忽略此邮件。

"); + html.append("

祝您使用愉快!

"); + html.append("

数学考试系统团队

"); + html.append("
"); + html.append("
"); + html.append("

此邮件由系统自动发送,请勿回复

"); + html.append("

2024 数学考试系统. All rights reserved.

"); + html.append("
"); + html.append("
"); + html.append(""); + html.append(""); + + return html.toString(); + } + + + /** + * 检查邮件服务是否可用 + */ + public boolean isAvailable() { + return isConfigured && session != null; + } +} diff --git a/src/main/java/com/example/mathsystemtogether/RegisterController.java b/src/main/java/com/example/mathsystemtogether/RegisterController.java new file mode 100644 index 0000000..9939189 --- /dev/null +++ b/src/main/java/com/example/mathsystemtogether/RegisterController.java @@ -0,0 +1,310 @@ +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 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 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 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 (!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 isUserExists(String username) { + try { + if (!Files.exists(Paths.get(USER_DATA_FILE))) { + return false; + } + + List 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 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); + } +} diff --git a/src/main/resources/com/example/mathsystemtogether/register-view.fxml b/src/main/resources/com/example/mathsystemtogether/register-view.fxml new file mode 100644 index 0000000..da5f9f4 --- /dev/null +++ b/src/main/resources/com/example/mathsystemtogether/register-view.fxml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/src/main/resources/mail.properties b/src/main/resources/mail.properties new file mode 100644 index 0000000..fbeb580 --- /dev/null +++ b/src/main/resources/mail.properties @@ -0,0 +1,43 @@ +# 邮件服务配置 +# QQ邮箱配置 + +# QQ邮箱SMTP配置 +mail.smtp.host=smtp.qq.com +mail.smtp.port=587 +mail.smtp.auth=true +mail.smtp.starttls.enable=true +mail.smtp.ssl.trust=smtp.qq.com + +# 发送方邮箱配置 +mail.from.email=1961004835@qq.com +mail.from.password=nixyzbpbcrekhgfe + +# 其他邮箱服务商配置示例: + +# Gmail配置 +# mail.smtp.host=smtp.gmail.com +# mail.smtp.port=587 +# mail.smtp.auth=true +# mail.smtp.starttls.enable=true +# mail.smtp.ssl.trust=smtp.gmail.com + +# 163邮箱配置 +# mail.smtp.host=smtp.163.com +# mail.smtp.port=587 +# mail.smtp.auth=true +# mail.smtp.starttls.enable=true +# mail.smtp.ssl.trust=smtp.163.com + +# 126邮箱配置 +# mail.smtp.host=smtp.126.com +# mail.smtp.port=587 +# mail.smtp.auth=true +# mail.smtp.starttls.enable=true +# mail.smtp.ssl.trust=smtp.126.com + +# 企业邮箱配置示例 +# mail.smtp.host=smtp.exmail.qq.com +# mail.smtp.port=587 +# mail.smtp.auth=true +# mail.smtp.starttls.enable=true +# mail.smtp.ssl.trust=smtp.exmail.qq.com diff --git a/user_data.txt b/user_data.txt new file mode 100644 index 0000000..af77928 --- /dev/null +++ b/user_data.txt @@ -0,0 +1 @@ +wgll|123456|ymhlovesLQX@163.com|小学|1760162416490