reward-service.ts — 奖励服务
文件: web/src/services/reward-service.ts
职责
所有模块的经验值(EXP)/ 积分(Points)/ 徽章发放的统一入口。确保奖励规则一致、去重、可追溯。所有奖励发放通过 useAchievementStore() 统一处理。
奖励常量
typescript
export const REWARD = {
WRITE_COMPLETE: { exp: 10, points: 1, source: "write_complete" },
WRITE_PERFECT: { exp: 10, points: 1, source: "write_perfect" },
FIRST_LEARN: { exp: 5, points: 1, source: "first_learn" },
RECOGNITION_PASS: { exp: 5, points: 0, source: "recognition_pass" },
RECOGNITION_FAIL: { exp: 1, points: 0, source: "recognition_fail" },
REVIEW_CHAR: { exp: 5, points: 1, source: "review_char" },
DAILY_REVIEW_FULL: { exp: 50, points: 5, source: "daily_review" },
DAILY_REVIEW_80: { exp: 30, points: 3, source: "daily_review" },
BADGE_UNLOCK: { exp: 50, points: 5, source: "badge_unlock" },
};| 常量 | EXP | 积分 | 触发场景 |
|---|---|---|---|
WRITE_COMPLETE | 10 | 1 | 完成一次书写练习 |
WRITE_PERFECT | 10 | 1 | 书写满分(score === 100) |
FIRST_LEARN | 5 | 1 | 首次学会某个汉字 |
RECOGNITION_PASS | 5 | 0 | 认读通过(score ≥ 60) |
RECOGNITION_FAIL | 1 | 0 | 认读未通过(score < 60) |
REVIEW_CHAR | 5 | 1 | 完成单个汉字复习全流程 |
DAILY_REVIEW_FULL | 50 | 5 | 每日复习完成率 100% |
DAILY_REVIEW_80 | 30 | 3 | 每日复习完成率 ≥ 80% |
BADGE_UNLOCK | 50 | 5 | 解锁新徽章 |
导出的函数
复习去重
resetReviewDedup(): void
重置复习去重集合(新会话开始时调用)。清除当前会话中已记录的所有复习汉字。
书写奖励
awardWriteReward(char: string, score: number, isFirstLearn?: boolean): void
书写完成时发放奖励。
- 参数:
char— 汉字字符score— 书写得分(0-100)isFirstLearn?— 是否首次学会,默认false
- 发放逻辑:
- 基础书写奖励(
WRITE_COMPLETE: 10 EXP + 1 积分) - 满分奖励(
score === 100时额外发放WRITE_PERFECT: 10 EXP + 1 积分) - 首次学会奖励(
isFirstLearn === true时发放FIRST_LEARN: 5 EXP + 1 积分) - 触发徽章检测(
handleNewRecord({ type: "write", char, score }))
- 基础书写奖励(
认读奖励
awardRecognitionReward(char: string, score: number): void
认读完成时发放奖励。
- 参数:
char— 汉字字符score— 认读得分
- 发放逻辑:
score >= 60:发放RECOGNITION_PASS(5 EXP)score < 60:发放RECOGNITION_FAIL(1 EXP)- 触发徽章检测(
handleNewRecord({ type: "recognition", char, score }))
复习奖励
awardReviewCharReward(char: string): void
复习单字完成时发放奖励(带去重,同一会话中同一汉字只发放一次)。
- 参数:
char— 汉字字符 - 发放:
REVIEW_CHAR(5 EXP + 1 积分)
awardDailyReviewReward(percent: number): void
每日复习完成时发放奖励。
- 参数:
percent— 完成率(0-100) - 发放逻辑: 委托给
achievementStore.handleDailyReviewComplete(percent),根据完成率自动判断发放DAILY_REVIEW_FULL或DAILY_REVIEW_80
游戏奖励
awardGameReward(gameType: string, level: number, stars: number, levelConfig: GameLevelConfig | null): GameRewardResult
游戏完成时发放奖励。
- 参数:
gameType— 游戏类型:"pictograph_match"(象形配对)、"stroke_puzzle"(拼图闯关)、"match3"(汉字消消乐)level— 关卡编号stars— 获得星级(0-3)levelConfig— 关卡配置(含rewards.exp、rewards.coins、difficulty)
- 返回值:
{ exp: number, coins: number }— 实际发放的 EXP 和积分 - 发放逻辑:
stars <= 0(失败):不发放奖励,返回{ exp: 0, coins: 0 }- 星级奖励为差额制:1 星 = 基础奖励 × 难度系数,2 星 = 1.5 倍,3 星 = 2 倍
- 难度系数从
levelConfig.difficulty映射:[1.0, 1.0, 1.2, 1.5, 1.8, 2.0]
徽章奖励
awardBadgeReward(badgeId: string): void
徽章解锁时发放奖励。
- 参数:
badgeId— 徽章 ID - 发放: 委托给
achievementStore.unlockBadge(badgeId),发放BADGE_UNLOCK(50 EXP + 5 积分)
调用路径
| 入口 | 函数 | 调用位置 |
|---|---|---|
| 书写模块 | awardWriteReward() | handleNewRecord() 完成后 |
| 复习模块 | awardReviewCharReward() / awardDailyReviewReward() | review-today.vue 的 onCharComplete() / onAllComplete() |
| 游戏模块 | awardGameReward() | 3 款游戏的 computeResult() / endGame() |
| 成就模块 | awardBadgeReward() | 徽章解锁时 |
使用示例
typescript
import { awardWriteReward, awardGameReward, resetReviewDedup, REWARD } from "@/services/reward-service";
// 书写完成
awardWriteReward("好", 100, true); // 满分 + 首次学会
// 游戏完成
const result = awardGameReward("pictograph_match", 5, 3, {
rewards: { exp: 20, coins: 10 },
difficulty: 2,
});
console.log(result); // { exp: 40, coins: 20 }
// 新会话开始时重置复习去重
resetReviewDedup();