You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
5.6 KiB
159 lines
5.6 KiB
package com.personalproject.service;
|
|
|
|
import com.personalproject.generator.QuestionGenerator;
|
|
import com.personalproject.model.DifficultyLevel;
|
|
import com.personalproject.model.ExamSession;
|
|
import com.personalproject.model.QuizQuestion;
|
|
import com.personalproject.storage.QuestionStorageService;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.EnumMap;
|
|
import java.util.LinkedHashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Random;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* 用于管理考试会话和生成测验题目的服务类.
|
|
*/
|
|
public final class ExamService {
|
|
|
|
private static final int OPTIONS_COUNT = 4;
|
|
private final Map<DifficultyLevel, QuestionGenerator> generators;
|
|
private final Random random = new Random();
|
|
private final QuestionGenerationService questionGenerationService;
|
|
private final QuestionStorageService questionStorageService;
|
|
|
|
/**
|
|
* 创建新的考试服务.
|
|
*
|
|
* @param generatorMap 难度级别到题目生成器的映射
|
|
* @param questionGenerationService 题目生成服务
|
|
* @param questionStorageService 题目存储服务
|
|
*/
|
|
public ExamService(
|
|
Map<DifficultyLevel, QuestionGenerator> generatorMap,
|
|
QuestionGenerationService questionGenerationService,
|
|
QuestionStorageService questionStorageService) {
|
|
this.generators = new EnumMap<>(DifficultyLevel.class);
|
|
this.generators.putAll(generatorMap);
|
|
this.questionGenerationService = questionGenerationService;
|
|
this.questionStorageService = questionStorageService;
|
|
}
|
|
|
|
/**
|
|
* 使用指定参数创建新的考试会话.
|
|
*
|
|
* @param username 考生用户名
|
|
* @param difficultyLevel 考试难度级别
|
|
* @param questionCount 考试中包含的题目数量
|
|
* @return 新的考试会话
|
|
*/
|
|
public ExamSession createExamSession(
|
|
String username, DifficultyLevel difficultyLevel, int questionCount) {
|
|
if (questionCount < 10 || questionCount > 30) {
|
|
throw new IllegalArgumentException("题目数量必须在10到30之间");
|
|
}
|
|
|
|
// 根据难度级别生成题目
|
|
if (!generators.containsKey(difficultyLevel)) {
|
|
throw new IllegalArgumentException("找不到难度级别的生成器: " + difficultyLevel);
|
|
}
|
|
|
|
try {
|
|
Set<String> existingQuestions = questionStorageService.loadExistingQuestions(username);
|
|
List<String> uniqueQuestions = questionGenerationService.generateUniqueQuestions(
|
|
difficultyLevel, questionCount, existingQuestions);
|
|
|
|
List<QuizQuestion> quizQuestions = new ArrayList<>();
|
|
for (String questionText : uniqueQuestions) {
|
|
quizQuestions.add(buildQuizQuestion(questionText));
|
|
}
|
|
|
|
questionStorageService.saveQuestions(username, uniqueQuestions);
|
|
return new ExamSession(username, difficultyLevel, quizQuestions);
|
|
} catch (IOException exception) {
|
|
throw new IllegalStateException("生成考试题目失败", exception);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 使用预定义题目创建考试会话(用于测试).
|
|
*
|
|
* @param username 考生用户名
|
|
* @param difficultyLevel 考试难度级别
|
|
* @param questions 预定义题目列表
|
|
* @return 新的考试会话
|
|
*/
|
|
public ExamSession createExamSession(
|
|
String username, DifficultyLevel difficultyLevel, List<QuizQuestion> questions) {
|
|
if (questions.size() < 10 || questions.size() > 30) {
|
|
throw new IllegalArgumentException("题目数量必须在10到30之间");
|
|
}
|
|
return new ExamSession(username, difficultyLevel, questions);
|
|
}
|
|
|
|
private QuizQuestion buildQuizQuestion(String questionText) {
|
|
try {
|
|
double correctAnswer = MathExpressionEvaluator.evaluate(questionText);
|
|
String formattedCorrectAnswer = formatAnswer(correctAnswer);
|
|
List<String> options = buildOptions(correctAnswer, formattedCorrectAnswer);
|
|
int correctAnswerIndex = options.indexOf(formattedCorrectAnswer);
|
|
if (correctAnswerIndex < 0) {
|
|
options.set(0, formattedCorrectAnswer);
|
|
correctAnswerIndex = 0;
|
|
}
|
|
return new QuizQuestion(questionText, options, correctAnswerIndex);
|
|
} catch (RuntimeException exception) {
|
|
List<String> fallbackOptions = new ArrayList<>();
|
|
for (int i = 1; i <= OPTIONS_COUNT; i++) {
|
|
fallbackOptions.add("选项" + i);
|
|
}
|
|
return new QuizQuestion(questionText, fallbackOptions, 0);
|
|
}
|
|
}
|
|
|
|
private List<String> buildOptions(double correctAnswer, String formattedCorrectAnswer) {
|
|
Set<String> optionSet = new LinkedHashSet<>();
|
|
optionSet.add(formattedCorrectAnswer);
|
|
|
|
int attempts = 0;
|
|
while (optionSet.size() < OPTIONS_COUNT && attempts < 100) {
|
|
double offset = generateOffset(correctAnswer);
|
|
double candidateValue = correctAnswer + offset;
|
|
if (Double.isNaN(candidateValue) || Double.isInfinite(candidateValue)) {
|
|
attempts++;
|
|
continue;
|
|
}
|
|
String candidate = formatAnswer(candidateValue);
|
|
if (!candidate.equals(formattedCorrectAnswer)) {
|
|
optionSet.add(candidate);
|
|
}
|
|
attempts++;
|
|
}
|
|
|
|
while (optionSet.size() < OPTIONS_COUNT) {
|
|
optionSet.add(formatAnswer(correctAnswer + optionSet.size() * 1.5));
|
|
}
|
|
|
|
List<String> options = new ArrayList<>(optionSet);
|
|
Collections.shuffle(options, random);
|
|
return options;
|
|
}
|
|
|
|
private double generateOffset(double base) {
|
|
double scale = Math.max(1.0, Math.abs(base) / 2.0);
|
|
double offset = random.nextGaussian() * scale;
|
|
if (Math.abs(offset) < 0.5) {
|
|
offset += offset >= 0 ? 1.5 : -1.5;
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
private String formatAnswer(double value) {
|
|
return String.format("%.2f", value);
|
|
}
|
|
}
|