Compare commits
6 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
e437465b09 | 3 months ago |
|
|
c5a93ebab3 | 3 months ago |
|
|
6e6d44ec97 | 3 months ago |
|
|
ef2d9b7baa | 3 months ago |
|
|
a1b65ae655 | 3 months ago |
|
|
35d6dfe82b | 3 months ago |
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GoogleJavaFormatSettings">
|
||||
<option name="enabled" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@ -1,38 +1,36 @@
|
||||
package com.pair;// src/main/java/com/pair/Test.java
|
||||
|
||||
package com.pair; // src/main/java/com/pair/Test.java
|
||||
|
||||
import com.pair.ui.MainWindow;
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Test extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
try {
|
||||
System.out.println("✅ 正在初始化 MainWindow...");
|
||||
MainWindow mainWindow = new MainWindow(primaryStage);
|
||||
Scene scene = new Scene(mainWindow, 1366, 786);
|
||||
primaryStage.setTitle("中小学数学答题系统");
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.setResizable(true);
|
||||
primaryStage.show();
|
||||
System.out.println("✅ 应用启动成功!");
|
||||
} catch (Throwable e) { // 捕获所有错误,包括 NoClassDefFoundError
|
||||
System.err.println("❌ 启动失败,异常信息:");
|
||||
e.printStackTrace();
|
||||
// 暂停 15 秒,防止窗口关闭
|
||||
try {
|
||||
Thread.sleep(15000);
|
||||
} catch (InterruptedException ignored) {}
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
launch(args);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
launch(args);
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
try {
|
||||
System.out.println("✅ 正在初始化 MainWindow...");
|
||||
MainWindow mainWindow = new MainWindow(primaryStage);
|
||||
Scene scene = new Scene(mainWindow, 1366, 786);
|
||||
primaryStage.setTitle("中小学数学答题系统");
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.setResizable(true);
|
||||
primaryStage.show();
|
||||
System.out.println("✅ 应用启动成功!");
|
||||
} catch (Throwable e) { // 捕获所有错误,包括 NoClassDefFoundError
|
||||
System.err.println("❌ 启动失败,异常信息:");
|
||||
e.printStackTrace();
|
||||
// 暂停 15 秒,防止窗口关闭
|
||||
try {
|
||||
Thread.sleep(15000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,28 @@
|
||||
package com.pair.model;
|
||||
|
||||
|
||||
public enum Grade {
|
||||
// 枚举常量,初始化时传入对应的中文描述
|
||||
ELEMENTARY("小学"),
|
||||
MIDDLE("初中"),
|
||||
HIGH("高中");
|
||||
// 枚举常量,初始化时传入对应的中文描述
|
||||
ELEMENTARY("小学"),
|
||||
MIDDLE("初中"),
|
||||
HIGH("高中");
|
||||
|
||||
private final String chineseName;
|
||||
private final String chineseName;
|
||||
|
||||
Grade(String chineseName) {
|
||||
this.chineseName = chineseName;
|
||||
}
|
||||
Grade(String chineseName) {
|
||||
this.chineseName = chineseName;
|
||||
}
|
||||
|
||||
public String getChineseName() {
|
||||
return chineseName;
|
||||
public static Grade valueOfChinese(String chineseName) {
|
||||
// 遍历所有枚举常量,匹配中文描述
|
||||
for (Grade grade : Grade.values()) {
|
||||
if (grade.chineseName.equals(chineseName)) {
|
||||
return grade;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("不存在对应的年级:" + chineseName);
|
||||
}
|
||||
|
||||
public static Grade valueOfChinese(String chineseName) {
|
||||
// 遍历所有枚举常量,匹配中文描述
|
||||
for (Grade grade : Grade.values()) {
|
||||
if (grade.chineseName.equals(chineseName)) {
|
||||
return grade;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("不存在对应的年级:" + chineseName);
|
||||
}
|
||||
public String getChineseName() {
|
||||
return chineseName;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,58 +1,55 @@
|
||||
package com.pair.model;
|
||||
|
||||
|
||||
//答题结果
|
||||
// 答题结果
|
||||
public class QuizResult {
|
||||
|
||||
private int totalQuestions; // 总题数
|
||||
private int correctCount; // 正确题数
|
||||
private int wrongCount; // 错误题数
|
||||
private int score; // 得分
|
||||
|
||||
|
||||
public QuizResult(int totalQuestions, int correctCount, int wrongCount, int score) {
|
||||
this.totalQuestions = totalQuestions;
|
||||
this.correctCount = correctCount;
|
||||
this.wrongCount = wrongCount;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
|
||||
public int getTotalQuestions() {
|
||||
return totalQuestions;
|
||||
}
|
||||
|
||||
public void setTotalQuestions(int totalQuestions) {
|
||||
this.totalQuestions = totalQuestions;
|
||||
}
|
||||
|
||||
public int getCorrectCount() {
|
||||
return correctCount;
|
||||
}
|
||||
|
||||
public void setCorrectCount(int correctCount) {
|
||||
this.correctCount = correctCount;
|
||||
}
|
||||
|
||||
public int getWrongCount() {
|
||||
return wrongCount;
|
||||
}
|
||||
|
||||
public void setWrongCount(int wrongCount) {
|
||||
this.wrongCount = wrongCount;
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public void setScore(int score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
int correctPercent = (int) ((double) correctCount / totalQuestions * 100);
|
||||
return "您答对了" + correctCount + "/" + totalQuestions + "题,得分:" + correctPercent;
|
||||
}
|
||||
}
|
||||
private int totalQuestions; // 总题数
|
||||
private int correctCount; // 正确题数
|
||||
private int wrongCount; // 错误题数
|
||||
private int score; // 得分
|
||||
|
||||
public QuizResult(int totalQuestions, int correctCount, int wrongCount, int score) {
|
||||
this.totalQuestions = totalQuestions;
|
||||
this.correctCount = correctCount;
|
||||
this.wrongCount = wrongCount;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public int getTotalQuestions() {
|
||||
return totalQuestions;
|
||||
}
|
||||
|
||||
public void setTotalQuestions(int totalQuestions) {
|
||||
this.totalQuestions = totalQuestions;
|
||||
}
|
||||
|
||||
public int getCorrectCount() {
|
||||
return correctCount;
|
||||
}
|
||||
|
||||
public void setCorrectCount(int correctCount) {
|
||||
this.correctCount = correctCount;
|
||||
}
|
||||
|
||||
public int getWrongCount() {
|
||||
return wrongCount;
|
||||
}
|
||||
|
||||
public void setWrongCount(int wrongCount) {
|
||||
this.wrongCount = wrongCount;
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public void setScore(int score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
int correctPercent = (int) ((double) correctCount / totalQuestions * 100);
|
||||
return "您答对了" + correctCount + "/" + totalQuestions + "题,得分:" + correctPercent;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,278 +1,273 @@
|
||||
package com.pair.service;
|
||||
|
||||
|
||||
import com.pair.model.*;
|
||||
import com.pair.util.AppDataDirectory;
|
||||
import com.pair.util.FileUtils;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.QuizHistory;
|
||||
import com.pair.model.User;
|
||||
import com.pair.util.AppDataDirectory;
|
||||
import com.pair.util.FileUtils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 文件IO服务
|
||||
* 负责所有数据的读写操作
|
||||
*/
|
||||
/** 文件IO服务 负责所有数据的读写操作 */
|
||||
public class FileIOService {
|
||||
|
||||
private static final String DATA_DIR = AppDataDirectory.getFullPath("data");
|
||||
private static final String USERS_DIR = AppDataDirectory.getFullPath("data/users");
|
||||
private static final String HISTORY_DIR = AppDataDirectory.getFullPath("data/history");
|
||||
private static final String DATA_DIR = AppDataDirectory.getFullPath("data");
|
||||
private static final String USERS_DIR = AppDataDirectory.getFullPath("data/users");
|
||||
private static final String HISTORY_DIR = AppDataDirectory.getFullPath("data/history");
|
||||
|
||||
private static final String REGISTRATION_CODES_FILE = AppDataDirectory.getFullPath("data/registration_codes.json");
|
||||
private static final String USERS_FILE = AppDataDirectory.getFullPath("data/users.json");
|
||||
private static final String CURRENT_USER_FILE = AppDataDirectory.getFullPath("data/current_user.json");
|
||||
private static final String REGISTRATION_CODES_FILE =
|
||||
AppDataDirectory.getFullPath("data/registration_codes.json");
|
||||
private static final String USERS_FILE = AppDataDirectory.getFullPath("data/users.json");
|
||||
private static final String CURRENT_USER_FILE =
|
||||
AppDataDirectory.getFullPath("data/current_user.json");
|
||||
|
||||
private static final Gson gson = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.setDateFormat("yyyy-MM-dd HH:mm:ss")
|
||||
.create();
|
||||
private static final Gson gson =
|
||||
new GsonBuilder().setPrettyPrinting().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
|
||||
|
||||
// ==================== 初始化 ====================
|
||||
// ==================== 初始化 ====================
|
||||
|
||||
public FileIOService() throws IOException {
|
||||
initDataDirectory();
|
||||
}
|
||||
|
||||
public void initDataDirectory() throws IOException {
|
||||
FileUtils.createDirectoryIfNotExists(DATA_DIR);
|
||||
FileUtils.createDirectoryIfNotExists(HISTORY_DIR);
|
||||
FileUtils.ensureFileExists(REGISTRATION_CODES_FILE);
|
||||
public FileIOService() throws IOException {
|
||||
initDataDirectory();
|
||||
}
|
||||
|
||||
if (!FileUtils.exists(USERS_FILE)) {
|
||||
Map<String, List<User>> data = new HashMap<>();
|
||||
data.put("users", new ArrayList<>());
|
||||
FileUtils.saveAsJson(data, USERS_FILE);
|
||||
}
|
||||
public void initDataDirectory() throws IOException {
|
||||
FileUtils.createDirectoryIfNotExists(DATA_DIR);
|
||||
FileUtils.createDirectoryIfNotExists(HISTORY_DIR);
|
||||
FileUtils.ensureFileExists(REGISTRATION_CODES_FILE);
|
||||
|
||||
System.out.println("✓ 数据目录初始化完成");
|
||||
if (!FileUtils.exists(USERS_FILE)) {
|
||||
Map<String, List<User>> data = new HashMap<>();
|
||||
data.put("users", new ArrayList<>());
|
||||
FileUtils.saveAsJson(data, USERS_FILE);
|
||||
}
|
||||
|
||||
// ==================== 用户操作 ====================
|
||||
System.out.println("✓ 数据目录初始化完成");
|
||||
}
|
||||
|
||||
public void saveUser(User user) throws IOException {
|
||||
Type type = new TypeToken<Map<String, List<User>>>(){}.getType();
|
||||
Map<String, List<User>> data = FileUtils.readJsonToObject(USERS_FILE, type);
|
||||
// ==================== 用户操作 ====================
|
||||
|
||||
List<User> users = data.get("users");
|
||||
public void saveUser(User user) throws IOException {
|
||||
Type type = new TypeToken<Map<String, List<User>>>() {}.getType();
|
||||
Map<String, List<User>> data = FileUtils.readJsonToObject(USERS_FILE, type);
|
||||
|
||||
boolean found = false;
|
||||
for (int i = 0; i < users.size(); i++) {
|
||||
if (users.get(i).getUserId().equals(user.getUserId())) {
|
||||
users.set(i, user);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
List<User> users = data.get("users");
|
||||
|
||||
if (!found) {
|
||||
users.add(user);
|
||||
}
|
||||
|
||||
FileUtils.saveAsJson(data, USERS_FILE);
|
||||
boolean found = false;
|
||||
for (int i = 0; i < users.size(); i++) {
|
||||
if (users.get(i).getUserId().equals(user.getUserId())) {
|
||||
users.set(i, user);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public List<User> loadAllUsers() throws IOException {
|
||||
if (!FileUtils.exists(USERS_FILE)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Type type = new TypeToken<Map<String, List<User>>>(){}.getType();
|
||||
Map<String, List<User>> data = FileUtils.readJsonToObject(USERS_FILE, type);
|
||||
|
||||
return data.get("users");
|
||||
if (!found) {
|
||||
users.add(user);
|
||||
}
|
||||
|
||||
public User findUserByUsername(String username) throws IOException {
|
||||
List<User> users = loadAllUsers();
|
||||
FileUtils.saveAsJson(data, USERS_FILE);
|
||||
}
|
||||
|
||||
for (User user : users) {
|
||||
if (user.getUsername().equals(username)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean existsUsername(String username) throws IOException {
|
||||
return findUserByUsername(username) != null;
|
||||
public List<User> loadAllUsers() throws IOException {
|
||||
if (!FileUtils.exists(USERS_FILE)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public User findUserByEmail(String email) throws IOException {
|
||||
List<User> users = loadAllUsers();
|
||||
|
||||
for (User user : users) {
|
||||
if (user.getEmail().equals(email)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
Type type = new TypeToken<Map<String, List<User>>>() {}.getType();
|
||||
Map<String, List<User>> data = FileUtils.readJsonToObject(USERS_FILE, type);
|
||||
|
||||
return null;
|
||||
}
|
||||
return data.get("users");
|
||||
}
|
||||
|
||||
public boolean existsEmail(String email) throws IOException {
|
||||
return findUserByEmail(email) != null;
|
||||
}
|
||||
public User findUserByUsername(String username) throws IOException {
|
||||
List<User> users = loadAllUsers();
|
||||
|
||||
public boolean isUsernameExists(String username) throws IOException {
|
||||
return findUserByUsername(username) != null;
|
||||
for (User user : users) {
|
||||
if (user.getUsername().equals(username)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmailExists(String email) throws IOException {
|
||||
return findUserByEmail(email) != null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void saveCurrentUser(User user) throws IOException {
|
||||
FileUtils.saveAsJson(user, CURRENT_USER_FILE);
|
||||
}
|
||||
public boolean existsUsername(String username) throws IOException {
|
||||
return findUserByUsername(username) != null;
|
||||
}
|
||||
|
||||
public User loadCurrentUser() throws IOException {
|
||||
if (!FileUtils.exists(CURRENT_USER_FILE)) {
|
||||
return null;
|
||||
}
|
||||
return FileUtils.readJsonToObject(CURRENT_USER_FILE, User.class);
|
||||
}
|
||||
public User findUserByEmail(String email) throws IOException {
|
||||
List<User> users = loadAllUsers();
|
||||
|
||||
public void clearCurrentUser() {
|
||||
FileUtils.deleteFile(CURRENT_USER_FILE);
|
||||
for (User user : users) {
|
||||
if (user.getEmail().equals(email)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 答题历史操作 ====================
|
||||
|
||||
public void saveQuizHistory(QuizHistory history) throws IOException {
|
||||
|
||||
String filename = HISTORY_DIR + "/" +
|
||||
sanitizeFilename(history.getUsername()) + "/" +
|
||||
System.currentTimeMillis() + ".txt";
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder content = new StringBuilder();
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
public boolean existsEmail(String email) throws IOException {
|
||||
return findUserByEmail(email) != null;
|
||||
}
|
||||
|
||||
content.append("========== 答题记录 ==========\n");
|
||||
content.append("用户:").append(history.getUsername()).append("\n");
|
||||
content.append("时间:").append(dateFormat.format(history.getTimestamp())).append("\n");
|
||||
content.append("总分:").append(history.getScore()).append(" 分\n");
|
||||
public boolean isUsernameExists(String username) throws IOException {
|
||||
return findUserByUsername(username) != null;
|
||||
}
|
||||
|
||||
// 调用 QuizService 的业务方法计算正确数和错误数
|
||||
int correctCount = calculateCorrectCount(history);
|
||||
int wrongCount = history.getQuestions().size() - correctCount;
|
||||
public boolean isEmailExists(String email) throws IOException {
|
||||
return findUserByEmail(email) != null;
|
||||
}
|
||||
|
||||
content.append("正确:").append(correctCount).append(" 题 ");
|
||||
content.append("错误:").append(wrongCount).append(" 题\n");
|
||||
content.append("=============================\n\n");
|
||||
public void saveCurrentUser(User user) throws IOException {
|
||||
FileUtils.saveAsJson(user, CURRENT_USER_FILE);
|
||||
}
|
||||
|
||||
List<ChoiceQuestion> questions = history.getQuestions();
|
||||
List<Integer> userAnswers = history.getUserAnswers();
|
||||
|
||||
for (int i = 0; i < questions.size(); i++) {
|
||||
ChoiceQuestion q = questions.get(i);
|
||||
Integer userAnswer = userAnswers.get(i);
|
||||
|
||||
content.append("【题目 ").append(i + 1).append("】\n");
|
||||
content.append(q.getQuestionText()).append("\n");
|
||||
public User loadCurrentUser() throws IOException {
|
||||
if (!FileUtils.exists(CURRENT_USER_FILE)) {
|
||||
return null;
|
||||
}
|
||||
return FileUtils.readJsonToObject(CURRENT_USER_FILE, User.class);
|
||||
}
|
||||
|
||||
public void clearCurrentUser() {
|
||||
FileUtils.deleteFile(CURRENT_USER_FILE);
|
||||
}
|
||||
|
||||
// ==================== 答题历史操作 ====================
|
||||
|
||||
public void saveQuizHistory(QuizHistory history) throws IOException {
|
||||
|
||||
String filename =
|
||||
HISTORY_DIR
|
||||
+ "/"
|
||||
+ sanitizeFilename(history.getUsername())
|
||||
+ "/"
|
||||
+ System.currentTimeMillis()
|
||||
+ ".txt";
|
||||
|
||||
StringBuilder content = new StringBuilder();
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
content.append("========== 答题记录 ==========\n");
|
||||
content.append("用户:").append(history.getUsername()).append("\n");
|
||||
content.append("时间:").append(dateFormat.format(history.getTimestamp())).append("\n");
|
||||
content.append("总分:").append(history.getScore()).append(" 分\n");
|
||||
|
||||
// 调用 QuizService 的业务方法计算正确数和错误数
|
||||
int correctCount = calculateCorrectCount(history);
|
||||
int wrongCount = history.getQuestions().size() - correctCount;
|
||||
|
||||
content.append("正确:").append(correctCount).append(" 题 ");
|
||||
content.append("错误:").append(wrongCount).append(" 题\n");
|
||||
content.append("=============================\n\n");
|
||||
|
||||
List<ChoiceQuestion> questions = history.getQuestions();
|
||||
List<Integer> userAnswers = history.getUserAnswers();
|
||||
|
||||
for (int i = 0; i < questions.size(); i++) {
|
||||
ChoiceQuestion q = questions.get(i);
|
||||
Integer userAnswer = userAnswers.get(i);
|
||||
|
||||
content.append("【题目 ").append(i + 1).append("】\n");
|
||||
content.append(q.getQuestionText()).append("\n");
|
||||
|
||||
List<?> options = q.getOptions();
|
||||
for (int j = 0; j < options.size(); j++) {
|
||||
content.append((char) ('A' + j)).append(". ").append(options.get(j)).append(" ");
|
||||
}
|
||||
content.append("\n");
|
||||
|
||||
int correctIndex = getCorrectAnswerIndex(q);
|
||||
content.append("正确答案:").append((char) ('A' + correctIndex)).append("\n");
|
||||
|
||||
content.append("用户答案:");
|
||||
if (userAnswer != null) {
|
||||
content.append((char) ('A' + userAnswer));
|
||||
} else {
|
||||
content.append("未作答");
|
||||
}
|
||||
content.append("\n");
|
||||
|
||||
boolean isCorrect = (userAnswer != null && userAnswer == correctIndex);
|
||||
content.append("结果:").append(isCorrect ? "✓ 正确" : "✗ 错误").append("\n\n");
|
||||
}
|
||||
|
||||
List<?> options = q.getOptions();
|
||||
for (int j = 0; j < options.size(); j++) {
|
||||
content.append((char)('A' + j)).append(". ")
|
||||
.append(options.get(j)).append(" ");
|
||||
}
|
||||
content.append("\n");
|
||||
FileUtils.writeStringToFile(filename, content.toString());
|
||||
}
|
||||
|
||||
int correctIndex = getCorrectAnswerIndex(q);
|
||||
content.append("正确答案:").append((char)('A' + correctIndex)).append("\n");
|
||||
public List<String> getHistoryQuestions(String username) throws IOException {
|
||||
List<String> historyQuestions = new ArrayList<>();
|
||||
File[] files = FileUtils.listFiles(HISTORY_DIR + "/" + username);
|
||||
|
||||
content.append("用户答案:");
|
||||
if (userAnswer != null) {
|
||||
content.append((char)('A' + userAnswer));
|
||||
} else {
|
||||
content.append("未作答");
|
||||
}
|
||||
content.append("\n");
|
||||
Arrays.sort(files, (f1, f2) -> Long.compare(f2.lastModified(), f1.lastModified()));
|
||||
|
||||
boolean isCorrect = (userAnswer != null && userAnswer == correctIndex);
|
||||
content.append("结果:").append(isCorrect ? "✓ 正确" : "✗ 错误").append("\n\n");
|
||||
}
|
||||
int count = 0;
|
||||
for (File file : files) {
|
||||
if (count++ >= 20) break;
|
||||
|
||||
FileUtils.writeStringToFile(filename, content.toString());
|
||||
}
|
||||
try {
|
||||
String content = FileUtils.readFileToString(file.getAbsolutePath());
|
||||
String[] lines = content.split("\n");
|
||||
|
||||
public List<String> getHistoryQuestions(String username) throws IOException {
|
||||
List<String> historyQuestions = new ArrayList<>();
|
||||
File[] files = FileUtils.listFiles(HISTORY_DIR + "/" + username);
|
||||
|
||||
Arrays.sort(files, (f1, f2) -> Long.compare(f2.lastModified(), f1.lastModified()));
|
||||
|
||||
int count = 0;
|
||||
for (File file : files) {
|
||||
if (count++ >= 20) break;
|
||||
|
||||
try {
|
||||
String content = FileUtils.readFileToString(file.getAbsolutePath());
|
||||
String[] lines = content.split("\n");
|
||||
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith("【题目")) {
|
||||
if (i + 1 < lines.length) {
|
||||
String questionText = lines[i + 1].trim();
|
||||
if (!questionText.isEmpty()) {
|
||||
historyQuestions.add(questionText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("读取历史文件失败: " + file.getName());
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith("【题目")) {
|
||||
if (i + 1 < lines.length) {
|
||||
String questionText = lines[i + 1].trim();
|
||||
if (!questionText.isEmpty()) {
|
||||
historyQuestions.add(questionText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return historyQuestions;
|
||||
} catch (IOException e) {
|
||||
System.err.println("读取历史文件失败: " + file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 业务逻辑方法(从 Model 移过来)====================
|
||||
return historyQuestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算答题历史的正确数
|
||||
*/
|
||||
private int calculateCorrectCount(QuizHistory history) {
|
||||
int count = 0;
|
||||
List<ChoiceQuestion> questions = history.getQuestions();
|
||||
List<Integer> userAnswers = history.getUserAnswers();
|
||||
// ==================== 业务逻辑方法(从 Model 移过来)====================
|
||||
|
||||
for (int i = 0; i < questions.size(); i++) {
|
||||
ChoiceQuestion question = questions.get(i);
|
||||
Integer userAnswer = userAnswers.get(i);
|
||||
/** 计算答题历史的正确数 */
|
||||
private int calculateCorrectCount(QuizHistory history) {
|
||||
int count = 0;
|
||||
List<ChoiceQuestion> questions = history.getQuestions();
|
||||
List<Integer> userAnswers = history.getUserAnswers();
|
||||
|
||||
if (userAnswer != null && userAnswer == getCorrectAnswerIndex(question)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
for (int i = 0; i < questions.size(); i++) {
|
||||
ChoiceQuestion question = questions.get(i);
|
||||
Integer userAnswer = userAnswers.get(i);
|
||||
|
||||
/**
|
||||
* 获取题目的正确答案索引
|
||||
*/
|
||||
private int getCorrectAnswerIndex(ChoiceQuestion question) {
|
||||
return question.getOptions().indexOf(question.getCorrectAnswer());
|
||||
if (userAnswer != null && userAnswer == getCorrectAnswerIndex(question)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
/** 获取题目的正确答案索引 */
|
||||
private int getCorrectAnswerIndex(ChoiceQuestion question) {
|
||||
return question.getOptions().indexOf(question.getCorrectAnswer());
|
||||
}
|
||||
|
||||
private String sanitizeFilename(String filename) {
|
||||
if (filename == null) {
|
||||
return "unknown";
|
||||
}
|
||||
return filename.replaceAll("[\\\\/:*?\"<>|]", "_");
|
||||
}
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
public String getRegistrationCodesFilePath() {
|
||||
return REGISTRATION_CODES_FILE;
|
||||
private String sanitizeFilename(String filename) {
|
||||
if (filename == null) {
|
||||
return "unknown";
|
||||
}
|
||||
return filename.replaceAll("[\\\\/:*?\"<>|]", "_");
|
||||
}
|
||||
|
||||
}
|
||||
public String getRegistrationCodesFilePath() {
|
||||
return REGISTRATION_CODES_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
package com.pair.service.question_generator.factory;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.question_generator.strategy.elementary.*;
|
||||
import com.pair.util.RandomUtils;
|
||||
import com.pair.service.question_generator.strategy.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 小学题目工厂
|
||||
*/
|
||||
public class ElementaryQuestionFactory implements QuestionFactory {
|
||||
|
||||
private final List<QuestionStrategy> strategies;
|
||||
|
||||
public ElementaryQuestionFactory() {
|
||||
strategies = new ArrayList<>();
|
||||
// 注册所有小学题目生成策略
|
||||
strategies.add(new AdditionStrategy());
|
||||
strategies.add(new SubtractionStrategy());
|
||||
strategies.add(new MultiplicationStrategy());
|
||||
strategies.add(new DivisionStrategy());
|
||||
strategies.add(new ParenthesesAddStrategy());
|
||||
strategies.add(new ParenthesesMultiplyStrategy());
|
||||
}
|
||||
|
||||
//重载接口方法
|
||||
@Override
|
||||
public ChoiceQuestion createQuestion() {
|
||||
// 从六个题型list中随机选择一个生成题目
|
||||
QuestionStrategy strategy = RandomUtils.randomChoice(strategies);
|
||||
return strategy.generate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Grade getSupportedGrade() {
|
||||
return Grade.ELEMENTARY;
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
package com.pair.service.question_generator.factory;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.question_generator.strategy.QuestionStrategy;
|
||||
import com.pair.service.question_generator.strategy.high.*;
|
||||
import com.pair.util.RandomUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 高中题目工厂
|
||||
*/
|
||||
public class HighQuestionFactory implements QuestionFactory {
|
||||
|
||||
private final List<QuestionStrategy> strategies;
|
||||
|
||||
public HighQuestionFactory() {
|
||||
strategies = new ArrayList<>();
|
||||
// 注册所有高中题目生成策略
|
||||
strategies.add(new SinStrategy());
|
||||
strategies.add(new CosStrategy());
|
||||
strategies.add(new TanStrategy());
|
||||
strategies.add(new TrigIdentityStrategy());
|
||||
strategies.add(new DerivativeStrategy());
|
||||
strategies.add(new ArithmeticSequenceSumStrategy());
|
||||
strategies.add(new LogarithmStrategy());
|
||||
strategies.add(new ProbabilityStrategy());
|
||||
strategies.add(new FunctionExtremeStrategy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion createQuestion() {
|
||||
QuestionStrategy strategy = RandomUtils.randomChoice(strategies);
|
||||
return strategy.generate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Grade getSupportedGrade() {
|
||||
return Grade.HIGH;
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
package com.pair.service.question_generator.factory;
|
||||
|
||||
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.question_generator.strategy.elementary.ParenthesesAddStrategy;
|
||||
import com.pair.service.question_generator.strategy.elementary.ParenthesesMultiplyStrategy;
|
||||
import com.pair.service.question_generator.strategy.middle.*;
|
||||
import com.pair.util.RandomUtils;
|
||||
import com.pair.service.question_generator.strategy.QuestionStrategy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 初中题目工厂
|
||||
*/
|
||||
public class MiddleQuestionFactory implements QuestionFactory {
|
||||
|
||||
private final List<QuestionStrategy> strategies;
|
||||
|
||||
public MiddleQuestionFactory() {
|
||||
strategies = new ArrayList<>();
|
||||
// 注册所有初中题目生成策略
|
||||
strategies.add(new SquareStrategy());
|
||||
strategies.add(new SquareAddStrategy());
|
||||
strategies.add(new SqrtStrategy());
|
||||
strategies.add(new SqrtAddStrategy());
|
||||
strategies.add(new MixedSquareSqrtStrategy());
|
||||
strategies.add(new ParenthesesAddStrategy());
|
||||
strategies.add(new ParenthesesMultiplyStrategy());
|
||||
strategies.add(new LinearEquationStrategy());
|
||||
strategies.add(new QuadraticEquationStrategy());
|
||||
strategies.add(new TriangleAreaStrategy());
|
||||
strategies.add(new CircleAreaStrategy());
|
||||
strategies.add(new LinearFunctionStrategy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion createQuestion() {
|
||||
QuestionStrategy strategy = RandomUtils.randomChoice(strategies);
|
||||
return strategy.generate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Grade getSupportedGrade() {
|
||||
return Grade.MIDDLE;
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package com.pair.service.question_generator.factory;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
|
||||
|
||||
/**
|
||||
* 题目工厂接口
|
||||
*/
|
||||
public interface QuestionFactory {
|
||||
|
||||
//创建题目
|
||||
ChoiceQuestion createQuestion();
|
||||
|
||||
//获取工厂支持的学段
|
||||
Grade getSupportedGrade();
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
package com.pair.service.question_generator.strategy;
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
|
||||
//题目生成题型接口
|
||||
public interface QuestionStrategy {
|
||||
|
||||
//生成题目
|
||||
ChoiceQuestion generate();
|
||||
|
||||
//题型
|
||||
String getStrategyName();
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
package com.pair.service.question_generator.strategy.elementary;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
|
||||
import com.pair.service.question_generator.strategy.AbstractQuestionStrategy;
|
||||
import com.pair.util.RandomUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Addition strategy for generating addition questions. */
|
||||
public class AdditionStrategy extends AbstractQuestionStrategy {
|
||||
|
||||
public AdditionStrategy() {
|
||||
super(Grade.ELEMENTARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion generate() {
|
||||
int num1 = RandomUtils.nextInt(1, 30);
|
||||
int num2 = RandomUtils.nextInt(1, 30);
|
||||
|
||||
String questionText = num1 + " + " + num2;
|
||||
double answer = num1 + num2;
|
||||
|
||||
List<Double> options = generateNumericOptions(answer);
|
||||
|
||||
return new ChoiceQuestion(questionText, answer, options, grade);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStrategyName() {
|
||||
return "Addition";
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package com.pair.service.question_generator.strategy.elementary;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.question_generator.strategy.AbstractQuestionStrategy;
|
||||
import com.pair.util.RandomUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Subtraction strategy for generating subtraction questions. */
|
||||
public class SubtractionStrategy extends AbstractQuestionStrategy {
|
||||
|
||||
public SubtractionStrategy() {
|
||||
super(Grade.ELEMENTARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion generate() {
|
||||
int num1 = RandomUtils.nextInt(1, 30);
|
||||
int num2 = RandomUtils.nextInt(1, 30);
|
||||
|
||||
// Ensure positive result
|
||||
int larger = Math.max(num1, num2);
|
||||
int smaller = Math.min(num1, num2);
|
||||
|
||||
String questionText = larger + " - " + smaller;
|
||||
double answer = larger - smaller;
|
||||
|
||||
List<Double> options = generateNumericOptions(answer);
|
||||
|
||||
return new ChoiceQuestion(questionText, answer, options, grade);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStrategyName() {
|
||||
return "Subtraction";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.pair.service.questiongenerator.factory;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.questiongenerator.strategy.QuestionStrategy;
|
||||
import com.pair.service.questiongenerator.strategy.elementary.*;
|
||||
import com.pair.util.RandomUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** 小学题目工厂 */
|
||||
public class ElementaryQuestionFactory implements QuestionFactory {
|
||||
|
||||
private final List<QuestionStrategy> strategies;
|
||||
|
||||
public ElementaryQuestionFactory() {
|
||||
strategies = new ArrayList<>();
|
||||
// 注册所有小学题目生成策略
|
||||
strategies.add(new AdditionStrategy());
|
||||
strategies.add(new SubtractionStrategy());
|
||||
strategies.add(new MultiplicationStrategy());
|
||||
strategies.add(new DivisionStrategy());
|
||||
strategies.add(new ParenthesesAddStrategy());
|
||||
strategies.add(new ParenthesesMultiplyStrategy());
|
||||
}
|
||||
|
||||
// 重载接口方法
|
||||
@Override
|
||||
public ChoiceQuestion createQuestion() {
|
||||
// 从六个题型list中随机选择一个生成题目
|
||||
QuestionStrategy strategy = RandomUtils.randomChoice(strategies);
|
||||
return strategy.generate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Grade getSupportedGrade() {
|
||||
return Grade.ELEMENTARY;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.pair.service.questiongenerator.factory;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.questiongenerator.strategy.QuestionStrategy;
|
||||
import com.pair.service.questiongenerator.strategy.high.*;
|
||||
import com.pair.util.RandomUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** 高中题目工厂 */
|
||||
public class HighQuestionFactory implements QuestionFactory {
|
||||
|
||||
private final List<QuestionStrategy> strategies;
|
||||
|
||||
public HighQuestionFactory() {
|
||||
strategies = new ArrayList<>();
|
||||
// 注册所有高中题目生成策略
|
||||
strategies.add(new SinStrategy());
|
||||
strategies.add(new CosStrategy());
|
||||
strategies.add(new TanStrategy());
|
||||
strategies.add(new TrigIdentityStrategy());
|
||||
strategies.add(new DerivativeStrategy());
|
||||
strategies.add(new ArithmeticSequenceSumStrategy());
|
||||
strategies.add(new LogarithmStrategy());
|
||||
strategies.add(new ProbabilityStrategy());
|
||||
strategies.add(new FunctionExtremeStrategy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion createQuestion() {
|
||||
QuestionStrategy strategy = RandomUtils.randomChoice(strategies);
|
||||
return strategy.generate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Grade getSupportedGrade() {
|
||||
return Grade.HIGH;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.pair.service.questiongenerator.factory;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.questiongenerator.strategy.QuestionStrategy;
|
||||
import com.pair.service.questiongenerator.strategy.elementary.ParenthesesAddStrategy;
|
||||
import com.pair.service.questiongenerator.strategy.elementary.ParenthesesMultiplyStrategy;
|
||||
import com.pair.service.questiongenerator.strategy.middle.*;
|
||||
import com.pair.util.RandomUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** 初中题目工厂 */
|
||||
public class MiddleQuestionFactory implements QuestionFactory {
|
||||
|
||||
private final List<QuestionStrategy> strategies;
|
||||
|
||||
public MiddleQuestionFactory() {
|
||||
strategies = new ArrayList<>();
|
||||
// 注册所有初中题目生成策略
|
||||
strategies.add(new SquareStrategy());
|
||||
strategies.add(new SquareAddStrategy());
|
||||
strategies.add(new SqrtStrategy());
|
||||
strategies.add(new SqrtAddStrategy());
|
||||
strategies.add(new MixedSquareSqrtStrategy());
|
||||
strategies.add(new ParenthesesAddStrategy());
|
||||
strategies.add(new ParenthesesMultiplyStrategy());
|
||||
strategies.add(new LinearEquationStrategy());
|
||||
strategies.add(new QuadraticEquationStrategy());
|
||||
strategies.add(new TriangleAreaStrategy());
|
||||
strategies.add(new CircleAreaStrategy());
|
||||
strategies.add(new LinearFunctionStrategy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion createQuestion() {
|
||||
QuestionStrategy strategy = RandomUtils.randomChoice(strategies);
|
||||
return strategy.generate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Grade getSupportedGrade() {
|
||||
return Grade.MIDDLE;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package com.pair.service.questiongenerator.strategy.elementary;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.questiongenerator.strategy.AbstractQuestionStrategy;
|
||||
import com.pair.util.RandomUtils;
|
||||
import java.util.List;
|
||||
|
||||
/** Addition strategy for generating addition questions. */
|
||||
public class AdditionStrategy extends AbstractQuestionStrategy {
|
||||
|
||||
public AdditionStrategy() {
|
||||
super(Grade.ELEMENTARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion generate() {
|
||||
int num1 = RandomUtils.nextInt(1, 30);
|
||||
int num2 = RandomUtils.nextInt(1, 30);
|
||||
|
||||
String questionText = num1 + " + " + num2;
|
||||
double answer = num1 + num2;
|
||||
|
||||
List<Double> options = generateNumericOptions(answer);
|
||||
|
||||
return new ChoiceQuestion(questionText, answer, options, grade);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStrategyName() {
|
||||
return "Addition";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.pair.service.questiongenerator.strategy.elementary;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.questiongenerator.strategy.AbstractQuestionStrategy;
|
||||
import com.pair.util.RandomUtils;
|
||||
import java.util.List;
|
||||
|
||||
/** 除法策略(确保整除) */
|
||||
public class DivisionStrategy extends AbstractQuestionStrategy {
|
||||
|
||||
public DivisionStrategy() {
|
||||
super(Grade.ELEMENTARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion generate() {
|
||||
int divisor = RandomUtils.nextInt(2, 10);
|
||||
int quotient = RandomUtils.nextInt(1, 10);
|
||||
int dividend = divisor * quotient;
|
||||
|
||||
String questionText = dividend + " ÷ " + divisor;
|
||||
double answer = quotient;
|
||||
|
||||
List<Double> options = generateNumericOptions(answer);
|
||||
|
||||
return new ChoiceQuestion(questionText, answer, options, grade);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStrategyName() {
|
||||
return "除法";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.pair.service.questiongenerator.strategy.elementary;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.questiongenerator.strategy.AbstractQuestionStrategy;
|
||||
import com.pair.util.RandomUtils;
|
||||
import java.util.List;
|
||||
|
||||
/** Subtraction strategy for generating subtraction questions. */
|
||||
public class SubtractionStrategy extends AbstractQuestionStrategy {
|
||||
|
||||
public SubtractionStrategy() {
|
||||
super(Grade.ELEMENTARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion generate() {
|
||||
int num1 = RandomUtils.nextInt(1, 30);
|
||||
int num2 = RandomUtils.nextInt(1, 30);
|
||||
|
||||
// Ensure positive result
|
||||
int larger = Math.max(num1, num2);
|
||||
int smaller = Math.min(num1, num2);
|
||||
|
||||
String questionText = larger + " - " + smaller;
|
||||
double answer = larger - smaller;
|
||||
|
||||
List<Double> options = generateNumericOptions(answer);
|
||||
|
||||
return new ChoiceQuestion(questionText, answer, options, grade);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStrategyName() {
|
||||
return "Subtraction";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.pair.service.questiongenerator.strategy.middle;
|
||||
|
||||
import com.pair.model.ChoiceQuestion;
|
||||
import com.pair.model.Grade;
|
||||
import com.pair.service.questiongenerator.strategy.AbstractQuestionStrategy;
|
||||
import com.pair.util.RandomUtils;
|
||||
import java.util.List;
|
||||
|
||||
/** 圆的面积策略 例如:半径为 5cm 的圆,面积是多少?(π取3.14) */
|
||||
public class CircleAreaStrategy extends AbstractQuestionStrategy {
|
||||
|
||||
public CircleAreaStrategy() {
|
||||
super(Grade.MIDDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoiceQuestion generate() {
|
||||
int radius = RandomUtils.nextInt(3, 10);
|
||||
|
||||
String questionText = "半径为 " + radius + "cm 的圆,面积是多少?(π取3.14)";
|
||||
double answer = 3.14 * radius * radius;
|
||||
|
||||
List<Double> options = generateNumericOptions(answer);
|
||||
|
||||
return new ChoiceQuestion(questionText, answer, options, grade);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStrategyName() {
|
||||
return "圆的面积";
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
package com.pair.ui;
|
||||
|
||||
public enum Panel {
|
||||
START, // 开始页面
|
||||
LOGIN, // 登录页面
|
||||
REGISTER, // 注册页面
|
||||
INF_GEN, // 个人信息+生成题目页面
|
||||
PASSWORDMODIFY, // 修改密码页面
|
||||
QUIZ, // 答题页面
|
||||
RESULT // 得分页面
|
||||
START, // 开始页面
|
||||
LOGIN, // 登录页面
|
||||
REGISTER, // 注册页面
|
||||
INF_GEN, // 个人信息+生成题目页面
|
||||
PASSWORDMODIFY, // 修改密码页面
|
||||
QUIZ, // 答题页面
|
||||
RESULT // 得分页面
|
||||
}
|
||||
|
||||
@ -1,153 +1,152 @@
|
||||
/* com/ui/StyleHelper.java */
|
||||
package com.pair.ui;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.Spinner;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
public class StyleHelper {
|
||||
/** 给按钮一次性加上通用样式、hover、pressed 动画 - 兼容方法 */
|
||||
public static void styleButton(Button btn) {
|
||||
btn.setStyle(UIConstants.BTN_NORMAL);
|
||||
// 仅背景深浅变化,字体、圆角始终一致
|
||||
btn.setOnMouseEntered(e -> btn.setStyle(UIConstants.BTN_HOVER));
|
||||
btn.setOnMouseExited(e -> btn.setStyle(UIConstants.BTN_NORMAL));
|
||||
btn.setOnMousePressed(e -> btn.setStyle(UIConstants.BTN_PRESSED));
|
||||
btn.setOnMouseReleased(e -> btn.setStyle(UIConstants.BTN_NORMAL));
|
||||
btn.setPrefSize(UIConstants.BTN_WIDTH, UIConstants.BTN_HEIGHT);
|
||||
}
|
||||
|
||||
/** 新的统一按钮样式方法 - 支持不同类型按钮 */
|
||||
public static void styleButton(Button btn, String type) {
|
||||
btn.setStyle(UIConstants.getButtonNormalStyle(type));
|
||||
btn.setOnMouseEntered(e -> btn.setStyle(UIConstants.getButtonHoverStyle(type)));
|
||||
btn.setOnMouseExited(e -> btn.setStyle(UIConstants.getButtonNormalStyle(type)));
|
||||
btn.setOnMousePressed(e -> btn.setStyle(UIConstants.getButtonPressedStyle(type)));
|
||||
btn.setOnMouseReleased(e -> btn.setStyle(UIConstants.getButtonNormalStyle(type)));
|
||||
|
||||
// 根据按钮类型设置尺寸
|
||||
switch (type) {
|
||||
case UIConstants.BTN_TYPE_PRIMARY:
|
||||
case UIConstants.BTN_TYPE_WARNING:
|
||||
btn.setPrefSize(UIConstants.BTN_LARGE_WIDTH, UIConstants.BTN_LARGE_HEIGHT);
|
||||
break;
|
||||
case UIConstants.BTN_TYPE_SUCCESS:
|
||||
case UIConstants.BTN_TYPE_ERROR:
|
||||
case UIConstants.BTN_TYPE_INFO:
|
||||
btn.setPrefSize(UIConstants.BTN_MEDIUM_WIDTH, UIConstants.BTN_MEDIUM_HEIGHT);
|
||||
break;
|
||||
default:
|
||||
btn.setPrefSize(UIConstants.BTN_SMALL_WIDTH, UIConstants.BTN_SMALL_HEIGHT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** 主要按钮 - 使用主色调 */
|
||||
public static void stylePrimaryButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_PRIMARY);
|
||||
}
|
||||
|
||||
/** 次要按钮 - 使用辅助色调 */
|
||||
public static void styleSecondaryButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_SECONDARY);
|
||||
}
|
||||
|
||||
/** 成功按钮 - 使用绿色调 */
|
||||
public static void styleSuccessButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_SUCCESS);
|
||||
}
|
||||
|
||||
/** 警告按钮 - 使用橙色调 */
|
||||
public static void styleWarningButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_WARNING);
|
||||
}
|
||||
|
||||
/** 错误按钮 - 使用红色调 */
|
||||
public static void styleErrorButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_ERROR);
|
||||
}
|
||||
|
||||
/** 信息按钮 - 使用蓝色调 */
|
||||
public static void styleInfoButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_INFO);
|
||||
}
|
||||
|
||||
/** 快速生成"白色卡片"VBox - 兼容方法 */
|
||||
public static VBox createCard() {
|
||||
VBox card = new VBox(UIConstants.DEFAULT_SPACING);
|
||||
card.setPadding(UIConstants.CARD_PADDING);
|
||||
card.setStyle(UIConstants.CARD_STYLE);
|
||||
card.setEffect(UIConstants.CARD_SHADOW);
|
||||
return card;
|
||||
}
|
||||
|
||||
/** 新的卡片生成方法 - 支持不同尺寸 */
|
||||
public static VBox createCard(String size) {
|
||||
VBox card = new VBox(UIConstants.MEDIUM_SPACING);
|
||||
card.setPadding(UIConstants.CARD_PADDING);
|
||||
card.setAlignment(Pos.CENTER);
|
||||
|
||||
switch (size) {
|
||||
case "SMALL":
|
||||
card.setStyle(UIConstants.CARD_STYLE_SMALL);
|
||||
break;
|
||||
case "LARGE":
|
||||
card.setStyle(UIConstants.CARD_STYLE_LARGE);
|
||||
break;
|
||||
default: // MEDIUM
|
||||
card.setStyle(UIConstants.CARD_STYLE_MEDIUM);
|
||||
break;
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
/** 创建小型卡片 */
|
||||
public static VBox createSmallCard() {
|
||||
return createCard("SMALL");
|
||||
}
|
||||
|
||||
/** 创建中型卡片 */
|
||||
public static VBox createMediumCard() {
|
||||
return createCard("MEDIUM");
|
||||
}
|
||||
|
||||
/** 创建大型卡片 */
|
||||
public static VBox createLargeCard() {
|
||||
return createCard("LARGE");
|
||||
}
|
||||
|
||||
/** 统一输入框样式 - TextField */
|
||||
public static void styleTextField(TextField textField, String width) {
|
||||
textField.setStyle(UIConstants.getInputStyle(width));
|
||||
}
|
||||
|
||||
/** 统一输入框样式 - PasswordField */
|
||||
public static void stylePasswordField(PasswordField passwordField, String width) {
|
||||
passwordField.setStyle(UIConstants.getInputStyle(width));
|
||||
}
|
||||
|
||||
/** 统一输入框样式 - ChoiceBox */
|
||||
public static void styleChoiceBox(ChoiceBox<?> choiceBox, String width) {
|
||||
choiceBox.setStyle(UIConstants.getInputStyle(width));
|
||||
}
|
||||
|
||||
/** 统一输入框样式 - Spinner */
|
||||
public static void styleSpinner(Spinner<?> spinner) {
|
||||
spinner.getEditor().setStyle(UIConstants.getInputStyle(String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH)));
|
||||
spinner.setStyle("-fx-background-radius: 8; -fx-border-radius: 8;");
|
||||
}
|
||||
|
||||
/** 快速设置输入框为中等宽度 */
|
||||
public static void styleInputField(TextField field) {
|
||||
styleTextField(field, String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH));
|
||||
}
|
||||
|
||||
/** 快速设置密码框为中等宽度 */
|
||||
public static void stylePasswordField(PasswordField field) {
|
||||
stylePasswordField(field, String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH));
|
||||
}
|
||||
}
|
||||
/** 给按钮一次性加上通用样式、hover、pressed 动画 - 兼容方法 */
|
||||
public static void styleButton(Button btn) {
|
||||
btn.setStyle(UIConstants.BTN_NORMAL);
|
||||
// 仅背景深浅变化,字体、圆角始终一致
|
||||
btn.setOnMouseEntered(e -> btn.setStyle(UIConstants.BTN_HOVER));
|
||||
btn.setOnMouseExited(e -> btn.setStyle(UIConstants.BTN_NORMAL));
|
||||
btn.setOnMousePressed(e -> btn.setStyle(UIConstants.BTN_PRESSED));
|
||||
btn.setOnMouseReleased(e -> btn.setStyle(UIConstants.BTN_NORMAL));
|
||||
btn.setPrefSize(UIConstants.BTN_WIDTH, UIConstants.BTN_HEIGHT);
|
||||
}
|
||||
|
||||
/** 新的统一按钮样式方法 - 支持不同类型按钮 */
|
||||
public static void styleButton(Button btn, String type) {
|
||||
btn.setStyle(UIConstants.getButtonNormalStyle(type));
|
||||
btn.setOnMouseEntered(e -> btn.setStyle(UIConstants.getButtonHoverStyle(type)));
|
||||
btn.setOnMouseExited(e -> btn.setStyle(UIConstants.getButtonNormalStyle(type)));
|
||||
btn.setOnMousePressed(e -> btn.setStyle(UIConstants.getButtonPressedStyle(type)));
|
||||
btn.setOnMouseReleased(e -> btn.setStyle(UIConstants.getButtonNormalStyle(type)));
|
||||
|
||||
// 根据按钮类型设置尺寸
|
||||
switch (type) {
|
||||
case UIConstants.BTN_TYPE_PRIMARY:
|
||||
case UIConstants.BTN_TYPE_WARNING:
|
||||
btn.setPrefSize(UIConstants.BTN_LARGE_WIDTH, UIConstants.BTN_LARGE_HEIGHT);
|
||||
break;
|
||||
case UIConstants.BTN_TYPE_SUCCESS:
|
||||
case UIConstants.BTN_TYPE_ERROR:
|
||||
case UIConstants.BTN_TYPE_INFO:
|
||||
btn.setPrefSize(UIConstants.BTN_MEDIUM_WIDTH, UIConstants.BTN_MEDIUM_HEIGHT);
|
||||
break;
|
||||
default:
|
||||
btn.setPrefSize(UIConstants.BTN_SMALL_WIDTH, UIConstants.BTN_SMALL_HEIGHT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** 主要按钮 - 使用主色调 */
|
||||
public static void stylePrimaryButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_PRIMARY);
|
||||
}
|
||||
|
||||
/** 次要按钮 - 使用辅助色调 */
|
||||
public static void styleSecondaryButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_SECONDARY);
|
||||
}
|
||||
|
||||
/** 成功按钮 - 使用绿色调 */
|
||||
public static void styleSuccessButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_SUCCESS);
|
||||
}
|
||||
|
||||
/** 警告按钮 - 使用橙色调 */
|
||||
public static void styleWarningButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_WARNING);
|
||||
}
|
||||
|
||||
/** 错误按钮 - 使用红色调 */
|
||||
public static void styleErrorButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_ERROR);
|
||||
}
|
||||
|
||||
/** 信息按钮 - 使用蓝色调 */
|
||||
public static void styleInfoButton(Button btn) {
|
||||
styleButton(btn, UIConstants.BTN_TYPE_INFO);
|
||||
}
|
||||
|
||||
/** 快速生成"白色卡片"VBox - 兼容方法 */
|
||||
public static VBox createCard() {
|
||||
VBox card = new VBox(UIConstants.DEFAULT_SPACING);
|
||||
card.setPadding(UIConstants.CARD_PADDING);
|
||||
card.setStyle(UIConstants.CARD_STYLE);
|
||||
card.setEffect(UIConstants.CARD_SHADOW);
|
||||
return card;
|
||||
}
|
||||
|
||||
/** 新的卡片生成方法 - 支持不同尺寸 */
|
||||
public static VBox createCard(String size) {
|
||||
VBox card = new VBox(UIConstants.MEDIUM_SPACING);
|
||||
card.setPadding(UIConstants.CARD_PADDING);
|
||||
card.setAlignment(Pos.CENTER);
|
||||
|
||||
switch (size) {
|
||||
case "SMALL":
|
||||
card.setStyle(UIConstants.CARD_STYLE_SMALL);
|
||||
break;
|
||||
case "LARGE":
|
||||
card.setStyle(UIConstants.CARD_STYLE_LARGE);
|
||||
break;
|
||||
default: // MEDIUM
|
||||
card.setStyle(UIConstants.CARD_STYLE_MEDIUM);
|
||||
break;
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
/** 创建小型卡片 */
|
||||
public static VBox createSmallCard() {
|
||||
return createCard("SMALL");
|
||||
}
|
||||
|
||||
/** 创建中型卡片 */
|
||||
public static VBox createMediumCard() {
|
||||
return createCard("MEDIUM");
|
||||
}
|
||||
|
||||
/** 创建大型卡片 */
|
||||
public static VBox createLargeCard() {
|
||||
return createCard("LARGE");
|
||||
}
|
||||
|
||||
/** 统一输入框样式 - TextField */
|
||||
public static void styleTextField(TextField textField, String width) {
|
||||
textField.setStyle(UIConstants.getInputStyle(width));
|
||||
}
|
||||
|
||||
/** 统一输入框样式 - PasswordField */
|
||||
public static void stylePasswordField(PasswordField passwordField, String width) {
|
||||
passwordField.setStyle(UIConstants.getInputStyle(width));
|
||||
}
|
||||
|
||||
/** 统一输入框样式 - ChoiceBox */
|
||||
public static void styleChoiceBox(ChoiceBox<?> choiceBox, String width) {
|
||||
choiceBox.setStyle(UIConstants.getInputStyle(width));
|
||||
}
|
||||
|
||||
/** 统一输入框样式 - Spinner */
|
||||
public static void styleSpinner(Spinner<?> spinner) {
|
||||
spinner
|
||||
.getEditor()
|
||||
.setStyle(UIConstants.getInputStyle(String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH)));
|
||||
spinner.setStyle("-fx-background-radius: 8; -fx-border-radius: 8;");
|
||||
}
|
||||
|
||||
/** 快速设置输入框为中等宽度 */
|
||||
public static void styleInputField(TextField field) {
|
||||
styleTextField(field, String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH));
|
||||
}
|
||||
|
||||
/** 快速设置密码框为中等宽度 */
|
||||
public static void stylePasswordField(PasswordField field) {
|
||||
stylePasswordField(field, String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,43 +1,36 @@
|
||||
package com.pair.util;
|
||||
|
||||
/**
|
||||
* 应用数据目录管理器
|
||||
* 根据不同操作系统返回合适的应用数据存储路径
|
||||
*/
|
||||
/** 应用数据目录管理器 根据不同操作系统返回合适的应用数据存储路径 */
|
||||
public class AppDataDirectory {
|
||||
private static final String APP_NAME = "Math-Quiz-App"; // 替换为你的应用名
|
||||
private static final String APP_NAME = "Math-Quiz-App"; // 替换为你的应用名
|
||||
|
||||
/**
|
||||
* 获取应用数据根目录
|
||||
*/
|
||||
public static String getApplicationDataDirectory() {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
String basePath;
|
||||
/** 获取应用数据根目录 */
|
||||
public static String getApplicationDataDirectory() {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
String basePath;
|
||||
|
||||
if (os.contains("win")) {
|
||||
// Windows
|
||||
String appData = System.getenv("APPDATA");
|
||||
basePath = (appData != null) ? appData : System.getProperty("user.home") + "/AppData/Roaming";
|
||||
} else if (os.contains("mac")) {
|
||||
// macOS
|
||||
basePath = System.getProperty("user.home") + "/Library/Application Support";
|
||||
} else {
|
||||
// Linux/Unix
|
||||
String xdgDataHome = System.getenv("XDG_DATA_HOME");
|
||||
if (xdgDataHome == null) {
|
||||
xdgDataHome = System.getProperty("user.home") + "/.local/share";
|
||||
}
|
||||
basePath = xdgDataHome;
|
||||
}
|
||||
|
||||
return basePath + "/" + APP_NAME;
|
||||
if (os.contains("win")) {
|
||||
// Windows
|
||||
String appData = System.getenv("APPDATA");
|
||||
basePath = (appData != null) ? appData : System.getProperty("user.home") + "/AppData/Roaming";
|
||||
} else if (os.contains("mac")) {
|
||||
// macOS
|
||||
basePath = System.getProperty("user.home") + "/Library/Application Support";
|
||||
} else {
|
||||
// Linux/Unix
|
||||
String xdgDataHome = System.getenv("XDG_DATA_HOME");
|
||||
if (xdgDataHome == null) {
|
||||
xdgDataHome = System.getProperty("user.home") + "/.local/share";
|
||||
}
|
||||
basePath = xdgDataHome;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的应用数据路径
|
||||
*/
|
||||
public static String getFullPath(String relativePath) {
|
||||
String appDataDir = getApplicationDataDirectory();
|
||||
return appDataDir + "/" + relativePath;
|
||||
}
|
||||
}
|
||||
return basePath + "/" + APP_NAME;
|
||||
}
|
||||
|
||||
/** 获取完整的应用数据路径 */
|
||||
public static String getFullPath(String relativePath) {
|
||||
String appDataDir = getApplicationDataDirectory();
|
||||
return appDataDir + "/" + relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in new issue