diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml
new file mode 100644
index 0000000..4ea72a9
--- /dev/null
+++ b/.idea/copilot.data.migration.agent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.ask.xml b/.idea/copilot.data.migration.ask.xml
new file mode 100644
index 0000000..7ef04e2
--- /dev/null
+++ b/.idea/copilot.data.migration.ask.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml
new file mode 100644
index 0000000..1f2ea11
--- /dev/null
+++ b/.idea/copilot.data.migration.ask2agent.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml
new file mode 100644
index 0000000..8648f94
--- /dev/null
+++ b/.idea/copilot.data.migration.edit.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/lib.xml b/.idea/libraries/lib.xml
new file mode 100644
index 0000000..7f1aa7e
--- /dev/null
+++ b/.idea/libraries/lib.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..31e1ebc
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..831f033
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/data/sqf/2025-10-02-11-29-36.txt b/data/sqf/2025-10-02-11-29-36.txt
deleted file mode 100644
index 0f1fc7c..0000000
--- a/data/sqf/2025-10-02-11-29-36.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-1. 37 + 35
-
-2. 82 * 28
-
-3. (16 / 56) + 42
-
-4. 6 * 52 + 35
-
-5. (99 + 26) + 37
-
-6. 40 * 63
-
-7. 52 - 61 - 16 - 70 * 51
-
-8. 65 + 83
-
-9. 84 / 64
-
-10. 24 - 47 + 21 + 1 - 33
-
diff --git a/data/users.cfg b/data/users.cfg
index 7842a5d..a790fcf 100644
--- a/data/users.cfg
+++ b/data/users.cfg
@@ -1,10 +1,12 @@
王五3 123 HIGH wangwu3@example.com
王五2 123 HIGH wangwu2@example.com
+123 Aa123456 PRIMARY 2571400460@qq.com
王五1 123 HIGH wangwu1@example.com
+scb Aa123456 PRIMARY 3110858122@qq.com
张三3 123 PRIMARY zhangsan3@example.com
张三1 123 PRIMARY zhangsan1@example.com
张三2 123 PRIMARY zhangsan2@example.com
李四3 123 MIDDLE lishi3@example.com
-sqf Aa123456 PRIMARY sqf090815@hnu.edu.cn
+sqf Qq123456 PRIMARY sqf090815@hnu.edu.cn
李四1 123 MIDDLE lishi1@example.com
李四2 123 MIDDLE lishi2@example.com
diff --git a/mathStudyApp.iml b/mathStudyApp.iml
index 8a266dd..fb8e866 100644
--- a/mathStudyApp.iml
+++ b/mathStudyApp.iml
@@ -8,7 +8,5 @@
-
-
\ No newline at end of file
diff --git a/out/production/mathStudyApp/Main.class b/out/production/mathStudyApp/Main.class
index 2880209..50bd932 100644
Binary files a/out/production/mathStudyApp/Main.class and b/out/production/mathStudyApp/Main.class differ
diff --git a/out/production/mathStudyApp/controller/FunctionController.class b/out/production/mathStudyApp/controller/FunctionController.class
index 099f702..621af2e 100644
Binary files a/out/production/mathStudyApp/controller/FunctionController.class and b/out/production/mathStudyApp/controller/FunctionController.class differ
diff --git a/out/production/mathStudyApp/model/Create.class b/out/production/mathStudyApp/model/Create.class
index d271ca1..722d227 100644
Binary files a/out/production/mathStudyApp/model/Create.class and b/out/production/mathStudyApp/model/Create.class differ
diff --git a/out/production/mathStudyApp/model/ExpressionEvaluator.class b/out/production/mathStudyApp/model/ExpressionEvaluator.class
index f896f4a..ab78d34 100644
Binary files a/out/production/mathStudyApp/model/ExpressionEvaluator.class and b/out/production/mathStudyApp/model/ExpressionEvaluator.class differ
diff --git a/out/production/mathStudyApp/model/Generator$1.class b/out/production/mathStudyApp/model/Generator$1.class
index d64e0b3..2f8516d 100644
Binary files a/out/production/mathStudyApp/model/Generator$1.class and b/out/production/mathStudyApp/model/Generator$1.class differ
diff --git a/out/production/mathStudyApp/model/Generator.class b/out/production/mathStudyApp/model/Generator.class
index c58172c..58ca828 100644
Binary files a/out/production/mathStudyApp/model/Generator.class and b/out/production/mathStudyApp/model/Generator.class differ
diff --git a/out/production/mathStudyApp/model/LanguageSwitch$1.class b/out/production/mathStudyApp/model/LanguageSwitch$1.class
index 3891474..f9b2a2b 100644
Binary files a/out/production/mathStudyApp/model/LanguageSwitch$1.class and b/out/production/mathStudyApp/model/LanguageSwitch$1.class differ
diff --git a/out/production/mathStudyApp/model/LanguageSwitch.class b/out/production/mathStudyApp/model/LanguageSwitch.class
index b337ced..c3c3ca6 100644
Binary files a/out/production/mathStudyApp/model/LanguageSwitch.class and b/out/production/mathStudyApp/model/LanguageSwitch.class differ
diff --git a/out/production/mathStudyApp/model/LoadFile.class b/out/production/mathStudyApp/model/LoadFile.class
index b4789e2..41b059e 100644
Binary files a/out/production/mathStudyApp/model/LoadFile.class and b/out/production/mathStudyApp/model/LoadFile.class differ
diff --git a/out/production/mathStudyApp/model/Login$Account.class b/out/production/mathStudyApp/model/Login$Account.class
index f9a637f..aa24861 100644
Binary files a/out/production/mathStudyApp/model/Login$Account.class and b/out/production/mathStudyApp/model/Login$Account.class differ
diff --git a/out/production/mathStudyApp/model/Login$Level.class b/out/production/mathStudyApp/model/Login$Level.class
index 2ad13ea..a35c914 100644
Binary files a/out/production/mathStudyApp/model/Login$Level.class and b/out/production/mathStudyApp/model/Login$Level.class differ
diff --git a/out/production/mathStudyApp/model/Login.class b/out/production/mathStudyApp/model/Login.class
index 790a1f9..e914c85 100644
Binary files a/out/production/mathStudyApp/model/Login.class and b/out/production/mathStudyApp/model/Login.class differ
diff --git a/out/production/mathStudyApp/model/Paper.class b/out/production/mathStudyApp/model/Paper.class
index 8e5f952..ee32776 100644
Binary files a/out/production/mathStudyApp/model/Paper.class and b/out/production/mathStudyApp/model/Paper.class differ
diff --git a/out/production/mathStudyApp/model/Save.class b/out/production/mathStudyApp/model/Save.class
index c21ca61..e08888b 100644
Binary files a/out/production/mathStudyApp/model/Save.class and b/out/production/mathStudyApp/model/Save.class differ
diff --git a/out/production/mathStudyApp/view/ExamFrame.class b/out/production/mathStudyApp/view/ExamFrame.class
index dadb467..e37f665 100644
Binary files a/out/production/mathStudyApp/view/ExamFrame.class and b/out/production/mathStudyApp/view/ExamFrame.class differ
diff --git a/out/production/mathStudyApp/view/ExamSetupFrame.class b/out/production/mathStudyApp/view/ExamSetupFrame.class
index c710d40..2850903 100644
Binary files a/out/production/mathStudyApp/view/ExamSetupFrame.class and b/out/production/mathStudyApp/view/ExamSetupFrame.class differ
diff --git a/out/production/mathStudyApp/view/LoginFrame.class b/out/production/mathStudyApp/view/LoginFrame.class
index 02d6b24..b8a1070 100644
Binary files a/out/production/mathStudyApp/view/LoginFrame.class and b/out/production/mathStudyApp/view/LoginFrame.class differ
diff --git a/out/production/mathStudyApp/view/MainMenuFrame$ChangePasswordDialog.class b/out/production/mathStudyApp/view/MainMenuFrame$ChangePasswordDialog.class
index f72d97f..1147586 100644
Binary files a/out/production/mathStudyApp/view/MainMenuFrame$ChangePasswordDialog.class and b/out/production/mathStudyApp/view/MainMenuFrame$ChangePasswordDialog.class differ
diff --git a/out/production/mathStudyApp/view/MainMenuFrame.class b/out/production/mathStudyApp/view/MainMenuFrame.class
index 4f406ae..de0d464 100644
Binary files a/out/production/mathStudyApp/view/MainMenuFrame.class and b/out/production/mathStudyApp/view/MainMenuFrame.class differ
diff --git a/out/production/mathStudyApp/view/RegisterFrame$1.class b/out/production/mathStudyApp/view/RegisterFrame$1.class
index dc8496c..9dd1b2a 100644
Binary files a/out/production/mathStudyApp/view/RegisterFrame$1.class and b/out/production/mathStudyApp/view/RegisterFrame$1.class differ
diff --git a/out/production/mathStudyApp/view/RegisterFrame.class b/out/production/mathStudyApp/view/RegisterFrame.class
index 65dca4f..eecd276 100644
Binary files a/out/production/mathStudyApp/view/RegisterFrame.class and b/out/production/mathStudyApp/view/RegisterFrame.class differ
diff --git a/out/production/mathStudyApp/view/ResultFrame.class b/out/production/mathStudyApp/view/ResultFrame.class
index 569f425..bc6b282 100644
Binary files a/out/production/mathStudyApp/view/ResultFrame.class and b/out/production/mathStudyApp/view/ResultFrame.class differ
diff --git a/src/Main.java b/src/Main.java
index cdc27b5..07d6d26 100644
--- a/src/Main.java
+++ b/src/Main.java
@@ -1,4 +1,5 @@
import javax.swing.SwingUtilities;
+
import view.LoginFrame;
/**
diff --git a/src/controller/AssignController.java b/src/controller/AssignController.java
deleted file mode 100644
index b31851c..0000000
--- a/src/controller/AssignController.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package controller;
-
-import model.Login;
-import model.LanguageSwitch;
-import view.ExamSetupFrame;
-
-import javax.swing.JOptionPane;
-
-/**
- * 登录后流程控制:负责从登录跳转到出题设置
- */
-public class AssignController {
- public void loginSuccess(Login.Account user) {
- JOptionPane.showMessageDialog(null,
- "登录成功。当前选择为 " + LanguageSwitch.levelToChinese(user.level) + " 出题");
- new ExamSetupFrame(user);
- }
-}
diff --git a/src/controller/FunctionController.java b/src/controller/FunctionController.java
index 21f9192..ef07cf9 100644
--- a/src/controller/FunctionController.java
+++ b/src/controller/FunctionController.java
@@ -1,16 +1,17 @@
package controller;
+import javax.swing.JOptionPane;
+
import model.Create;
import model.Login;
import model.Paper;
import view.ExamFrame;
-import javax.swing.JOptionPane;
-
/**
* 题目生成控制器:接收题数与用户信息,调用 Create 生成 Paper 并打开 ExamFrame
*/
public class FunctionController {
+
private Login.Account user;
public FunctionController(Login.Account user) {
diff --git a/src/model/Create.java b/src/model/Create.java
index 85cf2b7..d27a77a 100644
--- a/src/model/Create.java
+++ b/src/model/Create.java
@@ -1,44 +1,23 @@
package model;
-import java.util.*;
-import javax.script.*;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import javax.script.ScriptException;
/**
* 负责生成试卷并创建选择题选项(四选一)
*/
public class Create {
- // 把数学表达式转换为 JS 可执行表达式的简单转换器
- private static String toJsExpr(String expr) {
- String s = expr;
- s = s.replaceAll("\\^2", ".pow2"); // 临时占位
- s = s.replaceAll("sqrt\\(", "Math.sqrt(");
- s = s.replaceAll("sin\\(", "Math.sin(Math.toRadians(");
- s = s.replaceAll("cos\\(", "Math.cos(Math.toRadians(");
- s = s.replaceAll("tan\\(", "Math.tan(Math.toRadians(");
- // 修复 sin(Math.toRadians(x) 末尾多一层 ) -> we need to close paren
- // Simple approach: for each "Math.sin(Math.toRadians(", there will be following number or parenthesis, we close later with "))"
- // Replace our .pow2 placeholder into Math.pow(x,2)
- // Handle pow2: we earlier converted x^2 -> x.pow2, so replace (\d+|...) .pow2 to Math.pow(that,2)
- // For simplicity, replace pattern "([0-9)]+)\\.pow2" with "Math.pow($1,2)" — approximate
- s = s.replaceAll("(\\d+)\\.pow2", "Math.pow($1,2)");
- // If there are parentheses before .pow2: ") .pow2" unlikely; accept some limitations.
- // For sin/cos/tan we changed 'sin(' -> 'Math.sin(Math.toRadians(' so must add two closing ) after the argument.
- // To keep it simple, replace "Math.sin(Math.toRadians(" -> "Math.sin(Math.toRadians(" and later we will ensure parentheses in evaluation by appending "))" when needed.
- // We'll also replace any leftover "^2" directly
- s = s.replaceAll("\\^2", "");
- // Finally, replace any multiple spaces
- s = s.replaceAll("\\s+", "");
- return s;
- }
-
- // 评估表达式数值(double),若失败抛异常
// 评估表达式数值(double),若失败抛异常
private static double evalExpression(String expr) throws ScriptException {
return ExpressionEvaluator.evaluate(expr);
}
-
/**
* 生成试卷:返回 Paper 对象(questions, options, correctIndex)
*/
@@ -78,7 +57,6 @@ public class Create {
// 判断是否为整数答案
boolean isIntAnswer = Math.abs(correctVal - Math.round(correctVal)) < 1e-6;
-
// 产生三个干扰值
Set optsSet = new LinkedHashSet<>();
optsSet.add(correctStr);
@@ -90,7 +68,9 @@ public class Create {
if (isIntAnswer) {
// 整数题:干扰项也是整数(偏差 ±1~±10)
delta = r.nextInt(10) + 1;
- if (r.nextBoolean()) delta = -delta;
+ if (r.nextBoolean()) {
+ delta = -delta;
+ }
} else {
// 小数题:随机偏差比例
delta = (Math.abs(correctVal) < 1e-6)
@@ -122,7 +102,9 @@ public class Create {
try {
// 为保存,我们保存原题干列表(文本)
java.util.List raw = new ArrayList<>();
- for (String s : paper) raw.add(s);
+ for (String s : paper) {
+ raw.add(s);
+ }
Save.savePaper(user.username, raw);
} catch (RuntimeException re) {
// 抛给 UI 处理(但仍返回 Paper)
@@ -143,13 +125,17 @@ public class Create {
}
private static int findIndex(String[] arr, String target) {
- for (int i = 0; i < arr.length; i++) if (arr[i].equals(target)) return i;
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i].equals(target)) {
+ return i;
+ }
+ }
return 0;
}
private static String formatNumber(double v) {
if (Math.abs(v - Math.round(v)) < 1e-6) {
- return String.valueOf((long)Math.round(v));
+ return String.valueOf((long) Math.round(v));
} else {
return String.format("%.3f", v);
}
diff --git a/src/model/ExpressionEvaluator.java b/src/model/ExpressionEvaluator.java
index db5a20a..0fc214d 100644
--- a/src/model/ExpressionEvaluator.java
+++ b/src/model/ExpressionEvaluator.java
@@ -1,9 +1,17 @@
package model;
-import java.util.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.StringTokenizer;
+
public class ExpressionEvaluator {
private static final Map PRECEDENCE = new HashMap<>();
+
static {
PRECEDENCE.put("+", 1);
PRECEDENCE.put("-", 1);
@@ -26,12 +34,12 @@ public class ExpressionEvaluator {
private static List toRPN(String expr) {
List output = new ArrayList<>();
Stack stack = new Stack<>();
-
- // 正则拆分 token(数字、函数、符号、括号)
StringTokenizer tokenizer = new StringTokenizer(expr, "+-*/^() ", true);
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim();
- if (token.isEmpty()) continue;
+ if (token.isEmpty()) {
+ continue;
+ }
if (token.matches("[0-9.]+")) { // 数字
output.add(token);
@@ -52,7 +60,6 @@ public class ExpressionEvaluator {
if (!stack.isEmpty() && stack.peek().equals("(")) {
stack.pop();
}
- // 如果栈顶是函数,弹出函数
if (!stack.isEmpty() && stack.peek().matches("[a-zA-Z]+")) {
output.add(stack.pop());
}
@@ -76,20 +83,39 @@ public class ExpressionEvaluator {
double b = stack.pop();
double a = stack.pop();
switch (token) {
- case "+": stack.push(a + b); break;
- case "-": stack.push(a - b); break;
- case "*": stack.push(a * b); break;
- case "/": stack.push(a / b); break;
- case "^": stack.push(Math.pow(a, b)); break;
+ case "+":
+ stack.push(a + b);
+ break;
+ case "-":
+ stack.push(a - b);
+ break;
+ case "*":
+ stack.push(a * b);
+ break;
+ case "/":
+ stack.push(a / b);
+ break;
+ case "^":
+ stack.push(Math.pow(a, b));
+ break;
}
} else { // 函数
double a = stack.pop();
switch (token.toLowerCase()) {
- case "sin": stack.push(Math.sin(Math.toRadians(a))); break;
- case "cos": stack.push(Math.cos(Math.toRadians(a))); break;
- case "tan": stack.push(Math.tan(Math.toRadians(a))); break;
- case "sqrt": stack.push(Math.sqrt(a)); break;
- default: throw new RuntimeException("未知函数: " + token);
+ case "sin":
+ stack.push(Math.sin(Math.toRadians(a)));
+ break;
+ case "cos":
+ stack.push(Math.cos(Math.toRadians(a)));
+ break;
+ case "tan":
+ stack.push(Math.tan(Math.toRadians(a)));
+ break;
+ case "sqrt":
+ stack.push(Math.sqrt(a));
+ break;
+ default:
+ throw new RuntimeException("未知函数: " + token);
}
}
}
diff --git a/src/model/Generator.java b/src/model/Generator.java
index b26584a..da2f306 100644
--- a/src/model/Generator.java
+++ b/src/model/Generator.java
@@ -1,6 +1,11 @@
package model;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -31,7 +36,8 @@ public class Generator extends QuestionGenerator {
}
}
if (generated.size() < n) {
- System.out.println("注意:无法生成足够的不重复题目,已生成 " + generated.size() + " 道题(请求 " + n + " 道)");
+ System.out.println(
+ "注意:无法生成足够的不重复题目,已生成 " + generated.size() + " 道题(请求 " + n + " 道)");
}
return new ArrayList<>(generated);
}
@@ -59,7 +65,8 @@ public class Generator extends QuestionGenerator {
boolean useParens = RAND.nextBoolean();
if (useParens && operands >= 3 && RAND.nextBoolean()) {
sb.append("(");
- sb.append(randInt(1, 100)).append(" ").append(randomChoice(ops)).append(" ").append(randInt(1, 100));
+ sb.append(randInt(1, 100)).append(" ").append(randomChoice(ops)).append(" ")
+ .append(randInt(1, 100));
sb.append(")");
for (int i = 2; i < operands; i++) {
sb.append(" ").append(randomChoice(ops)).append(" ").append(randInt(1, 100));
@@ -75,8 +82,11 @@ public class Generator extends QuestionGenerator {
public String genMiddle(int operands) {
String expr = genPrimary(operands);
- if (RAND.nextBoolean()) expr = applySquare(expr);
- else expr = applySqrt(expr);
+ if (RAND.nextBoolean()) {
+ expr = applySquare(expr);
+ } else {
+ expr = applySqrt(expr);
+ }
return expr;
}
@@ -88,7 +98,9 @@ public class Generator extends QuestionGenerator {
public String applySquare(String expr) {
List spans = findNumberSpans(expr);
- if (spans.isEmpty()) return expr + "^2";
+ if (spans.isEmpty()) {
+ return expr + "^2";
+ }
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
@@ -98,7 +110,9 @@ public class Generator extends QuestionGenerator {
public String applySqrt(String expr) {
List spans = findNumberSpans(expr);
- if (spans.isEmpty()) return "sqrt(" + expr + ")";
+ if (spans.isEmpty()) {
+ return "sqrt(" + expr + ")";
+ }
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
@@ -109,7 +123,9 @@ public class Generator extends QuestionGenerator {
public String applyTrig(String expr) {
List spans = findNumberSpans(expr);
String func = randomChoice(Arrays.asList("sin", "cos", "tan"));
- if (spans.isEmpty()) return func + "(" + expr + ")";
+ if (spans.isEmpty()) {
+ return func + "(" + expr + ")";
+ }
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
@@ -124,10 +140,14 @@ public class Generator extends QuestionGenerator {
while (i < n) {
if (Character.isDigit(chs[i])) {
int j = i;
- while (j < n && (Character.isDigit(chs[j]))) j++;
+ while (j < n && (Character.isDigit(chs[j]))) {
+ j++;
+ }
spans.add(new int[]{i, j});
i = j;
- } else i++;
+ } else {
+ i++;
+ }
}
return spans;
}
diff --git a/src/model/LanguageSwitch.java b/src/model/LanguageSwitch.java
index 50bb180..a3c0501 100644
--- a/src/model/LanguageSwitch.java
+++ b/src/model/LanguageSwitch.java
@@ -1,6 +1,7 @@
package model;
public class LanguageSwitch {
+
public static String levelToChinese(Login.Level l) {
return switch (l) {
case PRIMARY -> "小学";
diff --git a/src/model/LoadFile.java b/src/model/LoadFile.java
index be1c88e..cb8a7a5 100644
--- a/src/model/LoadFile.java
+++ b/src/model/LoadFile.java
@@ -1,12 +1,16 @@
package model;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-import java.nio.file.*;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
import java.util.stream.Collectors;
+
/**
* 读取用户文件夹下已有题目的所有题目文本(用于查重)
*/
@@ -24,7 +28,9 @@ public class LoadFile {
StringBuilder cur = new StringBuilder();
for (String line : lines) {
if (line.matches("^\\s*\\d+\\..*")) {
- if (!cur.isEmpty()) all.add(cur.toString().trim());
+ if (!cur.isEmpty()) {
+ all.add(cur.toString().trim());
+ }
cur.setLength(0);
cur.append(line.replaceFirst("^\\s*\\d+\\.", "").trim());
} else {
@@ -34,16 +40,21 @@ public class LoadFile {
cur.setLength(0);
}
} else {
- if (!cur.isEmpty()) cur.append(" ");
+ if (!cur.isEmpty()) {
+ cur.append(" ");
+ }
cur.append(line.trim());
}
}
}
- if (!cur.isEmpty()) all.add(cur.toString().trim());
+ if (!cur.isEmpty()) {
+ all.add(cur.toString().trim());
+ }
}
} catch (IOException e) {
throw new RuntimeException("读取题目文件失败:" + e.getMessage(), e);
}
- return all.stream().map(String::trim).filter(s -> !s.isEmpty()).distinct().collect(Collectors.toList());
+ return all.stream().map(String::trim).filter(s -> !s.isEmpty()).distinct()
+ .collect(Collectors.toList());
}
}
diff --git a/src/model/Login.java b/src/model/Login.java
index 25a0269..54192f2 100644
--- a/src/model/Login.java
+++ b/src/model/Login.java
@@ -1,10 +1,14 @@
package model;
-import java.util.Map;
-import java.util.HashMap;
-import java.io.*;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.Serializable;
import java.nio.charset.StandardCharsets;
-import java.nio.file.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
/**
@@ -61,17 +65,30 @@ public class Login {
}
// 注册(GUI 调用),若用户名已存在返回 false
+ // 注册(GUI 调用),若用户名或邮箱已存在返回 false
public static synchronized boolean register(String username, String password, Level level,
String email) {
+ // 检查用户名是否重复
if (accounts.containsKey(username)) {
return false;
}
+
+ // 检查邮箱是否重复
+ for (Account existing : accounts.values()) {
+ if (existing.email != null && existing.email.equalsIgnoreCase(email)) {
+ // 邮箱重复,不区分大小写
+ return false;
+ }
+ }
+
+ // 都不重复,则注册成功
Account acc = new Account(username, password, level, email);
accounts.put(username, acc);
persistToFile();
return true;
}
+
// 修改密码(需提供原密码),返回是否成功
public static synchronized boolean changePassword(String username, String oldPwd, String newPwd) {
Account acc = accounts.get(username);
diff --git a/src/model/Paper.java b/src/model/Paper.java
index dc09a27..f2e73a4 100644
--- a/src/model/Paper.java
+++ b/src/model/Paper.java
@@ -6,6 +6,7 @@ import java.util.List;
* 试卷封装:每一道题为字符串(题干),每题有四个选项(字符串),且标记正确选项索引
*/
public class Paper {
+
public final List questions;
public final List options; // 每题 options[i] 长度为4
public final int[] correctIndex; // 每题正确选项下标 0..3
diff --git a/src/model/Save.java b/src/model/Save.java
index 23f2a9a..8b5cd7d 100644
--- a/src/model/Save.java
+++ b/src/model/Save.java
@@ -2,17 +2,22 @@ package model;
import java.io.BufferedWriter;
import java.io.IOException;
-import java.nio.file.*;
+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.Date;
import java.util.List;
-import java.nio.charset.StandardCharsets;
public class Save {
+
public static String savePaper(String username, List paper) {
Path userDir = Paths.get("data", username);
try {
- if (!Files.exists(userDir)) Files.createDirectories(userDir);
+ if (!Files.exists(userDir)) {
+ Files.createDirectories(userDir);
+ }
} catch (IOException e) {
throw new RuntimeException("无法创建用户文件夹:" + e.getMessage(), e);
}
diff --git a/src/view/ExamFrame.java b/src/view/ExamFrame.java
index 424b579..eda873e 100644
--- a/src/view/ExamFrame.java
+++ b/src/view/ExamFrame.java
@@ -1,7 +1,20 @@
package view;
-import javax.swing.*;
-import java.awt.*;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.ButtonGroup;
+import javax.swing.BorderFactory;
+import javax.swing.SwingConstants;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+
import model.Paper;
import model.Login;
@@ -9,6 +22,7 @@ import model.Login;
* 答题界面(选择题,四选一)
*/
public class ExamFrame extends JFrame {
+
private Paper paper;
private Login.Account user;
private int index = 0;
@@ -35,12 +49,12 @@ public class ExamFrame extends JFrame {
qLabel = new JLabel("", SwingConstants.LEFT);
qLabel.setFont(new Font("Serif", Font.PLAIN, 16));
JPanel top = new JPanel(new BorderLayout());
- top.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+ top.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
top.add(qLabel, BorderLayout.CENTER);
- centerPanel = new JPanel(new GridLayout(4,1,6,6));
+ centerPanel = new JPanel(new GridLayout(4, 1, 6, 6));
group = new ButtonGroup();
- for (int i=0;i<4;i++){
+ for (int i = 0; i < 4; i++) {
radioBtns[i] = new JRadioButton();
group.add(radioBtns[i]);
centerPanel.add(radioBtns[i]);
@@ -63,33 +77,43 @@ public class ExamFrame extends JFrame {
setVisible(true);
}
- /** 显示一道题 */
+ /**
+ * 显示一道题
+ */
private void showQuestion() {
if (index >= paper.size()) {
showSubmitPage();
return;
}
String q = paper.questions.get(index);
- qLabel.setText("第 " + (index+1) + " 题: " + q + "");
+ qLabel.setText("第 " + (index + 1) + " 题: " + q + "");
String[] opts = paper.options.get(index);
- for (int i=0;i<4;i++) {
+ for (int i = 0; i < 4; i++) {
radioBtns[i].setText(opts[i]);
}
// 清除上一次选择
group.clearSelection();
}
- /** 提交当前题 */
+ /**
+ * 提交当前题
+ */
private void submitAnswer() {
int selected = -1;
- for (int i=0;i<4;i++) if (radioBtns[i].isSelected()) selected = i;
+ for (int i = 0; i < 4; i++) {
+ if (radioBtns[i].isSelected()) {
+ selected = i;
+ }
+ }
if (selected == -1) {
JOptionPane.showMessageDialog(this, "请选择一个选项后再提交");
return;
}
// 判断对错
- if (selected == paper.correctIndex[index]) score++;
+ if (selected == paper.correctIndex[index]) {
+ score++;
+ }
index++;
if (index < paper.size()) {
@@ -100,7 +124,9 @@ public class ExamFrame extends JFrame {
}
}
- /** 做完所有题目后显示确认提交界面 */
+ /**
+ * 做完所有题目后显示确认提交界面
+ */
private void showSubmitPage() {
// 移除中间题目区域和按钮
getContentPane().remove(centerPanel);
@@ -134,10 +160,12 @@ public class ExamFrame extends JFrame {
repaint();
}
- /** 完成考试 → 进入成绩界面 */
+ /**
+ * 完成考试 → 进入成绩界面
+ */
private void finishExam() {
int total = paper.size();
- double percent = total==0 ? 0 : (100.0 * score / total);
+ double percent = total == 0 ? 0 : (100.0 * score / total);
dispose();
new ResultFrame(user, score, total, percent);
}
diff --git a/src/view/ExamSetupFrame.java b/src/view/ExamSetupFrame.java
index a67fcf4..0da4e57 100644
--- a/src/view/ExamSetupFrame.java
+++ b/src/view/ExamSetupFrame.java
@@ -1,7 +1,16 @@
package view;
-import javax.swing.*;
-import java.awt.*;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.BorderFactory;
+
+import java.awt.GridLayout;
+
import controller.FunctionController;
import model.Login;
import model.LanguageSwitch;
@@ -10,6 +19,7 @@ import model.LanguageSwitch;
* 出题设置:选择/切换难度(显示当前账户默认难度),输入题数(10-30)
*/
public class ExamSetupFrame extends JFrame {
+
private JTextField numberField;
private JComboBox levelBox;
private FunctionController controller;
@@ -24,10 +34,10 @@ public class ExamSetupFrame extends JFrame {
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- JPanel p = new JPanel(new GridLayout(4,2,8,8));
- p.setBorder(BorderFactory.createEmptyBorder(8,8,8,8));
+ JPanel p = new JPanel(new GridLayout(4, 2, 8, 8));
+ p.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
p.add(new JLabel("当前难度:"));
- levelBox = new JComboBox<>(new String[]{"小学","初中","高中"});
+ levelBox = new JComboBox<>(new String[]{"小学", "初中", "高中"});
levelBox.setSelectedItem(LanguageSwitch.levelToChinese(user.level));
p.add(levelBox);
@@ -56,7 +66,10 @@ public class ExamSetupFrame extends JFrame {
}
});
- backBtn.addActionListener(e -> { dispose(); new LoginFrame(); });
+ backBtn.addActionListener(e -> {
+ dispose();
+ new LoginFrame();
+ });
setVisible(true);
}
diff --git a/src/view/LoginFrame.java b/src/view/LoginFrame.java
index 01cea47..bda2dbb 100644
--- a/src/view/LoginFrame.java
+++ b/src/view/LoginFrame.java
@@ -1,9 +1,17 @@
package view;
-import javax.swing.*;
-import java.awt.*;
+import java.awt.GridLayout;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.JPasswordField;
+import javax.swing.BorderFactory;
+
import model.Login;
-import controller.AssignController;
/**
* 登录窗口
@@ -20,7 +28,7 @@ public class LoginFrame extends JFrame {
setLocationRelativeTo(null);
JPanel p = new JPanel(new GridLayout(4, 2, 8, 8));
- p.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+ p.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
p.add(new JLabel("用户名:"));
usernameField = new JTextField();
p.add(usernameField);
@@ -44,7 +52,8 @@ public class LoginFrame extends JFrame {
String u = usernameField.getText().trim();
String pwd = new String(passwordField.getPassword()).trim();
if (u.isEmpty() || pwd.isEmpty()) {
- JOptionPane.showMessageDialog(this, "请输入用户名和密码", "提示", JOptionPane.INFORMATION_MESSAGE);
+ JOptionPane.showMessageDialog(this, "请输入用户名和密码", "提示",
+ JOptionPane.INFORMATION_MESSAGE);
return;
}
Login.Account acc = Login.login(u, pwd);
@@ -52,7 +61,8 @@ public class LoginFrame extends JFrame {
dispose();
new MainMenuFrame(acc); // 登录后先进入主菜单
} else {
- JOptionPane.showMessageDialog(this, "用户名或密码错误", "登录失败", JOptionPane.ERROR_MESSAGE);
+ JOptionPane.showMessageDialog(this, "用户名或密码错误", "登录失败",
+ JOptionPane.ERROR_MESSAGE);
}
});
diff --git a/src/view/MainMenuFrame.java b/src/view/MainMenuFrame.java
index f80f3ac..7fdc377 100644
--- a/src/view/MainMenuFrame.java
+++ b/src/view/MainMenuFrame.java
@@ -1,56 +1,94 @@
package view;
-import javax.swing.*;
-import java.awt.*;
+import java.awt.GridLayout;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.BorderFactory;
+
import model.Login;
+
/**
- * 登录成功后的主菜单(可修改密码、开始出题、登出)
- * 现在由 ExamSetupFrame 直接替代,但保留此类可拓展
+ * 登录成功后的主菜单(可修改密码、开始出题、登出) 现在由 ExamSetupFrame 直接替代,但保留此类可拓展
*/
public class MainMenuFrame extends JFrame {
+
public MainMenuFrame(Login.Account user) {
setTitle("主菜单 - " + user.username);
- setSize(400,200);
+ setSize(400, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- JPanel p = new JPanel(new GridLayout(3,1,10,10));
+ JPanel p = new JPanel(new GridLayout(3, 1, 10, 10));
JButton changePwd = new JButton("修改密码");
JButton start = new JButton("出题设置");
JButton logout = new JButton("退出登录");
- p.add(start); p.add(changePwd); p.add(logout);
+ p.add(start);
+ p.add(changePwd);
+ p.add(logout);
add(p);
- start.addActionListener(e -> { new ExamSetupFrame(user); dispose(); });
+ start.addActionListener(e -> {
+ new ExamSetupFrame(user);
+ dispose();
+ });
changePwd.addActionListener(e -> new ChangePasswordDialog(this, user));
- logout.addActionListener(e -> { dispose(); new LoginFrame(); });
+ logout.addActionListener(e -> {
+ dispose();
+ new LoginFrame();
+ });
setVisible(true);
}
// 内部类:修改密码对话框
static class ChangePasswordDialog extends JDialog {
+
public ChangePasswordDialog(JFrame owner, Login.Account user) {
super(owner, "修改密码", true);
- setSize(350,200);
+ setSize(350, 200);
setLocationRelativeTo(owner);
- JPanel p = new JPanel(new GridLayout(4,2,6,6));
- p.setBorder(BorderFactory.createEmptyBorder(6,6,6,6));
- p.add(new JLabel("旧密码:")); JPasswordField oldp = new JPasswordField(); p.add(oldp);
- p.add(new JLabel("新密码:")); JPasswordField newp = new JPasswordField(); p.add(newp);
- p.add(new JLabel("再次输入新密码:")); JPasswordField newp2 = new JPasswordField(); p.add(newp2);
- JButton ok = new JButton("确定"); JButton cancel = new JButton("取消"); p.add(ok); p.add(cancel);
+ JPanel p = new JPanel(new GridLayout(4, 2, 6, 6));
+ p.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
+ p.add(new JLabel("旧密码:"));
+ JPasswordField oldp = new JPasswordField();
+ p.add(oldp);
+ p.add(new JLabel("新密码:"));
+ JPasswordField newp = new JPasswordField();
+ p.add(newp);
+ p.add(new JLabel("再次输入新密码:"));
+ JPasswordField newp2 = new JPasswordField();
+ p.add(newp2);
+ JButton ok = new JButton("确定");
+ JButton cancel = new JButton("取消");
+ p.add(ok);
+ p.add(cancel);
add(p);
ok.addActionListener(a -> {
String oldPwd = new String(oldp.getPassword()).trim();
String np = new String(newp.getPassword()).trim();
String np2 = new String(newp2.getPassword()).trim();
- if (!np.equals(np2)) { JOptionPane.showMessageDialog(this, "两次密码不一致"); return; }
- if (!Login.validatePasswordRules(np)) { JOptionPane.showMessageDialog(this, "密码不满足规则"); return; }
+ if (!np.equals(np2)) {
+ JOptionPane.showMessageDialog(this, "两次密码不一致");
+ return;
+ }
+ if (!Login.validatePasswordRules(np)) {
+ JOptionPane.showMessageDialog(this, "密码不满足规则");
+ return;
+ }
boolean okr = Login.changePassword(user.username, oldPwd, np);
- if (okr) { JOptionPane.showMessageDialog(this, "修改成功"); dispose(); }
- else JOptionPane.showMessageDialog(this, "旧密码错误");
+ if (okr) {
+ JOptionPane.showMessageDialog(this, "修改成功");
+ dispose();
+ } else {
+ JOptionPane.showMessageDialog(this, "旧密码错误");
+ }
});
cancel.addActionListener(a -> dispose());
setVisible(true);
diff --git a/src/view/RegisterFrame.java b/src/view/RegisterFrame.java
index a9627c6..a08c1a6 100644
--- a/src/view/RegisterFrame.java
+++ b/src/view/RegisterFrame.java
@@ -1,11 +1,28 @@
package view;
-import javax.swing.*;
-import java.awt.*;
+import java.awt.GridLayout;
import java.util.Properties;
import java.util.Random;
-import jakarta.mail.*;
-import jakarta.mail.internet.*;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+import javax.swing.BorderFactory;
+
+import jakarta.mail.Authenticator;
+import jakarta.mail.Message;
+import jakarta.mail.PasswordAuthentication;
+import jakarta.mail.Session;
+import jakarta.mail.Transport;
+import jakarta.mail.internet.InternetAddress;
+import jakarta.mail.internet.MimeMessage;
+
import model.Login;
import model.LanguageSwitch;
@@ -13,6 +30,7 @@ import model.LanguageSwitch;
* 注册界面(使用 QQ 邮箱发送验证码)
*/
public class RegisterFrame extends JDialog {
+
private JTextField usernameField;
private JTextField emailField;
private JComboBox levelBox;
@@ -106,7 +124,7 @@ public class RegisterFrame extends JDialog {
Login.Level lv = LanguageSwitch.chineseToLevel(levelStr);
boolean ok = Login.register(u, pwd, lv, email);
if (!ok) {
- JOptionPane.showMessageDialog(this, "用户名已存在,请换一个用户名");
+ JOptionPane.showMessageDialog(this, "用户名或邮箱已存在,请换一个用户名");
return;
}
JOptionPane.showMessageDialog(this, "注册成功,请用新用户登录");
@@ -126,7 +144,6 @@ public class RegisterFrame extends JDialog {
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true"); // ← 开启 SSL
-
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(FROM_EMAIL, AUTH_CODE);
diff --git a/src/view/ResultFrame.java b/src/view/ResultFrame.java
index 1e424d2..a5186c4 100644
--- a/src/view/ResultFrame.java
+++ b/src/view/ResultFrame.java
@@ -1,21 +1,29 @@
package view;
-import javax.swing.*;
-import java.awt.*;
+import java.awt.FlowLayout;
+import java.awt.GridLayout;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
import model.Login;
/**
* 显示成绩并提供“退出或继续做题”的选择
*/
public class ResultFrame extends JFrame {
+
public ResultFrame(Login.Account user, int score, int total, double percent) {
setTitle("成绩 - " + user.username);
- setSize(360,220);
+ setSize(360, 220);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- JPanel p = new JPanel(new GridLayout(5,1,6,6));
- p.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+ JPanel p = new JPanel(new GridLayout(5, 1, 6, 6));
+ p.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
p.add(new JLabel("用户名: " + user.username));
p.add(new JLabel("得分: " + score + " / " + total));
p.add(new JLabel(String.format("百分比: %.2f%%", percent)));