后端代码解耦且格式设置完毕 #8

Merged
ppy4sjqvf merged 5 commits from ybw_branch into develop 4 months ago

@ -1,37 +0,0 @@
package com.ybw.mathapp;
import com.ybw.mathapp.entity.QuestionWithOptions;
import com.ybw.mathapp.service.JuniorHighGenerator;
import com.ybw.mathapp.service.MultipleChoiceGenerator;
import com.ybw.mathapp.service.PrimarySchoolGenerator;
import com.ybw.mathapp.service.SeniorHighGenerator;
import java.util.List;
public class Main {
public static void main(String[] args) {
// 生成小学选择题
System.out.println("--- Primary School MCQs ---");
PrimarySchoolGenerator primaryGen = new PrimarySchoolGenerator();
MultipleChoiceGenerator primaryMC = new MultipleChoiceGenerator(primaryGen, "小学"); // 传入级别
List<QuestionWithOptions> primaryMCQs = primaryMC.generateMultipleChoiceQuestions(10);
primaryMCQs.forEach(q -> System.out.println(q + "\n"));
// 生成初中选择题
System.out.println("--- Junior High MCQs ---");
JuniorHighGenerator juniorGen = new JuniorHighGenerator();
MultipleChoiceGenerator juniorMC = new MultipleChoiceGenerator(juniorGen, "初中"); // 传入级别
List<QuestionWithOptions> juniorMCQs = juniorMC.generateMultipleChoiceQuestions(10);
juniorMCQs.forEach(q -> System.out.println(q + "\n"));
System.out.println();
// 生成高中选择题
System.out.println("--- Senior High MCQs ---");
SeniorHighGenerator seniorGen = new SeniorHighGenerator();
MultipleChoiceGenerator seniorMC = new MultipleChoiceGenerator(seniorGen, "高中"); // 传入级别
List<QuestionWithOptions> seniorMCQs = seniorMC.generateMultipleChoiceQuestions(10);
seniorMCQs.forEach(q -> System.out.println(q + "\n"));
}
}

@ -1,18 +1,13 @@
package com.ybw.mathapp.config;
public class EmailConfig {
// 发件人邮箱配置以QQ邮箱为例
public static final String SMTP_HOST = "smtp.qq.com";
public static final String SMTP_PORT = "587";
public static final String SENDER_EMAIL = "1798231811@qq.com"; // 替换为你的邮箱
public static final String SENDER_PASSWORD = "dzmfirotgnlceeae"; // 替换为你的授权码
// 如果使用Gmail
// public static final String SMTP_HOST = "smtp.gmail.com";
// public static final String SMTP_PORT = "587";
// public static final String SENDER_EMAIL = "your_email@gmail.com";
// public static final String SENDER_PASSWORD = "your_app_password";
public static final String EMAIL_SUBJECT = "【用户注册】验证码";
public static final int CODE_EXPIRY_MINUTES = 5;
}

@ -3,43 +3,105 @@ package com.ybw.mathapp.entity;
import java.util.List;
/**
*
*
*
* @author
* @author
* @since 2025
*/
public class QuestionWithOptions {
private String questionText; // 题干
private List<String> options; // 选项列表
private int correctAnswerIndex; // 正确答案的索引 (0-based)
/**
*
*/
private String questionText;
/**
*
*/
private List<String> options;
/**
* (0-based)
*/
private int correctAnswerIndex;
/**
*
*
* @param questionText
* @param options
* @param correctAnswerIndex (0-based)
* @throws IllegalArgumentException options null
*/
public QuestionWithOptions(String questionText, List<String> options, int correctAnswerIndex) {
if (options == null) {
throw new IllegalArgumentException("选项列表不能为 null");
}
if (correctAnswerIndex < 0 || correctAnswerIndex >= options.size()) {
throw new IllegalArgumentException("正确答案索引超出选项范围");
}
this.questionText = questionText;
this.options = options;
this.correctAnswerIndex = correctAnswerIndex;
}
/**
*
*
* @return
*/
public String getQuestionText() {
return questionText;
}
/**
*
*
* @param questionText
*/
public void setQuestionText(String questionText) {
this.questionText = questionText;
}
/**
*
*
* @return
*/
public List<String> getOptions() {
return options;
}
/**
*
*
* @param options
* @throws IllegalArgumentException options null
*/
public void setOptions(List<String> options) {
if (options == null) {
throw new IllegalArgumentException("选项列表不能为 null");
}
this.options = options;
}
/**
*
*
* @return (0-based)
*/
public int getCorrectAnswerIndex() {
return correctAnswerIndex;
}
/**
*
*
* @param correctAnswerIndex (0-based)
* @throws IllegalArgumentException
*/
public void setCorrectAnswerIndex(int correctAnswerIndex) {
if (correctAnswerIndex < 0 || correctAnswerIndex >= options.size()) {
throw new IllegalArgumentException("正确答案索引超出选项范围");
}
this.correctAnswerIndex = correctAnswerIndex;
}
@ -48,9 +110,10 @@ public class QuestionWithOptions {
StringBuilder sb = new StringBuilder();
sb.append("Question: ").append(questionText).append("\n");
for (int i = 0; i < options.size(); i++) {
sb.append("Option ").append((char)('A' + i)).append(": ").append(options.get(i)).append("\n");
sb.append("Option ").append((char) ('A' + i)).append(": ").append(options.get(i))
.append("\n");
}
sb.append("Correct Answer: ").append((char)('A' + correctAnswerIndex));
sb.append("Correct Answer: ").append((char) ('A' + correctAnswerIndex));
return sb.toString();
}
}
}

@ -12,32 +12,76 @@ package com.ybw.mathapp.entity;
*/
public class User {
public String getName() {
return name;
}
/** 用户名,不可修改。 */
/**
*
*/
private final String name;
/** 邮箱,不可修改。 */
/**
*
*/
private final String email;
/** 用户密码,不可修改。 */
/**
*
*/
private final String password;
/** 用户当前的学习级别,可以修改。 */
/**
*
*/
private String level;
/**
*
*
* @param email
* @param name
* @param email
* @param password
* @throws IllegalArgumentException nameemail password null
*/
public User(String name, String email, String password) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (email == null || email.trim().isEmpty()) {
throw new IllegalArgumentException("邮箱不能为空");
}
if (password == null || password.trim().isEmpty()) {
throw new IllegalArgumentException("密码不能为空");
}
this.name = name;
this.password = password;
this.email = email;
this.password = password;
}
/**
*
*
* <p> "name,email,password"
*
* @param line
* @return null
*/
public static User fromString(String line) {
if (line == null || line.trim().isEmpty()) {
return null;
}
String[] parts = line.split(",", 3); // 最多分割成3部分
if (parts.length == 3) {
return new User(parts[0].trim(), parts[1].trim(), parts[2].trim());
}
return null;
}
/**
*
*
* @return
*/
public String getName() {
return name;
}
/**
@ -59,42 +103,32 @@ public class User {
}
/**
*
*
*
* @return
* @param newLevel "小学""初中""高中"
*/
public String getEmail() {
return email;
public void setLevel(String newLevel) {
level = newLevel;
}
/**
*
*
*
* @param newLevel "小学""初中""高中"
* @return
*/
public void setLevel(String newLevel) {
level = newLevel;
public String getEmail() {
return email;
}
/**
* +
*
*
* <p> "name,email,password"
*
* @return +
* @return
*/
@Override
public String toString() {
return name + "," + email + "," + password;
}
public static User fromString(String line) {
if (line == null || line.trim().isEmpty()) {
return null;
}
String[] parts = line.split(",", 3); // 最多分割成3部分
if (parts.length == 3) {
return new User(parts[0].trim(), parts[1].trim(), parts[2].trim());
}
return null;
}
}
}

@ -1,21 +1,41 @@
package com.ybw.mathapp.service;
import java.util.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* (sin, cos, tan)
*
* (, , sin, cos, tan)
*
* : "3 + 开根号 16 平方" "3 + (sqrt(16))^2" = "3 + 4^2" = "3 + 16" = 19
* "开根号 ( 4 + 5 ) 平方" "(sqrt(4+5))^2" = "sqrt(9)^2" = "3^2" = 9
* "sin 30 + 5" "sin(30) + 5" (30)
* ArithmeticException
*
* <p>
* <ol>
* <li> {@code ( )} </li>
* <li> (, , sin, cos, tan) </li>
* <li></li>
* </ol>
*
* <p>:
* <ul>
* <li>{@code "3 + 开根号 16 平方"} {@code "3 + (sqrt(16))^2"} = {@code "3 + 4^2"} = {@code "3 + 16"} = 19</li>
* <li>{@code "开根号 ( 4 + 5 ) 平方"} {@code "(sqrt(4+5))^2"} = {@code "sqrt(9)^2"} = {@code "3^2"} = 9</li>
* <li>{@code "sin 30 + 5"} {@code "sin(30) + 5"} (30)</li>
* </ul>
*
* <p>: ArithmeticException
*
* @author
* @since 2025
*/
public class AdvancedCaculate {
private static final Set<String> OPERATORS = new HashSet<>(Arrays.asList("+", "-", "*", "/"));
private static final Set<String> ADVANCED_OPERATORS = new HashSet<>(Arrays.asList("平方", "开根号", "sin", "cos", "tan"));
private static final Set<String> BASIC_OPERATORS = new HashSet<>(
Arrays.asList("+", "-", "*", "/"));
private static final Set<String> ADVANCED_OPERATORS = new HashSet<>(
Arrays.asList("平方", "开根号", "sin", "cos", "tan"));
private static final Map<String, Integer> PRECEDENCE = new HashMap<>();
static {
@ -34,51 +54,28 @@ public class AdvancedCaculate {
/**
*
*
* @param expressionTokens ["3", "+", "开根号", "16", "平方"]
* <p>: ["3", "+", "开根号", "16", "平方"]
*
* @param expressionTokens
* @return
* @throws IllegalArgumentException
* @throws ArithmeticException
* @throws IllegalArgumentException
* @throws ArithmeticException
*/
public static double calculate(List<String> expressionTokens) {
Stack<Double> numberStack = new Stack<>();
Stack<String> operatorStack = new Stack<>();
for (int i = 0; i < expressionTokens.size(); i++) {
String token = expressionTokens.get(i);
for (String token : expressionTokens) {
if (isNumeric(token)) {
numberStack.push(Double.parseDouble(token));
} else if (token.equals("(")) {
operatorStack.push(token);
} else if (token.equals(")")) {
while (!operatorStack.isEmpty() && !operatorStack.peek().equals("(")) {
processOperator(numberStack, operatorStack.pop());
}
if (!operatorStack.isEmpty()) { // Pop the "("
operatorStack.pop();
}
handleClosingParenthesis(numberStack, operatorStack);
} else if (ADVANCED_OPERATORS.contains(token)) {
// 前置运算符 (开根号, sin, cos, tan) 直接入栈
// 后置运算符 (平方) 需要等待其操作数先计算出来
// 在标准调度场算法中,后置运算符通常在遇到时立即处理其栈顶的操作数
// 这里我们可以在遇到 "平方" 时,立即对 numberStack 的顶部元素进行平方操作
if ("平方".equals(token)) {
if (numberStack.isEmpty()) {
throw new IllegalArgumentException("Invalid expression: '平方' lacks an operand.");
}
double operand = numberStack.pop();
numberStack.push(Math.pow(operand, 2));
} else { // "开根号", "sin", "cos", "tan" 是前置运算符
operatorStack.push(token);
}
} else if (OPERATORS.contains(token)) {
// 处理四则运算符,遵循优先级
while (!operatorStack.isEmpty() &&
!operatorStack.peek().equals("(") &&
PRECEDENCE.get(token) <= PRECEDENCE.getOrDefault(operatorStack.peek(), 0)) {
processOperator(numberStack, operatorStack.pop());
}
operatorStack.push(token);
handleAdvancedOperator(token, numberStack, operatorStack);
} else if (BASIC_OPERATORS.contains(token)) {
handleBasicOperator(token, numberStack, operatorStack);
} else {
throw new IllegalArgumentException("Unknown token: " + token);
}
@ -94,78 +91,149 @@ public class AdvancedCaculate {
}
if (numberStack.size() != 1 || !operatorStack.isEmpty()) {
throw new IllegalArgumentException("Invalid expression: " + String.join(" ", expressionTokens));
throw new IllegalArgumentException(
"Invalid expression: " + String.join(" ", expressionTokens));
}
return numberStack.pop();
}
/**
* ')'
*/
private static void handleClosingParenthesis(Stack<Double> numberStack,
Stack<String> operatorStack) {
while (!operatorStack.isEmpty() && !operatorStack.peek().equals("(")) {
processOperator(numberStack, operatorStack.pop());
}
if (!operatorStack.isEmpty()) { // Pop the "("
operatorStack.pop();
} else {
throw new IllegalArgumentException("Mismatched parentheses in expression.");
}
}
/**
* , sin, cos, tan,
*/
private static void handleAdvancedOperator(String token, Stack<Double> numberStack,
Stack<String> operatorStack) {
if ("平方".equals(token)) {
if (numberStack.isEmpty()) {
throw new IllegalArgumentException("Invalid expression: '平方' lacks an operand.");
}
double operand = numberStack.pop();
numberStack.push(Math.pow(operand, 2));
} else { // "开根号", "sin", "cos", "tan" 是前置运算符
operatorStack.push(token);
}
}
/**
* +, -, *, /
*/
private static void handleBasicOperator(String token, Stack<Double> numberStack,
Stack<String> operatorStack) {
// 处理四则运算符,遵循优先级
while (!operatorStack.isEmpty() &&
!operatorStack.peek().equals("(") &&
PRECEDENCE.get(token) <= PRECEDENCE.getOrDefault(operatorStack.peek(), 0)) {
processOperator(numberStack, operatorStack.pop());
}
operatorStack.push(token);
}
/**
*
*
* @param numberStack
* @param operator
* @throws IllegalArgumentException
* @throws ArithmeticException
*/
private static void processOperator(Stack<Double> numberStack, String operator) {
if (numberStack.size() < 1) {
throw new IllegalArgumentException("Invalid expression: operator '" + operator + "' lacks operand(s).");
if (numberStack.isEmpty()) {
throw new IllegalArgumentException(
"Invalid expression: operator '" + operator + "' lacks operand(s).");
}
if (ADVANCED_OPERATORS.contains(operator)) {
double operand = numberStack.pop();
switch (operator) {
case "开根号":
if (operand < 0) {
// 抛出异常让调用者MultipleChoiceGenerator处理
throw new ArithmeticException("Cannot take square root of negative number: " + operand);
}
numberStack.push(Math.sqrt(operand));
break;
case "sin":
numberStack.push(Math.sin(Math.toRadians(operand))); // 假设输入是度数
break;
case "cos":
numberStack.push(Math.cos(Math.toRadians(operand)));
break;
case "tan":
// tan(90 + n*180) 会趋向无穷,这里不特别处理,让其返回 Infinity 或 -Infinity
numberStack.push(Math.tan(Math.toRadians(operand)));
break;
default:
throw new IllegalArgumentException("Unknown advanced operator: " + operator);
}
} else if (OPERATORS.contains(operator)) {
if (numberStack.size() < 2) {
throw new IllegalArgumentException("Invalid expression: operator '" + operator + "' lacks operand(s).");
}
double b = numberStack.pop();
double a = numberStack.pop();
double result;
switch (operator) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
case "/":
if (b == 0) {
throw new ArithmeticException("Division by zero");
}
result = a / b;
break;
default:
throw new IllegalArgumentException("Unknown operator: " + operator);
}
numberStack.push(result);
processAdvancedOperator(numberStack, operator);
} else if (BASIC_OPERATORS.contains(operator)) {
processBasicOperator(numberStack, operator);
} else {
throw new IllegalArgumentException("Unexpected operator in process: " + operator);
}
}
/**
* , sin, cos, tan
*/
private static void processAdvancedOperator(Stack<Double> numberStack, String operator) {
double operand = numberStack.pop();
double result;
switch (operator) {
case "开根号":
if (operand < 0) {
// 抛出异常让调用者MultipleChoiceGenerator处理
throw new ArithmeticException("Cannot take square root of negative number: " + operand);
}
result = Math.sqrt(operand);
break;
case "sin":
result = Math.sin(Math.toRadians(operand)); // 假设输入是度数
break;
case "cos":
result = Math.cos(Math.toRadians(operand));
break;
case "tan":
// tan(90 + n*180) 会趋向无穷,这里不特别处理,让其返回 Infinity 或 -Infinity
result = Math.tan(Math.toRadians(operand));
break;
default:
throw new IllegalArgumentException("Unknown advanced operator: " + operator);
}
numberStack.push(result);
}
/**
* +, -, *, /
*/
private static void processBasicOperator(Stack<Double> numberStack, String operator) {
if (numberStack.size() < 2) {
throw new IllegalArgumentException(
"Invalid expression: operator '" + operator + "' lacks operand(s).");
}
double b = numberStack.pop();
double a = numberStack.pop();
double result;
switch (operator) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
case "/":
if (b == 0) {
throw new ArithmeticException("Division by zero");
}
result = a / b;
break;
default:
throw new IllegalArgumentException("Unknown operator: " + operator);
}
numberStack.push(result);
}
/**
*
*
* @param str
* @return true false
*/
public static boolean isNumeric(String str) {
if (str == null || str.isEmpty()) {
return false;

@ -1,149 +0,0 @@
package com.ybw.mathapp.service;
import com.ybw.mathapp.config.EmailConfig;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;
//import javax.mail.*;
//import javax.mail.internet.*;
public class EmailService {
private static Map<String, VerificationCodeInfo> verificationCodes = new HashMap<>();
private static class VerificationCodeInfo {
String code;
long timestamp;
VerificationCodeInfo(String code, long timestamp) {
this.code = code;
this.timestamp = timestamp;
}
}
public static String generateVerificationCode() {
Random random = new Random();
int code = 100000 + random.nextInt(900000);
return String.valueOf(code);
}
public static boolean sendVerificationCode(String recipientEmail, String code) {
/*
try {
// 创建邮件会话
Properties props = new Properties();
props.put("mail.smtp.host", EmailConfig.SMTP_HOST);
props.put("mail.smtp.port", EmailConfig.SMTP_PORT);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
// 创建认证器
Authenticator auth = new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
EmailConfig.SENDER_EMAIL,
EmailConfig.SENDER_PASSWORD
);
}
};
Session session = Session.getInstance(props, auth);
// 创建邮件消息
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(EmailConfig.SENDER_EMAIL));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(recipientEmail));
message.setSubject(EmailConfig.EMAIL_SUBJECT);
// 创建邮件内容
String emailContent = createEmailContent(code);
message.setContent(emailContent, "text/html; charset=utf-8");
// 发送邮件
Transport.send(message);
// 存储验证码信息
verificationCodes.put(recipientEmail,
new VerificationCodeInfo(code, System.currentTimeMillis()));
System.out.println("验证码已发送到邮箱: " + recipientEmail);
return true;
} catch (Exception e) {
System.err.println("发送邮件失败: " + e.getMessage());
// 在开发环境中直接返回true用于测试
verificationCodes.put(recipientEmail,
new VerificationCodeInfo(code, System.currentTimeMillis()));
return true;
}
*/
return true;
}
private static String createEmailContent(String code) {
return "<!DOCTYPE html>" +
"<html>" +
"<head>" +
"<meta charset='UTF-8'>" +
"<style>" +
"body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }" +
".container { max-width: 600px; margin: 0 auto; padding: 20px; }" +
".header { background-color: #4CAF50; color: white; padding: 20px; text-align: center; }" +
".content { padding: 20px; background-color: #f9f9f9; margin: 20px 0; }" +
".code { font-size: 24px; font-weight: bold; color: #4CAF50; text-align: center; padding: 15px; background-color: white; border: 2px dashed #4CAF50; margin: 20px 0; }" +
".footer { text-align: center; color: #666; font-size: 12px; }" +
"</style>" +
"</head>" +
"<body>" +
"<div class='container'>" +
"<div class='header'>" +
"<h2>数学学习软件 - 注册验证码</h2>" +
"</div>" +
"<div class='content'>" +
"<p>您好!</p>" +
"<p>您正在注册数学学习软件账户,验证码如下:</p>" +
"<div class='code'>" + code + "</div>" +
"<p>验证码有效期为 " + EmailConfig.CODE_EXPIRY_MINUTES + " 分钟,请勿泄露给他人。</p>" +
"<p>如果这不是您本人的操作,请忽略此邮件。</p>" +
"</div>" +
"<div class='footer'>" +
"<p>此邮件为系统自动发送,请勿回复。</p>" +
"</div>" +
"</div>" +
"</body>" +
"</html>";
}
public static boolean verifyCode(String email, String inputCode) {
VerificationCodeInfo codeInfo = verificationCodes.get(email);
if (codeInfo == null) {
return false;
}
// 检查验证码是否过期
long currentTime = System.currentTimeMillis();
if (currentTime - codeInfo.timestamp > EmailConfig.CODE_EXPIRY_MINUTES * 60 * 1000) {
verificationCodes.remove(email);
return false;
}
return codeInfo.code.equals(inputCode);
}
public static void cleanupExpiredCodes() {
long currentTime = System.currentTimeMillis();
Iterator<Entry<String, VerificationCodeInfo>> iterator =
verificationCodes.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, VerificationCodeInfo> entry = iterator.next();
if (currentTime - entry.getValue().timestamp >
EmailConfig.CODE_EXPIRY_MINUTES * 60 * 1000) {
iterator.remove();
}
}
}
}

@ -9,21 +9,45 @@ import java.util.List;
import java.util.Random;
import java.util.Set;
/**
*
*
* 1. 2.
*
* @author
* <p>
*
* <ul>
* <li> {@link AdvancedCaculate} </li>
* <li></li>
* </ul>
*
* @author
* @since 2025
*/
public class MultipleChoiceGenerator {
/**
*
*/
private static final DecimalFormat df = new DecimalFormat("#0.00");
/**
*
*/
private final QuestionGenerator baseGenerator;
/**
*
*/
private final Random random = new Random();
private static final DecimalFormat df = new DecimalFormat("#0.00");
private final String level; // 记录当前题目类型,用于特殊处理
/**
*
*/
private final String level;
/**
*
*
* @param baseGenerator
* @param level ("小学", "初中", "高中")
*/
public MultipleChoiceGenerator(QuestionGenerator baseGenerator, String level) {
this.baseGenerator = baseGenerator;
this.level = level;
@ -32,8 +56,8 @@ public class MultipleChoiceGenerator {
/**
*
*
* @param count
* @return ()
* @param count
* @return
*/
public List<QuestionWithOptions> generateMultipleChoiceQuestions(int count) {
List<QuestionWithOptions> mcQuestions = new ArrayList<>();
@ -42,9 +66,8 @@ public class MultipleChoiceGenerator {
while (mcQuestions.size() < count) {
String baseQuestion = generateUniqueBaseQuestion(seenQuestionTexts);
if (baseQuestion == null) {
// 如果无法生成不重复的基础题目,可能需要退出或处理
// 例如,如果基础生成器的可能组合用尽了
break; // 或者抛出异常
// 如果无法生成不重复的基础题目,可能基础生成器的可能组合已用尽
break; // 退出循环
}
QuestionWithOptions mcq = generateSingleMCQ(baseQuestion);
if (mcq != null) {
@ -58,6 +81,9 @@ public class MultipleChoiceGenerator {
/**
*
*
* @param seenQuestionTexts
* @return null
*/
private String generateUniqueBaseQuestion(Set<String> seenQuestionTexts) {
int attempts = 0;
@ -75,25 +101,34 @@ public class MultipleChoiceGenerator {
/**
*
*
* @param baseQuestion "3 + 5 = ?"
* @return null
*/
private QuestionWithOptions generateSingleMCQ(String baseQuestion) {
try {
// 从基础题干中提取表达式部分,例如 "3 + 5 = ?" -> "3 + 5"
String expression = baseQuestion.substring(0, baseQuestion.lastIndexOf(" =")).trim();
List<String> tokens = tokenizeExpression(expression);
// 计算正确答案
double correctAnswer = AdvancedCaculate.calculate(tokens);
// 生成选项列表
List<String> options = generateOptions(correctAnswer);
if (options == null) {
// 无法生成足够的选项
// 无法生成足够的有效选项
return null;
}
// 随机打乱选项顺序
Collections.shuffle(options);
// 找到正确答案在打乱后列表中的索引
int correctIndex = options.indexOf(df.format(correctAnswer));
return new QuestionWithOptions(baseQuestion, options, correctIndex);
} catch (ArithmeticException | IllegalArgumentException e) {
// 计算或表达式格式错误,跳过此题
// System.out.println("计算或表达式错误,跳过题目: " + baseQuestion + ", Error: " + e.getMessage());
return null; // 返回 null 表示生成失败
}
@ -101,6 +136,11 @@ public class MultipleChoiceGenerator {
/**
* ( + )
*
* <p>
*
* @param correctAnswer
* @return null
*/
private List<String> generateOptions(double correctAnswer) {
Set<Double> wrongAnswers = new HashSet<>();
@ -109,12 +149,13 @@ public class MultipleChoiceGenerator {
int numWrongOptions = 3; // 假设总共4个选项需要3个错误答案
while (wrongAnswers.size() < numWrongOptions && attempts < maxAttempts) {
int offset = random.nextInt(20) + 1;
int offset = random.nextInt(20) + 1; // 生成 1-20 的偏移量
if (random.nextBoolean()) {
offset = -offset;
offset = -offset; // 随机正负
}
double wrongAnswer = correctAnswer + offset;
// 确保错误答案与正确答案不同,并且对于小学题不为负数
if (Math.abs(df.format(wrongAnswer).compareTo(df.format(correctAnswer))) != 0) {
if (!level.equals("小学") || wrongAnswer >= 0) {
wrongAnswers.add(wrongAnswer);
@ -128,10 +169,12 @@ public class MultipleChoiceGenerator {
return null; // 无法生成足够选项
}
// 将正确答案和错误答案合并
List<Double> allAnswers = new ArrayList<>();
allAnswers.add(correctAnswer);
allAnswers.addAll(wrongAnswers);
// 格式化所有答案为字符串
List<String> options = new ArrayList<>();
for (Double ans : allAnswers) {
options.add(df.format(ans));
@ -143,6 +186,13 @@ public class MultipleChoiceGenerator {
// --- 表达式分词逻辑 ---
// 将 "3 + 开根号 ( 4 ) 平方 - sin 30" 分割成 ["3", "+", "开根号", "(", "4", ")", "平方", "-", "sin", "30"]
/**
*
*
* @param expression
* @return
*/
private List<String> tokenizeExpression(String expression) {
List<String> tokens = new ArrayList<>();
int i = 0;
@ -165,8 +215,9 @@ public class MultipleChoiceGenerator {
/**
* token
*
* @param expression
* @param startPos
* @param startPos
* @return token null
*/
private String _findNextToken(String expression, int startPos) {
@ -182,7 +233,8 @@ public class MultipleChoiceGenerator {
if (Character.isDigit(c) || c == '.') {
// 查找连续的数字或小数点
int j = startPos;
while (j < expression.length() && (Character.isDigit(expression.charAt(j)) || expression.charAt(j) == '.')) {
while (j < expression.length() && (Character.isDigit(expression.charAt(j))
|| expression.charAt(j) == '.')) {
j++;
}
return expression.substring(startPos, j);

@ -1,166 +0,0 @@
package com.ybw.mathapp.service;
import com.ybw.mathapp.entity.User;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class StartController {
private double score;
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public void start() {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("=== 数学学习软件 ===");
System.out.println("1. 登录");
System.out.println("2. 注册");
System.out.print("请选择: ");
String choice = scanner.nextLine();
if ("1".equals(choice)) {
if (login()) {
showMainMenu(scanner);
}
} else if ("2".equals(choice)) {
if (register()) {
showMainMenu(scanner);
}
} else {
System.out.println("无效选择,请重新输入!");
}
}
}
private boolean login() {
System.out.println("\n=== 用户登录 ===");
Scanner scanner = new Scanner(System.in);
System.out.print("请输入邮箱: ");
String email = scanner.nextLine().trim();
System.out.print("请输入密码: ");
String password = scanner.nextLine();
// 这里应该调用真实的登录逻辑
System.out.println("登录功能待实现");
return true;
}
private boolean register() {
System.out.println("\n=== 用户注册 ===");
System.out.println("注册功能待实现");
return true;
}
private void showMainMenu(Scanner scanner) {
while (true) {
System.out.println("\n=== 主菜单 ===");
System.out.println("1. 开始练习");
System.out.println("2. 修改密码");
System.out.println("3. 退出登录");
System.out.print("请选择: ");
String choice = scanner.nextLine();
if ("1".equals(choice)) {
showLevelSelection(scanner);
} else if ("2".equals(choice)) {
changePassword(scanner);
} else if ("3".equals(choice)) {
System.out.println("退出登录成功!");
return;
} else {
System.out.println("无效选择,请重新输入!");
}
}
}
private void showLevelSelection(Scanner scanner) {
System.out.println("\n=== 选择题目级别 ===");
System.out.println("1. 小学");
System.out.println("2. 初中");
System.out.println("3. 高中");
System.out.println("4. 返回");
System.out.print("请选择: ");
String choice = scanner.nextLine();
String level = null;
switch (choice) {
case "1": level = "小学"; break;
case "2": level = "初中"; break;
case "3": level = "高中"; break;
case "4": return;
default:
System.out.println("无效选择!");
return;
}
showQuestionCount(scanner, level);
}
private void showQuestionCount(Scanner scanner, String level) {
System.out.println("\n=== 选择题目数量 ===");
System.out.print("请输入题目数量 (10-30): ");
try {
int count = Integer.parseInt(scanner.nextLine());
if (count < 10 || count > 30) {
System.out.println("题目数量必须在10-30之间");
return;
}
startQuiz(level, count);
} catch (NumberFormatException e) {
System.out.println("请输入有效的数字!");
}
}
private void startQuiz(String level, int count) {
System.out.println("\n开始 " + level + " 级别答题,共 " + count + " 题");
// 这里应该调用题目生成和答题逻辑
System.out.println("答题功能待实现");
// 模拟答题结果
this.score = 0.8; // 模拟80%的正确率
System.out.println("答题完成!得分: " + (score * 100) + "%");
}
private void changePassword(Scanner scanner) {
System.out.println("\n=== 修改密码 ===");
System.out.println("修改密码功能待实现");
}
public QuestionGenerator createGenerator(String level) {
switch (level) {
case "小学":
return new PrimarySchoolGenerator();
case "初中":
return new JuniorHighGenerator();
case "高中":
return new SeniorHighGenerator();
default:
return null;
}
}
public boolean isCorrectAnswer(String input, int correctAnswerIndex, List<Double> options) {
if (input.equals(String.valueOf(options.get(correctAnswerIndex)))) {
return true;
} else {
return false;
}
}
public double caculateScore(int rightCount, int totalCount) {
return rightCount / (double) totalCount;
}
}

@ -11,12 +11,40 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* <p>
*
*
* @author
* @since 2025
*/
public class ChangePassword {
static final List<String> lines = new ArrayList<>();
static String userLine = null;
static int userLineNumber = -1;
// 前端接口-修改密码
/**
*
*/
private static final List<String> lines = new ArrayList<>();
/**
*
*/
private static String userLine = null;
/**
* 0
*/
private static int userLineNumber = -1;
/**
*
*
* <p>
*
*
* @param name
* @param newPassword
* @return true false
*/
public static boolean changePassword(String name, String newPassword) {
File file = new File(USER_FILE);
if (!file.exists()) {
@ -25,7 +53,10 @@ public class ChangePassword {
}
// 1. 读取文件,查找用户
if(!findUserLine(name, file)) {
lines.clear(); // 清空上一次的缓存
userLine = null;
userLineNumber = -1;
if (!findUserLine(name, file)) {
return false;
}
@ -38,6 +69,7 @@ public class ChangePassword {
// 2. 更新找到的用户行中的密码
String[] parts = userLine.split(",");
if (parts.length != 3) {
System.err.println("用户文件中用户 '" + name + "' 的数据格式不正确,无法修改密码。");
return false;
}
parts[2] = newPassword; // 假设密码是第三个字段
@ -46,17 +78,24 @@ public class ChangePassword {
lines.set(userLineNumber, updatedLine); // 替换列表中的旧行
// 3. 将更新后的内容写回文件
if(!writeBack(lines, file)) {
if (!writeBack(lines, file)) {
return false;
}
return true;
}
public static boolean writeBack(List<String> lines, File file) {
/**
*
*
* @param lines
* @param file
* @return true false
*/
private static boolean writeBack(List<String> lines, File file) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
for (String l : lines) {
writer.write(l);
for (String line : lines) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
@ -66,7 +105,17 @@ public class ChangePassword {
return true;
}
public static boolean findUserLine(String name, File file) {
/**
*
*
* <p> {@code lines}
* {@code userLine} {@code userLineNumber}
*
* @param name
* @param file
* @return true false
*/
private static boolean findUserLine(String name, File file) {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
int currentLineNum = 0;
@ -74,10 +123,10 @@ public class ChangePassword {
lines.add(line);
String[] parts = line.split(",");
// 假设格式为: username,email,password
if (parts.length >= 3 && parts[0].equals(name)) {
if (parts.length >= 3 && parts[0].trim().equals(name.trim())) {
userLine = line; // 找到用户行
userLineNumber = currentLineNum;
break; // 找到后可以退出循环
// break; // 找到后可以退出循环,但为了读取所有行到 lines不在此处 break
}
currentLineNum++;
}
@ -87,5 +136,4 @@ public class ChangePassword {
}
return true;
}
}
}

@ -1,12 +1,6 @@
package com.ybw.mathapp.util;
import com.ybw.mathapp.config.EmailConfig;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;
import jakarta.mail.Authenticator;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
@ -15,29 +9,50 @@ import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;
/**
*
*
* <p>
* {@link EmailConfig}
*
* @author
* @since 2025
*/
public class EmailService {
private static Map<String, VerificationCodeInfo> verificationCodes = new HashMap<>();
// 验证码信息内部类
private static class VerificationCodeInfo {
String code;
long timestamp;
VerificationCodeInfo(String code, long timestamp) {
this.code = code;
this.timestamp = timestamp;
}
}
/**
*
*/
private static final Map<String, VerificationCodeInfo> verificationCodes = new HashMap<>();
// 生成6位随机验证码
/**
* 6
*
* @return 6
*/
public static String generateVerificationCode() {
Random random = new Random();
int code = 100000 + random.nextInt(900000);
return String.valueOf(code);
}
// 发送真实邮件验证码
/**
*
*
* <p>使 {@link EmailConfig} SMTP
*
*
* @param recipientEmail
* @param code
* @return true false
*/
public static boolean sendVerificationCode(String recipientEmail, String code) {
try {
// 创建邮件会话
@ -89,7 +104,12 @@ public class EmailService {
}
}
// 创建HTML格式的邮件内容
/**
* HTML
*
* @param code
* @return HTML
*/
private static String createEmailContent(String code) {
return "<!DOCTYPE html>" +
"<html>" +
@ -100,7 +120,8 @@ public class EmailService {
".container { max-width: 600px; margin: 0 auto; padding: 20px; }" +
".header { background-color: #4CAF50; color: white; padding: 20px; text-align: center; }" +
".content { padding: 20px; background-color: #f9f9f9; margin: 20px 0; }" +
".code { font-size: 24px; font-weight: bold; color: #4CAF50; text-align: center; padding: 15px; background-color: white; border: 2px dashed #4CAF50; margin: 20px 0; }" +
".code { font-size: 24px; font-weight: bold; color: #4CAF50; text-align: center; padding: 15px; background-color: white; border: 2px dashed #4CAF50; margin: 20px 0; }"
+
".footer { text-align: center; color: #666; font-size: 12px; }" +
"</style>" +
"</head>" +
@ -124,7 +145,13 @@ public class EmailService {
"</html>";
}
// 前端接口-验证验证码
/**
*
*
* @param email
* @param inputCode
* @return true false
*/
public static boolean verifyCode(String email, String inputCode) {
VerificationCodeInfo codeInfo = verificationCodes.get(email);
if (codeInfo == null) {
@ -141,7 +168,9 @@ public class EmailService {
return codeInfo.code.equals(inputCode);
}
// 清理过期的验证码(可选)
/**
*
*/
public static void cleanupExpiredCodes() {
long currentTime = System.currentTimeMillis();
Iterator<Entry<String, VerificationCodeInfo>> iterator =
@ -157,12 +186,13 @@ public class EmailService {
}
/**
* -
*
*
* @param email
* @return truefalse
* @return true false
*/
public static boolean isValidEmail(String email) {
if (email.isEmpty()) {
if (email == null || email.isEmpty()) {
System.out.println("邮箱地址不能为空!");
return false;
}
@ -173,17 +203,48 @@ public class EmailService {
return true;
}
// 前端接口-发送验证码
/**
*
*
* <p>
*
* @param email
* @return true false
*/
public static boolean sendCode(String email) {
// 发送真实邮件验证码
// 生成验证码
String verificationCode = EmailService.generateVerificationCode();
// 验证验证码是否成功发送
// 尝试发送邮件
if (!EmailService.sendVerificationCode(email, verificationCode)) {
// 如果发送失败sendVerificationCode 已经打印了错误信息
return false;
}
return true;
}
/**
*
*/
private static class VerificationCodeInfo {
/**
*
*/
String code;
/**
*
*/
long timestamp;
}
/**
*
*
* @param code
* @param timestamp
*/
VerificationCodeInfo(String code, long timestamp) {
this.code = code;
this.timestamp = timestamp;
}
}
}

@ -1,16 +1,32 @@
package com.ybw.mathapp.util;
/**
*
*
* <p>
*
* @author
* @since 2025
*/
public class Login {
// 前端接口-登录成功or失败
/**
* 使
*
* <p> {@link LoginFileUtils#validateUser(String, String)}
*
*
* @param name
* @param password
* @return true false
*/
public static boolean login(String name, String password) {
if (LoginFileUtils.validateUser(name, password)) {
System.out.println("登录成功!欢迎回来," + name);
return true;
} else {
System.out.println("邮箱或密码错误!");
System.out.println("用户名或密码错误!");
return false;
}
}
}
}

@ -1,14 +1,38 @@
package com.ybw.mathapp.util;
import com.ybw.mathapp.entity.User;
import java.io.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* <p> ({@code users.txt})
*
*
* @author
* @since 2025
*/
public class LoginFileUtils {
static final String USER_FILE = "users.txt";
// 读取所有用户
/**
*
*/
public static final String USER_FILE = "users.txt";
/**
*
*
* <p>
*
* @return
*/
public static List<User> readUsers() {
List<User> users = new ArrayList<>();
File file = new File(USER_FILE);
@ -26,7 +50,9 @@ public class LoginFileUtils {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.isEmpty()) continue;
if (line.isEmpty()) {
continue;
}
User user = User.fromString(line);
if (user != null) {
@ -39,7 +65,11 @@ public class LoginFileUtils {
return users;
}
// 保存用户到文件
/**
*
*
* @param user
*/
public static void saveUser(User user) {
try (PrintWriter writer = new PrintWriter(new FileWriter(USER_FILE, true))) {
writer.println(user.toString());
@ -48,7 +78,14 @@ public class LoginFileUtils {
}
}
// 前端接口-检查邮箱是否已注册
/**
*
*
* <p>
*
* @param email
* @return true false
*/
public static boolean isEmailRegistered(String email) {
List<User> users = readUsers();
for (User user : users) {
@ -59,7 +96,14 @@ public class LoginFileUtils {
return false;
}
// 前端接口-检查用户名是否已注册
/**
*
*
* <p>
*
* @param name
* @return true false
*/
public static boolean isNameRegistered(String name) {
List<User> users = readUsers();
for (User user : users) {
@ -70,20 +114,36 @@ public class LoginFileUtils {
return false;
}
// 前端接口-验证用户登录
/**
*
*
* <p>使/
*
* @param emailOrName
* @param password
* @return true false
*/
public static boolean validateUser(String emailOrName, String password) {
List<User> users = readUsers();
for (User user : users) {
if ((user.getEmail().equalsIgnoreCase(emailOrName)
|| user.getName().equalsIgnoreCase(emailOrName) )
&& user.getPassword().equals(password)) {
|| user.getName().equalsIgnoreCase(emailOrName))
&& user.getPassword().equals(password)) {
return true;
}
}
return false;
}
// 前端接口-通过邮箱查找用户名
public static String emailFindName (String email) {
/**
*
*
* <p>
*
* @param email
* @return null
*/
public static String emailFindName(String email) {
List<User> users = readUsers();
for (User user : users) {
if (user.getEmail().equalsIgnoreCase(email)) {
@ -92,4 +152,4 @@ public class LoginFileUtils {
}
return null;
}
}
}

@ -3,9 +3,27 @@ package com.ybw.mathapp.util;
import com.ybw.mathapp.entity.User;
import java.util.regex.Pattern;
/**
*
*
* <p>
*
* @author
* @since 2025
*/
public class Register {
// 前端接口-完成注册
/**
*
*
* <p>
*
* @param name
* @param email
* @param password1
* @return true true使
*/
public static boolean register(String name, String email, String password1) {
User user = new User(name, email, password1);
LoginFileUtils.saveUser(user);
@ -14,9 +32,17 @@ public class Register {
}
/**
* -
* @param password1
* @return truefalse
*
*
* <p>
* <ul>
* <li> 6 10 </li>
* <li></li>
* <li></li>
* </ul>
*
* @param password1
* @return true false
*/
public static boolean isVaildPassword(String password1) {
// 使用正则表达式验证长度6-10只包含字母数字且包含大小写字母和数字
@ -28,10 +54,11 @@ public class Register {
}
/**
* -
*
*
* @param password1
* @param password2
* @return truefalse
* @return true false
*/
public static boolean isEqualPassword(String password1, String password2) {
if (!password1.equals(password2)) {
@ -40,5 +67,4 @@ public class Register {
}
return true;
}
}
}
Loading…
Cancel
Save