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

发布时间:2026/11/4 20:49:29

HarmonyOS应用实战-启示散页-05-随机抽答案要避免连续重复:把算法放进服务层 HarmonyOS应用实战-启示散页-05-随机抽答案要避免连续重复把算法放进服务层答案之书的核心体验是“提问后给一个答案”。如果连续两次抽到同一句用户会立刻觉得应用敷衍如果页面自己写随机算法动画页、快捷入口和未来的小组件入口又可能各抽各的。随机不是 UI 小细节它是产品体验规则应该放在服务层统一处理。这篇文章会把问题拆成四个可落地的点把抽取算法从页面移到 AnswerDrawService。用最近结果窗口降低连续重复概率。区分候选池、运行期状态和展示动画。给出小题库、空题库、过滤后无结果的兜底策略。1. 随机体验不等于 Math.randomMath.random()能给出随机下标但不能保证体验。题库只有十几条时连续重复很容易出现用户提问后看到同一句答案会把算法概率理解成产品问题。答案之书把抽取逻辑放进 Service让所有入口都使用同一套规则。interfaceDrawAnswerOptions{deckId:string;question?:string;excludeRecent?:boolean;}interfaceDrawAnswerResult{deckId:string;answer:Answer;drawnAt:number;question?:string;}抽取结果不是一段文本而是带题库、问题和时间的领域结果。这样后续收藏和历史才能拿到完整来源。2. 候选池先由 Service 准备页面不应该自己加载题库再筛答案。Service 先读取当前题库过滤空答案再决定是否允许抽取。空题库、损坏题库、当前题库不存在都在这里统一兜底。asyncdraw(options:DrawAnswerOptions):PromiseDrawAnswerResult{constdeck:Deck|nullawaitDeckRepository.loadDeck(options.deckId);if(!deck){thrownewError(当前题库不存在请重新选择题库);}constcandidates:Answer[]deck.answers.filter((item:Answer):booleanitem.text.trim().length0);if(candidates.length0){thrownewError(当前题库没有可抽取的答案);}returnthis.pickFromCandidates(deck.id,candidates,options.question);}候选池准备属于业务层。页面只关心成功展示或失败提示不应该处理题库损坏的细节。3. 最近窗口只影响选择不改题库避免连续重复不需要修改题库顺序也不需要给答案加权。答案之书维护一个运行期 recent 窗口抽取时优先排除最近出现过的答案如果题库太小导致排除后为空就退回全量候选。privatereadonlyrecentAnswerIds:string[][];privatereadonlyrecentLimit:number3;privatefilterRecent(candidates:Answer[]):Answer[]{constfiltered:Answer[]candidates.filter((item:Answer):boolean{return!this.recentAnswerIds.includes(item.id);});returnfiltered.length0?filtered:candidates;}recent 是运行期体验状态不是持久化数据。它影响下一次抽取但不改变用户题库内容。4. 选中后再更新 recentrecent 窗口要在抽取成功后更新不能在动画结束后才更新。否则用户快速连续点击时第二次抽取仍可能看到旧窗口。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:Answerpool[this.chooseIndex(pool.length)];this.remember(answer.id);return{deckId,answer,question,drawnAt:Date.now()};}集中封装不是为了过度抽象而是为了让体验规则只有一个出口。6. DrawingPage 只处理状态机抽取页最容易把算法、动画和导航揉在一起。答案之书更稳的写法是页面进入 loading调用 Service 拿结果进入 revealing动画结束后进入 done。答案是什么由 Service 决定页面只负责让结果被看见。typeDrawingStateidle|drawing|revealing|done|error;StateprivatedrawingState:DrawingStateidle;StateprivatecurrentResult:DrawAnswerResult|nullnull;privateasyncstartDraw():Promisevoid{this.drawingStatedrawing;try{this.currentResultawaitAnswerDrawService.draw({deckId:this.deckId,question:this.question});this.drawingStaterevealing;}catch(err){this.drawingStateerror;}}状态机让页面逻辑可读也让失败态有入口。抽取失败不应该停在半段动画里。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.lengththis.recentLimit?this.filterRecent(candidates):candidates;constanswer:Answerpool[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 窗口、结果来源都放进服务层答案之书的核心体验才有一致性。
延伸阅读

更多相关文章

2026/11/1 12:51:19

如何快速恢复丢失的Ren‘Py游戏源码:终极反编译工具指南

如何快速恢复丢失的RenPy游戏源码:终极反编译工具指南 【免费下载链接】unrpyc A renpy script decompiler 项目地址: https://gitcode.com/gh_mirrors/un/unrpyc 你是否曾经遇到过RenPy游戏项目源码丢失的尴尬情况?或者需要分析已编译的游戏脚本…

2026/10/28 17:59:43

ADC药物研发热潮:技术突破与市场机遇

1. ADC药物赛道为何持续升温?抗体偶联药物(Antibody-Drug Conjugate,简称ADC)作为肿瘤靶向治疗的"生物导弹",近年来已成为全球医药研发最炙手可热的领域之一。根据Evaluate Pharma数据,2023年全球…

2026/10/31 9:13:22

信息素养大赛C++解题:从最大公约数看“必然事件”类数论问题

最近在准备信息素养大赛的同学,特别是C赛道的选手,常常会遇到一类题目:它不单纯考察语法,而是将编程逻辑与数学、概率等基础学科知识紧密结合,考察选手的综合素养。“必然事件”就是其中一道非常经典的题目&#xff0c…

2026/11/5 11:24:07

Makefile自动化构建:从原理到实战技巧

1. 从零开始理解Makefile的本质 作为一个在Linux环境下摸爬滚打多年的老码农,我见过太多新手面对Makefile时露出的困惑表情。Makefile本质上是一个自动化构建脚本,但它更像是一种"构建逻辑的声明式表达"。想象你是个建筑工头,Makef…

2026/11/5 11:24:07

可跑通的ADS射频仿真实例:S参数、匹配与功放谐波平衡

简介:面向射频与微波电路设计学习者的 ADS 仿真实例合集,围绕阻抗匹配、滤波器、低噪放、功分器、功率放大器与耦合器等典型模块展开,适合希望从原理走向电路建模、仿真与优化的初中级工程师及电子类专业学生查练。压缩包为 rar 格式&#xf…

2026/11/5 11:24:07

存储过程参数过多错误分析与解决方案

1. 问题现象与背景分析 最近在排查一个生产环境数据库问题时,遇到了"CDataBaseEngineSink::OnRequsetInsertCreateRecord 数据库异常:为过程或函数 GSP_GR_InsertCreateRecord 指定了过多的参数"的错误。这个错误发生在调用存储过程GSP_GR_Ins…

2026/11/5 11:24:07

RabbitMQ消息投递可靠性保障与补偿机制实战

1. 消息队列中的投递可靠性难题 在分布式系统架构中,RabbitMQ作为经典的消息中间件,其消息投递的可靠性直接影响着业务系统的稳定性。我经历过一个电商订单系统案例:促销高峰期每秒3000订单消息通过RabbitMQ流转,有0.3%的消息神秘…

2026/11/5 11:24:06

SIL2认证的本质:从IEC 61508到ControlLogix硬件诊断的确定性实现

简介:本资源为罗克韦尔自动化(Rockwell Automation)ControlLogix系列PLC于2021年通过IEC 61508:2010标准SIL2功能安全认证的官方技术文档,面向工业自动化工程师、安全系统设计人员及PLC应用开发者,解决安全回路设计中对…

2026/11/5 11:19:06

H5游戏图集拆解实战:plist/atlas解析与Python还原脚本

简介:面向Cocos、白鹭(Egret)与Laya等2D游戏引擎开发者的图集/合图分解实用工具,用于将精灵表按plist元数据还原为单张图片,便于资源编辑、版本更新与多设备适配。压缩包共6个文件、约227KB,以可执行程序为…

2026/11/4 12:38:23

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/4 12:38:23

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑