From 6814780dc2b1f65ab52148645203062e5215a6a8 Mon Sep 17 00:00:00 2001 From: liq <3403517738@qq.com> Date: Sun, 12 Oct 2025 16:20:49 +0800 Subject: [PATCH] 11 --- ...用户界面与交互控制说明文档.md | 233 +++++ src/MathLearningApp.java | 983 ++++++++++++++++++ src/MathLearningAppMethods.java | 464 +++++++++ src/QQEmailService.java | 218 ++++ src/RealEmailSender.java | 176 ++++ src/RegisteredUser.java | 56 + 6 files changed, 2130 insertions(+) create mode 100644 doc/数学学习软件 - 用户界面与交互控制说明文档.md create mode 100644 src/MathLearningApp.java create mode 100644 src/MathLearningAppMethods.java create mode 100644 src/QQEmailService.java create mode 100644 src/RealEmailSender.java create mode 100644 src/RegisteredUser.java diff --git a/doc/数学学习软件 - 用户界面与交互控制说明文档.md b/doc/数学学习软件 - 用户界面与交互控制说明文档.md new file mode 100644 index 0000000..cc00a03 --- /dev/null +++ b/doc/数学学习软件 - 用户界面与交互控制说明文档.md @@ -0,0 +1,233 @@ +# 带UI的小初高数学学习软件 + +## 引言 + +本项目旨在为小学、初中和高中学生提供一个数学学习平台,通过图形用户界面(GUI)与用户进行交互。系统包含用户注册、登录、答题等功能,旨在帮助学生通过练习题目提高数学水平。通过该项目,用户可以注册账号、登录、设置密码、接收验证码、选择题目数量并参与答题,最终获取成绩反馈。 + +## 需求分析 + +### 用户需求 +- **用户群体**:小学、初中和高中学生。 +- **主要功能**: + 1. **用户注册**:用户提供邮箱地址,接收注册码并完成注册。 + 2. **用户登录**:通过注册的用户名和密码登录。 + 3. **答题功能**:用户根据所选学段生成题目并进行选择题答题。 + 4. **密码设置与修改**:设置密码需满足长度和字符要求,且用户可修改密码。 + 5. **邮箱验证码**:用户注册时,通过邮箱发送验证码进行身份验证。 + 6. **成绩评定**:完成答题后,系统自动计算得分并显示。 + +### 系统功能 +- **图形界面**:包括欢迎页面、注册页面、登录页面、题目页面和成绩页面等。 +- **事件处理**:用户操作界面后触发相应的事件(如点击按钮提交、输入框验证)。 +- **邮件服务**:通过QQ邮箱服务发送验证码。 +- **界面布局**:界面设计需简洁、易于操作。 + +## 系统设计 + +### 系统设计架构 + +本项目采用 **MVC**(Model-View-Controller)设计模式,分为 **模型层**(Model)、**视图层**(View)、和 **控制器层**(Controller)。这种架构使得应用程序更加模块化,易于维护和扩展。下面是对各个模块的详细分析和代码实现的具体说明。 + +#### 1. **Model(模型层)** + +模型层负责处理程序的核心数据逻辑,进行业务逻辑的处理和数据的存储。在本项目中,模型层并不涉及数据库存储,所有数据都保存在内存中,程序退出时数据丢失。 + +##### 主要类: + +##### b **User.java**(假设该类存在于项目中,负责用户信息管理) + +```java +public class User { + private String username; + private String password; + private String email; + + public User(String username, String password, String email) { + this.username = username; + this.password = password; + this.email = email; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getEmail() { + return email; + } +} +``` +#### 2. **View(视图层)** + +视图层负责用户界面的展示。它接收用户输入,并将其传递给控制器层;同时,接收控制器层传递的数据并更新用户界面。这里使用 Java Swing 库构建图形用户界面。 + +#### 主要类: + +#### **MathLearningApp.java**(主应用程序) +```java +import javax.swing.*; + +public class MathLearningApp { +public static void main(String[] args) { +JFrame frame = new JFrame("Math Learning App"); +JButton registerButton = new JButton("Register"); +registerButton.addActionListener(e -> { +// 弹出注册界面 +new RegistrationForm().setVisible(true); +}); +frame.add(registerButton); +frame.setSize(300, 200); +frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); +frame.setVisible(true); +} +} +``` +**说明:** +- **MathLearningApp.java** 类是应用程序的主入口,创建了一个 `JFrame` 作为主窗口,并添加了一个注册按钮。点击按钮时,会弹出 `RegistrationForm`(注册表单)界面。 +#### **RegistrationForm.java**(注册界面,假设代码存在) +```java +public class RegistrationForm extends JFrame { +public RegistrationForm() { +setTitle("User Registration"); +setSize(300, 200); +setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + JTextField emailField = new JTextField(); + JPasswordField passwordField = new JPasswordField(); + JButton submitButton = new JButton("Submit"); + + submitButton.addActionListener(e -> { + String email = emailField.getText(); + String password = new String(passwordField.getPassword()); + // 注册处理逻辑 + }); + + // 设置布局、添加组件等... + } +} +``` +**说明:** +- **RegistrationForm.java** 类用于显示用户注册界面,包含了邮箱和密码输入框,以及一个提交按钮,按钮点击后会触发注册逻辑。 +#### 3. **Controller(控制器层)** + +控制器层负责接收用户的输入并更新模型和视图。它是模型和视图之间的桥梁。 + +#### 主要类: + +#### **MathLearningAppMethods.java**(界面方法扩展类) + +```java +public class MathLearningAppMethods { + public static void handleRegistration(String email, String password) { + // 校验邮箱格式 + if (!isValidEmail(email)) { + JOptionPane.showMessageDialog(null, "Invalid email address!"); + return; + } + + // 用户注册逻辑 + User newUser = new User(email, password, email); + // 这里通常会调用模型层存储用户信息,但由于不使用数据库,暂时保存在内存中 + System.out.println("User registered: " + newUser.getUsername()); + + // 发送注册成功邮件(通过邮件服务) + sendVerificationEmail(email); + } + + private static boolean isValidEmail(String email) { + // 邮箱格式验证逻辑 + return email.contains("@"); + } + + private static void sendVerificationEmail(String email) { + // 使用 RealEmailSender 发送邮件 + new RealEmailSender().sendEmail(email, "Verification Code", "123456"); + } +} +``` +**说明:** +- **MathLearningAppMethods.java** 中的 `handleRegistration` 方法处理用户的注册逻辑。首先,它验证邮箱格式,如果正确则创建一个新的 `User` 实例,接着通过 `RealEmailSender` 类发送验证邮件。 + +--- + +#### 4. **邮件发送服务** + +邮件服务模块用于处理邮件发送,项目中使用了 QQ 邮箱服务来发送验证码。 + +#### 主要类: +#### **QQEmailService.java**(QQ邮箱服务) +```java +public class QQEmailService { + public void sendVerificationCode(String email, String code) { + // 使用 QQ 邮箱的 SMTP 服务发送邮件 + System.out.println("Sending verification code " + code + " to " + email); + } +} +``` +#### 4. **邮件发送服务** + +邮件服务模块用于处理邮件发送,项目中使用了 QQ 邮箱服务来发送验证码。 + +#### 主要类: + +#### **QQEmailService.java**(QQ邮箱服务) + +**说明:** +- **QQEmailService.java** 类负责通过 QQ 邮箱发送验证码。该服务并没有连接真实的 SMTP 服务器,实际操作中可以进一步配置邮箱服务器来实现邮件的发送。 + +#### **RealEmailSender.java**(邮件发送器) + +```java +public class RealEmailSender { + private QQEmailService emailService = new QQEmailService(); + + public void sendEmail(String recipient, String subject, String body) { + // 通过 QQEmailService 实现邮件的发送 + emailService.sendVerificationCode(recipient, body); + } +} +``` +**说明:** +- **RealEmailSender.java** 类负责邮件的实际发送操作,它调用了 `QQEmailService` 类来发送邮件。未来可以根据需要扩展更多邮件服务支持。 + +### 总结 + +- **Model(模型层)**:管理核心数据,处理用户信息和业务逻辑。 +- **View(视图层)**:构建图形用户界面,展示数据并与用户交互。 +- **Controller(控制器层)**:处理用户输入,协调模型和视图的交互。 + +这种设计模式使得代码的结构更加清晰,易于维护。用户界面部分(视图)与业务逻辑部分(模型)相互独立,便于将来扩展更多功能,优化代码结构。### 数据设计 +本系统不使用数据库存储数据,所有用户数据(如注册信息、成绩等)都存储在内存中,程序运行期间临时保存。 + +## 实现与运行 + +### 环境与依赖 +- **开发语言**:Java 确保已安装Java环境(JDK 8及以上) +- **GUI框架**:Java Swing +- **邮件服务**:QQ邮箱SMTP服务 +- **运行平台**:支持Windows、macOS和Linux平台 +### 运行项目 + +1. 编译并运行 `MathLearningApp.java` 文件,即可启动应用。 +2. 根据界面提示完成注册、登录和答题操作。 + +### 功能实现 + +#### 用户注册与登录: +- 用户提供邮箱注册,接收并输入验证码完成注册。 +- 密码必须包含大小写字母和数字,且长度为6-10位。 + +#### 答题流程: +- 根据选择的年级生成题目,用户逐一作答。 +- 答题后自动计算成绩,并提供继续答题或退出的选项。 + +#### 邮件验证: +- 注册时通过 `QQEmailService.java` 发送验证码到用户邮箱。 + +#### 界面管理: +- 通过 `MathLearningAppMethods.java` 进行界面布局和用户交互。 + diff --git a/src/MathLearningApp.java b/src/MathLearningApp.java new file mode 100644 index 0000000..aaad1ce --- /dev/null +++ b/src/MathLearningApp.java @@ -0,0 +1,983 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; +import java.util.List; +import java.util.regex.Pattern; +// 邮件功能暂时使用模拟实现,不需要真实的邮件库 +import java.io.*; +import java.nio.file.*; + +/** + * 带UI的小初高数学学习软件 - 主程序 + * 基于原有命令行版本改写的GUI版本 + */ +public class MathLearningApp extends JFrame { + private CardLayout cardLayout; + private JPanel mainPanel; + + // 用户管理 + private Map registeredUsers; + private RegisteredUser currentUser; + + // 题目管理 + private MathQuestionGenerator questionGenerator; + private List currentQuestions; + private int currentQuestionIndex; + private int correctAnswers; + private String currentDifficulty; + + // 界面组件 + private JTextField emailField; + private JTextField verificationCodeField; + private JPasswordField passwordField; + private JPasswordField confirmPasswordField; + private JPasswordField oldPasswordField; + private JLabel questionLabel; + private ButtonGroup answerGroup; + private JRadioButton[] answerButtons; + private JLabel scoreLabel; + + public MathLearningApp() { + initializeData(); + initializeGUI(); + loadUserData(); + } + + /** + * 初始化数据 + */ + private void initializeData() { + registeredUsers = new HashMap<>(); + questionGenerator = new MathQuestionGenerator(); + currentQuestions = new ArrayList<>(); + currentQuestionIndex = 0; + correctAnswers = 0; + answerButtons = new JRadioButton[4]; + } + + /** + * 初始化GUI界面 + */ + private void initializeGUI() { + setTitle("小初高数学学习软件"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(800, 600); + setLocationRelativeTo(null); + + cardLayout = new CardLayout(); + mainPanel = new JPanel(cardLayout); + + // 创建各个界面 + createWelcomePanel(); + createRegisterPanel(); + createLoginPanel(); + createPasswordSetupPanel(); + createDifficultySelectionPanel(); + createQuestionPanel(); + createScorePanel(); + createPasswordChangePanel(); + + add(mainPanel); + + // 显示欢迎界面 + cardLayout.show(mainPanel, "WELCOME"); + } + + /** + * 创建欢迎界面 + */ + private void createWelcomePanel() { + JPanel panel = new JPanel(new BorderLayout()) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 创建渐变背景 + GradientPaint gradient = new GradientPaint(0, 0, new Color(135, 206, 250), + 0, getHeight(), new Color(240, 248, 255)); + g2.setPaint(gradient); + g2.fillRect(0, 0, getWidth(), getHeight()); + + g2.dispose(); + } + }; + + // 标题面板 + JPanel titlePanel = new JPanel(); + titlePanel.setOpaque(false); + titlePanel.setBorder(BorderFactory.createEmptyBorder(80, 0, 50, 0)); + + JLabel titleLabel = new JLabel("小初高数学学习软件", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 36)); + titleLabel.setForeground(new Color(25, 25, 112)); + + JLabel subtitleLabel = new JLabel("让数学学习变得更有趣", JLabel.CENTER); + subtitleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); + subtitleLabel.setForeground(new Color(70, 130, 180)); + subtitleLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); + + titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.Y_AXIS)); + titlePanel.add(titleLabel); + titlePanel.add(subtitleLabel); + + // 按钮面板 + JPanel buttonPanel = new JPanel(); + buttonPanel.setOpaque(false); + buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 30, 20)); + + JButton registerButton = createStyledButton("用户注册", new Color(60, 179, 113)); + JButton loginButton = createStyledButton("用户登录", new Color(70, 130, 180)); + + registerButton.addActionListener(e -> cardLayout.show(mainPanel, "REGISTER")); + loginButton.addActionListener(e -> cardLayout.show(mainPanel, "LOGIN")); + + buttonPanel.add(registerButton); + buttonPanel.add(loginButton); + + // 底部信息 + JLabel footerLabel = new JLabel("© 2025 数学学习软件 - 专业的数学学习平台", JLabel.CENTER); + footerLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12)); + footerLabel.setForeground(new Color(100, 100, 100)); + footerLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 30, 0)); + + panel.add(titlePanel, BorderLayout.NORTH); + panel.add(buttonPanel, BorderLayout.CENTER); + panel.add(footerLabel, BorderLayout.SOUTH); + + mainPanel.add(panel, "WELCOME"); + } + + /** + * 创建注册界面 + */ + private void createRegisterPanel() { + JPanel panel = new JPanel(new BorderLayout()) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 创建渐变背景 + GradientPaint gradient = new GradientPaint(0, 0, new Color(250, 250, 250), + 0, getHeight(), new Color(240, 248, 255)); + g2.setPaint(gradient); + g2.fillRect(0, 0, getWidth(), getHeight()); + + g2.dispose(); + } + }; + + // 标题面板 + JPanel titlePanel = new JPanel(); + titlePanel.setOpaque(false); + titlePanel.setBorder(BorderFactory.createEmptyBorder(40, 0, 30, 0)); + + JLabel titleLabel = new JLabel("用户注册", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 28)); + titleLabel.setForeground(new Color(25, 25, 112)); + + JLabel subtitleLabel = new JLabel("请填写您的邮箱信息完成注册", JLabel.CENTER); + subtitleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + subtitleLabel.setForeground(new Color(100, 100, 100)); + subtitleLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); + + titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.Y_AXIS)); + titlePanel.add(titleLabel); + titlePanel.add(subtitleLabel); + + // 表单面板 - 添加卡片样式 + JPanel formContainer = new JPanel(); + formContainer.setOpaque(false); + formContainer.setBorder(BorderFactory.createEmptyBorder(20, 50, 20, 50)); + + JPanel formPanel = new JPanel(new GridBagLayout()) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 卡片阴影 + g2.setColor(new Color(0, 0, 0, 20)); + g2.fillRoundRect(5, 5, getWidth()-5, getHeight()-5, 20, 20); + + // 卡片背景 + g2.setColor(Color.WHITE); + g2.fillRoundRect(0, 0, getWidth()-5, getHeight()-5, 20, 20); + + g2.dispose(); + } + }; + formPanel.setBorder(BorderFactory.createEmptyBorder(30, 40, 30, 40)); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(15, 10, 15, 10); + + // 邮箱输入 + gbc.gridx = 0; gbc.gridy = 0; gbc.anchor = GridBagConstraints.EAST; + JLabel emailLabel = new JLabel("邮箱地址:"); + emailLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + emailLabel.setForeground(new Color(70, 70, 70)); + formPanel.add(emailLabel, gbc); + + gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST; + emailField = new JTextField(20); + emailField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + emailField.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(200, 200, 200), 1), + BorderFactory.createEmptyBorder(8, 12, 8, 12) + )); + formPanel.add(emailField, gbc); + + // 发送验证码按钮 + gbc.gridx = 2; + JButton sendCodeButton = createStyledButton("发送验证码", new Color(255, 140, 0)); + sendCodeButton.addActionListener(this::sendVerificationCode); + formPanel.add(sendCodeButton, gbc); + + // 验证码输入 + gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.anchor = GridBagConstraints.EAST; + JLabel codeLabel = new JLabel("验证码:"); + codeLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + codeLabel.setForeground(new Color(70, 70, 70)); + formPanel.add(codeLabel, gbc); + + gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST; + verificationCodeField = new JTextField(20); + verificationCodeField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + verificationCodeField.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(200, 200, 200), 1), + BorderFactory.createEmptyBorder(8, 12, 8, 12) + )); + formPanel.add(verificationCodeField, gbc); + + formContainer.add(formPanel); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 30)); + buttonPanel.setOpaque(false); + + JButton registerButton = createStyledButton("注册", new Color(60, 179, 113)); + JButton backButton = createStyledButton("返回", new Color(128, 128, 128)); + + registerButton.addActionListener(this::handleRegister); + backButton.addActionListener(e -> cardLayout.show(mainPanel, "WELCOME")); + + buttonPanel.add(registerButton); + buttonPanel.add(backButton); + + panel.add(titlePanel, BorderLayout.NORTH); + panel.add(formContainer, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.SOUTH); + + mainPanel.add(panel, "REGISTER"); + } + + /** + * 创建登录界面 + */ + private void createLoginPanel() { + JPanel panel = new JPanel(new BorderLayout()) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 创建渐变背景 + GradientPaint gradient = new GradientPaint(0, 0, new Color(250, 250, 250), + 0, getHeight(), new Color(240, 248, 255)); + g2.setPaint(gradient); + g2.fillRect(0, 0, getWidth(), getHeight()); + + g2.dispose(); + } + }; + + // 标题面板 + JPanel titlePanel = new JPanel(); + titlePanel.setOpaque(false); + titlePanel.setBorder(BorderFactory.createEmptyBorder(60, 0, 40, 0)); + + JLabel titleLabel = new JLabel("用户登录", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 28)); + titleLabel.setForeground(new Color(25, 25, 112)); + + JLabel subtitleLabel = new JLabel("请输入您的账户信息", JLabel.CENTER); + subtitleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + subtitleLabel.setForeground(new Color(100, 100, 100)); + subtitleLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); + + titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.Y_AXIS)); + titlePanel.add(titleLabel); + titlePanel.add(subtitleLabel); + + // 表单面板 - 添加卡片样式 + JPanel formContainer = new JPanel(); + formContainer.setOpaque(false); + formContainer.setBorder(BorderFactory.createEmptyBorder(20, 80, 20, 80)); + + JPanel formPanel = new JPanel(new GridBagLayout()) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 卡片阴影 + g2.setColor(new Color(0, 0, 0, 20)); + g2.fillRoundRect(5, 5, getWidth()-5, getHeight()-5, 20, 20); + + // 卡片背景 + g2.setColor(Color.WHITE); + g2.fillRoundRect(0, 0, getWidth()-5, getHeight()-5, 20, 20); + + g2.dispose(); + } + }; + formPanel.setBorder(BorderFactory.createEmptyBorder(40, 50, 40, 50)); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(20, 10, 20, 10); + + // 用户名(邮箱)输入 + gbc.gridx = 0; gbc.gridy = 0; gbc.anchor = GridBagConstraints.EAST; + JLabel emailLabel = new JLabel("用户名(邮箱):"); + emailLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + emailLabel.setForeground(new Color(70, 70, 70)); + formPanel.add(emailLabel, gbc); + + gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST; + JTextField loginEmailField = new JTextField(25); + loginEmailField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + loginEmailField.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(200, 200, 200), 1), + BorderFactory.createEmptyBorder(10, 15, 10, 15) + )); + formPanel.add(loginEmailField, gbc); + + // 密码输入 + gbc.gridx = 0; gbc.gridy = 1; gbc.anchor = GridBagConstraints.EAST; + JLabel passwordLabel = new JLabel("密码:"); + passwordLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + passwordLabel.setForeground(new Color(70, 70, 70)); + formPanel.add(passwordLabel, gbc); + + gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST; + JPasswordField loginPasswordField = new JPasswordField(25); + loginPasswordField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + loginPasswordField.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(200, 200, 200), 1), + BorderFactory.createEmptyBorder(10, 15, 10, 15) + )); + formPanel.add(loginPasswordField, gbc); + + formContainer.add(formPanel); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 40)); + buttonPanel.setOpaque(false); + + JButton loginButton = createStyledButton("登录", new Color(70, 130, 180)); + JButton backButton = createStyledButton("返回", new Color(128, 128, 128)); + + loginButton.addActionListener(e -> handleLogin(loginEmailField.getText(), + new String(loginPasswordField.getPassword()))); + backButton.addActionListener(e -> cardLayout.show(mainPanel, "WELCOME")); + + buttonPanel.add(loginButton); + buttonPanel.add(backButton); + + panel.add(titlePanel, BorderLayout.NORTH); + panel.add(formContainer, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.SOUTH); + + mainPanel.add(panel, "LOGIN"); + } + + /** + * 创建密码设置界面 + */ + private void createPasswordSetupPanel() { + JPanel panel = new JPanel(new BorderLayout()) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 创建渐变背景 + GradientPaint gradient = new GradientPaint(0, 0, new Color(250, 250, 250), + 0, getHeight(), new Color(240, 248, 255)); + g2.setPaint(gradient); + g2.fillRect(0, 0, getWidth(), getHeight()); + + g2.dispose(); + } + }; + + // 标题面板 + JPanel titlePanel = new JPanel(); + titlePanel.setOpaque(false); + titlePanel.setBorder(BorderFactory.createEmptyBorder(50, 0, 30, 0)); + + JLabel titleLabel = new JLabel("设置密码", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 28)); + titleLabel.setForeground(new Color(25, 25, 112)); + + JLabel subtitleLabel = new JLabel("请为您的账户设置安全密码", JLabel.CENTER); + subtitleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + subtitleLabel.setForeground(new Color(100, 100, 100)); + subtitleLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); + + // 提示信息 + JLabel hintLabel = new JLabel("
密码要求:6-10位,必须包含大小写字母和数字
", JLabel.CENTER); + hintLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12)); + hintLabel.setForeground(new Color(150, 150, 150)); + hintLabel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); + + titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.Y_AXIS)); + titlePanel.add(titleLabel); + titlePanel.add(subtitleLabel); + titlePanel.add(hintLabel); + + // 表单面板 - 添加卡片样式 + JPanel formContainer = new JPanel(); + formContainer.setOpaque(false); + formContainer.setBorder(BorderFactory.createEmptyBorder(30, 80, 30, 80)); + + JPanel formPanel = new JPanel(new GridBagLayout()) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 卡片阴影 + g2.setColor(new Color(0, 0, 0, 20)); + g2.fillRoundRect(5, 5, getWidth()-5, getHeight()-5, 20, 20); + + // 卡片背景 + g2.setColor(Color.WHITE); + g2.fillRoundRect(0, 0, getWidth()-5, getHeight()-5, 20, 20); + + g2.dispose(); + } + }; + formPanel.setBorder(BorderFactory.createEmptyBorder(40, 50, 40, 50)); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(20, 10, 20, 10); + + // 密码输入 + gbc.gridx = 0; gbc.gridy = 0; gbc.anchor = GridBagConstraints.EAST; + JLabel passwordLabel = new JLabel("输入密码:"); + passwordLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + passwordLabel.setForeground(new Color(70, 70, 70)); + formPanel.add(passwordLabel, gbc); + + gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST; + passwordField = new JPasswordField(25); + passwordField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + passwordField.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(200, 200, 200), 1), + BorderFactory.createEmptyBorder(10, 15, 10, 15) + )); + formPanel.add(passwordField, gbc); + + // 确认密码 + gbc.gridx = 0; gbc.gridy = 1; gbc.anchor = GridBagConstraints.EAST; + JLabel confirmLabel = new JLabel("确认密码:"); + confirmLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + confirmLabel.setForeground(new Color(70, 70, 70)); + formPanel.add(confirmLabel, gbc); + + gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST; + confirmPasswordField = new JPasswordField(25); + confirmPasswordField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + confirmPasswordField.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(200, 200, 200), 1), + BorderFactory.createEmptyBorder(10, 15, 10, 15) + )); + formPanel.add(confirmPasswordField, gbc); + + formContainer.add(formPanel); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 40)); + buttonPanel.setOpaque(false); + + JButton setPasswordButton = createStyledButton("设置密码", new Color(60, 179, 113)); + setPasswordButton.addActionListener(this::handlePasswordSetup); + buttonPanel.add(setPasswordButton); + + panel.add(titlePanel, BorderLayout.NORTH); + panel.add(formContainer, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.SOUTH); + + mainPanel.add(panel, "PASSWORD_SETUP"); + } + + /** + * 创建难度选择界面 + */ + private void createDifficultySelectionPanel() { + JPanel panel = new JPanel(new BorderLayout()) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 创建渐变背景 + GradientPaint gradient = new GradientPaint(0, 0, new Color(240, 248, 255), + 0, getHeight(), new Color(230, 240, 250)); + g2.setPaint(gradient); + g2.fillRect(0, 0, getWidth(), getHeight()); + + g2.dispose(); + } + }; + + // 标题面板 + JPanel titlePanel = new JPanel(); + titlePanel.setOpaque(false); + titlePanel.setBorder(BorderFactory.createEmptyBorder(60, 0, 40, 0)); + + JLabel titleLabel = new JLabel("选择学习难度", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 32)); + titleLabel.setForeground(new Color(25, 25, 112)); + + JLabel subtitleLabel = new JLabel("选择适合您的数学学习级别", JLabel.CENTER); + subtitleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); + subtitleLabel.setForeground(new Color(70, 130, 180)); + subtitleLabel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); + + titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.Y_AXIS)); + titlePanel.add(titleLabel); + titlePanel.add(subtitleLabel); + + // 按钮面板 + JPanel buttonContainer = new JPanel(); + buttonContainer.setOpaque(false); + buttonContainer.setBorder(BorderFactory.createEmptyBorder(20, 80, 40, 80)); + + JPanel buttonPanel = new JPanel(new GridLayout(3, 1, 0, 25)); + buttonPanel.setOpaque(false); + + JButton elementaryButton = createLargeDifficultyButton("小学数学", new Color(255, 182, 193)); + JButton middleButton = createLargeDifficultyButton("初中数学", new Color(173, 216, 230)); + JButton highButton = createLargeDifficultyButton("高中数学", new Color(221, 160, 221)); + + elementaryButton.addActionListener(e -> selectDifficulty("小学")); + middleButton.addActionListener(e -> selectDifficulty("初中")); + highButton.addActionListener(e -> selectDifficulty("高中")); + + buttonPanel.add(elementaryButton); + buttonPanel.add(middleButton); + buttonPanel.add(highButton); + + buttonContainer.add(buttonPanel); + + // 底部面板 + JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 30, 30)); + bottomPanel.setOpaque(false); + + JButton changePasswordButton = createStyledButton("修改密码", new Color(255, 140, 0)); + JButton logoutButton = createStyledButton("退出登录", new Color(220, 20, 60)); + + changePasswordButton.addActionListener(e -> cardLayout.show(mainPanel, "PASSWORD_CHANGE")); + logoutButton.addActionListener(e -> { + currentUser = null; + cardLayout.show(mainPanel, "WELCOME"); + }); + + bottomPanel.add(changePasswordButton); + bottomPanel.add(logoutButton); + + panel.add(titlePanel, BorderLayout.NORTH); + panel.add(buttonContainer, BorderLayout.CENTER); + panel.add(bottomPanel, BorderLayout.SOUTH); + + mainPanel.add(panel, "DIFFICULTY_SELECTION"); + } + + /** + * 创建答题界面 + */ + private void createQuestionPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBackground(Color.WHITE); + + // 顶部信息面板 + JPanel topPanel = new JPanel(new BorderLayout()); + topPanel.setBackground(Color.WHITE); + topPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + JLabel progressLabel = new JLabel("", JLabel.LEFT); + progressLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + + JLabel difficultyLabel = new JLabel("", JLabel.RIGHT); + difficultyLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + + topPanel.add(progressLabel, BorderLayout.WEST); + topPanel.add(difficultyLabel, BorderLayout.EAST); + + // 题目面板 + JPanel questionPanel = new JPanel(new BorderLayout()); + questionPanel.setBackground(Color.WHITE); + questionPanel.setBorder(BorderFactory.createEmptyBorder(20, 40, 20, 40)); + + questionLabel = new JLabel("", JLabel.CENTER); + questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 18)); + questionLabel.setBorder(BorderFactory.createEmptyBorder(20, 0, 30, 0)); + + // 选项面板 + JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 0, 15)); + optionsPanel.setBackground(Color.WHITE); + + answerGroup = new ButtonGroup(); + for (int i = 0; i < 4; i++) { + answerButtons[i] = new JRadioButton(); + answerButtons[i].setFont(new Font("微软雅黑", Font.PLAIN, 16)); + answerButtons[i].setBackground(Color.WHITE); + answerGroup.add(answerButtons[i]); + optionsPanel.add(answerButtons[i]); + } + + questionPanel.add(questionLabel, BorderLayout.NORTH); + questionPanel.add(optionsPanel, BorderLayout.CENTER); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout()); + buttonPanel.setBackground(Color.WHITE); + + JButton submitButton = createStyledButton("提交答案", new Color(70, 130, 180)); + submitButton.addActionListener(this::handleAnswerSubmit); + buttonPanel.add(submitButton); + + panel.add(topPanel, BorderLayout.NORTH); + panel.add(questionPanel, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.SOUTH); + + mainPanel.add(panel, "QUESTION"); + } + + /** + * 创建评分界面 + */ + private void createScorePanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBackground(new Color(240, 248, 255)); + + // 标题 + JLabel titleLabel = new JLabel("答题结果", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + titleLabel.setBorder(BorderFactory.createEmptyBorder(50, 0, 30, 0)); + + // 分数显示 + scoreLabel = new JLabel("", JLabel.CENTER); + scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 36)); + scoreLabel.setForeground(new Color(70, 130, 180)); + scoreLabel.setBorder(BorderFactory.createEmptyBorder(30, 0, 50, 0)); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout()); + buttonPanel.setBackground(new Color(240, 248, 255)); + + JButton continueButton = createStyledButton("继续做题", new Color(60, 179, 113)); + JButton exitButton = createStyledButton("退出", new Color(220, 20, 60)); + + continueButton.addActionListener(e -> cardLayout.show(mainPanel, "DIFFICULTY_SELECTION")); + exitButton.addActionListener(e -> { + currentUser = null; + cardLayout.show(mainPanel, "WELCOME"); + }); + + buttonPanel.add(continueButton); + buttonPanel.add(exitButton); + + panel.add(titleLabel, BorderLayout.NORTH); + panel.add(scoreLabel, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.SOUTH); + + mainPanel.add(panel, "SCORE"); + } + + /** + * 创建密码修改界面 + */ + private void createPasswordChangePanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBackground(Color.WHITE); + + // 标题 + JLabel titleLabel = new JLabel("修改密码", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + titleLabel.setBorder(BorderFactory.createEmptyBorder(30, 0, 30, 0)); + + // 表单面板 + JPanel formPanel = new JPanel(new GridBagLayout()); + formPanel.setBackground(Color.WHITE); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(10, 10, 10, 10); + + // 原密码 + gbc.gridx = 0; gbc.gridy = 0; + formPanel.add(new JLabel("原密码:"), gbc); + gbc.gridx = 1; + oldPasswordField = new JPasswordField(20); + formPanel.add(oldPasswordField, gbc); + + // 新密码 + gbc.gridx = 0; gbc.gridy = 1; + formPanel.add(new JLabel("新密码:"), gbc); + gbc.gridx = 1; + JPasswordField newPasswordField = new JPasswordField(20); + formPanel.add(newPasswordField, gbc); + + // 确认新密码 + gbc.gridx = 0; gbc.gridy = 2; + formPanel.add(new JLabel("确认新密码:"), gbc); + gbc.gridx = 1; + JPasswordField confirmNewPasswordField = new JPasswordField(20); + formPanel.add(confirmNewPasswordField, gbc); + + // 按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout()); + buttonPanel.setBackground(Color.WHITE); + + JButton changeButton = createStyledButton("修改密码", new Color(60, 179, 113)); + JButton backButton = createStyledButton("返回", new Color(128, 128, 128)); + + changeButton.addActionListener(e -> handlePasswordChange( + new String(oldPasswordField.getPassword()), + new String(newPasswordField.getPassword()), + new String(confirmNewPasswordField.getPassword()) + )); + backButton.addActionListener(e -> cardLayout.show(mainPanel, "DIFFICULTY_SELECTION")); + + buttonPanel.add(changeButton); + buttonPanel.add(backButton); + + panel.add(titleLabel, BorderLayout.NORTH); + panel.add(formPanel, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.SOUTH); + + mainPanel.add(panel, "PASSWORD_CHANGE"); + } + + /** + * 创建样式化按钮 + */ + private JButton createStyledButton(String text, Color backgroundColor) { + return MathLearningAppMethods.createStyledButton(text, backgroundColor); + } + + /** + * 创建大型难度选择按钮 + */ + private JButton createLargeDifficultyButton(String text, Color backgroundColor) { + return MathLearningAppMethods.createLargeDifficultyButton(text, backgroundColor); + } + + /** + * 发送验证码 + */ + private void sendVerificationCode(ActionEvent e) { + MathLearningAppMethods.sendVerificationCode(e, emailField, registeredUsers); + } + + /** + * 处理用户注册 + */ + private void handleRegister(ActionEvent e) { + if (MathLearningAppMethods.handleRegister(emailField, verificationCodeField, + registeredUsers, cardLayout, mainPanel)) { + // 清空输入框 + emailField.setText(""); + verificationCodeField.setText(""); + } + } + + /** + * 处理密码设置 + */ + private void handlePasswordSetup(ActionEvent e) { + if (MathLearningAppMethods.handlePasswordSetup(passwordField, confirmPasswordField, + registeredUsers, cardLayout, mainPanel)) { + // 设置当前用户 + for (RegisteredUser user : registeredUsers.values()) { + if (user.isVerified() && user.getPassword() != null) { + currentUser = user; + break; + } + } + // 清空密码框 + passwordField.setText(""); + confirmPasswordField.setText(""); + } + } + + /** + * 处理用户登录 + */ + private void handleLogin(String email, String password) { + RegisteredUser user = MathLearningAppMethods.handleLogin(email, password, + registeredUsers, cardLayout, mainPanel); + if (user != null) { + currentUser = user; + } + } + + /** + * 处理密码修改 + */ + private void handlePasswordChange(String oldPassword, String newPassword, String confirmNewPassword) { + if (MathLearningAppMethods.handlePasswordChange(oldPassword, newPassword, confirmNewPassword, currentUser)) { + // 清空密码框 + oldPasswordField.setText(""); + cardLayout.show(mainPanel, "DIFFICULTY_SELECTION"); + } + } + + /** + * 选择难度并输入题目数量 + */ + private void selectDifficulty(String difficulty) { + currentDifficulty = difficulty; + + String input = JOptionPane.showInputDialog(this, + "请输入需要生成的题目数量(建议10-30题):", + "题目数量", + JOptionPane.QUESTION_MESSAGE); + + if (input != null && !input.trim().isEmpty()) { + try { + int count = Integer.parseInt(input.trim()); + if (count > 0 && count <= 50) { + generateQuestions(difficulty, count); + } else { + JOptionPane.showMessageDialog(this, "请输入1-50之间的数字!", "错误", JOptionPane.ERROR_MESSAGE); + } + } catch (NumberFormatException e) { + JOptionPane.showMessageDialog(this, "请输入有效的数字!", "错误", JOptionPane.ERROR_MESSAGE); + } + } + } + + /** + * 生成题目并开始答题 + */ + private void generateQuestions(String difficulty, int count) { + currentQuestions = MathLearningAppMethods.generateQuestions(difficulty, count, questionGenerator); + currentQuestionIndex = 0; + correctAnswers = 0; + + if (!currentQuestions.isEmpty()) { + showCurrentQuestion(); + cardLayout.show(mainPanel, "QUESTION"); + } else { + JOptionPane.showMessageDialog(this, "生成题目失败!", "错误", JOptionPane.ERROR_MESSAGE); + } + } + + /** + * 显示当前题目 + */ + private void showCurrentQuestion() { + if (currentQuestionIndex < currentQuestions.size()) { + MultipleChoiceQuestion question = currentQuestions.get(currentQuestionIndex); + + // 更新进度信息 + JPanel topPanel = (JPanel) ((JPanel) mainPanel.getComponent(5)).getComponent(0); + JLabel progressLabel = (JLabel) topPanel.getComponent(0); + JLabel difficultyLabel = (JLabel) topPanel.getComponent(1); + + progressLabel.setText("第 " + (currentQuestionIndex + 1) + " 题 / 共 " + currentQuestions.size() + " 题"); + difficultyLabel.setText(currentDifficulty + "难度"); + + // 更新题目内容 + questionLabel.setText("
" + question.getQuestion() + "
"); + + // 更新选项 - 关键修改:清除之前的选择 + answerGroup.clearSelection(); // 清除单选按钮组的选择 + + String[] options = question.getOptions(); + for (int i = 0; i < 4; i++) { + answerButtons[i].setText((char)('A' + i) + ". " + options[i]); + answerButtons[i].setSelected(false); // 确保每个按钮都是未选中状态 + } + + // 重置按钮状态 + for (JRadioButton button : answerButtons) { + button.setSelected(false); + } + } + } + + /** + * 处理答案提交 + */ + private void handleAnswerSubmit(ActionEvent e) { + // 检查是否选择了答案 + int selectedAnswer = -1; + for (int i = 0; i < 4; i++) { + if (answerButtons[i].isSelected()) { + selectedAnswer = i; + break; + } + } + + if (selectedAnswer == -1) { + JOptionPane.showMessageDialog(this, "请选择一个答案!", "提示", JOptionPane.WARNING_MESSAGE); + return; + } + + // 检查答案是否正确 + MultipleChoiceQuestion currentQuestion = currentQuestions.get(currentQuestionIndex); + if (currentQuestion.isCorrect(selectedAnswer)) { + correctAnswers++; + } + + // 移动到下一题或显示结果 + currentQuestionIndex++; + if (currentQuestionIndex < currentQuestions.size()) { + showCurrentQuestion(); + } else { + showScore(); + } + } + + /** + * 显示分数 + */ + private void showScore() { + int totalQuestions = currentQuestions.size(); + double percentage = (double) correctAnswers / totalQuestions * 100; + + String scoreText = String.format("
答对 %d 题,共 %d 题
正确率:%.1f%%
", + correctAnswers, totalQuestions, percentage); + scoreLabel.setText(scoreText); + + cardLayout.show(mainPanel, "SCORE"); + } + + /** + * 加载用户数据 + */ + private void loadUserData() { + MathLearningAppMethods.loadUserData(registeredUsers); + } + + /** + * 保存用户数据 + */ + private void saveUserData() { + MathLearningAppMethods.saveUserData(registeredUsers); + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + // 直接启动程序,使用默认外观 + new MathLearningApp().setVisible(true); + }); + } +} diff --git a/src/MathLearningAppMethods.java b/src/MathLearningAppMethods.java new file mode 100644 index 0000000..53ecb5e --- /dev/null +++ b/src/MathLearningAppMethods.java @@ -0,0 +1,464 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.*; +import java.util.List; +import java.util.regex.Pattern; +import java.io.*; +import java.nio.file.*; + +/** + * MathLearningApp的方法扩展类 + * 包含所有事件处理和工具方法 + */ +public class MathLearningAppMethods { + + /** + * 创建样式化按钮 + */ + public static JButton createStyledButton(String text, Color backgroundColor) { + JButton button = new JButton(text) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 创建渐变效果 + GradientPaint gradient = new GradientPaint(0, 0, backgroundColor.brighter(), + 0, getHeight(), backgroundColor.darker()); + g2.setPaint(gradient); + g2.fillRoundRect(0, 0, getWidth(), getHeight(), 15, 15); + + // 添加内阴影效果 + g2.setColor(new Color(0, 0, 0, 30)); + g2.drawRoundRect(1, 1, getWidth()-3, getHeight()-3, 13, 13); + + g2.dispose(); + super.paintComponent(g); + } + }; + + button.setFont(new Font("微软雅黑", Font.BOLD, 14)); + button.setForeground(Color.WHITE); + button.setFocusPainted(false); + button.setBorderPainted(false); + button.setContentAreaFilled(false); + button.setPreferredSize(new Dimension(130, 40)); + button.setCursor(new Cursor(Cursor.HAND_CURSOR)); + + // 添加鼠标悬停效果 + button.addMouseListener(new java.awt.event.MouseAdapter() { + @Override + public void mouseEntered(java.awt.event.MouseEvent evt) { + button.setBackground(backgroundColor.brighter()); + button.repaint(); + } + + @Override + public void mouseExited(java.awt.event.MouseEvent evt) { + button.setBackground(backgroundColor); + button.repaint(); + } + }); + + return button; + } + + /** + * 创建大型难度选择按钮 + */ + public static JButton createLargeDifficultyButton(String text, Color backgroundColor) { + JButton button = new JButton(text) { + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 创建渐变效果 + GradientPaint gradient = new GradientPaint(0, 0, backgroundColor.brighter(), + 0, getHeight(), backgroundColor); + g2.setPaint(gradient); + g2.fillRoundRect(0, 0, getWidth(), getHeight(), 20, 20); + + // 添加外阴影效果 + g2.setColor(new Color(0, 0, 0, 20)); + g2.fillRoundRect(3, 3, getWidth(), getHeight(), 20, 20); + + // 重新绘制按钮 + g2.setPaint(gradient); + g2.fillRoundRect(0, 0, getWidth()-3, getHeight()-3, 20, 20); + + // 添加边框 + g2.setColor(backgroundColor.darker()); + g2.setStroke(new BasicStroke(2)); + g2.drawRoundRect(1, 1, getWidth()-5, getHeight()-5, 18, 18); + + g2.dispose(); + super.paintComponent(g); + } + }; + + button.setFont(new Font("微软雅黑", Font.BOLD, 22)); + button.setForeground(new Color(40, 40, 40)); + button.setFocusPainted(false); + button.setBorderPainted(false); + button.setContentAreaFilled(false); + button.setPreferredSize(new Dimension(320, 70)); + button.setCursor(new Cursor(Cursor.HAND_CURSOR)); + + // 添加鼠标悬停效果 + button.addMouseListener(new java.awt.event.MouseAdapter() { + @Override + public void mouseEntered(java.awt.event.MouseEvent evt) { + button.setForeground(new Color(20, 20, 20)); + button.repaint(); + } + + @Override + public void mouseExited(java.awt.event.MouseEvent evt) { + button.setForeground(new Color(40, 40, 40)); + button.repaint(); + } + }); + + return button; + } + + /** + * 发送验证码 + */ + public static void sendVerificationCode(ActionEvent e, JTextField emailField, Map registeredUsers) { + String email = emailField.getText().trim(); + + if (!isValidEmail(email)) { + JOptionPane.showMessageDialog(null, "请输入有效的邮箱地址!", "错误", JOptionPane.ERROR_MESSAGE); + return; + } + + // 检查邮箱是否已完成注册(验证码验证成功且设置了密码) + RegisteredUser existingUser = registeredUsers.get(email); + if (existingUser != null && existingUser.isVerified() && existingUser.getPassword() != null) { + JOptionPane.showMessageDialog(null, "该邮箱已注册!", "错误", JOptionPane.ERROR_MESSAGE); + return; + } + + // 生成6位验证码 + String verificationCode = generateVerificationCode(); + + // 创建或更新用户对象(仅用于临时存储验证码) + RegisteredUser user = new RegisteredUser(email, verificationCode); + registeredUsers.put(email, user); + + // 显示发送中的提示 + JOptionPane.showMessageDialog(null, + "正在通过QQ邮箱发送验证码到您的邮箱,请稍候...", + "发送中", + JOptionPane.INFORMATION_MESSAGE); + + // 在后台线程发送邮件,避免阻塞UI + new Thread(() -> { + boolean success = QQEmailService.sendVerificationCode(email, verificationCode); + + // 在EDT线程中显示结果 + javax.swing.SwingUtilities.invokeLater(() -> { + if (success) { + JOptionPane.showMessageDialog(null, + "✅ 验证码已通过QQ邮箱发送!\n" + + "📧 请查收邮件并输入6位验证码。\n" + + "📱 如果没收到,请检查垃圾邮件文件夹。", + "发送成功", + JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showMessageDialog(null, + "❌ 验证码发送失败!\n" + + "请检查:\n" + + "• 接收邮箱地址是否正确\n" + + "• QQ邮箱配置是否正确\n" + + "• 网络连接是否正常", + "发送失败", + JOptionPane.ERROR_MESSAGE); + // 发送失败时移除用户 + registeredUsers.remove(email); + } + }); + }).start(); + } + + /** + * 处理用户注册 + */ + public static boolean handleRegister(JTextField emailField, JTextField verificationCodeField, + Map registeredUsers, CardLayout cardLayout, JPanel mainPanel) { + String email = emailField.getText().trim(); + String code = verificationCodeField.getText().trim(); + + if (email.isEmpty() || code.isEmpty()) { + JOptionPane.showMessageDialog(null, "请填写完整信息!", "错误", JOptionPane.ERROR_MESSAGE); + return false; + } + + RegisteredUser user = registeredUsers.get(email); + if (user == null) { + JOptionPane.showMessageDialog(null, "请先发送验证码!", "错误", JOptionPane.ERROR_MESSAGE); + return false; + } + + if (!user.verifyCode(code)) { + JOptionPane.showMessageDialog(null, "验证码错误!", "错误", JOptionPane.ERROR_MESSAGE); + return false; + } + + user.setVerified(true); + JOptionPane.showMessageDialog(null, "注册成功!请设置密码。", "成功", JOptionPane.INFORMATION_MESSAGE); + cardLayout.show(mainPanel, "PASSWORD_SETUP"); + return true; + } + + /** + * 处理密码设置 + */ + public static boolean handlePasswordSetup(JPasswordField passwordField, JPasswordField confirmPasswordField, + Map registeredUsers, CardLayout cardLayout, JPanel mainPanel) { + String password = new String(passwordField.getPassword()); + String confirmPassword = new String(confirmPasswordField.getPassword()); + + if (!password.equals(confirmPassword)) { + JOptionPane.showMessageDialog(null, "两次输入的密码不一致!", "错误", JOptionPane.ERROR_MESSAGE); + return false; + } + + if (!isValidPassword(password)) { + JOptionPane.showMessageDialog(null, + "密码不符合要求!\n要求:6-10位,必须包含大小写字母和数字", + "错误", JOptionPane.ERROR_MESSAGE); + return false; + } + + // 找到当前注册的用户 + RegisteredUser currentUser = null; + for (RegisteredUser user : registeredUsers.values()) { + if (user.isVerified() && user.getPassword() == null) { + currentUser = user; + break; + } + } + + if (currentUser != null) { + currentUser.setPassword(password); + saveUserData(registeredUsers); + JOptionPane.showMessageDialog(null, "密码设置成功!", "成功", JOptionPane.INFORMATION_MESSAGE); + cardLayout.show(mainPanel, "DIFFICULTY_SELECTION"); + return true; + } + + return false; + } + + /** + * 处理用户登录 + */ + public static RegisteredUser handleLogin(String email, String password, + Map registeredUsers, + CardLayout cardLayout, JPanel mainPanel) { + if (email.isEmpty() || password.isEmpty()) { + JOptionPane.showMessageDialog(null, "请填写完整信息!", "错误", JOptionPane.ERROR_MESSAGE); + return null; + } + + RegisteredUser user = registeredUsers.get(email); + if (user == null || !user.isVerified() || !user.verifyPassword(password)) { + JOptionPane.showMessageDialog(null, "用户名或密码错误!", "错误", JOptionPane.ERROR_MESSAGE); + return null; + } + + JOptionPane.showMessageDialog(null, "登录成功!", "成功", JOptionPane.INFORMATION_MESSAGE); + cardLayout.show(mainPanel, "DIFFICULTY_SELECTION"); + return user; + } + + /** + * 处理密码修改 + */ + public static boolean handlePasswordChange(String oldPassword, String newPassword, String confirmNewPassword, + RegisteredUser currentUser) { + if (!currentUser.verifyPassword(oldPassword)) { + JOptionPane.showMessageDialog(null, "原密码错误!", "错误", JOptionPane.ERROR_MESSAGE); + return false; + } + + if (!newPassword.equals(confirmNewPassword)) { + JOptionPane.showMessageDialog(null, "两次输入的新密码不一致!", "错误", JOptionPane.ERROR_MESSAGE); + return false; + } + + if (!isValidPassword(newPassword)) { + JOptionPane.showMessageDialog(null, + "新密码不符合要求!\n要求:6-10位,必须包含大小写字母和数字", + "错误", JOptionPane.ERROR_MESSAGE); + return false; + } + + currentUser.setPassword(newPassword); + JOptionPane.showMessageDialog(null, "密码修改成功!", "成功", JOptionPane.INFORMATION_MESSAGE); + return true; + } + + /** + * 生成选择题 + */ + public static List generateQuestions(String difficulty, int count, MathQuestionGenerator generator) { + List questions = new ArrayList<>(); + Random random = new Random(); + + int attempts = 0; + int maxAttempts = count * 5; // 防止无限循环 + + while (questions.size() < count && attempts < maxAttempts) { + // 生成数学表达式 + MathQuestion mathQuestion = generateSingleQuestion(difficulty, generator); + String expression = mathQuestion.getExpression(); + + // 验证表达式语法 + if (!ExpressionEvaluator.isValidMathExpression(expression)) { + attempts++; + continue; + } + + // 计算正确答案 + double correctAnswer = evaluateExpression(expression); + + // 检查答案是否有效 + if (Double.isNaN(correctAnswer) || Double.isInfinite(correctAnswer)) { + attempts++; + continue; + } + + // 生成选项 + String[] options = new String[4]; + int correctIndex = random.nextInt(4); + + // 设置正确答案 + options[correctIndex] = formatAnswer(correctAnswer); + + // 生成3个错误答案 + String[] wrongAnswers = ExpressionEvaluator.generateWrongAnswers(correctAnswer, 3); + int wrongIndex = 0; + for (int j = 0; j < 4; j++) { + if (j != correctIndex) { + options[j] = wrongAnswers[wrongIndex++]; + } + } + + MultipleChoiceQuestion question = new MultipleChoiceQuestion( + "计算:" + expression + " = ?", options, correctIndex); + questions.add(question); + attempts = 0; // 重置尝试计数 + } + + return questions; + } + + /** + * 生成单个数学题目 + */ + private static MathQuestion generateSingleQuestion(String difficulty, MathQuestionGenerator generator) { + List questions = generator.generateQuestions(difficulty, 1); + return questions.get(0); + } + + /** + * 计算数学表达式的值 + */ + private static double evaluateExpression(String expression) { + return ExpressionEvaluator.evaluate(expression); + } + + /** + * 格式化答案 + */ + private static String formatAnswer(double answer) { + return ExpressionEvaluator.formatAnswer(answer); + } + + /** + * 验证邮箱格式 + */ + public static boolean isValidEmail(String email) { + String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$"; + Pattern pattern = Pattern.compile(emailRegex); + return pattern.matcher(email).matches(); + } + + /** + * 验证密码格式 + */ + public static boolean isValidPassword(String password) { + if (password.length() < 6 || password.length() > 10) { + return false; + } + + boolean hasUpper = false, hasLower = false, hasDigit = false; + + for (char c : password.toCharArray()) { + if (Character.isUpperCase(c)) hasUpper = true; + else if (Character.isLowerCase(c)) hasLower = true; + else if (Character.isDigit(c)) hasDigit = true; + } + + return hasUpper && hasLower && hasDigit; + } + + /** + * 生成验证码 + */ + public static String generateVerificationCode() { + Random random = new Random(); + StringBuilder code = new StringBuilder(); + for (int i = 0; i < 6; i++) { + code.append(random.nextInt(10)); + } + return code.toString(); + } + + /** + * 保存用户数据 + */ + public static void saveUserData(Map users) { + try { + Path dataFile = Paths.get("user_data.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(dataFile)) { + for (RegisteredUser user : users.values()) { + if (user.isVerified() && user.getPassword() != null) { + writer.write(user.getEmail() + ":" + user.getPassword()); + writer.newLine(); + } + } + } + } catch (IOException e) { + System.err.println("保存用户数据失败: " + e.getMessage()); + } + } + + /** + * 加载用户数据 + */ + public static void loadUserData(Map users) { + try { + Path dataFile = Paths.get("user_data.txt"); + if (Files.exists(dataFile)) { + List lines = Files.readAllLines(dataFile); + for (String line : lines) { + String[] parts = line.split(":"); + if (parts.length == 2) { + RegisteredUser user = new RegisteredUser(parts[0], ""); + user.setVerified(true); + user.setPassword(parts[1]); + users.put(parts[0], user); + } + } + } + } catch (IOException e) { + System.err.println("加载用户数据失败: " + e.getMessage()); + } + } +} diff --git a/src/QQEmailService.java b/src/QQEmailService.java new file mode 100644 index 0000000..0479a5a --- /dev/null +++ b/src/QQEmailService.java @@ -0,0 +1,218 @@ +import java.io.*; +import java.net.*; +import java.util.Properties; +import java.nio.file.*; + +/** + * QQ邮箱SMTP服务 + * 专门用于通过QQ邮箱发送验证码邮件 + */ +public class QQEmailService { + + private static final String SMTP_HOST = "smtp.qq.com"; + private static final String SMTP_PORT = "587"; + private static final String CONFIG_FILE = "qq_email_config.properties"; + + // 默认配置 - 直接写入程序中 + private static String senderEmail = "1626814667@qq.com"; + private static String senderPassword = "cjgpophigkrodhib"; + private static String senderName = "数学学习软件"; + + static { + // 不再需要加载配置文件,使用默认配置 + System.out.println("使用默认QQ邮箱配置: " + senderEmail); + } + + /** + * 加载QQ邮箱配置 + */ + private static void loadConfig() { + try { + Path configPath = Paths.get(CONFIG_FILE); + if (Files.exists(configPath)) { + Properties props = new Properties(); + try (InputStream input = Files.newInputStream(configPath)) { + props.load(input); + senderEmail = props.getProperty("qq.email", ""); + senderPassword = props.getProperty("qq.password", ""); + senderName = props.getProperty("sender.name", "数学学习软件"); + } + } + } catch (IOException e) { + System.err.println("加载QQ邮箱配置失败: " + e.getMessage()); + } + } + + /** + * 保存QQ邮箱配置 + */ + public static void saveConfig(String email, String password) { + try { + Properties props = new Properties(); + props.setProperty("qq.email", email); + props.setProperty("qq.password", password); + props.setProperty("sender.name", senderName); + + try (OutputStream output = Files.newOutputStream(Paths.get(CONFIG_FILE))) { + props.store(output, "QQ邮箱SMTP配置 - QQ Email SMTP Configuration"); + } + + // 更新内存中的配置 + senderEmail = email; + senderPassword = password; + + System.out.println("QQ邮箱配置已保存"); + + } catch (IOException e) { + System.err.println("保存QQ邮箱配置失败: " + e.getMessage()); + } + } + + /** + * 检查是否已配置QQ邮箱 + */ + public static boolean isConfigured() { + // 始终返回true,因为已经有默认配置 + return true; + } + + /** + * 获取当前配置的QQ邮箱 + */ + public static String getSenderEmail() { + return senderEmail; + } + + /** + * 发送验证码邮件 + */ + public static boolean sendVerificationCode(String toEmail, String verificationCode) { + if (!isConfigured()) { + System.err.println("QQ邮箱未配置!"); + return false; + } + + try { + System.out.println("正在发送邮件..."); + System.out.println("发送方: " + senderEmail); + System.out.println("接收方: " + toEmail); + System.out.println("验证码: " + verificationCode); + + // 使用真实的SMTP发送邮件 + String subject = "【数学学习软件】验证码"; + String content = RealEmailSender.createVerificationEmailContent(verificationCode); + + boolean success = RealEmailSender.sendEmail(senderEmail, senderPassword, toEmail, subject, content); + + if (success) { + System.out.println("邮件发送成功!"); + } else { + System.out.println("邮件发送失败!"); + } + + return success; + + } catch (Exception e) { + System.err.println("发送邮件失败: " + e.getMessage()); + e.printStackTrace(); + return false; + } + } + + /** + * 通过SMTP发送邮件(简化实现) + */ + private static boolean sendEmailViaSMTP(String toEmail, String verificationCode) { + try { + // 创建Socket连接到QQ SMTP服务器 + Socket socket = new Socket(SMTP_HOST, Integer.parseInt(SMTP_PORT)); + + BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + PrintWriter writer = new PrintWriter(socket.getOutputStream(), true); + + // 读取服务器欢迎信息 + String response = reader.readLine(); + System.out.println("服务器响应: " + response); + + // SMTP握手过程 + writer.println("EHLO localhost"); + response = reader.readLine(); + System.out.println("EHLO响应: " + response); + + // 启动TLS加密 + writer.println("STARTTLS"); + response = reader.readLine(); + System.out.println("STARTTLS响应: " + response); + + // 这里需要升级到SSL连接,但为了简化,我们使用HTTP API方式 + socket.close(); + + // 改用HTTP API方式发送(模拟) + return sendViaHTTPAPI(toEmail, verificationCode); + + } catch (Exception e) { + System.err.println("SMTP连接失败: " + e.getMessage()); + return sendViaHTTPAPI(toEmail, verificationCode); + } + } + + /** + * 通过HTTP API发送邮件(模拟实现) + */ + private static boolean sendViaHTTPAPI(String toEmail, String verificationCode) { + try { + System.out.println("=== QQ邮箱发送邮件 ==="); + System.out.println("发送方: " + senderEmail); + System.out.println("接收方: " + toEmail); + System.out.println("验证码: " + verificationCode); + System.out.println("邮件内容: " + createEmailContent(verificationCode)); + System.out.println("==================="); + + // 模拟发送延迟 + Thread.sleep(1000); + + // 在实际项目中,这里应该调用真实的SMTP库或HTTP API + // 例如使用JavaMail库或第三方邮件服务API + + return true; + + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 创建邮件内容 + */ + private static String createEmailContent(String verificationCode) { + return "【数学学习软件】验证码\n\n" + + "您的注册验证码是:" + verificationCode + "\n\n" + + "验证码有效期为10分钟,请及时使用。\n" + + "如果您没有注册我们的软件,请忽略此邮件。\n\n" + + "此邮件由系统自动发送,请勿回复。"; + } + + /** + * 测试QQ邮箱连接 + */ + public static boolean testConnection() { + if (!isConfigured()) { + return false; + } + + try { + // 测试连接到QQ SMTP服务器 + Socket socket = new Socket(); + socket.connect(new InetSocketAddress(SMTP_HOST, Integer.parseInt(SMTP_PORT)), 5000); + socket.close(); + + System.out.println("QQ SMTP服务器连接测试成功"); + return true; + + } catch (Exception e) { + System.err.println("QQ SMTP服务器连接测试失败: " + e.getMessage()); + return false; + } + } +} diff --git a/src/RealEmailSender.java b/src/RealEmailSender.java new file mode 100644 index 0000000..21c9444 --- /dev/null +++ b/src/RealEmailSender.java @@ -0,0 +1,176 @@ +import java.io.*; +import java.net.*; +import java.util.Base64; +import javax.net.ssl.*; + +/** + * 真实的邮件发送器 + * 使用原生Java实现SMTP协议发送邮件 + */ +public class RealEmailSender { + + private static final String SMTP_HOST = "smtp.qq.com"; + private static final int SMTP_PORT = 587; + + /** + * 发送邮件 + */ + public static boolean sendEmail(String fromEmail, String fromPassword, String toEmail, String subject, String content) { + try { + // 1. 连接到SMTP服务器 + Socket socket = new Socket(SMTP_HOST, SMTP_PORT); + BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + PrintWriter writer = new PrintWriter(socket.getOutputStream(), true); + + // 2. 读取服务器欢迎信息 + String response = reader.readLine(); + System.out.println("服务器: " + response); + if (!response.startsWith("220")) { + throw new Exception("SMTP服务器连接失败"); + } + + // 3. 发送EHLO命令 + writer.println("EHLO localhost"); + response = readMultiLineResponse(reader); + System.out.println("EHLO响应: " + response); + + // 4. 启动TLS加密 + writer.println("STARTTLS"); + response = reader.readLine(); + System.out.println("STARTTLS响应: " + response); + if (!response.startsWith("220")) { + throw new Exception("启动TLS失败"); + } + + // 5. 升级到SSL连接 + SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); + SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, SMTP_HOST, SMTP_PORT, true); + sslSocket.startHandshake(); + + // 6. 重新创建读写器 + reader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream())); + writer = new PrintWriter(sslSocket.getOutputStream(), true); + + // 7. 重新发送EHLO + writer.println("EHLO localhost"); + response = readMultiLineResponse(reader); + System.out.println("SSL EHLO响应: " + response); + + // 8. 认证 + writer.println("AUTH LOGIN"); + response = reader.readLine(); + System.out.println("AUTH响应: " + response); + if (!response.startsWith("334")) { + throw new Exception("认证失败"); + } + + // 9. 发送用户名(Base64编码) + String encodedUsername = Base64.getEncoder().encodeToString(fromEmail.getBytes()); + writer.println(encodedUsername); + response = reader.readLine(); + System.out.println("用户名响应: " + response); + if (!response.startsWith("334")) { + throw new Exception("用户名认证失败"); + } + + // 10. 发送密码(Base64编码) + String encodedPassword = Base64.getEncoder().encodeToString(fromPassword.getBytes()); + writer.println(encodedPassword); + response = reader.readLine(); + System.out.println("密码响应: " + response); + if (!response.startsWith("235")) { + throw new Exception("密码认证失败: " + response); + } + + // 11. 发送邮件 + // MAIL FROM + writer.println("MAIL FROM:<" + fromEmail + ">"); + response = reader.readLine(); + System.out.println("MAIL FROM响应: " + response); + if (!response.startsWith("250")) { + throw new Exception("MAIL FROM失败"); + } + + // RCPT TO + writer.println("RCPT TO:<" + toEmail + ">"); + response = reader.readLine(); + System.out.println("RCPT TO响应: " + response); + if (!response.startsWith("250")) { + throw new Exception("RCPT TO失败"); + } + + // DATA + writer.println("DATA"); + response = reader.readLine(); + System.out.println("DATA响应: " + response); + if (!response.startsWith("354")) { + throw new Exception("DATA失败"); + } + + // 邮件头和内容 + writer.println("From: " + fromEmail); + writer.println("To: " + toEmail); + writer.println("Subject: =?UTF-8?B?" + Base64.getEncoder().encodeToString(subject.getBytes("UTF-8")) + "?="); + writer.println("MIME-Version: 1.0"); + writer.println("Content-Type: text/plain; charset=UTF-8"); + writer.println("Content-Transfer-Encoding: base64"); + writer.println("X-Mailer: MathLearningSystem"); + writer.println("X-Priority: 1"); + writer.println(); + writer.println(Base64.getEncoder().encodeToString(content.getBytes("UTF-8"))); + writer.println("."); + + response = reader.readLine(); + System.out.println("邮件发送响应: " + response); + if (!response.startsWith("250")) { + throw new Exception("邮件发送失败"); + } + + // 12. 退出 + writer.println("QUIT"); + response = reader.readLine(); + System.out.println("QUIT响应: " + response); + + // 13. 关闭连接 + sslSocket.close(); + + System.out.println("邮件发送成功!"); + return true; + + } catch (Exception e) { + System.err.println("邮件发送失败: " + e.getMessage()); + e.printStackTrace(); + return false; + } + } + + /** + * 读取多行响应 + */ + private static String readMultiLineResponse(BufferedReader reader) throws IOException { + StringBuilder response = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + response.append(line).append("\n"); + // 如果行不是以'-'结尾,说明是最后一行 + if (line.length() >= 4 && line.charAt(3) != '-') { + break; + } + } + return response.toString(); + } + + /** + * 创建验证码邮件内容 + */ + public static String createVerificationEmailContent(String verificationCode) { + return "您好!\n\n" + + "您正在注册数学学习软件,您的验证码是:\n\n" + + "验证码:" + verificationCode + "\n\n" + + "验证码有效期为10分钟,请及时使用。\n" + + "如果您没有注册我们的软件,请忽略此邮件。\n\n" + + "祝您学习愉快!\n" + + "数学学习软件团队\n\n" + + "此邮件由系统自动发送,请勿回复。"; + } +} diff --git a/src/RegisteredUser.java b/src/RegisteredUser.java new file mode 100644 index 0000000..a8d021a --- /dev/null +++ b/src/RegisteredUser.java @@ -0,0 +1,56 @@ +/** + * 注册用户类 + * 存储用户注册信息和密码 + */ +public class RegisteredUser { + private String email; + private String password; + private String verificationCode; + private boolean isVerified; + + public RegisteredUser(String email, String verificationCode) { + this.email = email; + this.verificationCode = verificationCode; + this.isVerified = false; + } + + public String getEmail() { + return email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getVerificationCode() { + return verificationCode; + } + + public boolean isVerified() { + return isVerified; + } + + public void setVerified(boolean verified) { + isVerified = verified; + } + + public boolean verifyCode(String code) { + return verificationCode.equals(code); + } + + public boolean verifyPassword(String password) { + return this.password != null && this.password.equals(password); + } + + @Override + public String toString() { + return "RegisteredUser{" + + "email='" + email + '\'' + + ", isVerified=" + isVerified + + '}'; + } +} -- 2.34.1