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}T^l<5*V`Ql%qV%eY|=vI7jA
zbIQpU)PycA5an1pPDsCTt)%Bj^|>ogE?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!#)NRNEHet2$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^g0et11dgtM=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|AxaOxyRD{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
zcmaJ<+e!m55It$D+pg8t``wE4p;mA!_@)RV3PQc0;(NNCwp-e)Y|{D(f_{ylC_eZB
zev~*_6d$|bg?3^P;Eq^
zGNkI>88riKB58t99YnHATa4QdRA6=l3Z?&mtCs?)y|7LKxoV*3xYw;wbRlaUn~}by
zJf)}+MqR4MWNq1(p1d|5^+_3TH-_xnc*~Nx=V34M=^(HKQ$HqKx8=;dul!D^1Jyht
z(+cYj=CfG9Vipe4S!8Uq1eaC*EwZ>NkUdn2qP>pPnzTSZ9tlP$u|Ovzs_6b
z`y#FsnEShdz_=#UyNbnqRdAPNDHbYypR_J+R4Q2J=NbTCIz&EiCGaaIcxT{pc8H@D
zKHH32UVDTnKOu3?Clz%XPXtb%}HLI_0M*jfl6kpw6~Xs|+u