HarmonyOS 应用开发《掌上英语》第27篇:学习统计持久化——StatisticsManager 的数据聚合方案
学习统计持久化——StatisticsManager 的数据聚合方案引言学习数据统计是教育类 App 的核心功能之一。用户完成一次练习后系统需要快速计算出正确率、各题型得分率、平均答题时长等指标并将这些数据持久化以便用户在学习报告等页面查看历史趋势。本文将以StatisticsManager的实现为例分析如何设计一套可扩展、可聚合的学习统计方案。一、数据模型设计统计数据的核心是PracticeStatistics接口它包含了全局统计和分题型统计两个维度exportinterfacePracticeStatistics{// 全局统计数据totalQuestions:number// 总答题数correctQuestions:number// 正确题数wrongQuestions:number// 错误题数correctRate:number// 正确率百分比vocabularyMasteryRate:number// 词汇掌握率listeningCorrectRate:number// 听力正确率readingCorrectRate:number// 阅读正确率clozeCorrectRate:number// 完形填空正确率avgAnswerTime:number// 平均答题时长秒// 分题型统计typeStatistics:TypeStat[]}exportinterfaceTypeStat{type:string// 题型编号1词汇 2听力 3阅读 4完形 5语法 6口语total:number// 该题型总题数correct:number// 正确数wrong:number// 错误数}设计亮点typeStatistics数组而非固定字段。这样可以支持未来新增题型而无需修改数据模型——符合开闭原则。二、单次练习统计计算calculateStatistics方法接收一次练习的题目列表和总时长实时计算各项指标publiccalculateStatistics(ques:TopicItemType[],duration:number):PracticeStatistics{try{lettotalQuestionsques.length;letcorrectQuestions0;letwrongQuestions0;lettypeStats:TypeStat[][];ques.forEach(item{if(item.isAnswer){// 判断答案是否正确letisCorrectthis.checkAnswerCorrect(item.selectQues,item.rightQues);if(isCorrect){correctQuestions;}else{wrongQuestions;}// 按题型累加统计lettypeKeyitem.type||其他;letfoundfalse;for(leti0;itypeStats.length;i){if(typeStats[i].typetypeKey){typeStats[i].total;if(isCorrect)typeStats[i].correct;elsetypeStats[i].wrong;foundtrue;break;}}if(!found){typeStats.push({type:typeKey,total:1,correct:isCorrect?1:0,wrong:isCorrect?0:1});}}});letcorrectRatetotalQuestions0?(correctQuestions/totalQuestions*100):0;// 各题型专项正确率letlisteningCorrectRatethis.getTypeCorrectRate(typeStats,2);letreadingCorrectRatethis.getTypeCorrectRate(typeStats,3);letclozeCorrectRatethis.getTypeCorrectRate(typeStats,4);letvocabularyMasteryRatethis.getTypeCorrectRate(typeStats,1);letavgAnswerTimetotalQuestions0?Math.floor(duration/totalQuestions):0;letstatistics:PracticeStatistics{totalQuestions,correctQuestions,wrongQuestions,correctRate:Math.round(correctRate*100)/100,vocabularyMasteryRate:Math.round(vocabularyMasteryRate*100)/100,listeningCorrectRate:Math.round(listeningCorrectRate*100)/100,readingCorrectRate:Math.round(readingCorrectRate*100)/100,clozeCorrectRate:Math.round(clozeCorrectRate*100)/100,avgAnswerTime,typeStatistics:typeStats};// 合并到历史数据this.saveStatistics(statistics);returnstatistics;}catch(e){Logger.error(StatisticsManager,计算统计数据失败:${JSON.stringify(e)});returnthis.getEmptyStatistics();}}核心逻辑遍历题目逐题判断是否正确同时按题型分类统计。答案比较checkAnswerCorrect比较selectQues和rightQues两个数组排序后是否完全一致。四舍五入使用Math.round(x * 100) / 100保留两位小数。降级处理异常时返回空统计数据不影响用户使用。三、历史数据聚合单次练习的数据需要与历史数据合并才能反映用户的长期学习趋势privatesaveStatistics(statistics:PracticeStatistics):void{try{lethistoryStatsthis.getHistoryStatistics();letmergedStats:PracticeStatistics{totalQuestions:historyStats.totalQuestionsstatistics.totalQuestions,correctQuestions:historyStats.correctQuestionsstatistics.correctQuestions,wrongQuestions:historyStats.wrongQuestionsstatistics.wrongQuestions,correctRate:statistics.totalQuestions0?statistics.correctRate:historyStats.correctRate,vocabularyMasteryRate:statistics.vocabularyMasteryRate,listeningCorrectRate:statistics.listeningCorrectRate,readingCorrectRate:statistics.readingCorrectRate,clozeCorrectRate:statistics.clozeCorrectRate,avgAnswerTime:statistics.avgAnswerTime,typeStatistics:this.mergeTypeStatistics(historyStats.typeStatistics,statistics.typeStatistics)};PreferenceUtil.getInstance().put(this.STATISTICS_KEY,mergedStats);}catch(e){Logger.error(StatisticsManager,保存统计数据失败:${JSON.stringify(e)});}}聚合策略累加型字段totalQuestions、correctQuestions、wrongQuestions历史值 新值直接相加。覆盖型字段correctRate、各专项正确率以最新一次计算为准。分题型统计通过mergeTypeStatistics方法进行深度合并。privatemergeTypeStatistics(history:TypeStat[],current:TypeStat[]):TypeStat[]{// 先深度复制历史数据letresult:TypeStat[][];for(leti0;ihistory.length;i){result.push({type:history[i].type,total:history[i].total,correct:history[i].correct,wrong:history[i].wrong});}// 合并当前数据for(leti0;icurrent.length;i){letfoundfalse;for(letj0;jresult.length;j){if(result[j].typecurrent[i].type){result[j].totalcurrent[i].total;result[j].correctcurrent[i].correct;result[j].wrongcurrent[i].wrong;foundtrue;break;}}if(!found){result.push({type:current[i].type,total:current[i].total,correct:current[i].correct,wrong:current[i].wrong});}}returnresult;}这里手动深度复制而非直接赋值是因为TypeStat是引用类型直接复制会共享底层对象导致历史数据被意外修改。四、历史数据读取与展示publicgetHistoryStatistics():PracticeStatistics{try{letstatsPreferenceUtil.getInstance().get(this.STATISTICS_KEY,undefined)asPracticeStatistics;if(!stats){returnthis.getEmptyStatistics();}returnstats;}catch(e){Logger.error(StatisticsManager,获取历史统计数据失败:${JSON.stringify(e)});returnthis.getEmptyStatistics();}}publicgetQuestionTypeStats():QuestionTypeStat[]{try{letstatsthis.getHistoryStatistics();letresult:QuestionTypeStat[][];for(leti0;istats.typeStatistics.length;i){lettypeStatstats.typeStatistics[i];result.push({typeName:this.getTypeName(typeStat.type),total:typeStat.total,correct:typeStat.correct,wrong:typeStat.wrong,correctRate:typeStat.total0?Math.round(typeStat.correct/typeStat.total*10000)/100:0});}returnresult.sort((a,b)b.total-a.total);}catch(e){Logger.error(StatisticsManager,获取题型统计失败:${JSON.stringify(e)});return[];}}getQuestionTypeStats将内部的数字题型编号转换为可读的中文名称并按总题数降序排列方便 UI 层展示。五、最佳实践5.1 空值安全首次使用或数据被清空时getHistoryStatistics返回空统计数据而非null避免调用方做空值检查privategetEmptyStatistics():PracticeStatistics{return{totalQuestions:0,correctQuestions:0,wrongQuestions:0,correctRate:0,vocabularyMasteryRate:0,listeningCorrectRate:0,readingCorrectRate:0,clozeCorrectRate:0,avgAnswerTime:0,typeStatistics:[]};}5.2 精度控制所有百分比值使用Math.round(x * 100) / 100确保保留两位小数避免浮点数精度问题导致 UI 上显示一长串小数。5.3 题型映射集中管理题型编号到中文名称的映射集中在getTypeName方法中避免在 UI 层散落 switch-caseprivategetTypeName(type:string):string{switch(type){case1:return单词拼写case2:return听力理解case3:return阅读理解case4:return完形填空case5:return语法练习case6:return口语练习default:return题型${type}}}六、总结StatisticsManager展现了一个典型的数据聚合场景从原始数据题目列表到计算指标正确率再到历史数据合并累加 覆盖最后到 UI 友好的展示格式题型名称转换 排序。这种分层设计使得统计逻辑高度内聚、易于测试同时在需要新增题型或统计维度时只需扩展TypeStat数组即可无需修改核心聚合逻辑。

相关新闻

Grok智能体后台模式详解:AI自动化任务的长时运行实践

Grok智能体后台模式详解:AI自动化任务的长时运行实践

今天来看一个值得关注的技术更新:Grok智能体现在支持后台模式运行。这个功能对于需要长时间运行AI任务的用户来说是个重要改进,意味着你可以让智能体在后台持续处理任务,而不需要保持终端窗口打开。Grok智能体是由xAI开发的AI执行系统&#x…

2026/7/24 21:11:53 阅读更多 →
第五章WSaiOS 多模态融合感知引擎实现

第五章WSaiOS 多模态融合感知引擎实现

第五章WSaiOS 多模态融合感知引擎实现WSaiOS Multimodal Fusion Perception Engine——构建统一世界感知数据入口5.1 多模态融合感知工程目标WSaiOS 多模态融合感知引擎的目标:将不同来源的数据:图像;视频;文本;音频&a…

2026/7/22 4:15:24 阅读更多 →
大模型时代程序员转型指南:从传统开发到AI工程化

大模型时代程序员转型指南:从传统开发到AI工程化

1. 大模型技术如何重塑程序员就业市场去年面试Java开发岗时,面试官突然问我是否了解LangChain框架,这个细节让我意识到技术风向正在发生根本性转变。现在打开任何招聘平台,算法工程师岗位JD中"熟悉大模型原理"几乎成为标配&#xf…

2026/7/22 4:15:24 阅读更多 →

最新新闻

中小企业费控难题:差旅与费用报销如何从“失控”到闭环

中小企业费控难题:差旅与费用报销如何从“失控”到闭环

中小企业费控难题:差旅与费用报销如何从"失控"到闭环 钱花在哪、超没超、合不合规,月底报销一团乱——这是很多中小企业的常态。本文拆解费用报销失控的四类来源、经营风险与分阶段建设思路,并给出选型与落地建议。 ▲ 报销失控看…

2026/7/25 1:23:04 阅读更多 →
2026 开源 ERP/进销存横评:功能、技术栈、二开成本三维度对比

2026 开源 ERP/进销存横评:功能、技术栈、二开成本三维度对比

2026 开源 ERP/进销存横评:功能、技术栈、二开成本三维度对比 🌐 演示地址:http://ruoyioffice.com | 📦 源码1GitHub:ruoyi-office | 📦 源码2GitCode:ruoyi-office | 📦 源码3Gite…

2026/7/25 1:23:04 阅读更多 →
设计EDA 专家级工程师 HR + 技术负责人双线、面试打分、综合评估、录用全流程

设计EDA 专家级工程师 HR + 技术负责人双线、面试打分、综合评估、录用全流程

一、岗位基础定义与角色分工 1. 岗位定位 EDA 专家工程师(资深专家 / 预研专家 / 模块技术负责人) 从业 6 年以上 EDA 全链路经验,可独立主导卡脖子技术攻关、自研算法架构、完整产品线规划、团队技术方案终审、行业标准 / 高价值专利输出,可带 5–10 人专项小组,对接晶…

2026/7/25 1:23:04 阅读更多 →
Java后端面试20天冲刺计划:从基础到分布式系统全覆盖

Java后端面试20天冲刺计划:从基础到分布式系统全覆盖

如果你从7月开始准备Java后端面试,这份20天冲刺计划能帮你系统覆盖所有核心考点。无论是校招还是社招,Java后端面试通常围绕Java基础、并发编程、JVM、MySQL、Spring框架、分布式系统等模块展开,而近年越来越多的公司开始加入场景题和AI相关问题的考察。 这个计划最大的特点…

2026/7/25 1:23:04 阅读更多 →
Nintendo Switch游戏安装终极指南:Awoo Installer如何解决所有格式兼容问题

Nintendo Switch游戏安装终极指南:Awoo Installer如何解决所有格式兼容问题

Nintendo Switch游戏安装终极指南:Awoo Installer如何解决所有格式兼容问题 【免费下载链接】Awoo-Installer A No-Bullshit NSP, NSZ, XCI, and XCZ Installer for Nintendo Switch 项目地址: https://gitcode.com/gh_mirrors/aw/Awoo-Installer 还在为Swit…

2026/7/25 1:23:04 阅读更多 →
Figma中文界面终极指南:3分钟安装,让英文设计软件变母语体验

Figma中文界面终极指南:3分钟安装,让英文设计软件变母语体验

Figma中文界面终极指南:3分钟安装,让英文设计软件变母语体验 【免费下载链接】figmaCN 中文 Figma 插件,设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 还在为Figma复杂的英文界面而头疼吗?专业…

2026/7/25 1:22:04 阅读更多 →

日新闻

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就是为了解决您的…

2026/7/25 0:00:35 阅读更多 →
C++ string类模拟实现:从深拷贝到内存管理的完整指南

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:00:35 阅读更多 →
三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:00:35 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻