|
|
|
|
@ -1,35 +1,31 @@
|
|
|
|
|
package com.mathgenerator.storage;
|
|
|
|
|
|
|
|
|
|
import com.google.gson.Gson;
|
|
|
|
|
import com.google.gson.GsonBuilder;
|
|
|
|
|
import com.google.gson.reflect.TypeToken;
|
|
|
|
|
import com.mathgenerator.model.ChoiceQuestion; // 导入新模型
|
|
|
|
|
|
|
|
|
|
import java.io.FileReader;
|
|
|
|
|
import java.io.FileWriter;
|
|
|
|
|
import com.mathgenerator.model.ChoiceQuestion;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.lang.reflect.Type;
|
|
|
|
|
import java.nio.file.Files;
|
|
|
|
|
import java.nio.file.Path;
|
|
|
|
|
import java.nio.file.Paths;
|
|
|
|
|
import java.nio.file.StandardOpenOption;
|
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.util.regex.Matcher;
|
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
import java.util.stream.Stream;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 负责文件读写,现已支持JSON格式的ChoiceQuestion对象。
|
|
|
|
|
* 负责文件读写,现已支持将试卷保存为格式化的 .txt 文件。
|
|
|
|
|
*/
|
|
|
|
|
public class FileManager {
|
|
|
|
|
private static final Path BASE_PATH = Paths.get("generated_papers");
|
|
|
|
|
// 格式化器,用于生成 "年-月-日-时-分-秒" 格式的文件名
|
|
|
|
|
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss");
|
|
|
|
|
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 将生成的选择题试卷以JSON格式保存到文件。
|
|
|
|
|
* 将生成的选择题试卷以 .txt 格式保存到文件。
|
|
|
|
|
* @param username 用户名
|
|
|
|
|
* @param paperContent 包含ChoiceQuestion对象的试卷列表
|
|
|
|
|
* @return 保存成功后的文件路径
|
|
|
|
|
@ -39,17 +35,38 @@ public class FileManager {
|
|
|
|
|
Files.createDirectories(userDir);
|
|
|
|
|
|
|
|
|
|
String timestamp = LocalDateTime.now().format(FORMATTER);
|
|
|
|
|
String fileName = timestamp + ".json"; // 文件后缀改为 .json
|
|
|
|
|
String fileName = timestamp + ".txt"; // 文件后缀改为 .txt
|
|
|
|
|
Path filePath = userDir.resolve(fileName);
|
|
|
|
|
|
|
|
|
|
try (FileWriter writer = new FileWriter(filePath.toFile())) {
|
|
|
|
|
gson.toJson(paperContent, writer);
|
|
|
|
|
// --- 核心修改:构建 .txt 文件内容 ---
|
|
|
|
|
StringBuilder formattedContent = new StringBuilder();
|
|
|
|
|
formattedContent.append("数学试卷\n");
|
|
|
|
|
formattedContent.append("用户: ").append(username).append("\n");
|
|
|
|
|
formattedContent.append("生成时间: ").append(timestamp).append("\n");
|
|
|
|
|
formattedContent.append("========================================\n\n");
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < paperContent.size(); i++) {
|
|
|
|
|
ChoiceQuestion question = paperContent.get(i);
|
|
|
|
|
// 写入题号和题干
|
|
|
|
|
formattedContent.append(i + 1).append(". ").append(question.questionText()).append("\n");
|
|
|
|
|
|
|
|
|
|
// 写入四个选项
|
|
|
|
|
char optionChar = 'A';
|
|
|
|
|
for (String option : question.options()) {
|
|
|
|
|
formattedContent.append(" ").append(optionChar++).append(". ").append(option).append("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// (可选) 写入正确答案,方便核对
|
|
|
|
|
String correctAnswer = question.options().get(question.correctOptionIndex());
|
|
|
|
|
formattedContent.append(" [正确答案: ").append(correctAnswer).append("]\n\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Files.writeString(filePath, formattedContent.toString(), StandardOpenOption.CREATE);
|
|
|
|
|
return filePath.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 加载指定用户的所有历史题目的题干文本,用于查重。
|
|
|
|
|
* (已修改) 加载指定用户的所有历史题目的题干文本,用于查重。
|
|
|
|
|
* @param username 用户名
|
|
|
|
|
* @return 包含所有历史题干文本的Set集合
|
|
|
|
|
*/
|
|
|
|
|
@ -61,9 +78,8 @@ public class FileManager {
|
|
|
|
|
|
|
|
|
|
try (Stream<Path> stream = Files.walk(userDir)) {
|
|
|
|
|
return stream
|
|
|
|
|
.filter(file -> !Files.isDirectory(file) && file.toString().endsWith(".json")) // 只读取 .json 文件
|
|
|
|
|
.flatMap(this::readQuestionsFromFile) // 使用方法引用
|
|
|
|
|
.map(ChoiceQuestion::questionText) // 提取每个对象的题干文本
|
|
|
|
|
.filter(file -> !Files.isDirectory(file) && file.toString().endsWith(".txt")) // 只读取 .txt 文件
|
|
|
|
|
.flatMap(this::readQuestionTextsFromTxtFile) // 使用新的解析方法
|
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
System.err.println("错误:读取历史文件失败 - " + e.getMessage());
|
|
|
|
|
@ -72,17 +88,21 @@ public class FileManager {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 从单个JSON文件中读取并解析ChoiceQuestion对象列表。
|
|
|
|
|
* (新增) 从单个 .txt 文件中解析出所有题目的题干。
|
|
|
|
|
* @param file 要读取的单个试卷文件的路径对象 (Path)。
|
|
|
|
|
* @return 一个包含该文件中所有ChoiceQuestion对象的流 (Stream)。
|
|
|
|
|
* @return 一个包含该文件中所有题干字符串的流 (Stream)。
|
|
|
|
|
*/
|
|
|
|
|
private Stream<ChoiceQuestion> readQuestionsFromFile(Path file) {
|
|
|
|
|
try (FileReader reader = new FileReader(file.toFile())) {
|
|
|
|
|
Type listType = new TypeToken<List<ChoiceQuestion>>() {}.getType();
|
|
|
|
|
List<ChoiceQuestion> questions = gson.fromJson(reader, listType);
|
|
|
|
|
return questions != null ? questions.stream() : Stream.empty();
|
|
|
|
|
private Stream<String> readQuestionTextsFromTxtFile(Path file) {
|
|
|
|
|
try (Stream<String> lines = Files.lines(file)) {
|
|
|
|
|
// 正则表达式,用于匹配 "1. 3 + 5" 这样的行并提取出 "3 + 5"
|
|
|
|
|
Pattern questionPattern = Pattern.compile("^\\d+\\.\\s+(.*)");
|
|
|
|
|
return lines
|
|
|
|
|
.map(String::trim)
|
|
|
|
|
.map(questionPattern::matcher)
|
|
|
|
|
.filter(Matcher::matches)
|
|
|
|
|
.map(matcher -> matcher.group(1).trim());
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
System.err.println("错误:读取或解析文件 " + file + " 失败 - " + e.getMessage());
|
|
|
|
|
System.err.println("错误:读取文件 " + file + " 失败 - " + e.getMessage());
|
|
|
|
|
return Stream.empty();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|