diff --git a/README.md b/README.md
deleted file mode 100644
index 4abb17d..0000000
--- a/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# AutoGeneratePuzzle
-
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000..d081805
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,153 @@
+# 数学题目生成系统项目说明文档
+
+## 1. 项目概述
+
+### 1.1 项目名称
+中小学数学卷子自动生成程序 (Math Puzzle System)
+
+### 1.2 项目简介
+数学题目生成系统是一个基于Java开发的教育类应用程序,专门用于生成不同教育级别的数学题目。系统支持小学、初中和高中三个级别的题目生成,并提供用户管理、题目去重、试卷保存等功能。
+
+### 1.3 项目目标
+- 为不同教育级别的学生提供定制化的数学练习题目
+- 避免生成重复题目,提高学习效率
+- 提供用户管理功能,支持多用户使用
+- 生成的题目可保存为文件,便于打印和复习
+
+## 2. 功能特性
+
+### 2.1 用户管理
+- **用户注册/登录**:支持现有用户登录系统
+- **用户级别切换**:支持在小学、初中、高中三个级别间动态切换
+- **个性化存储**:为每个用户创建独立的目录存储历史题目
+
+### 2.2 题目生成
+- **小学级别**:生成包含四则运算和括号的简单数学题目,且保证运算结果不会出现负数
+- **初中级别**:生成包含四则运算、平方、开根号的数学题目
+- **高中级别**:生成包含四则运算和三角函数(sin、cos、tan)的数学题目
+
+### 2.3 题目去重
+- **历史题目检查**:自动检查用户历史题目,避免生成与前述txt文件中的重复题目
+- **会话内去重**:防止同一次生成过程中出现重复题目
+
+### 2.4 文件管理
+- **自动目录创建**:为每个用户创建专属目录
+- **时间戳命名**:生成的试卷以时间戳命名,确保唯一性
+- **格式化输出**:题目按照标准格式:前有题号,题干之间添加一行间隔,便于阅读
+
+## 3. 系统架构
+
+### 3.1 核心模块
+
+#### 3.1.1 控制器层 (Controller)
+- `StartController`:系统主控制器,处理用户交互和系统流程控制
+
+#### 3.1.2 服务层 (Service)
+- `QuestionGenerator`:题目生成器接口
+- `PrimarySchoolGenerator`:小学题目生成器
+- `JuniorHighGenerator`:初中题目生成器
+- `SeniorHighGenerator`:高中题目生成器
+- `CaculatePrimary`:小学数学表达式计算器(保证结果不出现负数)
+- `FileHandler`:文件处理器
+- `QuestionDeduplicator`:题目去重器
+
+#### 3.1.3 实体层 (Entity)
+- `User`:用户实体类,包含用户名、密码和级别信息
+
+### 3.2 交互流程
+1. 用户启动程序
+2. 系统要求用户登录
+3. 用户选择题目数量和级别
+4. 系统生成相应级别的数学题目
+5. 题目去重检查
+6. 生成结果展示并保存到文件
+
+## 4. 使用说明
+
+### 4.1 启动程序
+```bash
+java -jar .\mathpuzzle.jar
+```
+
+### 4.2 用户操作
+1. **登录**:输入用户名和密码进行登录
+2. **输入题目数量**:输入10-30之间的数字(输入-1退出当前用户)
+3. **切换级别**:输入"切换为XX"指令切换到相应级别(如"切换为初中")
+4. **查看结果**:系统生成题目并显示,同时保存到用户目录
+
+### 4.3 文件结构
+```
+./
+├── [用户名]/
+│ ├── 2024-01-01-12-00-00.txt
+│ ├── 2024-01-01-12-05-30.txt
+│ └── ...
+```
+
+## 5. 技术特点
+
+### 5.1 设计模式
+- **策略模式**:不同级别的题目生成器实现统一接口
+- **工厂模式**:根据用户级别动态创建相应的题目生成器
+
+### 5.2 算法特色
+- **双栈算法**:用于计算小学级别的数学表达式
+- **随机生成算法**:确保题目多样性和随机性
+- **智能去重算法**:避免生成重复题目
+
+### 5.3 代码规范
+- 遵循Google Java编码规范
+- 完整的Javadoc文档
+- 清晰的包结构和类设计
+
+## 6. 系统要求
+
+### 6.1 运行环境
+- Java 8或更高版本
+- 至少50MB可用磁盘空间(用于存储题目文件)
+
+### 6.2 权限要求
+- 需要文件读写权限(用于创建用户目录和保存题目文件)
+
+## 7. 扩展性说明
+
+### 7.1 可扩展功能
+- 支持更多教育级别的题目生成
+- 添加更多数学运算符和函数
+- 实现题目难度分级
+- 增加图形界面支持
+
+### 7.2 代码扩展
+- 通过实现`QuestionGenerator`接口添加新的题目生成器
+- 通过继承或修改现有生成器类调整题目生成规则
+- 扩展用户实体类添加更多用户属性
+
+## 8. 项目维护
+
+### 8.1 代码维护
+- 遵循单一职责原则,每个类只负责特定功能
+- 使用接口编程,便于功能扩展和替换
+- 完整的异常处理机制
+
+### 8.2 版本管理
+- 建议使用Git进行版本控制
+- 遵循语义化版本控制规范
+- 定期备份重要数据文件
+
+## 9. 注意事项
+
+1. 题目数量限制在10-30道之间,确保生成效率
+2. 系统会自动过滤负数结果,确保题目合理性
+3. 生成的题目文件会自动保存,建议定期清理过期文件
+4. 初中和高中题目包含高级运算符,需要相应的数学知识
+5. 设置循环上限,在无法生成完全不重复的题目时自动退出并给出错误信息
+
+## 10. 联系方式
+
+如需技术支持或功能定制,请联系项目维护者。
+
+---
+
+**版本**:1.0
+**最后更新**:2025年
+**文档状态**:正式版
\ No newline at end of file
diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..79eb620
--- /dev/null
+++ b/src/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: mathpuzzle.Main
+
diff --git a/src/Main.java b/src/Main.java
deleted file mode 100644
index 2d15bb7..0000000
--- a/src/Main.java
+++ /dev/null
@@ -1,5 +0,0 @@
-public class Main {
- public static void main(String[] args) {
- System.out.println("Hello World!");
- }
-}
diff --git a/src/mathpuzzle/Main.java b/src/mathpuzzle/Main.java
new file mode 100644
index 0000000..d80e7a1
--- /dev/null
+++ b/src/mathpuzzle/Main.java
@@ -0,0 +1,29 @@
+package mathpuzzle;
+
+import mathpuzzle.controller.StartController;
+
+/**
+ * 程序主入口类,启动数学题目生成系统。
+ *
+ * 该类包含main方法,作为程序的入口点,负责启动整个数学题目生成系统。
+ * 系统启动后会初始化控制器并开始处理用户交互。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class Main {
+
+ /**
+ * 程序主入口方法。
+ *
+ *
该方法创建启动控制器实例并调用start方法,
+ * 启动整个数学题目生成系统的主循环。
+ *
+ * @param args 命令行参数(当前未使用)
+ */
+ public static void main(String[] args) {
+ StartController startController = new StartController();
+ startController.start();
+ }
+}
\ No newline at end of file
diff --git a/src/mathpuzzle/controller/StartController.java b/src/mathpuzzle/controller/StartController.java
new file mode 100644
index 0000000..757f7d6
--- /dev/null
+++ b/src/mathpuzzle/controller/StartController.java
@@ -0,0 +1,174 @@
+package mathpuzzle.controller;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import mathpuzzle.entity.User;
+import mathpuzzle.service.FileHandler;
+import mathpuzzle.service.JuniorHighGenerator;
+import mathpuzzle.service.PrimarySchoolGenerator;
+import mathpuzzle.service.QuestionDeduplicator;
+import mathpuzzle.service.QuestionGenerator;
+import mathpuzzle.service.SeniorHighGenerator;
+import mathpuzzle.system.LogSystem;
+
+/**
+ * 启动控制器,负责整个数学题目生成系统的主流程控制。
+ *
+ *
该控制器处理用户登录、题目生成数量输入、题目生成逻辑以及用户级别切换等功能。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class StartController {
+
+ /**
+ * 日志系统,用于用户登录和管理。
+ */
+ private LogSystem logSystem = new LogSystem();
+
+ /**
+ * 去重器,用于避免生成重复的数学题目。
+ */
+ private QuestionDeduplicator deduplicator = new QuestionDeduplicator();
+
+ /**
+ * 文件处理器,用于保存生成的题目试卷。
+ */
+ private FileHandler fileHandler = new FileHandler();
+
+ /**
+ * 启动整个数学题目生成系统。
+ *
+ *
该方法实现系统主循环,包括用户登录、题目生成和用户级别切换功能。
+ * 系统会持续运行直到程序被终止。
+ */
+ public void start() {
+
+ logSystem.userHashMapInit();
+ Scanner scanner = new Scanner(System.in);
+ while (true) {
+ User user = logSystem.login();
+ if (user == null) {
+ continue;
+ }
+ while (true) {
+ System.out.println("准备生成" + user.getLevel()
+ + "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):");
+ String input = scanner.nextLine();
+ try {
+ if ("-1".equals(input)) {
+ System.out.println("退出当前用户");
+ break;
+ }
+ int count = Integer.parseInt(input);
+ if (count < 10 || count > 30) {
+ System.out.println("题目数量必须在10-30之间!");
+ continue;
+ }
+ handleQuestionGeneration(user, count);
+ } catch (NumberFormatException e) {
+ handleLevelSwitch(user, input);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * 处理用户级别切换功能。
+ *
+ *
当用户输入格式为"切换为XX"的指令时,该方法会解析并切换用户的学习级别。
+ * 支持的级别包括:小学、初中、高中。
+ *
+ * @param user 当前登录的用户对象
+ * @param input 用户输入的字符串
+ */
+ private void handleLevelSwitch(User user, String input) {
+ if (input.startsWith("切换为")) {
+ String newLevel = input.substring(3);
+ if ("小学".equals(newLevel) || "初中".equals(newLevel) || "高中".equals(newLevel)) {
+ user.setLevel(newLevel);
+ } else {
+ System.out.println("请输入小学、初中和高中三个选项中的一个");
+ }
+ } else {
+ System.out.println("无效输入。请输入题目数量或'切换为 XX'指令。");
+ }
+ }
+
+ /**
+ * 根据指定的级别创建对应的题目生成器。
+ *
+ *
该方法根据用户选择的教育级别返回相应的题目生成器实例。
+ *
+ * @param level 教育级别,支持"小学"、"初中"、"高中"
+ * @return 对应级别的题目生成器实例,如果级别不支持则返回null
+ */
+ private QuestionGenerator createGenerator(String level) {
+ switch (level) {
+ case "小学":
+ return new PrimarySchoolGenerator();
+ case "初中":
+ return new JuniorHighGenerator();
+ case "高中":
+ return new SeniorHighGenerator();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * 处理题目生成逻辑,包括去重和保存功能。
+ *
+ *
该方法负责生成指定数量的不重复数学题目,使用去重器避免与历史题目重复,
+ * 并将生成的题目保存到文件中。
+ *
+ * @param user 当前用户对象
+ * @param count 需要生成的题目数量
+ * @throws IOException 当文件保存过程中发生错误时抛出
+ */
+ // 处理题目生成,进行去重工作
+ private void handleQuestionGeneration(User user, int count) throws IOException {
+ QuestionGenerator generator = createGenerator(user.getLevel());
+ if (generator == null) {
+ System.out.println("不支持的题目类型: " + user.getLevel());
+ return;
+ }
+ // 加载该用户所有历史题目用于查重
+ deduplicator.loadExistingQuestions(user);
+ List finalQuestions = new ArrayList<>();
+ int generatedCount = 0;
+ int maxAttempts = 1000; // 防止因题目空间耗尽而无限循环
+ int attempts = 0;
+ while (generatedCount < count && attempts < maxAttempts) {
+ attempts++;
+ List tempQuestions = generator.generateQuestions(1);
+ if (tempQuestions.isEmpty()) {
+ continue;
+ }
+ String newQuestion = tempQuestions.get(0);
+ String questionForDedup = newQuestion.endsWith(" =")
+ ? newQuestion.substring(0, newQuestion.length() - 2) : newQuestion;
+ if (!deduplicator.isDuplicate(questionForDedup)) {
+ // 题目不重复,加入最终列表和查重集
+ finalQuestions.add(newQuestion);
+ deduplicator.addQuestion(questionForDedup); // 加入本次会话的查重集,防止本次生成重复
+ generatedCount++;
+ }
+ // 如果重复,则丢弃,循环继续
+ }
+ if (generatedCount < count) {
+ System.out.println(
+ "警告:在尝试了 " + maxAttempts + " 次后,仅生成了 " + generatedCount + " 道不重复的题目。");
+ }
+ for (int i = 0; i < finalQuestions.size(); i++) {
+ System.out.println((i + 1) + ". " + finalQuestions.get(i));
+ }
+ fileHandler.savePaper(user, finalQuestions);
+ System.out.println("试卷已成功保存!");
+ }
+}
\ No newline at end of file
diff --git a/src/mathpuzzle/entity/User.java b/src/mathpuzzle/entity/User.java
new file mode 100644
index 0000000..d63b230
--- /dev/null
+++ b/src/mathpuzzle/entity/User.java
@@ -0,0 +1,72 @@
+package mathpuzzle.entity;
+
+/**
+ * 用户实体类,表示系统中的用户信息。
+ *
+ * 该类包含用户的基本信息,如用户名、密码和学习级别。
+ * 用户级别可以是小学、初中或高中。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class User {
+
+ /** 用户名,不可修改。 */
+ private final String name;
+
+ /** 用户密码,不可修改。 */
+ private final String password;
+
+ /** 用户当前的学习级别,可以修改。 */
+ private String level;
+
+ /**
+ * 构造一个新的用户对象。
+ *
+ * @param name 用户名,不能为空
+ * @param password 用户密码,不能为空
+ * @param level 用户学习级别,支持"小学"、"初中"、"高中"
+ */
+ public User(String name, String password, String level) {
+ this.name = name;
+ this.password = password;
+ this.level = level;
+ }
+
+ /**
+ * 获取用户名。
+ *
+ * @return 用户名
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * 获取用户密码。
+ *
+ * @return 用户密码
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * 获取用户当前的学习级别。
+ *
+ * @return 用户学习级别
+ */
+ public String getLevel() {
+ return level;
+ }
+
+ /**
+ * 设置用户的学习级别。
+ *
+ * @param newLevel 新的学习级别,支持"小学"、"初中"、"高中"
+ */
+ public void setLevel(String newLevel) {
+ level = newLevel;
+ }
+}
\ No newline at end of file
diff --git a/src/mathpuzzle/service/CaculatePrimary.java b/src/mathpuzzle/service/CaculatePrimary.java
new file mode 100644
index 0000000..ca7aadc
--- /dev/null
+++ b/src/mathpuzzle/service/CaculatePrimary.java
@@ -0,0 +1,95 @@
+package mathpuzzle.service;
+
+import static mathpuzzle.service.PrimarySchoolGenerator.isNumeric;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * 小学数学表达式计算器,用于计算小学级别的数学表达式。
+ *
+ *
该类使用双栈算法实现中缀表达式的计算,支持加减乘除四则运算和括号。
+ * 运算符优先级:乘除高于加减,括号具有最高优先级。
+ *
+ * @author 杨博文
+ * @version 1.0
+ * @since 2025
+ */
+public class CaculatePrimary {
+
+ /** 数字栈,用于存储操作数。 */
+ private final Stack numStack = new Stack<>();
+
+ /** 操作符栈,用于存储运算符和括号。 */
+ private final Stack opStack = new Stack<>();
+
+ /** 运算符优先级映射表,数字越大优先级越高。 */
+ private final Map precedence = new HashMap<>();
+
+ /**
+ * 计算给定的数学表达式。
+ *
+ * 该方法使用双栈算法计算表达式的值,支持四则运算和括号。
+ *
+ * @param expression 表达式元素列表,包含数字、运算符和括号
+ * @return 表达式的计算结果
+ */
+ public double caculate(List expression) {
+ precedence.put("+", 1);
+ precedence.put("-", 1);
+ precedence.put("*", 2);
+ precedence.put("/", 2);
+ for (String opOrNum : expression) {
+ if (isNumeric(opOrNum)) {
+ numStack.push(Double.parseDouble(opOrNum));
+ } else if (opOrNum.equals("(")) {
+ opStack.push(opOrNum);
+ } else if (opOrNum.equals(")")) {
+ while (!opStack.peek().equals("(") && !opStack.isEmpty()) {
+ doCaculte();
+ }
+ opStack.pop();
+ } else if (precedence.containsKey(opOrNum)) {
+ while (!opStack.isEmpty() && !opStack.peek().equals("(")
+ && precedence.get(opOrNum) <= precedence.get(opStack.peek())) {
+ doCaculte();
+ }
+ opStack.push(opOrNum);
+ }
+ }
+ while (!opStack.isEmpty()) {
+ doCaculte();
+ }
+ return numStack.pop();
+ }
+
+ /**
+ * 执行一次基本的二元运算操作。
+ *
+ * 从数字栈中弹出两个操作数,从操作符栈中弹出一个运算符,
+ * 执行相应的运算,并将结果压入数字栈。
+ */
+ public void doCaculte() {
+ String op = opStack.pop();
+ double num2 = numStack.pop();
+ double num1 = numStack.pop();
+ switch (op) {
+ case "+":
+ numStack.push(num1 + num2);
+ break;
+ case "-":
+ numStack.push(num1 - num2);
+ break;
+ case "*":
+ numStack.push(num1 * num2);
+ break;
+ case "/":
+ numStack.push(num1 / num2);
+ break;
+ default:
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/mathpuzzle/service/FileHandler.java b/src/mathpuzzle/service/FileHandler.java
new file mode 100644
index 0000000..cd6dabc
--- /dev/null
+++ b/src/mathpuzzle/service/FileHandler.java
@@ -0,0 +1,69 @@
+package mathpuzzle.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/mathpuzzle/service/JuniorHighGenerator.java b/src/mathpuzzle/service/JuniorHighGenerator.java
new file mode 100644
index 0000000..56b6c01
--- /dev/null
+++ b/src/mathpuzzle/service/JuniorHighGenerator.java
@@ -0,0 +1,186 @@
+package mathpuzzle.service;
+
+import static mathpuzzle.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.set(0, "开根号" + parts.get(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/mathpuzzle/service/PrimarySchoolGenerator.java b/src/mathpuzzle/service/PrimarySchoolGenerator.java
new file mode 100644
index 0000000..153e134
--- /dev/null
+++ b/src/mathpuzzle/service/PrimarySchoolGenerator.java
@@ -0,0 +1,121 @@
+package mathpuzzle.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() {
+ CaculatePrimary caculatePrimary = new CaculatePrimary();
+ 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 (caculatePrimary.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/mathpuzzle/service/QuestionDeduplicator.java b/src/mathpuzzle/service/QuestionDeduplicator.java
new file mode 100644
index 0000000..01b827b
--- /dev/null
+++ b/src/mathpuzzle/service/QuestionDeduplicator.java
@@ -0,0 +1,91 @@
+package mathpuzzle.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/mathpuzzle/service/QuestionGenerator.java b/src/mathpuzzle/service/QuestionGenerator.java
new file mode 100644
index 0000000..ab5c4fe
--- /dev/null
+++ b/src/mathpuzzle/service/QuestionGenerator.java
@@ -0,0 +1,28 @@
+package mathpuzzle.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/mathpuzzle/service/SeniorHighGenerator.java b/src/mathpuzzle/service/SeniorHighGenerator.java
new file mode 100644
index 0000000..eb6acbb
--- /dev/null
+++ b/src/mathpuzzle/service/SeniorHighGenerator.java
@@ -0,0 +1,126 @@
+package mathpuzzle.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/mathpuzzle/system/LogSystem.java b/src/mathpuzzle/system/LogSystem.java
new file mode 100644
index 0000000..6918d23
--- /dev/null
+++ b/src/mathpuzzle/system/LogSystem.java
@@ -0,0 +1,47 @@
+package mathpuzzle.system;
+import mathpuzzle.entity.User;
+import java.util.HashMap;
+import java.util.Scanner;
+
+public class LogSystem {
+ private final HashMap userHashMap = new HashMap<>();
+ public void userHashMapInit() {
+ // 小学
+ userHashMap.put("张三1", new User("张三 1", "123", "小学"));
+ userHashMap.put("张三2", new User("张三 2", "123", "小学"));
+ userHashMap.put("张三3", new User("张三 3", "123", "小学"));
+ // 初中
+ userHashMap.put("李四1", new User("李四 1", "123", "初中"));
+ userHashMap.put("李四2", new User("李四 2", "123", "初中"));
+ userHashMap.put("李四3", new User("李四 3", "123", "初中"));
+ // 高中
+ userHashMap.put("王五1", new User("王五 1", "123", "高中"));
+ userHashMap.put("王五2", new User("王五 2", "123", "高中"));
+ userHashMap.put("王五3", new User("王五 3", "123", "高中"));
+ }
+
+ 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;
+ }
+ }
+ }
+ }
+}