最终版 #7

Merged
p95fco63j merged 17 commits from develop into main 3 months ago

@ -0,0 +1,7 @@
import controller.AppController;
public class Main {
public static void main(String[] args) {
new AppController(); // 启动控制器
}
}

@ -0,0 +1,111 @@
package controller;
import ui.LoginPanel;
import ui.RegisterPanel;
import ui.DifficultySelectionPanel;
import model.User;
import view.QuizPanel;
import javax.swing.*;
import java.awt.*;
/**
*
*/
public class AppController {
private JFrame frame;
private UserManager userManager;
public AppController() {
userManager = new UserManager();
frame = new JFrame("学生数学学习系统");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(450, 400);
frame.setLocationRelativeTo(null);
frame.setLayout(new BorderLayout());
showLoginPanel();
frame.setVisible(true);
}
public void showLoginPanel() {
frame.getContentPane().removeAll();
frame.add(new LoginPanel(this));
frame.revalidate();
frame.repaint();
}
public void showRegisterPanel() {
frame.getContentPane().removeAll();
frame.add(new RegisterPanel(this));
frame.revalidate();
frame.repaint();
}
public void showDifficultySelectionPanel() {
frame.getContentPane().removeAll();
frame.add(new DifficultySelectionPanel(this));
frame.revalidate();
frame.repaint();
}
public void showQuizPanel(String level, int count) {
frame.getContentPane().removeAll();
frame.add(new QuizPanel(this, level, count));
frame.revalidate();
frame.repaint();
}
public void handleLogin(String email, String password) {
if (userManager.checkLogin(email, password)) {
JOptionPane.showMessageDialog(frame, "登录成功!");
// 获取用户并显示注册的用户名
User user = userManager.getUserByEmail(email);
String displayName = user != null ? user.getUsername() : email;
JOptionPane.showMessageDialog(frame, "欢迎回来," + displayName + "!");
showDifficultySelectionPanel();
} else {
JOptionPane.showMessageDialog(frame, "邮箱或密码错误!");
}
}
public void handleRegister(String email, String password, String username) {
if (userManager.isUserExist(email)) {
JOptionPane.showMessageDialog(frame, "该邮箱已注册!");
return;
}
// 传递 username 参数
userManager.addUser(email, password, username);
JOptionPane.showMessageDialog(frame, "注册成功!请登录。");
showLoginPanel();
}
public void handleDifficultySelection(String level) {
String countInput = JOptionPane.showInputDialog(
frame,
"请输入需要生成的题目数量 (1-50):",
"题目数量",
JOptionPane.INFORMATION_MESSAGE
);
if (countInput == null) {
return;
}
try {
int count = Integer.parseInt(countInput);
if (count < 1 || count > 50) {
JOptionPane.showMessageDialog(frame, "题目数量必须在1-50之间");
return;
}
showQuizPanel(level, count);
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(frame, "请输入有效的数字!");
}
}
public UserManager getUserManager() {
return userManager;
}
}

@ -0,0 +1,80 @@
package controller;
import model.User;
import java.io.*;
import java.util.*;
/**
*
*/
public class UserManager {
private static final String USER_FILE = "users.txt";
private Map<String, User> users = new HashMap<>(); // 修改为存储 User 对象
public UserManager() {
loadUsers();
}
public boolean checkLogin(String email, String password) {
User user = users.get(email);
return user != null && user.getPassword().equals(password);
}
public boolean isUserExist(String email) {
return users.containsKey(email);
}
public boolean isUsernameExists(String username) {
for (User user : users.values()) {
if (user.getUsername().equalsIgnoreCase(username)) {
return true;
}
}
return false;
}
// 修改 addUser 方法,接收 username 参数
public void addUser(String email, String password, String username) {
users.put(email, new User(email, password, username));
saveUsers();
}
// 添加获取用户的方法
public User getUserByEmail(String email) {
return users.get(email);
}
private void loadUsers() {
File file = new File(USER_FILE);
if (!file.exists()) return;
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
while ((line = br.readLine()) != null) {
String[] parts = line.split(",");
if (parts.length >= 2) {
String email = parts[0];
String password = parts[1];
String username = email; // 默认用户名为邮箱
if (parts.length >= 3) {
username = parts[2];
}
users.put(email, new User(email, password, username));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void saveUsers() {
try (PrintWriter pw = new PrintWriter(new FileWriter(USER_FILE))) {
for (Map.Entry<String, User> entry : users.entrySet()) {
User user = entry.getValue();
// 保存 email, password, username 三列
pw.println(user.getEmail() + "," + user.getPassword() + "," + user.getUsername());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

@ -0,0 +1,14 @@
package model;
public class Question {
private String text;
private String userAnswer;
public Question(String text) {
this.text = text;
}
public String getText() { return text; }
public String getUserAnswer() { return userAnswer; }
public void setUserAnswer(String ans) { this.userAnswer = ans; }
}

@ -0,0 +1,136 @@
package model;
import java.util.*;
/**
*
* + - * / ()
*
*
*/
public class QuestionGenerator {
private static final Random rand = new Random();
public static List<Question> generateQuestions(String level, int count) {
Set<String> uniqueQuestions = new HashSet<>();
List<Question> list = new ArrayList<>();
while (list.size() < count) {
String q;
switch (level) {
case "小学":
q = genPrimary();
break;
case "初中":
q = genMiddle();
break;
default:
q = genHigh();
}
if (!uniqueQuestions.contains(q)) {
uniqueQuestions.add(q);
double correct = calculate(q);
String[] opts = getOptions(q);
// 使用新构造函数传递正确答案
list.add(new Question(q, opts, String.format("%.2f", correct)));
}
}
return list;
}
// 小学难度
private static String genPrimary() {
int a = rand.nextInt(50) + 1;
int b = rand.nextInt(50) + 1;
char op = "+-*/".charAt(rand.nextInt(4));
return a + " " + op + " " + b;
}
// 初中难度
private static String genMiddle() {
int a = rand.nextInt(20) + 1;
int b = rand.nextInt(5) + 1;
String[] ops = {a + "^2", "√" + (a * b), "(" + a + "+" + b + ")^2"};
return ops[rand.nextInt(ops.length)];
}
// 高中难度
private static String genHigh() {
String[] funcs = {"sin", "cos", "tan"};
int angle = (rand.nextInt(12) + 1) * 15;
return funcs[rand.nextInt(3)] + "(" + angle + "°)";
}
// 模拟生成四个选项(正确答案随机放置)
private static String[] getOptions(String question) {
double correct = calculate(question);
String[] opts = new String[4];
int correctPos = rand.nextInt(4);
for (int i = 0; i < 4; i++) {
opts[i] = (i == correctPos)
? String.format("%.2f", correct)
: String.format("%.2f", correct + rand.nextDouble() * 5 - 2.5);
}
return opts;
}
private static double calculate(String q) {
try {
if (q.contains("sin")) {
int angle = Integer.parseInt(q.replaceAll("\\D", ""));
return Math.sin(Math.toRadians(angle));
}
if (q.contains("cos")) {
int angle = Integer.parseInt(q.replaceAll("\\D", ""));
return Math.cos(Math.toRadians(angle));
}
if (q.contains("tan")) {
int angle = Integer.parseInt(q.replaceAll("\\D", ""));
return Math.tan(Math.toRadians(angle));
}
if (q.contains("√")) {
int num = Integer.parseInt(q.replaceAll("\\D", ""));
return Math.sqrt(num);
}
if (q.contains("^2")) {
int num = Integer.parseInt(q.replaceAll("\\D", ""));
return num * num;
}
// 简单算术
String[] tokens = q.split(" ");
double a = Double.parseDouble(tokens[0]);
double b = Double.parseDouble(tokens[2]);
return switch (tokens[1]) {
case "+" -> a + b;
case "-" -> a - b;
case "*" -> a * b;
case "/" -> b != 0 ? a / b : 0;
default -> 0;
};
} catch (Exception e) {
return 0;
}
}
// Question 内部类
// 修改Question内部类添加新的构造函数
public static class Question {
public String questionText;
public String[] options;
public String correctAnswer;
public Question(String text, String[] options) {
this.questionText = text;
this.options = options;
// 旧的错误实现this.correctAnswer = options[0];
// 改为使用新构造函数
}
// 新增构造函数,正确设置正确答案
public Question(String text, String[] options, String correctAnswer) {
this.questionText = text;
this.options = options;
this.correctAnswer = correctAnswer;
}
}
}

@ -0,0 +1,24 @@
package model;
public class User {
private String email;
private String password;
private String username;
// 修改构造函数,添加 username 参数
public User(String email, String password, String username) {
this.email = email;
this.password = password;
this.username = username;
}
// 保留原有无参数构造函数,用于旧数据兼容
public User(String email, String password) {
this(email, password, email); // 默认用户名为邮箱
}
public String getEmail() { return email; }
public String getPassword() { return password; }
public String getUsername() { return username; }
}

@ -0,0 +1,39 @@
package ui;
import controller.AppController;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class DifficultySelectionPanel extends JPanel {
private AppController controller;
public DifficultySelectionPanel(AppController controller) {
this.controller = controller;
setLayout(new GridLayout(3, 1, 10, 10));
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 创建难度按钮
JButton elementaryBtn = createDifficultyButton("小学", "小学");
JButton middleBtn = createDifficultyButton("初中", "初中");
JButton highBtn = createDifficultyButton("高中", "高中");
add(elementaryBtn);
add(middleBtn);
add(highBtn);
}
private JButton createDifficultyButton(String text, String level) {
JButton btn = new JButton(text);
btn.setFont(new Font("微软雅黑", Font.BOLD, 16));
btn.setPreferredSize(new Dimension(200, 40));
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
controller.handleDifficultySelection(level);
}
});
return btn;
}
}

@ -0,0 +1,57 @@
package ui;
import controller.AppController;
import javax.swing.*;
import java.awt.*;
/**
*
*/
public class LoginPanel extends JPanel {
private JTextField emailField;
private JPasswordField passwordField;
public LoginPanel(AppController controller) {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(8, 8, 8, 8);
gbc.fill = GridBagConstraints.HORIZONTAL;
JLabel title = new JLabel("学生数学学习系统", JLabel.CENTER);
title.setFont(new Font("微软雅黑", Font.BOLD, 22));
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2;
add(title, gbc);
gbc.gridwidth = 1;
gbc.gridx = 0; gbc.gridy = 1;
add(new JLabel("邮箱:"), gbc);
gbc.gridx = 1;
emailField = new JTextField(20);
add(emailField, gbc);
gbc.gridx = 0; gbc.gridy = 2;
add(new JLabel("密码:"), gbc);
gbc.gridx = 1;
passwordField = new JPasswordField(20);
add(passwordField, gbc);
JButton loginButton = new JButton("登录");
gbc.gridx = 0; gbc.gridy = 3;
add(loginButton, gbc);
JButton registerButton = new JButton("注册");
gbc.gridx = 1;
add(registerButton, gbc);
loginButton.addActionListener(e -> {
String email = emailField.getText().trim();
String password = new String(passwordField.getPassword());
controller.handleLogin(email, password);
});
registerButton.addActionListener(e -> controller.showRegisterPanel());
}
}

@ -0,0 +1,127 @@
package ui;
import controller.AppController;
import controller.UserManager;
import util.EmailUtil;
import javax.swing.*;
import java.awt.*;
/**
*
*/
public class RegisterPanel extends JPanel {
private JTextField emailField;
private JTextField codeField;
private JPasswordField passwordField;
private JPasswordField confirmField;
private JTextField usernameField; // 新增用户名输入框
private String sentCode = null;
public RegisterPanel(AppController controller) {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(8, 8, 8, 8);
gbc.fill = GridBagConstraints.HORIZONTAL;
JLabel title = new JLabel("用户注册", JLabel.CENTER);
title.setFont(new Font("微软雅黑", Font.BOLD, 22));
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2;
add(title, gbc);
gbc.gridwidth = 1;
gbc.gridx = 0; gbc.gridy = 1;
add(new JLabel("邮箱:"), gbc);
gbc.gridx = 1;
emailField = new JTextField(20);
add(emailField, gbc);
gbc.gridx = 0; gbc.gridy = 2;
add(new JLabel("用户名:"), gbc); // 修改标签
gbc.gridx = 1;
usernameField = new JTextField(20); // 新增用户名输入框
add(usernameField, gbc);
gbc.gridx = 0; gbc.gridy = 3;
add(new JLabel("验证码:"), gbc);
gbc.gridx = 1;
codeField = new JTextField(10);
add(codeField, gbc);
gbc.gridx = 0; gbc.gridy = 4;
add(new JLabel("密码:"), gbc);
gbc.gridx = 1;
passwordField = new JPasswordField(20);
add(passwordField, gbc);
gbc.gridx = 0; gbc.gridy = 5;
add(new JLabel("确认密码:"), gbc);
gbc.gridx = 1;
confirmField = new JPasswordField(20);
add(confirmField, gbc);
JButton sendCodeButton = new JButton("发送验证码");
gbc.gridx = 0; gbc.gridy = 6;
add(sendCodeButton, gbc);
JButton registerButton = new JButton("注册");
gbc.gridx = 1;
add(registerButton, gbc);
JButton backButton = new JButton("返回登录");
gbc.gridx = 0; gbc.gridy = 7; gbc.gridwidth = 2;
add(backButton, gbc);
sendCodeButton.addActionListener(e -> {
String email = emailField.getText().trim();
if (!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
JOptionPane.showMessageDialog(this, "邮箱格式不正确!");
return;
}
sentCode = EmailUtil.sendVerificationCode(email);
JOptionPane.showMessageDialog(this, "验证码已发送(调试模式控制台查看)!");
});
registerButton.addActionListener(e -> {
String email = emailField.getText().trim();
String code = codeField.getText().trim();
String password = new String(passwordField.getPassword());
String confirm = new String(confirmField.getPassword());
String username = usernameField.getText().trim(); // 获取用户名
if (sentCode == null) {
JOptionPane.showMessageDialog(this, "请先获取验证码!");
return;
}
if (!code.equals(sentCode)) {
JOptionPane.showMessageDialog(this, "验证码错误!");
return;
}
if (!password.equals(confirm)) {
JOptionPane.showMessageDialog(this, "两次密码不一致!");
return;
}
if (!password.matches("(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[A-Za-z\\d]{6,10}")) {
JOptionPane.showMessageDialog(this, "密码需包含大小写字母与数字长度6-10位");
return;
}
//防止用户名重复
if (controller.getUserManager().isUsernameExists(username)) {
JOptionPane.showMessageDialog(this, "用户名已存在,请更换!");
return;
}
if (username.isEmpty() || password.isEmpty() || email.isEmpty() || code.isEmpty()) {
JOptionPane.showMessageDialog(this, "请填写完整信息!");
return;
}
controller.handleRegister(email, password, username); // 传递username
});
backButton.addActionListener(e -> controller.showLoginPanel());
}
}

@ -0,0 +1,50 @@
package util;
import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.util.Properties;
import java.util.Random;
/**
* QQ163
* 使 senderEmail / senderAuthCode
*/
public class EmailUtil {
// 修改为你的发件邮箱和授权码(⚠️不是密码)
private static final String senderEmail = "1193626695@qq.com";
private static final String senderAuthCode = "ldxzjbnsirnsbacj";
private static final String smtpHost = "smtp.qq.com"; // 163 改为 smtp.163.com
public static String sendVerificationCode(String toEmail) {
String code = String.valueOf(100000 + new Random().nextInt(900000));
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.port", "587");
props.put("mail.smtp.starttls.enable", "true");
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(senderEmail, senderAuthCode);
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(senderEmail));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
message.setSubject("【数学学习系统】注册验证码");
message.setText("您的验证码是:" + code + "\n有效期5分钟");
Transport.send(message);
System.out.println("✅ 邮件验证码已发送到:" + toEmail);
} catch (Exception e) {
System.err.println("⚠️ 邮件发送失败:" + e.getMessage());
}
return code;
}
}

@ -0,0 +1,31 @@
package util;
import model.User;
import java.io.*;
import java.util.*;
public class FileUtil {
public static Map<String, User> loadUsers(String fileName) {
Map<String, User> users = new HashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
String line;
while ((line = br.readLine()) != null) {
String[] parts = line.split(",");
users.put(parts[0], new User(parts[0], parts[1]));
}
} catch (IOException e) {
// 首次运行时文件可能不存在
}
return users;
}
public static void saveUsers(Map<String, User> users, String fileName) {
try (PrintWriter pw = new PrintWriter(new FileWriter(fileName))) {
for (User u : users.values()) {
pw.println(u.getEmail() + "," + u.getPassword());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

@ -0,0 +1,96 @@
package view;
import controller.AppController;
import model.QuestionGenerator;
import model.QuestionGenerator.Question;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class QuizPanel extends JPanel {
private List<Question> questions;
private int currentIndex = 0;
private int correctCount = 0;
private ButtonGroup group;
private JLabel questionLabel;
private JRadioButton[] options;
private JButton nextButton;
private AppController controller;
public QuizPanel(AppController controller, String level, int count) {
this.controller = controller;
this.questions = QuestionGenerator.generateQuestions(level, count);
setLayout(new BorderLayout());
questionLabel = new JLabel("", JLabel.CENTER);
questionLabel.setFont(new Font("微软雅黑", Font.BOLD, 18));
add(questionLabel, BorderLayout.NORTH);
JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 5, 5));
group = new ButtonGroup();
options = new JRadioButton[4];
for (int i = 0; i < 4; i++) {
options[i] = new JRadioButton();
group.add(options[i]);
optionsPanel.add(options[i]);
}
add(optionsPanel, BorderLayout.CENTER);
nextButton = new JButton("下一题");
add(nextButton, BorderLayout.SOUTH);
nextButton.addActionListener(e -> checkAndNext());
showQuestion();
}
private void showQuestion() {
if (currentIndex >= questions.size()) {
showResult();
return;
}
//解决进入下一题后会默认选择前一题的选项问题
group.clearSelection();
Question q = questions.get(currentIndex);
questionLabel.setText("第 " + (currentIndex + 1) + " 题:" + q.questionText);
for (int i = 0; i < 4; i++) {
options[i].setText(q.options[i]);
options[i].setSelected(false);
}
}
private void checkAndNext() {
// 从Question对象中获取正确答案而不是假设options[0]是正确答案
String correctAnswer = questions.get(currentIndex).correctAnswer;
for (JRadioButton r : options) {
if (r.isSelected() && r.getText().equals(correctAnswer)) {
correctCount++;
}
}
currentIndex++;
showQuestion();
}
private void showResult() {
removeAll();
setLayout(new GridLayout(3, 1, 10, 10));
// 添加除以0的检查
int total = questions.size();
int score = total > 0 ? (100 * correctCount / total) : 0;
JLabel result = new JLabel("你的得分:" + score + " 分", JLabel.CENTER);
result.setFont(new Font("微软雅黑", Font.BOLD, 22));
add(result);
// 其余代码保持不变
JButton retry = new JButton("再做一份");
JButton exit = new JButton("退出");
add(retry);
add(exit);
retry.addActionListener(e -> controller.showDifficultySelectionPanel());
exit.addActionListener(e -> System.exit(0));
revalidate();
repaint();
}
}

@ -0,0 +1,19 @@
package view;
import controller.AppController;
import model.User;
import javax.swing.*;
import java.awt.*;
public class ResultPanel extends JPanel {
public ResultPanel(AppController controller, User user, int score) {
setLayout(new BorderLayout());
JLabel lbl = new JLabel("用户:" + user.getEmail() + ",得分:" + score + " 分", JLabel.CENTER);
lbl.setFont(new Font("微软雅黑", Font.BOLD, 20));
add(lbl, BorderLayout.CENTER);
JButton btnBack = new JButton("返回登录");
btnBack.addActionListener(e -> controller.showLoginPanel());
add(btnBack, BorderLayout.SOUTH);
}
}
Loading…
Cancel
Save