乒乓球口袋教练 HarmonyOS 学习应用(07):学习进度与成就系统

发布时间:2026/7/30 19:11:46

乒乓球口袋教练 HarmonyOS 学习应用(07):学习进度与成就系统 一、成就不应成为另一份可编辑的用户数据学习应用里的“已解锁”必须能追溯到具体行为完成课程、累计学习秒数、连续天数、练习日志或测验结果。若只在成就卡片上把某个布尔值设为 true用户重启后就无法解释徽章从哪里来也不能处理编辑日志、删除记录或重复提交。本项目把成就视作对既有事实的计算结果而不是需要单独保存的页面状态。constHISTORY_LIMIT:number50;exportclassProgressService{staticgetMap():ProgressMap{returnAppStorage.getProgressMap(StorageKeys.LEARN_PROGRESS)??{}asProgressMap;}staticget(courseId:string):ProgressEntry|undefined{returnProgressService.getMap()[courseId];}staticmarkWatched(courseId:string,lastPos:number):void{constcur:ProgressMapProgressService.getMap();constnext:ProgressMap{}asProgressMap;Object.keys(cur).forEach((k:string){next[k]cur[k];});next[courseId]{watched:true,lastPos:lastPos,updatedAt:Date.now()}asProgressEntry;AppStorage.setOrCreateProgressMap(StorageKeys.LEARN_PROGRESS,next);PreferencesHelper.get().persist(StorageKeys.LEARN_PROGRESS,next);ProgressService.markStudied(courseId);}二、ProgressService 记录哪些学习事实ProgressService 管理课程进度图、学习历史和连续学习日期。markStudied 以 courseId 为键更新完成事实addSeconds 聚合累计时长touchToday 负责连续日期。每项数据都经由 StorageKeys 进入 AppStorage页面离开后再进入仍通过服务查询而不是依赖上一页内存里的圆形进度条。参与者输入输出或约束模型或配置稳定标识、模式或模块字段给出可追溯的工程事实服务或系统能力经过归一化的请求返回明确结果或失败原因页面回读后的结果只渲染不保存第二份事实exportclassAchievementService{staticcheckAll():string[]{constunlocked:string[][];// --- streak ---conststreak:numberProgressService.streak();if(streak3)unlocked.push(streak_3);if(streak7)unlocked.push(streak_7);if(streak14)unlocked.push(streak_14);if(streak30)unlocked.push(streak_30);// --- study ---conststudied:string[]AppStorage.getstring[](StorageKeys.STUDIED)??[];if(studied.length1)unlocked.push(study_1);if(studied.length5)unlocked.push(study_5);if(studied.length10)unlocked.push(study_10);constseconds:numberAppStorage.getnumber(StorageKeys.LEARN_SECONDS)??0;if(seconds3600)unlocked.push(time_1h);if(seconds18000)unlocked.push(time_5h);// --- practice ---三、AchievementService 怎样组合跨模块条件AchievementService.checkAll 同时读取 ProgressService、PracticeLogService 和 QuizService 的结果并用成就定义里的条件决定返回哪些 id。连续三天、完成课程和完成测验是不同条件不能用总点击次数替代。这个汇总层让规则集中但没有把日志或测验复制一份到成就页。Stateunlocked:string[][];aboutToAppear():void{this.unlockedAchievementService.checkAll();}build(){Column(){TopBar({title:我的成就});Scroll(){Column({space:AppSizes.s4}){this.summaryCard();ForEach(CATEGORIES,(cat:AchievementCategory){this.categorySection(cat);},(cat:AchievementCategory)cat);Column().height(AppSizes.s8);}.width(100%).padding({left:AppSizes.s4,right:AppSizes.s4,top:AppSizes.s2});}.layoutWeight(1).scrollBar(BarState.Off);四、个人页与成就页为何都要重新查询个人页展示已解锁数量成就页展示按类别分组的卡片两个页面都在显示时调用 checkAll。这样在课程详情完成一课、在练习页新增日志后用户从任一入口回到个人页都能看到同一数字。若写入失败卡片应保持未解锁而不是预先点亮避免产生无法重现的成功。情况容易出现的错误本文采用的处理数据或配置缺项伪造默认成功状态停在可解释的失败或空态页面重进使用上一页残留对象从模型、服务或系统重新回读重复动作再写一遍相同业务事实由稳定入口或回调收敛aboutToAppear():void{this.achieveCountAchievementService.checkAll().length;}build(){Column(){Row(){Text(我的).fontSize(AppText.fsTitle).fontWeight(AppText.fwSemi).fontColor(this.palette.textPrimary).layoutWeight(1);}.width(100%).height(AppSizes.topBar).padding({left:AppSizes.s4,right:AppSizes.s4});Scroll(){Column({space:AppSizes.s4}){this.userCard();this.statsCard();this.menuList();Column().height(AppSizes.s8);五、解锁与重进的验证顺序验收可先完成一门课程并重新进入个人页观察课程完成数变化再写入一条练习日志、完成一次测验打开成就页检查对应类别最后强制停止并再次启动比较连续天数、累计时长和解锁数量。配图用于回读成就界面真正的结论仍需由主题动作后的服务结果支持。验收阶段实际动作回读重点前置确认启动正确 bundle 或打开目标页标题、入口与模块身份主题操作执行搜索、切换、完成或跳转服务/系统返回的结果重进检查返回、重启或切换范围后再进入事实没有依赖旧页面残留六、实现边界与维护顺序课程完成、累计时长、连续学习、练习日志和测验结果各自保留事实来源成就页只汇总条件不制造第二本统计账。 新增需求时应先补齐模型、配置或服务合同再调整页面入口把同一个判断复制到多个组件短期看似方便后续会使结果无法回读。ArkTS 状态管理的基础机制可参考 HarmonyOS 官方文档。七、继续扩展时的约束课程完成与学习时长并不是同一维度。一门课程可以被标记完成却只有很短的停留时间播放器也可能累计时长但用户尚未完成关键动作。成就规则读取不同事实时必须明确各自阈值避免把看过页面误当成掌握动作。统计显示的数量应由服务计算页面不对数组长度做额外加减。特别是在练习日志编辑、测验重复进入和课程重新学习后手工维护显示值最容易把历史与本次操作混在一起。服务返回的结果是唯一输入个人页、进度页和成就页就能共享相同结论。若未来增加云同步应同步原始进度、日志和测验结果再在本地或服务端重新计算成就。直接同步解锁卡片虽然实现快却无法处理规则升级、重复数据和跨端冲突也失去解释每一枚徽章来源的能力。进度服务需要防止哪些重复写入课程详情、播放器和首页都可能触发学习动作所以服务必须让相同 courseId 的完成状态保持幂等。重复点击完成不应把已学课程数加两次相同日期的 touchToday 也不应把连续天数加两次。把防重复放在服务内比要求每个页面都记住上次点击更可靠。成就规则升级后的读取方式当“完成三门课程”改为“完成五门课程”时页面只需再次调用 checkAll 即可得到新结果。规则本身和原始学习事实分离历史数据不用迁移卡片状态。测试应同时覆盖恰好达到阈值、低于阈值和超过阈值三种情况避免边界比较符号写反。

相关新闻