# 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
艺术馆核心 Mock"] --> B["dataLoader.ts"] B --> C["部分列表/工具函数"] D["pages/detail.vue
硬编码详情"] --> E["详情页展示"] F["SearchPanel / ExplainList
自然博物馆硬编码 Mock"] --> G["搜索/讲解抽屉"] H["TencentMap / ThreeMap
硬编码地图点"] --> I["地图弹层"] J["static/data/f1-indoor-pois.json
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` 等返回类型 | 调用方无法获得类型保护 | | 使用 `any` 查找 | ID 类型、字段缺失、空数据不会被编译期发现 | | 异常统一变成 `[]` | 加载失败、数据为空、校验失败三种状态无法区分 | | 无响应 envelope | 后续接 API 时缺少 `version`、`updatedAt`、`source`、`errors`、`pagination` 规范 | 建议定义统一响应: ```ts interface DataResult { 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` | | 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`,支持校验错误 | | 改造详情页 | 按 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 | | 增加数据监控 | 资源失效、断链、翻译缺失告警 | | 建立回滚机制 | 数据版本可回退 | ## 数据治理建议 ### 数据目录建议 ```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": "", "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,而不是简单删除。