news 2026/7/21 3:24:33

HarmonyOS应用实战-启示散页-05-随机抽答案要避免连续重复:把算法放进服务层

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS应用实战-启示散页-05-随机抽答案要避免连续重复:把算法放进服务层

HarmonyOS应用实战-启示散页-05-随机抽答案要避免连续重复:把算法放进服务层

答案之书的核心体验是“提问后给一个答案”。如果连续两次抽到同一句,用户会立刻觉得应用敷衍;如果页面自己写随机算法,动画页、快捷入口和未来的小组件入口又可能各抽各的。随机不是 UI 小细节,它是产品体验规则,应该放在服务层统一处理。

这篇文章会把问题拆成四个可落地的点:

  1. 把抽取算法从页面移到 AnswerDrawService。
  2. 用最近结果窗口降低连续重复概率。
  3. 区分候选池、运行期状态和展示动画。
  4. 给出小题库、空题库、过滤后无结果的兜底策略。


1. 随机体验不等于 Math.random

Math.random()能给出随机下标,但不能保证体验。题库只有十几条时,连续重复很容易出现;用户提问后看到同一句答案,会把算法概率理解成产品问题。答案之书把抽取逻辑放进 Service,让所有入口都使用同一套规则。

interfaceDrawAnswerOptions{deckId:string;question?:string;excludeRecent?:boolean;}interfaceDrawAnswerResult{deckId:string;answer:Answer;drawnAt:number;question?:string;}

抽取结果不是一段文本,而是带题库、问题和时间的领域结果。这样后续收藏和历史才能拿到完整来源。

2. 候选池先由 Service 准备

页面不应该自己加载题库再筛答案。Service 先读取当前题库,过滤空答案,再决定是否允许抽取。空题库、损坏题库、当前题库不存在都在这里统一兜底。

asyncdraw(options:DrawAnswerOptions):Promise<DrawAnswerResult>{constdeck:Deck|null=awaitDeckRepository.loadDeck(options.deckId);if(!deck){thrownewError('当前题库不存在,请重新选择题库');}constcandidates:Answer[]=deck.answers.filter((item:Answer):boolean=>item.text.trim().length>0);if(candidates.length===0){thrownewError('当前题库没有可抽取的答案');}returnthis.pickFromCandidates(deck.id,candidates,options.question);}

候选池准备属于业务层。页面只关心成功展示或失败提示,不应该处理题库损坏的细节。

3. 最近窗口只影响选择,不改题库

避免连续重复不需要修改题库顺序,也不需要给答案加权。答案之书维护一个运行期 recent 窗口,抽取时优先排除最近出现过的答案;如果题库太小导致排除后为空,就退回全量候选。

privatereadonlyrecentAnswerIds:string[]=[];privatereadonlyrecentLimit:number=3;privatefilterRecent(candidates:Answer[]):Answer[]{constfiltered:Answer[]=candidates.filter((item:Answer):boolean=>{return!this.recentAnswerIds.includes(item.id);});returnfiltered.length>0?filtered:candidates;}

recent 是运行期体验状态,不是持久化数据。它影响下一次抽取,但不改变用户题库内容。

4. 选中后再更新 recent

recent 窗口要在抽取成功后更新,不能在动画结束后才更新。否则用户快速连续点击时,第二次抽取仍可能看到旧窗口。Service 返回结果前完成状态更新,页面动画只负责表现。

privateremember(answerId:string):void{this.recentAnswerIds.unshift(answerId);constunique:string[]=[];this.recentAnswerIds.forEach((id:string):void=>{if(!unique.includes(id)){unique.push(id);}});this.recentAnswerIds.splice(0,this.recentAnswerIds.length,...unique.slice(0,this.recentLimit));}

这段代码把 recent 维护成去重队列。它不追求复杂概率,只解决连续重复这个最刺眼的体验问题。

5. 随机下标封装成可替换方法

本地小应用不需要一上来引入复杂随机库,但随机方法应该集中。未来如果要做权重、节日题库或测试可复现,只改一个方法,不需要翻页面代码。

privatechooseIndex(total:number):number{returnMath.floor(Math.random()*total);}privatepickFromCandidates(deckId:string,candidates:Answer[],question?:string):DrawAnswerResult{constpool:Answer[]=this.filterRecent(candidates);constanswer:Answer=pool[this.chooseIndex(pool.length)];this.remember(answer.id);return{deckId,answer,question,drawnAt:Date.now()};}

集中封装不是为了过度抽象,而是为了让体验规则只有一个出口。

6. DrawingPage 只处理状态机

抽取页最容易把算法、动画和导航揉在一起。答案之书更稳的写法是:页面进入 loading,调用 Service 拿结果,进入 revealing,动画结束后进入 done。答案是什么由 Service 决定,页面只负责让结果被看见。

typeDrawingState='idle'|'drawing'|'revealing'|'done'|'error';@StateprivatedrawingState:DrawingState='idle';@StateprivatecurrentResult:DrawAnswerResult|null=null;privateasyncstartDraw():Promise<void>{this.drawingState='drawing';try{this.currentResult=awaitAnswerDrawService.draw({deckId:this.deckId,question:this.question});this.drawingState='revealing';}catch(err){this.drawingState='error';}}

状态机让页面逻辑可读,也让失败态有入口。抽取失败不应该停在半段动画里。

7. 收藏和历史使用同一个 DrawAnswerResult

如果收藏只存文本,历史又存 question,后续就无法判断同一句答案来自哪个题库。抽取 Service 返回完整结果后,收藏和历史都使用同一份来源信息,能减少数据对不上的问题。

awaitHistoryService.append({deckId:result.deckId,answerId:result.answer.id,answerText:result.answer.text,question:result.question,createdAt:result.drawnAt});

抽取结果是后续功能的源头。源头字段完整,收藏、历史、分享才不会各自补猜。

8. 小题库要接受重复,但不连续卡死

如果题库只有两条答案,强行不重复会很快无候选。Service 的策略应该是尽量避免最近重复,但候选池为空时回退全量。这比一直重试随机更稳,也不会出现死循环。

constpool:Answer[]=candidates.length>this.recentLimit?this.filterRecent(candidates):candidates;constanswer:Answer=pool[this.chooseIndex(pool.length)];

体验规则要尊重数据规模。小题库允许重复,但仍可以避免最常见的连续同一句。

9. 验证与排障

抽取验证不要只看“点一次有答案”。要连续抽 20 次观察重复分布,切换题库后 recent 是否影响新题库,空题库是否给出错误提示,收藏和历史是否拿到同一个结果来源。

验证点: 1. 同一题库连续抽取不频繁出现相邻重复 2. 两条答案的小题库不会死循环 3. 当前题库不存在时页面进入 error 状态 4. 收藏记录包含 deckId、answerId、answerText 5. 历史记录 createdAt 使用抽取结果时间

如果动画页偶发空白,先看 Service 是否抛错后页面没有进入 error,而不是先调整动画时间。

验证清单

  • 清应用数据后从冷启动进入,确认默认数据、页面状态和日志分支符合预期。
  • 对本文涉及的写路径准备正常、空值、重复、越界四类输入,确认错误停在 Service 或 Repository。
  • 页面返回、重新进入、切换题库、收藏、历史或删除后,确认对应刷新信号触发重新读取。
  • 修改资源或模块归属后重新构建,确认 HAP、HAR、HSP 的依赖方向没有反转。
  • 涉及真机体验、备份恢复、发布素材的内容,单独记录是否已经在设备或平台侧验证。

常见问题与处理

现象先看哪里处理方式
连续抽到同一句recentAnswerIds 是否更新抽取成功后立即记入 recent
小题库抽取卡住是否在 while 中强行排重排除后为空就回退全量候选
收藏来源丢失是否只保存 answerText保存 DrawAnswerResult 中的 deckId 和 answerId
切换题库后结果异常recent 是否跨题库污染recent 可按 deckId 分组或切换时清空

小结

随机抽取是产品规则,不是页面按钮里的临时表达式。把候选池、recent 窗口、结果来源都放进服务层,答案之书的核心体验才有一致性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/21 3:22:53

PyTorch图像预处理与张量操作实战指南

1. 为什么图像预处理是深度学习的必修课在计算机视觉任务中&#xff0c;原始图像数据就像未经雕琢的玉石&#xff0c;而PyTorch的数据预处理流程就是我们的雕刻工具。我处理过数万张医学影像数据集&#xff0c;深刻体会到&#xff1a;模型性能的差异&#xff0c;80%取决于数据质…

作者头像 李华
网站建设 2026/7/21 3:22:47

5分钟快速掌握m4s-converter:一键将B站缓存视频转换为永久MP4格式

5分钟快速掌握m4s-converter&#xff1a;一键将B站缓存视频转换为永久MP4格式 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经遇到过这…

作者头像 李华
网站建设 2026/7/21 3:22:45

Linux tcp_congestion_ops 拥塞控制算法结构体注册机制

Linux tcp_congestion_ops 拥塞控制算法结构体注册机制tcp_congestion_ops 是 TCP 拥塞控制算法的接口抽象&#xff0c;定义在 include/net/tcp.h 中。所有拥塞控制算法&#xff08;Cubic、BBR、Reno、Westwood、DCTCP 等&#xff09;通过该结构体向内核注册。结构体包含拥塞控…

作者头像 李华
网站建设 2026/7/21 3:21:46

LangChain与LangGraph:智能体开发新范式解析

1. Vibe Coding 与智能体开发新范式在2024年的AI应用开发领域&#xff0c;Vibe Coding正成为一种革命性的开发范式。这种开发方式强调通过自然语言交互和可视化工具快速构建AI应用&#xff0c;大幅降低了智能体开发的门槛。作为从业者&#xff0c;我亲历了从传统编码到Vibe Cod…

作者头像 李华
网站建设 2026/7/21 3:20:52

MacBook Neo垃圾清理指南与性能优化

1. MacBook Neo垃圾清理的必要性作为MacBook Neo用户&#xff0c;你可能已经注意到随着使用时间的增加&#xff0c;系统运行速度会逐渐变慢。这通常是由于系统缓存、日志文件、临时文件等垃圾数据的堆积造成的。定期清理这些垃圾文件不仅能释放宝贵的存储空间&#xff0c;还能显…

作者头像 李华
网站建设 2026/7/21 3:19:44

KDE Plasma桌面环境11种高效定制方法详解

1. KDE桌面定制概述 作为一名使用KDE Plasma桌面环境超过5年的Linux用户&#xff0c;我深刻体会到KDE的强大之处在于其近乎无限的定制能力。不同于其他桌面环境&#xff0c;KDE Plasma提供了从底层框架到表面细节的全面控制权&#xff0c;让每个用户都能打造出独一无二的工作环…

作者头像 李华