山海万灵 HarmonyOS 文化知识实战(06):知识图谱节点与推荐关系

📅 2026/8/4 10:30:30
山海万灵 HarmonyOS 文化知识实战(06):知识图谱节点与推荐关系
在文化知识应用中读者从一只神兽继续阅读时下一张卡片不能只按列表顺序出现。山海万灵把神兽、地域和展厅放进同一组稳定标识推荐项既带有目标节点也带有“同展厅关联”“同区域关联”等可读原因。页面据此展示推荐卡接口不可用时仍能基于本地目录给出一致的导览结果。从展品目录提取可比较的节点每条神兽记录包含id、regionId和hallId。这三个字段是推荐计算的输入而名称、摘要和卡片配色属于展示信息。把关联键放在领域对象中图鉴、馆长导览和展厅页面就不必各自维护一份关系表。export interface GraphRecommendationItem { nodeType: string nodeId: string name: string summary: string graphReason: string } export interface BeastItem { id: string name: string regionId: string hallId: string summary: string }nodeId保持业务主键卡片组件再用它查找完整神兽资料。这样推荐结果不会携带两套容易漂移的名称和图片数据当目录补充别名或来源信息时展示层仍从同一份神兽集合取值。字段负责的问题页面使用方式regionId表达神兽所属导览地域优先推荐同区域的相关条目hallId表达当前主题展厅为同馆展品提供更高关联度nodeId锁定可打开的目标节点点击推荐卡后进入对应图鉴详情graphReason解释推荐来源在卡片上下文中显示关联标签推荐排序先保留关系再处理展示本地推荐并不把“相关”写死在页面。ViewModel 先排除当前神兽再按地域和展厅计算分数同地域得到两分同展厅额外得到一分。排序后的前四项被映射为轻量推荐对象页面只消费稳定的输出结构。private buildLocalRecommendations(source: BeastItem, beasts: BeastItem[]): GraphRecommendationItem[] { return beasts .filter((item: BeastItem) item.id ! source.id) .slice() .sort((left: BeastItem, right: BeastItem) { const leftScore: number (left.regionId source.regionId ? 2 : 0) (left.hallId source.hallId ? 1 : 0) const rightScore: number (right.regionId source.regionId ? 2 : 0) (right.hallId source.hallId ? 1 : 0) return rightScore - leftScore }) .slice(0, 4) .map((item: BeastItem): GraphRecommendationItem ({ nodeType: BEAST, nodeId: item.id, name: item.name, summary: item.summary, graphReason: item.hallId source.hallId ? 同展厅关联 : (item.regionId source.regionId ? 同区域关联 : 补充图谱覆盖) })) }这个顺序让“关系理由”来自同一套计算而不是在 UI 中拼接说明文字。若两个候选只有地域相同读者会看到同区域关联当候选同时位于同一展厅标签会升级为同展厅关联。远端图谱与本地导览共用输出契约网络数据源请求图谱推荐时将服务端返回投影为GraphRecommendationItem。本地算法和 HTTP 映射最终都交给同一种类型因此组件不需要分辨数据来自服务端还是内置目录。async listGraphRecommendations(nodeType: string, nodeId: string): PromiseGraphRecommendationItem[] { const response await this.apiClient.getJsonApiEnvelopeApiGraphRecommendationPayload( /graph/recommendations?nodeType nodeType nodeId nodeId ) return response.data.items.map((item: ApiGraphRecommendation): GraphRecommendationItem ({ nodeType: item.nodeType, nodeId: item.nodeId, name: item.name, summary: item.summary, graphReason: item.graphReason })) }场景结果来源保持不变的边界图谱接口正常返回HTTP 映射后的推荐项nodeId、名称、摘要和关联理由接口请求失败本地目录计算的前四项点击卡片仍可打开目标神兽推荐为空不渲染推荐面板主导览内容不因空列表中断新增关系类型扩展graphReason的来源卡片组件继续按字符串渲染上下文推荐面板只负责渲染与跳转推荐面板根据数组是否为空决定是否出现。每个条目用nodeId作为列表稳定键再将关联理由传入神兽卡片。点击回调只上报目标神兽标识路由和详情加载仍由外层页面统一处理。if (this.recommendations.length 0) { Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) { ForEach(this.recommendations, (item: GraphRecommendationItem) { ShanhaiBeastCard({ beast: this.recommendationBeast(item), contextText: 关联 item.graphReason, onOpen: (beastId: string) this.onOpen(beastId) }) }, (item: GraphRecommendationItem) item.nodeId) } }这种拆分避免在卡片内部重新计算关系也避免把网络状态塞进组件。窄屏时推荐卡纵向排列较宽的内容区使用两列两种布局共用同一组推荐对象和点击回调。关系面板保留出处与导览边界馆长结果页同时显示来源、地域和展厅关系。展示文案把“本馆导览地域”“本馆主题展厅”与古籍事实区分开前者是应用中的导航关系后者由来源字段和核验信息承载。关系数组为空时组件按照当前神兽的地域与展厅生成保底导览行不让信息面板变成空白。private displayedRelations(): CuratorRelationItem[] { if (this.relations.length 0) return this.relations.slice(0, 2) const region SHANHAI_REGIONS.find((item: RegionItem) item.id this.beast.regionId) const hall SHANHAI_HALLS.find((item: HallItem) item.id this.beast.hallId) return [ { type: 本馆导览地域, targetType: REGION, targetId: this.beast.regionId, label: region?.name ?? 已登记地域, detail: 从此地域展开本馆导览不作为古籍地理断言 }, { type: 本馆主题展厅, targetType: HALL, targetId: this.beast.hallId, label: hall?.name ?? 已登记展厅, detail: 可继续查看本馆主题资料不作为古籍分类 } ] }验收关注关联原因和跳转目标验证时选择一只神兽进入馆长导览查看关系区和推荐卡中的关联说明再打开一张推荐卡核对详情对象与卡片的nodeId一致。随后切换到没有远端图谱结果的状态检查本地目录仍输出可打开的推荐项。ArkUI 的状态管理与组件更新机制可参考 HarmonyOS 状态管理文档。推荐关系由领域标识、排序策略和展示组件依次衔接。服务端可以逐步增加更丰富的关系权重应用仍通过同一输出契约保持导览卡片、关系标签和跳转行为一致。