diff --git a/doc/README.md b/doc/README.md deleted file mode 100644 index 6983ea1..0000000 --- a/doc/README.md +++ /dev/null @@ -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 - - - - - diff --git a/lib/activation-1.1.1.jar b/lib/activation-1.1.1.jar deleted file mode 100644 index 1b703ab..0000000 Binary files a/lib/activation-1.1.1.jar and /dev/null differ diff --git a/lib/javax.mail-1.6.2.jar b/lib/javax.mail-1.6.2.jar deleted file mode 100644 index 0cd0528..0000000 Binary files a/lib/javax.mail-1.6.2.jar and /dev/null differ diff --git a/src/Base/Email_settings.java b/src/Base/Email_settings.java deleted file mode 100644 index ea57b1f..0000000 --- a/src/Base/Email_settings.java +++ /dev/null @@ -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")); - } -} diff --git a/src/Base/Exam_result.java b/src/Base/Exam_result.java deleted file mode 100644 index 1b117aa..0000000 --- a/src/Base/Exam_result.java +++ /dev/null @@ -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 wrong_questions; // 错题索引 - - public Exam_result( String examType, int total, - int correct, double score, long duration, - List 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 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); - } -} diff --git a/src/Base/Question.java b/src/Base/Question.java deleted file mode 100644 index 71a0fe9..0000000 --- a/src/Base/Question.java +++ /dev/null @@ -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 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 numbers = new Stack<>(); - Stack 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; - } -} diff --git a/src/Base/User.java b/src/Base/User.java deleted file mode 100644 index 08fe956..0000000 --- a/src/Base/User.java +++ /dev/null @@ -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); - } -} diff --git a/src/Generator/G_ques.java b/src/Generator/G_ques.java deleted file mode 100644 index 5a18448..0000000 --- a/src/Generator/G_ques.java +++ /dev/null @@ -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); -} diff --git a/src/Generator/Generate_paper.java b/src/Generator/Generate_paper.java deleted file mode 100644 index ebf5c6d..0000000 --- a/src/Generator/Generate_paper.java +++ /dev/null @@ -1,53 +0,0 @@ -package Generator; - -import Base.Question; -import Service.Deal_file; - -import java.util.ArrayList; - -public class Generate_paper { - public static ArrayList g_paper(int num,String type,String id) { - ArrayList 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 all,String ques){ - for (Question q:all){ - if (q.getContent().equals(ques)){ - return true; - } - } - return false; - } -} diff --git a/src/Generator/Jun_g_ques.java b/src/Generator/Jun_g_ques.java deleted file mode 100644 index 4eb84df..0000000 --- a/src/Generator/Jun_g_ques.java +++ /dev/null @@ -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;i0){ - 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 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(); - } -} diff --git a/src/Generator/Sen_g_ques.java b/src/Generator/Sen_g_ques.java deleted file mode 100644 index f4ca55f..0000000 --- a/src/Generator/Sen_g_ques.java +++ /dev/null @@ -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 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 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(); - } - } -} diff --git a/src/Send_Email/Generate_i_code.java b/src/Send_Email/Generate_i_code.java deleted file mode 100644 index d9404ac..0000000 --- a/src/Send_Email/Generate_i_code.java +++ /dev/null @@ -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(); - } -} diff --git a/src/Send_Email/Send_email.java b/src/Send_Email/Send_email.java deleted file mode 100644 index 809024f..0000000 --- a/src/Send_Email/Send_email.java +++ /dev/null @@ -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( - "尊敬的用户:%s

" + - "您的注册验证码是:%s

" + - "验证码有效期为5分钟,请尽快完成注册。

" + - "如果不是您本人操作,请忽略此邮件。", - toEmail,Code - ); - message.setContent(content, "text/html;charset=UTF-8"); - // 发送邮件 - Transport.send(message); - return true; - } catch (Exception e) { - return false; - } - } -} diff --git a/src/Service/Deal_file.java b/src/Service/Deal_file.java deleted file mode 100644 index ff012b1..0000000 --- a/src/Service/Deal_file.java +++ /dev/null @@ -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 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()); - } - } -} diff --git a/src/Service/Exam_service.java b/src/Service/Exam_service.java deleted file mode 100644 index dc84cf2..0000000 --- a/src/Service/Exam_service.java +++ /dev/null @@ -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 paper; - private int now_index; - private Date start_time; - private Date end_time; - private Map 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 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 get_user_answers() {return user_answers;} - - public void set_now_index(int i){ - if (i==1 && now_index0){ - 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 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); - } -} diff --git a/src/Service/User_service.java b/src/Service/User_service.java deleted file mode 100644 index 8cac970..0000000 --- a/src/Service/User_service.java +++ /dev/null @@ -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 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; - } -} diff --git a/src/View/MainFrame.java b/src/View/MainFrame.java new file mode 100644 index 0000000..51ed72a --- /dev/null +++ b/src/View/MainFrame.java @@ -0,0 +1,731 @@ +package View; + +import Service.User_service; +import Service.Exam_service; +import Base.Exam_result; +import Base.Question; + +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 MainFrame() { + userService = new User_service(); + initializeUI(); + setTitle("数学学习系统"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(800, 600); + setLocationRelativeTo(null); + } + + private void initializeUI() { + 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); + + formPanel.add(new JLabel("新密码:", JLabel.RIGHT)); + JPasswordField newPasswordField = new JPasswordField(); + formPanel.add(newPasswordField); + + formPanel.add(new JLabel("确认新密码:", JLabel.RIGHT)); + JPasswordField confirmPasswordField = new JPasswordField(); + formPanel.add(confirmPasswordField); + + panel.add(formPanel, BorderLayout.CENTER); + + 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("
" + + question.toString() + "
"); + + 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( + "
" + + "

考试完成!

" + + "

%s数学测试

" + + "

总题数: %d

" + + "

答对题数: %d

" + + "

得分: %.1f

" + + "

用时: %s

" + + "

正确率: %s

" + + "
", + 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 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 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"); + } + + wrongQuestionsArea.setText(sb.toString()); + showWrongQuestionsPanel(); + } + 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); + + if (result.equals("修改成功")) { + showGradeSelectionPanel(); + } + } + + private void logout() { + currentUserId = null; + examService = null; + showLoginPanel(); + } + + public void exitchoice(){ + System.exit(0); + } + + // 主函数 + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + new MainFrame().setVisible(true); + }); + } +} \ No newline at end of file