Skip to content

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_COMPLETE101完成一次书写练习
WRITE_PERFECT101书写满分(score === 100)
FIRST_LEARN51首次学会某个汉字
RECOGNITION_PASS50认读通过(score ≥ 60)
RECOGNITION_FAIL10认读未通过(score < 60)
REVIEW_CHAR51完成单个汉字复习全流程
DAILY_REVIEW_FULL505每日复习完成率 100%
DAILY_REVIEW_80303每日复习完成率 ≥ 80%
BADGE_UNLOCK505解锁新徽章

导出的函数

复习去重

resetReviewDedup(): void

重置复习去重集合(新会话开始时调用)。清除当前会话中已记录的所有复习汉字。


书写奖励

awardWriteReward(char: string, score: number, isFirstLearn?: boolean): void

书写完成时发放奖励。

  • 参数:
    • char — 汉字字符
    • score — 书写得分(0-100)
    • isFirstLearn? — 是否首次学会,默认 false
  • 发放逻辑:
    1. 基础书写奖励(WRITE_COMPLETE: 10 EXP + 1 积分)
    2. 满分奖励(score === 100 时额外发放 WRITE_PERFECT: 10 EXP + 1 积分)
    3. 首次学会奖励(isFirstLearn === true 时发放 FIRST_LEARN: 5 EXP + 1 积分)
    4. 触发徽章检测(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_FULLDAILY_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.exprewards.coinsdifficulty
  • 返回值: { 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.vueonCharComplete() / 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();