# `/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 接口只作为地图落点和位置预览的辅助数据。