第一次正式版本提交 #1

Merged
p95fco63j merged 4 commits from jingyou_branch into develop 3 months ago

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

@ -0,0 +1,112 @@
package controller;
import ui.LoginPanel;
import ui.RegisterPanel;
import ui.DifficultySelectionPanel; // 添加难度选择面板
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();
}
/** 显示题目选择界面QuizPanel */
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, "登录成功!");
// 跳转到难度选择界面
showDifficultySelectionPanel();
} else {
JOptionPane.showMessageDialog(frame, "邮箱或密码错误!");
}
}
/** 注册逻辑 */
public void handleRegister(String email, String password) {
if (userManager.isUserExist(email)) {
JOptionPane.showMessageDialog(frame, "该邮箱已注册!");
return;
}
userManager.addUser(email, password);
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, "请输入有效的数字!");
}
}
}

@ -0,0 +1,60 @@
package controller;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
*
*/
public class UserManager {
private static final String USER_FILE = "users.txt";
private Map<String, String> users = new HashMap<>();
public UserManager() {
loadUsers();
}
/** 检查登录 */
public boolean checkLogin(String email, String password) {
return users.containsKey(email) && users.get(email).equals(password);
}
/** 判断用户是否存在 */
public boolean isUserExist(String email) {
return users.containsKey(email);
}
/** 添加用户 */
public void addUser(String email, String password) {
users.put(email, password);
saveUsers();
}
/** 加载用户文件 */
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)
users.put(parts[0], parts[1]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/** 保存用户信息 */
private void saveUsers() {
try (PrintWriter pw = new PrintWriter(new FileWriter(USER_FILE))) {
for (Map.Entry<String, String> entry : users.entrySet()) {
pw.println(entry.getKey() + "," + entry.getValue());
}
} 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,124 @@
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);
list.add(new Question(q, getOptions(q)));
}
}
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 内部类
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]; // 默认第一个是正确答案(计算时设置)
}
}
}

@ -0,0 +1,13 @@
package model;
public class User {
private String email;
private String password;
public User(String email, String password) {
this.email = email;
this.password = password;
}
public String getEmail() { return email; }
public String getPassword() { return password; }
}

@ -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,110 @@
package ui;
import controller.AppController;
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 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;
codeField = new JTextField(10);
add(codeField, gbc);
gbc.gridx = 0; gbc.gridy = 3;
add(new JLabel("密码:"), gbc);
gbc.gridx = 1;
passwordField = new JPasswordField(20);
add(passwordField, gbc);
gbc.gridx = 0; gbc.gridy = 4;
add(new JLabel("确认密码:"), gbc);
gbc.gridx = 1;
confirmField = new JPasswordField(20);
add(confirmField, gbc);
JButton sendCodeButton = new JButton("发送验证码");
gbc.gridx = 0; gbc.gridy = 5;
add(sendCodeButton, gbc);
JButton registerButton = new JButton("注册");
gbc.gridx = 1;
add(registerButton, gbc);
JButton backButton = new JButton("返回登录");
gbc.gridx = 0; gbc.gridy = 6; 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());
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;
}
controller.handleRegister(email, password);
});
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,88 @@
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;
}
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() {
for (JRadioButton r : options) {
if (r.isSelected() && r.getText().equals(questions.get(currentIndex).options[0])) {
correctCount++;
}
}
currentIndex++;
showQuestion();
}
private void showResult() {
removeAll();
setLayout(new GridLayout(3, 1, 10, 10));
JLabel result = new JLabel("你的得分:" + (100 * correctCount / questions.size()) + " 分", 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.showLoginPanel());
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