From d8693211a92f19d00c64fe5b3e685b4d15ee8c72 Mon Sep 17 00:00:00 2001 From: smallbailangui Date: Thu, 25 Sep 2025 12:59:39 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=94=B9=E7=94=A8maven=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- src/com/mathgenerator/Application.java | 25 --- src/com/mathgenerator/auth/Authenticator.java | 46 ------ src/com/mathgenerator/auth/User.java | 12 -- .../generator/JuniorHighSchoolGenerator.java | 44 ----- .../generator/PrimarySchoolGenerator.java | 72 --------- .../generator/QuestionGenerator.java | 12 -- .../generator/SeniorHighSchoolGenerator.java | 42 ----- src/com/mathgenerator/model/Level.java | 20 --- .../mathgenerator/service/PaperService.java | 63 -------- .../mathgenerator/storage/FileManager.java | 87 ---------- src/com/mathgenerator/ui/ConsoleUI.java | 152 ------------------ 12 files changed, 2 insertions(+), 576 deletions(-) delete mode 100644 src/com/mathgenerator/Application.java delete mode 100644 src/com/mathgenerator/auth/Authenticator.java delete mode 100644 src/com/mathgenerator/auth/User.java delete mode 100644 src/com/mathgenerator/generator/JuniorHighSchoolGenerator.java delete mode 100644 src/com/mathgenerator/generator/PrimarySchoolGenerator.java delete mode 100644 src/com/mathgenerator/generator/QuestionGenerator.java delete mode 100644 src/com/mathgenerator/generator/SeniorHighSchoolGenerator.java delete mode 100644 src/com/mathgenerator/model/Level.java delete mode 100644 src/com/mathgenerator/service/PaperService.java delete mode 100644 src/com/mathgenerator/storage/FileManager.java delete mode 100644 src/com/mathgenerator/ui/ConsoleUI.java diff --git a/.gitignore b/.gitignore index f056658..0973627 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ out/ .vscode/ bin/ questions/ -generated_papers/ \ No newline at end of file +generated_papers/ +target/ \ No newline at end of file diff --git a/src/com/mathgenerator/Application.java b/src/com/mathgenerator/Application.java deleted file mode 100644 index ce53846..0000000 --- a/src/com/mathgenerator/Application.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mathgenerator; - -import com.mathgenerator.auth.Authenticator; -import com.mathgenerator.service.PaperService; -import com.mathgenerator.storage.FileManager; -import com.mathgenerator.ui.ConsoleUI; // 导入新的UI类 - -/** - * 应用程序主入口。 - * 职责:创建并组装所有核心组件,然后启动用户界面。 - */ -public class Application { - public static void main(String[] args) { - // 1. 创建所有核心服务组件 - Authenticator authenticator = new Authenticator(); - FileManager fileManager = new FileManager(); - PaperService paperService = new PaperService(fileManager); - - // 2. 创建UI组件,并将服务注入其中 - ConsoleUI consoleUI = new ConsoleUI(authenticator, paperService); - - // 3. 运行UI - consoleUI.run(); - } -} \ No newline at end of file diff --git a/src/com/mathgenerator/auth/Authenticator.java b/src/com/mathgenerator/auth/Authenticator.java deleted file mode 100644 index 9281300..0000000 --- a/src/com/mathgenerator/auth/Authenticator.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.mathgenerator.auth; - -import com.mathgenerator.model.Level; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; -import java.util.stream.Collectors; - -/** - * 负责用户身份验证。 - */ -public class Authenticator { - - private static final Map USER_DATABASE = initializeUsers(); - - /** - * 初始化预设的用户数据。 - */ - private static Map initializeUsers() { - return Stream.of( - new User("张三1", "123", Level.PRIMARY), - new User("张三2", "123", Level.PRIMARY), - new User("张三3", "123", Level.PRIMARY), - new User("李四1", "123", Level.JUNIOR_HIGH), - new User("李四2", "123", Level.JUNIOR_HIGH), - new User("李四3", "123", Level.JUNIOR_HIGH), - new User("王五1", "123", Level.SENIOR_HIGH), - new User("王五2", "123", Level.SENIOR_HIGH), - new User("王五3", "123", Level.SENIOR_HIGH) - ).collect(Collectors.toMap(User::username, user -> user)); - } - - /** - * 验证用户登录凭据。 - * @param username 用户名 - * @param password 密码 - * @return 如果验证成功,返回包含User的Optional;否则返回空的Optional。 - */ - public Optional login(String username, String password) { - User user = USER_DATABASE.get(username); - if (user != null && user.password().equals(password)) { - return Optional.of(user); - } - return Optional.empty(); - } -} diff --git a/src/com/mathgenerator/auth/User.java b/src/com/mathgenerator/auth/User.java deleted file mode 100644 index b0584f8..0000000 --- a/src/com/mathgenerator/auth/User.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mathgenerator.auth; - -import com.mathgenerator.model.Level; - -/** - * 用户数据记录 (Record),用于封装不可变的用户信息。 - * @param username 用户名 - * @param password 密码 - * @param level 用户对应的学段 - */ -public record User(String username, String password, Level level) { -} \ No newline at end of file diff --git a/src/com/mathgenerator/generator/JuniorHighSchoolGenerator.java b/src/com/mathgenerator/generator/JuniorHighSchoolGenerator.java deleted file mode 100644 index 5b9cadb..0000000 --- a/src/com/mathgenerator/generator/JuniorHighSchoolGenerator.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.mathgenerator.generator; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * 初中题目生成器。 - * (已重构) 在保留括号和加减乘除的基础上,至少包含一个平方或开根号。 - */ -public class JuniorHighSchoolGenerator extends PrimarySchoolGenerator { - @Override - public String generateSingleQuestion() { - // 1. 先生成一个完整的小学难度题目(包含括号等) - String basicQuestion = super.generateSingleQuestion(); - ThreadLocalRandom random = ThreadLocalRandom.current(); - - // 2. 使用正则表达式查找出题目中所有的数字 - Pattern pattern = Pattern.compile("\\d+"); - Matcher matcher = pattern.matcher(basicQuestion); - List numbers = new ArrayList<>(); - while (matcher.find()) { - numbers.add(matcher.group()); - } - - if (numbers.isEmpty()) { - return basicQuestion; // 如果没有数字,直接返回 - } - - // 3. 随机选择一个数字进行修改 - String numberToModify = numbers.get(random.nextInt(numbers.size())); - String modifiedNumber; - if (random.nextBoolean()) { - modifiedNumber = numberToModify + "²"; // 平方 - } else { - modifiedNumber = "√" + numberToModify; // 开根号 - } - - // 4. 将修改后的数字替换回原题目,只替换第一个匹配项以保证随机性 - return basicQuestion.replaceFirst(Pattern.quote(numberToModify), modifiedNumber); - } -} \ No newline at end of file diff --git a/src/com/mathgenerator/generator/PrimarySchoolGenerator.java b/src/com/mathgenerator/generator/PrimarySchoolGenerator.java deleted file mode 100644 index 24eadea..0000000 --- a/src/com/mathgenerator/generator/PrimarySchoolGenerator.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.mathgenerator.generator; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -/** - * 小学题目生成器。 - * 生成包含 + - * / 和 () 的运算。 - * (已修正括号生成逻辑) - */ -public class PrimarySchoolGenerator implements QuestionGenerator { - - private static final String[] OPERATORS = {"+", "-", "*", "/"}; - - @Override - public String generateSingleQuestion() { - ThreadLocalRandom random = ThreadLocalRandom.current(); - int operandCount = random.nextInt(2, 6); // 2到5个操作数 - - // 步骤1: 先生成一个不带括号的表达式组件列表 - List parts = new ArrayList<>(); - parts.add(String.valueOf(getOperand())); - for (int i = 1; i < operandCount; i++) { - parts.add(getRandomOperator()); - parts.add(String.valueOf(getOperand())); - } - - // 步骤2: 如果条件满足,对组件列表进行处理以添加括号 - if (operandCount > 2 && random.nextBoolean()) { - addParentheses(parts); - } - - // 步骤3: 将组件用空格连接成最终的字符串 - return String.join(" ", parts); - } - - /** - * 获取一个1到100之间的随机操作数。 - */ - private int getOperand() { - return ThreadLocalRandom.current().nextInt(1, 101); - } - - /** - * 获取一个随机的运算符。 - */ - private String getRandomOperator() { - return OPERATORS[ThreadLocalRandom.current().nextInt(OPERATORS.length)]; - } - - /** - * (已重写) 一个更健壮的方法,用于在表达式组件列表中添加括号。 - * @param parts 表达式组件列表,例如 ["55", "+", "8", "-", "21"] - */ - private void addParentheses(List parts) { - ThreadLocalRandom random = ThreadLocalRandom.current(); - - // 操作数的位置在 0, 2, 4, ... - // 随机选择括号的起始和结束操作数 - int startOperandIndex = random.nextInt(parts.size() / 2); - int endOperandIndex = random.nextInt(startOperandIndex + 1, parts.size() / 2 + 1); - - // 将索引转换为在列表中的实际位置 - int startIndex = startOperandIndex * 2; - int endIndex = endOperandIndex * 2; - - // 在正确的位置插入括号 - parts.add(endIndex + 1, ")"); - parts.add(startIndex, "("); - } -} \ No newline at end of file diff --git a/src/com/mathgenerator/generator/QuestionGenerator.java b/src/com/mathgenerator/generator/QuestionGenerator.java deleted file mode 100644 index 15002d9..0000000 --- a/src/com/mathgenerator/generator/QuestionGenerator.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mathgenerator.generator; - -/** - * 题目生成器接口,定义了所有具体生成器必须实现的方法。 - */ -public interface QuestionGenerator { - /** - * 生成一道符合特定难度的数学题目。 - * @return 代表数学题目的字符串 - */ - String generateSingleQuestion(); -} diff --git a/src/com/mathgenerator/generator/SeniorHighSchoolGenerator.java b/src/com/mathgenerator/generator/SeniorHighSchoolGenerator.java deleted file mode 100644 index a187a4b..0000000 --- a/src/com/mathgenerator/generator/SeniorHighSchoolGenerator.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.mathgenerator.generator; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * 高中题目生成器。 - * (已重构) 继承初中生成器,在初中题目基础上,至少包含一个 sin, cos, 或 tan。 - */ -public class SeniorHighSchoolGenerator extends JuniorHighSchoolGenerator { // <-- 关键改动:继承自初中生成器 - private static final String[] TRIG_FUNCTIONS = {"sin", "cos", "tan"}; - - @Override - public String generateSingleQuestion() { - // 1. 先生成一个完整的初中难度题目(已包含小学内容和平方/开根号) - String juniorHighQuestion = super.generateSingleQuestion(); - ThreadLocalRandom random = ThreadLocalRandom.current(); - - // 2. 查找出题目中所有的数字 - Pattern pattern = Pattern.compile("\\d+"); - Matcher matcher = pattern.matcher(juniorHighQuestion); - List numbers = new ArrayList<>(); - while (matcher.find()) { - numbers.add(matcher.group()); - } - - if (numbers.isEmpty()) { - return juniorHighQuestion; - } - - // 3. 随机选择一个数字,用三角函数包裹 - String numberToModify = numbers.get(random.nextInt(numbers.size())); - String function = TRIG_FUNCTIONS[random.nextInt(TRIG_FUNCTIONS.length)]; - String modifiedNumber = function + "(" + numberToModify + ")"; - - // 4. 将修改后的部分替换回原题目 - return juniorHighQuestion.replaceFirst(Pattern.quote(numberToModify), modifiedNumber); - } -} \ No newline at end of file diff --git a/src/com/mathgenerator/model/Level.java b/src/com/mathgenerator/model/Level.java deleted file mode 100644 index 8a9ffba..0000000 --- a/src/com/mathgenerator/model/Level.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.mathgenerator.model; - -/** - * 学段枚举,用于类型安全地表示小学、初中和高中。 - */ -public enum Level { - PRIMARY("小学"), - JUNIOR_HIGH("初中"), - SENIOR_HIGH("高中"); - - private final String chineseName; - - Level(String chineseName) { - this.chineseName = chineseName; - } - - public String getChineseName() { - return chineseName; - } -} \ No newline at end of file diff --git a/src/com/mathgenerator/service/PaperService.java b/src/com/mathgenerator/service/PaperService.java deleted file mode 100644 index 3cb8718..0000000 --- a/src/com/mathgenerator/service/PaperService.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.mathgenerator.service; - -import com.mathgenerator.generator.JuniorHighSchoolGenerator; -import com.mathgenerator.generator.PrimarySchoolGenerator; -import com.mathgenerator.generator.QuestionGenerator; -import com.mathgenerator.generator.SeniorHighSchoolGenerator; -import com.mathgenerator.model.Level; -import com.mathgenerator.auth.User; -import com.mathgenerator.storage.FileManager; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * 业务逻辑核心,负责协调生成和存储。 - */ -public class PaperService { - private final FileManager fileManager; - - public PaperService(FileManager fileManager) { - this.fileManager = fileManager; - } - - /** - * 创建并保存一份完整的试卷。 - * @param user 用户对象 - * @param count 题目数量 - * @param currentLevel 当前选择的难度等级 - */ - public void createAndSavePaper(User user, int count, Level currentLevel) { - QuestionGenerator generator = createGenerator(currentLevel); - Set existingQuestions = fileManager.loadExistingQuestions(user.username()); - List newPaper = new ArrayList<>(); - Set generatedInSession = new HashSet<>(); - - System.out.println("正在生成题目,请稍候..."); - while (newPaper.size() < count) { - String question = generator.generateSingleQuestion(); - if (!existingQuestions.contains(question) && !generatedInSession.contains(question)) { - newPaper.add(question); - generatedInSession.add(question); - } - } - - try { - String filePath = fileManager.savePaper(user.username(), newPaper); - System.out.println("成功!" + count + "道" + currentLevel.getChineseName() + "数学题目已生成。"); - System.out.println("文件已保存至: " + filePath); - } catch (IOException e) { - System.err.println("错误:保存文件失败 - " + e.getMessage()); - } - } - - private QuestionGenerator createGenerator(Level level) { - return switch (level) { - case PRIMARY -> new PrimarySchoolGenerator(); - case JUNIOR_HIGH -> new JuniorHighSchoolGenerator(); - case SENIOR_HIGH -> new SeniorHighSchoolGenerator(); - }; - } -} \ No newline at end of file diff --git a/src/com/mathgenerator/storage/FileManager.java b/src/com/mathgenerator/storage/FileManager.java deleted file mode 100644 index 53f15a3..0000000 --- a/src/com/mathgenerator/storage/FileManager.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.mathgenerator.storage; - -import java.io.IOException; -//TODO:这里不能导入所有类 -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * 负责文件读写和历史题目加载。 - */ -public class FileManager { - private static final Path BASE_PATH = Paths.get("generated_papers"); - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"); - private static final Pattern QUESTION_PATTERN = Pattern.compile("^\\d+\\.\\s+(.*)"); - - /** - * 将生成的试卷保存到文件。 - * @param username 用户名 - * @param paperContent 试卷内容列表 - * @return 保存成功后的文件名 - */ - public String savePaper(String username, List paperContent) throws IOException { - Path userDir = BASE_PATH.resolve(username); - Files.createDirectories(userDir); - - String timestamp = LocalDateTime.now().format(FORMATTER); - String fileName = timestamp + ".txt"; - Path filePath = userDir.resolve(fileName); - - StringBuilder formattedContent = new StringBuilder(); - for (int i = 0; i < paperContent.size(); i++) { - formattedContent.append(i + 1).append(". ").append(paperContent.get(i)); - if (i < paperContent.size() - 1) { - formattedContent.append(System.lineSeparator()).append(System.lineSeparator()); - } - } - - Files.writeString(filePath, formattedContent.toString(), StandardOpenOption.CREATE); - return filePath.toString(); - } - - /** - * 加载指定用户的所有历史题目用于查重。 - * @param username 用户名 - * @return 包含所有历史题目的Set集合 - */ - public Set loadExistingQuestions(String username) { - Path userDir = BASE_PATH.resolve(username); - if (!Files.exists(userDir)) { - return new HashSet<>(); - } - - try (Stream stream = Files.walk(userDir)) { - return stream - .filter(file -> !Files.isDirectory(file) && file.toString().endsWith(".txt")) - .flatMap(this::readQuestionsFromFile) - .collect(Collectors.toSet()); - } catch (IOException e) { - System.err.println("错误:读取历史文件失败 - " + e.getMessage()); - return new HashSet<>(); - } - } - - private Stream readQuestionsFromFile(Path file) { - try { - //TODO:这里需要修改警告:(72, 26) 在没有 'try-with-resources' 语句的情况下使用 'Stream' - return Files.lines(file) - .map(String::trim) - .filter(line -> !line.isEmpty()) - .map(line -> QUESTION_PATTERN.matcher(line).matches() ? - QUESTION_PATTERN.matcher(line).replaceAll("$1") : line); - } catch (IOException e) { - System.err.println("错误:读取文件 " + file + " 失败 - " + e.getMessage()); - return Stream.empty(); - } - } -} \ No newline at end of file diff --git a/src/com/mathgenerator/ui/ConsoleUI.java b/src/com/mathgenerator/ui/ConsoleUI.java deleted file mode 100644 index 6fad7ed..0000000 --- a/src/com/mathgenerator/ui/ConsoleUI.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.mathgenerator.ui; - -import com.mathgenerator.auth.Authenticator; -import com.mathgenerator.auth.User; -import com.mathgenerator.model.Level; -import com.mathgenerator.service.PaperService; - -import java.util.Optional; -import java.util.Scanner; - -/** - * 负责所有控制台用户界面的显示和交互。 - */ -public class ConsoleUI { - private final Scanner scanner = new Scanner(System.in); - private final Authenticator authenticator; - private final PaperService paperService; - - // 通过构造函数接收依赖的服务,这是一种常见的设计模式(依赖注入) - public ConsoleUI(Authenticator authenticator, PaperService paperService) { - this.authenticator = authenticator; - this.paperService = paperService; - } - - /** - * 启动并运行整个用户界面主循环。 - */ - public void run() { - //TODO:警告:(29, 9) 'while' 语句不能在未抛出异常的情况下完成 - while (true) { - Optional userOptional = handleLogin(); - userOptional.ifPresent(this::showUserMenu); - } - } - - private Optional handleLogin() { - printHeader("用户登录"); // 统一标题格式 - System.out.print("请输入用户名和密码 (用空格隔开): (输入 exit 退出程序)\n> "); - String line = scanner.nextLine(); - - if ("exit".equalsIgnoreCase(line.trim())) { - System.out.println("感谢使用,程序已退出。"); - System.exit(0); - } - - String[] credentials = line.split("\\s+"); - if (credentials.length != 2) { - System.out.println("输入格式错误,请重新输入。"); - return Optional.empty(); - } - - Optional userOptional = authenticator.login(credentials[0], credentials[1]); - if (userOptional.isEmpty()) { - System.out.println("用户名或密码错误,请重新输入。"); - } - return userOptional; - } - - private void showUserMenu(User user) { - Level currentLevel = user.level(); - System.out.println("\n登录成功! 欢迎 " + user.username()); - System.out.println("当前选择为 " + currentLevel.getChineseName() + " 出题"); - - while (true) { - printHeader("当前用户: " + user.username() + " | 当前难度: " + currentLevel.getChineseName()); - System.out.println("1. 生成题目"); - System.out.println("2. 切换难度级别"); - System.out.println("3. 退出当前用户"); - System.out.println("4. 退出程序"); - printSeparator(); - System.out.print("请选择操作 (1-4): "); - String choice = scanner.nextLine().trim(); - - switch (choice) { - case "1" -> handleGeneration(user, currentLevel); - case "2" -> currentLevel = handleLevelSwitchMenu(currentLevel); - case "3" -> { - System.out.println("用户 " + user.username() + " 已退出。"); - return; - } - case "4" -> { - System.out.println("感谢使用,程序已退出。"); - System.exit(0); - } - default -> System.out.println("无效输入,请输入 1-4 之间的数字。"); - } - } - } - - /** - * 处理切换难度的子菜单,加入循环以处理无效输入。 - */ - private Level handleLevelSwitchMenu(Level currentLevel) { - while (true) { // <-- 增加循环 - printHeader("请选择难度级别"); - System.out.println("1. 小学 (+, -, *, /, 括号)"); - System.out.println("2. 初中 (包含平方、开根号)"); - System.out.println("3. 高中 (包含三角函数)"); - System.out.println("0. 返回主菜单"); - printSeparator(); - System.out.print("请选择 (0-3): "); - String choice = scanner.nextLine().trim(); - - switch (choice) { - case "1": - System.out.println("难度已成功切换为 " + Level.PRIMARY.getChineseName() + "。"); - return Level.PRIMARY; // 直接返回,退出循环 - case "2": - System.out.println("难度已成功切换为 " + Level.JUNIOR_HIGH.getChineseName() + "。"); - return Level.JUNIOR_HIGH; // 直接返回,退出循环 - case "3": - System.out.println("难度已成功切换为 " + Level.SENIOR_HIGH.getChineseName() + "。"); - return Level.SENIOR_HIGH; // 直接返回,退出循环 - case "0": - System.out.println("已返回主菜单。"); - return currentLevel; // 返回当前难度,退出循环 - default: - // 如果输入无效,打印提示信息,循环将继续,要求用户重新输入 - System.out.println("无效输入,请输入 0-3 之间的数字。"); - } - } - } - - private void handleGeneration(User user, Level currentLevel) { - printHeader("生成 " + currentLevel.getChineseName() + " 题目"); - System.out.print("请输入生成题目数量 (10-30,输入 0 返回主菜单): "); - try { - int count = Integer.parseInt(scanner.nextLine().trim()); - if (count == 0) { - System.out.println("已取消生成,返回主菜单。"); - return; - } - if (count >= 10 && count <= 30) { - paperService.createAndSavePaper(user, count, currentLevel); - } else { - System.out.println("输入无效,题目数量必须在 10 到 30 之间。"); - } - } catch (NumberFormatException e) { - System.out.println("输入无效,请输入一个有效的数字。"); - } - } - - private void printHeader(String title) { - printSeparator(); - System.out.println(title); - printSeparator(); - } - - private void printSeparator() { - System.out.println("======================================================"); - } -} \ No newline at end of file -- 2.34.1 From 3dbcd3916547715ac7e90287a38560bd90c16d02 Mon Sep 17 00:00:00 2001 From: smallbailangui Date: Thu, 25 Sep 2025 13:01:24 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=94=B9=E7=94=A8maven=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 34 ++++ .../java/com/mathgenerator/Application.java | 25 +++ .../com/mathgenerator/auth/Authenticator.java | 46 ++++++ .../java/com/mathgenerator/auth/User.java | 12 ++ .../generator/JuniorHighSchoolGenerator.java | 44 +++++ .../generator/PrimarySchoolGenerator.java | 72 +++++++++ .../generator/QuestionGenerator.java | 12 ++ .../generator/SeniorHighSchoolGenerator.java | 42 +++++ .../java/com/mathgenerator/model/Level.java | 20 +++ .../mathgenerator/service/PaperService.java | 63 ++++++++ .../mathgenerator/storage/FileManager.java | 87 ++++++++++ .../java/com/mathgenerator/ui/ConsoleUI.java | 152 ++++++++++++++++++ 12 files changed, 609 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/mathgenerator/Application.java create mode 100644 src/main/java/com/mathgenerator/auth/Authenticator.java create mode 100644 src/main/java/com/mathgenerator/auth/User.java create mode 100644 src/main/java/com/mathgenerator/generator/JuniorHighSchoolGenerator.java create mode 100644 src/main/java/com/mathgenerator/generator/PrimarySchoolGenerator.java create mode 100644 src/main/java/com/mathgenerator/generator/QuestionGenerator.java create mode 100644 src/main/java/com/mathgenerator/generator/SeniorHighSchoolGenerator.java create mode 100644 src/main/java/com/mathgenerator/model/Level.java create mode 100644 src/main/java/com/mathgenerator/service/PaperService.java create mode 100644 src/main/java/com/mathgenerator/storage/FileManager.java create mode 100644 src/main/java/com/mathgenerator/ui/ConsoleUI.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2bd9e87 --- /dev/null +++ b/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + com.mathgenerator + MathGenerator + 1.0.0 + + + 17 + 17 + UTF-8 + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + com.mathgenerator.Application + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/mathgenerator/Application.java b/src/main/java/com/mathgenerator/Application.java new file mode 100644 index 0000000..ce53846 --- /dev/null +++ b/src/main/java/com/mathgenerator/Application.java @@ -0,0 +1,25 @@ +package com.mathgenerator; + +import com.mathgenerator.auth.Authenticator; +import com.mathgenerator.service.PaperService; +import com.mathgenerator.storage.FileManager; +import com.mathgenerator.ui.ConsoleUI; // 导入新的UI类 + +/** + * 应用程序主入口。 + * 职责:创建并组装所有核心组件,然后启动用户界面。 + */ +public class Application { + public static void main(String[] args) { + // 1. 创建所有核心服务组件 + Authenticator authenticator = new Authenticator(); + FileManager fileManager = new FileManager(); + PaperService paperService = new PaperService(fileManager); + + // 2. 创建UI组件,并将服务注入其中 + ConsoleUI consoleUI = new ConsoleUI(authenticator, paperService); + + // 3. 运行UI + consoleUI.run(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mathgenerator/auth/Authenticator.java b/src/main/java/com/mathgenerator/auth/Authenticator.java new file mode 100644 index 0000000..9281300 --- /dev/null +++ b/src/main/java/com/mathgenerator/auth/Authenticator.java @@ -0,0 +1,46 @@ +package com.mathgenerator.auth; + +import com.mathgenerator.model.Level; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; +import java.util.stream.Collectors; + +/** + * 负责用户身份验证。 + */ +public class Authenticator { + + private static final Map USER_DATABASE = initializeUsers(); + + /** + * 初始化预设的用户数据。 + */ + private static Map initializeUsers() { + return Stream.of( + new User("张三1", "123", Level.PRIMARY), + new User("张三2", "123", Level.PRIMARY), + new User("张三3", "123", Level.PRIMARY), + new User("李四1", "123", Level.JUNIOR_HIGH), + new User("李四2", "123", Level.JUNIOR_HIGH), + new User("李四3", "123", Level.JUNIOR_HIGH), + new User("王五1", "123", Level.SENIOR_HIGH), + new User("王五2", "123", Level.SENIOR_HIGH), + new User("王五3", "123", Level.SENIOR_HIGH) + ).collect(Collectors.toMap(User::username, user -> user)); + } + + /** + * 验证用户登录凭据。 + * @param username 用户名 + * @param password 密码 + * @return 如果验证成功,返回包含User的Optional;否则返回空的Optional。 + */ + public Optional login(String username, String password) { + User user = USER_DATABASE.get(username); + if (user != null && user.password().equals(password)) { + return Optional.of(user); + } + return Optional.empty(); + } +} diff --git a/src/main/java/com/mathgenerator/auth/User.java b/src/main/java/com/mathgenerator/auth/User.java new file mode 100644 index 0000000..b0584f8 --- /dev/null +++ b/src/main/java/com/mathgenerator/auth/User.java @@ -0,0 +1,12 @@ +package com.mathgenerator.auth; + +import com.mathgenerator.model.Level; + +/** + * 用户数据记录 (Record),用于封装不可变的用户信息。 + * @param username 用户名 + * @param password 密码 + * @param level 用户对应的学段 + */ +public record User(String username, String password, Level level) { +} \ No newline at end of file diff --git a/src/main/java/com/mathgenerator/generator/JuniorHighSchoolGenerator.java b/src/main/java/com/mathgenerator/generator/JuniorHighSchoolGenerator.java new file mode 100644 index 0000000..5b9cadb --- /dev/null +++ b/src/main/java/com/mathgenerator/generator/JuniorHighSchoolGenerator.java @@ -0,0 +1,44 @@ +package com.mathgenerator.generator; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 初中题目生成器。 + * (已重构) 在保留括号和加减乘除的基础上,至少包含一个平方或开根号。 + */ +public class JuniorHighSchoolGenerator extends PrimarySchoolGenerator { + @Override + public String generateSingleQuestion() { + // 1. 先生成一个完整的小学难度题目(包含括号等) + String basicQuestion = super.generateSingleQuestion(); + ThreadLocalRandom random = ThreadLocalRandom.current(); + + // 2. 使用正则表达式查找出题目中所有的数字 + Pattern pattern = Pattern.compile("\\d+"); + Matcher matcher = pattern.matcher(basicQuestion); + List numbers = new ArrayList<>(); + while (matcher.find()) { + numbers.add(matcher.group()); + } + + if (numbers.isEmpty()) { + return basicQuestion; // 如果没有数字,直接返回 + } + + // 3. 随机选择一个数字进行修改 + String numberToModify = numbers.get(random.nextInt(numbers.size())); + String modifiedNumber; + if (random.nextBoolean()) { + modifiedNumber = numberToModify + "²"; // 平方 + } else { + modifiedNumber = "√" + numberToModify; // 开根号 + } + + // 4. 将修改后的数字替换回原题目,只替换第一个匹配项以保证随机性 + return basicQuestion.replaceFirst(Pattern.quote(numberToModify), modifiedNumber); + } +} \ No newline at end of file diff --git a/src/main/java/com/mathgenerator/generator/PrimarySchoolGenerator.java b/src/main/java/com/mathgenerator/generator/PrimarySchoolGenerator.java new file mode 100644 index 0000000..24eadea --- /dev/null +++ b/src/main/java/com/mathgenerator/generator/PrimarySchoolGenerator.java @@ -0,0 +1,72 @@ +package com.mathgenerator.generator; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 小学题目生成器。 + * 生成包含 + - * / 和 () 的运算。 + * (已修正括号生成逻辑) + */ +public class PrimarySchoolGenerator implements QuestionGenerator { + + private static final String[] OPERATORS = {"+", "-", "*", "/"}; + + @Override + public String generateSingleQuestion() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + int operandCount = random.nextInt(2, 6); // 2到5个操作数 + + // 步骤1: 先生成一个不带括号的表达式组件列表 + List parts = new ArrayList<>(); + parts.add(String.valueOf(getOperand())); + for (int i = 1; i < operandCount; i++) { + parts.add(getRandomOperator()); + parts.add(String.valueOf(getOperand())); + } + + // 步骤2: 如果条件满足,对组件列表进行处理以添加括号 + if (operandCount > 2 && random.nextBoolean()) { + addParentheses(parts); + } + + // 步骤3: 将组件用空格连接成最终的字符串 + return String.join(" ", parts); + } + + /** + * 获取一个1到100之间的随机操作数。 + */ + private int getOperand() { + return ThreadLocalRandom.current().nextInt(1, 101); + } + + /** + * 获取一个随机的运算符。 + */ + private String getRandomOperator() { + return OPERATORS[ThreadLocalRandom.current().nextInt(OPERATORS.length)]; + } + + /** + * (已重写) 一个更健壮的方法,用于在表达式组件列表中添加括号。 + * @param parts 表达式组件列表,例如 ["55", "+", "8", "-", "21"] + */ + private void addParentheses(List parts) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + + // 操作数的位置在 0, 2, 4, ... + // 随机选择括号的起始和结束操作数 + int startOperandIndex = random.nextInt(parts.size() / 2); + int endOperandIndex = random.nextInt(startOperandIndex + 1, parts.size() / 2 + 1); + + // 将索引转换为在列表中的实际位置 + int startIndex = startOperandIndex * 2; + int endIndex = endOperandIndex * 2; + + // 在正确的位置插入括号 + parts.add(endIndex + 1, ")"); + parts.add(startIndex, "("); + } +} \ No newline at end of file diff --git a/src/main/java/com/mathgenerator/generator/QuestionGenerator.java b/src/main/java/com/mathgenerator/generator/QuestionGenerator.java new file mode 100644 index 0000000..15002d9 --- /dev/null +++ b/src/main/java/com/mathgenerator/generator/QuestionGenerator.java @@ -0,0 +1,12 @@ +package com.mathgenerator.generator; + +/** + * 题目生成器接口,定义了所有具体生成器必须实现的方法。 + */ +public interface QuestionGenerator { + /** + * 生成一道符合特定难度的数学题目。 + * @return 代表数学题目的字符串 + */ + String generateSingleQuestion(); +} diff --git a/src/main/java/com/mathgenerator/generator/SeniorHighSchoolGenerator.java b/src/main/java/com/mathgenerator/generator/SeniorHighSchoolGenerator.java new file mode 100644 index 0000000..a187a4b --- /dev/null +++ b/src/main/java/com/mathgenerator/generator/SeniorHighSchoolGenerator.java @@ -0,0 +1,42 @@ +package com.mathgenerator.generator; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 高中题目生成器。 + * (已重构) 继承初中生成器,在初中题目基础上,至少包含一个 sin, cos, 或 tan。 + */ +public class SeniorHighSchoolGenerator extends JuniorHighSchoolGenerator { // <-- 关键改动:继承自初中生成器 + private static final String[] TRIG_FUNCTIONS = {"sin", "cos", "tan"}; + + @Override + public String generateSingleQuestion() { + // 1. 先生成一个完整的初中难度题目(已包含小学内容和平方/开根号) + String juniorHighQuestion = super.generateSingleQuestion(); + ThreadLocalRandom random = ThreadLocalRandom.current(); + + // 2. 查找出题目中所有的数字 + Pattern pattern = Pattern.compile("\\d+"); + Matcher matcher = pattern.matcher(juniorHighQuestion); + List numbers = new ArrayList<>(); + while (matcher.find()) { + numbers.add(matcher.group()); + } + + if (numbers.isEmpty()) { + return juniorHighQuestion; + } + + // 3. 随机选择一个数字,用三角函数包裹 + String numberToModify = numbers.get(random.nextInt(numbers.size())); + String function = TRIG_FUNCTIONS[random.nextInt(TRIG_FUNCTIONS.length)]; + String modifiedNumber = function + "(" + numberToModify + ")"; + + // 4. 将修改后的部分替换回原题目 + return juniorHighQuestion.replaceFirst(Pattern.quote(numberToModify), modifiedNumber); + } +} \ No newline at end of file diff --git a/src/main/java/com/mathgenerator/model/Level.java b/src/main/java/com/mathgenerator/model/Level.java new file mode 100644 index 0000000..8a9ffba --- /dev/null +++ b/src/main/java/com/mathgenerator/model/Level.java @@ -0,0 +1,20 @@ +package com.mathgenerator.model; + +/** + * 学段枚举,用于类型安全地表示小学、初中和高中。 + */ +public enum Level { + PRIMARY("小学"), + JUNIOR_HIGH("初中"), + SENIOR_HIGH("高中"); + + private final String chineseName; + + Level(String chineseName) { + this.chineseName = chineseName; + } + + public String getChineseName() { + return chineseName; + } +} \ No newline at end of file diff --git a/src/main/java/com/mathgenerator/service/PaperService.java b/src/main/java/com/mathgenerator/service/PaperService.java new file mode 100644 index 0000000..3cb8718 --- /dev/null +++ b/src/main/java/com/mathgenerator/service/PaperService.java @@ -0,0 +1,63 @@ +package com.mathgenerator.service; + +import com.mathgenerator.generator.JuniorHighSchoolGenerator; +import com.mathgenerator.generator.PrimarySchoolGenerator; +import com.mathgenerator.generator.QuestionGenerator; +import com.mathgenerator.generator.SeniorHighSchoolGenerator; +import com.mathgenerator.model.Level; +import com.mathgenerator.auth.User; +import com.mathgenerator.storage.FileManager; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 业务逻辑核心,负责协调生成和存储。 + */ +public class PaperService { + private final FileManager fileManager; + + public PaperService(FileManager fileManager) { + this.fileManager = fileManager; + } + + /** + * 创建并保存一份完整的试卷。 + * @param user 用户对象 + * @param count 题目数量 + * @param currentLevel 当前选择的难度等级 + */ + public void createAndSavePaper(User user, int count, Level currentLevel) { + QuestionGenerator generator = createGenerator(currentLevel); + Set existingQuestions = fileManager.loadExistingQuestions(user.username()); + List newPaper = new ArrayList<>(); + Set generatedInSession = new HashSet<>(); + + System.out.println("正在生成题目,请稍候..."); + while (newPaper.size() < count) { + String question = generator.generateSingleQuestion(); + if (!existingQuestions.contains(question) && !generatedInSession.contains(question)) { + newPaper.add(question); + generatedInSession.add(question); + } + } + + try { + String filePath = fileManager.savePaper(user.username(), newPaper); + System.out.println("成功!" + count + "道" + currentLevel.getChineseName() + "数学题目已生成。"); + System.out.println("文件已保存至: " + filePath); + } catch (IOException e) { + System.err.println("错误:保存文件失败 - " + e.getMessage()); + } + } + + private QuestionGenerator createGenerator(Level level) { + return switch (level) { + case PRIMARY -> new PrimarySchoolGenerator(); + case JUNIOR_HIGH -> new JuniorHighSchoolGenerator(); + case SENIOR_HIGH -> new SeniorHighSchoolGenerator(); + }; + } +} \ No newline at end of file diff --git a/src/main/java/com/mathgenerator/storage/FileManager.java b/src/main/java/com/mathgenerator/storage/FileManager.java new file mode 100644 index 0000000..53f15a3 --- /dev/null +++ b/src/main/java/com/mathgenerator/storage/FileManager.java @@ -0,0 +1,87 @@ +package com.mathgenerator.storage; + +import java.io.IOException; +//TODO:这里不能导入所有类 +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 负责文件读写和历史题目加载。 + */ +public class FileManager { + private static final Path BASE_PATH = Paths.get("generated_papers"); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"); + private static final Pattern QUESTION_PATTERN = Pattern.compile("^\\d+\\.\\s+(.*)"); + + /** + * 将生成的试卷保存到文件。 + * @param username 用户名 + * @param paperContent 试卷内容列表 + * @return 保存成功后的文件名 + */ + public String savePaper(String username, List paperContent) throws IOException { + Path userDir = BASE_PATH.resolve(username); + Files.createDirectories(userDir); + + String timestamp = LocalDateTime.now().format(FORMATTER); + String fileName = timestamp + ".txt"; + Path filePath = userDir.resolve(fileName); + + StringBuilder formattedContent = new StringBuilder(); + for (int i = 0; i < paperContent.size(); i++) { + formattedContent.append(i + 1).append(". ").append(paperContent.get(i)); + if (i < paperContent.size() - 1) { + formattedContent.append(System.lineSeparator()).append(System.lineSeparator()); + } + } + + Files.writeString(filePath, formattedContent.toString(), StandardOpenOption.CREATE); + return filePath.toString(); + } + + /** + * 加载指定用户的所有历史题目用于查重。 + * @param username 用户名 + * @return 包含所有历史题目的Set集合 + */ + public Set loadExistingQuestions(String username) { + Path userDir = BASE_PATH.resolve(username); + if (!Files.exists(userDir)) { + return new HashSet<>(); + } + + try (Stream stream = Files.walk(userDir)) { + return stream + .filter(file -> !Files.isDirectory(file) && file.toString().endsWith(".txt")) + .flatMap(this::readQuestionsFromFile) + .collect(Collectors.toSet()); + } catch (IOException e) { + System.err.println("错误:读取历史文件失败 - " + e.getMessage()); + return new HashSet<>(); + } + } + + private Stream readQuestionsFromFile(Path file) { + try { + //TODO:这里需要修改警告:(72, 26) 在没有 'try-with-resources' 语句的情况下使用 'Stream' + return Files.lines(file) + .map(String::trim) + .filter(line -> !line.isEmpty()) + .map(line -> QUESTION_PATTERN.matcher(line).matches() ? + QUESTION_PATTERN.matcher(line).replaceAll("$1") : line); + } catch (IOException e) { + System.err.println("错误:读取文件 " + file + " 失败 - " + e.getMessage()); + return Stream.empty(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/mathgenerator/ui/ConsoleUI.java b/src/main/java/com/mathgenerator/ui/ConsoleUI.java new file mode 100644 index 0000000..6fad7ed --- /dev/null +++ b/src/main/java/com/mathgenerator/ui/ConsoleUI.java @@ -0,0 +1,152 @@ +package com.mathgenerator.ui; + +import com.mathgenerator.auth.Authenticator; +import com.mathgenerator.auth.User; +import com.mathgenerator.model.Level; +import com.mathgenerator.service.PaperService; + +import java.util.Optional; +import java.util.Scanner; + +/** + * 负责所有控制台用户界面的显示和交互。 + */ +public class ConsoleUI { + private final Scanner scanner = new Scanner(System.in); + private final Authenticator authenticator; + private final PaperService paperService; + + // 通过构造函数接收依赖的服务,这是一种常见的设计模式(依赖注入) + public ConsoleUI(Authenticator authenticator, PaperService paperService) { + this.authenticator = authenticator; + this.paperService = paperService; + } + + /** + * 启动并运行整个用户界面主循环。 + */ + public void run() { + //TODO:警告:(29, 9) 'while' 语句不能在未抛出异常的情况下完成 + while (true) { + Optional userOptional = handleLogin(); + userOptional.ifPresent(this::showUserMenu); + } + } + + private Optional handleLogin() { + printHeader("用户登录"); // 统一标题格式 + System.out.print("请输入用户名和密码 (用空格隔开): (输入 exit 退出程序)\n> "); + String line = scanner.nextLine(); + + if ("exit".equalsIgnoreCase(line.trim())) { + System.out.println("感谢使用,程序已退出。"); + System.exit(0); + } + + String[] credentials = line.split("\\s+"); + if (credentials.length != 2) { + System.out.println("输入格式错误,请重新输入。"); + return Optional.empty(); + } + + Optional userOptional = authenticator.login(credentials[0], credentials[1]); + if (userOptional.isEmpty()) { + System.out.println("用户名或密码错误,请重新输入。"); + } + return userOptional; + } + + private void showUserMenu(User user) { + Level currentLevel = user.level(); + System.out.println("\n登录成功! 欢迎 " + user.username()); + System.out.println("当前选择为 " + currentLevel.getChineseName() + " 出题"); + + while (true) { + printHeader("当前用户: " + user.username() + " | 当前难度: " + currentLevel.getChineseName()); + System.out.println("1. 生成题目"); + System.out.println("2. 切换难度级别"); + System.out.println("3. 退出当前用户"); + System.out.println("4. 退出程序"); + printSeparator(); + System.out.print("请选择操作 (1-4): "); + String choice = scanner.nextLine().trim(); + + switch (choice) { + case "1" -> handleGeneration(user, currentLevel); + case "2" -> currentLevel = handleLevelSwitchMenu(currentLevel); + case "3" -> { + System.out.println("用户 " + user.username() + " 已退出。"); + return; + } + case "4" -> { + System.out.println("感谢使用,程序已退出。"); + System.exit(0); + } + default -> System.out.println("无效输入,请输入 1-4 之间的数字。"); + } + } + } + + /** + * 处理切换难度的子菜单,加入循环以处理无效输入。 + */ + private Level handleLevelSwitchMenu(Level currentLevel) { + while (true) { // <-- 增加循环 + printHeader("请选择难度级别"); + System.out.println("1. 小学 (+, -, *, /, 括号)"); + System.out.println("2. 初中 (包含平方、开根号)"); + System.out.println("3. 高中 (包含三角函数)"); + System.out.println("0. 返回主菜单"); + printSeparator(); + System.out.print("请选择 (0-3): "); + String choice = scanner.nextLine().trim(); + + switch (choice) { + case "1": + System.out.println("难度已成功切换为 " + Level.PRIMARY.getChineseName() + "。"); + return Level.PRIMARY; // 直接返回,退出循环 + case "2": + System.out.println("难度已成功切换为 " + Level.JUNIOR_HIGH.getChineseName() + "。"); + return Level.JUNIOR_HIGH; // 直接返回,退出循环 + case "3": + System.out.println("难度已成功切换为 " + Level.SENIOR_HIGH.getChineseName() + "。"); + return Level.SENIOR_HIGH; // 直接返回,退出循环 + case "0": + System.out.println("已返回主菜单。"); + return currentLevel; // 返回当前难度,退出循环 + default: + // 如果输入无效,打印提示信息,循环将继续,要求用户重新输入 + System.out.println("无效输入,请输入 0-3 之间的数字。"); + } + } + } + + private void handleGeneration(User user, Level currentLevel) { + printHeader("生成 " + currentLevel.getChineseName() + " 题目"); + System.out.print("请输入生成题目数量 (10-30,输入 0 返回主菜单): "); + try { + int count = Integer.parseInt(scanner.nextLine().trim()); + if (count == 0) { + System.out.println("已取消生成,返回主菜单。"); + return; + } + if (count >= 10 && count <= 30) { + paperService.createAndSavePaper(user, count, currentLevel); + } else { + System.out.println("输入无效,题目数量必须在 10 到 30 之间。"); + } + } catch (NumberFormatException e) { + System.out.println("输入无效,请输入一个有效的数字。"); + } + } + + private void printHeader(String title) { + printSeparator(); + System.out.println(title); + printSeparator(); + } + + private void printSeparator() { + System.out.println("======================================================"); + } +} \ No newline at end of file -- 2.34.1