# 小程序导览接口对接说明 > 统一维护约定:本文档是小程序对接的固定主文档。后续新增小程序 API、展示、播放、讲解词、异常处理和联调说明时,统一修订本文档,不再按单个功能新建小程序对接文档。 ## 0. 快速接入 小程序导览页使用 3 个接口: | 场景 | 接口 | 用途 | | --- | --- | --- | | 进入讲解页 | `stop-info` | 获取标题、简介、图片、绑定展品、当前语言音频/正文状态 | | 点击播放 | `play-info` | 获取唯一可播放 `playUrl` | | 展开正文 | `text-info` | 按需获取讲解词全文 | 推荐顺序: 1. 页面进入先调 `stop-info`。 2. 根据 `stop-info.audioStatus` 决定播放按钮是否可点。 3. 用户点击播放时再调 `play-info`。 4. 用户展开正文时再调 `text-info`。 ## 1. 通用参数 三个接口都使用同一组参数: | 参数 | 是否必填 | 取值 | 说明 | | --- | --- | --- | --- | | `targetType` | 是 | `ITEM` / `STOP` | `ITEM` 表示展品入口,`STOP` 表示讲解点入口 | | `targetId` | 是 | number | 必须和 `targetType` 匹配 | | `lang` | 否 | `zh-CN` / `en-US` | 不传默认 `zh-CN` | 调用示例: ```http GET /app-api/gis/guide/stop/info?targetType=ITEM&targetId=1001&lang=zh-CN GET /app-api/gis/guide/audio/play-info?targetType=ITEM&targetId=1001&lang=zh-CN GET /app-api/gis/guide/audio/text-info?targetType=ITEM&targetId=1001&lang=zh-CN ``` 注意: - `targetType=ITEM` 时,`targetId` 传展品 ID。 - `targetType=STOP` 时,`targetId` 传讲解点 ID。 - 小程序建议始终显式传 `lang`,语言切换后所有请求使用新的 `lang`。 - 不要传 `standard`、`extended`、`businessId`、MinIO 路径或四通道音频 URL。 ## 2. stop-info:讲解页展示信息 ```http GET /app-api/gis/guide/stop/info?targetType=ITEM&targetId=1001&lang=zh-CN ``` 成功响应: ```json { "code": 0, "data": { "available": true, "targetType": "ITEM", "targetId": 1001, "resolvedStopId": 2001, "lang": "zh-CN", "title": "青铜神树", "description": "本讲解点介绍三星堆出土的青铜神树……", "coverImageUrl": "https://cdn.example.com/cover.jpg", "galleryUrls": "[\"https://cdn.example.com/a.jpg\",\"https://cdn.example.com/b.jpg\"]", "imageStatus": "READY", "imageSource": "STOP", "linkedExhibits": [ { "id": 1001, "name": "青铜神树", "nameEn": "Bronze Sacred Tree", "exhibitCode": "EX-001", "coverImageUrl": "https://cdn.example.com/exhibit-cover.jpg" } ], "playTargetType": "STOP", "playTargetId": 2001, "hasAudio": true, "hasText": true, "supportedLanguages": ["zh-CN", "en-US"], "audioStatus": "READY", "reason": null } } ``` 不可用响应仍返回 `code=0`: ```json { "code": 0, "data": { "available": false, "targetType": "ITEM", "targetId": 9999, "lang": "zh-CN", "imageStatus": "MISSING", "imageSource": "NONE", "linkedExhibits": [], "hasAudio": false, "hasText": false, "supportedLanguages": [], "audioStatus": "MISSING", "reason": "NO_GUIDE_STOP" } } ``` 字段说明: | 字段 | 说明 | | --- | --- | | `available` | 是否有可展示的讲解点 | | `resolvedStopId` | 最终解析到的讲解点 ID | | `coverImageUrl` | 讲解点封面图 | | `galleryUrls` | 讲解点图集,JSON 字符串数组,客户端需安全解析 | | `imageStatus` | `READY` / `MISSING` | | `linkedExhibits` | 当前讲解点绑定的展品列表 | | `playTargetType` / `playTargetId` | 调用 `play-info`、`text-info` 时推荐使用的目标 | | `hasAudio` | 是否存在任一语言音频 | | `supportedLanguages` | 有音频的语言列表 | | `audioStatus` | 当前请求语言是否可播放:`READY` / `MISSING` | | `hasText` | 当前语言是否有讲解词正文 | | `reason` | 不可用原因 | 图片规则: - `imageStatus=READY`:使用 `coverImageUrl` 和 `galleryUrls` 渲染讲解点图片。 - `imageStatus=MISSING`:展示占位图或隐藏图片区域。 - 不要用 `linkedExhibits[*].coverImageUrl` 兜底讲解点主图。 音频按钮规则: - 播放按钮以 `audioStatus` 为准。 - `hasAudio=true` 只表示存在任一语言音频,不代表当前语言可播放。 - 如果 `audioStatus=MISSING`,当前语言播放按钮置灰。 ## 3. play-info:音频播放 ```http GET /app-api/gis/guide/audio/play-info?targetType=STOP&targetId=2001&lang=zh-CN ``` 可播放响应: ```json { "code": 0, "data": { "playable": true, "targetType": "STOP", "targetId": 2001, "lang": "zh-CN", "narrationTier": "STANDARD", "audioId": 8912, "title": "青铜神树", "duration": 120, "format": "mp3", "playUrl": "https://cdn.museum.com/tts-audio/xxx.mp3", "expiresAt": null, "subtitleUrl": null, "hasText": true, "fallback": false, "fallbackReason": null, "reason": null } } ``` 不可播放响应仍返回 `code=0`: ```json { "code": 0, "data": { "playable": false, "targetType": "STOP", "targetId": 2001, "lang": "en-US", "narrationTier": "STANDARD", "hasText": false, "fallback": false, "reason": "NO_PUBLISHED_AUDIO" } } ``` 字段说明: | 字段 | 说明 | | --- | --- | | `playable` | 是否可播放 | | `playUrl` | 可直接赋值给小程序音频组件的地址 | | `expiresAt` | 播放地址过期时间;为空表示当前地址无短期过期限制 | | `duration` | 音频时长,单位秒 | | `format` | 音频格式 | | `hasText` | 是否有同语言讲解词正文 | | `reason` | 不可播放原因 | 播放失败处理: - `playable=false`:不启动播放器,按 `reason` 展示提示。 - `playUrl` 播放失败、403、404 或疑似过期:重新请求一次 `play-info`。 - 请求英文没有音频时,不要自动播放中文音频。 ## 4. text-info:讲解词正文 ```http GET /app-api/gis/guide/audio/text-info?targetType=STOP&targetId=2001&lang=zh-CN ``` 可用响应: ```json { "code": 0, "data": { "available": true, "targetType": "STOP", "targetId": 2001, "lang": "zh-CN", "narrationTier": "STANDARD", "title": "青铜神树", "text": "青铜神树是三星堆遗址出土的重要青铜器...", "textLength": 1200, "textHash": "0b32a4..." } } ``` 不可用响应仍返回 `code=0`: ```json { "code": 0, "data": { "available": false, "targetType": "STOP", "targetId": 2001, "lang": "en-US", "narrationTier": "STANDARD", "reason": "NO_TEXT" } } ``` 字段说明: | 字段 | 说明 | | --- | --- | | `available` | 是否有正文 | | `text` | 讲解词正文 | | `textLength` | 正文字数 | | `textHash` | 正文版本标识,可用于本地缓存判断 | | `reason` | 不可用原因 | 正文建议按需加载,不需要在页面进入时强制请求。 ## 5. 页面推荐流程 ```ts async function enterGuidePage(targetType: "ITEM" | "STOP", targetId: number) { const lang = getGlobalLanguage() || "zh-CN" const stopInfo = await api.getGuideStopInfo({ targetType, targetId, lang }) if (!stopInfo.available) { showStopUnavailable(stopInfo.reason) return } renderStopPage({ title: stopInfo.title, description: stopInfo.description, cover: stopInfo.coverImageUrl, gallery: parseJsonArraySafely(stopInfo.galleryUrls), imageStatus: stopInfo.imageStatus, linkedExhibits: stopInfo.linkedExhibits }) setPlayButtonEnabled(stopInfo.audioStatus === "READY") bindPlayButton(async () => { if (stopInfo.audioStatus !== "READY") { showToast("当前语言暂无可播放音频") return } const playInfo = await api.getAudioPlayInfo({ targetType: stopInfo.playTargetType, targetId: stopInfo.playTargetId, lang }) if (!playInfo.playable || !playInfo.playUrl) { showToast(reasonToText(playInfo.reason)) return } audioContext.src = playInfo.playUrl audioContext.title = playInfo.title || "" audioContext.play() }) bindExpandText(async () => { if (!stopInfo.hasText) return const textInfo = await api.getAudioTextInfo({ targetType: stopInfo.playTargetType, targetId: stopInfo.playTargetId, lang }) if (textInfo.available) { renderNarrationText(textInfo.text) } }) } ``` ## 6. reason 处理建议 | reason | 建议提示 | | --- | --- | | `NO_GUIDE_STOP` | 该展品暂未配置讲解 | | `NO_GUIDE_CONTENT` | 该讲解点暂无讲解内容 | | `NO_PUBLISHED_AUDIO` | 当前语言暂无语音讲解 | | `NO_TEXT` | 当前语言暂无讲解词 | | `UNSUPPORTED_LANGUAGE` | 暂不支持该语言 | | `UNSUPPORTED_TARGET_TYPE` | 暂不支持该目标类型 | | `TARGET_NOT_FOUND` | 内容不存在或已下架 | 小程序可根据页面语气调整文案,但不要把以上业务空态当成系统异常弹窗。 ## 7. 小程序本地缓存建议 播放信息: - key:`targetType + targetId + lang` - 语言切换后清理旧语言缓存。 - 播放失败、403、404 或 URL 过期时,重新请求一次 `play-info`。 讲解词正文: - key:`targetType + targetId + lang` - value:`text + textHash` - 再次请求后如果 `textHash` 变化,替换本地正文。 - 不建议长期永久缓存正文。 ## 8. 联调检查清单 - 不传 `lang`:三个接口默认按 `zh-CN` 返回。 - 进入讲解页:先调 `stop-info`,不要用 `play-info` 承担图片、简介、绑定展品展示。 - 图片存在:`imageStatus=READY`,使用讲解点图片渲染。 - 图片缺失:`imageStatus=MISSING`,展示占位或隐藏图片区。 - 当前语言可播放:`audioStatus=READY`,播放按钮可点。 - 只有其他语言音频:`hasAudio=true` 且 `audioStatus=MISSING`,当前语言播放按钮不可点。 - 点击播放:`play-info.playable=true` 时设置 `audio.src=playUrl`。 - 英文缺音频:返回 `NO_PUBLISHED_AUDIO`,不自动播放中文。 - 展开正文:`text-info.available=true` 时渲染正文。 - 英文缺正文:返回 `NO_TEXT`,不影响音频播放。 - 展品未绑定讲解点:返回 `NO_GUIDE_STOP`。 - 播放地址失效:重新请求一次 `play-info`。