|
|
|
|
@ -8,14 +8,23 @@ import javafx.scene.layout.VBox;
|
|
|
|
|
import javafx.stage.Stage;
|
|
|
|
|
import javafx.collections.FXCollections;
|
|
|
|
|
import javafx.collections.ObservableList;
|
|
|
|
|
|
|
|
|
|
import java.time.Instant;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 考试系统控制器
|
|
|
|
|
*/
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
private final Map<String, RegisterInfo> pendingRegistratrations = new HashMap<>();
|
|
|
|
|
// 登录界面控件
|
|
|
|
|
@FXML private TextField usernameField;
|
|
|
|
|
@FXML private PasswordField passwordField;
|
|
|
|
|
@ -26,7 +35,7 @@ public class ExamController {
|
|
|
|
|
@FXML private Button verifyCodeButton;
|
|
|
|
|
@FXML private Label emailStatusLabel;
|
|
|
|
|
@FXML private Label loginStatusLabel;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 考试设置界面控件
|
|
|
|
|
@FXML private VBox examSetupPanel;
|
|
|
|
|
@FXML private Label welcomeLabel;
|
|
|
|
|
@ -35,8 +44,8 @@ public class ExamController {
|
|
|
|
|
@FXML private Button startExamButton;
|
|
|
|
|
@FXML private Button logoutButton;
|
|
|
|
|
@FXML private Label statusLabel;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 数据成员
|
|
|
|
|
private Account currentAccount;
|
|
|
|
|
private List<Question> examQuestions;
|
|
|
|
|
@ -46,11 +55,14 @@ public class ExamController {
|
|
|
|
|
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 int MIN_QUESTIONS = 5;
|
|
|
|
|
private static final int MAX_QUESTIONS = 20;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
|
public void initialize() {
|
|
|
|
|
initAccounts();
|
|
|
|
|
@ -60,8 +72,123 @@ public class ExamController {
|
|
|
|
|
if (emailStatusLabel != null) {
|
|
|
|
|
emailStatusLabel.setText("");
|
|
|
|
|
}
|
|
|
|
|
if (registerPanel != null) {
|
|
|
|
|
registerPanel.setVisible(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 添加内部类用于存储注册信息
|
|
|
|
|
static class RegisterInfo {
|
|
|
|
|
final String username;
|
|
|
|
|
final 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 handleRegister() {
|
|
|
|
|
String username = registerUsernameField.getText().trim();
|
|
|
|
|
String password = registerPasswordField.getText();
|
|
|
|
|
String email = registerEmailField.getText().trim();
|
|
|
|
|
|
|
|
|
|
if (username.isEmpty() || password.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 registerCode = String.valueOf(100000 + new Random().nextInt(900000));
|
|
|
|
|
pendingRegistrations.put(email, new RegisterInfo(username, password, email, registerCode));
|
|
|
|
|
|
|
|
|
|
registerStatusLabel.setText("注册验证码已发送到邮箱,请查收");
|
|
|
|
|
registerStatusLabel.setStyle("-fx-text-fill: green;");
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
registerStatusLabel.setText("发送验证码失败:" + e.getMessage());
|
|
|
|
|
registerStatusLabel.setStyle("-fx-text-fill: red;");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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) {
|
|
|
|
|
Level defaultLevel = Level.小学;
|
|
|
|
|
userMap.put(registerInfo.username, new Account(registerInfo.username, registerInfo.password, defaultLevel));
|
|
|
|
|
pendingRegistrations.remove(email); // 移除已注册的信息
|
|
|
|
|
|
|
|
|
|
registerStatusLabel.setText("注册成功!可以使用新账号登录");
|
|
|
|
|
registerStatusLabel.setStyle("-fx-text-fill: green;");
|
|
|
|
|
|
|
|
|
|
// 清空注册面板
|
|
|
|
|
registerUsernameField.clear();
|
|
|
|
|
registerPasswordField.clear();
|
|
|
|
|
registerEmailField.clear();
|
|
|
|
|
registerCodeField.clear();
|
|
|
|
|
registerPanel.setVisible(false);
|
|
|
|
|
} 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initAccounts() {
|
|
|
|
|
// 小学三个账号
|
|
|
|
|
userMap.put("张三1", new Account("张三1", "123", Level.小学));
|
|
|
|
|
@ -76,25 +203,25 @@ public class ExamController {
|
|
|
|
|
userMap.put("王五2", new Account("王五2", "123", Level.高中));
|
|
|
|
|
userMap.put("王五3", new Account("王五3", "123", Level.高中));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void setupLevelComboBox() {
|
|
|
|
|
ObservableList<String> levels = FXCollections.observableArrayList("小学", "初中", "高中");
|
|
|
|
|
levelComboBox.setItems(levels);
|
|
|
|
|
levelComboBox.setValue("小学");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
|
private void handleLogin() {
|
|
|
|
|
String username = usernameField.getText().trim();
|
|
|
|
|
String password = passwordField.getText();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (username.isEmpty() || password.isEmpty()) {
|
|
|
|
|
loginStatusLabel.setText("请输入用户名和密码");
|
|
|
|
|
loginStatusLabel.setStyle("-fx-text-fill: red;");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Account account = userMap.get(username);
|
|
|
|
|
if (account != null && account.password.equals(password)) {
|
|
|
|
|
currentAccount = account;
|
|
|
|
|
@ -113,7 +240,7 @@ public class ExamController {
|
|
|
|
|
loginStatusLabel.setStyle("-fx-text-fill: red;");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
|
private void handleLogout() {
|
|
|
|
|
currentAccount = null;
|
|
|
|
|
@ -130,7 +257,7 @@ public class ExamController {
|
|
|
|
|
if (emailCodeField != null) emailCodeField.clear();
|
|
|
|
|
if (emailStatusLabel != null) emailStatusLabel.setText("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
|
private void handleStartExam() {
|
|
|
|
|
if (currentAccount == null) {
|
|
|
|
|
@ -138,7 +265,7 @@ public class ExamController {
|
|
|
|
|
statusLabel.setStyle("-fx-text-fill: red;");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
int count = Integer.parseInt(questionCountField.getText());
|
|
|
|
|
if (count < MIN_QUESTIONS || count > MAX_QUESTIONS) {
|
|
|
|
|
@ -146,11 +273,11 @@ public class ExamController {
|
|
|
|
|
statusLabel.setStyle("-fx-text-fill: red;");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取选择的级别
|
|
|
|
|
String selectedLevel = levelComboBox.getValue();
|
|
|
|
|
ChoiceQuestionGenerator.Level level = ChoiceQuestionGenerator.Level.valueOf(selectedLevel);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!emailVerified) {
|
|
|
|
|
statusLabel.setText("请先完成邮箱验证码验证");
|
|
|
|
|
statusLabel.setStyle("-fx-text-fill: red;");
|
|
|
|
|
@ -160,16 +287,16 @@ public class ExamController {
|
|
|
|
|
// 生成考试题目
|
|
|
|
|
questionGenerator = new ChoiceQuestionGenerator();
|
|
|
|
|
examQuestions = questionGenerator.generateQuestions(level, count);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (examQuestions.isEmpty()) {
|
|
|
|
|
statusLabel.setText("无法生成题目,请重试");
|
|
|
|
|
statusLabel.setStyle("-fx-text-fill: red;");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 开始考试
|
|
|
|
|
startExam();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
statusLabel.setText("请输入有效的数字");
|
|
|
|
|
statusLabel.setStyle("-fx-text-fill: red;");
|
|
|
|
|
@ -226,7 +353,7 @@ public class ExamController {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void startExam() {
|
|
|
|
|
try {
|
|
|
|
|
// 打开专门的考试界面
|
|
|
|
|
@ -236,38 +363,38 @@ public class ExamController {
|
|
|
|
|
examStage.setTitle("数学考试 - " + currentAccount.username);
|
|
|
|
|
examStage.setScene(scene);
|
|
|
|
|
examStage.setResizable(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置考试数据
|
|
|
|
|
ExamTakingController examController = loader.getController();
|
|
|
|
|
examController.setExamData(examQuestions, currentAccount.username, levelComboBox.getValue());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 显示考试窗口
|
|
|
|
|
examStage.show();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 隐藏主窗口
|
|
|
|
|
Stage mainStage = (Stage) startExamButton.getScene().getWindow();
|
|
|
|
|
mainStage.hide();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
statusLabel.setText("启动考试失败:" + e.getMessage());
|
|
|
|
|
statusLabel.setStyle("-fx-text-fill: red;");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 内部类
|
|
|
|
|
static class Account {
|
|
|
|
|
final String username;
|
|
|
|
|
final String password;
|
|
|
|
|
final Level level;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Account(String username, String password, Level level) {
|
|
|
|
|
this.username = username;
|
|
|
|
|
this.password = password;
|
|
|
|
|
this.level = level;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum Level {
|
|
|
|
|
小学, 初中, 高中
|
|
|
|
|
}
|
|
|
|
|
|