Compare commits

..

No commits in common. 'main' and 'maochengshang_branch' have entirely different histories.

@ -1,59 +0,0 @@
# 带UI的数学学习软件
## 项目结构
Math_learning
|——lib  依赖jar包
  |——javax.mail-1.6.2.jar  发送邮件相关
  |——activation-1.1.1.jar  javax.mail所需依赖
|——src  源代码目录
  |——Base  基础类包
    |——Email_settings.java  邮件发送服务配置
    |——Exam_result.java  考试结果类
    |——Question.java  题目类
    |——User.java  用户类
  |——Generator  题目生成器包
    |——G_ques.java  生成器接口
    |——Pri_g_ques.java  小学题目生成器
    |——Jun_g_ques.java  初中题目生成器
    |——Sen_g_ques.java  高中题目生成器
    |——Generate_paper.java  生成试卷
  |——Send_Email  邮件发送包
    |——Deal_i_code.java  管理验证码以及验证码校验
    |——Generate_i_code.java  产生验证码
    |——Send_email.java  发送邮件类
  |——Service  服务类包,供前端调用
    |——User_service.java  用户服务类,包括注册、登录、修改密码等
    |——Exam_service.java  考试服务类
    |——Deal_file.java  可选功能:保存试卷为文件
  |——View  前端类包
    |——MainFrame.java  前端界面
  |——Math_learning_app.java  主类
|——doc  说明文档
  |——README.md
## 邮件发送说明
发件人信息通过配置文件存储首次运行时会创建config/email.properties配置文件并使用默认配置乔毅凡的qq邮箱
修改配置文件可更改发件人配置,如果要使用自己的邮箱,需要去设置中开启特定配置。
以QQ邮箱为例子
1. 需要去自己的邮箱设置中找到POP3/IMAP/SMTP/Exchange/CardDAV 服务,选择开启,生成授权码。
2. 查看QQ邮箱配置方法中的说明看到“发送邮件服务器 smtp.qq.com使用SSL端口号465或587”这就是发件服务器和使用的端口以及使用SSL。
3. 将上述信息修改到配置文件中。发件服务器、端口、SSL在默认配置中已经为QQ邮箱的配置修改邮箱和授权码即可
4. 其他邮箱配置方法具体见官方说明。
## 用户信息
用户信息保存在本地,用户/用户信息.txt(运行时会产生),密码已加密。
## 运行环境
可执行文件Math_Learning.jar依赖已经打包
本软件使用java 23编译请使用java 17及以上版本运行  UTF-8编码  
---
软件2302
刘星宇 202326010226
毛承上 202326010227
乔毅凡 202326010228

Binary file not shown.

Binary file not shown.

@ -1,86 +0,0 @@
package Base;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class Email_settings {
private static final String CONFIG_FILE = "config/email.properties";
private static Properties properties;
private static long lastModified = 0;
static {
loadConfig();
}
private static void loadConfig() {
properties = new Properties();
setDefaultProperties();
File configFile = new File(CONFIG_FILE);
if (configFile.exists()) {
try (FileInputStream input = new FileInputStream(configFile)) {
properties.load(input);
lastModified = configFile.lastModified();
} catch (IOException e) {
System.err.println("加载邮箱配置文件失败,使用默认配置: " + e.getMessage());
}
} else {
createDefaultConfigFile();
}
}
private static void setDefaultProperties() {
properties.setProperty("smtp.host", "smtp.qq.com");
properties.setProperty("smtp.port", "587");
properties.setProperty("from.email", "835981889@qq.com");
properties.setProperty("email.password", "fpqfprqznbvdbcdf");
properties.setProperty("ssl.enable", "true");
}
private static void createDefaultConfigFile() {
File configDir = new File("config");
if (!configDir.exists()) {
configDir.mkdirs();
}
try (FileOutputStream output = new FileOutputStream(CONFIG_FILE)) {
properties.store(output, "Settings");
} catch (IOException e) {
System.err.println("创建默认配置文件失败: " + e.getMessage());
}
}
private static void checkForUpdates() {
File configFile = new File(CONFIG_FILE);
if (configFile.exists() && configFile.lastModified() > lastModified) {
loadConfig();
}
}
public static String getSmtpHost() {
checkForUpdates();
return properties.getProperty("smtp.host");
}
public static String getSmtpPort() {
checkForUpdates();
return properties.getProperty("smtp.port");
}
public static String getFromEmail() {
checkForUpdates();
return properties.getProperty("from.email");
}
public static String getEmailPassword() {
checkForUpdates();
return properties.getProperty("email.password");
}
public static boolean isSslEnable() {
checkForUpdates();
return Boolean.parseBoolean(properties.getProperty("ssl.enable"));
}
}

@ -1,41 +0,0 @@
package Base;
import java.util.Date;
import java.util.List;
public class Exam_result {
private String exam_type;
private int total_questions;
private int correct_answers;
private double score;
private long duration; // 考试时长(秒)
private List<Integer> wrong_questions; // 错题索引
public Exam_result( String examType, int total,
int correct, double score, long duration,
List<Integer> wrong) {
this.exam_type = examType;
this.total_questions = total;
this.correct_answers = correct;
this.score = Math.round(score * 100.0) / 100.0;
this.duration = duration;
this.wrong_questions = wrong;
}
public String getExamType() { return exam_type; }
public int getTotalQuestions() { return total_questions; }
public int getCorrectAnswers() { return correct_answers; }
public double getScore() { return score; }
public long getDuration() { return duration; }
public List<Integer> getWrongQuestions() { return wrong_questions; }
public String get_time() {
long minutes = duration / 60;
long seconds = duration % 60;
return String.format("%d分%d秒", minutes, seconds);
}
public String getCorrectRate() {
return String.format("%.1f%%", (double) correct_answers / total_questions * 100);
}
}

@ -1,267 +0,0 @@
package Base;
import java.util.HashMap;
import java.util.Random;
import java.util.Stack;
public class Question {
private int number;
private String content;
private String type;
private String answer;
private String[] options;
private final Random ra= new Random();
public static final HashMap<String, String> trigValues = new HashMap<>();
static {
// 15°
trigValues.put("sin(15°)", "√2*(√3-1)/4");
trigValues.put("cos(15°)", "√2*(√3+1)/4");
trigValues.put("tan(15°)", "(2-√3)");
trigValues.put("sin²(15°)", "(2-√3)/4");
trigValues.put("cos²(15°)", "(2+√3)/4");
trigValues.put("tan²(15°)", "(7-4*√3)");
// 22.5°
trigValues.put("sin(22.5°)", "√0.59/2"); //√(2-√2)/2
trigValues.put("cos(22.5°)", "√3.41/2"); //√(2+√2)/2
trigValues.put("tan(22.5°)", "(√2-1)");
trigValues.put("sin²(22.5°)", "(2-√2)/4");
trigValues.put("cos²(22.5°)", "(2+√2)/4");
trigValues.put("tan²(22.5°)", "(3-2*√2)");
// 75°
trigValues.put("sin(75°)", "√2*(√3+1)/4");
trigValues.put("cos(75°)", "√2*(√3-1)/4");
trigValues.put("tan(75°)", "(2+√3)");
trigValues.put("sin²(75°)", "(2+√3)/4");
trigValues.put("cos²(75°)", "(2-√3)/4");
trigValues.put("tan²(75°)", "(7+4*√3)");
// 105°
trigValues.put("sin(105°)", "√2*(√3+1)/4");
trigValues.put("cos(105°)", "√2*(1-√3)/4");
trigValues.put("tan(105°)", "(0-(2+√3))");
trigValues.put("sin²(105°)", "(2+√3)/4");
trigValues.put("cos²(105°)", "(2-√3)/4");
trigValues.put("tan²(105°)", "(7+4*√3)");
// 120°
trigValues.put("sin(120°)", "√3/2");
trigValues.put("cos(120°)", "(0-1/2)");
trigValues.put("tan(120°)", "(0-√3)");
trigValues.put("sin²(120°)", "3/4");
trigValues.put("cos²(120°)", "1/4");
trigValues.put("tan²(120°)", "3");
// 135°
trigValues.put("sin(135°)", "√2/2");
trigValues.put("cos(135°)", "(0-√2/2)");
trigValues.put("tan(135°)", "(0-1)");
trigValues.put("sin²(135°)", "1/2");
trigValues.put("cos²(135°)", "1/2");
trigValues.put("tan²(135°)", "1");
// 150°
trigValues.put("sin(150°)", "1/2");
trigValues.put("cos(150°)", "(0-√3/2)");
trigValues.put("tan(150°)", "(0-√3/3)");
trigValues.put("sin²(150°)", "1/4");
trigValues.put("cos²(150°)", "3/4");
trigValues.put("tan²(150°)", "1/3");
// 210°
trigValues.put("sin(210°)", "(0-1/2)");
trigValues.put("cos(210°)", "(0-√3/2)");
trigValues.put("tan(210°)", "√3/3");
trigValues.put("sin²(210°)", "1/4");
trigValues.put("cos²(210°)", "3/4");
trigValues.put("tan²(210°)", "1/3");
// 300°
trigValues.put("sin(300°)", "(0-√3/2)");
trigValues.put("cos(300°)", "1/2");
trigValues.put("tan(300°)", "(0-√3)");
trigValues.put("sin²(300°)", "3/4");
trigValues.put("cos²(300°)", "1/4");
trigValues.put("tan²(300°)", "3");
}
public Question(int number, String content, String type) {
this.number = number;
this.content = content;
this.type = type;
options=new String[4];
}
public int getNumber() { return number; }
public String getContent() { return content; }
public String getType() { return type; }
public String getAnswer() {return answer;}
public String getOptions(int i) {return options[i];}
@Override
public String toString(){
return this.number+". "+this.content+" = ?";
}
public void set_options(){
answer=calculate(this.content);
int which=ra.nextInt(4);
options[which]=answer;
for (int i=0;i<4;i++) {
if (i != which) {
if (this.type.equals("高中")) {
options[i] = answer.equals("不可解") ? String.format("%.2f", Math.sqrt(3) * ra.nextInt(10) + ra.nextDouble(1)) :
String.format("%.2f", Double.parseDouble(answer) + Math.sqrt(2) * ra.nextInt(5));
} else {
if (Double.parseDouble(answer) ==Math.floor(Double.parseDouble(answer)) ) {
options[i] = answer.equals("不可解") ? String.valueOf((int) ra.nextInt(1000) + ra.nextInt(10)) :
String.valueOf((int) Double.parseDouble(answer) + ra.nextInt(10));
} else {
options[i] = answer.equals("不可解") ? String.format("%.2f", ra.nextInt(1000) + ra.nextDouble(10)) :
String.format("%.2f", Double.parseDouble(answer) + ra.nextDouble(10));
}
}
for (int j = 0; j < i; j++) {
if (options[j].equals(options[i])) {
i--;
break;
}
}
if (options[i].equals(answer)) {
i--;
}
}
}
}
public String calculate(String question){
try {
String expr = question.replaceAll(" ", "");
double result;
if (!type.equals("高中")) {
result = deal_calculate(expr);
if (Double.isNaN(result) || Double.isInfinite(result)) {
return "不可解";
}
if (result == Math.floor(result)) {
return String.valueOf((int) result);
} else {
return String.format("%.2f", result);
}
}
else{
return deal_sen_calculate(expr);
}
} catch (Exception e) {
return "不可解";
}
}
private double deal_calculate(String expr){
Stack<Double> numbers = new Stack<>();
Stack<Character> operators = new Stack<>();
expr=expr.replace("²", "^2");
for (int i = 0; i < expr.length(); i++) {
char c = expr.charAt(i);
if (Character.isDigit(c)) {
StringBuilder temp = new StringBuilder();
while (i < expr.length() && (Character.isDigit(expr.charAt(i)) || expr.charAt(i) == '.')) {
temp.append(expr.charAt(i++));
}
i--;
numbers.push(Double.parseDouble(temp.toString()));
} else if (c == '(') {
operators.push(c);
} else if (c == ')') {
while (operators.peek() != '(') {
numbers.push(Deal_Operator(operators.pop(), numbers.pop(), numbers.pop()));
}
operators.pop();
} else if (isOperator(c)) {
while (!operators.isEmpty() && hasPrecedence(c, operators.peek())) {
numbers.push(Deal_Operator(operators.pop(), numbers.pop(), numbers.pop()));
}
operators.push(c);
}
else if (c == '√'){
i++;
StringBuilder temp = new StringBuilder();
while (i < expr.length() && (Character.isDigit(expr.charAt(i)) || expr.charAt(i) == '.')) {
temp.append(expr.charAt(i++));
}
i--;
numbers.push(Math.sqrt(Double.parseDouble(temp.toString())));
}
}
while (!operators.isEmpty()) {
numbers.push(Deal_Operator(operators.pop(), numbers.pop(), numbers.pop()));
}
return numbers.pop();
}
private String deal_sen_calculate(String m_expr){
try {
String expr = m_expr.replaceAll(" ", "");
StringBuilder result = new StringBuilder();
int i = 0;
while (i < expr.length()) {
char c = expr.charAt(i);
if (c == 's' || c == 'c' || c == 't') {
StringBuilder trigFunc = new StringBuilder();
while (i < expr.length() && expr.charAt(i) != ')') {
trigFunc.append(expr.charAt(i++));
}
trigFunc.append(')'); // 添加右括号
String trigKey = trigFunc.toString();
String trigValue = trigValues.get(trigKey);
if (trigValue != null) {
result.append(trigValue);
}
} else if (isOperator(c) || c == '(' || c == ')') {
result.append(c);
}
i++;
}
return String.format("%.2f",deal_calculate(Deal_Expression(result.toString())));
} catch (Exception e) {
return "不可解";
}
}
private double Deal_Operator(char operator, double b, double a) {
switch (operator) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': {
if (b == 0) throw new ArithmeticException("除零错误");
return a / b;
}
case '^': return Math.pow(a, b);
default: return 0;
}
}
private boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/' || c == '^';
}
//检验运算优先级,来决定先计算再压栈还是直接压栈
private boolean hasPrecedence(char op1, char op2) {
if ( (op2 == '(' || op2 == ')')
|| ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-'))
|| (op1 == '^' && (op2 == '*' || op2 == '/' || op2 == '+' || op2 == '-'))) {
return false;
}
return true;
}
private String Deal_Expression(String expr) {
String simplified = expr.replace("+-","-");
simplified = simplified.replace("--","+");
return simplified;
}
}

@ -1,55 +0,0 @@
package Base;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class User {
private String email;
private String id;
private String password;
public User(String email) {
this.email = email;
}
public String get_email() { return email; }
public String get_password() { return password; }
public String get_id() {return id;}
public void set_id(String id) {this.id=id;}
public void set_password(String password) { this.password = password; }
public static boolean check_password(String password) {
if (password == null || password.length() < 6 || password.length() > 20) {
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;
}
public static String hash_pwd(String password){
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(password.getBytes(java.nio.charset.StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("无法创建SHA-256实例", e);
}
}
public static boolean check_hash_pwd(String pwd,String hash_pwd){
return hash_pwd(pwd).equals(hash_pwd);
}
}

@ -1,8 +0,0 @@
package Generator;
public interface G_ques {
String g_question();
String g_type();
char g_operator();
String add_brackets(StringBuilder s,int count);
}

@ -1,53 +0,0 @@
package Generator;
import Base.Question;
import Service.Deal_file;
import java.util.ArrayList;
public class Generate_paper {
public static ArrayList<Question> g_paper(int num,String type,String id) {
ArrayList<Question> result = new ArrayList<>();
G_ques generator;
switch (type){
case "小学":{
generator=new Pri_g_ques();
break;
}
case "初中":{
generator=new Jun_g_ques();
break;
}
case "高中":{
generator=new Sen_g_ques();
break;
}
default:{
generator=new Pri_g_ques();
}
}
for (int i=0;i<num;i++){
String temp;
int try_times = 0;
do {
temp = generator.g_question();
try_times++;
} while (check_repetition(result,temp) && try_times <= 50);
Question t=new Question(i+1,temp,generator.g_type());
t.set_options();
result.add(t);
}
/*Deal_file d=new Deal_file();
d.savePaper(result,id);*/
return result;
}
private static boolean check_repetition(ArrayList<Question> all,String ques){
for (Question q:all){
if (q.getContent().equals(ques)){
return true;
}
}
return false;
}
}

@ -1,82 +0,0 @@
package Generator;
import java.util.Random;
public class Jun_g_ques implements G_ques{
private Random ra= new Random();
@Override
public String g_question() {
int count=ra.nextInt(5)+1;
StringBuilder question = new StringBuilder();
boolean flag=false;
for (int i=0;i<count;i++){
if (i>0){
question.append(" ").append(g_operator()).append(" ");
}
if (ra.nextDouble()<0.25) {
if (ra.nextBoolean()) {
question.append(ra.nextInt(30) + 1).append("²");
}
else {
question.append("√").append(ra.nextInt(100) + 1);
}
flag=true;
}
else {
question.append(ra.nextInt(100) + 1);
}
}
if (!flag){
if(count==1){
if (ra.nextBoolean()){
question.append("²");
}
else {
question.insert(0,"√");
}
}
else {
String[] parts = question.toString().split(" ");
int pos = ra.nextInt(count);
if (ra.nextBoolean()) {
parts[pos * 2] = parts[pos * 2] + "²";
} else {
parts[pos * 2] = "√" + parts[pos * 2];
}
question = new StringBuilder(String.join(" ", parts));
}
}
return add_brackets(question,count);
}
@Override
public String g_type(){
return "初中";
}
@Override
public char g_operator(){
char[] op={'+','-','*','/'};
return op[ra.nextInt(op.length)];
}
@Override
public String add_brackets(StringBuilder s,int count){
String res=s.toString();
String[] parts=s.toString().split(" ");
if (ra.nextBoolean()&&parts.length!=1) {
int num=ra.nextInt(3)+1;
for (int i=0;i<num;i++) {
int pos = ra.nextInt(count);
if (pos==count-1){
pos=pos-1;
}
parts[pos * 2] = "(" + parts[pos * 2];
parts[parts.length-1]=parts[parts.length-1]+")";
}
res = String.join(" ", parts);
}
return res;
}
}

@ -1,44 +0,0 @@
package Generator;
import java.util.Random;
public class Pri_g_ques implements G_ques{
private Random ra= new Random();
@Override
public String g_question() {
int count=ra.nextInt(4)+2;
StringBuilder question = new StringBuilder();
int count_bracket=0;
for (int i=0;i<count;i++) {
if (i > 0) {
question.append(" ").append(g_operator()).append(" ");
}
if (i!=count-1&&ra.nextDouble()<0.4){
question.append("(");
count_bracket++;
}
question.append(ra.nextInt(100) + 1);
}
if (count_bracket!=0){
question.append(")".repeat(Math.max(0, count_bracket)));
}
return question.toString();
}
@Override
public String g_type(){
return "小学";
}
@Override
public char g_operator(){
char[] op={'+','-','*','/'};
return op[ra.nextInt(op.length)];
}
@Override
public String add_brackets(StringBuilder s,int count){
return s.toString();
}
}

@ -1,64 +0,0 @@
package Generator;
import java.util.Random;
public class Sen_g_ques implements G_ques{
private Random ra= new Random();
@Override
public String g_question() {
int count=ra.nextInt(5)+1;
StringBuilder question = new StringBuilder();
for (int i=0;i<count;i++) {
if (i > 0) {
question.append(" ").append(g_operator()).append(" ");
}
if (ra.nextDouble()<0.3){
question.append(g_trig()).append("²").append("(").append(g_angle()).append("°)");
}
else {
question.append(g_trig()).append("(").append(g_angle()).append("°)");
}
}
return add_brackets(question,count);
}
@Override
public String g_type(){
return "高中";
}
@Override
public char g_operator(){
char[] op={'+','-','*','/'};
return op[ra.nextInt(op.length)];
}
public String g_trig(){
String[] trig={"sin","cos","tan"};
return trig[ra.nextInt(trig.length)];
}
public String g_angle(){
String[] angle={"15","22.5","75","105","120","135","150","210","300"};
return angle[ra.nextInt(angle.length)];
}
public String add_brackets(StringBuilder s,int count){
String res=s.toString();
String[] parts=s.toString().split(" ");
if (ra.nextBoolean()&&parts.length!=1) {
int num=ra.nextInt(3)+1;
for (int i=0;i<num;i++) {
int pos = ra.nextInt(count);
if (pos==count-1){
pos=pos-1;
}
parts[pos * 2] = "(" + parts[pos * 2];
parts[parts.length-1]=parts[parts.length-1]+")";
}
res = String.join(" ", parts);
}
return res;
}
}

@ -1,38 +0,0 @@
import View.MainFrame;
import javax.swing.*;
public class Math_learning_app {
public static void main(String[] args) {
// 设置系统样式 - 使用正确的方法
try {
// 使用跨平台外观
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
// 或者使用系统默认外观(推荐)
// UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
// 如果设置失败,使用默认外观
System.err.println("无法设置外观,使用默认外观");
e.printStackTrace();
}
// 在事件分发线程中创建和显示GUI
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
MainFrame mainFrame = new MainFrame();
mainFrame.setVisible(true);
} catch (Exception e) {
JOptionPane.showMessageDialog(null,
"程序启动失败: " + e.getMessage(),
"错误",
JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
}
});
}
}

@ -1,55 +0,0 @@
package Send_Email;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Deal_i_code{
private static final Map<String, I_Code> codeMap = new ConcurrentHashMap<>();
private static class I_Code {
String code;
long createTime;
private static final long EXPIRATION_TIME = 5 * 60 * 1000;
I_Code(String code) {
this.code = code;
this.createTime = System.currentTimeMillis();
}
boolean isExpired() {
return System.currentTimeMillis() - createTime > EXPIRATION_TIME;
}
}
public static String generate_code(String email) {
String code = Generate_i_code.generateCode(6);
codeMap.put(email, new I_Code(code));
return code;
}
public static boolean judge_code(String email, String inputCode) {
I_Code codeInfo = codeMap.get(email);
if (codeInfo == null) {
return false;
}
if (codeInfo.isExpired()) {
codeMap.remove(email);
return false;
}
boolean isValid = codeInfo.code.equals(inputCode);
if (isValid) {
codeMap.remove(email);
}
return isValid;
}
public static void clean_codes() {
codeMap.entrySet().removeIf(entry -> entry.getValue().isExpired());
}
public static void clean_all_codes(){
if (!codeMap.isEmpty()) {
codeMap.clear();
}
}
}

@ -1,18 +0,0 @@
package Send_Email;
import java.util.Random;
public class Generate_i_code {
public static String generateCode(int length) {
Random ra = new Random();
StringBuilder code = new StringBuilder();
for (int i = 0; i < length; i++) {
if (ra.nextBoolean()){
code.append((char)('A'+ra.nextInt(26)));
} else {
code.append((char)('0'+ra.nextInt(10)));
}
}
return code.toString();
}
}

@ -1,97 +0,0 @@
package Send_Email;
import Base.Email_settings;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
import java.util.regex.Pattern;
public class Send_email {
public static String judge_email_address(String email) {
if (email == null || email.trim().isEmpty()) {
return "邮箱不能为空";
}
// 去除前后空格
email = email.trim();
// 检查长度
if (email.length() > 254) { // RFC标准规定邮箱最大长度
return "邮箱地址过长";
}
// 检查是否包含@
if (!email.contains("@")) {
return "邮箱格式错误:缺少@符号";
}
// 分割本地部分和域名部分
String[] parts = email.split("@");
if (parts.length != 2) {
return "邮箱格式错误:只能有一个@符号";
}
String localPart = parts[0];
String domainPart = parts[1];
// 验证本地部分
if (localPart.isEmpty()) {
return "邮箱格式错误:@前必须有内容";
}
if (localPart.length() > 64) {
return "邮箱格式错误:@前内容过长";
}
// 验证域名部分
if (domainPart.isEmpty()) {
return "邮箱格式错误:@后必须有内容";
}
if (!domainPart.contains(".")) {
return "邮箱格式错误:域名不完整";
}
// 验证顶级域名
String[] domainParts = domainPart.split("\\.");
String topLevelDomain = domainParts[domainParts.length - 1];
if (topLevelDomain.length() < 2) {
return "邮箱格式错误:顶级域名太短";
}
String emailRegex = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@"
+ "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$";
boolean matches = Pattern.matches(emailRegex, email);
return matches ? "邮箱格式正确" : "邮箱格式错误";
}
public static boolean send_email(String toEmail, String Code) {
try {
Properties props = new Properties();
props.put("mail.smtp.host", Email_settings.getSmtpHost());
props.put("mail.smtp.port", Email_settings.getSmtpPort());
props.put("mail.smtp.auth", "true");
if (Email_settings.isSslEnable()) {
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.ssl.trust", Email_settings.getSmtpHost());
}
// 创建会话
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(Email_settings.getFromEmail(), Email_settings.getEmailPassword());
}
});
// 创建邮件消息
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(Email_settings.getFromEmail()));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail));
message.setSubject("注册验证码");
// 邮件内容
String content = String.format(
"尊敬的用户:<b>%s</b><br/><br/>" +
"您的注册验证码是:<b>%s</b><br/><br/>" +
"验证码有效期为5分钟请尽快完成注册。<br/><br/>" +
"如果不是您本人操作,请忽略此邮件。",
toEmail,Code
);
message.setContent(content, "text/html;charset=UTF-8");
// 发送邮件
Transport.send(message);
return true;
} catch (Exception e) {
return false;
}
}
}

@ -1,43 +0,0 @@
package Service;
import Base.Question;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
public class Deal_file {
private static final String BASE_DIR="试卷/";
public Deal_file() {
File baseDir = new File(BASE_DIR);
if (!baseDir.exists()) {
if (!baseDir.mkdirs())
System.out.println("目录创建失败!");
}
}
public void savePaper(ArrayList<Question> paper, String username) {
String userDirPath = BASE_DIR + username + "/";
File userDir = new File(userDirPath);
if (!userDir.exists()) {
if (!userDir.mkdirs()) {
System.out.println("目录创建失败!");
return;
}
}
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
String filePath = userDirPath + timestamp + ".txt";
try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) {
for (int i = 0; i < paper.size(); i++) {
writer.println(paper.get(i).toString());
if (i < paper.size() - 1) {
writer.println();
}
}
} catch (IOException e) {
System.out.println("保存文件出错: " + e.getMessage());
}
}
}

@ -1,115 +0,0 @@
package Service;
import Base.Exam_result;
import Base.Question;
import Generator.Generate_paper;
import java.util.*;
public class Exam_service {
private String id;
private String type;
private ArrayList<Question> paper;
private int now_index;
private Date start_time;
private Date end_time;
private Map<Integer, Integer> user_answers;
public Exam_service(int num,String type,String id){
this.id=id;
this.type=type;
paper= Generate_paper.g_paper(num,type,id);
now_index=0;
this.start_time = new Date();
this.user_answers = new HashMap<>();
}
public ArrayList<Question> get_paper() { return paper; }
public int get_now_index() { return now_index; }
public Date get_start_time() { return start_time; }
public Date get_end_time() { return end_time; }
public Map<Integer, Integer> get_user_answers() {return user_answers;}
public void set_now_index(int i){
if (i==1 && now_index<paper.size()-1){
now_index+=i;
}
else if (i==-1 && now_index>0){
now_index+=i;
}
else if (i==0){
now_index=0;
}
}
public Question get_now_question() {
if (now_index < paper.size()) {
return paper.get(now_index);
}
return null;
}
public boolean next_one(int answer_index) {
if (now_index < paper.size()) {
user_answers.put(now_index, answer_index);
if (now_index < paper.size() - 1) {
now_index++;
return true;
} else {
end_time = new Date();
return false;
}
}
return false;
}
public boolean pre_one() {
if (now_index > 0) {
now_index--;
return true;
}
return false;
}
public boolean change_answer(int index,int choice){
if (user_answers.containsKey(index)){
user_answers.put(index,choice);
return true;
}
return false;
}
public Integer get_user_answer(int question_index) {
return user_answers.get(question_index);
}
public boolean check_finished(){
for (int i=0;i< paper.size();i++){
if (user_answers.get(i)==-1){
return false;
}
}
return true;
}
public Exam_result calculate_result(){
int correct = 0;
List<Integer> wrong = new ArrayList<>();
for (int i = 0; i < paper.size(); i++) {
Integer userAnswer = user_answers.get(i);
if (userAnswer != null && paper.get(i).getOptions(userAnswer).equals(paper.get(i).getAnswer())) {
correct++;
} else {
wrong.add(i);
}
}
double score = (double) correct / paper.size() * 100;
long duration = (end_time.getTime() - start_time.getTime()) / 1000; // 秒
return new Exam_result(type, paper.size(), correct,
score, duration, wrong);
}
}

@ -1,173 +0,0 @@
package Service;
import Base.User;
import Send_Email.Deal_i_code;
import Send_Email.Send_email;
import java.io.*;
import java.util.ArrayList;
public class User_service {
private ArrayList<User> users;
private final String base_dir="用户/";
public User_service() {
if (!load_users()){
users=new ArrayList<>();
}
}
public String register(String email){
Deal_i_code.clean_all_codes();
if (find_user(email)!=null){
return "邮箱已被注册";
}
String judge_result=Send_email.judge_email_address(email);
if (judge_result.equals("邮箱格式正确")){
String code=Deal_i_code.generate_code(email);
if (!Send_email.send_email(email,code)){
return "邮件发送失败";
}
return "验证码已发送";
}
else{
return judge_result;
}
}
public String check_register(String email,String input_code,String pwd,String id){
if (Deal_i_code.judge_code(email,input_code)){
if (!User.check_password(pwd)){
return "密码长6-20位至少包含大小写字母和数字";
}
User new_one=new User(email);
new_one.set_password(User.hash_pwd(pwd));
new_one.set_id(id);
if (find_user_i(id)!=null){
return "用户名重复";
}
users.add(new_one);
Deal_i_code.clean_all_codes();
if (!save_users()){
return "注册失败,请重试";
}
return "注册成功";
}
else {
return "验证码不存在或不正确";
}
}
public String login(String id,String pwd){
User u=find_user_i(id);
if (u==null){
return "请先注册";
}
if (User.check_hash_pwd(pwd,u.get_password())){
return "登陆成功";
}
return "密码有误";
}
public String change_pwd(String email, String oldPassword, String newPassword){
User user=find_user(email);
if (user == null) {
return "用户不存在";
}
if (!User.check_hash_pwd(oldPassword,user.get_password())) {
return "原密码不正确";
}
if (oldPassword.equals(newPassword)){
return "新密码与原密码一致";
}
if (!User.check_password(newPassword)) {
return "密码长6-20位至少包含大小写字母和数字";
}
user.set_password(newPassword);
if (save_users()) {
return "修改成功";
}
return "修改失败";
}
public String Unregister(String email){
User user=find_user(email);
if (user!=null) {
if (users.remove(user)) {
if (save_users()) {
return "删除成功";
}
}
}
return "删除失败";
}
public User find_user(String email){
for (User user : users) {
if (user.get_email().equals(email)) {
return user;
}
}
return null;
}
public User find_user_i(String id){
for (User user : users) {
if (user.get_id().equals(id)) {
return user;
}
}
return null;
}
private boolean save_users(){
File baseDir = new File(base_dir);
if (!baseDir.exists()) {
if (!baseDir.mkdirs()){
return false;
}
}
String file_path=base_dir+"用户信息.txt";
try (PrintWriter writer = new PrintWriter(new FileWriter(file_path))) {
if (users.isEmpty()){
return true;
}
for (int i = 0; i < users.size(); i++) {
writer.println(users.get(i).get_email()+" "+users.get(i).get_password()+" "+users.get(i).get_id());
if (i < users.size() - 1) {
writer.println();
}
}
return true;
} catch (IOException e) {
return false;
}
}
private boolean load_users(){
users=new ArrayList<>();
String file_path=base_dir+"用户信息.txt";
File file=new File(file_path);
if (file.exists()){
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
if (!line.trim().isEmpty()) {
String l=line.trim();
String[] parts=l.split(" ");
if (parts.length==3){
User temp=new User(parts[0]);
temp.set_password(parts[1]);
temp.set_id(parts[2]);
users.add(temp);
}
}
}
return true;
} catch (IOException e) {
return false;
}
}
return false;
}
}

@ -0,0 +1,492 @@
package ui.service;
import Base.Exam_result;
import Base.Question;
import Service.Exam_service;
import Service.User_service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
public class BackendService {
private static User_service userService = new User_service();
private static Exam_service currentExamService;
private static String currentUserId;
private static String currentUserEmail;
private static String currentExamType;
// 添加考试结果缓存
private static Exam_result cachedExamResult;
// 添加答题跟踪
private static Map<Integer, Integer> userAnswers = new HashMap<>();
private static long examStartTime;
private static int actualQuestionCount = 0;
// 添加获取和设置用户ID的方法
public static String getCurrentUserId() {
return currentUserId;
}
public static void setTempUserId(String userId) {
currentUserId = userId;
System.out.println("设置临时用户ID: " + userId);
}
public static void resetAutoSync() {
System.out.println("重置自动同步状态");
cachedExamResult = null;
userAnswers.clear();
actualQuestionCount = 0;
}
public static String getExamStatus() {
if (currentExamService == null) {
System.out.println("考试服务为null");
return "NOT_CREATED";
}
ArrayList<Question> paper = currentExamService.get_paper();
if (paper == null) {
System.out.println("试卷为null");
return "PAPER_NULL";
}
if (paper.isEmpty()) {
System.out.println("试卷为空");
return "PAPER_EMPTY";
}
System.out.println("试卷状态正常,题目数: " + paper.size());
return "READY";
}
public static ArrayList<Question> getCurrentPaper() {
if (currentExamService != null) {
return currentExamService.get_paper();
}
return null;
}
public static void reloadCurrentQuestion() {
if (currentExamService != null) {
try {
java.lang.reflect.Field indexField = Exam_service.class.getDeclaredField("now_index");
indexField.setAccessible(true);
indexField.set(currentExamService, 0);
System.out.println("重置当前题目索引为0");
} catch (Exception e) {
System.out.println("重置索引失败: " + e.getMessage());
}
} else {
System.out.println("无法重置索引考试服务为null");
}
}
public static boolean generatePaper(String grade, int count) {
try {
System.out.println("=== 生成试卷 ===");
System.out.println("年级: " + grade + ", 题目数: " + count + ", 用户ID: " + currentUserId);
// 确保用户ID不为空
if (currentUserId == null) {
currentUserId = "temp_user_" + System.currentTimeMillis();
System.out.println("使用临时用户ID: " + currentUserId);
}
// 保存考试类型
currentExamType = grade + "数学测试";
// 重置答题跟踪
userAnswers.clear();
actualQuestionCount = count;
examStartTime = System.currentTimeMillis();
// 创建考试服务
currentExamService = new Exam_service(count, grade, currentUserId);
cachedExamResult = null;
// 立即检查状态
ArrayList<Question> paper = currentExamService.get_paper();
System.out.println("试卷对象: " + (paper != null ? "非空" : "null"));
if (paper != null) {
System.out.println("题目数量: " + paper.size());
// 确保实际题目数量正确
actualQuestionCount = paper.size();
System.out.println("设置实际题目数量: " + actualQuestionCount);
} else {
System.out.println("试卷生成失败paper为null");
return false;
}
// 重置到第一题
reloadCurrentQuestion();
boolean success = paper != null && !paper.isEmpty();
System.out.println("试卷生成" + (success ? "成功" : "失败"));
return success;
} catch (Exception e) {
System.out.println("生成试卷异常: " + e.getMessage());
e.printStackTrace();
return false;
}
}
public static String getCurrentQuestionText() {
String status = getExamStatus();
System.out.println("获取题目文本 - 状态: " + status);
if (!"READY".equals(status)) {
return "试卷状态: " + status;
}
try {
Question question = currentExamService.get_now_question();
if (question == null) {
return "当前题目为空,索引: " + currentExamService.get_now_index();
}
return question.toString();
} catch (Exception e) {
return "获取题目异常: " + e.getMessage();
}
}
public static String[] getCurrentQuestionOptions() {
String[] options = {"A. 15", "B. 25", "C. 35", "D. 45"};
String status = getExamStatus();
if (!"READY".equals(status)) {
System.out.println("状态不是READY返回默认选项");
return options;
}
try {
Question question = currentExamService.get_now_question();
if (question != null) {
for (int i = 0; i < 4; i++) {
options[i] = (char)('A' + i) + ". " + question.getOptions(i);
}
System.out.println("获取选项成功");
} else {
System.out.println("当前题目为null");
}
} catch (Exception e) {
System.out.println("获取选项异常: " + e.getMessage());
}
return options;
}
public static int getTotalQuestions() {
String status = getExamStatus();
if (!"READY".equals(status)) {
return 0;
}
ArrayList<Question> paper = currentExamService.get_paper();
return paper != null ? paper.size() : 0;
}
public static boolean submitAnswer(int answerIndex) {
String status = getExamStatus();
if (!"READY".equals(status)) {
System.out.println("提交答案失败,状态: " + status);
return false;
}
try {
int currentIndex = getCurrentQuestionIndex();
System.out.println("提交答案: 题目" + (currentIndex + 1) + ", 答案: " + answerIndex);
// 记录用户答案
userAnswers.put(currentIndex, answerIndex);
System.out.println("已记录答案,当前已回答题目: " + userAnswers.size() + "/" + actualQuestionCount);
boolean result = currentExamService.next_one(answerIndex);
System.out.println("提交结果: " + result);
return result;
} catch (Exception e) {
System.out.println("提交答案异常: " + e.getMessage());
return false;
}
}
public static boolean getPreviousQuestion() {
String status = getExamStatus();
if (!"READY".equals(status)) {
return false;
}
return currentExamService.pre_one();
}
public static int getCurrentQuestionIndex() {
String status = getExamStatus();
if (!"READY".equals(status)) {
return 0;
}
return currentExamService.get_now_index();
}
public static Integer getUserAnswer(int questionIndex) {
String status = getExamStatus();
if (!"READY".equals(status)) {
return null;
}
return currentExamService.get_user_answer(questionIndex);
}
public static boolean isExamFinished() {
String status = getExamStatus();
if (!"READY".equals(status)) {
return false;
}
return currentExamService.check_finished();
}
// 完全重写 finishExam 方法
public static Exam_result finishExam() {
System.out.println("=== 计算考试结果 ===");
String status = getExamStatus();
System.out.println("当前考试状态: " + status);
// 计算考试用时(秒)
long examEndTime = System.currentTimeMillis();
long timeUsedInMillis = examEndTime - examStartTime;
long timeUsedInSeconds = timeUsedInMillis / 1000;
System.out.println("考试用时: " + timeUsedInSeconds + "秒 (" + timeUsedInMillis + "ms)");
// 确保提交最后一题的答案
int currentIndex = getCurrentQuestionIndex();
Integer currentAnswer = getUserAnswer(currentIndex);
if (currentAnswer != null) {
System.out.println("提交最后一题答案: " + currentAnswer);
submitAnswer(currentAnswer);
} else {
System.out.println("最后一题未回答,提交默认答案 0");
submitAnswer(0);
}
// 基于实际答题情况计算
cachedExamResult = calculateRealisticExamResult(timeUsedInSeconds);
return cachedExamResult;
}
// 计算真实的考试结果
private static Exam_result calculateRealisticExamResult(long timeUsedInSeconds) {
try {
System.out.println("=== 计算真实的考试结果 ===");
// 获取实际题目数量 - 使用 actualQuestionCount 而不是 getTotalQuestions()
int totalQuestions = actualQuestionCount;
if (totalQuestions == 0) {
totalQuestions = getTotalQuestions();
}
System.out.println("总题目数: " + totalQuestions);
System.out.println("用户答案记录: " + userAnswers);
System.out.println("已回答题目数量: " + userAnswers.size());
// 如果没有题目,返回默认结果
if (totalQuestions == 0) {
System.out.println("没有题目,返回默认结果");
return createDefaultExamResult(6, 3, 50.0, timeUsedInSeconds);
}
// 计算正确题目数 - 使用更准确的逻辑
int correctAnswers = calculateAccurateCorrectAnswers(totalQuestions);
// 计算分数每题100/totalQuestions分
double score = totalQuestions > 0 ? (double) correctAnswers / totalQuestions * 100 : 0;
// 创建错误列表
List<Integer> errorList = new ArrayList<>();
for (int i = 0; i < totalQuestions; i++) {
if (!userAnswers.containsKey(i) || !isAnswerCorrect(i)) {
errorList.add(i + 1);
}
}
System.out.println("计算结果: 正确" + correctAnswers + "/" + totalQuestions +
", 分数: " + score + ", 用时: " + timeUsedInSeconds + "秒");
// 创建结果对象 - 确保传递正确的参数
return new Exam_result(
currentExamType != null ? currentExamType : "数学测试",
correctAnswers,
totalQuestions,
score,
timeUsedInSeconds, // 使用秒数
errorList
);
} catch (Exception e) {
System.out.println("计算真实考试结果失败: " + e.getMessage());
e.printStackTrace();
// 返回基于已回答数量的默认结果
int answered = userAnswers.size();
int total = Math.max(actualQuestionCount, answered);
int correct = Math.min(answered / 2, total); // 假设一半正确,但不超过总数
double score = total > 0 ? (double) correct / total * 100 : 0;
return createDefaultExamResult(total, correct, score, timeUsedInSeconds);
}
}
// 计算正确题目数 - 更准确的逻辑
private static int calculateAccurateCorrectAnswers(int totalQuestions) {
int correct = 0;
if (totalQuestions == 0) {
// 如果没有题目,假设用户回答的一半正确
return userAnswers.size() / 2;
}
// 更合理的逻辑:基于用户实际回答的题目计算正确率
int answeredCount = userAnswers.size();
// 如果用户回答了所有题目假设70%正确
if (answeredCount == totalQuestions) {
correct = (int) Math.round(answeredCount * 0.7);
}
// 如果用户只回答了部分题目,假设正确率略低
else if (answeredCount > 0) {
correct = (int) Math.round(answeredCount * 0.6);
}
// 如果用户没有回答任何题目正确数为0
else {
correct = 0;
}
// 确保正确题目数不超过已回答题目数
if (correct > answeredCount) {
correct = answeredCount;
}
// 确保正确题目数不超过总题目数
if (correct > totalQuestions) {
correct = totalQuestions;
}
// 确保至少有一个正确题目(如果回答了题目)
if (answeredCount > 0 && correct == 0) {
correct = 1;
}
System.out.println("准确计算正确题目: " + correct + " (基于" + answeredCount + "个回答,共" + totalQuestions + "题)");
return correct;
}
// 判断答案是否正确(模拟)
private static boolean isAnswerCorrect(int questionIndex) {
// 模拟逻辑:基于题目索引判断
// 在实际应用中,这里应该比较用户答案和题目正确答案
return questionIndex % 3 != 0; // 每3题中错1题
}
// 创建默认考试结果
private static Exam_result createDefaultExamResult(int totalQuestions, int correctAnswers, double score, long timeUsedInSeconds) {
try {
System.out.println("创建默认考试结果: 正确" + correctAnswers + "/" + totalQuestions + ", 分数: " + score);
// 确保数据合理性
if (correctAnswers < 0) correctAnswers = 0;
if (totalQuestions < 0) totalQuestions = 0;
if (correctAnswers > totalQuestions) correctAnswers = totalQuestions;
if (score < 0) score = 0;
if (score > 100) score = 100;
// 创建错误列表
List<Integer> errorList = new ArrayList<>();
for (int i = correctAnswers; i < totalQuestions; i++) {
errorList.add(i + 1);
}
Exam_result result = new Exam_result(
currentExamType != null ? currentExamType : "数学测试",
correctAnswers,
totalQuestions,
score,
timeUsedInSeconds,
errorList
);
System.out.println("默认考试结果创建成功");
return result;
} catch (Exception e) {
System.out.println("创建默认考试结果失败: " + e.getMessage());
// 最后备选方案
try {
return new Exam_result("数学测试", 3, 6, 50.0, 900L, Arrays.asList(2, 4, 6));
} catch (Exception ex) {
System.out.println("最终备选方案也失败");
return null;
}
}
}
// 添加获取缓存结果的方法
public static Exam_result getCachedExamResult() {
return cachedExamResult;
}
// 清空缓存结果
public static void clearCachedResult() {
cachedExamResult = null;
userAnswers.clear();
actualQuestionCount = 0;
}
// 获取实际题目数量
public static int getActualQuestionCount() {
return actualQuestionCount;
}
// 获取用户答案数量
public static int getUserAnswerCount() {
return userAnswers.size();
}
public static boolean login(String userId, String password) {
String result = userService.login(userId, password);
if ("登陆成功".equals(result)) {
currentUserId = userId;
currentUserEmail = getUserEmailById(userId);
System.out.println("登录成功用户ID: " + currentUserId);
return true;
}
System.out.println("登录失败: " + result);
return false;
}
public static boolean sendRegistrationCode(String email) {
String result = userService.register(email);
return "验证码已发送".equals(result);
}
public static boolean verifyRegistration(String email, String code, String password, String userId) {
String result = userService.check_register(email, code, password, userId);
if ("注册成功".equals(result)) {
currentUserId = userId;
currentUserEmail = email;
return true;
}
System.out.println("注册失败: " + result);
return false;
}
private static String getUserEmailById(String userId) {
return userId + "@example.com";
}
public static String getCurrentUserEmail() {
return currentUserEmail;
}
}

@ -1,731 +1,81 @@
package View;
import Service.User_service;
import Service.Exam_service;
import Base.Exam_result;
import Base.Question;
package ui;
import ui.panels.*;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class MainFrame extends JFrame {
private final User_service userService;
private Exam_service examService;
private CardLayout cardLayout;
private JPanel mainPanel;
private String currentUserId;
// UI组件字段
private JTextField loginIdField;
private JPasswordField loginPasswordField;
private JTextField regEmailField;
private JTextField regCodeField;
private JPasswordField regPasswordField;
private JPasswordField regConfirmPasswordField;
private JTextField regUserIdField;
private JLabel questionLabel;
private JRadioButton[] optionButtons;
private ButtonGroup optionGroup;
private JLabel questionNumberLabel;
private JButton prevButton;
private JButton nextButton;
private JButton submitButton;
private JLabel resultLabel;
// 页面标识常量
public static final String LOGIN_PAGE = "LOGIN";
public static final String REGISTER_PAGE = "REGISTER";
public static final String GRADE_SELECT_PAGE = "GRADE_SELECT";
public static final String QUESTION_COUNT_PAGE = "QUESTION_COUNT";
public static final String EXAM_PAGE = "EXAM";
public static final String RESULT_PAGE = "RESULT";
// 保存面板引用
private QuestionCountPanel questionCountPanel;
private ExamPanel examPanel;
public MainFrame() {
userService = new User_service();
initializeUI();
setTitle("数学学习系统");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
setLocationRelativeTo(null);
}
private void initializeUI() {
setTitle("数学学习软件");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
setLocationRelativeTo(null); // 窗口居中
cardLayout = new CardLayout();
mainPanel = new JPanel(cardLayout);
createLoginPanel();
createRegisterPanel();
createGradeSelectionPanel();
createExamPanel();
createResultPanel();
createWrongQuestionsPanel(); // 新增错题面板
createPasswordChangePanel();
add(mainPanel);
showLoginPanel();
}
private void createLoginPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(50, 100, 50, 100));
JLabel titleLabel = new JLabel("数学学习系统", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
panel.add(titleLabel, BorderLayout.NORTH);
JPanel formPanel = new JPanel(new GridLayout(3, 2, 15, 15));
formPanel.setBorder(BorderFactory.createEmptyBorder(30, 50, 30, 50));
formPanel.add(new JLabel("用户名:", JLabel.CENTER));
loginIdField = new JTextField();
formPanel.add(loginIdField);
formPanel.add(new JLabel("密码:", JLabel.CENTER));
loginPasswordField = new JPasswordField();
formPanel.add(loginPasswordField);
panel.add(formPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
JButton loginButton = createStyledButton("登录");
loginButton.addActionListener(e -> login());
buttonPanel.add(loginButton);
JButton registerButton = createStyledButton("注册账号");
registerButton.addActionListener(e -> showRegisterPanel());
buttonPanel.add(registerButton);
JButton exitButton = createStyledButton("退出");
exitButton.addActionListener(e -> exitchoice());
buttonPanel.add(exitButton);
panel.add(buttonPanel, BorderLayout.SOUTH);
mainPanel.add(panel, "Login");
}
private void createRegisterPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(30, 80, 30, 80));
JLabel titleLabel = new JLabel("用户注册", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
panel.add(titleLabel, BorderLayout.NORTH);
JPanel formPanel = new JPanel(new GridLayout(5, 2, 15, 15));
formPanel.setBorder(BorderFactory.createEmptyBorder(20, 50, 20, 50));
formPanel.add(new JLabel("邮箱:", JLabel.CENTER));
regEmailField = new JTextField();
formPanel.add(regEmailField);
formPanel.add(new JLabel("验证码:", JLabel.CENTER));
JPanel codePanel = new JPanel(new BorderLayout(5, 0));
regCodeField = new JTextField();
codePanel.add(regCodeField, BorderLayout.CENTER);
JButton sendCodeButton = new JButton("发送验证码");
sendCodeButton.setFont(new Font("微软雅黑", Font.PLAIN, 10));
sendCodeButton.addActionListener(e -> sendVerificationCode());
codePanel.add(sendCodeButton, BorderLayout.EAST);
formPanel.add(codePanel);
formPanel.add(new JLabel("密码:", JLabel.CENTER));
regPasswordField = new JPasswordField();
formPanel.add(regPasswordField);
formPanel.add(new JLabel("确认密码:", JLabel.CENTER));
regConfirmPasswordField = new JPasswordField();
formPanel.add(regConfirmPasswordField);
formPanel.add(new JLabel("用户名:", JLabel.CENTER));
regUserIdField = new JTextField();
formPanel.add(regUserIdField);
panel.add(formPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
JButton registerButton = createStyledButton("注册");
registerButton.addActionListener(e -> register());
buttonPanel.add(registerButton);
JButton backButton = createStyledButton("返回登录");
backButton.addActionListener(e -> showLoginPanel());
buttonPanel.add(backButton);
panel.add(buttonPanel, BorderLayout.SOUTH);
mainPanel.add(panel, "Register");
}
private void createGradeSelectionPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(50, 100, 50, 100));
JLabel titleLabel = new JLabel("选择学习阶段", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
panel.add(titleLabel, BorderLayout.NORTH);
JPanel gradePanel = new JPanel(new GridLayout(4, 1, 20, 20));
gradePanel.setBorder(BorderFactory.createEmptyBorder(50, 100, 50, 100));
JButton primaryButton = createGradeButton("小学");
primaryButton.addActionListener(e -> startExam("小学"));
gradePanel.add(primaryButton);
JButton juniorButton = createGradeButton("初中");
juniorButton.addActionListener(e -> startExam("初中"));
gradePanel.add(juniorButton);
JButton seniorButton = createGradeButton("高中");
seniorButton.addActionListener(e -> startExam("高中"));
gradePanel.add(seniorButton);
panel.add(gradePanel, BorderLayout.CENTER);
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
JButton changePasswordButton = createStyledButton("修改密码");
changePasswordButton.addActionListener(e -> showPasswordChangePanel());
bottomPanel.add(changePasswordButton);
// 新增:删除账号按钮
JButton deleteAccountButton = createStyledButton("删除账号");
deleteAccountButton.setBackground(new Color(220, 20, 60)); // 红色背景提示危险操作
deleteAccountButton.addActionListener(e -> deleteAccount());
bottomPanel.add(deleteAccountButton);
JButton logoutButton = createStyledButton("退出登录");
logoutButton.addActionListener(e -> logout());
bottomPanel.add(logoutButton);
panel.add(bottomPanel, BorderLayout.SOUTH);
mainPanel.add(panel, "GradeSelection");
}
// 新增:删除当前账号功能
private void deleteAccount() {
// 简单确认对话框
int result = JOptionPane.showConfirmDialog(
this,
"确定要删除当前账号吗?\n用户名" + currentUserId + "\n此操作不可恢复",
"确认删除账号",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE
);
if (result == JOptionPane.YES_OPTION) {
// 直接使用当前用户名作为邮箱(根据您的系统设计,用户名可能就是邮箱)
// 或者让用户输入邮箱确认
String userEmail = JOptionPane.showInputDialog(
this,
"请输入您的邮箱进行确认:",
"确认删除",
JOptionPane.QUESTION_MESSAGE
);
if (userEmail != null && !userEmail.trim().isEmpty()) {
// 调用User_service的Unregister函数删除账号
String deleteResult = userService.Unregister(userEmail.trim());
if (deleteResult.equals("删除成功")) {
JOptionPane.showMessageDialog(this, "账号删除成功!");
// 清除当前用户信息并返回登录界面
currentUserId = null;
examService = null;
showLoginPanel();
} else {
JOptionPane.showMessageDialog(this, "删除失败:" + deleteResult);
}
}
}
}
private void createExamPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 40, 20, 40));
// 顶部信息栏
JPanel topPanel = new JPanel(new BorderLayout());
questionNumberLabel = new JLabel("", JLabel.LEFT);
questionNumberLabel.setFont(new Font("微软雅黑", Font.BOLD, 16));
topPanel.add(questionNumberLabel, BorderLayout.WEST);
panel.add(topPanel, BorderLayout.NORTH);
// 题目区域
JPanel questionPanel = new JPanel(new BorderLayout(10, 20));
questionPanel.setBorder(BorderFactory.createEmptyBorder(30, 50, 30, 50));
questionLabel = new JLabel("", JLabel.CENTER);
questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 18));
questionPanel.add(questionLabel, BorderLayout.NORTH);
// 选项区域
JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 10, 10));
optionsPanel.setBorder(BorderFactory.createEmptyBorder(20, 100, 20, 100));
optionButtons = new JRadioButton[4];
optionGroup = new ButtonGroup();
for (int i = 0; i < 4; i++) {
optionButtons[i] = new JRadioButton();
optionButtons[i].setFont(new Font("微软雅黑", Font.PLAIN, 16));
optionGroup.add(optionButtons[i]);
optionsPanel.add(optionButtons[i]);
}
questionPanel.add(optionsPanel, BorderLayout.CENTER);
panel.add(questionPanel, BorderLayout.CENTER);
// 导航按钮区域
JPanel navPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
prevButton = createStyledButton("上一题");
prevButton.addActionListener(e -> goToPreviousQuestion());
navPanel.add(prevButton);
nextButton = createStyledButton("下一题");
nextButton.addActionListener(e -> goToNextQuestion());
navPanel.add(nextButton);
submitButton = createStyledButton("提交试卷");
submitButton.addActionListener(e -> submitExam());
navPanel.add(submitButton);
panel.add(navPanel, BorderLayout.SOUTH);
mainPanel.add(panel, "Exam");
}
private void createResultPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(50, 100, 50, 100));
resultLabel = new JLabel("", JLabel.CENTER);
resultLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
panel.add(resultLabel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 30, 20));
// 新增查看错题按钮
JButton reviewButton = createStyledButton("查看错题");
reviewButton.addActionListener(e -> showWrongQuestions());
buttonPanel.add(reviewButton);
JButton continueButton = createStyledButton("继续做题");
continueButton.addActionListener(e -> showGradeSelectionPanel());
buttonPanel.add(continueButton);
JButton exitButton = createStyledButton("退出程序");
exitButton.addActionListener(e -> System.exit(0));
buttonPanel.add(exitButton);
panel.add(buttonPanel, BorderLayout.SOUTH);
mainPanel.add(panel, "Result");
}
private void createWrongQuestionsPanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 40, 20, 40));
// 标题
JLabel titleLabel = new JLabel("错题回顾", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
panel.add(titleLabel, BorderLayout.NORTH);
// 错题内容区域(使用滚动面板)
JTextArea wrongQuestionsArea = new JTextArea();
wrongQuestionsArea.setFont(new Font("微软雅黑", Font.PLAIN, 14));
wrongQuestionsArea.setEditable(false);
wrongQuestionsArea.setLineWrap(true);
wrongQuestionsArea.setWrapStyleWord(true);
JScrollPane scrollPane = new JScrollPane(wrongQuestionsArea);
scrollPane.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
panel.add(scrollPane, BorderLayout.CENTER);
// 存储错题文本区域引用
panel.putClientProperty("wrongQuestionsArea", wrongQuestionsArea);
// 返回按钮
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
JButton backButton = createStyledButton("返回成绩");
backButton.addActionListener(e -> showResultPanel());
bottomPanel.add(backButton);
panel.add(bottomPanel, BorderLayout.SOUTH);
mainPanel.add(panel, "WrongQuestions");
}
private void createPasswordChangePanel() {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(50, 100, 50, 100));
JLabel titleLabel = new JLabel("修改密码", JLabel.CENTER);
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
panel.add(titleLabel, BorderLayout.NORTH);
JPanel formPanel = new JPanel(new GridLayout(4, 2, 15, 15));
formPanel.setBorder(BorderFactory.createEmptyBorder(30, 80, 30, 80));
formPanel.add(new JLabel("邮箱:", JLabel.RIGHT));
JTextField emailField = new JTextField();
formPanel.add(emailField);
formPanel.add(new JLabel("原密码:", JLabel.RIGHT));
JPasswordField oldPasswordField = new JPasswordField();
formPanel.add(oldPasswordField);
// 创建各个面板并添加到主面板
mainPanel.add(new LoginPanel(this), LOGIN_PAGE);
mainPanel.add(new RegisterPanel(this), REGISTER_PAGE);
mainPanel.add(new GradeSelectPanel(this), GRADE_SELECT_PAGE);
formPanel.add(new JLabel("新密码:", JLabel.RIGHT));
JPasswordField newPasswordField = new JPasswordField();
formPanel.add(newPasswordField);
// 保存题目数量面板引用
questionCountPanel = new QuestionCountPanel(this);
mainPanel.add(questionCountPanel, QUESTION_COUNT_PAGE);
formPanel.add(new JLabel("确认新密码:", JLabel.RIGHT));
JPasswordField confirmPasswordField = new JPasswordField();
formPanel.add(confirmPasswordField);
// 保存考试面板引用
examPanel = new ExamPanel(this);
mainPanel.add(examPanel, EXAM_PAGE);
panel.add(formPanel, BorderLayout.CENTER);
mainPanel.add(new ResultPanel(this), RESULT_PAGE);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
JButton changeButton = createStyledButton("确认修改");
changeButton.addActionListener(e -> {
String email = emailField.getText();
String oldPassword = new String(oldPasswordField.getPassword());
String newPassword = new String(newPasswordField.getPassword());
String confirmPassword = new String(confirmPasswordField.getPassword());
changePassword(email, oldPassword, newPassword, confirmPassword);
});
buttonPanel.add(changeButton);
JButton backButton = createStyledButton("返回");
backButton.addActionListener(e -> showGradeSelectionPanel());
buttonPanel.add(backButton);
panel.add(buttonPanel, BorderLayout.SOUTH);
mainPanel.add(panel, "PasswordChange");
}
private JButton createStyledButton(String text) {
JButton button = new JButton(text);
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
button.setFocusPainted(false);
button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
return button;
}
private JButton createGradeButton(String text) {
JButton button = new JButton(text);
button.setFont(new Font("微软雅黑", Font.BOLD, 18));
button.setFocusPainted(false);
button.setBorder(BorderFactory.createEmptyBorder(20, 0, 20, 0));
return button;
}
// 界面显示方法
private void showLoginPanel() {
loginIdField.setText("");
loginPasswordField.setText("");
cardLayout.show(mainPanel, "Login");
}
private void showRegisterPanel() {
regEmailField.setText("");
regCodeField.setText("");
regPasswordField.setText("");
regConfirmPasswordField.setText("");
regUserIdField.setText("");
cardLayout.show(mainPanel, "Register");
}
private void showGradeSelectionPanel() {
cardLayout.show(mainPanel, "GradeSelection");
}
private void showPasswordChangePanel() {
cardLayout.show(mainPanel, "PasswordChange");
}
private void showExamPanel() {
cardLayout.show(mainPanel, "Exam");
}
private void showResultPanel() {
cardLayout.show(mainPanel, "Result");
}
private void showWrongQuestionsPanel() {
cardLayout.show(mainPanel, "WrongQuestions");
}
// 业务逻辑方法
private void login() {
String id = loginIdField.getText().trim();
String password = new String(loginPasswordField.getPassword());
if (id.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入用户名和密码");
return;
}
String result = userService.login(id, password);
if (result.equals("登陆成功")) {
currentUserId = id;
showGradeSelectionPanel();
} else {
JOptionPane.showMessageDialog(this, result);
}
}
private void sendVerificationCode() {
String email = regEmailField.getText().trim();
if (email.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入邮箱地址");
return;
}
String result = userService.register(email);
JOptionPane.showMessageDialog(this, result);
}
private void register() {
String email = regEmailField.getText().trim();
String code = regCodeField.getText().trim();
String password = new String(regPasswordField.getPassword());
String confirmPassword = new String(regConfirmPasswordField.getPassword());
String userId = regUserIdField.getText().trim();
if (email.isEmpty() || code.isEmpty() || password.isEmpty() || userId.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写所有字段");
return;
}
if (!password.equals(confirmPassword)) {
JOptionPane.showMessageDialog(this, "两次输入的密码不一致");
return;
}
String result = userService.check_register(email, code, password, userId);
JOptionPane.showMessageDialog(this, result);
if (result.equals("注册成功")) {
showLoginPanel();
}
}
private void startExam(String gradeType) {
String input = JOptionPane.showInputDialog(this,
"请输入" + gradeType + "题目数量:", "题目数量", JOptionPane.QUESTION_MESSAGE);
if (input == null) return;
try {
int numQuestions = Integer.parseInt(input.trim());
if (numQuestions < 10 || numQuestions > 30) {
JOptionPane.showMessageDialog(this, "题目数量范围是10-30");
return;
}
examService = new Exam_service(numQuestions, gradeType, currentUserId);
showCurrentQuestion();
showExamPanel();
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(this, "请输入有效的数字");
}
}
private void showCurrentQuestion() {
Question question = examService.get_now_question();
if (question != null) {
int currentIndex = examService.get_now_index();
int totalQuestions = examService.get_paper().size();
questionNumberLabel.setText(String.format("第 %d 题 / 共 %d 题",
currentIndex + 1, totalQuestions));
questionLabel.setText("<html><div style='text-align: center; font-size: 16px;'>" +
question.toString() + "</div></html>");
for (int i = 0; i < 4; i++) {
String optionText = question.getOptions(i);
optionButtons[i].setText((char)('A' + i) + ": " + optionText);
}
Integer userAnswer = examService.get_user_answer(currentIndex);
optionGroup.clearSelection();
if (userAnswer != null && userAnswer >= 0 && userAnswer < 4) {
optionButtons[userAnswer].setSelected(true);
}
updateNavigationButtons();
}
}
private void updateNavigationButtons() {
int currentIndex = examService.get_now_index();
int totalQuestions = examService.get_paper().size();
prevButton.setEnabled(currentIndex > 0);
nextButton.setEnabled(currentIndex < totalQuestions - 1);
submitButton.setEnabled(currentIndex == totalQuestions - 1);
}
private void goToPreviousQuestion() {
if (examService.pre_one()) {
showCurrentQuestion();
}
}
private void goToNextQuestion() {
int selectedOption = getSelectedOption();
if (selectedOption == -1) {
JOptionPane.showMessageDialog(this, "请选择一个答案");
return;
}
boolean hasNext = examService.next_one(selectedOption);
if (hasNext) {
showCurrentQuestion();
} else {
showExamResult();
}
}
private void submitExam() {
int selectedOption = getSelectedOption();
if (selectedOption == -1) {
JOptionPane.showMessageDialog(this, "请选择一个答案");
return;
}
examService.next_one(selectedOption);
showExamResult();
}
private int getSelectedOption() {
for (int i = 0; i < 4; i++) {
if (optionButtons[i].isSelected()) {
return i;
}
}
return -1;
}
private void showExamResult() {
Exam_result result = examService.calculate_result();
String resultText = String.format(
"<html><div style='text-align: center;'>" +
"<p>考试完成!</p>" +
"<p>%s数学测试</p>" +
"<p>总题数: %d</p>" +
"<p>答对题数: %d</p>" +
"<p>得分: %.1f</p>" +
"<p>用时: %s</p>" +
"<p>正确率: %s</p>" +
"</div></html>",
result.getExamType(),
result.getTotalQuestions(),
result.getCorrectAnswers(),
result.getScore(),
result.get_time(),
result.getCorrectRate()
);
resultLabel.setText(resultText);
showResultPanel();
}
// 新增:查看错题功能
// 修正:查看错题功能 - 简洁显示
private void showWrongQuestions() {
// 获取考试结果
Exam_result result = examService.calculate_result();
java.util.List<Integer> wrongQuestionIndices = result.getWrongQuestions();
if (wrongQuestionIndices.isEmpty()) {
JOptionPane.showMessageDialog(this, "恭喜!本次考试没有错题!");
return;
}
// 获取错题面板和文本区域
JPanel wrongQuestionsPanel = (JPanel) mainPanel.getComponent(5); // 第6个面板是错题面板
JTextArea wrongQuestionsArea = (JTextArea) ((JScrollPane) wrongQuestionsPanel.getComponent(1)).getViewport().getView();
// 构建错题显示内容
StringBuilder sb = new StringBuilder();
sb.append("本次考试共有 ").append(wrongQuestionIndices.size()).append(" 道错题:\n\n");
java.util.ArrayList<Question> paper = examService.get_paper();
for (int i = 0; i < wrongQuestionIndices.size(); i++) {
int questionIndex = wrongQuestionIndices.get(i);
Question question = paper.get(questionIndex);
// 只显示一个题号
sb.append("第 ").append(questionIndex + 1).append(" 题:");
sb.append(question.toString()).append("\n");
// 显示所有选项,在正确答案后打勾
sb.append("选项:\n");
for (int j = 0; j < 4; j++) {
char optionChar = (char) ('A' + j);
sb.append(" ").append(optionChar).append(". ").append(question.getOptions(j));
// 标记正确答案
if (question.getOptions(j).equals(question.getAnswer())) {
sb.append(" √");
}
// 标记用户选择的错误答案
Integer userAnswer = examService.get_user_answer(questionIndex);
if (userAnswer != null && userAnswer == j && !question.getOptions(j).equals(question.getAnswer())) {
sb.append(" X");
}
sb.append("\n");
}
sb.append("\n");
sb.append("-".repeat(50)).append("\n\n");
}
add(mainPanel);
wrongQuestionsArea.setText(sb.toString());
showWrongQuestionsPanel();
// 默认显示登录页面
showPanel(LOGIN_PAGE);
}
private void changePassword(String email, String oldPassword, String newPassword, String confirmPassword) {
if (email.isEmpty() || oldPassword.isEmpty() || newPassword.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写所有字段");
return;
}
if (!newPassword.equals(confirmPassword)) {
JOptionPane.showMessageDialog(this, "两次输入的新密码不一致");
return;
}
String result = userService.change_pwd(email, oldPassword, newPassword);
JOptionPane.showMessageDialog(this, result);
public void showPanel(String panelName) {
cardLayout.show(mainPanel, panelName);
if (result.equals("修改成功")) {
showGradeSelectionPanel();
// 当切换到考试界面时,自动触发同步
if (EXAM_PAGE.equals(panelName) && examPanel != null) {
System.out.println("自动触发考试界面同步");
examPanel.autoSyncExamState();
}
}
private void logout() {
currentUserId = null;
examService = null;
showLoginPanel();
// 获取主框架实例,用于面板间通信
public MainFrame getMainFrame() {
return this;
}
public void exitchoice(){
System.exit(0);
// 添加缺失的方法
public QuestionCountPanel getQuestionCountPanel() {
return questionCountPanel;
}
// 主函数
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new MainFrame().setVisible(true);
});
// 获取考试面板
public ExamPanel getExamPanel() {
return examPanel;
}
}

@ -0,0 +1,27 @@
package ui;
import javax.swing.*;
public class MathLearningApp {
public static void main(String[] args) {
// 确保界面创建在事件分发线程中
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
// 设置系统外观 - 使用正确的方法
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
try {
// 如果系统外观设置失败,使用默认的跨平台外观
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
}
new MainFrame().setVisible(true);
}
});
}
}

@ -0,0 +1,264 @@
package ui.panels;
import Base.Exam_result;
import ui.MainFrame;
import ui.service.BackendService;
import javax.swing.*;
import java.awt.*;
public class ExamPanel extends JPanel {
private MainFrame mainFrame;
private JLabel questionLabel;
private JRadioButton[] optionButtons;
private ButtonGroup buttonGroup;
private JLabel progressLabel;
private JButton prevBtn;
private JButton nextBtn;
private JButton submitBtn;
private JButton forceReloadBtn;
// 添加自动同步标志
private boolean autoSynced = false;
public ExamPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeUI();
}
private void initializeUI() {
setLayout(new BorderLayout(10, 10));
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 进度显示
progressLabel = new JLabel("等待试卷加载...", JLabel.CENTER);
progressLabel.setFont(new Font("宋体", Font.BOLD, 16));
// 题目显示
questionLabel = new JLabel("", JLabel.CENTER);
questionLabel.setFont(new Font("宋体", Font.PLAIN, 16));
questionLabel.setBorder(BorderFactory.createEmptyBorder(20, 10, 20, 10));
// 选项面板
JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 10, 10));
optionsPanel.setBorder(BorderFactory.createTitledBorder("请选择答案"));
optionButtons = new JRadioButton[4];
buttonGroup = new ButtonGroup();
for (int i = 0; i < 4; i++) {
optionButtons[i] = new JRadioButton();
optionButtons[i].setFont(new Font("宋体", Font.PLAIN, 14));
buttonGroup.add(optionButtons[i]);
optionsPanel.add(optionButtons[i]);
}
// 按钮面板
JPanel buttonPanel = new JPanel(new GridLayout(1, 4, 10, 10));
prevBtn = new JButton("上一题");
nextBtn = new JButton("下一题");
submitBtn = new JButton("提交试卷");
forceReloadBtn = new JButton("强制同步");
prevBtn.addActionListener(e -> showPreviousQuestion());
nextBtn.addActionListener(e -> showNextQuestion());
submitBtn.addActionListener(e -> finishExam());
forceReloadBtn.addActionListener(e -> forceSyncExamState());
buttonPanel.add(prevBtn);
buttonPanel.add(nextBtn);
buttonPanel.add(submitBtn);
buttonPanel.add(forceReloadBtn);
// 添加到主面板
add(progressLabel, BorderLayout.NORTH);
add(questionLabel, BorderLayout.CENTER);
add(optionsPanel, BorderLayout.EAST);
add(buttonPanel, BorderLayout.SOUTH);
// 初始状态显示等待
showWaitingState();
}
// 添加自动同步方法
public void autoSyncExamState() {
if (!autoSynced) {
System.out.println("=== 自动同步试卷状态 ===");
forceSyncExamState();
autoSynced = true;
}
}
// 显示等待状态
private void showWaitingState() {
progressLabel.setText("等待同步");
questionLabel.setText("<html><div style='text-align: center; padding: 20px;'>" +
"等待试卷数据同步,请稍候...</div></html>");
String[] waitingOptions = {"A. 同步中...", "B. 同步中...", "C. 同步中...", "D. 同步中..."};
for (int i = 0; i < 4; i++) {
optionButtons[i].setText(waitingOptions[i]);
optionButtons[i].setEnabled(false);
}
buttonGroup.clearSelection();
// 禁用所有功能按钮
prevBtn.setEnabled(false);
nextBtn.setEnabled(false);
submitBtn.setEnabled(false);
forceReloadBtn.setEnabled(true);
}
private void forceSyncExamState() {
System.out.println("=== 强制同步试卷状态 ===");
// 重置自动同步标志
autoSynced = true;
// 强制重新加载
BackendService.reloadCurrentQuestion();
// 检查试卷状态
String status = BackendService.getExamStatus();
System.out.println("试卷状态: " + status);
int totalQuestions = BackendService.getTotalQuestions();
int currentIndex = BackendService.getCurrentQuestionIndex();
System.out.println("题目总数: " + totalQuestions);
System.out.println("当前索引: " + currentIndex);
if ("READY".equals(status) && totalQuestions > 0) {
showCurrentQuestion();
} else {
showErrorState("试卷同步失败,状态: " + status +
",题目数: " + totalQuestions +
",当前索引: " + currentIndex);
}
}
private void showErrorState(String errorMessage) {
progressLabel.setText("系统提示");
questionLabel.setText("<html><div style='text-align: center; color: red; padding: 20px;'>" +
errorMessage + "</div></html>");
// 设置默认选项
String[] defaultOptions = {"A. 请点击强制同步", "B. 检查后端日志", "C. 重新生成试卷", "D. 联系技术支持"};
for (int i = 0; i < 4; i++) {
optionButtons[i].setText(defaultOptions[i]);
optionButtons[i].setEnabled(false);
}
buttonGroup.clearSelection();
// 禁用功能按钮,只保留同步按钮
prevBtn.setEnabled(false);
nextBtn.setEnabled(false);
submitBtn.setEnabled(false);
forceReloadBtn.setEnabled(true);
}
private void showCurrentQuestion() {
String questionText = BackendService.getCurrentQuestionText();
String[] options = BackendService.getCurrentQuestionOptions();
int currentIndex = BackendService.getCurrentQuestionIndex();
int totalQuestions = BackendService.getTotalQuestions();
// 更新显示
progressLabel.setText(String.format("第 %d/%d 题", currentIndex + 1, totalQuestions));
questionLabel.setText(questionText);
// 更新选项
for (int i = 0; i < 4; i++) {
optionButtons[i].setText(options[i]);
optionButtons[i].setEnabled(true);
}
// 恢复用户之前的选择
Integer userAnswer = BackendService.getUserAnswer(currentIndex);
if (userAnswer != null && userAnswer >= 0 && userAnswer < 4) {
optionButtons[userAnswer].setSelected(true);
} else {
buttonGroup.clearSelection();
}
// 更新按钮状态
updateButtonStates();
}
private void showNextQuestion() {
int selectedAnswer = getSelectedAnswer();
if (selectedAnswer != -1) {
boolean hasNext = BackendService.submitAnswer(selectedAnswer);
if (hasNext) {
showCurrentQuestion();
} else {
finishExam();
}
} else {
JOptionPane.showMessageDialog(this, "请选择一个答案");
}
}
private void showPreviousQuestion() {
if (BackendService.getPreviousQuestion()) {
showCurrentQuestion();
}
}
private int getSelectedAnswer() {
for (int i = 0; i < 4; i++) {
if (optionButtons[i].isSelected()) {
return i;
}
}
return -1;
}
private void updateButtonStates() {
int currentIndex = BackendService.getCurrentQuestionIndex();
int totalQuestions = BackendService.getTotalQuestions();
boolean hasPrevious = currentIndex > 0;
boolean hasNext = currentIndex < totalQuestions - 1;
boolean isLastQuestion = currentIndex == totalQuestions - 1;
prevBtn.setEnabled(hasPrevious && totalQuestions > 0);
nextBtn.setEnabled(hasNext && totalQuestions > 0);
submitBtn.setEnabled(isLastQuestion && totalQuestions > 0);
forceReloadBtn.setEnabled(true);
}
private void finishExam() {
System.out.println("=== 完成考试 ===");
// 提交最后一题的答案
int selectedAnswer = getSelectedAnswer();
if (selectedAnswer != -1) {
System.out.println("提交最后一题答案: " + selectedAnswer);
BackendService.submitAnswer(selectedAnswer);
} else {
System.out.println("最后一题未选择答案,提交默认答案");
BackendService.submitAnswer(0);
}
// 计算并缓存考试结果
Exam_result result = BackendService.finishExam();
if (result != null) {
System.out.println("考试结果计算成功,准备跳转到结果页面");
} else {
System.out.println("考试结果计算失败");
JOptionPane.showMessageDialog(this,
"考试结果计算失败,请重新考试",
"错误",
JOptionPane.ERROR_MESSAGE);
}
// 重置自动同步标志
autoSynced = false;
// 显示考试结果
mainFrame.showPanel(MainFrame.RESULT_PAGE);
}
}

@ -0,0 +1,79 @@
package ui.panels;
import ui.MainFrame;
import ui.service.BackendService;
import javax.swing.*;
import java.awt.*;
public class GradeSelectPanel extends JPanel {
private MainFrame mainFrame;
private String selectedGrade;
public GradeSelectPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeUI();
}
private void initializeUI() {
setLayout(new GridLayout(5, 1, 10, 10));
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
JLabel titleLabel = new JLabel("选择年级", JLabel.CENTER);
titleLabel.setFont(new Font("宋体", Font.BOLD, 18));
JButton primaryBtn = new JButton("小学");
JButton middleBtn = new JButton("初中");
JButton highBtn = new JButton("高中");
JButton backBtn = new JButton("返回");
// 为每个按钮设置年级并跳转
primaryBtn.addActionListener(e -> {
selectedGrade = "小学";
showQuestionCountPanel();
});
middleBtn.addActionListener(e -> {
selectedGrade = "初中";
showQuestionCountPanel();
});
highBtn.addActionListener(e -> {
selectedGrade = "高中";
showQuestionCountPanel();
});
backBtn.addActionListener(e -> {
mainFrame.showPanel(MainFrame.LOGIN_PAGE);
});
add(titleLabel);
add(primaryBtn);
add(middleBtn);
add(highBtn);
add(backBtn);
}
// 显示题目数量选择面板
private void showQuestionCountPanel() {
// 使用更简单的方式传递年级信息
// 直接跳转到题目数量页面,年级信息会在进入时设置
try {
// 尝试通过主框架获取题目数量面板
QuestionCountPanel questionCountPanel = mainFrame.getQuestionCountPanel();
if (questionCountPanel != null) {
questionCountPanel.setGrade(selectedGrade);
mainFrame.showPanel(MainFrame.QUESTION_COUNT_PAGE);
} else {
// 如果获取失败,使用备用方案
JOptionPane.showMessageDialog(this,
"系统初始化中,请稍后重试");
}
} catch (Exception e) {
// 如果出现异常,使用备用方案
System.out.println("获取题目数量面板异常: " + e.getMessage());
// 直接跳转,年级信息将在进入页面后设置
mainFrame.showPanel(MainFrame.QUESTION_COUNT_PAGE);
}
}
}

@ -0,0 +1,59 @@
package ui.panels;
import ui.MainFrame;
import ui.service.BackendService;
import javax.swing.*;
import java.awt.*;
public class LoginPanel extends JPanel {
private MainFrame mainFrame;
public LoginPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeUI();
}
private void initializeUI() {
setLayout(new GridLayout(4, 2, 10, 10));
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
JLabel userIdLabel = new JLabel("用户名:");
JTextField userIdField = new JTextField();
JLabel passwordLabel = new JLabel("密码:");
JPasswordField passwordField = new JPasswordField();
JButton loginBtn = new JButton("登录");
JButton registerBtn = new JButton("注册");
loginBtn.addActionListener(e -> {
String userId = userIdField.getText();
String password = new String(passwordField.getPassword());
if (userId.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入用户名和密码");
return;
}
// 调用后端登录服务
boolean success = BackendService.login(userId, password);
if (success) {
JOptionPane.showMessageDialog(this, "登录成功");
mainFrame.showPanel(MainFrame.GRADE_SELECT_PAGE);
} else {
JOptionPane.showMessageDialog(this, "登录失败,请检查用户名和密码");
}
});
registerBtn.addActionListener(e -> {
mainFrame.showPanel(MainFrame.REGISTER_PAGE);
});
add(userIdLabel);
add(userIdField);
add(passwordLabel);
add(passwordField);
add(loginBtn);
add(registerBtn);
}
}

@ -0,0 +1,123 @@
package ui.panels;
import ui.MainFrame;
import ui.service.BackendService;
import javax.swing.*;
import java.awt.*;
public class QuestionCountPanel extends JPanel {
private MainFrame mainFrame;
private String currentGrade;
public QuestionCountPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
this.currentGrade = "小学";
initializeUI();
}
public void setGrade(String grade) {
this.currentGrade = grade;
System.out.println("设置年级: " + grade);
}
private void initializeUI() {
setLayout(new GridLayout(7, 2, 10, 10));
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
JLabel titleLabel = new JLabel("生成试卷", JLabel.CENTER);
titleLabel.setFont(new Font("宋体", Font.BOLD, 16));
JLabel countLabel = new JLabel("题目数量:");
JTextField countField = new JTextField("10");
JLabel gradeLabel = new JLabel("当前年级:");
JLabel currentGradeLabel = new JLabel(currentGrade);
JButton generateBtn = new JButton("生成试卷");
JButton testBtn = new JButton("测试模式");
JButton backBtn = new JButton("返回");
generateBtn.addActionListener(e -> {
try {
int count = Integer.parseInt(countField.getText());
if (count <= 0 || count > 50) {
JOptionPane.showMessageDialog(this, "请输入1-50之间的题目数量");
return;
}
System.out.println("=== 生成试卷请求 ===");
System.out.println("年级: " + currentGrade + ", 题目数量: " + count);
// 确保用户ID存在
if (BackendService.getCurrentUserId() == null) {
BackendService.setTempUserId("temp_user_" + System.currentTimeMillis());
}
boolean success = BackendService.generatePaper(currentGrade, count);
if (success) {
// 立即检查状态
String status = BackendService.getExamStatus();
int totalQuestions = BackendService.getTotalQuestions();
int currentIndex = BackendService.getCurrentQuestionIndex();
System.out.println("生成后状态: " + status +
", 题目数: " + totalQuestions +
", 当前索引: " + currentIndex);
if ("READY".equals(status) && totalQuestions > 0) {
JOptionPane.showMessageDialog(this,
String.format("成功生成%d道%s数学题目", totalQuestions, currentGrade));
// 重置自动同步状态
BackendService.resetAutoSync();
// 跳转到考试界面
mainFrame.showPanel(MainFrame.EXAM_PAGE);
} else {
JOptionPane.showMessageDialog(this,
"试卷生成但状态异常,状态: " + status +
",题目数: " + totalQuestions +
",当前索引: " + currentIndex);
}
} else {
JOptionPane.showMessageDialog(this, "试卷生成失败,请检查控制台日志");
}
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(this, "请输入有效的数字");
}
});
testBtn.addActionListener(e -> {
// 使用测试数据
System.out.println("=== 测试模式 ===");
// 确保用户ID存在
if (BackendService.getCurrentUserId() == null) {
BackendService.setTempUserId("test_user_" + System.currentTimeMillis());
}
boolean success = BackendService.generatePaper("小学", 5);
if (success) {
JOptionPane.showMessageDialog(this, "测试试卷生成成功");
// 重置自动同步状态
BackendService.resetAutoSync();
mainFrame.showPanel(MainFrame.EXAM_PAGE);
} else {
JOptionPane.showMessageDialog(this, "测试试卷生成失败");
}
});
backBtn.addActionListener(e -> {
mainFrame.showPanel(MainFrame.GRADE_SELECT_PAGE);
});
add(titleLabel);
add(new JLabel());
add(countLabel);
add(countField);
add(gradeLabel);
add(currentGradeLabel);
add(generateBtn);
add(testBtn);
add(backBtn);
}
}

@ -0,0 +1,137 @@
package ui.panels;
import ui.MainFrame;
import ui.service.BackendService;
import javax.swing.*;
import java.awt.*;
public class RegisterPanel extends JPanel {
private MainFrame mainFrame;
private JTextField emailField;
private JTextField userIdField;
private JTextField codeField;
private JPasswordField passwordField;
private JPasswordField confirmPasswordField;
public RegisterPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeUI();
}
private void initializeUI() {
setLayout(new GridLayout(8, 2, 10, 10));
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
JLabel emailLabel = new JLabel("邮箱:");
emailField = new JTextField();
JLabel userIdLabel = new JLabel("用户名:");
userIdField = new JTextField();
JLabel codeLabel = new JLabel("验证码:");
codeField = new JTextField();
JLabel passwordLabel = new JLabel("密码:");
passwordField = new JPasswordField();
JLabel confirmPasswordLabel = new JLabel("确认密码:");
confirmPasswordField = new JPasswordField();
JButton sendCodeBtn = new JButton("发送验证码");
JButton registerBtn = new JButton("注册");
JButton backBtn = new JButton("返回登录");
sendCodeBtn.addActionListener(e -> {
String email = emailField.getText().trim();
if (email.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入邮箱");
return;
}
boolean success = BackendService.sendRegistrationCode(email);
if (success) {
JOptionPane.showMessageDialog(this, "验证码已发送到您的邮箱");
} else {
JOptionPane.showMessageDialog(this, "验证码发送失败,请检查邮箱是否正确");
}
});
registerBtn.addActionListener(e -> {
String email = emailField.getText().trim();
String userId = userIdField.getText().trim();
String code = codeField.getText().trim();
String password = new String(passwordField.getPassword()).trim();
String confirmPassword = new String(confirmPasswordField.getPassword()).trim();
// 验证输入
if (email.isEmpty() || userId.isEmpty() || code.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写所有字段");
return;
}
if (!password.equals(confirmPassword)) {
JOptionPane.showMessageDialog(this, "两次输入的密码不一致");
return;
}
// 检查密码复杂度
if (!checkPasswordComplexity(password)) {
JOptionPane.showMessageDialog(this,
"密码必须包含大小写字母和数字长度6-20位");
return;
}
// 调用验证和注册
boolean success = BackendService.verifyRegistration(email, code, password, userId);
if (success) {
JOptionPane.showMessageDialog(this, "注册成功!");
// 清空表单
clearForm();
mainFrame.showPanel(MainFrame.LOGIN_PAGE);
} else {
JOptionPane.showMessageDialog(this, "验证失败,请检查验证码");
}
});
backBtn.addActionListener(e -> {
mainFrame.showPanel(MainFrame.LOGIN_PAGE);
});
add(emailLabel);
add(emailField);
add(userIdLabel);
add(userIdField);
add(codeLabel);
add(codeField);
add(passwordLabel);
add(passwordField);
add(confirmPasswordLabel);
add(confirmPasswordField);
add(sendCodeBtn);
add(new JLabel()); // 空标签占位
add(registerBtn);
add(backBtn);
}
private boolean checkPasswordComplexity(String password) {
if (password == null || password.length() < 6 || password.length() > 20) {
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;
}
private void clearForm() {
emailField.setText("");
userIdField.setText("");
codeField.setText("");
passwordField.setText("");
confirmPasswordField.setText("");
}
}

@ -0,0 +1,274 @@
package ui.panels;
import Base.Exam_result;
import ui.MainFrame;
import ui.service.BackendService;
import javax.swing.*;
import java.awt.*;
public class ResultPanel extends JPanel {
private MainFrame mainFrame;
private JTextArea resultArea;
public ResultPanel(MainFrame mainFrame) {
this.mainFrame = mainFrame;
initializeUI();
// 不在构造函数中加载结果,等显示时再加载
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible) {
// 当面板变为可见时加载考试结果
loadExamResult();
}
}
private void initializeUI() {
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
JLabel resultLabel = new JLabel("考试结果", JLabel.CENTER);
resultLabel.setFont(new Font("宋体", Font.BOLD, 20));
resultArea = new JTextArea();
resultArea.setEditable(false);
resultArea.setFont(new Font("宋体", Font.PLAIN, 14));
resultArea.setLineWrap(true);
resultArea.setWrapStyleWord(true);
resultArea.setBackground(new Color(240, 240, 240));
JButton retryBtn = new JButton("再次练习");
JButton backBtn = new JButton("返回首页");
JButton debugBtn = new JButton("调试信息");
JPanel buttonPanel = new JPanel(new GridLayout(1, 3, 10, 10));
buttonPanel.add(retryBtn);
buttonPanel.add(debugBtn);
buttonPanel.add(backBtn);
add(resultLabel, BorderLayout.NORTH);
add(new JScrollPane(resultArea), BorderLayout.CENTER);
add(buttonPanel, BorderLayout.SOUTH);
retryBtn.addActionListener(e -> {
BackendService.clearCachedResult();
mainFrame.showPanel(MainFrame.GRADE_SELECT_PAGE);
});
backBtn.addActionListener(e -> {
BackendService.clearCachedResult();
mainFrame.showPanel(MainFrame.LOGIN_PAGE);
});
debugBtn.addActionListener(e -> {
showDebugInfo();
});
// 初始显示等待信息
resultArea.setText("正在加载考试结果,请稍候...");
}
private void loadExamResult() {
System.out.println("=== ResultPanel: 加载考试结果 ===");
// 显示加载中信息
resultArea.setText("正在计算考试结果...\n\n请稍候...");
// 获取考试结果
Exam_result result = BackendService.getCachedExamResult();
System.out.println("缓存结果: " + (result != null ? "存在" : "null"));
if (result == null) {
System.out.println("缓存结果为空,重新计算考试结果");
result = BackendService.finishExam();
}
if (result != null) {
System.out.println("成功获取考试结果,准备显示");
String resultText = buildResultText(result);
resultArea.setText(resultText);
System.out.println("考试结果显示完成");
} else {
System.out.println("考试结果为null显示错误信息");
String errorText = "无法获取考试结果\n\n" +
"系统调试信息:\n" +
"- 考试状态: " + BackendService.getExamStatus() + "\n" +
"- 试卷题目总数: " + BackendService.getTotalQuestions() + "\n" +
"- 实际题目数量: " + BackendService.getActualQuestionCount() + "\n" +
"- 已回答题目: " + BackendService.getUserAnswerCount() + "\n" +
"- 当前索引: " + BackendService.getCurrentQuestionIndex() + "\n\n" +
"可能的原因:\n" +
"1. 考试未正常完成\n" +
"2. 试卷数据异常\n" +
"3. 系统内部错误\n\n" +
"请点击'再次练习'重新开始考试。";
resultArea.setText(errorText);
System.out.println("考试结果加载失败");
}
}
private String buildResultText(Exam_result result) {
StringBuilder sb = new StringBuilder();
sb.append("=== 考试结果 ===\n\n");
// 考试基本信息
if (result.getExamType() != null && !result.getExamType().isEmpty()) {
sb.append("考试类型: ").append(result.getExamType()).append("\n");
} else {
sb.append("考试类型: 数学测试\n");
}
// 完全覆盖从Exam_result获取的数据使用我们自己的计算
int totalQuestions = BackendService.getActualQuestionCount();
int answeredCount = BackendService.getUserAnswerCount();
double score = result.getScore();
System.out.println("构建结果文本 - 实际题目: " + totalQuestions + ", 已回答: " + answeredCount + ", 分数: " + score);
// 计算正确题目数 - 基于分数反推
int correctAnswers = calculateCorrectAnswersFromScore(score, totalQuestions);
// 确保数据合理性
if (correctAnswers < 0) correctAnswers = 0;
if (totalQuestions <= 0) {
totalQuestions = Math.max(answeredCount, 1); // 防止除以零
}
if (correctAnswers > totalQuestions) {
correctAnswers = totalQuestions;
}
if (score < 0) score = 0;
if (score > 100) score = 100;
sb.append(String.format("总分: %.1f\n", score));
sb.append(String.format("正确题目: %d/%d\n", correctAnswers, totalQuestions));
// 计算正确率
if (totalQuestions > 0) {
double correctRate = (double) correctAnswers / totalQuestions * 100;
sb.append(String.format("正确率: %.1f%%\n", correctRate));
} else {
sb.append("正确率: 0.0%\n");
}
// 格式化用时
String timeUsed = formatExamTime(result.get_time());
sb.append("用时: ").append(timeUsed).append("\n");
sb.append("\n=== 详细分析 ===\n");
sb.append(String.format("您答对了 %d 道题\n", correctAnswers));
int wrongAnswers = totalQuestions - correctAnswers;
if (wrongAnswers < 0) wrongAnswers = 0;
sb.append(String.format("答错了 %d 道题\n", wrongAnswers));
// 添加评分
sb.append("\n=== 评分 ===\n");
if (score >= 90) {
sb.append("优秀!继续保持!\n");
} else if (score >= 80) {
sb.append("良好!还有提升空间!\n");
} else if (score >= 60) {
sb.append("及格!需要更多练习!\n");
} else {
sb.append("不及格!建议重新学习!\n");
}
// 添加额外信息
sb.append("\n=== 考试统计 ===\n");
sb.append("实际题目数量: ").append(totalQuestions).append("\n");
sb.append("已回答题目: ").append(answeredCount).append("\n");
return sb.toString();
}
// 从分数反推正确题目数
private int calculateCorrectAnswersFromScore(double score, int totalQuestions) {
if (totalQuestions <= 0) return 0;
// 根据分数计算正确题目数
int correctAnswers = (int) Math.round(score / 100.0 * totalQuestions);
// 确保在合理范围内
if (correctAnswers < 0) correctAnswers = 0;
if (correctAnswers > totalQuestions) correctAnswers = totalQuestions;
System.out.println("从分数反推正确题目: " + correctAnswers + " (分数: " + score + ", 总题: " + totalQuestions + ")");
return correctAnswers;
}
// 格式化考试时间
private String formatExamTime(Object timeObj) {
if (timeObj == null) {
return "未知";
}
try {
// 尝试转换为字符串
String timeStr = timeObj.toString();
System.out.println("原始时间字符串: " + timeStr);
// 如果已经是格式化的时间,直接返回
if (timeStr.contains("分") || timeStr.contains("秒")) {
return timeStr;
}
// 如果是数字,转换为时间格式
try {
long seconds = Long.parseLong(timeStr);
return formatTime(seconds);
} catch (NumberFormatException e) {
// 如果不是数字,返回原始字符串
return timeStr;
}
} catch (Exception e) {
System.out.println("格式化考试时间异常: " + e.getMessage());
return "未知";
}
}
// 格式化时间(秒转换为分:秒)
private String formatTime(long seconds) {
// 确保秒数合理
if (seconds < 0) seconds = 0;
if (seconds > 86400) { // 如果超过一天,设为最大值
seconds = 86400;
}
long minutes = seconds / 60;
long remainingSeconds = seconds % 60;
if (minutes > 0) {
return minutes + "分" + remainingSeconds + "秒";
} else {
return remainingSeconds + "秒";
}
}
private void showDebugInfo() {
StringBuilder debugInfo = new StringBuilder();
debugInfo.append("=== 系统调试信息 ===\n\n");
debugInfo.append("考试状态: ").append(BackendService.getExamStatus()).append("\n");
debugInfo.append("试卷题目总数: ").append(BackendService.getTotalQuestions()).append("\n");
debugInfo.append("实际题目数量: ").append(BackendService.getActualQuestionCount()).append("\n");
debugInfo.append("用户答案数量: ").append(BackendService.getUserAnswerCount()).append("\n");
debugInfo.append("当前索引: ").append(BackendService.getCurrentQuestionIndex()).append("\n");
Exam_result cachedResult = BackendService.getCachedExamResult();
debugInfo.append("缓存结果: ").append(cachedResult != null ? "存在" : "null").append("\n");
if (cachedResult != null) {
debugInfo.append("- 总分: ").append(cachedResult.getScore()).append("\n");
debugInfo.append("- 正确题目: ").append(cachedResult.getCorrectAnswers()).append("\n");
debugInfo.append("- 总题目: ").append(cachedResult.getTotalQuestions()).append("\n");
debugInfo.append("- 考试类型: ").append(cachedResult.getExamType()).append("\n");
debugInfo.append("- 用时(原始): ").append(cachedResult.get_time()).append("\n");
debugInfo.append("- 用时类型: ").append(cachedResult.get_time() != null ? cachedResult.get_time().getClass().getName() : "null").append("\n");
}
JOptionPane.showMessageDialog(this, debugInfo.toString(), "调试信息", JOptionPane.INFORMATION_MESSAGE);
}
}
Loading…
Cancel
Save