From f606612a825d5d083a8c58f820cb406b3d3080c9 Mon Sep 17 00:00:00 2001 From: yesitis Date: Wed, 8 Oct 2025 21:24:53 +0800 Subject: [PATCH 01/19] initial --- 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 ++++++++++++++++++ 6 files changed, 638 insertions(+) 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/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(); + } +} -- 2.34.1 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 02/19] =?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(); + } +} -- 2.34.1 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 03/19] =?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); } } -- 2.34.1 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 04/19] =?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); // 这里先简单显示一个消息,后续可以扩展为主界面 -- 2.34.1 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 05/19] =?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); -- 2.34.1 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 06/19] =?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, -- 2.34.1 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 07/19] =?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); } } -- 2.34.1 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 08/19] =?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 -- 2.34.1 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 09/19] =?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) { + + } + } +} -- 2.34.1 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 10/19] =?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); + } + } + } + } +} -- 2.34.1 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 11/19] =?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 { -- 2.34.1 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 12/19] =?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); -- 2.34.1 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 13/19] =?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(); + } + } +} -- 2.34.1 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 14/19] =?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; + } } -- 2.34.1 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 15/19] =?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]; + } } -- 2.34.1 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 16/19] =?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 -- 2.34.1 From 3a060b7af0e8dc6959b7ac9bb68994c2eafe8e6e Mon Sep 17 00:00:00 2001 From: yesitis Date: Sat, 11 Oct 2025 19:54:19 +0800 Subject: [PATCH 17/19] delete src --- 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 ------------------ 6 files changed, 638 deletions(-) delete mode 100644 src/main/java/mathlearning/App.java delete mode 100644 src/main/java/mathlearning/model/User.java delete mode 100644 src/main/java/mathlearning/service/EmailService.java delete mode 100644 src/main/java/mathlearning/service/UserService.java delete mode 100644 src/main/java/mathlearning/ui/LoginFrame.java delete mode 100644 src/main/java/mathlearning/ui/RegisterFrame.java diff --git a/src/main/java/mathlearning/App.java b/src/main/java/mathlearning/App.java deleted file mode 100644 index 0e2ff2d..0000000 --- a/src/main/java/mathlearning/App.java +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index 57b7887..0000000 --- a/src/main/java/mathlearning/model/User.java +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index 86f5b0d..0000000 --- a/src/main/java/mathlearning/service/EmailService.java +++ /dev/null @@ -1,57 +0,0 @@ -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 deleted file mode 100644 index f239562..0000000 --- a/src/main/java/mathlearning/service/UserService.java +++ /dev/null @@ -1,175 +0,0 @@ -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 deleted file mode 100644 index a9579d0..0000000 --- a/src/main/java/mathlearning/ui/LoginFrame.java +++ /dev/null @@ -1,111 +0,0 @@ -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 deleted file mode 100644 index ebc97c7..0000000 --- a/src/main/java/mathlearning/ui/RegisterFrame.java +++ /dev/null @@ -1,230 +0,0 @@ -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(); - } -} -- 2.34.1 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 18/19] =?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]); } } -- 2.34.1 From d87ac56bfa1a57ed55e51a59d576740684569cb7 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 20:22:50 +0800 Subject: [PATCH 19/19] =?UTF-8?q?=E6=9C=80=E7=BB=88=E7=89=881.2=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8F=90=E7=A4=BA=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/mathlearning/ui/ChangeCodeFrame.java | 4 ++-- .../java/mathlearning/ui/ChangePasswordFrame.java | 9 +++++++-- src/main/java/mathlearning/ui/LoginFrame.java | 2 +- src/main/java/mathlearning/ui/RegisterFrame.java | 13 ++++++++++--- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/java/mathlearning/ui/ChangeCodeFrame.java b/src/main/java/mathlearning/ui/ChangeCodeFrame.java index 37ad12b..d799553 100644 --- a/src/main/java/mathlearning/ui/ChangeCodeFrame.java +++ b/src/main/java/mathlearning/ui/ChangeCodeFrame.java @@ -65,7 +65,7 @@ public class ChangeCodeFrame extends JFrame { //表单面板 JPanel formPanel = new JPanel(new GridLayout(5, 2, 10, 10)); - JLabel emailLabel = new JLabel("邮箱:"); + JLabel emailLabel = new JLabel("QQ邮箱:"); emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); emailField = new JTextField(); @@ -80,7 +80,7 @@ public class ChangeCodeFrame extends JFrame { codePanel.add(codeField, BorderLayout.CENTER); codePanel.add(sendCodeButton, BorderLayout.EAST); - JLabel passwordLabel = new JLabel("新密码:"); + JLabel passwordLabel = new JLabel("新密码(大小写字母+数字):"); passwordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); passwordField = new JPasswordField(); diff --git a/src/main/java/mathlearning/ui/ChangePasswordFrame.java b/src/main/java/mathlearning/ui/ChangePasswordFrame.java index 96848fe..f79431b 100644 --- a/src/main/java/mathlearning/ui/ChangePasswordFrame.java +++ b/src/main/java/mathlearning/ui/ChangePasswordFrame.java @@ -46,7 +46,7 @@ public class ChangePasswordFrame extends JFrame { mainPanel.add(titleLabel, BorderLayout.NORTH); //表单 - JPanel infoPanel = new JPanel(new GridLayout(4, 2, 10, 10)); + JPanel infoPanel = new JPanel(new GridLayout(3, 2, 10, 10)); JLabel oldPasswordLabel = new JLabel("请输入旧密码:"); oldPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); oldPasswordField = new JPasswordField(); @@ -62,7 +62,12 @@ public class ChangePasswordFrame extends JFrame { confirmPasswordField = new JPasswordField(); infoPanel.add(confirmPasswordLabel); infoPanel.add(confirmPasswordField); - mainPanel.add(infoPanel, BorderLayout.CENTER); + JLabel infoLabel = new JLabel("密码需要包含大小写字母以及数字,6-10位。"); + infoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + JPanel centerPanel = new JPanel(new BorderLayout()); + centerPanel.add(infoPanel, BorderLayout.CENTER); + centerPanel.add(infoLabel, BorderLayout.SOUTH); + mainPanel.add(centerPanel, BorderLayout.CENTER); //按钮 JPanel buttonPanel = new JPanel(new FlowLayout()); diff --git a/src/main/java/mathlearning/ui/LoginFrame.java b/src/main/java/mathlearning/ui/LoginFrame.java index b2f5a96..e116abb 100644 --- a/src/main/java/mathlearning/ui/LoginFrame.java +++ b/src/main/java/mathlearning/ui/LoginFrame.java @@ -37,7 +37,7 @@ public class LoginFrame extends JFrame{ // 表单面板 JPanel formPanel = new JPanel(new GridLayout(3, 2, 10, 10)); - JLabel emailLabel = new JLabel("邮箱:"); + JLabel emailLabel = new JLabel("QQ邮箱:"); emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); emailField = new JTextField(); diff --git a/src/main/java/mathlearning/ui/RegisterFrame.java b/src/main/java/mathlearning/ui/RegisterFrame.java index fb0f5a9..ef7997e 100644 --- a/src/main/java/mathlearning/ui/RegisterFrame.java +++ b/src/main/java/mathlearning/ui/RegisterFrame.java @@ -64,13 +64,13 @@ public class RegisterFrame extends JFrame{ mainPanel.add(titleLabel, BorderLayout.NORTH); // 表单面板 - JPanel formPanel = new JPanel(new GridLayout(5, 2, 10, 10)); + JPanel formPanel = new JPanel(new GridLayout(6, 2, 10, 10)); JLabel nameLabel = new JLabel("用户名:"); nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); nameField = new JTextField(); - JLabel emailLabel = new JLabel("邮箱:"); + JLabel emailLabel = new JLabel("QQ邮箱:"); emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); emailField = new JTextField(); @@ -93,6 +93,9 @@ public class RegisterFrame extends JFrame{ confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); confirmPasswordField = new JPasswordField(); + JLabel infoLabel = new JLabel("提示:密码需要包含大小写字母以及数字,6-10位。"); + infoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + formPanel.add(nameLabel); formPanel.add(nameField); formPanel.add(emailLabel); @@ -104,7 +107,11 @@ public class RegisterFrame extends JFrame{ formPanel.add(confirmPasswordLabel); formPanel.add(confirmPasswordField); - mainPanel.add(formPanel, BorderLayout.CENTER); + JPanel centerPanel = new JPanel(new BorderLayout()); + centerPanel.add(formPanel, BorderLayout.CENTER); + centerPanel.add(infoLabel, BorderLayout.SOUTH); + + mainPanel.add(centerPanel, BorderLayout.CENTER); // 按钮面板 JPanel buttonPanel = new JPanel(new FlowLayout()); -- 2.34.1