This commit is contained in:
621
docs/QA/codex-user-testing-agents-guide-2026-06-30.md
Normal file
621
docs/QA/codex-user-testing-agents-guide-2026-06-30.md
Normal file
@@ -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 只出方案不改代码,将 `<write_policy>` 改为 `read_only`。若希望 Codex 落地最小测试示例,将 `<write_policy>` 改为 `minimal_test_examples`。
|
||||
|
||||
```xml
|
||||
<task>
|
||||
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.
|
||||
</task>
|
||||
|
||||
<write_policy>
|
||||
read_only
|
||||
</write_policy>
|
||||
|
||||
<product_truth>
|
||||
- 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.
|
||||
</product_truth>
|
||||
|
||||
<agent_team>
|
||||
<lead_agent>
|
||||
Own scope, sequencing, final synthesis, risk ranking, and the final test matrix.
|
||||
</lead_agent>
|
||||
|
||||
<guide_agent>
|
||||
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
|
||||
</guide_agent>
|
||||
|
||||
<explain_agent>
|
||||
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
|
||||
</explain_agent>
|
||||
|
||||
<data_agent>
|
||||
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
|
||||
</data_agent>
|
||||
|
||||
<ux_agent>
|
||||
Inspect mobile H5 user flows, canvas/overlay hit areas, loading/error/empty states, return/close/cancel/retry paths, and dead-end risks.
|
||||
</ux_agent>
|
||||
|
||||
<qa_agent>
|
||||
Convert findings into a test matrix with preconditions, steps, expected result, evidence type, and priority.
|
||||
</qa_agent>
|
||||
|
||||
<risk_agent>
|
||||
Check for false navigation claims, placeholder audio, legacy demo pollution, direct raw-data coupling, static/api/sdk mixing, and unrelated refactors.
|
||||
</risk_agent>
|
||||
</agent_team>
|
||||
|
||||
<required_test_loops>
|
||||
<guide_loop>
|
||||
- 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.
|
||||
</guide_loop>
|
||||
|
||||
<explain_loop>
|
||||
- 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.
|
||||
</explain_loop>
|
||||
|
||||
<data_loop>
|
||||
- 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.
|
||||
</data_loop>
|
||||
</required_test_loops>
|
||||
|
||||
<execution_plan>
|
||||
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.
|
||||
</execution_plan>
|
||||
|
||||
<action_safety>
|
||||
- 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.
|
||||
</action_safety>
|
||||
|
||||
<grounding_rules>
|
||||
- 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.
|
||||
</grounding_rules>
|
||||
|
||||
<structured_output_contract>
|
||||
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.
|
||||
</structured_output_contract>
|
||||
|
||||
<completion_criteria>
|
||||
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.
|
||||
</completion_criteria>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
```
|
||||
@@ -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 接口只作为地图落点和位置预览的辅助数据。
|
||||
@@ -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 冒烟用例覆盖“导览、讲解、搜索、详情、路线”五条主链路。
|
||||
|
||||
|
||||
469
docs/QA/h5-guide-explain-regression-2026-07-01.md
Normal file
469
docs/QA/h5-guide-explain-regression-2026-07-01.md
Normal file
@@ -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 未验证前保持位置关系/位置预览,不作为正式室内导航
|
||||
```
|
||||
@@ -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
|
||||
<text class="floor-label">{{ floor.label }}</text>
|
||||
```
|
||||
|
||||
证据:
|
||||
|
||||
- `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
|
||||
|
||||
387
docs/QA/miniapp-guide-api-test-results-2026-07-02.md
Normal file
387
docs/QA/miniapp-guide-api-test-results-2026-07-02.md
Normal file
@@ -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` 的连通性。
|
||||
@@ -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` 接入并完成独立验证。
|
||||
- 当前文档只新增测试报告,不修改业务实现。
|
||||
|
||||
@@ -76,11 +76,11 @@
|
||||
- 楼层切换到目标所在楼层
|
||||
- 失败时显示"目标暂无三维位置数据"
|
||||
|
||||
### 验证5:路线页室外预览返回
|
||||
1. 路线页点击"查看室外地图"
|
||||
2. 应切换到室外地图参考(2D模式、入口提示)
|
||||
3. 点击"返回室内3D"或"返回预览"
|
||||
4. **预期**:返回室内3D预览,目标对象不丢失
|
||||
### 验证5:路线页馆外预览返回
|
||||
1. 路线页点击"查看馆外地图"
|
||||
2. 应切换到馆外地图参考(2D模式、入口提示)
|
||||
3. 点击"返回馆内3D"或"返回预览"
|
||||
4. **预期**:返回馆内3D预览,目标对象不丢失
|
||||
|
||||
### 验证6:顶部Tab全局切换
|
||||
1. 路线页点击顶部"讲解"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user