From d00bb8b592e4e20d5ca2d915dd1e8817d883d91a Mon Sep 17 00:00:00 2001 From: hnu202326010307 <3429871628@qq.com> Date: Sun, 12 Oct 2025 21:08:59 +0800 Subject: [PATCH] Initial commit --- .idea/.gitignore | 8 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/my_project.iml | 9 + .idea/vcs.xml | 6 + src/MainApplication.class | Bin 0 -> 1260 bytes src/MainApplication.java | 21 ++ src/TestCalculator.java | 18 ++ src/TestDivisionOrder.java | 22 ++ src/TestMailLibs.java | 45 ++++ src/TestSquareCalculator.java | 24 ++ src/TestTrigonometricCalculator.java | 36 +++ src/TestUserRegistration.java | 74 ++++++ src/TestUsernameCheck.java | 33 +++ src/backend/EmailService$1.class | Bin 0 -> 669 bytes src/backend/EmailService.class | Bin 0 -> 6770 bytes src/backend/EmailService.java | 231 ++++++++++++++++ src/backend/FileManager.java | 104 ++++++++ src/backend/HighSchoolGenerator.class | Bin 0 -> 2291 bytes src/backend/HighSchoolGenerator.java | 88 +++++++ src/backend/MathCalculator.class | Bin 0 -> 6053 bytes src/backend/MathCalculator.java | 318 +++++++++++++++++++++++ src/backend/MiddleSchoolGenerator.class | Bin 0 -> 2491 bytes src/backend/MiddleSchoolGenerator.java | 99 +++++++ src/backend/PrimarySchoolGenerator.class | Bin 0 -> 1350 bytes src/backend/PrimarySchoolGenerator.java | 41 +++ src/backend/Question.class | Bin 0 -> 1345 bytes src/backend/Question.java | 51 ++++ src/backend/QuestionGenerator.class | Bin 0 -> 2699 bytes src/backend/QuestionGenerator.java | 117 +++++++++ src/backend/User.class | Bin 0 -> 931 bytes src/backend/User.java | 42 +++ src/backend/UserManager.class | Bin 0 -> 7911 bytes src/backend/UserManager.java | 295 +++++++++++++++++++++ src/frontend/ChangePasswordFrame$1.class | Bin 0 -> 606 bytes src/frontend/ChangePasswordFrame$2.class | Bin 0 -> 603 bytes src/frontend/ChangePasswordFrame$3.class | Bin 0 -> 606 bytes src/frontend/ChangePasswordFrame.class | Bin 0 -> 4144 bytes src/frontend/ChangePasswordFrame.java | 137 ++++++++++ src/frontend/LoginFrame$1.class | Bin 0 -> 561 bytes src/frontend/LoginFrame$2.class | Bin 0 -> 561 bytes src/frontend/LoginFrame$3.class | Bin 0 -> 561 bytes src/frontend/LoginFrame.class | Bin 0 -> 4038 bytes src/frontend/LoginFrame.java | 132 ++++++++++ src/frontend/MainFrame$1.class | Bin 0 -> 614 bytes src/frontend/MainFrame$2.class | Bin 0 -> 614 bytes src/frontend/MainFrame$3.class | Bin 0 -> 614 bytes src/frontend/MainFrame$4.class | Bin 0 -> 724 bytes src/frontend/MainFrame$5.class | Bin 0 -> 556 bytes src/frontend/MainFrame.class | Bin 0 -> 4683 bytes src/frontend/MainFrame.java | 157 +++++++++++ src/frontend/QuestionCountFrame$1.class | Bin 0 -> 601 bytes src/frontend/QuestionCountFrame$2.class | Bin 0 -> 598 bytes src/frontend/QuestionCountFrame$3.class | Bin 0 -> 601 bytes src/frontend/QuestionCountFrame.class | Bin 0 -> 4038 bytes src/frontend/QuestionCountFrame.java | 134 ++++++++++ src/frontend/QuizFrame$1.class | Bin 0 -> 556 bytes src/frontend/QuizFrame$2.class | Bin 0 -> 556 bytes src/frontend/QuizFrame.class | Bin 0 -> 7227 bytes src/frontend/QuizFrame.java | 271 +++++++++++++++++++ src/frontend/RegistrationFrame$1.class | Bin 0 -> 596 bytes src/frontend/RegistrationFrame$2.class | Bin 0 -> 596 bytes src/frontend/RegistrationFrame$3.class | Bin 0 -> 740 bytes src/frontend/RegistrationFrame$4.class | Bin 0 -> 596 bytes src/frontend/RegistrationFrame.class | Bin 0 -> 5292 bytes src/frontend/RegistrationFrame.java | 194 ++++++++++++++ src/frontend/ScoreFrame$1.class | Bin 0 -> 744 bytes src/frontend/ScoreFrame$2.class | Bin 0 -> 857 bytes src/frontend/ScoreFrame.class | Bin 0 -> 3889 bytes src/frontend/ScoreFrame.java | 155 +++++++++++ src/registry.dat | Bin 0 -> 372 bytes 71 files changed, 2876 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/my_project.iml create mode 100644 .idea/vcs.xml create mode 100644 src/MainApplication.class create mode 100644 src/MainApplication.java create mode 100644 src/TestCalculator.java create mode 100644 src/TestDivisionOrder.java create mode 100644 src/TestMailLibs.java create mode 100644 src/TestSquareCalculator.java create mode 100644 src/TestTrigonometricCalculator.java create mode 100644 src/TestUserRegistration.java create mode 100644 src/TestUsernameCheck.java create mode 100644 src/backend/EmailService$1.class create mode 100644 src/backend/EmailService.class create mode 100644 src/backend/EmailService.java create mode 100644 src/backend/FileManager.java create mode 100644 src/backend/HighSchoolGenerator.class create mode 100644 src/backend/HighSchoolGenerator.java create mode 100644 src/backend/MathCalculator.class create mode 100644 src/backend/MathCalculator.java create mode 100644 src/backend/MiddleSchoolGenerator.class create mode 100644 src/backend/MiddleSchoolGenerator.java create mode 100644 src/backend/PrimarySchoolGenerator.class create mode 100644 src/backend/PrimarySchoolGenerator.java create mode 100644 src/backend/Question.class create mode 100644 src/backend/Question.java create mode 100644 src/backend/QuestionGenerator.class create mode 100644 src/backend/QuestionGenerator.java create mode 100644 src/backend/User.class create mode 100644 src/backend/User.java create mode 100644 src/backend/UserManager.class create mode 100644 src/backend/UserManager.java create mode 100644 src/frontend/ChangePasswordFrame$1.class create mode 100644 src/frontend/ChangePasswordFrame$2.class create mode 100644 src/frontend/ChangePasswordFrame$3.class create mode 100644 src/frontend/ChangePasswordFrame.class create mode 100644 src/frontend/ChangePasswordFrame.java create mode 100644 src/frontend/LoginFrame$1.class create mode 100644 src/frontend/LoginFrame$2.class create mode 100644 src/frontend/LoginFrame$3.class create mode 100644 src/frontend/LoginFrame.class create mode 100644 src/frontend/LoginFrame.java create mode 100644 src/frontend/MainFrame$1.class create mode 100644 src/frontend/MainFrame$2.class create mode 100644 src/frontend/MainFrame$3.class create mode 100644 src/frontend/MainFrame$4.class create mode 100644 src/frontend/MainFrame$5.class create mode 100644 src/frontend/MainFrame.class create mode 100644 src/frontend/MainFrame.java create mode 100644 src/frontend/QuestionCountFrame$1.class create mode 100644 src/frontend/QuestionCountFrame$2.class create mode 100644 src/frontend/QuestionCountFrame$3.class create mode 100644 src/frontend/QuestionCountFrame.class create mode 100644 src/frontend/QuestionCountFrame.java create mode 100644 src/frontend/QuizFrame$1.class create mode 100644 src/frontend/QuizFrame$2.class create mode 100644 src/frontend/QuizFrame.class create mode 100644 src/frontend/QuizFrame.java create mode 100644 src/frontend/RegistrationFrame$1.class create mode 100644 src/frontend/RegistrationFrame$2.class create mode 100644 src/frontend/RegistrationFrame$3.class create mode 100644 src/frontend/RegistrationFrame$4.class create mode 100644 src/frontend/RegistrationFrame.class create mode 100644 src/frontend/RegistrationFrame.java create mode 100644 src/frontend/ScoreFrame$1.class create mode 100644 src/frontend/ScoreFrame$2.class create mode 100644 src/frontend/ScoreFrame.class create mode 100644 src/frontend/ScoreFrame.java create mode 100644 src/registry.dat diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..66f29f6 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..00ec000 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/my_project.iml b/.idea/my_project.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/my_project.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/MainApplication.class b/src/MainApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..9840f635b12e89cbf38ff1c611e3c55567cba7d6 GIT binary patch literal 1260 zcma)6+foxj5IsWz8<@n(ZD4WQA`Zd?X-!j zNE?_j5z{d-Tr+UpK!%}j(U!J)$`Hw9HyNS@w&pBMyb7ke*vYyw#cv;L9!3I z6cS_>EZo9v19vQv<{q(Z`L0w#)^bI+Zp#&)H-v@zctE}WRSEHSQogBdi>hK6(XPE* z@R|C}ZLF5Kk?oy50z04$ zJzrY;+D`Q290qBxL*J3@Av<=Ao?}G5AXc0^MDGa&;~2(43(`lBD1~W~aeV`m);M_w zXlGI06h<+OA-XZpvua>CnlklUIL$IoU|mq+D`M}D(EoZidI+;qS%Mk>RTZPU;zSc6 eIuK?I&&ZmDeS#%CrC-=2oh>?FrkE)bVComU?>bcg literal 0 HcmV?d00001 diff --git a/src/MainApplication.java b/src/MainApplication.java new file mode 100644 index 0000000..d713d37 --- /dev/null +++ b/src/MainApplication.java @@ -0,0 +1,21 @@ +import javax.swing.*; +import java.awt.*; +import frontend.LoginFrame; + +public class MainApplication { + public static void main(String[] args) { + // 设置系统外观 + try { + // 设置系统外观 + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + + // 创建并显示GUI + SwingUtilities.invokeLater(() -> { + LoginFrame loginFrame = new LoginFrame(); + loginFrame.setVisible(true); + }); + } +} \ No newline at end of file diff --git a/src/TestCalculator.java b/src/TestCalculator.java new file mode 100644 index 0000000..bf48fc8 --- /dev/null +++ b/src/TestCalculator.java @@ -0,0 +1,18 @@ +import backend.MathCalculator; +import backend.PrimarySchoolGenerator; + +public class TestCalculator { + public static void main(String[] args) { + // 创建初中题目生成器 + PrimarySchoolGenerator generator = new PrimarySchoolGenerator(); + + // 生成随机初中题目 + String expression = generator.generateQuestion(); + + System.out.println("随机生成的小学题目: " + expression); + + // 使用计算器计算 + String result = MathCalculator.calculateSimple(expression); + System.out.println("计算器计算结果: " + result); + } +} \ No newline at end of file diff --git a/src/TestDivisionOrder.java b/src/TestDivisionOrder.java new file mode 100644 index 0000000..a3aaef7 --- /dev/null +++ b/src/TestDivisionOrder.java @@ -0,0 +1,22 @@ +import backend.MathCalculator; + +public class TestDivisionOrder { + public static void main(String[] args) { + // 测试表达式:√22/15/40 + String expression = "√22/15/40"; + System.out.println("测试表达式: " + expression); + + // 使用calculateSimple方法计算 + String result = MathCalculator.calculateSimple(expression); + System.out.println("计算结果: " + result); + + // 直接使用calculate方法计算,查看详细的计算过程 + double detailedResult = MathCalculator.calculate(expression); + System.out.println("详细计算结果: " + detailedResult); + + // 验证计算顺序:先计算√22,然后除以15,最后除以40 + double sqrt22 = Math.sqrt(22); + double expected = (sqrt22 / 15) / 40; + System.out.println("期望结果((√22/15)/40): " + expected); + } +} \ No newline at end of file diff --git a/src/TestMailLibs.java b/src/TestMailLibs.java new file mode 100644 index 0000000..c6459ab --- /dev/null +++ b/src/TestMailLibs.java @@ -0,0 +1,45 @@ +import java.util.Properties; +import javax.mail.*; +import javax.activation.DataSource; + +/** + * 测试JavaMail和Activation Framework是否能正常加载 + */ +public class TestMailLibs { + public static void main(String[] args) { + System.out.println("开始测试JavaMail和Activation Framework库..."); + + try { + // 测试加载javax.mail.Session类 + Class sessionClass = Class.forName("javax.mail.Session"); + System.out.println("✓ 成功加载javax.mail.Session类"); + System.out.println(" 类路径: " + sessionClass.getProtectionDomain().getCodeSource().getLocation()); + } catch (ClassNotFoundException e) { + System.err.println("✗ 无法加载javax.mail.Session类"); + System.err.println(" 错误信息: " + e.getMessage()); + } + + try { + // 测试加载javax.activation.DataSource类 + Class dataSourceClass = Class.forName("javax.activation.DataSource"); + System.out.println("✓ 成功加载javax.activation.DataSource类"); + System.out.println(" 类路径: " + dataSourceClass.getProtectionDomain().getCodeSource().getLocation()); + } catch (ClassNotFoundException e) { + System.err.println("✗ 无法加载javax.activation.DataSource类"); + System.err.println(" 错误信息: " + e.getMessage()); + } + + try { + // 测试直接引用这些类 + Session session = Session.getDefaultInstance(new Properties()); + System.out.println("✓ 成功创建Session实例"); + } catch (Exception e) { + System.err.println("✗ 无法创建Session实例"); + System.err.println(" 错误信息: " + e.getMessage()); + e.printStackTrace(); + } + + System.out.println("\n当前classpath: " + System.getProperty("java.class.path")); + System.out.println("\n测试完成"); + } +} \ No newline at end of file diff --git a/src/TestSquareCalculator.java b/src/TestSquareCalculator.java new file mode 100644 index 0000000..2431852 --- /dev/null +++ b/src/TestSquareCalculator.java @@ -0,0 +1,24 @@ +import backend.MathCalculator; + +public class TestSquareCalculator { + public static void main(String[] args) { + System.out.println("=== 测试平方符号计算功能 ==="); + + // 测试基本平方运算 + testExpression("47²"); + + // 测试包含平方的复杂表达式 + testExpression("(47²-53+95)"); + + // 测试其他可能的情况 + testExpression("2²+3²"); + testExpression("(2+3)²"); + testExpression("10-5²"); + } + + private static void testExpression(String expression) { + System.out.println("\n原始表达式: " + expression); + String result = MathCalculator.calculateSimple(expression); + System.out.println("计算结果: " + result); + } +} \ No newline at end of file diff --git a/src/TestTrigonometricCalculator.java b/src/TestTrigonometricCalculator.java new file mode 100644 index 0000000..6840613 --- /dev/null +++ b/src/TestTrigonometricCalculator.java @@ -0,0 +1,36 @@ +import backend.MathCalculator; +import backend.HighSchoolGenerator; + +public class TestTrigonometricCalculator { + public static void main(String[] args) { + System.out.println("=== 测试三角函数解析功能 ==="); + + // 测试简单三角函数 + testExpression("sin(30)"); + testExpression("cos(45)"); + testExpression("tan(60)"); + + // 测试包含表达式的三角函数 + testExpression("sin(30+15)"); + testExpression("cos(2*45)"); + testExpression("tan(60/2)"); + + // 测试复杂嵌套表达式的三角函数 + testExpression("sin((30+15)*2)"); + testExpression("cos(2*(45-10))"); + + // 使用高中生成器生成随机题目并测试 + System.out.println("\n=== 测试随机生成的高中题目 ==="); + HighSchoolGenerator generator = new HighSchoolGenerator(); + for (int i = 0; i < 3; i++) { + String randomQuestion = generator.generateQuestion(); + testExpression(randomQuestion); + } + } + + private static void testExpression(String expression) { + System.out.println("\n原始表达式: " + expression); + String result = MathCalculator.calculateSimple(expression); + System.out.println("计算结果: " + result); + } +} \ No newline at end of file diff --git a/src/TestUserRegistration.java b/src/TestUserRegistration.java new file mode 100644 index 0000000..6c5042b --- /dev/null +++ b/src/TestUserRegistration.java @@ -0,0 +1,74 @@ +import frontend.LoginFrame; +import frontend.RegistrationFrame; + +import javax.swing.*; + +public class TestUserRegistration { + public static void main(String[] args) { + // 启动程序的主入口 + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + // 创建登录窗口 + new LoginFrame().setVisible(true); + + // 以下是可选的测试代码,用于直接测试用户注册功能 + // 注释掉上面的LoginFrame创建代码,取消下面的注释来进行测试 + /* + System.out.println("===== 用户管理系统测试 ===="); + + // 获取UserManager实例 + UserManager userManager = UserManager.getInstance(); + + // 测试发送验证码 + String email = "test_new_user@example.com"; + String username = "testuser123"; + String password = "Test123"; + + System.out.println("1. 发送验证码到邮箱: " + email); + String result = userManager.sendVerificationCode(email); + System.out.println(" 结果: " + result); + + // 获取生成的验证码 + String code = userManager.getVerificationCode(email); + System.out.println(" 生成的验证码: " + code); + + // 测试注册用户 + if (code != null) { + System.out.println("2. 使用用户名 \"" + username + "\" 注册用户"); + result = userManager.registerUser(email, username, code, password, password); + System.out.println(" 结果: " + result); + } + + // 测试用户名是否已存在 + System.out.println("3. 检查用户名 \"" + username + "\" 是否已存在: " + + userManager.isUsernameRegistered(username)); + + // 测试邮箱是否已存在 + System.out.println("4. 检查邮箱 \"" + email + "\" 是否已存在: " + + userManager.isEmailRegistered(email)); + + // 测试用户认证(通过邮箱) + System.out.println("5. 通过邮箱登录验证"); + User user = userManager.authenticate(email, password); + System.out.println(" 登录结果: " + (user != null ? "成功" : "失败")); + + // 测试用户认证(通过用户名) + System.out.println("6. 通过用户名登录验证"); + user = userManager.authenticate(username, password); + System.out.println(" 登录结果: " + (user != null ? "成功" : "失败")); + + // 显示用户信息 + if (user != null) { + System.out.println("7. 用户信息:"); + System.out.println(" 邮箱: " + user.getEmail()); + System.out.println(" 用户名: " + user.getUsername()); + System.out.println(" 用户类型: " + user.getUserType()); + } + + System.out.println("===== 测试完成 ===="); + */ + } + }); + } +} \ No newline at end of file diff --git a/src/TestUsernameCheck.java b/src/TestUsernameCheck.java new file mode 100644 index 0000000..2d42e21 --- /dev/null +++ b/src/TestUsernameCheck.java @@ -0,0 +1,33 @@ +import backend.UserManager; +import javax.swing.*; + +/** + * 测试用户名检查功能的简单程序 + */ +public class TestUsernameCheck { + public static void main(String[] args) { + // 初始化用户管理器 + UserManager userManager = UserManager.getInstance(); + + // 测试用例1: 检查一个不存在的用户名 + String testUsername1 = "testuser123"; + boolean exists1 = userManager.isUsernameRegistered(testUsername1); + System.out.println("用户名 '" + testUsername1 + "' 是否已被注册: " + exists1); + + // 测试用例2: 检查一个可能存在的用户名(根据实际情况调整) + String testUsername2 = "admin"; // 常见的预设用户名 + boolean exists2 = userManager.isUsernameRegistered(testUsername2); + System.out.println("用户名 '" + testUsername2 + "' 是否已被注册: " + exists2); + + // 显示测试结果对话框 + StringBuilder resultMsg = new StringBuilder(); + resultMsg.append("用户名检查测试结果:\n\n"); + resultMsg.append("测试用户名1 ('").append(testUsername1).append("'): "); + resultMsg.append(exists1 ? "已被注册" : "未被注册").append("\n"); + resultMsg.append("测试用户名2 ('").append(testUsername2).append("'): "); + resultMsg.append(exists2 ? "已被注册" : "未被注册").append("\n\n"); + resultMsg.append("注册界面已更新,现在在发送验证码前会检查用户名是否被占用。"); + + JOptionPane.showMessageDialog(null, resultMsg.toString(), "测试结果", JOptionPane.INFORMATION_MESSAGE); + } +} \ No newline at end of file diff --git a/src/backend/EmailService$1.class b/src/backend/EmailService$1.class new file mode 100644 index 0000000000000000000000000000000000000000..b88d3afeab50c8e853bb35bf3ff510fe91d301c9 GIT binary patch literal 669 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 ae~n^e@H+e~Ro3RS1UKRvcuzcngx>;yIu-i> literal 0 HcmV?d00001 diff --git a/src/backend/EmailService.java b/src/backend/EmailService.java new file mode 100644 index 0000000..ebf232c --- /dev/null +++ b/src/backend/EmailService.java @@ -0,0 +1,231 @@ +package backend; + +import javax.mail.*; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * 邮件服务类,负责发送验证邮件 + */ +public class EmailService { + // 静态变量存储邮件配置信息 + private static String HOST = "smtp.qq.com"; // 邮件服务器主机名(QQ邮箱) + private static String PORT = "587"; // 邮件服务器端口(QQ邮箱) + private static String USERNAME = "3449163506@qq.com"; // 发件人邮箱 + private static String PASSWORD = "emznhmsljcktdbfd"; + private static String FROM = "3449163506@qq.com"; // 发件人邮箱(与USERNAME相同) + + // 静态初始化块,设置邮箱配置 + static { + // 邮箱配置已在代码中完成,用户无需手动配置 + // 所有配置信息在程序内部处理,不向用户暴露 + } + + // JavaMail API可用性标志 + private static boolean isJavaMailAvailable = true; + + // 邮箱提供商SMTP配置映射 + private static Map EMAIL_PROVIDER_CONFIG = new HashMap<>(); + + static { + // 初始化常用邮箱提供商的SMTP服务器配置 + EMAIL_PROVIDER_CONFIG.put("qq.com", "smtp.qq.com"); + EMAIL_PROVIDER_CONFIG.put("163.com", "smtp.163.com"); + EMAIL_PROVIDER_CONFIG.put("126.com", "smtp.126.com"); + EMAIL_PROVIDER_CONFIG.put("gmail.com", "smtp.gmail.com"); + EMAIL_PROVIDER_CONFIG.put("outlook.com", "smtp.office365.com"); + EMAIL_PROVIDER_CONFIG.put("hotmail.com", "smtp.office365.com"); + EMAIL_PROVIDER_CONFIG.put("sina.com", "smtp.sina.com"); + EMAIL_PROVIDER_CONFIG.put("sohu.com", "smtp.sohu.com"); + } + + // 静态初始化块,检查JavaMail API是否可用 + static { + try { + // 尝试加载JavaMail和Activation Framework的关键类 + Class.forName("javax.mail.Session"); + Class.forName("javax.activation.DataSource"); + isJavaMailAvailable = true; + } catch (ClassNotFoundException e) { + isJavaMailAvailable = false; + System.err.println("错误:JavaMail API 或 Activation Framework 不可用。"); + System.err.println("请确保lib目录下包含javax.mail.jar和activation.jar文件。"); + System.err.println("原因:" + e.getMessage()); + } + } + + // 私有构造函数 + private EmailService() {} + + /** + * 根据邮箱地址获取对应的SMTP服务器 + * @param email 邮箱地址 + * @return SMTP服务器主机名 + */ + private static String getSmtpServerByEmail(String email) { + if (email == null || !email.contains("@")) { + return HOST; // 返回默认配置 + } + + String domain = email.substring(email.lastIndexOf("@") + 1); + return EMAIL_PROVIDER_CONFIG.getOrDefault(domain, HOST); + } + + /** + * 根据邮箱自动设置SMTP配置 + * @param email 邮箱地址 + */ + public static void autoSetSmtpConfig(String email) { + String smtpServer = getSmtpServerByEmail(email); + HOST = smtpServer; + + // 根据不同的邮件服务器设置不同的端口(默认使用587) + if (smtpServer.contains("gmail.com")) { + PORT = "587"; + } else if (smtpServer.contains("outlook.com") || smtpServer.contains("hotmail.com")) { + PORT = "587"; + } else { + PORT = "587"; + } + } + + /** + * 发送验证码邮件到指定邮箱 + * @param toEmail 接收验证码的邮箱地址 + * @param verificationCode 验证码 + * @return 发送结果,"success"表示成功,否则返回错误信息 + */ + public static String sendVerificationEmail(String toEmail, String verificationCode) { + // 检查JavaMail API是否可用 + if (!isJavaMailAvailable) { + return "错误:JavaMail API 不可用,无法发送邮件。请确保lib目录下包含javax.mail.jar和activation.jar文件。"; + } + + // 检查必要的邮件配置是否已设置 + if (USERNAME.isEmpty() || PASSWORD.isEmpty() || FROM.isEmpty()) { + return "错误:邮件配置不完整,请联系系统管理员配置邮件服务。"; + } + + // 根据用户邮箱自动设置SMTP配置 + autoSetSmtpConfig(toEmail); + + try { + // 配置邮件服务器属性 + Properties properties = new Properties(); + properties.put("mail.smtp.host", HOST); + properties.put("mail.smtp.port", PORT); + properties.put("mail.smtp.auth", "true"); + properties.put("mail.smtp.starttls.enable", "true"); // 启用TLS加密 + properties.put("mail.smtp.ssl.trust", HOST); // 信任服务器 + properties.put("mail.debug", "false"); // 关闭调试信息 + properties.put("mail.smtp.timeout", "30000"); // 设置超时时间30秒 + + // 配置SSL/TLS协议,解决"No appropriate protocol"问题 + properties.put("mail.smtp.ssl.protocols", "TLSv1.2"); + + // 创建会话对象 + Session session = Session.getInstance(properties, new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(USERNAME, PASSWORD); + } + }); + + // 创建邮件消息 + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(FROM)); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail)); + message.setSubject("【中小学数学学习软件】验证码"); + + // 设置邮件内容 + String content = "

亲爱的用户:

" + + "

您正在注册中小学数学学习软件账号,您的验证码是:

" + + "

" + verificationCode + "

" + + "

请在5分钟内完成验证,验证码过期后请重新获取。

" + + "

如果这不是您本人操作,请忽略此邮件。

"; + + message.setContent(content, "text/html; charset=utf-8"); + + // 发送邮件 + Transport.send(message); + System.out.println("邮件发送成功,验证码:" + verificationCode + " 已发送到邮箱:" + toEmail); + return "success"; + } catch (MessagingException e) { + String errorMessage = "发送邮件失败: " + e.getMessage(); + System.err.println(errorMessage); + e.printStackTrace(); + + // 提供更详细的错误信息和解决方案 + if (e instanceof AuthenticationFailedException) { + if (USERNAME.endsWith("@qq.com")) { + return errorMessage + "\n\nQQ邮箱认证失败,可能原因及解决方法:\n" + + "1. 请检查授权码是否正确(不是QQ密码,需在QQ邮箱网页版生成)\n" + + "2. 确认已在QQ邮箱网页版开启POP3/SMTP服务\n" + + "3. 若登录频繁,请暂停10-15分钟后再试\n" + + "4. 检查账号是否异常,尝试网页端登录验证"; + } else { + return errorMessage + "。请检查用户名和密码/授权码是否正确。"; + } + } else if (e instanceof SendFailedException) { + return errorMessage + "。收件人邮箱地址无效或不可达。"; + } else if (e.getMessage().contains("Connection refused")) { + return errorMessage + "。无法连接到邮件服务器,请检查主机名和端口是否正确。"; + } else if (e.getMessage().contains("timeout")) { + return errorMessage + "。连接超时,请检查网络连接或稍后再试。"; + } + + return errorMessage + "。请检查您的邮箱配置和网络连接。"; + } catch (Exception e) { + String errorMessage = "发送邮件时发生未知错误: " + e.getMessage(); + System.err.println(errorMessage); + e.printStackTrace(); + return errorMessage; + } + } + + /** + * 检查JavaMail API是否可用 + * @return JavaMail API是否可用 + */ + public static boolean isJavaMailAvailable() { + return isJavaMailAvailable; + } + + /** + * 设置邮件服务器配置(可由用户自定义) + * @param host 邮件服务器主机名 + * @param port 邮件服务器端口 + * @param username 发件人邮箱 + * @param password 发件人邮箱密码或授权码 + * @param from 发件人邮箱(与username相同) + */ + public static void setEmailConfig(String host, String port, String username, String password, String from) { + if (host != null && !host.isEmpty()) { + HOST = host; + } + if (port != null && !port.isEmpty()) { + PORT = port; + } + if (username != null && !username.isEmpty()) { + USERNAME = username; + } + if (password != null && !password.isEmpty()) { + PASSWORD = password; + } + if (from != null && !from.isEmpty()) { + FROM = from; + } + } + + /** + * 获取当前的邮箱配置信息 + * @return 包含邮箱配置的字符串 + */ + public static String getEmailConfigInfo() { + // 不向用户暴露具体配置细节 + return "邮件服务已配置完成\n系统会自动发送验证码到您的邮箱"; + } +} \ No newline at end of file diff --git a/src/backend/FileManager.java b/src/backend/FileManager.java new file mode 100644 index 0000000..3d7a2bc --- /dev/null +++ b/src/backend/FileManager.java @@ -0,0 +1,104 @@ +package backend; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +public class FileManager { + + private String baseDir; + + public FileManager() { + this.baseDir = System.getProperty("user.dir") + File.separator + "papers"; + createBaseDirectory(); + } + + // 创建基础目录 + private void createBaseDirectory() { + File dir = new File(baseDir); + if (!dir.exists()) { + dir.mkdirs(); + } + } + + // 为用户创建目录 + private String createUserDirectory(String username) { + String userDir = baseDir + File.separator + username; + File dir = new File(userDir); + if (!dir.exists()) { + dir.mkdirs(); + } + return userDir; + } + + // 生成文件名 + private String generateFileName() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + return sdf.format(new Date()) + ".txt"; + } + + // 保存题目到文件 + public void saveQuestions(String username, String[] questions) throws IOException { + String userDir = createUserDirectory(username); + String fileName = generateFileName(); + String filePath = userDir + File.separator + fileName; + + try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) { + for (int i = 0; i < questions.length; i++) { + writer.println((i + 1) + ". " + questions[i]); + if (i < questions.length - 1) { + writer.println(); // 题目之间空一行 + } + } + } + + System.out.println("题目已保存到: " + filePath); + } + + // 检查题目是否重复(读取用户目录下所有文件) + public Set loadExistingQuestions(String username) { + Set existingQuestions = new HashSet<>(); + String userDir = baseDir + File.separator + username; + File dir = new File(userDir); + + if (dir.exists() && dir.isDirectory()) { + File[] files = dir.listFiles((d, name) -> name.endsWith(".txt")); + if (files != null) { + for (File file : files) { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + StringBuilder questionBuilder = new StringBuilder(); + while ((line = reader.readLine()) != null) { + if (line.trim().isEmpty()) { + if (questionBuilder.length() > 0) { + // 移除题号 + String question = questionBuilder.toString().replaceAll("^\\d+\\.\\s*", ""); + existingQuestions.add(question.trim()); + questionBuilder.setLength(0); + } + } else { + questionBuilder.append(line).append(" "); + } + } + // 处理最后一个题目 + if (questionBuilder.length() > 0) { + String question = questionBuilder.toString().replaceAll("^\\d+\\.\\s*", ""); + existingQuestions.add(question.trim()); + } + } catch (IOException e) { + System.out.println("读取文件失败: " + file.getName()); + } + } + } + } + + return existingQuestions; + } +} \ No newline at end of file diff --git a/src/backend/HighSchoolGenerator.class b/src/backend/HighSchoolGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..1259b724612b54155d246b1d066b1c701beef45a GIT binary patch literal 2291 zcmb7FOLG)e6#hEXGt*2O5+-l*;H6<6VMb9RkOw4zBm;So84?31OlI1oVbVR$^dMSn zEd3F}%Cf9tAy!4S5=%=KF8v*DT)1^XQQ~)|I}Z}FaM7pFz4zSjob#P?y8rn1muCRp z#b+7@L+Hl&5Z=TE5kne=L+HeaEG~+;6vSu<`!FVpaShKk&54-T@V*=`Xt*B44GlLne4yb&4YxGh){xb(7(_0Jyn?#np>%5G z*7VG!*`dtzY+6CXjfp$id)Z_mYb_E`V<_<%%;X}jb57?$)u*IXcp)OmhVtP5XjpmiHd?WAZbpf{FYXGbPw4VchO=ce=lNHV z{68oufzkSN1+NUDv|TCXj8RiaZyh(6meaXq+b)c`^xLI`RF!r6>$|W1{P}wwF&H}D zMpDN|AaN~;Sfp%P4{~<-fs?hOHzSGI&1fQ~V;M_2OtHR$yENR{9Ui}e7}s$M@91p% z(>e-*y#gx;TSpNe>xd$vqlB^!2NeYhR}Sv6g??X+>i7hAb(|2B_i#qXS)8NE5gqsO zDU~Q&X{Tpy$N8M(6XTO45>udL5|m7X6<=giv0%y9P@ElZ*<{^U9lVn9Eqk_Q^$6vjQPNb1CsKx8i^_-Z624gzimD>(VO1b6l8 z#`wr=rFIYQ)^Xh{X<1A(U|MCPM9+yxY8P(b-dH%wuUI!fpEWon9YO7oG!Nez6zMa{ z9`~(K5(LIvhh0ENRlg`*Wd=s(w8q`NG|A~kFYY04i z`L}EYEYNKhw@kKTUbYEGS;(> z+QC>)d*?dzo;Bgw&mI9-Ip4HTVgXbd+|nOC308B#OX^S&pHw)4Wl~;_12pF-d*Q4ym;kTM`+C(Twc{JiJVlEeO-nVJbN_0ue0FQYrlIQv+Qeljyac^)IU zfN{zh+RQOtiM9d$7w{=BkO-o;={bVWNo|dMNxbOqkW8U`cn5`3+bQf-R!<)bmCh3N+^S#Ch=BuOxQS$<4ltxaCRM(xEscKIFr-Hws zbHNwxN-wD4!|4To_(*yo5I&mz5iJ4t=$ITGm!mhhTfddKJ6?XyLx8CL)yfV02l&SK AApigX literal 0 HcmV?d00001 diff --git a/src/backend/HighSchoolGenerator.java b/src/backend/HighSchoolGenerator.java new file mode 100644 index 0000000..36a825f --- /dev/null +++ b/src/backend/HighSchoolGenerator.java @@ -0,0 +1,88 @@ +package backend; + +public class HighSchoolGenerator extends QuestionGenerator { + + private static final String[] BASIC_OPERATORS = {"+", "-", "*", "/"}; + private static final String[] TRIG_FUNCTIONS = {"sin", "cos", "tan"}; + + public HighSchoolGenerator() { + super("高中"); + } + + @Override + public String generateQuestion() { + String question; + do { + question = generateSingleQuestion(); + } while (isDuplicate(question) || !question.matches(".*(sin|cos|tan)\\(.*\\).*")); // 确保包含三角函数 + + addToGenerated(question); + return question; + } + + private String generateSingleQuestion() { + int operatorCount = generateOperatorCount(); + StringBuilder questionBuilder = new StringBuilder(); + boolean hasTrigFunction; + + // 生成第一个操作数或三角函数 + hasTrigFunction = processFirstOperandOrTrig(questionBuilder); + + // 生成操作符和操作数 + processOperatorsAndOperands(questionBuilder, operatorCount, hasTrigFunction); + + return questionBuilder.toString(); + } + + private boolean processFirstOperandOrTrig(StringBuilder builder) { + if (random.nextBoolean()) { + // 使用三角函数 + String trigFunction = TRIG_FUNCTIONS[random.nextInt(TRIG_FUNCTIONS.length)]; + int angle = random.nextInt(360) + 1; // 1-360度 + builder.append(trigFunction).append("(").append(angle).append("°)"); + return true; + } else { + // 使用普通数字 + builder.append(generateOperand()); + return false; + } + } + + private void processOperatorsAndOperands(StringBuilder builder, int operatorCount, + boolean hasTrigFunction) { + for (int i = 0; i < operatorCount; i++) { + String operator = BASIC_OPERATORS[random.nextInt(BASIC_OPERATORS.length)]; + + // 决定下一个操作数是普通数字还是三角函数 + if (!hasTrigFunction && i == operatorCount - 1) { + // 确保至少有一个三角函数 + appendTrigFunction(builder, operator); + hasTrigFunction = true; + } else if (random.nextBoolean()) { + // 使用三角函数 + appendTrigFunction(builder, operator); + hasTrigFunction = true; + } else { + // 使用普通数字 + appendBasicOperand(builder, operator, operatorCount); + } + } + } + + private void appendTrigFunction(StringBuilder builder, String operator) { + String trigFunction = TRIG_FUNCTIONS[random.nextInt(TRIG_FUNCTIONS.length)]; + int angle = random.nextInt(360) + 1; + builder.append(" ").append(operator).append(" ").append(trigFunction).append("(").append(angle) + .append("°)"); + } + + private void appendBasicOperand(StringBuilder builder, String operator, int operatorCount) { + int operand = generateOperand(); + // 随机决定是否加括号 + if (random.nextBoolean() && operatorCount > 1) { + builder.insert(0, "(").append(" ").append(operator).append(" ").append(operand).append(")"); + } else { + builder.append(" ").append(operator).append(" ").append(operand); + } + } +} \ No newline at end of file diff --git a/src/backend/MathCalculator.class b/src/backend/MathCalculator.class new file mode 100644 index 0000000000000000000000000000000000000000..0967067644e3f43a1ed9bc46b499e5d3b98ff26a GIT binary patch literal 6053 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 E2dH_)x&QzG literal 0 HcmV?d00001 diff --git a/src/backend/MathCalculator.java b/src/backend/MathCalculator.java new file mode 100644 index 0000000..8e871b1 --- /dev/null +++ b/src/backend/MathCalculator.java @@ -0,0 +1,318 @@ +package backend; + +import java.util.Stack; + +public class MathCalculator { + + /** + * 计算数学表达式的结果 + * 支持基本运算:+、-、*、/ + */ + public static double calculate(String expression) { + try { + // 移除空格 + expression = expression.replaceAll("\\s+", ""); + + System.out.println("开始计算表达式: " + expression); + + // 使用双栈法计算表达式 + Stack numbers = new Stack<>(); + Stack operators = new Stack<>(); + + for (int i = 0; i < expression.length(); i++) { + char ch = expression.charAt(i); + + if (Character.isDigit(ch)) { + // 处理多位数 + StringBuilder numBuilder = new StringBuilder(); + while (i < expression.length() && (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) { + numBuilder.append(expression.charAt(i)); + i++; + } + i--; + double num = Double.parseDouble(numBuilder.toString()); + numbers.push(num); + System.out.println("压入数字: " + num); + } else if (ch == '(') { + operators.push(ch); + System.out.println("压入左括号"); + } else if (ch == ')') { + while (!operators.isEmpty() && operators.peek() != '(') { + char op = operators.pop(); + double b = numbers.pop(); + double a = numbers.pop(); + double result = applyOperation(op, b, a); + numbers.push(result); + System.out.println("计算: " + a + " " + op + " " + b + " = " + result); + } + if (!operators.isEmpty()) { + operators.pop(); // 移除'(' + System.out.println("弹出左括号"); + } + } else if (isOperator(ch)) { + while (!operators.isEmpty() && hasPrecedence(ch, operators.peek())) { + char op = operators.pop(); + double b = numbers.pop(); + double a = numbers.pop(); + double result = applyOperation(op, b, a); + numbers.push(result); + System.out.println("计算: " + a + " " + op + " " + b + " = " + result); + } + operators.push(ch); + System.out.println("压入运算符: " + ch); + } + } + + while (!operators.isEmpty()) { + char op = operators.pop(); + double b = numbers.pop(); + double a = numbers.pop(); + double result = applyOperation(op, b, a); + numbers.push(result); + System.out.println("计算: " + a + " " + op + " " + b + " = " + result); + } + + double finalResult = numbers.pop(); + System.out.println("最终计算结果: " + finalResult); + return finalResult; + } catch (Exception e) { + // 如果计算失败,返回一个随机值作为简化处理 + return Math.random() * 100 + 1; + } + } + + private static boolean isOperator(char ch) { + return ch == '+' || ch == '-' || ch == '*' || ch == '/'; + } + + private static boolean hasPrecedence(char op1, char op2) { + // 如果是左括号,优先级最低,返回false + if (op2 == '(' || op2 == ')') { + return false; + } + // 乘法和除法的优先级高于加法和减法 + // 当op1是加减,op2是乘除时,op2优先级更高,应该先计算op2,所以返回true + if ((op1 == '+' || op1 == '-') && (op2 == '*' || op2 == '/')) { + return true; + } + // 当op1和op2都是加减或都是乘除时,优先级相同,按照从左到右的顺序计算 + // 所以当当前运算符op1的优先级不高于栈顶运算符op2时,返回true + // 对于相同优先级的运算符(+, - 或 *, /),应该从左到右计算,即返回true + if (((op1 == '+' || op1 == '-') && (op2 == '+' || op2 == '-')) || + ((op1 == '*' || op1 == '/') && (op2 == '*' || op2 == '/'))) { + return true; + } + return false; + } + + private static double applyOperation(char operator, double b, double a) { + // 注意:在双栈法中,操作数出栈顺序是后入先出,所以a是第一个操作数,b是第二个操作数 + switch (operator) { + case '+': return a + b; + case '-': return a - b; + case '*': return a * b; + case '/': + if (b == 0) throw new ArithmeticException("除零错误"); + return a / b; + default: throw new IllegalArgumentException("无效操作符: " + operator); + } + } + + /** + * 简化计算:正确处理基本表达式、平方、平方根和三角函数 + */ + public static String calculateSimple(String expression) { + try { + // 移除空格 + expression = expression.replaceAll("\\s+", ""); + + // 将全角括号转换为半角括号 + expression = expression.replace('(', '(').replace(')', ')'); + + // 处理平方符号(²) - 转换为标准格式并计算 + expression = preprocessSquares(expression); + + // 处理平方根符号(√) - 转换为标准格式并计算 + expression = preprocessSquareRoots(expression); + + // 处理三角函数 + expression = preprocessTrigonometric(expression); + + // 计算最终结果 + double result = calculate(expression); + System.out.println("Expression: " + expression + ", Result: " + result); + return String.valueOf((int) Math.round(result)); // 返回整数结果 + + } catch (Exception e) { + // 记录错误信息以便调试 + e.printStackTrace(); + // 如果计算失败,返回一个随机值 + return String.valueOf((int) (Math.random() * 100 + 1)); + } + } + + /** + * 处理平方符号(²),将x²转换为x*x + */ + private static String preprocessSquares(String expression) { + StringBuilder result = new StringBuilder(); + int i = 0; + + while (i < expression.length()) { + if (expression.charAt(i) == '²' && i > 0) { + // 找到平方符号,需要回溯找到前面的完整数字 + int start = i - 1; + // 处理负数情况 + if (start > 0 && expression.charAt(start - 1) == '-') { + start--; + } + // 继续向前找完整数字 + while (start > 0 && (Character.isDigit(expression.charAt(start - 1)) || expression.charAt(start - 1) == '.')) { + start--; + } + // 提取数字字符串 + String numStr = expression.substring(start, i); + // 删除result中对应的数字部分 + result.delete(result.length() - (i - start), result.length()); + // 添加平方计算表达式 + result.append(numStr).append("*").append(numStr); + i++; + } else { + result.append(expression.charAt(i)); + i++; + } + } + + return result.toString(); + } + + /** + * 处理平方根符号(√),计算根号内的表达式 + */ + private static String preprocessSquareRoots(String expression) { + StringBuilder result = new StringBuilder(expression); + int sqrtIndex = result.indexOf("√"); + + while (sqrtIndex != -1) { + int endIndex = findMatchingEnd(result.toString(), sqrtIndex + 1); + if (endIndex == -1) { + // 如果没有匹配的结束位置,就取根号后第一个非数字字符前的部分 + endIndex = sqrtIndex + 1; + while (endIndex < result.length() && (Character.isDigit(result.charAt(endIndex)) || result.charAt(endIndex) == '.')) { + endIndex++; + } + } + + // 提取根号内的内容 + String insideSqrt = result.substring(sqrtIndex + 1, endIndex); + // 计算平方根 + double sqrtValue = Math.sqrt(Double.parseDouble(insideSqrt)); + // 替换根号表达式为计算结果 + result.replace(sqrtIndex, endIndex, String.valueOf(sqrtValue)); + // 继续寻找下一个根号符号 + sqrtIndex = result.indexOf("√"); + } + + return result.toString(); + } + + /** + * 处理三角函数 + */ + private static String preprocessTrigonometric(String expression) { + String[] functions = {"sin", "cos", "tan"}; + StringBuilder result = new StringBuilder(expression); + + for (String func : functions) { + int funcIndex = result.indexOf(func); + while (funcIndex != -1) { + if (funcIndex + func.length() < result.length() && result.charAt(funcIndex + func.length()) == '(') { + int closeParenIndex = findMatchingParenthesis(result.toString(), funcIndex + func.length()); + if (closeParenIndex != -1) { + String angleStr = result.substring(funcIndex + func.length() + 1, closeParenIndex); + try { + // 尝试先计算括号内的表达式 + String angleResult = calculateSimple(angleStr); + double angle = Double.parseDouble(angleResult); + double radians = Math.toRadians(angle); + double trigValue = 0; + + switch (func) { + case "sin": trigValue = Math.sin(radians); break; + case "cos": trigValue = Math.cos(radians); break; + case "tan": trigValue = Math.tan(radians); break; + } + + result.replace(funcIndex, closeParenIndex + 1, String.valueOf(trigValue)); + } catch (Exception e) { + // 记录错误信息以便调试 + System.out.println("三角函数计算错误: " + e.getMessage()); + // 如果计算失败,返回一个默认值 + result.replace(funcIndex, closeParenIndex + 1, "1"); + } + } + } + // 继续寻找下一个函数 + funcIndex = result.indexOf(func, funcIndex + 1); + } + } + + return result.toString(); + } + + /** + * 提取数字(包括小数点) + */ + private static String extractNumber(String expression, int startIndex) { + StringBuilder numBuilder = new StringBuilder(); + int i = startIndex; + + // 处理负号 + if (i < expression.length() && expression.charAt(i) == '-') { + numBuilder.append('-'); + i++; + } + + // 处理数字和小数点 + while (i < expression.length() && (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) { + numBuilder.append(expression.charAt(i)); + i++; + } + + return numBuilder.toString(); + } + + /** + * 寻找匹配的括号 + */ + private static int findMatchingParenthesis(String expression, int openParenIndex) { + int count = 1; + for (int i = openParenIndex + 1; i < expression.length(); i++) { + if (expression.charAt(i) == '(') { + count++; + } else if (expression.charAt(i) == ')') { + count--; + if (count == 0) { + return i; + } + } + } + return -1; // 没有找到匹配的括号 + } + + /** + * 寻找表达式的结束位置 + */ + private static int findMatchingEnd(String expression, int startIndex) { + // 如果下一个字符是括号,寻找匹配的括号 + if (startIndex < expression.length() && expression.charAt(startIndex) == '(') { + return findMatchingParenthesis(expression, startIndex); + } + // 否则,找到第一个非数字、非小数点的字符 + int i = startIndex; + while (i < expression.length() && (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) { + i++; + } + return i; + } +} \ No newline at end of file diff --git a/src/backend/MiddleSchoolGenerator.class b/src/backend/MiddleSchoolGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..cad383e4596e7f458fcbc7e0181b933c8b6b8e77 GIT binary patch literal 2491 zcmb7EOK%fb6#m8@+hb=E5<3qPCp16-Kk@)dp(Sa+c~L_ikVz8~%41?r!+nZ3;SAGILp@(3g(SGX)Z&?m_2_ zoi7wqR@OaVl(6F^0p`UW8?uIN6qb0y^#BrDV(Ik!me*-xcb|zmGeCb6**j0u_Qb!nak6t9TzbBzPXKs`vmOs`v;Wi{%q`9Z^-h zfj3ngMW2d(yrm+BxCE(_-PlPp#h8ju@fii`>o@YDI?1n_UxHsRTWEm|Xjs|aEt4Uk zb=|(+%TE&}p=on1`b6n!%;IoqIcH`#0x$5Imk?Oe?aXC^U0A1Qv(tGuu))4=S|5Y; zx)0?`mhH@4ngjQoRm_26=PQ%Tx{1liFL8n^eO0o}TogE?hA(cAq%m*O!->MG5UZu&JZhhf+fKRw;Xl zzu&ZqMu9|nV(Cq8eJ@7hqq&!lG2lU)=n+b7qP_SJLK5xBwVS)$^pN-uB*M}fJl|6! z7}pRD!r4!W^NaoNfs@n=OGjc2-myfo40+|rU&*kq4FByXe+oq)d5;brCrlVJ<$Czg zfM!GxMI#PS;}xhLF<5R~jgCGV_9edkDaBa{;~}rr@jQS5 z;t!L7xUmZFV;l(J_0S_S@`yLUW%d=h2k0^8JuElL?cZP|!5?qB{ix4LfWOt}Fk3+_ zC6W;w6UkOzlYb4Z^1vO`R(9=cXuBy(cb?oAg{<(~%OZj=Mi70>O+T|VK!(SNf1H{@ zjL>$9tKirO-(z^Ce-I~6srGLKACa3!JUTAdX*Y|>juo^;+vV)nh-^sXy;VdjX^lE* zebx+hJPf2!L%=A;9Y{7pln8v$T!fo+5{)pvAjuii*Td|q8fC702Or=62*lI3r)C+Q9E zDB~4}1DmpnOec42ot6rj=WJD6D#LM}<8gtLa0#PWaCpe@9E`B72A+;-+G$;&JMas3 w&UwSTv^hDvTbuKRU)ARP;XT?9i25p~h%mKK(I75eJODCAAJI=E2><{9 literal 0 HcmV?d00001 diff --git a/src/backend/MiddleSchoolGenerator.java b/src/backend/MiddleSchoolGenerator.java new file mode 100644 index 0000000..f259090 --- /dev/null +++ b/src/backend/MiddleSchoolGenerator.java @@ -0,0 +1,99 @@ +package backend; + +public class MiddleSchoolGenerator extends QuestionGenerator { + + private static final String[] BASIC_OPERATORS = {"+", "-", "*", "/"}; + private static final String[] ADVANCED_OPERATORS = {"²", "√"}; + + public MiddleSchoolGenerator() { + super("初中"); + } + + @Override + public String generateQuestion() { + String question; + do { + question = generateSingleQuestion(); + } while (isDuplicate(question) || !question.matches(".*[²√].*")); // 确保包含高级运算符 + + addToGenerated(question); + return question; + } + + private String generateSingleQuestion() { + int operatorCount = generateOperatorCount(); + StringBuilder questionBuilder = new StringBuilder(); + boolean hasAdvancedOperator; + + // 生成第一个操作数 + int firstOperand = generateOperand(); + + // 处理第一个操作数(可能使用高级运算符) + hasAdvancedOperator = processFirstOperand(questionBuilder, firstOperand); + + // 生成操作符和操作数 + processOperatorsAndOperands(questionBuilder, operatorCount, hasAdvancedOperator); + + return questionBuilder.toString(); + } + + private boolean processFirstOperand(StringBuilder builder, int operand) { + // 随机决定第一个操作数是否使用高级运算符 + if (random.nextBoolean()) { + String advancedOp = ADVANCED_OPERATORS[random.nextInt(ADVANCED_OPERATORS.length)]; + if (advancedOp.equals("²")) { + builder.append(operand).append("²"); + } else { + builder.append("√").append(operand); + } + return true; + } else { + builder.append(operand); + return false; + } + } + + private void processOperatorsAndOperands(StringBuilder builder, int operatorCount, + boolean hasAdvancedOperator) { + for (int i = 0; i < operatorCount; i++) { + String operator = determineOperator(i, operatorCount, hasAdvancedOperator); + int operand = generateOperand(); + + if (operator.equals("²") || operator.equals("√")) { + processAdvancedOperator(builder, operator, operand); + hasAdvancedOperator = true; + } else { + processBasicOperator(builder, operator, operand, operatorCount); + } + } + } + + private String determineOperator(int currentIndex, int totalOperators, + boolean hasAdvancedOperator) { + if (!hasAdvancedOperator && currentIndex == totalOperators - 1) { + // 确保至少有一个高级运算符 + return ADVANCED_OPERATORS[random.nextInt(ADVANCED_OPERATORS.length)]; + } else { + return BASIC_OPERATORS[random.nextInt(BASIC_OPERATORS.length)]; + } + } + + private void processAdvancedOperator(StringBuilder builder, String operator, int operand) { + String basicOp = BASIC_OPERATORS[random.nextInt(BASIC_OPERATORS.length)]; + if (operator.equals("²")) { + builder.append(" ").append(basicOp).append(" ").append(operand).append("²"); + } else { + builder.append(" ").append(basicOp).append(" √").append(operand); + } + } + + private void processBasicOperator(StringBuilder builder, String operator, int operand, + int operatorCount) { + // 随机决定是否加括号 + if (random.nextBoolean() && operatorCount > 1) { + builder.insert(0, "(").append(" ").append(operator).append(" ").append(operand).append(")"); + } else { + builder.append(" ").append(operator).append(" ").append(operand); + } + } +} \ No newline at end of file diff --git a/src/backend/PrimarySchoolGenerator.class b/src/backend/PrimarySchoolGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..8321f21de1878f8e6ade12ab5dfc7d923d574f4e GIT binary patch literal 1350 zcmaJ>TTc^F5dIFm>{6GDi-2gAi;AUKii)U+H!flo0a+BQzAWtqSIcg*Y>htnr0+iK zi-|G%q6w&}iAH~b|AQZ)iQ;$K;tjOT&Y3gwojEh#%_#e4?Uy1_}97LZKhg9@S-(eL;n?&ArOdlT^0xqyID)1 zZrpaPiNeC1<;|FLIa=!HEywaq-?~_^7Jb`w1h&MwHi}qd_-5wT1v6hdS;{2A^aRww zOs;ZS#Bd8<#yVrm(A%dxd%^VX8krlen;R?N<9ae=y0^YRznR#;s6pWzo(TWgFi?LCMLW7NE|Np={-D7pkZMN?aH3P_GH{1b5@{?jrQZtFv-#=w4#lRcNCeKrLk*La0X#8qmalE1J=Zt>`CDqh#tf zIeJ9<6PErAyYUj8SjJv52F3_smDrF%hfuJG6n?{X1)bsxI=<4Y61m24A3t027gauD lOFE!88tI_kWTZoSvyl$#Ta7nRe-R%p^&> 1) { + questionBuilder.insert(0, "(").append(" " + operator + " " + operand + ")"); + } else { + questionBuilder.append(" " + operator + " " + operand); + } + } + + question = questionBuilder.toString(); + + } while (isDuplicate(question)); + + addToGenerated(question); + return question; + } +} \ No newline at end of file diff --git a/src/backend/Question.class b/src/backend/Question.class new file mode 100644 index 0000000000000000000000000000000000000000..d30a80479420bac994a7b777b3318872411c1524 GIT binary patch literal 1345 zcmZ`&O;giQ6g@9Vn>3`fl+OZH`~cGgT0lfCMMP$pVFqNJGA?vuS|SD^g)~)mZu}E$ z-MDdqGg4+;@B`fWZ(O1Gr4$OSlbQQ&-aGHSd(M6D*V)fg05f=|B8GbrbVBDL&4Zy* zFjb6WLP3U)Sso@8OevV=>3tq%WIT{D%b@L7?TYV|-3|M|XGpAWTOX|Kj^%D;3%=*L zTXPI@d5c<2wEmhMe9CZ^O_EO21@~mdcwpLu}P? z?dR3qqU~*1#T}Z7Z`uA*{lxX4FoqsI-Acj`E%;XH{hGBG?3B?>bhP=!70D{i)Oko? zubysGAVb8doFBih${TC9qm|@}w1$~#pGKf0wNYg_7ZcwZ3{i?MS7}unyzHN@1B0KJ;eX#!aKenxqwUuhI z5?nQgK7D1SZ6}pd+4U`l-@&0~Gnd|3UV&<`-I5)g*Q>W9z%6JZH*iV0XT9;_-F;2h?^B96Lehkr=IEM6{<{1Vk_8~-3A!LL;{_;AAQ=`=+ z2G5y3(Q24b3&szKtLEvv_#`#_1(RlKIwu)P=>(x%h}NWJsD|(%x{{KZloE0cN)3^t z&m#N!_jf`)A_^fmbTFWc^JZh1MCg>wlBhftK18Tah-Xk`3^Qi_p#m9F7saILj}e9+ zC~(TUj&LwJMDYf0QsfRaL&fV*4jLg->rj&_6KPA#FiFiC7c)*;zFqLE1OP^<@G;8h zf}U?&4Eg~Kn2C-Wv_r(rAX7qbYKR`0m!e4&F&N~sNv=->LPap%uDfm3N!LrBBIqb_ H2pIbZE!7f& literal 0 HcmV?d00001 diff --git a/src/backend/Question.java b/src/backend/Question.java new file mode 100644 index 0000000..fdb337b --- /dev/null +++ b/src/backend/Question.java @@ -0,0 +1,51 @@ +package backend; + +public class Question { + private String questionText; + private String[] options; + private String correctAnswer; + + public Question(String questionText, String[] options, String correctAnswer) { + this.questionText = questionText; + this.options = options; + this.correctAnswer = correctAnswer; + } + + // 从所有选项中找出正确的选项标签(A、B、C、D) + public String getCorrectOption() { + for (String option : options) { + String answerPart = extractAnswerFromOption(option); + if (correctAnswer.equals(answerPart)) { + // 提取选项标签(如"A") + return option.substring(0, option.indexOf(".")); + } + } + return "A"; // 默认返回A(不应该发生) + } + + public String getQuestionText() { + return questionText; + } + + public String[] getOptions() { + return options; + } + + public boolean isCorrect(String answer) { + // 比较用户选择的答案和正确答案 + // 用户选择的是完整的选项文本(如"A. 42"),我们需要提取答案部分 + String userAnswer = extractAnswerFromOption(answer); + return correctAnswer.equals(userAnswer); + } + + private String extractAnswerFromOption(String option) { + if (option.contains(". ")) { + return option.substring(option.indexOf(". ") + 2); + } + return option; + } + + public String getCorrectAnswer() { + return correctAnswer; + } +} \ No newline at end of file diff --git a/src/backend/QuestionGenerator.class b/src/backend/QuestionGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..0868042e4018b3de6c0f52a47478c6e0bb30f09b GIT binary patch literal 2699 zcma)8?N3uz82{b&w)axXi$%~XD2Ps=2=iq)L39ENI25(`{>G)fLb0~B+%nyq+Y**( zw!~%0+;lOSBpb_|ri&B77yP(o-?qO&6hAC}Sh8ehj{VMU%ZspVCFh*yobx=-dCu?o zJ?HZC-*-j;tiihuEJKrmW(O>2O2blYRq(n4w#1}GtZWl|+tbmC9irRmKpu9f@QSfb z#cmaQRP0r;PsM%*4&b2Z4kIT)) zyn)V;X2hbpgyQMD>l4$U7V50@8qq+gvt|P=(tWXLR1X<#rz5mUo;_8Gk*yDe0>%aj zxg|4wlj;XKRMf2z1|RZEu$GiI)6ukbBOI*{$3ljL zyjj;*GRiL-QTqrbWy**qQoG3x56XjUp zL>LhTK_^Zk>O_y|46G&%M&lPouYc`C3{f#Vcje*GbqY_eeQpXxV>hlozVX?^p%0&2 z>3@8=@6o4ms*hd$W^80+^xlQ1_byA=_+L8bMn8Ig^y6!z!#5vY9Da1}Vp8~W`02g# zriXR^)5F7|^J4>_jDB#RVI*u7^g8hdPC0QJZ>F%B!gMFz!Wpr-S;9OMpedVmu|UwT z6N(0GcH(WEm0+nTbmARUOOQ6P)v?vHHAsL2cLzzLhy0Z@@-yw^){bu7N8)EpCWOh5 zkTVNc%;I6SDIxuo(JabECPP?FsxKTew19XKrd4Rdc}*!R!^+B_Vi%L0s8zduGNuJ- zk)VQkQ~K09b>>!$Xwe?sJYt%|wgQ<^A4$~dOf|xZ)8!&QsLS}0TOkWFQ<4RDiFp}i3p|{Y#+1{!R)I?P zFLTyJ3k%1>vfGf#?!vPFHmtWGQ?yaE--1%+xdrum>PT40K9Ao7AcOwWkOc=8AQOuz z$ykNeNv})klL?2j><7r%{z0V4?cYs!u$y~1v{*A?QIxbu6Ba5BUR0k?^;ag;i<9c< zLS32Fe?ASWur?WyFc9|?28w4G4hvp9p@;^rP5@JqG!R!12I&^;odiLA(LkWnv0Rq0 z4%O!6YB(lfE#N2su+dtLo0|f=#LX+XwSGeN4qaKfcSfkTSZY0ka9-oQiOmYfMqyov z^!L_sEJ9A^ZjQAcSH>VRFQLdyIRJwt0pycSCu>5ONdfDff|AExTo_$17Laa&1aK4)V@&w!_R{l_DmCn0E=z zf?TD&%07TO6#P~8T>BN++>mTSL_g@ed<6|gf+ zISyixx6jVwes$X>5$t9~0Go(K9jl}s^RWes(1>DeWd&|0o~>NnffnrIpO^n4?M4^& z@@ujWXQ|VNL%4v$_yR}JkE5h%J8p7q7=Ao}j-PN0zj6E>0W&L&NE;^;?Fu$AH45q^ z1$=Q~d(JdVzQw&iQTi7w(le}l4ppc>V+t+e6;C9S0r4g0H7XY-804WWeuAhb zc{mDEmADqiU4?O1QQWm8?kb){VF9@goIoK$ym^=bM)(??Bmz-0Y6Z4JGDuKp3l%MB F{0CN0f;<2K literal 0 HcmV?d00001 diff --git a/src/backend/QuestionGenerator.java b/src/backend/QuestionGenerator.java new file mode 100644 index 0000000..ef481b6 --- /dev/null +++ b/src/backend/QuestionGenerator.java @@ -0,0 +1,117 @@ +package backend; + +import java.util.HashSet; +import java.util.Random; +import java.util.Set; + +public abstract class QuestionGenerator { + + protected Random random = new Random(); + protected Set generatedQuestions = new HashSet<>(); + protected String currentType; + + public QuestionGenerator(String type) { + this.currentType = type; + } + + // 生成操作数 + protected int generateOperand() { + return random.nextInt(100) + 1; // 1-100 + } + + // 生成操作符数量 + protected int generateOperatorCount() { + return random.nextInt(5); // 1-5个操作符 + } + + protected int generateOperatorCount1() { + return random.nextInt(4) + 1; // 2-5个操作符 + } + + // 检查题目是否重复 + protected boolean isDuplicate(String question) { + return generatedQuestions.contains(question); + } + + // 添加题目到已生成集合 + protected void addToGenerated(String question) { + generatedQuestions.add(question); + } + + // 抽象方法:生成题目 + public abstract String generateQuestion(); + + // 获取当前类型 + public String getCurrentType() { + return currentType; + } + + // 获取题目描述前缀(根据不同级别返回不同描述) + public String getQuestionDescription() { + switch (currentType) { + case "小学": + return "计算下面的数学表达式:"; + case "初中": + return "计算下面的数学表达式(包含平方或平方根):"; + case "高中": + return "计算下面的数学表达式(包含三角函数):"; + default: + return "计算下面的数学表达式:"; + } + } + + // 生成选择题选项 + public String[] generateOptions(String correctAnswer) { + Set usedAnswers = new HashSet<>(); + int correctNum = Integer.parseInt(correctAnswer); + usedAnswers.add(correctNum); + + // 存储所有答案(不带标签) + String[] allAnswers = new String[4]; + allAnswers[0] = correctAnswer; + + // 生成三个不同的错误答案 + for (int i = 1; i < 4; i++) { + int wrongAnswer; + do { + // 生成一个与正确答案有一定差距的随机数 + int offset = random.nextInt(10) + 1; // 1-10的差距 + if (random.nextBoolean()) { + wrongAnswer = correctNum + offset; + } else { + wrongAnswer = correctNum - offset; + if (wrongAnswer <= 0) wrongAnswer = 1; // 确保答案为正数 + } + } while (usedAnswers.contains(wrongAnswer)); // 确保不重复 + + usedAnswers.add(wrongAnswer); + allAnswers[i] = String.valueOf(wrongAnswer); + } + + // 随机打乱答案顺序 + for (int i = 0; i < allAnswers.length; i++) { + int j = random.nextInt(allAnswers.length); + String temp = allAnswers[i]; + allAnswers[i] = allAnswers[j]; + allAnswers[j] = temp; + } + + // 添加选项标签(A、B、C、D) + String[] options = new String[4]; + for (int i = 0; i < 4; i++) { + options[i] = getOptionLabel(i) + ". " + allAnswers[i]; + } + + return options; + } + + private String getOptionLabel(int index) { + switch (index) { + case 0: return "A"; + case 1: return "B"; + case 2: return "C"; + case 3: return "D"; + default: return ""; + } + } +} \ No newline at end of file diff --git a/src/backend/User.class b/src/backend/User.class new file mode 100644 index 0000000000000000000000000000000000000000..3313e5ce37a8f177c2757258ad7af75983a1e581 GIT binary patch literal 931 zcmbVJO>fgc5Pj>|F-hE}G$}NJ(x$Xvn~z+$QT0-&1XO^OHup`kinev^%635Tt3V<` z;=m8!Mhh?JS#@tgPN?fm)s>o?HMd%F zy*@g$T^r;Z7TTzwO6M9a&VWOI(#y^ynPpPVG6_@Wz6P2U*XVXgcwT$k{|VtgnZfX% z&QS87&0zX1a|Y`>eHFOF7*Yi(m{=ms1J$g$MTR?s+$f~B7O8g#`kU}g-z0GZTmJw^zN8ue literal 0 HcmV?d00001 diff --git a/src/backend/User.java b/src/backend/User.java new file mode 100644 index 0000000..3f974b9 --- /dev/null +++ b/src/backend/User.java @@ -0,0 +1,42 @@ +package backend; + +import java.io.Serializable; + +public class User implements Serializable { + private static final long serialVersionUID = 1L; + private String email; + private String username; + private String password; + private String userType; // 小学、初中、高中 + + public User(String email, String username, String password, String userType) { + this.email = email; + this.username = username; + this.password = password; + this.userType = userType; + } + + public String getEmail() { + return email; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUserType() { + return userType; + } + + public void setUserType(String userType) { + this.userType = userType; + } +} \ No newline at end of file diff --git a/src/backend/UserManager.class b/src/backend/UserManager.class new file mode 100644 index 0000000000000000000000000000000000000000..784704ada7a5648fd663eda58012a7c44b1e90d8 GIT binary patch literal 7911 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 VHzG~OV6=?I6g~@!yy`&T`5$|o^H=}? literal 0 HcmV?d00001 diff --git a/src/backend/UserManager.java b/src/backend/UserManager.java new file mode 100644 index 0000000..91ec1c9 --- /dev/null +++ b/src/backend/UserManager.java @@ -0,0 +1,295 @@ +package backend; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import backend.EmailService; // 导入邮件服务类 +import backend.User; // 导入User类 + +public class UserManager { + private static UserManager instance; + private Map userMap; // 邮箱到用户的映射 + private Map usernameMap; // 用户名到用户的映射 + private Map verificationCodes; // 存储邮箱和验证码的映射 + private Map verificationCodeExpirationTimes; // 存储验证码过期时间 + private Random random; + private static final String REGISTRY_FILE_PATH = "registry.dat"; // 在项目根目录下的registry文件 + private static final long CODE_EXPIRATION_TIME = 5 * 60 * 1000; // 验证码有效期5分钟 + + // 私有构造函数 + private UserManager() { + userMap = new HashMap<>(); + usernameMap = new HashMap<>(); + verificationCodes = new ConcurrentHashMap<>(); // 使用线程安全的Map + verificationCodeExpirationTimes = new ConcurrentHashMap<>(); // 存储验证码过期时间 + random = new Random(); + loadUsersFromFile(); + if (userMap.isEmpty()) { + loadPresetUsers(); + } + } + + // 获取单例实例 + public static UserManager getInstance() { + if (instance == null) { + instance = new UserManager(); + } + return instance; + } + + /** + * 发送验证码到邮箱(实际发送邮件) + */ + public String sendVerificationCode(String email) { + if (!isValidEmail(email)) { + return "邮箱格式不正确"; + } + + if (userMap.containsKey(email)) { + return "该邮箱已被注册"; + } + + // 检查是否已发送过验证码且未过期 + if (verificationCodes.containsKey(email)) { + long expirationTime = verificationCodeExpirationTimes.get(email); + long currentTime = System.currentTimeMillis(); + if (currentTime < expirationTime) { + long remainingTime = (expirationTime - currentTime) / 1000; + return "验证码已发送,请在" + remainingTime + "秒后重试"; + } + } + + // 生成6位验证码 + String code = generateVerificationCode(); + verificationCodes.put(email, code); + verificationCodeExpirationTimes.put(email, System.currentTimeMillis() + CODE_EXPIRATION_TIME); + + // 发送实际邮件 + String result = EmailService.sendVerificationEmail(email, code); + + if ("success".equals(result)) { + System.out.println("验证码已成功发送到 " + email); + return "验证码已发送到您的邮箱,请注意查收"; + } else { + // 邮件发送失败,清理验证码 + verificationCodes.remove(email); + verificationCodeExpirationTimes.remove(email); + System.err.println("发送邮件失败: " + result); + return "邮件发送失败,请稍后重试。错误信息: " + result; + } + } + + /** + * 检查用户名是否已存在 + */ + public boolean isUsernameRegistered(String username) { + return usernameMap.containsKey(username); + } + + /** + * 验证验证码并注册用户 + */ + public String registerUser(String email, String username, String verificationCode, String password, String confirmPassword) { + if (!isValidEmail(email)) { + return "邮箱格式不正确"; + } + + if (userMap.containsKey(email)) { + return "该邮箱已被注册"; + } + + if (!verificationCodes.containsKey(email)) { + return "请先获取验证码"; + } + + // 检查验证码是否过期 + long expirationTime = verificationCodeExpirationTimes.get(email); + long currentTime = System.currentTimeMillis(); + if (currentTime > expirationTime) { + verificationCodes.remove(email); + verificationCodeExpirationTimes.remove(email); + return "验证码已过期,请重新获取"; + } + + if (!verificationCodes.get(email).equals(verificationCode)) { + return "验证码错误"; + } + + // 验证用户名(在验证码之后验证) + if (username == null || username.isEmpty()) { + return "用户名不能为空"; + } + + // 检查用户名格式(3-20位字母、数字或下划线) + if (!username.matches("^[a-zA-Z0-9_]{3,20}$")) { + return "用户名必须为3-20位字母、数字或下划线组合"; + } + + // 检查用户名是否已存在 + if (usernameMap.containsKey(username)) { + return "该用户名已被注册"; + } + + if (!password.equals(confirmPassword)) { + return "两次输入的密码不一致"; + } + + if (!isValidPassword(password)) { + return "密码必须为6-10位,且包含大小写字母和数字"; + } + + // 注册成功 + User user = new User(email, username, password, "未设置"); // 用户类型在设置密码时确定 + userMap.put(email, user); + usernameMap.put(username, user); + saveUsersToFile(); + verificationCodes.remove(email); // 清除验证码 + + return "注册成功"; + } + + /** + * 用户认证(支持邮箱或用户名) + */ + public User authenticate(String identifier, String password) { + // 尝试通过邮箱认证 + User user = userMap.get(identifier); + if (user != null && user.getPassword().equals(password)) { + return user; + } + + // 尝试通过用户名认证 + user = usernameMap.get(identifier); + if (user != null && user.getPassword().equals(password)) { + return user; + } + + return null; + } + + /** + * 修改密码 + */ + public String changePassword(String email, String oldPassword, String newPassword, String confirmPassword) { + User user = userMap.get(email); + if (user == null) { + return "用户不存在"; + } + + if (!user.getPassword().equals(oldPassword)) { + return "原密码错误"; + } + + if (!newPassword.equals(confirmPassword)) { + return "两次输入的新密码不一致"; + } + + if (!isValidPassword(newPassword)) { + return "新密码必须为6-10位,且包含大小写字母和数字"; + } + + user.setPassword(newPassword); + saveUsersToFile(); // 实时保存用户信息 + return "密码修改成功"; + } + + /** + * 设置用户类型 + */ + public void setUserType(String email, String userType) { + User user = userMap.get(email); + if (user != null) { + user.setUserType(userType); + saveUsersToFile(); // 实时保存用户信息 + } + } + + /** + * 获取验证码(用于显示给用户) + */ + public String getVerificationCode(String email) { + return verificationCodes.get(email); + } + + /** + * 检查邮箱是否已注册 + */ + public boolean isEmailRegistered(String email) { + return userMap.containsKey(email); + } + + private String generateVerificationCode() { + return String.format("%06d", random.nextInt(1000000)); + } + + private boolean isValidEmail(String email) { + return email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"); + } + + private boolean isValidPassword(String password) { + // 密码6-10位,必须包含大小写字母和数字 + return password.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{6,10}$"); + } + + private void loadPresetUsers() { + // 预设一些测试用户 + User testUser = new User("test@example.com", "testuser", "Test123", "小学"); + User studentUser = new User("student@example.com", "student", "Student123", "初中"); + User teacherUser = new User("teacher@example.com", "teacher", "Teacher123", "高中"); + + userMap.put(testUser.getEmail(), testUser); + userMap.put(studentUser.getEmail(), studentUser); + userMap.put(teacherUser.getEmail(), teacherUser); + + usernameMap.put(testUser.getUsername(), testUser); + usernameMap.put(studentUser.getUsername(), studentUser); + usernameMap.put(teacherUser.getUsername(), teacherUser); + + saveUsersToFile(); + } + + /** + * 从文件加载用户信息 + */ + @SuppressWarnings("unchecked") + private void loadUsersFromFile() { + File file = new File(REGISTRY_FILE_PATH); + if (!file.exists()) { + return; + } + + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { + Map loadedUsers = (Map) ois.readObject(); + for (User user : loadedUsers.values()) { + userMap.put(user.getEmail(), user); + usernameMap.put(user.getUsername(), user); + } + } catch (Exception e) { + System.err.println("加载用户信息失败: " + e.getMessage()); + } + } + + /** + * 保存用户信息到文件 + */ + private void saveUsersToFile() { + try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(REGISTRY_FILE_PATH))) { + oos.writeObject(userMap); + } catch (Exception e) { + System.err.println("保存用户信息失败: " + e.getMessage()); + } + } + + /** + * 设置用户类型 + */ + @Override + public String toString() { + return "UserManager{" + + "userMap.size=" + userMap.size() + + ", usernameMap.size=" + usernameMap.size() + + '}'; + } +} \ No newline at end of file diff --git a/src/frontend/ChangePasswordFrame$1.class b/src/frontend/ChangePasswordFrame$1.class new file mode 100644 index 0000000000000000000000000000000000000000..5c7041851149ef62dbd010f5826087327f426bc2 GIT binary patch literal 606 zcmaJ<+e!m55It$DYu9S){cctIP%F3Q=lSKIKx47w4$6+uWrU<(wzU%f{i(HWT;>vsh$&_h3<9~GUE zqL*-B&c=6^@63FDy?+2W#6}hw%!L@ie25t=1Xv8P9AHIY)U~87?hC9}8%8TfDv2ts zST*S;w)Rn*UV2o?&4aNV({A06FvakuMhB>b|Mdx%35={+Vvi7vS1ws@~C7s^%rR zmQDh!=CFoB4gtn;$a-rX1?D=8YVklIccB!SN{3fKRvn`3-4jIh37{k1a87w$hbg=ATMPS@?!nj-$*l4zOqztLR zYjmW#qbnK5PmvBzwd_%yk%yrQ&5^)XWi)i>Rv^=e0um@RLq!+;UW@dNY;~K>+Si5eMxz|x&`*a%G1WQA@+p`Hc_lfemQ5>qf zb26PMaIl)k8rJi0kj*1!tqr)$brjX|zCiv=DbkHDkAPxqC2P zhA~f&w7}Y^5(2X^nf^mU@h^t!q$>$s>H8G(Qff79^5Vw8!-psqEe+mhif;}sdnef1 z=C}2@)!kQ!>Kjte{9+0$If|uXIfFfs|0fH&jq0qR1v{2isiC8j®P6yRUZBE}9U!*}dP_ub*H4^Ij&I!B{(id+t5=zvrHN zwtL^pZ#{n=z*YE{h8vL7pkYTbdmYm3)X<4t(z{(|?{MHwp&wFlm(bnP+%3&L((F;O zSH(UJPVCoEf)8srfP>PzSH*oACg39~KI*{z8YbZZ86Q&dpoXbZ5AT*FLToh}C+7Cjzu;8CHEiP+;RPH6DpAr+51a8g4Rj>+UF9QdTrQ=-*r2R zCmi^+1D_GracMp)@I%r(Sxgn4lHOryo|c9hcWYRV9vM8N;*5q`^r(1N!%AV!6z1oI zp4G4#z0#x{NIP&&#d8`O@VtgTd|nLrf&=Ge{zV7AAUl82fiJ1}vI4z65;0@Gun~`& zaRtt3xMdyt9nn}zUC0c#D46EoZtO62*2FtPk=B~)b7mKFMa1mL;VCE!Mk8B8v9?Rt zdT^T&X*Fw;i9|G_U_!23n@xdw(1--ha0WIe88>4MM#N|}W3s5(2;OQ&T52|qu(@bV zFd2)Pk%Vv+jLSiBy12~(P5xj#aDEE6<^bF3qGhLgzdDz z8!vVb9C@z4Xa9xA@7D2kyrjUD6Ghg}I=+FIb$k=w((!G4N5^;Zy-R`XjEEVg-(_`; z(UGXBWn`K$ztI^@CPWpkHSv8FKhW_*{7A=-@smqs_>E>WEU;H-tYJZQ3<9d!f4+P0 zg`SI#?7P_apkRKg<7fCeV`v!Yi-w~ynfir}U*c7IXLxFLEYvch%4@V^|NSTIV=s~w zJogj6yk@g-oEJ`Z4|bo*=5Ikj$FK0~?A`0a(GGj}A@1-tndH%J zLAE<|+=QDM==~2I9Y`I}@d4bd<5tkRg^kfzVw=eS4d;D~I?19!#)NR&#NEHet2$oC z8#?;MNfpC6G)k<@nkQ$c$p&!Y-JtW|4X}Xbuj>9tD$fe6hAY5sF46z;yf( ze^T+Ljz8lsI{u2kF@$WF*rppeaH#*@6FUBme+)-=hWF3xh(C2^@a$>U%>QK`I%||7 z#v0aYCh8;cgp@z(>aNVGIYT9omCC{5upyL?Or2ntkeV#pSm`{Okd@|EBNwa$1%xcfBOMN<T5X@kfIeY`qyK-C=HsiXv8-vXajiZEKH47$4Ry zI#;YW_r zzsj6S*=&`7p-LdtoqA;O6}d$<7b*%&9K~eT`7`Itwxw+uv6WpdTv=xmj%9`-QBg#X zwQw=BH5N@qcyo@=LR&?|f~)q4h|f1#i1Os1hV46=(G^sV3bwJ9;%l3?n?YMIQKyzS zLq?s#TK@{)8m3dWXqGlwu*A|ln2fLEjhHa0RVj5_5tNY0TtEtDW^HcD-Bgo{u#P8D zJaRV`xGT5#B*wP*#6oH)Zh6bZ3)HOnY5U}hnmKRu>*N>Q?Qo@-VYY#ePB_Rbc?c>Ta=fuMR_w@l-D$A5foO- zt!!I4s-v8I(kY%a@;u&N3c_nFFMtO~vfcK;^V^KkQfh{55InI{g>BG!G8khHCmIb>)V1?y{?+BZ1!LAfo zQF+lj!ray61u0Z+s^(c|S)Fnk!5+`H$3>a3TROJpoTjg4i!d2dC*FquXs_SY}k2i&DHqo!|9r5dN zTuHx9rFW-c45rhk6>Kl#$HYu5#O3_Ln2oi>+yXb+;l(~w;}GWHBx>*!=79>}B`n~Z zYaw34)p!%v;NQ4bQLsqSuvjU@5@iaODl@Q5@nD6r9JPucJ|%!vih(*MgzJXkd; zSN5VoxetxXVXRZS(4;(u4U8?mOork$L@o2E?NVqFLfyzdF0Ka+Si;1s;LEuh3CiaSdPx53DC8forw^+G6w08v zE{%Hs(2y}B!-k*;^yT#rqm)@iqE(Z0F|S-}T>ipyXgotXMf}f5T^jK+ z*ZWgg--YoWcAE+a^Q5rh38p2-8}nru)yT8*eLi&dJE-(dD&0w?Z|7I%9asZ;3cGon zdzkfm5W`;1?&s(LqFgZ%1#h8NMZ5j4_&=y(&*607He&6%Go4fnT1ZnVBX*MF1QxnH Z#7XmLrA?$$SfW;v%CFWY{1&!;{{^@XbqD|e literal 0 HcmV?d00001 diff --git a/src/frontend/ChangePasswordFrame.java b/src/frontend/ChangePasswordFrame.java new file mode 100644 index 0000000..21963ff --- /dev/null +++ b/src/frontend/ChangePasswordFrame.java @@ -0,0 +1,137 @@ +package frontend; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import backend.UserManager; +import backend.User; + +public class ChangePasswordFrame extends JFrame { + private JPasswordField oldPasswordField; + private JPasswordField newPasswordField; + private JPasswordField confirmPasswordField; + private JButton changeButton, cancelButton; + private UserManager userManager; + private User currentUser; + private MainFrame mainFrame; + + public ChangePasswordFrame(MainFrame mainFrame, User user) { + this.mainFrame = mainFrame; + this.currentUser = user; + this.userManager = UserManager.getInstance(); + initializeUI(); + } + + private void initializeUI() { + setTitle("修改密码"); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setSize(400, 300); + setLocationRelativeTo(mainFrame); + setResizable(false); + + // 创建主面板 + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + // 标题 + JLabel titleLabel = new JLabel("修改密码", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 16)); + titleLabel.setForeground(new Color(0, 120, 215)); + + // 表单面板 + JPanel formPanel = new JPanel(new GridLayout(4, 2, 10, 15)); + + JLabel oldPasswordLabel = new JLabel("原密码:"); + oldPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + oldPasswordField = new JPasswordField(); + + JLabel newPasswordLabel = new JLabel("新密码:"); + newPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + newPasswordField = new JPasswordField(); + + JLabel confirmPasswordLabel = new JLabel("确认新密码:"); + confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + confirmPasswordField = new JPasswordField(); + + formPanel.add(oldPasswordLabel); + formPanel.add(oldPasswordField); + formPanel.add(newPasswordLabel); + formPanel.add(newPasswordField); + formPanel.add(confirmPasswordLabel); + formPanel.add(confirmPasswordField); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + changeButton = new JButton("确认修改"); + cancelButton = new JButton("取消"); + + setupButtonStyle(changeButton); + setupButtonStyle(cancelButton); + + buttonPanel.add(changeButton); + buttonPanel.add(cancelButton); + + // 添加组件到主面板 + mainPanel.add(titleLabel, BorderLayout.NORTH); + mainPanel.add(formPanel, BorderLayout.CENTER); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + add(mainPanel); + + // 添加事件监听器 + setupEventListeners(); + } + + private void setupButtonStyle(JButton button) { + button.setFont(new Font("微软雅黑", Font.BOLD, 14)); + button.setBackground(new Color(0, 120, 215)); + button.setForeground(Color.BLACK); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); + } + + private void setupEventListeners() { + changeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleChangePassword(); + } + }); + + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + + // 回车键确认修改 + confirmPasswordField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleChangePassword(); + } + }); + } + + private void handleChangePassword() { + String oldPassword = new String(oldPasswordField.getPassword()); + String newPassword = new String(newPasswordField.getPassword()); + String confirmPassword = new String(confirmPasswordField.getPassword()); + + if (oldPassword.isEmpty() || newPassword.isEmpty() || confirmPassword.isEmpty()) { + JOptionPane.showMessageDialog(this, "请填写所有字段", "输入错误", JOptionPane.WARNING_MESSAGE); + return; + } + + String result = userManager.changePassword(currentUser.getEmail(), oldPassword, newPassword, confirmPassword); + + if (result.equals("密码修改成功")) { + JOptionPane.showMessageDialog(this, result, "修改成功", JOptionPane.INFORMATION_MESSAGE); + dispose(); + } else { + JOptionPane.showMessageDialog(this, result, "修改失败", JOptionPane.ERROR_MESSAGE); + } + } +} \ No newline at end of file diff --git a/src/frontend/LoginFrame$1.class b/src/frontend/LoginFrame$1.class new file mode 100644 index 0000000000000000000000000000000000000000..358709ac6d9f87bf1ec80cfd0b9d2e0beb82dd6e GIT binary patch literal 561 zcmZuv+e!m55It$D+pg8td%aeq54D0@!8b(^u^_A$@Pa<3+i9E9W@R_k?-CTn2S31% z5+{q|OBZ%#ayU6TGs)NY$0vY2lyk^n&czt!UCdy?!J>mD2g?HErmNygU0~jCN1-;P zTb>_wlx{?FK>JKMP+FNofl_rOW9wERa~!rvpx`S_=fgphqHEdgTSNv%h5C}Bb{Gw) zB~Y&VJ^3g-`D8qLByGH-gmPl*eJkc(g~KSIhDrqfz3kY1EP0}Xei$p=IU~~zTMkz8 zSjAc%4zhXVY_$%T8yeA1Wmh17sx?K&eHq6T3ltMOeW^R%MYBhN5m+1P&{r`}ha!R5 z5z36lWQGq(AAeMqPgj$kWDroybFSC1!FMqR9xOz$Xif7KrTEXmWpsk8Os+$7EBdsaf??_|F2$WJg9jV*FY4e^UE3nwtVW4kZHR zKFW+5vb~42k3TBQXKQIsGK$EU>PDl1WqykR@Wn!uO3pMtQHEy$KBGfit?=3*etqo~ zqW*@=Gq2b=N8ZKpisLXwe)(0-j-Ri;K)fUS&-PRNnoqew7Ng9`poB5bIsOM_%y8vS I&2i@O162!rW&i*H literal 0 HcmV?d00001 diff --git a/src/frontend/LoginFrame$3.class b/src/frontend/LoginFrame$3.class new file mode 100644 index 0000000000000000000000000000000000000000..913018f0b39159ab31c0ae82e21ec2abcc9b5cf7 GIT binary patch literal 561 zcmZuvT}uK%6g{KX>Sk%#cUciWm_aMln-N451ltD`y^p&$9nsm?9qo4oMbSe)pdS_8 zSqfiV*n96B&YU^-&V2uTegQbbb^$pox|qU}iv=t@SaGoCU|nF^bX8Jo3M~1Laik6D z(DS2?(igEDP>Tu2N-J|Buv(wU*n1GjokbxDlzgS>dN^oPd@tL5i^#yJNZ(TY7{vn$ z1-9#cPmZJ~pN&T&(#AVYDd)CsSuyu68pZ)#s8nF=<-qP^$#Wg_qeSV>6`5`nI@l;; z6V)OdAD5rM%Qg^(Yc8>xhP@U+|R|!vtVu8v8 zWkwS+!>6>5e=5so>uFCi2q@t>H=EeuyO;tG7NT6Xrum98{1@OdI>Xg2uPx#>_TL~H z@5sFHik)-hT^vJ>vzYti*Em~#zVQn2f$W&=rg(=>=^~2~b22Dno^y`hK?SQ^*;8|z G`TGT0vV493 literal 0 HcmV?d00001 diff --git a/src/frontend/LoginFrame.class b/src/frontend/LoginFrame.class new file mode 100644 index 0000000000000000000000000000000000000000..1840cde69eef49ee3e9c56c9e1a0813040010e99 GIT binary patch literal 4038 zcmaJ^349b+8UIhN*-QqKECdLYvq(1^!V_;Ef$x{$L?Kl;DNl!F=$3&>pVF`hJwlvT@tlSl^r$$aVTmlxz-iI# ztcE%yR3v5PoDk2;@PY#=2R^IfyoTktAddW;$o#yDFKB4O7iDRGnR+#0vuEKo3_ zof|qM(UvsyhHU7H%(sLWg(9uNa7{-%9tkTb%Ld!aM2(D^t-)B_j1B;cI$~zD-Uu75 zW>mH{8-e@HaEoX4&@#6bEDVN&@jDdcx+>Nv$n!>8Oa;ZhVAyQvXlpj3tBmFlS9E~| zjZko#xw=k4Nz9CQwA-SZ;$4Cm=NeWs;X2AfFWq8><37=mPNTbuHW}fTkm(ca=#-{7 z6|6VfErAS_im%Wr8`GG!jXpL40W%hxK700TOXS9ABus`a9*a$F)Mzsoh{yq4wcxa- zNJlha)&@n-;vwWL36YK{T6KhF*oamYU)AvnzNVuErjE^MRWSY1xf8vo9_>Bx^yP!6 zIP(9}^GEy7cV9{+rc_PoJDlo0e^AHQ@eKvetgu@O>G&qTrQ_T9j*jo*dpf?4A6yMw zVT8>P!z8;sMrYhp!~8I#KBFtr5f{a{cO!nN;-Zcp;m10Df}dW^!)G)Lj|u-rTVe7u zI(tw|E#1fH_FgzibFMtG^GfP5!TemuFYrtHe-P-6gd$OS^{S3v;WY+z@YP+>V9SuA zzgAFsWyi_BlRcik1II7#PWJBGJ6|@^Ib&qcwKbvRUc6m_+S|RW@96gV3dU#QvI;`S zeRzkCOX$s1p*9rhv{gv24hk74hk}798E9tOUw-a*@2)*M0%+E8Kj^mnhDbENNjP8T zdoOcU>L#D5Bdcwa9({OCM?bFU_>DxcY?$PmUM;3FV!y@ju7j9!o%Ojoevj96`~k1C zz%1{E4EBzuc$6(f#~<-0;r(YFf5BfBOzZDX_FvfFyYtx9-tK*TkL^hYHjOxNxWBtw z!PqRnE^m(qBVoxr9e=~$b^HVW)bTILc;}#L^+qt9&f<;uw~qga<&&=#d3pCCTjZ6r zB;9s*?^8YfXO1zTgZ%7Ljv6A`;>&;yz8oq|s-D@f2+AY}+geep8Ltb+;zl^Y(jM!o zxK5jMumi0M#-;wt>^|km@U7J`#u2i_+-P)!;@(gsW-f0xZ7taqU3Jn^R3e(#0GSo8 zI%}njE51m;GN#cCao%FCiijdsFSH8T8)<8ggqcp36rDR7%~)`o-B$Bl>xJ`#47{Dj zX$!4A<(W~l`$DY|h)1GbEVV$?G~(vcw)S|J{Yt@9YDfLD+HtehPSRb4eZP=;4B#ha zckXPcs5fR9ms$1?*qP}|+cV-TySF$qY?BqsqykYh@8)2KrES3v`g3-+2t1F+f^4J2}cw!Pa z&ir~7dLpV>1sl165 z^2VEw962HQlq22Wn%Ty!nGM{Ud0KOJKw*n)<+zE|JmMA3CGI40-RF>Z7WsTE=;4}z zAV(+i%DDL5*Z*#~Ijw-<|Zl{`n!(EVo<}OwCl`1n6C~_+a=}YA%TfDw@Lnje4F27@@uddOi5zu{Jio!n?r8NNSlK@g=zjIZajjRi2kX#o8+O8BDhx%JTGiP^Y0=KMXRBj!L$|Jn6 z17lFY&yOOELmA33p1;YMh&lZ5Sj^1u5pxf28k634 z7$e-ydS^raIjra*B)^iYD;ug_#u#@3jVUzwnIWr~UaOyBbvV~J*X9b80&=XHo5h%d z<`q$e@o1;n4=_ei#;Aj)Z^50|%1^g0et11dg&#tM=uyb~FI+0x`Cs@3rl@$pL3X^k zr){V}uH_?E;yUqwpYqoaxj-Wrco;gimpBkZJiToqXK}5xg`6RHfwia++A%Ctxdux9 XTgWE|ozxk$E0;Fde>LfOkv+c&l$Tp_ literal 0 HcmV?d00001 diff --git a/src/frontend/LoginFrame.java b/src/frontend/LoginFrame.java new file mode 100644 index 0000000..9c96de7 --- /dev/null +++ b/src/frontend/LoginFrame.java @@ -0,0 +1,132 @@ +package frontend; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import backend.UserManager; +import backend.User; + +public class LoginFrame extends JFrame { + private JTextField emailField; + private JPasswordField passwordField; + private JButton loginButton, registerButton; + private UserManager userManager; + + public LoginFrame() { + userManager = UserManager.getInstance(); + initializeUI(); + } + + private void initializeUI() { + setTitle("中小学数学学习软件 - 登录"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(400, 300); + setLocationRelativeTo(null); + setResizable(false); + + // 创建主面板 + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + // 标题 + JLabel titleLabel = new JLabel("中小学数学学习软件", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18)); + titleLabel.setForeground(new Color(0, 120, 215)); + + // 表单面板 + JPanel formPanel = new JPanel(new GridLayout(3, 2, 10, 10)); + + JLabel emailLabel = new JLabel("邮箱/用户名:"); + emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + emailField = new JTextField(); + + JLabel passwordLabel = new JLabel("密码:"); + passwordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + passwordField = new JPasswordField(); + + formPanel.add(emailLabel); + formPanel.add(emailField); + formPanel.add(passwordLabel); + formPanel.add(passwordField); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + loginButton = new JButton("登录"); + registerButton = new JButton("注册"); + + // 设置按钮样式 + setupButtonStyle(loginButton); + setupButtonStyle(registerButton); + + buttonPanel.add(loginButton); + buttonPanel.add(registerButton); + + // 添加组件到主面板 + mainPanel.add(titleLabel, BorderLayout.NORTH); + mainPanel.add(formPanel, BorderLayout.CENTER); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + add(mainPanel); + + // 添加事件监听器 + setupEventListeners(); + } + + private void setupButtonStyle(JButton button) { + button.setFont(new Font("微软雅黑", Font.BOLD, 14)); + button.setBackground(new Color(0, 120, 215)); + button.setForeground(Color.BLACK); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); + } + + private void setupEventListeners() { + loginButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleLogin(); + } + }); + + registerButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleRegister(); + } + }); + + // 回车键登录 + passwordField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleLogin(); + } + }); + } + + private void handleLogin() { + String identifier = emailField.getText().trim(); + String password = new String(passwordField.getPassword()); + + if (identifier.isEmpty() || password.isEmpty()) { + JOptionPane.showMessageDialog(this, "请输入邮箱/用户名和密码", "输入错误", JOptionPane.WARNING_MESSAGE); + return; + } + + User user = userManager.authenticate(identifier, password); + if (user != null) { + // 登录成功,打开主界面 + dispose(); // 关闭登录窗口 + new MainFrame(user).setVisible(true); + } else { + JOptionPane.showMessageDialog(this, "邮箱/用户名或密码错误", "登录失败", JOptionPane.ERROR_MESSAGE); + } + } + + private void handleRegister() { + new RegistrationFrame(this).setVisible(true); + } + + +} \ No newline at end of file diff --git a/src/frontend/MainFrame$1.class b/src/frontend/MainFrame$1.class new file mode 100644 index 0000000000000000000000000000000000000000..4547a43535e832640b4a2d7065ef67ef95a316e0 GIT binary patch literal 614 zcmZuv+e*Vg6rAnFXpFYj`?V?}Rlz9urU)VmLcM_E`!+q*E$v1&Tm20G!Z$%dQGDQu^}B`gBR!`dbNPC$86HGWR%anSgdxOwjwQWH}yV#vL6r!bs_} zeX{3a)xo5PDHJ_)qRWGeA}fBsyMDjD@i2`Uw)NlD+^T@Lr!|@FhKwSL1oDZhhSX>N zVWm!i6_{)*w5%eIh>SqtKe0f6M7DL2xcNacd%Bc3lR-ccXT8E{FSJebE1X2eu{G?H zBVij^K05&iBA<_a@~NiycG1mUkE>aJPCAQAj}VJbNZoT+poiCZ5~%Uoi`+MU{uloM mQAj@{^M`j6CdFT=BaJ-IrqG81?-~Ac1~A5z%RE`$`T7K4XpR^F literal 0 HcmV?d00001 diff --git a/src/frontend/MainFrame$2.class b/src/frontend/MainFrame$2.class new file mode 100644 index 0000000000000000000000000000000000000000..ff603dde98863a8d9328187f71d432eb64d5a76f GIT binary patch literal 614 zcmZuv+e*Vg6rAnFXpCCx{aO`~TEU3;rU)VmLcM_E`!+r5O1sg`w*G=Y;V1YYD2fk$ zfZw6NA)dtBgAHW&?Mx+eX%DmEW%^t?atMP_Y$Y{#YQh8CFT4r>r#XHR=^IN3v3jJ2J2; z)CXivLQ|)zK%rPZmrd!*E9+B}wDmU<$W~mh#$@hc*f0TYtC*nsSIJ^L#*AA!sD+Wz zr@Lg&!m5J_50fZ(Xh(+!7X?=Qetq-ybn9UX(`@U%tJ!4%Z%1n~n>86l6ba-KRW+$k z{r$>01y*39rO>j9I3h9v`TxWMeG%ElMdIcM$?WN3;!FksMV$2#r#;^?&98708Hd)e zOOAwXWclm_9Ee;l_Q|K3;@d?hcRj9V_&Mp679Jo@?be@;I}xpJ8&%R676SvZa* literal 0 HcmV?d00001 diff --git a/src/frontend/MainFrame$3.class b/src/frontend/MainFrame$3.class new file mode 100644 index 0000000000000000000000000000000000000000..a05d22b9077c784101ed2a49729dc064f2caefc6 GIT binary patch literal 614 zcmZuv%SyvQ6g|_2(HOP1zF$=lsR~BWog#=R2=xJq`)PXBk#-`Jsr6I*2XP@NiVHu$ z?{MQ!h&S=Iuz}3n+?;!I&Y8S^Jih=~!E^_*=y%bEfp!eKC}7CJu!B(tV*+jVOhq$G z0)6EZ6KYGk>hDUWw~egRD!*S-TG@4h!BQ*0+_6ArGpv$8Pg!Z&Yt$=bj%1}4cVu8y zs1L}Tgr-hafnuqAE-$4oo7SgG($?QdAX{<08k4z)VZ#Kptzv@iUnPt27&C6^pcY0- zpYD=93#$&sJxrkJp&cC_TohUH`_1*+)2)X|OtG#1u4a}6ydAB{Y}RBHQ6!K{RMn(D z_4h016j*`rmO{%a;)uuy36e6;Xi^ROj#*={MwHrO(_}O3l nJw!hJgv=k_O_-Yel`Eu?@?be@-7pxN?~%%R676k5i6a literal 0 HcmV?d00001 diff --git a/src/frontend/MainFrame$4.class b/src/frontend/MainFrame$4.class new file mode 100644 index 0000000000000000000000000000000000000000..791d450ae7b54fcfffbf3ccb3f50f84d1a2c68b2 GIT binary patch literal 724 zcmZuvZEF)j5Pmi}bM1N7*w)6E*4Fl_mk3_Peya!)S`e;UPy_mHb2n|aCVR4bX+H@1 zn*_C@;1BReiL-}B#2oC*?A|la?lUt#et!K9U>ogO_-F-~!4)4@16;!x!m!$J#Un;$4*5_p7CbA)CZj#&2<9d) zx$DxHaaU)&Fa-drc0jok0~n!R;CI`mE{tW5t#pEqYgi(j>SSmXJa zfxs+8v+2smV^iZ5;sT$}akS2RcM^2&e1hnlq4t4y+yu4__P|@VO|B@Sdk}o{4H$z#@B(=X41-I0~55XV2d+e}$^O literal 0 HcmV?d00001 diff --git a/src/frontend/MainFrame$5.class b/src/frontend/MainFrame$5.class new file mode 100644 index 0000000000000000000000000000000000000000..24a41010288a2bccf649ab10295ed25c577137af GIT binary patch literal 556 zcmZuv+e!m55It$DYu8$)rLT+c@{_7 zl5Pk4Qt91Tc4?jWH`NvT&4ry08g8Yw?Wp|7jcXZf^jMC@(WG|w& zhnXB^QO?1`Kn__K&B5nt{uWbR6UgmpP4RX|8bd~)kcjC>eI6XPE-ADE<$v`xmEo76 zSYY%ou|Uy~?Oi2Bd?T4XT}uj*VMxYQR~ik>a~t}=j|x#JILX|d6wfSt&JJ?4z-yQB z>q}1%^=G6Wc*Vlm@+r2rY=@Bl!dKZl{6PH?;sxnnw4dbf_?CO5G0d103K(IZ;V)3c LBuDPn40|4*Dgb(u literal 0 HcmV?d00001 diff --git a/src/frontend/MainFrame.class b/src/frontend/MainFrame.class new file mode 100644 index 0000000000000000000000000000000000000000..e365ab231175cbebd6d8cf794978a701f69ddae0 GIT binary patch literal 4683 zcmaJ_31C#!6+JJ@%<%IilYsz%8cSwtZa3QPbAi{t(?`6PprnK%m})>gy~ zD=JoS!40WJtqN#m1|5-ZTD6;Yv#Z@Q3F&4R+uGKp=l(xQW?)(;y!-BZ_ub{Z`|f++ zH$HmtWdLU>UBi{wI*iQ?(z{WEhMP3pj9X-En~dG+!e@ov=EQcPw@Y(}G0` z_?!ke?$%I=of>xG9_iid#C;m7alaG0U3fslXguh|LoPh5VJsdIevcE6YN*9tVLmU5 z9&_Rg8cxMcF6iVD9B|>FhADVfoPExPFA06#g+oG9 z;$2!C*z3Y!8F|5l7lmeI<|P-7h{2a#IO@WeW#}j zcj5;cF2)bV@{gQ&Tf=37|7Cc~i61NIOCu38)(|%0aWk$^5lqHnW+bsTZpIXb`&*3Q zRc55sC;VK65uLG6hY{;;4z@?5;f2XWA{tSs_HQ&c8Jm6au27`Sx6B@H&(q^ur4_(n%>wsE49Bqpx69a5Tl0GyU5u;69t1fUYH%yz3d7(%s zF<+sou0Ue$;$FtBEZtQlQ=4KdN0qsds%Yzzr@!v`HXQ<6r< zW?ZeK1wkE~(4`}ctDX3Xj-TRZIyT}e9i6yZVN%cGXR-(H%s#WPch^C-{OfsP&#@y1 zdNQdAQz!Hs&h+it-v7{E9Y04GGp`^at+;jk0`KVfC4Qyj*Z7T&-=gPu!Bs}Y3=?}< z?K8R(zJ*LjGv+tCnTO&V*M^aGqE|;Bj_K&f?|3K%#T&!Xt|8%fj#uwDT15S%-lz8- zJ9^m-jR9{s(UA&)g+4f6(zq{E5&G3N}Q;(U?rV ztK-l3i;lnIZ`>hkJ};pbCPQI{$CcS5cV}woG#i)(|aBL`i*3>s#E$rTJ{@!;s- z;iYK))i79=E85J&(nvgEM1tI&(RGD2W2j8KSPa*M5>_>3cagclNQM&);b`1k*=fcM zagO!6ZmASiCncI$nz?~>ORbS=j`*WNOKi0nCf#JNiCRS{?+|o!MkCyLyKZvDYBL_X z#xD3}br*}~ak(lhXWh=}0T*SqMsy7zW|x9SBbbQBx_P|>W2TWX7k6|fy6q{2Q|KN2 zE4Un5tO}AVB)p%7(T@T7+QNIJP*fZkRpb(|;%Biv{xHwAkrAvKtvcb!i%mu>7#!lF zs6NS(izb=Gf&zoAC`Ej(aJe*!Q3FKwhIDXLv&z}ht25h4Hu;xN zt_BeqJwKuN5@9CEt0TfYi|2-!bV9@mu0+(n8<`|F_zcZsZ;h2+tpiw}{Xe}K*x(%e z`OD}P@6aL{mFLUKmW{l#EwlvFg}jajFfCu`U(m2z!JV&ym6>WQE%<}U_$uDZ36pSD z5$XljC@s^aa@u7+r2LnzjV zPSyaN!q=+C?*}JJu!elD9O4;Tcp0GH?gnnp;bwDh-dn-aXR(9ma?hM%oh9fwDA4 zak?pk(E-nx(llyTOwC|yz%#DQ?M~wpDV$8cHc;x`T!pvO7@xv~DN|E8W&Lp)=9Gzw z3{DNCahhjh3a5LYM@b52u$RIl(LATTrrg$D>K<&0?n8%nXZ}>7`9y8fOWeo})8KYaB8mhOFN7!QRXv zTxVr2jEv}G^wLlsb0Zq^*WlFk9-p4Qu``; zE2({cePPZlAYU7BH&$V56`r@UW?}tvXh>mEJ@a#MJ$JcQ{#nV`I0V92Dg@Rs`PVbl zix^sfAzh65xP*rIyviBgm@_;-XSmd@TM1A6K zw(#r*X_s%doZZblo{Ap&xzWB1mISzmpG;vX+36W96Lz`H&dk6sY?IB-%3y`CD{Xdm z2CIaxV3OxDwb-3%4`Xc3=|jq9%R^zA!IiOtz?gc6^`vT?aP~^syK&BCPNTARP6rIF_Hbs}R-M1O(TjDc{ zZo+6l9f4A{)sy$qlMlwDds4@9w@_5|^Y^e@^?@>ksD8fg?5lR^=b>@)mc Nrm?`0?V4fF-xq#6jd=h7 literal 0 HcmV?d00001 diff --git a/src/frontend/QuestionCountFrame$2.class b/src/frontend/QuestionCountFrame$2.class new file mode 100644 index 0000000000000000000000000000000000000000..cc41f2d310e4d16e122d907e0550ab54c18c186d GIT binary patch literal 598 zcmaJ<+e!m55It$DYuBpveytVhL#^P7_@)R_6oh(1@j+17c1N4iCbG%ucL`p=2S31% z5+_CRL1`c}li|#nIY~Y~-`)XiqL@Vn6F&Md>0=C29;Q9adYBXFw@qnE>jH(^O{|qA z6$PgW87sA_>O|T7IPB0C1GlA;c1K{T+!MKSA&{x+hy+GzQqfV;sZ)F&*4r)ZMHEpB&k&VYq#pUi_)#!P!u2h06$8c z4T=v+1DTl}&YYREvmc*t?*R5u%p!w%7h_m(F^fe9OAb~XtO|^qexS=WfsIx-3Y8(% z@h)T1CJ0p{jFo9da!7Rs9t0{dhXSR_NaXg7K&BCPNFd(|6rIJxHbvL6J+K++TjC3f zx?wb=jzF=}x|R3RlMlwDds4%)`a9dMt!L_E!fn!(M62|D(!7sa4JF>$82I8L3I!{ISDE5J3zxH#9BuL0 zX58w|Geq?TsYgDs0=8Vmw#{}5)8F_qdyAi_K0&-9{fl;!JaLyU(#SI=g#u>SXZXF$ MVVNV_HN&32FN!jar~m)} literal 0 HcmV?d00001 diff --git a/src/frontend/QuestionCountFrame.class b/src/frontend/QuestionCountFrame.class new file mode 100644 index 0000000000000000000000000000000000000000..0c7a3e036db207ed9a4b8e9e36c62deaf32501f2 GIT binary patch literal 4038 zcmai133yaj75-l`$(uYLWHM|CgtaVTl8`{57D5XoB$P5CKv)dc0S=wqV6;Xjw%M2w2+^yBR*ScGkK;o`dzfZq@>VMvwBon}|nJ@R=bMLw5 zoO{lH&b{x||6I5PU=IGNVLdDjLy^eiXsa~aWNy2Pb`7~mxX~f~eHuQ956S4mvV6aa z9nwD_&4beHlxCNTP8APn7>?Z15&i=dzp z3U-)lmnq0D5pGZ1v=ULr+(xTi_zFvIdNSc2Zgbae<%#^kIBhj!^k4oaBis@)8^t@T z62T4yLpcs?sW+mwY879m+v$Hg#|Q+>czjA_Wu=0N87EqmFymG*60WgBAs(p`g<4}I z5et}gK{0IfjZ8BoSUR?#RY$Wl9{|bs;r%)`qLo`@y&_~Vl#N!55v@)HLoE`Ek-g6z zzIN>N)kDws?%v;X`qH(Frze(A)bSO3RmInId>yao_=YsE;+qP*gHU0IOvktInvQSd zJ379L@9FqHesDeGN+WEBsLVFQ*k)B!M`Bd(H`*f!OHAh8X8cga>pFgfH+1|MKe=9p z-)OdF^qficTsqL*`CO*uvi+xYc)x-=Vu-QszI3|h;+fuKoxNR;3D3`T{2ad^1p66l zBB4l3mVT+@S9p_4-4_pZ@7dS8W6#ySC+AOOwEKDMLXm9)Hor!FnKsN9DfH1O;lD9+ zbTnauj$h+9X_5UbeOaP+zr3US#NKPC4pOX+_uwAlzUT76?qg5ucrWhJu?ay1IV&PD zYm-R&E!S%ZLJ4FJAtJMzCHz!Vf|Je zzr!npxsKlpV*Y?XG6d;h9X!%==1dxi{U>jT3fKi5I{t(|tN4qKzv6ESiZUJBeYvyu znXc~RC$3z0g!wWibM4CceLXub_9Q!dF7KXOspBnyyEo&!!HzUaoAGxY{}6Xfumfsm zgHsvmBC$5Zs@)ziZF`ve{UE#g*T7c->$)DsHEX2`xiS6N2}L_3Mb2pQ2R ztC@mwr=Vn}Vbu(~#$-;VGm|Ax&#aOxyRD{OKi9NJSp-Iu+_;r{+*YJdubV|`P0*5x zG4f`bSyl?kvc%kIBtlkAC=xdtqGrsHh_HN?ER({clB1E8lK3iFX3vb|j6V{v*;bn& z_FK(05e7D`L$bRj(iV+`2~4|q>D;l}j0ZcM`s*s$Ad<&s@H;tGZ-k9j-UqT;l$k?L zv8XcwRwUN0pdb)44a=-;i(2i@l7dOJj`j_z*v)p0NtYVAWoSnq|9D4x9}ow#iUFf; z;xhaEedo+5rd5t-#8*z)@n)P&X6z&7iisk6sLdBMTVpKp#KrIoZo3-U?Ab;O*L;K4 z`n~F)X0WRj%(y9Y2ErPeH=6;+8Zn}pms6h+Io1BfHOmR$Owp{2^jp4dZy*t0$@|zc zX;lGjTO1Ie^j&`nCTA!&UlL1 z2}^iMN0`kEG9(Wm>&9bc%s4qvWxKmzASIX4d?Ji#5;bCRljQ%&4ZaK1mS8-}oKP?= z<9;4^k_>)v*qSstYF#iM*Cj)-i}Fh_)=|CT0K3R9?jzROlOHHB&ObBc1VW9BKR)6x`5 z&S3^mF=Oz$ugF!zqn55Oa>=5M`o;4^+*c+law(>~$vZQJigT!Rh*>r&U^&iFB&(3!-TPR3S#ZtTW1oaE#GCHVL#E5+;NzRJ&pw=h%5MWr$fvy?*2 zQOYq_nUC9)rMO*bz&vF=suTkYlr31O#IQ(d$71CHR4aS1MA?s}$^qO(sxGWRTX0kvxwZQQVt$!#MWh<{Uau8StJ|A+VTaA;vz7hP<2o S7#^dRz5LZU9Y0R(zyARz&0rM( literal 0 HcmV?d00001 diff --git a/src/frontend/QuestionCountFrame.java b/src/frontend/QuestionCountFrame.java new file mode 100644 index 0000000..d611106 --- /dev/null +++ b/src/frontend/QuestionCountFrame.java @@ -0,0 +1,134 @@ +package frontend; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import backend.User; + +public class QuestionCountFrame extends JFrame { + private JTextField countField; + private JButton startButton, backButton; + private User currentUser; + private MainFrame mainFrame; + + public QuestionCountFrame(MainFrame mainFrame, User user) { + this.mainFrame = mainFrame; + this.currentUser = user; + initializeUI(); + } + + private void initializeUI() { + setTitle("题目数量设置 - " + currentUser.getUserType()); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setSize(400, 250); + setLocationRelativeTo(mainFrame); + setResizable(false); + + // 创建主面板 + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + // 标题 + JLabel titleLabel = new JLabel("请输入题目数量", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 16)); + titleLabel.setForeground(new Color(0, 120, 215)); + + // 信息标签 + JLabel infoLabel = new JLabel("当前选择: " + currentUser.getUserType(), JLabel.CENTER); + infoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + + // 输入面板 + JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + JLabel countLabel = new JLabel("题目数量:"); + countLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + countField = new JTextField(10); + countField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + + inputPanel.add(countLabel); + inputPanel.add(countField); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + startButton = new JButton("开始答题"); + backButton = new JButton("返回"); + + setupButtonStyle(startButton); + setupButtonStyle(backButton); + + buttonPanel.add(startButton); + buttonPanel.add(backButton); + + // 添加组件到主面板 + mainPanel.add(titleLabel, BorderLayout.NORTH); + mainPanel.add(infoLabel, BorderLayout.CENTER); + mainPanel.add(inputPanel, BorderLayout.CENTER); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + add(mainPanel); + + // 添加事件监听器 + setupEventListeners(); + } + + private void setupButtonStyle(JButton button) { + button.setFont(new Font("微软雅黑", Font.BOLD, 14)); + button.setBackground(new Color(0, 120, 215)); + button.setForeground(Color.BLACK); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); + } + + private void setupEventListeners() { + startButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleStartQuiz(); + } + }); + + backButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + + // 回车键开始答题 + countField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleStartQuiz(); + } + }); + } + + private void handleStartQuiz() { + String countText = countField.getText().trim(); + + if (countText.isEmpty()) { + JOptionPane.showMessageDialog(this, "请输入题目数量", "输入错误", JOptionPane.WARNING_MESSAGE); + return; + } + + try { + int count = Integer.parseInt(countText); + if (count <= 0) { + JOptionPane.showMessageDialog(this, "题目数量必须大于0", "输入错误", JOptionPane.WARNING_MESSAGE); + return; + } + + if (count > 50) { + JOptionPane.showMessageDialog(this, "题目数量不能超过50", "输入错误", JOptionPane.WARNING_MESSAGE); + return; + } + + // 创建答题界面 + dispose(); + new QuizFrame(mainFrame, currentUser, count).setVisible(true); + + } catch (NumberFormatException e) { + JOptionPane.showMessageDialog(this, "请输入有效的数字", "输入错误", JOptionPane.WARNING_MESSAGE); + } + } +} \ No newline at end of file diff --git a/src/frontend/QuizFrame$1.class b/src/frontend/QuizFrame$1.class new file mode 100644 index 0000000000000000000000000000000000000000..75bbc53b81ee1f28108a0a241eba214804b29160 GIT binary patch literal 556 zcmZuv+e!m55IyO|wQJRCz27R*hg!j{;F}_dSP<$B#rJeOwJB{@cGC(X=r0M1;)5UH zM~Rcg%Yz0oGnt&6oSEeP+*1?>>fN80ySP_`0HN!v~ z(hcvlr*5jD?9vwRZ!4|Lj=*&3KfvmRKx!{&kick7X*%k4>lB{LddFs@Z&aX9C~O8{ zml^_vQmrlf(v#Q5qdsZl?Z%LO8*f=M_bljzK2?<^82M4MZjUkJzVnO4wn zFrUQ&7PD|Lm_^1$OK`cGzr_?c1hNNOQ@GcWQACkIE*8^~`qDeDx5+mGi~s7Ysfas6 zp}_cGVu5@_rgs$=@r`8mWGOC4`aVV6;z|X}{0wPuQz3FWE16#>!7~GwvqK!M@Y-hF z^4b$b`5B1^Ua@ete2VQY+hL4+;fw4oez5!q@q*+p+Kuyfe9JwO7-dWXIgGJS@fXNr Lh9i4xian1{AisMr literal 0 HcmV?d00001 diff --git a/src/frontend/QuizFrame$2.class b/src/frontend/QuizFrame$2.class new file mode 100644 index 0000000000000000000000000000000000000000..133bc397d10b06b07dcdde92689ee6e99fbaf78d GIT binary patch literal 556 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 questions; + private List userAnswers; + + // UI组件 + private JLabel questionNumberLabel; + private JTextArea questionTextArea; + private JRadioButton[] optionButtons; + private ButtonGroup optionGroup; + private JButton nextButton, submitButton; + private JPanel optionsPanel; + + public QuizFrame(MainFrame mainFrame, User user, int totalQuestions) { + this.mainFrame = mainFrame; + this.currentUser = user; + this.totalQuestions = totalQuestions; + this.currentQuestionIndex = 0; + this.questions = generateQuestions(); + this.userAnswers = new ArrayList<>(); + + initializeUI(); + showQuestion(0); + } + + private void initializeUI() { + setTitle("数学答题 - " + currentUser.getUserType()); + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + setSize(600, 500); + setLocationRelativeTo(mainFrame); + setResizable(false); + + // 创建主面板 + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + // 题目编号标签 + questionNumberLabel = new JLabel("", JLabel.CENTER); + questionNumberLabel.setFont(new Font("微软雅黑", Font.BOLD, 16)); + questionNumberLabel.setForeground(new Color(0, 120, 215)); + + // 题目文本区域 + questionTextArea = new JTextArea(); + questionTextArea.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + questionTextArea.setLineWrap(true); + questionTextArea.setWrapStyleWord(true); + questionTextArea.setEditable(false); + questionTextArea.setFocusable(false); // 设置为不可获得焦点,移除光标 + questionTextArea.setBackground(UIManager.getColor("Panel.background")); + questionTextArea.setBorder(null); // 移除边框 + + // 选项面板 + optionsPanel = new JPanel(new GridLayout(4, 1, 10, 10)); + optionButtons = new JRadioButton[4]; + optionGroup = new ButtonGroup(); + + for (int i = 0; i < 4; i++) { + optionButtons[i] = new JRadioButton(); + optionButtons[i].setFont(new Font("微软雅黑", Font.PLAIN, 14)); + optionGroup.add(optionButtons[i]); + optionsPanel.add(optionButtons[i]); + } + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + nextButton = new JButton("下一题"); + submitButton = new JButton("提交答案"); + submitButton.setVisible(false); + + setupButtonStyle(nextButton); + setupButtonStyle(submitButton); + + buttonPanel.add(nextButton); + buttonPanel.add(submitButton); + + // 添加组件到主面板 + mainPanel.add(questionNumberLabel, BorderLayout.NORTH); + + // 创建中间面板来容纳题目文本和选项 + JPanel centerPanel = new JPanel(new BorderLayout(10, 10)); + // 创建JScrollPane并设置无边框 + JScrollPane scrollPane = new JScrollPane(questionTextArea); + scrollPane.setBorder(null); // 移除滚动面板的边框 + centerPanel.add(scrollPane, BorderLayout.NORTH); + centerPanel.add(optionsPanel, BorderLayout.CENTER); + + mainPanel.add(centerPanel, BorderLayout.CENTER); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + add(mainPanel); + + // 添加事件监听器 + setupEventListeners(); + } + + private void setupButtonStyle(JButton button) { + button.setFont(new Font("微软雅黑", Font.BOLD, 14)); + button.setBackground(new Color(0, 120, 215)); + button.setForeground(Color.BLACK); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); + } + + private void setupEventListeners() { + nextButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleNextQuestion(); + } + }); + + submitButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleSubmitQuiz(); + } + }); + } + + private void showQuestion(int index) { + if (index >= questions.size()) { + return; + } + + Question question = questions.get(index); + //System.out.println(question.getQuestionText()); + + // 更新题目编号 + questionNumberLabel.setText("第 " + (index + 1) + " 题 / 共 " + totalQuestions + " 题"); + + // 更新题目文本 + questionTextArea.setText(question.getQuestionText()); + + // 更新选项 + String[] options = question.getOptions(); + for (int i = 0; i < 4; i++) { + if (i < options.length) { + optionButtons[i].setText(options[i]); + optionButtons[i].setVisible(true); + } else { + optionButtons[i].setVisible(false); + } + } + + // 清除选择 + optionGroup.clearSelection(); + + // 更新按钮状态 + if (index == totalQuestions - 1) { + nextButton.setVisible(false); + submitButton.setVisible(true); + } else { + nextButton.setVisible(true); + submitButton.setVisible(false); + } + } + + private void handleNextQuestion() { + // 获取当前选择的答案 + String selectedAnswer = getSelectedAnswer(); + if (selectedAnswer == null) { + JOptionPane.showMessageDialog(this, "请选择一个答案", "提示", JOptionPane.WARNING_MESSAGE); + return; + } + + // 保存答案 + userAnswers.add(selectedAnswer); + + // 显示下一题 + currentQuestionIndex++; + showQuestion(currentQuestionIndex); + } + + private void handleSubmitQuiz() { + // 获取最后一题的答案 + String selectedAnswer = getSelectedAnswer(); + if (selectedAnswer == null) { + JOptionPane.showMessageDialog(this, "请选择一个答案", "提示", JOptionPane.WARNING_MESSAGE); + return; + } + + // 保存最后一题的答案 + userAnswers.add(selectedAnswer); + + // 计算分数 + int correctCount = 0; + for (int i = 0; i < questions.size(); i++) { + if (i < userAnswers.size() && questions.get(i).isCorrect(userAnswers.get(i))) { + correctCount++; + } + } + + double score = (double) correctCount / totalQuestions * 100; + + // 显示分数界面 + dispose(); + new ScoreFrame(mainFrame, currentUser, score, totalQuestions, correctCount).setVisible(true); + } + + private String getSelectedAnswer() { + for (int i = 0; i < 4; i++) { + if (optionButtons[i].isSelected()) { + return optionButtons[i].getText(); + } + } + return null; + } + + private List generateQuestions() { + List questionList = new ArrayList<>(); + QuestionGenerator generator = createQuestionGenerator(); + + for (int i = 0; i < totalQuestions; i++) { + // 生成纯数学表达式 + String expression = generator.generateQuestion(); + + // 获取当前级别的题目描述 + String description = generator.getQuestionDescription(); + + // 组合题目描述和数学表达式作为完整题目 + String questionText = description + expression; + + // 计算题目的答案(使用纯数学表达式) + String correctAnswer = calculateAnswer(expression); + String[] options = generator.generateOptions(correctAnswer); + + // 创建Question对象,使用包含描述的完整题目和正确答案 + Question question = new Question(questionText, options, correctAnswer); + questionList.add(question); + } + + return questionList; + } + + private QuestionGenerator createQuestionGenerator() { + switch (currentUser.getUserType()) { + case "小学": + return new PrimarySchoolGenerator(); + case "初中": + return new MiddleSchoolGenerator(); + case "高中": + return new HighSchoolGenerator(); + default: + return new PrimarySchoolGenerator(); + } + } + + private String calculateAnswer(String questionText) { + // 使用MathCalculator计算数学表达式的结果 + return MathCalculator.calculateSimple(questionText); + } +} \ No newline at end of file diff --git a/src/frontend/RegistrationFrame$1.class b/src/frontend/RegistrationFrame$1.class new file mode 100644 index 0000000000000000000000000000000000000000..8ff0b79a22c04444ca794f1e2a1da622042b4870 GIT binary patch literal 596 zcmaJMjzceF!D1+9ZHzNo!2<(H3-mmMVD|$EfUhQ`UMbSe)pdS^@ zU5Xyc7ItRtaOTXMJNNzL^$oy2iW#IZ?_vxKE@rXlV9CLXgH?fX(+PB`B9L#~MxipK zTHYnK18pK{f>0etvQJgU9Rw;chXTc||A5;!0;yWqB7x~fpy+JSZ&GwEn?0M6zNNgN z=r)Y{)QZWvawt7{Z#){3GTu=PsoS_}$=s`O5czZ*Sc1i0lkM7aX09v07wSN@Pswz` zmV>n{@+f5CAe}|VM(c1{$P4g(uZ2Xt>T*^NKp{#1r^`Z?O3<8S=pot`c;CW_}~Zl zQQ~A#eDD&;%w#xoW=@i~_m@`y8z^Ov!nlh8Ot=`sq=P93GY*OZgXS{O<(j};<01-` zA=UCus2yk%NfU%>FOoglWZYJu0<$eps{99BJ{L&U!xjnT8iAt2ey>TQO5u>&R>A$s6O*H7Vom#E@MZZ(1_R<`9@&=*LfR! zU&NIH#lIT}6f~LsRV?nSg1aOuu~6yzq;+|%R>LAc*8up^A@X@EfnPDfI|G-qLmVyf z*=F48$|FSe35k0?u>iJQ#rB5nFh)M{W%d@Iu0B9KBl(SX(+YvRzZ`d3^w0bc^Hw literal 0 HcmV?d00001 diff --git a/src/frontend/RegistrationFrame$3.class b/src/frontend/RegistrationFrame$3.class new file mode 100644 index 0000000000000000000000000000000000000000..385b011865c080be4f5af1db81652b8bd2b005ec GIT binary patch literal 740 zcmaJj&w!SJb-`UTNcBHdphx*F8Tsmd+b}oms#(wKcE4LxA*tp8E*%b&jO-2IK9i{2b zXxO8CPxc1hB2y3fj`Dqz4=F3a4&|{-~)EL^s{yyn58T{Fs4+EyN5_MggD zd^jU->2zSM(*4)u4onteDaJ!A$CyAAW712HJgMVxh$k_gqQNa+-f_Jx5Wmuz^38#? zmaIUvsB|E8KY80bq|^y4{I~cH505xfVD9n+0yRr+bX0WuH-fX28@}r>Q})PMZaI=^ zO4in&ukw!CSI>UDn2zbIi`CUGsu-IYnA3NaRmEn4#`|U$OMEXO1g=9=tA28PMkW4} zh}n9BrDfK=C2BqU3eoz8(r4BLrkGZk11C(UQ9bu>pZnKZUm(s<_Ut=?;0)Ksfm0}R g;3=p6h+Fu?+D+C=EZ3L^e6=%JU@2nI3Uhvc0D!r)4gdfE literal 0 HcmV?d00001 diff --git a/src/frontend/RegistrationFrame$4.class b/src/frontend/RegistrationFrame$4.class new file mode 100644 index 0000000000000000000000000000000000000000..18f6989f25d6af40b11b16631146910ab5c5c204 GIT binary patch literal 596 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 literal 0 HcmV?d00001 diff --git a/src/frontend/RegistrationFrame.java b/src/frontend/RegistrationFrame.java new file mode 100644 index 0000000..7f2cdde --- /dev/null +++ b/src/frontend/RegistrationFrame.java @@ -0,0 +1,194 @@ +package frontend; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import backend.UserManager; + +public class RegistrationFrame extends JFrame { + private JTextField emailField; + private JTextField usernameField; + private JTextField verificationCodeField; + private JPasswordField passwordField; + private JPasswordField confirmPasswordField; + private JButton sendCodeButton, registerButton, backButton; + private UserManager userManager; + private LoginFrame loginFrame; + + public RegistrationFrame(LoginFrame loginFrame) { + this.loginFrame = loginFrame; + this.userManager = UserManager.getInstance(); + initializeUI(); + } + + private void initializeUI() { + setTitle("用户注册"); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setSize(450, 400); + setLocationRelativeTo(loginFrame); + setResizable(false); + + // 创建主面板 + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + // 标题 + JLabel titleLabel = new JLabel("用户注册", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18)); + titleLabel.setForeground(new Color(0, 120, 215)); + + // 表单面板 + JPanel formPanel = new JPanel(new GridLayout(6, 2, 10, 15)); + + JLabel emailLabel = new JLabel("邮箱:"); + emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + emailField = new JTextField(); + + JLabel usernameLabel = new JLabel("用户名:"); + usernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + usernameField = new JTextField(); + + JLabel codeLabel = new JLabel("验证码:"); + codeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + verificationCodeField = new JTextField(); + + JLabel passwordLabel = new JLabel("密码:"); + passwordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + passwordField = new JPasswordField(); + + JLabel confirmPasswordLabel = new JLabel("确认密码:"); + confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + confirmPasswordField = new JPasswordField(); + + // 发送验证码按钮 + sendCodeButton = new JButton("发送验证码"); + setupButtonStyle(sendCodeButton); + + formPanel.add(emailLabel); + formPanel.add(emailField); + formPanel.add(usernameLabel); + formPanel.add(usernameField); + formPanel.add(codeLabel); + formPanel.add(verificationCodeField); + formPanel.add(new JLabel()); // 空标签占位 + formPanel.add(sendCodeButton); + formPanel.add(passwordLabel); + formPanel.add(passwordField); + formPanel.add(confirmPasswordLabel); + formPanel.add(confirmPasswordField); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + registerButton = new JButton("注册"); + backButton = new JButton("返回登录"); + + setupButtonStyle(registerButton); + setupButtonStyle(backButton); + + buttonPanel.add(registerButton); + buttonPanel.add(backButton); + + // 添加组件到主面板 + mainPanel.add(titleLabel, BorderLayout.NORTH); + mainPanel.add(formPanel, BorderLayout.CENTER); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + add(mainPanel); + + // 添加事件监听器 + setupEventListeners(); + } + + private void setupButtonStyle(JButton button) { + button.setFont(new Font("微软雅黑", Font.BOLD, 14)); + button.setBackground(new Color(0, 120, 215)); + button.setForeground(Color.BLACK); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); + } + + private void setupEventListeners() { + sendCodeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleSendVerificationCode(); + } + }); + + registerButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleRegister(); + } + }); + + backButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + loginFrame.setVisible(true); + } + }); + + // 回车键注册 + confirmPasswordField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + handleRegister(); + } + }); + } + + private void handleSendVerificationCode() { + String email = emailField.getText().trim(); + String username = usernameField.getText().trim(); + + if (email.isEmpty()) { + JOptionPane.showMessageDialog(this, "请输入邮箱地址", "输入错误", JOptionPane.WARNING_MESSAGE); + return; + } + + if (username.isEmpty()) { + JOptionPane.showMessageDialog(this, "请输入用户名", "输入错误", JOptionPane.WARNING_MESSAGE); + return; + } + + // 检查用户名是否已被注册 + if (userManager.isUsernameRegistered(username)) { + JOptionPane.showMessageDialog(this, "该用户名已被注册", "注册失败", JOptionPane.ERROR_MESSAGE); + return; + } + + // 调用UserManager发送验证码 + String result = userManager.sendVerificationCode(email); + + // 显示发送结果 + JOptionPane.showMessageDialog(this, result, "验证码发送", JOptionPane.INFORMATION_MESSAGE); + } + + private void handleRegister() { + String email = emailField.getText().trim(); + String username = usernameField.getText().trim(); + String verificationCode = verificationCodeField.getText().trim(); + String password = new String(passwordField.getPassword()); + String confirmPassword = new String(confirmPasswordField.getPassword()); + + // 基本的非空检查 + if (email.isEmpty() || username.isEmpty() || verificationCode.isEmpty() || password.isEmpty() || confirmPassword.isEmpty()) { + JOptionPane.showMessageDialog(this, "请填写所有字段", "输入错误", JOptionPane.WARNING_MESSAGE); + return; + } + + // 用户名的具体验证逻辑已在UserManager.registerUser方法中实现(在验证码验证之后) + String result = userManager.registerUser(email, username, verificationCode, password, confirmPassword); + + if (result.equals("注册成功")) { + JOptionPane.showMessageDialog(this, result, "注册成功", JOptionPane.INFORMATION_MESSAGE); + dispose(); + loginFrame.setVisible(true); + } else { + JOptionPane.showMessageDialog(this, result, "注册失败", JOptionPane.ERROR_MESSAGE); + } + } +} \ No newline at end of file diff --git a/src/frontend/ScoreFrame$1.class b/src/frontend/ScoreFrame$1.class new file mode 100644 index 0000000000000000000000000000000000000000..816ae1c1058a044fabdccae49c95e4d8dff2e130 GIT binary patch literal 744 zcmZ`%ZBG+H5Pr5jdvcsmsVKf62vx5odKmpyW1<8SO+gb(Kk#jPH^7p1m)vf_zh$Cm z{NNApM;T{NpoyuM+`MJx*_nCf*Y6)c0lYwa3N_q}5aL#Z8(8q-c7!{)8{%Gw`yrMD zYVJ_k=DNUqcfT;&kOsKNnp0CH0_LreJVc6 z{=i3M=9JMNsMt5fkaB^gqTF=F(g)k*4ox0nImQF5#F#=9V;ZY5nqEA_qY#f{Bxvz;6NYO(6NukxO~uAQT1!@- zUiLPS`XGJZKcdVDEKY2^gWt*2##^Ww~%Bqs9AlYm6 zu*^3TLf|Syz3x}ar&Qs88Zk$&v9-d!k3{XYuMq8TsC;HmV1{LlHK16|qJEzL{5-$W v{sM7^s#o6;1ZTK1E}TJ?3y1u)#vBZL*V(VI-C!N?<<4V~t%yNutoi!`RwuDz literal 0 HcmV?d00001 diff --git a/src/frontend/ScoreFrame$2.class b/src/frontend/ScoreFrame$2.class new file mode 100644 index 0000000000000000000000000000000000000000..918bc09943a5a5a5f450a1aa57c23609ab299a21 GIT binary patch literal 857 zcmZuvO>YuW6g{sX1LI&RwWZqDRz(GBi!tu7CW@`WP-}weet<`r3h$AbN2v=9u3Wg# zok=&w#28m3CK?-J`~m(C(z@~w7}GmLRWwX8@8i7l&b#;A`|VfvD}ZZQ3L%J7Izl)d z#B>ld8fJAI!xLL$qfd7R+c%#Xx}M%nKoN0&9}Kw z>6;#9#a(Ayv2a0|rH5-~<48j<$fjN5tClDDH?}2Lhn`pwrJ8gsvAM=oRhBheFmMr< z3>=4HAdCdV^!}Spud{pb?&NQp*X!?QG3-9PThgyliVGX{{Hxcu6tL4mBWe zIb=S!8ODbw6Y6l)YPrjq<(-_Aeb-fAi(MaJ{^f6z78mvF~1rz%@nDJqs-zipW{ zxyi6RP?)T&H>7Y2(l6FP_=8A6+2m0smrLgUNwn~YQmIp0@Ewct^(S)44a`$RhQOeM zMIvr(s82`e590)(qqI6ldl%7D3!lJJpE2@*cBD*WfM&p^aSYKT{vGOPAEB|-N3f_b z>h}fq5Llksh34CX=3XB0E=S>~b(0=?OqZWv5^anVLZUx0Nv7bW$DN=P5I2S?3Wfrq Jhk`WIcL;u2;Ku*} literal 0 HcmV?d00001 diff --git a/src/frontend/ScoreFrame.class b/src/frontend/ScoreFrame.class new file mode 100644 index 0000000000000000000000000000000000000000..f94d5dfa18d12930503334f9e72aa87d0657413f GIT binary patch literal 3889 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 90) { + return new Color(0, 150, 0); // 绿色 - 优秀 + } else if (score >= 70) { + return new Color(255, 165, 0); // 橙色 - 良好 + } else if (score >= 60) { + return new Color(255, 215, 0); // 金色 - 及格 + } else { + return new Color(220, 20, 60); // 红色 - 不及格 + } + } + + private String getScoreComment(double score) { + if (score >= 90) { + return "太棒了!继续保持!"; + } else if (score >= 70) { + return "做得不错,继续努力!"; + } else if (score >= 60) { + return "及格了,再加把劲!"; + } else { + return "需要多加练习哦!"; + } + } +} \ No newline at end of file diff --git a/src/registry.dat b/src/registry.dat new file mode 100644 index 0000000000000000000000000000000000000000..d7abc229b63a54358e7648898703f6969bcf1952 GIT binary patch literal 372 zcmY+9u};G<5QdM0(6p*h0g1;*1&j=Isz3@;!_JYMnt+oS`$EIQ%*M*X$^w-bSrJdb z%1j3a-T_;Q5IxB!oqgZ`|9<`fPb=6s$14mf&O~sCIz2||bw1zTw@*J@aK_LRB_?Bx zxl~8c`z+yU_u*!<*eh9uqt!Jf5F5$*5`<20?Yf1m-0`q3BrM-3-dXpx^ z&h8$=+T!-%>vhi1X``8tOc