Merge remote-tracking branch 'origin/develop' into develop2

# Conflicts:
#	user_data.txt
develop2
梁晨旭 2 months ago
commit da71917de0

@ -31,6 +31,8 @@ mvn exec:java -Dexec.mainClass="com.example.mathsystemtogether.HelloApplication"
## 👤 用户账号
### 预设测试账号
系统预设了9个测试账号
| 用户名 | 密码 | 难度级别 | 题目类型 |
@ -45,12 +47,54 @@ mvn exec:java -Dexec.mainClass="com.example.mathsystemtogether.HelloApplication"
| 王五2 | 123 | 高中 | 三角函数运算 |
| 王五3 | 123 | 高中 | 三角函数运算 |
### 新用户注册
系统支持新用户注册功能:
1. **点击注册按钮**:在登录界面点击"📝 注册"按钮
2. **填写注册信息**
- 用户名至少3个字符
- 密码至少6个字符
- 确认密码
- 邮箱地址
- 难度级别选择
3. **邮箱验证**
- 点击"📤 发送验证码"获取验证码
- 输入收到的6位数字验证码
- 验证码有效期为5分钟
4. **完成注册**:点击"🚀 注册"完成账户创建
5. **自动跳转**:注册成功后自动返回登录界面
**注意**
- 用户名不能重复
- 邮箱地址格式必须正确
- 验证码通过真实邮件发送
- 用户数据保存在本地 `user_data.txt` 文件中
### 邮件服务配置
系统支持真实的邮箱验证码功能已配置QQ邮箱
- **邮箱地址**1961004835@qq.com
- **SMTP服务器**smtp.qq.com:587
- **配置文件**`src/main/resources/mail.properties`
如需修改邮箱配置,请编辑 `mail.properties` 文件。
## 🎮 使用流程
### 1. 用户登录
### 1. 用户登录或注册
**登录现有账户:**
- 输入用户名张三1
- 输入密码123
- 点击"登录"按钮
- 点击"🚀 登录"按钮
**注册新账户:**
- 点击"📝 注册"按钮
- 填写注册信息(用户名、密码、邮箱、难度级别)
- 获取并输入邮箱验证码
- 点击"🚀 注册"完成账户创建
### 2. 设置考试
- 选择难度级别(小学/初中/高中)
@ -125,10 +169,16 @@ MathSystemTogether/
├── src/main/java/com/example/mathsystemtogether/
│ ├── HelloApplication.java # 主应用程序入口
│ ├── ExamController.java # 考试系统控制器
│ ├── RegisterController.java # 注册功能控制器
│ ├── EmailService.java # 邮件服务类
│ ├── Question.java # 选择题数据模型
│ └── ChoiceQuestionGenerator.java # 选择题生成器
├── src/main/resources/com/example/mathsystemtogether/
│ └── exam-view.fxml # 考试界面布局文件
│ ├── exam-view.fxml # 考试界面布局文件
│ └── register-view.fxml # 注册界面布局文件
├── src/main/resources/
│ └── mail.properties # 邮件服务配置文件
├── user_data.txt # 用户数据存储文件
├── papers/ # 题目文件存储目录
│ ├── 张三1/ # 用户文件夹
│ ├── 李四1/
@ -140,6 +190,12 @@ MathSystemTogether/
## 🔧 核心功能
### 用户管理功能
- **用户注册**:支持新用户注册,包含邮箱验证
- **用户登录**:支持预设账号和注册账号登录
- **数据存储**:用户信息保存在本地文件中
- **数据验证**:用户名唯一性、邮箱格式验证
### 题目生成算法
- **小学**:基础四则运算(+、-、×、÷)
- **初中**:平方运算(²)、开方运算(√)
@ -156,6 +212,14 @@ MathSystemTogether/
- 进度实时更新
- 结果自动统计
### 邮箱验证功能
- **真实邮件发送**通过SMTP服务器发送验证码邮件
- **验证码生成**6位数字随机验证码
- **时效控制**验证码5分钟有效期
- **防重复发送**60秒倒计时限制
- **格式验证**:邮箱地址格式检查
- **HTML邮件**美观的HTML格式验证码邮件
## 📊 考试结果
系统会显示:

@ -61,6 +61,14 @@
<target>21</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.example.mathsystemtogether.HelloApplication</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>

@ -7,11 +7,22 @@ echo 系统功能:
echo - 支持小学、初中、高中三个难度级别
echo - 选择题形式答题
echo - 自动评分和结果统计
echo - 用户注册功能(邮箱验证码)
echo - 本地用户数据存储
echo.
echo 测试账号:
echo - 小学张三1/123, 张三2/123, 张三3/123
echo - 初中李四1/123, 李四2/123, 李四3/123
echo - 高中王五1/123, 王五2/123, 王五3/123
echo.
echo 新用户注册:
echo - 点击"注册"按钮创建新账户
echo - 使用邮箱获取验证码
echo - 选择适合的难度级别
echo.
echo 邮件服务配置:
echo - 已配置QQ邮箱1961004835@qq.com
echo - 配置文件src/main/resources/mail.properties
echo.
mvn exec:java -Dexec.mainClass="com.example.mathsystemtogether.HelloApplication"
pause

@ -1,125 +0,0 @@
package com.example.mathsystemtogether;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import jakarta.mail.Authenticator;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.PasswordAuthentication;
import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
/**
*
* sendEmail SMTP
*/
public class EmailCodeService {
private static class CodeRecord {
final String code;
final Instant expireAt;
CodeRecord(String code, Instant expireAt) {
this.code = code;
this.expireAt = expireAt;
}
}
private final Map<String, CodeRecord> emailToCode = new ConcurrentHashMap<>();
private final Map<String, Instant> emailToLastSend = new ConcurrentHashMap<>();
private final Random random = new Random();
private final Duration codeTtl = Duration.ofMinutes(5);
private final Duration rateLimit = Duration.ofSeconds(60);
public void sendCode(String email) {
Instant now = Instant.now();
Instant last = emailToLastSend.get(email);
if (last != null && Duration.between(last, now).compareTo(rateLimit) < 0) {
long waitSeconds = rateLimit.minus(Duration.between(last, now)).getSeconds();
throw new IllegalStateException("请求过于频繁,请于" + waitSeconds + "秒后重试");
}
String code = generateCode();
emailToCode.put(email, new CodeRecord(code, now.plus(codeTtl)));
emailToLastSend.put(email, now);
// 使用 SMTP 发送邮件QQ邮箱
sendEmail(email, code);
}
public boolean verifyCode(String email, String code) {
CodeRecord record = emailToCode.get(email);
if (record == null) return false;
if (Instant.now().isAfter(record.expireAt)) {
emailToCode.remove(email);
return false;
}
boolean ok = record.code.equals(code);
if (ok) {
emailToCode.remove(email); // 一次性
}
return ok;
}
private String generateCode() {
return String.valueOf(100000 + random.nextInt(900000));
}
private void sendEmail(String email, String code) {
// 从资源文件读取发件配置
Properties conf = new Properties();
try {
conf.load(EmailCodeService.class.getResourceAsStream("/com/example/mathsystemtogether/mail.properties"));
} catch (Exception e) {
System.out.println("[EmailCodeService] 读取 mail.properties 失败,改为控制台输出验证码:" + code);
return;
}
String from = conf.getProperty("mail.from", "");
String authCode = conf.getProperty("mail.authCode", "");
String host = conf.getProperty("mail.host", "smtp.qq.com");
String port = conf.getProperty("mail.port", "465");
boolean ssl = Boolean.parseBoolean(conf.getProperty("mail.ssl", "true"));
String subject = conf.getProperty("mail.subject", "您的验证码");
String bodyPrefix = conf.getProperty("mail.bodyPrefix", "验证码:");
String bodySuffix = conf.getProperty("mail.bodySuffix", "5分钟内有效。");
if (from.isEmpty() || authCode.isEmpty()) {
System.out.println("[EmailCodeService] 未配置 mail.from 或 mail.authCode改为控制台输出验证码" + code);
return;
}
Properties props = new Properties();
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", String.valueOf(ssl));
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(from, authCode);
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(email));
message.setSubject(subject);
message.setText(bodyPrefix + code + bodySuffix);
Transport.send(message);
} catch (MessagingException e) {
throw new IllegalStateException("邮件发送失败:" + e.getMessage(), e);
}
}
}

@ -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("<!DOCTYPE html>");
html.append("<html>");
html.append("<head>");
html.append("<meta charset=\"UTF-8\">");
html.append("<title>邮箱验证码</title>");
html.append("<style>");
html.append("body { font-family: Arial, sans-serif; background-color: #f5f5f5; margin: 0; padding: 20px; }");
html.append(".container { max-width: 600px; margin: 0 auto; background-color: white; border-radius: 10px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); overflow: hidden; }");
html.append(".header { background: linear-gradient(135deg, #8A2BE2, #9932CC); color: white; padding: 30px; text-align: center; }");
html.append(".header h1 { margin: 0; font-size: 24px; }");
html.append(".content { padding: 30px; }");
html.append(".verification-code { background-color: #f8f9fa; border: 2px solid #8A2BE2; border-radius: 8px; padding: 20px; text-align: center; margin: 20px 0; }");
html.append(".code { font-size: 32px; font-weight: bold; color: #8A2BE2; letter-spacing: 5px; margin: 10px 0; }");
html.append(".footer { background-color: #f8f9fa; padding: 20px; text-align: center; color: #666; font-size: 14px; }");
html.append(".warning { background-color: #fff3cd; border: 1px solid #ffeaa7; border-radius: 5px; padding: 15px; margin: 20px 0; color: #856404; }");
html.append("</style>");
html.append("</head>");
html.append("<body>");
html.append("<div class=\"container\">");
html.append("<div class=\"header\">");
html.append("<h1>数学考试系统</h1>");
html.append("<p>邮箱验证码</p>");
html.append("</div>");
html.append("<div class=\"content\">");
html.append("<h2>您好!</h2>");
html.append("<p>您正在注册数学考试系统,请使用以下验证码完成注册:</p>");
html.append("<div class=\"verification-code\">");
html.append("<p><strong>验证码:</strong></p>");
html.append("<div class=\"code\">").append(verificationCode).append("</div>");
html.append("</div>");
html.append("<div class=\"warning\">");
html.append("<p><strong>重要提示:</strong></p>");
html.append("<ul>");
html.append("<li>验证码有效期为 <strong>5分钟</strong></li>");
html.append("<li>请勿将验证码泄露给他人</li>");
html.append("<li>如非本人操作,请忽略此邮件</li>");
html.append("</ul>");
html.append("</div>");
html.append("<p>如果您没有注册数学考试系统,请忽略此邮件。</p>");
html.append("<p>祝您使用愉快!</p>");
html.append("<p><strong>数学考试系统团队</strong></p>");
html.append("</div>");
html.append("<div class=\"footer\">");
html.append("<p>此邮件由系统自动发送,请勿回复</p>");
html.append("<p>2024 数学考试系统. All rights reserved.</p>");
html.append("</div>");
html.append("</div>");
html.append("</body>");
html.append("</html>");
return html.toString();
}
/**
*
*/
public boolean isAvailable() {
return isConfigured && session != null;
}
}

@ -8,35 +8,22 @@ import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.time.Instant;
import java.util.*;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
*
*/
public class ExamController {
@FXML private VBox registerPanel;
@FXML private TextField registerUsernameField;
@FXML private PasswordField registerPasswordField;
@FXML private TextField registerEmailField;
@FXML private TextField registerCodeField;
@FXML private Button sendRegisterCodeButton;
@FXML private Button verifyRegisterCodeButton;
@FXML private Label registerStatusLabel;
// 登录界面控件
@FXML private TextField usernameField;
@FXML private PasswordField passwordField;
@FXML private Button loginButton;
@FXML private TextField emailField;
@FXML private TextField emailCodeField;
@FXML private Button sendCodeButton;
@FXML private Button verifyCodeButton;
@FXML private Label emailStatusLabel;
@FXML private Label loginStatusLabel;
@FXML private Button registerButton;
@FXML private Label loginStatusLabel;
// 考试设置界面控件
@FXML private VBox examSetupPanel;
@ -47,36 +34,12 @@ public class ExamController {
@FXML private Button logoutButton;
@FXML private Label statusLabel;
// 添加设置密码面板相关控件引用需要在FXML中添加对应控件
@FXML private VBox setPasswordPanel;
@FXML private PasswordField setPasswordField;
@FXML private PasswordField confirmPasswordField;
@FXML private Button confirmSetPasswordButton;
@FXML private Label setPasswordStatusLabel;
// 添加修改密码面板相关控件引用
@FXML private VBox changePasswordPanel;
@FXML private PasswordField oldPasswordField;
@FXML private PasswordField newPasswordField;
@FXML private PasswordField confirmNewPasswordField;
@FXML private Button confirmChangePasswordButton;
@FXML private Button cancelChangePasswordButton;
@FXML private Label changePasswordStatusLabel;
// 添加修改密码按钮引用
@FXML private Button changePasswordButton;
// 数据成员
private Account currentAccount;
private List<Question> examQuestions;
private int currentQuestionIndex = 0;
private Map<Integer, String> userAnswers = new HashMap<>();
private ChoiceQuestionGenerator questionGenerator;
private final Map<String, Account> userMap = new HashMap<>();
private final EmailCodeService emailCodeService = new EmailCodeService();
private boolean emailVerified = false;
private final Map<String, RegisterInfo> pendingRegistrations = new HashMap<>();
private static final String USER_DATA_FILE = "user_data.txt";
// 常量定义
private static final int MIN_QUESTIONS = 5;
@ -85,344 +48,12 @@ public class ExamController {
@FXML
public void initialize() {
initAccounts();
loadUserDataFromFile();
setupLevelComboBox();
examSetupPanel.setVisible(false);
questionCountField.setText("10");
if (registerPanel != null) {
registerPanel.setVisible(false);
}
if (setPasswordPanel != null) {
setPasswordPanel.setVisible(false);
}
if (changePasswordPanel != null) {
changePasswordPanel.setVisible(false);
}
}
// 添加内部类用于存储注册信息
static class RegisterInfo {
final String username;
String password;
final String email;
final String registerCode;
final Instant expireTime;
boolean codeVerified;
RegisterInfo(String username, String password, String email, String registerCode) {
this.username = username;
this.password = password;
this.email = email;
this.registerCode = registerCode;
this.expireTime = Instant.now().plusSeconds(300); // 5分钟有效期
this.codeVerified = false;
}
public boolean isExpired() {
return Instant.now().isAfter(expireTime);
}
}
@FXML
private void handleVerifyRegisterCode() {
String email = registerEmailField.getText().trim();
String code = registerCodeField.getText().trim();
if (email.isEmpty() || code.isEmpty()) {
registerStatusLabel.setText("请填写邮箱和验证码");
registerStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
// 使用邮箱验证码服务验证
boolean isValid = emailCodeService.verifyCode(email, code);
if (isValid) {
// 检查是否有待处理的注册信息
RegisterInfo registerInfo = pendingRegistrations.get(email);
if (registerInfo != null) {
registerInfo.codeVerified = true;
registerStatusLabel.setText("验证码验证成功,请设置密码");
registerStatusLabel.setStyle("-fx-text-fill: green;");
// 显示设置密码面板
if (setPasswordPanel != null) {
setPasswordPanel.setVisible(true);
}
} else {
registerStatusLabel.setText("注册信息丢失,请重新注册");
registerStatusLabel.setStyle("-fx-text-fill: red;");
}
} else {
registerStatusLabel.setText("验证码错误或已过期");
registerStatusLabel.setStyle("-fx-text-fill: red;");
}
}
@FXML
private void showRegisterPanel() {
if (registerPanel != null) {
registerPanel.setVisible(true);
}
}
@FXML
private void hideRegisterPanel() {
// 隐藏注册面板
if (registerPanel != null) {
registerPanel.setVisible(false);
}
// 显示登录面板相关组件
if (usernameField != null) usernameField.setVisible(true);
if (passwordField != null) passwordField.setVisible(true);
if (emailField != null) emailField.setVisible(true);
if (emailCodeField != null) emailCodeField.setVisible(true);
if (sendCodeButton != null) sendCodeButton.setVisible(true);
if (verifyCodeButton != null) verifyCodeButton.setVisible(true);
if (loginButton != null) loginButton.setVisible(true);
if (emailStatusLabel != null) emailStatusLabel.setVisible(true);
if (loginStatusLabel != null) loginStatusLabel.setVisible(true);
// 清空注册相关字段
if (registerUsernameField != null) registerUsernameField.clear();
if (registerEmailField != null) registerEmailField.clear();
if (registerCodeField != null) registerCodeField.clear();
if (registerStatusLabel != null) registerStatusLabel.setText("");
}
@FXML
private void showSetPasswordPanel() {
if (setPasswordPanel != null) {
setPasswordPanel.setVisible(true);
}
}
/**
*
*/
@FXML
private void hideSetPasswordPanel() {
if (setPasswordPanel != null) {
setPasswordPanel.setVisible(false);
}
}
/**
*
*/
@FXML
private void handleSetPassword() {
String password = setPasswordField.getText();
String confirmPassword = confirmPasswordField.getText();
if (password.isEmpty() || confirmPassword.isEmpty()) {
setPasswordStatusLabel.setText("请输入密码");
setPasswordStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
if (!password.equals(confirmPassword)) {
setPasswordStatusLabel.setText("两次输入的密码不一致");
setPasswordStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
if (!isValidPassword(password)) {
setPasswordStatusLabel.setText("密码必须为6-10位包含大小写字母和数字");
setPasswordStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
// 获取邮箱(从注册邮箱字段)
String email = registerEmailField.getText().trim();
RegisterInfo registerInfo = pendingRegistrations.get(email);
if (registerInfo != null) {
// 更新注册信息中的密码
registerInfo.password = password;
// 完成注册
userMap.put(registerInfo.username, new Account(registerInfo.username, password, Level.));
pendingRegistrations.remove(email);
setPasswordStatusLabel.setText("注册成功!可以使用新账号登录");
setPasswordStatusLabel.setStyle("-fx-text-fill: green;");
// 清空所有注册相关输入框
registerUsernameField.clear();
registerEmailField.clear();
registerCodeField.clear();
setPasswordField.clear();
confirmPasswordField.clear();
// 隐藏面板
if (setPasswordPanel != null) {
setPasswordPanel.setVisible(false);
}
if (registerPanel != null) {
registerPanel.setVisible(false);
}
// 显示登录面板
hideRegisterPanel();
} else {
setPasswordStatusLabel.setText("注册信息丢失,请重新注册");
setPasswordStatusLabel.setStyle("-fx-text-fill: red;");
}
}
// 添加注册按钮事件处理方法
@FXML
private void handleRegisterUser() {
String username = registerUsernameField.getText().trim();
String email = registerEmailField.getText().trim();
if (username.isEmpty() || email.isEmpty()) {
registerStatusLabel.setText("请填写用户名和邮箱");
registerStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
if (userMap.containsKey(username)) {
registerStatusLabel.setText("用户名已存在");
registerStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
try {
// 发送注册验证码
emailCodeService.sendCode(email);
// 生成验证码并创建注册信息(密码将在后续设置)
String tempPassword = ""; // 临时空密码
RegisterInfo registerInfo = new RegisterInfo(username, tempPassword, email, "");
pendingRegistrations.put(email, registerInfo);
registerStatusLabel.setText("注册验证码已发送到邮箱,请查收");
registerStatusLabel.setStyle("-fx-text-fill: green;");
} catch (Exception e) {
registerStatusLabel.setText("发送验证码失败:" + e.getMessage());
registerStatusLabel.setStyle("-fx-text-fill: red;");
}
}
/**
*
*/
@FXML
private void showChangePasswordPanel() {
if (changePasswordPanel != null && currentAccount != null) {
changePasswordPanel.setVisible(true);
changePasswordPanel.toFront();
}
}
/**
*
*/
@FXML
private void hideChangePasswordPanel() {
if (changePasswordPanel != null) {
changePasswordPanel.setVisible(false);
// 清空输入框
oldPasswordField.clear();
newPasswordField.clear();
confirmNewPasswordField.clear();
changePasswordStatusLabel.setText("");
}
}
/**
*
*/
@FXML
private void handleChangePassword() {
if (currentAccount == null) {
changePasswordStatusLabel.setText("请先登录");
changePasswordStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
String oldPassword = oldPasswordField.getText();
String newPassword = newPasswordField.getText();
String confirmNewPassword = confirmNewPasswordField.getText();
if (oldPassword.isEmpty() || newPassword.isEmpty() || confirmNewPassword.isEmpty()) {
changePasswordStatusLabel.setText("请填写所有字段");
changePasswordStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
// 验证原密码
if (!currentAccount.password.equals(oldPassword)) {
changePasswordStatusLabel.setText("原密码错误");
changePasswordStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
// 验证新密码一致性
if (!newPassword.equals(confirmNewPassword)) {
changePasswordStatusLabel.setText("新密码两次输入不一致");
changePasswordStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
// 验证新密码强度
if (!isValidPassword(newPassword)) {
changePasswordStatusLabel.setText("新密码必须为6-10位包含大小写字母和数字");
changePasswordStatusLabel.setStyle("-fx-text-fill: red;");
return;
}
// 更新密码
Account account = userMap.get(currentAccount.username);
if (account != null) {
account.password = newPassword; // 注意需要修改Account类使password可修改
currentAccount.password = newPassword;
changePasswordStatusLabel.setText("密码修改成功");
changePasswordStatusLabel.setStyle("-fx-text-fill: green;");
// 清空输入框
oldPasswordField.clear();
newPasswordField.clear();
confirmNewPasswordField.clear();
// 隐藏面板
hideChangePasswordPanel();
}
}
/**
*
* @param password
* @return
*/
private boolean isValidPassword(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;
} else if (Character.isLowerCase(c)) {
hasLower = true;
} else if (Character.isDigit(c)) {
hasDigit = true;
}
}
return hasUpper && hasLower && hasDigit;
}
private void initAccounts() {
// 小学三个账号
userMap.put("张三1", new Account("张三1", "123", Level.));
@ -444,6 +75,34 @@ private void handleRegisterUser() {
levelComboBox.setValue("小学");
}
private void loadUserDataFromFile() {
try {
if (!Files.exists(Paths.get(USER_DATA_FILE))) {
return;
}
List<String> lines = Files.readAllLines(Paths.get(USER_DATA_FILE));
for (String line : lines) {
String[] parts = line.split("\\|");
if (parts.length >= 4) {
String username = parts[0];
String password = parts[1];
String levelStr = parts[3];
Level level;
try {
level = Level.valueOf(levelStr);
} catch (IllegalArgumentException e) {
level = Level.; // 默认级别
}
userMap.put(username, new Account(username, password, level));
}
}
} catch (IOException e) {
// 加载用户数据失败,静默处理
}
}
@FXML
private void handleLogin() {
@ -464,28 +123,39 @@ private void handleRegisterUser() {
examSetupPanel.setVisible(true);
loginStatusLabel.setText("登录成功!");
loginStatusLabel.setStyle("-fx-text-fill: green;");
} else {
loginStatusLabel.setText("用户名或密码错误");
loginStatusLabel.setStyle("-fx-text-fill: red;");
}
}
@FXML
private void handleRegister() {
try {
// 打开注册界面
FXMLLoader loader = new FXMLLoader(getClass().getResource("register-view.fxml"));
Scene scene = new Scene(loader.load(), 800, 800);
Stage registerStage = new Stage();
registerStage.setTitle("数学考试系统 - 用户注册");
registerStage.setScene(scene);
registerStage.setResizable(false);
registerStage.show();
} catch (Exception e) {
loginStatusLabel.setText("打开注册界面失败:" + e.getMessage());
loginStatusLabel.setStyle("-fx-text-fill: red;");
}
}
@FXML
private void handleLogout() {
currentAccount = null;
examQuestions = null;
currentQuestionIndex = 0;
userAnswers.clear();
examSetupPanel.setVisible(false);
usernameField.clear();
passwordField.clear();
loginStatusLabel.setText("");
statusLabel.setText("");
emailVerified = false;
if (emailField != null) emailField.clear();
if (emailCodeField != null) emailCodeField.clear();
if (emailStatusLabel != null) emailStatusLabel.setText("");
}
@FXML
@ -508,8 +178,6 @@ private void handleRegisterUser() {
String selectedLevel = levelComboBox.getValue();
ChoiceQuestionGenerator.Level level = ChoiceQuestionGenerator.Level.valueOf(selectedLevel);
// 生成考试题目
questionGenerator = new ChoiceQuestionGenerator();
examQuestions = questionGenerator.generateQuestions(level, count);
@ -529,10 +197,6 @@ private void handleRegisterUser() {
}
}
private void startExam() {
try {
// 打开专门的考试界面
@ -560,7 +224,6 @@ private void handleRegisterUser() {
}
}
// 内部类
static class Account {
final String username;
@ -577,4 +240,4 @@ private void handleRegisterUser() {
enum Level {
, ,
}
}
}

@ -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<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 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 (!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<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);
}
}

@ -57,7 +57,7 @@
</HBox>
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Button fx:id="loginButton" text="🚀 登录" onAction="#handleLogin" style="-fx-background-color: linear-gradient(to right, #8A2BE2, #9932CC); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 10 20; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
<Button fx:id="registerButton" text="📝 注册" onAction="#showRegisterPanel" style="-fx-background-color: linear-gradient(to right, #32CD32, #228B22); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 10 20; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
<Button fx:id="registerButton" text="📝 注册" onAction="#handleRegister" style="-fx-background-color: linear-gradient(to right, #32CD32, #228B22); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 10 20; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
<Label fx:id="loginStatusLabel" textFill="#8B0000" style="-fx-font-weight: bold;"/>
</HBox>
@ -93,78 +93,8 @@
<Label fx:id="statusLabel" textFill="#4B0082" style="-fx-font-weight: bold; -fx-font-size: 14;"/>
</VBox>
<!-- 在考试设置区域下方添加修改密码按钮 -->
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Button fx:id="changePasswordButton" text="🔑 修改密码" onAction="#showChangePasswordPanel" style="-fx-background-color: linear-gradient(to right, #FF8C00, #FFA500); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 10 20; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
</HBox>
<!-- 添加设置密码面板 -->
<VBox fx:id="setPasswordPanel" spacing="15.0" style="-fx-background-color: linear-gradient(to bottom, #F8F0FF, #E6E6FA, #D8BFD8); -fx-padding: 20; -fx-background-radius: 15; -fx-effect: dropshadow(gaussian, rgba(139,0,139,0.3), 15, 0, 0, 5);" visible="false">
<Label text="🔐 设置密码" textFill="#4B0082" textAlignment="CENTER">
<font>
<Font name="System Bold" size="18.0"/>
</font>
</Label>
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="🔑 密码:" minWidth="80.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<PasswordField fx:id="setPasswordField" promptText="6-10位包含大小写字母和数字" prefWidth="180.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
</HBox>
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="✅ 确认密码:" minWidth="80.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<PasswordField fx:id="confirmPasswordField" promptText="请再次输入密码" prefWidth="180.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
</HBox>
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Button fx:id="confirmSetPasswordButton" text="✅ 确认设置" onAction="#handleSetPassword" style="-fx-background-color: linear-gradient(to right, #32CD32, #228B22); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 10 20; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
<Button text="❌ 取消" onAction="#hideSetPasswordPanel" style="-fx-background-color: linear-gradient(to right, #DC143C, #B22222); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 10 20; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
<Label fx:id="setPasswordStatusLabel" textFill="#8B0000" style="-fx-font-weight: bold;"/>
</HBox>
</VBox>
<!-- 添加修改密码面板 -->
<VBox fx:id="changePasswordPanel" spacing="15.0" style="-fx-background-color: linear-gradient(to bottom, #F8F0FF, #E6E6FA, #D8BFD8); -fx-padding: 20; -fx-background-radius: 15; -fx-effect: dropshadow(gaussian, rgba(139,0,139,0.3), 15, 0, 0, 5);" visible="false">
<Label text="🔑 修改密码" textFill="#4B0082" textAlignment="CENTER">
<font>
<Font name="System Bold" size="18.0"/>
</font>
</Label>
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="🔒 原密码:" minWidth="80.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<PasswordField fx:id="oldPasswordField" promptText="请输入原密码" prefWidth="180.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
</HBox>
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="🆕 新密码:" minWidth="80.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<PasswordField fx:id="newPasswordField" promptText="6-10位包含大小写字母和数字" prefWidth="180.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
</HBox>
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="✅ 确认新密码:" minWidth="80.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<PasswordField fx:id="confirmNewPasswordField" promptText="请再次输入新密码" prefWidth="180.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
</HBox>
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Button fx:id="confirmChangePasswordButton" text="✅ 确认修改" onAction="#handleChangePassword" style="-fx-background-color: linear-gradient(to right, #32CD32, #228B22); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 10 20; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
<Button fx:id="cancelChangePasswordButton" text="❌ 取消" onAction="#hideChangePasswordPanel" style="-fx-background-color: linear-gradient(to right, #DC143C, #B22222); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 10 20; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
<Label fx:id="changePasswordStatusLabel" textFill="#8B0000" style="-fx-font-weight: bold;"/>
</HBox>
</VBox>
<!-- 使用说明 -->
<VBox spacing="15.0" style="-fx-background-color: linear-gradient(to bottom, #F8F0FF, #E6E6FA, #D8BFD8); -fx-padding: 20; -fx-background-radius: 15; -fx-effect: dropshadow(gaussian, rgba(139,0,139,0.2), 10, 0, 0, 3);">

@ -1,9 +0,0 @@
mail.from=1961004835@qq.com
mail.authCode=nixyzbpbcrekhgfe
mail.host=smtp.qq.com
mail.port=465
mail.ssl=true
mail.subject=您的验证码
mail.bodyPrefix=验证码:
mail.bodySuffix=5分钟内有效。

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.effect.DropShadow?>
<?import javafx.scene.paint.LinearGradient?>
<?import javafx.scene.paint.Stop?>
<BorderPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.mathsystemtogether.RegisterController" style="-fx-background-color: linear-gradient(to bottom, #6A0DAD, #4B0082, #2E0854);">
<top>
<VBox spacing="15.0" style="-fx-background-color: linear-gradient(to right, #8A2BE2, #9932CC, #8B008B); -fx-background-radius: 15; -fx-padding: 20;">
<padding>
<Insets bottom="15.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Label text="🎓 用户注册" textFill="white" textAlignment="CENTER" style="-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.5), 10, 0, 0, 2);">
<font>
<Font name="System Bold" size="28.0"/>
</font>
</Label>
<Label text="✨ 创建新账户 · 邮箱验证 · 安全可靠" textFill="#E6E6FA" textAlignment="CENTER">
<font>
<Font name="System" size="14.0"/>
</font>
</Label>
</VBox>
</top>
<center>
<VBox spacing="10.0">
<padding>
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0"/>
</padding>
<!-- 注册区域 -->
<VBox spacing="15.0" style="-fx-background-color: linear-gradient(to bottom, #E6E6FA, #D8BFD8, #DDA0DD); -fx-padding: 20; -fx-background-radius: 15; -fx-effect: dropshadow(gaussian, rgba(139,0,139,0.3), 15, 0, 0, 5);">
<Label text="📝 填写注册信息" textFill="#4B0082" textAlignment="CENTER">
<font>
<Font name="System Bold" size="18.0"/>
</font>
</Label>
<!-- 用户名输入 -->
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="👤 用户名:" minWidth="100.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<TextField fx:id="usernameField" promptText="请输入用户名" prefWidth="200.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
</HBox>
<!-- 密码输入 -->
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="🔑 密码:" minWidth="100.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<PasswordField fx:id="passwordField" promptText="请输入密码" prefWidth="200.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
</HBox>
<!-- 确认密码输入 -->
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="🔒 确认密码:" minWidth="100.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<PasswordField fx:id="confirmPasswordField" promptText="请再次输入密码" prefWidth="200.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
</HBox>
<!-- 邮箱输入 -->
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="📧 邮箱:" minWidth="100.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<TextField fx:id="emailField" promptText="请输入邮箱地址" prefWidth="200.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
</HBox>
<!-- 验证码输入 -->
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="🔢 验证码:" minWidth="100.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<TextField fx:id="verificationCodeField" promptText="请输入验证码" prefWidth="120.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2; -fx-padding: 8;"/>
<Button fx:id="sendCodeButton" text="📤 发送验证码" onAction="#handleSendCode" style="-fx-background-color: linear-gradient(to right, #32CD32, #228B22); -fx-text-fill: white; -fx-background-radius: 8; -fx-padding: 8 15; -fx-font-size: 12; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 3, 0, 0, 1);"/>
</HBox>
<!-- 难度级别选择 -->
<HBox spacing="15.0" alignment="CENTER_LEFT">
<Label text="📚 难度级别:" minWidth="100.0" textFill="#6A0DAD">
<font>
<Font name="System Bold" size="14.0"/>
</font>
</Label>
<ComboBox fx:id="levelComboBox" prefWidth="140.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #8A2BE2; -fx-border-radius: 8; -fx-border-width: 2;"/>
</HBox>
<!-- 按钮区域 -->
<HBox spacing="15.0" alignment="CENTER">
<Button fx:id="registerButton" text="🚀 注册" onAction="#handleRegister" style="-fx-background-color: linear-gradient(to right, #8A2BE2, #9932CC); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 12 25; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
<Button fx:id="backToLoginButton" text="🔙 返回登录" onAction="#handleBackToLogin" style="-fx-background-color: linear-gradient(to right, #DC143C, #B22222); -fx-text-fill: white; -fx-background-radius: 10; -fx-padding: 12 25; -fx-font-size: 14; -fx-font-weight: bold; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 5, 0, 0, 2);"/>
</HBox>
<!-- 状态标签 -->
<Label fx:id="statusLabel" textFill="#8B0000" style="-fx-font-weight: bold; -fx-font-size: 14;" textAlignment="CENTER"/>
</VBox>
<!-- 注册说明 -->
<VBox spacing="15.0" style="-fx-background-color: linear-gradient(to bottom, #F8F0FF, #E6E6FA, #D8BFD8); -fx-padding: 20; -fx-background-radius: 15; -fx-effect: dropshadow(gaussian, rgba(139,0,139,0.2), 10, 0, 0, 3);">
<Label text="📖 注册说明" textFill="#4B0082" textAlignment="CENTER">
<font>
<Font name="System Bold" size="16.0"/>
</font>
</Label>
<VBox spacing="8.0" style="-fx-padding: 10; -fx-background-color: rgba(255,255,255,0.8); -fx-background-radius: 10; -fx-effect: dropshadow(gaussian, rgba(139,0,139,0.1), 5, 0, 0, 2);">
<Label text="① 填写用户名、密码和邮箱信息" textFill="#6A0DAD" style="-fx-font-weight: bold;"/>
<Label text="② 点击'发送验证码'获取邮箱验证码" textFill="#6A0DAD" style="-fx-font-weight: bold;"/>
<Label text="③ 输入收到的验证码完成验证" textFill="#6A0DAD" style="-fx-font-weight: bold;"/>
<Label text="④ 选择适合的难度级别" textFill="#6A0DAD" style="-fx-font-weight: bold;"/>
<Label text="⑤ 点击'注册'完成账户创建" textFill="#6A0DAD" style="-fx-font-weight: bold;"/>
</VBox>
</VBox>
</VBox>
</center>
</BorderPane>

@ -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
Loading…
Cancel
Save