From 24699d2978a4d95631f1ed5ccf232e4aa7b51361 Mon Sep 17 00:00:00 2001 From: lyf <2514544224@qq.com> Date: Tue, 30 Jun 2026 02:07:47 +0800 Subject: [PATCH] chore: expose SGS hall POI diagnostics --- .superpowers/sdd/task-3-report.md | 24 +++++++ src/repositories/GuideModelRepository.ts | 80 ++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 .superpowers/sdd/task-3-report.md diff --git a/.superpowers/sdd/task-3-report.md b/.superpowers/sdd/task-3-report.md new file mode 100644 index 0000000..116741a --- /dev/null +++ b/.superpowers/sdd/task-3-report.md @@ -0,0 +1,24 @@ +# Task 3 Report + +Status: DONE + +Files changed: +- `src/repositories/GuideModelRepository.ts` + +Commands run: +- `node scripts/check-sgs-hall-poi-adapter.mjs` + - Output: `SGS hall POI adapter smoke check passed` +- `pnpm type-check` + - Output summary: `vue-tsc --noEmit` completed successfully in the main workspace. + +Commits created: +- Main workspace commit will be created by controller after validation. + +Self-review notes: +- Added dev-only SGS guide model diagnostics helper. +- Replaced silent floor endpoint catches with guarded diagnostics while preserving empty-array fallback behavior. +- Added hall-generation diagnostics for eligible spaces producing no renderable hall POIs and render filtering removing all hall POIs. +- No page/component SGS payload parsing was added. + +Concerns: +- None after main workspace validation. diff --git a/src/repositories/GuideModelRepository.ts b/src/repositories/GuideModelRepository.ts index 82c848c..62430aa 100644 --- a/src/repositories/GuideModelRepository.ts +++ b/src/repositories/GuideModelRepository.ts @@ -12,7 +12,9 @@ import { isIndoorNavigableFloor } from '@/domain/guideFloor' import { + createSgsHallPoiDiagnostics, formatSgsFloorLabel, + toMuseumHallPoisFromSgs, toMuseumPoiFromSgs } from '@/data/adapters/sgsSdkGuideAdapter' import { @@ -38,7 +40,9 @@ const toRenderPoi = (poi: StaticNavPoiPayload): GuideRenderPoi => ({ primaryCategoryZh: poi.primaryCategoryZh, iconType: poi.iconType || poi.primaryCategory, positionGltf: poi.positionGltf, - sourceObjectName: poi.sourceObjectName + sourceObjectName: poi.sourceObjectName, + kind: poi.primaryCategory === 'touring_poi' ? 'hall' : 'facility', + hallName: poi.primaryCategory === 'touring_poi' ? poi.name : undefined }) const toSgsRenderPoi = ( @@ -51,9 +55,22 @@ const toSgsRenderPoi = ( primaryCategoryZh: poi.primaryCategory.label, iconType: poi.primaryCategory.iconType || poi.primaryCategory.id, positionGltf: poi.positionGltf, - sourceObjectName: poi.sourceObjectName + sourceObjectName: poi.sourceObjectName, + kind: poi.kind, + hallId: poi.hallId, + hallName: poi.hallName, + entrances: poi.entrances }) +const dedupeRenderPoisById = (pois: GuideRenderPoi[]) => { + const seen = new Set() + return pois.filter((poi) => { + if (!poi.id || seen.has(poi.id)) return false + seen.add(poi.id) + return true + }) +} + const trimTrailingSlash = (value: string) => value.replace(/\/+$/, '') const resolveSgsAssetUrl = (url?: string | null) => { @@ -100,6 +117,14 @@ const isSharedSgsModelAsset = ( return modelUrl === overviewModelUrl || allModelUrls.filter((url) => url === modelUrl).length > 1 } +const warnSgsGuideModelDiagnostics = ( + message: string, + payload: Record +) => { + if (!import.meta.env.DEV) return + console.warn(`[SGS guide model] ${message}`, payload) +} + const getSgsFloorModelMatchKeys = (floor: SgsSdkFloorSummaryPayload, label: string) => ( [ String(floor.floorId), @@ -210,10 +235,55 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository { )) if (!matchedFloor) return [] - const pois = await this.provider.getFloorPois(String(matchedFloor.floorId)) - return pois - .map((poi) => toSgsRenderPoi(toMuseumPoiFromSgs(poi, manifest.floors))) + const resolvedFloorId = String(matchedFloor.floorId) + const loadFloorData = async ( + endpoint: 'pois' | 'spaces' | 'navigablePlaces', + loader: () => Promise + ) => { + try { + return await loader() + } catch (error) { + warnSgsGuideModelDiagnostics('floor data request failed', { + floorId: resolvedFloorId, + endpoint, + error: error instanceof Error ? error.message : String(error) + }) + return [] + } + } + + const [pois, spaces, navigablePlaces] = await Promise.all([ + loadFloorData('pois', () => this.provider.getFloorPois(resolvedFloorId)), + loadFloorData('spaces', () => this.provider.getFloorSpaces(resolvedFloorId)), + loadFloorData('navigablePlaces', () => this.provider.getNavigablePlaces(resolvedFloorId)) + ]) + const ordinaryPois = pois.map((poi) => toSgsRenderPoi(toMuseumPoiFromSgs(poi, manifest.floors))) + const museumHallPois = toMuseumHallPoisFromSgs(spaces, navigablePlaces, manifest.floors, resolvedFloorId) + const hallDiagnostics = createSgsHallPoiDiagnostics(spaces, navigablePlaces, museumHallPois) + + if (hallDiagnostics.eligibleSpaceCount > 0 && hallDiagnostics.hallPoiWithPositionCount === 0) { + warnSgsGuideModelDiagnostics('eligible hall spaces produced no renderable hall POIs', { + floorId: resolvedFloorId, + ...hallDiagnostics + }) + } + + const hallPois = museumHallPois.map(toSgsRenderPoi) + const renderPois = dedupeRenderPoisById([ + ...hallPois, + ...ordinaryPois + ]) .filter((poi) => Array.isArray(poi.positionGltf) && poi.positionGltf.length === 3) + + if (hallDiagnostics.hallPoiWithPositionCount > 0 && !renderPois.some((poi) => poi.primaryCategory === 'exhibition_hall')) { + warnSgsGuideModelDiagnostics('render POI filter removed all hall POIs', { + floorId: resolvedFloorId, + ...hallDiagnostics, + renderPoiCount: renderPois.length + }) + } + + return renderPois } }