chore: sync latest project updates
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
lyf
2026-07-03 14:42:38 +08:00
parent 8b2c36677e
commit 8fed715235
106 changed files with 6030 additions and 121 deletions

View File

@@ -0,0 +1,751 @@
# `/guide/exhibits` 与 miniapp 讲解接口数据源偏差诊断
诊断日期2026-07-02
范围:只检查“讲解”业务线数据来源,不展开 SDK 地图渲染、楼层、POI、空间面、路网等地图业务。
本次结论基于两类证据:
- 本地源码:`E:\MyWork\深圳国际艺术馆\智慧导览\smart-navigation-system`
- 本项目源码:`E:\MyWork\深圳国际艺术馆\museum-guide\museum-guide-v4.0\frontend-miniapp`
- 真实 HTTP 接口:`http://1.92.206.90:3001`
## 1. 核心结论
`http://1.92.206.90:3001/guide/exhibits` 管理端页面展示的数据,与 `frontend-miniapp` 当前讲解业务接口返回的数据不一致,主要不是因为数据库完全不同,而是因为:
1. 页面和 miniapp 调用的接口不同。
2. 接口读取的表层级不同。
3. 同一张表上的过滤条件不同。
4. 统计字段的计算口径不同。
管理端 `/guide/exhibits` 使用的是完整讲解业务数据链:
```text
SGS_EXHIBITION_HALL
-> SGS_EXHIBIT_OUTLINE
-> SGS_EXHIBIT_ITEM / SGS_GUIDE_STOP
-> SGS_GUIDE_CONTENT
-> sgs_guide_audio_channel
```
miniapp 当前讲解页虽然已经接入部分 App 端讲解接口,但“业务单元 / 讲解点列表”实际走的是 SDK 地图点位接口:
```text
GET /app-api/gis/sdk/halls/{hallId}/guide-stops
```
该接口只返回 `SGS_GUIDE_STOP` 中已启用且已标定 `mapX/mapY` 的地图点位子集,不等价于管理端 `/guide/exhibits` 所展示的完整讲解业务数据。
因此,当前偏差的根因可以概括为:
```text
管理端读完整讲解业务结构;
miniapp 列表读 SDK 地图标定子集;
App 展厅统计又直接读 hall 表静态字段;
所以展厅统计、业务单元、讲解点数量、音频状态都会出现偏差。
```
## 2. 管理端 `/guide/exhibits` 页面实际数据来源
### 2.1 前端项目与页面源码
页面所属项目:
```text
E:\MyWork\深圳国际艺术馆\智慧导览\smart-navigation-system\sgs-frontend-map
```
页面入口:
```text
sgs-frontend-map/src/app/(main)/guide/exhibits/page.tsx
```
相关前端文件:
| 用途 | 文件 |
| --- | --- |
| 页面组件 | `sgs-frontend-map/src/app/(main)/guide/exhibits/page.tsx` |
| 展厅 / 业务单元 / 展品 API client | `sgs-frontend-map/src/api/map/exhibit.ts` |
| 讲解点 API client | `sgs-frontend-map/src/api/map/space.ts` |
| 左侧树组件 | `sgs-frontend-map/src/components/v2/exhibits/V2ExhibitTreeBrowser.tsx` |
| 讲解模式 / 讲解点面板 | `sgs-frontend-map/src/components/v2/exhibits/GuideStopsPanel.tsx` |
| Axios baseURL | `sgs-frontend-map/src/lib/api.ts`,默认 `/admin-api` |
### 2.2 页面实际调用接口
| 页面区域 | 前端方法 | 实际接口 |
| --- | --- | --- |
| 展厅树 | `ExhibitApi.getTree()` | `GET /admin-api/guide/exhibits/tree` |
| 业务单元子节点 | `ExhibitApi.getChildren(nodeId)` | `GET /admin-api/guide/exhibits/tree/{nodeId}/children` |
| 树搜索 | `ExhibitApi.searchTree()` | `GET /admin-api/guide/exhibits/tree/search` |
| 展品列表 | `ExhibitApi.getPage(params)` | `GET /admin-api/guide/exhibits/list` |
| 讲解模式 / 讲解点 | `StopApi.list(outlineId)` | `GET /admin-api/gis/guide-stop/list?outlineId=...` |
| 跨业务单元搜索讲解点 | `StopApi.listByHallId(hallId)` | `GET /admin-api/gis/guide-stop/list-by-hall?hallId=...` |
### 2.3 管理端后端链路
| 接口 | Controller | Service / Mapper | 主要表 |
| --- | --- | --- | --- |
| `/guide/exhibits/tree` | `V2ExhibitTreeController` | `ExhibitHallMapper``SgsExhibitOutlineMapper``ExhibitItemMapper` | `SGS_EXHIBITION_HALL``SGS_EXHIBIT_OUTLINE``SGS_EXHIBIT_ITEM``SGS_GUIDE_CONTENT` |
| `/guide/exhibits/tree/{nodeId}/children` | `V2ExhibitTreeController` | `SgsExhibitOutlineMapper``ExhibitItemMapper` | `SGS_EXHIBIT_OUTLINE``SGS_EXHIBIT_ITEM``SGS_GUIDE_CONTENT` |
| `/guide/exhibits/list` | `V2ExhibitCrudController` | `ExhibitServiceImpl``ExhibitItemMapper``GuideContentMapper` | `SGS_EXHIBIT_ITEM``SGS_GUIDE_CONTENT` |
| `/gis/guide-stop/list` | `GuideStopController` | `SgsGuideStopServiceImpl``SgsGuideStopMapper` | `SGS_GUIDE_STOP` |
| `/gis/guide-stop/list-by-hall` | `GuideStopController` | `SgsGuideStopServiceImpl``SgsExhibitOutlineMapper``SgsGuideStopMapper` | `SGS_EXHIBIT_OUTLINE``SGS_GUIDE_STOP` |
管理端源码里已经明确说明:
```text
展厅 — SGS_EXHIBITION_HALL
单元 — SGS_EXHIBIT_OUTLINE
展品 — SGS_EXHIBIT_ITEM
```
对应源码:
```text
yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/controller/admin/guide/V2ExhibitTreeController.java
```
### 2.4 管理端统计口径
管理端 `/guide/exhibits/tree` 不直接使用 `SGS_EXHIBITION_HALL.exhibitCount` 作为展示统计。
它会:
1. 读取启用展厅:`SGS_EXHIBITION_HALL.status = 1`
2. 找到展厅下所有 `SGS_EXHIBIT_OUTLINE` 后代节点。
3. 按 outline 聚合 `SGS_EXHIBIT_ITEM` 数量。
4. 通过 `SGS_GUIDE_CONTENT` 判断哪些展品有讲解内容。
5. 汇总到展厅 / 业务单元节点展示。
因此管理端页面上的展厅数量、业务单元数量、讲解内容数量是“动态聚合结果”,不是 hall 表上一个静态字段。
## 3. miniapp 当前讲解页实际数据来源
### 3.1 当前配置
本项目:
```text
E:\MyWork\深圳国际艺术馆\museum-guide\museum-guide-v4.0\frontend-miniapp
```
配置文件:
```text
.env
src/config/dataSource.ts
```
当前配置:
```text
VITE_DATA_SOURCE_MODE=sdk
VITE_GUIDE_CONTENT_SOURCE_MODE=remote
VITE_API_BASE_URL=/app-api
VITE_SGS_API_BASE_URL=/app-api
```
含义:
- 地图/导览运行模式是 `sdk`
- 讲解内容数据源是 `remote`
- App API 基础路径是 `/app-api`
-`sdk` 模式不应被理解为讲解业务列表的数据源SDK 只应服务地图渲染/点位层。
### 3.2 miniapp 前端方法与接口
| miniapp 展示数据 | 前端方法链路 | 实际接口 |
| --- | --- | --- |
| 展厅列表 | `ExplainUseCase.loadExplainHalls()` -> `ExplainRepository.listHalls()` -> `BackendExplainContentProvider.requestHallList()` | `GET /app-api/gis/hall/list` |
| 业务单元统计 | `loadExplainHallSummaries()` -> `loadTemporaryBusinessUnitsByHall()` -> `groupGuideStopsByOutline()` | 基于 SDK guide-stops 分组 |
| 讲解点列表 | `BackendExplainContentProvider.listGuideStopsByHall()` -> `sgsSdkApiProvider.getGuideStopsByHall()` | `GET /app-api/gis/sdk/halls/{hallId}/guide-stops` |
| 讲解点详情 | `AudioPlayInfoRepository.getStopInfo()` | `GET /app-api/gis/guide/stop/info` |
| 音频播放信息 | `AudioPlayInfoRepository.getPlayInfo()` | `GET /app-api/gis/guide/audio/play-info` |
| 正文 | `AudioPlayInfoRepository.getTextInfo()` | `GET /app-api/gis/guide/audio/text-info` |
关键问题:
```text
miniapp 的“业务单元 / 讲解点列表”不是从讲解业务树接口读取,
而是从 SDK 地图点位接口读取。
```
## 4. App API 后端读取表与管理端差异
### 4.1 `/app-api/gis/hall/list`
Controller
```text
yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/controller/app/guide/AppExhibitController.java
```
接口:
```text
GET /app-api/gis/hall/list
```
读取:
```text
SGS_EXHIBITION_HALL
```
过滤:
```text
status = 1
```
返回 `exhibitCount` 的方式:
```text
vo.setExhibitCount(h.getExhibitCount())
```
也就是说App 端 hall/list 的 `exhibitCount` 直接来自 `SGS_EXHIBITION_HALL.exhibitCount` 字段。
这与管理端 `/guide/exhibits/tree` 动态聚合 `SGS_EXHIBIT_OUTLINE + SGS_EXHIBIT_ITEM + SGS_GUIDE_CONTENT` 的口径不同。
结论:
```text
即使两边都读取 SGS_EXHIBITION_HALL
展厅统计也可能不同。
管理端显示的是动态聚合数量;
App API 返回的是 hall 表静态 exhibitCount 字段。
```
### 4.2 `/app-api/gis/zone/list-by-hall`
Controller
```text
yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/controller/app/guide/AppExhibitController.java
```
接口:
```text
GET /app-api/gis/zone/list-by-hall?hallId=...
```
源码注释:
```text
展区数据应该从 SGS_EXHIBIT_OUTLINE 读取,这里返回空列表
TODO: 如果需要展区功能,应该查询 SGS_EXHIBIT_OUTLINE 表
```
结论:
```text
App 端目前没有真正提供与管理端业务单元一致的接口。
管理端业务单元来自 SGS_EXHIBIT_OUTLINE
App 端 zone/list-by-hall 当前返回空数组。
```
### 4.3 `/app-api/gis/sdk/halls/{hallId}/guide-stops`
Controller
```text
yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/controller/app/spatial/SdkMapController.java
```
Service
```text
yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/service/sdk/SdkMapServiceImpl.java
```
接口:
```text
GET /app-api/gis/sdk/halls/{hallId}/guide-stops
```
读取主表:
```text
SGS_GUIDE_STOP
```
过滤条件:
```text
status in GuideStopAvailability.AVAILABLE_STATUSES
mapX is not null
mapY is not null
```
其中 `GuideStopAvailability.AVAILABLE_STATUSES = {0, 1}`,用于兼容历史数据中 `0=启用``1=启用` 两套状态。
源码注释明确说明:
```text
SDK 只输出可落到地图上的讲解点;未标定的属于讲解内容,不进入点位层。
```
结论:
```text
该接口是 SDK 地图点位接口,不是完整讲解业务列表接口。
它只返回已启用且已地图标定 mapX/mapY 的讲解点。
未标定但有讲解内容、有音频、有正文的讲解点,会被该接口过滤掉。
```
### 4.4 `/app-api/gis/guide/stop/info`
Controller
```text
yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/controller/app/guide/AppGuideStopController.java
```
Service
```text
yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/service/guide/AppGuideStopServiceImpl.java
```
接口:
```text
GET /app-api/gis/guide/stop/info?targetType=STOP&targetId=...&lang=zh-CN
```
读取:
```text
SGS_GUIDE_STOP
SGS_GUIDE_CONTENT
```
音频摘要复用:
```text
GuideAudioPlayService
```
注意:单点详情不需要叠加 `mapX/mapY` 过滤。`GuideStopAvailability.java` 源码注释明确说明:
```text
SDK 列表查询需要叠加 mapX/mapY 过滤,但单点 ID 查询stop-info / play-info / text-info不需要叠加。
```
结论:
```text
同一个 stopId 可能不出现在 SDK 地图列表中,
但仍然可以通过 stop-info / play-info / text-info 查到讲解详情、音频、正文。
```
### 4.5 `/app-api/gis/guide/audio/play-info` 与 `text-info`
Controller
```text
yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/controller/app/guide/AppGuideAudioController.java
```
Service
```text
yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/service/guide/GuideAudioPlayServiceImpl.java
```
读取:
```text
SGS_GUIDE_STOP
SGS_GUIDE_CONTENT
sgs_guide_audio_channel
```
主要逻辑:
- `play-info` 判断是否有可播放音频。
- `text-info` 判断是否有同语言正文。
- 播放状态以 `SGS_GUIDE_CONTENT``sgs_guide_audio_channel` 的发布状态为准。
- 不以 SDK guide-stops 返回的 `hasAudio` 为准。
结论:
```text
SDK guide-stops 的 hasAudio 不等价于讲解播放服务的 hasAudio / playable。
SDK 列表的 hasAudio 主要看点位自身字段;
play-info 的 playable 看讲解内容和正式音频通道。
```
## 5. 真实接口测试结果
本轮复测基础地址:
```text
http://1.92.206.90:3001
```
### 5.1 展厅列表
请求:
```bash
GET http://1.92.206.90:3001/app-api/gis/hall/list
```
结果:
```text
HTTP 200
code = 0
msg = ""
data.length = 8
```
前 5 条:
| id | name | exhibitCount |
| --- | --- | --- |
| `715792102100832258` | 宇宙厅 | 0 |
| `715792102100832257` | 地球厅 | 0 |
| `715792102100832259` | 演化厅 | 0 |
| `715792102100832260` | 恐龙厅 | 0 |
| `715792102100832256` | 人类厅 | 0 |
说明:
```text
这里的 exhibitCount 来自 SGS_EXHIBITION_HALL.exhibitCount
不能直接拿来对比管理端 /guide/exhibits 页面动态聚合出的展品/讲解数量。
```
### 5.2 宇宙厅 SDK guide-stops
取 hallId
```text
715792102100832258
```
请求:
```bash
GET http://1.92.206.90:3001/app-api/gis/sdk/halls/715792102100832258/guide-stops
```
结果:
```text
HTTP 200
code = 0
msg = ""
data.length = 2
```
返回样例:
| id | name | targetType | targetId | outlineId | outlineName | hasAudio |
| --- | --- | --- | --- | --- | --- | --- |
| `1823450596808612` | 古典星盘 讲解 | `GUIDE_STOP` | `1823450596808612` | `7467940240901013505` | 第一单元:仰望苍穹——人类对宇宙认识的历程 | false |
| `1823450596814245` | 火星提森特陨石 讲解 | `GUIDE_STOP` | `1823450596814245` | `7467940240901013507` | 第三单元:采石知天——行星科学与深空探测 | false |
说明:
```text
这里的 2 条不是宇宙厅完整讲解点数量,
而是宇宙厅下已启用且已标定 mapX/mapY 的 SDK 地图点位数量。
```
### 5.3 stop-info
取 guideStop.id
```text
1823450596808612
```
请求:
```bash
GET http://1.92.206.90:3001/app-api/gis/guide/stop/info?targetType=STOP&targetId=1823450596808612&lang=zh-CN
```
结果:
```text
HTTP 200
code = 0
msg = ""
```
关键字段:
| 字段 | 值 |
| --- | --- |
| `available` | true |
| `targetType` | STOP |
| `targetId` | 1823450596808612 |
| `resolvedStopId` | 1823450596808612 |
| `title` | 古典星盘 讲解 |
| `playTargetType` | STOP |
| `playTargetId` | 1823450596808612 |
| `hasAudio` | true |
| `hasText` | true |
| `audioStatus` | READY |
| `reason` | null |
### 5.4 play-info
请求:
```bash
GET http://1.92.206.90:3001/app-api/gis/guide/audio/play-info?targetType=STOP&targetId=1823450596808612&lang=zh-CN
```
结果:
```text
HTTP 200
code = 0
msg = ""
```
关键字段:
| 字段 | 值 |
| --- | --- |
| `playable` | true |
| `playUrl` | 存在 |
| `duration` | 57 |
| `audioId` | 1253 |
| `title` | `[古典星盘] 标准解说` |
| `reason` | null |
### 5.5 text-info
请求:
```bash
GET http://1.92.206.90:3001/app-api/gis/guide/audio/text-info?targetType=STOP&targetId=1823450596808612&lang=zh-CN
```
结果:
```text
HTTP 200
code = 0
msg = ""
```
关键字段:
| 字段 | 值 |
| --- | --- |
| `available` | true |
| `textLength` | 269 |
| `title` | `[古典星盘] 标准解说` |
| `reason` | null |
正文摘要:
```text
这九个16至19世纪的金属星盘是前卫星时代的“科学计算器”集导航、计时、天文功能于一体……
```
## 6. 为什么 `/guide/exhibits` 与 miniapp 接口返回数据不一致
### 6.1 展厅列表看起来同源,但统计字段不同源
两边都可能读取 `SGS_EXHIBITION_HALL`,但:
| 端 | 展厅基础表 | 数量/统计口径 |
| --- | --- | --- |
| 管理端 `/guide/exhibits` | `SGS_EXHIBITION_HALL` | 通过 `SGS_EXHIBIT_OUTLINE` 后代聚合 `SGS_EXHIBIT_ITEM``SGS_GUIDE_CONTENT` |
| miniapp `/app-api/gis/hall/list` | `SGS_EXHIBITION_HALL` | 直接返回 `SGS_EXHIBITION_HALL.exhibitCount` |
所以线上 App API 返回的 `exhibitCount=0`,不能证明管理端页面也应该显示 0。
偏差原因:
```text
管理端动态算;
App API 直接读静态字段。
```
### 6.2 业务单元不是同一个接口来源
| 端 | 接口 | 表 |
| --- | --- | --- |
| 管理端 | `/admin-api/guide/exhibits/tree/{hallId}/children` | `SGS_EXHIBIT_OUTLINE` |
| miniapp 当前 | `/app-api/gis/zone/list-by-hall` | 源码 TODO当前返回空数组 |
| miniapp 实际展示分组 | 基于 `/app-api/gis/sdk/halls/{hallId}/guide-stops``outlineId/outlineName` 分组 | SDK 点位子集 |
偏差原因:
```text
管理端业务单元来自完整 SGS_EXHIBIT_OUTLINE
miniapp 没有使用等价 App 端 outline 接口,
而是从 SDK guide-stops 返回的少量点位里反推业务单元。
```
这会导致:
- 没有已标定讲解点的业务单元不显示。
- 有业务单元但未落图的讲解点不显示。
- 业务单元数量比管理端少。
### 6.3 讲解点列表不是同一个业务口径
| 端 | 接口 | 表 | 过滤 |
| --- | --- | --- | --- |
| 管理端讲解模式面板 | `/admin-api/gis/guide-stop/list?outlineId=...` | `SGS_GUIDE_STOP` | 按 `outlineId` |
| miniapp 当前列表 | `/app-api/gis/sdk/halls/{hallId}/guide-stops` | `SGS_GUIDE_STOP` | `status in {0,1}``mapX/mapY` 非空 |
偏差原因:
```text
管理端展示讲解业务点;
miniapp 当前展示 SDK 地图可落点。
```
这两个集合的关系是:
```text
SDK guide-stops ⊂ 管理端讲解业务 guide-stops
```
即 SDK guide-stops 通常是管理端讲解点中的一个子集。
### 6.4 音频状态字段不是同一个口径
真实接口已证明同一个 `stopId=1823450596808612`
| 接口 | 字段 | 值 |
| --- | --- | --- |
| `/app-api/gis/sdk/halls/{hallId}/guide-stops` | `hasAudio` | false |
| `/app-api/gis/guide/stop/info` | `hasAudio` | true |
| `/app-api/gis/guide/audio/play-info` | `playable` | true |
| `/app-api/gis/guide/audio/text-info` | `available` | true |
偏差原因:
```text
SDK guide-stops 的 hasAudio 主要看 SGS_GUIDE_STOP.audioUrl
讲解播放接口的 playable 看 SGS_GUIDE_CONTENT 与 sgs_guide_audio_channel。
```
所以 SDK 列表中的 `hasAudio=false` 不应作为讲解业务播放状态。
## 7. 与接口契约的关系
`docs/miniapp_integration.md` 中的 App 端讲解接口契约约定:
- `stop-info` 用于进入讲解页时获取标题、图片、绑定展品、当前语言音频/正文状态。
- `play-info` 用于点击播放时获取唯一可播放 `playUrl`
- `text-info` 用于展开正文时按需获取讲解词全文。
- `available=false` / `playable=false` 是业务不可用,不一定代表 HTTP 失败。
- 不可用响应仍可返回 `code=0`,客户端应看 `data.available``data.playable``reason`
这套契约解决的是“单个 ITEM / STOP 的展示、播放、正文”问题。
它没有解决:
```text
按展厅获取完整业务单元树;
按业务单元获取完整讲解点列表;
按管理端相同口径统计展品数/讲解数。
```
因此当前 miniapp 需要补充“讲解业务列表 / 树”类 App API而不是继续用 SDK 地图点位接口替代。
## 8. 推荐修复方向
### 8.1 后端补 App 端讲解业务接口
建议新增或实现以下接口之一:
```text
GET /app-api/gis/guide/halls/{hallId}/outlines
GET /app-api/gis/guide/outlines/{outlineId}/stops
```
或组合接口:
```text
GET /app-api/gis/guide/halls/{hallId}/explain-tree
```
接口读取口径应对齐管理端:
```text
SGS_EXHIBITION_HALL
SGS_EXHIBIT_OUTLINE
SGS_GUIDE_STOP
SGS_EXHIBIT_ITEM
SGS_GUIDE_CONTENT
sgs_guide_audio_channel
```
要求:
- 不按 `mapX/mapY` 过滤讲解点。
- 业务单元从 `SGS_EXHIBIT_OUTLINE` 获取。
- 讲解点从 `SGS_GUIDE_STOP` 获取。
- 展品绑定关系从 `SGS_EXHIBIT_ITEM.stopId` 获取。
- 音频状态复用 `GuideAudioPlayService` 的摘要口径。
- 展厅/业务单元统计口径对齐管理端 `/guide/exhibits/tree`
### 8.2 前端替换列表数据源
miniapp 前端应调整:
```text
BackendExplainContentProvider.listGuideStopsByHall()
BackendExplainContentProvider.listTemporaryBusinessUnitsByHall()
```
不要再从:
```text
/app-api/gis/sdk/halls/{hallId}/guide-stops
```
生成完整讲解业务单元和讲解点列表。
SDK guide-stops 可以保留为“查看位置 / 地图落点预览”的辅助数据,但不能作为讲解业务完整列表数据源。
### 8.3 展厅统计修复
`/app-api/gis/hall/list``exhibitCount` 当前直接来自 `SGS_EXHIBITION_HALL.exhibitCount`
如果 miniapp 需要展示与管理端一致的统计,应选择其一:
1. 后端在 `hall/list` 中动态聚合管理端同口径统计。
2. 新增 `guide/halls/{hallId}/explain-tree` 时返回统计。
3. 定期维护 `SGS_EXHIBITION_HALL.exhibitCount`,并明确它与管理端动态统计一致。
推荐优先采用第 2 种:在讲解业务树接口中返回统计,避免让通用 hall/list 承担复杂业务聚合。
## 9. 最终判断
`/guide/exhibits` 页面展示数据和 `frontend-miniapp` 当前接口返回数据不一致,根因不是简单的“接口坏了”,而是:
```text
管理端 /guide/exhibits 读取完整讲解业务表,并动态聚合统计;
miniapp 当前展厅列表读取 hall 表静态 exhibitCount
miniapp 当前业务单元和讲解点列表读取 SDK 地图标定点位子集;
miniapp 单点详情/播放/正文又读取讲解业务内容表和音频通道表。
```
因此当前 miniapp 同时混用了:
- 讲解业务数据源:`SGS_EXHIBITION_HALL``SGS_GUIDE_STOP``SGS_GUIDE_CONTENT``sgs_guide_audio_channel`
- SDK 地图数据源/点位口径:`SGS_GUIDE_STOP` 中已标定 `mapX/mapY` 的子集
- 静态统计字段:`SGS_EXHIBITION_HALL.exhibitCount`
正确方向是把“讲解业务列表 / 业务单元 / 讲解点数量 / 讲解模式”统一切回讲解业务接口SDK 接口只作为地图落点和位置预览的辅助数据。