【喵汪星球HarmonyOS 6.0】技术实战 09:健康症状库、关键词匹配与应急就医卡 前言健康模块是养宠 App 里最敏感的一部分。用户发现宠物异常时往往已经很焦虑。App 能做的是帮用户整理信息、识别明显风险、准备就医材料而不是给出“诊断结论”。喵汪星球的健康模块选择了一个克制的方案本地症状库 关键词匹配 风险提示 就医信息卡。它不联网、不做诊断、不替代兽医只帮助用户把信息组织得更清楚。这篇会讲如何设计症状内容模型如何做本地关键词匹配如何给未匹配输入做高风险兜底如何把宠物档案和当天记录聚合成就医卡健康类功能在文案和产品边界上的取舍先说边界健康模块不是问诊项目里多处出现类似提示该功能仅供娱乐参考症状比对不能替代兽医诊断或治疗建议这个提示不是走形式而是功能设计原则。健康模块只做三件事第一帮用户把“我观察到了什么”映射到可能相关的症状条目。第二提醒用户哪些情况需要尽快就医。第三把宠物档案、近期记录和准备清单整理成就医信息卡。它不做处方、不推荐药、不说“就是某个病”。这类边界写清楚产品才安全。症状模型内容必须结构化项目中的症状文章模型interface SymptomArticle { title: string risk: string summary: string doctor: string observe: string checklist: string keywords: string[] }字段分别对应页面信息块title症状或疾病条目名称。risk风险等级例如高风险、中风险、低中风险。summary常见表现。doctor什么情况下尽快就医。observe在家先观察哪些指标。checklist去医院前准备哪些材料。keywords用于本地关键词匹配。示例条目{ title: 猫传腹, risk: 高风险, summary: 可能表现为持续发热、精神差、消瘦、腹围变大、胸腹水、呼吸费力或眼部神经症状。, doctor: 反复发热、腹部明显变大、呼吸费力、黄疸、眼睛异常或神经症状建议尽快检查, observe: 体温曲线、体重变化、腹围、食欲、呼吸、眼睛和行动协调性, checklist: 体温记录、腹部照片、体重变化、血检和影像资料、发病时间线, keywords: [猫传腹, 传腹, fip, 腹水, 胸水, 腹围大, 肚子大] }为什么不写成长文本因为用户在异常场景下没有耐心读大段内容。结构化字段能让页面直接分区展示也方便后续导出就医卡。健康页交互流程页面的主流程如下mermaid flowchart TD A[用户输入症状描述] -- B[normalizeSymptomText 归一化] B -- C[遍历症状库 keywords] C -- D{是否命中关键词} D --|是| E[选择分数最高的 SymptomArticle] D --|否| F[展示未匹配兜底建议] E -- G[展示风险等级、表现、何时就医、观察指标、准备清单] G -- H{是否已建档} H --|是| I[生成就医信息卡] H --|否| J[引导添加宠物档案] 这条链路的核心不是算法多复杂而是每一步都要给用户明确反馈。输入分析空输入、未匹配、匹配成功都要处理用户点击“分析”后private analyzeSymptomInput(): void { const value this.symptomInput.trim() if (value.length 0) { this.selectedSymptomIndex -1 this.customHealthAdvice 请输入观察到的症状例如呕吐、腹泻、精神萎靡。 this.emergencyCardVisible false this.saveState() return } const matchedIndex this.matchSymptomIndex(value) if (matchedIndex 0) { this.selectedSymptomIndex -1 this.customHealthAdvice 暂时识别不出来。请补充体温、食欲、饮水、排便排尿、精神状态和症状持续时间如果已经精神差、不吃不喝、呼吸异常、抽搐、便血、尿不出或明显疼痛请尽快就医。 } else { this.selectedSymptomIndex matchedIndex const symptom this.symptoms[matchedIndex] this.customHealthAdvice 可能相关症状 symptom.title 。匹配到 this.matchedKeywordText(value, symptom) 。这只是本地症状比对不能替代兽医诊断请继续观察并按“何时就医”处理。 } this.emergencyCardVisible false this.saveState() }这个方法的质量在于兜底。空输入时不是报错而是教用户怎么描述。未匹配时不说“没事”而是提示补充体温、食欲、饮水、排便排尿、精神状态和持续时间并列出高风险症状。匹配成功时用“可能相关症状”表达并明确不能替代兽医诊断。健康类功能最怕两个极端一个是吓用户一个是安慰过头。这里的处理相对稳。关键词匹配MVP 阶段要可解释匹配算法很简单private matchSymptomIndex(value: string): number { const normalized this.normalizeSymptomText(value) let bestIndex -1 let bestScore 0 for (let index 0; index this.symptoms.length; index) { let score 0 const keywords this.symptoms[index].keywords for (let keyIndex 0; keyIndex keywords.length; keyIndex) { if (normalized.indexOf(this.normalizeSymptomText(keywords[keyIndex])) 0) { score } } if (score bestScore) { bestScore score bestIndex index } } return bestScore 0 ? bestIndex : -1 }归一化private normalizeSymptomText(value: string): string { return value.toLowerCase().replace(/\s/g, ) }这不是智能诊断但它有三个优点第一完全本地运行符合离线优先。第二可解释。用户能看到匹配到了哪些关键词。第三内容库可控。新增一个症状条目只需要补内容和关键词不会引入黑盒判断。MVP 阶段我更推荐这种可解释的规则系统而不是一上来就接一个无法解释的模型。展示命中的关键词为了避免用户觉得结果是“凭空猜的”项目会拼出命中的关键词private matchedKeywordText(value: string, symptom: SymptomArticle): string { const normalized this.normalizeSymptomText(value) let text let count 0 for (let index 0; index symptom.keywords.length; index) { const keyword symptom.keywords[index] if (normalized.indexOf(this.normalizeSymptomText(keyword)) 0) { if (count 0) { text 、 } text keyword count } } return text }比如用户输入“肚子大腹水”页面提示“匹配到腹水、肚子大”。这会让本地匹配结果更可信也方便用户检查自己输入是否准确。页面结构风险等级必须醒目健康页核心卡片包含Text(this.currentSymptom().title) Text(根据输入关键词本地比对) Text(this.currentSymptom().risk) Text(this.currentSymptom().summary) this.InfoRow(何时就医, this.currentSymptom().doctor) this.InfoRow(观察指标, this.currentSymptom().observe) this.InfoRow(准备清单, this.currentSymptom().checklist)风险等级使用不同颜色Text(this.currentSymptom().risk) .fontColor( this.currentSymptom().risk 高风险 || this.currentSymptom().risk 需就医 ? Theme.emergency : Theme.warning ) .backgroundColor( this.currentSymptom().risk 高风险 || this.currentSymptom().risk 需就医 ? Theme.emergencySoft : Theme.warningSoft )健康功能里“高风险”不能藏在段落里。它应该是用户扫一眼就能看到的状态。就医信息卡把档案和记录变成现场沟通材料症状详情页可以生成就医卡if (this.emergencyCardVisible this.hasPetProfile) { Column({ space: Theme.spaceS }) { Text(就医信息卡) Text(this.emergencyPetSummary()) Text(症状 this.currentSymptom().title · this.currentSymptom().risk) Text(this.emergencyRecentRecordText()) } }宠物摘要private emergencyPetSummary(): string { if (!this.hasPetProfile) { return 尚未添加宠物档案 } return this.selectedPetName · this.petMeta() }最近记录private emergencyRecentRecordText(): string { const records this.recordsForSelectedDay() if (records.length 0) { return 今天还没有补充记录建议记录食欲、饮水、排便排尿和精神状态。 } return records[0].title records[0].detail }应急携带private emergencyKitText(): string { return 带上近期照片/视频、叫声录音、疫苗驱虫记录、发病时间线和正在使用的药品。 }这个功能的价值在于“帮用户和医生沟通”。很多人一着急就忘记描述时间线、饮食饮水、排便排尿、疫苗驱虫记录。就医卡能把 App 里已有的数据组织起来。为什么健康模块要绑定宠物档案如果没有宠物档案症状库也能查但就医卡价值会低很多。有档案后就医卡能带上名字品种或类型年龄阶段体重性别和绝育状态疫苗信息驱虫信息过敏史慢性病史这些信息看似基础但在实际就医沟通中非常重要。宠物档案不是孤立页面它在健康场景里会变成应急信息的一部分。未匹配输入的高风险兜底我认为健康模块最值得保留的是这段未匹配文案如果已经精神差、不吃不喝、呼吸异常、抽搐、便血、尿不出或明显疼痛请尽快就医。为什么因为关键词库再怎么扩也不可能覆盖用户所有描述。用户可能输入“它不对劲”“一直趴着”“叫得很惨”。匹配不到时App 不能给用户安全错觉。所以未匹配并不等于低风险。未匹配只是说明本地知识库暂时识别不了仍然要提示用户观察高风险信号。后续可以怎么升级当前症状库写在 ArkTS 数组里。后续可以迁移resources/rawfile/symptoms.json - KnowledgeRepository - SymptomService - HealthPage还可以增加同义词词典例如“拉稀”和“腹泻”。高风险关键词强规则例如“尿不出”“抽搐”“呼吸困难”直接置顶。多症状组合匹配例如“呕吐 腹泻 幼猫 未免疫”提高风险提示。最近 7 天记录自动摘要。就医卡导出图片或 PDF。症状内容版本号方便后续更新。但无论怎么升级都要保留“不替代兽医诊断”的边界。这部分最容易踩的坑第一健康内容不能写成诊断结论。尽量使用“可能相关”“建议尽快检查”“不能替代诊断”。第二未匹配时不能说“没问题”。要提示补充信息和高风险情况。第三风险等级必须醒目。用户异常场景下不会细读长文。第四症状库要结构化。否则后续就医卡、搜索、导出都很难做。第五不要让就医卡只展示静态清单。它要聚合宠物档案和近期记录才有真实价值。本篇小结健康模块的核心不是算法炫技而是稳用结构化症状库承载内容。用本地关键词匹配保证离线可用。用命中关键词增强可解释性。用高风险兜底处理未匹配输入。用就医信息卡聚合档案和记录。用克制文案守住产品边界。下一篇是系列收尾主题系统、暗色模式、多设备适配和发布前体验打磨。一个 App 能不能像正式产品很多时候就看最后这些细节。