HarmonyOS应用<趣答>开发第06篇:QuizService测验服务实现详解——打造流畅的答题体验
·

📖 引言
在知识问答学习应用中,测验服务(QuizService)是核心业务组件之一。它负责管理整个测验流程,包括题目获取、答题处理、结果计算、统计更新等关键操作。一个设计良好的测验服务能够提供流畅的答题体验,确保数据的准确性和一致性。
本文将详细讲解 QuizService 的完整实现,包括测验会话管理、答题逻辑、结果计算等核心功能。通过本文,你将掌握:
- 如何设计测验会话管理机制
- 如何实现答题流程和时间管理
- 如何计算测验结果和星级评定
- 如何更新用户统计和成就
🎯 学习目标
完成本文后,你将能够:
- ✅ 理解 QuizService 的核心职责和功能
- ✅ 实现测验会话的创建和管理
- ✅ 处理用户答题和时间限制
- ✅ 计算测验结果和奖励
- ✅ 集成用户统计和成就系统
💡 需求分析
功能模块设计
| 模块 | 功能描述 | 技术要点 |
|---|---|---|
| 测验会话 | 创建和管理测验会话 | 状态管理、题目顺序 |
| 题目获取 | 获取关卡题目 | 随机抽取、难度匹配 |
| 答题处理 | 处理用户答案 | 正确性判断、时间记录 |
| 结果计算 | 计算测验结果 | 正确率、星级、奖励 |
| 统计更新 | 更新用户统计 | 答题数、正确率、积分 |
🛠️ 核心实现
步骤1: QuizService 接口设计
功能说明
定义 QuizService 的核心接口,规范服务的方法签名和返回类型。
完整代码
// services/interfaces/QuizServiceInterface.ts
import { ServiceResponse } from './ServiceInterface';
import { QuizSession, QuizRecord, QuestionResult, Question } from '../models/User';
/**
* 测验服务接口
*/
export interface QuizServiceInterface {
/**
* 创建测验会话
* @param userId - 用户ID
* @param levelId - 关卡ID
* @returns 测验会话
*/
createSession(userId: string, levelId: string): Promise<ServiceResponse<QuizSession>>;
/**
* 获取当前会话
* @returns 当前测验会话
*/
getCurrentSession(): ServiceResponse<QuizSession | null>;
/**
* 提交答案
* @param questionId - 题目ID
* @param answer - 用户答案
* @returns 答题结果
*/
submitAnswer(questionId: string, answer: string | string[]): ServiceResponse<QuestionResult>;
/**
* 使用工具
* @param toolType - 工具类型
* @returns 是否成功
*/
useTool(toolType: string): ServiceResponse<boolean>;
/**
* 结束测验
* @returns 测验记录
*/
finishQuiz(): Promise<ServiceResponse<QuizRecord>>;
/**
* 获取测验历史记录
* @param userId - 用户ID
* @param limit - 返回数量限制
* @returns 测验记录列表
*/
getQuizHistory(userId: string, limit?: number): Promise<ServiceResponse<QuizRecord[]>>;
/**
* 获取关卡测验记录
* @param userId - 用户ID
* @param levelId - 关卡ID
* @returns 关卡测验记录列表
*/
getLevelRecords(userId: string, levelId: string): Promise<ServiceResponse<QuizRecord[]>>;
/**
* 获取错题本
* @param userId - 用户ID
* @returns 错题列表
*/
getWrongQuestions(userId: string): Promise<ServiceResponse<Question[]>>;
/**
* 添加错题
* @param userId - 用户ID
* @param questionId - 题目ID
* @returns 是否成功
*/
addWrongQuestion(userId: string, questionId: string): Promise<ServiceResponse<boolean>>;
/**
* 移除错题
* @param userId - 用户ID
* @param questionId - 题目ID
* @returns 是否成功
*/
removeWrongQuestion(userId: string, questionId: string): Promise<ServiceResponse<boolean>>;
/**
* 获取测验统计
* @param userId - 用户ID
* @returns 统计数据
*/
getQuizStatistics(userId: string): Promise<ServiceResponse<QuizStatistics>>;
}
/**
* 测验统计接口
*/
export interface QuizStatistics {
totalQuizzes: number; // 总测验次数
averageAccuracy: number; // 平均正确率
averageTime: number; // 平均用时(秒)
highestStars: number; // 最高星级
perfectCount: number; // 满分次数
totalScore: number; // 累计获得积分
totalExperience: number; // 累计获得经验
}
代码解析
1. 接口方法分类
| 分类 | 方法 | 功能说明 |
|---|---|---|
| 会话管理 | createSession(), getCurrentSession() |
创建和获取测验会话 |
| 答题操作 | submitAnswer(), useTool() |
提交答案和使用工具 |
| 结果处理 | finishQuiz() |
结束测验并计算结果 |
| 记录查询 | getQuizHistory(), getLevelRecords() |
查询测验记录 |
| 错题管理 | getWrongQuestions(), addWrongQuestion(), removeWrongQuestion() |
错题本管理 |
| 统计查询 | getQuizStatistics() |
获取测验统计 |
步骤2: QuizService 实现
功能说明
实现 QuizService 的核心逻辑,包括测验会话管理、答题处理、结果计算等功能。
完整代码
// services/QuizService.ts
import { Singleton } from './base/BaseService';
import { BaseService } from './base/BaseService';
import { ServiceResponse, ServiceError, createSuccessResponse, createErrorResponse } from './interfaces/ServiceInterface';
import { QuizServiceInterface, QuizStatistics } from './interfaces/QuizServiceInterface';
import { QuizSession, QuizRecord, QuestionResult, Question } from '../models/User';
import { UserService } from './UserService';
import { LevelService } from './LevelService';
import { AchievementService } from './AchievementService';
import { QuestionService } from './QuestionService';
/**
* 测验服务实现
*/
@Singleton
export class QuizService extends BaseService implements QuizServiceInterface {
private userService: UserService;
private levelService: LevelService;
private achievementService: AchievementService;
private questionService: QuestionService;
private currentSession: QuizSession | null = null;
constructor() {
super();
this.userService = UserService.getInstance();
this.levelService = LevelService.getInstance();
this.achievementService = AchievementService.getInstance();
this.questionService = QuestionService.getInstance();
}
/**
* 创建测验会话
*/
async createSession(userId: string, levelId: string): Promise<ServiceResponse<QuizSession>> {
try {
// 获取用户
const userResult = await this.userService.getUserById(userId);
if (!userResult.success || !userResult.data) {
return createErrorResponse<QuizSession>(ServiceError.NOT_FOUND, '用户不存在');
}
// 获取关卡
const level = this.levelService.getLevelById(levelId);
if (!level) {
return createErrorResponse<QuizSession>(ServiceError.NOT_FOUND, '关卡不存在');
}
// 检查关卡是否解锁
if (!this.levelService.isLevelUnlocked(levelId, userResult.data)) {
return createErrorResponse<QuizSession>(ServiceError.CONFLICT, '关卡未解锁');
}
// 获取题目
const questions = await this.questionService.getRandomQuestions(
levelId,
level.questionCount
);
if (questions.length === 0) {
return createErrorResponse<QuizSession>(ServiceError.NOT_FOUND, '该关卡暂无题目');
}
// 创建会话
const session: QuizSession = {
id: this.generateSessionId(),
userId,
levelId,
questions,
startTime: new Date().toISOString(),
answers: [],
currentIndex: 0,
timeUsed: 0,
toolsUsed: {
hint: 0,
skip: 0,
refresh: 0,
doubleScore: false
}
};
this.currentSession = session;
return createSuccessResponse<QuizSession>(session);
} catch (error) {
return createErrorResponse<QuizSession>(ServiceError.ERROR, (error as Error).message);
}
}
/**
* 获取当前会话
*/
getCurrentSession(): ServiceResponse<QuizSession | null> {
return createSuccessResponse<QuizSession | null>(this.currentSession);
}
/**
* 提交答案
*/
submitAnswer(questionId: string, answer: string | string[]): ServiceResponse<QuestionResult> {
if (!this.currentSession) {
return createErrorResponse<QuestionResult>(ServiceError.NOT_INITIALIZED, '没有活动的测验会话');
}
const question = this.currentSession.questions.find(q => q.id === questionId);
if (!question) {
return createErrorResponse<QuestionResult>(ServiceError.NOT_FOUND, '题目不存在');
}
// 判断答案是否正确
const isCorrect = this.checkAnswer(question, answer);
// 记录答题结果
const result: QuestionResult = {
id: this.generateResultId(),
questionId: question.id,
userAnswer: Array.isArray(answer) ? answer.join(',') : answer,
isCorrect,
timeUsed: this.calculateQuestionTime(),
usedTools: []
};
// 记录使用的工具
if (this.currentSession.toolsUsed.hint > 0) {
result.usedTools.push('hint');
}
this.currentSession.answers.push(result);
this.currentSession.currentIndex++;
return createSuccessResponse<QuestionResult>(result);
}
/**
* 使用工具
*/
useTool(toolType: string): ServiceResponse<boolean> {
if (!this.currentSession) {
return createErrorResponse<boolean>(ServiceError.NOT_INITIALIZED, '没有活动的测验会话');
}
const userResult = this.userService.getCurrentUser();
if (!userResult.success || !userResult.data) {
return createErrorResponse<boolean>(ServiceError.NOT_FOUND, '用户不存在');
}
const user = userResult.data;
switch (toolType) {
case 'hint':
if (user.tools.hintCount <= 0) {
return createErrorResponse<boolean>(ServiceError.INVALID_PARAMS, '提示次数不足');
}
user.tools.hintCount--;
this.currentSession.toolsUsed.hint++;
break;
case 'skip':
if (user.tools.skipCount <= 0) {
return createErrorResponse<boolean>(ServiceError.INVALID_PARAMS, '跳过次数不足');
}
user.tools.skipCount--;
this.currentSession.toolsUsed.skip++;
// 跳过当前题目
this.currentSession.currentIndex++;
break;
case 'refresh':
if (user.tools.refreshCount <= 0) {
return createErrorResponse<boolean>(ServiceError.INVALID_PARAMS, '刷新次数不足');
}
user.tools.refreshCount--;
this.currentSession.toolsUsed.refresh++;
break;
case 'doubleScore':
if (user.tools.doubleScoreCount <= 0) {
return createErrorResponse<boolean>(ServiceError.INVALID_PARAMS, '双倍积分次数不足');
}
user.tools.doubleScoreCount--;
this.currentSession.toolsUsed.doubleScore = true;
break;
default:
return createErrorResponse<boolean>(ServiceError.INVALID_PARAMS, '未知工具类型');
}
// 保存用户工具更新
this.userService.updateUser(user.id, { tools: user.tools });
return createSuccessResponse<boolean>(true);
}
/**
* 结束测验
*/
async finishQuiz(): Promise<ServiceResponse<QuizRecord>> {
if (!this.currentSession) {
return createErrorResponse<QuizRecord>(ServiceError.NOT_INITIALIZED, '没有活动的测验会话');
}
try {
const session = this.currentSession;
// 获取用户
const userResult = await this.userService.getUserById(session.userId);
if (!userResult.success || !userResult.data) {
return createErrorResponse<QuizRecord>(ServiceError.NOT_FOUND, '用户不存在');
}
const user = userResult.data;
// 获取关卡
const level = this.levelService.getLevelById(session.levelId);
if (!level) {
return createErrorResponse<QuizRecord>(ServiceError.NOT_FOUND, '关卡不存在');
}
// 计算结果
const correctCount = session.answers.filter(a => a.isCorrect).length;
const totalCount = session.questions.length;
const accuracy = totalCount > 0 ? correctCount / totalCount : 0;
const stars = this.calculateStars(accuracy);
const timeUsed = this.calculateTotalTime(session);
// 计算奖励
let score = this.calculateScore(level, stars);
let experience = this.calculateExperience(level, stars);
// 应用双倍积分工具
if (session.toolsUsed.doubleScore) {
score *= 2;
}
// 创建测验记录
const record: QuizRecord = {
id: session.id,
userId: session.userId,
levelId: session.levelId,
startTime: session.startTime,
endTime: new Date().toISOString(),
totalQuestions: totalCount,
correctQuestions: correctCount,
accuracy,
score,
experience,
stars,
timeUsed,
questionResults: session.answers
};
// 更新用户统计
await this.updateUserStatistics(user, record);
// 更新关卡完成状态
await this.updateLevelCompletion(user, session.levelId, stars);
// 添加错题
await this.addWrongQuestions(user, session);
// 检查成就
await this.achievementService.checkAndUnlockAchievements(user);
// 保存测验记录到用户
user.quizHistory.push(record);
await this.userService.updateUser(user.id, {
quizHistory: user.quizHistory,
tools: user.tools
});
// 清理会话
this.currentSession = null;
return createSuccessResponse<QuizRecord>(record);
} catch (error) {
return createErrorResponse<QuizRecord>(ServiceError.ERROR, (error as Error).message);
}
}
/**
* 获取测验历史记录
*/
async getQuizHistory(userId: string, limit: number = 20): Promise<ServiceResponse<QuizRecord[]>> {
try {
const userResult = await this.userService.getUserById(userId);
if (!userResult.success || !userResult.data) {
return createErrorResponse<QuizRecord[]>(ServiceError.NOT_FOUND, '用户不存在');
}
const history = [...userResult.data.quizHistory]
.sort((a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime())
.slice(0, limit);
return createSuccessResponse<QuizRecord[]>(history);
} catch (error) {
return createErrorResponse<QuizRecord[]>(ServiceError.ERROR, (error as Error).message);
}
}
/**
* 获取关卡测验记录
*/
async getLevelRecords(userId: string, levelId: string): Promise<ServiceResponse<QuizRecord[]>> {
try {
const userResult = await this.userService.getUserById(userId);
if (!userResult.success || !userResult.data) {
return createErrorResponse<QuizRecord[]>(ServiceError.NOT_FOUND, '用户不存在');
}
const records = userResult.data.quizHistory
.filter(r => r.levelId === levelId)
.sort((a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime());
return createSuccessResponse<QuizRecord[]>(records);
} catch (error) {
return createErrorResponse<QuizRecord[]>(ServiceError.ERROR, (error as Error).message);
}
}
/**
* 获取错题本
*/
async getWrongQuestions(userId: string): Promise<ServiceResponse<Question[]>> {
try {
const userResult = await this.userService.getUserById(userId);
if (!userResult.success || !userResult.data) {
return createErrorResponse<Question[]>(ServiceError.NOT_FOUND, '用户不存在');
}
const wrongQuestionIds = userResult.data.wrongQuestions;
const questions = await this.questionService.getQuestionsByIds(wrongQuestionIds);
return createSuccessResponse<Question[]>(questions);
} catch (error) {
return createErrorResponse<Question[]>(ServiceError.ERROR, (error as Error).message);
}
}
/**
* 添加错题
*/
async addWrongQuestion(userId: string, questionId: string): Promise<ServiceResponse<boolean>> {
try {
const userResult = await this.userService.getUserById(userId);
if (!userResult.success || !userResult.data) {
return createErrorResponse<boolean>(ServiceError.NOT_FOUND, '用户不存在');
}
const user = userResult.data;
// 检查是否已存在
if (!user.wrongQuestions.includes(questionId)) {
user.wrongQuestions.push(questionId);
await this.userService.updateUser(userId, { wrongQuestions: user.wrongQuestions });
}
return createSuccessResponse<boolean>(true);
} catch (error) {
return createErrorResponse<boolean>(ServiceError.ERROR, (error as Error).message);
}
}
/**
* 移除错题
*/
async removeWrongQuestion(userId: string, questionId: string): Promise<ServiceResponse<boolean>> {
try {
const userResult = await this.userService.getUserById(userId);
if (!userResult.success || !userResult.data) {
return createErrorResponse<boolean>(ServiceError.NOT_FOUND, '用户不存在');
}
const user = userResult.data;
const index = user.wrongQuestions.indexOf(questionId);
if (index !== -1) {
user.wrongQuestions.splice(index, 1);
await this.userService.updateUser(userId, { wrongQuestions: user.wrongQuestions });
}
return createSuccessResponse<boolean>(true);
} catch (error) {
return createErrorResponse<boolean>(ServiceError.ERROR, (error as Error).message);
}
}
/**
* 获取测验统计
*/
async getQuizStatistics(userId: string): Promise<ServiceResponse<QuizStatistics>> {
try {
const userResult = await this.userService.getUserById(userId);
if (!userResult.success || !userResult.data) {
return createErrorResponse<QuizStatistics>(ServiceError.NOT_FOUND, '用户不存在');
}
const history = userResult.data.quizHistory;
if (history.length === 0) {
return createSuccessResponse<QuizStatistics>({
totalQuizzes: 0,
averageAccuracy: 0,
averageTime: 0,
highestStars: 0,
perfectCount: 0,
totalScore: 0,
totalExperience: 0
});
}
const totalQuizzes = history.length;
const averageAccuracy = history.reduce((sum, r) => sum + r.accuracy, 0) / totalQuizzes;
const averageTime = history.reduce((sum, r) => sum + r.timeUsed, 0) / totalQuizzes;
const highestStars = Math.max(...history.map(r => r.stars));
const perfectCount = history.filter(r => r.stars === 3).length;
const totalScore = history.reduce((sum, r) => sum + r.score, 0);
const totalExperience = history.reduce((sum, r) => sum + r.experience, 0);
return createSuccessResponse<QuizStatistics>({
totalQuizzes,
averageAccuracy,
averageTime,
highestStars,
perfectCount,
totalScore,
totalExperience
});
} catch (error) {
return createErrorResponse<QuizStatistics>(ServiceError.ERROR, (error as Error).message);
}
}
/**
* 检查答案是否正确
*/
private checkAnswer(question: Question, answer: string | string[]): boolean {
const correctAnswer = question.answer;
if (Array.isArray(correctAnswer)) {
// 多选题
if (!Array.isArray(answer)) {
return false;
}
const sortedCorrect = [...correctAnswer].sort();
const sortedAnswer = [...answer].sort();
return JSON.stringify(sortedCorrect) === JSON.stringify(sortedAnswer);
} else {
// 单选题、判断题、填空题
return String(answer) === String(correctAnswer);
}
}
/**
* 计算星级
*/
private calculateStars(accuracy: number): number {
if (accuracy >= 0.9) return 3;
if (accuracy >= 0.7) return 2;
if (accuracy >= 0.5) return 1;
return 0;
}
/**
* 计算积分
*/
private calculateScore(level: any, stars: number): number {
const baseScore = level.rewards?.baseScore || 100;
const multiplier = stars === 3 ? 2.0 : stars === 2 ? 1.5 : 1.0;
return Math.round(baseScore * multiplier);
}
/**
* 计算经验值
*/
private calculateExperience(level: any, stars: number): number {
const baseExp = level.rewards?.baseExperience || 50;
const multiplier = stars === 3 ? 2.0 : stars === 2 ? 1.5 : 1.0;
return Math.round(baseExp * multiplier);
}
/**
* 计算答题时间
*/
private calculateQuestionTime(): number {
// 实际实现中应该记录每题的开始时间
// 这里简化处理,返回一个模拟值
return Math.round(Math.random() * 10) + 5;
}
/**
* 计算总用时
*/
private calculateTotalTime(session: QuizSession): number {
return session.answers.reduce((sum, a) => sum + a.timeUsed, 0);
}
/**
* 更新用户统计
*/
private async updateUserStatistics(user: any, record: QuizRecord): Promise<void> {
// 更新统计数据
user.statistics.totalQuestions += record.totalQuestions;
user.statistics.correctQuestions += record.correctQuestions;
user.statistics.wrongQuestions += record.totalQuestions - record.correctQuestions;
user.statistics.averageAccuracy = user.statistics.correctQuestions / user.statistics.totalQuestions;
user.statistics.totalTime += record.timeUsed;
user.statistics.averageTimePerQuestion = user.statistics.totalTime / user.statistics.totalQuestions;
user.statistics.completedLevels++;
if (record.stars === 3) {
user.statistics.perfectLevels++;
}
// 更新积分和经验
user.totalScore += record.score;
user.totalExperience += record.experience;
// 检查升级
const expForNextLevel = user.level * 100;
if (user.totalExperience >= expForNextLevel) {
user.level++;
user.totalScore += user.level * 50; // 升级奖励
}
await this.userService.updateUser(user.id, {
statistics: user.statistics,
totalScore: user.totalScore,
totalExperience: user.totalExperience,
level: user.level
});
}
/**
* 更新关卡完成状态
*/
private async updateLevelCompletion(user: any, levelId: string, stars: number): Promise<void> {
// 添加到已完成关卡
if (!user.completedLevels.includes(levelId)) {
user.completedLevels.push(levelId);
}
// 更新星级(保留最高星级)
const currentStars = user.levelStars.get(levelId) || 0;
if (stars > currentStars) {
user.levelStars.set(levelId, stars);
}
await this.userService.updateUser(user.id, {
completedLevels: user.completedLevels,
levelStars: user.levelStars
});
}
/**
* 添加错题
*/
private async addWrongQuestions(user: any, session: QuizSession): Promise<void> {
const wrongAnswers = session.answers.filter(a => !a.isCorrect);
for (const answer of wrongAnswers) {
if (!user.wrongQuestions.includes(answer.questionId)) {
user.wrongQuestions.push(answer.questionId);
}
}
if (wrongAnswers.length > 0) {
await this.userService.updateUser(user.id, { wrongQuestions: user.wrongQuestions });
}
}
/**
* 生成会话ID
*/
private generateSessionId(): string {
return `quiz_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
/**
* 生成结果ID
*/
private generateResultId(): string {
return `result_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
代码解析
1. 测验会话创建流程
async createSession(userId: string, levelId: string): Promise<ServiceResponse<QuizSession>> {
// 1. 获取用户
// 2. 获取关卡
// 3. 检查关卡是否解锁
// 4. 获取题目
// 5. 创建会话
}
2. 答题处理逻辑
submitAnswer(questionId: string, answer: string | string[]): ServiceResponse<QuestionResult> {
// 1. 获取题目
// 2. 判断答案正确性
// 3. 记录答题结果
// 4. 更新当前索引
}
3. 星级计算逻辑
private calculateStars(accuracy: number): number {
if (accuracy >= 0.9) return 3; // 90%以上:3星
if (accuracy >= 0.7) return 2; // 70%-90%:2星
if (accuracy >= 0.5) return 1; // 50%-70%:1星
return 0; // 50%以下:0星
}
4. 奖励计算逻辑
private calculateScore(level: any, stars: number): number {
const baseScore = level.rewards?.baseScore || 100;
const multiplier = stars === 3 ? 2.0 : stars === 2 ? 1.5 : 1.0;
return Math.round(baseScore * multiplier);
}
⚠️ 常见问题与解决方案
问题1: 会话状态丢失
现象:
测验会话在页面刷新或应用重启后丢失。
错误代码:
// ❌ 错误:会话只存储在内存中
private currentSession: QuizSession | null = null;
正确代码:
// ✅ 正确:持久化会话状态
async createSession(userId: string, levelId: string): Promise<ServiceResponse<QuizSession>> {
const session: QuizSession = { /* ... */ };
// 保存到本地存储
await this.storageService.saveQuizSession(session);
this.currentSession = session;
return createSuccessResponse<QuizSession>(session);
}
// 初始化时恢复会话
async initialize(): Promise<void> {
const saved = await this.storageService.getQuizSession();
if (saved) {
this.currentSession = saved;
}
}
规则/建议:
- 将会话状态持久化到本地存储
- 在服务初始化时恢复会话
- 设置会话超时时间
问题2: 答题超时未处理
现象:
用户长时间不答题,超时后没有自动结束测验。
错误代码:
// ❌ 错误:没有超时处理
submitAnswer(questionId: string, answer: string | string[]): ServiceResponse<QuestionResult> {
// 直接处理答案
}
正确代码:
// ✅ 正确:添加超时处理
createSession(userId: string, levelId: string): Promise<ServiceResponse<QuizSession>> {
const session: QuizSession = {
// ...
startTime: new Date().toISOString(),
timeLimit: level.timeLimit * 1000 // 毫秒
};
// 设置超时定时器
setTimeout(() => {
this.handleTimeout(session.id);
}, session.timeLimit);
return createSuccessResponse<QuizSession>(session);
}
private handleTimeout(sessionId: string): void {
if (this.currentSession?.id === sessionId) {
// 自动提交未完成的题目
this.autoSubmitRemaining();
this.finishQuiz();
}
}
规则/建议:
- 设置超时定时器
- 超时后自动提交剩余题目
- 通知用户超时情况
问题3: 答案比较逻辑错误
现象:
多选题答案比较时顺序不同被判定为错误。
错误代码:
// ❌ 错误:直接比较数组
private checkAnswer(question: Question, answer: string | string[]): boolean {
return JSON.stringify(question.answer) === JSON.stringify(answer);
}
正确代码:
// ✅ 正确:排序后比较
private checkAnswer(question: Question, answer: string | string[]): boolean {
const correctAnswer = question.answer;
if (Array.isArray(correctAnswer)) {
if (!Array.isArray(answer)) {
return false;
}
const sortedCorrect = [...correctAnswer].sort();
const sortedAnswer = [...answer].sort();
return JSON.stringify(sortedCorrect) === JSON.stringify(sortedAnswer);
} else {
return String(answer) === String(correctAnswer);
}
}
规则/建议:
- 多选题答案排序后比较
- 统一答案格式(字符串化)
- 处理类型不一致情况
问题4: 工具使用未扣除次数
现象:
使用工具后用户的工具次数没有减少。
错误代码:
// ❌ 错误:没有更新用户工具次数
useTool(toolType: string): ServiceResponse<boolean> {
this.currentSession.toolsUsed.hint++;
return createSuccessResponse<boolean>(true);
}
正确代码:
// ✅ 正确:更新用户工具次数
useTool(toolType: string): ServiceResponse<boolean> {
const user = this.userService.getCurrentUser().data;
switch (toolType) {
case 'hint':
if (user.tools.hintCount <= 0) {
return createErrorResponse<boolean>(ServiceError.INVALID_PARAMS, '提示次数不足');
}
user.tools.hintCount--; // 扣除次数
this.currentSession.toolsUsed.hint++;
break;
}
// 保存更新
this.userService.updateUser(user.id, { tools: user.tools });
return createSuccessResponse<boolean>(true);
}
规则/建议:
- 使用工具前检查剩余次数
- 使用后扣除次数
- 保存用户数据更新
问题5: 错题重复添加
现象:
同一道错题被多次添加到错题本。
错误代码:
// ❌ 错误:没有检查是否已存在
addWrongQuestion(userId: string, questionId: string): Promise<ServiceResponse<boolean>> {
user.wrongQuestions.push(questionId);
}
正确代码:
// ✅ 正确:检查后添加
addWrongQuestion(userId: string, questionId: string): Promise<ServiceResponse<boolean>> {
if (!user.wrongQuestions.includes(questionId)) {
user.wrongQuestions.push(questionId);
}
}
规则/建议:
- 添加前检查是否已存在
- 使用 Set 数据结构或 includes() 检查
- 避免重复添加
📝 本章小结
核心知识点
本文详细讲解了 QuizService 测验服务的实现,主要包括:
1. 测验会话管理
- 创建和获取会话
- 会话状态维护
2. 答题流程
- 答案提交和正确性判断
- 工具使用(提示、跳过、刷新、双倍积分)
3. 结果计算
- 正确率和星级计算
- 积分和经验奖励
4. 统计更新
- 用户统计数据更新
- 关卡完成状态更新
- 错题本管理
最佳实践总结
✅ 会话管理
// 持久化会话
await this.storageService.saveQuizSession(session);
// 恢复会话
const saved = await this.storageService.getQuizSession();
✅ 答案比较
// 多选题排序后比较
const sortedCorrect = [...correctAnswer].sort();
const sortedAnswer = [...answer].sort();
return JSON.stringify(sortedCorrect) === JSON.stringify(sortedAnswer);
✅ 星级计算
if (accuracy >= 0.9) return 3;
if (accuracy >= 0.7) return 2;
if (accuracy >= 0.5) return 1;
return 0;
✅ 工具使用
// 检查次数并扣除
if (user.tools.hintCount <= 0) {
return createErrorResponse(ServiceError.INVALID_PARAMS, '提示次数不足');
}
user.tools.hintCount--;
下一步预告
在下一篇文章中,我们将:
- 🎨 讲解 LevelService 关卡服务实现详解
- 📚 介绍关卡解锁、进度管理和奖励发放
- 🏷️ 探索关卡数据的增删改查操作
🔗 相关链接
- 项目源码: Atomgit仓库
💡 提示: 建议结合项目源码阅读,动手实践效果更好!
更多推荐


所有评论(0)