123 #1

Merged
hnu202326010306 merged 3 commits from huangpeiyi_branch into main 4 months ago

@ -1,2 +1,140 @@
# my_project1
# 中小学数学学习软件
## 项目简介
这是一个基于Java Swing开发的中小学数学学习软件支持小学、初中和高中三个学段的数学题目生成和答题功能。软件采用MVC架构设计具有良好的用户界面和用户体验。
## 功能特性
### 1. 用户注册与登录
- 用户通过邮箱进行注册
- 注册时发送验证码到邮箱(模拟)
- 密码要求6-10位必须包含大小写字母和数字
- 登录状态下可以修改密码
### 2. 题目生成
- 根据用户选择的学段(小学、初中、高中)生成相应难度的数学题目
- 题目全部为选择题形式
- 同一张试卷不能有相同题目
### 3. 答题功能
- 界面显示题目和四个选项
- 用户选择答案后提交
- 自动跳转到下一题
- 最后一题提交后显示分数
### 4. 分数统计
- 根据答对的百分比计算分数
- 显示详细答题结果
- 提供继续做题或退出选项
## 技术架构
### MVC架构
- **Model模型**: User, Question, QuestionGenerator等业务逻辑类
- **View视图**: 各种Swing界面类LoginFrame, MainFrame等
- **Controller控制器**: 事件处理逻辑和业务协调
### 核心类说明
1. **MainApplication**: 程序入口点
2. **LoginFrame**: 登录界面
3. **RegistrationFrame**: 注册界面
4. **MainFrame**: 主界面(学校类型选择)
5. **QuestionCountFrame**: 题目数量设置界面
6. **QuizFrame**: 答题界面
7. **ScoreFrame**: 分数显示界面
8. **ChangePasswordFrame**: 修改密码界面
### 题目生成器
1. **QuestionGenerator**: 题目生成器基类
2. **PrimarySchoolGenerator**: 小学题目生成器
3. **MiddleSchoolGenerator**: 初中题目生成器
4. **HighSchoolGenerator**: 高中题目生成器
## 运行要求
- Java 8或更高版本
- 支持Swing的桌面环境
## 运行方式
直接双击运行项目根目录下的`run_jar.bat`批处理文件即可启动程序。
## 使用说明
### 1. 注册新用户
1. 启动程序后进入登录界面
2. 点击"注册"按钮
3. 输入邮箱地址,点击"发送验证码"
4. 输入收到的验证码
5. 设置符合要求的密码
6. 完成注册
### 2. 登录系统
1. 输入已注册的邮箱和密码
2. 点击"登录"按钮
### 3. 选择学校类型
1. 登录后进入主界面
2. 选择小学、初中或高中
### 4. 设置题目数量
1. 输入需要生成的题目数量1-50
2. 点击"开始答题"
### 5. 答题
1. 阅读题目和选项
2. 选择认为正确的答案
3. 点击"下一题"继续
4. 最后一题点击"提交答案"
### 6. 查看结果
1. 系统显示得分和正确率
2. 可以选择"继续做题"或"退出"
## 项目结构
```
B1/
├── src/ # 源代码目录
│ ├── MainApplication.java # 程序入口
│ ├── LoginFrame.java # 登录界面
│ ├── RegistrationFrame.java # 注册界面
│ ├── MainFrame.java # 主界面
│ ├── QuestionCountFrame.java # 题目数量设置
│ ├── QuizFrame.java # 答题界面
│ ├── ScoreFrame.java # 分数显示
│ ├── ChangePasswordFrame.java # 修改密码
│ ├── UserManager.java # 用户管理
│ ├── User.java # 用户类
│ ├── Question.java # 题目类
│ ├── QuestionGenerator.java # 题目生成器基类
│ ├── PrimarySchoolGenerator.java # 小学题目生成器
│ ├── MiddleSchoolGenerator.java # 初中题目生成器
│ ├── HighSchoolGenerator.java # 高中题目生成器
│ ├── MathCalculator.java # 数学计算器
│ └── FileManager.java # 文件管理
├── .gitignore # Git忽略文件
└── README.md # 项目说明
```
## 开发规范
1. **代码规范**: 遵循Java编码规范
2. **类设计**: 每个类职责单一,符合面向对象设计原则
3. **方法长度**: 方法代码行数不超过40行
4. **注释**: 重要方法和类添加必要的注释
## 注意事项
- 本项目不使用数据库存储数据,用户数据存储在文件中
- 验证码发送功能为模拟实现,实际使用时需要集成邮件服务
- 题目生成和计算功能为简化实现,实际应用需要更完善的数学表达式解析
## 版本信息
- 版本: 1.0
- 开发语言: Java
- UI框架: Swing
- 架构: MVC

Binary file not shown.

@ -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);
});
}
}

@ -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);
}
}

@ -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);
}
}

@ -0,0 +1,45 @@
import java.util.Properties;
import javax.mail.*;
import javax.activation.DataSource;
/**
* JavaMailActivation 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测试完成");
}
}

@ -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);
}
}

@ -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);
}
}

@ -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("===== 测试完成 ====");
*/
}
});
}
}

@ -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);
}
}

Binary file not shown.

@ -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<String, String> 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 = "<h3>亲爱的用户:</h3>"
+ "<p>您正在注册中小学数学学习软件账号,您的验证码是:</p>"
+ "<h2 style='color: #3498db;'>" + verificationCode + "</h2>"
+ "<p>请在5分钟内完成验证验证码过期后请重新获取。</p>"
+ "<p style='color: #999; font-size: 12px;'>如果这不是您本人操作,请忽略此邮件。</p>";
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系统会自动发送验证码到您的邮箱";
}
}

@ -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<String> loadExistingQuestions(String username) {
Set<String> 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;
}
}

@ -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);
}
}
}

Binary file not shown.

@ -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<Double> numbers = new Stack<>();
Stack<Character> 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;
}
}

@ -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);
}
}
}

@ -0,0 +1,41 @@
package backend;
public class PrimarySchoolGenerator extends QuestionGenerator {
private static final String[] OPERATORS = {"+", "-", "*", "/"};
public PrimarySchoolGenerator() {
super("小学");
}
@Override
public String generateQuestion() {
String question;
do {
int operatorCount = generateOperatorCount1();
StringBuilder questionBuilder = new StringBuilder();
// 生成第一个操作数
questionBuilder.append(generateOperand());
// 生成操作符和操作数
for (int i = 0; i < operatorCount; i++) {
String operator = OPERATORS[random.nextInt(OPERATORS.length)];
int operand = generateOperand();
// 随机决定是否加括号
if (random.nextBoolean() && operatorCount > 1) {
questionBuilder.insert(0, "(").append(" " + operator + " " + operand + ")");
} else {
questionBuilder.append(" " + operator + " " + operand);
}
}
question = questionBuilder.toString();
} while (isDuplicate(question));
addToGenerated(question);
return question;
}
}

Binary file not shown.

@ -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;
}
}

@ -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<String> 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<Integer> 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 "";
}
}
}

Binary file not shown.

@ -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;
}
}

Binary file not shown.

@ -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<String, User> userMap; // 邮箱到用户的映射
private Map<String, User> usernameMap; // 用户名到用户的映射
private Map<String, String> verificationCodes; // 存储邮箱和验证码的映射
private Map<String, Long> 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<String, User> loadedUsers = (Map<String, User>) 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() +
'}';
}
}

@ -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);
}
}
}

Binary file not shown.

@ -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);
}
}

Binary file not shown.

@ -0,0 +1,157 @@
package frontend;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import backend.User;
import backend.UserManager;
public class MainFrame extends JFrame {
private User currentUser;
private JButton primarySchoolButton, middleSchoolButton, highSchoolButton;
private JButton changePasswordButton, logoutButton;
private UserManager userManager;
public MainFrame(User user) {
this.currentUser = user;
this.userManager = UserManager.getInstance();
initializeUI();
}
private void initializeUI() {
setTitle("中小学数学学习软件 - 主界面");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 400);
setLocationRelativeTo(null);
setResizable(false);
// 创建主面板
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 欢迎面板
JPanel welcomePanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
JLabel welcomeLabel = new JLabel("欢迎使用中小学数学学习软件", JLabel.CENTER);
welcomeLabel.setFont(new Font("微软雅黑", Font.BOLD, 16));
welcomeLabel.setForeground(new Color(0, 120, 215));
welcomePanel.add(welcomeLabel);
// 用户信息面板
JPanel userInfoPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
JLabel userInfoLabel = new JLabel("当前用户: " + currentUser.getUsername(), JLabel.CENTER);
userInfoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
userInfoPanel.add(userInfoLabel);
// 学校选择面板
JPanel schoolPanel = new JPanel(new GridLayout(3, 1, 15, 15));
schoolPanel.setBorder(BorderFactory.createTitledBorder("请选择学校类型"));
primarySchoolButton = new JButton("小学");
middleSchoolButton = new JButton("初中");
highSchoolButton = new JButton("高中");
setupSchoolButtonStyle(primarySchoolButton);
setupSchoolButtonStyle(middleSchoolButton);
setupSchoolButtonStyle(highSchoolButton);
schoolPanel.add(primarySchoolButton);
schoolPanel.add(middleSchoolButton);
schoolPanel.add(highSchoolButton);
// 功能按钮面板
JPanel functionPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
changePasswordButton = new JButton("修改密码");
logoutButton = new JButton("退出登录");
setupFunctionButtonStyle(changePasswordButton);
setupFunctionButtonStyle(logoutButton);
functionPanel.add(changePasswordButton);
functionPanel.add(logoutButton);
// 添加组件到主面板
mainPanel.add(welcomePanel, BorderLayout.NORTH);
mainPanel.add(userInfoPanel, BorderLayout.CENTER);
mainPanel.add(schoolPanel, BorderLayout.CENTER);
mainPanel.add(functionPanel, BorderLayout.SOUTH);
add(mainPanel);
// 添加事件监听器
setupEventListeners();
}
private void setupSchoolButtonStyle(JButton button) {
button.setFont(new Font("微软雅黑", Font.BOLD, 16));
button.setBackground(new Color(70, 130, 180));
button.setForeground(Color.BLACK);
button.setFocusPainted(false);
button.setBorder(BorderFactory.createEmptyBorder(15, 30, 15, 30));
}
private void setupFunctionButtonStyle(JButton button) {
button.setFont(new Font("微软雅黑", Font.PLAIN, 12));
button.setBackground(new Color(169, 169, 169));
button.setForeground(Color.BLACK);
button.setFocusPainted(false);
button.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15));
}
private void setupEventListeners() {
primarySchoolButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleSchoolSelection("小学");
}
});
middleSchoolButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleSchoolSelection("初中");
}
});
highSchoolButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleSchoolSelection("高中");
}
});
changePasswordButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new ChangePasswordFrame(MainFrame.this, currentUser).setVisible(true);
}
});
logoutButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleLogout();
}
});
}
private void handleSchoolSelection(String schoolType) {
// 设置用户类型
userManager.setUserType(currentUser.getEmail(), schoolType);
currentUser.setUserType(schoolType);
// 打开题目数量输入界面
new QuestionCountFrame(this, currentUser).setVisible(true);
}
private void handleLogout() {
int result = JOptionPane.showConfirmDialog(this,
"确定要退出登录吗?", "确认退出",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.YES_OPTION) {
dispose();
new LoginFrame().setVisible(true);
}
}
}

@ -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);
}
}
}

Binary file not shown.

@ -0,0 +1,271 @@
package frontend;
import backend.MathCalculator;
import backend.QuestionGenerator;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import backend.User;
import backend.Question;
import backend.PrimarySchoolGenerator;
import backend.MiddleSchoolGenerator;
import backend.HighSchoolGenerator;
public class QuizFrame extends JFrame {
private User currentUser;
private MainFrame mainFrame;
private int totalQuestions;
private int currentQuestionIndex;
private List<Question> questions;
private List<String> 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<Question> generateQuestions() {
List<Question> 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);
}
}

@ -0,0 +1,194 @@
package frontend;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import backend.UserManager;
public class RegistrationFrame extends JFrame {
private JTextField emailField;
private JTextField usernameField;
private JTextField verificationCodeField;
private JPasswordField passwordField;
private JPasswordField confirmPasswordField;
private JButton sendCodeButton, registerButton, backButton;
private UserManager userManager;
private LoginFrame loginFrame;
public RegistrationFrame(LoginFrame loginFrame) {
this.loginFrame = loginFrame;
this.userManager = UserManager.getInstance();
initializeUI();
}
private void initializeUI() {
setTitle("用户注册");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(450, 400);
setLocationRelativeTo(loginFrame);
setResizable(false);
// 创建主面板
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 标题
JLabel titleLabel = new JLabel("用户注册", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
titleLabel.setForeground(new Color(0, 120, 215));
// 表单面板
JPanel formPanel = new JPanel(new GridLayout(6, 2, 10, 15));
JLabel emailLabel = new JLabel("邮箱:");
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
emailField = new JTextField();
JLabel usernameLabel = new JLabel("用户名:");
usernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
usernameField = new JTextField();
JLabel codeLabel = new JLabel("验证码:");
codeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
verificationCodeField = new JTextField();
JLabel passwordLabel = new JLabel("密码:");
passwordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
passwordField = new JPasswordField();
JLabel confirmPasswordLabel = new JLabel("确认密码:");
confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
confirmPasswordField = new JPasswordField();
// 发送验证码按钮
sendCodeButton = new JButton("发送验证码");
setupButtonStyle(sendCodeButton);
formPanel.add(emailLabel);
formPanel.add(emailField);
formPanel.add(usernameLabel);
formPanel.add(usernameField);
formPanel.add(codeLabel);
formPanel.add(verificationCodeField);
formPanel.add(new JLabel()); // 空标签占位
formPanel.add(sendCodeButton);
formPanel.add(passwordLabel);
formPanel.add(passwordField);
formPanel.add(confirmPasswordLabel);
formPanel.add(confirmPasswordField);
// 按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
registerButton = new JButton("注册");
backButton = new JButton("返回登录");
setupButtonStyle(registerButton);
setupButtonStyle(backButton);
buttonPanel.add(registerButton);
buttonPanel.add(backButton);
// 添加组件到主面板
mainPanel.add(titleLabel, BorderLayout.NORTH);
mainPanel.add(formPanel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
add(mainPanel);
// 添加事件监听器
setupEventListeners();
}
private void setupButtonStyle(JButton button) {
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
button.setBackground(new Color(0, 120, 215));
button.setForeground(Color.BLACK);
button.setFocusPainted(false);
button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
}
private void setupEventListeners() {
sendCodeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleSendVerificationCode();
}
});
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleRegister();
}
});
backButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
loginFrame.setVisible(true);
}
});
// 回车键注册
confirmPasswordField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
handleRegister();
}
});
}
private void handleSendVerificationCode() {
String email = emailField.getText().trim();
String username = usernameField.getText().trim();
if (email.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入邮箱地址", "输入错误", JOptionPane.WARNING_MESSAGE);
return;
}
if (username.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入用户名", "输入错误", JOptionPane.WARNING_MESSAGE);
return;
}
// 检查用户名是否已被注册
if (userManager.isUsernameRegistered(username)) {
JOptionPane.showMessageDialog(this, "该用户名已被注册", "注册失败", JOptionPane.ERROR_MESSAGE);
return;
}
// 调用UserManager发送验证码
String result = userManager.sendVerificationCode(email);
// 显示发送结果
JOptionPane.showMessageDialog(this, result, "验证码发送", JOptionPane.INFORMATION_MESSAGE);
}
private void handleRegister() {
String email = emailField.getText().trim();
String username = usernameField.getText().trim();
String verificationCode = verificationCodeField.getText().trim();
String password = new String(passwordField.getPassword());
String confirmPassword = new String(confirmPasswordField.getPassword());
// 基本的非空检查
if (email.isEmpty() || username.isEmpty() || verificationCode.isEmpty() || password.isEmpty() || confirmPassword.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写所有字段", "输入错误", JOptionPane.WARNING_MESSAGE);
return;
}
// 用户名的具体验证逻辑已在UserManager.registerUser方法中实现在验证码验证之后
String result = userManager.registerUser(email, username, verificationCode, password, confirmPassword);
if (result.equals("注册成功")) {
JOptionPane.showMessageDialog(this, result, "注册成功", JOptionPane.INFORMATION_MESSAGE);
dispose();
loginFrame.setVisible(true);
} else {
JOptionPane.showMessageDialog(this, result, "注册失败", JOptionPane.ERROR_MESSAGE);
}
}
}

Binary file not shown.

@ -0,0 +1,155 @@
package frontend;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import backend.User;
public class ScoreFrame extends JFrame {
private User currentUser;
private MainFrame mainFrame;
private double score;
private int totalQuestions;
private int correctCount;
public ScoreFrame(MainFrame mainFrame, User user, double score, int totalQuestions, int correctCount) {
this.mainFrame = mainFrame;
this.currentUser = user;
this.score = score;
this.totalQuestions = totalQuestions;
this.correctCount = correctCount;
initializeUI();
}
private void initializeUI() {
setTitle("答题结果");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(500, 400);
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, 20));
titleLabel.setForeground(new Color(0, 120, 215));
// 分数显示面板
JPanel scorePanel = new JPanel(new GridLayout(4, 1, 10, 10));
scorePanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 分数显示
JLabel scoreLabel = new JLabel(String.format("得分: %.1f分", score), JLabel.CENTER);
scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
scoreLabel.setForeground(getScoreColor(score));
// 详细信息
JLabel detailLabel = new JLabel(
String.format("答对 %d 题,共 %d 题", correctCount, totalQuestions),
JLabel.CENTER
);
detailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16));
// 百分比
JLabel percentageLabel = new JLabel(
String.format("正确率: %.1f%%", score),
JLabel.CENTER
);
percentageLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16));
// 评价
JLabel commentLabel = new JLabel(getScoreComment(score), JLabel.CENTER);
commentLabel.setFont(new Font("微软雅黑", Font.ITALIC, 14));
commentLabel.setForeground(Color.GRAY);
scorePanel.add(scoreLabel);
scorePanel.add(detailLabel);
scorePanel.add(percentageLabel);
scorePanel.add(commentLabel);
// 按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
JButton continueButton = new JButton("继续做题");
JButton exitButton = new JButton("退出");
setupButtonStyle(continueButton);
setupButtonStyle(exitButton);
buttonPanel.add(continueButton);
buttonPanel.add(exitButton);
// 添加组件到主面板
mainPanel.add(titleLabel, BorderLayout.NORTH);
mainPanel.add(scorePanel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
add(mainPanel);
// 添加事件监听器
setupEventListeners(continueButton, exitButton);
}
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(JButton continueButton, JButton exitButton) {
continueButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 返回主界面,用户可以重新选择学校类型
dispose();
mainFrame.setVisible(true);
}
});
exitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int result = JOptionPane.showConfirmDialog(
ScoreFrame.this,
"确定要退出程序吗?",
"确认退出",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE
);
if (result == JOptionPane.YES_OPTION) {
System.exit(0);
}
}
});
}
private Color getScoreColor(double score) {
if (score >= 90) {
return new Color(0, 150, 0); // 绿色 - 优秀
} else if (score >= 70) {
return new Color(255, 165, 0); // 橙色 - 良好
} else if (score >= 60) {
return new Color(255, 215, 0); // 金色 - 及格
} else {
return new Color(220, 20, 60); // 红色 - 不及格
}
}
private String getScoreComment(double score) {
if (score >= 90) {
return "太棒了!继续保持!";
} else if (score >= 70) {
return "做得不错,继续努力!";
} else if (score >= 60) {
return "及格了,再加把劲!";
} else {
return "需要多加练习哦!";
}
}
}

Binary file not shown.
Loading…
Cancel
Save