From 13e43b22ddef95add90116f30350ca5e848e3a5f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Wed, 8 Oct 2025 21:25:24 +0800
Subject: [PATCH 01/16] =?UTF-8?q?=E9=A6=96=E6=AC=A1=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 38 +++
.idea/.gitignore | 3 +
.idea/encodings.xml | 7 +
.idea/misc.xml | 14 ++
data/users.json | 8 +
pom.xml | 50 ++++
src/main/java/mathlearning/App.java | 24 ++
src/main/java/mathlearning/model/User.java | 41 ++++
.../mathlearning/service/EmailService.java | 57 +++++
.../mathlearning/service/UserService.java | 175 +++++++++++++
src/main/java/mathlearning/ui/LoginFrame.java | 111 +++++++++
.../java/mathlearning/ui/RegisterFrame.java | 230 ++++++++++++++++++
12 files changed, 758 insertions(+)
create mode 100644 .gitignore
create mode 100644 .idea/.gitignore
create mode 100644 .idea/encodings.xml
create mode 100644 .idea/misc.xml
create mode 100644 data/users.json
create mode 100644 pom.xml
create mode 100644 src/main/java/mathlearning/App.java
create mode 100644 src/main/java/mathlearning/model/User.java
create mode 100644 src/main/java/mathlearning/service/EmailService.java
create mode 100644 src/main/java/mathlearning/service/UserService.java
create mode 100644 src/main/java/mathlearning/ui/LoginFrame.java
create mode 100644 src/main/java/mathlearning/ui/RegisterFrame.java
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5ff6309
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..359bb53
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..001e756
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/data/users.json b/data/users.json
new file mode 100644
index 0000000..07dc93d
--- /dev/null
+++ b/data/users.json
@@ -0,0 +1,8 @@
+[ {
+ "username" : "小鱼",
+ "email" : "1280556515@qq.com",
+ "passwordHash" : "$2a$12$J6oCgotwddD.tOspkOnMlOBcB9C7RcMNmR0MvaCuAutnIneXSrHm6",
+ "registrationDate" : [ 2025, 10, 8, 21, 0, 37, 863490500 ],
+ "verificationCode" : "658919",
+ "verified" : true
+} ]
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..6f1c887
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,50 @@
+
+
+ 4.0.0
+
+ com.mathlearning
+ math-learning-app
+ 1.0.0
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+
+ com.sun.mail
+ javax.mail
+ 1.6.2
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.15.2
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.15.2
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ 2.15.2
+
+
+
+
+ at.favre.lib
+ bcrypt
+ 0.9.0
+
+
+
\ No newline at end of file
diff --git a/src/main/java/mathlearning/App.java b/src/main/java/mathlearning/App.java
new file mode 100644
index 0000000..0e2ff2d
--- /dev/null
+++ b/src/main/java/mathlearning/App.java
@@ -0,0 +1,24 @@
+package mathlearning;
+
+import mathlearning.service.UserService;
+import mathlearning.ui.LoginFrame;
+
+import javax.swing.*;
+
+public class App {
+ public static void main(String[] args) {
+ // 设置UI风格
+ try {
+ UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // 启动应用程序
+ SwingUtilities.invokeLater(() -> {
+ UserService userService = new UserService();
+ LoginFrame loginFrame = new LoginFrame(userService);
+ loginFrame.setVisible(true);
+ });
+ }
+}
diff --git a/src/main/java/mathlearning/model/User.java b/src/main/java/mathlearning/model/User.java
new file mode 100644
index 0000000..57b7887
--- /dev/null
+++ b/src/main/java/mathlearning/model/User.java
@@ -0,0 +1,41 @@
+package mathlearning.model;
+
+import java.time.LocalDateTime;
+
+public class User {
+ private String username;
+ private String email;
+ private String passwordHash;
+ private LocalDateTime registrationDate;
+ private String verificationCode;
+ private boolean verified;
+
+ public User() {}
+
+ public User(String email, String passwordHash, String verificationCode) {
+ this.email = email;
+ this.passwordHash = passwordHash;
+ this.verificationCode = verificationCode;
+ this.verified = false;
+ this.registrationDate = LocalDateTime.now();
+ }
+
+ // Getters and setters
+ public String getEmail() { return email; }
+ public void setEmail(String email) { this.email = email; }
+
+ public String getPasswordHash() { return passwordHash; }
+ public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
+
+ public String getVerificationCode() { return verificationCode; }
+ public void setVerificationCode(String verificationCode) { this.verificationCode = verificationCode; }
+
+ public boolean isVerified() { return verified; }
+ public void setVerified(boolean verified) { this.verified = verified; }
+
+ public LocalDateTime getRegistrationDate() { return registrationDate; }
+ public void setRegistrationDate(LocalDateTime registrationDate) { this.registrationDate = registrationDate; }
+
+ public String getUsername() { return username; }
+ public void setUsername(String username) {this.username = username; }
+}
diff --git a/src/main/java/mathlearning/service/EmailService.java b/src/main/java/mathlearning/service/EmailService.java
new file mode 100644
index 0000000..86f5b0d
--- /dev/null
+++ b/src/main/java/mathlearning/service/EmailService.java
@@ -0,0 +1,57 @@
+package mathlearning.service;
+
+import javax.mail.*;
+import javax.mail.internet.*;
+import java.util.Properties;
+
+public class EmailService {
+ private final String host;
+ private final String port;
+ private final String username;
+ private final String password;
+ private final boolean auth;
+
+ public EmailService(String host, String port, String username, String password, boolean auth) {
+ this.host = host;
+ this.port = port;
+ this.username = username;
+ this.password = password;
+ this.auth = auth;
+ }
+
+ public boolean sendVerificationCode(String toEmail, String verificationCode) {
+ try {
+ Properties props = new Properties();
+ props.put("mail.smtp.host", host);
+ props.put("mail.smtp.port", port);
+ props.put("mail.smtp.auth", auth);
+ props.put("mail.smtp.starttls.enable", "true");
+
+ Session session = Session.getInstance(props, new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+
+ Message message = new MimeMessage(session);
+ message.setFrom(new InternetAddress(username));
+ message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail));
+ message.setSubject("数学学习软件 - 注册验证码");
+
+ String emailContent = "尊敬的用户:\n\n" +
+ "您的注册验证码是:" + verificationCode + "\n\n" +
+ "该验证码有效期为10分钟。\n\n" +
+ "如果您没有注册本软件,请忽略此邮件。\n\n" +
+ "数学学习软件团队";
+
+ message.setText(emailContent);
+
+ Transport.send(message);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/mathlearning/service/UserService.java b/src/main/java/mathlearning/service/UserService.java
new file mode 100644
index 0000000..f239562
--- /dev/null
+++ b/src/main/java/mathlearning/service/UserService.java
@@ -0,0 +1,175 @@
+package mathlearning.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import mathlearning.model.User;
+import at.favre.lib.crypto.bcrypt.BCrypt;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class UserService {
+ private static final String USERS_FILE = "data/users.json";
+ private static final long VERIFICATION_CODE_EXPIRY_MINUTES = 10;
+
+ private Map users;
+ private Map verificationCodeTimestamps;
+ private ObjectMapper objectMapper;
+
+ public UserService() {
+ this.users = new ConcurrentHashMap<>();
+ this.verificationCodeTimestamps = new ConcurrentHashMap<>();
+ this.objectMapper = new ObjectMapper();
+ objectMapper.registerModule(new JavaTimeModule());
+ loadUsers();
+ }
+
+ private void loadUsers() {
+ try {
+ File file = new File(USERS_FILE);
+ if (file.exists() && file.length() > 0) {
+ User[] userArray = objectMapper.readValue(file, User[].class);
+ for (User user : userArray) {
+ users.put(user.getEmail(), user);
+ }
+ System.out.println("成功加载 " + userArray.length + " 个用户");
+ } else {
+ saveUsers();
+ System.out.println("创建新的用户数据文件");
+ }
+ } catch (IOException e) {
+ System.err.println("加载用户数据失败: " + e.getMessage());
+ backupAndRecreateFile();
+ }
+ }
+
+ private void backupAndRecreateFile() {
+ try {
+ File file = new File(USERS_FILE);
+ if (file.exists()) {
+ File backup = new File(USERS_FILE + ".bak." + System.currentTimeMillis());
+ file.renameTo(backup);
+ System.err.println("损坏的文件已备份为: " + backup.getName());
+ }
+ saveUsers();
+ System.out.println("已重新创建用户数据文件");
+ } catch (Exception e) {
+ System.err.println("备份文件失败: " + e.getMessage());
+ }
+ }
+
+ private void saveUsers() {
+ try {
+ File file = new File(USERS_FILE);
+ file.getParentFile().mkdirs();
+ objectMapper.writerWithDefaultPrettyPrinter().writeValue(file, users.values().toArray(new User[0]));
+ } catch (IOException e) {
+ System.err.println("保存用户数据失败: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ public boolean registerUser(String email, String verificationCode) {
+ if (users.containsKey(email)) {
+ return false; // 用户已存在
+ }
+
+ // 生成密码占位符,实际密码在验证后设置
+ String tempPasswordHash = BCrypt.withDefaults().hashToString(12, "temp".toCharArray());
+ User user = new User(email, tempPasswordHash, verificationCode);
+ users.put(email, user);
+ verificationCodeTimestamps.put(email, LocalDateTime.now());
+ saveUsers();
+ return true;
+ }
+
+ public boolean verifyUser(String username, String email, String verificationCode, String password) {
+ User user = users.get(email);
+ if (user == null || !user.getVerificationCode().equals(verificationCode)) {
+ return false;
+ }
+
+ // 检查验证码是否过期
+ LocalDateTime codeTime = verificationCodeTimestamps.get(email);
+ if (codeTime == null || codeTime.plusMinutes(VERIFICATION_CODE_EXPIRY_MINUTES).isBefore(LocalDateTime.now())) {
+ return false;
+ }
+
+ // 验证密码格式
+ if (!validatePassword(password)) {
+ return false;
+ }
+
+ // 设置实际密码
+ String passwordHash = BCrypt.withDefaults().hashToString(12, password.toCharArray());
+ user.setPasswordHash(passwordHash);
+ user.setUsername(username);
+ user.setVerified(true);
+ verificationCodeTimestamps.remove(email);
+ saveUsers();
+ return true;
+ }
+
+ public boolean login(String email, String password) {
+ User user = users.get(email);
+ if (user == null || !user.isVerified()) {
+ return false;
+ }
+
+ BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), user.getPasswordHash());
+ return result.verified;
+ }
+
+ public boolean changePassword(String email, String oldPassword, String newPassword) {
+ User user = users.get(email);
+ if (user == null) {
+ return false;
+ }
+
+ // 验证旧密码
+ BCrypt.Result result = BCrypt.verifyer().verify(oldPassword.toCharArray(), user.getPasswordHash());
+ if (!result.verified) {
+ return false;
+ }
+
+ // 验证新密码格式
+ if (!validatePassword(newPassword)) {
+ return false;
+ }
+
+ // 更新密码
+ String newPasswordHash = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray());
+ user.setPasswordHash(newPasswordHash);
+ saveUsers();
+ return true;
+ }
+
+ public boolean validatePassword(String password) {
+ if (password.length() < 6 || password.length() > 10) {
+ return false;
+ }
+
+ boolean hasUpper = false;
+ boolean hasLower = false;
+ boolean hasDigit = false;
+
+ for (char c : password.toCharArray()) {
+ if (Character.isUpperCase(c)) hasUpper = true;
+ if (Character.isLowerCase(c)) hasLower = true;
+ if (Character.isDigit(c)) hasDigit = true;
+ }
+
+ return hasUpper && hasLower && hasDigit;
+ }
+
+ public boolean userExists(String email) {
+ return users.containsKey(email);
+ }
+
+ public boolean isValidEmail(String email) {
+ return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/mathlearning/ui/LoginFrame.java b/src/main/java/mathlearning/ui/LoginFrame.java
new file mode 100644
index 0000000..a9579d0
--- /dev/null
+++ b/src/main/java/mathlearning/ui/LoginFrame.java
@@ -0,0 +1,111 @@
+package mathlearning.ui;
+
+import mathlearning.model.User;
+import mathlearning.service.UserService;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class LoginFrame extends JFrame{
+ private UserService userService;
+ private JTextField emailField;
+ private JPasswordField passwordField;
+
+ public LoginFrame(UserService userService) {
+ this.userService = userService;
+ initializeUI();
+ }
+
+ private void initializeUI() {
+ setTitle("数学学习软件 - 登录");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(400, 300);
+ setLocationRelativeTo(null);
+ setResizable(false);
+
+ // 主面板
+ JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ // 标题
+ JLabel titleLabel = new JLabel("用户登录", JLabel.CENTER);
+ titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
+ mainPanel.add(titleLabel, BorderLayout.NORTH);
+
+ // 表单面板
+ JPanel formPanel = new JPanel(new GridLayout(3, 2, 10, 10));
+
+ JLabel emailLabel = new JLabel("邮箱:");
+ emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ emailField = new JTextField();
+
+ JLabel passwordLabel = new JLabel("密码:");
+ passwordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ passwordField = new JPasswordField();
+
+ formPanel.add(emailLabel);
+ formPanel.add(emailField);
+ formPanel.add(passwordLabel);
+ formPanel.add(passwordField);
+
+ mainPanel.add(formPanel, BorderLayout.CENTER);
+
+ // 按钮面板
+ JPanel buttonPanel = new JPanel(new FlowLayout());
+
+ JButton loginButton = new JButton("登录");
+ loginButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ loginButton.addActionListener(new LoginButtonListener());
+
+ JButton registerButton = new JButton("注册");
+ registerButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ registerButton.addActionListener(e -> openRegisterFrame());
+
+ buttonPanel.add(loginButton);
+ buttonPanel.add(registerButton);
+
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+ add(mainPanel);
+ }
+
+ private class LoginButtonListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String email = emailField.getText().trim();
+ String password = new String(passwordField.getPassword());
+
+ if (email.isEmpty() || password.isEmpty()) {
+ JOptionPane.showMessageDialog(LoginFrame.this,
+ "请输入邮箱和密码", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (userService.login(email, password)) {
+ JOptionPane.showMessageDialog(LoginFrame.this,
+ "登录成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
+ // 这里可以打开主界面
+ openMainFrame(email);
+ } else {
+ JOptionPane.showMessageDialog(LoginFrame.this,
+ "邮箱或密码错误", "登录失败", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ private void openRegisterFrame() {
+ RegisterFrame registerFrame = new RegisterFrame(userService, this);
+ registerFrame.setVisible(true);
+ this.setVisible(false);
+ }
+
+ private void openMainFrame(String email) {
+
+ // 这里先简单显示一个消息,后续可以扩展为主界面
+ JOptionPane.showMessageDialog(this,
+ "欢迎 " + email + "!\n登录成功,主界面功能待实现。",
+ "登录成功", JOptionPane.INFORMATION_MESSAGE);
+ }
+}
diff --git a/src/main/java/mathlearning/ui/RegisterFrame.java b/src/main/java/mathlearning/ui/RegisterFrame.java
new file mode 100644
index 0000000..ebc97c7
--- /dev/null
+++ b/src/main/java/mathlearning/ui/RegisterFrame.java
@@ -0,0 +1,230 @@
+package mathlearning.ui;
+
+import mathlearning.service.EmailService;
+import mathlearning.service.UserService;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class RegisterFrame extends JFrame{
+ private UserService userService;
+ private EmailService emailService;
+ private LoginFrame loginFrame;
+
+ private JTextField nameField;
+ private JTextField emailField;
+ private JButton sendCodeButton;
+ private JTextField codeField;
+ private JPasswordField passwordField;
+ private JPasswordField confirmPasswordField;
+ private JButton registerButton;
+ private JButton backButton;
+
+ private String verificationCode;
+
+ public RegisterFrame(UserService userService, LoginFrame loginFrame) {
+ this.userService = userService;
+ this.loginFrame = loginFrame;
+
+ // 配置邮箱服务(需要替换为真实的邮箱配置)
+ this.emailService = new EmailService(
+ "smtp.qq.com",
+ "587",
+ "2793415226@qq.com", // 替换为你的QQ邮箱
+ "rmiomlakglpjddhb", // 替换为你的授权码
+ true
+ );
+
+ initializeUI();
+ }
+
+ private void initializeUI() {
+ setTitle("数学学习软件 - 注册");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(450, 400);
+ setLocationRelativeTo(null);
+ setResizable(false);
+
+ // 主面板
+ JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ // 标题
+ JLabel titleLabel = new JLabel("用户注册", JLabel.CENTER);
+ titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
+ mainPanel.add(titleLabel, BorderLayout.NORTH);
+
+ // 表单面板
+ JPanel formPanel = new JPanel(new GridLayout(5, 2, 10, 10));
+
+ JLabel nameLabel = new JLabel("用户名:");
+ nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ nameField = new JTextField();
+
+ JLabel emailLabel = new JLabel("邮箱:");
+ emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ emailField = new JTextField();
+
+ JLabel codeLabel = new JLabel("验证码:");
+ codeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+
+ JPanel codePanel = new JPanel(new BorderLayout());
+ codeField = new JTextField();
+ sendCodeButton = new JButton("发送验证码");
+ sendCodeButton.addActionListener(new SendCodeListener());
+
+ codePanel.add(codeField, BorderLayout.CENTER);
+ codePanel.add(sendCodeButton, BorderLayout.EAST);
+
+ JLabel passwordLabel = new JLabel("密码:");
+ passwordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ passwordField = new JPasswordField();
+
+ JLabel confirmPasswordLabel = new JLabel("确认密码:");
+ confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ confirmPasswordField = new JPasswordField();
+
+ formPanel.add(nameLabel);
+ formPanel.add(nameField);
+ formPanel.add(emailLabel);
+ formPanel.add(emailField);
+ formPanel.add(codeLabel);
+ formPanel.add(codePanel);
+ formPanel.add(passwordLabel);
+ formPanel.add(passwordField);
+ formPanel.add(confirmPasswordLabel);
+ formPanel.add(confirmPasswordField);
+
+ mainPanel.add(formPanel, BorderLayout.CENTER);
+
+ // 按钮面板
+ JPanel buttonPanel = new JPanel(new FlowLayout());
+
+ registerButton = new JButton("注册");
+ registerButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ registerButton.addActionListener(new RegisterButtonListener());
+
+ backButton = new JButton("返回登录");
+ backButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ backButton.addActionListener(e -> goBackToLogin());
+
+ buttonPanel.add(registerButton);
+ buttonPanel.add(backButton);
+
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+ add(mainPanel);
+ }
+
+ private class SendCodeListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String email = emailField.getText().trim();
+
+ if (email.isEmpty()) {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "请输入邮箱地址", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (!userService.isValidEmail(email)) {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "邮箱格式不正确", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (userService.userExists(email)) {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "该邮箱已注册", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // 生成6位验证码
+ verificationCode = String.valueOf((int)((Math.random() * 9 + 1) * 100000));
+
+ // 发送验证码邮件
+ boolean sent = emailService.sendVerificationCode(email, verificationCode);
+
+ if (sent) {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "验证码已发送到您的邮箱,请查收", "成功", JOptionPane.INFORMATION_MESSAGE);
+
+ // 禁用发送按钮60秒
+ sendCodeButton.setEnabled(false);
+ new Thread(() -> {
+ try {
+ for (int i = 60; i > 0; i--) {
+ final int current = i;
+ SwingUtilities.invokeLater(() ->
+ sendCodeButton.setText(current + "秒后重发"));
+ Thread.sleep(1000);
+ }
+ SwingUtilities.invokeLater(() -> {
+ sendCodeButton.setText("发送验证码");
+ sendCodeButton.setEnabled(true);
+ });
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }).start();
+
+ } else {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "验证码发送失败,请检查邮箱地址或网络连接", "错误", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ private class RegisterButtonListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String username = nameField.getText().trim();
+ String email = emailField.getText().trim();
+ String code = codeField.getText().trim();
+ String password = new String(passwordField.getPassword());
+ String confirmPassword = new String(confirmPasswordField.getPassword());
+
+ // 验证输入
+ if (email.isEmpty() || code.isEmpty() || password.isEmpty() || username.isEmpty()) {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "请填写所有字段", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (!password.equals(confirmPassword)) {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "两次输入的密码不一致", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (!userService.validatePassword(password)) {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "密码必须为6-10位,且包含大小写字母和数字", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // 先注册用户(临时状态)
+ if (userService.registerUser(email, verificationCode)) {
+ // 验证用户并设置密码
+ if (userService.verifyUser(username, email, code, password)) {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "注册成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
+ goBackToLogin();
+ } else {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "验证码错误或已过期", "错误", JOptionPane.ERROR_MESSAGE);
+ }
+ } else {
+ JOptionPane.showMessageDialog(RegisterFrame.this,
+ "注册失败,用户可能已存在", "错误", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ private void goBackToLogin() {
+ loginFrame.setVisible(true);
+ this.dispose();
+ }
+}
From 6bdec54a9303dcb99ac0a79a5245a5aaa025eb4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Thu, 9 Oct 2025 01:20:53 +0800
Subject: [PATCH 02/16] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=94=A8=E6=88=B7?=
=?UTF-8?q?=E5=AF=B9=E8=B1=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/mathlearning/service/UserService.java | 5 +++++
src/main/java/mathlearning/ui/LoginFrame.java | 4 ++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/main/java/mathlearning/service/UserService.java b/src/main/java/mathlearning/service/UserService.java
index f239562..37b4de9 100644
--- a/src/main/java/mathlearning/service/UserService.java
+++ b/src/main/java/mathlearning/service/UserService.java
@@ -172,4 +172,9 @@ public class UserService {
public boolean isValidEmail(String email) {
return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}
+
+ public User getUser(String email ) {
+ User user = users.get(email);
+ return user;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/mathlearning/ui/LoginFrame.java b/src/main/java/mathlearning/ui/LoginFrame.java
index a9579d0..7d1c365 100644
--- a/src/main/java/mathlearning/ui/LoginFrame.java
+++ b/src/main/java/mathlearning/ui/LoginFrame.java
@@ -102,10 +102,10 @@ public class LoginFrame extends JFrame{
}
private void openMainFrame(String email) {
-
+ User user = userService.getUser(email);
// 这里先简单显示一个消息,后续可以扩展为主界面
JOptionPane.showMessageDialog(this,
- "欢迎 " + email + "!\n登录成功,主界面功能待实现。",
+ "欢迎 " + user.getUsername() + "!\n登录成功,主界面功能待实现。",
"登录成功", JOptionPane.INFORMATION_MESSAGE);
}
}
From 7884e56895a0a26ddcf936945c930dcbf7f06edf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Thu, 9 Oct 2025 02:48:02 +0800
Subject: [PATCH 03/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=98=E8=AE=B0?=
=?UTF-8?q?=E5=AF=86=E7=A0=81=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/vcs.xml | 6 +
data/users.json | 4 +-
.../mathlearning/service/UserService.java | 47 ++++
.../java/mathlearning/ui/ChangeCodeFrame.java | 226 ++++++++++++++++++
src/main/java/mathlearning/ui/LoginFrame.java | 11 +
5 files changed, 292 insertions(+), 2 deletions(-)
create mode 100644 .idea/vcs.xml
create mode 100644 src/main/java/mathlearning/ui/ChangeCodeFrame.java
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/data/users.json b/data/users.json
index 07dc93d..d478b7e 100644
--- a/data/users.json
+++ b/data/users.json
@@ -1,8 +1,8 @@
[ {
"username" : "小鱼",
"email" : "1280556515@qq.com",
- "passwordHash" : "$2a$12$J6oCgotwddD.tOspkOnMlOBcB9C7RcMNmR0MvaCuAutnIneXSrHm6",
+ "passwordHash" : "$2a$12$0.CsIN83oxG1vbZe6vNVte3JaxvX2JU2j0hyprng6meq9/N4e732m",
"registrationDate" : [ 2025, 10, 8, 21, 0, 37, 863490500 ],
- "verificationCode" : "658919",
+ "verificationCode" : "963213",
"verified" : true
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/service/UserService.java b/src/main/java/mathlearning/service/UserService.java
index 37b4de9..978cf3c 100644
--- a/src/main/java/mathlearning/service/UserService.java
+++ b/src/main/java/mathlearning/service/UserService.java
@@ -147,6 +147,48 @@ public class UserService {
return true;
}
+ public boolean setPasswordResetCode(String email, String verificationCode) {
+ User user = users.get(email);
+ if (user == null) {
+ return false; // 用户不存在
+ }
+
+ user.setVerificationCode(verificationCode);
+ verificationCodeTimestamps.put(email, LocalDateTime.now());
+ saveUsers();
+ return true;
+ }
+
+ public boolean resetPasswordWithCode(String email, String verificationCode, String newPassword) {
+ User user = users.get(email);
+ if (user == null) {
+ return false;
+ }
+
+ // 验证验证码
+ if (!user.getVerificationCode().equals(verificationCode)) {
+ return false;
+ }
+
+ // 检查验证码是否过期
+ LocalDateTime codeTime = verificationCodeTimestamps.get(email);
+ if (codeTime == null || codeTime.plusMinutes(VERIFICATION_CODE_EXPIRY_MINUTES).isBefore(LocalDateTime.now())) {
+ return false;
+ }
+
+ // 验证新密码格式
+ if (!validatePassword(newPassword)) {
+ return false;
+ }
+
+ // 更新密码
+ String newPasswordHash = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray());
+ user.setPasswordHash(newPasswordHash);
+ verificationCodeTimestamps.remove(email); // 清除验证码
+ saveUsers();
+ return true;
+ }
+
public boolean validatePassword(String password) {
if (password.length() < 6 || password.length() > 10) {
return false;
@@ -169,6 +211,11 @@ public class UserService {
return users.containsKey(email);
}
+ public boolean isUserExistsAndVerified(String email) {
+ User user = users.get(email);
+ return user != null && user.isVerified();
+ }
+
public boolean isValidEmail(String email) {
return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}
diff --git a/src/main/java/mathlearning/ui/ChangeCodeFrame.java b/src/main/java/mathlearning/ui/ChangeCodeFrame.java
new file mode 100644
index 0000000..cace250
--- /dev/null
+++ b/src/main/java/mathlearning/ui/ChangeCodeFrame.java
@@ -0,0 +1,226 @@
+package mathlearning.ui;
+
+import at.favre.lib.crypto.bcrypt.BCrypt;
+import mathlearning.model.User;
+import mathlearning.service.EmailService;
+import mathlearning.service.UserService;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class ChangeCodeFrame extends JFrame {
+ private UserService userService;
+ private EmailService emailService;
+ private LoginFrame loginFrame;
+
+ private JTextField emailField;
+ private JButton sendCodeButton;
+ private JTextField codeField;
+ private JPasswordField passwordField;
+ private JPasswordField confirmPasswordField;
+ private JButton registerButton;
+ private JButton backButton;
+
+ private String verificationCode;
+
+ public ChangeCodeFrame(UserService userService, LoginFrame loginFrame) {
+ this.userService = userService;
+ this.loginFrame = loginFrame;
+
+ // 配置邮箱服务(需要替换为真实的邮箱配置)
+ this.emailService = new EmailService(
+ "smtp.qq.com",
+ "587",
+ "2793415226@qq.com", // 替换为你的QQ邮箱
+ "rmiomlakglpjddhb", // 替换为你的授权码
+ true
+ );
+
+ initializeUI();
+ }
+
+ private void initializeUI() {
+ setTitle("数学学习软件 - 忘记密码");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(450, 400);
+ setLocationRelativeTo(null);
+ setResizable(false);
+
+ // 主面板
+ JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ // 标题
+ JLabel titleLabel = new JLabel("重置密码", JLabel.CENTER);
+ titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
+ mainPanel.add(titleLabel, BorderLayout.NORTH);
+
+ //表单面板
+ JPanel formPanel = new JPanel(new GridLayout(5, 2, 10, 10));
+
+ JLabel emailLabel = new JLabel("邮箱:");
+ emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ emailField = new JTextField();
+
+ JLabel codeLabel = new JLabel("验证码:");
+ codeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+
+ JPanel codePanel = new JPanel(new BorderLayout());
+ codeField = new JTextField();
+ sendCodeButton = new JButton("发送验证码");
+ sendCodeButton.addActionListener(new SendCodeListener());
+
+ codePanel.add(codeField, BorderLayout.CENTER);
+ codePanel.add(sendCodeButton, BorderLayout.EAST);
+
+ JLabel passwordLabel = new JLabel("新密码:");
+ passwordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ passwordField = new JPasswordField();
+
+ JLabel confirmPasswordLabel = new JLabel("确认密码:");
+ confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ confirmPasswordField = new JPasswordField();
+
+ formPanel.add(emailLabel);
+ formPanel.add(emailField);
+ formPanel.add(codeLabel);
+ formPanel.add(codePanel);
+ formPanel.add(passwordLabel);
+ formPanel.add(passwordField);
+ formPanel.add(confirmPasswordLabel);
+ formPanel.add(confirmPasswordField);
+
+ mainPanel.add(formPanel, BorderLayout.CENTER);
+
+ // 按钮面板
+ JPanel buttonPanel = new JPanel(new FlowLayout());
+
+ registerButton = new JButton("更改密码");
+ registerButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ registerButton.addActionListener(new ChangeCodeFrame.ChangeCodeButtonListener());
+
+ backButton = new JButton("返回登录");
+ backButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ backButton.addActionListener(e -> goBackToLogin());
+
+ buttonPanel.add(registerButton);
+ buttonPanel.add(backButton);
+
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+ add(mainPanel);
+ }
+
+ private class SendCodeListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String email = emailField.getText().trim();
+
+ if (email.isEmpty()) {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "请输入邮箱地址", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (!userService.isValidEmail(email)) {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "邮箱格式不正确", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // 检查用户是否存在且已验证
+ if (!userService.isUserExistsAndVerified(email)) {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "该邮箱未注册或未验证", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // 生成6位验证码
+ verificationCode = String.valueOf((int)((Math.random() * 9 + 1) * 100000));
+
+ // 发送验证码邮件
+ boolean sent = emailService.sendVerificationCode(email, verificationCode);
+
+ if (sent) {
+ // 在服务中保存验证码
+ boolean codeSaved = userService.setPasswordResetCode(email, verificationCode);
+
+ if (codeSaved) {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "验证码已发送到您的邮箱,请查收", "成功", JOptionPane.INFORMATION_MESSAGE);
+
+ // 禁用发送按钮60秒
+ sendCodeButton.setEnabled(false);
+ new Thread(() -> {
+ try {
+ for (int i = 60; i > 0; i--) {
+ final int current = i;
+ SwingUtilities.invokeLater(() ->
+ sendCodeButton.setText(current + "秒后重发"));
+ Thread.sleep(1000);
+ }
+ SwingUtilities.invokeLater(() -> {
+ sendCodeButton.setText("发送验证码");
+ sendCodeButton.setEnabled(true);
+ });
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }).start();
+ } else {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "保存验证码失败", "错误", JOptionPane.ERROR_MESSAGE);
+ }
+ } else {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "验证码发送失败,请检查邮箱地址或网络连接", "错误", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ private class ChangeCodeButtonListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String email = emailField.getText().trim();
+ String code = codeField.getText().trim();
+ String password = new String(passwordField.getPassword());
+ String confirmPassword = new String(confirmPasswordField.getPassword());
+
+ // 验证输入
+ if (email.isEmpty() || code.isEmpty() || password.isEmpty()) {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "请填写所有字段", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (!password.equals(confirmPassword)) {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "两次输入的密码不一致", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (!userService.validatePassword(password)) {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "密码必须为6-10位,且包含大小写字母和数字", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // 使用新的重置密码方法
+ if (userService.resetPasswordWithCode(email, code, password)) {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "密码重置成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
+ goBackToLogin();
+ } else {
+ JOptionPane.showMessageDialog(ChangeCodeFrame.this,
+ "重置失败,请检查验证码是否正确或是否已过期", "错误", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ private void goBackToLogin() {
+ loginFrame.setVisible(true);
+ this.dispose();
+ }
+}
diff --git a/src/main/java/mathlearning/ui/LoginFrame.java b/src/main/java/mathlearning/ui/LoginFrame.java
index 7d1c365..8084d74 100644
--- a/src/main/java/mathlearning/ui/LoginFrame.java
+++ b/src/main/java/mathlearning/ui/LoginFrame.java
@@ -63,8 +63,13 @@ public class LoginFrame extends JFrame{
registerButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
registerButton.addActionListener(e -> openRegisterFrame());
+ JButton changeCodeButton = new JButton("忘记密码?");
+ changeCodeButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ changeCodeButton.addActionListener(e -> openChangeCodeFrame());
+
buttonPanel.add(loginButton);
buttonPanel.add(registerButton);
+ buttonPanel.add(changeCodeButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
@@ -101,6 +106,12 @@ public class LoginFrame extends JFrame{
this.setVisible(false);
}
+ private void openChangeCodeFrame() {
+ ChangeCodeFrame changeCodeFrame = new ChangeCodeFrame(userService, this);
+ changeCodeFrame.setVisible(true);
+ this.setVisible(false);
+ }
+
private void openMainFrame(String email) {
User user = userService.getUser(email);
// 这里先简单显示一个消息,后续可以扩展为主界面
From 2c28c23b14c80d0f0cb9fe19d7f402408cd75715 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Thu, 9 Oct 2025 02:56:34 +0800
Subject: [PATCH 04/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=98=E8=AE=B0?=
=?UTF-8?q?=E5=AF=86=E7=A0=81=E5=8A=9F=E8=83=BD=E5=AE=8C=E5=96=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/mathlearning/ui/ChangeCodeFrame.java | 5 -----
1 file changed, 5 deletions(-)
diff --git a/src/main/java/mathlearning/ui/ChangeCodeFrame.java b/src/main/java/mathlearning/ui/ChangeCodeFrame.java
index cace250..924f9ea 100644
--- a/src/main/java/mathlearning/ui/ChangeCodeFrame.java
+++ b/src/main/java/mathlearning/ui/ChangeCodeFrame.java
@@ -1,7 +1,5 @@
package mathlearning.ui;
-import at.favre.lib.crypto.bcrypt.BCrypt;
-import mathlearning.model.User;
import mathlearning.service.EmailService;
import mathlearning.service.UserService;
@@ -194,20 +192,17 @@ public class ChangeCodeFrame extends JFrame {
"请填写所有字段", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
-
if (!password.equals(confirmPassword)) {
JOptionPane.showMessageDialog(ChangeCodeFrame.this,
"两次输入的密码不一致", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
-
if (!userService.validatePassword(password)) {
JOptionPane.showMessageDialog(ChangeCodeFrame.this,
"密码必须为6-10位,且包含大小写字母和数字", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
- // 使用新的重置密码方法
if (userService.resetPasswordWithCode(email, code, password)) {
JOptionPane.showMessageDialog(ChangeCodeFrame.this,
"密码重置成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
From 3a306a6c35dde72772bd3037b451ca5397b22ff8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Thu, 9 Oct 2025 11:17:21 +0800
Subject: [PATCH 05/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=98=E8=AE=B0?=
=?UTF-8?q?=E5=AF=86=E7=A0=81=E5=8A=9F=E8=83=BD=E5=AE=8C=E5=96=84(?=
=?UTF-8?q?=E9=82=AE=E4=BB=B6=E5=86=85=E5=AE=B9)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
data/users.json | 4 +--
.../mathlearning/service/EmailService.java | 27 ++++++++++++++-----
.../java/mathlearning/ui/ChangeCodeFrame.java | 2 +-
.../java/mathlearning/ui/RegisterFrame.java | 2 +-
4 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/data/users.json b/data/users.json
index d478b7e..2bf6bd3 100644
--- a/data/users.json
+++ b/data/users.json
@@ -1,8 +1,8 @@
[ {
"username" : "小鱼",
"email" : "1280556515@qq.com",
- "passwordHash" : "$2a$12$0.CsIN83oxG1vbZe6vNVte3JaxvX2JU2j0hyprng6meq9/N4e732m",
+ "passwordHash" : "$2a$12$fAWGM2CJSSiulyFbDWyRyefrBWr9emYrMdkD.Rw2LPKFaYQeYYW9K",
"registrationDate" : [ 2025, 10, 8, 21, 0, 37, 863490500 ],
- "verificationCode" : "963213",
+ "verificationCode" : "926911",
"verified" : true
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/service/EmailService.java b/src/main/java/mathlearning/service/EmailService.java
index 86f5b0d..cc9dcec 100644
--- a/src/main/java/mathlearning/service/EmailService.java
+++ b/src/main/java/mathlearning/service/EmailService.java
@@ -19,7 +19,7 @@ public class EmailService {
this.auth = auth;
}
- public boolean sendVerificationCode(String toEmail, String verificationCode) {
+ public boolean sendVerificationCode(String toEmail, String verificationCode, int flag) {
try {
Properties props = new Properties();
props.put("mail.smtp.host", host);
@@ -38,12 +38,11 @@ public class EmailService {
message.setFrom(new InternetAddress(username));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail));
message.setSubject("数学学习软件 - 注册验证码");
+ String emailContent="";
+
+ if (flag == 1) emailContent = registerContent(verificationCode);
+ else if (flag == 2) emailContent = resetPasswordContent(verificationCode);
- String emailContent = "尊敬的用户:\n\n" +
- "您的注册验证码是:" + verificationCode + "\n\n" +
- "该验证码有效期为10分钟。\n\n" +
- "如果您没有注册本软件,请忽略此邮件。\n\n" +
- "数学学习软件团队";
message.setText(emailContent);
@@ -54,4 +53,20 @@ public class EmailService {
return false;
}
}
+
+ public String registerContent(String verificationCode) {
+ return "尊敬的用户:\n\n" +
+ "您的注册验证码是:" + verificationCode + "\n\n" +
+ "该验证码有效期为10分钟。\n\n" +
+ "如果您没有注册本软件,请忽略此邮件。\n\n" +
+ "数学学习软件团队";
+ }
+
+ public String resetPasswordContent(String verificationCode) {
+ return "尊敬的用户:\n\n" +
+ "您的重置验证码是:" + verificationCode + "\n\n" +
+ "该验证码有效期为10分钟。\n\n" +
+ "如果您没有重置密码,请忽略此邮件。\n\n" +
+ "数学学习软件团队";
+ }
}
diff --git a/src/main/java/mathlearning/ui/ChangeCodeFrame.java b/src/main/java/mathlearning/ui/ChangeCodeFrame.java
index 924f9ea..1255136 100644
--- a/src/main/java/mathlearning/ui/ChangeCodeFrame.java
+++ b/src/main/java/mathlearning/ui/ChangeCodeFrame.java
@@ -139,7 +139,7 @@ public class ChangeCodeFrame extends JFrame {
verificationCode = String.valueOf((int)((Math.random() * 9 + 1) * 100000));
// 发送验证码邮件
- boolean sent = emailService.sendVerificationCode(email, verificationCode);
+ boolean sent = emailService.sendVerificationCode(email, verificationCode, 2);
if (sent) {
// 在服务中保存验证码
diff --git a/src/main/java/mathlearning/ui/RegisterFrame.java b/src/main/java/mathlearning/ui/RegisterFrame.java
index ebc97c7..b5fc993 100644
--- a/src/main/java/mathlearning/ui/RegisterFrame.java
+++ b/src/main/java/mathlearning/ui/RegisterFrame.java
@@ -145,7 +145,7 @@ public class RegisterFrame extends JFrame{
verificationCode = String.valueOf((int)((Math.random() * 9 + 1) * 100000));
// 发送验证码邮件
- boolean sent = emailService.sendVerificationCode(email, verificationCode);
+ boolean sent = emailService.sendVerificationCode(email, verificationCode, 1);
if (sent) {
JOptionPane.showMessageDialog(RegisterFrame.this,
From ce8dd17ebcd844ec7ee2b1deed5794d19a61bd96 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Thu, 9 Oct 2025 11:39:07 +0800
Subject: [PATCH 06/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A6=96=E6=AC=A1?=
=?UTF-8?q?=E7=99=BB=E5=BD=95=E9=9C=80=E8=A6=81=E9=80=89=E6=8B=A9=E6=95=99?=
=?UTF-8?q?=E8=82=B2=E9=98=B6=E6=AE=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
data/users.json | 3 +-
src/main/java/mathlearning/model/User.java | 7 +++
.../mathlearning/service/UserService.java | 10 ++++
src/main/java/mathlearning/ui/LoginFrame.java | 49 +++++++++++++++++--
4 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/data/users.json b/data/users.json
index 2bf6bd3..78be335 100644
--- a/data/users.json
+++ b/data/users.json
@@ -4,5 +4,6 @@
"passwordHash" : "$2a$12$fAWGM2CJSSiulyFbDWyRyefrBWr9emYrMdkD.Rw2LPKFaYQeYYW9K",
"registrationDate" : [ 2025, 10, 8, 21, 0, 37, 863490500 ],
"verificationCode" : "926911",
- "verified" : true
+ "verified" : true,
+ "type" : "初中"
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/model/User.java b/src/main/java/mathlearning/model/User.java
index 57b7887..6ee96a3 100644
--- a/src/main/java/mathlearning/model/User.java
+++ b/src/main/java/mathlearning/model/User.java
@@ -1,5 +1,7 @@
package mathlearning.model;
+import com.fasterxml.jackson.databind.BeanProperty;
+
import java.time.LocalDateTime;
public class User {
@@ -9,6 +11,7 @@ public class User {
private LocalDateTime registrationDate;
private String verificationCode;
private boolean verified;
+ private String type;
public User() {}
@@ -18,6 +21,7 @@ public class User {
this.verificationCode = verificationCode;
this.verified = false;
this.registrationDate = LocalDateTime.now();
+ this.type = null;
}
// Getters and setters
@@ -38,4 +42,7 @@ public class User {
public String getUsername() { return username; }
public void setUsername(String username) {this.username = username; }
+
+ public String getType() { return type; }
+ public void setType(String type) { this.type = type; }
}
diff --git a/src/main/java/mathlearning/service/UserService.java b/src/main/java/mathlearning/service/UserService.java
index 978cf3c..f9c0c95 100644
--- a/src/main/java/mathlearning/service/UserService.java
+++ b/src/main/java/mathlearning/service/UserService.java
@@ -189,6 +189,16 @@ public class UserService {
return true;
}
+ public boolean updateUserType(String email, String type) {
+ User user = users.get(email);
+ if (user == null) {
+ return false;
+ }
+ user.setType(type);
+ saveUsers();
+ return true;
+ }
+
public boolean validatePassword(String password) {
if (password.length() < 6 || password.length() > 10) {
return false;
diff --git a/src/main/java/mathlearning/ui/LoginFrame.java b/src/main/java/mathlearning/ui/LoginFrame.java
index 8084d74..d42000c 100644
--- a/src/main/java/mathlearning/ui/LoginFrame.java
+++ b/src/main/java/mathlearning/ui/LoginFrame.java
@@ -114,9 +114,50 @@ public class LoginFrame extends JFrame{
private void openMainFrame(String email) {
User user = userService.getUser(email);
- // 这里先简单显示一个消息,后续可以扩展为主界面
- JOptionPane.showMessageDialog(this,
- "欢迎 " + user.getUsername() + "!\n登录成功,主界面功能待实现。",
- "登录成功", JOptionPane.INFORMATION_MESSAGE);
+ // 如果用户类型为空,让用户选择类型
+ if (user.getType() == null || user.getType().isEmpty()) {
+ String[] types = {"小学", "初中", "高中"};
+ String selectedType = (String) JOptionPane.showInputDialog(
+ this,
+ "欢迎 " + user.getUsername() + "!\n请选择您的教育阶段:",
+ "选择教育阶段",
+ JOptionPane.QUESTION_MESSAGE,
+ null,
+ types,
+ types[0] // 默认选择第一个
+ );
+
+ // 如果用户选择了类型(没有点击取消)
+ if (selectedType != null) {
+ // 更新用户类型
+ boolean updated = userService.updateUserType(email, selectedType);
+ if (updated) {
+ // 更新本地user对象
+ user.setType(selectedType);
+ JOptionPane.showMessageDialog(this,
+ "登录成功!\n教育阶段:" + selectedType,
+ "登录成功", JOptionPane.INFORMATION_MESSAGE);
+ } else {
+ JOptionPane.showMessageDialog(this,
+ "登录成功!\n但教育阶段设置失败",
+ "登录成功", JOptionPane.WARNING_MESSAGE);
+ }
+ } else {
+ // 如果用户取消选择,可以设置默认类型或者保持为空
+ userService.updateUserType(email, "小学");
+ user.setType("小学");
+ JOptionPane.showMessageDialog(this,
+ "登录成功!\n已为您选择默认教育阶段:小学",
+ "登录成功", JOptionPane.INFORMATION_MESSAGE);
+ }
+ } else {
+ // 如果已经有类型,直接显示欢迎信息
+ JOptionPane.showMessageDialog(this,
+ "欢迎 " + user.getUsername() + "!\n登录成功。\n教育阶段:" + user.getType(),
+ "登录成功", JOptionPane.INFORMATION_MESSAGE);
+ }
+
+ // 后续可以在这里打开主界面
+ // openMainApplicationWindow(user);
}
}
From b02f4178c0bd12eb29750f60dce3590706a54a27 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Thu, 9 Oct 2025 11:39:14 +0800
Subject: [PATCH 07/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A6=96=E6=AC=A1?=
=?UTF-8?q?=E7=99=BB=E5=BD=95=E9=9C=80=E8=A6=81=E9=80=89=E6=8B=A9=E6=95=99?=
=?UTF-8?q?=E8=82=B2=E9=98=B6=E6=AE=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/.name | 1 +
1 file changed, 1 insertion(+)
create mode 100644 .idea/.name
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..340ca5b
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+User.java
\ No newline at end of file
From b340f6960b71b566b41eb74515c68db5e2183e70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Thu, 9 Oct 2025 21:22:27 +0800
Subject: [PATCH 08/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=BB=E8=8F=9C?=
=?UTF-8?q?=E5=8D=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/.name | 2 +-
data/users.json | 2 +-
src/main/java/mathlearning/ui/LoginFrame.java | 9 +-
src/main/java/mathlearning/ui/MainFrame.java | 137 ++++++++++++++++++
4 files changed, 146 insertions(+), 4 deletions(-)
create mode 100644 src/main/java/mathlearning/ui/MainFrame.java
diff --git a/.idea/.name b/.idea/.name
index 340ca5b..4f2c764 100644
--- a/.idea/.name
+++ b/.idea/.name
@@ -1 +1 @@
-User.java
\ No newline at end of file
+UserService.java
\ No newline at end of file
diff --git a/data/users.json b/data/users.json
index 78be335..2c43fb0 100644
--- a/data/users.json
+++ b/data/users.json
@@ -5,5 +5,5 @@
"registrationDate" : [ 2025, 10, 8, 21, 0, 37, 863490500 ],
"verificationCode" : "926911",
"verified" : true,
- "type" : "初中"
+ "type" : "小学"
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/ui/LoginFrame.java b/src/main/java/mathlearning/ui/LoginFrame.java
index d42000c..b2f5a96 100644
--- a/src/main/java/mathlearning/ui/LoginFrame.java
+++ b/src/main/java/mathlearning/ui/LoginFrame.java
@@ -157,7 +157,12 @@ public class LoginFrame extends JFrame{
"登录成功", JOptionPane.INFORMATION_MESSAGE);
}
- // 后续可以在这里打开主界面
- // openMainApplicationWindow(user);
+ openMainApplicationWindow(user, userService);
+ }
+
+ private void openMainApplicationWindow(User user, UserService userService) {
+ MainFrame mainFrame = new MainFrame(user, userService);
+ mainFrame.setVisible(true);
+ dispose();
}
}
diff --git a/src/main/java/mathlearning/ui/MainFrame.java b/src/main/java/mathlearning/ui/MainFrame.java
new file mode 100644
index 0000000..8072cb1
--- /dev/null
+++ b/src/main/java/mathlearning/ui/MainFrame.java
@@ -0,0 +1,137 @@
+package mathlearning.ui;
+
+import com.sun.tools.javac.Main;
+import mathlearning.model.User;
+import mathlearning.service.UserService;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class MainFrame extends JFrame {
+ private User currentUser;
+ private UserService userService;
+ private JLabel welcomeLabel;
+ private JLabel typeLabel;
+
+ public MainFrame(User user, UserService userService) {
+ this.currentUser = user;
+ this.userService = userService;
+ InitializeUI();
+ }
+
+ private void InitializeUI() {
+ setTitle("数学学习软件 - 菜单");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(500, 400);
+ setLocationRelativeTo(null);
+ setResizable(false);
+
+ // 主面板
+ JPanel titlePanel = new JPanel(new BorderLayout());
+ JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ // 欢迎用户与type信息
+ welcomeLabel = new JLabel("欢迎"+currentUser.getUsername()+"!", JLabel.CENTER);
+ welcomeLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
+
+ typeLabel = new JLabel("当前选择的测试题为" + currentUser.getType() + "难度", JLabel.CENTER);
+ typeLabel.setFont(new Font("微软雅黑", Font.BOLD, 16));
+ typeLabel.setForeground(Color.BLUE);
+
+ titlePanel.add(welcomeLabel, BorderLayout.NORTH);
+ titlePanel.add(typeLabel, BorderLayout.CENTER);
+ mainPanel.add(titlePanel, BorderLayout.NORTH);
+
+ // 功能按钮区域
+ JPanel centerButtonPanel = new JPanel(new GridLayout(2, 1, 15, 15));
+ centerButtonPanel.setBorder(BorderFactory.createEmptyBorder(30, 50, 30, 50));
+ // 切换教育阶段按钮
+ JButton changeTypeButton = new JButton("切换教育阶段");
+ changeTypeButton.setFont(new Font("微软雅黑", Font.BOLD, 18));
+ changeTypeButton.setBackground(new Color(70, 130, 180));
+ changeTypeButton.setForeground(Color.WHITE);
+ changeTypeButton.setPreferredSize(new Dimension(200, 60));
+ changeTypeButton.addActionListener(new ChangeTypeListener());
+
+ // 自我测试按钮
+ JButton selfTestButton = new JButton("自我测试");
+ selfTestButton.setFont(new Font("微软雅黑", Font.BOLD, 18));
+ selfTestButton.setBackground(new Color(34, 139, 34));
+ selfTestButton.setForeground(Color.WHITE);
+ selfTestButton.setPreferredSize(new Dimension(200, 60));
+ //selfTestButton.addActionListener(new SelfTestListener());
+
+ centerButtonPanel.add(changeTypeButton);
+ centerButtonPanel.add(selfTestButton);
+ mainPanel.add(centerButtonPanel, BorderLayout.CENTER);
+
+ // 底部按钮面板 - 两个较小按钮
+ JPanel bottomButtonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
+
+ // 退出登录按钮
+ JButton logoutButton = new JButton("退出登录");
+ logoutButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ logoutButton.setBackground(new Color(220, 20, 60));
+ logoutButton.setForeground(Color.WHITE);
+ logoutButton.addActionListener(new LogoutListener());
+
+ // 个人资料按钮
+ JButton profileButton = new JButton("个人资料");
+ profileButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ profileButton.setBackground(new Color(100, 149, 237));
+ profileButton.setForeground(Color.WHITE);
+ profileButton.addActionListener(new ProfileListener());
+
+ bottomButtonPanel.add(logoutButton);
+ bottomButtonPanel.add(profileButton);
+ mainPanel.add(bottomButtonPanel, BorderLayout.SOUTH);
+
+ add(mainPanel);
+ }
+
+ private class ChangeTypeListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String[] types = {"小学", "初中", "高中" };
+ String selectedType = (String) JOptionPane.showInputDialog(MainFrame.this, "请选择需要切换的难度:", "选择难度", JOptionPane.QUESTION_MESSAGE, null, types, currentUser.getType());
+ if (selectedType != null) {
+ boolean updated = userService.updateUserType(currentUser.getEmail(), selectedType);
+
+ if (updated) {
+ currentUser.setType(selectedType);
+ JOptionPane.showMessageDialog(MainFrame.this, "当前难度已更改为" + currentUser.getType() , "切换成功", JOptionPane.INFORMATION_MESSAGE);
+ updateTypeDisplay();
+ } else {
+ JOptionPane.showMessageDialog(MainFrame.this, "切换失败", "错误", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+ }
+
+ private void updateTypeDisplay() {
+ typeLabel.setText("当前选择的测试题为" + currentUser.getType() + "难度");
+ }
+
+ private class LogoutListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int result = JOptionPane.showConfirmDialog(MainFrame.this, "确定要退出登陆吗?", "退出登录", JOptionPane.YES_NO_OPTION);
+
+ if (result == JOptionPane.YES_OPTION) {
+ LoginFrame loginFrame = new LoginFrame(userService);
+ loginFrame.setVisible(true);
+ dispose();
+ }
+ }
+ }
+
+ private class ProfileListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ }
+ }
+}
From c1b2f6f11172e88999171cac3714189c995e3832 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Fri, 10 Oct 2025 22:35:54 +0800
Subject: [PATCH 09/16] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=B8=BB=E8=8F=9C?=
=?UTF-8?q?=E5=8D=95=E5=A4=A7=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
data/users.json | 8 +-
src/main/java/mathlearning/model/User.java | 2 +-
.../mathlearning/service/UserService.java | 10 ++
src/main/java/mathlearning/ui/MainFrame.java | 20 ++-
.../java/mathlearning/ui/ProfileFrame.java | 121 ++++++++++++++++++
5 files changed, 152 insertions(+), 9 deletions(-)
create mode 100644 src/main/java/mathlearning/ui/ProfileFrame.java
diff --git a/data/users.json b/data/users.json
index 2c43fb0..c9e9c1c 100644
--- a/data/users.json
+++ b/data/users.json
@@ -1,9 +1,9 @@
[ {
"username" : "小鱼",
"email" : "1280556515@qq.com",
- "passwordHash" : "$2a$12$fAWGM2CJSSiulyFbDWyRyefrBWr9emYrMdkD.Rw2LPKFaYQeYYW9K",
- "registrationDate" : [ 2025, 10, 8, 21, 0, 37, 863490500 ],
- "verificationCode" : "926911",
+ "passwordHash" : "$2a$12$CsCIbkduy54ow1QwQDN0M.lJTf8jteN9n/Z3FrMklm.I5b/JmcUwG",
+ "registrationDate" : [ 2025, 10, 10, 11, 7, 5, 853200500 ],
+ "verificationCode" : "737858",
"verified" : true,
- "type" : "小学"
+ "type" : "高中"
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/model/User.java b/src/main/java/mathlearning/model/User.java
index 6ee96a3..cbf61cd 100644
--- a/src/main/java/mathlearning/model/User.java
+++ b/src/main/java/mathlearning/model/User.java
@@ -13,7 +13,7 @@ public class User {
private boolean verified;
private String type;
- public User() {}
+ public User( ) {}
public User(String email, String passwordHash, String verificationCode) {
this.email = email;
diff --git a/src/main/java/mathlearning/service/UserService.java b/src/main/java/mathlearning/service/UserService.java
index f9c0c95..45d8fce 100644
--- a/src/main/java/mathlearning/service/UserService.java
+++ b/src/main/java/mathlearning/service/UserService.java
@@ -226,6 +226,16 @@ public class UserService {
return user != null && user.isVerified();
}
+ public boolean updateUsername(String email, String newUsername) {
+ User user = users.get(email);
+ if (user == null) {
+ return false;
+ }
+ user.setUsername(newUsername);
+ saveUsers();
+ return true;
+ }
+
public boolean isValidEmail(String email) {
return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}
diff --git a/src/main/java/mathlearning/ui/MainFrame.java b/src/main/java/mathlearning/ui/MainFrame.java
index 8072cb1..74aca54 100644
--- a/src/main/java/mathlearning/ui/MainFrame.java
+++ b/src/main/java/mathlearning/ui/MainFrame.java
@@ -14,6 +14,8 @@ public class MainFrame extends JFrame {
private UserService userService;
private JLabel welcomeLabel;
private JLabel typeLabel;
+ private JButton changeTypeButton;
+ private JPanel mainPanel;
public MainFrame(User user, UserService userService) {
this.currentUser = user;
@@ -30,7 +32,7 @@ public class MainFrame extends JFrame {
// 主面板
JPanel titlePanel = new JPanel(new BorderLayout());
- JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+ mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 欢迎用户与type信息
@@ -49,7 +51,7 @@ public class MainFrame extends JFrame {
JPanel centerButtonPanel = new JPanel(new GridLayout(2, 1, 15, 15));
centerButtonPanel.setBorder(BorderFactory.createEmptyBorder(30, 50, 30, 50));
// 切换教育阶段按钮
- JButton changeTypeButton = new JButton("切换教育阶段");
+ changeTypeButton = new JButton("切换教育阶段");
changeTypeButton.setFont(new Font("微软雅黑", Font.BOLD, 18));
changeTypeButton.setBackground(new Color(70, 130, 180));
changeTypeButton.setForeground(Color.WHITE);
@@ -120,7 +122,15 @@ public class MainFrame extends JFrame {
public void actionPerformed(ActionEvent e) {
int result = JOptionPane.showConfirmDialog(MainFrame.this, "确定要退出登陆吗?", "退出登录", JOptionPane.YES_NO_OPTION);
- if (result == JOptionPane.YES_OPTION) {
+ if (result == JOptionPane.YES_OPTION) {// 为按钮添加边框和圆角效果
+ changeTypeButton.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(new Color(50, 100, 150), 1),
+ BorderFactory.createEmptyBorder(10, 20, 10, 20)));
+ changeTypeButton.setFocusPainted(false); // 去除焦点边框
+
+ // 为主面板添加渐变背景
+ mainPanel.setBackground(new Color(245, 245, 245));
+
LoginFrame loginFrame = new LoginFrame(userService);
loginFrame.setVisible(true);
dispose();
@@ -131,7 +141,9 @@ public class MainFrame extends JFrame {
private class ProfileListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
-
+ ProfileFrame profileFrame = new ProfileFrame(currentUser, userService);
+ profileFrame.setVisible(true);
+ dispose();
}
}
}
diff --git a/src/main/java/mathlearning/ui/ProfileFrame.java b/src/main/java/mathlearning/ui/ProfileFrame.java
new file mode 100644
index 0000000..2e59ad6
--- /dev/null
+++ b/src/main/java/mathlearning/ui/ProfileFrame.java
@@ -0,0 +1,121 @@
+package mathlearning.ui;
+
+import mathlearning.model.User;
+import mathlearning.service.UserService;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class ProfileFrame extends JFrame {
+ private User user;
+ private UserService userService;
+ private JPanel mainPanel;
+ private JLabel usernameValue;
+
+ public ProfileFrame(User user, UserService userService) {
+ this.user = user;
+ this.userService = userService;
+ InitUI();
+ }
+
+ private void InitUI() {
+ setTitle("个人资料");
+ setSize(400, 300);
+ setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ setLocationRelativeTo(null);
+ setResizable(false);
+
+ // 创建主面板
+ mainPanel = new JPanel(new BorderLayout(10, 10));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ //标题
+ JLabel titleLabel = new JLabel("个人资料", JLabel.CENTER);
+ titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
+ mainPanel.add(titleLabel, BorderLayout.NORTH);
+
+ //个人信息(表单)
+ JPanel infoPanel = new JPanel(new GridLayout(3, 2, 10, 10));
+ JLabel usernameLabel = new JLabel("用户名:");
+ usernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ usernameValue = new JLabel(user.getUsername());
+ usernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ infoPanel.add(usernameLabel);
+ infoPanel.add(usernameValue);
+ JLabel mailLabel = new JLabel("QQ邮箱:");
+ mailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ JLabel mailValue = new JLabel(user.getEmail());
+ infoPanel.add(mailLabel);
+ infoPanel.add(mailValue);
+ mainPanel.add(infoPanel, BorderLayout.CENTER);
+
+ //三个按钮
+ JPanel buttonPanel = new JPanel(new FlowLayout());
+ JButton returnToMainButton = new JButton("返回");
+ returnToMainButton.addActionListener(new returnToMainButtonListener());
+
+ JButton changePasswordButton = new JButton("修改密码");
+ changePasswordButton.addActionListener(e -> openChangePasswordFrame());
+
+ JButton changeUsernameButton = new JButton("更改用户名");
+ changeUsernameButton.addActionListener(new ChangeUsernameButtonListener());
+
+ buttonPanel.add(returnToMainButton);
+ buttonPanel.add(changePasswordButton);
+ buttonPanel.add(changeUsernameButton);
+
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+ add(mainPanel);
+ }
+
+ private class returnToMainButtonListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ MainFrame mainFrame = new MainFrame(user, userService);
+ mainFrame.setVisible(true);
+ dispose();
+ }
+ }
+
+ private void openChangePasswordFrame() {
+
+ }
+
+ private class ChangeUsernameButtonListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e ) {
+ String newUsername = JOptionPane.showInputDialog(ProfileFrame.this, "请输入您的新用户名:", "修改用户名", JOptionPane.QUESTION_MESSAGE);
+
+ if (newUsername == null ) {
+ return;
+ }
+
+ newUsername = newUsername.trim();
+ if (newUsername.isEmpty()) {
+ JOptionPane.showMessageDialog( ProfileFrame.this, "用户名不能为空!", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ if (newUsername.equals(user.getUsername())) {
+ JOptionPane.showMessageDialog( ProfileFrame.this, "新用户名与原用户名相同!", "提示", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ int confirm = JOptionPane.showConfirmDialog(ProfileFrame.this,"确定要修改用户名为\"" + newUsername + "\"吗?", "修改用户名", JOptionPane.YES_NO_OPTION);
+ if (confirm == JOptionPane.YES_OPTION) {
+ String oldUsername = user.getUsername();
+
+ boolean updated = userService.updateUsername(user.getEmail(), newUsername);
+ if (updated) {
+ usernameValue.setText(newUsername);
+ JOptionPane.showMessageDialog(ProfileFrame.this, "修改成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
+ }
+ else {
+ user.setUsername(oldUsername);
+ JOptionPane.showMessageDialog(ProfileFrame.this, "修改失败!", "错误", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+ }
+}
From 4174733034c80aabe72c397bdd73c20e4c656c58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Fri, 10 Oct 2025 23:46:14 +0800
Subject: [PATCH 10/16] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=B8=BB=E8=8F=9C?=
=?UTF-8?q?=E5=8D=95=E9=99=A4=E8=87=AA=E6=B5=8B=E5=8A=9F=E8=83=BD=E6=89=80?=
=?UTF-8?q?=E6=9C=89=E7=95=8C=E9=9D=A2=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
data/users.json | 4 +-
.../mathlearning/service/UserService.java | 14 +-
.../mathlearning/ui/ChangePasswordFrame.java | 120 ++++++++++++++++++
.../java/mathlearning/ui/ProfileFrame.java | 8 +-
4 files changed, 136 insertions(+), 10 deletions(-)
create mode 100644 src/main/java/mathlearning/ui/ChangePasswordFrame.java
diff --git a/data/users.json b/data/users.json
index c9e9c1c..a60e027 100644
--- a/data/users.json
+++ b/data/users.json
@@ -1,9 +1,9 @@
[ {
"username" : "小鱼",
"email" : "1280556515@qq.com",
- "passwordHash" : "$2a$12$CsCIbkduy54ow1QwQDN0M.lJTf8jteN9n/Z3FrMklm.I5b/JmcUwG",
+ "passwordHash" : "$2a$12$T4LMhwE2r/WJyLjK/gg9MuwEZDPHd/BEmKqd7Y1rfdww261wvGSx.",
"registrationDate" : [ 2025, 10, 10, 11, 7, 5, 853200500 ],
- "verificationCode" : "737858",
+ "verificationCode" : "863928",
"verified" : true,
"type" : "高中"
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/service/UserService.java b/src/main/java/mathlearning/service/UserService.java
index 45d8fce..930e3b1 100644
--- a/src/main/java/mathlearning/service/UserService.java
+++ b/src/main/java/mathlearning/service/UserService.java
@@ -123,28 +123,32 @@ public class UserService {
return result.verified;
}
- public boolean changePassword(String email, String oldPassword, String newPassword) {
+ public int changePassword(String email, String oldPassword, String newPassword) {
User user = users.get(email);
if (user == null) {
- return false;
+ return 1;
}
// 验证旧密码
BCrypt.Result result = BCrypt.verifyer().verify(oldPassword.toCharArray(), user.getPasswordHash());
if (!result.verified) {
- return false;
+ return 2;
}
// 验证新密码格式
if (!validatePassword(newPassword)) {
- return false;
+ return 3;
+ }
+
+ if (oldPassword.equals(newPassword)) {
+ return 4;
}
// 更新密码
String newPasswordHash = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray());
user.setPasswordHash(newPasswordHash);
saveUsers();
- return true;
+ return 5;
}
public boolean setPasswordResetCode(String email, String verificationCode) {
diff --git a/src/main/java/mathlearning/ui/ChangePasswordFrame.java b/src/main/java/mathlearning/ui/ChangePasswordFrame.java
new file mode 100644
index 0000000..96848fe
--- /dev/null
+++ b/src/main/java/mathlearning/ui/ChangePasswordFrame.java
@@ -0,0 +1,120 @@
+package mathlearning.ui;
+
+import mathlearning.model.User;
+import mathlearning.service.UserService;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class ChangePasswordFrame extends JFrame {
+ private User user;
+ private UserService userService;
+ private ProfileFrame profileFrame;
+ private JPasswordField oldPasswordField;
+ private JPasswordField newPasswordField;
+ private JPasswordField confirmPasswordField;
+
+ public ChangePasswordFrame(User user, UserService userService, ProfileFrame profileFrame) {
+ this.user = user;
+ this.userService = userService;
+ this.profileFrame = profileFrame;
+ addWindowListener(new java.awt.event.WindowAdapter() {
+ @Override
+ public void windowClosing(java.awt.event.WindowEvent windowEvent) {
+ profileFrame.setVisible(true);
+ }
+ });
+ InitUI();
+ }
+
+ private void InitUI() {
+ setTitle("修改密码");
+ setSize(400, 300);
+ setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ setLocationRelativeTo(null);
+ setResizable(false);
+
+ // 创建主面板
+ JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ // 标题
+ JLabel titleLabel = new JLabel("修改密码", JLabel.CENTER);
+ titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
+ mainPanel.add(titleLabel, BorderLayout.NORTH);
+
+ //表单
+ JPanel infoPanel = new JPanel(new GridLayout(4, 2, 10, 10));
+ JLabel oldPasswordLabel = new JLabel("请输入旧密码:");
+ oldPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ oldPasswordField = new JPasswordField();
+ infoPanel.add(oldPasswordLabel);
+ infoPanel.add(oldPasswordField);
+ JLabel newPasswordLabel = new JLabel("请输入新密码:");
+ newPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ newPasswordField = new JPasswordField();
+ infoPanel.add(newPasswordLabel);
+ infoPanel.add(newPasswordField);
+ JLabel confirmPasswordLabel = new JLabel("请再次输入新密码:");
+ confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ confirmPasswordField = new JPasswordField();
+ infoPanel.add(confirmPasswordLabel);
+ infoPanel.add(confirmPasswordField);
+ mainPanel.add(infoPanel, BorderLayout.CENTER);
+
+ //按钮
+ JPanel buttonPanel = new JPanel(new FlowLayout());
+ JButton changeButton = new JButton("修改");
+ changeButton.addActionListener(new ChangeButtonListener());
+ JButton cancelButton = new JButton("取消并返回");
+ cancelButton.addActionListener(e -> returnToProfileFrame());
+
+ buttonPanel.add(changeButton);
+ buttonPanel.add(cancelButton);
+
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+ add(mainPanel);
+ }
+
+ private void returnToProfileFrame() {
+ profileFrame.setVisible(true);
+ dispose();
+ }
+
+ private class ChangeButtonListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String oldPassword = new String(oldPasswordField.getPassword());
+ String newPassword = new String(newPasswordField.getPassword());
+ String confirmPassword = new String(confirmPasswordField.getPassword());
+
+ if (!newPassword.equals(confirmPassword)) {
+ JOptionPane.showMessageDialog(ChangePasswordFrame.this, "两次输入的密码不相同!", "错误", JOptionPane.ERROR_MESSAGE );
+ return;
+ }
+
+ int changed = userService.changePassword(user.getEmail(), oldPassword, newPassword);
+
+ if (changed == 1) {
+ JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改失败!用户账户异常!", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ else if (changed == 2) {
+ JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改失败!旧密码输入有误!", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ } else if (changed == 3) {
+ JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改失败!新密码的格式有误!密码必须为6-10位,且包含大小写字母和数字", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ } else if (changed == 4) {
+ JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改失败!旧密码与新密码一致!", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ } else {
+ JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
+ }
+ returnToProfileFrame();
+ }
+ }
+}
diff --git a/src/main/java/mathlearning/ui/ProfileFrame.java b/src/main/java/mathlearning/ui/ProfileFrame.java
index 2e59ad6..17982f7 100644
--- a/src/main/java/mathlearning/ui/ProfileFrame.java
+++ b/src/main/java/mathlearning/ui/ProfileFrame.java
@@ -57,7 +57,7 @@ public class ProfileFrame extends JFrame {
returnToMainButton.addActionListener(new returnToMainButtonListener());
JButton changePasswordButton = new JButton("修改密码");
- changePasswordButton.addActionListener(e -> openChangePasswordFrame());
+ changePasswordButton.addActionListener(e -> oppenChangePasswordFrame());
JButton changeUsernameButton = new JButton("更改用户名");
changeUsernameButton.addActionListener(new ChangeUsernameButtonListener());
@@ -80,8 +80,10 @@ public class ProfileFrame extends JFrame {
}
}
- private void openChangePasswordFrame() {
-
+ private void oppenChangePasswordFrame() {
+ ChangePasswordFrame changePasswordFrame = new ChangePasswordFrame(user, userService, this);
+ changePasswordFrame.setVisible(true);
+ this.setVisible(false);
}
private class ChangeUsernameButtonListener implements ActionListener {
From c618d0fe2c059e5a67caa6939bdb8216c8df9783 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Sat, 11 Oct 2025 11:52:54 +0800
Subject: [PATCH 11/16] =?UTF-8?q?=E7=95=8C=E9=9D=A2=E4=BA=A4=E4=BA=92?=
=?UTF-8?q?=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
data/users.json | 4 ++--
src/main/java/mathlearning/ui/ChangeCodeFrame.java | 9 ++++++++-
src/main/java/mathlearning/ui/ProfileFrame.java | 11 +++++++++--
src/main/java/mathlearning/ui/RegisterFrame.java | 9 ++++++++-
4 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/data/users.json b/data/users.json
index a60e027..34c9019 100644
--- a/data/users.json
+++ b/data/users.json
@@ -1,9 +1,9 @@
[ {
"username" : "小鱼",
"email" : "1280556515@qq.com",
- "passwordHash" : "$2a$12$T4LMhwE2r/WJyLjK/gg9MuwEZDPHd/BEmKqd7Y1rfdww261wvGSx.",
+ "passwordHash" : "$2a$12$dbNwBK6NBj7mXU6YzNMAweTMhD9NOxsjPGzW2SfIM.QvGdWt7Lyvy",
"registrationDate" : [ 2025, 10, 10, 11, 7, 5, 853200500 ],
- "verificationCode" : "863928",
+ "verificationCode" : "688201",
"verified" : true,
"type" : "高中"
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/ui/ChangeCodeFrame.java b/src/main/java/mathlearning/ui/ChangeCodeFrame.java
index 1255136..37ad12b 100644
--- a/src/main/java/mathlearning/ui/ChangeCodeFrame.java
+++ b/src/main/java/mathlearning/ui/ChangeCodeFrame.java
@@ -36,12 +36,19 @@ public class ChangeCodeFrame extends JFrame {
true
);
+ addWindowListener(new java.awt.event.WindowAdapter() {
+ @Override
+ public void windowClosing(java.awt.event.WindowEvent windowEvent) {
+ loginFrame.setVisible(true);
+ }
+ });
+
initializeUI();
}
private void initializeUI() {
setTitle("数学学习软件 - 忘记密码");
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(450, 400);
setLocationRelativeTo(null);
setResizable(false);
diff --git a/src/main/java/mathlearning/ui/ProfileFrame.java b/src/main/java/mathlearning/ui/ProfileFrame.java
index 17982f7..8f2b3cf 100644
--- a/src/main/java/mathlearning/ui/ProfileFrame.java
+++ b/src/main/java/mathlearning/ui/ProfileFrame.java
@@ -17,6 +17,13 @@ public class ProfileFrame extends JFrame {
public ProfileFrame(User user, UserService userService) {
this.user = user;
this.userService = userService;
+ addWindowListener(new java.awt.event.WindowAdapter() {
+ @Override
+ public void windowClosing(java.awt.event.WindowEvent windowEvent) {
+ MainFrame mainFrame = new MainFrame(user, userService);
+ mainFrame.setVisible(true);
+ }
+ });
InitUI();
}
@@ -57,7 +64,7 @@ public class ProfileFrame extends JFrame {
returnToMainButton.addActionListener(new returnToMainButtonListener());
JButton changePasswordButton = new JButton("修改密码");
- changePasswordButton.addActionListener(e -> oppenChangePasswordFrame());
+ changePasswordButton.addActionListener(e -> openChangePasswordFrame());
JButton changeUsernameButton = new JButton("更改用户名");
changeUsernameButton.addActionListener(new ChangeUsernameButtonListener());
@@ -80,7 +87,7 @@ public class ProfileFrame extends JFrame {
}
}
- private void oppenChangePasswordFrame() {
+ private void openChangePasswordFrame() {
ChangePasswordFrame changePasswordFrame = new ChangePasswordFrame(user, userService, this);
changePasswordFrame.setVisible(true);
this.setVisible(false);
diff --git a/src/main/java/mathlearning/ui/RegisterFrame.java b/src/main/java/mathlearning/ui/RegisterFrame.java
index b5fc993..fb0f5a9 100644
--- a/src/main/java/mathlearning/ui/RegisterFrame.java
+++ b/src/main/java/mathlearning/ui/RegisterFrame.java
@@ -37,12 +37,19 @@ public class RegisterFrame extends JFrame{
true
);
+ addWindowListener(new java.awt.event.WindowAdapter() {
+ @Override
+ public void windowClosing(java.awt.event.WindowEvent windowEvent) {
+ loginFrame.setVisible(true);
+ }
+ });
+
initializeUI();
}
private void initializeUI() {
setTitle("数学学习软件 - 注册");
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(450, 400);
setLocationRelativeTo(null);
setResizable(false);
From 92c63026eed304162d8e97d12e77c03053f3670c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Sat, 11 Oct 2025 14:57:50 +0800
Subject: [PATCH 12/16] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=89=80=E6=9C=89?=
=?UTF-8?q?=E7=95=8C=E9=9D=A2=E4=B8=8E=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
data/users.json | 2 +-
src/main/java/mathlearning/ui/MainFrame.java | 30 +-
.../java/mathlearning/ui/SelTestFrame.java | 278 ++++++++++++++++++
3 files changed, 307 insertions(+), 3 deletions(-)
create mode 100644 src/main/java/mathlearning/ui/SelTestFrame.java
diff --git a/data/users.json b/data/users.json
index 34c9019..2ada8b1 100644
--- a/data/users.json
+++ b/data/users.json
@@ -5,5 +5,5 @@
"registrationDate" : [ 2025, 10, 10, 11, 7, 5, 853200500 ],
"verificationCode" : "688201",
"verified" : true,
- "type" : "高中"
+ "type" : "小学"
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/ui/MainFrame.java b/src/main/java/mathlearning/ui/MainFrame.java
index 74aca54..18b6a0f 100644
--- a/src/main/java/mathlearning/ui/MainFrame.java
+++ b/src/main/java/mathlearning/ui/MainFrame.java
@@ -64,7 +64,7 @@ public class MainFrame extends JFrame {
selfTestButton.setBackground(new Color(34, 139, 34));
selfTestButton.setForeground(Color.WHITE);
selfTestButton.setPreferredSize(new Dimension(200, 60));
- //selfTestButton.addActionListener(new SelfTestListener());
+ selfTestButton.addActionListener(e -> openSelfTestFrame());
centerButtonPanel.add(changeTypeButton);
centerButtonPanel.add(selfTestButton);
@@ -76,7 +76,7 @@ public class MainFrame extends JFrame {
// 退出登录按钮
JButton logoutButton = new JButton("退出登录");
logoutButton.setFont(new Font("微软雅黑", Font.PLAIN, 14));
- logoutButton.setBackground(new Color(220, 20, 60));
+ logoutButton.setBackground(Color.CYAN);
logoutButton.setForeground(Color.WHITE);
logoutButton.addActionListener(new LogoutListener());
@@ -146,4 +146,30 @@ public class MainFrame extends JFrame {
dispose();
}
}
+
+ private void openSelfTestFrame() {
+ String input = JOptionPane.showInputDialog(MainFrame.this, "请输入题目数量:(10-30)", "题目数量", JOptionPane.QUESTION_MESSAGE);
+ if (input == null) {
+ return;
+ }
+
+ try {
+ int num = Integer.parseInt(input.trim());
+ if (num < 10 || num > 30) {
+ JOptionPane.showMessageDialog(MainFrame.this, "题目数量应该在10-30之间!", "错误", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ SelTestFrame selTestFrame = new SelTestFrame(this, currentUser, userService, num);
+ selTestFrame.setVisible(true);
+ this.setVisible(false);
+
+ } catch (NumberFormatException e) {
+ JOptionPane.showMessageDialog(MainFrame.this, "请输入数字!", "错误", JOptionPane.ERROR_MESSAGE);
+ }
+
+ }
+
+ public void reTryTest() {
+ openSelfTestFrame();
+ }
}
diff --git a/src/main/java/mathlearning/ui/SelTestFrame.java b/src/main/java/mathlearning/ui/SelTestFrame.java
new file mode 100644
index 0000000..cc7ae84
--- /dev/null
+++ b/src/main/java/mathlearning/ui/SelTestFrame.java
@@ -0,0 +1,278 @@
+package mathlearning.ui;
+
+import mathlearning.model.User;
+import mathlearning.service.UserService;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class SelTestFrame extends JFrame {
+ private MainFrame mainFrame;
+ private User user;
+ private UserService userService;
+
+ private int questionNum;
+ private int currentQuestionDex;
+ private String[] answers;
+
+ private JLabel titleLabel;
+ private JButton optionA;
+ private JButton optionB;
+ private JButton optionC;
+ private JButton optionD;
+ private JButton prevButton;
+ private JButton nextButton;
+ private JButton submitButton;
+
+ public SelTestFrame(MainFrame mainFrame, User user, UserService userService, int num) {
+ this.mainFrame = mainFrame;
+ this.user = user;
+ this.userService = userService;
+ this.questionNum = num;
+ this.currentQuestionDex = 0;
+ this.answers = new String[questionNum];
+ InitUI();
+ addWindowListener(new java.awt.event.WindowAdapter() {
+ @Override
+ public void windowClosing(java.awt.event.WindowEvent windowEvent) {
+ handleCloseOperation();
+ }
+ });
+ }
+
+ private void InitUI() {
+ setTitle("答题界面");
+ setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+ setSize(800, 600);
+ setLocationRelativeTo(null);
+
+ //主面板
+ JPanel mainPanel = new JPanel(new BorderLayout());
+
+ //上
+ titleLabel = new JLabel("当前为:第" + (currentQuestionDex + 1) + "题");
+
+ //左侧面板
+ JPanel leftPanel = createLeftPanel();
+
+ //中
+ JPanel centerPanel = centerPanel();
+
+ //右
+ JPanel rightPanel = createRightPanel();
+
+ //底
+ JPanel bottomPanel = createButtonPanel();
+
+ mainPanel.add(titleLabel, BorderLayout.NORTH);
+ mainPanel.add(leftPanel, BorderLayout.WEST);
+ mainPanel.add(centerPanel, BorderLayout.CENTER);
+ mainPanel.add(rightPanel, BorderLayout.EAST);
+ mainPanel.add(bottomPanel, BorderLayout.SOUTH);
+
+ add(mainPanel);
+
+ updateNavigationButtons();
+ }
+
+ private JPanel createLeftPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+
+ JLabel usernameLabel = new JLabel("用户:" + user.getUsername());
+ usernameLabel.setFont(new Font("微软雅黑", Font.BOLD, 16));
+ usernameLabel.setForeground(Color.BLUE);
+ panel.add(usernameLabel, BorderLayout.NORTH);
+
+ JList questionList = new JList<>();
+ String[] questions = new String[questionNum + 1];
+ for (int i = 0; i < questionNum; i++) {
+ questions[i] = "题目" + (i + 1);
+ }
+ questionList.setListData(questions);
+ panel.add(questionList, BorderLayout.CENTER);
+
+ return panel;
+ }
+
+ private JPanel centerPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+
+ return panel;
+ }
+
+ private JPanel createRightPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+
+ JLabel titleLabel = new JLabel("作答区域:");
+ titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 16));
+ panel.add(titleLabel, BorderLayout.NORTH);
+
+ JPanel buttonPanel = new JPanel(new GridLayout(4, 1));
+
+ optionA = new JButton("A");
+ optionA.setBackground(Color.LIGHT_GRAY);
+ optionA.setForeground(Color.BLACK);
+ optionA.addActionListener(e -> saveAnswer("A"));
+ optionB = new JButton("B");
+ optionB.setBackground(Color.LIGHT_GRAY);
+ optionB.setForeground(Color.BLACK);
+ optionB.addActionListener(e -> saveAnswer("B"));
+ optionC = new JButton("C");
+ optionC.setBackground(Color.LIGHT_GRAY);
+ optionC.setForeground(Color.BLACK);
+ optionC.addActionListener(e -> saveAnswer("C"));
+ optionD = new JButton("D");
+ optionD.setBackground(Color.LIGHT_GRAY);
+ optionD.setForeground(Color.BLACK);
+ optionD.addActionListener(e -> saveAnswer("D"));
+ buttonPanel.add(optionA);
+ buttonPanel.add(optionB);
+ buttonPanel.add(optionC);
+ buttonPanel.add(optionD);
+ panel.add(buttonPanel, BorderLayout.CENTER);
+
+ return panel;
+ }
+
+ private JPanel createButtonPanel() {
+ JPanel panel = new JPanel(new FlowLayout());
+
+ prevButton = new JButton("上一题");
+ prevButton.addActionListener(e -> goToPrevQuestion());
+
+ nextButton = new JButton("下一题");
+ nextButton.addActionListener(e -> goToNextQuestion());
+
+ submitButton = new JButton("提交");
+ submitButton.addActionListener(e -> goToSubmit());
+
+ panel.add(prevButton);
+ panel.add(nextButton);
+ panel.add(submitButton);
+ return panel;
+ }
+
+ private void saveAnswer(String answer) {
+ resetButtonColors();
+
+ switch (answer) {
+ case "A":
+ optionA.setBackground(Color.GREEN);
+ answers[currentQuestionDex] = answer;
+ break;
+ case "B":
+ optionB.setBackground(Color.GREEN);
+ answers[currentQuestionDex] = answer;
+ break;
+ case "C":
+ optionC.setBackground(Color.GREEN);
+ answers[currentQuestionDex] = answer;
+ break;
+ case "D":
+ optionD.setBackground(Color.GREEN);
+ answers[currentQuestionDex] = answer;
+ break;
+ }
+
+ }
+
+ private void resetButtonColors() {
+ optionA.setBackground(Color.LIGHT_GRAY);
+ optionB.setBackground(Color.LIGHT_GRAY);
+ optionC.setBackground(Color.LIGHT_GRAY);
+ optionD.setBackground(Color.LIGHT_GRAY);
+ }
+
+ private void goToPrevQuestion() {
+ if (currentQuestionDex > 0) {
+ currentQuestionDex--;
+ updateTitleLabel();
+ if (answers[currentQuestionDex] == null) {
+ resetButtonColors();
+ }
+ else {
+ saveAnswer(answers[currentQuestionDex]);
+ }
+
+ updateNavigationButtons();
+ }
+ }
+
+ private void goToNextQuestion() {
+ if (currentQuestionDex < questionNum - 1) {
+ currentQuestionDex++;
+ updateTitleLabel();
+ if (answers[currentQuestionDex] == null) {
+ resetButtonColors();
+ } else {
+ saveAnswer(answers[currentQuestionDex]);
+ }
+
+ updateNavigationButtons();
+ }
+ }
+
+ private void goToSubmit() {
+ int confirm = JOptionPane.showConfirmDialog(SelTestFrame.this, "你确定要提交试卷吗?", "提示", JOptionPane.YES_NO_OPTION);
+ if (confirm == JOptionPane.YES_OPTION) {
+ int score = 0;
+
+ // 弹出分数结果
+ String[] options = {"返回主菜单", "继续做题"};
+ int choice = JOptionPane.showOptionDialog(
+ SelTestFrame.this,
+ "您的得分是: " + score + "分",
+ "测试结果",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.INFORMATION_MESSAGE,
+ null,
+ options,
+ options[0]
+ );
+
+ if (choice == 0) {
+ mainFrame.setVisible(true);
+ dispose();
+ } else if (choice == 1) {
+ mainFrame.reTryTest();
+ dispose();
+ }
+ }
+ }
+
+ private void updateNavigationButtons() {
+ if (currentQuestionDex == 0) {
+ prevButton.setEnabled(false);
+ prevButton.setVisible(false);
+ }
+ else {
+ prevButton.setEnabled(true);
+ prevButton.setVisible(true);
+ }
+
+ if (currentQuestionDex == questionNum - 1) {
+ nextButton.setEnabled(false);
+ nextButton.setVisible(false);
+ submitButton.setEnabled(true);
+ submitButton.setVisible(true);
+ } else {
+ nextButton.setEnabled(true);
+ nextButton.setVisible(true);
+ submitButton.setEnabled(false);
+ submitButton.setVisible(false);
+ }
+ }
+
+ private void updateTitleLabel() {
+ titleLabel.setText("当前为:第" + (currentQuestionDex + 1) + "题");
+ }
+
+ private void handleCloseOperation() {
+ int result = JOptionPane.showConfirmDialog(SelTestFrame.this, "确定要中途退出答题吗?当前答题进度将会丢失!", "确认退出", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
+
+ if (result == JOptionPane.YES_OPTION) {
+ mainFrame.setVisible(true);
+ dispose();
+ }
+ }
+}
From 3bb158eccdabe93c7803aba5299034e3c27b4def Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Sat, 11 Oct 2025 15:30:53 +0800
Subject: [PATCH 13/16] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=89=80=E6=9C=89?=
=?UTF-8?q?=E7=95=8C=E9=9D=A2=E4=B8=8E=E5=8A=9F=E8=83=BD1.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
data/users.json | 2 +-
.../java/mathlearning/ui/SelTestFrame.java | 40 +++++++++++++------
2 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/data/users.json b/data/users.json
index 2ada8b1..3f0ec0d 100644
--- a/data/users.json
+++ b/data/users.json
@@ -5,5 +5,5 @@
"registrationDate" : [ 2025, 10, 10, 11, 7, 5, 853200500 ],
"verificationCode" : "688201",
"verified" : true,
- "type" : "小学"
+ "type" : "初中"
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/ui/SelTestFrame.java b/src/main/java/mathlearning/ui/SelTestFrame.java
index cc7ae84..851002d 100644
--- a/src/main/java/mathlearning/ui/SelTestFrame.java
+++ b/src/main/java/mathlearning/ui/SelTestFrame.java
@@ -13,7 +13,7 @@ public class SelTestFrame extends JFrame {
private int questionNum;
private int currentQuestionDex;
- private String[] answers;
+ private String[] myAnswers;
private JLabel titleLabel;
private JButton optionA;
@@ -30,7 +30,7 @@ public class SelTestFrame extends JFrame {
this.userService = userService;
this.questionNum = num;
this.currentQuestionDex = 0;
- this.answers = new String[questionNum];
+ this.myAnswers = new String[questionNum];
InitUI();
addWindowListener(new java.awt.event.WindowAdapter() {
@Override
@@ -158,19 +158,19 @@ public class SelTestFrame extends JFrame {
switch (answer) {
case "A":
optionA.setBackground(Color.GREEN);
- answers[currentQuestionDex] = answer;
+ myAnswers[currentQuestionDex] = answer;
break;
case "B":
optionB.setBackground(Color.GREEN);
- answers[currentQuestionDex] = answer;
+ myAnswers[currentQuestionDex] = answer;
break;
case "C":
optionC.setBackground(Color.GREEN);
- answers[currentQuestionDex] = answer;
+ myAnswers[currentQuestionDex] = answer;
break;
case "D":
optionD.setBackground(Color.GREEN);
- answers[currentQuestionDex] = answer;
+ myAnswers[currentQuestionDex] = answer;
break;
}
@@ -187,11 +187,11 @@ public class SelTestFrame extends JFrame {
if (currentQuestionDex > 0) {
currentQuestionDex--;
updateTitleLabel();
- if (answers[currentQuestionDex] == null) {
+ if (myAnswers[currentQuestionDex] == null) {
resetButtonColors();
}
else {
- saveAnswer(answers[currentQuestionDex]);
+ saveAnswer(myAnswers[currentQuestionDex]);
}
updateNavigationButtons();
@@ -202,10 +202,10 @@ public class SelTestFrame extends JFrame {
if (currentQuestionDex < questionNum - 1) {
currentQuestionDex++;
updateTitleLabel();
- if (answers[currentQuestionDex] == null) {
+ if (myAnswers[currentQuestionDex] == null) {
resetButtonColors();
} else {
- saveAnswer(answers[currentQuestionDex]);
+ saveAnswer(myAnswers[currentQuestionDex]);
}
updateNavigationButtons();
@@ -215,13 +215,16 @@ public class SelTestFrame extends JFrame {
private void goToSubmit() {
int confirm = JOptionPane.showConfirmDialog(SelTestFrame.this, "你确定要提交试卷吗?", "提示", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
- int score = 0;
+ int correctCount = 0;
+
+ double accuracy = Math.round((double) correctCount / myAnswers.length * 10000) / 100.0;
+ double score = Math.round((double) correctCount * 100 / myAnswers.length * 10) / 10.0;
// 弹出分数结果
String[] options = {"返回主菜单", "继续做题"};
int choice = JOptionPane.showOptionDialog(
SelTestFrame.this,
- "您的得分是: " + score + "分",
+ "您答对了" + score + "道题,正确率为" + String.format("%.2f", accuracy) + "\n您的得分是: " + score + "分",
"测试结果",
JOptionPane.YES_NO_OPTION,
JOptionPane.INFORMATION_MESSAGE,
@@ -275,4 +278,17 @@ public class SelTestFrame extends JFrame {
dispose();
}
}
+
+ private int getScore(String[] answers, String[] myAnswers) {
+ if (answers == null || myAnswers == null){
+ return 0;
+ }
+ int score = 0;
+ for (int i = 0; i < answers.length; i++) {
+ if (answers[i] != null && answers[i].equals(myAnswers[i])){
+ score++;
+ }
+ }
+ return score;
+ }
}
From 5f5dc962c7aa75b60ef29a08737bf0cbe41620ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Sat, 11 Oct 2025 18:18:03 +0800
Subject: [PATCH 14/16] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=89=80=E6=9C=89?=
=?UTF-8?q?=E7=95=8C=E9=9D=A2=E4=B8=8E=E5=8A=9F=E8=83=BD1.01?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/mathlearning/ui/SelTestFrame.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/main/java/mathlearning/ui/SelTestFrame.java b/src/main/java/mathlearning/ui/SelTestFrame.java
index 851002d..ceac476 100644
--- a/src/main/java/mathlearning/ui/SelTestFrame.java
+++ b/src/main/java/mathlearning/ui/SelTestFrame.java
@@ -291,4 +291,8 @@ public class SelTestFrame extends JFrame {
}
return score;
}
+
+ private String getQuestion(String[] questions, int dex) {
+ return questions[dex];
+ }
}
From ac7c3053f917f7c341bb03436b1d949a8ff81fc2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Sat, 11 Oct 2025 19:01:54 +0800
Subject: [PATCH 15/16] =?UTF-8?q?=E5=90=88=E5=B9=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ComputingUnit.java | 190 ++++++++++++++++++
.../MultipleChoiceGenerator.java | 90 +++++++++
.../MultipleChoiceGeneratorTest.java | 37 ++++
.../QuestionGenerator/PrimaryGenerator.java | 22 ++
.../QuestionGenerator/QuestionGenerator.java | 142 +++++++++++++
.../QuestionGenerator/highGenerator.java | 24 +++
.../QuestionGenerator/middleGenerator.java | 23 +++
7 files changed, 528 insertions(+)
create mode 100644 src/main/java/mathlearning/service/MultipleChoiceGenerator/ComputingUnit.java
create mode 100644 src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java
create mode 100644 src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGeneratorTest.java
create mode 100644 src/main/java/mathlearning/service/QuestionGenerator/PrimaryGenerator.java
create mode 100644 src/main/java/mathlearning/service/QuestionGenerator/QuestionGenerator.java
create mode 100644 src/main/java/mathlearning/service/QuestionGenerator/highGenerator.java
create mode 100644 src/main/java/mathlearning/service/QuestionGenerator/middleGenerator.java
diff --git a/src/main/java/mathlearning/service/MultipleChoiceGenerator/ComputingUnit.java b/src/main/java/mathlearning/service/MultipleChoiceGenerator/ComputingUnit.java
new file mode 100644
index 0000000..33c4a37
--- /dev/null
+++ b/src/main/java/mathlearning/service/MultipleChoiceGenerator/ComputingUnit.java
@@ -0,0 +1,190 @@
+
+package mathlearning.service.MultipleChoiceGenerator;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+public class ComputingUnit {
+ String expression;
+ Stack numbers = new Stack<>();
+ Stack operators = new Stack<>();
+
+ ComputingUnit(String Question){
+ expression = Question;
+ }
+
+ private void SplitAndChange(){
+
+ String[] split = expression.split(" ");
+ split = separateParentheses(split);
+ for(int i = 0 ; i < split.length ; i++){
+ if (split[i].charAt(0) == '√') {
+ String StrNum = split[i].substring(1);
+ double num = Math.sqrt(Double.parseDouble(StrNum));
+ split[i] = String.valueOf(num);
+ } else if (split[i].charAt(split[i].length()-1) == '²') {
+ String StrNum = split[i].substring(0, split[i].length()-1);
+ double result = Math.pow(Double.parseDouble(StrNum), 2);
+ split[i] = String.valueOf(result);
+ } else if ((split[i].charAt(0) == 's' || split[i].charAt(0) == 'c'
+ || split[i].charAt(0) == 't' )) {
+ int index = split[i].indexOf(')');
+ int index2 = split[i].indexOf('(');
+ String StrNum = split[i].substring(index2 + 1, index);
+ double num = Double.parseDouble(SquOrRoot(StrNum));
+ double result = getFunc(split[i].charAt(0), num);
+ split[i] = String.valueOf(result);
+ }
+ }
+ this.expression = String.join(" ", split);
+ }
+
+ public String[] separateParentheses(String[] originalArray) {
+ List result = new ArrayList<>();
+
+ for (String item : originalArray) {
+ if (item.startsWith("(") && item.length() > 1) {
+ // 如果字符串以(开头且长度大于1,分离出(
+ result.add("(");
+ result.add(item.substring(1).trim());
+ } else if (item.endsWith(")") && item.length() > 1 &&
+ (item.charAt(0) != 's' && item.charAt(0) != 'c' && item.charAt(0) != 't')) {
+ // 如果字符串以)结尾且长度大于1,分离出)
+ result.add(item.substring(0, item.length() - 1).trim());
+ result.add(")");
+ } else if ((item.charAt(0) == 's' || item.charAt(0) == 'c' || item.charAt(0) == 't') &&
+ (item.endsWith(")") && item.charAt(item.length()-2) == ')')) {
+ result.add(item.substring(0, item.length() - 1).trim());
+ result.add(")");
+ } else {
+ // 其他情况保持不变
+ result.add(item);
+ }
+ }
+
+ return result.toArray(new String[0]);
+ }
+
+
+ private String SquOrRoot(String StrNum){
+ if(StrNum.charAt(0) == '√'){
+ return String.valueOf(Math.sqrt(Double.parseDouble(StrNum.substring(1))));
+ } else if (StrNum.charAt(StrNum.length()-1) == '²') {
+ return String.valueOf(Math.pow(Double.parseDouble(StrNum.substring(0, StrNum.length()-1)), 2));
+ }else {
+ return StrNum;
+ }
+ }
+
+ private double getFunc(char func, double num){
+ switch (func) {
+ case 's':
+ return Math.sin(num);
+ case 'c':
+ return Math.cos(num);
+ case 't':
+ return Math.tan(num);
+ default:
+ return 0;
+ }
+ }
+
+ private void priority(String operator){
+ if(operators.isEmpty() || operators.peek().equals("(")){
+ operators.push(operator);
+ }else{
+ if((operators.peek().equals("+")|| operators.peek().equals("-")) &&
+ (operator.equals("×") || operator.equals("÷"))){
+ operators.push(operator);
+ }else{
+ CulAndPushOperator(operator);
+ }
+ }
+ }
+
+ private void CulAndPushOperator(String operator){
+ String num1 = numbers.pop();
+ String num2 = numbers.pop();
+ String operator1 = operators.pop();
+ operators.push(operator);
+ String result = SingleCul(num2, operator1, num1);
+ numbers.push(result);
+ }
+
+ private String Compute(){
+ String[] spilt = expression.split(" ");
+ for (int i = 0; i < spilt.length; i++){
+ if(spilt[i].equals("+") || spilt[i].equals("-") ||
+ spilt[i].equals("×") || spilt[i].equals("÷") ||
+ spilt[i].equals("(") ){//处理运算符
+ if( spilt[i].equals("(")){
+ operators.push(spilt[i]);
+ }else{
+ priority(spilt[i]);
+ }
+ }else if(spilt[i].equals(")")){
+ String tempResult = numbers.pop();
+ while (!operators.peek().equals("(")){
+ String operator = operators.pop();
+ String num1 = numbers.pop();
+ tempResult = SingleCul(num1, operator, tempResult);
+ }
+ if(operators.peek().equals("(")){
+ operators.pop();
+ }
+ numbers.push(tempResult);
+ } else {
+ numbers.push(spilt[i]);
+ }
+ }
+ return CulWithoutPriority();
+ }
+
+ private String CulWithoutPriority(){
+ if(numbers.isEmpty()){
+ return "0";
+ }
+ String result = numbers.pop();
+ while (!operators.isEmpty() && !numbers.isEmpty()){
+ String num1 = numbers.pop();
+ String operator = operators.pop();
+ result = SingleCul(num1, operator, result);
+ }
+ return result;
+ }
+
+ public String getAnswer(){
+ SplitAndChange();
+ return Compute();
+ }
+
+ private String SingleCul(String nowResult, String operator, String num){
+ // 使用 trim() 去除首尾空格
+ // 使用 split("\\s+") 按空格分割,只取第一个元素(数字)
+ String cleanNowResult = nowResult.trim().split("\\s+")[0];
+ String cleanNum = num.trim().split("\\s+")[0];
+
+ // 现在可以安全地解析数字了
+ Double result = Double.parseDouble(cleanNowResult);
+ switch (operator) {
+ case "+":
+ result += Double.parseDouble(cleanNum);
+ break;
+ case "-":
+ result -= Double.parseDouble(cleanNum);
+ break;
+ case "×":
+ result *= Double.parseDouble(cleanNum);
+ break;
+ case "÷":
+ result /= Double.parseDouble(cleanNum);
+ break;
+ }
+ return String.valueOf(result);
+ }
+
+
+}
+
diff --git a/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java b/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java
new file mode 100644
index 0000000..54f729a
--- /dev/null
+++ b/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java
@@ -0,0 +1,90 @@
+package mathlearning.service.MultipleChoiceGenerator;
+import mathlearning.model.User;
+import mathlearning.service.QuestionGenerator.*;
+
+import java.util.*;
+
+public class MultipleChoiceGenerator {
+ private static QuestionGenerator QuestionGenerator = new PrimaryGenerator();
+ String[] QuestionList ;
+ String[] AnswerList ;
+ String[] ChoiceList ;
+
+ MultipleChoiceGenerator(int count, User nowUser){// 如此声明MultipleChoiceGenerator实例,再调用下面三个接口
+ this.QuestionList = new String[count];
+ this.ChoiceList = new String[count];
+ this.QuestionList = SetQuestionList(count, nowUser);
+ SetChoiceList();
+ }
+
+ public String[] GetQuestionList(){
+ return this.QuestionList;
+ }
+
+ public String[] GetChoiceList(){
+ return this.ChoiceList;
+ }
+
+ public String[] GetAnswerList(){
+ return this.AnswerList;
+ }
+
+ private void SetChoiceList(){
+ for(int i = 0 ; i < this.AnswerList.length ; i++){
+ Random random = new Random();
+ String[] choices = new String[4];
+ double correctChoice = Double.parseDouble(this.AnswerList[i]);
+ int position = random.nextInt(4);
+ choices[position] = String.format("%.2f", correctChoice);
+
+ for(int j = 0 ; j < 4 ; j++){
+ if(j != position){
+ double choice = correctChoice;
+ double offset = random.nextInt(41) - 20;
+ choice += offset;
+ choices[j] = String.format("%.2f", choice);
+ }
+ }
+
+ this.ChoiceList[i] = String.join("\n", choices);
+ }
+ }
+
+ private String[] SetQuestionList(int count, User nowUser){
+ String[] Questions= new String[count];
+ if(nowUser.getType().equals("小学") ) {
+ QuestionGenerator = new PrimaryGenerator();
+ }
+ else if( nowUser.getType().equals("初中") ) {
+ QuestionGenerator = new middleGenerator();
+ }
+ else if( nowUser.getType().equals("高中") ) {
+ QuestionGenerator = new highGenerator();
+ }
+
+ List questions = new ArrayList<>();
+ Set generatedQuestions = new HashSet<>();
+
+ for (int i = 0; i < count; i++) {
+ String question;
+ do {
+ question = QuestionGenerator.generateQuestion();
+ } while ( generatedQuestions.contains(question ));
+ generatedQuestions.add(question);
+ questions.add(question);
+ }
+
+ for(int i = 0 ; i< count ; i++){
+ Questions[i] = questions.get(i);
+ }
+ this.AnswerList = new String[count];
+ for(int i = 0 ; i< count ; i++){
+ ComputingUnit computingUnit = new ComputingUnit(Questions[i]);
+ this.AnswerList[i] = computingUnit.getAnswer();
+ }
+ return Questions;
+ }
+
+
+
+}
diff --git a/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGeneratorTest.java b/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGeneratorTest.java
new file mode 100644
index 0000000..260dd42
--- /dev/null
+++ b/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGeneratorTest.java
@@ -0,0 +1,37 @@
+package mathlearning.service.MultipleChoiceGenerator;
+
+import mathlearning.model.User;
+
+public class MultipleChoiceGeneratorTest {
+ public static void main(String[] args) {
+ // 创建一个模拟用户
+ User testUser = new User();
+ testUser.setType("小学"); // 可以分别测试"小学"、"初中"、"高中"
+
+ // 测试生成10道题目
+ int questionCount = 10;
+ MultipleChoiceGenerator generator = new MultipleChoiceGenerator(questionCount, testUser);
+
+ // 获取生成的题目、答案和选项
+ String[] questions = generator.GetQuestionList();
+ String[] answers = generator.GetAnswerList();
+ String[] choices = generator.GetChoiceList();
+
+ // 输出测试结果
+ System.out.println("=== 数学题目生成测试 ===");
+ System.out.println("用户类型: " + testUser.getType());
+ System.out.println("生成题目数量: " + questionCount);
+ System.out.println();
+
+ for (int i = 0; i < questions.length; i++) {
+ System.out.println("题目 " + (i + 1) + ": " + questions[i]);
+ System.out.println("答案: " + answers[i]);
+ System.out.println("选项:");
+ String[] choiceArray = choices[i].split("\n");
+ for (int j = 0; j < choiceArray.length; j++) {
+ System.out.println(" " + (char)('A' + j) + ". " + choiceArray[j]);
+ }
+ System.out.println("----------------------------------------");
+ }
+ }
+}
diff --git a/src/main/java/mathlearning/service/QuestionGenerator/PrimaryGenerator.java b/src/main/java/mathlearning/service/QuestionGenerator/PrimaryGenerator.java
new file mode 100644
index 0000000..d4f73fc
--- /dev/null
+++ b/src/main/java/mathlearning/service/QuestionGenerator/PrimaryGenerator.java
@@ -0,0 +1,22 @@
+package mathlearning.service.QuestionGenerator;
+
+public class PrimaryGenerator extends QuestionGenerator{
+ public PrimaryGenerator() {
+ super("小学");
+ }
+
+ @Override
+ public String generateQuestion() {
+
+ int operandCount = random.nextInt(4) + 2;
+
+ // 生成操作数
+ int[] operands = new int[operandCount];
+ for (int i = 0; i < operandCount; i++) {
+ operands[i] = random.nextInt(100) + 1; // 1-100
+ }
+
+ String question = preForOper(operands);
+ return addParen(question);
+ }
+}
diff --git a/src/main/java/mathlearning/service/QuestionGenerator/QuestionGenerator.java b/src/main/java/mathlearning/service/QuestionGenerator/QuestionGenerator.java
new file mode 100644
index 0000000..b41d315
--- /dev/null
+++ b/src/main/java/mathlearning/service/QuestionGenerator/QuestionGenerator.java
@@ -0,0 +1,142 @@
+package mathlearning.service.QuestionGenerator;
+
+import java.util.Random;
+
+public abstract class QuestionGenerator{
+ protected Random random = new Random();
+ public abstract String generateQuestion() ;
+ protected String type;
+
+ public String getType() {
+ return type;
+ }
+
+ QuestionGenerator() {
+ type = "无";
+ }
+
+ QuestionGenerator(String Type) {
+ type = Type;
+ }
+
+ protected String preForOper(int[] operands) {
+ StringBuilder question = new StringBuilder();
+ String[] operators = {"+", "-", "×", "÷"};
+ question.append(operands[0]);
+
+ for (int i = 1; i < operands.length; i++) {
+ String op = operators[ random.nextInt (operators.length)];
+ question.append(" ").append(op).append(" ").append(operands[i]);
+ }
+ return question.toString();
+
+ }
+
+ protected boolean Check_num(String expression) {
+ if(!(expression.equals("+") || expression.equals("-") || expression.equals("×") || expression.equals("÷"))) {
+ return true;
+ }
+ else{
+ return false;
+ }
+ }
+
+ protected String addParen(String expression) {
+ String[] parts = expression.split(" ");
+ StringBuilder result = new StringBuilder();
+ boolean r_paren_needed = false;
+
+ for (int i = 0; i < parts.length; i++) {
+ if(Check_num ( parts [i]) ) {
+ if( !r_paren_needed ) {
+ if(i <= parts.length -3 )
+ {
+ if( random.nextBoolean() )
+ { result.append("(");r_paren_needed = true;}
+ }
+ result.append(parts[i]);
+ } else {
+ result.append( parts [i]);
+ if( !random.nextBoolean()) {
+ result.append(")");r_paren_needed = false;
+ }
+ }
+ } else {
+ result.append( parts [i] );
+ }
+ if( i < parts.length -1 ) {
+ result.append(" ");
+ }
+ }
+
+ if( r_paren_needed ){
+ result.append(")");r_paren_needed = false;
+ }
+ return result.toString();
+ }
+
+ protected String add_squs(String expression) {
+ String[] parts = expression.split(" ");
+ StringBuilder result = new StringBuilder();
+ boolean has_squs = false;
+
+ for (int i = 0; i < parts.length; i++) {
+ if( Check_num( parts [i] )) {
+ double Thres = 0.3;
+ if( !has_squs){
+ Thres = 0.7;
+ }
+ if ( random.nextDouble() < Thres ||(i == parts.length -1 && !has_squs)) {
+ if ( random.nextBoolean() ) {
+ result.append(parts[i]);
+ result.append("²");
+ has_squs = true;
+ } else {
+ result.append("√");
+ result.append(parts[i]);
+ has_squs = true;
+ }
+ } else {
+ result.append(parts[i]);
+ }
+ } else {
+ result.append(parts[i]);
+ }
+ if( i < parts.length -1 ) {
+ result.append(" ");
+ }
+ }
+ return result.toString();
+ }
+
+ protected String add_sins(String expression) {
+ String[] parts = expression.split(" ");
+ StringBuilder result = new StringBuilder();
+ String[] functions = {"sin", "cos", "tan"};
+ boolean has_func = false;
+
+ for (int i = 0; i < parts.length; i++) {
+ double Thres = 0.4;
+ if(!has_func){Thres = 0.8;}
+ if(Check_num(parts[i]))
+ {
+ if ( random.nextDouble() < Thres ||(i == parts.length-1 && !has_func) ) {
+ String func = functions[random.nextInt(functions.length)];
+ result.append(func).append("(").append(parts[i]).append(")");
+ } else {
+ result.append(parts[i]);
+ }
+ } else {
+ result.append(parts[i]);
+ }
+ if( i < parts.length-1 ) {
+ result.append(" ");
+ }
+
+ }
+
+ return result.toString();
+ }
+}
+
+
diff --git a/src/main/java/mathlearning/service/QuestionGenerator/highGenerator.java b/src/main/java/mathlearning/service/QuestionGenerator/highGenerator.java
new file mode 100644
index 0000000..bfc17fc
--- /dev/null
+++ b/src/main/java/mathlearning/service/QuestionGenerator/highGenerator.java
@@ -0,0 +1,24 @@
+package mathlearning.service.QuestionGenerator;
+
+public class highGenerator extends QuestionGenerator{
+
+ public highGenerator() {
+ super("高中");
+ }
+
+ @Override
+ public String generateQuestion() {
+ int operandCount = random.nextInt(4) + 2;
+
+ // 生成操作数
+ int[] operands = new int[ operandCount ];
+ for (int i = 0; i < operandCount; i++) {
+ operands[i] = random.nextInt(100) + 1; // 1-100
+ }
+
+ String question = preForOper( operands );
+ question = add_squs( question );
+ question = add_sins( question );
+ return addParen( question );
+ }
+}
diff --git a/src/main/java/mathlearning/service/QuestionGenerator/middleGenerator.java b/src/main/java/mathlearning/service/QuestionGenerator/middleGenerator.java
new file mode 100644
index 0000000..b1d3b61
--- /dev/null
+++ b/src/main/java/mathlearning/service/QuestionGenerator/middleGenerator.java
@@ -0,0 +1,23 @@
+package mathlearning.service.QuestionGenerator;
+
+public class middleGenerator extends QuestionGenerator{
+
+ public middleGenerator() {
+ super("初中");
+ }
+
+ @Override
+ public String generateQuestion() {
+ int operandCount = random.nextInt(4) + 2;
+
+ // 生成操作数
+ int[] operands = new int [ operandCount ];
+ for (int i = 0; i < operandCount; i++) {
+ operands[i] = random.nextInt(100) + 1; // 1-100
+ }
+
+ String question = preForOper(operands);
+ question = add_squs(question);
+ return addParen(question);
+ }
+}
\ No newline at end of file
From 144e5f5e7569a3cda5b701345051c684e0897a3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=98=A0=E6=B1=9F?=
<15547749+cyj-050209@user.noreply.gitee.com>
Date: Sat, 11 Oct 2025 19:58:57 +0800
Subject: [PATCH 16/16] =?UTF-8?q?=E6=9C=80=E7=BB=88=E7=89=881.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
data/users.json | 10 +++++-
.../MultipleChoiceGenerator.java | 21 ++++++++---
.../java/mathlearning/ui/SelTestFrame.java | 36 ++++++++++++++++---
3 files changed, 56 insertions(+), 11 deletions(-)
diff --git a/data/users.json b/data/users.json
index 3f0ec0d..721399b 100644
--- a/data/users.json
+++ b/data/users.json
@@ -1,9 +1,17 @@
[ {
+ "username" : "LXR",
+ "email" : "2759462569@qq.com",
+ "passwordHash" : "$2a$12$AiupZKqpZMXvstX6XYNGUuBDZOMdzqXy/ADaYzurUYatM293Lbxii",
+ "registrationDate" : [ 2025, 10, 11, 19, 40, 59, 177105400 ],
+ "verificationCode" : "371978",
+ "verified" : true,
+ "type" : "高中"
+}, {
"username" : "小鱼",
"email" : "1280556515@qq.com",
"passwordHash" : "$2a$12$dbNwBK6NBj7mXU6YzNMAweTMhD9NOxsjPGzW2SfIM.QvGdWt7Lyvy",
"registrationDate" : [ 2025, 10, 10, 11, 7, 5, 853200500 ],
"verificationCode" : "688201",
"verified" : true,
- "type" : "初中"
+ "type" : "高中"
} ]
\ No newline at end of file
diff --git a/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java b/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java
index 54f729a..f844f1a 100644
--- a/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java
+++ b/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java
@@ -9,10 +9,12 @@ public class MultipleChoiceGenerator {
String[] QuestionList ;
String[] AnswerList ;
String[] ChoiceList ;
+ String[] CorrectAnswerNo;
- MultipleChoiceGenerator(int count, User nowUser){// 如此声明MultipleChoiceGenerator实例,再调用下面三个接口
+ public MultipleChoiceGenerator(int count, User nowUser){// 如此声明MultipleChoiceGenerator实例,再调用下面三个接口
this.QuestionList = new String[count];
this.ChoiceList = new String[count];
+ this.CorrectAnswerNo = new String[count];
this.QuestionList = SetQuestionList(count, nowUser);
SetChoiceList();
}
@@ -29,24 +31,33 @@ public class MultipleChoiceGenerator {
return this.AnswerList;
}
+ public String[] GetCorrectAnswerNo(){
+ return this.CorrectAnswerNo;
+ }
+
private void SetChoiceList(){
for(int i = 0 ; i < this.AnswerList.length ; i++){
Random random = new Random();
+ String[] choiceNo = {"A","B","C","D"};
String[] choices = new String[4];
double correctChoice = Double.parseDouble(this.AnswerList[i]);
int position = random.nextInt(4);
- choices[position] = String.format("%.2f", correctChoice);
+ choices[position] = choiceNo[position] + String.format("%.2f", correctChoice);
+
for(int j = 0 ; j < 4 ; j++){
if(j != position){
double choice = correctChoice;
double offset = random.nextInt(41) - 20;
choice += offset;
- choices[j] = String.format("%.2f", choice);
+ choices[j] =choiceNo[j] +"." + String.format("%.2f", choice);
+ }
+ else{
+ CorrectAnswerNo[i] = choiceNo[j];
}
}
- this.ChoiceList[i] = String.join("\n", choices);
+ this.ChoiceList[i] = String.join(" ", choices);
}
}
@@ -87,4 +98,4 @@ public class MultipleChoiceGenerator {
-}
+}
\ No newline at end of file
diff --git a/src/main/java/mathlearning/ui/SelTestFrame.java b/src/main/java/mathlearning/ui/SelTestFrame.java
index ceac476..327e3d7 100644
--- a/src/main/java/mathlearning/ui/SelTestFrame.java
+++ b/src/main/java/mathlearning/ui/SelTestFrame.java
@@ -1,6 +1,7 @@
package mathlearning.ui;
import mathlearning.model.User;
+import mathlearning.service.MultipleChoiceGenerator.MultipleChoiceGenerator;
import mathlearning.service.UserService;
import javax.swing.*;
@@ -10,9 +11,11 @@ public class SelTestFrame extends JFrame {
private MainFrame mainFrame;
private User user;
private UserService userService;
+ private MultipleChoiceGenerator multipleChoiceGenerator;
private int questionNum;
private int currentQuestionDex;
+ private String[] answers;
private String[] myAnswers;
private JLabel titleLabel;
@@ -23,6 +26,8 @@ public class SelTestFrame extends JFrame {
private JButton prevButton;
private JButton nextButton;
private JButton submitButton;
+ private JLabel question;
+ private JLabel choice;
public SelTestFrame(MainFrame mainFrame, User user, UserService userService, int num) {
this.mainFrame = mainFrame;
@@ -31,6 +36,8 @@ public class SelTestFrame extends JFrame {
this.questionNum = num;
this.currentQuestionDex = 0;
this.myAnswers = new String[questionNum];
+ this.multipleChoiceGenerator = new MultipleChoiceGenerator(num, user);
+ this.answers = multipleChoiceGenerator.GetCorrectAnswerNo();
InitUI();
addWindowListener(new java.awt.event.WindowAdapter() {
@Override
@@ -95,7 +102,21 @@ public class SelTestFrame extends JFrame {
}
private JPanel centerPanel() {
- JPanel panel = new JPanel(new BorderLayout());
+ JPanel panel = new JPanel(new GridLayout(6, 1));
+
+ JLabel questionLabel = new JLabel("题目:");
+ questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ question = new JLabel(multipleChoiceGenerator.GetQuestionList()[currentQuestionDex]);
+ question.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ panel.add(questionLabel);
+ panel.add(question);
+
+ JLabel choiceLabel = new JLabel("选项:");
+ choiceLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+ choice = new JLabel(multipleChoiceGenerator.GetChoiceList()[currentQuestionDex]);
+ choiceLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+ panel.add(choiceLabel);
+ panel.add(choice);
return panel;
}
@@ -187,6 +208,7 @@ public class SelTestFrame extends JFrame {
if (currentQuestionDex > 0) {
currentQuestionDex--;
updateTitleLabel();
+ updateQuestion();
if (myAnswers[currentQuestionDex] == null) {
resetButtonColors();
}
@@ -202,6 +224,7 @@ public class SelTestFrame extends JFrame {
if (currentQuestionDex < questionNum - 1) {
currentQuestionDex++;
updateTitleLabel();
+ updateQuestion();
if (myAnswers[currentQuestionDex] == null) {
resetButtonColors();
} else {
@@ -216,6 +239,7 @@ public class SelTestFrame extends JFrame {
int confirm = JOptionPane.showConfirmDialog(SelTestFrame.this, "你确定要提交试卷吗?", "提示", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
int correctCount = 0;
+ correctCount = getScore(answers, myAnswers);
double accuracy = Math.round((double) correctCount / myAnswers.length * 10000) / 100.0;
double score = Math.round((double) correctCount * 100 / myAnswers.length * 10) / 10.0;
@@ -224,7 +248,7 @@ public class SelTestFrame extends JFrame {
String[] options = {"返回主菜单", "继续做题"};
int choice = JOptionPane.showOptionDialog(
SelTestFrame.this,
- "您答对了" + score + "道题,正确率为" + String.format("%.2f", accuracy) + "\n您的得分是: " + score + "分",
+ "您答对了" + correctCount + "道题,正确率为" + String.format("%.2f", accuracy) + "\n您的得分是: " + score + "分",
"测试结果",
JOptionPane.YES_NO_OPTION,
JOptionPane.INFORMATION_MESSAGE,
@@ -237,8 +261,9 @@ public class SelTestFrame extends JFrame {
mainFrame.setVisible(true);
dispose();
} else if (choice == 1) {
- mainFrame.reTryTest();
dispose();
+ mainFrame.setVisible(true);
+ mainFrame.reTryTest();
}
}
}
@@ -292,7 +317,8 @@ public class SelTestFrame extends JFrame {
return score;
}
- private String getQuestion(String[] questions, int dex) {
- return questions[dex];
+ private void updateQuestion() {
+ question.setText(multipleChoiceGenerator.GetQuestionList()[currentQuestionDex]);
+ choice.setText(multipleChoiceGenerator.GetChoiceList()[currentQuestionDex]);
}
}