42 KiB
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 类型枚举与图标映射未统一 |
当前数据流可以概括为:
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 必填字段缺失与占位符
结构字段层面,展品、展厅、设施、路线大多包含基础字段,但业务必填字段明显不足:没有馆藏编号、自然史分类、状态、更新时间、版权、资源元信息、语种、坐标系。
静态校验结果:
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
虽然基础字段未大量为空,但资源字段失效非常严重:
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:
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 不一致,且未绑定详情数据 |
硬编码证据截图:
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 关联关系完整性
路线站点都能在艺术馆展品表中找到,但楼层到设施存在断链:
BROKEN REFERENCES
floor floor_b1 facility parking_b1
floor floor_b1 facility storage_b1
更大的关联问题是 static/data/f1-indoor-pois.json 与核心实体完全脱钩。该文件已确认为真实 1F 设施点位基准,包含 67 个 POI,类型分布为:
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 |
建议新增分层模型:
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,但返回值未声明类型,错误时统一返回空数组:
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 规范 |
建议定义统一响应:
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:
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 和缺失资源 | 所有核心展品可展示/播放 |
| 建立中英文内容字段 | 英文名、学名、英文简介、英文音频 |
| 生成搜索索引 | 支持中文、英文、别名、学名 |
| 增加本地缓存和版本更新 | 弱网可用,支持增量更新 |
阶段 4:CMS/API 接入,2 到 4 周
目标:从静态 Mock 过渡到可维护的馆方内容系统。
| 任务 | 产出 |
|---|---|
| 定义 API response envelope | version/source/updatedAt/errors |
| 接入 CMS 或后台服务 | 馆方可维护内容 |
| 增加审核流 | draft 到 published |
| 增加数据监控 | 资源失效、断链、翻译缺失告警 |
| 建立回滚机制 | 数据版本可回退 |
数据治理建议
数据目录建议
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、审核流、多语言、监控和回滚机制可用 |
下一步行动清单
- 建立
scripts/audit-data.ts,把本报告中的数量、断链、资源缺失、统计不一致变成可重复命令。 - 先删除或隔离旧艺术馆 Mock 数据,防止新页面继续引用。
- 制定深圳自然博物馆首批 MVP 数据清单:8 个展厅、30 个展品、30 个设施、5 条路线、F1 全量 POI 绑定。
- 改造
dataLoader为 typed repository,详情页、搜索、地图统一从 repository 读取。 - 在发布流程中加入 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 点位包含的代表性设施和区域:
展厅/场馆:展厅人类厅、巨幕影院、动感多维影院、自然剧场、球幕影院、展厅
入口/服务:售票处、售票机、服务台
设施:女卫、男卫、无障碍卫生间、楼梯、电梯、扶梯、存包处、轮椅及儿童车租车处、贵宾接待区、贵宾卫生间、茶水间、母婴间
真实 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 文件可以继续作为原始点位数据,但业务层需要派生规范实体:
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 }
}
更新后的优先修复顺序
- P0:让 3D POI 点击支持
facility和entrance,否则真实 1F 点位 61 个服务类点位无法交互。 - P0:用真实 POI 重建 1F
facilities和halls,移除艺术馆设施/展厅对 1F 的覆盖。 - P1:建立
poiId -> entityId映射,让地图、搜索、详情、路线共享同一实体。 - P1:新增设施分类字典,把
女卫001、男卫002、电梯020、楼梯.001等归一到可筛选类别。 - P1:让
floor_1f从真实 POI 派生设施和展厅索引,避免手工维护数量不一致。 - 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 管理 |
建议新增模型清单
{
"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"
}
}
]
}
修正后的下一步
- 保留
static/models/f1-indoor.glb、static/models/f1-floor.glb作为真实资产,不再按占位资源处理。 - 为
facilities.json.position增加来源字段,例如sourceModel: "f1-floor"、sourceCoordinateSystem: "model_floor_projection"。 - 把
facilities.json中的业务字段与模型坐标分离:坐标可来自模型,设施名称/类型/描述需按真实自然博物馆内容重建。 - 建立
f1-floor坐标与f1-indoorPOI 坐标的关系说明,否则 web 端点击、3D 高亮和导航路径会有错位风险。 - 明确
public/models和static/models的分工;如果public/models是 web 端专用副本,应写入 README/manifest,而不是简单删除。