diff --git a/src/main/java/com/ybw/mathapp/LoginAndRegister.java b/src/main/java/com/ybw/mathapp/LoginAndRegister.java
new file mode 100644
index 0000000..b4b83c8
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/LoginAndRegister.java
@@ -0,0 +1,132 @@
+package com.ybw.mathapp;
+
+// UserService.java
+import com.ybw.mathapp.entity.User;
+import com.ybw.mathapp.util.EmailService;
+import com.ybw.mathapp.util.LoginFileUtils;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+
+public class LoginAndRegister {
+ private static Scanner scanner = new Scanner(System.in);
+
+ // UserService.java 中的注册方法更新
+ public static boolean register() {
+ System.out.println("\n=== 用户注册 ===");
+
+ // 输入邮箱
+ System.out.print("请输入邮箱地址: ");
+ String email = scanner.nextLine().trim();
+
+ if (!isValidEmail(email)) {
+ return false;
+ }
+
+ if (LoginFileUtils.isEmailRegistered(email)) {
+ System.out.println("该邮箱已注册,请直接登录!");
+ return false;
+ }
+
+ // 发送、验证验证码
+ if (!sendAndVerifyCode(email)) {
+ return false;
+ }
+
+ // 设置密码(其余代码保持不变)
+ System.out.print("请输入密码: ");
+ String password1 = scanner.nextLine();
+ System.out.print("请再次输入密码: ");
+ String password2 = scanner.nextLine();
+ if(!isVaildPassword(password1, password2)) {
+ return false;
+ }
+
+ User user = new User(email, password1);
+ LoginFileUtils.saveUser(user);
+ System.out.println("注册成功!您可以使用邮箱和密码登录了。");
+ return true;
+ }
+
+ // 登录流程
+ public static boolean login() {
+ System.out.println("\n=== 用户登录 ===");
+
+ System.out.print("请输入邮箱: ");
+ String email = scanner.nextLine().trim();
+
+ System.out.print("请输入密码: ");
+ String password = scanner.nextLine();
+
+ if (LoginFileUtils.validateUser(email, password)) {
+ System.out.println("登录成功!欢迎回来," + email);
+ return true;
+ } else {
+ System.out.println("邮箱或密码错误!");
+ return false;
+ }
+ }
+
+ //
+ /**
+ * 邮箱格式验证
+ * @param email 待验证的邮箱地址
+ * @return true表示邮箱格式正确,false表示邮箱格式错误
+ */
+ private static boolean isValidEmail(String email) {
+ if (email.isEmpty()) {
+ System.out.println("邮箱地址不能为空!");
+ return false;
+ }
+ if (!(email.contains("@") && email.contains("."))) {
+ System.out.println("邮箱格式不正确!");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * 密码格式验证
+ * @param password1 第一次输入的密码
+ * @param password2 第二次输入的密码
+ * @return true表示符合要求,false表示不符合
+ */
+ public static boolean isVaildPassword(String password1, String password2) {
+ if (password1 == null || password1.length() < 6 || password1.length() > 10) {
+ return false;
+ }
+
+ // 使用正则表达式验证:长度6-10,只包含字母数字,且包含大小写字母和数字
+ String regex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{6,10}$";
+ if (!Pattern.matches(regex, password1)) {
+ return false;
+ }
+
+ System.out.print("请再次输入密码: ");
+ if (!password1.equals(password2)) {
+ System.out.println("两次输入的密码不一致!");
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean sendAndVerifyCode(String email) {
+ // 发送真实邮件验证码
+ String verificationCode = EmailService.generateVerificationCode();
+ System.out.println("正在发送验证码邮件,请稍候...");
+
+ if (!EmailService.sendVerificationCode(email, verificationCode)) {
+ System.out.println("发送验证码失败,请检查邮箱配置或稍后重试!");
+ return false;
+ }
+
+ // 验证验证码
+ System.out.print("请输入收到的验证码: ");
+ String inputCode = scanner.nextLine().trim();
+
+ if (!EmailService.verifyCode(email, inputCode)) {
+ System.out.println("验证码错误或已过期!");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/ybw/mathapp/config/EmailConfig.java b/src/main/java/com/ybw/mathapp/config/EmailConfig.java
new file mode 100644
index 0000000..8331ea5
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/config/EmailConfig.java
@@ -0,0 +1,18 @@
+package com.ybw.mathapp.config;
+
+public class EmailConfig {
+ // 发件人邮箱配置(以QQ邮箱为例)
+ public static final String SMTP_HOST = "smtp.qq.com";
+ public static final String SMTP_PORT = "587";
+ public static final String SENDER_EMAIL = "1798231811@qq.com"; // 替换为你的邮箱
+ public static final String SENDER_PASSWORD = "dzmfirotgnlceeae"; // 替换为你的授权码
+
+ // 如果使用Gmail
+ // public static final String SMTP_HOST = "smtp.gmail.com";
+ // public static final String SMTP_PORT = "587";
+ // public static final String SENDER_EMAIL = "your_email@gmail.com";
+ // public static final String SENDER_PASSWORD = "your_app_password";
+
+ public static final String EMAIL_SUBJECT = "【用户注册】验证码";
+ public static final int CODE_EXPIRY_MINUTES = 5;
+}
diff --git a/src/main/java/com/ybw/mathapp/entity/User.java b/src/main/java/com/ybw/mathapp/entity/User.java
new file mode 100644
index 0000000..91e46c0
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/entity/User.java
@@ -0,0 +1,95 @@
+package com.ybw.mathapp.entity;
+
+/**
+ * 用户实体类,表示系统中的用户信息。
+ *
+ * 该类包含用户的基本信息,如用户名、密码和学习级别。
+ * 用户级别可以是小学、初中或高中。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class User {
+
+ /** 用户名,不可修改。 */
+ // private final String name;
+
+ /** 邮箱,不可修改。 */
+ private final String email;
+
+ /** 用户密码,不可修改。 */
+ private final String password;
+
+ /** 用户当前的学习级别,可以修改。 */
+ private String level;
+
+ /**
+ * 构造一个新的用户对象。
+ *
+ * @param email 邮箱,不能为空 用户名,不能为空
+ * @param password 用户密码,不能为空
+ */
+ public User(String email, String password) {
+ this.password = password;
+ this.email = email;
+ }
+
+ /**
+ * 获取用户密码。
+ *
+ * @return 用户密码
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * 获取用户当前的学习级别。
+ *
+ * @return 用户学习级别
+ */
+ public String getLevel() {
+ return level;
+ }
+
+ /**
+ * 获取用户邮箱。
+ *
+ * @return 用户邮箱
+ */
+ public String getEmail() {
+ return email;
+ }
+
+ /**
+ * 设置用户的学习级别。
+ *
+ * @param newLevel 新的学习级别,支持"小学"、"初中"、"高中"
+ */
+ public void setLevel(String newLevel) {
+ level = newLevel;
+ }
+
+ /**
+ * 保存邮箱+密码。
+ *
+ * @return 邮箱+密码
+ */
+ @Override
+ public String toString() {
+ return email + "," + password;
+ }
+
+ public static User fromString(String line) {
+ if (line == null || line.trim().isEmpty()) {
+ return null;
+ }
+
+ String[] parts = line.split(",", 2); // 最多分割成2部分
+ if (parts.length == 2) {
+ return new User(parts[0].trim(), parts[1].trim());
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/ybw/mathapp/service/Caculate.java b/src/main/java/com/ybw/mathapp/service/Caculate.java
new file mode 100644
index 0000000..9592645
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/Caculate.java
@@ -0,0 +1,198 @@
+package com.ybw.mathapp.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * 用于计算包含四则运算、括号、平方、开根号、三角函数的表达式。
+ * 注意:三角函数的输入通常认为是度数 (Degree)。
+ */
+public class Caculate {
+
+ /**
+ * 计算表达式的值。
+ * @param parts 表达式分解后的列表,例如 ["(", "2", "+", "3", ")", "*", "4"]
+ * @return 计算结果
+ */
+ public double caculate(List parts) {
+ // 将中缀表达式转换为后缀表达式(逆波兰表示法)
+ List postfix = infixToPostfix(parts);
+ // 计算后缀表达式的值
+ return evaluatePostfix(postfix);
+ }
+
+ private List infixToPostfix(List infix) {
+ Map precedence = new HashMap<>();
+ precedence.put("+", 1);
+ precedence.put("-", 1);
+ precedence.put("*", 2);
+ precedence.put("/", 2);
+ // "平方" 和 "开根号" 作为后缀运算符,优先级最高
+ // "sin", "cos", "tan" 作为前缀函数,优先级也很高
+ // 这里简化处理,将它们都视为高优先级,但需要特殊处理其结合性
+ // 实际上,"平方", "开根号" 是后缀一元运算符
+ // "sin", "cos", "tan" 是前缀一元函数
+ // 标准的调度场算法需要扩展来处理一元运算符和函数
+ // 为了简化,我们假设它们的优先级为 3
+ precedence.put("平方", 3);
+ precedence.put("开根号", 3);
+ precedence.put("sin", 3);
+ precedence.put("cos", 3);
+ precedence.put("tan", 3);
+
+ Stack operatorStack = new Stack<>();
+ List postfix = new ArrayList<>();
+
+ for (String token : infix) {
+ if (isNumeric(token)) {
+ postfix.add(token);
+ } else if ("(".equals(token)) {
+ operatorStack.push(token);
+ } else if (")".equals(token)) {
+ while (!operatorStack.isEmpty() && !"(".equals(operatorStack.peek())) {
+ postfix.add(operatorStack.pop());
+ }
+ if (!operatorStack.isEmpty()) { // Pop the '('
+ operatorStack.pop();
+ }
+ } else if (precedence.containsKey(token)) {
+ // 处理运算符和函数
+ // 对于前缀函数 (sin, cos, tan),它们没有左操作数,直接入栈
+ // 对于后缀运算符 (平方, 开根号),它们作用于前面的一个操作数
+ // 这里简化处理,将它们都当作普通二元运算符入栈,然后在计算时特殊处理
+ // 实际上,对于后缀运算符,应该立即处理它前面的一个操作数
+ // 对于前缀函数,应该立即处理它后面的一个操作数
+ // 这需要修改调度场算法或在 evaluatePostfix 中处理
+ // 最好的方式是:在生成器生成时,将 "x平方" -> ["x", "平方"],将 "sin(x)" -> ["sin", "x"]
+ // 这样 "平方", "开根号" 就是后缀,"sin", "cos", "tan" 就是前缀
+ // 但生成器可能生成 "开根号(x)" 或 "(x)平方"
+ // 这使得解析变得复杂
+ // 我们尝试一种方法:在解析时,如果遇到 "开根号",它后面必须跟 "(" 和表达式 ")"
+ // 将 "开根号(...)" 视为一个整体 token
+ // 同理,"sin(...)", "cos(...)", "tan(...)" 也是如此
+ // 或者,在调度场算法中,当遇到 "开根号", "平方" 时,它们是后缀,立即处理
+ // 当遇到 "sin", "cos", "tan" 时,它们是前缀,立即处理
+ // 这需要修改算法
+ // 一个简化的处理方法是:假设 "开根号", "平方" 总是作用于紧随其后的表达式(可能用括号包围)
+ // "sin", "cos", "tan" 也是作用于紧随其后的表达式(通常用括号包围)
+ // 但这与标准的 "x平方" 或 "开根号(x)" 不同
+ // 标准的 "x平方" -> ["x", "平方"] (后缀)
+ // 标准的 "sin(x)" -> ["sin", "x"] (前缀)
+ // 生成器生成的 "开根号(16)" -> ["开根号", "(", "16", ")"]
+ // 生成器生成的 "(2+3)平方" -> ["(", "2", "+", "3", ")", "平方"]
+ // 生成器生成的 "sin 30" -> ["sin", "30"] (如果生成器是这样分词的)
+ // 生成器生成的 "sin(30)" -> ["sin", "(", "30", ")"] (如果生成器是这样分词的)
+ // 这里我们假设 parts 已经是标准的后缀/前缀形式,或者调度场算法能处理 "开根号", "平方", "sin", "cos", "tan" 作为特殊运算符
+ // 我们尝试一个近似方法:将 "开根号", "平方", "sin", "cos", "tan" 视为高优先级的特殊运算符
+ // 在调度场算法中,遇到它们就立即处理(如果它们作用于前面或后面的操作数)
+ // 这在某些复杂嵌套情况下可能不准确,但对于大多数情况应该足够
+ // 更好的方法是使用 Shunting-yard 的扩展版本,或者使用递归下降解析器
+
+ // 暂时按照标准调度场算法处理,但在 evaluatePostfix 中特殊处理
+ // 将 "平方", "开根号", "sin", "cos", "tan" 放入运算符栈
+ while (!operatorStack.isEmpty() &&
+ precedence.containsKey(operatorStack.peek()) &&
+ precedence.get(operatorStack.peek()) >= precedence.get(token)) {
+ postfix.add(operatorStack.pop());
+ }
+ operatorStack.push(token);
+ } else {
+ // 其他token,例如数字的一部分(如果格式错误)
+ throw new IllegalArgumentException("Unknown token: " + token);
+ }
+ }
+
+ while (!operatorStack.isEmpty()) {
+ postfix.add(operatorStack.pop());
+ }
+
+ return postfix;
+ }
+
+ private double evaluatePostfix(List postfix) {
+ Stack stack = new Stack<>();
+ for (String token : postfix) {
+ if (isNumeric(token)) {
+ stack.push(Double.parseDouble(token));
+ } else {
+ double result = 0;
+ double operand = 0; // 用于一元运算符
+ double operand2 = 0; // 用于二元运算符
+ double operand1 = 0; // 用于二元运算符
+
+ switch (token) {
+ case "+":
+ operand2 = stack.pop();
+ operand1 = stack.pop();
+ stack.push(operand1 + operand2);
+ break;
+ case "-":
+ operand2 = stack.pop();
+ operand1 = stack.pop();
+ stack.push(operand1 - operand2);
+ break;
+ case "*":
+ operand2 = stack.pop();
+ operand1 = stack.pop();
+ stack.push(operand1 * operand2);
+ break;
+ case "/":
+ operand2 = stack.pop();
+ operand1 = stack.pop();
+ if (operand2 == 0) {
+ throw new ArithmeticException("Division by zero");
+ }
+ stack.push(operand1 / operand2);
+ break;
+ case "平方": // 一元后缀运算符
+ operand = stack.pop();
+ stack.push(operand * operand);
+ break;
+ case "开根号": // 一元后缀运算符
+ operand = stack.pop();
+ if (operand < 0) {
+ throw new ArithmeticException("Square root of negative number");
+ }
+ stack.push(Math.sqrt(operand));
+ break;
+ case "sin": // 一元前缀函数
+ operand = stack.pop();
+ // 假设输入是度数 (Degree)
+ stack.push(Math.sin(Math.toRadians(operand)));
+ break;
+ case "cos": // 一元前缀函数
+ operand = stack.pop();
+ // 假设输入是度数 (Degree)
+ stack.push(Math.cos(Math.toRadians(operand)));
+ break;
+ case "tan": // 一元前缀函数
+ operand = stack.pop();
+ // 假设输入是度数 (Degree)
+ stack.push(Math.tan(Math.toRadians(operand)));
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown operator in postfix: " + token);
+ }
+ }
+ }
+ if (stack.size() != 1) {
+ throw new IllegalStateException("Invalid expression evaluation - stack size: " + stack.size());
+ }
+ return stack.peek();
+ }
+
+ private static boolean isNumeric(String str) {
+ if (str == null || str.isEmpty()) {
+ return false;
+ }
+ try {
+ Double.parseDouble(str);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java b/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java
new file mode 100644
index 0000000..dae8fc3
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java
@@ -0,0 +1,191 @@
+package com.ybw.mathapp.service;
+
+// File: mathpuzzle/service/MultipleChoiceGenerator.java
+import java.util.*;
+
+/**
+ * 选择题生成器,负责为给定的题目生成器生成的题目添加选项,并在本次生成中去重。
+ *
+ * 该生成器会调用传入的 {@link QuestionGenerator} 生成原始题目,
+ * 计算正确答案,并生成指定数量的干扰项,最终形成选择题。
+ * 干扰项的生成方式是基于正确答案添加一个随机的小误差。
+ * 查重逻辑确保本次生成的题目列表中没有重复。
+ *
+ * @author 你的名字
+ * @version 1.0
+ * @since 2025
+ */
+public class ChoiceGenerator {
+
+ private final QuestionGenerator generator;
+ private final Random random = new Random();
+ private static final int OPTIONS_COUNT = 4; // 默认选项数量
+
+ /**
+ * 构造函数,指定题目生成器。
+ *
+ * @param generator 用于生成题目的 {@link QuestionGenerator} 实例。
+ */
+ public ChoiceGenerator(QuestionGenerator generator) {
+ this.generator = generator;
+ }
+
+ /**
+ * 生成指定数量的选择题。
+ * 该方法会生成原始题目和答案,然后为每道题生成干扰项,
+ * 并将选项打乱顺序。同时,确保生成的题目在本次调用中不重复。
+ *
+ * @param count 需要生成的选择题数量。
+ * @return 包含选择题的列表,每个元素包含题目、选项和正确答案索引。
+ */
+ public List generateMultipleChoiceQuestions(int count) {
+ List mcQuestions = new ArrayList<>();
+ Set generatedQuestionTexts = new HashSet<>(); // 用于本次生成过程中的查重
+ Caculate calculator = new Caculate(); // 使用计算器实例
+
+ int attempts = 0;
+ int maxAttempts = count * 100; // 设置最大尝试次数,防止无限循环
+
+ while (mcQuestions.size() < count && attempts < maxAttempts) {
+ attempts++;
+ // 生成原始题目
+ List rawQuestions = generator.generateQuestions(1);
+ if (rawQuestions.isEmpty()) {
+ continue; // 如果生成器返回空,跳过
+ }
+
+ String rawQuestion = rawQuestions.get(0);
+ String questionTextForDedup = rawQuestion.endsWith(" =") ?
+ rawQuestion.substring(0, rawQuestion.length() - 2) : rawQuestion;
+
+ // 检查是否重复
+ if (generatedQuestionTexts.contains(questionTextForDedup)) {
+ continue; // 如果重复,重新生成
+ }
+
+ // 计算正确答案
+ double correctAnswer = calculateAnswer(rawQuestion, calculator);
+ if (Double.isNaN(correctAnswer) || Double.isInfinite(correctAnswer)) {
+ continue; // 如果计算出错,跳过这道题
+ }
+
+ // 生成选项
+ List options = generateOptions(correctAnswer);
+ // 随机打乱选项
+ Collections.shuffle(options);
+ // 找到正确答案在打乱后列表中的索引
+ int correctIndex = options.indexOf(correctAnswer);
+
+ // 添加到结果列表和查重集合
+ mcQuestions.add(new MultipleChoiceQuestion(rawQuestion, options, correctIndex));
+ generatedQuestionTexts.add(questionTextForDedup);
+ }
+
+ if (mcQuestions.size() < count) {
+ System.out.println("警告:在尝试了 " + maxAttempts + " 次后,仅生成了 " + mcQuestions.size() + " 道不重复的选择题。");
+ }
+
+ return mcQuestions;
+ }
+
+ /**
+ * 计算给定题目的答案。
+ * @param question 题目字符串,例如 "2 + 3 * 4 ="
+ * @param calc 计算器实例
+ * @return 计算得出的答案。
+ */
+ private double calculateAnswer(String question, Caculate calc) {
+ // 移除 " ="
+ String expression = question.substring(0, question.length() - 2).trim();
+ // 将表达式字符串分割成部分
+ // 这里需要根据 QuestionGenerator 生成的格式来决定如何分割
+ // 如果 QuestionGenerator 生成的是 "开根号(16)" 或 "(2+3)平方",则按空格分割可能不够
+ // 但通常,生成器会生成 "开根号 ( 16 )" 或 "开根号 16" 或 "( 2 + 3 ) 平方" 这样的格式
+ // 按空格分割 ["开根号", "(", "16", ")"] 或 ["开根号", "16"] 或 ["(", "2", "+", "3", ")", "平方"]
+ // CaculatePrimary 需要能处理这些格式
+ List parts = Arrays.asList(expression.split("\\s+")); // 按空格分割
+
+ try {
+ return calc.caculate(parts);
+ } catch (Exception e) {
+ System.err.println("计算表达式失败: " + expression + ", 错误: " + e.getMessage());
+ return Double.NaN; // 或者抛出异常
+ }
+ }
+
+ /**
+ * 生成选项列表,包含一个正确答案和若干干扰项。
+ *
+ * @param correctAnswer 正确答案。
+ * @return 包含正确答案和干扰项的列表。
+ */
+ private List generateOptions(double correctAnswer) {
+ List options = new ArrayList<>();
+ options.add(correctAnswer); // 添加正确答案
+
+ for (int i = 1; i < OPTIONS_COUNT; i++) {
+ // 生成干扰项:在正确答案基础上加一个随机误差
+ // 误差范围可以根据需要调整,例如 +/- 10% 或固定范围
+ double error = correctAnswer * (random.nextDouble() * 0.2 - 0.1); // +/- 10% 的误差
+ // 为了避免干扰项过于接近或重复,可以添加一些逻辑
+ double incorrectAnswer = correctAnswer + error;
+ // 确保干扰项不等于正确答案,且不重复
+ while (Math.abs(incorrectAnswer - correctAnswer) < 0.001 || options.contains(incorrectAnswer)) { // 使用一个小的容差比较
+ error = correctAnswer * (random.nextDouble() * 0.2 - 0.1);
+ incorrectAnswer = correctAnswer + error;
+ // 防止无限循环,如果误差太小,强制加一个最小值
+ if (Math.abs(error) < 0.001) {
+ incorrectAnswer = correctAnswer + (random.nextBoolean() ? 0.01 : -0.01);
+ }
+ }
+ options.add(incorrectAnswer);
+ }
+
+ return options;
+ }
+
+ /**
+ * 用于封装一道选择题的内部类。
+ */
+ public static class MultipleChoiceQuestion {
+ private final String question; // 题目文本
+ private final List options; // 选项列表
+ private final int correctIndex; // 正确选项的索引
+
+ public MultipleChoiceQuestion(String question, List options, int correctIndex) {
+ this.question = question;
+ this.options = new ArrayList<>(options); // 创建副本以防外部修改
+ this.correctIndex = correctIndex;
+ }
+
+ public String getQuestion() {
+ return question;
+ }
+
+ public List getOptions() {
+ return new ArrayList<>(this.options); // 返回副本
+ }
+
+ public int getCorrectIndex() {
+ return correctIndex;
+ }
+
+ public Double getCorrectAnswer() {
+ if (correctIndex >= 0 && correctIndex < options.size()) {
+ return options.get(correctIndex);
+ }
+ return null; // 或抛出异常
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Question: ").append(question).append("\n");
+ for (int i = 0; i < options.size(); i++) {
+ sb.append((char)('A' + i)).append(". ").append(options.get(i)).append("\n");
+ }
+ sb.append("Correct Answer: ").append((char)('A' + correctIndex)).append(" (").append(options.get(correctIndex)).append(")\n");
+ return sb.toString();
+ }
+ }
+}
diff --git a/src/main/java/com/ybw/mathapp/service/FileHandler.java b/src/main/java/com/ybw/mathapp/service/FileHandler.java
new file mode 100644
index 0000000..8cd486a
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/FileHandler.java
@@ -0,0 +1,69 @@
+package com.ybw.mathapp.service;
+
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import mathpuzzle.entity.User;
+
+/**
+ * 文件处理器,负责创建用户目录和保存试卷文件。
+ *
+ * 该类提供用户目录管理功能和试卷文件保存功能。
+ * 每次生成的试卷会以时间戳命名保存到对应用户的目录中。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class FileHandler {
+
+ /**
+ * 确保用户目录存在,如果不存在则创建。
+ *
+ *
该方法检查用户对应的目录是否存在,如果不存在则创建该目录。
+ * 用户目录以用户名命名,位于当前工作目录下。
+ *
+ * @param user 需要创建目录的用户对象
+ * @throws IOException 当目录创建过程中发生错误时抛出
+ */
+ public void ensureUserDirectory(User user) throws IOException {
+ String dirPath = "./" + user.getName();
+ Path path = Paths.get(dirPath);
+ if (!Files.exists(path)) {
+ Files.createDirectories(path);
+ }
+ }
+
+ /**
+ * 保存试卷到用户目录中。
+ *
+ *
该方法将生成的题目列表保存到文件中,文件名包含时间戳信息,
+ * 以确保每次生成的试卷都有唯一的文件名。每个题目前添加题号, 题目之间用空行分隔。
+ *
+ * @param user 试卷所属的用户对象
+ * @param questions 需要保存的题目列表
+ * @throws IOException 当文件写入过程中发生错误时抛出
+ */
+ public void savePaper(User user, List questions) throws IOException {
+ ensureUserDirectory(user);
+ // 生成文件名:年-月-日-时-分-秒.txt
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss");
+ String fileName = LocalDateTime.now().format(formatter) + ".txt";
+ String filePath = "./" + user.getName() + "/" + fileName;
+
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
+ for (int i = 0; i < questions.size(); i++) {
+ writer.write((i + 1) + ". " + questions.get(i)); // 添加题号
+ writer.newLine();
+ writer.newLine(); // 每题之间空一行
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/JuniorHighGenerator.java b/src/main/java/com/ybw/mathapp/service/JuniorHighGenerator.java
new file mode 100644
index 0000000..11bc7b1
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/JuniorHighGenerator.java
@@ -0,0 +1,187 @@
+package com.ybw.mathapp.service;
+
+import static com.ybw.mathapp.service.PrimarySchoolGenerator.isNumeric;
+import static com.ybw.mathapp.service.PrimarySchoolGenerator.isNumeric;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * 初中题目生成器,负责生成包含平方或开根号运算的初中级别数学题目。
+ *
+ * 该生成器确保每道题目都包含至少一个高级运算符(平方或开根号),
+ * 题目结构包含基本的四则运算和高级运算的组合。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class JuniorHighGenerator implements QuestionGenerator {
+
+ /**
+ * 高级运算符数组,包含"平方"和"开根号"。
+ */
+ private static final String[] ADVANCED_OPS = {"平方", "开根号"};
+
+ /**
+ * 基本运算符数组,包含四则运算符号。
+ */
+ private static final String[] OPERATORS = {"+", "-", "*", "/"};
+
+ /**
+ * 随机数生成器,用于生成随机题目。
+ */
+ private final Random random = new Random();
+
+ @Override
+ public List generateQuestions(int count) {
+ List questions = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ String question = generateSingleQuestion();
+ questions.add(question);
+ }
+ return questions;
+ }
+
+ /**
+ * 生成单个初中级别的数学题目。
+ *
+ * 该方法确保生成的题目包含至少一个高级运算符(平方或开根号),
+ * 并根据操作数数量采用不同的生成策略。
+ *
+ * @return 生成的数学题目字符串
+ */
+ private String generateSingleQuestion() {
+ List parts = new ArrayList<>();
+ int operandCount = random.nextInt(5) + 1;
+ parts = generateBase(operandCount, parts);
+ // hasAdvancedOp用以检测下面的循环是否加入了高级运算符,如果没有就启动保底
+ boolean hasAdvancedOp = false;
+ if (operandCount == 1) {
+ if ("平方".equals(ADVANCED_OPS[random.nextInt(ADVANCED_OPS.length)])) {
+ parts.add("平方");
+ } else {
+ parts.add(0, "开根号");
+ }
+ hasAdvancedOp = true;
+ } else {
+ // 遍历查找左括号的合理位置
+ for (int i = 0; i < parts.size() - 2; i++) {
+ // 该位置要为操作数且随机添加括号
+ if (isNumeric(parts.get(i)) && random.nextBoolean()) {
+ // 随机数看取出来的是不是开根号运算符
+ if ("开根号".equals(ADVANCED_OPS[random.nextInt(ADVANCED_OPS.length)])) {
+ parts = generateRoot(parts, i);
+ } else { // 如果不是开根号就是平方运算
+ parts = generateSquare(parts, i);
+ }
+ hasAdvancedOp = true;
+ break;
+ }
+ }
+ }
+ // 启动保底强制加入一个高级运算符
+ if (!hasAdvancedOp) {
+ parts = forceAddAdvancedOp(parts);
+ }
+ return String.join(" ", parts) + " =";
+ }
+
+ /**
+ * 生成基本的四则运算表达式部分。
+ *
+ * 该方法生成指定数量的操作数和运算符,构成基础的数学表达式。
+ *
+ * @param operandCount 操作数的数量
+ * @param parts 用于存储表达式各部分的列表
+ * @return 包含基本运算表达式的列表
+ */
+ public List generateBase(int operandCount, List parts) {
+ for (int i = 0; i < operandCount; i++) {
+ int num = random.nextInt(100) + 1;
+ parts.add(String.valueOf(num));
+ if (i < operandCount - 1) {
+ parts.add(OPERATORS[random.nextInt(OPERATORS.length)]);
+ }
+ }
+ return parts;
+ }
+
+ /**
+ * 强制在表达式中添加一个高级运算符作为保底机制。
+ *
+ * 当随机生成过程中没有添加高级运算符时,使用此方法确保
+ * 每道题目都包含至少一个高级运算符。
+ *
+ * @param parts 包含表达式各部分的列表
+ * @return 添加了高级运算符的表达式列表
+ */
+ public List forceAddAdvancedOp(List parts) {
+ String advancedOp = ADVANCED_OPS[random.nextInt(ADVANCED_OPS.length)];
+ if ("平方".equals(advancedOp)) {
+ parts.add("平方");
+ } else { // 开根号
+ parts.set(0, "开根号(" + parts.get(0));
+ parts.set(parts.size() - 1, parts.get(parts.size() - 1) + ")");
+ }
+ return parts;
+ }
+
+ /**
+ * 在指定位置生成开根号运算。
+ *
+ * 该方法在表达式指定位置添加开根号运算,可能只对单个操作数
+ * 进行开根号,或者对一段子表达式进行开根号。
+ *
+ * @param parts 包含表达式各部分的列表
+ * @param i 开根号运算的起始位置
+ * @return 添加了开根号运算的表达式列表
+ */
+ public List generateRoot(List parts, int i) {
+ if (random.nextBoolean()) {
+ parts.set(i, "开根号(" + parts.get(i) + ")");
+ } else {
+ parts.set(i, "开根号(" + parts.get(i));
+ // 为避免随机数上限出现0,此处要单独判断一下左括号正好括住倒数第二个操作数的情况
+ if (i == parts.size() - 3) {
+ parts.set(parts.size() - 1, parts.get(parts.size() - 1) + ")");
+ } else {
+ while (true) {
+ int i2 = random.nextInt(parts.size() - 3 - i) + 2;
+ if (isNumeric(parts.get(i + i2))) {
+ parts.set(i + i2, parts.get(i + i2) + ")");
+ break;
+ }
+ }
+ }
+ }
+ return parts;
+ }
+
+ /**
+ * 在指定位置生成平方运算。
+ *
+ * 该方法在表达式指定位置添加平方运算,对一段子表达式进行平方运算。
+ *
+ * @param parts 包含表达式各部分的列表
+ * @param i 平方运算的起始位置
+ * @return 添加了平方运算的表达式列表
+ */
+ public List generateSquare(List parts, int i) {
+ parts.set(i, "(" + parts.get(i));
+ // 为避免随机数上限出现0,此处要单独判断一下左括号正好括住倒数第二个操作数的情况
+ if (i == parts.size() - 3) {
+ parts.set(parts.size() - 1, parts.get(parts.size() - 1) + ")");
+ } else {
+ while (true) {
+ int i2 = random.nextInt(parts.size() - 3 - i) + 2;
+ if (isNumeric(parts.get(i + i2))) {
+ parts.set(i + i2, parts.get(i + i2) + ")平方");
+ break;
+ }
+ }
+ }
+ return parts;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java b/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java
new file mode 100644
index 0000000..e70946f
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java
@@ -0,0 +1,121 @@
+package com.ybw.mathapp.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * 小学题目生成器,负责生成包含四则运算和括号的小学级别数学题目。
+ *
+ * 该生成器专门用于生成适合小学生的数学题目,题目仅包含加减乘除四则运算
+ * 和括号,确保计算结果为非负数。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class PrimarySchoolGenerator implements QuestionGenerator {
+
+ /** 运算符数组,包含四则运算符号。 */
+ private static final String[] OPERATORS = {"+", "-", "*", "/"};
+
+ /** 随机数生成器,用于生成随机题目。 */
+ private final Random random = new Random();
+
+ @Override
+ public List generateQuestions(int count) {
+ List questions = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ String question = generateSingleQuestion();
+ questions.add(question);
+ }
+ return questions;
+ }
+
+ /**
+ * 生成单个小级别的数学题目.
+ *
+ * 该方法生成包含2-5个操作数的四则运算表达式,可能包含括号,
+ * 并确保计算结果为非负数。如果计算结果为负数,则重新生成。
+ *
+ * @return 生成的小学数学题目字符串
+ */
+ private String generateSingleQuestion() {
+ Caculate caculate = new Caculate();
+ int operandCount = random.nextInt(4) + 2; // 2-5个操作数
+ List parts = new ArrayList<>();
+ while (true) {
+ // 生成基础操作
+ parts = generateBase(operandCount, parts);
+ // 简单添加括号逻辑:随机加一个括号
+ if (operandCount > 2 && random.nextBoolean()) {
+ // 遍历查找左括号的合理位置
+ for (int i = 0; i < parts.size() - 2; i++) {
+ // 该位置要为操作数且随机添加括号
+ if (isNumeric(parts.get(i)) && random.nextBoolean()) {
+ parts.add(i, "(");
+ i++;
+ // 为避免随机数上限出现0,此处要单独判断一下左括号正好括住倒数第二个操作数的情况
+ if (i == parts.size() - 3) {
+ parts.add(")");
+ } else {
+ while (true) {
+ int i2 = random.nextInt(parts.size() - 3 - i) + 2;
+ if (isNumeric(parts.get(i + i2))) {
+ parts.add(i + i2 + 1, ")");
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ if (caculate.caculate(parts) >= 0) {
+ return String.join(" ", parts) + " =";
+ } else {
+ parts.clear();
+ }
+ }
+ }
+
+ /**
+ * 判断给定字符串是否为数字。
+ *
+ * 该方法检查字符串是否可以转换为数字格式。
+ *
+ * @param str 待检查的字符串
+ * @return 如果字符串是数字则返回true,否则返回false
+ */
+ public static boolean isNumeric(String str) {
+ if (str == null || str.isEmpty()) {
+ return false;
+ }
+ try {
+ Double.parseDouble(str);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ /**
+ * 生成基本的四则运算表达式部分。
+ *
+ *
该方法生成指定数量的操作数和运算符,构成基础的数学表达式。
+ *
+ * @param operandCount 操作数的数量
+ * @param parts 用于存储表达式各部分的列表
+ * @return 包含基本运算表达式的列表
+ */
+ public List generateBase(int operandCount, List parts) {
+ for (int i = 0; i < operandCount; i++) {
+ int num = random.nextInt(100) + 1;
+ parts.add(String.valueOf(num));
+ if (i < operandCount - 1) {
+ parts.add(OPERATORS[random.nextInt(OPERATORS.length)]);
+ }
+ }
+ return parts;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
new file mode 100644
index 0000000..b4319ee
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
@@ -0,0 +1,91 @@
+package com.ybw.mathapp.service;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import mathpuzzle.entity.User;
+
+/**
+ * 题目查重器,负责加载历史题目并检查新题目是否重复。
+ *
+ * 该类维护一个题目集合,用于检测当前生成的题目是否与用户的历史题目重复。
+ * 通过加载用户目录下的所有历史试卷文件,提取题目内容进行去重检查。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class QuestionDeduplicator {
+
+ /**
+ * 存储用户历史题目的集合。
+ */
+ private final Set existingQuestions = new HashSet<>();
+
+ /**
+ * 加载指定用户的所有历史题目。
+ *
+ * 该方法遍历用户目录下的所有.txt文件,读取并解析题目内容,
+ * 将历史题目添加到去重集合中。此操作会清空之前的题目记录。
+ *
+ * @param user 需要加载历史题目的用户对象
+ */
+ public void loadExistingQuestions(User user) {
+ existingQuestions.clear();
+ String userDir = "./" + user.getName();
+ File dir = new File(userDir);
+
+ if (!dir.exists()) {
+ return; // 目录不存在,无历史题目
+ }
+
+ File[] files = dir.listFiles((d, name) -> name.endsWith(".txt"));
+ if (files == null) {
+ return;
+ }
+
+ for (File file : files) {
+ try (BufferedReader br = new BufferedReader(new FileReader(file))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ // 只加载题目行,忽略题号和空行
+ if (line.trim().isEmpty() || line.matches("\\d+\\. .*")) {
+ String questionContent = line.replaceFirst("\\d+\\. ", "").trim();
+ if (!questionContent.isEmpty() && !questionContent.equals("=")) {
+ existingQuestions.add(questionContent);
+ }
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("读取历史文件时出错: " + file.getName());
+ }
+ }
+ }
+
+ /**
+ * 检查指定题目是否为重复题目。
+ *
+ *
该方法检查给定的题目是否已经存在于历史题目集合中。
+ *
+ * @param question 待检查的题目内容
+ * @return 如果题目重复则返回true,否则返回false
+ */
+ public boolean isDuplicate(String question) {
+ return existingQuestions.contains(question);
+ }
+
+ /**
+ * 将新题目添加到去重集合中。
+ *
+ *
该方法将当前生成的题目添加到去重集合中,用于防止
+ * 在同一次生成过程中出现重复题目。
+ *
+ * @param question 需要添加的题目内容
+ */
+ public void addQuestion(String question) {
+ existingQuestions.add(question);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/QuestionGenerator.java b/src/main/java/com/ybw/mathapp/service/QuestionGenerator.java
new file mode 100644
index 0000000..104466d
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/QuestionGenerator.java
@@ -0,0 +1,28 @@
+package com.ybw.mathapp.service;
+
+import java.util.List;
+
+/**
+ * 题目生成器接口,定义了题目生成器的标准方法。
+ *
+ *
所有具体的题目生成器都应该实现此接口,提供统一的题目生成功能。
+ * 不同级别的题目生成器(如小学、初中、高中)可以根据各自的特点
+ * 实现不同的生成算法。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public interface QuestionGenerator {
+
+ /**
+ * 生成指定数量的数学题目。
+ *
+ *
该方法根据实现类的特定规则生成指定数量的数学题目。
+ * 生成的题目应该符合对应教育级别的难度要求。
+ *
+ * @param count 需要生成的题目数量
+ * @return 包含生成题目的字符串列表
+ */
+ List generateQuestions(int count);
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/QuestionWithAnswer.java b/src/main/java/com/ybw/mathapp/service/QuestionWithAnswer.java
new file mode 100644
index 0000000..336a461
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/QuestionWithAnswer.java
@@ -0,0 +1,31 @@
+package com.ybw.mathapp.service;
+
+// File: mathpuzzle/entity/QuestionWithAnswer.java
+/**
+ * 用于封装一道题目及其正确答案。
+ */
+public class QuestionWithAnswer {
+ private final String question; // 题目字符串,例如 "2 + 3 ="
+ private final double correctAnswer; // 计算得出的正确答案
+
+ public QuestionWithAnswer(String question, double correctAnswer) {
+ this.question = question;
+ this.correctAnswer = correctAnswer;
+ }
+
+ public String getQuestion() {
+ return question;
+ }
+
+ public double getCorrectAnswer() {
+ return correctAnswer;
+ }
+
+ @Override
+ public String toString() {
+ return "QuestionWithAnswer{" +
+ "question='" + question + '\'' +
+ ", correctAnswer=" + correctAnswer +
+ '}';
+ }
+}
diff --git a/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java b/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java
new file mode 100644
index 0000000..074e5d7
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java
@@ -0,0 +1,126 @@
+package com.ybw.mathapp.service;
+
+import static mathpuzzle.service.PrimarySchoolGenerator.isNumeric;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * 高中题目生成器,负责生成包含三角函数运算的高中级别数学题目。
+ *
+ * 该生成器确保每道题目都包含至少一个三角函数运算符(sin、cos或tan),
+ * 题目结构包含基本的四则运算和三角函数运算的组合。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class SeniorHighGenerator implements QuestionGenerator {
+
+ /** 三角函数运算符数组,包含"sin"、"cos"和"tan"。 */
+ private static final String[] TRIG_FUNCS = {"sin", "cos", "tan"};
+
+ /** 基本运算符数组,包含四则运算符号。 */
+ private static final String[] OPERATORS = {"+", "-", "*", "/"};
+
+ /** 随机数生成器,用于生成随机题目。 */
+ private final Random random = new Random();
+
+ @Override
+ public List generateQuestions(int count) {
+ List questions = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ String question = generateSingleQuestion();
+ questions.add(question);
+ }
+ return questions;
+ }
+
+ /**
+ * 生成单个高中级别的数学题目。
+ *
+ * 该方法确保生成的题目包含至少一个三角函数运算符,
+ * 并根据操作数数量采用不同的生成策略。
+ *
+ * @return 生成的数学题目字符串
+ */
+ private String generateSingleQuestion() {
+ List parts = new ArrayList<>();
+ int operandCount = random.nextInt(5) + 1;
+ parts = generateBase(operandCount, parts);
+ String advancedOp;
+ if (operandCount == 1) {
+ advancedOp = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)];
+ parts.set(0, advancedOp + parts.get(0));
+ } else {
+ // 遍历查找左括号的合理位置
+ for (int i = 0; i < parts.size(); i++) {
+ // 最后一次循环保底生成高中三角函数
+ if (i == parts.size() - 1) {
+ advancedOp = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)];
+ parts.set(i, advancedOp + parts.get(i));
+ } else if (isNumeric(parts.get(i)) && random.nextBoolean()) { // 随机数看是否为操作数且随即进入生成程序
+ // 进入随机生成tan\sin\cos的程序
+ parts = generateTrig(parts, i);
+ break;
+ }
+ }
+ }
+ return String.join(" ", parts) + " =";
+ }
+
+ /**
+ * 生成基本的四则运算表达式部分。
+ *
+ * 该方法生成指定数量的操作数和运算符,构成基础的数学表达式。
+ *
+ * @param operandCount 操作数的数量
+ * @param parts 用于存储表达式各部分的列表
+ * @return 包含基本运算表达式的列表
+ */
+ // 产生基本操作
+ public List generateBase(int operandCount, List parts) {
+ for (int i = 0; i < operandCount; i++) {
+ int num = random.nextInt(100) + 1;
+ parts.add(String.valueOf(num));
+ if (i < operandCount - 1) {
+ parts.add(OPERATORS[random.nextInt(OPERATORS.length)]);
+ }
+ }
+ return parts;
+ }
+
+ /**
+ * 在指定位置生成三角函数运算。
+ *
+ * 该方法在表达式指定位置添加三角函数运算,可能只对单个操作数
+ * 进行三角函数运算,或者对一段子表达式进行三角函数运算。
+ *
+ * @param parts 包含表达式各部分的列表
+ * @param i 三角函数运算的位置
+ * @return 添加了三角函数运算的表达式列表
+ */
+ // 产生三角函数运算符
+ public List generateTrig(List parts, int i) {
+ String trigOp = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)];
+ if (random.nextBoolean()) {
+ parts.set(i, trigOp + parts.get(i));
+ } else {
+ parts.set(i, trigOp + "(" + parts.get(i));
+ // 为避免随机数上限出现0,此处要单独判断一下左括号正好括住倒数第二个操作数的情况
+ if (i == parts.size() - 3) {
+ parts.set(parts.size() - 1, parts.get(parts.size() - 1) + ")");
+ } else {
+ while (true) {
+ int i2 = random.nextInt(parts.size() - 3 - i) + 2;
+ if (isNumeric(parts.get(i + i2))) {
+ parts.set(i + i2, parts.get(i + i2) + ")");
+ break;
+ }
+ }
+ }
+ }
+ return parts;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/StartController.java b/src/main/java/com/ybw/mathapp/service/StartController.java
new file mode 100644
index 0000000..ef0ff3c
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/StartController.java
@@ -0,0 +1,132 @@
+package com.ybw.mathapp.service;
+
+import com.ybw.mathapp.LoginAndRegister;
+import com.ybw.mathapp.service.ChoiceGenerator.MultipleChoiceQuestion;
+import com.ybw.mathapp.system.LogSystem;
+import java.io.IOException;
+import java.util.List;
+import java.util.Scanner;
+
+public class StartController {
+
+ private LogSystem logSystem = new LogSystem();
+ private FileHandler fileHandler = new FileHandler();
+ private double score;
+
+ public double getScore() {
+ return score;
+ }
+
+ public void setScore(double score) {
+ this.score = score;
+ }
+
+ public void start() {
+ Scanner scanner = new Scanner(System.in);
+ while (true) {
+ // 此处应添加初始界面(登录、注册界面)函数
+ if (registerButtonPressed()) {
+ // 此处添加加载注册界面函数
+ if (!LoginAndRegister.register()) {
+ continue;
+ }
+ } else {
+ if (!LoginAndRegister.login()) {
+ continue;
+ }
+ }
+ while (true) {
+ // 此处添加小学、初中、高中选择界面函数 (修改密码按钮也应在此界面)
+ if (changePasswordButtonPressed()) {
+ ChangePassword();
+ } else {
+ while (true) {
+ // 此处添加小学、初中、高中按钮点击响应函数,creatGenerate函数,返回值为QuestionGenerator
+ QuestionGenerator generator = null;
+ // 出题数目页面
+ String input = " ";
+ try {
+ int count = Integer.parseInt(input);
+ if (count < 10 || count > 30) {
+ System.out.println("题目数量必须在10-30之间!");
+ continue;
+ }
+ try {
+ handleMultipleChoiceGeneration(generator, count); // 调用生成题目函数
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ } catch (NumberFormatException e) {
+ System.out.println("请输入数字!");
+ continue;
+ }
+ System.out.println(score);
+ score = 0;
+ }
+ }
+ }
+ }
+ }
+
+ // ... (handleLevelSwitch 和 createGenerator 保持不变)
+
+ // 处理选择题生成,进行本次生成内的去重工作
+ private void handleMultipleChoiceGeneration(QuestionGenerator generator, int count) throws IOException {
+
+ ChoiceGenerator mcGenerator = new ChoiceGenerator(generator);
+
+ // 生成选择题列表,内部已处理去重
+ List finalQuestions = mcGenerator.generateMultipleChoiceQuestions(count);
+ int rightCount = 0;
+ // 显示选择题
+ for (int i = 0; i < finalQuestions.size(); i++) {
+ MultipleChoiceQuestion mcq = finalQuestions.get(i);
+ System.out.println((i + 1) + ". " + mcq.getQuestion());
+ List options = mcq.getOptions();
+ int correctAnswerIndex = mcq.getCorrectIndex();
+ for (int j = 0; j < options.size(); j++) {
+ // 此处可以转换成ABCD
+ System.out.println(" " + (char)('A' + j) + ". " + options.get(j));
+ }
+ // 显示选择题界面,等待用户选择
+ while (true) {
+ if(nextButtonPressed()) {
+ if (isCorrectAnswer (input, correctAnswerIndex, options)) {
+ rightCount++;
+ System.out.println("正确!");
+ } else {
+ System.out.println("答案错误!");
+ }
+ break;
+ }
+ }
+ }
+ setScore(caculateScore(rightCount, finalQuestions.size()));
+ }
+
+ public QuestionGenerator createGenerator(String level) {
+ switch (level) {
+ case "小学":
+ return new PrimarySchoolGenerator();
+ case "初中":
+ return new JuniorHighGenerator();
+ case "高中":
+ return new SeniorHighGenerator();
+ default:
+ return null;
+ }
+ }
+
+ public boolean isCorrectAnswer(String input, int correctAnswerIndex, List options) {
+ if(input.equals(String.valueOf(options.get(correctAnswerIndex)))) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public double caculateScore(int rightCount, int totalCount) {
+ return rightCount / (double) totalCount;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/system/LogSystem.java b/src/main/java/com/ybw/mathapp/system/LogSystem.java
new file mode 100644
index 0000000..c867a63
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/system/LogSystem.java
@@ -0,0 +1,39 @@
+package com.ybw.mathapp.system;
+
+import java.util.HashMap;
+import java.util.Scanner;
+import com.ybw.mathapp.entity.User;
+
+public class LogSystem {
+ private final HashMap userHashMap = new HashMap<>();
+ public void userHashMapInit() {
+ // 小学
+ userHashMap.put("1798231811@qq.com", new User("1798231811@qq.com", "1234567"));
+
+ }
+
+ public User login() {
+ System.out.println("请输入用户名和密码,两者之间用空格隔开,用户名为邮箱账号");
+ while(true) {
+ Scanner scanner = new Scanner(System.in);
+ String[] info = scanner.nextLine().split(" ");
+ if(info.length != 2) {
+ System.out.println("请输入正确格式");
+ } else {
+ String name = info[0];
+ String password = info[1];
+ User user = userHashMap.get(name);
+ if (user == null) {
+ System.out.println("邮箱未注册");
+ }
+ else if (!user.getPassword().equals(password)) {
+ System.out.println("请输入正确的用户名、密码");
+ }
+ else {
+ System.out.println("当前选择为" + user.getLevel() + "出题");
+ return user;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/ybw/mathapp/util/EmailService.java b/src/main/java/com/ybw/mathapp/util/EmailService.java
new file mode 100644
index 0000000..7de816d
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/util/EmailService.java
@@ -0,0 +1,158 @@
+package com.ybw.mathapp.util;
+
+import com.ybw.mathapp.config.EmailConfig;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Random;
+import jakarta.mail.Authenticator;
+import jakarta.mail.Message;
+import jakarta.mail.MessagingException;
+import jakarta.mail.PasswordAuthentication;
+import jakarta.mail.Session;
+import jakarta.mail.Transport;
+import jakarta.mail.internet.InternetAddress;
+import jakarta.mail.internet.MimeMessage;
+
+public class EmailService {
+ private static Map verificationCodes = new HashMap<>();
+
+ // 验证码信息内部类
+ private static class VerificationCodeInfo {
+ String code;
+ long timestamp;
+
+ VerificationCodeInfo(String code, long timestamp) {
+ this.code = code;
+ this.timestamp = timestamp;
+ }
+ }
+
+ // 生成6位随机验证码
+ public static String generateVerificationCode() {
+ Random random = new Random();
+ int code = 100000 + random.nextInt(900000);
+ return String.valueOf(code);
+ }
+
+ // 发送真实邮件验证码
+ public static boolean sendVerificationCode(String recipientEmail, String code) {
+ try {
+ // 创建邮件会话
+ Properties props = new Properties();
+ props.put("mail.smtp.host", EmailConfig.SMTP_HOST);
+ props.put("mail.smtp.port", EmailConfig.SMTP_PORT);
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.starttls.enable", "true");
+ props.put("mail.smtp.ssl.protocols", "TLSv1.2");
+
+ // 创建认证器
+ Authenticator auth = new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(
+ EmailConfig.SENDER_EMAIL,
+ EmailConfig.SENDER_PASSWORD
+ );
+ }
+ };
+
+ Session session = Session.getInstance(props, auth);
+
+ // 创建邮件消息
+ Message message = new MimeMessage(session);
+ message.setFrom(new InternetAddress(EmailConfig.SENDER_EMAIL));
+ message.setRecipients(Message.RecipientType.TO,
+ InternetAddress.parse(recipientEmail));
+ message.setSubject(EmailConfig.EMAIL_SUBJECT);
+
+ // 创建邮件内容
+ String emailContent = createEmailContent(code);
+ message.setContent(emailContent, "text/html; charset=utf-8");
+
+ // 发送邮件
+ Transport.send(message);
+
+ // 存储验证码信息
+ verificationCodes.put(recipientEmail,
+ new VerificationCodeInfo(code, System.currentTimeMillis()));
+
+ System.out.println("验证码已发送到邮箱: " + recipientEmail);
+ return true;
+
+ } catch (MessagingException e) {
+ System.err.println("发送邮件失败: " + e.getMessage());
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ // 创建HTML格式的邮件内容
+ private static String createEmailContent(String code) {
+ return "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "
您好!
" +
+ "
您正在注册账户,验证码如下:
" +
+ "
" + code + "
" +
+ "
验证码有效期为 " + EmailConfig.CODE_EXPIRY_MINUTES + " 分钟,请勿泄露给他人。
" +
+ "
如果这不是您本人的操作,请忽略此邮件。
" +
+ "
" +
+ "" +
+ "
" +
+ "" +
+ "";
+ }
+
+ // 验证验证码
+ public static boolean verifyCode(String email, String inputCode) {
+ VerificationCodeInfo codeInfo = verificationCodes.get(email);
+ if (codeInfo == null) {
+ return false;
+ }
+
+ // 检查验证码是否过期
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - codeInfo.timestamp > EmailConfig.CODE_EXPIRY_MINUTES * 60 * 1000) {
+ verificationCodes.remove(email);
+ return false;
+ }
+
+ return codeInfo.code.equals(inputCode);
+ }
+
+ // 清理过期的验证码(可选)
+ public static void cleanupExpiredCodes() {
+ long currentTime = System.currentTimeMillis();
+ Iterator> iterator =
+ verificationCodes.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry entry = iterator.next();
+ if (currentTime - entry.getValue().timestamp >
+ EmailConfig.CODE_EXPIRY_MINUTES * 60 * 1000) {
+ iterator.remove();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/ybw/mathapp/util/LoginFileUtils.java b/src/main/java/com/ybw/mathapp/util/LoginFileUtils.java
new file mode 100644
index 0000000..f99a56f
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/util/LoginFileUtils.java
@@ -0,0 +1,74 @@
+package com.ybw.mathapp.util;
+
+import com.ybw.mathapp.entity.User;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class LoginFileUtils {
+ private static final String USER_FILE = "users.txt";
+
+ // 读取所有用户
+ // FileUtils.java 中的 readUsers 方法(简化版)
+ public static List readUsers() {
+ List users = new ArrayList<>();
+ File file = new File(USER_FILE);
+
+ if (!file.exists()) {
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ System.err.println("创建用户文件失败: " + e.getMessage());
+ }
+ return users;
+ }
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty()) continue;
+
+ User user = User.fromString(line);
+ if (user != null) {
+ users.add(user);
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("读取用户文件失败: " + e.getMessage());
+ }
+ return users;
+ }
+
+ // 保存用户到文件
+ public static void saveUser(User user) {
+ try (PrintWriter writer = new PrintWriter(new FileWriter(USER_FILE, true))) {
+ writer.println(user.toString());
+ } catch (IOException e) {
+ System.err.println("保存用户信息失败: " + e.getMessage());
+ }
+ }
+
+ // 检查邮箱是否已注册
+ public static boolean isEmailRegistered(String email) {
+ List users = readUsers();
+ for (User user : users) {
+ if (user.getEmail().equalsIgnoreCase(email)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // 验证用户登录
+ public static boolean validateUser(String email, String password) {
+ List users = readUsers();
+ for (User user : users) {
+ if (user.getEmail().equalsIgnoreCase(email) &&
+ user.getPassword().equals(password)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 2b5b80f..6902ee7 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -10,6 +10,7 @@ module com.ybw.mathapp {
requires org.kordamp.bootstrapfx.core;
requires eu.hansolo.tilesfx;
requires com.almasb.fxgl.all;
+ requires jakarta.mail;
opens com.ybw.mathapp to javafx.fxml;
exports com.ybw.mathapp;
diff --git a/src/main/resources/META-INF/javamail.default.address.map b/src/main/resources/META-INF/javamail.default.address.map
new file mode 100644
index 0000000..e3baea0
--- /dev/null
+++ b/src/main/resources/META-INF/javamail.default.address.map
@@ -0,0 +1,2 @@
+rfc822=smtp
+smtp=smtp
\ No newline at end of file
--
2.34.1
From e2e0d99656380f14feeadcfa5a792dff4bcb3aff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=8E=96=E5=85=AE=E5=86=89?=
<15550273+wang-shengfa-11@user.noreply.gitee.com>
Date: Mon, 6 Oct 2025 23:14:48 +0800
Subject: [PATCH 02/16] =?UTF-8?q?v1.0=20=E5=A4=A7=E6=A6=82=E7=9A=84?=
=?UTF-8?q?=E6=A1=86=E6=9E=B6=E5=B7=B2=E7=BB=8F=E5=86=99=E5=A5=BD=EF=BC=8C?=
=?UTF-8?q?=E6=9C=AA=E4=B8=8E=E5=90=8E=E7=AB=AF=E5=AF=B9=E6=8E=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 4 +-
src/main/java/com/wsf/mathapp/App.java | 97 -----------
src/main/java/com/wsf/mathapp/Main.java | 17 ++
.../com/wsf/mathapp/controller/Login.fxml | 23 ---
.../mathapp/controller/LoginController.java | 75 --------
.../wsf/mathapp/controller/SceneManager.java | 69 ++++++++
.../com/wsf/mathapp/service/EmailService.java | 79 +++++++++
.../wsf/mathapp/service/QuestionService.java | 34 ++++
.../com/wsf/mathapp/service/UserService.java | 81 +++++++++
.../wsf/mathapp/view/LevelSelectionView.java | 77 +++++++++
.../java/com/wsf/mathapp/view/LoginView.java | 97 +++++++++++
.../com/wsf/mathapp/view/MainMenuView.java | 112 ++++++++++++
.../wsf/mathapp/view/QuestionCountView.java | 100 +++++++++++
.../java/com/wsf/mathapp/view/QuizView.java | 148 ++++++++++++++++
.../com/wsf/mathapp/view/RegisterView.java | 160 ++++++++++++++++++
.../java/com/wsf/mathapp/view/ResultView.java | 102 +++++++++++
.../java/com/ybw/mathapp/entity/User.java | 9 +-
.../com/ybw/mathapp/service/FileHandler.java | 2 +-
.../mathapp/service/QuestionDeduplicator.java | 2 +-
19 files changed, 1087 insertions(+), 201 deletions(-)
delete mode 100644 src/main/java/com/wsf/mathapp/App.java
create mode 100644 src/main/java/com/wsf/mathapp/Main.java
delete mode 100644 src/main/java/com/wsf/mathapp/controller/Login.fxml
delete mode 100644 src/main/java/com/wsf/mathapp/controller/LoginController.java
create mode 100644 src/main/java/com/wsf/mathapp/controller/SceneManager.java
create mode 100644 src/main/java/com/wsf/mathapp/service/EmailService.java
create mode 100644 src/main/java/com/wsf/mathapp/service/QuestionService.java
create mode 100644 src/main/java/com/wsf/mathapp/service/UserService.java
create mode 100644 src/main/java/com/wsf/mathapp/view/LevelSelectionView.java
create mode 100644 src/main/java/com/wsf/mathapp/view/LoginView.java
create mode 100644 src/main/java/com/wsf/mathapp/view/MainMenuView.java
create mode 100644 src/main/java/com/wsf/mathapp/view/QuestionCountView.java
create mode 100644 src/main/java/com/wsf/mathapp/view/QuizView.java
create mode 100644 src/main/java/com/wsf/mathapp/view/RegisterView.java
create mode 100644 src/main/java/com/wsf/mathapp/view/ResultView.java
diff --git a/pom.xml b/pom.xml
index 56d7799..67d77d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -156,14 +156,14 @@
0.0.8
- com.wsf.mathapp.App
+ com.wsf.mathapp.Main
default-cli
- com.wsf.mathapp.App
+ com.wsf.mathapp.Main
diff --git a/src/main/java/com/wsf/mathapp/App.java b/src/main/java/com/wsf/mathapp/App.java
deleted file mode 100644
index 8df2322..0000000
--- a/src/main/java/com/wsf/mathapp/App.java
+++ /dev/null
@@ -1,97 +0,0 @@
-// src/main/java/com/wsf/mathapp/App.java
-package com.wsf.mathapp;
-
-import com.wsf.mathapp.controller.LoginController;
-import javafx.application.Application;
-import javafx.fxml.FXMLLoader;
-import javafx.scene.Parent;
-import javafx.scene.Scene;
-import javafx.stage.Stage;
-import java.io.IOException;
-
-public class App extends Application {
-
- private Stage primaryStage;
-
- @Override
- public void start(Stage primaryStage) throws IOException {
- this.primaryStage = primaryStage;
- this.primaryStage.setTitle("数学学习软件");
-
- showLoginScene();
- }
-
- public void showLoginScene() throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/login.fxml"));
- Parent root = loader.load();
- LoginController controller = loader.getController();
- controller.setApp(this);
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- primaryStage.show();
- }
-
- public void showRegisterScene() throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/register.fxml"));
- Parent root = loader.load();
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public void showSetPasswordScene(String email) throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/set_password.fxml"));
- Parent root = loader.load();
- // Pass email to SetPasswordController if needed
- // SetPasswordController controller = loader.getController();
- // controller.setEmail(email);
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public void showGradeSelectionScene() throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/grade_selection.fxml"));
- Parent root = loader.load();
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public void showQuestionScene(java.util.List questions) throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/question.fxml"));
- Parent root = loader.load();
- com.wsf.mathapp.controller.QuestionController controller = loader.getController();
- controller.setApp(this);
- controller.setQuestions(questions);
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public void showResultScene(com.wsf.mathapp.model.QuizResult result) throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/result.fxml"));
- Parent root = loader.load();
- com.wsf.mathapp.controller.ResultController controller = loader.getController();
- controller.setApp(this);
- controller.setResult(result);
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public Stage getPrimaryStage() {
- return primaryStage;
- }
-
- public static void main(String[] args) {
- launch();
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/Main.java b/src/main/java/com/wsf/mathapp/Main.java
new file mode 100644
index 0000000..21c2960
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/Main.java
@@ -0,0 +1,17 @@
+package com.wsf.mathapp;
+
+import com.wsf.mathapp.controller.SceneManager;
+import javafx.application.Application;
+import javafx.stage.Stage;
+
+public class Main extends Application {
+ @Override
+ public void start(Stage primaryStage) {
+ SceneManager sceneManager = new SceneManager(primaryStage);
+ sceneManager.showLoginView();
+ }
+
+ public static void main(String[] args) {
+ launch(args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/controller/Login.fxml b/src/main/java/com/wsf/mathapp/controller/Login.fxml
deleted file mode 100644
index 14309c8..0000000
--- a/src/main/java/com/wsf/mathapp/controller/Login.fxml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/java/com/wsf/mathapp/controller/LoginController.java b/src/main/java/com/wsf/mathapp/controller/LoginController.java
deleted file mode 100644
index c4cbbad..0000000
--- a/src/main/java/com/wsf/mathapp/controller/LoginController.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// src/main/java/com/wsf/mathapp/controller/LoginController.java
-package com.wsf.mathapp.controller;
-
-import com.wsf.mathapp.App;
-import com.wsf.mathapp.model.User;
-import com.wsf.mathapp.service.BackendService; // 需要后端实现
-import com.wsf.mathapp.util.ValidationUtils;
-import javafx.fxml.FXML;
-import javafx.scene.control.Button;
-import javafx.scene.control.Label;
-import javafx.scene.control.PasswordField;
-import javafx.scene.control.TextField;
-
-public class LoginController {
-
- @FXML private TextField emailField;
- @FXML private PasswordField passwordField;
- @FXML private Button loginButton;
- @FXML private Button registerButton;
- @FXML private Label messageLabel;
-
- private App app;
- private BackendService backendService; // 需要注入后端服务实现
-
- public void setApp(App app) {
- this.app = app;
- // 示例:注入后端服务实现
- // this.backendService = new MyBackendServiceImpl(); // 你需要后端同学提供实现
- }
-
- @FXML
- private void handleLogin() {
- String email = emailField.getText().trim();
- String password = passwordField.getText();
-
- // 1. 验证邮箱格式 (需求:对注册邮箱进行格式校验)
- if (!ValidationUtils.isValidEmail(email)) {
- messageLabel.setText("请输入有效的邮箱地址");
- return;
- }
-
- // 2. 验证密码是否为空 (需求:不输入密码也能直接进入主功能页面 -> 修复)
- if (password.isEmpty()) {
- messageLabel.setText("请输入密码");
- return;
- }
-
- // 3. 调用后端服务进行登录
- User user = backendService.loginUser(email, password);
- if (user != null) {
- messageLabel.setText("登录成功");
- try {
- // 登录成功后跳转到年级选择界面
- app.showGradeSelectionScene();
- } catch (Exception e) {
- e.printStackTrace();
- messageLabel.setText("界面跳转失败");
- }
- } else {
- // 4. 登录失败,显示错误信息 (需求:无法使用已注册用户登陆)
- messageLabel.setText("邮箱或密码错误");
- }
- }
-
- @FXML
- private void handleRegister() {
- try {
- // 点击注册按钮,跳转到注册界面
- app.showRegisterScene();
- } catch (Exception e) {
- e.printStackTrace();
- messageLabel.setText("界面跳转失败");
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/controller/SceneManager.java b/src/main/java/com/wsf/mathapp/controller/SceneManager.java
new file mode 100644
index 0000000..137578d
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/controller/SceneManager.java
@@ -0,0 +1,69 @@
+package com.wsf.mathapp.controller;
+
+import com.wsf.mathapp.view.*;
+import javafx.stage.Stage;
+
+public class SceneManager {
+ private Stage primaryStage;
+ private LoginView loginView;
+ private RegisterView registerView;
+ private MainMenuView mainMenuView;
+ private LevelSelectionView levelSelectionView;
+ private QuestionCountView questionCountView;
+ private QuizView quizView;
+ private ResultView resultView;
+
+ public SceneManager(Stage primaryStage) {
+ this.primaryStage = primaryStage;
+ this.primaryStage.setTitle("数学学习软件");
+ this.primaryStage.setResizable(false);
+
+ // 初始化所有视图
+ this.loginView = new LoginView(this);
+ this.registerView = new RegisterView(this);
+ this.mainMenuView = new MainMenuView(this);
+ this.levelSelectionView = new LevelSelectionView(this);
+ this.questionCountView = new QuestionCountView(this);
+ this.quizView = new QuizView(this);
+ this.resultView = new ResultView(this);
+ }
+
+ public void showLoginView() {
+ primaryStage.setScene(loginView.getScene());
+ primaryStage.show();
+ }
+
+ public void showRegisterView() {
+ primaryStage.setScene(registerView.getScene());
+ }
+
+ public void showMainMenuView() {
+ primaryStage.setScene(mainMenuView.getScene());
+ }
+
+ public void showLevelSelectionView() {
+ primaryStage.setScene(levelSelectionView.getScene());
+ }
+
+ public void showQuestionCountView() {
+ primaryStage.setScene(questionCountView.getScene());
+ }
+
+ public void showQuizView() {
+ primaryStage.setScene(quizView.getScene());
+ }
+
+ public void showResultView(double score) {
+ resultView.setScore(score);
+ primaryStage.setScene(resultView.getScene());
+ }
+
+ // Getter methods for views
+ public LoginView getLoginView() { return loginView; }
+ public RegisterView getRegisterView() { return registerView; }
+ public MainMenuView getMainMenuView() { return mainMenuView; }
+ public LevelSelectionView getLevelSelectionView() { return levelSelectionView; }
+ public QuestionCountView getQuestionCountView() { return questionCountView; }
+ public QuizView getQuizView() { return quizView; }
+ public ResultView getResultView() { return resultView; }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/service/EmailService.java b/src/main/java/com/wsf/mathapp/service/EmailService.java
new file mode 100644
index 0000000..84baaaf
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/service/EmailService.java
@@ -0,0 +1,79 @@
+package com.wsf.mathapp.service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 前端的模拟邮件服务
+ * 在实际部署时,可以替换为真实的邮件服务
+ */
+public class EmailService {
+ private static Map verificationCodes = new HashMap<>();
+
+ private static class VerificationCodeInfo {
+ String code;
+ long timestamp;
+
+ VerificationCodeInfo(String code, long timestamp) {
+ this.code = code;
+ this.timestamp = timestamp;
+ }
+ }
+
+ // 生成6位随机验证码
+ public static String generateVerificationCode() {
+ //Random random = new Random();
+ //int code = 100000 + random.nextInt(900000);
+ //return String.valueOf(code);
+ return "1";
+ }
+
+ // 模拟发送验证码
+ public static boolean sendVerificationCode(String recipientEmail, String code) {
+ try {
+ // 在实际环境中,这里会发送真实的邮件
+ // 现在只是模拟发送,将验证码存储起来
+ System.out.println("模拟发送验证码到: " + recipientEmail + ", 验证码: " + code);
+
+ // 存储验证码信息
+ verificationCodes.put(recipientEmail,
+ new VerificationCodeInfo(code, System.currentTimeMillis()));
+
+ // 模拟网络延迟
+ Thread.sleep(1000);
+
+ return true;
+
+ } catch (Exception e) {
+ System.err.println("发送邮件失败: " + e.getMessage());
+ // 在开发环境中,即使失败也存储验证码用于测试
+ verificationCodes.put(recipientEmail,
+ new VerificationCodeInfo(code, System.currentTimeMillis()));
+ return true;
+ }
+ }
+
+ // 验证验证码
+ public static boolean verifyCode(String email, String inputCode) {
+ VerificationCodeInfo codeInfo = verificationCodes.get(email);
+ if (codeInfo == null) {
+ return false;
+ }
+
+ // 检查验证码是否过期(5分钟)
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - codeInfo.timestamp > 5 * 60 * 1000) {
+ verificationCodes.remove(email);
+ return false;
+ }
+
+ return codeInfo.code.equals(inputCode);
+ }
+
+ // 清理过期的验证码
+ public static void cleanupExpiredCodes() {
+ long currentTime = System.currentTimeMillis();
+ verificationCodes.entrySet().removeIf(entry ->
+ currentTime - entry.getValue().timestamp > 5 * 60 * 1000);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/service/QuestionService.java b/src/main/java/com/wsf/mathapp/service/QuestionService.java
new file mode 100644
index 0000000..cdbcc2b
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/service/QuestionService.java
@@ -0,0 +1,34 @@
+package com.wsf.mathapp.service;
+
+import com.ybw.mathapp.service.ChoiceGenerator;
+import com.ybw.mathapp.service.PrimarySchoolGenerator;
+import com.ybw.mathapp.service.JuniorHighGenerator;
+import com.ybw.mathapp.service.SeniorHighGenerator;
+import com.ybw.mathapp.service.QuestionGenerator;
+import java.util.List;
+
+public class QuestionService {
+
+ public QuestionGenerator createGenerator(String level) {
+ switch (level) {
+ case "小学":
+ return new PrimarySchoolGenerator();
+ case "初中":
+ return new JuniorHighGenerator();
+ case "高中":
+ return new SeniorHighGenerator();
+ default:
+ return null;
+ }
+ }
+
+ public List generateQuestions(String level, int count) {
+ QuestionGenerator generator = createGenerator(level);
+ if (generator == null) {
+ throw new IllegalArgumentException("不支持的题目级别: " + level);
+ }
+
+ ChoiceGenerator choiceGenerator = new ChoiceGenerator(generator);
+ return choiceGenerator.generateMultipleChoiceQuestions(count);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/service/UserService.java b/src/main/java/com/wsf/mathapp/service/UserService.java
new file mode 100644
index 0000000..df20881
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/service/UserService.java
@@ -0,0 +1,81 @@
+package com.wsf.mathapp.service;
+
+import com.ybw.mathapp.util.LoginFileUtils;
+
+public class UserService {
+
+ public boolean register(String email, String password, String confirmPassword) {
+ // 验证邮箱格式
+ if (!isValidEmail(email)) {
+ return false;
+ }
+
+ // 检查邮箱是否已注册
+ if (LoginFileUtils.isEmailRegistered(email)) {
+ return false;
+ }
+
+ // 验证密码格式和匹配
+ if (!isValidPassword(password, confirmPassword)) {
+ return false;
+ }
+
+ // 创建用户并保存
+ com.ybw.mathapp.entity.User user = new com.ybw.mathapp.entity.User(email, password);
+ LoginFileUtils.saveUser(user);
+ return true;
+ }
+
+ public boolean login(String email, String password) {
+ return LoginFileUtils.validateUser(email, password);
+ }
+
+ public boolean changePassword(String email, String oldPassword, String newPassword, String confirmPassword) {
+ // 验证旧密码
+ if (!LoginFileUtils.validateUser(email, oldPassword)) {
+ return false;
+ }
+
+ // 验证新密码格式和匹配
+ if (!isValidPassword(newPassword, confirmPassword)) {
+ return false;
+ }
+
+ // 更新密码逻辑
+ return updateUserPassword(email, newPassword);
+ }
+
+ private boolean updateUserPassword(String email, String newPassword) {
+ // 实现密码更新逻辑
+ // 这里需要扩展后端的LoginFileUtils来支持密码更新
+ return true;
+ }
+
+ private boolean isValidEmail(String email) {
+ if (email == null || email.trim().isEmpty()) {
+ return false;
+ }
+ return email.contains("@") && email.contains(".");
+ }
+
+ private boolean isValidPassword(String password, String confirmPassword) {
+ if (password == null || password.length() < 6 || password.length() > 10) {
+ return false;
+ }
+
+ // 验证密码格式:必须包含大小写字母和数字
+ boolean hasLower = false, hasUpper = false, hasDigit = false;
+ for (char c : password.toCharArray()) {
+ if (Character.isLowerCase(c)) hasLower = true;
+ if (Character.isUpperCase(c)) hasUpper = true;
+ if (Character.isDigit(c)) hasDigit = true;
+ }
+
+ if (!hasLower || !hasUpper || !hasDigit) {
+ return false;
+ }
+
+ // 验证两次输入是否一致
+ return password.equals(confirmPassword);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java b/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java
new file mode 100644
index 0000000..1fcd7c4
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java
@@ -0,0 +1,77 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+
+public class LevelSelectionView {
+ private Scene scene;
+ private SceneManager sceneManager;
+
+ public LevelSelectionView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(20);
+ root.setPadding(new Insets(40));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("选择题目级别");
+ titleLabel.setFont(Font.font(24));
+
+ Button primaryButton = new Button("小学题目");
+ primaryButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ primaryButton.setPrefSize(200, 50);
+
+ Button juniorButton = new Button("初中题目");
+ juniorButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 14px;");
+ juniorButton.setPrefSize(200, 50);
+
+ Button seniorButton = new Button("高中题目");
+ seniorButton.setStyle("-fx-background-color: #9C27B0; -fx-text-fill: white; -fx-font-size: 14px;");
+ seniorButton.setPrefSize(200, 50);
+
+ Button backButton = new Button("返回");
+ backButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white;");
+ backButton.setPrefSize(200, 40);
+
+ primaryButton.setOnAction(e -> {
+ String selectedLevel = "小学";
+ sceneManager.getQuestionCountView().setLevel(selectedLevel);
+ sceneManager.showQuestionCountView();
+ });
+
+ juniorButton.setOnAction(e -> {
+ String selectedLevel = "初中";
+ sceneManager.getQuestionCountView().setLevel(selectedLevel);
+ sceneManager.showQuestionCountView();
+ });
+
+ seniorButton.setOnAction(e -> {
+ String selectedLevel = "高中";
+ sceneManager.getQuestionCountView().setLevel(selectedLevel);
+ sceneManager.showQuestionCountView();
+ });
+
+ backButton.setOnAction(e -> {
+ sceneManager.showMainMenuView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, primaryButton, juniorButton, seniorButton, backButton
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/LoginView.java b/src/main/java/com/wsf/mathapp/view/LoginView.java
new file mode 100644
index 0000000..aa5f659
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/LoginView.java
@@ -0,0 +1,97 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import com.wsf.mathapp.service.UserService;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+
+public class LoginView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private UserService userService;
+
+ public LoginView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ this.userService = new UserService();
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(20);
+ root.setPadding(new Insets(40));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("数学学习软件");
+ titleLabel.setFont(Font.font(24));
+
+ Label subtitleLabel = new Label("用户登录");
+ subtitleLabel.setFont(Font.font(18));
+
+ TextField emailField = new TextField();
+ emailField.setPromptText("请输入邮箱");
+ emailField.setMaxWidth(300);
+ emailField.setPrefHeight(40);
+
+ PasswordField passwordField = new PasswordField();
+ passwordField.setPromptText("请输入密码");
+ passwordField.setMaxWidth(300);
+ passwordField.setPrefHeight(40);
+
+ Button loginButton = new Button("登录");
+ loginButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ loginButton.setPrefSize(300, 40);
+
+ Button registerButton = new Button("注册账号");
+ registerButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 14px;");
+ registerButton.setPrefSize(300, 40);
+
+ Label statusLabel = new Label();
+
+ loginButton.setOnAction(e -> {
+ String email = emailField.getText().trim();
+ String password = passwordField.getText();
+
+ if (email.isEmpty() || password.isEmpty()) {
+ showError(statusLabel, "邮箱和密码不能为空!");
+ return;
+ }
+
+ boolean success = userService.login(email, password);
+ if (success) {
+ showSuccess(statusLabel, "登录成功!");
+ sceneManager.showMainMenuView();
+ } else {
+ showError(statusLabel, "邮箱或密码错误!");
+ }
+ });
+
+ registerButton.setOnAction(e -> {
+ sceneManager.showRegisterView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, subtitleLabel, emailField, passwordField,
+ loginButton, registerButton, statusLabel
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ private void showError(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: red;");
+ }
+
+ private void showSuccess(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: green;");
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/MainMenuView.java b/src/main/java/com/wsf/mathapp/view/MainMenuView.java
new file mode 100644
index 0000000..67d6af2
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/MainMenuView.java
@@ -0,0 +1,112 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+
+public class MainMenuView {
+ private Scene scene;
+ private SceneManager sceneManager;
+
+ public MainMenuView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(30);
+ root.setPadding(new Insets(50));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("数学学习软件");
+ titleLabel.setFont(Font.font(28));
+
+ Label welcomeLabel = new Label("欢迎使用数学学习软件");
+ welcomeLabel.setFont(Font.font(18));
+
+ Button startButton = new Button("开始练习");
+ startButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 16px;");
+ startButton.setPrefSize(200, 50);
+
+ Button changePasswordButton = new Button("修改密码");
+ changePasswordButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 16px;");
+ changePasswordButton.setPrefSize(200, 50);
+
+ Button logoutButton = new Button("退出登录");
+ logoutButton.setStyle("-fx-background-color: #f44336; -fx-text-fill: white; -fx-font-size: 16px;");
+ logoutButton.setPrefSize(200, 50);
+
+ startButton.setOnAction(e -> {
+ sceneManager.showLevelSelectionView();
+ });
+
+ changePasswordButton.setOnAction(e -> {
+ showChangePasswordDialog();
+ });
+
+ logoutButton.setOnAction(e -> {
+ sceneManager.showLoginView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, welcomeLabel, startButton,
+ changePasswordButton, logoutButton
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ private void showChangePasswordDialog() {
+ // 创建修改密码对话框
+ javafx.scene.control.Dialog dialog = new javafx.scene.control.Dialog<>();
+ dialog.setTitle("修改密码");
+ dialog.setHeaderText("请输入密码信息");
+
+ // 创建表单
+ javafx.scene.layout.GridPane grid = new javafx.scene.layout.GridPane();
+ grid.setHgap(10);
+ grid.setVgap(10);
+ grid.setPadding(new Insets(20, 150, 10, 10));
+
+ javafx.scene.control.PasswordField oldPassword = new javafx.scene.control.PasswordField();
+ oldPassword.setPromptText("原密码");
+ javafx.scene.control.PasswordField newPassword = new javafx.scene.control.PasswordField();
+ newPassword.setPromptText("新密码");
+ javafx.scene.control.PasswordField confirmPassword = new javafx.scene.control.PasswordField();
+ confirmPassword.setPromptText("确认新密码");
+
+ grid.add(new Label("原密码:"), 0, 0);
+ grid.add(oldPassword, 1, 0);
+ grid.add(new Label("新密码:"), 0, 1);
+ grid.add(newPassword, 1, 1);
+ grid.add(new Label("确认密码:"), 0, 2);
+ grid.add(confirmPassword, 1, 2);
+
+ dialog.getDialogPane().setContent(grid);
+
+ // 添加按钮
+ dialog.getDialogPane().getButtonTypes().addAll(
+ javafx.scene.control.ButtonType.OK,
+ javafx.scene.control.ButtonType.CANCEL
+ );
+
+ dialog.setResultConverter(dialogButton -> {
+ if (dialogButton == javafx.scene.control.ButtonType.OK) {
+ // 这里可以添加修改密码的逻辑
+ System.out.println("修改密码功能待实现");
+ }
+ return null;
+ });
+
+ dialog.showAndWait();
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/QuestionCountView.java b/src/main/java/com/wsf/mathapp/view/QuestionCountView.java
new file mode 100644
index 0000000..60f8ae3
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/QuestionCountView.java
@@ -0,0 +1,100 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+
+public class QuestionCountView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private String level;
+
+ public QuestionCountView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(20);
+ root.setPadding(new Insets(40));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("选择题目数量");
+ titleLabel.setFont(Font.font(20));
+
+ Label levelLabel = new Label();
+ levelLabel.setFont(Font.font(16));
+
+ TextField countField = new TextField();
+ countField.setPromptText("请输入题目数量 (10-30)");
+ countField.setMaxWidth(250);
+ countField.setPrefHeight(40);
+
+ Button startButton = new Button("开始答题");
+ startButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ startButton.setPrefSize(250, 45);
+
+ Button backButton = new Button("返回");
+ backButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white;");
+ backButton.setPrefSize(250, 40);
+
+ Label statusLabel = new Label();
+
+ startButton.setOnAction(e -> {
+ try {
+ String countText = countField.getText().trim();
+ if (countText.isEmpty()) {
+ showError(statusLabel, "请输入题目数量!");
+ return;
+ }
+
+ int count = Integer.parseInt(countText);
+ if (count < 10 || count > 30) {
+ showError(statusLabel, "题目数量必须在10-30之间!");
+ return;
+ }
+
+ // 设置题目数量和级别,然后开始答题
+ sceneManager.getQuizView().setQuizParameters(level, count);
+ sceneManager.showQuizView();
+
+ } catch (NumberFormatException ex) {
+ showError(statusLabel, "请输入有效的数字!");
+ }
+ });
+
+ backButton.setOnAction(e -> {
+ sceneManager.showLevelSelectionView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, levelLabel, countField, startButton, backButton, statusLabel
+ );
+
+ scene = new Scene(root, 400, 400);
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ // 更新界面显示当前选择的级别
+ if (scene != null) {
+ VBox root = (VBox) scene.getRoot();
+ Label levelLabel = (Label) root.getChildren().get(1);
+ levelLabel.setText("当前级别: " + level);
+ levelLabel.setStyle("-fx-text-fill: #2196F3;");
+ }
+ }
+
+ private void showError(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: red;");
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/QuizView.java b/src/main/java/com/wsf/mathapp/view/QuizView.java
new file mode 100644
index 0000000..b25a7c4
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/QuizView.java
@@ -0,0 +1,148 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import com.wsf.mathapp.service.QuestionService;
+import com.ybw.mathapp.service.ChoiceGenerator;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+import java.util.List;
+
+public class QuizView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private QuestionService questionService;
+ private String currentLevel;
+ private int questionCount;
+ private List questions;
+ private int currentQuestionIndex = 0;
+ private int correctAnswers = 0;
+
+ private Label questionLabel;
+ private ToggleGroup optionsGroup;
+ private VBox optionsContainer;
+ private Button nextButton;
+ private Label progressLabel;
+ private Label questionNumberLabel;
+
+ public QuizView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ this.questionService = new QuestionService();
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(20);
+ root.setPadding(new Insets(30));
+ root.setAlignment(Pos.TOP_CENTER);
+
+ progressLabel = new Label();
+ progressLabel.setFont(Font.font(14));
+
+ questionNumberLabel = new Label();
+ questionNumberLabel.setFont(Font.font(16));
+ questionNumberLabel.setStyle("-fx-text-fill: #666;");
+
+ questionLabel = new Label();
+ questionLabel.setFont(Font.font(18));
+ questionLabel.setWrapText(true);
+ questionLabel.setStyle("-fx-padding: 10 0 20 0;");
+
+ optionsGroup = new ToggleGroup();
+ optionsContainer = new VBox(15);
+ optionsContainer.setAlignment(Pos.CENTER_LEFT);
+ optionsContainer.setPadding(new Insets(10));
+
+ nextButton = new Button("下一题");
+ nextButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ nextButton.setPrefSize(150, 40);
+ nextButton.setDisable(true);
+
+ root.getChildren().addAll(progressLabel, questionNumberLabel, questionLabel, optionsContainer, nextButton);
+
+ scene = new Scene(root, 600, 500);
+ }
+
+ public void setQuizParameters(String level, int count) {
+ this.currentLevel = level;
+ this.questionCount = count;
+ this.currentQuestionIndex = 0;
+ this.correctAnswers = 0;
+
+ // 生成题目
+ generateQuestions();
+ showQuestion(0);
+ }
+
+ private void generateQuestions() {
+ questions = questionService.generateQuestions(currentLevel, questionCount);
+ }
+
+ private void showQuestion(int index) {
+ if (index >= questions.size()) {
+ // 所有题目已回答,显示结果
+ double score = (double) correctAnswers / questions.size();
+ sceneManager.showResultView(score);
+ return;
+ }
+
+ ChoiceGenerator.MultipleChoiceQuestion question = questions.get(index);
+
+ // 更新进度和题号
+ progressLabel.setText("进度: " + (index + 1) + "/" + questions.size());
+ questionNumberLabel.setText("第 " + (index + 1) + " 题");
+
+ // 设置题目内容
+ String questionText = question.getQuestion();
+ // 移除末尾的 "="
+ if (questionText.endsWith(" =")) {
+ questionText = questionText.substring(0, questionText.length() - 2);
+ }
+ questionLabel.setText(questionText + " = ?");
+
+ // 清空选项容器
+ optionsContainer.getChildren().clear();
+ optionsGroup = new ToggleGroup();
+
+ // 添加选项
+ List options = question.getOptions();
+ for (int i = 0; i < options.size(); i++) {
+ RadioButton radioButton = new RadioButton((char)('A' + i) + ". " + options.get(i));
+ radioButton.setToggleGroup(optionsGroup);
+ radioButton.setFont(Font.font(14));
+ radioButton.setWrapText(true);
+ radioButton.setPrefWidth(500);
+ optionsContainer.getChildren().add(radioButton);
+ }
+
+ // 设置下一题按钮
+ nextButton.setDisable(true);
+ nextButton.setText(index == questions.size() - 1 ? "提交答案" : "下一题");
+ nextButton.setOnAction(e -> {
+ checkAnswer(question);
+ showQuestion(index + 1);
+ });
+
+ // 启用下一题按钮当选择了一个选项时
+ optionsGroup.selectedToggleProperty().addListener((obs, oldVal, newVal) -> {
+ nextButton.setDisable(newVal == null);
+ });
+ }
+
+ private void checkAnswer(ChoiceGenerator.MultipleChoiceQuestion question) {
+ RadioButton selectedRadioButton = (RadioButton) optionsGroup.getSelectedToggle();
+ if (selectedRadioButton != null) {
+ int selectedIndex = optionsContainer.getChildren().indexOf(selectedRadioButton);
+ if (selectedIndex == question.getCorrectIndex()) {
+ correctAnswers++;
+ }
+ }
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/RegisterView.java b/src/main/java/com/wsf/mathapp/view/RegisterView.java
new file mode 100644
index 0000000..0b2a1a2
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/RegisterView.java
@@ -0,0 +1,160 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import com.wsf.mathapp.service.UserService;
+import com.wsf.mathapp.service.EmailService;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+
+public class RegisterView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private UserService userService;
+
+ public RegisterView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ this.userService = new UserService();
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(15);
+ root.setPadding(new Insets(30));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("用户注册");
+ titleLabel.setFont(Font.font(20));
+
+ TextField emailField = new TextField();
+ emailField.setPromptText("请输入邮箱");
+ emailField.setMaxWidth(300);
+ emailField.setPrefHeight(35);
+
+ Button sendCodeButton = new Button("发送验证码");
+ sendCodeButton.setStyle("-fx-background-color: #FF9800; -fx-text-fill: white;");
+ sendCodeButton.setPrefSize(120, 35);
+
+ TextField codeField = new TextField();
+ codeField.setPromptText("请输入验证码");
+ codeField.setMaxWidth(300);
+ codeField.setPrefHeight(35);
+
+ PasswordField passwordField = new PasswordField();
+ passwordField.setPromptText("请输入密码(6-10位,含大小写字母和数字)");
+ passwordField.setMaxWidth(300);
+ passwordField.setPrefHeight(35);
+
+ PasswordField confirmPasswordField = new PasswordField();
+ confirmPasswordField.setPromptText("请再次输入密码");
+ confirmPasswordField.setMaxWidth(300);
+ confirmPasswordField.setPrefHeight(35);
+
+ Button registerButton = new Button("注册");
+ registerButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ registerButton.setPrefSize(300, 40);
+
+ Button backButton = new Button("返回登录");
+ backButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white;");
+ backButton.setPrefSize(300, 35);
+
+ Label statusLabel = new Label();
+
+ sendCodeButton.setOnAction(e -> {
+ String email = emailField.getText().trim();
+ if (email.isEmpty() || !email.contains("@") || !email.contains(".")) {
+ showError(statusLabel, "请输入有效的邮箱地址!");
+ return;
+ }
+
+ // 生成并发送验证码
+ String verificationCode = EmailService.generateVerificationCode();
+ boolean sent = EmailService.sendVerificationCode(email, verificationCode);
+
+ if (sent) {
+ showSuccess(statusLabel, "验证码已发送到您的邮箱!");
+
+ // 禁用发送按钮60秒
+ sendCodeButton.setDisable(true);
+ startCountdown(sendCodeButton);
+ } else {
+ showError(statusLabel, "发送验证码失败,请稍后重试!");
+ }
+ });
+
+ registerButton.setOnAction(e -> {
+ String email = emailField.getText().trim();
+ String code = codeField.getText().trim();
+ String password = passwordField.getText();
+ String confirmPassword = confirmPasswordField.getText();
+
+ if (email.isEmpty() || code.isEmpty() || password.isEmpty()) {
+ showError(statusLabel, "请填写所有字段!");
+ return;
+ }
+
+ // 验证验证码
+ if (!EmailService.verifyCode(email, code)) {
+ showError(statusLabel, "验证码错误或已过期!");
+ return;
+ }
+
+ boolean success = userService.register(email, password, confirmPassword);
+ if (success) {
+ showSuccess(statusLabel, "注册成功!");
+ sceneManager.showLoginView();
+ } else {
+ showError(statusLabel, "注册失败,请检查信息!");
+ }
+ });
+
+ backButton.setOnAction(e -> {
+ sceneManager.showLoginView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, emailField, sendCodeButton, codeField,
+ passwordField, confirmPasswordField, registerButton,
+ backButton, statusLabel
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ private void startCountdown(Button button) {
+ new Thread(() -> {
+ try {
+ for (int i = 60; i > 0; i--) {
+ int finalI = i;
+ javafx.application.Platform.runLater(() -> {
+ button.setText(finalI + "秒后重发");
+ });
+ Thread.sleep(1000);
+ }
+ javafx.application.Platform.runLater(() -> {
+ button.setText("发送验证码");
+ button.setDisable(false);
+ });
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }).start();
+ }
+
+ private void showError(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: red;");
+ }
+
+ private void showSuccess(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: green;");
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/ResultView.java b/src/main/java/com/wsf/mathapp/view/ResultView.java
new file mode 100644
index 0000000..0b71a26
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/ResultView.java
@@ -0,0 +1,102 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+
+public class ResultView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private double score;
+
+ public ResultView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(30);
+ root.setPadding(new Insets(50));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("答题完成");
+ titleLabel.setFont(Font.font(24));
+
+ Label scoreLabel = new Label();
+ scoreLabel.setFont(Font.font(48));
+
+ Label messageLabel = new Label();
+ messageLabel.setFont(Font.font(18));
+
+ Button restartButton = new Button("再次练习");
+ restartButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 16px;");
+ restartButton.setPrefSize(200, 50);
+
+ Button mainMenuButton = new Button("返回主菜单");
+ mainMenuButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 16px;");
+ mainMenuButton.setPrefSize(200, 50);
+
+ Button exitButton = new Button("退出系统");
+ exitButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white; -fx-font-size: 16px;");
+ exitButton.setPrefSize(200, 50);
+
+ restartButton.setOnAction(e -> {
+ sceneManager.showLevelSelectionView();
+ });
+
+ mainMenuButton.setOnAction(e -> {
+ sceneManager.showMainMenuView();
+ });
+
+ exitButton.setOnAction(e -> {
+ System.exit(0);
+ });
+
+ root.getChildren().addAll(
+ titleLabel, scoreLabel, messageLabel,
+ restartButton, mainMenuButton, exitButton
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ public void setScore(double score) {
+ this.score = score;
+ updateScoreDisplay();
+ }
+
+ private void updateScoreDisplay() {
+ if (scene != null) {
+ VBox root = (VBox) scene.getRoot();
+ Label scoreLabel = (Label) root.getChildren().get(1);
+ Label messageLabel = (Label) root.getChildren().get(2);
+
+ int percentage = (int) (score * 100);
+ scoreLabel.setText(percentage + "分");
+
+ // 根据分数设置不同的颜色和评语
+ if (score >= 0.9) {
+ scoreLabel.setStyle("-fx-text-fill: #4CAF50;");
+ messageLabel.setText("优秀!表现非常出色!");
+ } else if (score >= 0.7) {
+ scoreLabel.setStyle("-fx-text-fill: #FF9800;");
+ messageLabel.setText("良好!继续加油!");
+ } else if (score >= 0.6) {
+ scoreLabel.setStyle("-fx-text-fill: #FFC107;");
+ messageLabel.setText("及格!还有进步空间!");
+ } else {
+ scoreLabel.setStyle("-fx-text-fill: #f44336;");
+ messageLabel.setText("需要多加练习!");
+ }
+ }
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/entity/User.java b/src/main/java/com/ybw/mathapp/entity/User.java
index 91e46c0..0baf6d7 100644
--- a/src/main/java/com/ybw/mathapp/entity/User.java
+++ b/src/main/java/com/ybw/mathapp/entity/User.java
@@ -13,7 +13,7 @@ package com.ybw.mathapp.entity;
public class User {
/** 用户名,不可修改。 */
- // private final String name;
+ private final String name;
/** 邮箱,不可修改。 */
private final String email;
@@ -30,7 +30,8 @@ public class User {
* @param email 邮箱,不能为空 用户名,不能为空
* @param password 用户密码,不能为空
*/
- public User(String email, String password) {
+ public User(String name,String email, String password) {
+ this.name = name;
this.password = password;
this.email = email;
}
@@ -92,4 +93,8 @@ public class User {
}
return null;
}
+
+ public String getName() {
+ return this.name;
+ }
}
diff --git a/src/main/java/com/ybw/mathapp/service/FileHandler.java b/src/main/java/com/ybw/mathapp/service/FileHandler.java
index 8cd486a..c41c4d4 100644
--- a/src/main/java/com/ybw/mathapp/service/FileHandler.java
+++ b/src/main/java/com/ybw/mathapp/service/FileHandler.java
@@ -10,7 +10,7 @@ import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
-import mathpuzzle.entity.User;
+import com.ybw.mathapp.entity.User;
/**
* 文件处理器,负责创建用户目录和保存试卷文件。
diff --git a/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
index b4319ee..cf51e49 100644
--- a/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
+++ b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
@@ -6,7 +6,7 @@ import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
-import mathpuzzle.entity.User;
+import com.ybw.mathapp.entity.User;
/**
* 题目查重器,负责加载历史题目并检查新题目是否重复。
--
2.34.1
From c7763f9ce9aba5ea2dd1c3b12fde98d29ec0eaca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=8E=96=E5=85=AE=E5=86=89?=
<15550273+wang-shengfa-11@user.noreply.gitee.com>
Date: Tue, 7 Oct 2025 19:38:27 +0800
Subject: [PATCH 03/16] =?UTF-8?q?v1.1=20=E5=A4=A7=E6=A6=82=E7=9A=84?=
=?UTF-8?q?=E6=A1=86=E6=9E=B6=E5=B7=B2=E7=BB=8F=E5=86=99=E5=A5=BD=EF=BC=8C?=
=?UTF-8?q?=E4=B8=8E=E5=90=8E=E7=AB=AF=E8=83=BD=E9=83=BD=E5=AF=B9=E6=8E=A5?=
=?UTF-8?q?=EF=BC=8C=E4=BD=86=E5=8A=9F=E8=83=BD=E6=9C=AA=E5=AE=8C=E5=96=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/wsf/mathapp/service/EmailService.java | 79 --------
.../com/wsf/mathapp/service/UserService.java | 3 +-
.../com/wsf/mathapp/view/RegisterView.java | 2 +-
.../com/ybw/mathapp/LoginAndRegister.java | 2 +-
.../java/com/ybw/mathapp/entity/User.java | 71 ++-----
.../service/{Caculate.java => Calculate.java} | 4 +-
.../ybw/mathapp/service/ChoiceGenerator.java | 8 +-
.../com/ybw/mathapp/service/EmailService.java | 149 ++++++++++++++
.../com/ybw/mathapp/service/FileHandler.java | 4 +-
.../service/PrimarySchoolGenerator.java | 4 +-
.../mathapp/service/QuestionDeduplicator.java | 2 +-
.../mathapp/service/SeniorHighGenerator.java | 4 +-
.../ybw/mathapp/service/StartController.java | 186 +++++++++++-------
.../com/ybw/mathapp/system/LogSystem.java | 4 +-
src/main/java/module-info.java | 3 +-
15 files changed, 295 insertions(+), 230 deletions(-)
delete mode 100644 src/main/java/com/wsf/mathapp/service/EmailService.java
rename src/main/java/com/ybw/mathapp/service/{Caculate.java => Calculate.java} (99%)
create mode 100644 src/main/java/com/ybw/mathapp/service/EmailService.java
diff --git a/src/main/java/com/wsf/mathapp/service/EmailService.java b/src/main/java/com/wsf/mathapp/service/EmailService.java
deleted file mode 100644
index 84baaaf..0000000
--- a/src/main/java/com/wsf/mathapp/service/EmailService.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.wsf.mathapp.service;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 前端的模拟邮件服务
- * 在实际部署时,可以替换为真实的邮件服务
- */
-public class EmailService {
- private static Map verificationCodes = new HashMap<>();
-
- private static class VerificationCodeInfo {
- String code;
- long timestamp;
-
- VerificationCodeInfo(String code, long timestamp) {
- this.code = code;
- this.timestamp = timestamp;
- }
- }
-
- // 生成6位随机验证码
- public static String generateVerificationCode() {
- //Random random = new Random();
- //int code = 100000 + random.nextInt(900000);
- //return String.valueOf(code);
- return "1";
- }
-
- // 模拟发送验证码
- public static boolean sendVerificationCode(String recipientEmail, String code) {
- try {
- // 在实际环境中,这里会发送真实的邮件
- // 现在只是模拟发送,将验证码存储起来
- System.out.println("模拟发送验证码到: " + recipientEmail + ", 验证码: " + code);
-
- // 存储验证码信息
- verificationCodes.put(recipientEmail,
- new VerificationCodeInfo(code, System.currentTimeMillis()));
-
- // 模拟网络延迟
- Thread.sleep(1000);
-
- return true;
-
- } catch (Exception e) {
- System.err.println("发送邮件失败: " + e.getMessage());
- // 在开发环境中,即使失败也存储验证码用于测试
- verificationCodes.put(recipientEmail,
- new VerificationCodeInfo(code, System.currentTimeMillis()));
- return true;
- }
- }
-
- // 验证验证码
- public static boolean verifyCode(String email, String inputCode) {
- VerificationCodeInfo codeInfo = verificationCodes.get(email);
- if (codeInfo == null) {
- return false;
- }
-
- // 检查验证码是否过期(5分钟)
- long currentTime = System.currentTimeMillis();
- if (currentTime - codeInfo.timestamp > 5 * 60 * 1000) {
- verificationCodes.remove(email);
- return false;
- }
-
- return codeInfo.code.equals(inputCode);
- }
-
- // 清理过期的验证码
- public static void cleanupExpiredCodes() {
- long currentTime = System.currentTimeMillis();
- verificationCodes.entrySet().removeIf(entry ->
- currentTime - entry.getValue().timestamp > 5 * 60 * 1000);
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/service/UserService.java b/src/main/java/com/wsf/mathapp/service/UserService.java
index df20881..4a2bc61 100644
--- a/src/main/java/com/wsf/mathapp/service/UserService.java
+++ b/src/main/java/com/wsf/mathapp/service/UserService.java
@@ -1,6 +1,7 @@
package com.wsf.mathapp.service;
import com.ybw.mathapp.util.LoginFileUtils;
+import com.ybw.mathapp.entity.User;
public class UserService {
@@ -21,7 +22,7 @@ public class UserService {
}
// 创建用户并保存
- com.ybw.mathapp.entity.User user = new com.ybw.mathapp.entity.User(email, password);
+ User user = new User(email, password);
LoginFileUtils.saveUser(user);
return true;
}
diff --git a/src/main/java/com/wsf/mathapp/view/RegisterView.java b/src/main/java/com/wsf/mathapp/view/RegisterView.java
index 0b2a1a2..58caca6 100644
--- a/src/main/java/com/wsf/mathapp/view/RegisterView.java
+++ b/src/main/java/com/wsf/mathapp/view/RegisterView.java
@@ -2,7 +2,7 @@ package com.wsf.mathapp.view;
import com.wsf.mathapp.controller.SceneManager;
import com.wsf.mathapp.service.UserService;
-import com.wsf.mathapp.service.EmailService;
+import com.ybw.mathapp.service.EmailService;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
diff --git a/src/main/java/com/ybw/mathapp/LoginAndRegister.java b/src/main/java/com/ybw/mathapp/LoginAndRegister.java
index b4b83c8..d71d548 100644
--- a/src/main/java/com/ybw/mathapp/LoginAndRegister.java
+++ b/src/main/java/com/ybw/mathapp/LoginAndRegister.java
@@ -2,7 +2,7 @@ package com.ybw.mathapp;
// UserService.java
import com.ybw.mathapp.entity.User;
-import com.ybw.mathapp.util.EmailService;
+import com.ybw.mathapp.service.EmailService;
import com.ybw.mathapp.util.LoginFileUtils;
import java.util.Scanner;
import java.util.regex.Pattern;
diff --git a/src/main/java/com/ybw/mathapp/entity/User.java b/src/main/java/com/ybw/mathapp/entity/User.java
index 0baf6d7..13cfb82 100644
--- a/src/main/java/com/ybw/mathapp/entity/User.java
+++ b/src/main/java/com/ybw/mathapp/entity/User.java
@@ -1,82 +1,41 @@
package com.ybw.mathapp.entity;
/**
- * 用户实体类,表示系统中的用户信息。
- *
- * 该类包含用户的基本信息,如用户名、密码和学习级别。
- * 用户级别可以是小学、初中或高中。
- *
- * @author 杨博文
- * @version 1.0
- * @since 2025
+ * 用户实体类
*/
public class User {
-
- /** 用户名,不可修改。 */
- private final String name;
-
- /** 邮箱,不可修改。 */
private final String email;
-
- /** 用户密码,不可修改。 */
private final String password;
-
- /** 用户当前的学习级别,可以修改。 */
private String level;
/**
* 构造一个新的用户对象。
*
- * @param email 邮箱,不能为空 用户名,不能为空
+ * @param email 邮箱,不能为空
* @param password 用户密码,不能为空
*/
- public User(String name,String email, String password) {
- this.name = name;
- this.password = password;
+ public User(String email, String password) {
this.email = email;
+ this.password = password;
+ this.level = "小学"; // 默认级别
+ }
+
+ public String getEmail() {
+ return email;
}
- /**
- * 获取用户密码。
- *
- * @return 用户密码
- */
public String getPassword() {
return password;
}
- /**
- * 获取用户当前的学习级别。
- *
- * @return 用户学习级别
- */
public String getLevel() {
return level;
}
- /**
- * 获取用户邮箱。
- *
- * @return 用户邮箱
- */
- public String getEmail() {
- return email;
+ public void setLevel(String level) {
+ this.level = level;
}
- /**
- * 设置用户的学习级别。
- *
- * @param newLevel 新的学习级别,支持"小学"、"初中"、"高中"
- */
- public void setLevel(String newLevel) {
- level = newLevel;
- }
-
- /**
- * 保存邮箱+密码。
- *
- * @return 邮箱+密码
- */
@Override
public String toString() {
return email + "," + password;
@@ -87,14 +46,12 @@ public class User {
return null;
}
- String[] parts = line.split(",", 2); // 最多分割成2部分
+ String[] parts = line.split(",", 2);
if (parts.length == 2) {
return new User(parts[0].trim(), parts[1].trim());
}
return null;
}
- public String getName() {
- return this.name;
- }
-}
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/Caculate.java b/src/main/java/com/ybw/mathapp/service/Calculate.java
similarity index 99%
rename from src/main/java/com/ybw/mathapp/service/Caculate.java
rename to src/main/java/com/ybw/mathapp/service/Calculate.java
index 9592645..334db03 100644
--- a/src/main/java/com/ybw/mathapp/service/Caculate.java
+++ b/src/main/java/com/ybw/mathapp/service/Calculate.java
@@ -10,14 +10,14 @@ import java.util.Stack;
* 用于计算包含四则运算、括号、平方、开根号、三角函数的表达式。
* 注意:三角函数的输入通常认为是度数 (Degree)。
*/
-public class Caculate {
+public class Calculate {
/**
* 计算表达式的值。
* @param parts 表达式分解后的列表,例如 ["(", "2", "+", "3", ")", "*", "4"]
* @return 计算结果
*/
- public double caculate(List parts) {
+ public double calculate(List parts) {
// 将中缀表达式转换为后缀表达式(逆波兰表示法)
List postfix = infixToPostfix(parts);
// 计算后缀表达式的值
diff --git a/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java b/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java
index dae8fc3..af8d43e 100644
--- a/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java
+++ b/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java
@@ -41,7 +41,7 @@ public class ChoiceGenerator {
public List generateMultipleChoiceQuestions(int count) {
List mcQuestions = new ArrayList<>();
Set generatedQuestionTexts = new HashSet<>(); // 用于本次生成过程中的查重
- Caculate calculator = new Caculate(); // 使用计算器实例
+ Calculate calculator = new Calculate(); // 使用计算器实例
int attempts = 0;
int maxAttempts = count * 100; // 设置最大尝试次数,防止无限循环
@@ -94,7 +94,7 @@ public class ChoiceGenerator {
* @param calc 计算器实例
* @return 计算得出的答案。
*/
- private double calculateAnswer(String question, Caculate calc) {
+ private double calculateAnswer(String question, Calculate calc) {
// 移除 " ="
String expression = question.substring(0, question.length() - 2).trim();
// 将表达式字符串分割成部分
@@ -102,11 +102,11 @@ public class ChoiceGenerator {
// 如果 QuestionGenerator 生成的是 "开根号(16)" 或 "(2+3)平方",则按空格分割可能不够
// 但通常,生成器会生成 "开根号 ( 16 )" 或 "开根号 16" 或 "( 2 + 3 ) 平方" 这样的格式
// 按空格分割 ["开根号", "(", "16", ")"] 或 ["开根号", "16"] 或 ["(", "2", "+", "3", ")", "平方"]
- // CaculatePrimary 需要能处理这些格式
+ // CalculatePrimary 需要能处理这些格式
List parts = Arrays.asList(expression.split("\\s+")); // 按空格分割
try {
- return calc.caculate(parts);
+ return calc.calculate(parts);
} catch (Exception e) {
System.err.println("计算表达式失败: " + expression + ", 错误: " + e.getMessage());
return Double.NaN; // 或者抛出异常
diff --git a/src/main/java/com/ybw/mathapp/service/EmailService.java b/src/main/java/com/ybw/mathapp/service/EmailService.java
new file mode 100644
index 0000000..4728a47
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/EmailService.java
@@ -0,0 +1,149 @@
+package com.ybw.mathapp.service;
+
+import com.ybw.mathapp.config.EmailConfig;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Random;
+//import javax.mail.*;
+//import javax.mail.internet.*;
+
+public class EmailService {
+ private static Map verificationCodes = new HashMap<>();
+
+ private static class VerificationCodeInfo {
+ String code;
+ long timestamp;
+
+ VerificationCodeInfo(String code, long timestamp) {
+ this.code = code;
+ this.timestamp = timestamp;
+ }
+ }
+
+ public static String generateVerificationCode() {
+ Random random = new Random();
+ int code = 100000 + random.nextInt(900000);
+ return String.valueOf(code);
+ }
+
+ public static boolean sendVerificationCode(String recipientEmail, String code) {
+ /*
+ try {
+ // 创建邮件会话
+ Properties props = new Properties();
+ props.put("mail.smtp.host", EmailConfig.SMTP_HOST);
+ props.put("mail.smtp.port", EmailConfig.SMTP_PORT);
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.starttls.enable", "true");
+
+ // 创建认证器
+ Authenticator auth = new Authenticator() {
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(
+ EmailConfig.SENDER_EMAIL,
+ EmailConfig.SENDER_PASSWORD
+ );
+ }
+ };
+
+ Session session = Session.getInstance(props, auth);
+
+ // 创建邮件消息
+ Message message = new MimeMessage(session);
+ message.setFrom(new InternetAddress(EmailConfig.SENDER_EMAIL));
+ message.setRecipients(Message.RecipientType.TO,
+ InternetAddress.parse(recipientEmail));
+ message.setSubject(EmailConfig.EMAIL_SUBJECT);
+
+ // 创建邮件内容
+ String emailContent = createEmailContent(code);
+ message.setContent(emailContent, "text/html; charset=utf-8");
+
+ // 发送邮件
+ Transport.send(message);
+
+ // 存储验证码信息
+ verificationCodes.put(recipientEmail,
+ new VerificationCodeInfo(code, System.currentTimeMillis()));
+
+ System.out.println("验证码已发送到邮箱: " + recipientEmail);
+ return true;
+
+ } catch (Exception e) {
+ System.err.println("发送邮件失败: " + e.getMessage());
+ // 在开发环境中,直接返回true用于测试
+ verificationCodes.put(recipientEmail,
+ new VerificationCodeInfo(code, System.currentTimeMillis()));
+ return true;
+ }
+ */
+ return true;
+ }
+
+ private static String createEmailContent(String code) {
+ return "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "
您好!
" +
+ "
您正在注册数学学习软件账户,验证码如下:
" +
+ "
" + code + "
" +
+ "
验证码有效期为 " + EmailConfig.CODE_EXPIRY_MINUTES + " 分钟,请勿泄露给他人。
" +
+ "
如果这不是您本人的操作,请忽略此邮件。
" +
+ "
" +
+ "" +
+ "
" +
+ "" +
+ "";
+ }
+
+ public static boolean verifyCode(String email, String inputCode) {
+ VerificationCodeInfo codeInfo = verificationCodes.get(email);
+ if (codeInfo == null) {
+ return false;
+ }
+
+ // 检查验证码是否过期
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - codeInfo.timestamp > EmailConfig.CODE_EXPIRY_MINUTES * 60 * 1000) {
+ verificationCodes.remove(email);
+ return false;
+ }
+
+ return codeInfo.code.equals(inputCode);
+ }
+
+ public static void cleanupExpiredCodes() {
+ long currentTime = System.currentTimeMillis();
+ Iterator> iterator =
+ verificationCodes.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry entry = iterator.next();
+ if (currentTime - entry.getValue().timestamp >
+ EmailConfig.CODE_EXPIRY_MINUTES * 60 * 1000) {
+ iterator.remove();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/FileHandler.java b/src/main/java/com/ybw/mathapp/service/FileHandler.java
index c41c4d4..b58db18 100644
--- a/src/main/java/com/ybw/mathapp/service/FileHandler.java
+++ b/src/main/java/com/ybw/mathapp/service/FileHandler.java
@@ -34,7 +34,7 @@ public class FileHandler {
* @throws IOException 当目录创建过程中发生错误时抛出
*/
public void ensureUserDirectory(User user) throws IOException {
- String dirPath = "./" + user.getName();
+ String dirPath = "./" + user.getEmail();
Path path = Paths.get(dirPath);
if (!Files.exists(path)) {
Files.createDirectories(path);
@@ -56,7 +56,7 @@ public class FileHandler {
// 生成文件名:年-月-日-时-分-秒.txt
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss");
String fileName = LocalDateTime.now().format(formatter) + ".txt";
- String filePath = "./" + user.getName() + "/" + fileName;
+ String filePath = "./" + user.getEmail() + "/" + fileName;
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
for (int i = 0; i < questions.size(); i++) {
diff --git a/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java b/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java
index e70946f..346e505 100644
--- a/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java
+++ b/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java
@@ -41,7 +41,7 @@ public class PrimarySchoolGenerator implements QuestionGenerator {
* @return 生成的小学数学题目字符串
*/
private String generateSingleQuestion() {
- Caculate caculate = new Caculate();
+ Calculate calculate = new Calculate();
int operandCount = random.nextInt(4) + 2; // 2-5个操作数
List parts = new ArrayList<>();
while (true) {
@@ -71,7 +71,7 @@ public class PrimarySchoolGenerator implements QuestionGenerator {
}
}
}
- if (caculate.caculate(parts) >= 0) {
+ if (calculate.calculate(parts) >= 0) {
return String.join(" ", parts) + " =";
} else {
parts.clear();
diff --git a/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
index cf51e49..5fdc08a 100644
--- a/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
+++ b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
@@ -35,7 +35,7 @@ public class QuestionDeduplicator {
*/
public void loadExistingQuestions(User user) {
existingQuestions.clear();
- String userDir = "./" + user.getName();
+ String userDir = "./" + user.getEmail();
File dir = new File(userDir);
if (!dir.exists()) {
diff --git a/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java b/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java
index 074e5d7..05f346b 100644
--- a/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java
+++ b/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java
@@ -1,6 +1,8 @@
package com.ybw.mathapp.service;
-import static mathpuzzle.service.PrimarySchoolGenerator.isNumeric;
+
+
+import static com.ybw.mathapp.service.PrimarySchoolGenerator.isNumeric;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/main/java/com/ybw/mathapp/service/StartController.java b/src/main/java/com/ybw/mathapp/service/StartController.java
index ef0ff3c..00b99cc 100644
--- a/src/main/java/com/ybw/mathapp/service/StartController.java
+++ b/src/main/java/com/ybw/mathapp/service/StartController.java
@@ -1,16 +1,12 @@
package com.ybw.mathapp.service;
-import com.ybw.mathapp.LoginAndRegister;
-import com.ybw.mathapp.service.ChoiceGenerator.MultipleChoiceQuestion;
-import com.ybw.mathapp.system.LogSystem;
+import com.ybw.mathapp.entity.User;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class StartController {
- private LogSystem logSystem = new LogSystem();
- private FileHandler fileHandler = new FileHandler();
private double score;
public double getScore() {
@@ -24,85 +20,123 @@ public class StartController {
public void start() {
Scanner scanner = new Scanner(System.in);
while (true) {
- // 此处应添加初始界面(登录、注册界面)函数
- if (registerButtonPressed()) {
- // 此处添加加载注册界面函数
- if (!LoginAndRegister.register()) {
- continue;
+ System.out.println("=== 数学学习软件 ===");
+ System.out.println("1. 登录");
+ System.out.println("2. 注册");
+ System.out.print("请选择: ");
+
+ String choice = scanner.nextLine();
+ if ("1".equals(choice)) {
+ if (login()) {
+ showMainMenu(scanner);
}
- } else {
- if (!LoginAndRegister.login()) {
- continue;
- }
- }
- while (true) {
- // 此处添加小学、初中、高中选择界面函数 (修改密码按钮也应在此界面)
- if (changePasswordButtonPressed()) {
- ChangePassword();
- } else {
- while (true) {
- // 此处添加小学、初中、高中按钮点击响应函数,creatGenerate函数,返回值为QuestionGenerator
- QuestionGenerator generator = null;
- // 出题数目页面
- String input = " ";
- try {
- int count = Integer.parseInt(input);
- if (count < 10 || count > 30) {
- System.out.println("题目数量必须在10-30之间!");
- continue;
- }
- try {
- handleMultipleChoiceGeneration(generator, count); // 调用生成题目函数
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
- } catch (NumberFormatException e) {
- System.out.println("请输入数字!");
- continue;
- }
- System.out.println(score);
- score = 0;
- }
+ } else if ("2".equals(choice)) {
+ if (register()) {
+ showMainMenu(scanner);
}
+ } else {
+ System.out.println("无效选择,请重新输入!");
}
}
}
- // ... (handleLevelSwitch 和 createGenerator 保持不变)
-
- // 处理选择题生成,进行本次生成内的去重工作
- private void handleMultipleChoiceGeneration(QuestionGenerator generator, int count) throws IOException {
-
- ChoiceGenerator mcGenerator = new ChoiceGenerator(generator);
-
- // 生成选择题列表,内部已处理去重
- List finalQuestions = mcGenerator.generateMultipleChoiceQuestions(count);
- int rightCount = 0;
- // 显示选择题
- for (int i = 0; i < finalQuestions.size(); i++) {
- MultipleChoiceQuestion mcq = finalQuestions.get(i);
- System.out.println((i + 1) + ". " + mcq.getQuestion());
- List options = mcq.getOptions();
- int correctAnswerIndex = mcq.getCorrectIndex();
- for (int j = 0; j < options.size(); j++) {
- // 此处可以转换成ABCD
- System.out.println(" " + (char)('A' + j) + ". " + options.get(j));
+ private boolean login() {
+ System.out.println("\n=== 用户登录 ===");
+ Scanner scanner = new Scanner(System.in);
+
+ System.out.print("请输入邮箱: ");
+ String email = scanner.nextLine().trim();
+
+ System.out.print("请输入密码: ");
+ String password = scanner.nextLine();
+
+ // 这里应该调用真实的登录逻辑
+ System.out.println("登录功能待实现");
+ return true;
+ }
+
+ private boolean register() {
+ System.out.println("\n=== 用户注册 ===");
+ System.out.println("注册功能待实现");
+ return true;
+ }
+
+ private void showMainMenu(Scanner scanner) {
+ while (true) {
+ System.out.println("\n=== 主菜单 ===");
+ System.out.println("1. 开始练习");
+ System.out.println("2. 修改密码");
+ System.out.println("3. 退出登录");
+ System.out.print("请选择: ");
+
+ String choice = scanner.nextLine();
+ if ("1".equals(choice)) {
+ showLevelSelection(scanner);
+ } else if ("2".equals(choice)) {
+ changePassword(scanner);
+ } else if ("3".equals(choice)) {
+ System.out.println("退出登录成功!");
+ return;
+ } else {
+ System.out.println("无效选择,请重新输入!");
}
- // 显示选择题界面,等待用户选择
- while (true) {
- if(nextButtonPressed()) {
- if (isCorrectAnswer (input, correctAnswerIndex, options)) {
- rightCount++;
- System.out.println("正确!");
- } else {
- System.out.println("答案错误!");
- }
- break;
- }
+ }
+ }
+
+ private void showLevelSelection(Scanner scanner) {
+ System.out.println("\n=== 选择题目级别 ===");
+ System.out.println("1. 小学");
+ System.out.println("2. 初中");
+ System.out.println("3. 高中");
+ System.out.println("4. 返回");
+ System.out.print("请选择: ");
+
+ String choice = scanner.nextLine();
+ String level = null;
+ switch (choice) {
+ case "1": level = "小学"; break;
+ case "2": level = "初中"; break;
+ case "3": level = "高中"; break;
+ case "4": return;
+ default:
+ System.out.println("无效选择!");
+ return;
+ }
+
+ showQuestionCount(scanner, level);
+ }
+
+ private void showQuestionCount(Scanner scanner, String level) {
+ System.out.println("\n=== 选择题目数量 ===");
+ System.out.print("请输入题目数量 (10-30): ");
+
+ try {
+ int count = Integer.parseInt(scanner.nextLine());
+ if (count < 10 || count > 30) {
+ System.out.println("题目数量必须在10-30之间!");
+ return;
}
+
+ startQuiz(level, count);
+
+ } catch (NumberFormatException e) {
+ System.out.println("请输入有效的数字!");
}
- setScore(caculateScore(rightCount, finalQuestions.size()));
+ }
+
+ private void startQuiz(String level, int count) {
+ System.out.println("\n开始 " + level + " 级别答题,共 " + count + " 题");
+ // 这里应该调用题目生成和答题逻辑
+ System.out.println("答题功能待实现");
+
+ // 模拟答题结果
+ this.score = 0.8; // 模拟80%的正确率
+ System.out.println("答题完成!得分: " + (score * 100) + "%");
+ }
+
+ private void changePassword(Scanner scanner) {
+ System.out.println("\n=== 修改密码 ===");
+ System.out.println("修改密码功能待实现");
}
public QuestionGenerator createGenerator(String level) {
@@ -119,7 +153,7 @@ public class StartController {
}
public boolean isCorrectAnswer(String input, int correctAnswerIndex, List options) {
- if(input.equals(String.valueOf(options.get(correctAnswerIndex)))) {
+ if (input.equals(String.valueOf(options.get(correctAnswerIndex)))) {
return true;
} else {
return false;
diff --git a/src/main/java/com/ybw/mathapp/system/LogSystem.java b/src/main/java/com/ybw/mathapp/system/LogSystem.java
index c867a63..a610d31 100644
--- a/src/main/java/com/ybw/mathapp/system/LogSystem.java
+++ b/src/main/java/com/ybw/mathapp/system/LogSystem.java
@@ -1,15 +1,15 @@
package com.ybw.mathapp.system;
+import com.ybw.mathapp.entity.User;
import java.util.HashMap;
import java.util.Scanner;
-import com.ybw.mathapp.entity.User;
public class LogSystem {
private final HashMap userHashMap = new HashMap<>();
+
public void userHashMapInit() {
// 小学
userHashMap.put("1798231811@qq.com", new User("1798231811@qq.com", "1234567"));
-
}
public User login() {
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 894bf3e..1b7863b 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -3,7 +3,8 @@ module com.ybw.mathapp {
requires javafx.fxml;
requires javafx.base;
requires javafx.graphics;
-
+ requires jakarta.mail;
+
opens com.wsf.mathapp to javafx.fxml;
exports com.wsf.mathapp;
}
\ No newline at end of file
--
2.34.1
From f063724d4b88ecb307a7a3c8993ba9f1de87deb1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=8E=96=E5=85=AE=E5=86=89?=
<15550273+wang-shengfa-11@user.noreply.gitee.com>
Date: Tue, 7 Oct 2025 19:38:27 +0800
Subject: [PATCH 04/16] =?UTF-8?q?v1.1=20=E5=A4=A7=E6=A6=82=E7=9A=84?=
=?UTF-8?q?=E6=A1=86=E6=9E=B6=E5=B7=B2=E7=BB=8F=E5=86=99=E5=A5=BD=EF=BC=8C?=
=?UTF-8?q?=E4=B8=8E=E5=90=8E=E7=AB=AF=E8=83=BD=E9=83=BD=E5=AF=B9=E6=8E=A5?=
=?UTF-8?q?=EF=BC=8C=E4=BD=86=E5=8A=9F=E8=83=BD=E6=9C=AA=E5=AE=8C=E5=96=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/wsf/mathapp/service/EmailService.java | 79 --------
.../com/wsf/mathapp/service/UserService.java | 3 +-
.../com/wsf/mathapp/view/RegisterView.java | 2 +-
.../com/ybw/mathapp/LoginAndRegister.java | 2 +-
.../java/com/ybw/mathapp/entity/User.java | 71 ++-----
.../service/{Caculate.java => Calculate.java} | 4 +-
.../ybw/mathapp/service/ChoiceGenerator.java | 8 +-
.../com/ybw/mathapp/service/EmailService.java | 149 ++++++++++++++
.../com/ybw/mathapp/service/FileHandler.java | 4 +-
.../service/PrimarySchoolGenerator.java | 4 +-
.../mathapp/service/QuestionDeduplicator.java | 2 +-
.../mathapp/service/SeniorHighGenerator.java | 4 +-
.../ybw/mathapp/service/StartController.java | 186 +++++++++++-------
.../com/ybw/mathapp/system/LogSystem.java | 4 +-
src/main/java/module-info.java | 3 +-
15 files changed, 295 insertions(+), 230 deletions(-)
delete mode 100644 src/main/java/com/wsf/mathapp/service/EmailService.java
rename src/main/java/com/ybw/mathapp/service/{Caculate.java => Calculate.java} (99%)
create mode 100644 src/main/java/com/ybw/mathapp/service/EmailService.java
diff --git a/src/main/java/com/wsf/mathapp/service/EmailService.java b/src/main/java/com/wsf/mathapp/service/EmailService.java
deleted file mode 100644
index 84baaaf..0000000
--- a/src/main/java/com/wsf/mathapp/service/EmailService.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.wsf.mathapp.service;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * 前端的模拟邮件服务
- * 在实际部署时,可以替换为真实的邮件服务
- */
-public class EmailService {
- private static Map verificationCodes = new HashMap<>();
-
- private static class VerificationCodeInfo {
- String code;
- long timestamp;
-
- VerificationCodeInfo(String code, long timestamp) {
- this.code = code;
- this.timestamp = timestamp;
- }
- }
-
- // 生成6位随机验证码
- public static String generateVerificationCode() {
- //Random random = new Random();
- //int code = 100000 + random.nextInt(900000);
- //return String.valueOf(code);
- return "1";
- }
-
- // 模拟发送验证码
- public static boolean sendVerificationCode(String recipientEmail, String code) {
- try {
- // 在实际环境中,这里会发送真实的邮件
- // 现在只是模拟发送,将验证码存储起来
- System.out.println("模拟发送验证码到: " + recipientEmail + ", 验证码: " + code);
-
- // 存储验证码信息
- verificationCodes.put(recipientEmail,
- new VerificationCodeInfo(code, System.currentTimeMillis()));
-
- // 模拟网络延迟
- Thread.sleep(1000);
-
- return true;
-
- } catch (Exception e) {
- System.err.println("发送邮件失败: " + e.getMessage());
- // 在开发环境中,即使失败也存储验证码用于测试
- verificationCodes.put(recipientEmail,
- new VerificationCodeInfo(code, System.currentTimeMillis()));
- return true;
- }
- }
-
- // 验证验证码
- public static boolean verifyCode(String email, String inputCode) {
- VerificationCodeInfo codeInfo = verificationCodes.get(email);
- if (codeInfo == null) {
- return false;
- }
-
- // 检查验证码是否过期(5分钟)
- long currentTime = System.currentTimeMillis();
- if (currentTime - codeInfo.timestamp > 5 * 60 * 1000) {
- verificationCodes.remove(email);
- return false;
- }
-
- return codeInfo.code.equals(inputCode);
- }
-
- // 清理过期的验证码
- public static void cleanupExpiredCodes() {
- long currentTime = System.currentTimeMillis();
- verificationCodes.entrySet().removeIf(entry ->
- currentTime - entry.getValue().timestamp > 5 * 60 * 1000);
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/service/UserService.java b/src/main/java/com/wsf/mathapp/service/UserService.java
index df20881..4a2bc61 100644
--- a/src/main/java/com/wsf/mathapp/service/UserService.java
+++ b/src/main/java/com/wsf/mathapp/service/UserService.java
@@ -1,6 +1,7 @@
package com.wsf.mathapp.service;
import com.ybw.mathapp.util.LoginFileUtils;
+import com.ybw.mathapp.entity.User;
public class UserService {
@@ -21,7 +22,7 @@ public class UserService {
}
// 创建用户并保存
- com.ybw.mathapp.entity.User user = new com.ybw.mathapp.entity.User(email, password);
+ User user = new User(email, password);
LoginFileUtils.saveUser(user);
return true;
}
diff --git a/src/main/java/com/wsf/mathapp/view/RegisterView.java b/src/main/java/com/wsf/mathapp/view/RegisterView.java
index 0b2a1a2..58caca6 100644
--- a/src/main/java/com/wsf/mathapp/view/RegisterView.java
+++ b/src/main/java/com/wsf/mathapp/view/RegisterView.java
@@ -2,7 +2,7 @@ package com.wsf.mathapp.view;
import com.wsf.mathapp.controller.SceneManager;
import com.wsf.mathapp.service.UserService;
-import com.wsf.mathapp.service.EmailService;
+import com.ybw.mathapp.service.EmailService;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
diff --git a/src/main/java/com/ybw/mathapp/LoginAndRegister.java b/src/main/java/com/ybw/mathapp/LoginAndRegister.java
index b4b83c8..d71d548 100644
--- a/src/main/java/com/ybw/mathapp/LoginAndRegister.java
+++ b/src/main/java/com/ybw/mathapp/LoginAndRegister.java
@@ -2,7 +2,7 @@ package com.ybw.mathapp;
// UserService.java
import com.ybw.mathapp.entity.User;
-import com.ybw.mathapp.util.EmailService;
+import com.ybw.mathapp.service.EmailService;
import com.ybw.mathapp.util.LoginFileUtils;
import java.util.Scanner;
import java.util.regex.Pattern;
diff --git a/src/main/java/com/ybw/mathapp/entity/User.java b/src/main/java/com/ybw/mathapp/entity/User.java
index 0baf6d7..13cfb82 100644
--- a/src/main/java/com/ybw/mathapp/entity/User.java
+++ b/src/main/java/com/ybw/mathapp/entity/User.java
@@ -1,82 +1,41 @@
package com.ybw.mathapp.entity;
/**
- * 用户实体类,表示系统中的用户信息。
- *
- * 该类包含用户的基本信息,如用户名、密码和学习级别。
- * 用户级别可以是小学、初中或高中。
- *
- * @author 杨博文
- * @version 1.0
- * @since 2025
+ * 用户实体类
*/
public class User {
-
- /** 用户名,不可修改。 */
- private final String name;
-
- /** 邮箱,不可修改。 */
private final String email;
-
- /** 用户密码,不可修改。 */
private final String password;
-
- /** 用户当前的学习级别,可以修改。 */
private String level;
/**
* 构造一个新的用户对象。
*
- * @param email 邮箱,不能为空 用户名,不能为空
+ * @param email 邮箱,不能为空
* @param password 用户密码,不能为空
*/
- public User(String name,String email, String password) {
- this.name = name;
- this.password = password;
+ public User(String email, String password) {
this.email = email;
+ this.password = password;
+ this.level = "小学"; // 默认级别
+ }
+
+ public String getEmail() {
+ return email;
}
- /**
- * 获取用户密码。
- *
- * @return 用户密码
- */
public String getPassword() {
return password;
}
- /**
- * 获取用户当前的学习级别。
- *
- * @return 用户学习级别
- */
public String getLevel() {
return level;
}
- /**
- * 获取用户邮箱。
- *
- * @return 用户邮箱
- */
- public String getEmail() {
- return email;
+ public void setLevel(String level) {
+ this.level = level;
}
- /**
- * 设置用户的学习级别。
- *
- * @param newLevel 新的学习级别,支持"小学"、"初中"、"高中"
- */
- public void setLevel(String newLevel) {
- level = newLevel;
- }
-
- /**
- * 保存邮箱+密码。
- *
- * @return 邮箱+密码
- */
@Override
public String toString() {
return email + "," + password;
@@ -87,14 +46,12 @@ public class User {
return null;
}
- String[] parts = line.split(",", 2); // 最多分割成2部分
+ String[] parts = line.split(",", 2);
if (parts.length == 2) {
return new User(parts[0].trim(), parts[1].trim());
}
return null;
}
- public String getName() {
- return this.name;
- }
-}
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/Caculate.java b/src/main/java/com/ybw/mathapp/service/Calculate.java
similarity index 99%
rename from src/main/java/com/ybw/mathapp/service/Caculate.java
rename to src/main/java/com/ybw/mathapp/service/Calculate.java
index 9592645..334db03 100644
--- a/src/main/java/com/ybw/mathapp/service/Caculate.java
+++ b/src/main/java/com/ybw/mathapp/service/Calculate.java
@@ -10,14 +10,14 @@ import java.util.Stack;
* 用于计算包含四则运算、括号、平方、开根号、三角函数的表达式。
* 注意:三角函数的输入通常认为是度数 (Degree)。
*/
-public class Caculate {
+public class Calculate {
/**
* 计算表达式的值。
* @param parts 表达式分解后的列表,例如 ["(", "2", "+", "3", ")", "*", "4"]
* @return 计算结果
*/
- public double caculate(List parts) {
+ public double calculate(List parts) {
// 将中缀表达式转换为后缀表达式(逆波兰表示法)
List postfix = infixToPostfix(parts);
// 计算后缀表达式的值
diff --git a/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java b/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java
index dae8fc3..af8d43e 100644
--- a/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java
+++ b/src/main/java/com/ybw/mathapp/service/ChoiceGenerator.java
@@ -41,7 +41,7 @@ public class ChoiceGenerator {
public List generateMultipleChoiceQuestions(int count) {
List mcQuestions = new ArrayList<>();
Set generatedQuestionTexts = new HashSet<>(); // 用于本次生成过程中的查重
- Caculate calculator = new Caculate(); // 使用计算器实例
+ Calculate calculator = new Calculate(); // 使用计算器实例
int attempts = 0;
int maxAttempts = count * 100; // 设置最大尝试次数,防止无限循环
@@ -94,7 +94,7 @@ public class ChoiceGenerator {
* @param calc 计算器实例
* @return 计算得出的答案。
*/
- private double calculateAnswer(String question, Caculate calc) {
+ private double calculateAnswer(String question, Calculate calc) {
// 移除 " ="
String expression = question.substring(0, question.length() - 2).trim();
// 将表达式字符串分割成部分
@@ -102,11 +102,11 @@ public class ChoiceGenerator {
// 如果 QuestionGenerator 生成的是 "开根号(16)" 或 "(2+3)平方",则按空格分割可能不够
// 但通常,生成器会生成 "开根号 ( 16 )" 或 "开根号 16" 或 "( 2 + 3 ) 平方" 这样的格式
// 按空格分割 ["开根号", "(", "16", ")"] 或 ["开根号", "16"] 或 ["(", "2", "+", "3", ")", "平方"]
- // CaculatePrimary 需要能处理这些格式
+ // CalculatePrimary 需要能处理这些格式
List parts = Arrays.asList(expression.split("\\s+")); // 按空格分割
try {
- return calc.caculate(parts);
+ return calc.calculate(parts);
} catch (Exception e) {
System.err.println("计算表达式失败: " + expression + ", 错误: " + e.getMessage());
return Double.NaN; // 或者抛出异常
diff --git a/src/main/java/com/ybw/mathapp/service/EmailService.java b/src/main/java/com/ybw/mathapp/service/EmailService.java
new file mode 100644
index 0000000..4728a47
--- /dev/null
+++ b/src/main/java/com/ybw/mathapp/service/EmailService.java
@@ -0,0 +1,149 @@
+package com.ybw.mathapp.service;
+
+import com.ybw.mathapp.config.EmailConfig;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Random;
+//import javax.mail.*;
+//import javax.mail.internet.*;
+
+public class EmailService {
+ private static Map verificationCodes = new HashMap<>();
+
+ private static class VerificationCodeInfo {
+ String code;
+ long timestamp;
+
+ VerificationCodeInfo(String code, long timestamp) {
+ this.code = code;
+ this.timestamp = timestamp;
+ }
+ }
+
+ public static String generateVerificationCode() {
+ Random random = new Random();
+ int code = 100000 + random.nextInt(900000);
+ return String.valueOf(code);
+ }
+
+ public static boolean sendVerificationCode(String recipientEmail, String code) {
+ /*
+ try {
+ // 创建邮件会话
+ Properties props = new Properties();
+ props.put("mail.smtp.host", EmailConfig.SMTP_HOST);
+ props.put("mail.smtp.port", EmailConfig.SMTP_PORT);
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.starttls.enable", "true");
+
+ // 创建认证器
+ Authenticator auth = new Authenticator() {
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(
+ EmailConfig.SENDER_EMAIL,
+ EmailConfig.SENDER_PASSWORD
+ );
+ }
+ };
+
+ Session session = Session.getInstance(props, auth);
+
+ // 创建邮件消息
+ Message message = new MimeMessage(session);
+ message.setFrom(new InternetAddress(EmailConfig.SENDER_EMAIL));
+ message.setRecipients(Message.RecipientType.TO,
+ InternetAddress.parse(recipientEmail));
+ message.setSubject(EmailConfig.EMAIL_SUBJECT);
+
+ // 创建邮件内容
+ String emailContent = createEmailContent(code);
+ message.setContent(emailContent, "text/html; charset=utf-8");
+
+ // 发送邮件
+ Transport.send(message);
+
+ // 存储验证码信息
+ verificationCodes.put(recipientEmail,
+ new VerificationCodeInfo(code, System.currentTimeMillis()));
+
+ System.out.println("验证码已发送到邮箱: " + recipientEmail);
+ return true;
+
+ } catch (Exception e) {
+ System.err.println("发送邮件失败: " + e.getMessage());
+ // 在开发环境中,直接返回true用于测试
+ verificationCodes.put(recipientEmail,
+ new VerificationCodeInfo(code, System.currentTimeMillis()));
+ return true;
+ }
+ */
+ return true;
+ }
+
+ private static String createEmailContent(String code) {
+ return "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "
您好!
" +
+ "
您正在注册数学学习软件账户,验证码如下:
" +
+ "
" + code + "
" +
+ "
验证码有效期为 " + EmailConfig.CODE_EXPIRY_MINUTES + " 分钟,请勿泄露给他人。
" +
+ "
如果这不是您本人的操作,请忽略此邮件。
" +
+ "
" +
+ "" +
+ "
" +
+ "" +
+ "";
+ }
+
+ public static boolean verifyCode(String email, String inputCode) {
+ VerificationCodeInfo codeInfo = verificationCodes.get(email);
+ if (codeInfo == null) {
+ return false;
+ }
+
+ // 检查验证码是否过期
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - codeInfo.timestamp > EmailConfig.CODE_EXPIRY_MINUTES * 60 * 1000) {
+ verificationCodes.remove(email);
+ return false;
+ }
+
+ return codeInfo.code.equals(inputCode);
+ }
+
+ public static void cleanupExpiredCodes() {
+ long currentTime = System.currentTimeMillis();
+ Iterator> iterator =
+ verificationCodes.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry entry = iterator.next();
+ if (currentTime - entry.getValue().timestamp >
+ EmailConfig.CODE_EXPIRY_MINUTES * 60 * 1000) {
+ iterator.remove();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/service/FileHandler.java b/src/main/java/com/ybw/mathapp/service/FileHandler.java
index c41c4d4..b58db18 100644
--- a/src/main/java/com/ybw/mathapp/service/FileHandler.java
+++ b/src/main/java/com/ybw/mathapp/service/FileHandler.java
@@ -34,7 +34,7 @@ public class FileHandler {
* @throws IOException 当目录创建过程中发生错误时抛出
*/
public void ensureUserDirectory(User user) throws IOException {
- String dirPath = "./" + user.getName();
+ String dirPath = "./" + user.getEmail();
Path path = Paths.get(dirPath);
if (!Files.exists(path)) {
Files.createDirectories(path);
@@ -56,7 +56,7 @@ public class FileHandler {
// 生成文件名:年-月-日-时-分-秒.txt
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss");
String fileName = LocalDateTime.now().format(formatter) + ".txt";
- String filePath = "./" + user.getName() + "/" + fileName;
+ String filePath = "./" + user.getEmail() + "/" + fileName;
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
for (int i = 0; i < questions.size(); i++) {
diff --git a/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java b/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java
index e70946f..346e505 100644
--- a/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java
+++ b/src/main/java/com/ybw/mathapp/service/PrimarySchoolGenerator.java
@@ -41,7 +41,7 @@ public class PrimarySchoolGenerator implements QuestionGenerator {
* @return 生成的小学数学题目字符串
*/
private String generateSingleQuestion() {
- Caculate caculate = new Caculate();
+ Calculate calculate = new Calculate();
int operandCount = random.nextInt(4) + 2; // 2-5个操作数
List parts = new ArrayList<>();
while (true) {
@@ -71,7 +71,7 @@ public class PrimarySchoolGenerator implements QuestionGenerator {
}
}
}
- if (caculate.caculate(parts) >= 0) {
+ if (calculate.calculate(parts) >= 0) {
return String.join(" ", parts) + " =";
} else {
parts.clear();
diff --git a/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
index cf51e49..5fdc08a 100644
--- a/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
+++ b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
@@ -35,7 +35,7 @@ public class QuestionDeduplicator {
*/
public void loadExistingQuestions(User user) {
existingQuestions.clear();
- String userDir = "./" + user.getName();
+ String userDir = "./" + user.getEmail();
File dir = new File(userDir);
if (!dir.exists()) {
diff --git a/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java b/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java
index 074e5d7..05f346b 100644
--- a/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java
+++ b/src/main/java/com/ybw/mathapp/service/SeniorHighGenerator.java
@@ -1,6 +1,8 @@
package com.ybw.mathapp.service;
-import static mathpuzzle.service.PrimarySchoolGenerator.isNumeric;
+
+
+import static com.ybw.mathapp.service.PrimarySchoolGenerator.isNumeric;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/main/java/com/ybw/mathapp/service/StartController.java b/src/main/java/com/ybw/mathapp/service/StartController.java
index ef0ff3c..00b99cc 100644
--- a/src/main/java/com/ybw/mathapp/service/StartController.java
+++ b/src/main/java/com/ybw/mathapp/service/StartController.java
@@ -1,16 +1,12 @@
package com.ybw.mathapp.service;
-import com.ybw.mathapp.LoginAndRegister;
-import com.ybw.mathapp.service.ChoiceGenerator.MultipleChoiceQuestion;
-import com.ybw.mathapp.system.LogSystem;
+import com.ybw.mathapp.entity.User;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class StartController {
- private LogSystem logSystem = new LogSystem();
- private FileHandler fileHandler = new FileHandler();
private double score;
public double getScore() {
@@ -24,85 +20,123 @@ public class StartController {
public void start() {
Scanner scanner = new Scanner(System.in);
while (true) {
- // 此处应添加初始界面(登录、注册界面)函数
- if (registerButtonPressed()) {
- // 此处添加加载注册界面函数
- if (!LoginAndRegister.register()) {
- continue;
+ System.out.println("=== 数学学习软件 ===");
+ System.out.println("1. 登录");
+ System.out.println("2. 注册");
+ System.out.print("请选择: ");
+
+ String choice = scanner.nextLine();
+ if ("1".equals(choice)) {
+ if (login()) {
+ showMainMenu(scanner);
}
- } else {
- if (!LoginAndRegister.login()) {
- continue;
- }
- }
- while (true) {
- // 此处添加小学、初中、高中选择界面函数 (修改密码按钮也应在此界面)
- if (changePasswordButtonPressed()) {
- ChangePassword();
- } else {
- while (true) {
- // 此处添加小学、初中、高中按钮点击响应函数,creatGenerate函数,返回值为QuestionGenerator
- QuestionGenerator generator = null;
- // 出题数目页面
- String input = " ";
- try {
- int count = Integer.parseInt(input);
- if (count < 10 || count > 30) {
- System.out.println("题目数量必须在10-30之间!");
- continue;
- }
- try {
- handleMultipleChoiceGeneration(generator, count); // 调用生成题目函数
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
- } catch (NumberFormatException e) {
- System.out.println("请输入数字!");
- continue;
- }
- System.out.println(score);
- score = 0;
- }
+ } else if ("2".equals(choice)) {
+ if (register()) {
+ showMainMenu(scanner);
}
+ } else {
+ System.out.println("无效选择,请重新输入!");
}
}
}
- // ... (handleLevelSwitch 和 createGenerator 保持不变)
-
- // 处理选择题生成,进行本次生成内的去重工作
- private void handleMultipleChoiceGeneration(QuestionGenerator generator, int count) throws IOException {
-
- ChoiceGenerator mcGenerator = new ChoiceGenerator(generator);
-
- // 生成选择题列表,内部已处理去重
- List finalQuestions = mcGenerator.generateMultipleChoiceQuestions(count);
- int rightCount = 0;
- // 显示选择题
- for (int i = 0; i < finalQuestions.size(); i++) {
- MultipleChoiceQuestion mcq = finalQuestions.get(i);
- System.out.println((i + 1) + ". " + mcq.getQuestion());
- List options = mcq.getOptions();
- int correctAnswerIndex = mcq.getCorrectIndex();
- for (int j = 0; j < options.size(); j++) {
- // 此处可以转换成ABCD
- System.out.println(" " + (char)('A' + j) + ". " + options.get(j));
+ private boolean login() {
+ System.out.println("\n=== 用户登录 ===");
+ Scanner scanner = new Scanner(System.in);
+
+ System.out.print("请输入邮箱: ");
+ String email = scanner.nextLine().trim();
+
+ System.out.print("请输入密码: ");
+ String password = scanner.nextLine();
+
+ // 这里应该调用真实的登录逻辑
+ System.out.println("登录功能待实现");
+ return true;
+ }
+
+ private boolean register() {
+ System.out.println("\n=== 用户注册 ===");
+ System.out.println("注册功能待实现");
+ return true;
+ }
+
+ private void showMainMenu(Scanner scanner) {
+ while (true) {
+ System.out.println("\n=== 主菜单 ===");
+ System.out.println("1. 开始练习");
+ System.out.println("2. 修改密码");
+ System.out.println("3. 退出登录");
+ System.out.print("请选择: ");
+
+ String choice = scanner.nextLine();
+ if ("1".equals(choice)) {
+ showLevelSelection(scanner);
+ } else if ("2".equals(choice)) {
+ changePassword(scanner);
+ } else if ("3".equals(choice)) {
+ System.out.println("退出登录成功!");
+ return;
+ } else {
+ System.out.println("无效选择,请重新输入!");
}
- // 显示选择题界面,等待用户选择
- while (true) {
- if(nextButtonPressed()) {
- if (isCorrectAnswer (input, correctAnswerIndex, options)) {
- rightCount++;
- System.out.println("正确!");
- } else {
- System.out.println("答案错误!");
- }
- break;
- }
+ }
+ }
+
+ private void showLevelSelection(Scanner scanner) {
+ System.out.println("\n=== 选择题目级别 ===");
+ System.out.println("1. 小学");
+ System.out.println("2. 初中");
+ System.out.println("3. 高中");
+ System.out.println("4. 返回");
+ System.out.print("请选择: ");
+
+ String choice = scanner.nextLine();
+ String level = null;
+ switch (choice) {
+ case "1": level = "小学"; break;
+ case "2": level = "初中"; break;
+ case "3": level = "高中"; break;
+ case "4": return;
+ default:
+ System.out.println("无效选择!");
+ return;
+ }
+
+ showQuestionCount(scanner, level);
+ }
+
+ private void showQuestionCount(Scanner scanner, String level) {
+ System.out.println("\n=== 选择题目数量 ===");
+ System.out.print("请输入题目数量 (10-30): ");
+
+ try {
+ int count = Integer.parseInt(scanner.nextLine());
+ if (count < 10 || count > 30) {
+ System.out.println("题目数量必须在10-30之间!");
+ return;
}
+
+ startQuiz(level, count);
+
+ } catch (NumberFormatException e) {
+ System.out.println("请输入有效的数字!");
}
- setScore(caculateScore(rightCount, finalQuestions.size()));
+ }
+
+ private void startQuiz(String level, int count) {
+ System.out.println("\n开始 " + level + " 级别答题,共 " + count + " 题");
+ // 这里应该调用题目生成和答题逻辑
+ System.out.println("答题功能待实现");
+
+ // 模拟答题结果
+ this.score = 0.8; // 模拟80%的正确率
+ System.out.println("答题完成!得分: " + (score * 100) + "%");
+ }
+
+ private void changePassword(Scanner scanner) {
+ System.out.println("\n=== 修改密码 ===");
+ System.out.println("修改密码功能待实现");
}
public QuestionGenerator createGenerator(String level) {
@@ -119,7 +153,7 @@ public class StartController {
}
public boolean isCorrectAnswer(String input, int correctAnswerIndex, List options) {
- if(input.equals(String.valueOf(options.get(correctAnswerIndex)))) {
+ if (input.equals(String.valueOf(options.get(correctAnswerIndex)))) {
return true;
} else {
return false;
diff --git a/src/main/java/com/ybw/mathapp/system/LogSystem.java b/src/main/java/com/ybw/mathapp/system/LogSystem.java
index c867a63..a610d31 100644
--- a/src/main/java/com/ybw/mathapp/system/LogSystem.java
+++ b/src/main/java/com/ybw/mathapp/system/LogSystem.java
@@ -1,15 +1,15 @@
package com.ybw.mathapp.system;
+import com.ybw.mathapp.entity.User;
import java.util.HashMap;
import java.util.Scanner;
-import com.ybw.mathapp.entity.User;
public class LogSystem {
private final HashMap userHashMap = new HashMap<>();
+
public void userHashMapInit() {
// 小学
userHashMap.put("1798231811@qq.com", new User("1798231811@qq.com", "1234567"));
-
}
public User login() {
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 894bf3e..1b7863b 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -3,7 +3,8 @@ module com.ybw.mathapp {
requires javafx.fxml;
requires javafx.base;
requires javafx.graphics;
-
+ requires jakarta.mail;
+
opens com.wsf.mathapp to javafx.fxml;
exports com.wsf.mathapp;
}
\ No newline at end of file
--
2.34.1
From 42d1b8716a6f2c7b09e82f88db66949619fafd53 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=A8=E5=8D=9A=E6=96=87?=
<15549487+FX_YBW@user.noreply.gitee.com>
Date: Wed, 8 Oct 2025 23:29:28 +0800
Subject: [PATCH 05/16] =?UTF-8?q?v1.1=20=E7=99=BB=E5=BD=95=E3=80=81?=
=?UTF-8?q?=E6=B3=A8=E5=86=8C=E6=8E=A5=E5=8F=A3=E4=B9=A6=E5=86=99=E5=AE=8C?=
=?UTF-8?q?=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/ybw/mathapp/LoginAndRegister.java | 132 ------------------
.../ybw/mathapp/service/StartController.java | 1 -
.../com/ybw/mathapp/util/EmailService.java | 33 ++++-
src/main/java/com/ybw/mathapp/util/Login.java | 18 +++
.../java/com/ybw/mathapp/util/Register.java | 41 ++++++
5 files changed, 91 insertions(+), 134 deletions(-)
delete mode 100644 src/main/java/com/ybw/mathapp/LoginAndRegister.java
create mode 100644 src/main/java/com/ybw/mathapp/util/Login.java
create mode 100644 src/main/java/com/ybw/mathapp/util/Register.java
diff --git a/src/main/java/com/ybw/mathapp/LoginAndRegister.java b/src/main/java/com/ybw/mathapp/LoginAndRegister.java
deleted file mode 100644
index b4b83c8..0000000
--- a/src/main/java/com/ybw/mathapp/LoginAndRegister.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package com.ybw.mathapp;
-
-// UserService.java
-import com.ybw.mathapp.entity.User;
-import com.ybw.mathapp.util.EmailService;
-import com.ybw.mathapp.util.LoginFileUtils;
-import java.util.Scanner;
-import java.util.regex.Pattern;
-
-public class LoginAndRegister {
- private static Scanner scanner = new Scanner(System.in);
-
- // UserService.java 中的注册方法更新
- public static boolean register() {
- System.out.println("\n=== 用户注册 ===");
-
- // 输入邮箱
- System.out.print("请输入邮箱地址: ");
- String email = scanner.nextLine().trim();
-
- if (!isValidEmail(email)) {
- return false;
- }
-
- if (LoginFileUtils.isEmailRegistered(email)) {
- System.out.println("该邮箱已注册,请直接登录!");
- return false;
- }
-
- // 发送、验证验证码
- if (!sendAndVerifyCode(email)) {
- return false;
- }
-
- // 设置密码(其余代码保持不变)
- System.out.print("请输入密码: ");
- String password1 = scanner.nextLine();
- System.out.print("请再次输入密码: ");
- String password2 = scanner.nextLine();
- if(!isVaildPassword(password1, password2)) {
- return false;
- }
-
- User user = new User(email, password1);
- LoginFileUtils.saveUser(user);
- System.out.println("注册成功!您可以使用邮箱和密码登录了。");
- return true;
- }
-
- // 登录流程
- public static boolean login() {
- System.out.println("\n=== 用户登录 ===");
-
- System.out.print("请输入邮箱: ");
- String email = scanner.nextLine().trim();
-
- System.out.print("请输入密码: ");
- String password = scanner.nextLine();
-
- if (LoginFileUtils.validateUser(email, password)) {
- System.out.println("登录成功!欢迎回来," + email);
- return true;
- } else {
- System.out.println("邮箱或密码错误!");
- return false;
- }
- }
-
- //
- /**
- * 邮箱格式验证
- * @param email 待验证的邮箱地址
- * @return true表示邮箱格式正确,false表示邮箱格式错误
- */
- private static boolean isValidEmail(String email) {
- if (email.isEmpty()) {
- System.out.println("邮箱地址不能为空!");
- return false;
- }
- if (!(email.contains("@") && email.contains("."))) {
- System.out.println("邮箱格式不正确!");
- return false;
- }
- return true;
- }
-
- /**
- * 密码格式验证
- * @param password1 第一次输入的密码
- * @param password2 第二次输入的密码
- * @return true表示符合要求,false表示不符合
- */
- public static boolean isVaildPassword(String password1, String password2) {
- if (password1 == null || password1.length() < 6 || password1.length() > 10) {
- return false;
- }
-
- // 使用正则表达式验证:长度6-10,只包含字母数字,且包含大小写字母和数字
- String regex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{6,10}$";
- if (!Pattern.matches(regex, password1)) {
- return false;
- }
-
- System.out.print("请再次输入密码: ");
- if (!password1.equals(password2)) {
- System.out.println("两次输入的密码不一致!");
- return false;
- }
- return true;
- }
-
- public static boolean sendAndVerifyCode(String email) {
- // 发送真实邮件验证码
- String verificationCode = EmailService.generateVerificationCode();
- System.out.println("正在发送验证码邮件,请稍候...");
-
- if (!EmailService.sendVerificationCode(email, verificationCode)) {
- System.out.println("发送验证码失败,请检查邮箱配置或稍后重试!");
- return false;
- }
-
- // 验证验证码
- System.out.print("请输入收到的验证码: ");
- String inputCode = scanner.nextLine().trim();
-
- if (!EmailService.verifyCode(email, inputCode)) {
- System.out.println("验证码错误或已过期!");
- return false;
- }
- return true;
- }
-}
diff --git a/src/main/java/com/ybw/mathapp/service/StartController.java b/src/main/java/com/ybw/mathapp/service/StartController.java
index ef0ff3c..232c368 100644
--- a/src/main/java/com/ybw/mathapp/service/StartController.java
+++ b/src/main/java/com/ybw/mathapp/service/StartController.java
@@ -1,6 +1,5 @@
package com.ybw.mathapp.service;
-import com.ybw.mathapp.LoginAndRegister;
import com.ybw.mathapp.service.ChoiceGenerator.MultipleChoiceQuestion;
import com.ybw.mathapp.system.LogSystem;
import java.io.IOException;
diff --git a/src/main/java/com/ybw/mathapp/util/EmailService.java b/src/main/java/com/ybw/mathapp/util/EmailService.java
index 7de816d..eba77a8 100644
--- a/src/main/java/com/ybw/mathapp/util/EmailService.java
+++ b/src/main/java/com/ybw/mathapp/util/EmailService.java
@@ -124,7 +124,7 @@ public class EmailService {
"