diff --git a/README.md b/README.md deleted file mode 100644 index cea4d0e..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# wanglei - diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..3e5709a --- /dev/null +++ b/doc/README.md @@ -0,0 +1,156 @@ +# 中小学数学卷子自动生成程序 + +## 项目简介 + +这是一个为中小学数学老师设计的命令行卷子自动生成程序,支持小学、初中、高中三个难度级别。程序能够根据教师账户类型自动生成相应难度的数学题目,并确保题目不重复。 + +## 功能特性 + +- **用户认证系统** - 支持小学、初中、高中各三个预设账户 +- **多难度题目生成** - 根据不同学段生成相应难度的数学题目 +- **智能查重** - 确保同一教师的题目不重复 +- **灵活切换** - 支持在登录状态下切换出题难度 +- **自动保存** - 按用户分文件夹保存,文件命名规范 + +## 预设账户 + +| 账户类型 | 用户名 | 密码 | +|---------|--------|------| +| 小学 | 张三1, 张三2, 张三3 | 123 | +| 初中 | 李四1, 李四2, 李四3 | 123 | +| 高中 | 王五1, 王五2, 王五3 | 123 | + +## 题目难度要求 + +### 小学题目 +- **运算符**: `+`, `-`, `*`, `/`, `()` +- **要求**: 只能包含基本四则运算和括号 +- **示例**: `15 + (23 - 8) * 3 = ` + +### 初中题目 +- **运算符**: `+`, `-`, `*`, `/`, `()`, `²`, `√` +- **要求**: 至少包含一个平方或开根号运算符 +- **示例**: `5² + √16 * 3 = ` + +### 高中题目 +- **运算符**: `+`, `-`, `*`, `/`, `()`, `sin`, `cos`, `tan` +- **要求**: 至少包含一个三角函数运算符 +- **示例**: `sin(45°) + cos(30°) * 2 = ` + +## 技术架构 + +### 项目结构 +src/ +├── Main.java # 程序入口 +├── User.java # 用户模型 +├── UserType.java # 用户类型枚举 +├── AuthManager.java # 认证管理器 +├── QuestionGenerator.java # 题目生成器接口 +├── PrimaryQuestionGenerator.java # 小学题目生成器 +├── JuniorQuestionGenerator.java # 初中题目生成器 +├── SeniorQuestionGenerator.java # 高中题目生成器 +├── QuestionGeneratorFactory.java # 工厂类 +├── QuestionService.java # 题目服务 +├── DuplicateChecker.java # 查重服务 +├── FileService.java # 文件服务 +└── Question.java # 题目模型 + +text + +### 设计模式 +- **工厂模式** - 题目生成器工厂 +- **策略模式** - 不同难度的题目生成策略 +- **面向对象设计** - 类职责分离 + +## 安装与运行 + +### 环境要求 +- Java 8 或更高版本 +- 支持命令行操作的系统 + +### 编译项目 +```bash +# 进入项目src目录 +cd src + +# 编译所有Java文件 +javac *.java + +# 运行程序 +java Main +使用说明 +1. 登录系统 +text +=== 中小学数学卷子自动生成程序 === +请输入用户名和密码(用空格隔开): +张三1 123 +当前选择为小学出题 +2. 生成题目 +text +准备生成小学数学题目,请输入生成题目数量(10-30,输入-1将退出当前用户,重新登录): +15 +题目生成完成!题目已保存到: questions/张三1/2025-01-20-14-30-25.txt +3. 切换难度 +text +切换为初中 +已切换为初中出题 +4. 退出登录 +text +-1 +已退出当前用户 +文件保存规范 +目录结构 +text +questions/ +├── 张三1/ +│ ├── 2025-01-20-14-30-25.txt +│ └── 2025-01-20-15-45-10.txt +├── 李四1/ +│ └── 2025-01-20-16-20-35.txt +└── 王五1/ + └── 2025-01-20-17-15-40.txt +文件格式 +text +1. 25 + 13 * (45 - 12) = + +2. 15 / 3 + 28 = + +3. (67 + 34) * 2 - 15 = +故障排除 +常见问题 +编译错误 + +确保所有Java文件在同一目录下 + +检查Java环境变量配置 + +运行错误 + +确认所有.class文件已生成 + +检查文件读写权限 + +题目生成失败 + +确认输入题目数量在10-30范围内 + +检查磁盘空间是否充足 + + 项目规范 +代码符合面向对象设计原则 + +类职责单一,方法简洁 + +包含完整的异常处理 + +支持题目查重功能 + + 版本信息 + +作者: 王磊 + +班级: 软件2301 + +学号:202326010106 + +项目名称: 中小学数学卷子自动生成程序 \ No newline at end of file diff --git a/src/AuthManager.java b/src/AuthManager.java new file mode 100644 index 0000000..24cfcf3 --- /dev/null +++ b/src/AuthManager.java @@ -0,0 +1,25 @@ +import java.util.Arrays; +import java.util.List; + +public class AuthManager { + private static final List USERS = Arrays.asList( + new User("张三1", "123", UserType.PRIMARY), + new User("张三2", "123", UserType.PRIMARY), + new User("张三3", "123", UserType.PRIMARY), + new User("李四1", "123", UserType.JUNIOR), + new User("李四2", "123", UserType.JUNIOR), + new User("李四3", "123", UserType.JUNIOR), + new User("王五1", "123", UserType.SENIOR), + new User("王五2", "123", UserType.SENIOR), + new User("王五3", "123", UserType.SENIOR) + ); + + public User authenticate(String username, String password) { + for (User user : USERS) { + if (user.getUsername().equals(username) && user.getPassword().equals(password)) { + return user; + } + } + return null; + } +} \ No newline at end of file diff --git a/src/DuplicateChecker.java b/src/DuplicateChecker.java new file mode 100644 index 0000000..c7da4b7 --- /dev/null +++ b/src/DuplicateChecker.java @@ -0,0 +1,71 @@ +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public class DuplicateChecker { + + public boolean isDuplicate(String username, String questionContent) { + String userDir = "questions/" + username + "/"; + File dir = new File(userDir); + + if (!dir.exists()) { + return false; + } + + File[] files = dir.listFiles((d, name) -> name.endsWith(".txt")); + if (files == null) { + return false; + } + + for (File file : files) { + try { + List lines = Files.readAllLines(Paths.get(file.getAbsolutePath())); + for (String line : lines) { + if (line.trim().isEmpty()) continue; + // 提取题目内容(去掉题号) + String content = line.substring(line.indexOf('.') + 1).trim(); + if (content.equals(questionContent)) { + return true; + } + } + } catch (IOException e) { + System.err.println("读取文件失败: " + file.getName()); + } + } + + return false; + } + + public List getUserQuestions(String username) { + List questions = new ArrayList<>(); + String userDir = "questions/" + username + "/"; + File dir = new File(userDir); + + if (!dir.exists()) { + return questions; + } + + File[] files = dir.listFiles((d, name) -> name.endsWith(".txt")); + if (files == null) { + return questions; + } + + for (File file : files) { + try { + List lines = Files.readAllLines(Paths.get(file.getAbsolutePath())); + for (String line : lines) { + if (line.trim().isEmpty()) continue; + String content = line.substring(line.indexOf('.') + 1).trim(); + questions.add(content); + } + } catch (IOException e) { + System.err.println("读取文件失败: " + file.getName()); + } + } + + return questions; + } +} \ No newline at end of file diff --git a/src/FileService.java b/src/FileService.java new file mode 100644 index 0000000..38a46a9 --- /dev/null +++ b/src/FileService.java @@ -0,0 +1,32 @@ +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +public class FileService { + + public String saveQuestions(String username, List questions) throws IOException { + // 创建用户目录 + String userDir = "questions/" + username + "/"; + File dir = new File(userDir); + if (!dir.exists()) { + dir.mkdirs(); + } + + // 生成文件名 + String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); + String filename = userDir + timestamp + ".txt"; + + // 写入文件 + try (FileWriter writer = new FileWriter(filename)) { + for (int i = 0; i < questions.size(); i++) { + writer.write(questions.get(i).toString()); + writer.write("\n\n"); // 题目之间空一行 + } + } + + return filename; + } +} \ No newline at end of file diff --git a/src/JuniorQuestionGenerator.java b/src/JuniorQuestionGenerator.java new file mode 100644 index 0000000..362edfa --- /dev/null +++ b/src/JuniorQuestionGenerator.java @@ -0,0 +1,47 @@ +import java.util.Random; + +public class JuniorQuestionGenerator implements QuestionGenerator { + private Random random = new Random(); + private final String[] OPERATORS = {"+", "-", "*", "/"}; + + @Override + public String generateQuestion() { + int operandCount = random.nextInt(3) + 2; // 2-4个操作数 + StringBuilder question = new StringBuilder(); + + // 确保至少有一个平方或开根号 + boolean hasSpecialOperator = false; + int specialOperatorPosition = random.nextInt(operandCount); + + for (int i = 0; i < operandCount; i++) { + if (i > 0) { + question.append(" ").append(OPERATORS[random.nextInt(OPERATORS.length)]).append(" "); + } + + // 在特定位置添加平方或开根号 + if (i == specialOperatorPosition || !hasSpecialOperator) { + if (random.nextBoolean()) { + // 平方 + int num = random.nextInt(20) + 1; // 1-20的平方 + question.append(num).append("²"); + hasSpecialOperator = true; + } else { + // 开根号 + int num = random.nextInt(100) + 1; // 1-100开根号 + question.append("√").append(num); + hasSpecialOperator = true; + } + } else { + question.append(random.nextInt(100) + 1); + } + } + + return question.toString() + " = "; + } + + @Override + public boolean isValidQuestion(String question) { + return (question.contains("²") || question.contains("√")) && + question.contains("="); + } +} \ No newline at end of file diff --git a/src/PrimaryQuestionGenerator.java b/src/PrimaryQuestionGenerator.java new file mode 100644 index 0000000..7c79686 --- /dev/null +++ b/src/PrimaryQuestionGenerator.java @@ -0,0 +1,41 @@ +import java.util.Random; + +public class PrimaryQuestionGenerator implements QuestionGenerator { + private Random random = new Random(); + private final String[] OPERATORS = {"+", "-", "*", "/"}; + + @Override + public String generateQuestion() { + int operandCount = random.nextInt(3) + 2; // 2-4个操作数 + StringBuilder question = new StringBuilder(); + + // 随机决定是否添加括号 + boolean hasParentheses = operandCount >= 3 && random.nextBoolean(); + int parenthesesPosition = hasParentheses ? random.nextInt(operandCount - 1) : -1; + + for (int i = 0; i < operandCount; i++) { + if (i > 0) { + question.append(" ").append(OPERATORS[random.nextInt(OPERATORS.length)]).append(" "); + } + + if (hasParentheses && i == parenthesesPosition) { + question.append("("); + } + + question.append(random.nextInt(100) + 1); // 1-100 + + if (hasParentheses && i == parenthesesPosition + 1) { + question.append(")"); + } + } + + return question.toString() + " = "; + } + + @Override + public boolean isValidQuestion(String question) { + return question.matches(".*[+\\-*/].*") && + !question.matches(".*[√²sincostan].*") && + question.contains("="); + } +} \ No newline at end of file diff --git a/src/Question.java b/src/Question.java new file mode 100644 index 0000000..50cb0f8 --- /dev/null +++ b/src/Question.java @@ -0,0 +1,20 @@ +public class Question { + private int number; + private String content; + private String userType; + + public Question(int number, String content, String userType) { + this.number = number; + this.content = content; + this.userType = userType; + } + + public int getNumber() { return number; } + public String getContent() { return content; } + public String getUserType() { return userType; } + + @Override + public String toString() { + return number + ". " + content; + } +} \ No newline at end of file diff --git a/src/QuestionGenerator.java b/src/QuestionGenerator.java new file mode 100644 index 0000000..70bf9ae --- /dev/null +++ b/src/QuestionGenerator.java @@ -0,0 +1,4 @@ +public interface QuestionGenerator { + String generateQuestion(); + boolean isValidQuestion(String question); +} \ No newline at end of file diff --git a/src/QuestionGeneratorFactory.java b/src/QuestionGeneratorFactory.java new file mode 100644 index 0000000..7a5afe3 --- /dev/null +++ b/src/QuestionGeneratorFactory.java @@ -0,0 +1,14 @@ +public class QuestionGeneratorFactory { + public static QuestionGenerator createGenerator(UserType userType) { + switch (userType) { + case PRIMARY: + return new PrimaryQuestionGenerator(); + case JUNIOR: + return new JuniorQuestionGenerator(); + case SENIOR: + return new SeniorQuestionGenerator(); + default: + throw new IllegalArgumentException("不支持的题目类型: " + userType); + } + } +} \ No newline at end of file diff --git a/src/QuestionService.java b/src/QuestionService.java new file mode 100644 index 0000000..314ce84 --- /dev/null +++ b/src/QuestionService.java @@ -0,0 +1,41 @@ +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class QuestionService { + private DuplicateChecker duplicateChecker = new DuplicateChecker(); + private FileService fileService = new FileService(); + + public void generateAndSaveQuestions(User user, int count) { + QuestionGenerator generator = QuestionGeneratorFactory.createGenerator(user.getUserType()); + List questions = new ArrayList<>(); + int generated = 0; + int attempts = 0; + int maxAttempts = count * 10; // 防止无限循环 + + while (generated < count && attempts < maxAttempts) { + String questionContent = generator.generateQuestion(); + + // 检查题目是否有效且不重复 + if (generator.isValidQuestion(questionContent) && + !duplicateChecker.isDuplicate(user.getUsername(), questionContent)) { + + questions.add(new Question(generated + 1, questionContent, + user.getUserType().getDisplayName())); + generated++; + } + attempts++; + } + + if (generated < count) { + System.out.println("警告:只生成了 " + generated + " 个不重复的题目(请求数量:" + count + ")"); + } + + try { + String filename = fileService.saveQuestions(user.getUsername(), questions); + System.out.println("题目已保存到: " + filename); + } catch (IOException e) { + System.out.println("保存文件失败: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/SeniorQuestionGenerator.java b/src/SeniorQuestionGenerator.java new file mode 100644 index 0000000..35206bc --- /dev/null +++ b/src/SeniorQuestionGenerator.java @@ -0,0 +1,41 @@ +import java.util.Random; + +public class SeniorQuestionGenerator implements QuestionGenerator { + private Random random = new Random(); + private final String[] OPERATORS = {"+", "-", "*", "/"}; + private final String[] TRIG_FUNCTIONS = {"sin", "cos", "tan"}; + + @Override + public String generateQuestion() { + int operandCount = random.nextInt(3) + 2; // 2-4个操作数 + StringBuilder question = new StringBuilder(); + + // 确保至少有一个三角函数 + boolean hasTrigFunction = false; + int trigFunctionPosition = random.nextInt(operandCount); + + for (int i = 0; i < operandCount; i++) { + if (i > 0) { + question.append(" ").append(OPERATORS[random.nextInt(OPERATORS.length)]).append(" "); + } + + // 在特定位置添加三角函数 + if (i == trigFunctionPosition || !hasTrigFunction) { + String trigFunction = TRIG_FUNCTIONS[random.nextInt(TRIG_FUNCTIONS.length)]; + int angle = random.nextInt(360); // 0-359度 + question.append(trigFunction).append("(").append(angle).append("°)"); + hasTrigFunction = true; + } else { + question.append(random.nextInt(100) + 1); + } + } + + return question.toString() + " = "; + } + + @Override + public boolean isValidQuestion(String question) { + return (question.contains("sin") || question.contains("cos") || question.contains("tan")) && + question.contains("="); + } +} \ No newline at end of file diff --git a/src/User.java b/src/User.java new file mode 100644 index 0000000..fadf54e --- /dev/null +++ b/src/User.java @@ -0,0 +1,25 @@ +public class User { + private String username; + private String password; + private UserType userType; + + public User(String username, String password, UserType userType) { + this.username = username; + this.password = password; + this.userType = userType; + } + + // Getters + public String getUsername() { return username; } + public String getPassword() { return password; } + public UserType getUserType() { return userType; } + + public void setUserType(UserType userType) { + this.userType = userType; + } + + @Override + public String toString() { + return username + " (" + userType.getDisplayName() + ")"; + } +} \ No newline at end of file diff --git a/src/UserType.java b/src/UserType.java new file mode 100644 index 0000000..82b9a91 --- /dev/null +++ b/src/UserType.java @@ -0,0 +1,24 @@ +public enum UserType { + PRIMARY("小学"), + JUNIOR("初中"), + SENIOR("高中"); + + private final String displayName; + + UserType(String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName; + } + + public static UserType fromDisplayName(String displayName) { + for (UserType type : values()) { + if (type.displayName.equals(displayName)) { + return type; + } + } + return null; + } +} \ No newline at end of file diff --git a/src/main.java b/src/main.java new file mode 100644 index 0000000..1d62258 --- /dev/null +++ b/src/main.java @@ -0,0 +1,95 @@ +import java.util.Scanner; + +public class Main { + private static final AuthManager authManager = new AuthManager(); + private static final QuestionService questionService = new QuestionService(); + private static final Scanner scanner = new Scanner(System.in); + private static User currentUser = null; + + public static void main(String[] args) { + System.out.println("=== 中小学数学卷子自动生成程序 ==="); + + while (true) { + if (currentUser == null) { + login(); + } else { + generateQuestions(); + } + } + } + + private static void login() { + System.out.println("请输入用户名和密码(用空格隔开):"); + try { + String input = scanner.nextLine().trim(); + if (input.isEmpty()) { + return; + } + + String[] credentials = input.split("\\s+"); + + if (credentials.length != 2) { + System.out.println("请输入正确的用户名、密码"); + return; + } + + currentUser = authManager.authenticate(credentials[0], credentials[1]); + + if (currentUser == null) { + System.out.println("请输入正确的用户名、密码"); + } else { + System.out.println("当前选择为" + currentUser.getUserType().getDisplayName() + "出题"); + } + } catch (Exception e) { + System.out.println("输入格式错误,请重新输入"); + } + } + + private static void generateQuestions() { + System.out.println("准备生成" + currentUser.getUserType().getDisplayName() + + "数学题目,请输入生成题目数量(10-30,输入-1将退出当前用户,重新登录):"); + + try { + String input = scanner.nextLine().trim(); + + if (input.startsWith("切换为")) { + handleSwitchCommand(input); + return; + } + + int count = Integer.parseInt(input); + + if (count == -1) { + currentUser = null; + System.out.println("已退出当前用户"); + return; + } + + if (count < 10 || count > 30) { + System.out.println("题目数量范围应为10-30"); + return; + } + + questionService.generateAndSaveQuestions(currentUser, count); + System.out.println("题目生成完成!可继续生成题目或输入-1退出登录"); + + } catch (NumberFormatException e) { + System.out.println("请输入有效的数字"); + } catch (Exception e) { + System.out.println("发生错误:" + e.getMessage()); + } + } + + private static void handleSwitchCommand(String command) { + String targetType = command.substring(3).trim(); + + UserType newType = UserType.fromDisplayName(targetType); + if (newType == null) { + System.out.println("请输入小学、初中和高中三个选项中的一个"); + return; + } + + currentUser.setUserType(newType); + System.out.println("已切换为" + currentUser.getUserType().getDisplayName() + "出题"); + } +} \ No newline at end of file