HarmonyOS应用<趣答>开发第07篇:LevelService关卡服务实现详解——管理游戏关卡与进度
·

📖 引言
在知识问答学习应用中,关卡服务(LevelService)是管理关卡数据和用户进度的核心组件。它负责处理关卡的解锁、完成状态、进度查询等关键操作,是连接用户学习路径和游戏化体验的桥梁。
本文将详细讲解 LevelService 的完整实现,包括关卡数据管理、解锁机制、进度跟踪等核心功能。通过本文,你将掌握:
- 如何设计关卡数据管理机制
- 如何实现关卡解锁和完成逻辑
- 如何跟踪用户关卡进度
- 如何在实际项目中管理关卡奖励
🎯 学习目标
完成本文后,你将能够:
- ✅ 理解 LevelService 的核心职责和功能
- ✅ 实现关卡数据的增删改查
- ✅ 管理关卡解锁和完成状态
- ✅ 计算关卡进度和奖励
- ✅ 集成学科和关卡的关联关系
💡 需求分析
功能模块设计
| 模块 | 功能描述 | 技术要点 |
|---|---|---|
| 关卡数据管理 | CRUD操作关卡数据 | 数据持久化、缓存 |
| 解锁机制 | 管理关卡解锁条件 | 前置关卡、用户等级 |
| 进度跟踪 | 跟踪用户关卡进度 | 完成状态、星级 |
| 奖励计算 | 计算关卡通关奖励 | 积分、经验、星级倍数 |
| 学科管理 | 管理学科和关卡的关联 | 一对多关系 |
🛠️ 核心实现
步骤1: LevelService 接口设计
功能说明
定义 LevelService 的核心接口,规范服务的方法签名和返回类型。
完整代码
// services/interfaces/LevelServiceInterface.ts
import { ServiceResponse } from './ServiceInterface';
import { Level, Subject } from '../models/Level';
import { User } from '../models/User';
/**
* 关卡服务接口
*/
export interface LevelServiceInterface {
/**
* 获取所有学科
* @returns 学科列表
*/
getAllSubjects(): ServiceResponse<Subject[]>;
/**
* 根据ID获取学科
* @param subjectId - 学科ID
* @returns 学科信息
*/
getSubjectById(subjectId: string): ServiceResponse<Subject | undefined>;
/**
* 获取所有关卡
* @returns 关卡列表
*/
getAllLevels(): ServiceResponse<Level[]>;
/**
* 根据ID获取关卡
* @param levelId - 关卡ID
* @returns 关卡信息
*/
getLevelById(levelId: string): ServiceResponse<Level | undefined>;
/**
* 获取学科下的所有关卡
* @param subjectId - 学科ID
* @returns 关卡列表
*/
getLevelsBySubject(subjectId: string): ServiceResponse<Level[]>;
/**
* 获取用户可用的关卡
* @param user - 用户对象
* @returns 可用关卡列表
*/
getAvailableLevels(user: User): ServiceResponse<Level[]>;
/**
* 判断关卡是否已解锁
* @param levelId - 关卡ID
* @param user - 用户对象
* @returns 是否已解锁
*/
isLevelUnlocked(levelId: string, user: User): ServiceResponse<boolean>;
/**
* 判断关卡是否已完成
* @param levelId - 关卡ID
* @param user - 用户对象
* @returns 是否已完成
*/
isLevelCompleted(levelId: string, user: User): ServiceResponse<boolean>;
/**
* 获取关卡星级
* @param levelId - 关卡ID
* @param user - 用户对象
* @returns 星级(0-3)
*/
getLevelStars(levelId: string, user: User): ServiceResponse<number>;
/**
* 获取学科进度
* @param subjectId - 学科ID
* @param user - 用户对象
* @returns 进度信息
*/
getSubjectProgress(subjectId: string, user: User): ServiceResponse<SubjectProgress>;
/**
* 获取用户关卡进度摘要
* @param user - 用户对象
* @returns 进度摘要
*/
getUserLevelProgress(user: User): ServiceResponse<UserLevelProgress>;
/**
* 更新关卡完成状态
* @param levelId - 关卡ID
* @param user - 用户对象
* @param stars - 星级
* @returns 是否成功
*/
updateLevelCompletion(levelId: string, user: User, stars: number): ServiceResponse<boolean>;
/**
* 计算关卡奖励
* @param levelId - 关卡ID
* @param stars - 星级
* @returns 奖励信息
*/
calculateLevelRewards(levelId: string, stars: number): ServiceResponse<LevelReward>;
/**
* 添加新关卡
* @param level - 关卡信息
* @returns 创建的关卡
*/
addLevel(level: Level): ServiceResponse<Level>;
/**
* 更新关卡信息
* @param levelId - 关卡ID
* @param updates - 更新内容
* @returns 更新后的关卡
*/
updateLevel(levelId: string, updates: Partial<Level>): ServiceResponse<Level>;
/**
* 删除关卡
* @param levelId - 关卡ID
* @returns 是否成功
*/
deleteLevel(levelId: string): ServiceResponse<boolean>;
}
/**
* 学科进度接口
*/
export interface SubjectProgress {
subjectId: string; // 学科ID
subjectName: string; // 学科名称
totalLevels: number; // 总关卡数
completedLevels: number; // 已完成关卡数
unlockedLevels: number; // 已解锁关卡数
progressPercent: number; // 进度百分比
stars: number; // 总星级
perfectLevels: number; // 满分关卡数
}
/**
* 用户关卡进度摘要
*/
export interface UserLevelProgress {
totalLevels: number; // 总关卡数
completedLevels: number; // 已完成关卡数
unlockedLevels: number; // 已解锁关卡数
totalStars: number; // 总星级
perfectLevels: number; // 满分关卡数
subjectProgress: SubjectProgress[]; // 各学科进度
}
/**
* 关卡奖励接口
*/
export interface LevelReward {
score: number; // 积分奖励
experience: number; // 经验奖励
starMultiplier: number; // 星级倍数
}
代码解析
1. 接口方法分类
| 分类 | 方法 | 功能说明 |
|---|---|---|
| 学科管理 | getAllSubjects(), getSubjectById() |
获取学科信息 |
| 关卡查询 | getAllLevels(), getLevelById(), getLevelsBySubject() |
查询关卡数据 |
| 进度判断 | isLevelUnlocked(), isLevelCompleted(), getLevelStars() |
判断关卡状态 |
| 进度统计 | getSubjectProgress(), getUserLevelProgress() |
获取进度信息 |
| 状态更新 | updateLevelCompletion() |
更新关卡完成状态 |
| 奖励计算 | calculateLevelRewards() |
计算关卡奖励 |
| 数据管理 | addLevel(), updateLevel(), deleteLevel() |
关卡CRUD操作 |
步骤2: LevelService 实现
功能说明
实现 LevelService 的核心逻辑,包括关卡数据管理、解锁机制、进度跟踪等功能。
完整代码
// services/LevelService.ts
import { Singleton } from './base/BaseService';
import { BaseService } from './base/BaseService';
import { ServiceResponse, ServiceError, createSuccessResponse, createErrorResponse } from './interfaces/ServiceInterface';
import { LevelServiceInterface, SubjectProgress, UserLevelProgress, LevelReward } from './interfaces/LevelServiceInterface';
import { Level, Subject, Difficulty } from '../models/Level';
import { User } from '../models/User';
/**
* 关卡服务实现
*/
@Singleton
export class LevelService extends BaseService implements LevelServiceInterface {
private subjects: Subject[] = [];
constructor() {
super();
this.initializeSubjects();
}
/**
* 初始化学科和关卡数据
*/
private initializeSubjects(): void {
// 初始化学科数据
this.subjects = this.loadSubjectData();
}
/**
* 加载学科数据(模拟)
*/
private loadSubjectData(): Subject[] {
// 实际项目中从服务器或本地存储加载
return [
this.createMathSubject(),
this.createGeographySubject(),
this.createLifeSubject(),
this.createLiteratureSubject(),
this.createSportsSubject()
];
}
/**
* 创建数学学科
*/
private createMathSubject(): Subject {
return {
id: 'math',
name: '数学',
description: '培养数学思维,提升逻辑推理能力',
icon: 'https://example.com/icons/math.png',
color: '#4A90D9',
levels: [
this.createLevel('math', 1, Difficulty.EASY),
this.createLevel('math', 2, Difficulty.EASY),
this.createLevel('math', 3, Difficulty.MEDIUM),
this.createLevel('math', 4, Difficulty.MEDIUM),
this.createLevel('math', 5, Difficulty.HARD),
this.createLevel('math', 6, Difficulty.HARD),
this.createLevel('math', 7, Difficulty.EXPERT),
this.createLevel('math', 8, Difficulty.EXPERT)
]
};
}
/**
* 创建地理学科
*/
private createGeographySubject(): Subject {
return {
id: 'geography',
name: '地理',
description: '探索地球奥秘,了解世界文化',
icon: 'https://example.com/icons/geography.png',
color: '#4CAF50',
levels: [
this.createLevel('geography', 1, Difficulty.EASY),
this.createLevel('geography', 2, Difficulty.EASY),
this.createLevel('geography', 3, Difficulty.MEDIUM),
this.createLevel('geography', 4, Difficulty.MEDIUM),
this.createLevel('geography', 5, Difficulty.HARD),
this.createLevel('geography', 6, Difficulty.HARD),
this.createLevel('geography', 7, Difficulty.EXPERT),
this.createLevel('geography', 8, Difficulty.EXPERT)
]
};
}
/**
* 创建生活学科
*/
private createLifeSubject(): Subject {
return {
id: 'life',
name: '生活',
description: '学习生活常识,提升生活技能',
icon: 'https://example.com/icons/life.png',
color: '#FF9800',
levels: [
this.createLevel('life', 1, Difficulty.EASY),
this.createLevel('life', 2, Difficulty.EASY),
this.createLevel('life', 3, Difficulty.MEDIUM),
this.createLevel('life', 4, Difficulty.MEDIUM),
this.createLevel('life', 5, Difficulty.HARD),
this.createLevel('life', 6, Difficulty.HARD),
this.createLevel('life', 7, Difficulty.EXPERT),
this.createLevel('life', 8, Difficulty.EXPERT)
]
};
}
/**
* 创建文学学科
*/
private createLiteratureSubject(): Subject {
return {
id: 'literature',
name: '文学',
description: '品味文学经典,提升文化修养',
icon: 'https://example.com/icons/literature.png',
color: '#9C27B0',
levels: [
this.createLevel('literature', 1, Difficulty.EASY),
this.createLevel('literature', 2, Difficulty.EASY),
this.createLevel('literature', 3, Difficulty.MEDIUM),
this.createLevel('literature', 4, Difficulty.MEDIUM),
this.createLevel('literature', 5, Difficulty.HARD),
this.createLevel('literature', 6, Difficulty.HARD),
this.createLevel('literature', 7, Difficulty.EXPERT),
this.createLevel('literature', 8, Difficulty.EXPERT)
]
};
}
/**
* 创建体育学科
*/
private createSportsSubject(): Subject {
return {
id: 'sports',
name: '体育',
description: '了解体育知识,热爱运动生活',
icon: 'https://example.com/icons/sports.png',
color: '#F44336',
levels: [
this.createLevel('sports', 1, Difficulty.EASY),
this.createLevel('sports', 2, Difficulty.EASY),
this.createLevel('sports', 3, Difficulty.MEDIUM),
this.createLevel('sports', 4, Difficulty.MEDIUM),
this.createLevel('sports', 5, Difficulty.HARD),
this.createLevel('sports', 6, Difficulty.HARD),
this.createLevel('sports', 7, Difficulty.EXPERT),
this.createLevel('sports', 8, Difficulty.EXPERT)
]
};
}
/**
* 创建关卡
*/
private createLevel(subjectId: string, order: number, difficulty: Difficulty): Level {
const difficultyLevel = this.getDifficultyLevel(difficulty);
const subject = this.subjects.find(s => s.id === subjectId);
return {
id: `${subjectId}_level_${order}`,
subjectId,
subjectName: subject?.name || '',
name: this.getLevelName(subjectId, order),
description: this.getLevelDescription(difficulty),
difficulty,
difficultyLevel,
order,
unlockCondition: this.getUnlockCondition(subjectId, order),
questionCount: 10,
timeLimit: this.getTimeLimit(difficulty),
rewards: {
baseScore: this.getBaseScore(difficulty),
baseExperience: this.getBaseExperience(difficulty),
star1Multiplier: 1.0,
star2Multiplier: 1.5,
star3Multiplier: 2.0
},
icon: `https://example.com/levels/${subjectId}_${order}.png`,
background: `https://example.com/bg/${subjectId}_${order}.jpg`
};
}
/**
* 获取难度等级数值
*/
private getDifficultyLevel(difficulty: Difficulty): number {
switch (difficulty) {
case Difficulty.EASY: return 1;
case Difficulty.MEDIUM: return 2;
case Difficulty.HARD: return 3;
case Difficulty.EXPERT: return 4;
default: return 1;
}
}
/**
* 获取关卡名称
*/
private getLevelName(subjectId: string, order: number): string {
const names: Record<string, string[]> = {
math: ['数学入门', '基础运算', '代数基础', '几何初步', '函数进阶', '概率统计', '微积分入门', '数学竞赛'],
geography: ['地理入门', '地球概览', '气候环境', '人文地理', '区域分析', '地图技能', '地理综合', '地理挑战'],
life: ['生活入门', '日常常识', '健康知识', '安全防护', '应急处理', '生活技能', '生活综合', '生活挑战'],
literature: ['文学入门', '诗词鉴赏', '名著导读', '文学流派', '写作技巧', '文学理论', '文学综合', '文学挑战'],
sports: ['体育入门', '球类运动', '田径运动', '水上运动', '冰雪运动', '武术搏击', '体育综合', '体育挑战']
};
return names[subjectId]?.[order - 1] || `第${order}关`;
}
/**
* 获取关卡描述
*/
private getLevelDescription(difficulty: Difficulty): string {
const descriptions: Record<Difficulty, string> = {
[Difficulty.EASY]: '基础关卡,适合新手入门',
[Difficulty.MEDIUM]: '进阶关卡,需要一定知识储备',
[Difficulty.HARD]: '困难关卡,考验你的能力',
[Difficulty.EXPERT]: '专家关卡,挑战极限'
};
return descriptions[difficulty];
}
/**
* 获取解锁条件
*/
private getUnlockCondition(subjectId: string, order: number): string {
if (order === 1) {
return '无(初始关卡)';
}
return `通关"${this.getLevelName(subjectId, order - 1)}"`;
}
/**
* 获取时间限制(秒)
*/
private getTimeLimit(difficulty: Difficulty): number {
const limits: Record<Difficulty, number> = {
[Difficulty.EASY]: 300, // 5分钟
[Difficulty.MEDIUM]: 240, // 4分钟
[Difficulty.HARD]: 180, // 3分钟
[Difficulty.EXPERT]: 180 // 3分钟
};
return limits[difficulty];
}
/**
* 获取基础积分
*/
private getBaseScore(difficulty: Difficulty): number {
const scores: Record<Difficulty, number> = {
[Difficulty.EASY]: 100,
[Difficulty.MEDIUM]: 150,
[Difficulty.HARD]: 200,
[Difficulty.EXPERT]: 300
};
return scores[difficulty];
}
/**
* 获取基础经验
*/
private getBaseExperience(difficulty: Difficulty): number {
const exp: Record<Difficulty, number> = {
[Difficulty.EASY]: 50,
[Difficulty.MEDIUM]: 75,
[Difficulty.HARD]: 100,
[Difficulty.EXPERT]: 150
};
return exp[difficulty];
}
/**
* 获取所有学科
*/
getAllSubjects(): ServiceResponse<Subject[]> {
return createSuccessResponse<Subject[]>(this.subjects);
}
/**
* 根据ID获取学科
*/
getSubjectById(subjectId: string): ServiceResponse<Subject | undefined> {
const subject = this.subjects.find(s => s.id === subjectId);
return createSuccessResponse<Subject | undefined>(subject);
}
/**
* 获取所有关卡
*/
getAllLevels(): ServiceResponse<Level[]> {
const allLevels = this.subjects.flatMap(s => s.levels);
return createSuccessResponse<Level[]>(allLevels);
}
/**
* 根据ID获取关卡
*/
getLevelById(levelId: string): ServiceResponse<Level | undefined> {
for (const subject of this.subjects) {
const level = subject.levels.find(l => l.id === levelId);
if (level) {
return createSuccessResponse<Level | undefined>(level);
}
}
return createSuccessResponse<Level | undefined>(undefined);
}
/**
* 获取学科下的所有关卡
*/
getLevelsBySubject(subjectId: string): ServiceResponse<Level[]> {
const subject = this.subjects.find(s => s.id === subjectId);
return createSuccessResponse<Level[]>(subject?.levels || []);
}
/**
* 获取用户可用的关卡
*/
getAvailableLevels(user: User): ServiceResponse<Level[]> {
const available: Level[] = [];
for (const subject of this.subjects) {
for (const level of subject.levels) {
if (this.isLevelUnlockedInternal(level.id, user)) {
available.push(level);
}
}
}
return createSuccessResponse<Level[]>(available);
}
/**
* 判断关卡是否已解锁(内部方法)
*/
private isLevelUnlockedInternal(levelId: string, user: User): boolean {
const level = this.getLevelByIdInternal(levelId);
if (!level) {
return false;
}
// 第一个关卡默认解锁
if (level.order === 1) {
return true;
}
// 检查是否在已解锁列表中
if (user.unlockedLevels.includes(levelId)) {
return true;
}
// 检查前置关卡是否完成
const subject = this.subjects.find(s => s.id === level.subjectId);
if (!subject) {
return false;
}
const previousLevel = subject.levels.find(l => l.order === level.order - 1);
if (!previousLevel) {
return true;
}
return user.completedLevels.includes(previousLevel.id);
}
/**
* 判断关卡是否已解锁(公开方法)
*/
isLevelUnlocked(levelId: string, user: User): ServiceResponse<boolean> {
const unlocked = this.isLevelUnlockedInternal(levelId, user);
return createSuccessResponse<boolean>(unlocked);
}
/**
* 判断关卡是否已完成
*/
isLevelCompleted(levelId: string, user: User): ServiceResponse<boolean> {
const completed = user.completedLevels.includes(levelId);
return createSuccessResponse<boolean>(completed);
}
/**
* 获取关卡星级
*/
getLevelStars(levelId: string, user: User): ServiceResponse<number> {
const stars = user.levelStars.get(levelId) || 0;
return createSuccessResponse<number>(stars);
}
/**
* 获取学科进度
*/
getSubjectProgress(subjectId: string, user: User): ServiceResponse<SubjectProgress> {
const subject = this.subjects.find(s => s.id === subjectId);
if (!subject) {
return createErrorResponse<SubjectProgress>(ServiceError.NOT_FOUND, '学科不存在');
}
const totalLevels = subject.levels.length;
const completedLevels = subject.levels.filter(l => user.completedLevels.includes(l.id)).length;
const unlockedLevels = subject.levels.filter(l => this.isLevelUnlockedInternal(l.id, user)).length;
const stars = subject.levels.reduce((sum, l) => sum + (user.levelStars.get(l.id) || 0), 0);
const perfectLevels = subject.levels.filter(l => user.levelStars.get(l.id) === 3).length;
return createSuccessResponse<SubjectProgress>({
subjectId: subject.id,
subjectName: subject.name,
totalLevels,
completedLevels,
unlockedLevels,
progressPercent: totalLevels > 0 ? (completedLevels / totalLevels) * 100 : 0,
stars,
perfectLevels
});
}
/**
* 获取用户关卡进度摘要
*/
getUserLevelProgress(user: User): ServiceResponse<UserLevelProgress> {
const allLevels = this.getAllLevels().data || [];
const totalLevels = allLevels.length;
const completedLevels = allLevels.filter(l => user.completedLevels.includes(l.id)).length;
const unlockedLevels = allLevels.filter(l => this.isLevelUnlockedInternal(l.id, user)).length;
const totalStars = allLevels.reduce((sum, l) => sum + (user.levelStars.get(l.id) || 0), 0);
const perfectLevels = allLevels.filter(l => user.levelStars.get(l.id) === 3).length;
const subjectProgress = this.subjects.map(s => {
return this.getSubjectProgress(s.id, user).data!;
});
return createSuccessResponse<UserLevelProgress>({
totalLevels,
completedLevels,
unlockedLevels,
totalStars,
perfectLevels,
subjectProgress
});
}
/**
* 更新关卡完成状态
*/
updateLevelCompletion(levelId: string, user: User, stars: number): ServiceResponse<boolean> {
const level = this.getLevelByIdInternal(levelId);
if (!level) {
return createErrorResponse<boolean>(ServiceError.NOT_FOUND, '关卡不存在');
}
// 添加到已完成关卡
if (!user.completedLevels.includes(levelId)) {
user.completedLevels.push(levelId);
}
// 更新星级(保留最高星级)
const currentStars = user.levelStars.get(levelId) || 0;
if (stars > currentStars) {
user.levelStars.set(levelId, stars);
}
// 解锁下一关
this.unlockNextLevel(level, user);
return createSuccessResponse<boolean>(true);
}
/**
* 解锁下一关
*/
private unlockNextLevel(level: Level, user: User): void {
const subject = this.subjects.find(s => s.id === level.subjectId);
if (!subject) {
return;
}
const nextLevel = subject.levels.find(l => l.order === level.order + 1);
if (nextLevel && !user.unlockedLevels.includes(nextLevel.id)) {
user.unlockedLevels.push(nextLevel.id);
}
}
/**
* 计算关卡奖励
*/
calculateLevelRewards(levelId: string, stars: number): ServiceResponse<LevelReward> {
const level = this.getLevelByIdInternal(levelId);
if (!level) {
return createErrorResponse<LevelReward>(ServiceError.NOT_FOUND, '关卡不存在');
}
const rewards = level.rewards;
const multiplier = stars === 3 ? rewards.star3Multiplier :
stars === 2 ? rewards.star2Multiplier : rewards.star1Multiplier;
return createSuccessResponse<LevelReward>({
score: Math.round(rewards.baseScore * multiplier),
experience: Math.round(rewards.baseExperience * multiplier),
starMultiplier: multiplier
});
}
/**
* 添加新关卡
*/
addLevel(level: Level): ServiceResponse<Level> {
const subject = this.subjects.find(s => s.id === level.subjectId);
if (!subject) {
return createErrorResponse<Level>(ServiceError.NOT_FOUND, '学科不存在');
}
// 检查ID是否已存在
if (subject.levels.find(l => l.id === level.id)) {
return createErrorResponse<Level>(ServiceError.CONFLICT, '关卡ID已存在');
}
subject.levels.push(level);
return createSuccessResponse<Level>(level);
}
/**
* 更新关卡信息
*/
updateLevel(levelId: string, updates: Partial<Level>): ServiceResponse<Level> {
for (const subject of this.subjects) {
const index = subject.levels.findIndex(l => l.id === levelId);
if (index !== -1) {
subject.levels[index] = { ...subject.levels[index], ...updates };
return createSuccessResponse<Level>(subject.levels[index]);
}
}
return createErrorResponse<Level>(ServiceError.NOT_FOUND, '关卡不存在');
}
/**
* 删除关卡
*/
deleteLevel(levelId: string): ServiceResponse<boolean> {
for (const subject of this.subjects) {
const index = subject.levels.findIndex(l => l.id === levelId);
if (index !== -1) {
subject.levels.splice(index, 1);
return createSuccessResponse<boolean>(true);
}
}
return createErrorResponse<boolean>(ServiceError.NOT_FOUND, '关卡不存在');
}
/**
* 根据ID获取关卡(内部方法)
*/
private getLevelByIdInternal(levelId: string): Level | undefined {
for (const subject of this.subjects) {
const level = subject.levels.find(l => l.id === levelId);
if (level) {
return level;
}
}
return undefined;
}
}
代码解析
1. 关卡解锁逻辑
private isLevelUnlockedInternal(levelId: string, user: User): boolean {
const level = this.getLevelByIdInternal(levelId);
// 第一个关卡默认解锁
if (level.order === 1) {
return true;
}
// 检查是否在已解锁列表中
if (user.unlockedLevels.includes(levelId)) {
return true;
}
// 检查前置关卡是否完成
const previousLevel = subject.levels.find(l => l.order === level.order - 1);
return user.completedLevels.includes(previousLevel.id);
}
2. 关卡奖励计算
calculateLevelRewards(levelId: string, stars: number): ServiceResponse<LevelReward> {
const rewards = level.rewards;
const multiplier = stars === 3 ? rewards.star3Multiplier :
stars === 2 ? rewards.star2Multiplier : rewards.star1Multiplier;
return {
score: Math.round(rewards.baseScore * multiplier),
experience: Math.round(rewards.baseExperience * multiplier),
starMultiplier: multiplier
};
}
3. 自动解锁下一关
private unlockNextLevel(level: Level, user: User): void {
const nextLevel = subject.levels.find(l => l.order === level.order + 1);
if (nextLevel && !user.unlockedLevels.includes(nextLevel.id)) {
user.unlockedLevels.push(nextLevel.id);
}
}
⚠️ 常见问题与解决方案
问题1: 关卡顺序不连续
现象:
关卡顺序跳号或重复,导致解锁逻辑错误。
错误代码:
// ❌ 错误:顺序不连续
levels: [
{ id: 'math_level_1', order: 1 },
{ id: 'math_level_2', order: 3 }, // 跳过了 order=2
{ id: 'math_level_3', order: 2 } // 顺序混乱
]
正确代码:
// ✅ 正确:顺序连续且递增
levels: [
{ id: 'math_level_1', order: 1 },
{ id: 'math_level_2', order: 2 },
{ id: 'math_level_3', order: 3 }
]
规则/建议:
- 关卡顺序必须从1开始连续递增
- 使用一致的命名格式
- 避免跳号和重复
问题2: 解锁条件检查错误
现象:
前置关卡完成后,下一关没有自动解锁。
错误代码:
// ❌ 错误:没有解锁下一关
updateLevelCompletion(levelId: string, user: User, stars: number): ServiceResponse<boolean> {
user.completedLevels.push(levelId); // 只标记完成,没有解锁下一关
}
正确代码:
// ✅ 正确:完成后解锁下一关
updateLevelCompletion(levelId: string, user: User, stars: number): ServiceResponse<boolean> {
user.completedLevels.push(levelId);
this.unlockNextLevel(level, user); // 解锁下一关
}
规则/建议:
- 完成关卡后自动解锁下一关
- 使用
unlockNextLevel()方法 - 确保解锁逻辑正确执行
问题3: 星级计算错误
现象:
关卡星级计算不正确,导致奖励错误。
错误代码:
// ❌ 错误:星级判断逻辑错误
calculateLevelRewards(levelId: string, stars: number): ServiceResponse<LevelReward> {
const multiplier = stars === 1 ? 2.0 : stars === 2 ? 1.5 : 1.0; // 逻辑颠倒
}
正确代码:
// ✅ 正确:星级越高倍数越大
calculateLevelRewards(levelId: string, stars: number): ServiceResponse<LevelReward> {
const multiplier = stars === 3 ? 2.0 : stars === 2 ? 1.5 : 1.0;
}
规则/建议:
- 3星奖励最高(2.0倍)
- 2星次之(1.5倍)
- 1星最低(1.0倍)
问题4: 关卡数据不同步
现象:
关卡数据在内存中修改后没有持久化,导致重启后丢失。
错误代码:
// ❌ 错误:只修改内存数据
addLevel(level: Level): ServiceResponse<Level> {
subject.levels.push(level); // 没有保存到持久化存储
}
正确代码:
// ✅ 正确:同步持久化
addLevel(level: Level): ServiceResponse<Level> {
subject.levels.push(level);
await this.storageService.saveLevels(this.subjects); // 保存到存储
}
规则/建议:
- 修改关卡数据后立即保存
- 使用 StorageService 持久化
- 在初始化时恢复数据
问题5: 学科和关卡关联错误
现象:
关卡的 subjectId 与实际所属学科不匹配。
错误代码:
// ❌ 错误:subjectId 不匹配
const level: Level = {
id: 'math_level_1',
subjectId: 'geography', // 错误:应该是 'math'
subjectName: '数学'
};
正确代码:
// ✅ 正确:subjectId 匹配
const level: Level = {
id: 'math_level_1',
subjectId: 'math',
subjectName: '数学'
};
规则/建议:
- 确保 subjectId 与所属学科一致
- 使用统一的命名规范
- 在创建关卡时验证关联关系
📝 本章小结
核心知识点
本文详细讲解了 LevelService 关卡服务的实现,主要包括:
1. 学科和关卡管理
- 学科数据结构设计
- 关卡数据的增删改查
2. 解锁机制
- 前置关卡完成解锁
- 自动解锁下一关
3. 进度跟踪
- 完成状态管理
- 星级记录和更新
4. 奖励计算
- 根据星级计算奖励
- 积分和经验奖励
最佳实践总结
✅ 关卡命名规范
id: 'math_level_1' // {subjectId}_level_{order}
✅ 解锁逻辑
if (level.order === 1) return true;
return user.completedLevels.includes(previousLevel.id);
✅ 奖励计算
const multiplier = stars === 3 ? 2.0 : stars === 2 ? 1.5 : 1.0;
const score = baseScore * multiplier;
✅ 自动解锁
const nextLevel = subject.levels.find(l => l.order === level.order + 1);
if (nextLevel) user.unlockedLevels.push(nextLevel.id);
下一步预告
在下一篇文章中,我们将:
- 🎨 讲解 AchievementService 成就服务实现详解
- 📚 介绍成就解锁、进度跟踪和奖励发放
- 🏷️ 探索成就数据的增删改查操作
🔗 相关链接
- 项目源码: Atomgit仓库
💡 提示: 建议结合项目源码阅读,动手实践效果更好!
更多推荐


所有评论(0)