|
|
import javax.swing.*;
|
|
|
import java.awt.*;
|
|
|
import java.awt.event.ActionEvent;
|
|
|
import java.util.*;
|
|
|
import java.util.List;
|
|
|
import java.util.regex.Pattern;
|
|
|
import java.io.*;
|
|
|
import java.nio.file.*;
|
|
|
|
|
|
/**
|
|
|
* MathLearningApp的方法扩展类
|
|
|
* 包含所有事件处理和工具方法
|
|
|
*/
|
|
|
public class MathLearningAppMethods {
|
|
|
|
|
|
/**
|
|
|
* 创建样式化按钮
|
|
|
*/
|
|
|
public static JButton createStyledButton(String text, Color backgroundColor) {
|
|
|
JButton button = new JButton(text) {
|
|
|
@Override
|
|
|
protected void paintComponent(Graphics g) {
|
|
|
Graphics2D g2 = (Graphics2D) g.create();
|
|
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
|
|
|
// 创建渐变效果
|
|
|
GradientPaint gradient = new GradientPaint(0, 0, backgroundColor.brighter(),
|
|
|
0, getHeight(), backgroundColor.darker());
|
|
|
g2.setPaint(gradient);
|
|
|
g2.fillRoundRect(0, 0, getWidth(), getHeight(), 15, 15);
|
|
|
|
|
|
// 添加内阴影效果
|
|
|
g2.setColor(new Color(0, 0, 0, 30));
|
|
|
g2.drawRoundRect(1, 1, getWidth()-3, getHeight()-3, 13, 13);
|
|
|
|
|
|
g2.dispose();
|
|
|
super.paintComponent(g);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
|
|
button.setForeground(Color.WHITE);
|
|
|
button.setFocusPainted(false);
|
|
|
button.setBorderPainted(false);
|
|
|
button.setContentAreaFilled(false);
|
|
|
button.setPreferredSize(new Dimension(130, 40));
|
|
|
button.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
|
|
|
|
// 添加鼠标悬停效果
|
|
|
button.addMouseListener(new java.awt.event.MouseAdapter() {
|
|
|
@Override
|
|
|
public void mouseEntered(java.awt.event.MouseEvent evt) {
|
|
|
button.setBackground(backgroundColor.brighter());
|
|
|
button.repaint();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void mouseExited(java.awt.event.MouseEvent evt) {
|
|
|
button.setBackground(backgroundColor);
|
|
|
button.repaint();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
return button;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 创建大型难度选择按钮
|
|
|
*/
|
|
|
public static JButton createLargeDifficultyButton(String text, Color backgroundColor) {
|
|
|
JButton button = new JButton(text) {
|
|
|
@Override
|
|
|
protected void paintComponent(Graphics g) {
|
|
|
Graphics2D g2 = (Graphics2D) g.create();
|
|
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
|
|
|
// 创建渐变效果
|
|
|
GradientPaint gradient = new GradientPaint(0, 0, backgroundColor.brighter(),
|
|
|
0, getHeight(), backgroundColor);
|
|
|
g2.setPaint(gradient);
|
|
|
g2.fillRoundRect(0, 0, getWidth(), getHeight(), 20, 20);
|
|
|
|
|
|
// 添加外阴影效果
|
|
|
g2.setColor(new Color(0, 0, 0, 20));
|
|
|
g2.fillRoundRect(3, 3, getWidth(), getHeight(), 20, 20);
|
|
|
|
|
|
// 重新绘制按钮
|
|
|
g2.setPaint(gradient);
|
|
|
g2.fillRoundRect(0, 0, getWidth()-3, getHeight()-3, 20, 20);
|
|
|
|
|
|
// 添加边框
|
|
|
g2.setColor(backgroundColor.darker());
|
|
|
g2.setStroke(new BasicStroke(2));
|
|
|
g2.drawRoundRect(1, 1, getWidth()-5, getHeight()-5, 18, 18);
|
|
|
|
|
|
g2.dispose();
|
|
|
super.paintComponent(g);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
button.setFont(new Font("微软雅黑", Font.BOLD, 22));
|
|
|
button.setForeground(new Color(40, 40, 40));
|
|
|
button.setFocusPainted(false);
|
|
|
button.setBorderPainted(false);
|
|
|
button.setContentAreaFilled(false);
|
|
|
button.setPreferredSize(new Dimension(320, 70));
|
|
|
button.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
|
|
|
|
// 添加鼠标悬停效果
|
|
|
button.addMouseListener(new java.awt.event.MouseAdapter() {
|
|
|
@Override
|
|
|
public void mouseEntered(java.awt.event.MouseEvent evt) {
|
|
|
button.setForeground(new Color(20, 20, 20));
|
|
|
button.repaint();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void mouseExited(java.awt.event.MouseEvent evt) {
|
|
|
button.setForeground(new Color(40, 40, 40));
|
|
|
button.repaint();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
return button;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 发送验证码
|
|
|
*/
|
|
|
public static void sendVerificationCode(ActionEvent e, JTextField emailField, Map<String, RegisteredUser> registeredUsers) {
|
|
|
String email = emailField.getText().trim();
|
|
|
|
|
|
if (!isValidEmail(email)) {
|
|
|
JOptionPane.showMessageDialog(null, "请输入有效的邮箱地址!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 检查邮箱是否已完成注册(验证码验证成功且设置了密码)
|
|
|
RegisteredUser existingUser = registeredUsers.get(email);
|
|
|
if (existingUser != null && existingUser.isVerified() && existingUser.getPassword() != null) {
|
|
|
JOptionPane.showMessageDialog(null, "该邮箱已注册!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 生成6位验证码
|
|
|
String verificationCode = generateVerificationCode();
|
|
|
|
|
|
// 创建或更新用户对象(仅用于临时存储验证码)
|
|
|
RegisteredUser user = new RegisteredUser(email, verificationCode);
|
|
|
registeredUsers.put(email, user);
|
|
|
|
|
|
// 显示发送中的提示
|
|
|
JOptionPane.showMessageDialog(null,
|
|
|
"正在通过QQ邮箱发送验证码到您的邮箱,请稍候...",
|
|
|
"发送中",
|
|
|
JOptionPane.INFORMATION_MESSAGE);
|
|
|
|
|
|
// 在后台线程发送邮件,避免阻塞UI
|
|
|
new Thread(() -> {
|
|
|
boolean success = QQEmailService.sendVerificationCode(email, verificationCode);
|
|
|
|
|
|
// 在EDT线程中显示结果
|
|
|
javax.swing.SwingUtilities.invokeLater(() -> {
|
|
|
if (success) {
|
|
|
JOptionPane.showMessageDialog(null,
|
|
|
"✅ 验证码已通过QQ邮箱发送!\n" +
|
|
|
"📧 请查收邮件并输入6位验证码。\n" +
|
|
|
"📱 如果没收到,请检查垃圾邮件文件夹。",
|
|
|
"发送成功",
|
|
|
JOptionPane.INFORMATION_MESSAGE);
|
|
|
} else {
|
|
|
JOptionPane.showMessageDialog(null,
|
|
|
"❌ 验证码发送失败!\n" +
|
|
|
"请检查:\n" +
|
|
|
"• 接收邮箱地址是否正确\n" +
|
|
|
"• QQ邮箱配置是否正确\n" +
|
|
|
"• 网络连接是否正常",
|
|
|
"发送失败",
|
|
|
JOptionPane.ERROR_MESSAGE);
|
|
|
// 发送失败时移除用户
|
|
|
registeredUsers.remove(email);
|
|
|
}
|
|
|
});
|
|
|
}).start();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 处理用户注册
|
|
|
*/
|
|
|
public static boolean handleRegister(JTextField emailField, JTextField verificationCodeField,
|
|
|
Map<String, RegisteredUser> registeredUsers, CardLayout cardLayout, JPanel mainPanel) {
|
|
|
String email = emailField.getText().trim();
|
|
|
String code = verificationCodeField.getText().trim();
|
|
|
|
|
|
if (email.isEmpty() || code.isEmpty()) {
|
|
|
JOptionPane.showMessageDialog(null, "请填写完整信息!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
RegisteredUser user = registeredUsers.get(email);
|
|
|
if (user == null) {
|
|
|
JOptionPane.showMessageDialog(null, "请先发送验证码!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (!user.verifyCode(code)) {
|
|
|
JOptionPane.showMessageDialog(null, "验证码错误!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
user.setVerified(true);
|
|
|
JOptionPane.showMessageDialog(null, "注册成功!请设置密码。", "成功", JOptionPane.INFORMATION_MESSAGE);
|
|
|
cardLayout.show(mainPanel, "PASSWORD_SETUP");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 处理密码设置
|
|
|
*/
|
|
|
public static boolean handlePasswordSetup(JPasswordField passwordField, JPasswordField confirmPasswordField,
|
|
|
Map<String, RegisteredUser> registeredUsers, CardLayout cardLayout, JPanel mainPanel) {
|
|
|
String password = new String(passwordField.getPassword());
|
|
|
String confirmPassword = new String(confirmPasswordField.getPassword());
|
|
|
|
|
|
if (!password.equals(confirmPassword)) {
|
|
|
JOptionPane.showMessageDialog(null, "两次输入的密码不一致!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (!isValidPassword(password)) {
|
|
|
JOptionPane.showMessageDialog(null,
|
|
|
"密码不符合要求!\n要求:6-10位,必须包含大小写字母和数字",
|
|
|
"错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 找到当前注册的用户
|
|
|
RegisteredUser currentUser = null;
|
|
|
for (RegisteredUser user : registeredUsers.values()) {
|
|
|
if (user.isVerified() && user.getPassword() == null) {
|
|
|
currentUser = user;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (currentUser != null) {
|
|
|
currentUser.setPassword(password);
|
|
|
saveUserData(registeredUsers);
|
|
|
JOptionPane.showMessageDialog(null, "密码设置成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
|
|
|
cardLayout.show(mainPanel, "DIFFICULTY_SELECTION");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 处理用户登录
|
|
|
*/
|
|
|
public static RegisteredUser handleLogin(String email, String password,
|
|
|
Map<String, RegisteredUser> registeredUsers,
|
|
|
CardLayout cardLayout, JPanel mainPanel) {
|
|
|
if (email.isEmpty() || password.isEmpty()) {
|
|
|
JOptionPane.showMessageDialog(null, "请填写完整信息!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
RegisteredUser user = registeredUsers.get(email);
|
|
|
if (user == null || !user.isVerified() || !user.verifyPassword(password)) {
|
|
|
JOptionPane.showMessageDialog(null, "用户名或密码错误!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
JOptionPane.showMessageDialog(null, "登录成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
|
|
|
cardLayout.show(mainPanel, "DIFFICULTY_SELECTION");
|
|
|
return user;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 处理密码修改
|
|
|
*/
|
|
|
public static boolean handlePasswordChange(String oldPassword, String newPassword, String confirmNewPassword,
|
|
|
RegisteredUser currentUser) {
|
|
|
if (!currentUser.verifyPassword(oldPassword)) {
|
|
|
JOptionPane.showMessageDialog(null, "原密码错误!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (!newPassword.equals(confirmNewPassword)) {
|
|
|
JOptionPane.showMessageDialog(null, "两次输入的新密码不一致!", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (!isValidPassword(newPassword)) {
|
|
|
JOptionPane.showMessageDialog(null,
|
|
|
"新密码不符合要求!\n要求:6-10位,必须包含大小写字母和数字",
|
|
|
"错误", JOptionPane.ERROR_MESSAGE);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
currentUser.setPassword(newPassword);
|
|
|
JOptionPane.showMessageDialog(null, "密码修改成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 生成选择题
|
|
|
*/
|
|
|
public static List<MultipleChoiceQuestion> generateQuestions(String difficulty, int count, MathQuestionGenerator generator) {
|
|
|
List<MultipleChoiceQuestion> questions = new ArrayList<>();
|
|
|
Random random = new Random();
|
|
|
|
|
|
int attempts = 0;
|
|
|
int maxAttempts = count * 5; // 防止无限循环
|
|
|
|
|
|
while (questions.size() < count && attempts < maxAttempts) {
|
|
|
// 生成数学表达式
|
|
|
MathQuestion mathQuestion = generateSingleQuestion(difficulty, generator);
|
|
|
String expression = mathQuestion.getExpression();
|
|
|
|
|
|
// 验证表达式语法
|
|
|
if (!ExpressionEvaluator.isValidMathExpression(expression)) {
|
|
|
attempts++;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// 计算正确答案
|
|
|
double correctAnswer = evaluateExpression(expression);
|
|
|
|
|
|
// 检查答案是否有效
|
|
|
if (Double.isNaN(correctAnswer) || Double.isInfinite(correctAnswer)) {
|
|
|
attempts++;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// 生成选项
|
|
|
String[] options = new String[4];
|
|
|
int correctIndex = random.nextInt(4);
|
|
|
|
|
|
// 设置正确答案
|
|
|
options[correctIndex] = formatAnswer(correctAnswer);
|
|
|
|
|
|
// 生成3个错误答案
|
|
|
String[] wrongAnswers = ExpressionEvaluator.generateWrongAnswers(correctAnswer, 3);
|
|
|
int wrongIndex = 0;
|
|
|
for (int j = 0; j < 4; j++) {
|
|
|
if (j != correctIndex) {
|
|
|
options[j] = wrongAnswers[wrongIndex++];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
MultipleChoiceQuestion question = new MultipleChoiceQuestion(
|
|
|
"计算:" + expression + " = ?", options, correctIndex);
|
|
|
questions.add(question);
|
|
|
attempts = 0; // 重置尝试计数
|
|
|
}
|
|
|
|
|
|
return questions;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 生成单个数学题目
|
|
|
*/
|
|
|
private static MathQuestion generateSingleQuestion(String difficulty, MathQuestionGenerator generator) {
|
|
|
List<MathQuestion> questions = generator.generateQuestions(difficulty, 1);
|
|
|
return questions.get(0);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 计算数学表达式的值
|
|
|
*/
|
|
|
private static double evaluateExpression(String expression) {
|
|
|
return ExpressionEvaluator.evaluate(expression);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 格式化答案
|
|
|
*/
|
|
|
private static String formatAnswer(double answer) {
|
|
|
return ExpressionEvaluator.formatAnswer(answer);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 验证邮箱格式
|
|
|
*/
|
|
|
public static boolean isValidEmail(String email) {
|
|
|
String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
|
|
|
Pattern pattern = Pattern.compile(emailRegex);
|
|
|
return pattern.matcher(email).matches();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 验证密码格式
|
|
|
*/
|
|
|
public static boolean isValidPassword(String password) {
|
|
|
if (password.length() < 6 || password.length() > 10) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
boolean hasUpper = false, hasLower = false, 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;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 生成验证码
|
|
|
*/
|
|
|
public static String generateVerificationCode() {
|
|
|
Random random = new Random();
|
|
|
StringBuilder code = new StringBuilder();
|
|
|
for (int i = 0; i < 6; i++) {
|
|
|
code.append(random.nextInt(10));
|
|
|
}
|
|
|
return code.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 保存用户数据
|
|
|
*/
|
|
|
public static void saveUserData(Map<String, RegisteredUser> users) {
|
|
|
try {
|
|
|
Path dataFile = Paths.get("user_data.txt");
|
|
|
try (BufferedWriter writer = Files.newBufferedWriter(dataFile)) {
|
|
|
for (RegisteredUser user : users.values()) {
|
|
|
if (user.isVerified() && user.getPassword() != null) {
|
|
|
writer.write(user.getEmail() + ":" + user.getPassword());
|
|
|
writer.newLine();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} catch (IOException e) {
|
|
|
System.err.println("保存用户数据失败: " + e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 加载用户数据
|
|
|
*/
|
|
|
public static void loadUserData(Map<String, RegisteredUser> users) {
|
|
|
try {
|
|
|
Path dataFile = Paths.get("user_data.txt");
|
|
|
if (Files.exists(dataFile)) {
|
|
|
List<String> lines = Files.readAllLines(dataFile);
|
|
|
for (String line : lines) {
|
|
|
String[] parts = line.split(":");
|
|
|
if (parts.length == 2) {
|
|
|
RegisteredUser user = new RegisteredUser(parts[0], "");
|
|
|
user.setVerified(true);
|
|
|
user.setPassword(parts[1]);
|
|
|
users.put(parts[0], user);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} catch (IOException e) {
|
|
|
System.err.println("加载用户数据失败: " + e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
}
|