You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MathEaxmApp/controller/AppController.java

316 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package controller;
import model.*;
import service.*;
import view.*;
import javax.swing.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class AppController {
// 模型和服务
private final UserManager userManager;
private final ExamManager examManager;
private final EmailService emailService;
// 视图
private final MainFrame mainFrame;
private final LoginView loginView;
private final RegisterView registerView;
private final PasswordSetView passwordSetView;
private final MainMenuView mainMenuView;
private final ExamView examView;
private final ScoreView scoreView;
// 应用状态
private User currentUser;
private String emailForRegistration;
private String usernameForRegistration;
private final Map<String, String> registrationCodes = new HashMap<>();
private List<Question> currentExam;
private int currentQuestionIndex;
private int score;
private int[] userAnswers; // 新增:用于存储用户每一题的答案
public AppController() {
this.mainFrame = new MainFrame();
this.userManager = new UserManager();
this.examManager = new ExamManager();
this.emailService = new EmailService();
this.loginView = new LoginView();
this.registerView = new RegisterView();
this.passwordSetView = new PasswordSetView();
this.mainMenuView = new MainMenuView();
this.examView = new ExamView();
this.scoreView = new ScoreView();
mainFrame.addPanel(loginView, "LOGIN");
mainFrame.addPanel(registerView, "REGISTER");
mainFrame.addPanel(passwordSetView, "PASSWORD_SET");
mainFrame.addPanel(mainMenuView, "MAIN_MENU");
mainFrame.addPanel(examView, "EXAM");
mainFrame.addPanel(scoreView, "SCORE");
attachListeners();
mainFrame.showPanel("LOGIN");
}
/**
* 附加所有UI组件的事件监听器。
*/
private void attachListeners() {
// --- 登录逻辑 ---
loginView.getLoginButton().addActionListener(e -> handleLogin());
loginView.getRegisterButton().addActionListener(e -> mainFrame.showPanel("REGISTER"));
// --- 注册逻辑 ---
registerView.getGetCodeButton().addActionListener(e -> handleGetCode());
registerView.getRegisterButton().addActionListener(e -> handleVerifyCode());
registerView.getBackButton().addActionListener(e -> mainFrame.showPanel("LOGIN"));
// --- 密码设置逻辑 ---
passwordSetView.getSubmitButton().addActionListener(e -> handleSetPassword());
// --- 主菜单逻辑 ---
mainMenuView.getStartExamButton().addActionListener(e -> handleStartExam());
mainMenuView.getChangePasswordButton().addActionListener(e -> handleChangePassword());
mainMenuView.getLogoutButton().addActionListener(e -> handleLogout());
// --- 考试逻辑 (已重构) ---
examView.getPrevButton().addActionListener(e -> handlePreviousQuestion());
examView.getNextButton().addActionListener(e -> handleNextQuestion());
examView.getFinishButton().addActionListener(e -> handleFinishExam());
// --- 分数界面逻辑 ---
scoreView.getContinueButton().addActionListener(e -> {
String welcomeName = currentUser.getUsername() != null && !currentUser.getUsername().isEmpty() ? currentUser.getUsername() : currentUser.getEmail();
mainMenuView.setWelcomeMessage("欢迎, " + welcomeName);
mainFrame.showPanel("MAIN_MENU");
});
scoreView.getExitButton().addActionListener(e -> handleLogout());
}
// --- 核心处理方法 ---
private void handleLogin() {
User user = userManager.authenticate(loginView.getIdentifier(), loginView.getPassword());
if (user != null) {
currentUser = user;
String welcomeName = user.getUsername() != null && !user.getUsername().isEmpty() ? user.getUsername() : user.getEmail();
mainMenuView.setWelcomeMessage("欢迎, " + welcomeName);
mainFrame.showPanel("MAIN_MENU");
} else {
JOptionPane.showMessageDialog(mainFrame, "邮箱/用户名或密码错误", "登录失败", JOptionPane.ERROR_MESSAGE);
}
loginView.clearFields();
}
private void handleGetCode() {
String email = registerView.getEmail();
String username = registerView.getUsername();
if (!ValidationService.isValidEmail(email)) {
JOptionPane.showMessageDialog(mainFrame, "请输入有效的邮箱格式!", "格式错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (username.trim().isEmpty() || username.contains("|") || username.contains(" ")) {
JOptionPane.showMessageDialog(mainFrame, "用户名不能为空,且不能包含'|'或空格!", "格式错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (userManager.emailExists(email)) {
JOptionPane.showMessageDialog(mainFrame, "该邮箱已被注册!", "注册失败", JOptionPane.ERROR_MESSAGE);
return;
}
if (userManager.usernameExists(username)) {
JOptionPane.showMessageDialog(mainFrame, "该用户名已被使用!", "注册失败", JOptionPane.ERROR_MESSAGE);
return;
}
emailForRegistration = email;
usernameForRegistration = username;
String code = String.format("%06d", new Random().nextInt(1000000));
registrationCodes.put(emailForRegistration, code);
final JButton getCodeButton = registerView.getGetCodeButton();
getCodeButton.setEnabled(false);
getCodeButton.setText("发送中...");
SwingWorker<Void, Void> worker = new SwingWorker<>() {
private Exception mailException = null;
@Override
protected Void doInBackground() {
try {
emailService.sendVerificationCode(emailForRegistration, code);
} catch (Exception e) {
this.mailException = e;
}
return null;
}
@Override
protected void done() {
getCodeButton.setEnabled(true);
getCodeButton.setText("发送验证邮件");
if (mailException == null) {
JOptionPane.showMessageDialog(mainFrame, "一封包含验证码的【电子邮件】已发送至您的邮箱,请登录邮箱查收。", "发送成功", JOptionPane.INFORMATION_MESSAGE);
} else {
String errorMessage = "邮件发送失败,请检查:\n1. 网络连接是否正常。\n2. config.properties 中的邮箱和授权码是否正确。\n\n错误详情: " + mailException.getMessage();
JOptionPane.showMessageDialog(mainFrame, errorMessage, "发送失败", JOptionPane.ERROR_MESSAGE);
mailException.printStackTrace();
}
}
};
worker.execute();
}
private void handleVerifyCode() {
String code = registerView.getCode();
if (code.equals(registrationCodes.get(emailForRegistration))) {
mainFrame.showPanel("PASSWORD_SET");
} else {
JOptionPane.showMessageDialog(mainFrame, "注册码错误!", "验证失败", JOptionPane.ERROR_MESSAGE);
}
}
private void handleSetPassword() {
String pass1 = passwordSetView.getPassword();
String pass2 = passwordSetView.getConfirmPassword();
if (!pass1.equals(pass2)) {
JOptionPane.showMessageDialog(mainFrame, "两次输入的密码不一致!", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!ValidationService.isPasswordStrong(pass1)) {
JOptionPane.showMessageDialog(mainFrame, "密码必须为6-10位且包含大小写字母和数字。", "密码格式错误", JOptionPane.ERROR_MESSAGE);
return;
}
userManager.registerUser(emailForRegistration, usernameForRegistration, pass1, UserType.PRIMARY);
JOptionPane.showMessageDialog(mainFrame, "注册成功!请返回登录。", "成功", JOptionPane.INFORMATION_MESSAGE);
mainFrame.showPanel("LOGIN");
}
private void handleStartExam() {
UserType type = mainMenuView.getSelectedType();
int count = mainMenuView.getQuestionCount();
if(currentUser.getUserType() != type) {
currentUser.setUserType(type);
userManager.updateUser(currentUser);
}
currentExam = examManager.generateExam(currentUser, type, count);
if (currentExam.size() < count) {
JOptionPane.showMessageDialog(mainFrame, "抱歉,未能生成足够数量的不重复题目,实际生成 " + currentExam.size() + " 道。", "提示", JOptionPane.WARNING_MESSAGE);
}
if (currentExam.isEmpty()) return;
currentQuestionIndex = 0;
score = 0;
userAnswers = new int[currentExam.size()];
Arrays.fill(userAnswers, -1); // -1 代表未作答
examView.setupForExam(currentExam.size());
displayCurrentQuestion();
mainFrame.showPanel("EXAM");
}
private void displayCurrentQuestion() {
Question q = currentExam.get(currentQuestionIndex);
int savedAnswer = userAnswers[currentQuestionIndex];
examView.displayQuestion(q, currentQuestionIndex + 1, currentExam.size(), savedAnswer);
examView.updateNavigationButtons(currentQuestionIndex, currentExam.size());
}
private void handlePreviousQuestion() {
saveCurrentAnswer();
if (currentQuestionIndex > 0) {
currentQuestionIndex--;
displayCurrentQuestion();
}
}
private void handleNextQuestion() {
saveCurrentAnswer();
if (currentQuestionIndex < currentExam.size() - 1) {
currentQuestionIndex++;
displayCurrentQuestion();
}
}
private void saveCurrentAnswer() {
int selectedIndex = examView.getSelectedIndex();
userAnswers[currentQuestionIndex] = selectedIndex;
}
private void handleFinishExam() {
saveCurrentAnswer();
int choice = JOptionPane.showConfirmDialog(mainFrame,
"您确定要交卷吗?\n未完成的题目将按 0 分计算。",
"确认交卷",
JOptionPane.YES_NO_OPTION);
if (choice == JOptionPane.YES_OPTION) {
calculateScore();
int finalScore = (int) Math.round((double) score / currentExam.size() * 100);
scoreView.setScore(finalScore, score, currentExam.size());
mainFrame.showPanel("SCORE");
}
}
private void calculateScore() {
score = 0;
for (int i = 0; i < currentExam.size(); i++) {
if (userAnswers[i] != -1 && userAnswers[i] == currentExam.get(i).getCorrectOptionIndex()) {
score++;
}
}
}
private void handleChangePassword() {
ChangePasswordView dialog = new ChangePasswordView(mainFrame);
dialog.getConfirmButton().addActionListener(e -> {
String oldPass = dialog.getOldPassword();
String newPass = dialog.getNewPassword();
String confirmPass = dialog.getConfirmPassword();
if (userManager.authenticate(currentUser.getEmail(), oldPass) == null) {
JOptionPane.showMessageDialog(dialog, "原密码错误!", "验证失败", JOptionPane.ERROR_MESSAGE);
return;
}
if (!newPass.equals(confirmPass)) {
JOptionPane.showMessageDialog(dialog, "两次输入的新密码不一致!", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!ValidationService.isPasswordStrong(newPass)) {
JOptionPane.showMessageDialog(dialog, "新密码必须为6-10位且包含大小写字母和数字。", "密码格式错误", JOptionPane.ERROR_MESSAGE);
return;
}
currentUser.setHashedPassword(newPass);
userManager.updateUser(currentUser);
JOptionPane.showMessageDialog(dialog, "密码修改成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
dialog.dispose();
});
dialog.getCancelButton().addActionListener(e -> dialog.dispose());
dialog.setVisible(true);
}
private void handleLogout() {
currentUser = null;
loginView.clearFields();
mainFrame.showPanel("LOGIN");
}
}