pull/4/head
yesitis 5 months ago
commit 1299f6fe5e

38
.gitignore vendored

@ -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

3
.idea/.gitignore vendored

@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml

@ -0,0 +1 @@
UserService.java

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

@ -1,6 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="openjdk-25" project-jdk-type="JavaSDK">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="23" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -1,8 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="3b1bb474-3d43-47ea-b452-37db5e702367" name="更改" comment="">
<change afterPath="$PROJECT_DIR$/doc/README.md" afterDir="false" />
<list default="true" id="3b1bb474-3d43-47ea-b452-37db5e702367" name="更改" comment="1">
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/.gitignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/.name" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/encodings.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/data/users.json" afterDir="false" />
<change afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/mathlearning/ui/ChangePasswordFrame.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/mathlearning/ui/ProfileFrame.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/mathlearning/ui/SelTestFrame.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/modules.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/mathlearning/model/User.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/mathlearning/model/User.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/mathlearning/service/UserService.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/mathlearning/service/UserService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/mathlearning/ui/ChangeCodeFrame.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/mathlearning/ui/ChangeCodeFrame.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/mathlearning/ui/LoginFrame.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/mathlearning/ui/LoginFrame.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/mathlearning/ui/MainFrame.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/mathlearning/ui/MainFrame.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/mathlearning/ui/RegisterFrame.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/mathlearning/ui/RegisterFrame.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -12,9 +34,9 @@
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 8
}]]></component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="33v4xVg7CMc104IhcpHMl9ks5xm" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
@ -24,12 +46,26 @@
"keyToString": {
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "liuxinrui__branch",
"git-widget-placeholder": "正在合并 liuxinrui__branch",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "D:/filee/软导/java/结对/project/MathLearningApp/git"
}
}]]></component>
<component name="RunManager">
<configuration default="true" type="JetRunConfigurationType">
<module name="git" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="KotlinStandaloneScriptRunConfigurationType">
<module name="git" />
<option name="filePath" />
<method v="2" />
</configuration>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="默认任务">
<changelist id="3b1bb474-3d43-47ea-b452-37db5e702367" name="更改" comment="" />
@ -40,4 +76,19 @@
</task>
<servers />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="1" />
<option name="LAST_COMMIT_MESSAGE" value="1" />
</component>
</project>

@ -0,0 +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" : "高中"
} ]

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mathlearning</groupId>
<artifactId>math-learning-app</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- JavaMail for email verification -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
<!-- JSON processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.15.2</version>
</dependency>
<!-- Password hashing -->
<dependency>
<groupId>at.favre.lib</groupId>
<artifactId>bcrypt</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies>
</project>

@ -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;

@ -14,7 +14,7 @@ public class MultipleChoiceGenerator {
public MultipleChoiceGenerator(int count, User nowUser){// 如此声明MultipleChoiceGenerator实例,再调用下面三个接口
this.QuestionList = new String[count];
this.ChoiceList = new String[count];
this.CorrectAnswerNo = new String[count];
this.CorrectAnswerNo = new String[count];
this.QuestionList = SetQuestionList(count, nowUser);
SetChoiceList();
}
@ -98,4 +98,4 @@ public class MultipleChoiceGenerator {
}
}

@ -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) {
@ -226,6 +230,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+_.-]+@(.+)$");
}

@ -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);

@ -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();
}
}
}

@ -157,6 +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();
}
}

@ -1,5 +1,6 @@
package mathlearning.ui;
import com.sun.tools.javac.Main;
import mathlearning.model.User;
import mathlearning.service.UserService;
@ -12,6 +13,9 @@ public class MainFrame extends JFrame {
private User currentUser;
private UserService userService;
private JLabel welcomeLabel;
private JLabel typeLabel;
private JButton changeTypeButton;
private JPanel mainPanel;
public MainFrame(User user, UserService userService) {
this.currentUser = user;
@ -28,14 +32,14 @@ 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信息
welcomeLabel = new JLabel("欢迎"+currentUser.getUsername()+"!", JLabel.CENTER);
welcomeLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
JLabel typeLabel = new JLabel("当前选择的测试题为" + currentUser.getType() + "难度", JLabel.CENTER);
typeLabel = new JLabel("当前选择的测试题为" + currentUser.getType() + "难度", JLabel.CENTER);
typeLabel.setFont(new Font("微软雅黑", Font.BOLD, 16));
typeLabel.setForeground(Color.BLUE);
@ -46,8 +50,126 @@ public class MainFrame extends JFrame {
// 功能按钮区域
JPanel centerButtonPanel = new JPanel(new GridLayout(2, 1, 15, 15));
centerButtonPanel.setBorder(BorderFactory.createEmptyBorder(30, 50, 30, 50));
// 切换教育阶段按钮
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(e -> openSelfTestFrame());
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(Color.CYAN);
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) {// 为按钮添加边框和圆角效果
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();
}
}
}
private class ProfileListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
ProfileFrame profileFrame = new ProfileFrame(currentUser, userService);
profileFrame.setVisible(true);
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();
}
}

@ -0,0 +1,130 @@
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;
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();
}
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() {
ChangePasswordFrame changePasswordFrame = new ChangePasswordFrame(user, userService, this);
changePasswordFrame.setVisible(true);
this.setVisible(false);
}
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);
}
}
}
}
}

@ -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);

@ -0,0 +1,324 @@
package mathlearning.ui;
import mathlearning.model.User;
import mathlearning.service.MultipleChoiceGenerator.MultipleChoiceGenerator;
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 MultipleChoiceGenerator multipleChoiceGenerator;
private int questionNum;
private int currentQuestionDex;
private String[] answers;
private String[] myAnswers;
private JLabel titleLabel;
private JButton optionA;
private JButton optionB;
private JButton optionC;
private JButton optionD;
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;
this.user = user;
this.userService = userService;
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
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<String> 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 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;
}
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);
myAnswers[currentQuestionDex] = answer;
break;
case "B":
optionB.setBackground(Color.GREEN);
myAnswers[currentQuestionDex] = answer;
break;
case "C":
optionC.setBackground(Color.GREEN);
myAnswers[currentQuestionDex] = answer;
break;
case "D":
optionD.setBackground(Color.GREEN);
myAnswers[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();
updateQuestion();
if (myAnswers[currentQuestionDex] == null) {
resetButtonColors();
}
else {
saveAnswer(myAnswers[currentQuestionDex]);
}
updateNavigationButtons();
}
}
private void goToNextQuestion() {
if (currentQuestionDex < questionNum - 1) {
currentQuestionDex++;
updateTitleLabel();
updateQuestion();
if (myAnswers[currentQuestionDex] == null) {
resetButtonColors();
} else {
saveAnswer(myAnswers[currentQuestionDex]);
}
updateNavigationButtons();
}
}
private void goToSubmit() {
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;
// 弹出分数结果
String[] options = {"返回主菜单", "继续做题"};
int choice = JOptionPane.showOptionDialog(
SelTestFrame.this,
"您答对了" + correctCount + "道题,正确率为" + String.format("%.2f", accuracy) + "\n您的得分是: " + score + "分",
"测试结果",
JOptionPane.YES_NO_OPTION,
JOptionPane.INFORMATION_MESSAGE,
null,
options,
options[0]
);
if (choice == 0) {
mainFrame.setVisible(true);
dispose();
} else if (choice == 1) {
dispose();
mainFrame.setVisible(true);
mainFrame.reTryTest();
}
}
}
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();
}
}
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;
}
private void updateQuestion() {
question.setText(multipleChoiceGenerator.GetQuestionList()[currentQuestionDex]);
choice.setText(multipleChoiceGenerator.GetChoiceList()[currentQuestionDex]);
}
}
Loading…
Cancel
Save