diff --git a/.agents/skills/shenzhen-natural-museum-dev/SKILL.md b/.agents/skills/shenzhen-natural-museum-dev/SKILL.md index 725d89b..64dddcf 100644 --- a/.agents/skills/shenzhen-natural-museum-dev/SKILL.md +++ b/.agents/skills/shenzhen-natural-museum-dev/SKILL.md @@ -1,6 +1,6 @@ --- name: shenzhen-natural-museum-dev -description: Shenzhen Natural Museum mobile H5 spatial content guide architecture standards. Use when working in this frontend-miniapp project on guide and explain businesses, indoor 3D/Three.js/GLB, SGS Map SDK H5 integration, iframe/postMessage SDK rendering, API/static data-source switching, POI/location preview, exhibit/hall/facility content, audio explanation, transcript/media data, Tencent Map/outdoor reference logic, static nav assets, Museum Content/Guide/Explain Data Access Layers, provider/adapter/repository architecture, route_graph/nav_data readiness, browser-simulated H5 user-flow audits, business-logic audits, engineering architecture reviews, legacy demo data cleanup, mobile overlay/canvas compatibility, and quality gates. Treat mini-program/mp-weixin as out of scope unless the user explicitly asks for it. +description: Shenzhen Natural Museum mobile H5 spatial content guide architecture and client visual specification standards. Use when working in this frontend-miniapp project on guide and explain businesses, visual styling, typography, hall icons, homepage tab labels, indoor 3D/Three.js/GLB, SGS Map SDK H5 integration, iframe/postMessage SDK rendering, API/static data-source switching, POI/location preview, exhibit/hall/facility content, audio explanation, transcript/media data, Tencent Map/outdoor reference logic, static nav assets, Museum Content/Guide/Explain Data Access Layers, provider/adapter/repository architecture, route_graph/nav_data readiness, browser-simulated H5 user-flow audits, business-logic audits, engineering architecture reviews, legacy demo data cleanup, mobile overlay/canvas compatibility, and quality gates. Treat mini-program/mp-weixin as out of scope unless the user explicitly asks for it. --- # Shenzhen Natural Museum Dev @@ -23,6 +23,33 @@ The product has two first-class H5 businesses: - Current guide capability is indoor 3D display plus POI/location preview. Do not present it as certified real indoor navigation until `route_graph` and `nav_data` are available and verified. - Current explain capability must be described as content/audio explanation only when real media data is available. Do not invent working audio, transcript, or exhibit data from placeholders. +## Client Visual Design Standards + +Use `E:\MyWork\深圳国际艺术馆\museum-guide\docs\深圳自然博物馆0504-设计` as the primary client visual source when styling the Shenzhen Natural Museum H5 experience. + +- Brand theme: follow "探索万物生息" and the creative idea "生物轨迹就是自然的地图". +- Visual language: prefer organic natural-history forms, biological traces, leaves, fossils, earth layers, micro-symbols, and clear educational structure over generic decorative gradients or unrelated illustration styles. +- Typography: use `鸿蒙黑体` first. Recommended CSS stack: `'鸿蒙黑体', 'HarmonyOS Sans SC', 'HarmonyOS Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif`. +- Color and surfaces: keep the existing light neutral system (`#FFFFFF`, `#F3F3F3`, `#F5F5ED`), near-black primary text (`#262421`), charcoal secondary text (`#424754`), and museum yellow-green accent `#E0E100` for selected/active states. +- Homepage top tabs: use `馆内` for the indoor guide field and `讲解` for the explain field. +- Hall identity: use the eight client-provided hall icons from `docs/深圳自然博物馆0504-设计/full.md`, stored in the H5 project under `static/icons/halls/`. + +Hall icon mapping: + +| Hall | H5 asset | +| --- | --- | +| 宇宙厅 | `/static/icons/halls/universe.jpg` | +| 地球厅 | `/static/icons/halls/earth.jpg` | +| 演化厅 | `/static/icons/halls/evolution.jpg` | +| 恐龙厅 | `/static/icons/halls/dinosaur.jpg` | +| 人类厅 | `/static/icons/halls/human.jpg` | +| 动物厅 | `/static/icons/halls/animal.jpg` | +| 生物厅 | `/static/icons/halls/biology.jpg` | +| 生态厅 | `/static/icons/halls/ecology.jpg` | +| 家园厅 | `/static/icons/halls/homeland.jpg` | + +Use `生物厅` as the live-data alias for the sixth source-document hall (`动物厅`) until content taxonomy is reconciled. Render hall icons with `image` `mode="aspectFit"` so the client-provided art is not cropped, and fall back to a neutral placeholder only when no mapped asset exists. + ## Non-Negotiable Architecture Rules - Keep data and presentation decoupled. Pages and visual components must consume domain models or view models, not raw static package JSON, backend fields, GLB manifest details, or ad hoc demo arrays. diff --git a/.claude/skills/shenzhen-natural-museum-dev/SKILL.md b/.claude/skills/shenzhen-natural-museum-dev/SKILL.md index 725d89b..64dddcf 100644 --- a/.claude/skills/shenzhen-natural-museum-dev/SKILL.md +++ b/.claude/skills/shenzhen-natural-museum-dev/SKILL.md @@ -1,6 +1,6 @@ --- name: shenzhen-natural-museum-dev -description: Shenzhen Natural Museum mobile H5 spatial content guide architecture standards. Use when working in this frontend-miniapp project on guide and explain businesses, indoor 3D/Three.js/GLB, SGS Map SDK H5 integration, iframe/postMessage SDK rendering, API/static data-source switching, POI/location preview, exhibit/hall/facility content, audio explanation, transcript/media data, Tencent Map/outdoor reference logic, static nav assets, Museum Content/Guide/Explain Data Access Layers, provider/adapter/repository architecture, route_graph/nav_data readiness, browser-simulated H5 user-flow audits, business-logic audits, engineering architecture reviews, legacy demo data cleanup, mobile overlay/canvas compatibility, and quality gates. Treat mini-program/mp-weixin as out of scope unless the user explicitly asks for it. +description: Shenzhen Natural Museum mobile H5 spatial content guide architecture and client visual specification standards. Use when working in this frontend-miniapp project on guide and explain businesses, visual styling, typography, hall icons, homepage tab labels, indoor 3D/Three.js/GLB, SGS Map SDK H5 integration, iframe/postMessage SDK rendering, API/static data-source switching, POI/location preview, exhibit/hall/facility content, audio explanation, transcript/media data, Tencent Map/outdoor reference logic, static nav assets, Museum Content/Guide/Explain Data Access Layers, provider/adapter/repository architecture, route_graph/nav_data readiness, browser-simulated H5 user-flow audits, business-logic audits, engineering architecture reviews, legacy demo data cleanup, mobile overlay/canvas compatibility, and quality gates. Treat mini-program/mp-weixin as out of scope unless the user explicitly asks for it. --- # Shenzhen Natural Museum Dev @@ -23,6 +23,33 @@ The product has two first-class H5 businesses: - Current guide capability is indoor 3D display plus POI/location preview. Do not present it as certified real indoor navigation until `route_graph` and `nav_data` are available and verified. - Current explain capability must be described as content/audio explanation only when real media data is available. Do not invent working audio, transcript, or exhibit data from placeholders. +## Client Visual Design Standards + +Use `E:\MyWork\深圳国际艺术馆\museum-guide\docs\深圳自然博物馆0504-设计` as the primary client visual source when styling the Shenzhen Natural Museum H5 experience. + +- Brand theme: follow "探索万物生息" and the creative idea "生物轨迹就是自然的地图". +- Visual language: prefer organic natural-history forms, biological traces, leaves, fossils, earth layers, micro-symbols, and clear educational structure over generic decorative gradients or unrelated illustration styles. +- Typography: use `鸿蒙黑体` first. Recommended CSS stack: `'鸿蒙黑体', 'HarmonyOS Sans SC', 'HarmonyOS Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif`. +- Color and surfaces: keep the existing light neutral system (`#FFFFFF`, `#F3F3F3`, `#F5F5ED`), near-black primary text (`#262421`), charcoal secondary text (`#424754`), and museum yellow-green accent `#E0E100` for selected/active states. +- Homepage top tabs: use `馆内` for the indoor guide field and `讲解` for the explain field. +- Hall identity: use the eight client-provided hall icons from `docs/深圳自然博物馆0504-设计/full.md`, stored in the H5 project under `static/icons/halls/`. + +Hall icon mapping: + +| Hall | H5 asset | +| --- | --- | +| 宇宙厅 | `/static/icons/halls/universe.jpg` | +| 地球厅 | `/static/icons/halls/earth.jpg` | +| 演化厅 | `/static/icons/halls/evolution.jpg` | +| 恐龙厅 | `/static/icons/halls/dinosaur.jpg` | +| 人类厅 | `/static/icons/halls/human.jpg` | +| 动物厅 | `/static/icons/halls/animal.jpg` | +| 生物厅 | `/static/icons/halls/biology.jpg` | +| 生态厅 | `/static/icons/halls/ecology.jpg` | +| 家园厅 | `/static/icons/halls/homeland.jpg` | + +Use `生物厅` as the live-data alias for the sixth source-document hall (`动物厅`) until content taxonomy is reconciled. Render hall icons with `image` `mode="aspectFit"` so the client-provided art is not cropped, and fall back to a neutral placeholder only when no mapped asset exists. + ## Non-Negotiable Architecture Rules - Keep data and presentation decoupled. Pages and visual components must consume domain models or view models, not raw static package JSON, backend fields, GLB manifest details, or ad hoc demo arrays. diff --git a/.gitignore b/.gitignore index bfdb4c0..5042c29 100644 --- a/.gitignore +++ b/.gitignore @@ -11,9 +11,15 @@ coverage/ .eslintcache # 本地环境配置 +.env .env.local .env.*.local .claude/*.local.json +.claude/worktrees/ +.codex-artifacts/ +.codex-audit-output/ +.codex-work/ +.cursor/ # IDE 配置 .idea/ @@ -38,3 +44,4 @@ Thumbs.db *.log tmp/ temp/ +__pycache__/ diff --git a/docs/Benchmark/generated-guide-designs-0519/01-guide-home-0519.png b/docs/Benchmark/generated-guide-designs-0519/01-guide-home-0519.png new file mode 100644 index 0000000..3f3e964 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-0519/01-guide-home-0519.png differ diff --git a/docs/Benchmark/generated-guide-designs-0519/02-search-poi-0519.png b/docs/Benchmark/generated-guide-designs-0519/02-search-poi-0519.png new file mode 100644 index 0000000..f73e5db Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-0519/02-search-poi-0519.png differ diff --git a/docs/Benchmark/generated-guide-designs-0519/03-poi-selected-0519.png b/docs/Benchmark/generated-guide-designs-0519/03-poi-selected-0519.png new file mode 100644 index 0000000..3880a8d Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-0519/03-poi-selected-0519.png differ diff --git a/docs/Benchmark/generated-guide-designs-0519/04-route-planning-0519.png b/docs/Benchmark/generated-guide-designs-0519/04-route-planning-0519.png new file mode 100644 index 0000000..f1d5cb1 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-0519/04-route-planning-0519.png differ diff --git a/docs/Benchmark/generated-guide-designs-0519/05-indoor-navigation-active-0519.png b/docs/Benchmark/generated-guide-designs-0519/05-indoor-navigation-active-0519.png new file mode 100644 index 0000000..c76f4fe Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-0519/05-indoor-navigation-active-0519.png differ diff --git a/docs/Benchmark/generated-guide-designs-0519/06-cross-floor-navigation-0519.png b/docs/Benchmark/generated-guide-designs-0519/06-cross-floor-navigation-0519.png new file mode 100644 index 0000000..6469ac7 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-0519/06-cross-floor-navigation-0519.png differ diff --git a/docs/Benchmark/generated-guide-designs-0519/07-arrival-complete-0519.png b/docs/Benchmark/generated-guide-designs-0519/07-arrival-complete-0519.png new file mode 100644 index 0000000..071295c Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-0519/07-arrival-complete-0519.png differ diff --git a/docs/Benchmark/generated-guide-designs-0519/08-error-fallback-0519.png b/docs/Benchmark/generated-guide-designs-0519/08-error-fallback-0519.png new file mode 100644 index 0000000..615c6fb Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-0519/08-error-fallback-0519.png differ diff --git a/docs/Benchmark/generated-guide-designs-0519/README.md b/docs/Benchmark/generated-guide-designs-0519/README.md new file mode 100644 index 0000000..69503e2 --- /dev/null +++ b/docs/Benchmark/generated-guide-designs-0519/README.md @@ -0,0 +1,46 @@ +# 0519 规范版导览功能设计稿 + +## 说明 + +本目录为新版“导览 / 馆内导览”功能逻辑设计稿的 0519 视觉规范重生成版本。生成依据包括: + +- 对标逻辑分析:`docs/Benchmark/guide-video-ux-logic-analysis.md` +- UI 规范目录:`E:\MyWork\深圳国际艺术馆\museum-guide\docs\UI设计\深圳自然博物馆小程序+设计规范0519` +- 规范图片:`色彩.png`、`布局.png`、`按钮.png`、`文字.png`、`圆角.png`、`图标.png`、`示例.png` + +这些图片只表达功能逻辑、UX 状态和页面结构,不是最终视觉规范或可直接切图的交付稿。正式落地仍需产品、设计和路线数据 readiness 共同确认。 + +## 0519 规范应用要点 + +- 色彩:系统主色 / 按钮文字 `#E0DF00`,主按钮 `#000000`,选中文字高亮 `#BEBD02`,链接 `#1B6EC0`,标签 `#2C854F`,错误 `#E73333`,背景 `#F9FAFB`。 +- 文本:主标题 `#000000`,正文 `#3F3F3F`,占位 / 默认提示 `#ADB0B4`,分割线 `#D9D9D9`。 +- 布局:页面边距约 15-16px,基础间距 10px,保持 6 列网格感。 +- 圆角:标签 / 选项 4px,按钮 / 输入框 / 搜索框 8px,卡片 / 弹框 16px。 +- 字体:中文以微软雅黑、思源黑体、苹方风格为准;层级参考 20pt、18pt、16pt、14pt、12pt。 +- 图标:线性小程序图标风格,未选中灰色,选中使用系统主色。 + +## 图片清单 + +| 文件 | 业务状态 | 功能闭环 | 对应落地参考 | +| --- | --- | --- | --- | +| `01-guide-home-0519.png` | 导览首页初始态 | 地图作为导览工作台,搜索、楼层、快捷入口同屏可达 | `src/pages/index/index.vue`、`GuideMapShell.vue` | +| `02-search-poi-0519.png` | 搜索地点 / 筛选态 | 搜索结果可查看位置、设起点、去这里,不脱离地图上下文 | `src/pages/search/index.vue`、未来地图内 overlay | +| `03-poi-selected-0519.png` | POI 选中态 | POI 卡片承接查看位置、从这里出发、去这里、相关讲解 | `index.vue` 的 `selectedGuidePoi` 相关逻辑 | +| `04-route-planning-0519.png` | 路线规划态 | 起终点完整性、偏好选项、路线 readiness 和降级文案 | `RoutePlannerPanel.vue`、`guideRouteUseCase.ts` | +| `05-indoor-navigation-active-0519.png` | 馆内导览进行中 | 当前路线、下一步、当前楼层、进度、暂停 / 退出 / 重新定位 | 未来导航运行态;当前需受路线 readiness 约束 | +| `06-cross-floor-navigation-0519.png` | 跨楼层导览态 | 当前层路线、换层节点、下一层路线关系 | 未来 `route_graph`、`nav_data`、楼层连接器 | +| `07-arrival-complete-0519.png` | 到达目标 / 结束导览 | 到达确认、查看详情、相关讲解、重新规划、返回首页 | 未来导航完成态和讲解联动 | +| `08-error-fallback-0519.png` | 异常 / 降级态 | SDK / 定位 / 路线不可用时返回位置预览或手动选择 | `guideReadiness.ts`、`route/detail.vue` 降级逻辑 | + +## 需要确认的问题 + +1. 0519 规范中的底部导航栏目是“首页 / 展览 / 券码 / 商城 / 我的”,本导览业务是否需要保留项目当前“导览 / 讲解”顶部入口,还是纳入底部主导航。 +2. `#E0DF00` 作为导览路线高亮是否足够清晰,还是路线应优先使用标签绿 `#2C854F`,主色仅用于按钮和选中态。 +3. 路线未就绪阶段,主按钮是否统一为“查看位置关系”,避免出现“开始导览”误导。 +4. “模拟导览”是否面向用户开放,还是只作为内部演示 / 测试状态。 +5. 跨楼层偏好是否需要在第一期只保留“电梯优先”,待 route graph 验证后再开放楼梯 / 扶梯。 +6. 到达页是否优先承接“相关讲解”,以形成导览到讲解的跨业务闭环。 + +## 生成方式 + +按 `C:\Users\Administrator\.codex\skills\.system\imagegen\SKILL.md` 使用内置 `image_gen` 工具逐张生成,再复制到当前目录。原始生成图保留在 Codex 默认生成目录中,未删除。 diff --git a/docs/Benchmark/generated-guide-designs-h5-home-hi-fi/01-h5-home-high-fidelity.png b/docs/Benchmark/generated-guide-designs-h5-home-hi-fi/01-h5-home-high-fidelity.png new file mode 100644 index 0000000..63b822a Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-h5-home-hi-fi/01-h5-home-high-fidelity.png differ diff --git a/docs/Benchmark/generated-guide-designs-h5-home-hi-fi/01-h5-home-high-fidelity.svg b/docs/Benchmark/generated-guide-designs-h5-home-hi-fi/01-h5-home-high-fidelity.svg new file mode 100644 index 0000000..b5674f5 --- /dev/null +++ b/docs/Benchmark/generated-guide-designs-h5-home-hi-fi/01-h5-home-high-fidelity.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 导览 + + 来馆 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 搜索展厅、设施或地点 + + + + + + + 地图 + 列表 + + + + + + 图例 + + + + i服务台 + 电梯 + 扶梯 + WC洗手间 + 出入口 + + + + + + 地球演化厅1F-03 + + + 恐龙化石展厅1F-02 + + + + + 生命起源区1F-05 + 矿物与岩石厅1F-04 + 1号展厅1F-01 + + + + 主入口 + + + + + 3F + 2F + 1F + B1 + + + + + + + - + + + + + 当前展示 1F 馆内三维地图 + + + + 开始导览 + \ No newline at end of file diff --git a/docs/Benchmark/generated-guide-designs-h5-home-hi-fi/README.md b/docs/Benchmark/generated-guide-designs-h5-home-hi-fi/README.md new file mode 100644 index 0000000..9902f2b --- /dev/null +++ b/docs/Benchmark/generated-guide-designs-h5-home-hi-fi/README.md @@ -0,0 +1,36 @@ +# H5 导览首页高保真设计稿 + +本目录为新版 H5 导览首页高保真设计稿,仅用于产品和设计评审,不是最终视觉规范文件。 + +## 文件 + +- `01-h5-home-high-fidelity.png`:高保真首页 PNG,按 390x844 画布的 2x 精度输出。 +- `01-h5-home-high-fidelity.svg`:可编辑源稿,便于继续调整布局和样式。 + +## 设计重点 + +- 纯 H5 首页:不包含手机状态栏、微信小程序胶囊、小程序标题栏或浏览器外壳。 +- 首屏重点突出馆内三维地图,地图占据页面主体。 +- 其他组件使用浮层和缩放形式:搜索、地图/列表切换、图例、设施入口、楼层切换、定位/缩放。 +- 只保留核心功能入口:`来馆`、地点搜索、设施筛选、楼层切换、`开始导览`。 +- 不加入收藏、推荐、我的、讲解推荐等扩展功能。 + +## 视觉依据 + +- 参考用户提供的首页图的信息架构和页面优先级。 +- 参考 `深圳自然博物馆小程序+设计规范0519` 的主色、按钮、边距、圆角和文字关系。 +- 三维地图采用高保真示意表达,强调建筑层次、点位标注、楼层控制和导览主按钮。 + +## 本项目落地对应 + +- 首页页面:`src/pages/index/index.vue` +- 导览地图外壳:`src/components/navigation/GuideMapShell.vue` +- 地图渲染:`src/components/map/SgsMapRenderer.vue` +- 后续路线规划:`src/components/navigation/RoutePlannerPanel.vue` + +## 需要确认 + +- `开始导览` 点击后进入路线规划,还是直接进入当前位置导览态。 +- 地图/列表切换是否保留在 H5 首页首屏。 +- 设施入口是否仅保留服务台、电梯、扶梯、洗手间、出入口。 +- 楼层数据是否固定展示,或由 SDK/后台动态返回。 diff --git a/docs/Benchmark/generated-guide-designs-h5-home-map-focus/01-h5-home-3d-map-focus.png b/docs/Benchmark/generated-guide-designs-h5-home-map-focus/01-h5-home-3d-map-focus.png new file mode 100644 index 0000000..6e21ebf Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-h5-home-map-focus/01-h5-home-3d-map-focus.png differ diff --git a/docs/Benchmark/generated-guide-designs-h5-home-map-focus/01-h5-home-3d-map-focus.svg b/docs/Benchmark/generated-guide-designs-h5-home-map-focus/01-h5-home-3d-map-focus.svg new file mode 100644 index 0000000..d1e8a05 --- /dev/null +++ b/docs/Benchmark/generated-guide-designs-h5-home-map-focus/01-h5-home-3d-map-focus.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + 导览 + 来馆 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 搜索展厅、设施或地点 + + + + + + + 地图 + 列表 + + + + + + 图例 + + + + i服务台 + 电梯 + 扶梯 + WC洗手间 + 出入口 + + + + + 恐龙化石展厅 + 1F-02 + + + + + 地球演化厅1F-03 + 生命起源区1F-05 + 矿物与岩石厅1F-04 + 1号展厅1F-01 + 主入口 + + + + + 3F + 2F + 1F + B1 + + + + + + + + + + 当前展示 1F 馆内三维地图 + + 开始导览 + \ No newline at end of file diff --git a/docs/Benchmark/generated-guide-designs-h5-home-map-focus/README.md b/docs/Benchmark/generated-guide-designs-h5-home-map-focus/README.md new file mode 100644 index 0000000..3358de2 --- /dev/null +++ b/docs/Benchmark/generated-guide-designs-h5-home-map-focus/README.md @@ -0,0 +1,35 @@ +# H5 导览首页精简设计稿 + +本目录为新版 H5 导览首页的精简设计稿,仅表达功能逻辑和页面结构,不作为最终视觉规范。 + +## 文件 + +- `01-h5-home-3d-map-focus.png`:H5 首页设计稿,重点突出馆内三维地图。 +- `01-h5-home-3d-map-focus.svg`:用于复查和二次调整的可编辑源稿。 + +## 设计逻辑 + +- 平台定位为纯 H5 页面:无手机状态栏、无微信小程序胶囊、无小程序标题栏。 +- 首页核心目标是进入馆内导览,因此三维地图占据首屏主体区域。 +- 搜索、地图/列表切换、图例、设施入口、楼层切换、定位和缩放控件均以地图浮层形式出现,减少对地图主区域的挤占。 +- 底部只保留主要操作「开始导览」,不加入收藏、推荐、我的等扩展功能。 +- 右上角保留「来馆」入口,对应馆外/到馆路线入口。 + +## 参考依据 + +- 用户提供的首页参考图:保留其信息架构,包括标题、来馆、地图/列表、搜索、设施入口、楼层和开始导览。 +- `深圳自然博物馆小程序+设计规范0519`:参考背景色、主按钮色、按钮文字色、标签绿色、分割线和基础边距。 + +## 本项目落地对应 + +- 首页入口:`src/pages/index/index.vue` +- 导览地图容器:`src/components/navigation/GuideMapShell.vue` +- 地图渲染区域:`src/components/map/SgsMapRenderer.vue` +- 后续路线规划面板:`src/components/navigation/RoutePlannerPanel.vue` + +## 需要确认 + +- H5 首页是否默认展示地图模式,列表是否作为次级 tab 保留。 +- 「开始导览」点击后是进入路线规划、当前位置预览,还是直接进入模拟导览。 +- 设施入口是否只保留服务台、电梯、扶梯、洗手间、出入口这 5 类。 +- 楼层列表是否固定为 `3F / 2F / 1F / B1`,或需要从 SDK/后台动态读取。 diff --git a/docs/Benchmark/generated-guide-designs-h5-home-simple/01-h5-guide-home-simple.png b/docs/Benchmark/generated-guide-designs-h5-home-simple/01-h5-guide-home-simple.png new file mode 100644 index 0000000..ae9c6de Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-h5-home-simple/01-h5-guide-home-simple.png differ diff --git a/docs/Benchmark/generated-guide-designs-h5-home-simple/01-h5-guide-home-simple.svg b/docs/Benchmark/generated-guide-designs-h5-home-simple/01-h5-guide-home-simple.svg new file mode 100644 index 0000000..50acf29 --- /dev/null +++ b/docs/Benchmark/generated-guide-designs-h5-home-simple/01-h5-guide-home-simple.svg @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 导览 + + 来馆 + + + + + + + + 搜索展厅、设施、出入口 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 图例 + + + + + 3F + + 2F + + 1F + B1 + + + + + + + 设施 + + + + + 恐龙化石展厅 + 1F-02 + + + + + + + + + 矿物与岩石厅 + 1F-04 + + + + 生命起源展区 + 1F-05 + + + + 序厅 + 1F + + + + + + 主入口 + + + + + + + + + + + 1F + + + 定位 + + + 路线 + + + + + 开始导览 + + diff --git a/docs/Benchmark/generated-guide-designs-h5-home-simple/README.md b/docs/Benchmark/generated-guide-designs-h5-home-simple/README.md new file mode 100644 index 0000000..874aa3c --- /dev/null +++ b/docs/Benchmark/generated-guide-designs-h5-home-simple/README.md @@ -0,0 +1,30 @@ +# H5 精简版导览首页设计稿 + +本目录为新版导览 H5 首页的单页设计稿,仅表达功能逻辑和页面结构,不是最终视觉规范,也不是小程序版界面。 + +## 文件 + +- `01-h5-guide-home-simple.png`:H5 导览首页精简版 +- `01-h5-guide-home-simple.svg`:可复查的矢量源稿 + +## 设计逻辑 + +- 平台形态:纯 H5 移动端页面,不包含手机状态栏、微信胶囊、小程序标题栏或浏览器外壳。 +- 首页重点:三维地图作为首屏核心,占据页面最大面积。 +- 搜索入口:保留一个顶部轻量搜索框,支持查找展厅、设施、出入口。 +- 功能缩放:设施、图例、楼层、定位、路线等操作以小型悬浮控件呈现,避免挤占地图。 +- 主任务闭环:用户进入导览首页后,可搜索地点、切换楼层、查看地图点位,并通过底部主按钮进入“开始导览”。 +- 精简范围:不包含收藏、推荐、我的、讲解推荐等扩展功能。 + +## 依据 + +- 用户提供的参考图用于信息架构参考:导览标题、来馆入口、搜索、设施入口、三维地图、楼层切换、底部开始导览。 +- 设计方向按用户要求调整为“终点突出三维地图,其他组件尽量使用缩放形式”。 +- 视觉仍参考项目 0519 规范的基础色彩关系:白/浅灰背景、黑色主按钮、荧黄选中/按钮文字、绿色辅助状态。 + +## 待确认 + +- “开始导览”是否直接进入导航中,还是先进入起终点/路线规划面板。 +- H5 首页是否需要保留“地图/列表”切换,当前精简稿已去掉切换以突出 3D 地图。 +- 设施快捷入口是否默认展开,还是点击“设施”后再展示分类。 +- 楼层是否固定在地图右侧,或在小屏下收进底部工具条。 diff --git a/docs/Benchmark/generated-guide-designs-h5-simple/01-h5-guide-home-simple.png b/docs/Benchmark/generated-guide-designs-h5-simple/01-h5-guide-home-simple.png new file mode 100644 index 0000000..55500c2 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-h5-simple/01-h5-guide-home-simple.png differ diff --git a/docs/Benchmark/generated-guide-designs-h5-simple/02-h5-search-poi-simple.png b/docs/Benchmark/generated-guide-designs-h5-simple/02-h5-search-poi-simple.png new file mode 100644 index 0000000..34ec5fc Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-h5-simple/02-h5-search-poi-simple.png differ diff --git a/docs/Benchmark/generated-guide-designs-h5-simple/03-h5-poi-selected-simple.png b/docs/Benchmark/generated-guide-designs-h5-simple/03-h5-poi-selected-simple.png new file mode 100644 index 0000000..00ef914 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-h5-simple/03-h5-poi-selected-simple.png differ diff --git a/docs/Benchmark/generated-guide-designs-h5-simple/04-h5-route-planning-simple.png b/docs/Benchmark/generated-guide-designs-h5-simple/04-h5-route-planning-simple.png new file mode 100644 index 0000000..fcf18da Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-h5-simple/04-h5-route-planning-simple.png differ diff --git a/docs/Benchmark/generated-guide-designs-h5-simple/05-h5-navigation-active-simple.png b/docs/Benchmark/generated-guide-designs-h5-simple/05-h5-navigation-active-simple.png new file mode 100644 index 0000000..bd39314 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-h5-simple/05-h5-navigation-active-simple.png differ diff --git a/docs/Benchmark/generated-guide-designs-h5-simple/06-h5-error-fallback-simple.png b/docs/Benchmark/generated-guide-designs-h5-simple/06-h5-error-fallback-simple.png new file mode 100644 index 0000000..65428dc Binary files /dev/null and b/docs/Benchmark/generated-guide-designs-h5-simple/06-h5-error-fallback-simple.png differ diff --git a/docs/Benchmark/generated-guide-designs-h5-simple/README.md b/docs/Benchmark/generated-guide-designs-h5-simple/README.md new file mode 100644 index 0000000..22ad856 --- /dev/null +++ b/docs/Benchmark/generated-guide-designs-h5-simple/README.md @@ -0,0 +1,45 @@ +# H5 精简版导览设计稿 + +## 说明 + +本目录为 H5 版本的精简导览设计稿。相比前两版,本版刻意减少功能入口,不包含收藏、推荐、我的等扩展功能,重点保留导览最小闭环: + +1. 打开导览首页。 +2. 搜索或按设施 / 楼层找点位。 +3. 选中点位。 +4. 选择起点 / 终点并查看位置关系。 +5. 进入简化导览中状态。 +6. 路线或定位不可用时回到位置预览。 + +点位搜索页参考了用户提供的界面结构:顶部标题、地图 / 搜索入口、设施分类图标、服务设施标签、馆名、楼层侧栏和点位列表。视觉仍按 0519 规范重绘,没有照抄参考图。 + +## 设计约束 + +- H5 移动端竖屏,页面尽量简单。 +- 不做收藏、推荐、我的、商城、票务等非导览功能。 +- 保留 0519 规范:`#F9FAFB` 背景、`#000000` 主按钮、`#E0DF00` 主按钮文字 / 选中态、`#2C854F` 标签 / 路线辅助、`#D9D9D9` 分割线。 +- 圆角遵循 0519:标签 4px,按钮 / 搜索框 8px,卡片 / 弹层 16px。 +- 路线未验证时不承诺正式导航,使用“位置预览 / 查看位置关系 / 模拟导览 / 暂不支持正式导航”等安全文案。 + +## 图片清单 + +| 文件 | 状态 | 说明 | +| --- | --- | --- | +| `01-h5-guide-home-simple.png` | H5 导览首页 | 搜索、地图预览、楼层、开始导览,不含推荐 / 收藏 | +| `02-h5-search-poi-simple.png` | 导览点位搜索 | 参考截图的信息架构:设施分类、服务设施、楼层列表、点位列表 | +| `03-h5-poi-selected-simple.png` | 点位选中 | 仅保留查看位置、从这里出发、去这里 | +| `04-h5-route-planning-simple.png` | 路线规划 | 起点 / 终点、同层距离、查看位置关系、重新选择 | +| `05-h5-navigation-active-simple.png` | 导览中 | 下一步提示、路线、进度、重新定位、退出导览 | +| `06-h5-error-fallback-simple.png` | 异常降级 | 路线数据 / 定位不可用时返回位置预览或手动选择起点 | + +## 需要确认 + +1. H5 首页是否只保留“开始导览”,还是保留“地图 / 列表”切换。 +2. 点位搜索页的设施分类是否固定为服务台、电梯、扶梯、洗手间、母婴室、出入口。 +3. “服务设施”是否只保留饮料售卖机、票务售卖机、纪念品售卖机。 +4. 路线未就绪时,主按钮是否统一使用“查看位置关系”。 +5. H5 是否需要保留“讲解”入口,还是先从本版导览闭环中移除。 + +## 生成方式 + +按 `C:\Users\Administrator\.codex\skills\.system\imagegen\SKILL.md` 使用内置 `image_gen` 工具生成。用户提供的截图作为结构参考图,不作为编辑目标;生成结果已复制到本项目目录,原始生成文件保留在 Codex 默认生成目录。 diff --git a/docs/Benchmark/generated-guide-designs/01-guide-home.png b/docs/Benchmark/generated-guide-designs/01-guide-home.png new file mode 100644 index 0000000..1f8ffcb Binary files /dev/null and b/docs/Benchmark/generated-guide-designs/01-guide-home.png differ diff --git a/docs/Benchmark/generated-guide-designs/02-search-poi.png b/docs/Benchmark/generated-guide-designs/02-search-poi.png new file mode 100644 index 0000000..43668e9 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs/02-search-poi.png differ diff --git a/docs/Benchmark/generated-guide-designs/03-poi-selected.png b/docs/Benchmark/generated-guide-designs/03-poi-selected.png new file mode 100644 index 0000000..1fce9e6 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs/03-poi-selected.png differ diff --git a/docs/Benchmark/generated-guide-designs/04-route-planning.png b/docs/Benchmark/generated-guide-designs/04-route-planning.png new file mode 100644 index 0000000..b38e37c Binary files /dev/null and b/docs/Benchmark/generated-guide-designs/04-route-planning.png differ diff --git a/docs/Benchmark/generated-guide-designs/05-indoor-navigation-active.png b/docs/Benchmark/generated-guide-designs/05-indoor-navigation-active.png new file mode 100644 index 0000000..d70b33e Binary files /dev/null and b/docs/Benchmark/generated-guide-designs/05-indoor-navigation-active.png differ diff --git a/docs/Benchmark/generated-guide-designs/06-cross-floor-navigation.png b/docs/Benchmark/generated-guide-designs/06-cross-floor-navigation.png new file mode 100644 index 0000000..6bf6499 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs/06-cross-floor-navigation.png differ diff --git a/docs/Benchmark/generated-guide-designs/07-arrival-complete.png b/docs/Benchmark/generated-guide-designs/07-arrival-complete.png new file mode 100644 index 0000000..9a067f6 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs/07-arrival-complete.png differ diff --git a/docs/Benchmark/generated-guide-designs/08-error-fallback.png b/docs/Benchmark/generated-guide-designs/08-error-fallback.png new file mode 100644 index 0000000..d404c93 Binary files /dev/null and b/docs/Benchmark/generated-guide-designs/08-error-fallback.png differ diff --git a/docs/Benchmark/generated-guide-designs/README.md b/docs/Benchmark/generated-guide-designs/README.md new file mode 100644 index 0000000..90bdc5f --- /dev/null +++ b/docs/Benchmark/generated-guide-designs/README.md @@ -0,0 +1,50 @@ +# 新版导览功能设计稿图片说明 + +## 说明 + +本目录图片基于 `docs/Benchmark/guide-video-ux-logic-analysis.md` 提炼出的对标导览应用功能逻辑生成,用于表达本项目新版“导览 / 馆内导览”的业务闭环、UX 状态和页面结构。 + +这些图片不是最终视觉规范,不用于直接切图落地;图片中的颜色、图标、细节文案和布局只作为功能逻辑设计参考。正式实现仍需以本项目设计规范、组件能力、路线数据 readiness 和产品评审结论为准。 + +生成方式:按 `C:\Users\Administrator\.codex\skills\.system\imagegen\SKILL.md` 使用内置 `image_gen` 工具逐张生成,并复制到本项目目录。 + +## 图片清单 + +| 文件 | 业务状态 | 依据的对标视频逻辑 | 本项目落地参考 | +| --- | --- | --- | --- | +| `01-guide-home.png` | 导览首页初始态 | 地图作为导览工作台,搜索、楼层、更多和快捷入口同屏可达 | `src/pages/index/index.vue`、`GuideMapShell.vue`、`ThreeMap.vue` | +| `02-search-poi.png` | 搜索地点 / 筛选态 | 搜索不脱离地图上下文,结果可继续查看位置、设起点或设终点 | `src/pages/search/index.vue`、未来可内聚到 `GuideMapShell` overlay | +| `03-poi-selected.png` | POI 选中态 | 地图点位选中后成为任务枢纽,提供查看位置、从这里出发、去这里、相关讲解 | `index.vue` 的 `selectedGuidePoi` 卡片和 `handleSetSelectedPoiAsStart/End` | +| `04-route-planning.png` | 路线规划态 | 起点 / 终点完整性校验,路线偏好和规划状态分层展示 | `RoutePlannerPanel.vue`、`RoutePointPicker.vue`、`guideRouteUseCase.ts` | +| `05-indoor-navigation-active.png` | 馆内导览进行中 | 从“看路线”进入“执行指令”,持续展示下一步、距离、当前楼层和退出能力 | 未来导航运行态;当前仅可作为模拟导览 / 产品目标 | +| `06-cross-floor-navigation.png` | 跨楼层导览态 | 跨层路线必须解释当前层路线、换层节点和下一层延续关系 | 未来 `route_graph`、`nav_data`、楼层连接器和路线渲染 | +| `07-arrival-complete.png` | 到达目标 / 结束导览 | 导览闭环结束后提供查看详情、相关讲解、重新规划、返回首页 | 未来导航完成态;可复用位置详情、讲解联动和路线清理逻辑 | +| `08-error-fallback.png` | 异常 / 降级态 | SDK、定位、路线不可用时给出明确原因和恢复路径,不让用户卡死 | `src/domain/guideReadiness.ts`、`route/detail.vue` 的降级提示、SDK 错误态 | + +## 对标逻辑到本项目的转译原则 + +- 不照抄对标小程序 UI,只吸收任务闭环:找点、选点、规划、导览中、跨层、到达、退出。 +- 地图保持为导览主工作台,搜索、POI、路线面板尽量不打断空间上下文。 +- 当前路线数据未就绪时,只表达“位置预览 / 查看位置关系 / 能力不可用原因”,不承诺正式馆内导航。 +- “推荐路线 / 少走楼梯 / 电梯优先”需要真实拓扑、楼层连接器和可通行边权支撑后才能成为有效功能。 +- 馆外“来馆”和馆内导览是两条业务线,需要保持解耦。 + +## 需要产品确认的问题 + +1. 首页快捷入口是否保留“推荐展厅 / 我的收藏”,还是只保留“馆内导览 / 来馆”。 +2. 搜索结果的默认动作优先级:先“查看位置”,还是直接支持“去这里”。 +3. 在 `NAV_ROUTE_GRAPH_READY=false` 阶段,路线面板主按钮文案是否统一为“查看位置关系”。 +4. “模拟导览”是否作为正式导航前的演示能力开放,还是仅用于内部验证。 +5. 跨楼层导览是否优先支持“电梯优先”,以及无障碍路线是否需要独立入口。 +6. 到达目标后的下一步优先级:查看详情、相关讲解、收藏、重新规划如何排序。 +7. SDK 未加载、定位不可用、路线不可用是否需要统一错误码和诊断入口。 + +## 生成 Prompt 摘要 + +每张图均使用 `ui-mockup` 类型,移动端 H5 / 小程序竖屏构图,强调以下信息: + +- 画面名称和业务状态。 +- 用户当前目标。 +- 关键组件与交互意图。 +- 本项目视觉语气:浅灰绿地图底、白色浮层、黑色主按钮、低饱和边框、清晰中文层级。 +- 避免项:不复刻对标视频 UI,不使用对标颜色 / 图标 / 字体 / 精确布局,不承诺未经验证的正式导航能力。 diff --git a/docs/Benchmark/generated-guide-home-5-options/00-contact-sheet.png b/docs/Benchmark/generated-guide-home-5-options/00-contact-sheet.png new file mode 100644 index 0000000..776f40d Binary files /dev/null and b/docs/Benchmark/generated-guide-home-5-options/00-contact-sheet.png differ diff --git a/docs/Benchmark/generated-guide-home-5-options/01-01-map-first.png b/docs/Benchmark/generated-guide-home-5-options/01-01-map-first.png new file mode 100644 index 0000000..cda0960 Binary files /dev/null and b/docs/Benchmark/generated-guide-home-5-options/01-01-map-first.png differ diff --git a/docs/Benchmark/generated-guide-home-5-options/01-01-map-first.svg b/docs/Benchmark/generated-guide-home-5-options/01-01-map-first.svg new file mode 100644 index 0000000..9258880 --- /dev/null +++ b/docs/Benchmark/generated-guide-home-5-options/01-01-map-first.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 地球演化厅1F-03 + 恐龙化石展厅1F-02 + + 矿物与岩石厅1F-04 + + 搜索地点 + 讲解来馆 + 图例WC设施 + 2F1FB1 + + 开始馆内导览 +导览地图优先型 \ No newline at end of file diff --git a/docs/Benchmark/generated-guide-home-5-options/02-02-immersive-rail.png b/docs/Benchmark/generated-guide-home-5-options/02-02-immersive-rail.png new file mode 100644 index 0000000..9cf6bc5 Binary files /dev/null and b/docs/Benchmark/generated-guide-home-5-options/02-02-immersive-rail.png differ diff --git a/docs/Benchmark/generated-guide-home-5-options/02-02-immersive-rail.svg b/docs/Benchmark/generated-guide-home-5-options/02-02-immersive-rail.svg new file mode 100644 index 0000000..0711ecc --- /dev/null +++ b/docs/Benchmark/generated-guide-home-5-options/02-02-immersive-rail.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 地球演化厅1F-03 + 恐龙化石展厅1F-02 + + 矿物与岩石厅1F-04 + + + 讲解来馆 + 搜索展厅或设施 + 讲解来馆 + 2F1FB1 + 1F 三维地图 + 开始导览 +导览沉浸任务轨 \ No newline at end of file diff --git a/docs/Benchmark/generated-guide-home-5-options/03-03-bottom-dock.png b/docs/Benchmark/generated-guide-home-5-options/03-03-bottom-dock.png new file mode 100644 index 0000000..76c80f6 Binary files /dev/null and b/docs/Benchmark/generated-guide-home-5-options/03-03-bottom-dock.png differ diff --git a/docs/Benchmark/generated-guide-home-5-options/03-03-bottom-dock.svg b/docs/Benchmark/generated-guide-home-5-options/03-03-bottom-dock.svg new file mode 100644 index 0000000..cafd285 --- /dev/null +++ b/docs/Benchmark/generated-guide-home-5-options/03-03-bottom-dock.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 地球演化厅1F-03 + 恐龙化石展厅1F-02 + + 矿物与岩石厅1F-04 + + 搜索展厅、设施1F + 2F1FB1 + 查看图例设施筛选 + 准备开始导览选择讲解或来馆,也可以直接进入馆内导览讲解来馆开始导览 +导览底部任务 Dock \ No newline at end of file diff --git a/docs/Benchmark/generated-guide-home-5-options/04-04-poi-focus.png b/docs/Benchmark/generated-guide-home-5-options/04-04-poi-focus.png new file mode 100644 index 0000000..4d1b127 Binary files /dev/null and b/docs/Benchmark/generated-guide-home-5-options/04-04-poi-focus.png differ diff --git a/docs/Benchmark/generated-guide-home-5-options/04-04-poi-focus.svg b/docs/Benchmark/generated-guide-home-5-options/04-04-poi-focus.svg new file mode 100644 index 0000000..19bb6cf --- /dev/null +++ b/docs/Benchmark/generated-guide-home-5-options/04-04-poi-focus.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 地球演化厅1F-03 + 恐龙化石展厅1F-02 + + 矿物与岩石厅1F-04 + + 搜索 + 地图1F + 恐龙化石展厅1F-02 · 已在三维地图中定位开始导览 + ▶ 讲解听展厅内容↗ 来馆查看到馆方式 +导览点位聚焦型 \ No newline at end of file diff --git a/docs/Benchmark/generated-guide-home-5-options/05-05-ultra-minimal.png b/docs/Benchmark/generated-guide-home-5-options/05-05-ultra-minimal.png new file mode 100644 index 0000000..c86e7c1 Binary files /dev/null and b/docs/Benchmark/generated-guide-home-5-options/05-05-ultra-minimal.png differ diff --git a/docs/Benchmark/generated-guide-home-5-options/05-05-ultra-minimal.svg b/docs/Benchmark/generated-guide-home-5-options/05-05-ultra-minimal.svg new file mode 100644 index 0000000..5005970 --- /dev/null +++ b/docs/Benchmark/generated-guide-home-5-options/05-05-ultra-minimal.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + 深圳自然博物馆馆内三维导览 + + + + + + + + + + + + + + + + + + + + + 三维地图已就绪查看展厅位置、楼层与服务设施 + 1F + 你想先做什么?开始导览讲解来馆 +导览极简主视觉 \ No newline at end of file diff --git a/docs/Benchmark/generated-guide-home-5-options/README.md b/docs/Benchmark/generated-guide-home-5-options/README.md new file mode 100644 index 0000000..c4d0232 --- /dev/null +++ b/docs/Benchmark/generated-guide-home-5-options/README.md @@ -0,0 +1,44 @@ +# H5 导览首页 5 套设计方向 + +本目录提供 5 套 H5 首页高保真方向稿,用于选择信息架构和首页主体验证。设计重点为:三维组件、`讲解`入口、`来馆`入口。其他功能仅作为弱辅助控件出现。 + +## 文件 + +- `00-contact-sheet.png`:5 套方案总览图。 +- `01-01-map-first.png`:方案一,地图画布 + 顶部双入口。 +- `02-02-immersive-rail.png`:方案二,沉浸三维 + 右侧任务轨。 +- `03-03-bottom-dock.png`:方案三,三维首页 + 底部三任务 Dock。 +- `04-04-poi-focus.png`:方案四,点位聚焦 + 双入口卡片。 +- `05-05-ultra-minimal.png`:方案五,极简三维主视觉。 + +每套 PNG 均配有同名 SVG 源稿,方便后续继续调整。 + +## 方案说明 + +### 方案一:地图画布 + 顶部双入口 + +三维地图占据主屏,`讲解`和`来馆`作为顶部轻量胶囊入口。适合保留传统导览首页结构,同时弱化其他功能。 + +### 方案二:沉浸三维 + 右侧任务轨 + +三维地图更沉浸,`讲解`和`来馆`在右侧任务轨中出现,强调地图操作感。适合希望首页更像地图工具的方向。 + +### 方案三:三维首页 + 底部三任务 Dock + +三维地图在上,底部集中承接 `讲解`、`来馆`、`开始导览`。适合把首页主任务集中到下方操作区的方向。 + +### 方案四:点位聚焦 + 双入口卡片 + +默认突出一个展厅点位,底部提供 `讲解`和`来馆`两个卡片入口。适合首页默认带展厅推荐点位或当前位置点位的方向。 + +### 方案五:极简三维主视觉 + +最大幅度减少功能组件,只保留三维地图主视觉和底部核心任务选择。适合追求首页干净、低学习成本的方向。 + +## 需要确认 + +- 首页是否需要默认展示搜索入口,还是仅保留三维地图和任务入口。 +- `讲解`入口是进入讲解列表,还是进入当前展厅讲解。 +- `来馆`入口是外部导航、路线说明,还是馆外地图页。 +- `开始导览`是否仍作为首页主按钮保留,或被三维地图点击/点位点击替代。 +- 楼层、定位、图例、设施筛选是否必须首屏可见,还是可以收纳到地图工具菜单中。 diff --git a/docs/Benchmark/guide-video-ux-logic-analysis.md b/docs/Benchmark/guide-video-ux-logic-analysis.md new file mode 100644 index 0000000..a2ca351 --- /dev/null +++ b/docs/Benchmark/guide-video-ux-logic-analysis.md @@ -0,0 +1,221 @@ +# 对标导览小程序 UX 与功能逻辑分析 + +## 1. 分析范围 + +- 对标视频:`E:\下载\导览.mp4` +- 视频信息:约 66.43 秒,448 x 960,30fps,竖屏小程序录屏。 +- 本报告只抽取导览应用的功能逻辑、任务闭环、状态机和交互流程,不做 UI 视觉还原。 +- 本项目对照范围:`frontend-miniapp` 的 H5 导览业务源码,重点看 `src/pages/index/index.vue`、`src/components/navigation/*`、`src/components/map/ThreeMap.vue`、`src/pages/search/index.vue`、`src/pages/route/detail.vue`、`src/domain/guideReadiness.ts`、`src/usecases/guideRouteUseCase.ts`。 + +## 2. 抽帧与时间线方法 + +已使用 ffmpeg 抽帧,并用 OpenCV 计算逐秒帧差,形成视频时间线。 + +分析素材: + +- 抽帧拼图:`.codex-artifacts/benchmark-guide-logic/timeline_contact_sheet.jpg` +- 逐秒帧:`.codex-artifacts/benchmark-guide-logic/frames/t_001.jpg` 至 `t_066.jpg` +- 关键帧:`.codex-artifacts/benchmark-guide-logic/key_01.jpg` 至 `key_16.jpg` +- OpenCV 时间线:`.codex-artifacts/benchmark-guide-logic/opencv_timeline.json` + +OpenCV 识别出的主要画面转折点为:`00:02`、`00:06`、`00:15`、`00:18`、`00:24`、`00:34`、`00:38`、`00:39`、`00:40`、`00:43`、`00:53`、`00:62`、`00:63`。这些时间点基本对应加载完成、进入分类列表、切换选点态、键盘搜索、确认起点、路线预览、开始模拟导航、退出导览等状态变化。 + +## 3. 视频时间线 + +| 时间段 | 画面 / 状态 | 用户动作 | 系统反馈 | 关键帧 | +| --- | --- | --- | --- | --- | +| 00:00-00:02 | 小程序加载页,展示馆名和加载进度 | 打开小程序 | 进入导览主页前的启动反馈 | `key_01.jpg` | +| 00:02-00:06 | 导览主页,地图为主界面,顶部搜索,右侧工具,底部功能入口 | 等待地图加载或浏览首页 | 默认落在导览地图,可直接搜索、来馆、更多或进入推荐内容 | `key_02.jpg` | +| 00:06-00:15 | 分类 / 地图列表态,展示设施分类图标、楼层切换、POI 列表 | 点击地图 / 分类入口,浏览设施 | 同屏保留地图上下文和下方列表,用户不离开导览上下文 | `key_03.jpg` | +| 00:15-00:18 | 分类无结果提示 | 点击暂无数据的分类,如饮料售卖机 | Toast 提示暂无结果,仍停留当前列表,不打断任务 | `key_04.jpg` | +| 00:18-00:24 | 路线规划选点态,顶部起终点条,下方提示选择起点,模拟导航按钮置灰 | 进入路线规划,未选起点时尝试继续 | 提示必须先选起点,主按钮保持不可用 | `key_05.jpg`、`key_06.jpg` | +| 00:24-00:34 | 搜索 / 选点态,弹出键盘,用户输入或选择点位 | 选择起点或终点 | 候选点位列表更新,地图仍作为空间参考 | `key_09.jpg`、`key_10.jpg` | +| 00:34-00:38 | 选择确认态,地图上出现选点标记,底部弹出确认或点位卡 | 确认将该点设为起点 / 终点 | 起终点条更新,进入路线可规划状态 | `key_11.jpg`、`key_12.jpg` | +| 00:38-00:43 | 路线方案态,展示推荐路线、不走楼梯、电梯等方案,显示时间 / 距离 | 查看路线方案,点击模拟导航 | 路线叠加到地图,主按钮由不可用变为可启动 | `key_08.jpg` | +| 00:43-00:53 | 导航中态,顶部转向指令卡,地图路线、当前位置箭头、语音 / 低速 / 楼层 / 缩放控件,底部目的地与退出 | 模拟导航进行中 | 给出连续导航指令和当前位置反馈,保留退出导览能力 | `key_07.jpg` | +| 00:53-00:62 | 导航中跨步骤 / 结束前状态 | 继续模拟导航或操作退出 | 当前步骤更新,路线和目的地状态持续可见 | `key_13.jpg`、`key_14.jpg` | +| 00:62-00:66 | 退出导览后回到地图 / 列表态 | 点击退出或完成导览 | 清理导航态,回到可再次搜索 / 分类 / 规划的导览首页 | `key_15.jpg`、`key_16.jpg` | + +## 4. 对标应用的用户任务闭环 + +对标应用的核心闭环不是单个“搜索结果页”,而是围绕地图持续完成任务: + +1. 进入导览首页:地图即工作台,用户先获得空间上下文。 +2. 找目标:通过搜索、分类、楼层列表或地图点选定位目标。 +3. 定起终点:路线规划需要明确起点和终点,未满足条件时主动作禁用并给出原因。 +4. 看方案:系统生成推荐路线,并提供偏好方案,如不走楼梯、电梯。 +5. 开始导览:从路线方案进入导航中,地图、路线、位置、转向指令同步变化。 +6. 导航中纠偏:持续显示当前指令、楼层、缩放、语音等控制,用户可以退出。 +7. 结束 / 退出:退出后清理导航态,返回导览主页,可继续搜索或重新规划。 + +这个闭环的 UX 关键点是“上下文不断裂”:搜索、分类、选点、路线、导航都嵌在地图任务里,用户很少被推到孤立页面。 + +## 5. 导览状态机 + +```mermaid +stateDiagram-v2 + [*] --> Loading + Loading --> MapHome + MapHome --> CategoryList: 打开地图/分类 + MapHome --> SearchSelecting: 搜索或选点 + MapHome --> RoutePointPicking: 进入路线规划 + CategoryList --> EmptyCategory: 分类无结果 + EmptyCategory --> CategoryList: Toast 后停留 + CategoryList --> PoiSelected: 点击 POI + SearchSelecting --> PoiSelected: 选择候选点 + PoiSelected --> RoutePointPicking: 设为起点/终点 + RoutePointPicking --> RoutePointPicking: 起终点不完整/提示 + RoutePointPicking --> RoutePreview: 起终点完整并规划 + RoutePreview --> Navigating: 开始/模拟导航 + Navigating --> Navigating: 步进/转向/楼层变化 + Navigating --> MapHome: 退出导览 + RoutePreview --> RoutePointPicking: 返回修改起终点 + RoutePointPicking --> MapHome: 取消/清除 +``` + +状态机说明: + +- `MapHome` 是中心状态,不只是入口页;其它状态多数都能回到它。 +- `RoutePointPicking` 对起点 / 终点完整性有强校验,防止用户在条件不足时进入导航。 +- `RoutePreview` 与 `Navigating` 是两个不同状态:前者看方案,后者进入指令驱动。 +- `Navigating` 必须有退出路径,否则用户进入沉浸态后会失控。 + +## 6. 路线规划流程 + +```mermaid +flowchart TD + A[进入路线规划] --> B{是否已有起点} + B -- 否 --> C[提示选择起点并禁用主按钮] + C --> D[搜索/地图点选/列表选择起点] + B -- 是 --> E{是否已有终点} + D --> E + E -- 否 --> F[提示选择终点并禁用主按钮] + F --> G[搜索/地图点选/列表选择终点] + E -- 是 --> H[展示路线偏好] + G --> H + H --> I[请求路线规划] + I --> J{是否有可用路线} + J -- 否 --> K[展示无路线/能力不可用原因] + J -- 是 --> L[展示推荐路线、距离、时间、跨层信息] + L --> M[开始模拟/正式导航] + M --> N[导航中指令、定位、楼层、退出] + N --> O[结束或退出,清理导航态] +``` + +对标流程里的路线偏好包含“推荐路线 / 不走楼梯 / 电梯”。这不只是三个按钮,而是路线规划输入条件的一部分:它们应改变路径权重、可通行边、垂直交通工具偏好,不能只改变文案。 + +## 7. 搜索 / 选点 / 楼层 / 导航中 / 结束逻辑 + +### 搜索逻辑 + +- 搜索框始终贴近地图主场景,搜索不是脱离地图的单独任务。 +- 输入时可出现键盘与候选列表;确认后应回填到起点或终点,而不是只跳详情。 +- 搜索结果应可被用于三类动作:查看位置、设为起点、设为终点。 +- 空结果使用轻量 Toast 或空态,不破坏当前选点任务。 + +### 选点逻辑 + +- 路线规划明确区分起点和终点。 +- 当前正在选起点还是终点,需要有持续提示。 +- 未选起点 / 终点时,模拟导航等主动作应禁用。 +- 选点后需要地图标记、文本条和底部确认态同步更新。 + +### 楼层逻辑 + +- 楼层既是浏览过滤条件,也是路线语义的一部分。 +- 分类 / POI 列表与楼层选择联动,用户可以按楼层缩小范围。 +- 跨层路线需要展示跨层提示,并在导航中支持楼层切换或自动跟随。 +- 电梯、扶梯、楼梯不只是设施 POI;在正式路线里应作为垂直连接器参与拓扑。 + +### 导航中逻辑 + +- 导航中要从“看路线”切换到“执行指令”:顶部当前动作、地图路线、当前位置、目的地、退出按钮是最小闭环。 +- 语音、低速、楼层、缩放等控制属于导航运行态控制,不应和路线方案态混淆。 +- 模拟导航可以作为无真实定位时的演示模式,但需要和正式导航文案区分。 + +### 结束导览逻辑 + +- 结束或退出要清理路线、当前位置模拟、转向指令和目的地卡片。 +- 退出后应回到地图主页或路线方案可编辑态,而不是落到空白或无法继续的状态。 +- 对“已到达”与“用户主动退出”应分别处理:前者可给到到达反馈,后者保持可重规划。 + +## 8. 本项目当前逻辑对照 + +| 模块 | 当前状态 | 源码证据 | 与对标逻辑的差距 | +| --- | --- | --- | --- | +| 导览主页地图工作台 | 部分实现 | `src/pages/index/index.vue` 已有 `is3DMode`、`guideOutdoorState`、`indoorView`、`activeGuideFloor`、`selectedGuidePoi` 等状态;`GuideMapShell` 统一承载馆内 / 馆外、搜索、楼层、工具事件 | 已具备地图主页骨架,但搜索 / 分类 / 选点 / 路线的“同一任务上下文”还不如对标视频连贯 | +| “来馆”入口 | 已实现 | `src/pages/index/index.vue` 更多菜单标题已为“来馆”,并通过 `openOutdoorNavPanel()` 切到馆外 2D 地图 | 与对标里的“来馆/馆外参考”接近,继续保持与馆内导航解耦 | +| 馆外导航 / 来馆 | 已实现 | `src/components/navigation/OutdoorNavigationPanel.vue` 支持 GPS、手动起点、地址搜索、出行方式、路线结果、开始导航 / 清除 | 与对标导览里的馆内路线逻辑是两条业务线,不能混成馆内导航能力 | +| 馆内 3D 和楼层 | 部分实现 | `src/components/map/ThreeMap.vue` 和 `GuideMapShell` 已有馆内模型、楼层、POI 聚焦、路线预览渲染入口 | 具备位置预览和楼层浏览基础;正式跨层导航还缺经验证的拓扑与连接器 | +| POI 选择与卡片 | 部分实现 | `src/pages/index/index.vue` 有 `selectedGuidePoi`、POI 卡片、设起点 / 终点相关处理 | 对标视频中选点和路线起终点绑定更强;本项目仍偏“点位预览 + 路线面板” | +| 搜索 | 部分实现 | `src/pages/search/index.vue` 有搜索输入、筛选 chip、结果列表、点击详情、结果动作跳 `route/detail` | 已有搜索基础,但不是对标那种地图内分类面板 + 楼层 POI 列表 + 选起终点一体化流程 | +| 路线规划面板 | 部分实现 | `src/components/navigation/RoutePlannerPanel.vue` 有起点、终点、交换、清除、收起、推荐路线 / 少走楼梯 / 电梯优先、主按钮 | 面板结构接近对标,但路线偏好大多禁用;主流程受路线数据门禁限制 | +| 起终点搜索 / 选择 | 已实现基础 | `RoutePointPicker.vue` 被 `RoutePlannerPanel.vue` 使用,支持起点 / 终点 picker 与搜索 | 可继续吸收对标的“地图点选确认”和“当前选择角色持续提示” | +| 路线可用性门禁 | 已实现且必须保留 | `src/domain/guideReadiness.ts` 中 `NAV_ROUTE_GRAPH_READY = false`,不可用文案为“当前暂不支持正式导航,可查看位置预览、路线示意和馆外参考” | 当前不能对外宣称正式馆内导航,这是正确边界 | +| 真正路线规划 | 缺失 / 被门禁关闭 | `src/usecases/guideRouteUseCase.ts` 的 `planRoute()` 先读 readiness;未 ready 直接返回 error;`GuideRouteRepository` 也经过 `applyRouteReadinessGate` | 对标视频可进入路线方案和导航中,本项目当前只能做位置预览或能力不可用提示 | +| 导航中 turn-by-turn | 缺失 | `src/pages/index/index.vue` 有 `isSimulatingRoute` 和模拟态 UI 变量,但由于 `routeReady=false` 通常无法获得真实 `activeRoutePreview` | 缺真实路线、实时 / 模拟位置步进、转向指令、到达判断、偏航处理 | +| 路线偏好 | 缺失 / 占位 | `RoutePlannerPanel.vue` 中“少走楼梯”“电梯优先”均 `disabled: true`,提示待路线数据验证 | 需要真实 graph 边权、垂直连接器、无障碍属性后才可启用 | +| 结束导览 | 部分实现 | `handleRouteBack()`、`handleRouteClear()`、`handleRouteSimulate()` 可切换 / 清理部分路线状态 | 对标中的“导航中退出 -> 回到地图 / 方案态”闭环可借鉴,但需先有稳定导航中状态 | + +## 9. 已有能力、缺失能力与优先级 + +### 已有,可继续打磨 + +- 地图作为导览主页:本项目已通过 `GuideMapShell`、`ThreeMap`、`TencentMap` 分离馆内 / 馆外渲染。 +- POI / 楼层 / 位置预览:已有基础数据流和 UI 状态。 +- 起终点面板:已有起点、终点、交换、清除、picker 搜索等基础能力。 +- 路线能力门禁:当前 `NAV_ROUTE_GRAPH_READY=false`,避免误导用户,这是上线前必须保留的保护。 +- 来馆:馆外导航面板已存在,适合承接对标视频里的“来馆”任务。 + +### 部分已有,建议按 UX 闭环补齐 + +- 地图内分类列表:当前搜索页是独立页面样式,建议未来改为地图上下文内的分类 / 楼层 / POI 列表,不打断导览。 +- 点位到路线的动作:搜索结果、POI 卡片、地图点选应统一提供“查看位置 / 设为起点 / 设为终点”。 +- 路线方案态:可先在 route graph 未就绪时展示“位置关系 / 路线示意不可用原因”,路线就绪后再切换到推荐路线。 +- 导航退出闭环:现有清理函数可复用,但应明确区分取消规划、退出模拟、到达结束。 + +### 缺失,需数据和算法支撑 + +- 经验证的 `route_graph` / `nav_data` 加载、校验和冒烟测试。 +- 馆内路径规划算法或 SDK 路线规划结果适配。 +- 楼层连接器:楼梯、扶梯、电梯与图节点 / 边的关联。 +- 路线偏好:推荐、不走楼梯、电梯优先对应不同权重和约束。 +- 导航中 turn-by-turn:分步指令、当前位置更新、下一步提示、到达判断、偏航 / 重规划。 +- 正式导航和模拟导航的文案与状态区分。 + +## 10. 建议吸收的 UX 逻辑 + +P1:先补齐不依赖真实路线的闭环。 + +- 将搜索、分类、楼层、POI 列表尽量保持在导览地图上下文内。 +- 统一点位动作模型:`preview`、`setStart`、`setEnd`、`clear`。 +- 路线未就绪时,主按钮和状态文案只表达“查看位置 / 位置关系 / 能力不可用原因”,不出现正式导航承诺。 +- 空结果、未选起点、未选终点用即时反馈,不跳离当前任务。 + +P2:在 route graph 就绪后启用路线方案态。 + +- 接入真实 `route_graph`、`nav_data`、楼层连接器和 POI 到可行走节点映射。 +- 将推荐路线、不走楼梯、电梯优先从 UI 占位变成规划输入。 +- 路线方案卡展示距离、预计时间、跨层信息、关键连接器。 + +P3:再启用导航中状态。 + +- 明确模拟导航与正式导航两条状态。 +- 导航中提供当前指令、下一步、楼层跟随、缩放、语音开关、退出。 +- 结束导览后清理导航态并回到地图主页或路线方案态。 + +## 11. 风险边界 + +本项目当前不能按对标视频完整复刻“正式馆内导航”。原因不是 UI 缺按钮,而是路线能力被明确门禁: + +- `src/domain/guideReadiness.ts` 将 `NAV_ROUTE_GRAPH_READY` 固定为 `false`。 +- `guideRouteUseCase.planRoute()` 在 readiness 未就绪时直接返回错误,不进入真实路线规划。 +- “少走楼梯 / 电梯优先”在路线面板中仍是禁用占位。 + +因此,对标视频中的路线方案、转向指令、模拟导航、到达结束等逻辑可以作为产品目标和状态机设计参考,但在本项目中必须等 `route_graph`、`nav_data`、楼层连接器和路线冒烟测试完成后才能启用。短期应继续使用“位置预览 / 查看位置关系 / 馆外参考 / 来馆”作为真实能力表述。 + +## 12. 结论 + +对标小程序的核心优势是把导览任务组织成一个连续地图工作流:找点、选点、看路线、开始导航、退出导览都在同一空间上下文中完成。本项目已经具备地图 shell、馆内 3D、楼层、POI、路线面板、来馆面板和路线能力门禁,基础方向正确。 + +当前最大差距在于真实馆内路线数据和导航运行态:对标视频已经展示路线方案与导航中指令,本项目仍应停留在位置预览和能力不可用提示。建议先吸收不依赖路线数据的 UX 闭环,再在数据验证完成后逐步开放路线方案和导航中状态。 diff --git a/docs/Benchmark/home-redesign-options/README.md b/docs/Benchmark/home-redesign-options/README.md new file mode 100644 index 0000000..b8da196 --- /dev/null +++ b/docs/Benchmark/home-redesign-options/README.md @@ -0,0 +1,140 @@ +# 导览首页精简版 5 套方案 + +本目录提供 5 套移动端 H5 导览首页设计稿,画布比例为 390x844。设计目标是把首页收敛为三个核心模块: + +- 三维组件:馆内 3D 或馆外地图主区域。 +- 讲解入口:进入展厅 / 展品讲解内容。 +- 来馆入口:进入到馆 / 馆外参考位置。 + +这些图片只表达首页布局、入口层级和交互逻辑,不承诺真实馆内导航、路线规划或到达判断能力。 + +## 输出文件 + +- `option-1.png`:地图画布 + 顶部双入口 +- `option-2.png`:沉浸三维 + 右侧任务轨 +- `option-3.png`:三维首页 + 底部任务面板 +- `option-4.png`:馆内三维 / 来馆并列预览 +- `option-5.png`:讲解优先浮层 +- `interaction-middle-map-operating.png`:从首页进入点位搜索前的三维地图操作态 +- `options-overview.png`:5 套方案总览,便于快速横向比较 + +## 生成说明 + +已按 `imagegen` skill 先读取项目材料和设计约束,并使用默认内置 `image_gen` 模式生成 5 张高保真 PNG。生成后的图片已从 `$CODEX_HOME/generated_images/...` 复制到本目录,文件名为 `option-1.png` 至 `option-5.png`。未修改 `src` 下业务代码。 + +## 现有首页功能点对照 + +从 `src/pages/index/index.vue` 和 `GuideMapShell.vue` 读取到的现有能力包括: + +- `GuideMapShell` 承载馆内 3D、馆外地图、搜索、模式切换、楼层、缩放、更多入口、POI 点击等。 +- 首页当前有 `guide` / `explain` 顶部 tab。 +- `来馆` 当前在更多菜单和 `OutdoorNavigationPanel` 中承接。 +- POI 卡片包含查看位置、从这里出发、去这里、相关讲解。 +- 路线规划面板已有起终点、交换、路线预览、模拟导览等结构,但正式馆内路线能力仍受门禁约束。 + +本次 5 套方案的共同取舍: + +- 搜索:不作为首页核心组件展示,除方案一 / 二保留极弱入口外,其余收纳或移除。 +- 楼层:只保留最小当前楼层提示或收纳,不展开完整楼层列表。 +- 缩放 / 更多 / 工具按钮:默认隐藏,收纳到地图菜单或地图手势中。 +- POI 卡片:不作为首页默认模块,只在用户点选地图后出现。 +- 路线规划:不放在首页主入口,不承诺真实导航能力。 +- 顶部 tab:弱化为直接的 `讲解` 入口,而不是占用首屏的复杂导航。 + +## 方案对比 + +### 方案一:地图画布 + 顶部双入口 + +- 三维组件:约占屏幕 75%,从标题下方延伸到底部主按钮上方。 +- 讲解入口:顶部右侧胶囊按钮。 +- 来馆入口:顶部右侧胶囊按钮,与讲解并列。 +- 次要功能:保留极简 `馆内3D / 馆外` 切换和当前楼层提示;搜索、缩放、更多、路线规划不展开。 +- 设计意图:最接近现有首页结构,改造成本较低,用户容易理解。 +- 优点:稳定、清晰、落地阻力小。 +- 缺点:仍然保留了一些地图工具感,不是最极简。 + +### 方案二:沉浸三维 + 右侧任务轨 + +- 三维组件:接近全屏,地图从顶部内容区一直延伸到底部。 +- 讲解入口:右侧悬浮任务轨中的上半按钮,同时顶部也有弱入口。 +- 来馆入口:右侧悬浮任务轨中的下半按钮,同时顶部也有弱入口。 +- 次要功能:只保留 `馆内3D / 馆外` 极简切换;搜索、楼层、缩放、更多收纳到地图菜单。 +- 设计意图:让首页更像一个沉浸式三维地图工具。 +- 优点:三维组件存在感最强。 +- 缺点:右侧任务轨需要验证是否遮挡地图主体。 + +### 方案三:三维首页 + 底部任务面板 + +- 三维组件:约占屏幕 60%,上半屏为地图主区域。 +- 讲解入口:底部任务面板中的小卡片。 +- 来馆入口:底部任务面板中的小卡片。 +- 次要功能:地图上只保留当前模式和楼层提示;搜索、完整楼层、更多、路线规划全部收纳。 +- 设计意图:把首页决策集中到底部,适合单手操作。 +- 优点:任务入口非常明确,适合普通用户。 +- 缺点:三维地图占比低于方案一 / 二。 + +### 方案四:馆内三维 / 来馆并列预览 + +- 三维组件:上半屏为馆内 3D 主区域,约占屏幕 45%。 +- 讲解入口:顶部文字入口 + 底部讲解卡片。 +- 来馆入口:下半屏独立来馆卡片,配馆外地图预览。 +- 次要功能:搜索、楼层、缩放、更多完全移除;只保留三维和来馆两个空间入口。 +- 设计意图:把“馆内”和“来馆”拆成两个明确任务区。 +- 优点:来馆入口最突出,适合到馆前用户。 +- 缺点:三维组件主导性弱一些。 + +### 方案五:讲解优先浮层 + +- 三维组件:作为全屏背景主视觉,承托首页空间感。 +- 讲解入口:顶部前置大卡片中的主按钮。 +- 来馆入口:顶部前置大卡片中的次按钮。 +- 次要功能:搜索、楼层、路线规划明确收纳,不在首页展开。 +- 设计意图:适合把首页定位为内容导览入口,而三维地图作为空间背景。 +- 优点:首页最简洁,讲解入口最强。 +- 缺点:如果产品希望三维地图可操作感更强,这套会偏内容入口。 + +## 推荐排序 + +1. 方案一:推荐作为第一落地方向。它保留三维地图主导地位,同时把 `讲解` 和 `来馆` 清晰前置,和现有组件结构最容易对接。 +2. 方案三:适合强调首页任务选择和单手操作,产品表达最稳。 +3. 方案二:视觉冲击和三维沉浸感最好,但需要进一步验证遮挡和触控区域。 +4. 方案四:适合来馆任务权重很高的版本,但首页会更像入口聚合页。 +5. 方案五:最极简,适合内容讲解优先策略;若导览首页必须突出地图操作,则优先级较低。 + +最终选择仍建议由产品确认首页主任务权重:如果“看地图”优先,选方案一或二;如果“选任务”优先,选方案三;如果“到馆”优先,选方案四;如果“听讲解”优先,选方案五。 + +## 首页到点位搜索的中间操作页 + +`interaction-middle-map-operating.png` 用于补全“首页沉浸态”和“点位搜索页”之间的交互断点。 + +### 页面定位 + +这个页面不是首页,也不是点位搜索页,而是用户点击首页 `开始浏览` / `进入三维` 后进入的三维地图操作态。它的作用是: + +- 让三维地图从展示态进入可操作态。 +- 允许用户拖动、缩放、点选地图。 +- 通过底部 `查找点位` 按钮进入点位搜索页。 +- 通过 `设施` / `楼层` 进入轻量筛选,不在首页直接堆功能。 + +### 推荐交互链路 + +```text +导览首页 + ├─ 点击「讲解」→ 讲解内容入口 + ├─ 点击「来馆」→ 来馆 / 馆外参考入口 + └─ 点击「开始浏览」或「进入三维」 + → interaction-middle-map-operating.png(三维地图操作态) + ├─ 拖动 / 缩放地图 → 浏览空间 + ├─ 点击地图点位 → 展示点位预览卡 + ├─ 点击「查找点位」→ 点位搜索页 + ├─ 点击「设施」→ 展开服务设施筛选 + └─ 点击「楼层」→ 展开楼层选择 +``` + +### 与点位搜索页的关系 + +点位搜索页承接的是中间页底部 `查找点位` 的结果。进入点位搜索后,用户可以搜索地点、点击服务台 / 电梯 / 扶梯 / 洗手间 / 母婴室 / 出入口等分类,或切换楼层查看点位列表。点击任一点位后,建议返回三维地图操作态,并在地图上聚焦该点位。 + +### 能力边界 + +中间页只表达“浏览地图、查看位置、查找点位”的能力,不表达正式路线规划或实时导航。点位卡建议使用 `查看详情`、`查看位置`、`相关讲解` 等文案,避免使用 `开始导航`。 diff --git a/docs/Benchmark/home-redesign-options/interaction-middle-map-operating.png b/docs/Benchmark/home-redesign-options/interaction-middle-map-operating.png new file mode 100644 index 0000000..f28adfc Binary files /dev/null and b/docs/Benchmark/home-redesign-options/interaction-middle-map-operating.png differ diff --git a/docs/Benchmark/home-redesign-options/option-1.png b/docs/Benchmark/home-redesign-options/option-1.png new file mode 100644 index 0000000..dc9cd02 Binary files /dev/null and b/docs/Benchmark/home-redesign-options/option-1.png differ diff --git a/docs/Benchmark/home-redesign-options/option-1.svg b/docs/Benchmark/home-redesign-options/option-1.svg new file mode 100644 index 0000000..1d8ad4f --- /dev/null +++ b/docs/Benchmark/home-redesign-options/option-1.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + 导览三维地图工作台 + ▶ 讲解↗ 来馆 + + + + + + + + + + + + + + + + + + + + 恐龙化石展厅1F-02地球演化厅1F-03 + + 馆内3D馆外 + 2F1F + 进入三维导览 \ No newline at end of file diff --git a/docs/Benchmark/home-redesign-options/option-2.png b/docs/Benchmark/home-redesign-options/option-2.png new file mode 100644 index 0000000..fddac91 Binary files /dev/null and b/docs/Benchmark/home-redesign-options/option-2.png differ diff --git a/docs/Benchmark/home-redesign-options/option-2.svg b/docs/Benchmark/home-redesign-options/option-2.svg new file mode 100644 index 0000000..337d336 --- /dev/null +++ b/docs/Benchmark/home-redesign-options/option-2.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 恐龙化石展厅1F-02地球演化厅1F-03 + + + 导览沉浸三维组件 + 讲解来馆 + 讲解来馆 + 馆内3D馆外 + 次要功能收纳至地图菜单 + 开始浏览 \ No newline at end of file diff --git a/docs/Benchmark/home-redesign-options/option-3.png b/docs/Benchmark/home-redesign-options/option-3.png new file mode 100644 index 0000000..ddac766 Binary files /dev/null and b/docs/Benchmark/home-redesign-options/option-3.png differ diff --git a/docs/Benchmark/home-redesign-options/option-3.svg b/docs/Benchmark/home-redesign-options/option-3.svg new file mode 100644 index 0000000..92a736c --- /dev/null +++ b/docs/Benchmark/home-redesign-options/option-3.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + 导览任务入口集中 + + + + + + + + + + + + + + + + + + + + 恐龙化石展厅1F-02地球演化厅1F-03 + + 3D馆外 + 1F + 你想先做什么?三维地图作为首页主体,入口集中到底部浏览三维讲解来馆 + \ No newline at end of file diff --git a/docs/Benchmark/home-redesign-options/option-4.png b/docs/Benchmark/home-redesign-options/option-4.png new file mode 100644 index 0000000..046cc68 Binary files /dev/null and b/docs/Benchmark/home-redesign-options/option-4.png differ diff --git a/docs/Benchmark/home-redesign-options/option-4.svg b/docs/Benchmark/home-redesign-options/option-4.svg new file mode 100644 index 0000000..41d171c --- /dev/null +++ b/docs/Benchmark/home-redesign-options/option-4.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + 导览馆内 / 来馆并列预览 + 讲解 › + + + + + + + + + + + + + + + + + + + + + + 馆内三维馆内地图主区域进入三维 + + + + + + + + + + 博物馆主入口馆外参考位置 + + 来馆查看到馆入口与馆外参考位置打开来馆 + ▶ 讲解从展厅内容进入音频讲解 + \ No newline at end of file diff --git a/docs/Benchmark/home-redesign-options/option-5.png b/docs/Benchmark/home-redesign-options/option-5.png new file mode 100644 index 0000000..62568c4 Binary files /dev/null and b/docs/Benchmark/home-redesign-options/option-5.png differ diff --git a/docs/Benchmark/home-redesign-options/option-5.svg b/docs/Benchmark/home-redesign-options/option-5.svg new file mode 100644 index 0000000..b476690 --- /dev/null +++ b/docs/Benchmark/home-redesign-options/option-5.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 导览讲解优先入口 + 先听讲解,或直接看地图首页只保留讲解、来馆与三维组件进入讲解查看来馆 + 三维地图组件轻触地图查看馆内 / 馆外空间 + 搜索、楼层、路线规划暂收纳,不在首页展开 + \ No newline at end of file diff --git a/docs/Benchmark/home-redesign-options/options-overview.png b/docs/Benchmark/home-redesign-options/options-overview.png new file mode 100644 index 0000000..74fdb35 Binary files /dev/null and b/docs/Benchmark/home-redesign-options/options-overview.png differ diff --git a/docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md b/docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md index 9794d52..f5b4b96 100644 --- a/docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md +++ b/docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md @@ -225,7 +225,7 @@ MuseumFloor { | 后端 floorCode | 领域 label | | --- | --- | -| `EXTERIOR` | `室外` | +| `EXTERIOR` | `馆外` | | `L-2` | `B2` | | `L-1` | `B1` | | `L1` | `1F` | @@ -328,7 +328,7 @@ interface SgsNavigablePlaceDomain { ```text mapDiagnostics.status === 'OK' -且所有室内可导航楼层 routePlanningReady=true +且所有馆内可导航楼层 routePlanningReady=true 且 routeNodeCount > 0 且 routeEdgeCount > 0 且关键楼层 navigablePlaceCount > 0 @@ -536,7 +536,7 @@ pnpm build:h5 ## 12. 上线前阻断项 -以下问题未解决前,不建议对用户宣称“正式室内导航”: +以下问题未解决前,不建议对用户宣称“正式馆内导航”: - `L-1` 无可导航目的地。 - `EXTERIOR` 路网未就绪。 diff --git a/docs/Data/data-audit-2026-05-28.md b/docs/Data/data-audit-2026-05-28.md index 5a0b9de..dd59635 100644 --- a/docs/Data/data-audit-2026-05-28.md +++ b/docs/Data/data-audit-2026-05-28.md @@ -3,7 +3,7 @@ 审计日期:2026-05-28 审计分支:`analysis/ux-ui-audit-2026-05-28` 审计对象:`museum-guide-v4.0/frontend-miniapp` -审计范围:Mock 数据、类型定义、数据加载、搜索/地图/详情页硬编码数据、静态资源、3D/室内 POI 数据、数据治理与性能策略。 +审计范围:Mock 数据、类型定义、数据加载、搜索/地图/详情页硬编码数据、静态资源、3D/馆内 POI 数据、数据治理与性能策略。 ## 执行摘要 @@ -25,7 +25,7 @@ | 方法 | 覆盖内容 | 证据来源 | | --- | --- | --- | -| JSON 结构扫描 | 展品、展厅、设施、路线、楼层、室内 POI 数量与字段 | `src/assets/data/*.json`、`static/data/f1-indoor-pois.json` | +| JSON 结构扫描 | 展品、展厅、设施、路线、楼层、馆内 POI 数量与字段 | `src/assets/data/*.json`、`static/data/f1-indoor-pois.json` | | 引用完整性校验 | 展品到展厅、楼层到展厅/设施、路线到站点 | Node 脚本读取 JSON 后交叉比对 | | 静态资源存在性校验 | 图片、音频、展厅图是否存在于 `static/` | 文件系统校验 | | 硬编码扫描 | 页面、组件、地图、搜索中的本地数组和占位 URL | `rg` 搜索 | @@ -40,7 +40,7 @@ | `src/assets/data/facilities.json` | 8 | 基础设施/模型坐标混合数据 | 点位坐标来自 `f1-floor.glb` 提取,应保留;但实体名称、类型、描述仍与真实 1F POI 和自然博物馆语义不一致 | | `src/assets/data/routes.json` | 3 | 艺术馆路线 Mock | 路线名称、站点均围绕艺术作品;不适配自然博物馆参观动线 | | `src/assets/data/floors.json` | 4 | 楼层索引 | `B1` 引用不存在设施,且无展厅 | -| `static/data/f1-indoor-pois.json` | 67 | 真实 1F 室内点位基准 | 其它设施、展厅、搜索、地图详情数据未以它为准,存在大面积冲突 | +| `static/data/f1-indoor-pois.json` | 67 | 真实 1F 馆内点位基准 | 其它设施、展厅、搜索、地图详情数据未以它为准,存在大面积冲突 | | `static/models/*.glb` | 2 | 真实 1F 3D 模型资产 | `f1-indoor.glb`、`f1-floor.glb` 均为自然博物馆 1F 真实模型;需补模型清单、坐标系与部署目录策略 | | `static/icons/*.svg` | 8 | 地图图标 | 可用,但 POI 类型枚举与图标映射未统一 | @@ -53,7 +53,7 @@ flowchart TD D["pages/detail.vue
硬编码详情"] --> E["详情页展示"] F["SearchPanel / ExplainList
自然博物馆硬编码 Mock"] --> G["搜索/讲解抽屉"] H["TencentMap / ThreeMap
硬编码地图点"] --> I["地图弹层"] - J["static/data/f1-indoor-pois.json
67 个室内 POI"] -.未统一接入.-> I + J["static/data/f1-indoor-pois.json
67 个馆内 POI"] -.未统一接入.-> I ``` ## 1. 数据完整性审计 @@ -64,7 +64,7 @@ flowchart TD | --- | --- | --- | --- | | 展品 | 5 个艺术作品 | 缺少标本、化石、矿物、动植物、年代、分类、馆藏编号、展陈状态、讲解文本层级 | P0 | | 展厅 | 5 个艺术主题展厅 | 缺少自然史展厅分区、楼层分布、入口/出口、展厅开放状态、人流/容量 | P0 | -| 设施 | 8 个基础设施 | 室内 POI 有 58 个设施,但未与设施表合并;缺少服务台、母婴室、寄存、无障碍、楼梯等正式类型 | P1 | +| 设施 | 8 个基础设施 | 馆内 POI 有 58 个设施,但未与设施表合并;缺少服务台、母婴室、寄存、无障碍、楼梯等正式类型 | P1 | | 路线 | 3 条艺术馆路线 | 缺少亲子、研学、无障碍、快速参观、自然史主题路线 | P0 | | 楼层 | 4 层索引 | 只有简单数组引用,无真实地图区域、楼层坐标系、模型版本、开放状态 | P1 | | POI | F1 有 67 个模型点 | 只覆盖 `1F`,未绑定展品/展厅/路线/设施详情 | P1 | @@ -202,7 +202,7 @@ POI floors: 1F | 类型 | 当前定义 | 发现的问题 | 严重性 | | --- | --- | --- | --- | -| `Position` | `{ x, y }` | 2D 坐标,不支持室内 POI 的 `{ x, y, z }`,也不支持腾讯地图的 `{ latitude, longitude }` | P1 | +| `Position` | `{ x, y }` | 2D 坐标,不支持馆内 POI 的 `{ x, y, z }`,也不支持腾讯地图的 `{ latitude, longitude }` | P1 | | `Facility.type` | `restroom/cafe/shop/exit/elevator/info` | 不包含 `entrance`、`hall`、`stairs`、`mother_baby`、`accessible_restroom`、`service_desk` 等真实 POI 类型 | P1 | | `POIMarker.type` | `exhibit/hall/facility/location` | 不包含 `entrance`,与 `ThreeMap`、`f1-indoor-pois.json` 不一致 | P1 | | `Exhibit` | 艺术品字段 `artist/year/material/size` | 自然博物馆需要 `taxonomy`、`period`、`specimenType`、`collectionNo`、`scientificName`、`ageRange` 等字段 | P0 | @@ -309,9 +309,9 @@ interface DataResult { | --- | ---: | ---: | ---: | | 展品/标本 | 5 | 30 到 60 | 200+ | | 展厅/展区 | 5 | 8 到 12 | 按真实楼层和展陈分区完整覆盖 | -| 设施 | 8 | 30 到 60 | 与室内 POI 全量绑定 | +| 设施 | 8 | 30 到 60 | 与馆内 POI 全量绑定 | | 路线 | 3 | 5 到 8 | 支持人群、时长、无障碍、拥堵策略 | -| 室内 POI | 67,仅 1F | 每层 50+ | 全楼层、全关键节点 | +| 馆内 POI | 67,仅 1F | 每层 50+ | 全楼层、全关键节点 | 当前数量只适合演示 UI,无法支撑真实导览。 @@ -323,9 +323,9 @@ interface DataResult { | --- | --- | --- | | 核心 JSON | `position: { x, y }` | 未声明坐标系、楼层、比例尺和原点 | | 3D POI | `{ x, y, z }` | 未进入 `types/index.ts`,未绑定实体 | -| 腾讯地图 | `{ latitude, longitude }` | 硬编码在组件内,未与室内 POI 建立转换关系 | +| 腾讯地图 | `{ latitude, longitude }` | 硬编码在组件内,未与馆内 POI 建立转换关系 | -`TencentMap.vue` 里的建筑轮廓和 `ThreeMap.vue` 的中心点均硬编码为深圳自然博物馆附近坐标,但无法验证这些点与 GLB 模型、室内 POI、楼层图是否同源。 +`TencentMap.vue` 里的建筑轮廓和 `ThreeMap.vue` 的中心点均硬编码为深圳自然博物馆附近坐标,但无法验证这些点与 GLB 模型、馆内 POI、楼层图是否同源。 ### 3.4 多语言数据 @@ -419,7 +419,7 @@ flowchart LR | 资源 | 路径 | 大小 | | --- | --- | ---: | -| 室内模型 | `static/models/f1-indoor.glb` | 1,562,816 bytes | +| 馆内模型 | `static/models/f1-indoor.glb` | 1,562,816 bytes | | 楼层模型 | `static/models/f1-floor.glb` | 95,720 bytes;`facilities.json` 点位坐标来源 | | 部署副本 | `public/models/f1-indoor.glb`、`public/models/f1-floor.glb` | 与 `static/models` 同大小;如果两端都会打包,需要明确去重或按端分发 | @@ -673,7 +673,7 @@ scripts/ | P1 | 设施类型粒度不足 | 真实 POI 全部压成 `facility/entrance/hall`,具体类别藏在中文 label 中 | 搜索、筛选、图标、无障碍路线无法可靠判断类别 | 增加 `category/subtype`,如 `restroom_female`、`elevator`、`stairs`、`ticket_office`、`nursing_room` | | P1 | UI 筛选分类与真实标签没有规范映射 | `AreaSelector` 有 `卫生间/电梯/楼梯/停车场/母婴室/服务中心/寄存处/饮水处/影院`,真实标签是 `女卫001`、`电梯020`、`楼梯.001`、`存包处`、`茶水间` 等 | 用户选择分类后很难精确筛出真实点位 | 建立 label 归一化和分类映射表,区分展示名与检索名 | | P1 | 搜索数据未覆盖真实 1F 设施 | 搜索 Mock 只有 `洗手间`、`咖啡厅`、`服务中心`、`纪念品商店` 等 | 搜索不到真实存在的售票机、服务台、母婴间、轮椅及儿童车租车处、贵宾卫生间等 | 搜索索引从真实 POI 和设施实体生成 | -| P1 | 存在两套 POI 坐标模型 | 真实 POI 为 `{x,y,z}`;`ThreeMap.vue` props/default POI 为 `{latitude,longitude}` | 组件接口同名但数据形态不同,后续接入容易错用 | 将室内 3D POI 与室外经纬度 POI 拆成不同类型 | +| P1 | 存在两套 POI 坐标模型 | 真实 POI 为 `{x,y,z}`;`ThreeMap.vue` props/default POI 为 `{latitude,longitude}` | 组件接口同名但数据形态不同,后续接入容易错用 | 将馆内 3D POI 与馆外经纬度 POI 拆成不同类型 | | P2 | 真实 POI label 带模型导出痕迹 | `电梯001`、`楼梯.001`、`无障碍卫生间.001` | 直接展示会显得粗糙,也不利于搜索同义词 | 保留原始 label,同时新增 `displayName`、`instanceNo`、`normalizedName` | | P2 | `entrance` 类型语义混杂 | `售票处`、`售票机`、`服务台` 被标为 `entrance` | 类型名称无法表达真实服务属性 | 原始类型可保留,业务层补充 `category: ticket/service` | @@ -685,7 +685,7 @@ scripts/ | `src/assets/data/halls.json` | 1F 展厅名称和数量与真实 POI 冲突 | 重建 halls,并绑定 `poi_0/poi_51` 到 `poi_55` | | `src/assets/data/floors.json` | 1F 只引用 3 个旧设施 | 从真实 POI 派生楼层设施索引 | | `src/pages/index/index.vue` | `markerDataMap` 不认识 `poi_*`,点击设施被忽略 | 改为根据 `poiId/entityId` 查询 repository | -| `src/components/map/ThreeMap.vue` | 默认 POI 使用经纬度,和真实室内 `{x,y,z}` POI 混用 | 拆分 `IndoorPOI` 与 `OutdoorMarker` | +| `src/components/map/ThreeMap.vue` | 默认 POI 使用经纬度,和真实馆内 `{x,y,z}` POI 混用 | 拆分 `IndoorPOI` 与 `OutdoorMarker` | | `src/types/index.ts` | `Position` 只有 `{x,y}`,`POIMarker` 不含 `entrance` | 扩展坐标和 POI 类型 | | `src/components/search/SearchPanel.vue` | 搜索 Mock 未来自真实 POI | 搜索索引从 `pois + entities` 生成 | | `src/components/area/AreaSelector.vue` | 分类只停留在 UI 文案,没有连接真实 POI subtype | 建立设施分类字典和 label 归一化规则 | @@ -736,7 +736,7 @@ interface FacilityInstance { | 数据/资产 | 可信部分 | 仍存在的问题 | | --- | --- | --- | -| `static/models/f1-indoor.glb` | 真实 1F 室内 3D 模型 | 缺少 `modelId`、hash、版本、来源、坐标系、比例尺、与 POI 文件的绑定说明 | +| `static/models/f1-indoor.glb` | 真实 1F 馆内 3D 模型 | 缺少 `modelId`、hash、版本、来源、坐标系、比例尺、与 POI 文件的绑定说明 | | `static/models/f1-floor.glb` | 真实 1F 楼层模型,且是 `facilities.json.position` 的提取来源 | `facilities.json` 未记录 `sourceModel`、提取工具、提取时间、坐标原点、单位、投影规则 | | `static/data/f1-indoor-pois.json` | 真实 1F POI 点位,使用 `{x,y,z}` 模型空间坐标 | 未绑定业务实体,label 仍是模型导出名称,需要业务归一化 | | `src/assets/data/facilities.json` | `position` 字段有真实模型来源 | 设施名称、类型、描述仍是旧艺术馆语义;坐标字段只有 `{x,y}`,无法判断与 `f1-indoor-pois.json` 的 `{x,y,z}` 如何互转 | diff --git a/docs/Data/miniapp_audio_play_api_integration.md b/docs/Data/miniapp_audio_play_api_integration.md new file mode 100644 index 0000000..91efa7d --- /dev/null +++ b/docs/Data/miniapp_audio_play_api_integration.md @@ -0,0 +1,281 @@ +# 小程序语音播放接口对接说明 + +## 0. 对接速览 + +小程序播放语音时统一调用后端播放解析接口,返回的是可播放文件地址 `playUrl` 和元信息,不再从展品列表或详情中直接读取四通道音频 URL,也不再按字节流方式处理音频。 + +```http +GET /app-api/gis/guide/audio/play-info?targetType=ITEM&targetId=1001&lang=zh-CN +GET /app-api/gis/guide/audio/play-info?targetType=STOP&targetId=2001&lang=en-US +``` + +小程序只需要传: + +| 参数 | 取值 | 说明 | +| --- | --- | --- | +| `targetType` | `ITEM` / `STOP` | `ITEM` 为展品入口,`STOP` 为讲解点入口 | +| `targetId` | number | 展品 ID 或讲解点 ID,必须和 `targetType` 匹配 | +| `lang` | `zh-CN` / `en-US` | 来自小程序全局语言状态 | + +小程序不要传标准版 / 拓展版,版本由后端根据用户权益解析。返回 `playable=true` 时直接播放 `playUrl`;返回 `playable=false` 时按 `reason` 展示不可播放提示。 + +## 1. 对接原则 + +Phase 1 后,小程序不要再从展品列表或详情里的四个音频字段自行判断播放地址。播放时统一调用后端播放解析接口,由后端根据展品/讲解点、全局语言和后续权益策略返回唯一播放资源。 + +小程序只维护三件事: + +1. 全局语言状态:`zh-CN` / `en-US`。 +2. 播放目标:`targetType + targetId + lang`。 +3. 使用后端返回的唯一 `playUrl` 播放音频。 + +不要在小程序内做这些事情: + +- 不要读取 `standardAudioUrl`、`standardAudioUrlEn`、`extendedAudioUrl`、`extendedAudioUrlEn` 来选择音频。 +- 不要给游客提供“标准版 / 拓展版”切换。 +- 不要在小程序内硬编码收费、会员、白名单等权益判断。 +- 不要把某个固定 MinIO 或 CDN URL 当作长期有效地址永久缓存。 + +## 2. 语言码约定 + +播放接口、小程序和 TTS 资产统一使用 TTS 标准语言码: + +| 语言 | 小程序入参 | 后端响应 | TTS 表字段 | +| --- | --- | --- | --- | +| 中文 | `zh-CN` | `zh-CN` | `tts_audio_file.language` / `tts_voice.language` | +| 英文 | `en-US` | `en-US` | `tts_audio_file.language` / `tts_voice.language` | + +注意: + +- 小程序不要把 `zh-CN` 转成 `zh`,也不要把 `en-US` 转成 `en`。 +- 后端会临时兼容历史入参 `zh` / `en` / `zh_CN` / `en_US`,但这不是小程序对接契约。 +- GIS 历史 `businessId = guide_content:{id}:{version}:{zh/en}` 只属于服务端内部兼容细节,不传给小程序。 +- 英文没有音频时,后端不会自动降级播放中文。 + +## 3. 播放目标入参规则 + +Phase 1 同时支持 `ITEM` 和 `STOP`,但两者语义不同: + +| 场景 | 推荐入参 | 说明 | +| --- | --- | --- | +| 小程序当前只有展品 ID | `targetType=ITEM&targetId=展品ID` | 兼容入口。后端根据展品绑定的 `stopId` 解析到讲解点音频,小程序不需要自己查讲解点。 | +| 小程序已经拿到讲解点 ID | `targetType=STOP&targetId=讲解点ID` | 首选入口。讲解点是当前阶段真实的语音生产和播放单元。 | + +关键规则: + +- `ITEM` 的 `targetId` 必须是展品 ID,不要传讲解点 ID。 +- `STOP` 的 `targetId` 必须是讲解点 ID,不要传展品 ID。 +- 如果展品没有绑定讲解点,后端返回 `playable=false`,`reason=NO_GUIDE_STOP`。 +- 如果讲解点存在但没有对应语言的发布音频,后端返回 `playable=false`,`reason=NO_PUBLISHED_AUDIO` 或 `NO_GUIDE_CONTENT`。 +- Phase 1 响应里的 `targetType`、`targetId` 保持为请求目标,便于小程序按请求维度缓存;后端内部是否解析到 `STOP` 不要求小程序感知。 +- 后续如果展品列表或详情响应增加 `playTargetType`、`playTargetId`,小程序优先使用这两个字段;没有这两个字段时继续按 `ITEM + 展品ID` 调用。 + +也就是说,小程序现在按展品 ID 播放不是问题,但它应该调用播放解析接口,而不是自己读取展品里的音频 URL。后台负责把“展品 -> 讲解点 -> 当前语言/权益音频”这条链路解析完。 + +## 4. 单个播放解析 + +```http +GET /app-api/gis/guide/audio/play-info?targetType=ITEM&targetId=1001&lang=zh-CN +GET /app-api/gis/guide/audio/play-info?targetType=STOP&targetId=2001&lang=zh-CN +``` + +Query 参数: + +| 参数 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `targetType` | string | 是 | `ITEM` 表示展品,`STOP` 表示讲解点 | +| `targetId` | number | 是 | 对应目标类型的业务 ID:展品 ID 或讲解点 ID | +| `lang` | string | 是 | 全局语言,`zh-CN` 或 `en-US` | + +Header: + +| Header | 必填 | 说明 | +| --- | --- | --- | +| `Authorization` | 否 | 登录用户建议携带;Phase 1 未登录默认按免费标准版处理 | + +可播放响应: + +```json +{ + "code": 0, + "data": { + "playable": true, + "targetType": "ITEM", + "targetId": 1001, + "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, + "fallback": false, + "fallbackReason": null, + "reason": null + } +} +``` + +不可播放响应仍是业务成功,靠 `playable=false` 表达: + +```json +{ + "code": 0, + "data": { + "playable": false, + "targetType": "ITEM", + "targetId": 1001, + "lang": "en-US", + "narrationTier": "STANDARD", + "fallback": false, + "reason": "NO_PUBLISHED_AUDIO" + } +} +``` + +常见 `reason`: + +| reason | 小程序建议处理 | +| --- | --- | +| `NO_PUBLISHED_AUDIO` | 提示“当前语言暂无语音讲解” | +| `NO_GUIDE_STOP` | 提示“该展品暂未配置语音讲解” | +| `NO_GUIDE_CONTENT` | 提示“该目标暂无讲解内容” | +| `UNSUPPORTED_LANGUAGE` | 提示“不支持该语言” | +| `UNSUPPORTED_TARGET_TYPE` | 记录错误,不展示播放入口 | +| `TARGET_NOT_FOUND` | 提示“目标信息不存在” | + +## 5. 展品列表摘要字段 + +展品列表和详情响应会补充轻量音频摘要,供页面决定是否展示播放入口: + +| 字段 | 说明 | +| --- | --- | +| `hasAudio` | 是否存在任一可播放音频 | +| `supportedLanguages` | 可播放语言列表,值为 `zh-CN` / `en-US` | +| `audioStatus` | `READY` / `MISSING` | +| `audioCount` | 可播放音频通道数量 | +| `playTargetType` | 可选;后端建议的小程序播放目标,通常为 `STOP` | +| `playTargetId` | 可选;后端建议的小程序播放目标 ID,通常为讲解点 ID | + +这些字段只用于列表展示和按钮可用态。真正播放前仍调用 `play-info` 获取当次可播放 URL。 + +`playUrl` 是“可播放地址”,不是音频字节流。小程序拿到后直接赋给 `audio.src` 即可,不需要再做下载、转 Blob 或手动拼接媒体流。 + +如果响应里没有 `playTargetType`、`playTargetId`,小程序按兼容规则使用 `targetType=ITEM&targetId=展品ID` 即可。 + +## 6. 推荐播放流程 + +```ts +type AudioPlayInfo = { + playable: boolean + targetType: "ITEM" | "STOP" + targetId: number + lang: "zh-CN" | "en-US" + narrationTier: "STANDARD" | "EXTENDED" + playUrl?: string + expiresAt?: string | null + title?: string + reason?: string +} + +const audioPlayInfoMap = new Map() + +function audioKey(targetType: "ITEM" | "STOP", targetId: number, lang: string) { + return `${targetType}:${targetId}:${lang}` +} + +function isExpired(expiresAt?: string | null) { + return !!expiresAt && new Date(expiresAt).getTime() <= Date.now() +} + +async function playGuideAudio(targetType: "ITEM" | "STOP", targetId: number) { + const lang = getGlobalLanguage() // "zh-CN" | "en-US" + const key = audioKey(targetType, targetId, lang) + let info = audioPlayInfoMap.get(key) + + if (!info || isExpired(info.expiresAt)) { + info = await api.getAudioPlayInfo({ + targetType, + targetId, + lang + }) + audioPlayInfoMap.set(key, info) + } + + if (!info.playable || !info.playUrl) { + showToast(reasonToText(info.reason)) + return + } + + audioContext.stop() + audioContext.src = info.playUrl + audioContext.title = info.title || "" + audioContext.play() +} +``` + +播放失败或 URL 过期时,重新请求一次单个解析接口: + +```ts +audioContext.onError(async () => { + const lang = getGlobalLanguage() + const fresh = await api.getAudioPlayInfo({ + targetType: currentTargetType, + targetId: currentTargetId, + lang + }) + + audioPlayInfoMap.set(audioKey(currentTargetType, currentTargetId, lang), fresh) + + if (fresh.playable && fresh.playUrl) { + audioContext.src = fresh.playUrl + audioContext.play() + } else { + showToast(reasonToText(fresh.reason)) + } +}) +``` + +## 7. 缓存与流量策略 + +Phase 1 已接入 Redis 播放解析缓存。当前后端会缓存 `play-info` 结果和必要的播放元信息,但不会把音频二进制放进 Redis。 + +职责边界: + +- 音频文件播放流量应直接走对象存储 / CDN,业务后端只承接轻量的播放解析请求。 +- 后端对 `play-info` 做基础 IP 限流,避免脚本刷解析接口。 +- 后端使用 Redis 缓存播放解析结果,缓存维度至少包含 `targetType + targetId + lang`,并在发布、撤回、重建快照后主动失效。 +- 如果后续 `playUrl` 改成短期签名地址,本期小程序按 `expiresAt` 和播放失败重试机制重新请求 `play-info` 即可。 + +小程序侧配合: + +- 不要预下载大量音频文件,点击播放时拿到 `playUrl` 后再播放。 +- 本地可以短暂缓存 `play-info` 结果,缓存 key 使用 `targetType + targetId + lang`;语言切换后清理旧语言缓存。 +- 播放失败、403、404 或 URL 过期时,重新请求一次 `play-info`,不要无限重试。 +- 连续快速点击多个展品时,只保留最后一次点击的播放请求和播放结果。 +- 切换语言时清理旧语言的本地播放缓存,避免中文/英文串播。 + +后续如果真实流量证明播放解析接口成为瓶颈,再进入 Phase 2 增加 Redis 元信息缓存。届时缓存 key 至少应包含 `targetType + targetId + lang + narrationTier`,且后台发布音频时需要精确失效对应缓存。真正的大流量压力仍应由 CDN / 对象存储承担。 + +## 8. 语言切换处理 + +用户切换全局语言后: + +- 后续所有 `play-info` 都传新语言码。 +- 本地播放结果缓存按 `targetType + targetId + lang` 分开,或直接清理旧语言缓存。 +- 当前正在播放的音频建议停止,提示用户重新播放当前展品的新语言版本。 +- 如果新语言没有音频,展示不可播放提示,不自动播放另一种语言。 + +## 9. 联调检查清单 + +- 中文标准音频展品:`lang=zh-CN` 能拿到唯一 `playUrl` 并播放。 +- 展品 ID 兼容播放:`targetType=ITEM&targetId=展品ID` 能由后端解析到绑定讲解点音频。 +- 中文讲解点音频:`targetType=STOP&lang=zh-CN` 能拿到唯一 `playUrl` 并播放。 +- 展品未绑定讲解点:返回 `playable=false`、`reason=NO_GUIDE_STOP`。 +- 英文缺失展品:`lang=en-US` 返回 `playable=false`,不会拿中文 URL。 +- 语言切换后:请求参数和本地缓存 key 都发生变化。 +- 未登录用户:返回 `STANDARD`。 +- 播放 URL 失效:重新请求 `play-info` 后恢复播放或展示原因。 +- 快速连续点击多个展品:最终只播放最后一次点击的展品。 diff --git a/docs/Data/miniapp_audio_play_text_api_integration.md b/docs/Data/miniapp_audio_play_text_api_integration.md new file mode 100644 index 0000000..5ae2c82 --- /dev/null +++ b/docs/Data/miniapp_audio_play_text_api_integration.md @@ -0,0 +1,352 @@ +# 小程序导览音频与讲解词接口对接说明 + +## 0. 当前结论 + +小程序播放导览音频时,先调用播放解析接口: + +```http +GET /app-api/gis/guide/audio/play-info?targetType=ITEM&targetId=1001&lang=zh-CN +``` + +如果需要展示讲解词正文,再按需调用文本接口: + +```http +GET /app-api/gis/guide/audio/text-info?targetType=ITEM&targetId=1001&lang=zh-CN +``` + +两个接口刻意分开: + +- `play-info` 只负责尽快返回唯一可播放 `playUrl`,保证游客点击后音频优先播放。 +- `text-info` 只在页面确实要展示正文、字幕面板或讲解词详情时调用,避免每次播放都带上大段文本。 +- 音频播放失败不应被文本查询拖累;文本缺失也不影响音频播放。 +- 两类数据缓存策略不同:播放解析是短 TTL 元信息缓存,讲解词是热门文本缓存 + LRU 淘汰。 + +## 1. 默认参数规则 + +`targetType` 和 `targetId` 必须传,不做默认值。 + +`lang` 可以不传,不传时后端默认按中文标准语言 `zh-CN` 解析。 + +```http +GET /app-api/gis/guide/audio/play-info?targetType=ITEM&targetId=1001 +GET /app-api/gis/guide/audio/text-info?targetType=ITEM&targetId=1001 +``` + +等价于: + +```http +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` / `STOP` | `ITEM` 是展品入口,`STOP` 是讲解点入口 | +| `targetId` | 是 | number | 必须和 `targetType` 匹配 | +| `lang` | 否 | `zh-CN` / `en-US` | 不传默认 `zh-CN`;兼容历史 `zh` / `en` 入参 | + +不要传: + +- `standard` / `extended` +- `zh` / `en` 作为正式对接语言码 +- `businessId` +- MinIO 路径 +- 四通道 URL,如 `standardAudioUrl`、`extendedAudioUrl` + +## 2. ITEM / STOP 语义 + +`STOP` 是真实讲解播放单元。 + +`ITEM` 是小程序兼容入口。 + +| 场景 | 推荐调用 | +| --- | --- | +| 小程序只有展品 ID | `targetType=ITEM&targetId=展品ID` | +| 小程序已经拿到讲解点 ID | `targetType=STOP&targetId=讲解点ID` | + +规则: + +- `ITEM` 的 `targetId` 必须是展品 ID,不要传讲解点 ID。 +- `STOP` 的 `targetId` 必须是讲解点 ID,不要传展品 ID。 +- `ITEM` 请求内部会根据展品 `stopId` 找到讲解点,但响应里的 `targetType`、`targetId` 仍保持小程序请求值,便于客户端按请求维度缓存。 + +## 3. 播放解析接口 + +```http +GET /app-api/gis/guide/audio/play-info?targetType=ITEM&targetId=1001&lang=zh-CN +``` + +可播放响应: + +```json +{ + "code": 0, + "data": { + "playable": true, + "targetType": "ITEM", + "targetId": 1001, + "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`,通过 `playable=false` 表达: + +```json +{ + "code": 0, + "data": { + "playable": false, + "targetType": "ITEM", + "targetId": 1001, + "lang": "en-US", + "narrationTier": "STANDARD", + "hasText": false, + "fallback": false, + "reason": "NO_PUBLISHED_AUDIO" + } +} +``` + +字段说明: + +| 字段 | 说明 | +| --- | --- | +| `playable` | 是否可播放 | +| `playUrl` | 可直接赋值给小程序音频组件的 HTTPS/CDN/对象存储地址 | +| `narrationTier` | 后端解析出的版本:`STANDARD` / `EXTENDED` | +| `hasText` | 当前语言和版本是否有讲解词正文;有正文时可按需调用 `text-info` | +| `fallback` | 是否发生版本降级或历史音频回退 | +| `reason` | 不可播放原因 | + +常见 `reason`: + +| reason | 小程序建议处理 | +| --- | --- | +| `NO_GUIDE_STOP` | 展品未绑定讲解点 | +| `NO_GUIDE_CONTENT` | 讲解点没有讲解词 | +| `NO_PUBLISHED_AUDIO` | 当前语言没有已发布音频 | +| `UNSUPPORTED_LANGUAGE` | 语言不支持 | +| `UNSUPPORTED_TARGET_TYPE` | targetType 不支持 | +| `TARGET_NOT_FOUND` | 目标不存在 | + +## 4. 讲解词正文接口 + +```http +GET /app-api/gis/guide/audio/text-info?targetType=ITEM&targetId=1001&lang=zh-CN +``` + +可用响应: + +```json +{ + "code": 0, + "data": { + "available": true, + "targetType": "ITEM", + "targetId": 1001, + "lang": "zh-CN", + "narrationTier": "STANDARD", + "title": "青铜神树", + "text": "青铜神树是三星堆遗址出土的重要青铜器...", + "textLength": 1200, + "textHash": "0b32a4..." + } +} +``` + +不可用响应同样返回 `code=0`: + +```json +{ + "code": 0, + "data": { + "available": false, + "targetType": "ITEM", + "targetId": 1001, + "lang": "en-US", + "narrationTier": "STANDARD", + "reason": "NO_TEXT" + } +} +``` + +字段说明: + +| 字段 | 说明 | +| --- | --- | +| `available` | 是否有正文 | +| `text` | 讲解词正文,`available=false` 时为空 | +| `textLength` | 正文字数,按 Java 字符数统计 | +| `textHash` | 正文 MD5,用于小程序本地缓存版本判断 | +| `reason` | 不可用原因 | + +`text-info` 和 `play-info` 使用同一套语言、版本和目标解析逻辑。也就是说,小程序不需要自己判断标准版/拓展版;文本接口会返回与当前后端播放解析一致的正文版本。 + +## 5. 推荐调用流程 + +小程序点击播放时: + +1. 调用 `play-info`。 +2. 如果 `playable=false`,展示不可播放原因,不调用 `text-info`。 +3. 如果 `playable=true`,立即设置 `audio.src = playUrl` 并开始播放。 +4. 如果当前页面要展示讲解词,且 `hasText=true`,再异步调用 `text-info`。 +5. `text-info` 成功后渲染正文;失败或 `available=false` 不影响音频播放。 + +示例: + +```ts +async function playGuideAudio(targetType: "ITEM" | "STOP", targetId: number) { + const lang = getGlobalLanguage() || "zh-CN" + const playInfo = await api.getAudioPlayInfo({ targetType, targetId, lang }) + + if (!playInfo.playable || !playInfo.playUrl) { + showToast(reasonToText(playInfo.reason)) + return + } + + audioContext.stop() + audioContext.src = playInfo.playUrl + audioContext.title = playInfo.title || "" + audioContext.play() + + if (playInfo.hasText && shouldShowNarrationText()) { + loadNarrationTextLazy(targetType, targetId, lang) + } +} + +async function loadNarrationTextLazy(targetType: "ITEM" | "STOP", targetId: number, lang: string) { + const cacheKey = `${targetType}:${targetId}:${lang}` + const cached = narrationTextCache.get(cacheKey) + if (cached) { + renderNarrationText(cached.text) + return + } + + const textInfo = await api.getAudioTextInfo({ targetType, targetId, lang }) + if (!textInfo.available) { + return + } + + narrationTextCache.set(cacheKey, { + text: textInfo.text, + textHash: textInfo.textHash + }) + renderNarrationText(textInfo.text) +} +``` + +## 6. 为什么不在 play-info 里直接返回正文 + +从系统架构看,音频播放和正文展示是两个不同优先级的动作。 + +播放路径应该尽量短: + +- 游客点击播放后的第一目标是尽快拿到 `playUrl` 并开始播放。 +- 大段正文会增加接口响应体,弱网下会拖慢播放启动。 +- 很多播放场景只需要音频,不一定展开讲解词面板。 +- 音频地址和正文的缓存策略不同,混在一个接口里会让缓存粒度变粗。 +- 文本接口失败时,不应该影响音频播放。 + +因此当前设计是: + +- `play-info`:轻量、高频、短 TTL,返回播放必要信息。 +- `text-info`:按需、可延迟、热门文本 LRU 缓存,返回正文。 + +这对小程序实现也比较直接:音频先播,文本异步补上。页面可以先显示标题、加载态或正文骨架,文本回来后再填充。 + +## 7. 缓存策略 + +### 7.1 后端播放解析缓存 + +`play-info` 使用 Redis 缓存轻量播放解析结果。 + +| 项 | 策略 | +| --- | --- | +| Redis key | `gis:guide:play-info:{targetType}:{targetId}:{lang}` | +| TTL | 5 分钟 | +| 缓存内容 | 播放元信息,不含音频二进制 | +| 失效时机 | 音频发布、撤回、通道快照刷新、全量快照重建 | + +音频文件流量仍然走对象存储 / CDN / HTTPS 静态地址,业务后端不代理音频流。 + +### 7.2 后端热门文本缓存 + LRU 淘汰 + +`text-info` 使用 Redis 做热门文本缓存,只缓存 `available=true` 的正文响应。 + +| 项 | 策略 | +| --- | --- | +| Redis key | `gis:guide:text-info:{targetType}:{targetId}:{lang}:{narrationTier}` | +| LRU 索引 | `gis:guide:text-info:lru` | +| TTL | 60 分钟 | +| 最大条数 | 1000 条 | +| 淘汰方式 | Redis ZSet 记录最近访问时间,超过上限时删除最久未访问的 key | +| 大文本处理 | 响应 JSON 超过 2KB 时 gzip 后 base64 存储 | +| 负结果缓存 | 不缓存 `available=false`,避免后台补文本后长期命中旧缺失状态 | + +`textHash` 返回给小程序,用于本地缓存版本判断;后端 Redis key 不把 `textHash` 放进去,因为那样会导致每次命中缓存前还要先查数据库计算 hash,反而失去缓存意义。 + +### 7.3 小程序本地缓存建议 + +播放信息本地缓存: + +- key:`targetType + targetId + lang` +- 语言切换后清理旧语言缓存。 +- 播放失败、403、404 或 URL 过期时,重新请求一次 `play-info`。 + +正文文本本地缓存: + +- key:`targetType + targetId + lang` +- value:`text + textHash` +- 若重新请求 `text-info` 后 `textHash` 变化,替换本地正文。 +- 不要在小程序侧长期永久缓存正文;建议按会话或短期持久化缓存即可。 + +## 8. 2 万游客/天的性能口径 + +按一天 2 万游客访问,播放解析接口和文本接口都应该由 Redis 承接热点请求,但系统瓶颈不能放在 Spring Boot 音频流上。 + +当前职责分工: + +- `play-info`:轻量解析,Redis 短 TTL 缓存。 +- `text-info`:热门正文 Redis 缓存,LRU 控制容量。 +- 音频文件:对象存储 / CDN / HTTPS 直出。 + +这样即使热门展品被大量点击: + +- 播放地址解析大多命中 `play-info` 缓存。 +- 正文展示大多命中 `text-info` 热文本缓存。 +- 最大流量的 MP3 文件不经过业务后端。 + +后续如果真实压测显示 Redis 或数据库仍有压力,再考虑: + +- 对展厅/热门展品做预热缓存。 +- 对 `play-info` 和 `text-info` 增加批量预取,但播放前仍以单个 `play-info` 为准。 +- 将音频和文本静态化到 CDN 边缘,但仍由后端接口返回当前可用地址。 + +## 9. 联调检查清单 + +- 不传 `lang`:`play-info` 默认返回中文标准版解析结果。 +- 不传 `lang`:`text-info` 默认返回中文标准版正文。 +- 中文可播放:`playable=true`,`playUrl` 可直接播放,`hasText` 正确。 +- 中文有正文:`text-info` 返回 `available=true`、`text`、`textLength`、`textHash`。 +- 英文缺音频:`play-info` 返回 `NO_PUBLISHED_AUDIO`,不自动播放中文。 +- 英文缺正文:`text-info` 返回 `available=false`,不影响中文和音频播放。 +- 展品未绑定讲解点:`NO_GUIDE_STOP`。 +- 讲解点无讲解词:`NO_GUIDE_CONTENT`。 +- 播放 URL 失效:小程序重新请求一次 `play-info`。 +- 后台发布/刷新音频后:对应 `play-info` 和 `text-info` 缓存应失效。 +- 热门正文多次请求:第二次开始命中 Redis 文本缓存。 diff --git a/docs/H5_DEPLOYMENT_GUIDE.md b/docs/H5_DEPLOYMENT_GUIDE.md index 7b4eb07..1590576 100644 --- a/docs/H5_DEPLOYMENT_GUIDE.md +++ b/docs/H5_DEPLOYMENT_GUIDE.md @@ -145,7 +145,7 @@ b772216640a14171ba5655085c8523be 页面验证: - 打开 https://guide.whaoyue.com/#/ -- 切换到 `室内3D` +- 切换到 `馆内3D` - 首次进入应加载当前楼层模型,而不是先加载全馆 overview 模型 - 不应出现 `Unexpected token '<', " { ### 立即测试(必须) 1. **H5 浏览器测试**: - - 进入室内 3D,查看初始状态是否为"建筑外观" + - 进入馆内 3D,查看初始状态是否为"建筑外观" - 放大建筑外观,观察是否自动切换到单楼层 - 缩小单楼层视图,观察是否自动切换回建筑外观 - 手动点击"单层/多层"按钮,观察 10 秒内是否不会自动切换 diff --git a/docs/QA/codex-user-testing-agents-guide-2026-06-30.md b/docs/QA/codex-user-testing-agents-guide-2026-06-30.md new file mode 100644 index 0000000..3c6cc5a --- /dev/null +++ b/docs/QA/codex-user-testing-agents-guide-2026-06-30.md @@ -0,0 +1,621 @@ +# Codex 用户测试操作指南:Lead + 专家团覆盖导览/讲解业务闭环 + +> 日期:2026-06-30 +> 项目:深圳自然博物馆 `frontend-miniapp` +> 适用对象:Codex / GPT-5.4 类 coding agent +> 默认目标:H5 用户测试与业务逻辑闭环验证 +> 推荐模式:Lead Agent + 专家团协作 + +--- + +## 1. 目标与边界 + +本指南用于让 Codex 以专业 agents 团队方式,为本项目执行用户测试设计、测试示例编写、H5 smoke 验证和风险汇总。 + +测试目标不是“证明系统已经具备真实室内导航”,而是验证当前产品能力是否闭环: + +```text +导览 guide:室内 3D 展示 + 楼层切换 + POI/位置预览 +讲解 explain:内容/讲解入口 + 音频状态 + 查看位置联动 +``` + +### 1.1 必须遵守的产品真相 + +Codex 执行测试时必须遵守以下边界: + +1. 默认只测 H5,不测 `mp-weixin`,除非用户明确要求。 +2. 当前导览能力是“室内 3D 展示 + POI/位置预览”。 +3. `route_graph` / `nav_data` 未验证前,不能把功能描述为“开始馆内导航”“路线导航”“到达引导”或 turn-by-turn。 +4. 路线相关测试应验证 unavailable / disabled / readiness gate,而不是强行证明路线可用。 +5. 讲解能力必须依赖真实内容/媒体数据;不能把 `example.com/audio.mp3` 或 placeholder 当作可用音频。 +6. 页面、组件和测试应消费 domain / repository / use case 数据,不应绕过数据层直接解析静态包或后端字段。 +7. 不引入 legacy nav assets service。 +8. 不做无关重构、不删除旧 demo 数据、不清理历史文件,除非用户另行授权。 + +### 1.2 推荐测试层级 + +Codex 应按轻到重执行: + +```text +源码审查 + -> 纯业务/adapter 单元测试示例 + -> repository/usecase 闭环测试示例 + -> H5 smoke 用户流测试 + -> 风险报告与修复建议 +``` + +如果项目暂未引入测试框架,Codex 应先给方案或最小 Vitest 示例,不应直接引入重型 E2E 框架。 + +--- + +## 2. Lead + 专家团角色分工 + +### 2.1 Lead Agent:测试总负责人 + +职责: + +- 读取任务目标和项目边界。 +- 分配专家 Agent 的测试范围。 +- 汇总每个专家的发现。 +- 去重、排序、判定阻塞级别。 +- 形成最终测试矩阵、执行结果和修复建议。 + +Lead Agent 不应直接跳过专家结论,也不应把假设当事实。 + +### 2.2 Guide Agent:导览闭环专家 + +关注范围: + +- 首页导览入口。 +- 馆外/馆内切换。 +- 室内 3D 初始状态。 +- 全馆/多层/单层切换。 +- 楼层切换。 +- POI 展示、点击、选中、聚焦。 +- 位置预览卡片。 +- route readiness 和不可导航状态。 + +重点文件: + +```text +src/pages/index/index.vue +src/components/navigation/GuideMapShell.vue +src/components/map/ThreeMap.vue +src/domain/guideFloor.ts +src/domain/guideReadiness.ts +src/domain/guideModel.ts +src/usecases/guideUseCase.ts +``` + +### 2.3 Explain Agent:讲解闭环专家 + +关注范围: + +- 讲解 tab 入口。 +- 讲解列表加载。 +- 展厅/展品/讲解项详情。 +- 音频播放、暂停、关闭、错误态。 +- 缺失音频 unavailable 状态。 +- 讲解项“查看位置”跳回 guide location preview。 +- guide/explain tab 状态保持。 + +重点文件: + +```text +src/components/explain +src/components/audio +src/pages/index/index.vue +src/pages/exhibit +src/pages/hall +src/repositories +src/usecases +``` + +### 2.4 Data Agent:数据契约与 readiness 专家 + +关注范围: + +- Provider / Adapter / Repository / UseCase 数据边界。 +- static/api/sdk 模式切换。 +- SGS 坐标归一化。 +- 楼层 ID、label、order、floorCode 一致性。 +- POI floorId 绑定。 +- 展厅/space/poi/guide stop 关系。 +- route graph / nav data readiness gate。 + +重点文件: + +```text +src/config/dataSource.ts +src/data/providers +src/data/adapters +src/repositories/GuideRepository.ts +src/repositories/GuideModelRepository.ts +src/domain/museum.ts +src/domain/guideModel.ts +``` + +### 2.5 UX Agent:移动端用户流与遮挡专家 + +关注范围: + +- 手机 viewport 下顶部/底部/楼层控件是否可点击。 +- 3D canvas 是否遮挡 search、floor switcher、POI card、tab、audio player。 +- 返回、关闭、取消、重试是否形成闭环。 +- 搜索、定位、讲解、查看位置之间是否有死路。 +- 加载/错误/空状态是否可理解。 + +### 2.6 QA Agent:测试矩阵与执行记录专家 + +职责: + +- 把 Guide / Explain / Data / UX 结论转换成测试矩阵。 +- 明确每条测试:前置条件、操作步骤、期望结果、证据类型。 +- 区分源码审核、单元测试、H5 smoke、人工复核。 +- 记录命令输出。 + +### 2.7 Risk Agent:风险与反误导专家 + +职责: + +- 检查是否误称真实导航。 +- 检查是否使用 placeholder 音频。 +- 检查是否混用 legacy demo 数据。 +- 检查是否绕过数据层。 +- 检查是否把 SDK/API/static 数据混在一起。 +- 检查是否做了无关重构或破坏 H5 边界。 + +--- + +## 3. 导览 guide 用户测试闭环 + +### 3.1 导览入口闭环 + +测试目标:用户从首页进入馆内导览后,能理解当前状态。 + +检查点: + +1. 首页是否展示 `馆内` 入口。 +2. 点击后是否进入 guide 业务而不是 explain。 +3. 初始状态是否清楚表达是馆外、全馆、单层还是多层。 +4. 如果进入室内 3D,是否有加载状态。 +5. 模型加载失败是否有错误和重试。 + +预期结果: + +```text +用户能进入馆内 3D/位置预览体验,且不会看到“已开始导航”等误导文案。 +``` + +### 3.2 楼层切换闭环 + +测试目标:楼层切换后模型、POI 和 UI 楼层状态一致。 + +检查点: + +1. 楼层列表只展示室内可导览楼层。 +2. 楼层顺序符合 B2/B1/1F/2F/3F 等语义。 +3. 点击某一楼层后,UI 显示 loading 或状态变化。 +4. 模型切换到目标楼层。 +5. POI 只展示目标楼层点位。 +6. 切换失败时保留旧状态或给出明确错误。 +7. 父组件 active floor 与 ThreeMap rendered floor 不应长期不一致。 + +建议测试示例: + +```text +Given 当前在 1F +When 点击 2F +Then 楼层按钮高亮 2F +And ThreeMap 渲染 2F 模型 +And POI 列表只包含 floorId=2F/L2 对应点位 +And 不出现 B1/1F POI +``` + +### 3.3 POI 展示与位置预览闭环 + +测试目标:用户点击 POI 后能看懂“这是什么、在哪层、如何查看位置”。 + +检查点: + +1. POI 有稳定 id、name、category、floorId。 +2. POI 坐标可用于渲染。 +3. SGS 模式下 `position.y` 不应误作单层 marker 高度。 +4. 点击 POI 后弹出卡片。 +5. 卡片展示名称、楼层、类型或展厅信息。 +6. 展厅类 POI 可进入“查看展厅”或“相关讲解”。 +7. 位置预览不应宣称真实导航。 + +建议测试示例: + +```text +Given 当前楼层有展厅 POI +When 点击该 POI +Then POI card 展示展厅名和所在楼层 +And 相机聚焦到该 POI +And 文案使用“查看位置/查看展厅/相关讲解” +And 不出现“开始导航/到达引导” +``` + +### 3.4 route readiness 闭环 + +测试目标:没有 route graph/nav data 时,路线能力被正确阻断。 + +检查点: + +1. `NAV_ROUTE_GRAPH_READY` 为 false 时,不应展示已可用导航。 +2. route panel 或按钮应显示未开放、位置预览、不可用等状态。 +3. 测试中不能断言路线规划成功。 +4. 如 SDK/API 返回路线失败,应有错误处理。 + +--- + +## 4. 讲解 explain 用户测试闭环 + +### 4.1 讲解入口闭环 + +测试目标:用户能从首页进入讲解业务,且不会污染导览状态。 + +检查点: + +1. 顶部或首页入口使用 `讲解`。 +2. 点击后加载讲解列表。 +3. 返回 `馆内` 时,导览基本状态不异常丢失。 +4. 讲解列表空状态或加载失败有提示。 + +### 4.2 讲解内容与音频状态闭环 + +测试目标:讲解项的内容、音频和 unavailable 状态可信。 + +检查点: + +1. 讲解项有稳定 id、标题、所属展厅/展品。 +2. 音频 URL 来自真实媒体数据。 +3. 缺失音频时显示 unavailable,不自动使用 placeholder。 +4. 播放、暂停、关闭不泄漏状态。 +5. 页面返回后 player 状态符合产品预期。 + +### 4.3 讲解查看位置闭环 + +测试目标:用户从讲解项能回到对应导览位置预览。 + +检查点: + +1. 讲解项具备 `poiId`、`floorId`、`hallId` 或 `exhibitId` 中至少一种可解析关系。 +2. 点击“查看位置”后切回 guide。 +3. guide 切到目标楼层。 +4. 目标 POI 被聚焦或显示 preview card。 +5. 如缺少位置关系,应提示无法定位,不应假装成功。 + +建议测试示例: + +```text +Given 一个讲解项绑定 poiId 和 floorId +When 点击“查看位置” +Then 当前 tab 切换到馆内 +And active floor 等于讲解项 floorId +And target focus 指向对应 poiId +And 显示位置预览卡片 +``` + +--- + +## 5. 数据与状态核对规则 + +### 5.1 楼层数据 + +必须核对: + +- `floorId` 是否稳定。 +- `label` 是否仅用于显示。 +- `order/ordinal` 是否用于排序。 +- 外立面、馆外、建筑外观是否被过滤。 +- API / static / SDK 模式下楼层语义是否一致。 + +### 5.2 POI 数据 + +必须核对: + +- POI 必须有 `id`、`name`、`floorId`、`category`。 +- 渲染 POI 必须有可用 display coordinate。 +- 缺坐标 POI 不应进入 3D marker 渲染。 +- 展厅 space center 不应直接等同 route destination。 +- route destination 应未来使用 entrance / route node。 + +### 5.3 SGS 坐标归一化 + +SGS 坐标进入 ThreeMap 前必须明确: + +```text +source.x -> render x +source.z -> render z +source.y -> 原始高度/绝对高程,不直接作为单层 marker y +``` + +测试断言建议: + +```text +Given SGS position = { x: 10, y: 99, z: 20 } +When 转换成 ThreeMap render POI +Then render position 应使用水平坐标 10/20 +And marker 高度不应等于 99 +``` + +### 5.4 route readiness + +必须核对: + +- route graph/nav data 未 ready 时,route planning 不应被视为通过。 +- 所有测试报告中应使用“位置预览”或“路线未开放”。 +- 不把 SDK mode 当作真实导航 ready 的证据。 + +--- + +## 6. H5 smoke 用户测试建议 + +当 Codex 需要执行浏览器级 H5 smoke 时,按以下顺序: + +1. 启动 H5 dev server: + +```powershell +pnpm dev:h5 +``` + +2. 在移动 viewport 打开本地 URL。 +3. 执行以下用户流: + +### 6.1 导览 smoke + +- 打开首页。 +- 点击 `馆内`。 +- 等待 3D 加载。 +- 切换全馆/单层/多层。 +- 点击 1F、2F、B1 等楼层。 +- 点击一个 POI。 +- 检查卡片是否出现。 +- 检查楼层控件、搜索、卡片、底部导航是否仍可点击。 + +### 6.2 讲解 smoke + +- 点击 `讲解`。 +- 查看讲解列表。 +- 点击讲解项。 +- 尝试播放音频。 +- 如无音频,检查 unavailable 状态。 +- 点击“查看位置”。 +- 检查是否回到馆内并显示目标位置预览。 + +### 6.3 异常 smoke + +- 模拟模型加载失败。 +- 模拟 POI 空列表。 +- 模拟音频缺失。 +- 模拟 route unavailable。 +- 检查是否有重试、返回、关闭或明确提示。 + +--- + +## 7. 验收标准 + +### 7.1 通过标准 + +一次 Codex 测试任务可判定为通过,必须满足: + +1. 明确列出测试范围。 +2. 明确说明 guide 只是位置预览,不是认证导航。 +3. 至少覆盖 guide 楼层切换和 POI 点击闭环。 +4. 至少覆盖 explain 查看位置或音频 unavailable 闭环。 +5. 输出测试矩阵。 +6. 输出命令结果或说明为何未执行。 +7. 标记 source-only、unit、H5 smoke、manual 的证据类型。 +8. 不做无关重构。 + +### 7.2 阻塞问题 + +以下问题应标为 P1: + +- 切换楼层后模型和 POI 楼层不一致。 +- 点击 POI 后卡片展示错误楼层或错误对象。 +- route 未 ready 但 UI 宣称可导航。 +- 讲解项缺真实音频却显示可播放。 +- 点击“查看位置”后进入死路或白屏。 +- 3D canvas 遮挡关键控件。 + +### 7.3 P2 问题 + +- 楼层 label 不清晰。 +- POI 分类图标不一致。 +- 搜索结果没有按楼层表达。 +- 讲解返回后状态丢失。 +- 错误提示不够明确。 + +### 7.4 P3 问题 + +- 视觉 polish。 +- 动效不顺。 +- 非核心文案优化。 +- 后续 E2E 覆盖建议。 + +--- + +## 8. 可直接复制给 Codex 的 XML Prompt + +> 使用方式:将以下 prompt 复制给 Codex。若希望 Codex 只出方案不改代码,将 `` 改为 `read_only`。若希望 Codex 落地最小测试示例,将 `` 改为 `minimal_test_examples`。 + +```xml + +You are operating in the Shenzhen Natural Museum frontend-miniapp repository: +E:\MyWork\深圳国际艺术馆\museum-guide\museum-guide-v4.0\frontend-miniapp + +Create a professional user-testing operation plan and, if allowed by write_policy, minimal test examples for closing the guide/explain business logic loops. +Use a Lead Agent + specialist agents mental model. Do not spawn real external agents unless the runtime supports it; simulate the team by producing separate role findings. + + + +read_only + + + +- Target platform is H5 only. Do not test mp-weixin unless explicitly requested. +- The guide business currently supports indoor 3D display plus POI/location preview. +- Do not claim certified indoor navigation, turn-by-turn guidance, arrival guidance, or route planning unless route_graph/nav_data and runtime behavior are verified. +- Route-related UI should be tested as route readiness / unavailable / location preview. +- The explain business is content/audio explanation only when real media exists. +- Missing audio/transcript must show unavailable state. Do not treat example.com or placeholder media as working capability. +- Keep data and presentation decoupled: Provider -> Adapter -> Repository -> UseCase -> ViewModel/Component. +- Do not reintroduce retired legacy nav assets service. +- Do not delete historical demo data or perform broad cleanup. + + + + +Own scope, sequencing, final synthesis, risk ranking, and the final test matrix. + + + +Inspect guide entry, indoor/outdoor switch, full-building/multi-floor/single-floor switching, floor state, POI display, POI click, target focus, location preview card, and route readiness gates. +Primary files: +- src/pages/index/index.vue +- src/components/navigation/GuideMapShell.vue +- src/components/map/ThreeMap.vue +- src/domain/guideFloor.ts +- src/domain/guideReadiness.ts +- src/domain/guideModel.ts +- src/usecases/guideUseCase.ts + + + +Inspect explain tab/list/detail, audio play/unavailable state, guide-location linkage, top-tab preservation, and missing media behavior. +Primary files: +- src/components/explain +- src/components/audio +- src/pages/index/index.vue +- src/pages/exhibit +- src/pages/hall +- src/repositories +- src/usecases + + + +Inspect provider/adapter/repository/usecase contracts, data-source mode handling, SGS coordinate normalization, floorId/label/order consistency, POI floor binding, and route readiness. +Primary files: +- src/config/dataSource.ts +- src/data/providers +- src/data/adapters +- src/repositories/GuideRepository.ts +- src/repositories/GuideModelRepository.ts +- src/domain/museum.ts +- src/domain/guideModel.ts + + + +Inspect mobile H5 user flows, canvas/overlay hit areas, loading/error/empty states, return/close/cancel/retry paths, and dead-end risks. + + + +Convert findings into a test matrix with preconditions, steps, expected result, evidence type, and priority. + + + +Check for false navigation claims, placeholder audio, legacy demo pollution, direct raw-data coupling, static/api/sdk mixing, and unrelated refactors. + + + + + +- Home -> guide/馆内 entry. +- Indoor 3D loading state. +- Overview/multi-floor/single-floor switching. +- Floor switch: requested floor, rendered model, active UI floor, loaded POIs. +- POI click -> focus -> preview card. +- Route graph not ready -> location preview/unavailable, not real navigation. + + + +- Home -> explain/讲解 entry. +- Explain list load/empty/error. +- Explain item detail. +- Audio play/pause/close or unavailable if missing real media. +- Explain item 查看位置 -> guide tab -> target floor -> target POI/location preview. +- Back/top-tab state preservation. + + + +- Floor filtering and sorting. +- POI floor binding. +- SGS position normalization: source x/z become horizontal render coordinates; source y must not be misused as single-floor marker height. +- Missing position POIs do not enter render marker set. +- Route readiness stays false unless graph/nav data are loaded and verified. + + + + +1. Inspect package.json and identify available scripts. +2. Inspect the primary files listed by each specialist agent. +3. Build a source-based test matrix first. +4. If write_policy is minimal_test_examples, add the smallest test framework or test scripts needed, preferably Vitest, without breaking H5 build. +5. Prefer domain/adapter/repository/usecase tests before component or E2E tests. +6. If H5 smoke is requested and feasible, run pnpm dev:h5 and browser-test mobile viewport flows. +7. Run the smallest meaningful verification commands available, such as pnpm type-check and pnpm lint. Do not run build:h5 unless explicitly approved because it writes dist. + + + +- Keep changes narrow. +- Do not perform broad refactors. +- Do not delete files. +- Do not modify unrelated UI styling. +- Do not install dependencies unless the plan explains why and the user allowed write-capable execution. +- If a test requires browser automation that is unavailable, mark it manual/H5-smoke-pending rather than inventing results. + + + +- Every finding must cite file path and line number when source-based. +- Every browser finding must include the tested URL, viewport, steps, and observed result. +- Distinguish confirmed behavior from hypothesis. +- If data is missing, say data insufficient instead of guessing. + + + +Return in this order: +1. Codex setup assumptions and scripts discovered. +2. Agent-by-agent findings. +3. Business-loop test matrix. +4. Recommended minimal automated tests. +5. H5 smoke/manual test script. +6. P1/P2/P3 risks. +7. Commands run and exact results. +8. Files changed, or state "no files changed". +9. Follow-up recommendations. + + + +The task is complete only when: +- Guide loop and explain loop are both covered. +- Route readiness is not misrepresented as real navigation. +- Audio placeholder risk is checked. +- Floor/POI/SGS coordinate risks are checked. +- Output includes an actionable test matrix. + +``` + +--- + +## 9. 推荐 Codex 分阶段执行命令 + +### 9.1 只做方案,不改代码 + +```text +Use the XML prompt in docs/QA/codex-user-testing-agents-guide-2026-06-30.md with write_policy=read_only. Produce a professional Lead+specialist-agents user-testing operation plan for the guide/explain H5 business loops. Do not modify files. +``` + +### 9.2 落地最小测试示例 + +```text +Use the XML prompt in docs/QA/codex-user-testing-agents-guide-2026-06-30.md with write_policy=minimal_test_examples. Add the smallest unit-test examples needed to cover guideFloor, route readiness, SGS coordinate normalization, POI floor binding, and explain 查看位置/unavailable audio behavior. Keep changes narrow and run pnpm type-check and pnpm lint. +``` + +### 9.3 做 H5 smoke 测试 + +```text +Use the XML prompt in docs/QA/codex-user-testing-agents-guide-2026-06-30.md with write_policy=read_only. Start or use the H5 dev server if available, test mobile viewport guide/explain loops, and return a browser-observed test report. Do not change code. +``` diff --git a/docs/QA/guide-exhibits-miniapp-data-source-diagnosis-2026-07-02.md b/docs/QA/guide-exhibits-miniapp-data-source-diagnosis-2026-07-02.md new file mode 100644 index 0000000..8847249 --- /dev/null +++ b/docs/QA/guide-exhibits-miniapp-data-source-diagnosis-2026-07-02.md @@ -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 接口只作为地图落点和位置预览的辅助数据。 diff --git a/docs/QA/h5-business-flow-audit-2026-06-09.md b/docs/QA/h5-business-flow-audit-2026-06-09.md index c2ac20e..b838d37 100644 --- a/docs/QA/h5-business-flow-audit-2026-06-09.md +++ b/docs/QA/h5-business-flow-audit-2026-06-09.md @@ -71,25 +71,25 @@ Production audit:48/100,Blocked。H5 与微信小程序都能构建成功, 建议:去掉 `example.com` 兜底;无音频时显示“暂无讲解”;有音频时使用真实 `audioUrl`,并在列表、详情、底部播放器之间共享播放状态。 -### P0:室内 3D/路线导航仍是演示态 +### P0:馆内 3D/路线导航仍是演示态 证据: -- `src/components/navigation/GuideMapShell.vue:5` 至 `src/components/navigation/GuideMapShell.vue:8` 室内地图分支是 `/static/images/guide-indoor-3d-bg.png` 静态图。 -- `src/components/navigation/GuideMapShell.vue:11` 室外分支已使用原 `TencentMap`,但室内没有使用 `ThreeMap`。 +- `src/components/navigation/GuideMapShell.vue:5` 至 `src/components/navigation/GuideMapShell.vue:8` 馆内地图分支是 `/static/images/guide-indoor-3d-bg.png` 静态图。 +- `src/components/navigation/GuideMapShell.vue:11` 馆外分支已使用原 `TencentMap`,但馆内没有使用 `ThreeMap`。 - `src/pages/route/detail.vue:156` 路线固定指向“1F 南侧卫生间”。 - `src/pages/route/detail.vue:358` 开始导航只是把 `navigationState` 改成 `navigating`。 -- `src/pages/route/detail.vue:377` 返回室内继续也只是改状态。 +- `src/pages/route/detail.vue:377` 返回馆内继续也只是改状态。 影响:用户从设施详情点击“开始导航”后,看起来进入了路线页,但没有真实路径、定位、楼层切换、到达判定,也没有和 3D 模型或地图 POI 绑定。 -建议:将路线页目标、起点、路径段、楼层、地图渲染层统一绑定;室内模式接入 `ThreeMap` 或明确降级为 2D 平面图,不能用静态设计图冒充可导航地图。 +建议:将路线页目标、起点、路径段、楼层、地图渲染层统一绑定;馆内模式接入 `ThreeMap` 或明确降级为 2D 平面图,不能用静态设计图冒充可导航地图。 -### P1:室外 TencentMap 恢复了,但 marker 交互不闭环 +### P1:馆外 TencentMap 恢复了,但 marker 交互不闭环 证据: -- `src/components/navigation/GuideMapShell.vue:11` 当前室外地图使用 `TencentMap`,这是正确方向。 +- `src/components/navigation/GuideMapShell.vue:11` 当前馆外地图使用 `TencentMap`,这是正确方向。 - `src/components/map/TencentMap.vue:239` marker 点击先 `emit('markerClick')`。 - `src/components/map/TencentMap.vue:244` 至 `src/components/map/TencentMap.vue:250` 同一个点击又立即 `navigateTo`。 - `src/pages/index/index.vue:232` 至 `src/pages/index/index.vue:282` 首页准备了 marker 详情、导航、讲解、收藏等处理函数,但 `GuideMapShell` 没有把 `TencentMap` 的 marker 事件继续暴露给首页。 @@ -121,7 +121,7 @@ Production audit:48/100,Blocked。H5 与微信小程序都能构建成功, - `src/pages/index/index.vue:127` 至 `src/pages/index/index.vue:128` 首页只有 `导览` 和 `讲解` 两个内容 tab。 - `src/pages/index/index.vue:81` 讲解分支渲染 `ExplainList`。 -结论:原有“讲解”能力没有完全消失,但它不是独立页面,而是被内嵌为首页 tab。当前点击“讲解”不会进入室内模型;它进入 `ExplainList`。真正的问题是讲解 tab 内的数据、音频、详情页没有连成闭环。 +结论:原有“讲解”能力没有完全消失,但它不是独立页面,而是被内嵌为首页 tab。当前点击“讲解”不会进入馆内模型;它进入 `ExplainList`。真正的问题是讲解 tab 内的数据、音频、详情页没有连成闭环。 建议:如果产品需要“讲解”作为一级业务,应明确它是首页 tab 还是独立页面;然后补齐分享/返回/深链/播放状态保存规则。 @@ -142,19 +142,19 @@ Production audit:48/100,Blocked。H5 与微信小程序都能构建成功, | 流程 | 当前状态 | 断点 | 闭环建议 | | --- | --- | --- | --- | -| 导览首页 -> 室外地图 -> 点 marker -> 详情/导航/讲解 | 未闭环 | marker 组件内部直接跳转,首页 sheet 逻辑未接上;ID 与详情数据不一致。 | marker 只发事件,页面统一打开 POI sheet;sheet 操作分别进入详情、路线、音频。 | -| 导览首页 -> 室内 3D -> 选 POI -> 路线 | 未闭环 | 室内是静态图,未接 `ThreeMap`、POI、路径。 | 接入真实室内地图/3D 场景;POI ID 与路线目标一致。 | +| 导览首页 -> 馆外地图 -> 点 marker -> 详情/导航/讲解 | 未闭环 | marker 组件内部直接跳转,首页 sheet 逻辑未接上;ID 与详情数据不一致。 | marker 只发事件,页面统一打开 POI sheet;sheet 操作分别进入详情、路线、音频。 | +| 导览首页 -> 馆内 3D -> 选 POI -> 路线 | 未闭环 | 馆内是静态图,未接 `ThreeMap`、POI、路径。 | 接入真实馆内地图/3D 场景;POI ID 与路线目标一致。 | | 搜索关键词 -> 结果 -> 详情 -> 导航 | 未闭环 | 搜索页不使用关键词做综合搜索;详情页不加载 ID;详情导航返回上一页。 | 搜索接统一数据源;详情按 ID 渲染;导航按钮带目标进入路线页。 | | 讲解 -> 筛选/搜索 -> 展品 -> 播放音频 -> 结束/返回 | 未闭环 | `activeFilter` 对数据组织影响弱;音频 URL 为空却标记可播放;详情页播放是假状态。 | 按展厅/主题真实分组;无音频禁用播放;播放器跨列表/详情共享状态。 | | 设施详情 -> 选择起点 -> 开始导航 -> 到达 | 未闭环 | 选择起点没有输入结果;路线页固定目标;开始导航只切状态。 | 起点选择写入 route query/store;路线页按起终点生成路径;提供到达/结束态。 | -| 路线中 -> 查看室外地图 -> 返回室内继续 | 部分演示 | 暂停/恢复只改 `navigationState`,未保留地图层、楼层、进度。 | 保存 route session;室外/室内切换只换展示层,不丢路径和当前步骤。 | +| 路线中 -> 查看馆外地图 -> 返回馆内继续 | 部分演示 | 暂停/恢复只改 `navigationState`,未保留地图层、楼层、进度。 | 保存 route session;馆外/馆内切换只换展示层,不丢路径和当前步骤。 | ## 建议补充的 E2E 用例 按照 `playwright-e2e-tester` skill,本项目至少需要以下端到端用例,作为业务闭环验收: -1. 点击首页“讲解”后,应显示讲解列表,不应加载室内 3D/静态室内地图。 -2. 首页“导览”室外模式应渲染 `TencentMap` 容器,并能点击 marker 打开 POI 操作面板。 +1. 点击首页“讲解”后,应显示讲解列表,不应加载馆内 3D/静态馆内地图。 +2. 首页“导览”馆外模式应渲染 `TencentMap` 容器,并能点击 marker 打开 POI 操作面板。 3. 搜索“卫生间”应只展示匹配设施;点击结果进入对应设施详情;点击“开始导航”进入路线页并保留目标 ID。 4. 讲解列表中 `audioUrl` 为空的展品应显示“暂无讲解”或禁用播放,不应请求 `example.com`。 5. 点击自然馆展品讲解,应进入同一个展品详情,并能播放同一个音频对象。 @@ -167,7 +167,7 @@ Production audit:48/100,Blocked。H5 与微信小程序都能构建成功, 2. 让详情页、搜索页、讲解页全部接入 `dataLoader`/`searchAll`,消除硬编码默认对象。 3. 移除 `https://example.com/audio.mp3`,补真实音频状态和无音频状态。 4. 拆清 `TencentMap` 责任:地图只发事件,页面负责业务动作。 -5. 路线页接入真实目标、起点和路径状态;室内地图不要再用静态设计图冒充导航。 +5. 路线页接入真实目标、起点和路径状态;馆内地图不要再用静态设计图冒充导航。 6. 修复 `vue-tsc` 工具链版本,让类型检查成为有效质量门。 7. 增加 Playwright 冒烟用例覆盖“导览、讲解、搜索、详情、路线”五条主链路。 diff --git a/docs/QA/h5-guide-explain-regression-2026-07-01.md b/docs/QA/h5-guide-explain-regression-2026-07-01.md new file mode 100644 index 0000000..fc023da --- /dev/null +++ b/docs/QA/h5-guide-explain-regression-2026-07-01.md @@ -0,0 +1,469 @@ +# H5 导览/讲解业务闭环回归记录 + +> 日期:2026-07-01 +> 项目:深圳自然博物馆 `frontend-miniapp` +> 范围:H5 guide / explain 业务闭环 +> 验证来源:Codex 源码审查 + H5 browser smoke + 命令行质量门禁 +> 测试目标:验证三轮修复后,导览位置预览、讲解音频状态、馆内楼层控件交互是否闭环。 + +--- + +## 1. 总体结论 + +本轮 H5 guide / explain 回归结论:**通过,发布前条件通过**。 + +已验证通过的闭环: + +| 编号 | 闭环 | 结果 | 说明 | +|---|---|---|---| +| P1-1 | 讲解 -> guide 位置预览 | 通过 | 人类厅、展品详情均可进入位置预览 | +| P1/P2-2 | 音频可播放/不可用状态 | 通过 | 大猩猩不再误标音频讲解,播放失败后降级图文讲解 | +| P2-3 | 馆内楼层控件遮挡/误触 | 通过 | 馆内入口不再自动打开终点选择,楼层按钮可正常点击 | +| 文案边界 | 不越权宣称真实导航 | 通过 | 仍使用位置预览、位置关系、暂不提供正式室内导航等文案 | +| 质量门禁 | `pnpm type-check` / `pnpm lint` | 通过 | lint warnings 已清理 | + +当前仍保留一个非阻塞发布前复测建议:在真实约 390px 宽度移动设备或 WebView 中,再做一次楼层按钮物理点击区 spot check。 + +--- + +## 2. 修复背景 + +Codex H5 用户测试最初发现 3 类问题: + +### 2.1 P1:讲解到位置预览闭环未闭合 + +现象: + +- 人类厅“查看展厅位置”未跳转到 guide / route preview。 +- 展品详情“查看位置”未跳转到位置预览。 +- toast: + - `该展厅暂无三维位置数据` + - `该讲解暂无所属展厅位置数据` + +根因: + +- explain 内容域 `hallId` / `poiId` 与 guide 导览域 POI ID 存在命名空间差异。 +- 页面层尝试直接用 content hall id 查 guide POI,导致运行时找不到位置。 +- static bridge 中已有 `hallId -> nav POI` 映射,但原点击链路没有稳定走 use case / repository 解析。 + +### 2.2 P1/P2:音频可播放标记与真实播放不一致 + +现象: + +- 人类厅列表中“大猩猩”显示 `22秒 · 音频讲解`。 +- 详情显示音频面板和 `23秒`。 +- 点击后没有真实可播放 audio src。 +- toast:`音频加载失败,当前提供图文讲解。` +- console:`详情音频不可播放`。 + +根因: + +- 列表/详情展示层依据 `audioAvailable`、`hasAudio`、duration 等 metadata 判断“可播放”。 +- 播放层需要真实 `playUrl` 或 H5 可加载 URL。 +- 两套 truth source 不一致,导致 UI 宣称可播但实际不可播。 + +### 2.3 P2:馆内楼层控件遮挡/误触 + +现象: + +- 点击“馆内”后自动打开 route/guide panel。 +- 点击左侧 `B1` 区域打开“选择终点”,而不是切换楼层。 +- 面板收起后仍遮挡 `B1/B2` 区域。 +- 点击 `2F` 后进入多层加载状态,active floor 表达不稳定。 + +根因: + +- “馆内”入口调用 route planner 打开逻辑。 +- `RoutePlannerPanel` / `RoutePointPicker` z-index 高于 floor switcher,且拦截 touch。 +- floor header sticky 造成点击区域覆盖。 +- multi 模式下 active floor 样式被抑制,用户误以为没有当前楼层。 + +--- + +## 3. 变更范围 + +本轮修复涉及文件如下: + +```text +src/usecases/guideUseCase.ts +src/repositories/GuideRepository.ts +src/data/adapters/backendExplainDataAdapter.ts +src/pages/hall/detail.vue +src/pages/exhibit/detail.vue +src/view-models/explainViewModels.ts +src/usecases/explainUseCase.ts +src/pages/index/index.vue +src/components/navigation/GuideMapShell.vue +src/data/providers/backendExplainContentProvider.ts +``` + +### 3.1 位置预览闭环修复 + +涉及: + +```text +src/usecases/guideUseCase.ts +src/repositories/GuideRepository.ts +src/data/adapters/backendExplainDataAdapter.ts +src/pages/hall/detail.vue +src/pages/exhibit/detail.vue +``` + +主要变化: + +- 新增/集中 explain content -> guide preview target 解析逻辑。 +- 页面不再重复做 direct id / name guessing。 +- 解析顺序变为: + +```text +direct POI + -> bridged location.poiId + -> hall id as hall-like POI + -> hall name fallback + -> target name fallback + -> unavailable toast +``` + +- SDK mode 中允许 static bridge POI ID 通过 SDK POI name fallback 或 static fallback 转换成可预览目标。 +- 展品详情若无 exhibit POI,可 fallback 到所属展厅位置。 +- 未引入正式导航文案,仍跳转 `route/detail?...&state=preview`。 + +### 3.2 音频状态修复 + +涉及: + +```text +src/view-models/explainViewModels.ts +src/data/adapters/backendExplainDataAdapter.ts +src/usecases/explainUseCase.ts +src/pages/exhibit/detail.vue +``` + +主要变化: + +- 列表/详情 playable 状态收紧为“存在真实非空 H5 media URL / playUrl”。 +- duration、`audioAvailable`、`hasAudio`、`audioStatus` 不再单独导致“音频讲解”可播放展示。 +- metadata 保留为描述性信息,不再等同播放能力。 +- H5 播放失败后详情页降级为图文讲解 / 音频暂不可用。 +- 未添加 fake / placeholder audio。 + +### 3.3 楼层控件遮挡修复 + +涉及: + +```text +src/pages/index/index.vue +src/components/navigation/GuideMapShell.vue +``` + +主要变化: + +- “馆内”入口进入室内 3D 单层预览,不再自动打开 route planner。 +- route planner 显示时隐藏 floor switcher,避免“看得见但点不到”。 +- floor header 从 sticky 改为 relative,避免覆盖楼层按钮。 +- multi 模式 header 显示 `多层`。 +- active floor 样式在 multi/single 状态中保持可见或有明确模式提示。 + +### 3.4 lint 收尾清理 + +涉及: + +```text +src/data/providers/backendExplainContentProvider.ts +``` + +主要变化: + +- 删除未使用 type imports: + - `ExplainTrack` + - `MediaAsset` + - `MuseumHall` +- 仅 import 清理,无业务逻辑变更。 + +--- + +## 4. 命令验证 + +### 4.1 TypeScript 类型检查 + +命令: + +```powershell +pnpm type-check +``` + +实际输出摘要: + +```text +$ vue-tsc --noEmit +``` + +结果:通过,exit code `0`。 + +### 4.2 ESLint + +命令: + +```powershell +pnpm lint +``` + +实际输出摘要: + +```text +$ eslint "src/**/*.{ts,vue}" +``` + +结果:通过,exit code `0`。 + +最终收尾后,之前 `src/data/providers/backendExplainContentProvider.ts` 中 3 个 unused import warnings 已清理。 + +### 4.3 H5 build + +未运行: + +```powershell +pnpm build:h5 +``` + +原因:该命令会写入 `dist`,本轮以源码修复、type-check、lint 和 H5 smoke 为验证基线。 + +--- + +## 5. H5 smoke 验证记录 + +### 5.1 测试环境 + +URL: + +```text +http://localhost:5173/#/pages/index/index?tab=guide +``` + +视口: + +```text +请求 viewport:390x844 +实际 browser CSS viewport:728x912,DPR 1 +``` + +说明:Codex in-app browser 的 CSS viewport 与请求值不完全一致,因此发布前仍建议在真实约 390px 宽移动设备或 WebView 上 spot check。 + +--- + +## 6. Guide smoke 结果 + +### 6.1 操作步骤 + +1. 打开 guide tab。 +2. 点击首页“馆内”。 +3. 观察是否自动打开 route planner / 选择终点。 +4. 点击可见楼层控件: + - `B2` + - `B1` + - `1F` + - `2F` + +### 6.2 观察结果 + +- 点击“馆内”后未自动打开 route planner。 +- 未出现 `选择终点` 或 `选择起点`。 +- `B2`、`B1`、`1F`、`2F` 均可点击。 +- 点击楼层后对应楼层成为 active floor。 +- 点击楼层未触发 route panel / picker。 +- 未发现室内真实导航越权文案。 + +### 6.3 结论 + +Guide smoke:通过。 + +--- + +## 7. Explain -> Guide 位置预览 smoke 结果 + +### 7.1 人类厅查看展厅位置 + +步骤: + +```text +讲解 -> 人类厅 -> 查看展厅位置 +``` + +观察结果: + +- 页面跳转到: + +```text +/pages/route/detail?...&state=preview +``` + +- 页面标题: + +```text +位置预览 +``` + +- 页面内容包含: + +```text +位置预览:人类厅 +``` + +结论:通过。 + +### 7.2 大猩猩 / 展品详情查看位置 + +步骤: + +```text +讲解 -> 人类厅 -> 大猩猩 -> 查看位置 +``` + +观察结果: + +- 页面跳转到: + +```text +/pages/route/detail?...&state=preview +``` + +- 因大猩猩展品自身无稳定 exhibit POI,fallback 到所属展厅 preview。 +- 页面内容显示: + +```text +位置预览:展厅5人类厅 +``` + +- 未出现正式室内导航承诺。 + +结论:通过。 + +--- + +## 8. Audio smoke 结果 + +### 8.1 列表状态 + +步骤: + +```text +讲解 -> 人类厅 -> 查看大猩猩列表项 +``` + +观察结果: + +- 人类厅列表包含“大猩猩”。 +- 大猩猩显示为: + +```text +图文讲解 +``` + +- 人类厅列表中 `音频讲解` count 为 `0`。 + +结论:列表不再误标可播放音频,通过。 + +### 8.2 详情状态与播放失败降级 + +步骤: + +```text +讲解 -> 人类厅 -> 大猩猩 -> 点击音频面板 +``` + +观察结果: + +- 详情初始可解析到 direct URL,因此显示 `23秒` 音频面板。 +- 点击后 H5 播放失败。 +- 页面降级为: + +```text +图文讲解 +音频加载失败,当前提供图文讲解。 +``` + +- `23秒` 播放面板消失。 + +结论:播放失败后状态如实降级,通过。 + +--- + +## 9. 室内导航话术审计 + +本轮测试确认,guide / route preview 相关文案仍保持位置预览和 readiness 边界,没有宣称正式室内导航。 + +已观察到的安全文案包括: + +```text +位置预览 · 查看位置关系 +当前可查看位置预览和位置关系,暂不提供正式室内导航 +位置关系不可用 +当前不作为正式室内导航 +``` + +说明:室外 Tencent 路线失败等文案不属于室内 certified-navigation 承诺范围。 + +结论:通过。 + +--- + +## 10. 剩余风险与后续建议 + +| 优先级 | 风险 | 状态 | 建议 | +|---|---|---|---| +| P1 | 三个已验证闭环出现阻塞回归 | 未发现 | 保持当前回归用例 | +| P2 | Codex in-app browser 实际 CSS viewport 与请求 390px 不一致 | 存在 | 发布前用真机或标准 Chrome mobile viewport spot check | +| P2 | SDK/static mixed POI fallback 掩盖后端 ID 规范缺口 | 存在 | 作为数据层迁移债记录,推动 SGS/API 提供稳定 hallId/poiId/floorId 映射 | +| P3 | preview 页个别文案略别扭 | 存在 | 后续文案 polish,例如 `无法定位,当前仅支持点位位置预览` | +| P3 | H5 build 未跑 | 有意跳过 | 如进入发布流程,再运行 `pnpm build:h5` | + +--- + +## 11. 发布前推荐复测清单 + +建议在真实约 390px 宽移动设备或 WebView 中执行: + +### Guide + +- 打开首页。 +- 点击 `馆内`。 +- 点击 `B2 / B1 / 1F / 2F`。 +- 确认不会打开 `选择终点`。 +- 确认 active floor 稳定。 + +### Explain -> Guide + +- `讲解 -> 人类厅 -> 查看展厅位置`。 +- 确认进入 `位置预览:人类厅`。 + +### Exhibit -> Guide + +- `讲解 -> 人类厅 -> 大猩猩 -> 查看位置`。 +- 确认进入 `位置预览:展厅5人类厅`。 + +### Audio + +- 人类厅列表查看“大猩猩”。 +- 确认列表不误标 `音频讲解`。 +- 打开详情并点击音频。 +- 如果音频加载失败,确认降级为图文讲解并显示失败提示。 + +--- + +## 12. 最终状态 + +当前本轮 Codex 修复与 H5 smoke 结果可归档为: + +```text +H5 guide/explain business-loop regression: PASS +Release readiness: CONDITIONAL PASS +Condition: real 390px mobile/WebView floor-control spot check before production release. +``` + +本轮不改变产品能力边界: + +```text +guide = 室内 3D 展示 + POI/位置预览 +explain = 内容/讲解 + 真实媒体可用时音频播放 +route = route graph/nav data 未验证前保持位置关系/位置预览,不作为正式室内导航 +``` diff --git a/docs/QA/indoor-guide-professional-standard-gap-analysis-2026-06-30.md b/docs/QA/indoor-guide-professional-standard-gap-analysis-2026-06-30.md new file mode 100644 index 0000000..cf79bff --- /dev/null +++ b/docs/QA/indoor-guide-professional-standard-gap-analysis-2026-06-30.md @@ -0,0 +1,792 @@ +# 室内导览楼层切换、点位展示与行业专业规范差异分析 + +> 日期:2026-06-30 +> 范围:深圳自然博物馆 `frontend-miniapp` H5 导览页 +> 证据类型:源码审核 + deep-research 外部资料核验 +> 说明:外部研究依据主要来自 OGC IMDF / IndoorGML、VA/WBDG 综合导视指南、Smithsonian 可访问展览设计指南、CMHR 博物馆 wayfinding 指南和博物馆移动导览案例。IMDF/IndoorGML 是数据模型标准,不直接规定 UI 视觉样式;本文将其作为室内导览数据与交互审计标尺。 + +--- + +## 1. 结论摘要 + +当前项目已经具备“移动 H5 室内 3D 位置预览”的基础能力: + +- 支持馆外/馆内入口切换。 +- 支持楼层按钮切换。 +- 支持建筑外观、单层、多层视图。 +- 支持 POI 标记、点击、聚焦与底部卡片。 +- 支持导览和讲解业务的部分联动。 + +但和行业内专业室内导览系统相比,当前实现仍有明显差距。核心差距不是单一 UI,而是**楼层上下文、POI 空间语义、坐标体系、数据 readiness 与导航能力边界**尚未完整专业化。 + +最关键的问题包括: + +1. **楼层切换更像 3D 模型切换,不是完整 floor context 切换。** +2. **POI 当前主要是单点渲染,缺少 display point / entrance point / route node / space geometry 的专业拆分。** +3. **SGS 数据源下存在坐标轴语义进入 ThreeMap 后未完全归一化的风险,楼层切换后容易出现模型与点位错位。** +4. **楼层信息展示主要是 `1F / 2F / B1` label,缺少楼层内容、数据状态、目标楼层、路线途经楼层等专业状态表达。** +5. **当前能力应定义为“室内 3D 展示 + 位置预览”,不能定义为专业可导航室内导览。** + +--- + +## 2. 当前项目实现概览 + +### 2.1 导览页入口 + +首页导览页通过 `GuideMapShell` 组织导览地图、楼层控件、模式状态、POI 卡片和路线面板。 + +证据: + +- `src/pages/index/index.vue:9` 使用 `GuideMapShell`。 +- `src/pages/index/index.vue:23-30` 传入 `indoorModelSource`、`guideFloors`、`indoorView`、`indoorLayerMode`、`activeGuideFloor`。 +- `src/pages/index/index.vue:48-53` 监听 `floor-change`、`indoor-view-change`、`poi-click`、`selection-clear`、`auto-switch`。 + +当前页面结构说明: + +```text +index.vue + └─ GuideMapShell + ├─ ThreeMap / TencentMap + ├─ 搜索入口 + ├─ 楼层切换控件 + ├─ 缩放/工具按钮 + ├─ POI 卡片 + └─ 路线/位置预览相关 UI +``` + +### 2.2 楼层切换现状 + +楼层控件位于 `GuideMapShell`: + +- `scroll-view` 展示楼层列表。 +- `floorItems` 来自 `props.floors` 过滤后的室内楼层。 +- 点击楼层后调用 `indoorRendererRef.value?.switchFloor?.(floorId)`。 +- 同时向父组件发送 `indoorViewChange`、`layerModeChange`、`floorChange`。 + +证据: + +- `src/components/navigation/GuideMapShell.vue:129-158`:楼层列表渲染。 +- `src/components/navigation/GuideMapShell.vue:526-535`:楼层点击处理。 + +`ThreeMap` 的楼层加载逻辑包括: + +- 根据 `floorId` 查找楼层模型。 +- 加载 GLB。 +- 应用楼层可见性。 +- 加载当前楼层 POI。 +- 适配相机。 + +证据: + +- `src/components/map/ThreeMap.vue:2612-2725`:`loadFloor()`。 +- `src/components/map/ThreeMap.vue:3768-3789`:`handleFloorChange()`。 + +### 2.3 POI 展示现状 + +当前 POI 展示已经有初步的专业化方向: + +- 按视图模式区分 POI 显示密度:`overview`、`multi`、`floor`。 +- 按相机距离做可见性分级:`tight`、`balanced`、`full`。 +- 按 POI 类别和选中状态计算优先级。 +- 点击后设置选中态、聚焦相机、弹出底部卡片。 + +证据: + +- `src/components/map/ThreeMap.vue:617-640`:`getPoiDisplayMode()` 与 `shouldShowPoiInCurrentMode()`。 +- `src/components/map/ThreeMap.vue:667-718`:POI 距离可见性、数量限制、优先级计算。 +- `src/components/map/ThreeMap.vue:3373-3451`:POI marker group 创建与加载。 +- `src/pages/index/index.vue:65-106`:POI 底部卡片。 + +### 2.4 楼层信息展示现状 + +当前楼层信息主要表现为楼层按钮 label: + +```vue +{{ floor.label }} +``` + +证据: + +- `src/components/navigation/GuideMapShell.vue:149-157` + +楼层排序和室内楼层判定由 `guideFloor.ts` 处理: + +- 支持 `L1`、`L-1`、`1F`、`B1`、`L1.5` 等格式。 +- 可过滤 `EXTERIOR`、`OUTDOOR`、外立面、馆外等非室内楼层。 +- 可按语义楼层从高到低排序。 + +证据: + +- `src/domain/guideFloor.ts:18-22` +- `src/domain/guideFloor.ts:68-82` +- `src/domain/guideFloor.ts:98-114` + +--- + +## 3. 当前项目与行业专业规范的差异 + +## 3.1 楼层切换差异 + +### 当前项目特点 + +当前楼层切换偏向“模型视图切换”: + +```text +点击楼层按钮 + -> ThreeMap.switchFloor(floorId) + -> loadFloor(floorId) + -> 加载/复用 GLB + -> 加载该 floorId 的 POI + -> fit camera +``` + +问题在于,专业室内地图的楼层切换不只是切换模型,而是切换完整 floor context。 + +### 行业专业规范 + +专业室内导览中的一次楼层切换,应同步更新以下上下文: + +1. 当前楼层 ID。 +2. 当前楼层 label/name/order/elevation。 +3. 当前楼层底图或 3D 模型。 +4. 当前楼层空间面/展厅区域。 +5. 当前楼层 POI。 +6. 当前楼层路径网络图层。 +7. 当前楼层垂直交通点。 +8. 当前用户定位状态。 +9. 当前目标点是否在本层。 +10. 当前路线是否途经本层。 +11. 当前楼层数据质量与可导航状态。 + +专业表述: + +```text +Floor Switch = Floor Context Switch +不是只换模型,而是同步切换模型、空间、POI、路径、定位、状态和任务上下文。 +``` + +### 当前差距 + +| 维度 | 当前项目 | 专业规范 | 风险 | +|---|---|---|---| +| 切换对象 | 主要是模型 + POI | 完整 floor context | 模型、POI、路径、定位上下文可能不同步 | +| 切换状态 | `activeGuideFloor` 近似单状态 | requested / loading / rendered / failed 分离 | UI 可能显示已切换,但模型或 POI 仍未加载成功 | +| 模型匹配 | 依赖 `floorId`、`label`、节点名、fallback | floor-model binding 明确 | 节点名不规范时误显示非本层构件 | +| 多楼层 | 有 multi 视觉展示 | 有跨层路线、垂直交通、换乘提示 | 只能看多层,不能证明可导航 | +| 失败处理 | 模型 loadError | 模型、POI、空间面、路网分别诊断 | 用户不知道失败发生在哪一层 | + +### 具体源码风险 + +`GuideMapShell` 点击楼层后立即 emit 父组件状态: + +- `emit('indoorViewChange', 'floor')` +- `emit('layerModeChange', 'single')` +- `emit('floorChange', floor.label)` + +证据:`src/components/navigation/GuideMapShell.vue:526-535` + +但 `ThreeMap.switchFloor()` 是异步加载模型。如果模型加载失败,父组件已经切换了楼层状态,可能出现: + +```text +UI 显示已切换到 2F +但 3D 场景仍停留在旧楼层或进入错误状态 +``` + +专业做法应拆分: + +```ts +requestedFloorId +loadingFloorId +renderedFloorId +failedFloorId +``` + +只有 ThreeMap 成功渲染目标楼层后,父级才更新“当前已渲染楼层”。 + +--- + +## 3.2 POI 展示差异 + +### 当前项目特点 + +当前 POI 主要以 `GuideRenderPoi.positionGltf` 作为渲染点,使用 sprite/label 展示。 + +证据: + +- `src/domain/guideModel.ts:6-18`:`GuideRenderPoi` 只有 `positionGltf`,没有 display/entrance/route node 的拆分。 +- `src/components/map/ThreeMap.vue:3384-3390`:POI marker 直接读取 `[x, y, z]` 并设置 sprite 坐标。 + +### 行业专业规范 + +专业室内导览中,POI 通常不是“一个点”,而是一个空间对象。至少应拆分: + +| 字段 | 用途 | +|---|---| +| `displayPoint` | 图标和标签展示点 | +| `anchorPoint` | 相机聚焦点 | +| `entrancePoint` | 用户可到达入口点 | +| `routeNodeId` | 路网节点,用于路径规划 | +| `geometry` | 房间、展厅、空间区域边界 | +| `floorId` | 稳定楼层 ID | +| `sourceConfidence` | 坐标来源和可信度 | + +推荐领域模型: + +```ts +interface IndoorPoi { + id: string + name: string + categoryId: string + floorId: string + + displayPoint: [number, number, number] + anchorPoint?: [number, number, number] + entrancePoints?: Array<[number, number, number]> + routeNodeIds?: string[] + + geometry?: Polygon | MultiPolygon + unitId?: string + spaceId?: string + + priority: number + minZoom?: number + maxZoom?: number + labelPolicy: 'always' | 'selected' | 'adaptive' | 'hidden' + sourceConfidence: 'verified' | 'backend' | 'model-derived' | 'fallback' + dataStatus: 'ready' | 'partial' | 'unverified' +} +``` + +### 当前差距 + +| 维度 | 当前项目 | 专业规范 | 风险 | +|---|---|---|---| +| POI 坐标 | 单个 `positionGltf` | display / anchor / entrance / route node 分离 | 展厅中心点可能被误作路线终点 | +| POI 区域 | 以点为主 | 点 + 面 + 入口 + 路网节点 | 展厅语义表达不足 | +| 坐标高度 | 直接使用 `[x,y,z]` | 楼层绝对高程与本层贴地高度分离 | 切换楼层后 marker 和模型错位 | +| 可达性 | 有分类但不完整 | 入口、无障碍路线、垂直交通联动 | 不足以支持专业无障碍导览 | +| 展示策略 | 代码内硬编码优先级 | 可配置图层样式与任务态策略 | 扩展和调参困难 | + +### SGS 坐标风险 + +当前 SGS adapter 中 `normalizePositionSource()` 直接输出 `[x, y, z]`: + +证据:`src/data/adapters/sgsSdkGuideAdapter.ts:124-130` + +`ThreeMap` 又直接把 `y` 用作 sprite 高度: + +证据:`src/components/map/ThreeMap.vue:3384-3390` + +但 SGS 坐标常见语义是: + +```text +x = GLB 水平 X +z = GLB 水平 Z +y = 高度或楼层绝对高程 +``` + +专业渲染中,POI 展示应使用: + +```text +renderX = source.x +renderZ = source.z +renderY = surfaceOffset 或 floor-local height +``` + +而不是直接把 SGS `position.y` 当作当前单层模型的局部高度。 + +--- + +## 3.3 楼层信息展示差异 + +### 当前项目特点 + +当前楼层信息主要是楼层按钮: + +```text +B2 / B1 / 1F / 2F / 3F ... +``` + +选中态主要依赖: + +```vue +:class="{ active: activeFloorId === floor.id && layerMode !== 'multi' }" +``` + +证据:`src/components/navigation/GuideMapShell.vue:149-154` + +### 行业专业规范 + +专业楼层信息不应只是 label,而应包含: + +```ts +interface IndoorFloor { + id: string + code: string // L1 / L2 / L-1 + label: string // 1F / 2F / B1 + name?: string // 一层大厅 / 二层展厅区 + level: number // -1, 1, 2 + elevation?: number + order: number + isIndoor: boolean + isNavigable: boolean + isOpen: boolean + modelStatus: 'ready' | 'missing' | 'loading' | 'error' + poiStatus: 'ready' | 'empty' | 'partial' | 'error' + routeStatus: 'ready' | 'not-ready' | 'partial' +} +``` + +专业楼层 UI 至少要区分: + +| 状态 | 展示建议 | +|---|---| +| 当前显示楼层 | 高亮 | +| 用户所在楼层 | “你在这”/定位点 | +| 目标所在楼层 | 目标标记 | +| 路线途经楼层 | 路线提示/小圆点 | +| 有搜索结果楼层 | 数量 badge | +| 加载中楼层 | spinner | +| 数据缺失楼层 | 灰化/警告 | +| 不开放楼层 | 锁定/禁用 | + +### 当前差距 + +| 维度 | 当前项目 | 专业规范 | +|---|---|---| +| 楼层 label | 已实现 | 已实现 | +| 楼层名称 | 缺少 | 应显示“一层大厅 / 二层展厅区”等 | +| 楼层内容摘要 | 缺少 | 应显示主要展厅、服务设施数量 | +| 用户所在楼层 | 缺少 | 应和当前显示楼层区分 | +| 目标楼层 | 缺少 | 从搜索/讲解进入时应标注 | +| 路线途经楼层 | 部分 route 数据有,但 UI 未系统表达 | 应在楼层控件中明确标注 | +| 数据状态 | 缺少 | 应标注模型/POI/路网是否 ready | + +--- + +## 4. 行业内专业展示规范总结 + +## 4.1 楼层切换规范 + +专业室内导览的楼层切换应满足: + +1. 楼层 ID 稳定,不使用纯展示 label 作为主键。 +2. 楼层排序基于语义 level,而不是字符串排序。 +3. 切换楼层时,模型、空间面、POI、路线、定位状态同步更新。 +4. 切换过程有 loading/pending 状态。 +5. 切换失败保留旧楼层,并提示失败原因。 +6. 用户所在楼层、目标所在楼层、当前查看楼层应分开表达。 +7. 跨楼层路线时,应标记路线涉及楼层。 +8. 非开放、无数据、无路网楼层应禁用或显示状态。 + +推荐状态模型: + +```ts +interface FloorViewState { + requestedFloorId?: string + loadingFloorId?: string + renderedFloorId?: string + userLocatedFloorId?: string + targetFloorId?: string + failedFloorId?: string + routeFloorIds: string[] +} +``` + +## 4.2 POI 展示规范 + +专业 POI 展示应满足: + +1. 区分展示点、入口点、路线节点、空间几何。 +2. POI 必须绑定稳定 `floorId`。 +3. POI 坐标必须声明坐标系、单位、轴向和变换。 +4. POI label 应按 zoom/camera distance/task state 自适应显示。 +5. POI 高密度区域必须做避让、聚合和优先级裁剪。 +6. 搜索命中、选中、路线起终点、换乘点应强制显示。 +7. 设施、展厅、展品、交通、无障碍、安全设施应有独立图层策略。 +8. 不同任务模式应有不同 POI 图层:浏览、找设施、路线、无障碍、讲解联动。 + +推荐 POI 点位拆分: + +```text +展厅中心点:用于标签展示 +展厅入口点:用于路线终点 +路网节点:用于路径计算 +空间面:用于区域高亮 +锚点:用于相机聚焦 +``` + +## 4.3 楼层信息展示规范 + +专业楼层信息应包含: + +1. 楼层短 label:如 `1F`、`2F`、`B1`。 +2. 楼层名称:如 `一层大厅`、`二层展厅区`。 +3. 主要内容摘要:如 `宇宙厅 / 服务台 / 卫生间`。 +4. 点位数量或搜索结果数量。 +5. 模型加载状态。 +6. POI 数据状态。 +7. 路线数据状态。 +8. 当前用户所在楼层。 +9. 当前目标所在楼层。 +10. 当前路线途经楼层。 + +移动端推荐形式: + +```text +楼层按钮:2F +当前楼层 chip:2F · 地球厅 / 演化厅 · 23 个点位 +楼层详情面板:展示展厅、设施、数据状态、路线状态 +``` + +## 4.4 3D 室内导览规范 + +专业 3D 室内导览应满足: + +1. 3D 模型只是底图/底座,业务语义来自数据层。 +2. 不应依赖模型节点名推断核心业务楼层语义。 +3. GLB 模型、POI、空间面、路线点必须共享或可转换到同一坐标系。 +4. 模型 translation/rotation/scale 必须被业务图层同步应用。 +5. 单层模型和全馆模型切换不能改变 POI 的真实语义坐标。 +6. 模型加载失败、POI 加载失败、路网加载失败应分别提示。 +7. 移动端应控制 GLB 体积、解码时间和 WebGL 资源释放。 + +## 4.5 导航能力规范 + +如果产品要宣称“室内导航”,至少需要: + +1. 可步行路网节点。 +2. 路网边和权重。 +3. 垂直交通连接:电梯、楼梯、扶梯。 +4. 跨楼层路线分段。 +5. POI 到可达入口/路网节点的映射。 +6. 无障碍路线约束。 +7. 一方通行/封闭/施工等约束。 +8. 路线可视化。 +9. 起终点和换乘点状态。 +10. 数据 readiness 与 smoke test。 + +当前项目在 route graph / nav data 未验证前,应继续使用: + +```text +位置预览 / 查看位置 / 查看三维位置 +``` + +不应使用: + +```text +开始馆内导航 / 到达引导 / turn-by-turn / 精准导航 +``` + +--- + +## 5. 当前项目优先改进建议 + +## P1:统一 SGS 坐标归一化 + +当前最优先问题是避免 SGS `position.y` 直接进入 ThreeMap marker 的 Y 坐标。 + +建议: + +```ts +// SGS 渲染坐标:只用 x/z 做水平定位,Y 使用本层贴地偏移 +positionGltf = [source.x, 0, source.z] +``` + +如果需要保留原始高度: + +```ts +rawPosition = [source.x, source.y, source.z] +heightMode = 'absolute-elevation' +``` + +不要让绝对高程直接控制单层模型里的 marker 高度。 + +## P1:楼层切换状态闭环 + +把当前单一 `activeGuideFloor` 拆成: + +```ts +requestedFloorId +loadingFloorId +renderedFloorId +failedFloorId +``` + +流程: + +```text +点击楼层 + -> requested/loading + -> ThreeMap 加载模型和 POI + -> 成功后 emit renderedFloorChange + -> 父组件更新 active/rendered floor + -> 失败后保留旧楼层并提示 +``` + +## P1:POI 专业语义拆分 + +将 `GuideRenderPoi.positionGltf` 逐步升级为: + +```ts +displayPoint +anchorPoint +entrancePoints +routeNodeIds +geometry +``` + +展厅类 POI 尤其需要: + +- `space.geometry` 用于区域高亮。 +- `space.center` 用于标签。 +- `entrancePoints` 用于路线。 +- `routeNodeIds` 用于导航。 + +## P2:楼层信息面板升级 + +建议当前楼层展示从单 label 升级为: + +```text +2F · 地球厅 / 演化厅 · 23 个点位 +``` + +楼层按钮可增加: + +- 当前目标所在楼层标记。 +- 路线途经楼层标记。 +- 搜索结果数量。 +- 数据未就绪/加载失败状态。 + +## P2:POI 图层配置化 + +把 `ThreeMap.vue` 中硬编码的 POI 类别优先级迁到配置或 view model: + +```ts +interface PoiLayerStyle { + categoryId: string + icon: string + color: string + priority: number + labelPolicy: 'always' | 'adaptive' | 'selected-only' + routeRelevant: boolean + accessibilityRelevant: boolean +} +``` + +## P2:空间面成为一等图层 + +博物馆展厅不应只显示一个点。建议将 `spaces` 转成专业图层: + +- 展厅区域 polygon/mesh highlight。 +- 展厅中心标签。 +- 展厅入口点。 +- 点击区域选中展厅。 +- 展厅卡片与讲解内容联动。 + +## P3:路线能力逐步专业化 + +后续如要升级为真正室内导航,需要完成: + +- route graph / nav data 接入。 +- 跨层连接。 +- POI entrance -> route node 映射。 +- 无障碍路线。 +- 路线分段楼层展示。 +- route readiness smoke test。 + +--- + +## 6. 建议实施顺序 + +| 优先级 | 事项 | 目标 | +|---|---|---| +| P1 | SGS 坐标归一化 | 解决楼层切换后 POI 与模型错位 | +| P1 | 楼层切换状态拆分 | 防止 UI 楼层状态与实际渲染楼层不同步 | +| P1 | POI display/entrance/routeNode 拆分 | 建立专业导览数据基础 | +| P1 | 继续限制导航话术 | 防止能力误导 | +| P2 | 楼层信息展示升级 | 让用户理解每层内容和状态 | +| P2 | POI 图层配置化 | 支持搜索、设施、路线、无障碍等任务态 | +| P2 | 空间面展厅图层 | 从“点位地图”升级到“空间地图” | +| P3 | route graph / nav data 闭环 | 支持真正路线预览/导航 | +| P3 | 移动端性能优化 | 控制 GLB、POI、标签和 WebGL 资源成本 | + +--- + +## 8. deep-research 外部资料核验补充 + +> 本节为 2026-06-30 deep-research 工作流补充。研究问题:对比当前深圳自然博物馆 `frontend-miniapp` 导览页的楼层切换、点位展示、楼层信息展示,与室内导览/室内地图行业专业展示规范的差异,并总结行业专业规范。 + +### 8.1 研究限制 + +- 外部结论主要基于博物馆/公共建筑无障碍指南、OGC IMDF/IndoorGML 标准和少量博物馆案例研究。 +- 这些资料适合作为专业规范与审计标尺,不等同于某一司法辖区对移动 H5 导览页的硬性法律要求。 +- IMDF/IndoorGML 是数据模型标准,不直接规定 UI 视觉样式;本文关于“楼层切换 UI 应如何呈现”的部分,是从专业数据模型和地图应用行为要求推导出的应用性结论。 +- 部分可访问性来源讨论实体展览空间、实体地图、导视牌或触觉/印刷材料;映射到 `frontend-miniapp` 时,应理解为同一 wayfinding 原则在移动端的对应实现。 + +### 8.2 外部核验后的高置信结论 + +#### 8.2.1 专业室内导览首先把楼层/层级认知当作核心问题 + +专业室内导览不是简单楼层按钮切换。对于多楼层、多 level、局部区域才能上下转换的建筑,楼层切换必须帮助用户理解: + +- 我在哪一层。 +- 当前这一层有什么。 +- 目标在哪一层。 +- 如何去另一层。 +- 应通过哪个电梯、楼梯、坡道或换乘点。 + +外部依据:British Museum 移动导览案例显示,复杂博物馆中的多楼层和多 level 会直接造成方向感问题;用户难以理解自己在哪一层、如何换层,以及如何阅读多层表示。VA/WBDG 指南要求每层目录识别该层公共目的地,说明楼层信息应服务于定位和决策,而不是只作为地图图层开关。 + +对当前项目的含义: + +- 当前 `floor-switcher` 只展示楼层 label,不能充分支撑复杂博物馆场景下的楼层认知。 +- 应补充楼层内容摘要、用户所在楼层、目标楼层、路线途经楼层和可达性提示。 + +#### 8.2.2 专业楼层切换应由结构化 Level 数据驱动 + +OGC IMDF 将楼层建模为 `Level Feature`,核心要求包括: + +- 稳定 `id`。 +- `feature_type = level`。 +- polygonal geometry。 +- venue-declared `name`。 +- `short_name`。 +- 数字 `ordinal`。 + +其中 `ordinal` 表示真实楼层堆叠位置,应与 UI 显示 label 分离。例如 `B1`、`1F`、`L1` 是展示和命名问题,而 `ordinal` 是排序、跨层关系和空间推理问题。 + +对当前项目的含义: + +- `guideFloor.ts` 已经有楼层 label/code 解析和排序,这是正确方向。 +- 但当前领域模型仍缺少 IMDF 式 Level geometry、ordinal、display point、楼层范围和数据状态的完整表达。 +- 后续不应只依赖 `floor.label` 或模型节点名推断楼层语义。 + +#### 8.2.3 专业 floor switcher 不应有任意初始状态 + +IMDF Level geometry 规则要求 venue organization 考虑: + +- 无楼层选择时默认显示哪些楼层。 +- 用户选择单体建筑时默认显示哪层。 +- 复杂结构中 physical parity 与 ordinal parity 不一致时如何处理。 + +对当前项目的含义: + +- 当前页面传入 `indoor-initial-view="overview"`,同时存在 `activeGuideFloor` 默认值;这应被明确为产品策略,而不是偶然状态。 +- 应定义:初次进入馆内时显示全馆、用户主动选楼层后显示单层、从搜索/讲解定位进入时显示目标楼层。 +- 需要区分 `requestedFloorId`、`renderedFloorId`、`targetFloorId` 和 `userLocatedFloorId`。 + +#### 8.2.4 POI/amenity 必须 floor-aware,且要避免跨楼层重叠混淆 + +IMDF Amenity 规则定义 `correlation_id`,用于不同 Level 上同一服务设施的关联。其目标之一是:当多个楼层垂直重叠存在同类设施,而用户未选楼层时,地图不应混杂或重复展示。 + +对当前项目的含义: + +- POI 展示必须严格绑定当前渲染楼层。 +- 多层/全馆模式下,不应无差别显示所有楼层 POI。 +- 同一垂直位置的电梯、楼梯、卫生间等跨层设施需要关联关系,而不是多个互不相关的点。 +- 当前代码已有按楼层加载 POI 的方向,但 POI 专业语义仍需从单 `positionGltf` 升级为 floor-aware amenity/space 模型。 + +#### 8.2.5 点位和地图可读性依赖清晰视觉层级与可区分符号系统 + +British Museum 案例中,地图改版通过颜色和样式区分图标、按钮和地图区域,以降低用户对缩放、滚动、选择对象和地图定向的困难。 + +对当前项目的含义: + +- 当前 `ThreeMap` 已经有 POI 类别颜色、优先级、距离过滤和 label 策略,是正确方向。 +- 但专业系统还应有配置化图层、碰撞避让、聚合、色盲友好、非颜色唯一编码、搜索/路线/无障碍任务态强制显示策略。 + +#### 8.2.6 楼层信息展示应组合地图、文本/语音锚点和附近地标 + +British Museum Museum Navigator 使用 floor、level、room number、方位和附近 landmark 组织位置描述,并结合音频说明、地标图片和高亮路线。COSIT、Met 和 V&A 相关证据也支持在多楼层空间中使用房间号、楼层、地标和可识别转折点辅助定位。 + +对当前项目的含义: + +- 楼层按钮仅显示 `1F / 2F / B1` 不足。 +- 楼层信息应显示主要展厅、服务设施、附近地标、空间区域名称和换层节点。 +- POI 卡片也应包含“所在楼层 + 附近地标 + 如何到达/查看位置”的组合描述。 + +#### 8.2.7 专业室内导览是 integrated wayfinding system + +CMHR 指南将 wayfinding 描述为包含 signs、maps、spoken directions、technology、mobile application、website、tactile indicators、lighting 和 amenity communication 的冗余线索系统。VA/WBDG integrated wayfinding 要求所有工具使用一致目的地名称、命名逻辑、视觉语言和同一套 canonical map 信息。 + +对当前项目的含义: + +- `frontend-miniapp` 不应自成一套命名体系。 +- 展厅名、楼层名、设施名、房间号、讲解内容应与现场实体导视、后台 SGS 数据和讲解内容库一致。 +- 当前项目需要明确 canonical nomenclature,即“哪一套命名为准”。 + +#### 8.2.8 专业导览应在决策点反复确认方向和楼层上下文 + +VA/WBDG 指南要求访客在导航过程中获得 frequent intervals 的 reinforcement and guidance,并强调 decision points、floor directories、you-are-here 和室内导航能力。CMHR 指南也指出 You-Are-Here maps 应布置在电梯、坡道等方向决策点附近。 + +移动端对应规范: + +- 到达电梯/楼梯/坡道/入口/服务台/转折点时,UI 应强化当前楼层和下一步。 +- 跨楼层路线应明确“当前楼层路径”和“目标楼层路径”。 +- 即使没有实时定位,也应在位置预览中表达“目标在 2F,建议从某电梯/楼梯上楼”。 + +当前项目由于 route graph/nav data readiness 尚未闭环,目前不应承诺此类真实导航,只能在数据可用时逐步增加。 + +#### 8.2.9 可访问楼层图和平面图是专业展览/博物馆导览的重要组成 + +Smithsonian Accessible Exhibition Design 要求提供 accessible floorplan 帮助访客 wayfinding,并建议在展览入口、信息台或中心位置提供;它还要求 circulation route 清楚定义、易跟随,并在 level changes、unexpected turns 或 obstacles 等处清晰表达路线。 + +对移动端的映射: + +- 楼层图应可被理解,不只是 3D 模型。 +- 路线、无障碍路线、电梯/坡道、台阶规避等应被明确表达。 +- 若 route graph 未就绪,则 UI 应清楚显示“位置预览可用,路线导航未开放”。 + +#### 8.2.10 专业室内导览需要导航网络/连通性模型支撑 + +OGC IndoorGML 1.1 的范围是 indoor navigation network models 的表示与交换,强调为室内导航应用建立通用 schema,并建模室内空间拓扑和语义关系。 + +这不意味着本项目必须采用 IndoorGML,但说明专业室内导航能力应由以下数据支撑: + +- 可步行路网节点。 +- 边和权重。 +- 垂直连接。 +- 空间拓扑。 +- POI 到可达入口或路网节点的映射。 +- 跨楼层路径分段。 + +当前项目若只有 GLB 和散点 POI,应继续定位为“位置预览”。 + +### 8.3 外部研究映射到当前项目的专项差距 + +| 专业规范 | 当前项目状态 | 差距 | +|---|---|---| +| 结构化 Level/ordinal 数据 | 有 `guideFloor.ts` label/code 解析,但缺少完整 Level geometry/ordinal/display point | 楼层模型、POI、路线和 UI 状态还未统一到专业 Level 模型 | +| floor-aware POI | 有按 floorId 加载 POI | POI 仍以单 `positionGltf` 为主,缺少 amenity correlation、entrance、route node、geometry | +| 楼层目录/内容摘要 | 楼层按钮只显示 label | 缺少每层目的地、主要展厅、服务设施和状态说明 | +| 决策点强化 | 暂无完整路线/决策点 UI | 电梯、楼梯、坡道、入口、转折点未形成导览状态机 | +| integrated wayfinding | miniapp 内部已有导览/讲解联动雏形 | 仍需与现场导视、SGS 后台、讲解内容库统一命名和空间关系 | +| 可访问 floorplan | 当前是 3D 展示 + POI 预览 | 缺少无障碍路线、可访问入口、坡道/电梯优先等表达 | +| 导航网络 | route graph/nav data readiness 未验证 | 不能宣称专业室内导航 | + +### 8.4 deep-research 推荐追问 + +后续专项审计建议回答以下问题: + +1. 当前 `frontend-miniapp` 的楼层切换是否已经有真实 Level 数据模型支撑,还是仅使用 UI 标签/静态图层状态? +2. 深圳自然博物馆现场实体导视、房间/展厅编号、楼层命名、服务设施名称与 miniapp 中的 POI 命名是否一致?如果不一致,应以哪一套 canonical nomenclature 为准? +3. 当前导览页是否支持无障碍路径语义,例如电梯、坡道、无障碍卫生间、台阶规避、跨楼层可达路线?这些信息是否来自可维护的数据源? +4. SGS Map SDK 或上游 `sgs-frontend-map` 场景设置是否提供 route graph/nav_data、level ordinal、POI level binding、vertical connector 等字段?如果没有,miniapp 应补充适配层还是推动上游数据治理? + +### 8.5 外部来源 + +- OGC Indoor Mapping Data Format (IMDF): https://www.ogc.org/standards/indoor-mapping-data-format/ +- OGC IMDF Level: https://docs.ogc.org/cs/20-094/Level/index.html +- OGC IMDF Reference: https://docs.ogc.org/cs/20-094/Reference/index.html +- OGC IMDF Amenity: https://docs.ogc.org/cs/20-094/Amenity/index.html +- OGC IndoorGML 1.1: https://docs.ogc.org/is/19-011r4/19-011r4.html +- VA/WBDG Integrated Wayfinding: https://www.wbdg.org/FFC/VA/VASIGN/wayfinding_new_chapter2.pdf +- Smithsonian Accessible Exhibition Design: https://affiliations.si.edu/wp-content/uploads/PDFs/Accessible-Exhibition-Design.pdf +- ADA Museum Access guide: https://archive.ada.gov/business/museum_access.htm +- CMHR Wayfinding: https://id.humanrights.ca/visitor-supports/wayfinding/ +- British Museum mobile wayfinding case: https://www.museumsandtheweb.com/mw2011/papers/mobile_devices_for_orientation_and_way_finding +- COSIT museum orientation reference: https://drops.dagstuhl.de/entities/document/10.4230/LIPIcs.COSIT.2017.18 +- V&A digital map: https://www.vam.ac.uk/features/digitalmap/where-am-i +- Metropolitan Museum map design case: https://www.100archive.com/projects/metropolitan-museum-of-art-map-design + diff --git a/docs/QA/miniapp-guide-api-test-results-2026-07-02.md b/docs/QA/miniapp-guide-api-test-results-2026-07-02.md new file mode 100644 index 0000000..a20cfb5 --- /dev/null +++ b/docs/QA/miniapp-guide-api-test-results-2026-07-02.md @@ -0,0 +1,387 @@ +# 小程序讲解接口测试结果记录 + +测试日期:2026-07-02 + +## 1. 测试目标 + +验证小程序讲解业务接口在不同基础地址下的可访问性、实际响应数据和当前失败原因,重点关注: + +- 展厅列表 +- 展厅导览点列表 +- 讲解详情 `stop-info` +- 音频播放信息 `play-info` +- 讲解词正文 `text-info` + +## 2. 测试基础地址 + +| 基础地址 | 测试结果 | 说明 | +| --- | --- | --- | +| `http://1.92.206.90:3001` | 可访问 | 当前公网可用的小程序接口入口 | +| `http://1.92.206.90:48080/yudao-server/` | 公网超时 | 外网访问 `48080` 连接超时 | +| `http://127.0.0.1:48080/yudao-server/` | 服务器本机可访问 | Java/Apusic 后端实际本机入口 | +| `https://guide.whaoyue.com/app-api` | 可访问 | 经 Nginx 转发到 `3001/app-api` | +| `https://guide.whaoyue.com/yudao-server/app-api` | 502 | Nginx 容器无法连接宿主 `48080` | + +## 3. 实际后端路径判断 + +Java 后端实际上下文路径为: + +```text +http://127.0.0.1:48080/yudao-server/app-api +``` + +公网当前稳定可用路径为: + +```text +http://1.92.206.90:3001/app-api +https://guide.whaoyue.com/app-api +``` + +直接访问以下地址不可用: + +```text +http://1.92.206.90:48080/yudao-server/app-api +``` + +表现为连接超时。 + +## 4. 测试接口与结果 + +本轮以 `http://1.92.206.90:3001` 为主要公网基础地址。 + +| 接口 | 测试 URL | HTTP | 业务结果 | 结论 | +| --- | --- | --- | --- | --- | +| 展厅列表 | `/app-api/gis/hall/list` | 200 | `code=0`,返回 8 个展厅 | 正常 | +| 宇宙厅导览点 | `/app-api/gis/sdk/halls/715792102100832258/guide-stops` | 200 | `code=0`,返回 2 个导览点 | 正常 | +| 展厅分区 | `/app-api/gis/zone/list-by-hall?hallId=715792102100832258` | 200 | `code=0`,返回空数组 | 接口正常,当前无数据 | +| 讲解详情 | `/app-api/gis/guide/stop/info?targetType=STOP&targetId=1823450596808612&lang=zh-CN` | 200 | `code=404`,接口不存在 | 异常 | +| 播放信息 | `/app-api/gis/guide/audio/play-info?targetType=STOP&targetId=1823450596808612&lang=zh-CN` | 200 | `code=0`,`playable=false`,`reason=TARGET_NOT_FOUND` | 路由存在,数据不可用 | +| 讲解词正文 | `/app-api/gis/guide/audio/text-info?targetType=STOP&targetId=1823450596808612&lang=zh-CN` | 200 | `code=0`,`available=false`,`reason=TARGET_NOT_FOUND` | 路由存在,数据不可用 | + +## 5. 实际返回数据 + +### 5.1 展厅列表 + +请求: + +```bash +curl "http://1.92.206.90:3001/app-api/gis/hall/list" +``` + +核心响应: + +```json +{ + "code": 0, + "msg": "", + "data": [ + { "id": "715792102100832258", "hallCode": "E1", "name": "宇宙厅" }, + { "id": "715792102100832257", "hallCode": "E2", "name": "地球厅" }, + { "id": "715792102100832259", "hallCode": "E3", "name": "演化厅" }, + { "id": "715792102100832260", "hallCode": "E4", "name": "恐龙厅" }, + { "id": "715792102100832256", "hallCode": "E5", "name": "人类厅" }, + { "id": "715792102100832261", "hallCode": "E6", "name": "生物厅" }, + { "id": "715792102100832262", "hallCode": "E7", "name": "生态厅" }, + { "id": "715792102100832263", "hallCode": "E8", "name": "家园厅" } + ] +} +``` + +### 5.2 宇宙厅导览点 + +请求: + +```bash +curl "http://1.92.206.90:3001/app-api/gis/sdk/halls/715792102100832258/guide-stops" +``` + +响应: + +```json +{ + "code": 0, + "msg": "", + "data": [ + { + "id": "1823450596808612", + "name": "古典星盘 讲解", + "type": "guide_stop", + "typeName": "讲解点", + "floorId": "2065808920714276866", + "position": { + "x": -107.86390357145089, + "y": 0.0, + "z": 42.1015203335146 + }, + "targetType": "GUIDE_STOP", + "targetId": "1823450596808612", + "audioUrl": null, + "coverImageUrl": null, + "description": null, + "status": "ACTIVE", + "located": true, + "hasAudio": false, + "poiId": null, + "outlineId": "7467940240901013505", + "outlineName": "第一单元:仰望苍穹——人类对宇宙认识的历程", + "hallId": "715792102100832258", + "hallName": "宇宙厅", + "sort": 0, + "routeId": null, + "routeName": null, + "seqOrder": null, + "stayMinutes": null + }, + { + "id": "1823450596814245", + "name": "火星提森特陨石 讲解", + "type": "guide_stop", + "typeName": "讲解点", + "floorId": "2065808920714276866", + "position": { + "x": -94.37030868849183, + "y": 0.0, + "z": 41.8425968285253 + }, + "targetType": "GUIDE_STOP", + "targetId": "1823450596814245", + "audioUrl": null, + "coverImageUrl": null, + "description": null, + "status": "ACTIVE", + "located": true, + "hasAudio": false, + "poiId": null, + "outlineId": "7467940240901013507", + "outlineName": "第三单元:采石知天——行星科学与深空探测", + "hallId": "715792102100832258", + "hallName": "宇宙厅", + "sort": 0, + "routeId": null, + "routeName": null, + "seqOrder": null, + "stayMinutes": null + } + ] +} +``` + +### 5.3 展厅分区 + +请求: + +```bash +curl "http://1.92.206.90:3001/app-api/gis/zone/list-by-hall?hallId=715792102100832258" +``` + +响应: + +```json +{ + "code": 0, + "msg": "", + "data": [] +} +``` + +### 5.4 stop-info + +请求: + +```bash +curl "http://1.92.206.90:3001/app-api/gis/guide/stop/info?targetType=STOP&targetId=1823450596808612&lang=zh-CN" +``` + +响应: + +```json +{ + "code": 404, + "msg": "请求地址不存在:app-api/gis/guide/stop/info", + "data": null +} +``` + +文档样例 `targetType=ITEM&targetId=1001` 结果一致: + +```json +{ + "code": 404, + "msg": "请求地址不存在:app-api/gis/guide/stop/info", + "data": null +} +``` + +### 5.5 play-info + +请求: + +```bash +curl "http://1.92.206.90:3001/app-api/gis/guide/audio/play-info?targetType=STOP&targetId=1823450596808612&lang=zh-CN" +``` + +响应: + +```json +{ + "code": 0, + "msg": "", + "data": { + "playable": false, + "targetType": "STOP", + "targetId": 1823450596808612, + "lang": "zh-CN", + "narrationTier": "STANDARD", + "audioId": null, + "title": null, + "duration": null, + "format": null, + "playUrl": null, + "expiresAt": null, + "subtitleUrl": null, + "hasText": false, + "fallback": false, + "fallbackReason": null, + "reason": "TARGET_NOT_FOUND" + } +} +``` + +文档样例 `targetType=ITEM&targetId=1001` 响应: + +```json +{ + "code": 0, + "msg": "", + "data": { + "playable": false, + "targetType": "ITEM", + "targetId": 1001, + "lang": "zh-CN", + "narrationTier": "STANDARD", + "audioId": null, + "title": null, + "duration": null, + "format": null, + "playUrl": null, + "expiresAt": null, + "subtitleUrl": null, + "hasText": false, + "fallback": false, + "fallbackReason": null, + "reason": "TARGET_NOT_FOUND" + } +} +``` + +### 5.6 text-info + +请求: + +```bash +curl "http://1.92.206.90:3001/app-api/gis/guide/audio/text-info?targetType=STOP&targetId=1823450596808612&lang=zh-CN" +``` + +响应: + +```json +{ + "code": 0, + "msg": "", + "data": { + "available": false, + "targetType": "STOP", + "targetId": 1823450596808612, + "lang": "zh-CN", + "narrationTier": "STANDARD", + "title": null, + "text": null, + "textLength": null, + "textHash": null, + "reason": "TARGET_NOT_FOUND" + } +} +``` + +文档样例 `targetType=ITEM&targetId=1001` 响应: + +```json +{ + "code": 0, + "msg": "", + "data": { + "available": false, + "targetType": "ITEM", + "targetId": 1001, + "lang": "zh-CN", + "narrationTier": "STANDARD", + "title": null, + "text": null, + "textLength": null, + "textHash": null, + "reason": "TARGET_NOT_FOUND" + } +} +``` + +## 6. 服务器侧补充验证 + +服务器监听情况: + +```text +3001 -> next-server +48080 -> Java/Apusic +80/443 -> Docker Nginx +``` + +Nginx 转发配置显示: + +```text +/app-api/ -> http://172.17.0.1:3001/app-api/ +/yudao-server/app-api/ -> http://172.17.0.1:48080/yudao-server/app-api/ +``` + +但 Nginx 容器访问宿主 `48080` 失败,因此: + +```text +https://guide.whaoyue.com/yudao-server/app-api/... -> 502 +``` + +部署包检查显示当前 app 端存在: + +```text +/gis/guide/audio/play-info +/gis/guide/audio/summary +/gis/guide/audio/text-info +``` + +未发现 app 端: + +```text +/gis/guide/stop/info +``` + +后台管理端存在 `GuideStopController`,但映射为: + +```text +/gis/guide-stop +``` + +并且属于后台权限接口,不等同于小程序 app 端 `stop-info`。 + +## 7. 结论 + +1. `http://1.92.206.90:3001` 是当前可用的小程序公网接口基础地址。 +2. `http://1.92.206.90:48080/yudao-server/` 当前公网不可用,连接超时。 +3. 展厅列表和导览点列表接口可正常返回实际数据。 +4. 当前 `宇宙厅` 返回 2 个导览点,但 `hasAudio=false`。 +5. `stop-info` 接口在当前后端部署中不存在,是讲解详情加载失败的直接原因。 +6. `play-info` 和 `text-info` 路由存在,但对当前测试 ID 返回 `TARGET_NOT_FOUND`,没有可播放音频和讲解词正文。 + +## 8. 建议处理 + +1. 前端公网环境继续使用 `http://1.92.206.90:3001/app-api` 或域名 `/app-api` 通道。 +2. 后端需要补充或重新部署 app 端 `GET /app-api/gis/guide/stop/info` 接口。 +3. 如果文档中的 `stop-info` 已废弃,需要同步更新接口说明和前端调用逻辑。 +4. 后端需要为至少一个真实导览点发布音频和讲解词,用于验证 `playable=true` 与 `available=true` 的正向链路。 +5. 若计划开放 `48080/yudao-server` 公网访问,需要同步检查安全组、防火墙和 Nginx 容器到宿主 `48080` 的连通性。 diff --git a/docs/QA/user-flow-closure-audit-2026-06-10.md b/docs/QA/user-flow-closure-audit-2026-06-10.md index 4ddb27d..ca2df97 100644 --- a/docs/QA/user-flow-closure-audit-2026-06-10.md +++ b/docs/QA/user-flow-closure-audit-2026-06-10.md @@ -10,11 +10,11 @@ ## 结论 -本次测试确认:导览首页到搜索、搜索结果到设施详情/路线预览、路线页室外预览返回室内预览等主路径已经可以渲染,浏览器或系统返回也能在若干场景中恢复上一页;搜索筛选状态从设施详情返回后仍能保留,是一个正向结果。 +本次测试确认:导览首页到搜索、搜索结果到设施详情/路线预览、路线页馆外预览返回馆内预览等主路径已经可以渲染,浏览器或系统返回也能在若干场景中恢复上一页;搜索筛选状态从设施详情返回后仍能保留,是一个正向结果。 但用户逻辑闭环仍未完成。当前问题不是页面打不开,而是用户在进入二级页面后缺少稳定的页面级返回、取消、重置和继续路径;部分关键按钮只 `toast` 或 `console.log`,导致任务停在中间态。尤其是搜索页不能重新输入关键词、设施页不能真正选择起点、路线页不能继续到“目标位置确认/导航完成”,这些都属于闭环断点。 -需要特别说明:当前产品能力仍应表述为“室内 3D 展示 + POI/位置预览”。源码中的提示也明确指出正式路线数据尚未接入,不能把现有路径宣传为真实室内路线规划或实时导航。 +需要特别说明:当前产品能力仍应表述为“馆内 3D 展示 + POI/位置预览”。源码中的提示也明确指出正式路线数据尚未接入,不能把现有路径宣传为真实馆内路线规划或实时导航。 ## 当前已验证正向路径 @@ -22,9 +22,9 @@ | --- | --- | --- | | 首页导览 -> 点击搜索框 -> 搜索页 | 成功进入 `/pages/search/index` | 搜索页默认关键词为“卫生间”。 | | 搜索页 -> 结果卡片“查看” -> 路线页 | 最终可渲染路线详情页 | 首次进入时出现过短暂空白,后续编译完成后恢复。 | -| 路线页 -> 查看室外地图 -> 返回预览 | 可切换到 `outdoor-preview`,并通过按钮回到 `preview` | `handleViewOutdoorMap` 与 `handleReturnToPreview` 有明确状态切换。 | +| 路线页 -> 查看馆外地图 -> 返回预览 | 可切换到 `outdoor-preview`,并通过按钮回到 `preview` | `handleViewOutdoorMap` 与 `handleReturnToPreview` 有明确状态切换。 | | 搜索页 -> 设施卡片 -> 设施详情 -> 浏览器返回 | 可回到搜索页 | 测试中搜索筛选状态保留,例如“无障碍”过滤未丢失。 | -| 首页室内 3D 主 CTA | 已从“开始馆内导航”调整为“选择目标地点”并跳搜索页 | 避免直接进入未完成的路线规划态。 | +| 首页馆内 3D 主 CTA | 已从“开始馆内导航”调整为“选择目标地点”并跳搜索页 | 避免直接进入未完成的路线规划态。 | ## 浏览器验证问题 @@ -62,14 +62,14 @@ 用户路径:搜索页 -> 结果“查看” -> 路线详情页 -> 点击“查看目标位置” 观察结果:按钮只显示“正式路线数据尚未接入,可先查看馆内三维位置”的提示,没有切换到目标位置高亮、POI 卡片、3D 定位或可恢复状态。 -期望闭环:在真实路线数据未接入前,按钮文案和行为应稳定落到“位置预览”:例如高亮目标 POI、展示楼层/区域/说明、提供返回搜索和查看室外参考入口。 +期望闭环:在真实路线数据未接入前,按钮文案和行为应稳定落到“位置预览”:例如高亮目标 POI、展示楼层/区域/说明、提供返回搜索和查看馆外参考入口。 影响:用户已经选择目标,但点击后没有获得更多可操作信息,会误以为目标定位失败或导航不可用。 源码证据: - `src/services/navAssets.ts:3`:路线不可用提示为“正式路线数据尚未接入,可先查看馆内三维位置”。 - `src/pages/route/detail.vue:333` 至 `src/pages/route/detail.vue:338`:`handleShowTargetLocation` 仅 `uni.showToast`。 -- `src/pages/route/detail.vue:329` 至 `src/pages/route/detail.vue:341`:室外预览有状态切换和返回,但目标位置按钮没有同等状态落点。 -- `src/pages/route/detail.vue:84`:室外预览文案说明真实馆内路线需等待 `route_graph/nav_data` 接入。 +- `src/pages/route/detail.vue:329` 至 `src/pages/route/detail.vue:341`:馆外预览有状态切换和返回,但目标位置按钮没有同等状态落点。 +- `src/pages/route/detail.vue:84`:馆外预览文案说明真实馆内路线需等待 `route_graph/nav_data` 接入。 建议:短期把该动作改为“高亮目标位置/查看位置详情”,并切换到明确的 preview 子状态;中期接入 POI 坐标后,在 3D 视图中定位目标点;正式导航能力上线前,不使用“开始导航/路线规划已完成”等表述。 @@ -140,7 +140,7 @@ | 设施起点取消 | 设施详情 -> 选择起点 -> 取消 | 返回设施详情,不丢失目标设施。 | | 设施起点确认 | 设施详情 -> 选择起点 -> 确认 -> 查看位置 | 路线页能读取目标和起点;未接真实路线时明确展示预览态。 | | 路线目标预览 | 搜索结果“查看” -> 路线页 -> 查看目标位置 | 页面出现目标位置高亮或目标卡片,不只 toast。 | -| 路线室外返回 | 路线页 -> 查看室外地图 -> 返回预览/返回室内 3D | 状态回到室内预览,目标对象不丢失。 | +| 路线馆外返回 | 路线页 -> 查看馆外地图 -> 返回预览/返回馆内 3D | 状态回到馆内预览,目标对象不丢失。 | | 二级页返回 | 搜索 -> 设施详情/路线页 -> 点击页面返回 | 返回上一页,搜索关键词和筛选保留。 | | 顶部 tab 误触 | 路线页 -> 点击“讲解” -> 再回导览 | 行为符合产品定义;若会丢失路线上下文,应有明确提示或可恢复路径。 | | 首次加载路线 | 清缓存或首次打开 -> 搜索结果“查看” -> 路线页 | 不出现无反馈白屏;至少显示 loading、失败重试和返回。 | @@ -149,5 +149,5 @@ - 本报告接续旧会话的浏览器自动化结果,没有在当前会话重新启动 H5 服务或重复跑全量点击。 - 本报告只覆盖 H5 用户逻辑闭环,不覆盖 mp-weixin。 -- 真实室内路线规划、定位、到达判定不在当前已验证能力内;需等待 `route_graph/nav_data` 接入并完成独立验证。 +- 真实馆内路线规划、定位、到达判定不在当前已验证能力内;需等待 `route_graph/nav_data` 接入并完成独立验证。 - 当前文档只新增测试报告,不修改业务实现。 diff --git a/docs/QA/user-flow-closure-verification-2026-06-12.md b/docs/QA/user-flow-closure-verification-2026-06-12.md index a752c7f..f81f2dd 100644 --- a/docs/QA/user-flow-closure-verification-2026-06-12.md +++ b/docs/QA/user-flow-closure-verification-2026-06-12.md @@ -76,11 +76,11 @@ - 楼层切换到目标所在楼层 - 失败时显示"目标暂无三维位置数据" -### 验证5:路线页室外预览返回 -1. 路线页点击"查看室外地图" -2. 应切换到室外地图参考(2D模式、入口提示) -3. 点击"返回室内3D"或"返回预览" -4. **预期**:返回室内3D预览,目标对象不丢失 +### 验证5:路线页馆外预览返回 +1. 路线页点击"查看馆外地图" +2. 应切换到馆外地图参考(2D模式、入口提示) +3. 点击"返回馆内3D"或"返回预览" +4. **预期**:返回馆内3D预览,目标对象不丢失 ### 验证6:顶部Tab全局切换 1. 路线页点击顶部"讲解" diff --git a/docs/QA/user-flow-map-2026-06-09.md b/docs/QA/user-flow-map-2026-06-09.md index d822916..f6c3629 100644 --- a/docs/QA/user-flow-map-2026-06-09.md +++ b/docs/QA/user-flow-map-2026-06-09.md @@ -12,15 +12,15 @@ flowchart TD C -->|"导览"| D["导览页地图"] D --> E{"地图模式"} - E -->|"室外"| F["TencentMap 室外地图"] - E -->|"室内 3D"| G["室内静态设计图"] + E -->|"馆外"| F["TencentMap 馆外地图"] + E -->|"馆内 3D"| G["馆内静态设计图"] F --> H["点击 marker"] H --> H1["组件内部直接跳详情"] H --> H2["首页预期打开 POI 操作卡片"] H2 -. "事件未接通" .-> X1["断点:无法稳定选择详情/导航/讲解"] - G -. "未接 ThreeMap/POI/路径" .-> X2["断点:室内导航只是展示图"] + G -. "未接 ThreeMap/POI/路径" .-> X2["断点:馆内导航只是展示图"] D --> I["点击搜索"] I --> J["搜索页"] @@ -59,8 +59,8 @@ flowchart TD C -->|"我要找位置/设施"| D["导览"] D --> D1{"选择地图层"} - D1 -->|"室外"| D2["TencentMap 室外地图"] - D1 -->|"室内"| D3["ThreeMap 或真实室内平面图"] + D1 -->|"馆外"| D2["TencentMap 馆外地图"] + D1 -->|"馆内"| D3["ThreeMap 或真实馆内平面图"] D2 --> E["点击 POI"] D3 --> E E --> F["POI 操作卡片"] @@ -99,7 +99,7 @@ flowchart TD H3 --> H4["开始导航"] H4 --> H5["导航中:楼层/步骤/距离/方向"] H5 --> H6{"用户中途操作"} - H6 -->|"查看室外"| H7["切换室外地图层并保留路线"] + H6 -->|"查看馆外"| H7["切换馆外地图层并保留路线"] H7 --> H5 H6 -->|"暂停/继续"| H8["保留当前进度"] H8 --> H5 diff --git a/docs/SHENZHEN_NATURAL_MUSEUM_DEV_SKILL_USAGE.md b/docs/SHENZHEN_NATURAL_MUSEUM_DEV_SKILL_USAGE.md index bfe507d..8158c35 100644 --- a/docs/SHENZHEN_NATURAL_MUSEUM_DEV_SKILL_USAGE.md +++ b/docs/SHENZHEN_NATURAL_MUSEUM_DEV_SKILL_USAGE.md @@ -14,8 +14,8 @@ 处理以下任务时应使用该 skill: - 导览模块页面、组件、交互或数据流调整 -- 室内三维导览、Three.js、GLB/GLTF 模型加载、WebGL 性能问题 -- 腾讯地图、室外导览、地图 SDK 或地图标记逻辑调整 +- 馆内三维导览、Three.js、GLB/GLTF 模型加载、WebGL 性能问题 +- 腾讯地图、馆外导览、地图 SDK 或地图标记逻辑调整 - `static/nav-assets/app_nav_assets_v2_clean_20260611_093623` 资源包相关工作 - `src/data/providers/staticNavAssetsProvider.ts`、`src/repositories/GuideRepository.ts`、`src/repositories/GuideModelRepository.ts` 导览数据访问层相关工作 - `src/assets/data` 历史 demo 数据清理、隔离或迁移 @@ -36,11 +36,11 @@ 也可以在任务描述里包含明确上下文,Codex 应能自动匹配: ```text -帮我修复室内三维导览切换后顶部菜单被遮挡的问题。 +帮我修复馆内三维导览切换后顶部菜单被遮挡的问题。 ``` ```text -请检查腾讯地图和室内 3D 是否有耦合风险。 +请检查腾讯地图和馆内 3D 是否有耦合风险。 ``` ```text @@ -54,7 +54,7 @@ - `src/assets/data` 是历史 demo 数据区域,不应再作为当前导览模块的数据源。 - 在 `route_graph` 和 `nav_data` 未准备并验证前,不应声明“真实馆内导航”或启用真实路线规划。 - POI 坐标只能作为展示或位置预览候选,不能当作已认证导航锚点。 -- Three.js / GLB 室内三维逻辑应与腾讯地图 / 室外导览逻辑隔离。 +- Three.js / GLB 馆内三维逻辑应与腾讯地图 / 馆外导览逻辑隔离。 - Three.js/WebGL 当前按 H5 能力处理;不要默认增加小程序 fallback 或 mp-weixin 兼容工作。 - 移动端顶部菜单、搜索、卡片、按钮和楼层控件不能被 3D canvas 遮挡。 - 不做无关重构,不直接删除历史数据;先盘点引用、隔离影响,再按确认范围清理。 @@ -70,13 +70,13 @@ 三维模型问题: ```text -请使用 shenzhen-natural-museum-dev skill,排查室内 3D 模型加载失败,并保证 H5 有加载中和失败兜底。 +请使用 shenzhen-natural-museum-dev skill,排查馆内 3D 模型加载失败,并保证 H5 有加载中和失败兜底。 ``` 腾讯地图问题: ```text -请使用 shenzhen-natural-museum-dev skill,检查腾讯地图逻辑是否被室内导览改动影响,只读审计并给出证据文件。 +请使用 shenzhen-natural-museum-dev skill,检查腾讯地图逻辑是否被馆内导览改动影响,只读审计并给出证据文件。 ``` 历史数据清理: @@ -105,7 +105,7 @@ pnpm build:h5 - `pnpm build:*` 会写入 `dist`,如果任务是只读审计,应先说明风险再执行。 - `pnpm lint` 即使命令成功,警告也应视为技术债。 -- 导览 UI 改动后,应额外做 H5 冒烟检查:顶部菜单、室内 3D、开始导航/位置预览、搜索结果、路线详情页。 +- 导览 UI 改动后,应额外做 H5 冒烟检查:顶部菜单、馆内 3D、开始导航/位置预览、搜索结果、路线详情页。 - 只有用户明确要求小程序/mp-weixin 时,才额外运行 `pnpm build:mp-weixin` 或处理小程序兼容。 ## 维护方式 diff --git a/docs/UX/ux-ui-audit-2026-05-28.md b/docs/UX/ux-ui-audit-2026-05-28.md index a4bd68d..86b6549 100644 --- a/docs/UX/ux-ui-audit-2026-05-28.md +++ b/docs/UX/ux-ui-audit-2026-05-28.md @@ -9,7 +9,7 @@ ### 1.1 总体判断 -当前项目已经具备“导览原型”的主要界面部件:2D 腾讯地图、Three.js 3D 室内地图、顶部导览/讲解切换、搜索框、设施筛选、讲解抽屉、音频播放器、展品/展厅/设施/路线详情页。 +当前项目已经具备“导览原型”的主要界面部件:2D 腾讯地图、Three.js 3D 馆内地图、顶部导览/讲解切换、搜索框、设施筛选、讲解抽屉、音频播放器、展品/展厅/设施/路线详情页。 但从真实到馆任务看,它还没有形成可完整测试的用户体验闭环。最关键的问题不是视觉细节,而是入口、数据、状态和操作结果之间没有打通: @@ -176,7 +176,7 @@ | 项目 | 内容 | | --- | --- | -| 任务 | 进入 3D 室内地图,查看某楼层展厅和点位关系 | +| 任务 | 进入 3D 馆内地图,查看某楼层展厅和点位关系 | | 成功标准 | 5 秒内出现可用模型或骨架;有 POI;可切换楼层 | | 预期行为 | 用户会探索模型并点击点位 | | 当前风险 | 首页传入空 `poiList`,3D 默认点位不会出现;3D 与路线/详情未连接 | @@ -519,7 +519,7 @@ flowchart LR | 方向 | 竞品优势 | 当前差距 | 建议 | | --- | --- | --- | --- | | 内容分发 | British Museum / MoMA / The Met 使用成熟的音频和导览内容体系 | 当前音频 URL、内容分类、详情加载均未成熟 | 建立内容模型和 CMS/API 接口 | -| 定位与路线 | 故宫 LBS 路线和 AR 导航 | 当前路径是模拟,详情导航只返回 | 先做静态路线状态,再接真实室内定位 | +| 定位与路线 | 故宫 LBS 路线和 AR 导航 | 当前路径是模拟,详情导航只返回 | 先做静态路线状态,再接真实馆内定位 | | 预下载 | British Museum 建议到馆前下载音频 | 当前没有缓存策略 | H5/小程序缓存地图、音频元数据、轻量图片 | | 多语言 | British Museum、The Met、MoMA 支持多语言或 Bloomberg Connects 多语言能力 | 当前只有中文 | 中期增加中英双语,长期多语言 | | 无障碍 | 故宫、British Museum、MoMA、The Met 都有可访问内容或服务入口 | 当前语义和内容均缺失 | 建立 a11y 组件规范和无障碍路线数据 | @@ -632,7 +632,7 @@ P2: - 自然馆真实内容 taxonomy 和 seed data。 - 展厅、展品、设施、路线的统一 ID。 - 音频资源和版权状态。 -- 室内点位坐标和无障碍设施数据。 +- 馆内点位坐标和无障碍设施数据。 - 是否接入实时客流、定位、AR 或 AI 后端服务。 ## 9. 用户测试计划 diff --git a/docs/miniapp_integration.md b/docs/miniapp_integration.md new file mode 100644 index 0000000..1506845 --- /dev/null +++ b/docs/miniapp_integration.md @@ -0,0 +1,365 @@ +# 小程序导览接口对接说明 + +> 统一维护约定:本文档是小程序对接的固定主文档。后续新增小程序 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`。 diff --git a/docs/pdca/actions.md b/docs/pdca/actions.md index 13fa5bb..829bb7b 100644 --- a/docs/pdca/actions.md +++ b/docs/pdca/actions.md @@ -2,7 +2,7 @@ | id | action | owner | due | status | source | | --- | --- | --- | --- | --- | --- | -| A-001 | 对 guide.whaoyue.com 执行 fresh browser/真实设备室内 3D 冒烟测试 | TBD | 2026-06-11 | todo | C-008 / R-002 | +| A-001 | 对 guide.whaoyue.com 执行 fresh browser/真实设备馆内 3D 冒烟测试 | TBD | 2026-06-11 | todo | C-008 / R-002 | | A-002 | 将用户流程闭环审计拆成可执行修复任务并排期 | TBD | 2026-06-11 | todo | C-007 / R-001 | | A-003 | 确认下一轮发布验收标准和负责人 | TBD | 2026-06-11 | todo | M-005 | | A-004 | 监控模型资源缓存与 404 行为,确认无 `2026-06-10
- 室内 3D 模型资源请求修复 + 馆内 3D 模型资源请求修复
M-002 已完成 @@ -1141,7 +1141,7 @@
- 浏览器 fresh load 复测室内 3D + 浏览器 fresh load 复测馆内 3D
编号: T-007状态: 待办负责人: 待确认截止日期: 2026-06-11阻塞项: 当前 in-app browser 调试通道初始化异常,需人工或恢复工具后复测
@@ -1243,9 +1243,9 @@ data-hard-blocked="false" data-missing-evidence="false" data-counted="true" - data-search="t-004 优化室内 3d 首屏加载策略 do done codex 2026-06-10 `src/components/navigation/guidemapshell.vue` 传入 `initial-view="floor"` 和当前楼层 id"> + data-search="t-004 优化馆内 3d 首屏加载策略 do done codex 2026-06-10 `src/components/navigation/guidemapshell.vue` 传入 `initial-view="floor"` 和当前楼层 id"> T-004 - 优化室内 3D 首屏加载策略 + 优化馆内 3D 首屏加载策略 执行 已完成 Codex @@ -1315,9 +1315,9 @@ data-hard-blocked="false" data-missing-evidence="false" data-counted="false" - data-search="t-007 浏览器 fresh load 复测室内 3d check todo tbd 2026-06-11 当前 in-app browser 调试通道初始化异常,需人工或恢复工具后复测"> + data-search="t-007 浏览器 fresh load 复测馆内 3d check todo tbd 2026-06-11 当前 in-app browser 调试通道初始化异常,需人工或恢复工具后复测"> T-007 - 浏览器 fresh load 复测室内 3D + 浏览器 fresh load 复测馆内 3D 检查 待办 待确认 @@ -1483,7 +1483,7 @@
- 对 guide.whaoyue.com 执行 fresh browser/真实设备室内 3D 冒烟测试 + 对 guide.whaoyue.com 执行 fresh browser/真实设备馆内 3D 冒烟测试
编号: A-001负责人: 待确认截止日期: 2026-06-11来源: C-008 / R-002
@@ -1542,7 +1542,7 @@
由项目内 PDCA 进度工具生成 · 来源:项目管理目录
- +