From 200026a01bf373b8037a17386b0a41a97d730dc0 Mon Sep 17 00:00:00 2001
From: LYQ <3048494657@qq.com>
Date: Sun, 12 Oct 2025 21:30:50 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9C=80=E7=BB=88=E7=89=88=E6=9C=AC2.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/workspace.xml | 47 ++--
.../java/com/student/mathquiz/MainApp.java | 9 +-
.../question/HighQuestionGenerator.java | 129 +++++++++--
.../question/PrimaryQuestionGenerator.java | 213 ++++++++++++++----
.../mathquiz/service/IUserService.java | 9 +-
.../mathquiz/service/UserServiceImpl.java | 19 +-
.../mathquiz/view/LoginViewController.java | 15 +-
user_data/users.json | 4 +-
8 files changed, 339 insertions(+), 106 deletions(-)
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 4596258..19dde93 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,7 +4,15 @@
-
+
+
+
+
+
+
+
+
+
@@ -29,25 +37,25 @@
- {
+ "keyToString": {
+ "Maven.mathquiz [clean].executor": "Run",
+ "Maven.mathquiz [compile].executor": "Run",
+ "Maven.mathquiz [org.openjfx:javafx-maven-plugin:0.0.8:run].executor": "Run",
+ "Maven.mathquiz [package].executor": "Run",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "git-widget-placeholder": "develop",
+ "ignore.virus.scanning.warn.message": "true",
+ "kotlin-language-version-configured": "true",
+ "last_opened_file_path": "C:/Users/lyq20/OneDrive/桌面/Finally/MathQuiz",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.tslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "vue.rearranger.settings.migration": "true"
}
-}]]>
+}
@@ -69,6 +77,7 @@
+
diff --git a/src/main/java/com/student/mathquiz/MainApp.java b/src/main/java/com/student/mathquiz/MainApp.java
index 9fcd0de..c3849c4 100644
--- a/src/main/java/com/student/mathquiz/MainApp.java
+++ b/src/main/java/com/student/mathquiz/MainApp.java
@@ -74,11 +74,14 @@ public class MainApp extends Application {
return null;
}
}
- // ... (所有其他方法保持不变)
- public void handleLoginSuccess(String email) {
- this.currentUserEmail = email;
+
+ // ★★★ 修改这个方法的参数类型,从 String 改为 User ★★★
+ public void handleLoginSuccess(com.student.mathquiz.model.User user) {
+ // ★★★ 从 User 对象中,准确地获取并保存 email!★★★
+ this.currentUserEmail = user.getEmail();
showMainMenuView();
}
+
public void showLoginView() { switchScene("LoginView.fxml"); }
public void showRegisterView() { switchScene("RegisterView.fxml"); }
public void showMainMenuView() { switchScene("MainMenuView.fxml"); }
diff --git a/src/main/java/com/student/mathquiz/question/HighQuestionGenerator.java b/src/main/java/com/student/mathquiz/question/HighQuestionGenerator.java
index 2f0c611..e89d86f 100644
--- a/src/main/java/com/student/mathquiz/question/HighQuestionGenerator.java
+++ b/src/main/java/com/student/mathquiz/question/HighQuestionGenerator.java
@@ -3,43 +3,119 @@ package com.student.mathquiz.question;
import java.util.*;
public class HighQuestionGenerator implements QuestionGenerator {
+
private static final Random RANDOM = new Random();
- private static final String[] TRIG_FUNCTIONS = {"sin", "cos", "tan"};
- private static final Map TRIG_VALUES = new HashMap<>();
+ private static final String[] FUNCTIONS = {"sin", "cos", "tan"};
+ private static final Map TRIG_EXACT_VALUES = new HashMap<>();
+ private final Set seenQuestions = new HashSet<>();
+ // 静态初始化三角函数精确值表(特殊角)
static {
- TRIG_VALUES.put("sin30", "1/2"); TRIG_VALUES.put("sin45", "√2/2");
- TRIG_VALUES.put("sin60", "√3/2"); TRIG_VALUES.put("cos30", "√3/2");
- TRIG_VALUES.put("cos45", "√2/2"); TRIG_VALUES.put("cos60", "1/2");
- TRIG_VALUES.put("tan30", "√3/3"); TRIG_VALUES.put("tan45", "1");
- TRIG_VALUES.put("tan60", "√3");
+ // 0°
+ TRIG_EXACT_VALUES.put("sin0", "0");
+ TRIG_EXACT_VALUES.put("cos0", "1");
+ TRIG_EXACT_VALUES.put("tan0", "0");
+
+ // 15°
+ TRIG_EXACT_VALUES.put("sin15", "(√6-√2)/4");
+ TRIG_EXACT_VALUES.put("cos15", "(√6+√2)/4");
+ TRIG_EXACT_VALUES.put("tan15", "2-√3");
+
+ // 30°
+ TRIG_EXACT_VALUES.put("sin30", "1/2");
+ TRIG_EXACT_VALUES.put("cos30", "√3/2");
+ TRIG_EXACT_VALUES.put("tan30", "√3/3");
+
+ // 45°
+ TRIG_EXACT_VALUES.put("sin45", "√2/2");
+ TRIG_EXACT_VALUES.put("cos45", "√2/2");
+ TRIG_EXACT_VALUES.put("tan45", "1");
+
+ // 60°
+ TRIG_EXACT_VALUES.put("sin60", "√3/2");
+ TRIG_EXACT_VALUES.put("cos60", "1/2");
+ TRIG_EXACT_VALUES.put("tan60", "√3");
+
+ // 75°
+ TRIG_EXACT_VALUES.put("sin75", "(√6+√2)/4");
+ TRIG_EXACT_VALUES.put("cos75", "(√6-√2)/4");
+ TRIG_EXACT_VALUES.put("tan75", "2+√3");
+
+ // 90°
+ TRIG_EXACT_VALUES.put("sin90", "1");
+ TRIG_EXACT_VALUES.put("cos90", "0");
+ TRIG_EXACT_VALUES.put("tan90", null); // 无定义
+
+ // 105°
+ TRIG_EXACT_VALUES.put("sin105", "(√6+√2)/4");
+ TRIG_EXACT_VALUES.put("cos105", "-(√6-√2)/4");
+ TRIG_EXACT_VALUES.put("tan105", "-2-√3");
+
+ // 120°
+ TRIG_EXACT_VALUES.put("sin120", "√3/2");
+ TRIG_EXACT_VALUES.put("cos120", "-1/2");
+ TRIG_EXACT_VALUES.put("tan120", "-√3");
+
+ // 135°
+ TRIG_EXACT_VALUES.put("sin135", "√2/2");
+ TRIG_EXACT_VALUES.put("cos135", "-√2/2");
+ TRIG_EXACT_VALUES.put("tan135", "-1");
+
+ // 150°
+ TRIG_EXACT_VALUES.put("sin150", "1/2");
+ TRIG_EXACT_VALUES.put("cos150", "-√3/2");
+ TRIG_EXACT_VALUES.put("tan150", "-√3/3");
+
+ // 165°
+ TRIG_EXACT_VALUES.put("sin165", "(√6-√2)/4");
+ TRIG_EXACT_VALUES.put("cos165", "-(√6+√2)/4");
+ TRIG_EXACT_VALUES.put("tan165", "-2+√3");
+
+ // 180°
+ TRIG_EXACT_VALUES.put("sin180", "0");
+ TRIG_EXACT_VALUES.put("cos180", "-1");
+ TRIG_EXACT_VALUES.put("tan180", "0");
+
+ // 其他角度可以继续补充...
}
@Override
public Question generateQuestion() {
- return generateTrigQuestion();
- }
+ while (true) {
+ String func = FUNCTIONS[RANDOM.nextInt(FUNCTIONS.length)];
+ int angle = 15 * RANDOM.nextInt(201); // 0° ~ 3000°,步长15°
+ int normalizedAngle = angle % 360;
- private Question generateTrigQuestion() {
- String func = TRIG_FUNCTIONS[RANDOM.nextInt(TRIG_FUNCTIONS.length)];
- String[] angles = {"30", "45", "60"};
- String angle = angles[RANDOM.nextInt(angles.length)];
+ String key = func + normalizedAngle;
+ String correctAnswer = TRIG_EXACT_VALUES.get(key);
- String content = func + "(" + angle + "°) = ?";
- String correctAnswer = TRIG_VALUES.get(func + angle);
+ // 跳过无定义的角度(如 tan90°)
+ if (correctAnswer == null) continue;
- List options = generateOptions(correctAnswer);
- int correctIndex = shuffleOptions(options, correctAnswer);
- return new Question(content, correctAnswer, options, correctIndex);
+ String content = func + "(" + angle + "°) = ?";
+ if (seenQuestions.contains(content)) continue;
+ seenQuestions.add(content);
+
+ List options = generateOptions(correctAnswer);
+ int correctIndex = shuffleOptions(options, correctAnswer);
+
+ return new Question(content, correctAnswer, options, correctIndex);
+ }
}
+ /**
+ * 生成3个干扰项 + 1个正确答案
+ */
private List generateOptions(String correctAnswer) {
Set options = new HashSet<>();
options.add(correctAnswer);
- List allValues = new ArrayList<>(TRIG_VALUES.values());
+
+ List allValues = new ArrayList<>(TRIG_EXACT_VALUES.values());
while (options.size() < 4) {
- options.add(allValues.get(RANDOM.nextInt(allValues.size())));
+ String wrong = allValues.get(RANDOM.nextInt(allValues.size()));
+ if (wrong != null) options.add(wrong);
}
+
return new ArrayList<>(options);
}
@@ -47,4 +123,15 @@ public class HighQuestionGenerator implements QuestionGenerator {
Collections.shuffle(options);
return options.indexOf(correctAnswer);
}
-}
+
+ /**
+ * 一次性生成指定数量的题目
+ */
+ public List generateQuestions(int count) {
+ List questions = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ questions.add(generateQuestion());
+ }
+ return questions;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/student/mathquiz/question/PrimaryQuestionGenerator.java b/src/main/java/com/student/mathquiz/question/PrimaryQuestionGenerator.java
index 71ee2a8..c6cac77 100644
--- a/src/main/java/com/student/mathquiz/question/PrimaryQuestionGenerator.java
+++ b/src/main/java/com/student/mathquiz/question/PrimaryQuestionGenerator.java
@@ -8,19 +8,15 @@ public class PrimaryQuestionGenerator implements QuestionGenerator {
@Override
public Question generateQuestion() {
- int operandCount = RANDOM.nextInt(2) + 2;
+ // 操作数数量2-5个
+ int operandCount = RANDOM.nextInt(4) + 2; // nextInt(4)→0-3,+2→2-5
List operands = new ArrayList<>();
List ops = new ArrayList<>();
- for (int i = 0; i < operandCount; i++) {
- operands.add(RANDOM.nextInt(100) + 1);
- }
- for (int i = 0; i < operandCount - 1; i++) {
- ops.add(OPERATORS[RANDOM.nextInt(OPERATORS.length)]);
- }
-
- handleDivision(operands, ops);
+ // 生成合法表达式(修复后)
+ buildValidExpression(operands, ops, operandCount);
+ // 拼接题目内容
StringBuilder contentSb = new StringBuilder();
for (int i = 0; i < operandCount; i++) {
contentSb.append(operands.get(i));
@@ -29,69 +25,194 @@ public class PrimaryQuestionGenerator implements QuestionGenerator {
}
}
String content = contentSb.toString() + " = ?";
+
+ // 计算正确答案
String correctAnswer = calculateAnswer(operands, ops);
+
+ // 生成选项
List options = generateOptions(correctAnswer);
+
+ // 打乱选项
int correctIndex = shuffleOptions(options, correctAnswer);
+
return new Question(content, correctAnswer, options, correctIndex);
}
- private void handleDivision(List operands, List ops) {
- for (int i = 0; i < ops.size(); i++) {
- if (ops.get(i) == '/') {
- int divisor = operands.get(i + 1);
- if (divisor == 0) {
- divisor = 1;
- operands.set(i + 1, divisor);
+ /**
+ * 修复核心:确保operands和ops长度匹配(operands.size() = ops.size() + 1)
+ * 每次循环只添加1个运算符+1个操作数,避免重复
+ */
+ private void buildValidExpression(List operands, List ops, int operandCount) {
+ // 第一个操作数:1-50
+ int firstOperand = RANDOM.nextInt(50) + 1;
+ operands.add(firstOperand);
+
+ // 循环生成“1个运算符+1个操作数”(共operandCount-1组)
+ for (int i = 0; i < operandCount - 1; i++) {
+ int nextOperand;
+ char op;
+ // 循环重试:直到生成能让中间结果非负的组合
+ while (true) {
+ // 1. 只生成1个运算符(无重复)
+ op = OPERATORS[RANDOM.nextInt(OPERATORS.length)];
+ // 2. 计算当前中间结果(此时ops还没加新op,operands也没加新数,长度匹配)
+ int currentMidResult = tempCalculate(operands, ops);
+ // 3. 基于当前中间结果生成合法操作数
+ nextOperand = generateValidNextOperand(currentMidResult, op);
+ // 4. 临时添加运算符和操作数(此时才开始添加,确保成对)
+ ops.add(op);
+ operands.add(nextOperand);
+ // 5. 检查新的中间结果是否非负
+ int newMidResult = tempCalculate(operands, ops);
+ if (newMidResult >= 0) {
+ // 合格,保留当前组合
+ break;
+ } else {
+ // 不合格,回滚(移除刚加的运算符和操作数,长度恢复匹配)
+ ops.remove(ops.size() - 1);
+ operands.remove(operands.size() - 1);
}
- int quotient = RANDOM.nextInt(10) + 1;
- operands.set(i, divisor * quotient);
}
}
}
+ /**
+ * 临时计算中间结果(无修改,确保参数长度匹配即可)
+ */
+ private int tempCalculate(List tempOperands, List tempOps) {
+ // 防御性判断:避免极端情况下长度不匹配
+ if (tempOperands.size() != tempOps.size() + 1) {
+ return -1; // 触发重试
+ }
+
+ List nums = new ArrayList<>(tempOperands);
+ List operators = new ArrayList<>(tempOps);
+
+ // 先处理乘除
+ int i = 0;
+ while (i < operators.size()) {
+ char op = operators.get(i);
+ if (op == '*' || op == '/') {
+ int left = nums.get(i);
+ int right = nums.get(i + 1); // 现在长度匹配,不会越界
+ int result = (op == '*') ? left * right : left / right;
+ nums.set(i, result);
+ nums.remove(i + 1);
+ operators.remove(i);
+ } else {
+ i++;
+ }
+ }
+
+ // 再处理加减
+ int midResult = nums.get(0);
+ for (i = 0; i < operators.size(); i++) {
+ int next = nums.get(i + 1);
+ midResult = (operators.get(i) == '+') ? midResult + next : midResult - next;
+ }
+ return midResult;
+ }
+
+ /**
+ * 生成合法操作数
+ */
+ private int generateValidNextOperand(int currentMidResult, char op) {
+ switch (op) {
+ case '+':
+ return RANDOM.nextInt(30) + 1;
+ case '-':
+ // 基于中间结果生成,确保非负
+ return currentMidResult <= 0 ? 1 : RANDOM.nextInt(currentMidResult) + 1;
+ case '*':
+ return RANDOM.nextInt(5) + 1;
+ case '/':
+ List divisors = getDivisors(currentMidResult);
+ return divisors.get(RANDOM.nextInt(divisors.size()));
+ default:
+ return 1;
+ }
+ }
+
+ /**
+ * 获取除数
+ */
+ private List getDivisors(int num) {
+ List divisors = new ArrayList<>();
+ for (int i = 1; i <= num / 2; i++) {
+ if (num % i == 0) {
+ divisors.add(i);
+ }
+ }
+ if (divisors.isEmpty()) {
+ divisors.add(1);
+ }
+ return divisors;
+ }
+
+ /**
+ * 计算答案
+ */
private String calculateAnswer(List operands, List ops) {
- // Simplified calculation for primary school (no operator precedence)
- int result = operands.get(0);
- for (int i = 0; i < ops.size(); i++) {
- int nextOperand = operands.get(i + 1);
- switch (ops.get(i)) {
- case '+': result += nextOperand; break;
- case '-': result -= nextOperand; break;
- case '*': result *= nextOperand; break;
- case '/': result /= nextOperand; break;
+ List nums = new ArrayList<>(operands);
+ List operators = new ArrayList<>(ops);
+
+ // 先处理乘除
+ int i = 0;
+ while (i < operators.size()) {
+ char op = operators.get(i);
+ if (op == '*' || op == '/') {
+ int left = nums.get(i);
+ int right = nums.get(i + 1);
+ int result = (op == '*') ? left * right : left / right;
+ nums.set(i, result);
+ nums.remove(i + 1);
+ operators.remove(i);
+ } else {
+ i++;
}
}
- return String.valueOf(result);
+
+ // 再处理加减
+ int finalResult = nums.get(0);
+ for (i = 0; i < operators.size(); i++) {
+ int next = nums.get(i + 1);
+ switch (operators.get(i)) {
+ case '+':
+ finalResult += next;
+ break;
+ case '-':
+ finalResult -= next;
+ break;
+ }
+ }
+
+ return String.valueOf(finalResult);
}
- // 在 PrimaryQuestionGenerator.java 中
+ /**
+ * 生成选项
+ */
private List generateOptions(String correctAnswer) {
- Set options = new HashSet<>(); // 使用Set来自动处理重复问题
+ Set options = new HashSet<>();
options.add(correctAnswer);
int correctNum = Integer.parseInt(correctAnswer);
- // ★★★ 修正后的逻辑 ★★★
while (options.size() < 4) {
- int offset = RANDOM.nextInt(20) + 1; // 偏移量 1-20
- // 随机生成一个错误答案,确保它不等于正确答案
- int wrongNum = (RANDOM.nextBoolean()) ? correctNum + offset : correctNum - offset;
-
- // 确保错误答案是正数且不等于正确答案
- if (wrongNum > 0 && wrongNum != correctNum) {
- options.add(String.valueOf(wrongNum));
- } else {
- // 如果生成的不好,就再生成一个绝对不会重复的
- options.add(String.valueOf(correctNum + options.size() + 1));
+ int offset = RANDOM.nextInt(20) + 1;
+ int wrongNum = RANDOM.nextBoolean() ? correctNum + offset : correctNum - offset;
+ if (wrongNum <= 0) {
+ wrongNum = correctNum + offset + 10;
}
+ options.add(String.valueOf(wrongNum));
}
-
- List result = new ArrayList<>(options);
- return result; // 返回最终的选项列表
+ return new ArrayList<>(options);
}
-
+ /**
+ * 打乱选项
+ */
private int shuffleOptions(List options, String correctAnswer) {
Collections.shuffle(options);
return options.indexOf(correctAnswer);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/student/mathquiz/service/IUserService.java b/src/main/java/com/student/mathquiz/service/IUserService.java
index 7f88c3d..9caed76 100644
--- a/src/main/java/com/student/mathquiz/service/IUserService.java
+++ b/src/main/java/com/student/mathquiz/service/IUserService.java
@@ -1,10 +1,13 @@
package com.student.mathquiz.service;
+import com.student.mathquiz.model.User; // ★★★ 确保导入了这个类 ★★★
+
public interface IUserService {
boolean sendVerificationCode(String email);
- // ★★★ 修改 register 方法,加入 username 参数 ★★★
String register(String username, String email, String code, String password, String confirmPassword);
- // ★★★ 修改 login 方法,参数从 email 改为 identifier (标识符) ★★★
- boolean login(String identifier, String password);
+
+ // ★★★ 修改这里的返回类型 ★★★
+ User login(String identifier, String password);
+
String changePassword(String email, String oldPassword, String newPassword1, String newPassword2);
}
diff --git a/src/main/java/com/student/mathquiz/service/UserServiceImpl.java b/src/main/java/com/student/mathquiz/service/UserServiceImpl.java
index 8f1ed98..572d380 100644
--- a/src/main/java/com/student/mathquiz/service/UserServiceImpl.java
+++ b/src/main/java/com/student/mathquiz/service/UserServiceImpl.java
@@ -98,17 +98,16 @@ public class UserServiceImpl implements IUserService {
return null;
}
- // ★★★ 用这个新版本替换掉旧的 login 方法 ★★★
+
+ // 在 UserServiceImpl.java 中
@Override
- public boolean login(String identifier, String password) {
+ public User login(String identifier, String password) {
User user = null;
// 智能判断是邮箱还是用户名
if (identifier.contains("@")) {
- // 按邮箱登录
user = userDatabase.get(identifier);
} else {
- // 按用户名登录,需要遍历查找
for (User u : userDatabase.values()) {
if (u.getUsername().equalsIgnoreCase(identifier)) {
user = u;
@@ -117,17 +116,21 @@ public class UserServiceImpl implements IUserService {
}
}
- // 如果找不到用户,直接返回失败
if (user == null) {
- return false;
+ return null; // 找不到用户,返回 null
}
- // 找到用户后,验证密码(逻辑和以前一样)
+ // 验证密码
String encryptedInputPwd = Base64.getEncoder().encodeToString(password.getBytes());
- return user.getEncryptedPwd().equals(encryptedInputPwd);
+ if (user.getEncryptedPwd().equals(encryptedInputPwd)) {
+ return user; // ★★★ 密码正确,返回整个 User 对象! ★★★
+ } else {
+ return null; // 密码错误,返回 null
+ }
}
+
@Override
public String changePassword(String email, String oldPassword, String newPassword1, String newPassword2) {
User user = userDatabase.get(email);
diff --git a/src/main/java/com/student/mathquiz/view/LoginViewController.java b/src/main/java/com/student/mathquiz/view/LoginViewController.java
index 9cd06ba..dd757ba 100644
--- a/src/main/java/com/student/mathquiz/view/LoginViewController.java
+++ b/src/main/java/com/student/mathquiz/view/LoginViewController.java
@@ -20,17 +20,24 @@ public class LoginViewController {
this.userService = mainApp.getUserService();
}
+ // 在 LoginViewController.java 中
@FXML
private void handleLogin() {
- String email = emailField.getText();
+ String identifier = emailField.getText(); // 我们把变量名改得更准确
String password = passwordField.getText();
- if (userService.login(email, password)) {
- mainApp.handleLoginSuccess(email);
+
+ // ★★★ 调用新的 login 方法,它会返回一个 User 对象 ★★★
+ com.student.mathquiz.model.User loggedInUser = userService.login(identifier, password);
+
+ if (loggedInUser != null) { // 如果返回的不是 null,说明登录成功
+ // ★★★ 把完整的 User 对象传给 MainApp ★★★
+ mainApp.handleLoginSuccess(loggedInUser);
} else {
- statusLabel.setText("邮箱或密码错误!");
+ statusLabel.setText("用户名/邮箱或密码错误!");
}
}
+
@FXML
private void handleGoToRegister() {
mainApp.showRegisterView();
diff --git a/user_data/users.json b/user_data/users.json
index 9840f3f..dc6dd59 100644
--- a/user_data/users.json
+++ b/user_data/users.json
@@ -2,7 +2,7 @@
{
"username": "清清",
"email": "lyqqqq1214@163.com",
- "encryptedPwd": "NjY2NjY2bEw\u003d",
+ "encryptedPwd": "MTIzNDU2bEw\u003d",
"userType": "PRIMARY"
}
-]
+]
\ No newline at end of file