Feat: 添加 src 和 lib 核心代码

yimuran_branch
ymr 6 months ago
parent aa7dfe06bc
commit fc8b0d9b65

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,20 @@
import com.formdev.flatlaf.FlatLightLaf;
import controller.AppController;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
System.setProperty("awt.useSystemAAFontSettings", "on");
System.setProperty("swing.aatext", "true");
try {
UIManager.setLookAndFeel(new FlatLightLaf());
} catch (UnsupportedLookAndFeelException e) {
System.err.println("无法设置UI主题: " + e.getMessage());
}
SwingUtilities.invokeLater(AppController::new);
}
}

@ -0,0 +1,10 @@
smtp.host=smtp.qq.com
smtp.port=465
smtp.ssl.enable=true
smtp.auth=true
mail.sender.email=2631495488@qq.com
mail.sender.password=vdjvddhflexgdjfd

@ -0,0 +1,19 @@
package model;
import java.util.List;
public class Question {
private final String questionText;
private final List<String> options;
private final int correctOptionIndex;
public Question(String questionText, List<String> options, int correctOptionIndex) {
this.questionText = questionText;
this.options = options;
this.correctOptionIndex = correctOptionIndex;
}
public String getQuestionText() { return questionText; }
public List<String> getOptions() { return options; }
public int getCorrectOptionIndex() { return correctOptionIndex; }
}

@ -0,0 +1,56 @@
package model;
public class User {
private final String email;
private final String username; // 新增字段
private String hashedPassword;
private UserType userType;
public User(String email, String username, String hashedPassword, UserType userType) {
this.email = email;
this.username = username;
this.hashedPassword = hashedPassword;
this.userType = userType;
}
public String getEmail() { return email; }
public String getUsername() { return username; } // 新增Getter
public String getHashedPassword() { return hashedPassword; }
public UserType getUserType() { return userType; }
public void setUserType(UserType userType) { this.userType = userType; }
public void setHashedPassword(String plainPassword) {
this.hashedPassword = Integer.toString(plainPassword.hashCode());
}
private void setHashedPasswordDirectly(String hashedPassword) {
this.hashedPassword = hashedPassword;
}
@Override
public String toString() {
// 新的4字段格式用户名可以为空字符串
return String.join("|", email, username == null ? "" : username, hashedPassword, userType.name());
}
public static User fromString(String line) {
String[] parts = line.split("\\|");
User user = null;
if (parts.length == 4) { // 新格式: email|username|password|type
String email = parts[0];
String username = parts[1].isEmpty() ? null : parts[1];
String password = parts[2];
UserType type = UserType.valueOf(parts[3]);
user = new User(email, username, "", type);
user.setHashedPasswordDirectly(password);
} else if (parts.length == 3) { // 旧格式: email|password|type
String email = parts[0];
String password = parts[1];
UserType type = UserType.valueOf(parts[2]);
user = new User(email, null, "", type); // 旧用户没有用户名
user.setHashedPasswordDirectly(password);
}
return user;
}
}

@ -0,0 +1,17 @@
package model;
public enum UserType {
PRIMARY("小学"),
JUNIOR("初中"),
SENIOR("高中");
private final String displayName;
UserType(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}

@ -0,0 +1,74 @@
package service;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class EmailService {
final Properties props = new Properties();
private final String senderEmail;
private final String senderPassword;
public EmailService() {
try (InputStream input = EmailService.class.getClassLoader().getResourceAsStream("config.properties")) {
if (input == null) {
System.err.println("致命错误:在类路径中找不到 config.properties 文件!");
System.err.println("请确认 'config.properties' 文件已放置在 'src' 文件夹根目录下。");
throw new RuntimeException("加载配置文件失败: config.properties not found in classpath");
}
props.load(input);
} catch (IOException e) {
System.err.println("错误:加载 config.properties 文件时发生IO错误");
throw new RuntimeException("加载配置文件失败", e);
}
this.senderEmail = props.getProperty("mail.sender.email");
this.senderPassword = props.getProperty("mail.sender.password");
}
public void sendVerificationCode(String recipientEmail, String code) throws MessagingException {
Properties mailProps = new Properties();
mailProps.put("mail.smtp.host", props.getProperty("smtp.host"));
mailProps.put("mail.smtp.port", props.getProperty("smtp.port"));
mailProps.put("mail.smtp.ssl.enable", props.getProperty("smtp.ssl.enable"));
mailProps.put("mail.smtp.auth", props.getProperty("smtp.auth"));
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(senderEmail, senderPassword);
}
};
Session session = Session.getInstance(mailProps, authenticator);
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(senderEmail));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipientEmail));
message.setSubject("您的数学学习软件验证码", "UTF-8");
String content = "<h3>欢迎使用数学学习软件!</h3>" +
"<p>您的注册验证码是:<b style='color: #007bff; font-size: 18px;'>" + code + "</b></p>" +
"<p>该验证码5分钟内有效请勿泄露给他人。</p>";
message.setContent(content, "text/html; charset=UTF-8");
Transport.send(message);
System.out.println("验证码邮件已成功发送至 " + recipientEmail);
}
}

@ -0,0 +1,169 @@
package service;
import model.Question;
import model.User;
import model.UserType;
import service.generator.*;
import java.io.PrintWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
public class ExamManager {
private final Map<UserType, QuestionGenerator> generators = new HashMap<>();
public ExamManager() {
generators.put(UserType.PRIMARY, new PrimaryQuestionGenerator());
generators.put(UserType.JUNIOR, new JuniorQuestionGenerator());
generators.put(UserType.SENIOR, new SeniorQuestionGenerator());
}
public List<Question> generateExam(User user, UserType userType, int count) {
QuestionGenerator generator = generators.get(userType);
if (generator == null) throw new IllegalArgumentException("不支持的用户类型");
List<Question> questions = new ArrayList<>();
Set<String> generatedTexts = new HashSet<>();
int maxAttempts = count * 20;
int attempts = 0;
while (questions.size() < count && attempts < maxAttempts) {
String qText = generator.generateQuestion();
if (generatedTexts.contains(qText)) {
attempts++;
continue;
}
try {
double answer = ExpressionEvaluator.evaluate(qText);
if (Double.isInfinite(answer) || Double.isNaN(answer)) continue;
List<String> options = generateOptions(answer); // 调用已优化的方法
int correctIndex = options.indexOf(formatAnswer(answer));
questions.add(new Question(qText, options, correctIndex));
generatedTexts.add(qText);
} catch (Exception e) {
} finally {
attempts++;
}
}
if (!questions.isEmpty()) {
saveExamToFile(user, questions);
}
return questions;
}
private List<String> generateOptions(double correctAnswer) {
Set<String> options = new LinkedHashSet<>();
options.add(formatAnswer(correctAnswer));
Random rand = new Random();
// 判断答案是否为整数
boolean isIntegerAnswer = Math.abs(correctAnswer - Math.round(correctAnswer)) < 1e-9;
while (options.size() < 4) {
String distractorStr;
if (isIntegerAnswer) {
int intAnswer = (int) Math.round(correctAnswer);
int distractor;
int type = rand.nextInt(3); // 三种整数干扰策略
switch(type) {
case 0: // 策略一:在答案附近加减一个较小的随机数
distractor = intAnswer + (rand.nextInt(8) + 1) * (rand.nextBoolean() ? 1 : -1);
break;
case 1: // 策略二模拟看错位的错误加减10
distractor = intAnswer + (rand.nextBoolean() ? 10 : -10);
break;
default: // 策略三:颠倒个位和十位(如果答案是两位数)
if (intAnswer >= 10 && intAnswer <= 99) {
distractor = (intAnswer % 10) * 10 + (intAnswer / 10);
} else {
// 如果不是两位数,则使用策略一
distractor = intAnswer + (rand.nextInt(8) + 1) * (rand.nextBoolean() ? 1 : -1);
}
break;
}
// 确保干扰项不等于正确答案
if (distractor == intAnswer) {
distractor++;
}
distractorStr = String.valueOf(distractor);
} else {
double distractor;
if (rand.nextBoolean()) { // 乘法干扰
distractor = correctAnswer * (rand.nextDouble() * 1.5 + 0.5);
} else { // 临近值干扰
distractor = correctAnswer + (rand.nextDouble() - 0.5) * Math.max(1, Math.abs(correctAnswer * 0.2));
}
distractorStr = formatAnswer(distractor);
}
// 确保选项不重复
if (!options.contains(distractorStr)) {
options.add(distractorStr);
}
}
List<String> shuffledOptions = new ArrayList<>(options);
Collections.shuffle(shuffledOptions);
return shuffledOptions;
}
private void saveExamToFile(User user, List<Question> questions) {
try {
Path userExamDir = Paths.get("exams", user.getEmail());
Files.createDirectories(userExamDir);
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
Path examFile = userExamDir.resolve("exam_" + timestamp + ".txt");
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(examFile, StandardCharsets.UTF_8))) {
writer.println("用户: " + user.getEmail());
writer.println("时间: " + new Date());
writer.println("题目类型: " + user.getUserType().getDisplayName());
writer.println("题目数量: " + questions.size());
writer.println("========================================");
writer.println();
for (int i = 0; i < questions.size(); i++) {
Question q = questions.get(i);
writer.println((i + 1) + ". " + q.getQuestionText());
List<String> options = q.getOptions();
char optionLabel = 'A';
for (int j = 0; j < options.size(); j++) {
writer.println(" " + optionLabel + ". " + options.get(j));
optionLabel++;
}
writer.println(" 正确答案: " + (char)('A' + q.getCorrectOptionIndex()));
writer.println();
}
}
System.out.println("试卷已成功保存至: " + examFile.toAbsolutePath());
} catch (IOException e) {
System.err.println("保存试卷文件时出错: " + e.getMessage());
}
}
private String formatAnswer(double number) {
if (Math.abs(number - Math.round(number)) < 1e-9) {
return String.valueOf((long) Math.round(number));
} else {
return String.format("%.2f", number);
}
}
}

@ -0,0 +1,120 @@
package service;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ExpressionEvaluator {
public static double evaluate(String expression) {
// 1. 预处理,将特殊运算(开方、平方、三角函数)直接计算并替换为数值
String processedExpr = preprocessSpecialOperations(expression);
// 2. 使用双栈算法计算最终的四则运算表达式
return calculateStandardExpression(processedExpr);
}
private static String preprocessSpecialOperations(String expr) {
// 替换中文运算符
expr = expr.replace('×', '*').replace('÷', '/');
// 匹配并计算三角函数,例如 sin(30°)
Pattern trigPattern = Pattern.compile("(sin|cos|tan)\\((\\d+)°\\)");
Matcher trigMatcher = trigPattern.matcher(expr);
while (trigMatcher.find()) {
String func = trigMatcher.group(1);
double angle = Double.parseDouble(trigMatcher.group(2));
double value = 0.0;
switch (func) {
case "sin": value = Math.sin(Math.toRadians(angle)); break;
case "cos": value = Math.cos(Math.toRadians(angle)); break;
case "tan": value = Math.tan(Math.toRadians(angle)); break;
}
expr = trigMatcher.replaceFirst(String.format("%.4f", value));
trigMatcher = trigPattern.matcher(expr);
}
// 匹配并计算平方,例如 5^2
Pattern squarePattern = Pattern.compile("(\\d+)\\^2");
Matcher squareMatcher = squarePattern.matcher(expr);
while (squareMatcher.find()) {
double base = Double.parseDouble(squareMatcher.group(1));
expr = squareMatcher.replaceFirst(String.valueOf(base * base));
squareMatcher = squarePattern.matcher(expr);
}
// 匹配并计算开方,例如 √(16)
Pattern sqrtPattern = Pattern.compile("√\\((\\d+)\\)");
Matcher sqrtMatcher = sqrtPattern.matcher(expr);
while (sqrtMatcher.find()) {
double num = Double.parseDouble(sqrtMatcher.group(1));
expr = sqrtMatcher.replaceFirst(String.valueOf(Math.sqrt(num)));
sqrtMatcher = sqrtPattern.matcher(expr);
}
return expr;
}
private static double calculateStandardExpression(String expression) {
Stack<Double> values = new Stack<>();
Stack<Character> ops = new Stack<>();
char[] tokens = expression.toCharArray();
for (int i = 0; i < tokens.length; i++) {
if (tokens[i] == ' ') continue;
if ((tokens[i] >= '0' && tokens[i] <= '9') || tokens[i] == '.') {
StringBuilder sbuf = new StringBuilder();
while (i < tokens.length && ((tokens[i] >= '0' && tokens[i] <= '9') || tokens[i] == '.')) {
sbuf.append(tokens[i++]);
}
i--;
values.push(Double.parseDouble(sbuf.toString()));
} else if (tokens[i] == '(') {
ops.push(tokens[i]);
} else if (tokens[i] == ')') {
while (ops.peek() != '(') {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
ops.pop();
} else if (isOperator(tokens[i])) {
while (!ops.empty() && hasPrecedence(tokens[i], ops.peek())) {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
ops.push(tokens[i]);
}
}
while (!ops.empty()) {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
return values.pop();
}
private static boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
private static boolean hasPrecedence(char op1, char op2) {
if (op2 == '(' || op2 == ')') return false;
return (op1 != '*' && op1 != '/') || (op2 != '+' && op2 != '-');
}
private static double applyOp(char op, double b, double a) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/':
if (b == 0) throw new UnsupportedOperationException("不能除以零");
return a / b;
}
return 0;
}
}

@ -0,0 +1,97 @@
package service;
import model.User;
import model.UserType;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
public class UserManager {
private final Path usersFile = Paths.get("data", "users.txt");
private final Map<String, User> usersByEmail = new HashMap<>(); // 使用邮箱作为主键
public UserManager() {
try {
Files.createDirectories(usersFile.getParent());
if (!Files.exists(usersFile)) Files.createFile(usersFile);
loadUsers();
} catch (IOException e) {
System.err.println("初始化用户管理器时出错: " + e.getMessage());
}
}
private void loadUsers() throws IOException {
List<String> lines = Files.readAllLines(usersFile, StandardCharsets.UTF_8);
for (String line : lines) {
User user = User.fromString(line);
if (user != null) usersByEmail.put(user.getEmail(), user);
}
}
private void saveUsers() {
try (BufferedWriter writer = Files.newBufferedWriter(usersFile, StandardCharsets.UTF_8)) {
for (User user : usersByEmail.values()) writer.write(user.toString() + "\n");
} catch (IOException e) {
System.err.println("保存用户时出错: " + e.getMessage());
}
}
public User authenticate(String identifier, String password) {
String hashedInput = hashPassword(password);
// 策略一:尝试将 identifier 作为邮箱进行快速查找
User userByEmail = usersByEmail.get(identifier);
if (userByEmail != null && userByEmail.getHashedPassword().equals(hashedInput)) {
return userByEmail; // 邮箱匹配成功
}
// 策略二:如果邮箱查找失败,则遍历所有用户,尝试匹配用户名
for (User user : usersByEmail.values()) {
if (user.getUsername() != null && user.getUsername().equals(identifier)) {
if (user.getHashedPassword().equals(hashedInput)) {
return user; // 用户名匹配成功
}
break; // 用户名是唯一的,找到后无需继续遍历
}
}
return null; // 认证失败
}
public boolean emailExists(String email) {
return usersByEmail.containsKey(email);
}
public boolean usernameExists(String username) {
if (username == null || username.trim().isEmpty()) return false;
for (User user : usersByEmail.values()) {
if (username.equals(user.getUsername())) {
return true;
}
}
return false;
}
public void registerUser(String email, String username, String password, UserType type) {
User newUser = new User(email, username, "", type);
newUser.setHashedPassword(password);
usersByEmail.put(email, newUser);
saveUsers();
}
public void updateUser(User user) {
if (usersByEmail.containsKey(user.getEmail())) {
usersByEmail.put(user.getEmail(), user);
saveUsers();
}
}
private String hashPassword(String password) {
return Integer.toString(password.hashCode());
}
}

@ -0,0 +1,25 @@
package service;
public class ValidationService {
public static boolean isValidEmail(String email) {
if (email == null || email.trim().isEmpty()) {
return false;
}
String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
return email.matches(emailRegex);
}
public static boolean isPasswordStrong(String password) {
if (password == null || password.length() < 6 || password.length() > 10) {
return false;
}
boolean hasUpperCase = password.matches(".*[A-Z].*");
boolean hasLowerCase = password.matches(".*[a-z].*");
boolean hasDigit = password.matches(".*[0-9].*");
return hasUpperCase && hasLowerCase && hasDigit;
}
}

@ -0,0 +1,61 @@
package service.generator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public abstract class AbstractQuestionGenerator implements QuestionGenerator {
protected final Random rand = new Random();
private static final char[] OPS = {'+', '-', '×', '÷'};
protected String buildComplexExpression(List<String> operands) {
int numOperands = operands.size();
if (numOperands < 2) return operands.get(0); // 如果只有一个操作数,直接返回
List<Character> operators = new ArrayList<>();
for (int i = 0; i < numOperands - 1; i++) {
operators.add(OPS[rand.nextInt(OPS.length)]);
}
int parensType = rand.nextInt(5); // 增加不加括号的概率
switch (parensType) {
case 1: // 单括号 (a op b) op c ...
if (numOperands >= 3) {
Collections.swap(operands, 0, rand.nextInt(numOperands));
Collections.swap(operands, 1, rand.nextInt(numOperands));
return String.format("(%s %c %s) %c %s", operands.get(0), operators.get(0), operands.get(1), operators.get(1), buildRest(operands, operators, 2));
}
break;
case 2: // a op (b op c) op d ...
if (numOperands >= 3) {
return String.format("%s %c (%s %c %s)", operands.get(0), operators.get(0), operands.get(1), operators.get(1), buildRest(operands, operators, 2));
}
break;
case 3: // 双重括号 ((a op b) op c) op d ...
if (numOperands >= 4) {
return String.format("((%s %c %s) %c %s) %c %s", operands.get(0), operators.get(0), operands.get(1), operators.get(1), operands.get(2), operators.get(2), buildRest(operands, operators, 3));
}
break;
case 4: // 双重括号 a op (b op (c op d)) ...
if (numOperands >= 4) {
return String.format("%s %c (%s %c (%s %c %s))", operands.get(0), operators.get(0), operands.get(1), operators.get(1), operands.get(2), operators.get(2), buildRest(operands, operators, 3));
}
break;
}
// 默认情况:无括号或无法应用括号
return buildRest(operands, operators, 0);
}
private String buildRest(List<String> operands, List<Character> operators, int startIndex) {
StringBuilder sb = new StringBuilder();
sb.append(operands.get(startIndex));
for (int i = startIndex; i < operators.size(); i++) {
sb.append(" ").append(operators.get(i)).append(" ");
sb.append(operands.get(i + 1));
}
return sb.toString();
}
}

@ -0,0 +1,49 @@
package service.generator;
import java.util.Random;
public class JuniorQuestionGenerator implements QuestionGenerator {
private final Random rand = new Random();
@Override
public String generateQuestion() {
int numOperands = rand.nextInt(4) + 2; // 2-5个操作数
String[] operands = new String[numOperands];
// 先填充普通数字
for (int i = 0; i < numOperands; i++) {
operands[i] = String.valueOf(num(15));
}
// 随机替换一个为特殊运算
int specialIndex = rand.nextInt(numOperands);
operands[specialIndex] = specialOp();
// 根据操作数数量选择模板
switch (numOperands) {
case 2:
return String.format("%s %c %s", operands[0], op(), operands[1]);
case 3:
return String.format("(%s %c %s) %c %s", operands[0], opLow(), operands[1], opHigh(), operands[2]);
case 4:
return String.format("(%s %c %s) %c (%s %c %s)", operands[0], opHigh(), operands[1], opLow(), operands[2], opLow(), operands[3]);
case 5:
default:
return String.format("((%s %c %s) %c %s) %c (%s %c %s)", operands[0], opLow(), operands[1], opHigh(), operands[2], opLow(), operands[3], opLow(), operands[4]);
}
}
// --- 辅助方法 ---
private int num(int bound) { return rand.nextInt(bound) + 1; }
private char op() { return "+-×".charAt(rand.nextInt(3)); }
private char opLow() { return "+-".charAt(rand.nextInt(2)); }
private char opHigh() { return '×'; }
private String specialOp() {
if (rand.nextBoolean()) {
return (rand.nextInt(9) + 2) + "^2"; // 2^2 to 10^2
} else {
int base = rand.nextInt(10) + 1;
return "√(" + (base * base) + ")";
}
}
}

@ -0,0 +1,69 @@
package service.generator;
import java.util.Random;
public class PrimaryQuestionGenerator implements QuestionGenerator {
private final Random rand = new Random();
@Override
public String generateQuestion() {
int numOperands = rand.nextInt(4) + 2; // 随机生成2-5个操作数
switch (numOperands) {
case 2:
return simpleExpression(num(50), op(), num(50));
case 3:
return generateThreeOperandExpression();
case 4:
return generateFourOperandExpression();
case 5:
default:
return generateFiveOperandExpression();
}
}
private String generateThreeOperandExpression() {
if (rand.nextBoolean()) {
// 模板: (a + b) * c
return String.format("(%d %c %d) %c %d", num(20), opLow(), num(20), opHigh(), num(10));
} else {
// 模板: a * (b - c)
int b = num(29) + 1; // b 的范围是 2 到 30
int c = num(b - 1); // c 的范围是 1 到 b-1确保 c < b
return String.format("%d %c (%d %c %d)", num(10), opHigh(), b, opLow(), c);
}
}
private String generateFourOperandExpression() {
if (rand.nextBoolean()) {
// 模板: ((a + b) * c) - d
return String.format("((%d %c %d) %c %d) %c %d", num(10), opLow(), num(10), opHigh(), num(5), opLow(), num(20));
} else {
// 模板: a * (b - (c + d))
return String.format("%d %c (%d %c (%d %c %d))", num(5), opHigh(), num(50), opLow(), num(10), opLow(), num(10));
}
}
private String generateFiveOperandExpression() {
// 模板: (a * b) + (c - d) * e
int c = num(29) + 1; // c 的范围是 2 到 30
int d = num(c - 1); // d 的范围是 1 到 c-1确保 d < c
return String.format("(%d %c %d) %c (%d %c %d) %c %d", num(10), opHigh(), num(10), opLow(), c, opLow(), d, opHigh(), num(5));
}
// --- 辅助方法 ---
private int num(int bound) {
// 健壮性检查防止外部调用传入无效的bound
if (bound <= 0) return 1;
return rand.nextInt(bound) + 1;
}
private char op() { return "+-×÷".charAt(rand.nextInt(4)); }
private char opLow() { return "+-".charAt(rand.nextInt(2)); } // 低优先级
private char opHigh() { return "×÷".charAt(rand.nextInt(2)); } // 高优先级
private String simpleExpression(int a, char op, int b) {
if (op == '÷') a = b * num(10);
if (op == '-') a = Math.max(a, b + 1);
return String.format("%d %c %d", a, op, b);
}
}

@ -0,0 +1,5 @@
package service.generator;
public interface QuestionGenerator {
String generateQuestion();
}

@ -0,0 +1,51 @@
package service.generator;
import java.util.Random;
public class SeniorQuestionGenerator implements QuestionGenerator {
private final Random rand = new Random();
private static final String[] TRIG_FUNCS = {"sin", "cos", "tan"};
private static final int[] COMMON_ANGLES = {0, 30, 45, 60, 90,135,180};
private static final int[] TAN_SAFE_ANGLES = {0, 30, 45, 60,135};
@Override
public String generateQuestion() {
int numOperands = rand.nextInt(3) + 2; // 2-4个操作数以控制复杂度
String[] operands = new String[numOperands];
for (int i = 0; i < numOperands; i++) {
operands[i] = String.valueOf(num(10));
}
int specialIndex = rand.nextInt(numOperands);
operands[specialIndex] = specialOp();
switch (numOperands) {
case 2:
return String.format("%s %c %s", operands[0], op(), operands[1]);
case 3:
return String.format("(%s %c %s) %c %s", operands[0], opLow(), operands[1], opHigh(), operands[2]);
case 4:
default:
return String.format("%s %c (%s %c (%s %c %s))", operands[0], opHigh(), operands[1], opLow(), operands[2], opLow(), operands[3]);
}
}
// --- 辅助方法 ---
private int num(int bound) { return rand.nextInt(bound) + 1; }
private char op() { return "+-×".charAt(rand.nextInt(3)); }
private char opLow() { return "+-".charAt(rand.nextInt(2)); }
private char opHigh() { return '×'; }
private String specialOp() {
String func = TRIG_FUNCS[rand.nextInt(TRIG_FUNCS.length)];
int angle;
if (func.equals("tan")) {
angle = TAN_SAFE_ANGLES[rand.nextInt(TAN_SAFE_ANGLES.length)];
} else {
angle = COMMON_ANGLES[rand.nextInt(COMMON_ANGLES.length)];
}
return String.format("%s(%d°)", func, angle);
}
}
Loading…
Cancel
Save