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 registrationCodes = new HashMap<>(); private List 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 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"); } }