Files
frontend-miniapp/docs/Data/data-audit-2026-05-28.md

800 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# frontend-miniapp 数据层全面审计报告
审计日期2026-05-28
审计分支:`analysis/ux-ui-audit-2026-05-28`
审计对象:`museum-guide-v4.0/frontend-miniapp`
审计范围Mock 数据、类型定义、数据加载、搜索/地图/详情页硬编码数据、静态资源、3D/室内 POI 数据、数据治理与性能策略。
## 执行摘要
当前 `frontend-miniapp` 的数据层处于“多套 Mock 数据并存”的原型状态,不能直接作为深圳自然博物馆导览的数据基础。最高风险集中在三类:业务定位数据错配、数据源不统一、资源与关联关系不可验证。
| 优先级 | 关键风险 | 影响 | 建议 |
| --- | --- | --- | --- |
| P0 | 核心 JSON 仍是“深圳国际艺术馆/世界名画”内容,而产品标识是“深圳自然博物馆” | 搜索、详情、路线、讲解内容与真实馆方定位冲突 | 立即建立自然博物馆数据字典和迁移清单,冻结旧艺术馆 Mock 数据新增 |
| P0 | 列表、详情、地图、搜索各自硬编码不同实体与 ID | 用户从搜索/地图进入详情会看到不一致内容,甚至找不到对应数据 | 建立单一数据源,所有页面按 `entityId` 查询同一仓库 |
| P0 | 15 个图片/音频 URL 指向不存在资源,另有 `https://example.com/audio.mp3` 占位 | 展品图、展厅图、语音讲解失败,影响核心导览体验 | 建立资源清单校验,缺失资源上线前阻断 |
| P1 | 类型定义与真实 POI/地图数据不匹配 | TypeScript 无法保护 3D 坐标、入口/展厅/设施枚举,后续扩展易破 | 扩展 `Position``POI``FacilityType`、多语言与状态字段 |
| P1 | 无数据版本、校验、缓存、错误分级 | 数据异常会静默返回空数组,难以监控和降级 | 引入 schema 校验、数据版本元信息和错误态 UI |
建议采用两步治理:先用 1 周完成“数据源收敛和阻断性校验”,再用 2 到 4 周完成自然博物馆正式数据迁移、地图 POI 绑定、资源治理与多语言模型。
## 审计方法
本次审计使用静态扫描和结构化脚本校验,未修改业务代码。
| 方法 | 覆盖内容 | 证据来源 |
| --- | --- | --- |
| JSON 结构扫描 | 展品、展厅、设施、路线、楼层、室内 POI 数量与字段 | `src/assets/data/*.json``static/data/f1-indoor-pois.json` |
| 引用完整性校验 | 展品到展厅、楼层到展厅/设施、路线到站点 | Node 脚本读取 JSON 后交叉比对 |
| 静态资源存在性校验 | 图片、音频、展厅图是否存在于 `static/` | 文件系统校验 |
| 硬编码扫描 | 页面、组件、地图、搜索中的本地数组和占位 URL | `rg` 搜索 |
| 类型与数据流审查 | TypeScript 接口、数据加载、搜索工具、服务层 | `src/types/index.ts``src/utils/dataLoader.ts``src/utils/search.ts``src/services/map/Map3DManager.ts` |
## 数据资产盘点
| 数据源 | 数量 | 现状判断 | 主要问题 |
| --- | ---: | --- | --- |
| `src/assets/data/exhibits.json` | 5 | 艺术馆展品 Mock | 全部是世界名画,不是自然博物馆展品;图片/音频缺失 |
| `src/assets/data/halls.json` | 5 | 艺术馆展厅 Mock | 展厅主题为文艺复兴、印象派、现代艺术;展品数量与实际 JSON 不一致 |
| `src/assets/data/facilities.json` | 8 | 基础设施/模型坐标混合数据 | 点位坐标来自 `f1-floor.glb` 提取,应保留;但实体名称、类型、描述仍与真实 1F POI 和自然博物馆语义不一致 |
| `src/assets/data/routes.json` | 3 | 艺术馆路线 Mock | 路线名称、站点均围绕艺术作品;不适配自然博物馆参观动线 |
| `src/assets/data/floors.json` | 4 | 楼层索引 | `B1` 引用不存在设施,且无展厅 |
| `static/data/f1-indoor-pois.json` | 67 | 真实 1F 室内点位基准 | 其它设施、展厅、搜索、地图详情数据未以它为准,存在大面积冲突 |
| `static/models/*.glb` | 2 | 真实 1F 3D 模型资产 | `f1-indoor.glb``f1-floor.glb` 均为自然博物馆 1F 真实模型;需补模型清单、坐标系与部署目录策略 |
| `static/icons/*.svg` | 8 | 地图图标 | 可用,但 POI 类型枚举与图标映射未统一 |
当前数据流可以概括为:
```mermaid
flowchart TD
A["assets/data/*.json<br/>艺术馆核心 Mock"] --> B["dataLoader.ts"]
B --> C["部分列表/工具函数"]
D["pages/detail.vue<br/>硬编码详情"] --> E["详情页展示"]
F["SearchPanel / ExplainList<br/>自然博物馆硬编码 Mock"] --> G["搜索/讲解抽屉"]
H["TencentMap / ThreeMap<br/>硬编码地图点"] --> I["地图弹层"]
J["static/data/f1-indoor-pois.json<br/>67 个室内 POI"] -.未统一接入.-> I
```
## 1. 数据完整性审计
### 1.1 Mock 数据覆盖度
| 实体 | 当前覆盖 | 与真实自然博物馆导览的差距 | 严重性 |
| --- | --- | --- | --- |
| 展品 | 5 个艺术作品 | 缺少标本、化石、矿物、动植物、年代、分类、馆藏编号、展陈状态、讲解文本层级 | P0 |
| 展厅 | 5 个艺术主题展厅 | 缺少自然史展厅分区、楼层分布、入口/出口、展厅开放状态、人流/容量 | P0 |
| 设施 | 8 个基础设施 | 室内 POI 有 58 个设施,但未与设施表合并;缺少服务台、母婴室、寄存、无障碍、楼梯等正式类型 | P1 |
| 路线 | 3 条艺术馆路线 | 缺少亲子、研学、无障碍、快速参观、自然史主题路线 | P0 |
| 楼层 | 4 层索引 | 只有简单数组引用,无真实地图区域、楼层坐标系、模型版本、开放状态 | P1 |
| POI | F1 有 67 个模型点 | 只覆盖 `1F`,未绑定展品/展厅/路线/设施详情 | P1 |
### 1.2 必填字段缺失与占位符
结构字段层面,展品、展厅、设施、路线大多包含基础字段,但业务必填字段明显不足:没有馆藏编号、自然史分类、状态、更新时间、版权、资源元信息、语种、坐标系。
静态校验结果:
```text
COUNTS
exhibits: 5
halls: 5
facilities: 8
routes: 3
floors: 4
pois: 67
MISSING REQUIRED
exhibits: none
halls: none
facilities: none
routes: none
floors: floor_b1:halls
```
虽然基础字段未大量为空,但资源字段失效非常严重:
```text
exhibit_1.image -> /static/exhibits/mona-lisa.jpg missing
exhibit_1.audioUrl -> /static/audio/exhibit-1.mp3 missing
exhibit_2.image -> /static/exhibits/last-supper.jpg missing
exhibit_2.audioUrl -> /static/audio/exhibit-2.mp3 missing
exhibit_3.image -> /static/exhibits/starry-night.jpg missing
exhibit_3.audioUrl -> /static/audio/exhibit-3.mp3 missing
exhibit_4.image -> /static/exhibits/sunflowers.jpg missing
exhibit_4.audioUrl -> /static/audio/exhibit-4.mp3 missing
exhibit_5.image -> /static/exhibits/david.jpg missing
exhibit_5.audioUrl -> /static/audio/exhibit-5.mp3 missing
hall_1.image -> /static/halls/hall-1.jpg missing
hall_2.image -> /static/halls/hall-2.jpg missing
hall_3.image -> /static/halls/hall-3.jpg missing
hall_4.image -> /static/halls/hall-4.jpg missing
hall_5.image -> /static/halls/hall-5.jpg missing
```
页面中还存在音频占位 URL
```text
frontend-miniapp/src/pages/index/index.vue:306
audioUrl: exhibit.audioUrl || 'https://example.com/audio.mp3'
frontend-miniapp/src/pages/index/index.vue:355
audioUrl: exhibit.audioUrl || 'https://example.com/audio.mp3'
```
### 1.3 数据一致性
展厅统计与实际展品数量完全不一致:
| 展厅 | `halls.json` 标称展品数 | `exhibits.json` 实际归属数 | 差异 |
| --- | ---: | ---: | ---: |
| `hall_1` | 25 | 3 | -22 |
| `hall_2` | 18 | 2 | -16 |
| `hall_3` | 30 | 0 | -30 |
| `hall_4` | 22 | 0 | -22 |
| `hall_5` | 15 | 0 | -15 |
同一逻辑实体在不同模块中出现了多套 ID 和名称:
| 模块 | 示例 | 问题 |
| --- | --- | --- |
| 核心 JSON | `exhibit_1``蒙娜丽莎` | 艺术馆实体 |
| 搜索页 | `id: '1', name: '蒙娜丽莎'` | ID 不是 `exhibit_1`,进入详情时无法保证匹配 |
| 搜索面板 | `id: '1', name: '霸王龙化石骨架'` | 同一 ID `1` 在另一模块代表自然博物馆展品 |
| 讲解抽屉 | `id: '1', name: '银杏化石'` | 同一 ID `1` 又代表第三个实体 |
| 地图首页 | `exhibit-1``恐龙化石展区` | ID 命名与核心 JSON 不一致 |
| 3D 地图 | `exhibit-1``恐龙化石展区` | 与 `exhibit_1` 不一致,且未绑定详情数据 |
硬编码证据截图:
```text
frontend-miniapp/src/pages/search/index.vue:101
{ id: '1', name: '蒙娜丽莎', artist: '达芬奇', hall: '1号展厅', hasAudio: true }
frontend-miniapp/src/components/search/SearchPanel.vue:143
{ id: '1', name: '霸王龙化石骨架', desc: '恐龙厅 · 2F', type: 'exhibit' }
frontend-miniapp/src/components/explain/ExplainList.vue:293
{ id: '1', name: '银杏化石', hall: '演化厅 B1', hasAudio: true, audioUrl: '' }
frontend-miniapp/src/pages/index/index.vue:164
'exhibit-1': { id: 'exhibit-1', name: '恐龙化石展区', subtitle: '中生代恐龙化石展示', type: 'exhibit', floor: '2F' }
```
### 1.4 关联关系完整性
路线站点都能在艺术馆展品表中找到,但楼层到设施存在断链:
```text
BROKEN REFERENCES
floor floor_b1 facility parking_b1
floor floor_b1 facility storage_b1
```
更大的关联问题是 `static/data/f1-indoor-pois.json` 与核心实体完全脱钩。该文件已确认为真实 1F 设施点位基准,包含 67 个 POI类型分布为
```text
POI TYPES
{ hall: 6, facility: 58, entrance: 3 }
POI floors: 1F
```
这些 POI 的 `id` 形如 `poi_0`,没有 `entityId` 指向 `hall_1``facility_*` 或路线站点,无法支撑“点击地图点打开统一详情”“按路线高亮真实点位”等核心导览能力。
### 1.5 数据时效性
当前数据没有以下字段,无法判断是否过期:
| 缺失能力 | 影响 |
| --- | --- |
| `status` / `visibility` | 无法区分开放、临时关闭、下架、维护中 |
| `validFrom` / `validTo` | 无法表达临展、活动、设施维修周期 |
| `updatedAt` / `sourceVersion` | 无法追踪数据来源和更新批次 |
| `reviewedBy` / `reviewedAt` | 无法形成馆方内容审核闭环 |
因此“已下架展品、已关闭设施”目前无法自动识别,只能依赖人工回归。
## 2. 数据结构与类型规范性
### 2.1 TypeScript 类型定义
`src/types/index.ts` 更像早期 Mock 类型,无法覆盖现有地图和真实业务数据。
| 类型 | 当前定义 | 发现的问题 | 严重性 |
| --- | --- | --- | --- |
| `Position` | `{ x, y }` | 2D 坐标,不支持室内 POI 的 `{ x, y, z }`,也不支持腾讯地图的 `{ latitude, longitude }` | P1 |
| `Facility.type` | `restroom/cafe/shop/exit/elevator/info` | 不包含 `entrance``hall``stairs``mother_baby``accessible_restroom``service_desk` 等真实 POI 类型 | P1 |
| `POIMarker.type` | `exhibit/hall/facility/location` | 不包含 `entrance`,与 `ThreeMap``f1-indoor-pois.json` 不一致 | P1 |
| `Exhibit` | 艺术品字段 `artist/year/material/size` | 自然博物馆需要 `taxonomy``period``specimenType``collectionNo``scientificName``ageRange` 等字段 | P0 |
| 全部实体 | 无多语言、无状态、无版本 | 无法支撑中英文切换、数据审核、增量更新和下线 | P1 |
建议新增分层模型:
```ts
type LocaleText = { zhCN: string; enUS?: string }
type Coordinate =
| { system: 'indoor_3d'; floorId: string; x: number; y: number; z: number }
| { system: 'indoor_2d'; floorId: string; x: number; y: number }
| { system: 'wgs84' | 'gcj02'; latitude: number; longitude: number }
type EntityStatus = 'draft' | 'published' | 'hidden' | 'closed' | 'maintenance'
```
### 2.2 API 响应格式
`src/services/` 下只有 `map/Map3DManager.ts`,没有业务 API service。数据加载集中在 `src/utils/dataLoader.ts`,但返回值未声明类型,错误时统一返回空数组:
```ts
export const loadExhibits = async () => {
try {
const data = await import('@/assets/data/exhibits.json')
return data.default || data
} catch (error) {
console.error('加载展品数据失败:', error)
return []
}
}
```
风险:
| 问题 | 影响 |
| --- | --- |
| 无 `Promise<Exhibit[]>` 等返回类型 | 调用方无法获得类型保护 |
| 使用 `any` 查找 | ID 类型、字段缺失、空数据不会被编译期发现 |
| 异常统一变成 `[]` | 加载失败、数据为空、校验失败三种状态无法区分 |
| 无响应 envelope | 后续接 API 时缺少 `version``updatedAt``source``errors``pagination` 规范 |
建议定义统一响应:
```ts
interface DataResult<T> {
ok: boolean
data: T
version: string
updatedAt: string
source: 'mock' | 'cms' | 'cache'
errors?: DataError[]
}
```
### 2.3 枚举值规范
枚举值目前分散在类型、地图、组件、数据文件中:
| 位置 | 枚举/类别 | 问题 |
| --- | --- | --- |
| `types/index.ts` | `Facility.type` | 只覆盖 6 种旧设施 |
| `components/area/AreaSelector.vue` | `storage``accessible` 等 | UI 类别未进入类型系统 |
| `static/data/f1-indoor-pois.json` | `hall/facility/entrance` | 与 `POIMarker.type` 不一致 |
| `ThreeMap.vue` | `entrance/exhibit/facility/hall` | 局部定义,与全局类型重复 |
| `TencentMap.vue` | marker 图标路径 | 通过硬编码绑定,不受枚举约束 |
建议将枚举集中到 `src/types/enums.ts``src/domain/guideSchema.ts`,并由数据校验脚本复用。
### 2.4 数据转换逻辑
当前没有明确的 adapter/transformer。结果是 2D JSON、3D POI、腾讯地图经纬度、页面详情对象各自拥有不同字段形态。
建议新增三层转换:
| 层级 | 职责 |
| --- | --- |
| Raw Schema | 接收 CMS/JSON 原始字段,严格校验 |
| Domain Model | 统一实体、坐标、资源、状态、多语言 |
| View Model | 为卡片、地图弹层、路线列表、搜索结果生成轻量字段 |
## 3. 业务数据合理性
### 3.1 内容与项目定位不符
项目描述与页面标题已经使用“深圳自然博物馆”,但核心数据仍是艺术馆:
| 位置 | 当前内容 | 业务判断 |
| --- | --- | --- |
| `manifest.json` | `深圳自然博物馆智能导览应用` | 项目定位明确为自然博物馆 |
| `exhibits.json` | `蒙娜丽莎``最后的晚餐``星空``向日葵``大卫像` | 与自然博物馆定位冲突 |
| `halls.json` | 文艺复兴、印象派、现代艺术 | 与自然史展陈冲突 |
| `routes.json` | 经典艺术之旅、印象派精选 | 与自然博物馆导览路线冲突 |
| `SearchPanel.vue` / `ExplainList.vue` | 恐龙、化石、银杏、矿石 | 内容方向正确,但与核心 JSON 脱节 |
这是 P0 风险,因为它会直接导致“产品名是自然博物馆,但内容是艺术馆”的体验断裂。
### 3.2 数量级合理性
真实自然博物馆导览通常需要覆盖更多实体:
| 数据类型 | 当前数量 | 建议 MVP 数量 | 建议正式版数量 |
| --- | ---: | ---: | ---: |
| 展品/标本 | 5 | 30 到 60 | 200+ |
| 展厅/展区 | 5 | 8 到 12 | 按真实楼层和展陈分区完整覆盖 |
| 设施 | 8 | 30 到 60 | 与室内 POI 全量绑定 |
| 路线 | 3 | 5 到 8 | 支持人群、时长、无障碍、拥堵策略 |
| 室内 POI | 67仅 1F | 每层 50+ | 全楼层、全关键节点 |
当前数量只适合演示 UI无法支撑真实导览。
### 3.3 地理坐标准确性
坐标系统存在三套并行:
| 坐标来源 | 字段 | 问题 |
| --- | --- | --- |
| 核心 JSON | `position: { x, y }` | 未声明坐标系、楼层、比例尺和原点 |
| 3D POI | `{ x, y, z }` | 未进入 `types/index.ts`,未绑定实体 |
| 腾讯地图 | `{ latitude, longitude }` | 硬编码在组件内,未与室内 POI 建立转换关系 |
`TencentMap.vue` 里的建筑轮廓和 `ThreeMap.vue` 的中心点均硬编码为深圳自然博物馆附近坐标,但无法验证这些点与 GLB 模型、室内 POI、楼层图是否同源。
### 3.4 多语言数据
当前核心实体没有英文名、英文说明、拼音、别名、检索关键词,也没有语言切换后的字段 fallback。
建议每个可展示字段采用 `LocaleText`,搜索索引包括:
| 字段 | 用途 |
| --- | --- |
| `name.zhCN` / `name.enUS` | 中英文展示 |
| `aliases.zhCN[]` / `aliases.enUS[]` | 同义词、简称、俗名 |
| `scientificName` | 自然史学名 |
| `keywords[]` | 搜索、推荐、路线匹配 |
| `audio.zhCN` / `audio.enUS` | 多语言讲解 |
## 4. 数据管理与维护性
### 4.1 数据源管理
当前数据分散在至少 11 类位置:
| 数据位置 | 数据类型 | 风险 |
| --- | --- | --- |
| `src/assets/data/*.json` | 核心 Mock | 内容过旧,艺术馆化 |
| `static/data/f1-indoor-pois.json` | 3D 模型 POI | 未接入业务实体 |
| `pages/search/index.vue` | 搜索页本地数组 | 与核心 JSON ID 不一致 |
| `components/search/SearchPanel.vue` | 搜索弹层本地数组 | 自然博物馆 Mock与核心 JSON 不一致 |
| `components/explain/ExplainList.vue` | 讲解列表本地数组 | 音频字段为空ID 冲突 |
| `pages/index/index.vue` | 地图详情 `markerDataMap` | 自然博物馆硬编码,与 POI/JSON 不一致 |
| `components/map/ThreeMap.vue` | `defaultPOIs` | 默认值会被空数组 props 覆盖,且 ID 不统一 |
| `components/map/TencentMap.vue` | 经纬度 markers | 地图数据不可配置 |
| `pages/exhibit/detail.vue` | 展品详情默认对象 | 不按 ID 加载真实数据 |
| `pages/hall/detail.vue` | 展厅详情默认对象 | 不按 ID 加载真实数据 |
| `pages/facility/detail.vue` | 设施详情默认对象 | 不按 ID 加载真实数据 |
建议将所有页面改为读取同一份 domain store
```mermaid
flowchart LR
A["CMS / Mock JSON"] --> B["schema validation"]
B --> C["domain repository"]
C --> D["search index"]
C --> E["map POI view model"]
C --> F["detail pages"]
C --> G["route engine"]
C --> H["offline cache"]
```
### 4.2 数据更新机制
当前没有发现以下机制:
| 机制 | 当前状态 | 建议 |
| --- | --- | --- |
| 数据版本控制 | 无 `version` 文件 | 新增 `data-manifest.json` |
| 增量更新 | 无 | 按实体类型和 `updatedAt` 增量拉取 |
| 本地缓存 | 未见 `uni.setStorage`/`uni.getStorage` 数据缓存 | 小程序端缓存核心 JSON 和搜索索引 |
| 灰度发布 | 无 | 支持 `draft/published` 与版本回滚 |
| 数据来源追踪 | 无 | 每条记录保留 `sourceId``sourceUpdatedAt` |
### 4.3 数据校验
建议建立 `scripts/audit-data.ts`,在 CI 或提交前执行:
| 校验类型 | 规则 |
| --- | --- |
| Schema 校验 | 必填字段、枚举、坐标类型、资源字段 |
| 引用校验 | 展品到展厅、路线到站点、POI 到实体、楼层到设施 |
| 资源校验 | 图片/音频/模型路径存在,远程 URL 可访问或有降级 |
| 业务校验 | 开放时间格式、路线时长总和、展厅展品数可计算 |
| 多语言校验 | 中文必填,英文按上线策略设阈值 |
| 审核状态校验 | 非 `published` 数据不得进入正式包 |
### 4.4 错误处理
当前 `dataLoader.ts` 在异常时返回空数组,页面无法知道错误原因。建议改为:
| 异常类型 | 降级方式 |
| --- | --- |
| JSON 加载失败 | 展示“数据暂不可用”,上报错误码 |
| Schema 校验失败 | 阻止使用该批数据,回退到上一个缓存版本 |
| 资源缺失 | 展示占位图,隐藏播放按钮,上报资源 ID |
| 关联断链 | 从路线/地图中隐藏该站点,保留错误日志 |
| 多语言缺失 | 回退中文,并记录缺失翻译 |
## 5. 性能与优化
### 5.1 数据体积与加载策略
当前 JSON 体积不大;`static/models/` 下模型已确认为自然博物馆 1F 真实 3D 模型资产,不应按普通占位资源处理。但模型资产仍需要元数据和部署治理:
| 资源 | 路径 | 大小 |
| --- | --- | ---: |
| 室内模型 | `static/models/f1-indoor.glb` | 1,562,816 bytes |
| 楼层模型 | `static/models/f1-floor.glb` | 95,720 bytes`facilities.json` 点位坐标来源 |
| 部署副本 | `public/models/f1-indoor.glb``public/models/f1-floor.glb` | 与 `static/models` 同大小;如果两端都会打包,需要明确去重或按端分发 |
风险:
| 问题 | 影响 |
| --- | --- |
| `public/models``static/models` 同时存在 | 若二者同时进入同一端包体,会造成重复;若分别服务 web/小程序,需要 manifest 标注用途 |
| 图片/音频 URL 缺失 | 无法验证真实媒体体积和懒加载策略 |
| 3D 模型与 POI 分离加载 | 需要版本锁定,否则 POI 坐标与模型不匹配 |
建议:
| 优先级 | 建议 |
| --- | --- |
| P1 | 明确模型部署目录策略:同一端只保留一份,跨端复用则在 manifest 中声明 |
| P1 | 模型按楼层拆包并懒加载,非当前楼层不加载 |
| P1 | 图片提供缩略图、详情图、原图三级资源 |
| P2 | 音频使用按需加载,播放前预取下一站讲解 |
### 5.2 请求与预加载
当前没有真实请求层,数据通过动态 import 加载;页面和组件中重复本地数组导致“无请求但重复数据维护”。未来接 API 时建议:
| 场景 | 策略 |
| --- | --- |
| 首页启动 | 加载 `data-manifest`、楼层、轻量 POI、热门展品 |
| 搜索打开 | 延迟构建或读取本地搜索索引 |
| 进入详情 | 按 `entityId` 读取详情,缺资源时显示降级态 |
| 开始路线 | 预加载路线站点、当前位置楼层 POI、下一站音频元信息 |
### 5.3 本地存储与离线可用性
导览类小程序对弱网很敏感,应支持基础离线:
| 数据 | 离线等级 |
| --- | --- |
| 楼层、展厅、设施、核心 POI | 必须离线可用 |
| 展品详情、路线 | 建议离线可用 |
| 图片缩略图 | 建议缓存最近访问和路线相关资源 |
| 音频 | 可选缓存,需考虑版权与包体 |
| 3D 模型 | 按楼层缓存,版本变更后更新 |
## 数据问题清单
### P0 阻塞性问题
| ID | 问题 | 证据 | 影响 | 修复建议 |
| --- | --- | --- | --- | --- |
| D-P0-01 | 核心数据仍是艺术馆内容 | `exhibits.json``蒙娜丽莎` 等世界名画 | 产品定位与内容冲突 | 建立自然博物馆实体数据集并替换核心 JSON |
| D-P0-02 | 多套硬编码实体互相冲突 | 同一 `id: '1'` 分别代表蒙娜丽莎、霸王龙、银杏化石 | 搜索、讲解、详情跳转不可信 | 收敛到统一 `entityId`,禁止页面本地 Mock |
| D-P0-03 | 资源 URL 大量失效 | 5 张展品图、5 个音频、5 张展厅图缺失 | 图片/音频核心体验失败 | 上线前加入资源存在性校验 |
| D-P0-04 | 详情页未按 ID 加载 | `exhibit/detail.vue``hall/detail.vue` 等使用默认对象 | 用户点击任意实体都可能看到同一详情 | 详情页接入 repository `findById` |
| D-P0-05 | 路线仍为艺术馆路线 | `routes.json` 为经典艺术之旅等 | 参观规划完全不适配自然博物馆 | 重建路线数据模型和正式路线 |
### P1 重要问题
| ID | 问题 | 证据 | 影响 | 修复建议 |
| --- | --- | --- | --- | --- |
| D-P1-01 | 楼层引用断链 | `floor_b1` 引用 `parking_b1``storage_b1` | 楼层设施展示不完整 | 补齐设施或删除无效引用 |
| D-P1-02 | 展厅 `exhibitCount` 与实际数量不一致 | `hall_1` 标称 25实际 3 | 统计误导用户 | 改为派生计算或校验阻断 |
| D-P1-03 | 3D POI 未绑定实体 | 67 个 `poi_*``entityId` | 地图无法打开统一详情 | 增加 `entityType/entityId` |
| D-P1-04 | 类型定义不支持 3D/经纬度坐标 | `Position` 只有 `{x,y}` | 地图类型保护失效 | 定义统一 `Coordinate` union |
| D-P1-05 | 设施枚举不完整 | 真实 POI 含入口、母婴室、服务台等 | UI 图标和筛选无法统一 | 建立设施类型字典 |
| D-P1-06 | 无数据版本与缓存策略 | 未见 manifest 和 storage 机制 | 无法增量更新、离线弱 | 新增 `data-manifest` 和本地缓存 |
| D-P1-07 | 错误处理吞掉原因 | `dataLoader` 异常返回 `[]` | 无法区分空数据和加载失败 | 返回 `DataResult<T>` |
| D-P1-08 | 多语言字段缺失 | 核心 JSON 无英文/别名/学名 | 国际访客和英文搜索不可用 | 引入 `LocaleText` 与搜索索引 |
### P2 优化项
| ID | 问题 | 证据 | 影响 | 修复建议 |
| --- | --- | --- | --- | --- |
| D-P2-01 | 模型部署目录未说明 | `static/models``public/models` 同名同大小 | 可能包体重复,也可能是跨端分发但缺少说明 | 建立模型 manifest明确每个端使用哪个路径 |
| D-P2-02 | 搜索工具未统一使用 | 页面自建数组,`utils/search.ts` 被绕开 | 搜索规则重复 | 搜索统一走 index service |
| D-P2-03 | 缺少资源元数据 | 图片/音频只有 URL | 版权、尺寸、时长不可控 | 建立 `Asset` 模型 |
| D-P2-04 | 缺少数据生成文档 | 无数据字典和维护手册 | 后续运营难维护 | 建立数据治理规范 |
## 数据迁移方案:从艺术馆到深圳自然博物馆
### 阶段 0冻结与基线1 到 2 天
目标:防止旧 Mock 继续扩散。
| 任务 | 产出 |
| --- | --- |
| 标记艺术馆数据为 legacy | `legacy-art-gallery` 数据目录或删除计划 |
| 建立数据审计脚本 | 可输出数量、断链、资源缺失 |
| 定义自然博物馆实体清单 | 展厅、展品、设施、路线、POI、资源字段 |
| 统一 ID 规范 | `exhibit_dino_trex_001``hall_dinosaur_2f``facility_restroom_1f_east` |
### 阶段 1核心模型重建3 到 5 天
目标:让所有页面可以读同一套自然博物馆数据。
| 任务 | 产出 |
| --- | --- |
| 重写 TypeScript domain 类型 | `Exhibit` 从艺术品模型改为自然史模型 |
| 新增 `Coordinate``Asset``LocaleText``EntityStatus` | 支撑地图、资源、多语言、上下线 |
| 建立 mock data v1 | 至少 30 个展品、8 个展厅、30 个设施、5 条路线 |
| 改造 `dataLoader` | 返回 typed `DataResult<T>`,支持校验错误 |
| 改造详情页 | 按 URL `id` 从 repository 查询 |
### 阶段 2地图与路线绑定5 到 8 天
目标地图、POI、路线、详情形成闭环。
| 任务 | 产出 |
| --- | --- |
| 为 67 个 F1 POI 增加 `entityType/entityId` | 点击 POI 打开统一详情 |
| 建立楼层坐标系元信息 | `origin``scale``rotation``modelVersion` |
| 把 `TencentMap`/`ThreeMap` 的硬编码 marker 迁出组件 | 地图 view model |
| 路线站点绑定 POI | 路线可在 2D/3D 地图高亮 |
| 增加无障碍/亲子/研学路线 | 覆盖典型人群 |
### 阶段 3资源、多语言与离线5 到 10 天
目标:接近可运营数据质量。
| 任务 | 产出 |
| --- | --- |
| 建立图片、音频资源清单 | 路径、尺寸、时长、版权、语言 |
| 替换占位 URL 和缺失资源 | 所有核心展品可展示/播放 |
| 建立中英文内容字段 | 英文名、学名、英文简介、英文音频 |
| 生成搜索索引 | 支持中文、英文、别名、学名 |
| 增加本地缓存和版本更新 | 弱网可用,支持增量更新 |
### 阶段 4CMS/API 接入2 到 4 周
目标:从静态 Mock 过渡到可维护的馆方内容系统。
| 任务 | 产出 |
| --- | --- |
| 定义 API response envelope | `version/source/updatedAt/errors` |
| 接入 CMS 或后台服务 | 馆方可维护内容 |
| 增加审核流 | draft 到 published |
| 增加数据监控 | 资源失效、断链、翻译缺失告警 |
| 建立回滚机制 | 数据版本可回退 |
## 数据治理建议
### 数据目录建议
```text
src/domain/
guideTypes.ts
guideEnums.ts
guideRepository.ts
guideAdapters.ts
src/assets/data/
data-manifest.json
museum/
exhibits.json
halls.json
facilities.json
floors.json
routes.json
pois.json
assets.json
scripts/
audit-data.ts
build-search-index.ts
```
### 数据质量门禁
| 门禁 | 触发时机 | 阻断规则 |
| --- | --- | --- |
| 本地审计 | 提交前 | P0 不允许提交 |
| CI 审计 | PR/合并前 | P0/P1 必须有处理或豁免 |
| 资源审计 | 打包前 | 核心图片/音频缺失阻断 |
| 内容审计 | 发布前 | 未审核、未发布数据不能进正式包 |
| 多语言审计 | 英文版发布前 | 英文关键字段缺失阻断 |
### 数据字典建议
每个实体至少包含:
| 字段组 | 必填内容 |
| --- | --- |
| 标识 | `id``type``status``version` |
| 展示 | `name``summary``description``keywords` |
| 位置 | `floorId``hallId``coordinates[]``poiIds[]` |
| 资源 | `coverImage``gallery[]``audio[]``modelAssetId` |
| 运营 | `validFrom``validTo``updatedAt``reviewedAt` |
| 多语言 | `zhCN` 必填,`enUS` 按上线范围要求 |
## 预估修复工作量
| 工作包 | 角色 | 预估工时 | 优先级 |
| --- | --- | ---: | --- |
| 数据审计脚本与 CI 门禁 | 前端/工具链 | 1 到 2 人日 | P0 |
| 自然博物馆数据模型重建 | 前端 + 内容策划 | 2 到 3 人日 | P0 |
| 核心 Mock 数据迁移 v1 | 内容策划 + 前端 | 3 到 5 人日 | P0 |
| 详情页/搜索/地图统一接 repository | 前端 | 4 到 6 人日 | P0 |
| POI 与实体绑定 | 前端 + 3D/地图 | 3 到 5 人日 | P1 |
| 资源清单与缺失资源替换 | 内容/设计/前端 | 3 到 6 人日 | P1 |
| 多语言字段与搜索索引 | 前端 + 翻译/内容 | 4 到 8 人日 | P1 |
| 离线缓存与增量更新 | 前端 | 4 到 7 人日 | P1 |
| CMS/API 接入 | 后端 + 前端 | 10 到 20 人日 | P1 |
建议最小可交付排期:
| 周期 | 目标 | 验收标准 |
| --- | --- | --- |
| 短期1 周内 | 解决 P0 | 核心数据为自然博物馆;搜索/详情/地图 ID 统一;资源缺失校验可运行 |
| 中期2 到 4 周 | 解决主要 P1 | POI 绑定、路线可用、缓存和版本机制上线 |
| 长期1 到 2 个月 | 数据治理闭环 | CMS/API、审核流、多语言、监控和回滚机制可用 |
## 下一步行动清单
1. 建立 `scripts/audit-data.ts`,把本报告中的数量、断链、资源缺失、统计不一致变成可重复命令。
2. 先删除或隔离旧艺术馆 Mock 数据,防止新页面继续引用。
3. 制定深圳自然博物馆首批 MVP 数据清单8 个展厅、30 个展品、30 个设施、5 条路线、F1 全量 POI 绑定。
4. 改造 `dataLoader` 为 typed repository详情页、搜索、地图统一从 repository 读取。
5. 在发布流程中加入 P0 数据门禁:资源缺失、引用断链、实体冲突、非自然博物馆内容不得进入正式包。
## 补充审计:以真实 F1 点位为基准
补充前提:`static/data/f1-indoor-pois.json` 是真实 1 楼设施点位信息。因此它不应被视为普通 Mock而应作为校验 1F 设施、展厅、入口、地图点击、搜索和筛选数据的基准。
### 基准数据摘要
| 指标 | 结果 |
| --- | --- |
| POI 总数 | 67 |
| 类型分布 | `facility: 58``hall: 6``entrance: 3` |
| 楼层分布 | 全部为 `1F` |
| 坐标范围 | `x: -106.59 到 150.26``y: 0 到 0``z: -37.73 到 79.06` |
| 重复坐标 | 无 |
| 加载路径 | `Map3DManager` 会从 `/static/models/f1-indoor.glb` 推断并加载 `/static/data/f1-indoor-pois.json` |
真实 1F 点位包含的代表性设施和区域:
```text
展厅/场馆:展厅人类厅、巨幕影院、动感多维影院、自然剧场、球幕影院、展厅
入口/服务:售票处、售票机、服务台
设施:女卫、男卫、无障碍卫生间、楼梯、电梯、扶梯、存包处、轮椅及儿童车租车处、贵宾接待区、贵宾卫生间、茶水间、母婴间
```
### 真实 F1 点位反查出的新增问题
| 严重性 | 问题 | 证据 | 影响 | 建议 |
| --- | --- | --- | --- | --- |
| P0 | `facilities.json` 的 1F 设施与真实点位完全不匹配 | 1F 设施只有 `洗手间``艺术咖啡厅``艺术品商店``主出口`,真实 POI 中均无这些同名点位 | 设施列表、设施详情、楼层设施入口都会展示不存在或不准确的设施 | 以 `f1-indoor-pois.json` 生成 1F 设施实例,旧艺术馆设施迁入 legacy |
| P0 | `halls.json` 的 1F 展厅与真实点位完全不匹配 | 1F 展厅为 `1号展厅``2号展厅`;真实 POI 展厅为 `展厅人类厅``巨幕影院``动感多维影院``自然剧场``球幕影院``展厅` | 地图点位点击与展厅详情无法对应,展厅统计不可用 | 建立真实 1F 展厅/场馆实体,并用 `poiId` 绑定 |
| P0 | 3D 点击逻辑会忽略大部分真实设施点位 | `handlePOIClick` 只处理 `exhibit``hall`;真实 POI 中 58/67 为 `facility` | 用户点击厕所、电梯、楼梯、母婴间、售票处等核心服务点位不会弹出详情 | 点击逻辑必须支持 `facility``entrance`,并进入统一设施详情/导航 |
| P1 | 真实 POI 与 `markerDataMap` 无法映射 | 真实 ID 为 `poi_0``poi_66`;首页映射使用 `0``1``hall-1``exhibit-1` | POI 点击只能走临时 fallback无法获得图片、说明、导航策略、收藏状态 | 用 `poiId``entityId` 建立真实映射,删除手写 `markerDataMap` |
| P1 | `floors.json` 的 1F 设施引用严重不足 | `floor_1f.facilities` 只引用 3 个设施,真实 1F 有 58 个 facility POI 和 3 个 entrance POI | 楼层筛选和设施面板无法覆盖真实服务点 | `floor_1f` 应引用真实 POI 或引用从 POI 派生的 facility instances |
| P1 | 设施类型粒度不足 | 真实 POI 全部压成 `facility/entrance/hall`,具体类别藏在中文 label 中 | 搜索、筛选、图标、无障碍路线无法可靠判断类别 | 增加 `category/subtype`,如 `restroom_female``elevator``stairs``ticket_office``nursing_room` |
| P1 | UI 筛选分类与真实标签没有规范映射 | `AreaSelector``卫生间/电梯/楼梯/停车场/母婴室/服务中心/寄存处/饮水处/影院`,真实标签是 `女卫001``电梯020``楼梯.001``存包处``茶水间` 等 | 用户选择分类后很难精确筛出真实点位 | 建立 label 归一化和分类映射表,区分展示名与检索名 |
| P1 | 搜索数据未覆盖真实 1F 设施 | 搜索 Mock 只有 `洗手间``咖啡厅``服务中心``纪念品商店` 等 | 搜索不到真实存在的售票机、服务台、母婴间、轮椅及儿童车租车处、贵宾卫生间等 | 搜索索引从真实 POI 和设施实体生成 |
| P1 | 存在两套 POI 坐标模型 | 真实 POI 为 `{x,y,z}``ThreeMap.vue` props/default POI 为 `{latitude,longitude}` | 组件接口同名但数据形态不同,后续接入容易错用 | 将室内 3D POI 与室外经纬度 POI 拆成不同类型 |
| P2 | 真实 POI label 带模型导出痕迹 | `电梯001``楼梯.001``无障碍卫生间.001` | 直接展示会显得粗糙,也不利于搜索同义词 | 保留原始 label同时新增 `displayName``instanceNo``normalizedName` |
| P2 | `entrance` 类型语义混杂 | `售票处``售票机``服务台` 被标为 `entrance` | 类型名称无法表达真实服务属性 | 原始类型可保留,业务层补充 `category: ticket/service` |
### 与真实 F1 点位冲突的关键文件
| 文件 | 冲突点 | 处理建议 |
| --- | --- | --- |
| `src/assets/data/facilities.json` | 1F 设施与真实 POI 无同名匹配 | 用真实 POI 重建 1F facilities |
| `src/assets/data/halls.json` | 1F 展厅名称和数量与真实 POI 冲突 | 重建 halls并绑定 `poi_0/poi_51``poi_55` |
| `src/assets/data/floors.json` | 1F 只引用 3 个旧设施 | 从真实 POI 派生楼层设施索引 |
| `src/pages/index/index.vue` | `markerDataMap` 不认识 `poi_*`,点击设施被忽略 | 改为根据 `poiId/entityId` 查询 repository |
| `src/components/map/ThreeMap.vue` | 默认 POI 使用经纬度,和真实室内 `{x,y,z}` POI 混用 | 拆分 `IndoorPOI``OutdoorMarker` |
| `src/types/index.ts` | `Position` 只有 `{x,y}``POIMarker` 不含 `entrance` | 扩展坐标和 POI 类型 |
| `src/components/search/SearchPanel.vue` | 搜索 Mock 未来自真实 POI | 搜索索引从 `pois + entities` 生成 |
| `src/components/area/AreaSelector.vue` | 分类只停留在 UI 文案,没有连接真实 POI subtype | 建立设施分类字典和 label 归一化规则 |
### 建议的数据归一化模型
真实 POI 文件可以继续作为原始点位数据,但业务层需要派生规范实体:
```ts
interface RawIndoorPOI {
id: string
label: string
x: number
y: number
z: number
type: 'hall' | 'facility' | 'entrance'
floor: '1F'
}
interface FacilityInstance {
id: string
poiId: string
floorId: string
displayName: string
normalizedName: string
category: 'restroom' | 'elevator' | 'stairs' | 'escalator' | 'ticket' | 'service' | 'storage' | 'nursing' | 'theater' | 'vip' | 'water' | 'entrance'
accessibility?: string[]
coordinate: { system: 'indoor_3d'; modelId: 'f1-indoor'; x: number; y: number; z: number }
}
```
### 更新后的优先修复顺序
1. P0让 3D POI 点击支持 `facility``entrance`,否则真实 1F 点位 61 个服务类点位无法交互。
2. P0用真实 POI 重建 1F `facilities``halls`,移除艺术馆设施/展厅对 1F 的覆盖。
3. P1建立 `poiId -> entityId` 映射,让地图、搜索、详情、路线共享同一实体。
4. P1新增设施分类字典`女卫001``男卫002``电梯020``楼梯.001` 等归一到可筛选类别。
5. P1`floor_1f` 从真实 POI 派生设施和展厅索引,避免手工维护数量不一致。
6. P2保留原始 label 作为 `sourceLabel`,增加面向用户的 `displayName`,避免模型导出编号直接暴露。
## 补充审计:真实模型与 `f1-floor.glb` 坐标来源
补充前提:`frontend-miniapp/static/models/` 下的模型是自然博物馆 1F 真实 3D 模型;`facilities.json` 中的点位坐标来自 `frontend-miniapp/static/models/f1-floor.glb` 提取,目的是为后续 web 端页面与模型交互准备数据。
因此,对模型和坐标的审计结论需要修正为:模型资产和部分坐标来源可信,不能简单当作 Mock 删除;真正的问题是缺少模型元数据、坐标系说明、实体语义绑定和跨端资源治理。
### 修正后的判断
| 数据/资产 | 可信部分 | 仍存在的问题 |
| --- | --- | --- |
| `static/models/f1-indoor.glb` | 真实 1F 室内 3D 模型 | 缺少 `modelId`、hash、版本、来源、坐标系、比例尺、与 POI 文件的绑定说明 |
| `static/models/f1-floor.glb` | 真实 1F 楼层模型,且是 `facilities.json.position` 的提取来源 | `facilities.json` 未记录 `sourceModel`、提取工具、提取时间、坐标原点、单位、投影规则 |
| `static/data/f1-indoor-pois.json` | 真实 1F POI 点位,使用 `{x,y,z}` 模型空间坐标 | 未绑定业务实体label 仍是模型导出名称,需要业务归一化 |
| `src/assets/data/facilities.json` | `position` 字段有真实模型来源 | 设施名称、类型、描述仍是旧艺术馆语义;坐标字段只有 `{x,y}`,无法判断与 `f1-indoor-pois.json``{x,y,z}` 如何互转 |
| `public/models/*.glb` | 可能用于 web 端静态访问 | 与 `static/models/*.glb` 同名同大小,需说明是否为跨端副本,避免构建重复打包 |
### 新增数据风险
| 严重性 | 问题 | 影响 | 建议 |
| --- | --- | --- | --- |
| P0 | 坐标来源真实,但业务实体语义不真实 | 后续 web 与模型交互可能点位能落到模型上,但弹出的名称、类别、详情仍是错的 | 将 `facilities.json` 拆为“模型点位层”和“业务设施层”,用 `poiId/entityId` 连接 |
| P1 | 缺少模型 manifest | 无法保证 `f1-floor.glb``f1-indoor.glb`、POI JSON、设施坐标来自同一版本 | 新增 `static/models/model-manifest.json``src/assets/data/model-manifest.json` |
| P1 | 缺少坐标系/变换元数据 | `facilities.json.position {x,y}``f1-indoor-pois {x,y,z}` 可能来自不同模型或投影空间,未来 web 交互容易错位 | 为每组坐标声明 `sourceModel``coordinateSystem``unit``origin``scale``axisMapping` |
| P1 | `Map3DManager` 运行时加载 `f1-indoor-pois.json`,但 `facilities.json` 坐标来自 `f1-floor.glb` | 运行态点位和业务设施点位可能使用两套坐标基准 | 明确 `f1-floor``f1-indoor` 的关系,建立统一转换或只让业务层引用同一基准 |
| P2 | 模型部署目录策略不明确 | web 端和小程序端可能各自引用不同路径,造成缓存、版本和包体问题 | 约定 web 用 `public/models``static/models` 之一,并通过 manifest 管理 |
### 建议新增模型清单
```json
{
"models": [
{
"id": "f1-indoor",
"path": "/static/models/f1-indoor.glb",
"purpose": "runtime-3d-indoor-map",
"floor": "1F",
"version": "2026-05-28",
"hash": "<sha256>",
"poiDataset": "/static/data/f1-indoor-pois.json",
"coordinateSystem": {
"type": "model_world_3d",
"unit": "model_unit",
"scaleInMap3DManager": 0.3
}
},
{
"id": "f1-floor",
"path": "/static/models/f1-floor.glb",
"purpose": "floor-point-extraction",
"floor": "1F",
"version": "2026-05-28",
"derivedDatasets": [
"src/assets/data/facilities.json"
],
"coordinateSystem": {
"type": "model_floor_projection",
"unit": "model_unit",
"axisMapping": "to-be-documented"
}
}
]
}
```
### 修正后的下一步
1. 保留 `static/models/f1-indoor.glb``static/models/f1-floor.glb` 作为真实资产,不再按占位资源处理。
2.`facilities.json.position` 增加来源字段,例如 `sourceModel: "f1-floor"``sourceCoordinateSystem: "model_floor_projection"`
3.`facilities.json` 中的业务字段与模型坐标分离:坐标可来自模型,设施名称/类型/描述需按真实自然博物馆内容重建。
4. 建立 `f1-floor` 坐标与 `f1-indoor` POI 坐标的关系说明,否则 web 端点击、3D 高亮和导航路径会有错位风险。
5. 明确 `public/models``static/models` 的分工;如果 `public/models` 是 web 端专用副本,应写入 README/manifest而不是简单删除。