Compare commits

...

21 Commits

@ -1,2 +0,0 @@
# jiedui

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,6 @@
在windows11 powershell下运行jdk-22
在运行前确保安装了curl可以在powershell中输入
curl.exe --version查看版本 我的是8.14.1
运行前先输入命令
[Console]::InputEncoding=[System.Text.Encoding]::UTF8
[Console]::OutputEncoding=[System.Text.Encoding]::UTF8

Binary file not shown.

Binary file not shown.

@ -0,0 +1,10 @@
import controller.MainController;
import javax.swing.*;
public class MathLearningApp {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new MainController().showLogin();
});
}
}

@ -0,0 +1,33 @@
import service.QuestionService;
import model.Question;
import service.OptionsResult;
import java.util.List;
class Test {
public static void main(String[] args) {
System.out.println("=== 测试题目生成 ===\n");
QuestionService questionService = new QuestionService();
List<Question> questions = questionService.generateQuestions("高中", 30);
for (int i = 0; i < questions.size(); i++) {
Question question = questions.get(i);
System.out.println((i + 1) + ". " + question.getContent());
// 通过 OptionsResult 获取选项列表
OptionsResult optionsResult = question.getOptions();
List<String> options = optionsResult.getOptions();
System.out.print(" 选项: ");
for (int j = 0; j < options.size(); j++) {
char optionChar = (char) ('A' + j);
System.out.print(optionChar + "." + options.get(j) + " ");
}
char correctChar = (char) ('A' + optionsResult.getCorrectIndex());
System.out.println(" [答案:" + correctChar + "]");
System.out.println();
}
}
}

@ -0,0 +1,46 @@
package controller;
import service.*;
import view.*;
public class AuthController {
private UserService userService = new UserService();
private EmailService emailService = new EmailService();
private MainController mainController;
private String currentUser;
public AuthController(MainController mainController) {
this.mainController = mainController;
}
public boolean login(String email, String password) {
if (userService.login(email, password)) {
currentUser = email;
mainController.showMainFrame();
return true;
}
return false;
}
public String register(String email) {
String code = userService.registerUser(email);
if (code != null) {
int sendResult = emailService.sendRegistrationCode(email, code);
if (sendResult == 1) {
return code;
}
}
return null;
}
public boolean completeRegistration(String email, String code, String password, String confirmPassword) {
return password.equals(confirmPassword) && userService.completeRegistration(email, code, password);
}
public void showLogin() {
new LoginFrame(this);
}
public void showRegister() {
new RegisterFrame(this);
}
}

@ -0,0 +1,9 @@
package controller;
public class Config {
public static final String USER_DATA_FILE = "users.dat";
public static final int MIN_PASSWORD_LENGTH = 6;
public static final int MAX_PASSWORD_LENGTH = 10;
public static final int MIN_QUESTIONS = 1;
public static final int MAX_QUESTIONS = 100;
}

@ -0,0 +1,86 @@
package controller;
import service.QuestionService;
import view.ExamFrame;
import java.util.List;
import javax.swing.JOptionPane;
public class ExamController {
private MainController mainController;
private QuestionService questionService = new QuestionService();
public ExamController(MainController mainController) {
this.mainController = mainController;
}
public void startExam(String difficulty) {
try {
// 显示题目数量输入对话框包含10-30的限制提示
String input = JOptionPane.showInputDialog(
null,
"请输入题目数量 (10-30题):",
"题目数量",
JOptionPane.QUESTION_MESSAGE
);
if (input != null) {
int count = Integer.parseInt(input.trim());
if (count >= 10 && count <= 30) {
List<model.Question> questions = questionService.generateQuestions(difficulty, count);
if (questions != null && !questions.isEmpty()) {
new ExamFrame(this, questions);
} else {
JOptionPane.showMessageDialog(null, "生成题目失败,请重试");
mainController.showMainFrame();
}
} else {
JOptionPane.showMessageDialog(null,
"题目数量必须在10-30之间\n当前输入" + count + "题",
"输入错误",
JOptionPane.ERROR_MESSAGE);
mainController.showMainFrame();
}
} else {
// 用户取消输入,返回主界面
mainController.showMainFrame();
}
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null,
"请输入有效数字!",
"输入错误",
JOptionPane.ERROR_MESSAGE);
mainController.showMainFrame();
} catch (Exception e) {
JOptionPane.showMessageDialog(null,
"生成题目时出现错误: " + e.getMessage(),
"错误",
JOptionPane.ERROR_MESSAGE);
mainController.showMainFrame();
}
}
public void showResult(int score, int total) {
// 计算百分比
int percentage = (int) ((double) score / total * 100);
// 显示成绩
JOptionPane.showMessageDialog(null,
"考试结束!\n" +
"得分: " + score + "/" + total + "\n" +
"正确率: " + percentage + "%",
"考试结果",
JOptionPane.INFORMATION_MESSAGE);
// 重新显示主界面
mainController.showMainFrame();
}
public void returnToMain() {
// 返回到主界面
mainController.showMainFrame();
}
// 获取主控制器(如果需要)
public MainController getMainController() {
return mainController;
}
}

@ -0,0 +1,93 @@
package controller;
import view.*;
public class MainController {
private AuthController authController = new AuthController(this);
private ExamController examController = new ExamController(this);
private MainFrame mainFrame;
public MainController() {
// 控制器初始化
}
/**
*
*/
public void showLogin() {
// 如果主界面存在,先关闭它
if (mainFrame != null) {
mainFrame.dispose();
mainFrame = null;
}
// 创建并显示登录界面
new LoginFrame(authController);
}
/**
*
*/
public void showMainFrame() {
if (mainFrame == null) {
// 第一次显示,创建主界面
mainFrame = new MainFrame(this);
}
// 显示主界面
mainFrame.showFrame();
// 确保主界面位于前台
mainFrame.toFront();
mainFrame.requestFocus();
}
/**
*
* @param difficulty
*/
public void startExam(String difficulty) {
examController.startExam(difficulty);
}
/**
* 退
*/
public void logout() {
// 清理主界面
if (mainFrame != null) {
mainFrame.dispose();
mainFrame = null;
}
// 显示登录界面
showLogin();
}
/**
*
*/
public AuthController getAuthController() {
return authController;
}
/**
*
*/
public ExamController getExamController() {
return examController;
}
/**
*
*/
public MainFrame getMainFrame() {
return mainFrame;
}
/**
* 退
*/
public void exitApplication() {
// 清理资源
if (mainFrame != null) {
mainFrame.dispose();
}
System.exit(0);
}
}

@ -0,0 +1,21 @@
package model;
import java.util.List;
public class Exam {
private List<Question> questions;
private String difficulty;
public Exam(List<Question> questions, String difficulty) {
this.questions = questions;
this.difficulty = difficulty;
}
public List<Question> getQuestions() {
return questions;
}
public String getDifficulty() {
return difficulty;
}
}

@ -0,0 +1,34 @@
package model;
import java.util.List;
import service.OptionsResult;
public class Question {
private String content;
private OptionsResult options;
private int correctPos;
private String difficulty;
public Question(String content, OptionsResult options, int correctPos, String difficulty) {
this.content = content;
this.options = options;
this.correctPos = correctPos;
this.difficulty = difficulty;
}
public String getContent() {
return content;
}
public OptionsResult getOptions() {
return options;
}
public int getCorrectAnswer() {
return correctPos;
}
public String getDifficulty() {
return difficulty;
}
}

@ -0,0 +1,41 @@
package model;
import java.io.Serializable;
public class User implements Serializable {
private String email;
private String password;
private String registrationCode;
private boolean isRegistered;
public User(String email, String registrationCode) {
this.email = email;
this.registrationCode = registrationCode;
this.isRegistered = false;
this.password = null;
}
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRegistrationCode() {
return registrationCode;
}
public boolean isRegistered() {
return isRegistered;
}
public void setRegistered(boolean registered) {
isRegistered = registered;
}
}

@ -0,0 +1,51 @@
package service;
import model.Question;
import java.util.List;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.Collections;
import java.util.Random;
public abstract class AbstractQuestionGenerator implements QuestionGenerator {
protected String difficulty;
protected Random random = new Random();
public AbstractQuestionGenerator(String difficulty) {
this.difficulty = difficulty;
}
@Override
public OptionsResult generateOptions(int correct) {
List<String> options = new ArrayList<>();
Set<Integer> used = new HashSet<>();
used.add(correct);
// 先生成3个错误选项
while (options.size() < 3) {
int wrong = correct + (int)(Math.random() * 10) - 5;
if (!used.contains(wrong) && wrong != correct) {
options.add(String.valueOf(wrong));
used.add(wrong);
}
}
// 在随机位置插入正确答案
int correctPosition = (int)(Math.random() * 4); // 0-3的随机位置
options.add(correctPosition, String.valueOf(correct));
return new OptionsResult(options, correctPosition);
}
public String getDifficulty() {
return difficulty;
}
protected int getRandomOperand() {
return random.nextInt(20)+1;
}
protected String getRandomOperator(String[] operators) {
return operators[random.nextInt(operators.length)];
}
}

@ -0,0 +1,80 @@
package service;
import utils.EmailValidator;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class EmailService {
public int sendRegistrationCode(String email, String code) {
System.out.println("=== CommandLineEmailService ===");
if (!EmailValidator.isValid(email)) {
return 0; //0表示邮箱格式错误
}
try {
// 创建临时文件
File tempFile = File.createTempFile("email", ".txt");
tempFile.deleteOnExit();
// 写入邮件内容
String emailContent = createEmailContent(email, code);
try (FileWriter writer = new FileWriter(tempFile)) {
writer.write(emailContent);
}
String[] command = {
"curl.exe",
"--url", "smtps://smtp.163.com:465",
"--ssl-reqd",
"--mail-from", "17857002985@163.com",
"--mail-rcpt", email,
"--user", "17857002985@163.com:EMqvpAhrvDw5rwuD", //EMqvpAhrvDw5rwuD授权码
"-T", tempFile.getAbsolutePath(), // 从文件读取内容
"--insecure"
};
System.out.println("执行命令: " + String.join(" ", command));
Process process = Runtime.getRuntime().exec(command);
// 读取输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
int exitCode = process.waitFor();
System.out.println("退出码: " + exitCode);
if (exitCode == 0) {
System.out.println("邮件发送成功");
return 1; //1表示成功发送
} else {
System.out.println("邮件发送失败");
return 2; //2表示邮箱格式正确但发送失败
}
} catch (Exception e) {
System.out.println("执行命令失败: " + e.getMessage());
return 2;
}
}
private String createEmailContent(String toEmail, String code) {
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
String date = sdf.format(new Date());
return "From: 17857002985@163.com\n" +
"To: " + toEmail + "\n" +
"Subject: 注册验证码\n" +
"Date: " + date + "\n" +
"\n" +
"您的注册验证码是: " + code + "\n" +
"验证码有效期为15分钟请尽快使用。\n" +
"\n" +
"如果不是您本人操作,请忽略此邮件。\n";
}
}

@ -0,0 +1,379 @@
package service;
import model.Question;
import java.util.List;
import java.util.ArrayList;
import java.util.Stack;
public class HighSchoolQuestionGenerator extends AbstractQuestionGenerator {
// 修正三角函数角度选择,确保所有角度都能产生合理的结果
private static final int[] SAFE_SIN_ANGLES = {0, 30, 90, 150, 180, 210, 270, 330};
private static final int[] SAFE_COS_ANGLES = {0, 60, 90, 120, 180, 240, 270, 300};
private static final int[] SAFE_TAN_ANGLES = {0, 180, 360}; // tan只使用结果为0的角度
private static final String[] TRIG_FUNCTIONS = {"sin", "cos", "tan"};
public HighSchoolQuestionGenerator() {
super("高中");
}
@Override
public Question generateQuestion() {
boolean valid = false;
String content = "";
double answer = 0;
int attempts = 0;
int maxAttempts = 50;
while (!valid && attempts < maxAttempts) {
attempts++;
// 生成2-4个操作数的题目
int operandCount = random.nextInt(3) + 2; // 2-4个操作数
List<Object> operands = new ArrayList<>();
List<String> operators = new ArrayList<>();
List<Boolean> isTrigFunction = new ArrayList<>();
// 生成操作数,确保至少有一个三角函数,可以有多个
int trigCount = 0;
for (int i = 0; i < operandCount; i++) {
// 至少有一个三角函数,其他位置随机决定是否使用三角函数
if (trigCount == 0 || (trigCount < 2 && random.nextDouble() < 0.5)) {
// 生成三角函数(确保不会产生无理数)
operands.add(generateSafeTrigExpression());
isTrigFunction.add(true);
trigCount++;
} else {
// 生成普通数字1-10的小整数
operands.add(random.nextInt(10) + 1);
isTrigFunction.add(false);
}
}
// 生成运算符(使用+、-、×)
for (int i = 0; i < operandCount - 1; i++) {
String[] possibleOps = {"+", "-", "×"};
String op = getRandomOperator(possibleOps);
operators.add(op);
}
content = buildHighSchoolContent(operands, operators, isTrigFunction);
answer = calculateHighSchoolExpression(content);
valid = !Double.isNaN(answer) && !Double.isInfinite(answer); // 允许负数结果
}
if (!valid) {
return generateSimpleTrigQuestion();
}
OptionsResult options = generateOptions(answer);
return new Question(content, options, options.getCorrectIndex(), difficulty);
}
/**
*
*/
private String generateSafeTrigExpression() {
String trigFunc = getRandomOperator(TRIG_FUNCTIONS);
int angle;
// 根据三角函数类型选择安全的角度
switch (trigFunc) {
case "sin":
angle = SAFE_SIN_ANGLES[random.nextInt(SAFE_SIN_ANGLES.length)];
break;
case "cos":
angle = SAFE_COS_ANGLES[random.nextInt(SAFE_COS_ANGLES.length)];
break;
case "tan":
angle = SAFE_TAN_ANGLES[random.nextInt(SAFE_TAN_ANGLES.length)];
break;
default:
angle = 0;
}
return trigFunc + "(" + angle + ")";
}
/**
*
*/
private String buildHighSchoolContent(List<Object> operands, List<String> operators,
List<Boolean> isTrigFunction) {
StringBuilder content = new StringBuilder();
boolean useParentheses = random.nextDouble() < 0.6 && operands.size() > 2;
if (useParentheses) {
int parenLength = random.nextInt(operands.size() - 1) + 2;
int start = random.nextInt(operands.size() - parenLength + 1);
int end = start + parenLength - 1;
for (int i = 0; i < operands.size(); i++) {
if (i == start) content.append("(");
if (isTrigFunction.get(i)) {
content.append(operands.get(i)); // 显示三角函数表达式
} else {
content.append(operands.get(i)); // 显示数字
}
if (i == end) content.append(")");
if (i < operators.size()) content.append(" ").append(operators.get(i)).append(" ");
}
} else {
for (int i = 0; i < operands.size(); i++) {
if (isTrigFunction.get(i)) {
content.append(operands.get(i));
} else {
content.append(operands.get(i));
}
if (i < operators.size()) content.append(" ").append(operators.get(i)).append(" ");
}
}
content.append(" = ?");
return content.toString();
}
/**
*
*/
private double calculateTrigFunction(String trigFunc, int angle) {
switch (trigFunc) {
case "sin":
return calculateExactSin(angle);
case "cos":
return calculateExactCos(angle);
case "tan":
return calculateExactTan(angle);
default:
return 0;
}
}
/**
* sin
*/
private double calculateExactSin(int angle) {
angle = angle % 360;
if (angle < 0) angle += 360;
switch (angle) {
case 0: case 180: case 360: return 0.0;
case 30: case 150: return 0.5;
case 90: return 1.0;
case 210: case 330: return -0.5;
case 270: return -1.0;
default: return 0.0;
}
}
/**
* cos
*/
private double calculateExactCos(int angle) {
angle = angle % 360;
if (angle < 0) angle += 360;
switch (angle) {
case 0: case 360: return 1.0;
case 60: case 300: return 0.5;
case 90: case 270: return 0.0;
case 120: case 240: return -0.5;
case 180: return -1.0;
default: return 1.0;
}
}
/**
* tan
*/
private double calculateExactTan(int angle) {
angle = angle % 360;
if (angle < 0) angle += 360;
switch (angle) {
case 0: case 180: case 360: return 0.0;
default: return 0.0;
}
}
/**
*
*/
private double calculateHighSchoolExpression(String expression) {
try {
String mathExpr = expression.replace(" = ?", "").trim();
return evaluateHighSchool(mathExpr);
} catch (Exception e) {
return Double.NaN;
}
}
/**
*
*/
private double evaluateHighSchool(String expression) {
Stack<Double> numbers = new Stack<>();
Stack<Character> ops = new Stack<>();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (c == ' ') continue;
if (Character.isDigit(c)) {
// 读取数字
double num = 0;
while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
num = num * 10 + (expression.charAt(i) - '0');
i++;
}
i--;
numbers.push(num);
} else if (c == 's' || c == 'c' || c == 't') {
// 处理三角函数
String trigFunc = parseTrigFunction(expression, i);
i += trigFunc.length();
// 跳过左括号
while (i < expression.length() && expression.charAt(i) != '(') {
i++;
}
i++; // 跳过 '('
// 读取角度
double angle = 0;
while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
angle = angle * 10 + (expression.charAt(i) - '0');
i++;
}
// 跳过右括号
while (i < expression.length() && expression.charAt(i) != ')') {
i++;
}
// 计算三角函数值
double trigValue = calculateTrigFunction(trigFunc, (int)angle);
numbers.push(trigValue);
} else if (c == '(') {
ops.push(c);
} else if (c == ')') {
while (!ops.isEmpty() && ops.peek() != '(') {
numbers.push(applyDoubleOp(ops.pop(), numbers.pop(), numbers.pop()));
}
ops.pop();
} else if (isOperator(c)) {
while (!ops.isEmpty() && hasPrecedence(c, ops.peek())) {
numbers.push(applyDoubleOp(ops.pop(), numbers.pop(), numbers.pop()));
}
ops.push(c);
}
}
while (!ops.isEmpty()) {
numbers.push(applyDoubleOp(ops.pop(), numbers.pop(), numbers.pop()));
}
return numbers.pop(); // 允许返回任何数值,包括负数
}
/**
*
*/
private String parseTrigFunction(String expression, int start) {
if (expression.startsWith("sin", start)) {
return "sin";
} else if (expression.startsWith("cos", start)) {
return "cos";
} else if (expression.startsWith("tan", start)) {
return "tan";
}
return "";
}
private boolean isOperator(char c) {
return c == '+' || c == '-' || c == '×' || c == '÷';
}
private boolean hasPrecedence(char op1, char op2) {
if (op2 == '(' || op2 == ')') {
return false;
}
// 如果栈顶运算符(op2)的优先级高于当前运算符(op1),则先计算栈顶的
if ((op2 == '×' || op2 == '÷') && (op1 == '+' || op1 == '-')) {
return true;
}
// 同级运算符从左到右计算
if ((op2 == '+' || op2 == '-') && (op1 == '+' || op1 == '-')) {
return true;
}
if ((op2 == '×' || op2 == '÷') && (op1 == '×' || op1 == '÷')) {
return true;
}
return false;
}
/**
*
*/
private double applyDoubleOp(char op, double b, double a) {
switch (op) {
case '+': return a + b;
case '-': return a - b; // 允许负数
case '×': return a * b;
case '÷':
if (b == 0) return Double.NaN;
return a / b;
default: return Double.NaN;
}
}
/**
* generateOptionsdouble1
*/
public OptionsResult generateOptions(double correct) {
List<String> options = new ArrayList<>();
java.util.Set<String> used = new java.util.HashSet<>();
used.add(formatDouble(correct));
// 生成3个错误选项使用有理数偏移
while (options.size() < 3) {
double offset = (random.nextInt(3) + 1) * 0.5; // 0.5, 1.0, 1.5
if (random.nextBoolean()) offset = -offset;
double wrongValue = correct + offset;
String wrongStr = formatDouble(wrongValue);
if (!used.contains(wrongStr)) {
options.add(wrongStr);
used.add(wrongStr);
}
}
// 在随机位置插入正确答案
int correctPosition = random.nextInt(4);
options.add(correctPosition, formatDouble(correct));
return new OptionsResult(options, correctPosition);
}
/**
* double1
*/
private String formatDouble(double value) {
// 处理-0.0的情况
if (Math.abs(value) < 0.0001) {
return "0.0";
}
return String.format("%.1f", value);
}
// 简单的保底题目
private Question generateSimpleTrigQuestion() {
int angle = random.nextBoolean() ? 0 : 180;
String trigFunc = "sin";
String content = trigFunc + "(" + angle + ") = ?";
double answer = calculateTrigFunction(trigFunc, angle);
OptionsResult options = generateOptions(answer);
return new Question(content, options, options.getCorrectIndex(), difficulty);
}
}

@ -0,0 +1,258 @@
package service;
import model.Question;
import java.util.List;
import java.util.ArrayList;
import java.util.Stack;
public class MiddleSchoolQuestionGenerator extends AbstractQuestionGenerator {
public MiddleSchoolQuestionGenerator() {
super("初中");
}
@Override
public Question generateQuestion() {
// 随机选择生成平方题目或开根号题目
if (random.nextBoolean()) {
return generateSquareQuestion();
} else {
return generateSquareRootQuestion();
}
}
private Question generateSquareQuestion() {
boolean valid = false;
String content = "";
int answer = 0;
int attempts = 0;
int maxAttempts = 50;
while (!valid && attempts < maxAttempts) {
attempts++;
// 生成2-4个操作数
int operandCount = random.nextInt(3) + 2;
List<Integer> operands = new ArrayList<>();
List<String> operators = new ArrayList<>();
List<Boolean> isSquared = new ArrayList<>(); // 记录哪些操作数要显示为平方形式
// 生成操作数,确保平方数是完全平方数
for (int i = 0; i < operandCount; i++) {
operands.add(getRandomOperand());
isSquared.add(false);
}
// 确定至少一个位置显示平方符号,并确保它是完全平方数
int squarePosition = random.nextInt(operandCount);
// 将选中的操作数改为完全平方数
int base = random.nextInt(10) + 1; // 1-10
operands.set(squarePosition, base * base);
isSquared.set(squarePosition, true);
// 生成运算符
for (int i = 0; i < operandCount - 1; i++) {
String op = getRandomOperator(new String[]{"+", "-", "×"});
operators.add(op);
}
content = buildMiddleSchoolContent(operands, operators, isSquared, true);
answer = calculateExpression(content);
valid = true; // 初中题目允许任何整数结果,包括负数
}
if (!valid) {
return generateSimpleQuestion();
}
OptionsResult options = generateOptions(answer);
return new Question(content, options, options.getCorrectIndex(), difficulty);
}
private Question generateSquareRootQuestion() {
boolean valid = false;
String content = "";
int answer = 0;
int attempts = 0;
int maxAttempts = 50;
while (!valid && attempts < maxAttempts) {
attempts++;
// 生成2-4个操作数
int operandCount = random.nextInt(3) + 2;
List<Integer> operands = new ArrayList<>();
List<String> operators = new ArrayList<>();
List<Boolean> isSquareRoot = new ArrayList<>(); // 记录哪些操作数要显示为根号形式
// 生成操作数,确保至少有一个完全平方数用于开根号
boolean hasPerfectSquare = false;
for (int i = 0; i < operandCount; i++) {
if (!hasPerfectSquare) {
// 生成一个完全平方数
int base = random.nextInt(10) + 1; // 1-10的平方
operands.add(base * base);
isSquareRoot.add(true); // 这个数显示为根号形式
hasPerfectSquare = true;
} else {
operands.add(getRandomOperand());
isSquareRoot.add(false);
}
}
// 生成运算符
for (int i = 0; i < operandCount - 1; i++) {
String op = getRandomOperator(new String[]{"+", "-", "×"});
operators.add(op);
}
content = buildMiddleSchoolContent(operands, operators, isSquareRoot, false);
answer = calculateExpression(content);
valid = true; // 初中题目允许任何整数结果
}
if (!valid) {
return generateSimpleQuestion();
}
OptionsResult options = generateOptions(answer);
return new Question(content, options, options.getCorrectIndex(), difficulty);
}
private String buildMiddleSchoolContent(List<Integer> operands, List<String> operators,
List<Boolean> showSpecial, boolean isSquareQuestion) {
StringBuilder content = new StringBuilder();
boolean useParentheses = random.nextBoolean() && operands.size() > 2;
if (useParentheses) {
int parenLength = random.nextInt(operands.size() - 1) + 2;
int start = random.nextInt(operands.size() - parenLength + 1);
int end = start + parenLength - 1;
for (int i = 0; i < operands.size(); i++) {
if (i == start) content.append("(");
// 强制显示特殊符号
if (showSpecial.get(i)) {
if (isSquareQuestion) { // 显示为平方形式
int base = (int) Math.sqrt(operands.get(i));
content.append(base).append("²");
} else {
content.append("√").append(operands.get(i)); // 显示为开根号形式√a
}
} else {
content.append(operands.get(i));
}
if (i == end) content.append(")");
if (i < operators.size()) content.append(" ").append(operators.get(i)).append(" ");
}
} else {
for (int i = 0; i < operands.size(); i++) {
// 强制显示特殊符号
if (showSpecial.get(i)) {
if (isSquareQuestion) {
int base = (int) Math.sqrt(operands.get(i));
content.append(base).append("²");
} else {
content.append("√").append(operands.get(i));
}
} else {
content.append(operands.get(i));
}
if (i < operators.size()) content.append(" ").append(operators.get(i)).append(" ");
}
}
content.append(" = ?");
return content.toString();
}
private int calculateExpression(String expression) {
try {
String mathExpr = expression.replace(" = ?", "").trim();
return evaluateMiddleSchool(mathExpr);
} catch (Exception e) {
return Integer.MIN_VALUE; // 使用特殊值表示计算失败
}
}
private int evaluateMiddleSchool(String expression) {
Stack<Integer> numbers = new Stack<>();
Stack<Character> ops = new Stack<>();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (c == ' ') continue;
if (Character.isDigit(c)) {
int num = 0;
while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
num = num * 10 + (expression.charAt(i) - '0');
i++;
}
i--;
numbers.push(num);
} else if (c == '²') { // 处理平方:弹出栈顶数字,计算平方后压回
if (!numbers.isEmpty()) {
int num = numbers.pop();
numbers.push(num * num);
}
} else if (c == '√') { // 处理开根号:读取后面的数字并开根
i++; // 跳过 '√'
int num = 0;
while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
num = num * 10 + (expression.charAt(i) - '0');
i++;
}
i--;
int sqrt = (int) Math.sqrt(num);
numbers.push(sqrt);
} else if (c == '(') {
ops.push(c);
} else if (c == ')') {
while (!ops.isEmpty() && ops.peek() != '(') {
numbers.push(applyOp(ops.pop(), numbers.pop(), numbers.pop()));
}
ops.pop();
} else if (isOperator(c)) {
while (!ops.isEmpty() && hasPrecedence(c, ops.peek())) {
numbers.push(applyOp(ops.pop(), numbers.pop(), numbers.pop()));
}
ops.push(c);
}
}
while (!ops.isEmpty()) {
numbers.push(applyOp(ops.pop(), numbers.pop(), numbers.pop()));
}
return numbers.pop(); // 允许返回任何整数,包括负数
}
private boolean isOperator(char c) {
return c == '+' || c == '-' || c == '×';
}
private boolean hasPrecedence(char op1, char op2) {
if (op2 == '(' || op2 == ')') {
return false;
}
// 如果栈顶运算符(op2)的优先级高于当前运算符(op1),则先计算栈顶的
if ((op2 == '×') && (op1 == '+' || op1 == '-')) {
return true;
}
// 同级运算符从左到右计算
if ((op2 == '+' || op2 == '-') && (op1 == '+' || op1 == '-')) {
return true;
}
if ((op2 == '×') && (op1 == '×')) {
return true;
}
return false;
}
private int applyOp(char op, int b, int a) {
switch (op) {
case '+': return a + b;
case '-': return a - b; // 允许负数结果
case '×': return a * b;
default: return 0;
}
}
// 简单的保底题目
private Question generateSimpleQuestion() {
int a = random.nextInt(10) + 1;
int b = random.nextInt(10) + 1;
String content = a + "² - " + b + "² = ?";
int answer = a*a - b*b;
OptionsResult options = generateOptions(answer);
return new Question(content, options, options.getCorrectIndex(), difficulty);
}
}

@ -0,0 +1,21 @@
package service;
import java.util.List;
public class OptionsResult {
private List<String> options;
private int correctIndex;
public OptionsResult(List<String> options, int correctIndex) {
this.options = options;
this.correctIndex = correctIndex;
}
public List<String> getOptions() {
return options;
}
public int getCorrectIndex() {
return correctIndex;
}
}

@ -0,0 +1,238 @@
package service;
import model.Question;
import java.util.List;
import java.util.ArrayList;
import java.util.Stack;
public class PrimaryQuestionGenerator extends AbstractQuestionGenerator {
private static final String[] OPERATORS = {"+", "-" , "×", "÷"};
public PrimaryQuestionGenerator() {
super("小学");
}
@Override
public Question generateQuestion() {
int operandCount = random.nextInt(4) + 2; // 2-5个操作数
List<Integer> operands = new ArrayList<>();
List<String> operators = new ArrayList<>();
// 生成操作数和运算符,确保结果为非负整数
boolean valid = false;
String content = "";
int answer = 0;
int attempts = 0;
int maxAttempts = 100; // 添加最大尝试次数
while (!valid && attempts < maxAttempts) {
attempts++; // 计数
operands.clear();
operators.clear();
// 生成操作数
for (int i = 0; i < operandCount; i++) {
operands.add(getRandomOperand());
}
// 生成运算符,限制最多只有一个除法
boolean hasDivision = false;
for (int i = 0; i < operandCount - 1; i++) {
String op;
// 如果已经有除法了,就只生成其他运算符
if (hasDivision) {
String[] otherOperators = {"+", "-", "×"};
op = getRandomOperator(otherOperators);
} else {
op = getRandomOperator(OPERATORS);
if (op.equals("÷")) {
hasDivision = true;
// 确保除法能整除:调整被除数
int divisor = operands.get(i + 1);
int dividend = operands.get(i);
// 如果当前不能整除,调整被除数
if (divisor == 0 || dividend % divisor != 0) {
// 如果除数为0重新生成除数
if (divisor == 0) {
divisor = random.nextInt(9) + 2; // 2-10
operands.set(i + 1, divisor);
}
// 调整被除数为除数的倍数
int multiple = random.nextInt(3) + 1; // 1-3倍
operands.set(i, divisor * multiple);
}
}
}
operators.add(op);
}
content = buildPrimaryContent(operands, operators);
answer = calculateExpression(content);
valid = answer >= 0; // 验证答案是否非负整数
}
// 如果多次尝试都失败,生成一个简单的保底题目
if (!valid) {
return generateSimpleQuestion();
}
OptionsResult options = generateOptions(answer);
return new Question(content, options, options.getCorrectIndex(), difficulty);
}
// 生成简单的保底题目
private Question generateSimpleQuestion() {
int a = random.nextInt(10) + 5; // 5-14
int b = random.nextInt(5) + 1; // 1-5
String[] simpleOps = {"+", "×"};
String op = simpleOps[random.nextInt(simpleOps.length)];
String content;
int answer;
if (op.equals("+")) {
content = a + " + " + b + " = ?";
answer = a + b;
} else {
content = a + " × " + b + " = ?";
answer = a * b;
}
OptionsResult options = generateOptions(answer);
return new Question(content, options, options.getCorrectIndex(), difficulty);
}
private String buildPrimaryContent(List<Integer> operands, List<String> operators) {
StringBuilder content = new StringBuilder();
// 随机决定是否加括号只在有3个及以上操作数时
boolean useParentheses = random.nextDouble() < 0.6 && operands.size() > 2;
if (useParentheses) {
// 随机决定括号包含的操作数数量至少2个最多 operands.size()-1 个)
int parenLength = random.nextInt(operands.size() - 1) + 2;
// 随机决定括号的起始位置
int start = random.nextInt(operands.size() - parenLength + 1);
int end = start + parenLength - 1;
for (int i = 0; i < operands.size(); i++) {
if (i == start) {
content.append("(");
}
content.append(operands.get(i));
if (i == end) {
content.append(")");
}
if (i < operators.size()) {
content.append(" ").append(operators.get(i)).append(" ");
}
}
} else {
for (int i = 0; i < operands.size(); i++) {
content.append(operands.get(i));
if (i < operators.size()) {
content.append(" ").append(operators.get(i)).append(" ");
}
}
}
content.append(" = ?");
return content.toString();
}
private int calculateExpression(String expression) {
try {
// 移除 "= ?" 部分
String mathExpr = expression.replace(" = ?", "").trim();
return evaluate(mathExpr);
} catch (Exception e) {
return -1; // 计算失败
}
}
private int evaluate(String expression) {
Stack<Integer> numbers = new Stack<>();
Stack<Character> ops = new Stack<>();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (c == ' ') {
continue; // 跳过空格
}
if (Character.isDigit(c)) {
// 读取完整数字
int num = 0;
while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
num = num * 10 + (expression.charAt(i) - '0');
i++;
}
i--;
numbers.push(num);
} else if (c == '(') {
ops.push(c);
} else if (c == ')') {
// 计算括号内的所有运算
while (ops.peek() != '(') {
int result = applyOp(ops.pop(), numbers.pop(), numbers.pop());
if (result < 0) return -1; // 检查中间结果是否负数
numbers.push(result);
}
ops.pop(); // 弹出 '('
} else if (isOperator(c)) {
// 处理运算符优先级:如果栈顶运算符优先级更高或相等,先计算栈顶的
while (!ops.isEmpty() && hasPrecedence(ops.peek(), c)) {
int result = applyOp(ops.pop(), numbers.pop(), numbers.pop());
if (result < 0) return -1; // 检查中间结果是否负数
numbers.push(result);
}
ops.push(c);
}
}
// 计算剩余表达式
while (!ops.isEmpty()) {
int result = applyOp(ops.pop(), numbers.pop(), numbers.pop());
if (result < 0) return -1; // 检查中间结果是否负数
numbers.push(result);
}
return numbers.pop();
}
private boolean hasPrecedence(char op1, char op2) {
if (op1 == '(' || op1 == ')') {
return false;
}
// op1是栈顶运算符op2是当前运算符
// 如果栈顶运算符优先级高于或等于当前运算符返回true先计算栈顶的
if ((op1 == '×' || op1 == '÷') && (op2 == '+' || op2 == '-')) {
return true;
}
// 同级运算符从左到右计算
if ((op1 == '+' || op1 == '-') && (op2 == '+' || op2 == '-')) {
return true;
}
if ((op1 == '×' || op1 == '÷') && (op2 == '×' || op2 == '÷')) {
return true;
}
return false;
}
private boolean isOperator(char c) {
return c == '+' || c == '-' || c == '×' || c == '÷';
}
private int applyOp(char op, int b, int a) {
switch (op) {
case '+':
return a + b;
case '-':
int result = a - b;
return result >= 0 ? result : -1; // 确保非负
case '×':
return a * b;
case '÷':
if (b == 0 || a % b != 0) {
return -1; // 除数为0或不整除
}
return a / b;
default:
return -1;
}
}
}

@ -0,0 +1,9 @@
package service;
import model.Question;
import java.util.List;
public interface QuestionGenerator {
Question generateQuestion();
OptionsResult generateOptions(int correctAnswer);
}

@ -0,0 +1,18 @@
package service;
import java.util.HashMap;
import java.util.Map;
public class QuestionGeneratorFactory {
private static final Map<String, QuestionGenerator> generators = new HashMap<>();
static {
generators.put("小学", new PrimaryQuestionGenerator());
generators.put("初中", new MiddleSchoolQuestionGenerator());
generators.put("高中", new HighSchoolQuestionGenerator());
}
public static QuestionGenerator getGenerator(String difficulty) {
return generators.getOrDefault(difficulty, generators.get("小学"));
}
}

@ -0,0 +1,25 @@
package service;
import model.Question;
import java.util.*;
public class QuestionService {
/*生成试卷的题目部分*/
public List<Question> generateQuestions(String difficulty, int count) {
if(count < 10 || count > 30) {
return null;
}
List<Question> questions = new ArrayList<>();
Set<String> usedQuestions = new HashSet<>();
QuestionGenerator generator = QuestionGeneratorFactory.getGenerator(difficulty);
while (questions.size() < count) {
Question q = generator.generateQuestion();
if (usedQuestions.add(q.getContent())) {
questions.add(q);
}
}
return questions;
}
}

@ -0,0 +1,61 @@
package service;
import model.User;
import utils.FileStorage;
import utils.PasswordValidator;
import java.util.HashMap;
import java.util.Map;
public class UserService {
private Map<String, User> users = new HashMap<>();
private FileStorage fileStorage = new FileStorage();
public UserService() {
users = fileStorage.loadUsers(); //加载所有已有的用户
}
/*注册用户第一阶段-生成验证码*/
public String registerUser(String email) {
if (users.containsKey(email) && users.get(email).isRegistered()){
return "registered"; //注册过的用户不能再注册
//return null;
}
if (users.containsKey(email)) {
return "recheck your email"; //已发过验证码也不再发了
//return null;
}
String code = String.valueOf(100000 + (int)(Math.random() * 900000)); //生成6位注册码
users.put(email, new User(email, code));
fileStorage.saveUsers(users);
return code;
}
/*注册用户第二阶段-完成注册*/
public boolean completeRegistration(String email, String code, String password) {
User user = users.get(email);
if (user != null && user.getRegistrationCode().equals(code) && PasswordValidator.isValid(password)) {
user.setPassword(password);
user.setRegistered(true);
fileStorage.saveUsers(users);
return true;
}
return false;
}
/*用户登录*/
public boolean login(String email, String password) {
User user = users.get(email);
return user != null && user.isRegistered() && user.getPassword().equals(password);
}
/*修改密码*/
public boolean changePassword(String email, String oldPassword, String newPassword) {
User user = users.get(email);
if (user != null && user.getPassword().equals(oldPassword) && PasswordValidator.isValid(newPassword)) {
user.setPassword(newPassword);
fileStorage.saveUsers(users);
return true;
}
return false;
}
}

@ -0,0 +1,12 @@
package utils;
import java.util.regex.Pattern;
public class EmailValidator {
private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
/*只检查邮箱的格式是否正确,但不保证这个邮箱存在*/
public static boolean isValid(String email) {
return email != null && EMAIL_PATTERN.matcher(email).matches();
}
}

@ -0,0 +1,30 @@
package utils;
import model.User;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class FileStorage {
/*完全擦除原来的文件并重新写入*/
public void saveUsers(Map<String, User> users) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users.dat"))) {
oos.writeObject(users);
} catch (IOException e) {
e.printStackTrace();
}
}
/*从文件中载入*/
@SuppressWarnings("unchecked")
public Map<String, User> loadUsers() {
File file = new File("users.dat");
if (!file.exists()) return new HashMap<>();
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users.dat"))) {
return (Map<String, User>) ois.readObject();
} catch (Exception e) {
return new HashMap<>();
}
}
}

@ -0,0 +1,18 @@
package utils;
public class PasswordValidator {
public static boolean isValid(String password) {
if (password == null || password.length() < 6 || password.length() > 10) {
return false;
}
boolean hasUpper = false;
boolean hasLower = false;
boolean hasDigit = false;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) hasUpper = true;
if (Character.isLowerCase(c)) hasLower = true;
if (Character.isDigit(c)) hasDigit = true;
}
return hasUpper && hasLower && hasDigit;
}
}

@ -0,0 +1,140 @@
package view;
import controller.ExamController;
import model.Question;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class ExamFrame extends JFrame {
private List<Question> questions;
private int current = 0;
private int[] answers;
private ButtonGroup group;
private JButton nextBtn;
private JButton prevBtn;
public ExamFrame(ExamController controller, List<Question> questions) {
this.questions = questions;
this.answers = new int[questions.size()];
for (int i = 0; i < answers.length; i++) answers[i] = -1;
setTitle("考试 - 共" + questions.size() + "题");
setSize(500, 300);
setLocationRelativeTo(null);
JPanel panel = new JPanel(new BorderLayout());
JLabel questionLabel = new JLabel();
group = new ButtonGroup();
JRadioButton[] options = new JRadioButton[4];
JPanel optionsPanel = new JPanel(new GridLayout(4, 1));
for (int i = 0; i < 4; i++) {
options[i] = new JRadioButton();
group.add(options[i]);
optionsPanel.add(options[i]);
int index = i;
options[i].addActionListener(e -> answers[current] = index);
}
prevBtn = new JButton("上一题");
nextBtn = new JButton("下一题");
JButton submit = new JButton("提交");
JPanel buttonPanel = new JPanel();
prevBtn.addActionListener(e -> showQuestion(current - 1, questionLabel, options));
nextBtn.addActionListener(e -> {
if (current == questions.size() - 1) {
// 已经是最后一题,提示用户
JOptionPane.showMessageDialog(this,
"已经是最后一题!\n请点击提交按钮完成考试。",
"提示",
JOptionPane.INFORMATION_MESSAGE);
} else {
showQuestion(current + 1, questionLabel, options);
}
});
submit.addActionListener(e -> {
// 检查是否有未答题目
int unanswered = 0;
for (int answer : answers) {
if (answer == -1) unanswered++;
}
String message;
if (unanswered > 0) {
message = "还有 " + unanswered + " 道题目未作答,确定要提交吗?";
} else {
message = "确定要提交试卷吗?";
}
// 确认提交对话框
int result = JOptionPane.showConfirmDialog(
this,
message,
"确认提交",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE
);
if (result == JOptionPane.YES_OPTION) {
int score = 0;
for (int i = 0; i < questions.size(); i++) {
if (answers[i] == questions.get(i).getCorrectAnswer()) score++;
}
controller.showResult(score, questions.size());
dispose();
}
});
buttonPanel.add(prevBtn);
buttonPanel.add(nextBtn);
buttonPanel.add(submit);
panel.add(questionLabel, BorderLayout.NORTH);
panel.add(optionsPanel, BorderLayout.CENTER);
panel.add(buttonPanel, BorderLayout.SOUTH);
add(panel);
showQuestion(0, questionLabel, options);
setVisible(true);
}
private void showQuestion(int index, JLabel label, JRadioButton[] options) {
if (index < 0 || index >= questions.size()) return;
current = index;
Question q = questions.get(index);
label.setText("第" + (index + 1) + "题: " + q.getContent() + " (" + (index + 1) + "/" + questions.size() + ")");
List<String> optionTexts = q.getOptions().getOptions();
// 先清空所有选项的选择状态
group.clearSelection();
// 设置选项文本,并根据当前题目的答案状态设置选择
for (int i = 0; i < 4; i++) {
options[i].setText(optionTexts.get(i));
if (answers[current] == i) {
options[i].setSelected(true);
}
}
// 更新按钮状态
updateButtonStates();
}
private void updateButtonStates() {
// 上一题按钮状态
prevBtn.setEnabled(current > 0);
// 下一题按钮状态
nextBtn.setEnabled(current < questions.size() - 1);
// 如果是最后一题,修改下一题按钮文本为提示
if (current == questions.size() - 1) {
nextBtn.setText("最后一题");
} else {
nextBtn.setText("下一题");
}
}
}

@ -0,0 +1,46 @@
package view;
import controller.AuthController;
import javax.swing.*;
import java.awt.*;
public class LoginFrame extends JFrame {
public LoginFrame(AuthController controller) {
setTitle("登录");
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(3, 2, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
JTextField emailField = new JTextField();
JPasswordField passwordField = new JPasswordField();
JButton loginBtn = new JButton("登录");
JButton registerBtn = new JButton("注册");
panel.add(new JLabel("邮箱:"));
panel.add(emailField);
panel.add(new JLabel("密码:"));
panel.add(passwordField);
panel.add(loginBtn);
panel.add(registerBtn);
loginBtn.addActionListener(e -> {
String email = emailField.getText();
String password = new String(passwordField.getPassword());
if (controller.login(email, password)) {
dispose();
} else {
JOptionPane.showMessageDialog(this, "登录失败");
}
});
registerBtn.addActionListener(e -> {
dispose();
controller.showRegister();
});
add(panel);
setVisible(true);
}
}

@ -0,0 +1,137 @@
package view;
import controller.MainController;
import javax.swing.*;
import java.awt.*;
public class MainFrame extends JFrame {
private MainController controller;
public MainFrame(MainController controller) {
this.controller = controller;
initializeWindow();
createComponents();
setVisible(true);
}
private void initializeWindow() {
setTitle("数学学习系统 - 主界面");
setSize(400, 400); // 增大窗口尺寸
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
}
private void createComponents() {
// 使用 BorderLayout 作为主布局
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
// 标题面板
JPanel titlePanel = new JPanel(new FlowLayout());
JLabel titleLabel = new JLabel("选择考试难度");
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
titlePanel.add(titleLabel);
// 提示面板
JPanel hintPanel = new JPanel(new FlowLayout());
JLabel hintLabel = new JLabel("题目数量限制10-30题");
hintLabel.setForeground(Color.GRAY);
hintLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
hintPanel.add(hintLabel);
// 按钮面板 - 使用 GridLayout 让按钮垂直排列
JPanel buttonPanel = new JPanel(new GridLayout(4, 1, 15, 15)); // 4行1列间距15
buttonPanel.setBorder(BorderFactory.createEmptyBorder(20, 50, 20, 50)); // 左右边距
JButton primaryBtn = new JButton("小学");
JButton middleBtn = new JButton("初中");
JButton highBtn = new JButton("高中");
JButton logoutBtn = new JButton("退出登录");
// 设置按钮字体和大小
Font buttonFont = new Font("微软雅黑", Font.PLAIN, 16);
primaryBtn.setFont(buttonFont);
middleBtn.setFont(buttonFont);
highBtn.setFont(buttonFont);
logoutBtn.setFont(buttonFont);
// 设置按钮大小
Dimension buttonSize = new Dimension(200, 50);
primaryBtn.setPreferredSize(buttonSize);
middleBtn.setPreferredSize(buttonSize);
highBtn.setPreferredSize(buttonSize);
logoutBtn.setPreferredSize(buttonSize);
// 设置按钮颜色
primaryBtn.setBackground(new Color(173, 216, 230)); // 浅蓝色
middleBtn.setBackground(new Color(144, 238, 144)); // 浅绿色
highBtn.setBackground(new Color(255, 182, 193)); // 浅粉色
logoutBtn.setBackground(new Color(240, 240, 240)); // 浅灰色
// 设置按钮边框
primaryBtn.setBorder(BorderFactory.createRaisedBevelBorder());
middleBtn.setBorder(BorderFactory.createRaisedBevelBorder());
highBtn.setBorder(BorderFactory.createRaisedBevelBorder());
logoutBtn.setBorder(BorderFactory.createRaisedBevelBorder());
// 按钮事件监听
primaryBtn.addActionListener(e -> {
setVisible(false); // 隐藏主界面
controller.startExam("小学");
});
middleBtn.addActionListener(e -> {
setVisible(false); // 隐藏主界面
controller.startExam("初中");
});
highBtn.addActionListener(e -> {
setVisible(false); // 隐藏主界面
controller.startExam("高中");
});
logoutBtn.addActionListener(e -> {
int result = JOptionPane.showConfirmDialog(
this,
"确定要退出登录吗?",
"确认退出",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE
);
if (result == JOptionPane.YES_OPTION) {
controller.logout();
}
});
// 添加按钮到按钮面板
buttonPanel.add(primaryBtn);
buttonPanel.add(middleBtn);
buttonPanel.add(highBtn);
buttonPanel.add(logoutBtn);
// 将所有面板添加到主面板
panel.add(titlePanel, BorderLayout.NORTH);
panel.add(hintPanel, BorderLayout.CENTER);
panel.add(buttonPanel, BorderLayout.SOUTH);
add(panel);
}
/**
*
*/
public void showFrame() {
setVisible(true);
toFront(); // 确保窗口在前台
requestFocus(); // 请求焦点
}
/**
* dispose 退
*/
@Override
public void dispose() {
super.dispose();
}
}

@ -0,0 +1,166 @@
package view;
import controller.AuthController;
import javax.swing.*;
import java.awt.*;
public class RegisterFrame extends JFrame {
public RegisterFrame(AuthController controller) {
setTitle("注册");
setSize(400, 350);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
// 使用 GridBagLayout 更灵活布局
JPanel panel = new JPanel(new GridBagLayout());
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(5, 5, 5, 5);
JTextField emailField = new JTextField();
JTextField codeField = new JTextField();
JPasswordField passwordField = new JPasswordField();
JPasswordField confirmField = new JPasswordField();
JButton sendCodeBtn = new JButton("发送验证码");
JButton registerBtn = new JButton("注册");
JButton backBtn = new JButton("返回");
// 邮箱行
gbc.gridx = 0; gbc.gridy = 0;
panel.add(new JLabel("邮箱:"), gbc);
gbc.gridx = 1; gbc.gridy = 0; gbc.gridwidth = 2;
gbc.weightx = 1.0;
panel.add(emailField, gbc);
// 验证码行 - 修复布局
gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.weightx = 0;
panel.add(new JLabel("验证码:"), gbc);
gbc.gridx = 1; gbc.gridy = 1; gbc.weightx = 1.0;
panel.add(codeField, gbc);
gbc.gridx = 2; gbc.gridy = 1; gbc.weightx = 0;
gbc.fill = GridBagConstraints.NONE;
panel.add(sendCodeBtn, gbc);
gbc.fill = GridBagConstraints.HORIZONTAL; // 恢复填充
// 密码行
gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 1; gbc.weightx = 0;
panel.add(new JLabel("密码:"), gbc);
gbc.gridx = 1; gbc.gridy = 2; gbc.gridwidth = 2; gbc.weightx = 1.0;
panel.add(passwordField, gbc);
// 确认密码行
gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 1; gbc.weightx = 0;
panel.add(new JLabel("确认密码:"), gbc);
gbc.gridx = 1; gbc.gridy = 3; gbc.gridwidth = 2; gbc.weightx = 1.0;
panel.add(confirmField, gbc);
// 按钮行 - 添加返回按钮
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.add(registerBtn);
buttonPanel.add(backBtn);
gbc.gridx = 0; gbc.gridy = 4; gbc.gridwidth = 3;
gbc.weightx = 1.0;
panel.add(buttonPanel, gbc);
// 发送验证码按钮事件监听器
sendCodeBtn.addActionListener(e -> {
String email = emailField.getText().trim();
if (email.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入邮箱地址", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
// 禁用按钮
sendCodeBtn.setEnabled(false);
sendCodeBtn.setText("发送中...");
new Thread(() -> {
try {
String result = controller.register(email);
SwingUtilities.invokeLater(() -> {
sendCodeBtn.setEnabled(true);
sendCodeBtn.setText("发送验证码");
if (result != null && !result.startsWith("already") &&
!result.startsWith("code") && !result.startsWith("invalid") &&
!result.startsWith("send")) {
// 成功发送,只显示通用提示
JOptionPane.showMessageDialog(this,
"验证码已发送到您的邮箱,请查收",
"发送成功",
JOptionPane.INFORMATION_MESSAGE);
} else if (result != null) {
// 处理错误情况(根据你的 AuthController 返回值调整)
JOptionPane.showMessageDialog(this,
"发送失败,请检查邮箱地址",
"错误",
JOptionPane.ERROR_MESSAGE);
} else {
JOptionPane.showMessageDialog(this,
"注册失败,请重试",
"错误",
JOptionPane.ERROR_MESSAGE);
}
});
} catch (Exception ex) {
SwingUtilities.invokeLater(() -> {
sendCodeBtn.setEnabled(true);
sendCodeBtn.setText("发送验证码");
JOptionPane.showMessageDialog(this,
"发送验证码时出现错误",
"错误",
JOptionPane.ERROR_MESSAGE);
});
}
}).start();
});
registerBtn.addActionListener(e -> {
String email = emailField.getText().trim();
String code = codeField.getText().trim();
String password = new String(passwordField.getPassword());
String confirmPassword = new String(confirmField.getPassword());
// 输入验证
if (email.isEmpty() || code.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写所有字段", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
if (!password.equals(confirmPassword)) {
JOptionPane.showMessageDialog(this, "两次输入的密码不一致", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
boolean success = controller.completeRegistration(email, code, password, confirmPassword);
if (success) {
JOptionPane.showMessageDialog(this,
"注册成功!\n请使用注册的邮箱和密码登录",
"注册成功",
JOptionPane.INFORMATION_MESSAGE);
dispose();
controller.showLogin();
} else {
JOptionPane.showMessageDialog(this,
"注册失败!\n可能的原因\n- 验证码错误\n- 密码不符合要求6-10位包含大小写字母和数字\n- 邮箱已被注册",
"注册失败",
JOptionPane.ERROR_MESSAGE);
}
});
backBtn.addActionListener(e -> {
dispose();
controller.showLogin();
});
// 添加回车键提交支持
getRootPane().setDefaultButton(registerBtn);
add(panel);
setVisible(true);
}
}

@ -0,0 +1,54 @@
package view;
import controller.ExamController;
import javax.swing.*;
import java.awt.*;
public class ResultFrame extends JFrame {
public ResultFrame(ExamController controller, int score, int total) {
setTitle("考试结果");
setSize(300, 200);
setLocationRelativeTo(null);
JPanel panel = new JPanel(new BorderLayout());
// 创建结果信息面板
JPanel resultPanel = new JPanel(new GridLayout(3, 1));
JLabel scoreLabel = new JLabel("得分: " + score + "/" + total, JLabel.CENTER);
JLabel totalLabel = new JLabel("总题数: " + total + "题", JLabel.CENTER);
double percentage = (double) score / total * 100;
JLabel percentageLabel = new JLabel(String.format("正确率: %.1f%%", percentage), JLabel.CENTER);
// 设置字体
Font boldFont = new Font("微软雅黑", Font.BOLD, 16);
scoreLabel.setFont(boldFont);
totalLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
percentageLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
resultPanel.add(scoreLabel);
resultPanel.add(totalLabel);
resultPanel.add(percentageLabel);
JButton continueBtn = new JButton("继续做题");
JButton exitBtn = new JButton("退出");
continueBtn.addActionListener(e -> {
dispose();
controller.returnToMain();
});
exitBtn.addActionListener(e -> {
dispose();
controller.returnToMain();
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(continueBtn);
buttonPanel.add(exitBtn);
panel.add(resultPanel, BorderLayout.CENTER);
panel.add(buttonPanel, BorderLayout.SOUTH);
add(panel);
setVisible(true);
}
}
Loading…
Cancel
Save