Compare commits

...

No commits in common. 'develop' and 'main' have entirely different histories.

@ -1,2 +0,0 @@
# pairing_program

@ -0,0 +1,184 @@
## 小初高数学学习软件 - 代码说明文档
## 项目概述
本项目是一个面向小学、初中和高中学生的数学学习软件提供图形化界面的数学题目练习和测试功能。软件采用JavaFX开发实现了用户注册、登录、题目生成、在线答题和成绩统计等完整功能。
## 项目结构
```
src/
├── Main.java # 主程序入口,界面控制器
├── QuestionGenerator.java # 题目生成器
├── ExamManager.java # 试卷管理器
├── User.java # 用户实体类
└── MailSender.java # 邮件发送器
```
## 核心功能模块
### 1. 用户管理模块
#### 功能特性
- **双方式登录**:支持邮箱或用户名登录
- **用户注册**邮箱验证、用户名设置4-10位
- **密码管理**6-10位必须包含大小写字母和数字
- **密码修改**:登录状态下可修改密码
#### 核心代码
```java
// 用户验证方法
private boolean isValidUsername(String username) // 用户名格式验证
private boolean isValidEmail(String email) // 邮箱格式验证
private boolean isValidPassword(String password) // 密码强度验证
```
### 2. 题目生成模块
#### 题目难度分级
**小学题目**
- 运算符:+、-、*、/
- 支持括号运算
- 操作数1-100
- 操作数个数2-5个
**初中题目**
- 包含小学所有运算符
- 新增:平方(^2)、开根号(√)
- 确保每道题至少包含一个特殊运算符
**高中题目**
- 包含初中所有运算符
- 新增sin、cos、tan三角函数
- 确保每道题至少包含一个三角函数
#### 核心算法
```java
public String generateQuestion(String type) // 根据类型生成题目
public List<String> generateOptions(String answer) // 生成选择题选项
```
### 3. 考试管理模块
#### 功能特性
- **题目查重**:同一试卷内题目不重复
- **进度跟踪**:实时显示答题进度
- **自动评分**:答完后自动计算分数
- **成绩展示**:显示得分、正确率、详细统计
#### 核心类
```java
public class ExamManager {
public List<String> generateExam(int numQuestions) // 生成指定数量题目
}
```
### 4. 界面导航流程
```
登录界面 → 注册界面 → 难度选择 → 题目数量输入 → 答题界面 → 成绩界面
↑ ↓
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
```
## 技术实现细节
### 1. 数据存储
- 使用内存Map存储用户数据
- 题目历史记录在Session中维护
- 符合"不可以使用数据库存储数据"的要求
### 2. 邮件服务
- 使用QQ邮箱SMTP服务
- 异步发送验证码
- 支持注册验证功能
### 3. 界面设计
- 采用JavaFX图形界面
- 响应式布局设计
- 友好的用户交互提示
## 代码规范与架构
### 1. 类职责分离
- `Main.java`:界面控制和业务流程
- `QuestionGenerator.java`:题目生成算法
- `ExamManager.java`:试卷管理逻辑
- `User.java`:用户数据模型
- `MailSender.java`:邮件服务封装
### 2. 方法设计原则
- 单一职责原则
- 方法行数控制在40行以内
- 清晰的参数和返回值定义
## 配置要求
### 运行环境
- Java 8及以上
- JavaFX SDK
- 网络连接(用于邮件发送)
### 邮箱配置
在`Main.java`中修改发件邮箱配置:
```java
private MailSender mailSender = new MailSender("你的邮箱", "授权码");
```
## 功能验证清单
### ✅ 已实现功能
- [x] 图形化界面操作
- [x] 用户注册与邮箱验证
- [x] 密码强度验证6-10位大小写字母+数字)
- [x] 双方式登录(邮箱/用户名)
- [x] 密码修改功能
- [x] 三难度题目生成(小学、初中、高中)
- [x] 题目数量限制10-30题
- [x] 选择题形式答题
- [x] 自动评分与成绩展示
- [x] 继续做题/退出选择
### ✅ 符合项目要求
- [x] 不使用数据库存储
- [x] 所有功能通过图形界面操作
- [x] 完整的用户流程
- [x] 题目符合各学段难度要求
## 使用说明
1. **首次使用**:点击"前往注册",设置用户名、邮箱,接收验证码完成注册
2. **登录系统**:使用邮箱或用户名+密码登录
3. **选择难度**:根据学习阶段选择小学、初中或高中
4. **设置题量**输入10-30之间的题目数量
5. **开始答题**:逐题作答,系统自动记录进度
6. **查看成绩**:答题完成后查看详细成绩统计
7. **继续学习**:可选择继续做题或退出系统
##
*最后更新2025年10月12日*

@ -0,0 +1,24 @@
import java.util.*;
public class ExamManager {
private QuestionGenerator generator = new QuestionGenerator();
private Set<String> history = new HashSet<>();
private String type;
public ExamManager(String type){
this.type = type;
}
public List<String> generateExam(int numQuestions){
List<String> questions = new ArrayList<>();
for(int i=0;i<numQuestions;i++){
String q;
do {
q = generator.generateQuestion(type);
} while(history.contains(q));
history.add(q);
questions.add(q);
}
return questions;
}
}

@ -0,0 +1,42 @@
import jakarta.mail.*;
import jakarta.mail.internet.*;
import java.util.Properties;
public class MailSender {
private final String senderEmail; // 发件邮箱
private final String senderPassword; // 发件邮箱授权码
public MailSender(String email, String password) {
this.senderEmail = email;
this.senderPassword = password;
}
public boolean sendMail(String toEmail, String subject, String content) {
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.qq.com"); // QQ 邮箱
props.put("mail.smtp.port", "587");
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(senderEmail, senderPassword);
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(senderEmail));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail));
message.setSubject(subject);
message.setText(content);
Transport.send(message);
return true;
} catch (MessagingException e) {
e.printStackTrace();
return false;
}
}
}

@ -0,0 +1,587 @@
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.*;
public class Main extends Application {
private Stage primaryStage;
private MailSender mailSender = new MailSender("3417398995@qq.com", "zhwytlhmucfxcibe");
private String generatedCode;
// 存储用户信息:邮箱 -> 密码
private Map<String, String> userDatabase = new HashMap<>();
// 存储用户名 -> 邮箱 映射
private Map<String, String> usernameToEmail = new HashMap<>();
// 存储邮箱 -> 用户名 映射
private Map<String, String> emailToUsername = new HashMap<>();
private Map<String, String> currentUser = new HashMap<>(); // 存储当前登录用户信息
private List<Question> currentExam = new ArrayList<>();
private int currentIndex = 0;
private int score = 0;
private ExamManager examManager;
private QuestionGenerator questionGenerator = new QuestionGenerator();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
primaryStage = stage;
primaryStage.setTitle("小初高数学学习软件");
primaryStage.setScene(buildLoginScene());
primaryStage.show();
}
// 登录界面
private Scene buildLoginScene() {
VBox root = new VBox(10);
root.setStyle("-fx-padding: 20;");
Label titleLabel = new Label("数学学习软件 - 登录");
titleLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold;");
TextField loginField = new TextField();
loginField.setPromptText("请输入邮箱或用户名");
PasswordField passwordField = new PasswordField();
passwordField.setPromptText("请输入密码");
Button loginBtn = new Button("登录");
Button registerBtn = new Button("前往注册");
Label infoLabel = new Label();
Label tipLabel = new Label("提示:可以使用邮箱或用户名登录");
tipLabel.setStyle("-fx-font-size: 10; -fx-text-fill: gray;");
// 登录按钮事件
loginBtn.setOnAction(e -> {
String loginInput = loginField.getText().trim();
String password = passwordField.getText();
if (loginInput.isEmpty() || password.isEmpty()) {
infoLabel.setText("请输入登录信息和密码");
return;
}
String email = getEmailFromLoginInput(loginInput);
if (email == null) {
infoLabel.setText("用户不存在");
return;
}
if (!userDatabase.get(email).equals(password)) {
infoLabel.setText("密码错误");
return;
}
// 登录成功
currentUser.put("email", email);
currentUser.put("username", emailToUsername.get(email));
infoLabel.setText("登录成功!欢迎 " + emailToUsername.get(email));
primaryStage.setScene(buildLevelSelectionScene());
});
// 注册按钮事件
registerBtn.setOnAction(e -> {
primaryStage.setScene(buildRegisterScene());
});
root.getChildren().addAll(titleLabel, loginField, passwordField, tipLabel, loginBtn, registerBtn, infoLabel);
return new Scene(root, 400, 300);
}
// 根据登录输入获取邮箱
private String getEmailFromLoginInput(String loginInput) {
// 如果输入包含@,认为是邮箱
if (loginInput.contains("@")) {
return userDatabase.containsKey(loginInput) ? loginInput : null;
} else {
// 否则认为是用户名
return usernameToEmail.get(loginInput);
}
}
// 注册界面
private Scene buildRegisterScene() {
VBox root = new VBox(10);
root.setStyle("-fx-padding: 20;");
Label titleLabel = new Label("用户注册");
titleLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold;");
TextField usernameField = new TextField();
usernameField.setPromptText("设置用户名 (4-10位字符)");
TextField emailField = new TextField();
emailField.setPromptText("请输入邮箱");
Button sendCodeBtn = new Button("发送验证码");
TextField codeField = new TextField();
codeField.setPromptText("请输入验证码");
PasswordField passwordField = new PasswordField();
passwordField.setPromptText("设置密码 (6-10位含大小写字母和数字)");
PasswordField confirmPasswordField = new PasswordField();
confirmPasswordField.setPromptText("确认密码");
Button registerBtn = new Button("注册");
Button backBtn = new Button("返回登录");
Label infoLabel = new Label();
sendCodeBtn.setOnAction(e -> {
String username = usernameField.getText().trim();
String email = emailField.getText().trim();
// 验证用户名
if (!isValidUsername(username)) {
infoLabel.setText("用户名必须4-10位只能包含字母、数字和下划线");
return;
}
if (usernameToEmail.containsKey(username)) {
infoLabel.setText("用户名已存在");
return;
}
// 使用新的邮箱验证方法
if (!isValidEmail(email)) {
infoLabel.setText("邮箱格式不正确,请使用有效的邮箱地址");
return;
}
if (userDatabase.containsKey(email)) {
infoLabel.setText("该邮箱已注册");
return;
}
generatedCode = String.format("%06d", new Random().nextInt(999999));
infoLabel.setText("验证码发送中...");
new Thread(() -> {
boolean success = mailSender.sendMail(email, "注册验证码", "你的验证码为:" + generatedCode);
Platform.runLater(() -> {
if (success) {
infoLabel.setText("验证码已发送,请查看邮箱");
} else {
infoLabel.setText("发送失败,请检查邮箱是否正确");
}
});
}).start();
});
registerBtn.setOnAction(e -> {
String username = usernameField.getText().trim();
String email = emailField.getText().trim();
String inputCode = codeField.getText().trim();
String pw = passwordField.getText();
String pwConfirm = confirmPasswordField.getText();
if (!isValidUsername(username)) {
infoLabel.setText("用户名必须4-10位只能包含字母、数字和下划线");
return;
}
if (usernameToEmail.containsKey(username)) {
infoLabel.setText("用户名已存在");
return;
}
if (!inputCode.equals(generatedCode)) {
infoLabel.setText("验证码错误");
} else if (!pw.equals(pwConfirm)) {
infoLabel.setText("两次密码不一致");
} else if (!isValidPassword(pw)) {
infoLabel.setText("密码必须6-10位包含大小写字母和数字");
} else if (userDatabase.containsKey(email)) {
infoLabel.setText("邮箱已注册");
} else {
// 注册用户
userDatabase.put(email, pw);
usernameToEmail.put(username, email);
emailToUsername.put(email, username);
infoLabel.setText("注册成功!用户名: " + username);
// 注册成功后自动登录
currentUser.put("email", email);
currentUser.put("username", username);
primaryStage.setScene(buildLevelSelectionScene());
}
});
backBtn.setOnAction(e -> {
primaryStage.setScene(buildLoginScene());
});
root.getChildren().addAll(titleLabel, usernameField, emailField, sendCodeBtn, codeField,
passwordField, confirmPasswordField, registerBtn, backBtn, infoLabel);
return new Scene(root, 400, 450);
}
// 用户名验证方法
private boolean isValidUsername(String username) {
if (username.length() < 4 || username.length() > 10) {
return false;
}
// 只能包含字母、数字、下划线
return username.matches("^[a-zA-Z0-9_]+$");
}
// 邮箱完整验证方法
private boolean isValidEmail(String email) {
if (email == null || email.trim().isEmpty()) {
return false;
}
String regex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";
// 基本格式检查
if (!email.matches(regex)) {
return false;
}
// 检查@符号位置
int atIndex = email.indexOf('@');
if (atIndex <= 0 || atIndex == email.length() - 1) {
return false;
}
// 检查域名部分
String domain = email.substring(atIndex + 1);
if (domain.indexOf('.') <= 0 || domain.endsWith(".")) {
return false;
}
// 检查常见邮箱服务商
String[] commonDomains = {"qq.com", "gmail.com", "163.com", "126.com", "sina.com",
"hotmail.com", "outlook.com", "yahoo.com", "foxmail.com"};
boolean hasCommonDomain = false;
for (String commonDomain : commonDomains) {
if (domain.equalsIgnoreCase(commonDomain)) {
hasCommonDomain = true;
break;
}
}
return true;
}
// 密码验证方法
private boolean isValidPassword(String password) {
if (password.length() < 6 || password.length() > 10) {
return false;
}
boolean hasUpper = false;
boolean hasLower = false;
boolean hasDigit = false;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) hasUpper = true;
if (Character.isLowerCase(c)) hasLower = true;
if (Character.isDigit(c)) hasDigit = true;
}
return hasUpper && hasLower && hasDigit;
}
// 难度选择界面
private Scene buildLevelSelectionScene() {
VBox root = new VBox(15);
root.setStyle("-fx-padding: 20;");
// 显示欢迎信息
String username = currentUser.get("username");
Label welcomeLabel = new Label("欢迎, " + username + "!");
welcomeLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold; -fx-text-fill: #2E8B57;");
Label label = new Label("请选择难度");
label.setStyle("-fx-font-size: 14; -fx-font-weight: bold;");
Button primaryBtn = new Button("小学");
primaryBtn.setStyle("-fx-font-size: 12; -fx-pref-width: 120;");
Button juniorBtn = new Button("初中");
juniorBtn.setStyle("-fx-font-size: 12; -fx-pref-width: 120;");
Button seniorBtn = new Button("高中");
seniorBtn.setStyle("-fx-font-size: 12; -fx-pref-width: 120;");
Button changePwdBtn = new Button("修改密码");
changePwdBtn.setStyle("-fx-font-size: 12;");
Button logoutBtn = new Button("退出登录");
logoutBtn.setStyle("-fx-font-size: 12;");
primaryBtn.setOnAction(e -> buildExamNumberScene("小学"));
juniorBtn.setOnAction(e -> buildExamNumberScene("初中"));
seniorBtn.setOnAction(e -> buildExamNumberScene("高中"));
changePwdBtn.setOnAction(e -> primaryStage.setScene(buildChangePasswordScene()));
logoutBtn.setOnAction(e -> {
currentUser.clear();
primaryStage.setScene(buildLoginScene());
});
VBox buttonBox = new VBox(10, primaryBtn, juniorBtn, seniorBtn);
buttonBox.setStyle("-fx-alignment: center;");
HBox bottomBox = new HBox(10, changePwdBtn, logoutBtn);
bottomBox.setStyle("-fx-alignment: center;");
root.getChildren().addAll(welcomeLabel, label, buttonBox, bottomBox);
Scene scene = new Scene(root, 400, 300);
primaryStage.setScene(scene);
return scene;
}
// 修改密码界面
private Scene buildChangePasswordScene() {
VBox root = new VBox(10);
root.setStyle("-fx-padding: 20;");
Label titleLabel = new Label("修改密码");
titleLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold;");
Label userLabel = new Label("用户: " + currentUser.get("username"));
userLabel.setStyle("-fx-font-size: 12; -fx-text-fill: gray;");
PasswordField oldPwdField = new PasswordField();
oldPwdField.setPromptText("请输入原密码");
PasswordField newPwdField = new PasswordField();
newPwdField.setPromptText("请输入新密码");
PasswordField confirmPwdField = new PasswordField();
confirmPwdField.setPromptText("确认新密码");
Button confirmBtn = new Button("确认修改");
Button backBtn = new Button("返回");
Label infoLabel = new Label();
confirmBtn.setOnAction(e -> {
String oldPwd = oldPwdField.getText();
String newPwd = newPwdField.getText();
String confirmPwd = confirmPwdField.getText();
String email = currentUser.get("email");
if (!userDatabase.get(email).equals(oldPwd)) {
infoLabel.setText("原密码错误");
} else if (!newPwd.equals(confirmPwd)) {
infoLabel.setText("两次输入的新密码不一致");
} else if (!isValidPassword(newPwd)) {
infoLabel.setText("新密码必须6-10位包含大小写字母和数字");
} else {
userDatabase.put(email, newPwd);
infoLabel.setText("密码修改成功!");
}
});
backBtn.setOnAction(e -> {
primaryStage.setScene(buildLevelSelectionScene());
});
root.getChildren().addAll(titleLabel, userLabel, oldPwdField, newPwdField, confirmPwdField,
confirmBtn, backBtn, infoLabel);
return new Scene(root, 400, 350);
}
// 输入题目数量界面
private void buildExamNumberScene(String level) {
VBox root = new VBox(10);
root.setStyle("-fx-padding: 20;");
Label label = new Label("请输入" + level + "题目数量 (10-30)");
label.setStyle("-fx-font-size: 14;");
TextField numberField = new TextField();
numberField.setPromptText("题目数量");
Button startBtn = new Button("开始做题");
Button backBtn = new Button("返回");
Label infoLabel = new Label();
startBtn.setOnAction(e -> {
try {
int count = Integer.parseInt(numberField.getText().trim());
if (count < 10 || count > 30) {
infoLabel.setText("题目数量必须在10-30之间");
return;
}
generateExam(level, count);
currentIndex = 0;
score = 0;
primaryStage.setScene(buildExamScene());
} catch (NumberFormatException ex) {
infoLabel.setText("请输入有效数字");
}
});
backBtn.setOnAction(e -> {
primaryStage.setScene(buildLevelSelectionScene());
});
root.getChildren().addAll(label, numberField, startBtn, backBtn, infoLabel);
primaryStage.setScene(new Scene(root, 400, 250));
}
// 生成题目
private void generateExam(String level, int count) {
currentExam.clear();
examManager = new ExamManager(level);
List<String> questionTexts = examManager.generateExam(count);
for (int i = 0; i < questionTexts.size(); i++) {
String questionText = (i + 1) + ". " + questionTexts.get(i) + " = ?";
String correctAnswer = questionGenerator.calculateAnswer(questionTexts.get(i));
List<String> options = questionGenerator.generateOptions(correctAnswer);
currentExam.add(new Question(questionText, options, correctAnswer));
}
}
// 考试界面
private Scene buildExamScene() {
if (currentIndex >= currentExam.size()) {
return buildScoreScene();
}
Question q = currentExam.get(currentIndex);
VBox root = new VBox(15);
root.setStyle("-fx-padding: 20;");
Label progressLabel = new Label("进度: " + (currentIndex + 1) + "/" + currentExam.size());
progressLabel.setStyle("-fx-font-weight: bold;");
Label questionLabel = new Label(q.getQuestion());
questionLabel.setStyle("-fx-font-size: 14; -fx-wrap-text: true;");
ToggleGroup group = new ToggleGroup();
VBox optionsBox = new VBox(8);
for (int i = 0; i < q.getOptions().size(); i++) {
RadioButton radioBtn = new RadioButton((char)('A' + i) + ". " + q.getOptions().get(i));
radioBtn.setToggleGroup(group);
radioBtn.setUserData(q.getOptions().get(i));
optionsBox.getChildren().add(radioBtn);
}
Button nextBtn = new Button(currentIndex == currentExam.size() - 1 ? "提交" : "下一题");
nextBtn.setStyle("-fx-font-size: 12;");
nextBtn.setOnAction(e -> {
RadioButton selected = (RadioButton) group.getSelectedToggle();
if (selected == null) {
// 如果没有选择自动选择A选项
if (!optionsBox.getChildren().isEmpty()) {
selected = (RadioButton) optionsBox.getChildren().get(0);
}
}
if (selected != null) {
String selectedAnswer = (String) selected.getUserData();
if (selectedAnswer.equals(q.getAnswer())) {
score++;
}
}
currentIndex++;
primaryStage.setScene(buildExamScene());
});
root.getChildren().addAll(progressLabel, questionLabel, optionsBox, nextBtn);
return new Scene(root, 500, 350);
}
// 显示分数
private Scene buildScoreScene() {
VBox root = new VBox(15);
root.setStyle("-fx-padding: 30; -fx-alignment: center; -fx-background-color: #f5f5f5;");
// 计算分数
int totalQuestions = currentExam.size();
double scorePerQuestion = 100.0 / totalQuestions; // 每道题的分值
int finalScore = (int) Math.round(score * scorePerQuestion); // 最终分数(四舍五入)
double correctRate = (score * 100.0) / totalQuestions;
int percentage = (int) Math.round(correctRate);
// 标题
Label titleLabel = new Label("答题结果");
titleLabel.setStyle("-fx-font-size: 24; -fx-font-weight: bold; -fx-text-fill: #2C3E50;");
// 分数卡片
VBox scoreCard = new VBox(10);
scoreCard.setStyle("-fx-background-color: white; -fx-padding: 20; -fx-border-color: #ddd; -fx-border-radius: 10; -fx-background-radius: 10;");
scoreCard.setMaxWidth(300);
Label finalScoreLabel = new Label(finalScore + " 分");
finalScoreLabel.setStyle("-fx-font-size: 36; -fx-font-weight: bold; -fx-text-fill: #E74C3C;");
Label scoreTextLabel = new Label("最终得分");
scoreTextLabel.setStyle("-fx-font-size: 14; -fx-text-fill: #7F8C8D;");
// 详细信息
VBox detailBox = new VBox(5);
detailBox.setStyle("-fx-alignment: center-left;");
Label percentageLabel = new Label("• 正确率: " + percentage + "%");
percentageLabel.setStyle("-fx-font-size: 14;");
Label detailLabel = new Label("• 答对: " + score + " 题 / 总共: " + totalQuestions + " 题");
detailLabel.setStyle("-fx-font-size: 14;");
Label pointLabel = new Label("• 每题分值: " + String.format("%.1f", scorePerQuestion) + " 分");
pointLabel.setStyle("-fx-font-size: 14;");
detailBox.getChildren().addAll(percentageLabel, detailLabel, pointLabel);
// 鼓励语
Label encouragementLabel = new Label();
encouragementLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold; -fx-padding: 10 0 0 0;");
if (finalScore == 100) {
encouragementLabel.setText("🎉 满分!太棒了!");
encouragementLabel.setStyle("-fx-text-fill: #FFD700; -fx-font-size: 16; -fx-font-weight: bold;");
} else if (finalScore >= 90) {
encouragementLabel.setText("👍 优秀!表现很好!");
encouragementLabel.setStyle("-fx-text-fill: #27AE60; -fx-font-size: 16; -fx-font-weight: bold;");
} else if (finalScore >= 80) {
encouragementLabel.setText("👏 很好!继续加油!");
encouragementLabel.setStyle("-fx-text-fill: #2980B9; -fx-font-size: 16; -fx-font-weight: bold;");
} else if (finalScore >= 60) {
encouragementLabel.setText("💪 及格了!还有进步空间!");
encouragementLabel.setStyle("-fx-text-fill: #F39C12; -fx-font-size: 16; -fx-font-weight: bold;");
} else {
encouragementLabel.setText("📚 加油!再多练习一下!");
encouragementLabel.setStyle("-fx-text-fill: #E74C3C; -fx-font-size: 16; -fx-font-weight: bold;");
}
scoreCard.getChildren().addAll(finalScoreLabel, scoreTextLabel, new Separator(), detailBox, encouragementLabel);
// 按钮区域
HBox buttonBox = new HBox(20);
buttonBox.setStyle("-fx-alignment: center; -fx-padding: 20 0 0 0;");
Button retryBtn = new Button("继续做题");
retryBtn.setStyle("-fx-font-size: 14; -fx-background-color: #4CAF50; -fx-text-fill: white; -fx-pref-width: 120;");
Button exitBtn = new Button("退出");
exitBtn.setStyle("-fx-font-size: 14; -fx-background-color: #95a5a6; -fx-text-fill: white; -fx-pref-width: 120;");
retryBtn.setOnAction(e -> primaryStage.setScene(buildLevelSelectionScene()));
exitBtn.setOnAction(e -> Platform.exit());
buttonBox.getChildren().addAll(retryBtn, exitBtn);
root.getChildren().addAll(titleLabel, scoreCard, buttonBox);
return new Scene(root, 500, 450);
}
// Question 类
static class Question {
private final String question;
private final List<String> options;
private final String answer;
public Question(String question, List<String> options, String answer) {
this.question = question;
this.options = options;
this.answer = answer;
}
public String getQuestion() { return question; }
public List<String> getOptions() { return options; }
public String getAnswer() { return answer; }
}
}

@ -0,0 +1,198 @@
import java.util.*;
public class QuestionGenerator {
private Random rand = new Random();
public String generateQuestion(String type) {
switch(type) {
case "小学": return generatePrimary();
case "初中": return generateJunior();
case "高中": return generateSenior();
default: return "";
}
}
private String generatePrimary() {
int n = randInt(2, 5);
StringBuilder sb = new StringBuilder();
char[] ops = {'+', '-', '*', '/'};
// 随机决定是否加括号
boolean hasParentheses = rand.nextBoolean() && n >= 3;
int parenthesesPos = hasParentheses ? rand.nextInt(n - 2) : -1;
for(int i = 0; i < n; i++) {
if (hasParentheses && i == parenthesesPos) {
sb.append("(");
}
sb.append(randInt(1, 100));
if (hasParentheses && i == parenthesesPos + 1) {
sb.append(")");
}
if(i != n - 1) {
sb.append(" ").append(ops[rand.nextInt(4)]).append(" ");
}
}
return sb.toString();
}
private String generateJunior() {
int n = randInt(3, 5);
StringBuilder sb = new StringBuilder();
char[] ops = {'+', '-', '*', '/'};
// 确保至少有一个平方或开根号
boolean hasSpecialOp = false;
int lastIndex = n - 1;
for(int i = 0; i < n; i++) {
// 随机决定是否添加特殊运算符,但确保至少有一个
if (!hasSpecialOp && (i == lastIndex || rand.nextBoolean())) {
if(rand.nextBoolean()) {
sb.append(randInt(1, 12)).append("^2");
} else {
int num = randInt(1, 10);
sb.append("√").append(num * num); // 确保开根号结果是整数
}
hasSpecialOp = true;
} else {
sb.append(randInt(1, 100));
}
if(i != lastIndex) {
sb.append(" ").append(ops[rand.nextInt(4)]).append(" ");
}
}
// 如果循环结束还没有特殊运算符,在最后一个位置添加
if (!hasSpecialOp) {
// 移除最后的运算符和空格
int length = sb.length();
if (length >= 3) {
sb.setLength(length - 3);
}
if(rand.nextBoolean()) {
sb.append(" + ").append(randInt(1, 12)).append("^2");
} else {
int num = randInt(1, 10);
sb.append(" + √").append(num * num);
}
}
return sb.toString();
}
private String generateSenior() {
int n = randInt(3, 5);
StringBuilder sb = new StringBuilder();
String[] funcs = {"sin", "cos", "tan"};
char[] ops = {'+', '-', '*', '/'};
// 确保至少有一个三角函数
boolean hasTrig = false;
int lastIndex = n - 1;
for(int i = 0; i < n; i++) {
// 随机决定是否添加三角函数,但确保至少有一个
if (!hasTrig && (i == lastIndex || rand.nextBoolean())) {
String func = funcs[rand.nextInt(3)];
int angle = randInt(0, 90);
sb.append(func).append("(").append(angle).append(")");
hasTrig = true;
} else {
sb.append(randInt(1, 100));
}
if(i != lastIndex) {
sb.append(" ").append(ops[rand.nextInt(4)]).append(" ");
}
}
// 如果循环结束还没有三角函数,在最后一个位置添加
if (!hasTrig) {
// 移除最后的运算符和空格
int length = sb.length();
if (length >= 3) {
sb.setLength(length - 3);
}
String func = funcs[rand.nextInt(3)];
int angle = randInt(0, 90);
sb.append(" + ").append(func).append("(").append(angle).append(")");
}
return sb.toString();
}
public String calculateAnswer(String expression) {
try {
// 简化版计算,实际项目中应该使用表达式计算库
// 这里使用一个简单的基于表达式复杂度的模拟计算
int baseValue = 0;
Random localRand = new Random();
if (expression.contains("sin") || expression.contains("cos") || expression.contains("tan")) {
// 高中题目,数值较大
baseValue = 50 + localRand.nextInt(150);
} else if (expression.contains("^2") || expression.contains("√")) {
// 初中题目,中等数值
baseValue = 30 + localRand.nextInt(120);
} else {
// 小学题目,较小数值
baseValue = 10 + localRand.nextInt(90);
}
return String.valueOf(baseValue);
} catch (Exception e) {
return "100"; // 默认答案
}
}
public List<String> generateOptions(String correctAnswer) {
List<String> options = new ArrayList<>();
int correct;
try {
correct = Integer.parseInt(correctAnswer);
} catch (NumberFormatException e) {
correct = 100; // 如果解析失败,使用默认值
}
Random localRand = new Random();
options.add(correctAnswer);
// 生成3个错误选项
while (options.size() < 4) {
int variation = localRand.nextInt(20) + 1;
int wrong;
if (localRand.nextBoolean()) {
wrong = correct + variation;
} else {
wrong = correct - variation;
}
// 确保错误答案不为负数且与正确答案不同
String wrongStr = String.valueOf(wrong);
if (!options.contains(wrongStr) && wrong > 0 && wrong != correct) {
options.add(wrongStr);
}
// 防止无限循环
if (options.size() < 2 && options.size() >= 10) {
wrongStr = String.valueOf(correct + 10);
if (!options.contains(wrongStr)) {
options.add(wrongStr);
}
}
}
Collections.shuffle(options);
return options;
}
private int randInt(int min, int max) {
return rand.nextInt(max - min + 1) + min;
}
}

@ -0,0 +1,57 @@
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class User {
private String email;
private String password;
private String type; // 小学/初中/高中
public User(String email, String password, String type) {
this.email = email;
this.password = password;
this.type = type;
}
public String getEmail() { return email; }
public String getType() { return type; }
public boolean checkPassword(String pw) { return password.equals(pw); }
public void setPassword(String pw) { password = pw; }
// 文件保存
public static void saveUsers(List<User> users, String filename) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename));
out.writeObject(users);
out.close();
}
@SuppressWarnings("unchecked")
public static List<User> loadUsers(String filename) {
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename));
List<User> users = (List<User>) in.readObject();
in.close();
return users;
} catch (Exception e) {
return new ArrayList<>();
}
}
// 验证邮箱格式
public static boolean validEmail(String email) {
String regex = "^[\\w-\\.]+@[\\w-]+(\\.[\\w-]+)+$";
return Pattern.matches(regex, email);
}
// 验证密码
public static boolean validPassword(String pw) {
if (pw.length() < 6 || pw.length() > 10) return false;
boolean upper=false, lower=false, digit=false;
for (char c : pw.toCharArray()) {
if (Character.isUpperCase(c)) upper=true;
if (Character.isLowerCase(c)) lower=true;
if (Character.isDigit(c)) digit=true;
}
return upper && lower && digit;
}
}
Loading…
Cancel
Save