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.
445 lines
13 KiB
445 lines
13 KiB
package com.service;
|
|
|
|
import com.model.*;
|
|
import com.service.question_generator.QuestionFactoryManager;
|
|
import java.io.IOException;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* 答题服务(包含所有答题相关业务逻辑)
|
|
*/
|
|
public class QuizService {
|
|
|
|
private final FileIOService fileIOService;
|
|
private final UserService userService;
|
|
|
|
private List<ChoiceQuestion> currentQuestions;
|
|
private List<Integer> userAnswers;
|
|
private int currentQuestionIndex;
|
|
private int answerNumber;
|
|
|
|
// ==================== 构造方法 ====================
|
|
|
|
public QuizService() {
|
|
this.fileIOService = new FileIOService();
|
|
this.userService = new UserService(fileIOService);
|
|
this.currentQuestions = new ArrayList<>();
|
|
this.userAnswers = new ArrayList<>();
|
|
this.currentQuestionIndex = 0;
|
|
}
|
|
|
|
public QuizService(FileIOService fileIOService, UserService userService) {
|
|
this.fileIOService = fileIOService;
|
|
this.userService = userService;
|
|
this.currentQuestions = new ArrayList<>();
|
|
this.userAnswers = new ArrayList<>();
|
|
this.currentQuestionIndex = 0;
|
|
}
|
|
|
|
// ==================== 答题会话管理 ====================
|
|
|
|
public void startNewQuiz(User user, int questionCount) throws IOException {
|
|
currentQuestions.clear();
|
|
userAnswers.clear();
|
|
currentQuestionIndex = 0;
|
|
|
|
Set<String> historyQuestions = getRecentHistoryQuestions();
|
|
|
|
Grade grade = user.getGrade();
|
|
currentQuestions = QuestionFactoryManager.generateQuestions(
|
|
grade, questionCount, historyQuestions
|
|
);
|
|
|
|
for (int i = 0; i < currentQuestions.size(); i++) {
|
|
userAnswers.add(null);
|
|
}
|
|
|
|
System.out.println("✓ 已生成 " + currentQuestions.size() + " 道 " + grade + " 题目");
|
|
}
|
|
|
|
private Set<String> getRecentHistoryQuestions() throws IOException {
|
|
List<String> historyList = fileIOService.getHistoryQuestions();
|
|
return new HashSet<>(historyList);
|
|
}
|
|
|
|
// ==================== 题目访问 ====================
|
|
|
|
public ChoiceQuestion getCurrentQuestion() {
|
|
if (currentQuestionIndex >= 0 && currentQuestionIndex < currentQuestions.size()) {
|
|
return currentQuestions.get(currentQuestionIndex);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public ChoiceQuestion getQuestion(int index) {
|
|
if (index >= 0 && index < currentQuestions.size()) {
|
|
return currentQuestions.get(index);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public List<ChoiceQuestion> getAllQuestions() {
|
|
return new ArrayList<>(currentQuestions);
|
|
}
|
|
|
|
public boolean nextQuestion() {
|
|
if (currentQuestionIndex < currentQuestions.size() - 1) {
|
|
currentQuestionIndex++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public boolean previousQuestion() {
|
|
if (currentQuestionIndex > 0) {
|
|
currentQuestionIndex--;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public boolean goToQuestion(int index) {
|
|
if (index >= 0 && index < currentQuestions.size()) {
|
|
currentQuestionIndex = index;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public int getCurrentQuestionIndex() {
|
|
return currentQuestionIndex;
|
|
}
|
|
|
|
public int getTotalQuestions() {
|
|
return currentQuestions.size();
|
|
}
|
|
|
|
public boolean isFirstQuestion() {
|
|
return currentQuestionIndex == 0;
|
|
}
|
|
|
|
public boolean isLastQuestion() {
|
|
return currentQuestionIndex == currentQuestions.size() - 1;
|
|
}
|
|
|
|
// ==================== 答题操作 ====================
|
|
|
|
public boolean submitAnswer(int questionIndex, int optionIndex) {
|
|
if (questionIndex < 0 || questionIndex >= currentQuestions.size()) {
|
|
throw new IllegalArgumentException("题目索引无效: " + questionIndex);
|
|
}
|
|
|
|
ChoiceQuestion question = currentQuestions.get(questionIndex);
|
|
|
|
if (optionIndex < 0 || optionIndex >= question.getOptions().size()) {
|
|
throw new IllegalArgumentException("选项索引无效: " + optionIndex);
|
|
}
|
|
|
|
userAnswers.set(questionIndex, optionIndex);
|
|
|
|
return checkAnswer(question, optionIndex);
|
|
}
|
|
|
|
public boolean submitCurrentAnswer(int optionIndex) {
|
|
return submitAnswer(currentQuestionIndex, optionIndex);
|
|
}
|
|
|
|
public Integer getUserAnswer(int questionIndex) {
|
|
if (questionIndex >= 0 && questionIndex < userAnswers.size()) {
|
|
return userAnswers.get(questionIndex);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public List<Integer> getAllUserAnswers() {
|
|
return new ArrayList<>(userAnswers);
|
|
}
|
|
|
|
public boolean isAllAnswered() {
|
|
for (Integer answer : userAnswers) {
|
|
if (answer == null) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public int getAnsweredCount() {
|
|
int count = 0;
|
|
for (Integer answer : userAnswers) {
|
|
if (answer != null) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public boolean isAnswered(int questionIndex) {
|
|
return userAnswers.get(questionIndex) != null ;
|
|
}
|
|
|
|
// ==================== 成绩计算 ====================
|
|
|
|
public QuizResult calculateResult() {
|
|
int correctCount = 0;
|
|
int totalQuestions = currentQuestions.size();
|
|
|
|
for (int i = 0; i < totalQuestions; i++) {
|
|
ChoiceQuestion question = currentQuestions.get(i);
|
|
Integer userAnswer = userAnswers.get(i);
|
|
|
|
if (userAnswer != null && checkAnswer(question, userAnswer)) {
|
|
correctCount++;
|
|
}
|
|
}
|
|
|
|
int wrongCount = totalQuestions - correctCount;
|
|
int score = totalQuestions > 0 ? (correctCount * 100) / totalQuestions : 0;
|
|
|
|
return new QuizResult(totalQuestions, correctCount, wrongCount, score);
|
|
}
|
|
|
|
public List<Integer> getCorrectQuestionIndices() {
|
|
List<Integer> correctIndices = new ArrayList<>();
|
|
|
|
for (int i = 0; i < currentQuestions.size(); i++) {
|
|
ChoiceQuestion question = currentQuestions.get(i);
|
|
Integer userAnswer = userAnswers.get(i);
|
|
|
|
if (userAnswer != null && checkAnswer(question, userAnswer)) {
|
|
correctIndices.add(i);
|
|
}
|
|
}
|
|
|
|
return correctIndices;
|
|
}
|
|
|
|
public List<Integer> getWrongQuestionIndices() {
|
|
List<Integer> wrongIndices = new ArrayList<>();
|
|
|
|
for (int i = 0; i < currentQuestions.size(); i++) {
|
|
ChoiceQuestion question = currentQuestions.get(i);
|
|
Integer userAnswer = userAnswers.get(i);
|
|
|
|
if (userAnswer != null && !checkAnswer(question, userAnswer)) {
|
|
wrongIndices.add(i);
|
|
}
|
|
}
|
|
|
|
return wrongIndices;
|
|
}
|
|
|
|
public List<Integer> getUnansweredQuestionIndices() {
|
|
List<Integer> unansweredIndices = new ArrayList<>();
|
|
|
|
for (int i = 0; i < userAnswers.size(); i++) {
|
|
if (userAnswers.get(i) == null) {
|
|
unansweredIndices.add(i);
|
|
}
|
|
}
|
|
|
|
return unansweredIndices;
|
|
}
|
|
|
|
// ==================== 业务逻辑方法(从 Model 移过来)====================
|
|
|
|
/**
|
|
* 检查答案是否正确
|
|
*/
|
|
public boolean checkAnswer(ChoiceQuestion question, int userAnswerIndex) {
|
|
if (userAnswerIndex < 0 || userAnswerIndex >= question.getOptions().size()) {
|
|
return false;
|
|
}
|
|
|
|
Object userAnswer = question.getOptions().get(userAnswerIndex);
|
|
return question.getCorrectAnswer().equals(userAnswer);
|
|
}
|
|
|
|
/**
|
|
* 获取题目的正确答案索引
|
|
*/
|
|
public int getCorrectAnswerIndex(ChoiceQuestion question) {
|
|
return question.getOptions().indexOf(question.getCorrectAnswer());
|
|
}
|
|
|
|
/**
|
|
* 获取正确答案的字母形式
|
|
*/
|
|
public String getCorrectAnswerLetter(ChoiceQuestion question) {
|
|
int index = getCorrectAnswerIndex(question);
|
|
if (index >= 0 && index < 4) {
|
|
return String.valueOf((char)('A' + index));
|
|
}
|
|
return "未知";
|
|
}
|
|
|
|
/**
|
|
* 获取答题结果的正确率
|
|
*/
|
|
public double getAccuracy(QuizResult result) {
|
|
if (result.getTotalQuestions() == 0) {
|
|
return 0.0;
|
|
}
|
|
return (result.getCorrectCount() * 100.0) / result.getTotalQuestions();
|
|
}
|
|
|
|
/**
|
|
* 判断是否及格
|
|
*/
|
|
public boolean isPassed(QuizResult result) {
|
|
return result.getScore() >= 60;
|
|
}
|
|
|
|
/**
|
|
* 获取评级
|
|
*/
|
|
public String getGrade(QuizResult result) {
|
|
int score = result.getScore();
|
|
if (score >= 90) return "优秀";
|
|
if (score >= 80) return "良好";
|
|
if (score >= 70) return "中等";
|
|
if (score >= 60) return "及格";
|
|
return "不及格";
|
|
}
|
|
|
|
/**
|
|
* 计算答题历史的正确数
|
|
*/
|
|
public int getCorrectCount(QuizHistory history) {
|
|
int count = 0;
|
|
List<ChoiceQuestion> questions = history.getQuestions();
|
|
List<Integer> userAnswers = history.getUserAnswers();
|
|
|
|
for (int i = 0; i < questions.size(); i++) {
|
|
ChoiceQuestion question = questions.get(i);
|
|
Integer userAnswer = userAnswers.get(i);
|
|
|
|
if (userAnswer != null && checkAnswer(question, userAnswer)) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* 计算答题历史的错误数
|
|
*/
|
|
public int getWrongCount(QuizHistory history) {
|
|
return history.getQuestions().size() - getCorrectCount(history);
|
|
}
|
|
|
|
// ==================== 格式化输出 ====================
|
|
|
|
public String formatQuestion(ChoiceQuestion question) {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(question.getQuestionText()).append("\n");
|
|
|
|
List<?> options = question.getOptions();
|
|
for (int i = 0; i < options.size(); i++) {
|
|
sb.append((char)('A' + i)).append(". ").append(options.get(i));
|
|
|
|
if (i % 2 == 1) {
|
|
sb.append("\n");
|
|
} else {
|
|
sb.append(" ");
|
|
}
|
|
}
|
|
|
|
return sb.toString();
|
|
}
|
|
|
|
public String formatCurrentQuestion() {
|
|
ChoiceQuestion question = getCurrentQuestion();
|
|
if (question == null) {
|
|
return "没有可用的题目";
|
|
}
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("第 ").append(currentQuestionIndex + 1)
|
|
.append(" / ").append(currentQuestions.size()).append(" 题\n");
|
|
sb.append(formatQuestion(question));
|
|
|
|
return sb.toString();
|
|
}
|
|
|
|
public String formatQuestionWithAnswer(ChoiceQuestion question, Integer userAnswerIndex) {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(question.getQuestionText()).append("\n");
|
|
|
|
List<?> options = question.getOptions();
|
|
int correctIndex = getCorrectAnswerIndex(question);
|
|
|
|
for (int i = 0; i < options.size(); i++) {
|
|
sb.append((char)('A' + i)).append(". ").append(options.get(i));
|
|
|
|
if (i == correctIndex) {
|
|
sb.append(" ✓");
|
|
}
|
|
|
|
if (userAnswerIndex != null && i == userAnswerIndex) {
|
|
boolean isCorrect = checkAnswer(question, userAnswerIndex);
|
|
sb.append(isCorrect ? " [您的答案:正确]" : " [您的答案:错误]");
|
|
}
|
|
|
|
if (i % 2 == 1) {
|
|
sb.append("\n");
|
|
} else {
|
|
sb.append(" ");
|
|
}
|
|
}
|
|
|
|
return sb.toString();
|
|
}
|
|
|
|
public String formatResult(QuizResult result) {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("\n========== 答题结束 ==========\n");
|
|
sb.append("总题数:").append(result.getTotalQuestions()).append(" 题\n");
|
|
sb.append("正确:").append(result.getCorrectCount()).append(" 题\n");
|
|
sb.append("错误:").append(result.getWrongCount()).append(" 题\n");
|
|
sb.append("得分:").append(result.getScore()).append(" 分\n");
|
|
sb.append("正确率:").append(String.format("%.1f%%", getAccuracy(result))).append("\n");
|
|
sb.append("评级:").append(getGrade(result)).append("\n");
|
|
sb.append("===============================\n");
|
|
|
|
return sb.toString();
|
|
}
|
|
|
|
// ==================== 数据持久化 ====================
|
|
|
|
public void saveQuizHistory(User user) throws IOException {
|
|
QuizResult result = calculateResult();
|
|
|
|
QuizHistory history = new QuizHistory(
|
|
user.getUsername(),
|
|
new Date(),
|
|
currentQuestions,
|
|
userAnswers,
|
|
result.getScore()
|
|
);
|
|
|
|
fileIOService.saveQuizHistory(history);
|
|
|
|
userService.updateUserStatistics(user, result.getScore());
|
|
|
|
System.out.println("✓ 答题记录已保存");
|
|
}
|
|
|
|
// ==================== Getters ====================
|
|
|
|
public int getAnswerNumber() {
|
|
return answerNumber;
|
|
}
|
|
|
|
public void setAnswerNumber(int answerNumber) {
|
|
this.answerNumber = answerNumber;
|
|
}
|
|
|
|
public List<ChoiceQuestion> getCurrentQuestions() {
|
|
return new ArrayList<>(currentQuestions);
|
|
}
|
|
|
|
public List<Integer> getUserAnswers() {
|
|
return new ArrayList<>(userAnswers);
|
|
}
|
|
} |