Files
frontend-miniapp/docs/SGS_SDK_3D_RENDERING_COMPARISON_REPORT.md
lyf f0d27441d6
Some checks failed
CI / verify (push) Has been cancelled
完善讲解配置与H5构建校验
2026-07-24 22:34:16 +08:00

233 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SGS SDK 与当前 H5 三维渲染对照研究报告
> 版本2026-07-18
> 范围:`frontend-miniapp` 当前本地 ThreeMap 渲染器,与 `smart-navigation-system/sgs-frontend-map/sdk-engine` 的 SDK Engine。
> 结论边界:本报告是参数、空间契约与验证方案研究,**不是**将当前 H5 迁移为 SDK iframe 的建议;不将外观首页和 SDK 单楼层 Demo 误判为应像素级一致的场景。
## 1. 执行摘要
当前 H5 应继续采用“本地 ThreeMap 渲染 + SGS REST 数据”的架构。`src/config/dataSource.ts:91-94` 中的 SDK 脚本和 Engine URL 只是配置能力;实际 SDK 数据路径是 `sgsSdkApiProvider``uni.request`,经 `GuideModelRepository` 转为本地模型资源供 Three.js 加载。既有的本地渲染和 SDK Engine 在背景、相机投影、DPR、渲染器、色调映射、阴影与三灯参数上已基本对齐因此不建议进行无目标的颜色/灯光再调参。
当前最高优先级不是视觉参数,而是 Bundle 的模型变换契约缺失SDK Engine 会尝试读取变换但静默回退单位矩阵H5 类型、领域模型和仓储也只传递 URL。实时 Bundle 已确认不含变换字段;但尚未证明生产数据库中存在非单位变换。因此 P0 仅应完成真实激活模型的只读数据核验,不能先行扩展运行时代码。
SDK 与 H5 的相机、POI 密度与标签实现有刻意的产品策略差异。SDK 单楼层 AutoFit 与 H5 外观/楼层状态机不是相同视图;后续视觉验收必须固定同一楼层、同一模型、同一视口、同一 DPR 和同一 view mode。POI LOD 可做受控实验,但应保留 H5 的 WebGL 命中目标、DOM 标签和碰撞规避。
## 2. 证据口径
| 标签 | 含义 | 本报告用法 |
| --- | --- | --- |
| 源码事实 | 可定位到当前两仓源码的声明或执行路径 | 可以用于架构、参数和契约判断 |
| 实时接口事实 | 2026-07-18 对已部署接口的实际响应 | 只说明当时、该地图/楼层的数据 |
| 浏览器观察 | 指定服务、URL、视口下的实际运行结果 | 不能外推为所有发布物或设备 |
| 尚未证明 | 当前证据不足以作出肯定结论 | 列为核验或回归项,不写作已修复 |
## 3. 两项目架构边界与数据链
| 维度 | 当前 H5 (`frontend-miniapp`) | SDK Engine / Web Demo | 判断 |
| --- | --- | --- | --- |
| 数据取得 | `sgsSdkApiProvider``uni.request` 请求 `/app-api/gis/sdk/*``src/data/providers/sgsSdkApiProvider.ts:381,458,489` | Engine 读取 SDK API / Bundle | 数据可比,渲染运行时不同 |
| 模型资源 | `GuideModelRepository``modelUrl` / `fallbackModelUrl`,生成本地 `GuideModelFloorAsset``src/repositories/GuideModelRepository.ts:364-378` | Engine 直接消费 Bundle model | 都依赖 Bundle但不可假设变换已传递 |
| 渲染器 | 本地 Three.js `ThreeMap.vue` | SDK iframe 内 Engine | 不建议因本次研究替换 H5 渲染器 |
| SDK URL 配置 | 存在 `sgsSdkScriptUrl``sgsMapEngineUrl` 和 timeout`src/config/dataSource.ts:91-94` | 发布 Demo 用相对 `../engine/` | 配置存在不等于当前 H5 走 iframe |
| 页面能力 | H5 有自己的楼层基线、状态机、DOM 标签和 WebGL 命中目标 | 单楼层 AutoFit、HTML POI 层、可选服务端 LOD | 应比较契约/行为,不要求视觉像素等价 |
**源码事实。** 当前 H5 的领域类型 `GuideModelFloorAsset` 只含楼层、标签、顺序和 URL 类字段(`src/domain/guideModel.ts:24-31`);这与 REST 提供者 `SgsModelInfoPayload` 的字段集合一致(`src/data/providers/sgsSdkApiProvider.ts:194-202`)。本报告不建议跨仓抽取共享运行时包:两端发布、部署和生命周期边界不同,优先使用契约快照防漂移。
## 4. 三维基础渲染参数对照
| 项 | SDK Engine | 当前 H5 | 结论 |
| --- | --- | --- | --- |
| 场景背景 | `#ECEFF1` | `0xeceff1``ThreeMap.vue:106-108,1918-1920` | 一致 |
| PerspectiveCamera | FOV 42、near 0.1、far 20000`Viewport3D.tsx:509-558` | FOV 42、near 0.1、far 20000`ThreeMap.vue:108-112,1923-1928` | 一致 |
| Renderer | DPR cap 1.5、high-performance、antialias、alpha=false | 同值,`ThreeMap.vue:114-121,1931-1937` | 一致 |
| 色调/曝光/阴影 | NoToneMapping、1.0、关闭阴影 | 同值,`ThreeMap.vue:119-121,1939-1941` | 一致 |
| 半球灯 | `#fff/#b0bec5`、1.7 | 同值,`ThreeMap.vue:123-127,1968-1973` | 一致 |
| 主光 | `[80,120,80]`、2.2 | 同值,`ThreeMap.vue:129-132,1975-1980` | 一致 |
| 补光 | `[-60,70,-50]`、0.55 | 同值,`ThreeMap.vue:134-137,1982-1987` | 一致 |
**源码事实。** SDK 参数来源为 `sgs-frontend-map/sdk-engine/src/components/Viewport3D.tsx:509-558`H5 参数集中在 `src/components/map/ThreeMap.vue:106-137,1918-1987`。因此基础色彩、灯光和 renderer 默认值不是当前主要根因;除非有受控的同场景截图证据,否则不应启动另一轮全局视觉调参。
## 5. 相机与视图策略
SDK AutoFit 以 `__model_root__``Box3`、模型尺寸和预设计算相机(`sdk-engine/src/components/AutoFitCamera.tsx:164-321`),常规距离约为 `fitDim * 1.2`,高度/目标依赖 preset。预设 `sdk-kiosk-landscape``sdk-mobile-portrait``default` 定义在 `sdk-engine/src/config/viewport-presets.ts:67-160`;竖屏会旋转约 90 度并设置 `minFitDimension=260`
H5 则保存自己的参考外观相机和楼层基线:楼层基线从模型盒、屏幕偏移和相机方向计算(`ThreeMap.vue:3505-3554``setCameraView` / `fitCameraToObject` 根据 `overview``floor` 使用不同方向、距离与偏移(`ThreeMap.vue:3833-3921`),楼层恢复还会回放快照(`ThreeMap.vue:4245-4250`)。
**源码事实与验收结论。** H5 外观首页和 SDK 单楼层 Demo 是不同产品视图,不能要求像素级相同。视觉对照的前置条件是:相同 floorId、模型 URL/版本、视口尺寸、DPR、view mode、相机 preset/基线以及无 POI 焦点状态。此前宽屏初始化后缩窄的空白,固定 `430x720` 刷新未复现,故为“尚未证明的生产缺陷”;保留“相机快照污染”回归测试即可。
## 6. 模型变换契约
| 层级 | 现状 | 证据类型 | 风险 |
| --- | --- | --- | --- |
| SDK Engine | Bundle model 被 `any` 读取,尝试 `translationX/Y/Z``rotationX/Y/Z``scaleX/Y/Z`;缺失回退 `[0,0,0] / [0,0,0] / [1,1,1]` | 源码事实:`H5SdkEngine.tsx:5078-5099,5224-5239` | 非单位数据会被静默忽略 |
| SDK Bundle VO | 只发布 URL、压缩/版本、节点数和大小 | 源码事实:`SdkModelInfoVO.java:15-36` | 外部契约未承载矩阵 |
| H5 Provider / Domain / Repo | 同样只承载 URL 等字段 | 源码事实:`sgsSdkApiProvider.ts:194-202``guideModel.ts:24-31``GuideModelRepository.ts:364-378` | H5 也使用单位变换 |
| 另一后端模型接口/DO | 存在变换字段 | 源码事实:`AppGisModelController.java:70-83``Map3dModelDO.java:57-87` | 数据能力与 SDK Bundle 契约脱节 |
| L2 实时 Bundle | `floorId=2065808921578303490``code=0`、30 POImodel keys 仅 URL/压缩/版本/统计字段,所有变换字段缺失 | 实时接口事实2026-07-18 | 只证明该 Bundle 未发布变换 |
**根因判断。** 如果数据库中存在非单位模型变换,当前 SDK 与 H5 都会静默按单位矩阵渲染可能导致模型、POI、路线和点击坐标失配。**尚未证明:** 当前没有证据证明任一激活模型的数据库变换为非单位值;不可把该风险表述为已发生故障。
**P0 核验。** 在不改运行代码的前提下,通过只读 SQL 或管理接口枚举所有激活模型的平移、旋转、缩放;记录 modelId、floorId、版本和原始字段。只有发现非单位值才进入 P2。
## 7. POI、标签与 LOD 策略
SDK 的 `PoiMarkerLayer` 是 HTML 标签,结合类型配置、遮挡检测与绝对相机距离阈值;非 kiosk 为 far=400、mid=150`sdk-engine/src/components/layers/PoiMarkerLayer.tsx:771-855,1080-1086`)。服务端 LOD API 能力存在(`sdk-engine/src/api/sdk-api-loader.ts:524-533`),但 Engine real-preview 默认关闭,仅 URL 带 `enablePoiLabelLod=1/true` 才启用(`H5SdkEngine.tsx:1187-1225`);当前 Web Demo 未传该参数。
实时接口 `/app-api/gis/sdk/maps/1/poi-label-lod` 返回 9 层;总体为 far=22、mid=181、near=183、sourcePoiCount=183、clusterCount=24、singleCount=362L2 为 far=3 / mid=26 / near=26实时接口事实2026-07-18。这证明服务端可供实验不证明当前 Engine Demo 已消费它。
H5 使用模型尺度归一化:点位阈值 `0.32/0.20`、标签阈值 `0.42/0.26``ThreeMap.vue:1260-1328`);点位数量 tight=5、balanced=9、full=无限,屏幕间距 `116/78/0``ThreeMap.vue:1300-1309,1425-1551`)。标记为 WebGL Sprite另设命中 Sprite`ThreeMap.vue:4805-4828,4880-4897`DOM 标签带碰撞规避与 `ResizeObserver``ThreeMap.vue:4550-4591`)。
**建议。** 仅以 feature flag 引入服务端 LOD 作为候选数据源;保留 DOM 标签、碰撞规避及 WebGL 点击命中目标,失败时回退本地策略。不要直接用 SDK HTML POI 替换移动 H5 实现。
## 8. Web Demo 与运行证据
| 观察 | 证据类型 | 判断与后续动作 |
| --- | --- | --- |
| 发布 Demo `app.js:482-530` 从 scene/floorCode 读取参数、固定相对 `../engine/`,等待 `sdk.whenReady()` | 源码事实 | 宿主和 Engine 发布目录是关键依赖 |
| 本地 5555 Demo 持续“连接中”30 秒 `whenReady` 超时iframe 指向 `5555/engine`;直开 `5556/engine` 成功 | 浏览器观察 | 优先查静态托管目录/发布版本不一致;不能归咎渲染器 |
| 直开 SDK Engine1280x720 Canvas 非空L2 Bundle 约117ms、30 POI、Draco 约0.24MB、160节点AutoFit 约262x133m、distance 314m、height 220m发出 `ENGINE_READY` | 浏览器观察 | Engine 在该直接入口可运行 |
| 曾出现一次 `webglcontextlost`,最终画面非空;`Viewport3D.tsx:531-539` 有恢复监听 | 浏览器观察 + 源码事实 | 仍需 context lost/restored 回归,不得视为已完全排除 |
| 当前 H5 外观页正常;`430x720` 刷新后诊断 API 切到 1F`activeView=floor`、单位变换误差0、7个 WebGL 点、2个 DOM 标签都在 Canvas 内 | 浏览器观察 | 证明此固定条件可用,非全部设备证明 |
## 9. 风险分级与分阶段实施方案
| 优先级 | 风险 / 目标 | 实施范围 | 退出条件 |
| --- | --- | --- | --- |
| P0 | Bundle 模型变换是否漏传 | 报告 + 所有激活模型的只读 SQL/管理接口核验;不改运行代码 | 有带版本的单位/非单位清单 |
| P1 | 两仓参数与 Bundle schema 漂移 | 增加参数与 schema 契约快照/测试;不抽取跨仓共享 runtime 包 | 两仓变化能在 CI/本地契约检查中定位 |
| P2 | 仅在 P0 发现非单位变换时,补齐变换契约 | `SdkModelInfoVO`、service mapper、SDK TS 类型、H5 provider/domain/repo/render matrix | 模型、POI、路线、点击坐标共用矩阵 |
| P3 | 验证服务端 POI LOD 是否优于本地密度策略 | feature flag、候选数据源与对照记录保持现有标签/命中目标 | 通过完整 LOD 验收,否则回退 |
| P4 | Demo 宿主/Engine 版本闭环和 WebGL 韧性 | 统一静态发布目录与版本链;增加 context lost/restored 回归 | `whenReady``ENGINE_READY` 稳定闭环 |
P2 的最小正确实现不是“给模型加 transform”即可而是定义同一 `M`:模型使用 `M`POI 与路线使用同一正向投影,点击/射线结果用 `M^-1` 回到业务坐标。同步覆盖单位矩阵和非单位平移/旋转/缩放,禁止局部补偿。
## 10. 验收矩阵
| 验收主题 | 条件 | 必须记录 / 断言 | 失败处理 |
| --- | --- | --- | --- |
| Bundle 变换 | P0 全部激活模型 | 原始字段、floor/model/version、是否单位矩阵 | 未完成前禁止宣称 P2 必要 |
| 变换契约(若 P2 | 单位与非单位矩阵 | 模型位置、POI 投影、路线点、点击反变换、同楼层视觉验证 | 任一失配阻断上线 |
| 视觉对照 | 同楼层、同模型版本、同视口、同 view mode | 相机参数、DPR、截图及差异说明 | 不可用跨场景截图判定 |
| LOD 实验 | L1/L2/EXTERIOR390x844DPR 1 与 3 | 可见标签数、重叠数、点击命中、帧时间 | 失败立即回退当前本地策略 |
| Demo 发布 | Demo 与 Engine 同一发布目录/版本 | `whenReady` resolve、`ENGINE_READY`、楼层切换、30 POI、模型 Canvas 非空 | 调查宿主/资源/版本链 |
| WebGL 恢复 | 主动或受控触发 context lost/restored | 日志、恢复后 Canvas、楼层、POI、交互状态 | 失败阻断 P4 闭环 |
| H5 相机回归 | 固定刷新与宽屏后缩窄 | 外观/楼层快照、Canvas 非空、标签边界 | 标记相机快照污染并修复后复验 |
## 11. 实测与测试证据
- **测试事实:** 当前 H5 已执行 `tests/unit/poiDomLabels.spec.ts``GuideMapShell.spec.ts``GuideModelStateReset.spec.ts`,共 9/9 通过。
- **测试事实:** SDK 相关测试 19/20 通过;`model-load-lifecycle.test.ts` 的唯一失败来自只匹配 LF 的正则读取 CRLF 文件。实际 remount key 代码存在,因此这是 Windows 跨平台测试脆弱性,不是生命周期实现缺失。
- **证据边界:** 上述结果是既有审核提供的执行证据,本报告只整理记录,未在本次文档任务重跑测试或 build避免写入 `output` / `dist`
## 12. 明确不建议事项
1. 不因本报告将当前 H5 本地 ThreeMap 替换为 SDK iframe。
2. 不在没有同场景证据时继续做全局颜色、灯光或 renderer 参数调参。
3. 不把外观首页与 SDK 单楼层 AutoFit 作为像素级等价对象。
4. 不在未核验数据库存在非单位变换前,盲目扩展 Bundle、SDK 或 H5 运行时代码。
5. 不单独变换模型而遗漏 POI、路线和点击坐标也不采用页面级坐标补丁。
6. 不直接照搬 SDK HTML POI替换 H5 的 DOM 碰撞标签和 WebGL 命中 Sprite。
7. 不把 5555 Demo “连接中”直接归因为渲染器故障先证明发布目录、Engine 版本和静态资源链一致。
8. 不以单次 `webglcontextlost` 后画面恢复为 context-recovery 已验收。
## 13. 交接清单
后续执行者应先完成 P0 只读核验,再决定是否开启 P2并在每个阶段把“源码事实、实时接口事实、浏览器观察、尚未证明”分列记录。任何变更前保留当前本地策略作为回退基线特别是移动端 DOM 标签、WebGL 命中目标、楼层基线和相机快照状态机。
## 14. SDK Demo 业务功能与当前 H5 对比
### 14.1 范围、概念与运行边界
本节分析的 SDK Demo 是 `E:/MyWork/深圳国际艺术馆/智慧导览/smart-navigation-system/sgs-map-sdk-release/demo/`,主要证据为 `README.md``index.html``app.js`,以及 Engine 的 `sgs-frontend-map/sdk-engine/src/H5SdkEngine.tsx`。Demo README 明确其定位是“展示 SDK 能开发成什么效果,同时用于调试”,独立静态页不直接调用 `/app-api``/admin-api`,而是由 SDK iframe/Engine 连接远端地图服务(`demo/README.md:1-35`)。它不是当前博物馆移动 H5 的产品需求清单。
本节严格区分四个概念:
| 概念 | 判定依据 | 不能推导出的结论 |
| --- | --- | --- |
| Demo 展示入口 | `index.html` Tab/按钮和 `app.js` 处理函数存在 | 不代表发布链可用,也不代表适合移动端 |
| SDK 已实现能力 | SDK 公开方法可调用,且 Engine 有对应命令/事件处理 | 不代表业务数据已发布或产品流程已设计 |
| 后端接口能力 | Engine 调用的数据接口/Bundle 字段存在 | 不代表 Demo 或 H5 已做端到端错误恢复 |
| 当前 H5 已产品化能力 | H5 页面/组件、Repository/provider 和用户流程共同存在 | 不以按钮文案、类型声明或历史 mock 单独认定 |
**浏览器观察2026-07-18** URL 场景为本地 `5555` 发布 Demoiframe 指向 `5555/engine` 时持续“连接中”,约 30 秒 `whenReady()` 超时;直接打开 `5556/engine` 则可获得非空 Canvas 和 `ENGINE_READY`。因此Demo 的入口和源代码均已验证,但完整宿主链未闭环;本节不把 Demo 所列业务能力标记为“5555 发布版已实测成功”。具体发布链限制见第 8 节。
### 14.2 SDK Demo 功能清单
| 功能域 | Demo 触发入口 | SDK 方法 / 事件 | Engine / 数据接口证据 | 运行状态 |
| --- | --- | --- | --- | --- |
| 初始化与状态 | URL `server/env/mapId/floorCode/token`,创建 `SGSMapSDK` | `whenReady()``on('ready')` | `app.js:479-535`Engine 收到 `HELLO` 后发 `ENGINE_READY``H5SdkEngine.tsx:2521-2525` | 直接 Engine 已观察成功5555 Demo 链未闭环 |
| 事件/错误日志 | “事件”Tab | `ready``poiClick``floorChanged``error``loadError``routeTransfer`、模型/Bundle/路线/漫游事件 | `app.js:554-643` | 展示和诊断入口,不是访客业务 UI |
| 地图/楼层/图层/视图 | 楼层浮栏、2D/3D、全局、Tab 切换 | `getManifest``changeFloor``setVisibleLayers``setViewMode``resetView``setRouteViewActive` | `app.js:172-315,655-704,718-795``CHANGE_FLOOR` 在 Engine 实现,`H5SdkEngine.tsx:2527-2546` | 方法存在;宿主发布链仍是前置风险 |
| 空间查询与聚焦 | “空间”Tab搜索、列表、详情 | `getSpaces()``highlightPolygon()` | `app.js:890-999,999-1068``GET_SPACES` 返回 Bundle spaces`H5SdkEngine.tsx:4111-4145`;高亮设置空间与 fly-to`3188-3218` | SDK/接口能力可确认Demo 是否完整运行受 `whenReady` 限制 |
| POI 查询、筛选、详情、聚焦 | “POI”Tab类型 chip、搜索、列表 | `getFloorPois()``focusTo()``poiClick` | `app.js:1137-1288``GET_FLOOR_POIS``H5SdkEngine.tsx:4070-4104`Canvas 点击上报并 fly-to`4920-4938` | SDK/Engine 已实现Demo 全链未闭环 |
| 讲解点查询 | “讲解点”Tab跨楼层扫描、搜索、详情 | `getGuideStops()``getGuideStopsByHall()` | `app.js:1289-1464``GET_GUIDE_STOPS` 返回 Bundle guideStops`H5SdkEngine.tsx:4159-4176`,按展厅查询 `4178-4193` | 查询能力,不等于讲解内容产品流程 |
| 同层/跨层路径 | “导航”Tab起终点楼层/地点、无障碍、overview/handoff、推荐路线 | `getNavigablePlaces()``planRouteV2()`/`planRoute()``clearRoute()` | `app.js:803-825,1465-1777,1814-1986`Engine `PLAN_ROUTE` 支持 displayMode 并激活跨层 viewer`2785-2816` | SDK 实现存在;真实路网、起终点与发布 Demo 仍应单独验收 |
| 路线分段/换层/漫游 | 路线结果分段按钮、漫游控制 | `routeTransfer``routeReady``startRoaming()`、暂停/停止 | `app.js:587-602,827-889`Engine 有 `START_ROAMING` 命令(`H5SdkEngine.tsx:3243-3299` | SDK 技术能力;不是 H5 当前可宣称的馆内导航 |
| 推荐/精品动线 | “导航”推荐按钮及“动线”Tab | `getFeaturedRoutes()``getFeaturedRoute()``planFeaturedRoutePreview()``showFlowline()` | `app.js:1793-1986,2348-2563`Engine `GET_FEATURED_ROUTES``4437-4471` | 可供后续产品评估,未证明当前博物馆数据已可用 |
| 商户 POI | “商户”Tab餐饮/商店/咖啡厅筛选 | `getBusinessPois()``focusTo()` | `index.html:177-193``app.js:2145-2245`Engine `GET_BUSINESS_POIS``4378-4396` | 后端/SDK 可调用,不代表自然博物馆有商户业务需求 |
| 诊断 | “诊断”Tab | `getDiagnostics()``getFloorDiagnostics()` | `index.html:161-175``app.js:1987-2144`Engine `GET_DIAGNOSTICS``4300-4333` | 仅调试/运维能力,不应作为访客入口 |
| 场景模式 | URL `scene=desktop/mobile/kiosk` | Engine 查询参数与 view preset | `demo/README.md:25-34``index.html:47-64`SDK 预设见第 5 节 | 展示适配维度,不代表 H5 应复制 kiosk UI |
### 14.3 当前 H5 的实际业务路径
当前 H5 不使用 Demo iframe仍是本地 ThreeMap 加 REST 数据(第 3 节)。其已可由源码证明的产品能力如下:
| 用户目标 | 真实 H5 路径与实现 | 证据 | 产品化判断 |
| --- | --- | --- | --- |
| 进入馆内/切换楼层/查看模型 | 首页 -> `GuideMapShell` -> `ThreeMap`,带外观/楼层/多层状态、楼层请求与失败状态 | `src/pages/index/index.vue:11-51,395-445``src/components/navigation/GuideMapShell.vue:12-26,136-165``src/components/map/ThreeMap.vue:27-43,444-452` | 已产品化 |
| 搜索并定位 POI | 首页或搜索页 -> `PoiSearchPanel` 分类、关键词、楼层结果 -> 详情/聚焦请求 | `src/components/search/PoiSearchPanel.vue:3-203,263-324``src/pages/index/index.vue:157-179,1637-1709` | 已产品化;不同于 Demo 的单列表/类型 chip |
| 地图 POI 点击、卡片和聚焦 | ThreeMap 发出 `poiClick`H5 使用自己的渲染、命中和详情卡片状态 | `src/components/map/ThreeMap.vue:43-45,4805-4828,4880-4897`DOM 标签见第 7 节 | 已产品化,采用 WebGL 命中 Sprite + DOM 标签而非 iframe HTML 图层 |
| 小屏可读标签 | DOM 标签投影、碰撞规避、`ResizeObserver`,配合模型尺度和屏幕密度策略 | `ThreeMap.vue:1260-1328,1425-1551,4550-4591` | 已产品化,优于直接复制 Demo 标签层 |
| 讲解浏览与详情 | `讲解 -> 展厅列表 -> 讲解对象列表 -> 讲解详情`;列表按展厅分页 | `src/utils/explainNavigation.ts:1-3``src/usecases/explainUseCase.ts:481-513``src/components/explain/ExplainGuideStopCatalog.vue:40-75` | 已产品化的内容 IA不能退化成 Demo 按楼层查“讲解点” |
| 音频讲解 | 讲解详情选择语言、播放/重试/进度;全局播放器具备 H5 audio lifecycle | `src/pages/exhibit/detail.vue:76-114,291-316``src/components/audio/AudioPlayer.vue:122-158,231-295` | 有源码能力;具体对象音频仍取决于真实内容数据 |
| 位置预览而非导航承诺 | 路线 use case 先读取 readiness不就绪返回“暂不提供正式室内导航” | `src/usecases/guideRouteUseCase.ts:43-76``src/pages/route/detail.vue:144-148,361-363,736-766` | 产品意图明确:不可将 Demo 路径规划视为当前 H5 缺陷 |
| 来馆导航桥接 | H5 通过微信小程序宿主位置页传入经校验的目标 | `src/services/WechatMiniProgramBridgeService.ts:55-121` | 已实现桥接;保证结果是微信内置地图能力,不等同直接拉起任意第三方地图 |
### 14.4 完整业务功能矩阵
| 功能域 | SDK Demo 能力 | 当前 H5 能力 | 差异状态 | 差异根因 | 产品建议 | 源码证据 |
| --- | --- | --- | --- | --- | --- | --- |
| 初始化、ready、错误日志 | `whenReady` + 12 类事件日志 | 本地模型加载/失败和诊断事件,无 SDK iframe ready | 不适用 | 渲染架构不同Demo 还承担调试 | 保持 H5 本地生命周期;仅复用可观测性字段 | Demo `app.js:479-643`H5 `ThreeMap.vue:451-452` |
| Manifest/楼层切换 | Manifest、SDK changeFloor、重置视图 | Repository/Guide shell/ThreeMap 楼层状态、基线与预加载 | 部分覆盖 | H5 有自己的数据/相机契约 | 保持 H5可对齐错误码和诊断快照 | Demo `655-795`H5 `GuideMapShell.vue:136-165``ThreeMap.vue:3505-3554` |
| 图层/2D-3D/场景 | Tab 驱动 `setVisibleLayers`、2D/3D、desktop/mobile/kiosk | overview/floor/multi、自动切换非 Demo 场景模式 | 部分覆盖 | 产品体验不同 | 不接 kiosk只在同场景评估必要的 view control | Demo `172-315`H5 `ThreeMap.vue:90,3833-3921` |
| 空间面查询/详情 | 搜索空间、`highlightPolygon` | 已有馆内 POI/展厅位置预览;未证明有独立空间面详情产品 | 部分覆盖 | H5 面向访客 POI不是 GIS 空间管理 | P2仅当“展厅/区域详情”有真实内容与模型锚点 | Demo `999-1136`Engine `4111-4145`H5 `GuideRepository.ts:115-153` |
| POI 搜索/分类/详情/聚焦 | 当前楼层类型筛选、搜索、focus、点击事件 | 分类搜索、跨楼层结果、详情/定位、点击卡片 | 相同(流程不同) | H5 针对移动端任务流程深化 | 保持 H5不迁移 iframe 列表实现 | Demo `1137-1288`H5 `PoiSearchPanel.vue:3-203``index.vue:1637-1709` |
| POI 标签与点击命中 | Engine HTML 标签、poiClick/fly-to | WebGL Sprite + hit target + DOM 碰撞标签 | 相同(实现不同) | H5 需移动端性能与命中稳定性 | 保持 H5P3 才实验服务端 LOD | Engine `4920-4938`H5 `ThreeMap.vue:4550-4591,4805-4897` |
| 讲解点查询 | 按楼层扫描/搜索/详情 | 按展厅分页的讲解对象列表和详情 | 部分覆盖H5 更产品化) | Demo 以 SDK 数据查询为主H5 是内容 IA | 保持 H5 IA可在后台按 hallId 补充关联,不改变用户导航 | Demo `1289-1464`Engine `4159-4193`H5 `explainUseCase.ts:481-513` |
| 讲解内容/音频 | Demo 仅展示 guide stop 字段 | 讲解详情、语言、音频/图文状态、播放器 | 当前 H5 更高 | Demo 未形成内容播放流程 | 保持 H5先补真实内容数据而非接 SDK UI | H5 `exhibit/detail.vue:76-114,291-316``AudioPlayer.vue:231-295` |
| 同层/跨层路线 | 可选起终点、轮椅、overview/handoff、路线段、换层和漫游 | 有 readiness 门控的位置关系/预览;不把其称为正式导航 | 缺失(有意门控) | 路网/导航数据与真实运行未验收 | P0 数据核验后再评估,不能直接打开功能 | Demo `1465-1777,827-889`H5 `guideRouteUseCase.ts:43-76` |
| 推荐/精品动线 | 内置推荐和 featured-route 预览/展示 | 未证明有当前博物馆的路线产品数据 | 当前业务不需要/数据未证实 | Demo 是 SDK showcase数据依赖待确认 | P2先做内容/运营需求与路线数据评审 | Demo `1793-1986,2348-2563`Engine `4437-4471` |
| 商户 POI | 餐饮/商店/咖啡筛选 | H5 有通用服务/设施和搜索,但未证明商户运营域 | 不适用 | 自然博物馆当前产品域与商业体不同 | 不接入,除非业务明确新增餐饮/商店运营数据 | Demo `index.html:177-193``app.js:2145-2245` |
| 诊断与事件面板 | 地图/楼层诊断、Event Log | 开发诊断、测试和控制台,不呈现访客面板 | 仅调试能力 | 受众不同,暴露技术细节会损害访客体验 | 不接入访客 H5可做受权限保护的 QA 工具 | Demo `1987-2144,554-643`Engine `4300-4333` |
| 来馆/室外导航 | 未见宿主导航桥接 | 微信小程序宿主位置页桥接 | H5 独有 | H5 部署容器和用户任务不同 | 保持现状 | `WechatMiniProgramBridgeService.ts:86-121` |
### 14.5 建议接入、保持现状与不接入
| 决策 | 能力 | 优先级 | 前置条件与原因 |
| --- | --- | --- | --- |
| 建议接入 | 路网/可达性只读核验与契约快照 | P0 | 先验证每个激活楼层 `routePlanningReady`、节点/边、可导航地点、无障碍连接器和真实路径;没有数据不得开放导航。可复用 `SdkGuideRouteRepository` 的诊断逻辑(`src/repositories/SdkGuideRouteRepository.ts:92-155`),不接 iframe。 |
| 建议接入 | Bundle/参数/路线 readiness 自动化契约检查 | P1 | 继承本报告第 9 节 P1SDK Engine 的 `GET_NAVIGABLE_PLACES` / diagnostics 仅说明接口存在(`H5SdkEngine.tsx:4205-4238,4300-4333`),需要 H5 数据层的稳定契约。 |
| 建议接入 | 受 feature flag 控制的服务端 POI LOD 对照 | P1 | 遵循第 7、10 节,保留 DOM 标签、碰撞与 WebGL 点击。只采集对比数据,不迁移 SDK HTML 标签层。 |
| 建议接入 | 精品路线的产品/数据可行性探索 | P2 | 先取得博物馆运营定义、真实 routeId/waypoints、审核后图和无障碍规则再在 H5 `GuideRouteRepository`/use case 后实现,不采用 Demo 硬编码推荐路线。 |
| 建议接入 | 访客需要的展厅/区域空间详情 | P2 | 必须有空间-内容稳定 ID、模型面/锚点和有效详情数据;走 Repository/view model不能把 `highlightPolygon` 当作完整产品。 |
| 保持现状 | 本地 ThreeMap + REST、H5 搜索、卡片、相机基线、POI DOM/WebGL 混合策略 | P0 | 当前架构和移动体验已针对本项目;详见第 3、5、7 节。 |
| 保持现状 | 展厅列表 -> 讲解对象列表 -> 讲解详情及音频路径 | P0 | 这是当前内容业务 IADemo 的按楼层 guide stop 查询不应改变此流程。 |
| 不接入 | Demo iframe/Engine 的整体替换 | 不适用 | 发布 `whenReady` 链未闭环,且会破坏本地 ThreeMap 的相机、标签、点击和状态机契约。 |
| 不接入 | 访客端诊断/事件日志、模型性能日志 | 不适用 | 仅调试/运维能力;需要时应做独立、受权限保护的 QA 面板。 |
| 不接入 | kiosk/desktop 控制面与商户 POI 业务 | 不适用 | 设备和业务域不匹配;不能仅因 SDK Demo 展示而迁移。 |
| 不接入 | 在路网未验收时的漫游、跨层 turn-by-turn、无障碍承诺 | 不适用 | 属于真实产品缺口但不应以 UI 模拟填补,必须先满足 P0 数据与现场验收。 |
### 14.6 后续实施边界、接口契约与验收
若通过 P0 证明路网和目标业务数据可用,实施边界必须保持如下分层,禁止页面直接调 SDK 或后端接口:
| 目标 | 文件边界 | 必要契约 | 测试与移动端验收 |
| --- | --- | --- | --- |
| 路线 readiness | `src/data/providers/sgsSdkApiProvider.ts` -> `src/repositories/SdkGuideRouteRepository.ts` -> `src/usecases/guideRouteUseCase.ts` -> `src/pages/route/detail.vue` / `src/pages/index/index.vue` | 楼层、route node/edge、navigable place、connector、wheelchair constraint、数据版本失败必须给出不可用原因 | 单测覆盖无数据/部分楼层/全就绪390x844 验证禁用、重试、返回位置预览,不显示“开始导航”承诺 |
| 路线渲染(仅获批后) | `GuideRouteRepository` / `guideRouteUseCase` -> `GuideMapShell.vue` -> `ThreeMap.vue` | 同一模型矩阵 M 下的 route point、floorId、段类型、换层连接器、起终点与第 6 节变换契约一并演进 | 单位/非单位矩阵、同层/跨层/无障碍路线、路线点/点击反变换390x844、DPR 1/3 下分段切楼层与取消清理 |
| 精品路线 | 新 Repository/provider/use case 与页面 view model不在 `ThreeMap.vue` 硬编码路线 | routeId、name、waypoints、运营状态、无障碍规则、内容关联、版本 | 空/失效 waypoints、跨层、重复进入、关闭恢复内容审核和运营验收后才暴露入口 |
| 空间详情 | provider/adapter/repository -> `GuideMapShell` 或专用详情页 | `spaceId``hallId/poiId`、几何锚点、展示内容、有效状态 | 空间缺面/缺内容/锚点不匹配降级;不遮挡移动端控件 |
| POI LOD 实验 | provider/adaptor 候选数据 -> `ThreeMap.vue` feature flag | server LOD 层、POI 稳定 ID、当前本地优先级和回退标志 | L1/L2/EXTERIOR、390x844、DPR 1/3记录标签数/重叠/命中/帧时间;失败回退 |
所有实施都应补充:同一发布目录下的 SDK Demo `whenReady resolve``ENGINE_READY`、楼层切换、模型非空、30 POI 与 context lost/restored 验收,但这些是 SDK 发布物的独立回归项,不能作为把 iframe 引入当前 H5 的理由。