From f9058bed2cf0678d70aa01aa2775cd80c5fb9019 Mon Sep 17 00:00:00 2001 From: "@yu-xiuhui" <2810672597@qq.com> Date: Sat, 11 Oct 2025 21:37:01 +0800 Subject: [PATCH 01/10] 1 --- src/Main.java | 8 ++++++++ src/auth/User.java | 20 ++++++++++++++++++++ src/auth/UserManager.java | 13 +++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 src/Main.java create mode 100644 src/auth/User.java create mode 100644 src/auth/UserManager.java diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..c58f279 --- /dev/null +++ b/src/Main.java @@ -0,0 +1,8 @@ +import ui.MainApplication; + +public class Main { + public static void main(String[] args) { + // R1: 启动图形化界面应用 (JavaFX) + MainApplication.launch(MainApplication.class, args); + } +} \ No newline at end of file diff --git a/src/auth/User.java b/src/auth/User.java new file mode 100644 index 0000000..fdb6079 --- /dev/null +++ b/src/auth/User.java @@ -0,0 +1,20 @@ +package auth; + +public class User { + private String username; + private String password; + private String type; + private String email; + + public User(String username, String password, String type, String email) { + this.username = username; + this.password = password; + this.type = type; + this.email = email; + } + + public String getUsername() { return username; } + public String getPassword() { return password; } + public String getType() { return type; } + public String getEmail() { return email; } +} diff --git a/src/auth/UserManager.java b/src/auth/UserManager.java new file mode 100644 index 0000000..f2efced --- /dev/null +++ b/src/auth/UserManager.java @@ -0,0 +1,13 @@ +package auth; + +public class UserManager { + private final AuthService authService; + + public UserManager() { + this.authService = new AuthService(); + } + + public User login(String username, String password) { + return authService.login(username, password); + } +} -- 2.34.1 From 3b85d70641e61416297f326356b20dd79620c0af Mon Sep 17 00:00:00 2001 From: "@yu-xiuhui" <2810672597@qq.com> Date: Sun, 12 Oct 2025 11:43:07 +0800 Subject: [PATCH 02/10] 2 --- src/auth/AuthService.java | 206 +++++++++++++++++++ src/generator/HighGenerator.java | 15 ++ src/generator/MiddleGenerator.java | 15 ++ src/generator/PrimaryGenerator.java | 15 ++ src/generator/Problem.java | 61 ++++++ src/generator/ProblemGenerator.java | 7 + src/service/ExamService.java | 109 ++++++++++ src/util/EmailService.java | 69 +++++++ src/util/ExpressionUtils.java | 306 ++++++++++++++++++++++++++++ src/util/FileUtils.java | 79 +++++++ src/util/ModifyUtils.java | 76 +++++++ 11 files changed, 958 insertions(+) create mode 100644 src/auth/AuthService.java create mode 100644 src/generator/HighGenerator.java create mode 100644 src/generator/MiddleGenerator.java create mode 100644 src/generator/PrimaryGenerator.java create mode 100644 src/generator/Problem.java create mode 100644 src/generator/ProblemGenerator.java create mode 100644 src/service/ExamService.java create mode 100644 src/util/EmailService.java create mode 100644 src/util/ExpressionUtils.java create mode 100644 src/util/FileUtils.java create mode 100644 src/util/ModifyUtils.java diff --git a/src/auth/AuthService.java b/src/auth/AuthService.java new file mode 100644 index 0000000..3452034 --- /dev/null +++ b/src/auth/AuthService.java @@ -0,0 +1,206 @@ +package auth; + +import util.ModifyUtils; +import util.EmailService; +import java.io.*; +import java.util.*; +import java.util.regex.Pattern; + +public class AuthService { + private static final String USER_FILE_PATH = "user.txt"; + private final EmailService emailService = new EmailService(); + + + + + public AuthService() { + // 检查用户文件是否存在 + File userFile = new File(USER_FILE_PATH); + if (!userFile.exists()) { + System.err.println("用户文件不存在: " + USER_FILE_PATH); + try { + // 尝试创建文件 + if (userFile.createNewFile()) { + System.out.println("已创建用户文件: " + USER_FILE_PATH); + } + } catch (IOException e) { + System.err.println("创建用户文件失败: " + e.getMessage()); + } + } + + } + + + + // --- 校验和辅助方法 --- + + private static final Pattern EMAIL_PATTERN = + Pattern.compile("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}$"); + + // R3: 密码校验: 6-10 位, 必须含大小写字母和数字 + public static boolean isPasswordValid(String password) { + if (password == null || password.length() < 6 || + password.length() > 10) { + return false; + } + Pattern pattern = + Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{6,10}$"); + return pattern.matcher(password).matches(); + } + + public boolean validateEmail(String email) { + if (email == null){ + return false; + } + return EMAIL_PATTERN.matcher(email).matches(); + } + + // --- 用户加载和保存 --- + + private User parseLine(String line, int lineNumber) { + line = line.trim(); + if (line.isEmpty()) { + return null; + } + + String[] parts = line.split("\\s+"); + if (parts.length != 4) { + System.out.println("警告: 第" + lineNumber + + "行数据格式不正确,需要4个数据。"); + return null; + } + return new User(parts[0], parts[1], parts[2], parts[3]); + } + + public List loadUsers() { + List users = new ArrayList<>(); + int lineNumber = 1; + + try (BufferedReader br = new BufferedReader(new FileReader(USER_FILE_PATH))) { + String line; + while ((line = br.readLine()) != null) { + User user = parseLine(line, lineNumber); + if (user != null) { + users.add(user); + } + lineNumber++; + } + } catch (IOException e) { + System.out.println("读取文件时发生错误: " + e.getMessage()); + } + + return users; + } + + // --- 认证和注册 --- + + public User login(String username, String password) { + List users = loadUsers(); + + for (User user : users) { + if ((user.getUsername().equals(username) || user.getEmail().equals(username)) && + user.getPassword().equals(password)) { + return user; + } + } + System.out.println("登录失败!"); + return null; + } + + public boolean isUsernameOrEmailTaken(String username, String email) { + List users = loadUsers(); + for (User user : users) { + if ((username != null && user.getUsername().equals(username)) || + (email != null && user.getEmail().equals(email))) { + return true; + } + } + return false; + } + + // R2: 实际发送验证邮件 + public String sendVerificationCode(String email) { + if (!validateEmail(email) || isUsernameOrEmailTaken(null, email)) { + return null; + } + String code = emailService.generateVerificationCode(); + if (emailService.sendVerificationEmail(email, code)) { + return code; + } + return null; + } + + private void writeUserData(String userData) throws IOException { + try (BufferedWriter writer = + new BufferedWriter(new FileWriter(USER_FILE_PATH, true))) { + writer.write(userData); + writer.newLine(); + } + } + + // R2: 注册用户 + public boolean registerUser(String username, String password, String email) { + if (isUsernameOrEmailTaken(username, email)) { + return false; + } + + // 格式: username password type email + String userData = username + " " + password + " 小学 " + email; + try { + writeUserData(userData); + return true; + } catch (IOException e) { + System.out.println("写入文件时发生错误: " + e.getMessage()); + return false; + } + } + + // --- 用户修改 --- + + public boolean updatePassword(String username, String newPassword) { + // 索引 1 是密码 + int result = ModifyUtils.modifyFileField(USER_FILE_PATH, username, 1, newPassword); + return result != -1; + } + + public boolean updateDifficulty(String username, String newDifficulty) { + List lines = new ArrayList<>(); + boolean found = false; + + try (BufferedReader br = new BufferedReader(new FileReader(USER_FILE_PATH))) { + String line; + while ((line = br.readLine()) != null) { + String[] parts = line.trim().split("\\s+"); + + if (parts.length >= 4 && parts[0].equals(username)) { + // 找到目标用户,更新难度 + parts[2] = newDifficulty; + line = String.join(" ", parts); + found = true; + } + lines.add(line); + } + } catch (IOException e) { + System.err.println("读取用户文件错误: " + e.getMessage()); + return false; + } + + if (!found) { + System.err.println("未找到用户: " + username); + return false; + } + + // 写回文件 + try (BufferedWriter bw = new BufferedWriter(new FileWriter(USER_FILE_PATH))) { + for (String line : lines) { + bw.write(line); + bw.newLine(); + } + return true; + } catch (IOException e) { + System.err.println("写入用户文件错误: " + e.getMessage()); + return false; + } + } + +} \ No newline at end of file diff --git a/src/generator/HighGenerator.java b/src/generator/HighGenerator.java new file mode 100644 index 0000000..84749e8 --- /dev/null +++ b/src/generator/HighGenerator.java @@ -0,0 +1,15 @@ +package generator; + +import util.ExpressionUtils; +import java.util.*; + +public class HighGenerator implements ProblemGenerator { + @Override + public List generateProblems(int count) { + List problems = new ArrayList<>(); + for (int i = 0; i < count; i++) { + problems.add(ExpressionUtils.generateHighExpr()); + } + return problems; + } +} \ No newline at end of file diff --git a/src/generator/MiddleGenerator.java b/src/generator/MiddleGenerator.java new file mode 100644 index 0000000..1815304 --- /dev/null +++ b/src/generator/MiddleGenerator.java @@ -0,0 +1,15 @@ +package generator; + +import util.ExpressionUtils; +import java.util.*; + +public class MiddleGenerator implements ProblemGenerator { + @Override + public List generateProblems(int count) { + List problems = new ArrayList<>(); + for (int i = 0; i < count; i++) { + problems.add(ExpressionUtils.generateMiddleExpr()); + } + return problems; + } +} \ No newline at end of file diff --git a/src/generator/PrimaryGenerator.java b/src/generator/PrimaryGenerator.java new file mode 100644 index 0000000..4609a5a --- /dev/null +++ b/src/generator/PrimaryGenerator.java @@ -0,0 +1,15 @@ +package generator; + +import util.ExpressionUtils; +import java.util.*; + +public class PrimaryGenerator implements ProblemGenerator { + @Override + public List generateProblems(int count) { + List problems = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + problems.add( ExpressionUtils.generatePrimaryExpr()); + } + return problems; + } +} \ No newline at end of file diff --git a/src/generator/Problem.java b/src/generator/Problem.java new file mode 100644 index 0000000..bbf5127 --- /dev/null +++ b/src/generator/Problem.java @@ -0,0 +1,61 @@ +package generator; + +import java.util.List; +import java.util.Collections; + +public class Problem { + private final String expression; + private final double result; + private final List options; + private final String correctAnswerOption; + + public Problem(String expression, double result, List options, + String correctAnswerOption) { + this.expression = expression; + this.result = result; + this.options = options; + this.correctAnswerOption = correctAnswerOption; + } + + public String getQuestionText() { + return "计算: " + expression + " = ?"; + } + + public String getExpression() { + return expression; + } + + public List getOptions() { + return Collections.unmodifiableList(options); + } + + public String getCorrectAnswerOption() { + return formatResult(result); + } + + private String formatResult(double result) { + if (Double.isNaN(result)) { + return "Error"; + } + if (Math.abs(result - Math.round(result)) < 0.0001) { + return String.valueOf((int) Math.round(result)); + } + return String.format("%.2f", result); + } + + public double getResult() { + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getQuestionText()).append("\n"); + char optionChar = 'A'; + for (String option : options) { + sb.append(optionChar++).append(". ").append(option).append(" "); + } + sb.append("\n正确答案: ").append(correctAnswerOption); + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/generator/ProblemGenerator.java b/src/generator/ProblemGenerator.java new file mode 100644 index 0000000..d9a821e --- /dev/null +++ b/src/generator/ProblemGenerator.java @@ -0,0 +1,7 @@ +package generator; + +import java.util.List; + +public interface ProblemGenerator { + List generateProblems(int count); +} diff --git a/src/service/ExamService.java b/src/service/ExamService.java new file mode 100644 index 0000000..ff1e339 --- /dev/null +++ b/src/service/ExamService.java @@ -0,0 +1,109 @@ +package service; + +import auth.User; +import generator.Problem; +import generator.ProblemGenerator; +import generator.PrimaryGenerator; +import generator.MiddleGenerator; +import generator.HighGenerator; +import util.FileUtils; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class ExamService { + + public ProblemGenerator getGenerator(String type) { + switch (type) { + case "小学": return new PrimaryGenerator(); + case "初中": return new MiddleGenerator(); + case "高中": return new HighGenerator(); + default: return null; + } + } + + private Problem generateUniqueProblem(ProblemGenerator generator, + Set historyExpressions) { + int attempts = 0; + final int maxAttemptsPerProblem = 1000; + + while (attempts < maxAttemptsPerProblem) { + Problem problem = generator.generateProblems(1).get(0); + // 查重基于题干 (表达式字符串) + if (!historyExpressions.contains(problem.getExpression())) { + historyExpressions.add(problem.getExpression()); + return problem; + } + attempts++; + } + return null; + } + + // R5: 生成一张唯一试卷,并将其保存到用户的历史记录中。 + public List generateExam(User currentUser, int count) { + if (count < 10 || count > 30) { + throw new IllegalArgumentException("题目数量必须在 10 到 30 之间"); + } + + ProblemGenerator generator = getGenerator(currentUser.getType()); + if (generator == null) { + System.err.println("错误: 无法获取题目生成器,用户类型: " + currentUser.getType()); + return new ArrayList<>(); + } + + Set historyExpressions = FileUtils.loadHistory(currentUser.getUsername()); + List problems = new ArrayList<>(); + + for(int i = 0; i < count; i++) { + Problem problem = generateUniqueProblem(generator, historyExpressions); + if (problem != null) { + problems.add(problem); + } else { + System.err.println("警告: 无法生成足够数量的唯一题目。已生成: " + problems.size() + " 题"); + break; + } + } + + if (!problems.isEmpty()) { + FileUtils.saveProblems(currentUser.getUsername(), problems); + } else { + System.err.println("错误: 未能生成任何题目"); + } + + return problems; + } + + // 将这个私有方法改为公共方法,以便在其他类中调用 + public boolean isAnswerCorrect(Problem problem, String userAnswer) { + if (userAnswer == null || problem.getCorrectAnswerOption() == null) { + return false; + } + + boolean isCorrect = userAnswer.trim().equalsIgnoreCase(problem.getCorrectAnswerOption().trim()); + return isCorrect; + } + + // R6: 根据答对的百分比计算分数。 + public int calculateScore(List problems, List answers) { + if (problems == null || answers == null || + problems.size() != answers.size() || + problems.isEmpty()) { + System.err.println("评分错误: 题目和答案数量不匹配"); + return 0; + } + + int correctCount = 0; + for (int i = 0; i < problems.size(); i++) { + Problem problem = problems.get(i); + String userAnswer = answers.get(i); + + if (isAnswerCorrect(problem, userAnswer)) { + correctCount++; + } + } + + double score = ((double) correctCount / problems.size()) * 100; + int finalScore = (int) Math.round(score); + return finalScore; + } +} \ No newline at end of file diff --git a/src/util/EmailService.java b/src/util/EmailService.java new file mode 100644 index 0000000..1535aab --- /dev/null +++ b/src/util/EmailService.java @@ -0,0 +1,69 @@ +package util; + +import java.util.Properties; +import java.util.Random; +import javax.mail.*; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; + +public class EmailService { + // 邮件配置:请修改此处或设置环境变量 + private static final String SENDER_EMAIL = "2810672597@qq.com"; + private static final String SENDER_PASSWORD =("acyhhpwgcqkqdgff"); + private static final String SMTP_HOST = "smtp.qq.com"; // 例如: smtp.gmail.com + private static final String SMTP_PORT = "587"; // 或 465 (SSL) + private static final String SUBJECT = "数学学习软件注册验证码"; + + public String generateVerificationCode() { + Random rand = new Random(); + return String.format("%06d", rand.nextInt(1000000)); + } + + private Properties getMailProperties() { + Properties props = new Properties(); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.starttls.enable", "true"); + props.put("mail.smtp.host", SMTP_HOST); + props.put("mail.smtp.port", SMTP_PORT); + return props; + } + + private Message createMessage(Session session, String recipientEmail, + String code) throws MessagingException { + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(SENDER_EMAIL)); + message.setRecipients( + Message.RecipientType.TO, + InternetAddress.parse(recipientEmail) + ); + message.setSubject(SUBJECT); + message.setText("您的注册验证码是: " + code + + "\n\n请在应用中输入此代码完成注册。"); + return message; + } + + // R2: 实际发送验证邮件 + public boolean sendVerificationEmail(String recipientEmail, String code) { + if (SENDER_EMAIL == null || SENDER_PASSWORD == null) { + System.err.println("错误: 邮件发送凭证未设置。"); + return false; + } + + Session session = Session.getInstance(getMailProperties(), new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(SENDER_EMAIL, SENDER_PASSWORD); + } + }); + + try { + Message message = createMessage(session, recipientEmail, code); + Transport.send(message); + return true; + + } catch (MessagingException e) { + System.err.println("邮件发送失败: " + e.getMessage()); + return false; + } + } +} diff --git a/src/util/ExpressionUtils.java b/src/util/ExpressionUtils.java new file mode 100644 index 0000000..832bfe2 --- /dev/null +++ b/src/util/ExpressionUtils.java @@ -0,0 +1,306 @@ +package util; + +import generator.Problem; + +import java.util.*; + +public class ExpressionUtils { + private static Random rand = new Random(); + + private static final String[] OPS_PRIMARY = {"+", "-", "*", "/"}; + private static final String[] OPS_MIDDLE = {"+", "-", "*", "/"}; + private static final String[] OPS_HIGH = {"+", "-", "*", "/", "^2", "sqrt"}; + + + public static String randomNumber() { + return String.valueOf(rand.nextInt(100) + 1); + } + + // --- 表达式求解和选项生成辅助方法 --- + + public static double solveExpression(String expr) { + // 将 x^2 替换为 pow(x, 2) 以便解析 + String parsableExpr = expr.replaceAll("(\\d+|\\([^)]+\\))\\^2", "pow($1, 2)"); + + try { + return new Object() { + int pos = -1, ch; + + void nextChar() { + ch = (++pos < parsableExpr.length()) ? parsableExpr.charAt(pos) : -1; + } + + boolean eat(int charToEat) { + while (ch == ' ') nextChar(); + if (ch == charToEat) { + nextChar(); + return true; + } + return false; + } + + double parse() { + nextChar(); + double x = parseExpression(); + if (pos < parsableExpr.length()) throw new RuntimeException("Unexpected: " + (char) ch); + return x; + } + + double parseExpression() { + double x = parseTerm(); + for (; ; ) { + if (eat('+')) x += parseTerm(); // addition + else if (eat('-')) x -= parseTerm(); // subtraction + else return x; + } + } + + double parseTerm() { + double x = parseFactor(); + for (; ; ) { + if (eat('*')) x *= parseFactor(); // multiplication + else if (eat('/')) { // division + double divisor = parseFactor(); + if (divisor == 0) throw new ArithmeticException("Division by zero"); + x /= divisor; + } else return x; + } + } + + double parseFactor() { + if (eat('+')) return parseFactor(); // unary plus + if (eat('-')) return -parseFactor(); // unary minus + + double x; + int startPos = this.pos; + if (eat('(')) { // parentheses + x = parseExpression(); + eat(')'); + } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers + while ((ch >= '0' && ch <= '9') || ch == '.') nextChar(); + x = Double.parseDouble(parsableExpr.substring(startPos, this.pos)); + } else if (ch >= 'a' && ch <= 'z') { // functions + while (ch >= 'a' && ch <= 'z') nextChar(); + String func = parsableExpr.substring(startPos, this.pos); + x = parseFactor(); + switch (func) { + case "sqrt": + x = Math.sqrt(x); + break; + case "sin": + x = Math.sin(Math.toRadians(x)); + break; + case "cos": + x = Math.cos(Math.toRadians(x)); + break; + case "tan": + x = Math.tan(Math.toRadians(x)); + break; + case "pow": + eat(','); + double y = parseExpression(); + x = Math.pow(x, y); + eat(')'); + break; + default: + throw new RuntimeException("Unknown function: " + func); + } + } else { + throw new RuntimeException("Unexpected: " + (char) ch); + } + + return x; + } + }.parse(); + } catch (Exception e) { + System.err.println("Error parsing expression: '" + expr + "'. Parsable version: '" + parsableExpr + "'. Error: " + e.getMessage()); + return Double.NaN; + } + } + + + private static String formatResult(double result) { + if (Double.isNaN(result)) { + return "Error"; + } + if (Math.abs(result - Math.round(result)) < 0.0001) { + return String.valueOf((int) Math.round(result)); + } + return String.format("%.2f", result); + } + + /** + * MODIFICATION: 确保为整数答案生成整数干扰项 + */ + private static String generateDistractor(double correctResult, List existingOptions) { + if (Double.isNaN(correctResult)) { + return String.valueOf(rand.nextInt(100)); + } + + // 检查正确答案是否为整数 + boolean isIntegerResult = Math.abs(correctResult - Math.round(correctResult)) < 0.0001; + + double distractor; + String distractorStr; + + do { + if (isIntegerResult) { + // 生成一个整数干扰项 + int offset = rand.nextInt(20) + 1; // 扩大随机范围以避免重复 + distractor = rand.nextBoolean() ? Math.round(correctResult) + offset : Math.round(correctResult) - offset; + // 确保干扰项不为负数 + if (distractor < 0) { + distractor = Math.round(correctResult) + offset; + } + } else { + // 答案是小数,则生成小数干扰项 + distractor = correctResult + + (rand.nextInt(10) + 1) * (rand.nextBoolean() ? 1 : -1) + + (rand.nextDouble() * 0.9); + } + distractorStr = formatResult(distractor); + } while (existingOptions.contains(distractorStr)); // 确保选项不重复 + return distractorStr; + } + + + public static List generateOptions(double correctResult) { + List options = new ArrayList<>(); + options.add(formatResult(correctResult)); + + for (int i = 0; i < 3; i++) { + options.add(generateDistractor(correctResult, options)); + } + + Collections.shuffle(options); + return options; + } + + private static Problem createProblem(String expression) { + double result = solveExpression(expression); + String correctAnswerOption = formatResult(result); + List options = generateOptions(result); + return new Problem(expression, result, options, correctAnswerOption); + } + + // --- 题目生成方法 --- + + private static int getDivisor(int dividend) { + List divisors = new ArrayList<>(); + for (int j = 1; j <= dividend; j++) { + if (dividend % j == 0) { + divisors.add(j); + } + } + return divisors.get(rand.nextInt(divisors.size())); + } + + /** + * MODIFICATION: 增加答案非负、括号数量和操作数关联的逻辑 + */ + public static Problem generatePrimaryExpr() { + Problem problem; + double result; + + do { + // 生成 2 到 4 个操作数 + int operands = rand.nextInt(3) + 2; + List parts = new ArrayList<>(); + + for (int i = 0; i < operands; i++) { + if (i > 0) { + parts.add(OPS_PRIMARY[rand.nextInt(OPS_PRIMARY.length)]); + } + + // 仅当操作数 >= 3 时,才有可能生成括号 + if (operands >= 3 && rand.nextBoolean() && i < operands - 1) { + int num1 = rand.nextInt(50) + 1; + String innerOp = OPS_PRIMARY[rand.nextInt(OPS_PRIMARY.length)]; + int num2; + if (innerOp.equals("/")) { + num2 = getDivisor(num1); + } else { + num2 = rand.nextInt(50) + 1; + } + parts.add("(" + num1 + " " + innerOp + " " + num2 + ")"); + i++; // 括号表达式计为2个操作数 + } else { + int num; + if (i > 0 && parts.get(parts.size() - 1).equals("/")) { + String dividendStr = parts.get(parts.size() - 2); + if (dividendStr.startsWith("(")) { + // 为避免 (a+b)/c 这种复杂情况无法保证整数结果,直接替换运算符 + parts.set(parts.size() - 1, OPS_PRIMARY[rand.nextInt(3)]); // +, -, * + num = rand.nextInt(100) + 1; + } else { + int dividend = Integer.parseInt(dividendStr); + num = getDivisor(dividend); + } + } else { + num = rand.nextInt(100) + 1; + } + parts.add(String.valueOf(num)); + } + } + String expression = String.join(" ", parts); + problem = createProblem(expression); + result = problem.getResult(); + // 循环直到答案为非负整数 + } while (result < 0 || Double.isNaN(result) || Math.abs(result - Math.round(result)) > 0.0001); + + return problem; + } + + + public static Problem generateMiddleExpr() { + int operands = rand.nextInt(5) + 1; + StringBuilder expr = new StringBuilder(); + boolean hasSquareOrSqrt = false; + + for (int i = 0; i < operands; i++) { + if (i > 0) { + expr.append(" ").append(OPS_MIDDLE[rand.nextInt(OPS_MIDDLE.length)]).append(" "); + } + int num = rand.nextInt(100) + 1; + if (!hasSquareOrSqrt && rand.nextBoolean()) { + expr.append(rand.nextBoolean() ? "sqrt(" + num + ")" : num + "^2"); + hasSquareOrSqrt = true; + } else { + expr.append(num); + } + } + + if (!hasSquareOrSqrt) { + expr.append(" + ").append(rand.nextInt(50) + 1).append("^2"); + } + + return createProblem(expr.toString()); + } + + public static Problem generateHighExpr() { + int operands = rand.nextInt(5) + 1; + StringBuilder expr = new StringBuilder(); + boolean hasTrig = false; + + for (int i = 0; i < operands; i++) { + if (i > 0) { + expr.append(" ").append(OPS_HIGH[rand.nextInt(OPS_HIGH.length)]).append(" "); + } + int num = rand.nextInt(100) + 1; + if (!hasTrig && rand.nextBoolean()) { + String[] funcs = {"sin", "cos", "tan"}; + expr.append(funcs[rand.nextInt(funcs.length)]).append("(") + .append(rand.nextInt(90) + 1).append(")"); + hasTrig = true; + } else { + expr.append(num); + } + } + + if (!hasTrig) { + expr.append(" + sin(").append(rand.nextInt(90) + 1).append(")"); + } + + return createProblem(expr.toString()); + } +} \ No newline at end of file diff --git a/src/util/FileUtils.java b/src/util/FileUtils.java new file mode 100644 index 0000000..ce0c7f7 --- /dev/null +++ b/src/util/FileUtils.java @@ -0,0 +1,79 @@ +package util; + +import generator.Problem; +import java.io.*; +import java.text.SimpleDateFormat; +import java.util.*; + +public class FileUtils { + private static final String FOLDER_PATH = "exams/"; + + public static String getUserFolder(String username) { + String folder = FOLDER_PATH + username; + File dir = new File(folder); + if (!dir.exists()) { + dir.mkdirs(); + } + return folder; + } + + private static void processHistoryFile(File file, Set history) { + try (BufferedReader br = new BufferedReader(new FileReader(file))) { + String line; + while ((line = br.readLine()) != null) { + if (!line.trim().isEmpty() && line.matches("^\\d+\\..*")) { + // 仅提取题干 (表达式字符串) 用于查重 + history.add(line.substring(line.indexOf('.') + 1).trim()); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static Set loadHistory(String username) { + Set history = new HashSet<>(); + File dir = new File(getUserFolder(username)); + File[] files = dir.listFiles((d, name) -> name.endsWith(".txt")); + + if (files != null) { + for (File file : files) { + processHistoryFile(file, history); + } + } + return history; + } + + private static void writeProblem(BufferedWriter bw, int index, Problem problem) throws IOException { + bw.write(index + "." + problem.getExpression()); + bw.newLine(); + + char optionChar = 'A'; + StringBuilder optionsLine = new StringBuilder(); + for (String option : problem.getOptions()) { + optionsLine.append(optionChar++).append(". ").append(option).append(" "); + } + + bw.write(optionsLine.toString().trim()); + bw.newLine(); + bw.write("答案: " + problem.getCorrectAnswerOption()); + bw.newLine(); + bw.newLine(); + } + + public static void saveProblems(String username, List problems) { + String folder = getUserFolder(username); + String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); + String filename = folder + "/" + timestamp + ".txt"; + + try (BufferedWriter bw = new BufferedWriter(new FileWriter(filename))) { + int index = 1; + for (Problem problem : problems) { + writeProblem(bw, index++, problem); + } + System.out.println("卷子已保存到: " + filename); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/util/ModifyUtils.java b/src/util/ModifyUtils.java new file mode 100644 index 0000000..ab02695 --- /dev/null +++ b/src/util/ModifyUtils.java @@ -0,0 +1,76 @@ +package util; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class to modify specific fields in user.txt. + * 替换了 ModifyClass.java 和 ModifyPassword.java。 + */ +public class ModifyUtils { + + private static List readAllLines(String filePath, List lines) throws IOException { + try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { + String line; + while ((line = reader.readLine()) != null) { + lines.add(line); + } + } + return lines; + } + + private static void writeAllLines(String filePath, List lines) throws IOException { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) { + for (String l : lines) { + writer.write(l + System.lineSeparator()); + } + } + } + + private static int findTargetLine(List lines, String targetName) { + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i).trim(); + if (line.isEmpty()) continue; + + String[] parts = line.split("\\s+"); + if (parts.length > 0 && parts[0].equals(targetName)) { + return i; // 0-based index + } + } + return -1; + } + + // 核心逻辑:修改文件中特定用户的某个字段 (0-based) + public static int modifyFileField(String filePath, String targetName, + int fieldIndex, String newValue) { + List lines = new ArrayList<>(); + try { + readAllLines(filePath, lines); + } catch (IOException e) { + System.err.println("读取文件错误: " + e.getMessage()); + return -1; + } + + int idx = findTargetLine(lines, targetName); + if (idx == -1) { + return -1; + } + + String[] parts = lines.get(idx).trim().split("\\s+"); + if (parts.length > fieldIndex) { + parts[fieldIndex] = newValue; + lines.set(idx, String.join(" ", parts)); + try { + writeAllLines(filePath, lines); + return idx + 1; // 返回 1-based 行号 + } catch (IOException e) { + System.err.println("写入文件错误: " + e.getMessage()); + return -1; + } + } else { + System.out.println("目标行元素不足。"); + return -1; + } + } +} \ No newline at end of file -- 2.34.1 From fa1bff21c2d65a7bcfffd756eece160650ab7bb8 Mon Sep 17 00:00:00 2001 From: "@yu-xiuhui" <2810672597@qq.com> Date: Sun, 12 Oct 2025 12:09:08 +0800 Subject: [PATCH 03/10] 3 --- ...学习软件 - 结对编程项目介绍.md | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 doc/小初高数学学习软件 - 结对编程项目介绍.md diff --git a/doc/小初高数学学习软件 - 结对编程项目介绍.md b/doc/小初高数学学习软件 - 结对编程项目介绍.md new file mode 100644 index 0000000..ca8b24b --- /dev/null +++ b/doc/小初高数学学习软件 - 结对编程项目介绍.md @@ -0,0 +1,208 @@ +# 小初高数学学习软件 - 结对编程项目介绍 + + + + + +## 一、系统概述 + + + +本系统是一个基于 Java 和 JavaFX 技术实现的桌面应用程序,旨在为小学、初中及高中学生提供一个交互式的数学学习与练习平台。系统完全通过图形化界面(UI)进行操作,涵盖了从用户注册、登录、密码管理到生成个性化试卷、在线答题和自动评分的全过程。项目严格遵循无数据库要求,所有用户数据和生成的试卷均通过本地文件进行存储和管理。 + + + +## 二、项目目录结构 + + + +``` +partner_project/ +├── .gitignore # Git忽略配置文件,用于排除IDE配置和编译产物 +├── pom.xml # Maven配置文件 (如果使用Maven) +├── src/ # 源代码目录 +│ ├── auth/ # 用户认证与管理模块 +│ │ ├── User.java +│ │ └── AuthService.java +│ ├── generator/ # 题目生成模块 +│ │ ├── Problem.java +│ │ ├── ProblemGenerator.java +│ │ ├── PrimaryGenerator.java +│ │ ├── MiddleGenerator.java +│ │ └── HighGenerator.java +│ ├── service/ # 业务逻辑服务模块 +│ │ └── ExamService.java +│ ├── ui/ # JavaFX界面控制器模块 +│ │ ├── MainApplication.java +│ │ ├── LoginController.java +│ │ ├── RegisterController.java +│ │ ├── ModifyPasswordController.java +│ │ └── ExamController.java +│ ├── util/ # 工具类模块 +│ │ ├── EmailService.java +│ │ ├── ExpressionUtils.java +│ │ ├── FileUtils.java +│ │ └── ModifyUtils.java +│ └── Main.java # 主程序入口 +└── resources/ # 资源文件目录 + ├── fxml/ # FXML界面布局文件 + │ ├── Login.fxml + │ ├── register-email.fxml + │ └── ... + ├── images/ # 存放应用的图片资源 + │ ├── logo.png + │ └── background.png + └── styles/ # CSS样式文件 + └── style.css +``` + + + +## 三、核心功能 + + + +1. **用户管理模块** + + - **邮箱注册**: 新用户需通过邮箱接收验证码以完成注册 。 + + - **登录与密码**: 用户使用用户名(或邮箱)和密码登录。密码长度为6-10位,且必须包含大小写字母和数字 2。 + + - **密码修改**: 用户在登录状态下,可通过验证旧密码来设置新密码 。 + + + +2. **试卷生成与答题模块** + + - **难度选择**: 用户登录后可选择“小学”、“初中”、“高中”三种难度级别 。 + + - **个性化试卷**: 用户可指定生成的题目数量 5。系统确保同一张试卷内的题目不会重复,且题目均以选择题形式呈现 6。 + + - **在线答题**: 界面逐一展示题目及其选项,用户选择答案后进入下一题,直至完成所有题目 。 + + + +3. **评分与反馈模块** + + - **自动评分**: 完成答题后,系统会根据用户的答题正确率自动计算并显示最终得分 。 + + - **流程选择**: 在分数界面,用户可以选择继续做题(返回难度选择界面)或退出程序 。 + + + + + +## 四、主要包结构与核心类介绍 + + + + + +### (一) 主要包结构 + + + +- `auth`: 负责用户认证与管理,包括用户信息的读取、验证、注册和修改。 +- `generator`: 负责数学题目的生成逻辑,定义了题目生成的标准接口和针对不同难度的具体实现。 +- `service`: 封装核心业务逻辑,协调 `auth` 和 `generator` 包,提供生成试卷、计算分数等服务。 +- `ui`: 包含所有 JavaFX 的界面控制器,负责处理用户交互和界面跳转。 +- `util`: 提供项目所需的通用工具类,如文件操作、邮件发送和表达式计算等。 +- `resources/fxml`: 存放所有界面的 FXML 布局文件。 + + + +### (二) 核心类介绍 + + + + + +#### 1. 认证与数据模块 (`auth`) + + + +- `User.java`: 用户数据模型,用于封装用户的基本信息,如用户名、密码、学习阶段和邮箱。 +- `AuthService.java`: 用户服务核心类。负责处理所有与用户相关的操作,包括登录验证、新用户注册、检查用户名或邮箱是否被占用、以及更新密码和用户难度等级。 + + + +#### 2. 题目生成模块 (`generator`) + + + +- `Problem.java`: 题目数据模型,封装了一道题目的所有信息,包括题干表达式、四个选项、正确答案和计算结果。 +- `ProblemGenerator.java`: **题目生成器接口**,定义了 `generateProblems(int count)` 的标准方法,确保所有生成器都具有统一的行为。 +- `PrimaryGenerator.java`, `MiddleGenerator.java`, `HighGenerator.java`: `ProblemGenerator` 接口的具体实现类,分别用于生成小学、初中和高中难度的数学题目。 + + + +#### 3. 业务服务模块 (`service`) + + + +- `ExamService.java`: 考试服务类。它是业务逻辑的中心,负责调用 `ProblemGenerator` 生成指定数量且不重复的试卷,并根据用户的答案计算最终得分。 + + + +#### 4. 界面控制模块 (`ui`) + + + +- `MainApplication.java`: JavaFX 应用的入口,负责加载初始界面 (Login.fxml) 并启动窗口。 +- `LoginController.java`: 控制登录、跳转注册和修改密码界面的逻辑。 +- `RegisterController.java`: 控制用户注册的全过程,包括发送验证码、验证、设置用户名和密码等多个步骤。 +- `ExamController.java`: 最核心的界面控制器,负责处理难度选择、试卷生成、题目展示、答案提交、界面跳转以及最终分数展示的所有逻辑。 + + + +#### 5. 工具类模块 (`util`) + + + +- `ExpressionUtils.java`: 表达式工具类,提供了计算字符串表达式结果、生成干扰选项等核心数学计算功能。 +- `FileUtils.java`: 文件操作工具类,封装了对用户历史题目文件的读取和新试卷的保存功能,是实现题目去重的基础。 +- `EmailService.java`: 邮件服务类,负责生成验证码并通过 SMTP 服务器发送邮件给注册用户。 +- `ModifyUtils.java`: 一个通用的文件修改工具,用于根据用户名精确地修改 `user.txt` 文件中特定字段(如密码或难度)。 + + + +## 五、数据存储 + + + +- **用户信息**: 存储在项目根目录下的 `user.txt` 文件中,每行代表一个用户,格式为:`用户名 密码 学习阶段 邮箱`。 +- **用户试卷**: 每个用户生成的试卷和答题历史都保存在 `exams/` 目录下以其用户名命名的子文件夹中,用于实现题目去重功能。 + + + +## 六、题目特点 + + + +- **小学难度**: 包含2到5个操作数的加、减、乘、除运算,并随机引入括号以增加复杂性。确保答案为非负整数。 +- **初中难度**: 在小学基础上,增加了平方(`^2`)和开平方根(`sqrt()`)运算。 +- **高中难度**: 在初中基础上,引入了三角函数 (`sin`, `cos`, `tan`) 运算。 + + + +## 七、使用流程 + + + +1. 启动 `Main.java` 中的 `MainApplication`,应用显示登录界面。 + +2. **首次使用**: 点击“注册账户”按钮,通过邮箱验证流程创建新账户并设置密码 。 + +3. **登录**: 输入用户名/邮箱和密码进行登录。 + +4. **难度与数量选择**: 登录成功后,进入难度选择界面。选择一个学段(小学/初中/高中),并输入希望生成的题目数量(10-30) 。 + +5. **开始答题**: 点击“生成试卷”按钮,系统会加载并显示第一道题。依次回答所有题目 。 + +6. **查看分数**: 完成最后一题后,界面跳转至分数展示页面 。 + +7. **后续操作**: 在分数页面,可选择“继续做题”返回难度选择界面,或选择“退出登录”、“退出程序” 。 + + + + \ No newline at end of file -- 2.34.1 From d8b9d9eeb9ee934ee0a7b4537b208310d02bc627 Mon Sep 17 00:00:00 2001 From: hnu202326010422 <2810672597@qq.com> Date: Sun, 12 Oct 2025 12:11:22 +0800 Subject: [PATCH 04/10] Update README.md --- README.md | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/README.md b/README.md index 8a4459c..89fd859 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,209 @@ # partner_project +# 小初高数学学习软件 - 结对编程项目介绍 + + + + +## 一、系统概述 + + + +本系统是一个基于 Java 和 JavaFX 技术实现的桌面应用程序,旨在为小学、初中及高中学生提供一个交互式的数学学习与练习平台。系统完全通过图形化界面(UI)进行操作,涵盖了从用户注册、登录、密码管理到生成个性化试卷、在线答题和自动评分的全过程。项目严格遵循无数据库要求,所有用户数据和生成的试卷均通过本地文件进行存储和管理。 + + + +## 二、项目目录结构 + + + +``` +partner_project/ +├── .gitignore # Git忽略配置文件,用于排除IDE配置和编译产物 +├── pom.xml # Maven配置文件 (如果使用Maven) +├── src/ # 源代码目录 +│ ├── auth/ # 用户认证与管理模块 +│ │ ├── User.java +│ │ └── AuthService.java +│ ├── generator/ # 题目生成模块 +│ │ ├── Problem.java +│ │ ├── ProblemGenerator.java +│ │ ├── PrimaryGenerator.java +│ │ ├── MiddleGenerator.java +│ │ └── HighGenerator.java +│ ├── service/ # 业务逻辑服务模块 +│ │ └── ExamService.java +│ ├── ui/ # JavaFX界面控制器模块 +│ │ ├── MainApplication.java +│ │ ├── LoginController.java +│ │ ├── RegisterController.java +│ │ ├── ModifyPasswordController.java +│ │ └── ExamController.java +│ ├── util/ # 工具类模块 +│ │ ├── EmailService.java +│ │ ├── ExpressionUtils.java +│ │ ├── FileUtils.java +│ │ └── ModifyUtils.java +│ └── Main.java # 主程序入口 +└── resources/ # 资源文件目录 + ├── fxml/ # FXML界面布局文件 + │ ├── Login.fxml + │ ├── register-email.fxml + │ └── ... + ├── images/ # 存放应用的图片资源 + │ ├── logo.png + │ └── background.png + └── styles/ # CSS样式文件 + └── style.css +``` + + + +## 三、核心功能 + + + +1. **用户管理模块** + + - **邮箱注册**: 新用户需通过邮箱接收验证码以完成注册 。 + + - **登录与密码**: 用户使用用户名(或邮箱)和密码登录。密码长度为6-10位,且必须包含大小写字母和数字 2。 + + - **密码修改**: 用户在登录状态下,可通过验证旧密码来设置新密码 。 + + + +2. **试卷生成与答题模块** + + - **难度选择**: 用户登录后可选择“小学”、“初中”、“高中”三种难度级别 。 + + - **个性化试卷**: 用户可指定生成的题目数量 5。系统确保同一张试卷内的题目不会重复,且题目均以选择题形式呈现 6。 + + - **在线答题**: 界面逐一展示题目及其选项,用户选择答案后进入下一题,直至完成所有题目 。 + + + +3. **评分与反馈模块** + + - **自动评分**: 完成答题后,系统会根据用户的答题正确率自动计算并显示最终得分 。 + + - **流程选择**: 在分数界面,用户可以选择继续做题(返回难度选择界面)或退出程序 。 + + + + + +## 四、主要包结构与核心类介绍 + + + + + +### (一) 主要包结构 + + + +- `auth`: 负责用户认证与管理,包括用户信息的读取、验证、注册和修改。 +- `generator`: 负责数学题目的生成逻辑,定义了题目生成的标准接口和针对不同难度的具体实现。 +- `service`: 封装核心业务逻辑,协调 `auth` 和 `generator` 包,提供生成试卷、计算分数等服务。 +- `ui`: 包含所有 JavaFX 的界面控制器,负责处理用户交互和界面跳转。 +- `util`: 提供项目所需的通用工具类,如文件操作、邮件发送和表达式计算等。 +- `resources/fxml`: 存放所有界面的 FXML 布局文件。 + + + +### (二) 核心类介绍 + + + + + +#### 1. 认证与数据模块 (`auth`) + + + +- `User.java`: 用户数据模型,用于封装用户的基本信息,如用户名、密码、学习阶段和邮箱。 +- `AuthService.java`: 用户服务核心类。负责处理所有与用户相关的操作,包括登录验证、新用户注册、检查用户名或邮箱是否被占用、以及更新密码和用户难度等级。 + + + +#### 2. 题目生成模块 (`generator`) + + + +- `Problem.java`: 题目数据模型,封装了一道题目的所有信息,包括题干表达式、四个选项、正确答案和计算结果。 +- `ProblemGenerator.java`: **题目生成器接口**,定义了 `generateProblems(int count)` 的标准方法,确保所有生成器都具有统一的行为。 +- `PrimaryGenerator.java`, `MiddleGenerator.java`, `HighGenerator.java`: `ProblemGenerator` 接口的具体实现类,分别用于生成小学、初中和高中难度的数学题目。 + + + +#### 3. 业务服务模块 (`service`) + + + +- `ExamService.java`: 考试服务类。它是业务逻辑的中心,负责调用 `ProblemGenerator` 生成指定数量且不重复的试卷,并根据用户的答案计算最终得分。 + + + +#### 4. 界面控制模块 (`ui`) + + + +- `MainApplication.java`: JavaFX 应用的入口,负责加载初始界面 (Login.fxml) 并启动窗口。 +- `LoginController.java`: 控制登录、跳转注册和修改密码界面的逻辑。 +- `RegisterController.java`: 控制用户注册的全过程,包括发送验证码、验证、设置用户名和密码等多个步骤。 +- `ExamController.java`: 最核心的界面控制器,负责处理难度选择、试卷生成、题目展示、答案提交、界面跳转以及最终分数展示的所有逻辑。 + + + +#### 5. 工具类模块 (`util`) + + + +- `ExpressionUtils.java`: 表达式工具类,提供了计算字符串表达式结果、生成干扰选项等核心数学计算功能。 +- `FileUtils.java`: 文件操作工具类,封装了对用户历史题目文件的读取和新试卷的保存功能,是实现题目去重的基础。 +- `EmailService.java`: 邮件服务类,负责生成验证码并通过 SMTP 服务器发送邮件给注册用户。 +- `ModifyUtils.java`: 一个通用的文件修改工具,用于根据用户名精确地修改 `user.txt` 文件中特定字段(如密码或难度)。 + + + +## 五、数据存储 + + + +- **用户信息**: 存储在项目根目录下的 `user.txt` 文件中,每行代表一个用户,格式为:`用户名 密码 学习阶段 邮箱`。 +- **用户试卷**: 每个用户生成的试卷和答题历史都保存在 `exams/` 目录下以其用户名命名的子文件夹中,用于实现题目去重功能。 + + + +## 六、题目特点 + + + +- **小学难度**: 包含2到5个操作数的加、减、乘、除运算,并随机引入括号以增加复杂性。确保答案为非负整数。 +- **初中难度**: 在小学基础上,增加了平方(`^2`)和开平方根(`sqrt()`)运算。 +- **高中难度**: 在初中基础上,引入了三角函数 (`sin`, `cos`, `tan`) 运算。 + + + +## 七、使用流程 + + + +1. 启动 `Main.java` 中的 `MainApplication`,应用显示登录界面。 + +2. **首次使用**: 点击“注册账户”按钮,通过邮箱验证流程创建新账户并设置密码 。 + +3. **登录**: 输入用户名/邮箱和密码进行登录。 + +4. **难度与数量选择**: 登录成功后,进入难度选择界面。选择一个学段(小学/初中/高中),并输入希望生成的题目数量(10-30) 。 + +5. **开始答题**: 点击“生成试卷”按钮,系统会加载并显示第一道题。依次回答所有题目 。 + +6. **查看分数**: 完成最后一题后,界面跳转至分数展示页面 。 + +7. **后续操作**: 在分数页面,可选择“继续做题”返回难度选择界面,或选择“退出登录”、“退出程序” 。 + + + + -- 2.34.1 From 4bd442c211939526d32b692046b05518a3d8674d Mon Sep 17 00:00:00 2001 From: "@yu-xiuhui" <2810672597@qq.com> Date: Sun, 12 Oct 2025 17:21:01 +0800 Subject: [PATCH 05/10] 4 --- src/auth/AuthService.java | 2 +- src/generator/HighGenerator.java | 17 +++++- src/util/ExpressionUtils.java | 96 ++++++++++++++++++++++++-------- 3 files changed, 90 insertions(+), 25 deletions(-) diff --git a/src/auth/AuthService.java b/src/auth/AuthService.java index 3452034..fb1871c 100644 --- a/src/auth/AuthService.java +++ b/src/auth/AuthService.java @@ -103,7 +103,7 @@ public class AuthService { return user; } } - System.out.println("登录失败!"); + return null; } diff --git a/src/generator/HighGenerator.java b/src/generator/HighGenerator.java index 84749e8..905edf1 100644 --- a/src/generator/HighGenerator.java +++ b/src/generator/HighGenerator.java @@ -7,9 +7,22 @@ public class HighGenerator implements ProblemGenerator { @Override public List generateProblems(int count) { List problems = new ArrayList<>(); - for (int i = 0; i < count; i++) { - problems.add(ExpressionUtils.generateHighExpr()); + int attempts = 0; + final int maxAttempts = count * 3; // 最多尝试3倍数量 + + while (problems.size() < count && attempts < maxAttempts) { + Problem problem = ExpressionUtils.generateHighExpr(); + if (problem != null) { + problems.add(problem); + } + attempts++; } + + // 如果无法生成足够题目,用简单题目填充 + while (problems.size() < count) { + problems.add(ExpressionUtils.createProblem("sin(30) + " + (problems.size() + 1))); + } + return problems; } } \ No newline at end of file diff --git a/src/util/ExpressionUtils.java b/src/util/ExpressionUtils.java index 832bfe2..02d59a1 100644 --- a/src/util/ExpressionUtils.java +++ b/src/util/ExpressionUtils.java @@ -9,7 +9,7 @@ public class ExpressionUtils { private static final String[] OPS_PRIMARY = {"+", "-", "*", "/"}; private static final String[] OPS_MIDDLE = {"+", "-", "*", "/"}; - private static final String[] OPS_HIGH = {"+", "-", "*", "/", "^2", "sqrt"}; + //private static final String[] OPS_HIGH = {"+", "-", "*", "/", "^2", "sqrt"}; public static String randomNumber() { @@ -19,8 +19,11 @@ public class ExpressionUtils { // --- 表达式求解和选项生成辅助方法 --- public static double solveExpression(String expr) { - // 将 x^2 替换为 pow(x, 2) 以便解析 - String parsableExpr = expr.replaceAll("(\\d+|\\([^)]+\\))\\^2", "pow($1, 2)"); + // 预处理表达式,确保格式正确 + String parsableExpr = expr + .replaceAll("(\\d+|\\([^)]+\\))\\^2", "pow($1, 2)") // 处理平方 + .replaceAll("sqrt\\s+(\\d+)", "sqrt($1)") // 处理缺少括号的sqrt + .replaceAll("\\s+", " "); // 标准化空格 try { return new Object() { @@ -113,7 +116,7 @@ public class ExpressionUtils { } }.parse(); } catch (Exception e) { - System.err.println("Error parsing expression: '" + expr + "'. Parsable version: '" + parsableExpr + "'. Error: " + e.getMessage()); + System.err.println("Error parsing expression: '" + expr + "'. Parsable version: '" + parsableExpr + "'. Error: " + e.getMessage()); return Double.NaN; } } @@ -176,8 +179,14 @@ public class ExpressionUtils { return options; } - private static Problem createProblem(String expression) { + public static Problem createProblem(String expression) { double result = solveExpression(expression); + + // 如果解析失败,返回null,让调用者重新生成 + if (Double.isNaN(result)) { + return null; + } + String correctAnswerOption = formatResult(result); List options = generateOptions(result); return new Problem(expression, result, options, correctAnswerOption); @@ -278,29 +287,72 @@ public class ExpressionUtils { } public static Problem generateHighExpr() { - int operands = rand.nextInt(5) + 1; - StringBuilder expr = new StringBuilder(); - boolean hasTrig = false; + int attempts = 0; + final int maxAttempts = 10; + + while (attempts < maxAttempts) { + int operands = rand.nextInt(3) + 2; // 2-4个操作数 + StringBuilder expr = new StringBuilder(); + boolean hasTrig = false; - for (int i = 0; i < operands; i++) { - if (i > 0) { - expr.append(" ").append(OPS_HIGH[rand.nextInt(OPS_HIGH.length)]).append(" "); + for (int i = 0; i < operands; i++) { + if (i > 0) { + String[] validOps = {"+", "-", "*", "/"}; + String op = validOps[rand.nextInt(validOps.length)]; + expr.append(" ").append(op).append(" "); + } + + // 强制至少有一个操作数是三角函数 + if (!hasTrig && (i == operands - 1 || rand.nextBoolean())) { + String[] funcs = {"sin", "cos", "tan"}; + String func = funcs[rand.nextInt(funcs.length)]; + int angle = rand.nextInt(90) + 1; // 1-90度 + expr.append(func).append("(").append(angle).append(")"); + hasTrig = true; + } else { + // 其他操作数可以是普通数字、平方或开方 + int num = rand.nextInt(100) + 1; + if (rand.nextBoolean() && hasTrig) { // 确保已经有三角函数后再添加其他函数 + if (rand.nextBoolean()) { + expr.append(num).append("^2"); + } else { + expr.append("sqrt(").append(num).append(")"); + } + } else { + expr.append(num); + } + } } - int num = rand.nextInt(100) + 1; - if (!hasTrig && rand.nextBoolean()) { + + // 如果没有三角函数,强制添加一个 + if (!hasTrig) { String[] funcs = {"sin", "cos", "tan"}; - expr.append(funcs[rand.nextInt(funcs.length)]).append("(") - .append(rand.nextInt(90) + 1).append(")"); + String func = funcs[rand.nextInt(funcs.length)]; + int angle = rand.nextInt(90) + 1; + + if (rand.nextBoolean()) { + // 在开头添加 + expr.insert(0, func + "(" + angle + ") + "); + } else { + // 在结尾添加 + expr.append(" + ").append(func).append("(").append(angle).append(")"); + } hasTrig = true; - } else { - expr.append(num); } - } - if (!hasTrig) { - expr.append(" + sin(").append(rand.nextInt(90) + 1).append(")"); + String expression = expr.toString(); + + // 验证表达式 + Problem problem = createProblem(expression); + if (problem != null) { + return problem; + } + + attempts++; + System.err.println("生成高中题目失败,尝试次数: " + attempts + ", 表达式: " + expression); } - - return createProblem(expr.toString()); + + return createProblem("sin(30) + cos(60)"); } + } \ No newline at end of file -- 2.34.1 From c55c3fe6e9e9b6edb68f4e6f84b9323633e1d940 Mon Sep 17 00:00:00 2001 From: hnu202326010422 <2810672597@qq.com> Date: Sun, 12 Oct 2025 17:56:25 +0800 Subject: [PATCH 06/10] Update README.md --- README.md | 208 ------------------------------------------------------ 1 file changed, 208 deletions(-) diff --git a/README.md b/README.md index 89fd859..cc1693c 100644 --- a/README.md +++ b/README.md @@ -1,209 +1 @@ # partner_project -# 小初高数学学习软件 - 结对编程项目介绍 - - - - - -## 一、系统概述 - - - -本系统是一个基于 Java 和 JavaFX 技术实现的桌面应用程序,旨在为小学、初中及高中学生提供一个交互式的数学学习与练习平台。系统完全通过图形化界面(UI)进行操作,涵盖了从用户注册、登录、密码管理到生成个性化试卷、在线答题和自动评分的全过程。项目严格遵循无数据库要求,所有用户数据和生成的试卷均通过本地文件进行存储和管理。 - - - -## 二、项目目录结构 - - - -``` -partner_project/ -├── .gitignore # Git忽略配置文件,用于排除IDE配置和编译产物 -├── pom.xml # Maven配置文件 (如果使用Maven) -├── src/ # 源代码目录 -│ ├── auth/ # 用户认证与管理模块 -│ │ ├── User.java -│ │ └── AuthService.java -│ ├── generator/ # 题目生成模块 -│ │ ├── Problem.java -│ │ ├── ProblemGenerator.java -│ │ ├── PrimaryGenerator.java -│ │ ├── MiddleGenerator.java -│ │ └── HighGenerator.java -│ ├── service/ # 业务逻辑服务模块 -│ │ └── ExamService.java -│ ├── ui/ # JavaFX界面控制器模块 -│ │ ├── MainApplication.java -│ │ ├── LoginController.java -│ │ ├── RegisterController.java -│ │ ├── ModifyPasswordController.java -│ │ └── ExamController.java -│ ├── util/ # 工具类模块 -│ │ ├── EmailService.java -│ │ ├── ExpressionUtils.java -│ │ ├── FileUtils.java -│ │ └── ModifyUtils.java -│ └── Main.java # 主程序入口 -└── resources/ # 资源文件目录 - ├── fxml/ # FXML界面布局文件 - │ ├── Login.fxml - │ ├── register-email.fxml - │ └── ... - ├── images/ # 存放应用的图片资源 - │ ├── logo.png - │ └── background.png - └── styles/ # CSS样式文件 - └── style.css -``` - - - -## 三、核心功能 - - - -1. **用户管理模块** - - - **邮箱注册**: 新用户需通过邮箱接收验证码以完成注册 。 - - - **登录与密码**: 用户使用用户名(或邮箱)和密码登录。密码长度为6-10位,且必须包含大小写字母和数字 2。 - - - **密码修改**: 用户在登录状态下,可通过验证旧密码来设置新密码 。 - - - -2. **试卷生成与答题模块** - - - **难度选择**: 用户登录后可选择“小学”、“初中”、“高中”三种难度级别 。 - - - **个性化试卷**: 用户可指定生成的题目数量 5。系统确保同一张试卷内的题目不会重复,且题目均以选择题形式呈现 6。 - - - **在线答题**: 界面逐一展示题目及其选项,用户选择答案后进入下一题,直至完成所有题目 。 - - - -3. **评分与反馈模块** - - - **自动评分**: 完成答题后,系统会根据用户的答题正确率自动计算并显示最终得分 。 - - - **流程选择**: 在分数界面,用户可以选择继续做题(返回难度选择界面)或退出程序 。 - - - - - -## 四、主要包结构与核心类介绍 - - - - - -### (一) 主要包结构 - - - -- `auth`: 负责用户认证与管理,包括用户信息的读取、验证、注册和修改。 -- `generator`: 负责数学题目的生成逻辑,定义了题目生成的标准接口和针对不同难度的具体实现。 -- `service`: 封装核心业务逻辑,协调 `auth` 和 `generator` 包,提供生成试卷、计算分数等服务。 -- `ui`: 包含所有 JavaFX 的界面控制器,负责处理用户交互和界面跳转。 -- `util`: 提供项目所需的通用工具类,如文件操作、邮件发送和表达式计算等。 -- `resources/fxml`: 存放所有界面的 FXML 布局文件。 - - - -### (二) 核心类介绍 - - - - - -#### 1. 认证与数据模块 (`auth`) - - - -- `User.java`: 用户数据模型,用于封装用户的基本信息,如用户名、密码、学习阶段和邮箱。 -- `AuthService.java`: 用户服务核心类。负责处理所有与用户相关的操作,包括登录验证、新用户注册、检查用户名或邮箱是否被占用、以及更新密码和用户难度等级。 - - - -#### 2. 题目生成模块 (`generator`) - - - -- `Problem.java`: 题目数据模型,封装了一道题目的所有信息,包括题干表达式、四个选项、正确答案和计算结果。 -- `ProblemGenerator.java`: **题目生成器接口**,定义了 `generateProblems(int count)` 的标准方法,确保所有生成器都具有统一的行为。 -- `PrimaryGenerator.java`, `MiddleGenerator.java`, `HighGenerator.java`: `ProblemGenerator` 接口的具体实现类,分别用于生成小学、初中和高中难度的数学题目。 - - - -#### 3. 业务服务模块 (`service`) - - - -- `ExamService.java`: 考试服务类。它是业务逻辑的中心,负责调用 `ProblemGenerator` 生成指定数量且不重复的试卷,并根据用户的答案计算最终得分。 - - - -#### 4. 界面控制模块 (`ui`) - - - -- `MainApplication.java`: JavaFX 应用的入口,负责加载初始界面 (Login.fxml) 并启动窗口。 -- `LoginController.java`: 控制登录、跳转注册和修改密码界面的逻辑。 -- `RegisterController.java`: 控制用户注册的全过程,包括发送验证码、验证、设置用户名和密码等多个步骤。 -- `ExamController.java`: 最核心的界面控制器,负责处理难度选择、试卷生成、题目展示、答案提交、界面跳转以及最终分数展示的所有逻辑。 - - - -#### 5. 工具类模块 (`util`) - - - -- `ExpressionUtils.java`: 表达式工具类,提供了计算字符串表达式结果、生成干扰选项等核心数学计算功能。 -- `FileUtils.java`: 文件操作工具类,封装了对用户历史题目文件的读取和新试卷的保存功能,是实现题目去重的基础。 -- `EmailService.java`: 邮件服务类,负责生成验证码并通过 SMTP 服务器发送邮件给注册用户。 -- `ModifyUtils.java`: 一个通用的文件修改工具,用于根据用户名精确地修改 `user.txt` 文件中特定字段(如密码或难度)。 - - - -## 五、数据存储 - - - -- **用户信息**: 存储在项目根目录下的 `user.txt` 文件中,每行代表一个用户,格式为:`用户名 密码 学习阶段 邮箱`。 -- **用户试卷**: 每个用户生成的试卷和答题历史都保存在 `exams/` 目录下以其用户名命名的子文件夹中,用于实现题目去重功能。 - - - -## 六、题目特点 - - - -- **小学难度**: 包含2到5个操作数的加、减、乘、除运算,并随机引入括号以增加复杂性。确保答案为非负整数。 -- **初中难度**: 在小学基础上,增加了平方(`^2`)和开平方根(`sqrt()`)运算。 -- **高中难度**: 在初中基础上,引入了三角函数 (`sin`, `cos`, `tan`) 运算。 - - - -## 七、使用流程 - - - -1. 启动 `Main.java` 中的 `MainApplication`,应用显示登录界面。 - -2. **首次使用**: 点击“注册账户”按钮,通过邮箱验证流程创建新账户并设置密码 。 - -3. **登录**: 输入用户名/邮箱和密码进行登录。 - -4. **难度与数量选择**: 登录成功后,进入难度选择界面。选择一个学段(小学/初中/高中),并输入希望生成的题目数量(10-30) 。 - -5. **开始答题**: 点击“生成试卷”按钮,系统会加载并显示第一道题。依次回答所有题目 。 - -6. **查看分数**: 完成最后一题后,界面跳转至分数展示页面 。 - -7. **后续操作**: 在分数页面,可选择“继续做题”返回难度选择界面,或选择“退出登录”、“退出程序” 。 - - - - -- 2.34.1 From 2186717f9ca3853c385b4ae0bb16b31d33b1c730 Mon Sep 17 00:00:00 2001 From: hnu202326010422 <2810672597@qq.com> Date: Sun, 12 Oct 2025 19:03:35 +0800 Subject: [PATCH 07/10] Delete 'README.md' --- README.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index cc1693c..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# partner_project -- 2.34.1 From abf9198a5b013a8f2296769bc895b728ceda8432 Mon Sep 17 00:00:00 2001 From: hnu202326010422 <2810672597@qq.com> Date: Sun, 12 Oct 2025 19:03:42 +0800 Subject: [PATCH 08/10] Delete 'src/Main.java' --- src/Main.java | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 src/Main.java diff --git a/src/Main.java b/src/Main.java deleted file mode 100644 index c58f279..0000000 --- a/src/Main.java +++ /dev/null @@ -1,8 +0,0 @@ -import ui.MainApplication; - -public class Main { - public static void main(String[] args) { - // R1: 启动图形化界面应用 (JavaFX) - MainApplication.launch(MainApplication.class, args); - } -} \ No newline at end of file -- 2.34.1 From 5370a284a11a152b07cb8bda9ad3fca0a12b6532 Mon Sep 17 00:00:00 2001 From: hnu202326010422 <2810672597@qq.com> Date: Sun, 12 Oct 2025 19:03:50 +0800 Subject: [PATCH 09/10] =?UTF-8?q?Delete=20'doc/=E5=B0=8F=E5=88=9D=E9=AB=98?= =?UTF-8?q?=E6=95=B0=E5=AD=A6=E5=AD=A6=E4=B9=A0=E8=BD=AF=E4=BB=B6=20-=20?= =?UTF-8?q?=E7=BB=93=E5=AF=B9=E7=BC=96=E7=A8=8B=E9=A1=B9=E7=9B=AE=E4=BB=8B?= =?UTF-8?q?=E7=BB=8D.md'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...学习软件 - 结对编程项目介绍.md | 208 ------------------ 1 file changed, 208 deletions(-) delete mode 100644 doc/小初高数学学习软件 - 结对编程项目介绍.md diff --git a/doc/小初高数学学习软件 - 结对编程项目介绍.md b/doc/小初高数学学习软件 - 结对编程项目介绍.md deleted file mode 100644 index ca8b24b..0000000 --- a/doc/小初高数学学习软件 - 结对编程项目介绍.md +++ /dev/null @@ -1,208 +0,0 @@ -# 小初高数学学习软件 - 结对编程项目介绍 - - - - - -## 一、系统概述 - - - -本系统是一个基于 Java 和 JavaFX 技术实现的桌面应用程序,旨在为小学、初中及高中学生提供一个交互式的数学学习与练习平台。系统完全通过图形化界面(UI)进行操作,涵盖了从用户注册、登录、密码管理到生成个性化试卷、在线答题和自动评分的全过程。项目严格遵循无数据库要求,所有用户数据和生成的试卷均通过本地文件进行存储和管理。 - - - -## 二、项目目录结构 - - - -``` -partner_project/ -├── .gitignore # Git忽略配置文件,用于排除IDE配置和编译产物 -├── pom.xml # Maven配置文件 (如果使用Maven) -├── src/ # 源代码目录 -│ ├── auth/ # 用户认证与管理模块 -│ │ ├── User.java -│ │ └── AuthService.java -│ ├── generator/ # 题目生成模块 -│ │ ├── Problem.java -│ │ ├── ProblemGenerator.java -│ │ ├── PrimaryGenerator.java -│ │ ├── MiddleGenerator.java -│ │ └── HighGenerator.java -│ ├── service/ # 业务逻辑服务模块 -│ │ └── ExamService.java -│ ├── ui/ # JavaFX界面控制器模块 -│ │ ├── MainApplication.java -│ │ ├── LoginController.java -│ │ ├── RegisterController.java -│ │ ├── ModifyPasswordController.java -│ │ └── ExamController.java -│ ├── util/ # 工具类模块 -│ │ ├── EmailService.java -│ │ ├── ExpressionUtils.java -│ │ ├── FileUtils.java -│ │ └── ModifyUtils.java -│ └── Main.java # 主程序入口 -└── resources/ # 资源文件目录 - ├── fxml/ # FXML界面布局文件 - │ ├── Login.fxml - │ ├── register-email.fxml - │ └── ... - ├── images/ # 存放应用的图片资源 - │ ├── logo.png - │ └── background.png - └── styles/ # CSS样式文件 - └── style.css -``` - - - -## 三、核心功能 - - - -1. **用户管理模块** - - - **邮箱注册**: 新用户需通过邮箱接收验证码以完成注册 。 - - - **登录与密码**: 用户使用用户名(或邮箱)和密码登录。密码长度为6-10位,且必须包含大小写字母和数字 2。 - - - **密码修改**: 用户在登录状态下,可通过验证旧密码来设置新密码 。 - - - -2. **试卷生成与答题模块** - - - **难度选择**: 用户登录后可选择“小学”、“初中”、“高中”三种难度级别 。 - - - **个性化试卷**: 用户可指定生成的题目数量 5。系统确保同一张试卷内的题目不会重复,且题目均以选择题形式呈现 6。 - - - **在线答题**: 界面逐一展示题目及其选项,用户选择答案后进入下一题,直至完成所有题目 。 - - - -3. **评分与反馈模块** - - - **自动评分**: 完成答题后,系统会根据用户的答题正确率自动计算并显示最终得分 。 - - - **流程选择**: 在分数界面,用户可以选择继续做题(返回难度选择界面)或退出程序 。 - - - - - -## 四、主要包结构与核心类介绍 - - - - - -### (一) 主要包结构 - - - -- `auth`: 负责用户认证与管理,包括用户信息的读取、验证、注册和修改。 -- `generator`: 负责数学题目的生成逻辑,定义了题目生成的标准接口和针对不同难度的具体实现。 -- `service`: 封装核心业务逻辑,协调 `auth` 和 `generator` 包,提供生成试卷、计算分数等服务。 -- `ui`: 包含所有 JavaFX 的界面控制器,负责处理用户交互和界面跳转。 -- `util`: 提供项目所需的通用工具类,如文件操作、邮件发送和表达式计算等。 -- `resources/fxml`: 存放所有界面的 FXML 布局文件。 - - - -### (二) 核心类介绍 - - - - - -#### 1. 认证与数据模块 (`auth`) - - - -- `User.java`: 用户数据模型,用于封装用户的基本信息,如用户名、密码、学习阶段和邮箱。 -- `AuthService.java`: 用户服务核心类。负责处理所有与用户相关的操作,包括登录验证、新用户注册、检查用户名或邮箱是否被占用、以及更新密码和用户难度等级。 - - - -#### 2. 题目生成模块 (`generator`) - - - -- `Problem.java`: 题目数据模型,封装了一道题目的所有信息,包括题干表达式、四个选项、正确答案和计算结果。 -- `ProblemGenerator.java`: **题目生成器接口**,定义了 `generateProblems(int count)` 的标准方法,确保所有生成器都具有统一的行为。 -- `PrimaryGenerator.java`, `MiddleGenerator.java`, `HighGenerator.java`: `ProblemGenerator` 接口的具体实现类,分别用于生成小学、初中和高中难度的数学题目。 - - - -#### 3. 业务服务模块 (`service`) - - - -- `ExamService.java`: 考试服务类。它是业务逻辑的中心,负责调用 `ProblemGenerator` 生成指定数量且不重复的试卷,并根据用户的答案计算最终得分。 - - - -#### 4. 界面控制模块 (`ui`) - - - -- `MainApplication.java`: JavaFX 应用的入口,负责加载初始界面 (Login.fxml) 并启动窗口。 -- `LoginController.java`: 控制登录、跳转注册和修改密码界面的逻辑。 -- `RegisterController.java`: 控制用户注册的全过程,包括发送验证码、验证、设置用户名和密码等多个步骤。 -- `ExamController.java`: 最核心的界面控制器,负责处理难度选择、试卷生成、题目展示、答案提交、界面跳转以及最终分数展示的所有逻辑。 - - - -#### 5. 工具类模块 (`util`) - - - -- `ExpressionUtils.java`: 表达式工具类,提供了计算字符串表达式结果、生成干扰选项等核心数学计算功能。 -- `FileUtils.java`: 文件操作工具类,封装了对用户历史题目文件的读取和新试卷的保存功能,是实现题目去重的基础。 -- `EmailService.java`: 邮件服务类,负责生成验证码并通过 SMTP 服务器发送邮件给注册用户。 -- `ModifyUtils.java`: 一个通用的文件修改工具,用于根据用户名精确地修改 `user.txt` 文件中特定字段(如密码或难度)。 - - - -## 五、数据存储 - - - -- **用户信息**: 存储在项目根目录下的 `user.txt` 文件中,每行代表一个用户,格式为:`用户名 密码 学习阶段 邮箱`。 -- **用户试卷**: 每个用户生成的试卷和答题历史都保存在 `exams/` 目录下以其用户名命名的子文件夹中,用于实现题目去重功能。 - - - -## 六、题目特点 - - - -- **小学难度**: 包含2到5个操作数的加、减、乘、除运算,并随机引入括号以增加复杂性。确保答案为非负整数。 -- **初中难度**: 在小学基础上,增加了平方(`^2`)和开平方根(`sqrt()`)运算。 -- **高中难度**: 在初中基础上,引入了三角函数 (`sin`, `cos`, `tan`) 运算。 - - - -## 七、使用流程 - - - -1. 启动 `Main.java` 中的 `MainApplication`,应用显示登录界面。 - -2. **首次使用**: 点击“注册账户”按钮,通过邮箱验证流程创建新账户并设置密码 。 - -3. **登录**: 输入用户名/邮箱和密码进行登录。 - -4. **难度与数量选择**: 登录成功后,进入难度选择界面。选择一个学段(小学/初中/高中),并输入希望生成的题目数量(10-30) 。 - -5. **开始答题**: 点击“生成试卷”按钮,系统会加载并显示第一道题。依次回答所有题目 。 - -6. **查看分数**: 完成最后一题后,界面跳转至分数展示页面 。 - -7. **后续操作**: 在分数页面,可选择“继续做题”返回难度选择界面,或选择“退出登录”、“退出程序” 。 - - - - \ No newline at end of file -- 2.34.1 From 2af1b263df1fee6d590c75c44258b33c0d3eb79b Mon Sep 17 00:00:00 2001 From: hnu202326010422 <2810672597@qq.com> Date: Sun, 12 Oct 2025 19:03:56 +0800 Subject: [PATCH 10/10] Delete 'src/auth/AuthService.java' --- src/auth/AuthService.java | 206 -------------------------------------- 1 file changed, 206 deletions(-) delete mode 100644 src/auth/AuthService.java diff --git a/src/auth/AuthService.java b/src/auth/AuthService.java deleted file mode 100644 index fb1871c..0000000 --- a/src/auth/AuthService.java +++ /dev/null @@ -1,206 +0,0 @@ -package auth; - -import util.ModifyUtils; -import util.EmailService; -import java.io.*; -import java.util.*; -import java.util.regex.Pattern; - -public class AuthService { - private static final String USER_FILE_PATH = "user.txt"; - private final EmailService emailService = new EmailService(); - - - - - public AuthService() { - // 检查用户文件是否存在 - File userFile = new File(USER_FILE_PATH); - if (!userFile.exists()) { - System.err.println("用户文件不存在: " + USER_FILE_PATH); - try { - // 尝试创建文件 - if (userFile.createNewFile()) { - System.out.println("已创建用户文件: " + USER_FILE_PATH); - } - } catch (IOException e) { - System.err.println("创建用户文件失败: " + e.getMessage()); - } - } - - } - - - - // --- 校验和辅助方法 --- - - private static final Pattern EMAIL_PATTERN = - Pattern.compile("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}$"); - - // R3: 密码校验: 6-10 位, 必须含大小写字母和数字 - public static boolean isPasswordValid(String password) { - if (password == null || password.length() < 6 || - password.length() > 10) { - return false; - } - Pattern pattern = - Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{6,10}$"); - return pattern.matcher(password).matches(); - } - - public boolean validateEmail(String email) { - if (email == null){ - return false; - } - return EMAIL_PATTERN.matcher(email).matches(); - } - - // --- 用户加载和保存 --- - - private User parseLine(String line, int lineNumber) { - line = line.trim(); - if (line.isEmpty()) { - return null; - } - - String[] parts = line.split("\\s+"); - if (parts.length != 4) { - System.out.println("警告: 第" + lineNumber + - "行数据格式不正确,需要4个数据。"); - return null; - } - return new User(parts[0], parts[1], parts[2], parts[3]); - } - - public List loadUsers() { - List users = new ArrayList<>(); - int lineNumber = 1; - - try (BufferedReader br = new BufferedReader(new FileReader(USER_FILE_PATH))) { - String line; - while ((line = br.readLine()) != null) { - User user = parseLine(line, lineNumber); - if (user != null) { - users.add(user); - } - lineNumber++; - } - } catch (IOException e) { - System.out.println("读取文件时发生错误: " + e.getMessage()); - } - - return users; - } - - // --- 认证和注册 --- - - public User login(String username, String password) { - List users = loadUsers(); - - for (User user : users) { - if ((user.getUsername().equals(username) || user.getEmail().equals(username)) && - user.getPassword().equals(password)) { - return user; - } - } - - return null; - } - - public boolean isUsernameOrEmailTaken(String username, String email) { - List users = loadUsers(); - for (User user : users) { - if ((username != null && user.getUsername().equals(username)) || - (email != null && user.getEmail().equals(email))) { - return true; - } - } - return false; - } - - // R2: 实际发送验证邮件 - public String sendVerificationCode(String email) { - if (!validateEmail(email) || isUsernameOrEmailTaken(null, email)) { - return null; - } - String code = emailService.generateVerificationCode(); - if (emailService.sendVerificationEmail(email, code)) { - return code; - } - return null; - } - - private void writeUserData(String userData) throws IOException { - try (BufferedWriter writer = - new BufferedWriter(new FileWriter(USER_FILE_PATH, true))) { - writer.write(userData); - writer.newLine(); - } - } - - // R2: 注册用户 - public boolean registerUser(String username, String password, String email) { - if (isUsernameOrEmailTaken(username, email)) { - return false; - } - - // 格式: username password type email - String userData = username + " " + password + " 小学 " + email; - try { - writeUserData(userData); - return true; - } catch (IOException e) { - System.out.println("写入文件时发生错误: " + e.getMessage()); - return false; - } - } - - // --- 用户修改 --- - - public boolean updatePassword(String username, String newPassword) { - // 索引 1 是密码 - int result = ModifyUtils.modifyFileField(USER_FILE_PATH, username, 1, newPassword); - return result != -1; - } - - public boolean updateDifficulty(String username, String newDifficulty) { - List lines = new ArrayList<>(); - boolean found = false; - - try (BufferedReader br = new BufferedReader(new FileReader(USER_FILE_PATH))) { - String line; - while ((line = br.readLine()) != null) { - String[] parts = line.trim().split("\\s+"); - - if (parts.length >= 4 && parts[0].equals(username)) { - // 找到目标用户,更新难度 - parts[2] = newDifficulty; - line = String.join(" ", parts); - found = true; - } - lines.add(line); - } - } catch (IOException e) { - System.err.println("读取用户文件错误: " + e.getMessage()); - return false; - } - - if (!found) { - System.err.println("未找到用户: " + username); - return false; - } - - // 写回文件 - try (BufferedWriter bw = new BufferedWriter(new FileWriter(USER_FILE_PATH))) { - for (String line : lines) { - bw.write(line); - bw.newLine(); - } - return true; - } catch (IOException e) { - System.err.println("写入用户文件错误: " + e.getMessage()); - return false; - } - } - -} \ No newline at end of file -- 2.34.1