HarmonyOS应用实战-启示散页-64-提问历史别无限增长:把去重、上限和刷新统一交给服务 📅 2026/8/2 3:07:58 HarmonyOS 应用实战 64提问历史别无限增长把去重、上限和刷新统一交给服务提问历史如果由各个页面自己 append很快就会失控同一句重复出现、历史无限增长、首页写了历史但抽屉不刷新。历史不是一个页面数组而是一组有去重、上限和刷新规则的本地数据。本文解决四个问题把历史写入集中到 QuestionHistoryService统一 trim、去重、置顶和截断用刷新信号通知抽屉重读验证重复输入、空输入和上限溢出历史列表失控通常从一行 push 开始页面里history.push(question)最容易写也最容易留下重复和无上限。等历史抽屉、快捷问题、再次提问都能写历史时就必须把规则收口。故障链多个入口 append - 重复问题堆积 - Preferences 变大 - 抽屉读取慢 - 用户分不清真实提问历史失控一般不是大设计错误而是某个页面顺手push了一次。只要有第二个写入点去重、上限和刷新信号就会慢慢失效。HistoryEntry 保留最小展示事实历史不需要保存答案全文也不需要保存页面状态。对答案之书来说问题文本、来源和时间已经足够。interfaceQuestionHistoryEntry{id:string;question:string;source:manual|recommendation|historyAgain;createdAt:number;}HistoryEntry只保留展示需要的最小事实。问题文本、时间和来源足够复现列表行为不要把整套题库或答案上下文塞进历史。Service 执行 trim、去重和上限去重应在保存前完成且重复项要置顶不要生成第二条。classQuestionHistoryService{privatestaticreadonlyMAX30;asyncadd(question:string,source:QuestionHistoryEntry[source]):PromiseQuestionHistoryEntry[]{constnormalizedquestion.trim();if(normalized.length0){returnQuestionHistoryRepository.loadAll();}constoldawaitQuestionHistoryRepository.loadAll();constnext[{id:IdFactory.fromText(normalized),question:normalized,source,createdAt:Date.now()},...old.filter((item)item.question!normalized)].slice(0,QuestionHistoryService.MAX);awaitQuestionHistoryRepository.saveAll(next);AppStorage.setOrCreate(questionHistory.changedAt,Date.now());returnnext;}}服务层同时执行 trim、去重和上限才能保证每一次写入都得到同一套结果。页面只负责提交用户确认的问题。快捷推荐不等于用户历史系统推荐可以填入输入框但只有用户确认提交后才写历史。否则历史会混入提示问题和推荐文案。classHomeQuestionController{selectRecommendation(text:string):void{this.inputTexttext;}asyncsubmit():Promisevoid{awaitQuestionHistoryService.add(this.inputText,manual);this.goDrawingPage(this.inputText);}}快捷推荐不能直接进历史。用户点了推荐但没有真正提问时它只是输入建议不是用户行为记录。抽屉订阅信号后重新读取不要把完整历史塞进 AppStorage。AppStorage 只放 changedAt抽屉收到后从服务读取最终列表。Componentstruct QuestionHistorySheet{StorageLink(questionHistory.changedAt)privatechangedAt:number0;Stateprivateitems:QuestionHistoryEntry[][];Watch(reload)asyncreload():Promisevoid{this.itemsawaitQuestionHistoryRepository.loadAll();}}抽屉通过轻量信号重新读取而不是直接拿页面传来的临时数组。这样返回、重进、冷启动看到的都是仓储最终事实。历史回归要制造重复和溢出至少准备 35 条问题、重复问题、空问题、推荐问题四类输入。只点一次提交无法证明去重和上限可靠。验证重复问题只出现一次并置顶空问题不写入第 31 条后最旧项被移除推荐卡片只填入不提交不入历史。重复和溢出样本必须一起测。只测一个重复问题会漏掉第 31 条之后上限截断是否正确。历史问题排查表看到历史异常先查 Service而不是抽屉布局。现象根因修复重复问题很多页面直接 append统一 Service.add历史无限增长没有 MAX 截断save 前 slice抽屉不刷新没有 changedAt保存后发轻量信号历史排查先搜写入点。发现页面直接改列表就先收回 Service再谈 UI 刷新和排序。历史写入要能被搜索到唯一入口高质量文章不能只说“统一交给服务”。要给出读者可执行的检查方式全局搜索历史写入确认只有QuestionHistoryService.addQuestion或等价 owner 能改变列表。rg-nhistory|QuestionHistory|saveAll|push\(entry hsp har搜索命中后要分三类合法服务写入、仓储落盘、页面展示读取。若页面里直接出现push或saveAll说明历史规则仍然可能被绕过。去重、上限和刷新要一起验历史列表最常见的假通过是去重能过但上限不过或者保存成功但抽屉不刷新。验证时要把三者绑在同一组用例里。输入样本预期列表还要观察同一问题连续提交两次只保留一条并置顶changedAt更新35 条不同问题只保留最近 30 条最旧项被截断只点推荐不提交不进入历史输入框可填入冷启动后打开抽屉与保存前一致Repository 读取最终事实这样写读者能直接照着造数据而不是只理解“历史要去重”的原则。记录边界历史不是诊断日志历史保存的是用户主动提交的问题因此它本身就是用户数据。文章里可以展示字段模型和去重策略但发布或诊断材料不能把真实历史贴出来当证据。要证明历史能力使用演示数据或脱敏样本。证据可以保留不应保留单元样本构造的问题 A/B/C用户真实问题截图演示题库历史抽屉私人咨询内容日志count、qLen、事件码questionText 原文历史条目的隐私边界也要写清提问历史既是功能数据也是用户数据。文章里展示questionText字段是为了说明模型但交付证据不能使用真实用户问题。更合适的做法是准备演示问题集并在截图和日志里只展示演示内容或统计字段。演示问题集 Q001今天先做哪件小事 Q002要不要继续这个计划 Q003是否需要休息十分钟如果发布清单里需要证明历史去重就用这类演示数据跑重复和上限如果诊断日志需要证明历史写入只记录historyCount、qLen和事件码。这样第 64 篇和第 58、69 篇的隐私边界能对齐避免一边说日志不泄露另一边又把真实历史截图当验证材料。人工评审时看历史和推荐是否混线提问历史和快捷推荐在 UI 上经常靠得很近但它们代表两种不同事实。人工评审时可以准备一个推荐词先点击填入输入框但不提交再关闭页面重进确认它没有进入历史。操作历史应变化吗原因点击推荐词填入输入框否用户尚未提交修改推荐词后点击抽取是用户形成真实提问抽取失败且问题为空否无有效输入重复提交旧问题是但只置顶保留唯一历史项这张表把用户行为和系统建议分开。文章写清这点读者才不会为了“入口方便”把推荐文案也写成历史数据。最后一项看跨端材料是否误用真实历史历史功能经常被拿来做发布截图或文章配图这一步也要守边界。截图里出现的历史问题应该来自演示题库不能从开发机真实历史里截取。若需要展示“去重后置顶”就用固定样本连续提交两次若需要展示“超过上限”就用编号问题批量生成。这样证据能说明功能行为又不会把用户真实提问带进文章、发布材料或诊断附件。材料用途推荐样本禁止做法文章截图编号演示问题截真实历史回归记录固定重复样本贴用户原文小结提问历史要像小型数据集一样治理Service 统一写入、Repository 保存最终列表、AppStorage 只通知刷新。这样历史能去重、有上限、可重进也不会被推荐文案污染。