366 lines
10 KiB
Markdown
366 lines
10 KiB
Markdown
# 小程序导览接口对接说明
|
||
|
||
> 统一维护约定:本文档是小程序对接的固定主文档。后续新增小程序 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`。
|