chore: expose SGS hall POI diagnostics
This commit is contained in:
24
.superpowers/sdd/task-3-report.md
Normal file
24
.superpowers/sdd/task-3-report.md
Normal file
@@ -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.
|
||||||
@@ -12,7 +12,9 @@ import {
|
|||||||
isIndoorNavigableFloor
|
isIndoorNavigableFloor
|
||||||
} from '@/domain/guideFloor'
|
} from '@/domain/guideFloor'
|
||||||
import {
|
import {
|
||||||
|
createSgsHallPoiDiagnostics,
|
||||||
formatSgsFloorLabel,
|
formatSgsFloorLabel,
|
||||||
|
toMuseumHallPoisFromSgs,
|
||||||
toMuseumPoiFromSgs
|
toMuseumPoiFromSgs
|
||||||
} from '@/data/adapters/sgsSdkGuideAdapter'
|
} from '@/data/adapters/sgsSdkGuideAdapter'
|
||||||
import {
|
import {
|
||||||
@@ -38,7 +40,9 @@ const toRenderPoi = (poi: StaticNavPoiPayload): GuideRenderPoi => ({
|
|||||||
primaryCategoryZh: poi.primaryCategoryZh,
|
primaryCategoryZh: poi.primaryCategoryZh,
|
||||||
iconType: poi.iconType || poi.primaryCategory,
|
iconType: poi.iconType || poi.primaryCategory,
|
||||||
positionGltf: poi.positionGltf,
|
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 = (
|
const toSgsRenderPoi = (
|
||||||
@@ -51,9 +55,22 @@ const toSgsRenderPoi = (
|
|||||||
primaryCategoryZh: poi.primaryCategory.label,
|
primaryCategoryZh: poi.primaryCategory.label,
|
||||||
iconType: poi.primaryCategory.iconType || poi.primaryCategory.id,
|
iconType: poi.primaryCategory.iconType || poi.primaryCategory.id,
|
||||||
positionGltf: poi.positionGltf,
|
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<string>()
|
||||||
|
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 trimTrailingSlash = (value: string) => value.replace(/\/+$/, '')
|
||||||
|
|
||||||
const resolveSgsAssetUrl = (url?: string | null) => {
|
const resolveSgsAssetUrl = (url?: string | null) => {
|
||||||
@@ -100,6 +117,14 @@ const isSharedSgsModelAsset = (
|
|||||||
return modelUrl === overviewModelUrl || allModelUrls.filter((url) => url === modelUrl).length > 1
|
return modelUrl === overviewModelUrl || allModelUrls.filter((url) => url === modelUrl).length > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const warnSgsGuideModelDiagnostics = (
|
||||||
|
message: string,
|
||||||
|
payload: Record<string, unknown>
|
||||||
|
) => {
|
||||||
|
if (!import.meta.env.DEV) return
|
||||||
|
console.warn(`[SGS guide model] ${message}`, payload)
|
||||||
|
}
|
||||||
|
|
||||||
const getSgsFloorModelMatchKeys = (floor: SgsSdkFloorSummaryPayload, label: string) => (
|
const getSgsFloorModelMatchKeys = (floor: SgsSdkFloorSummaryPayload, label: string) => (
|
||||||
[
|
[
|
||||||
String(floor.floorId),
|
String(floor.floorId),
|
||||||
@@ -210,10 +235,55 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
|
|||||||
))
|
))
|
||||||
if (!matchedFloor) return []
|
if (!matchedFloor) return []
|
||||||
|
|
||||||
const pois = await this.provider.getFloorPois(String(matchedFloor.floorId))
|
const resolvedFloorId = String(matchedFloor.floorId)
|
||||||
return pois
|
const loadFloorData = async <T>(
|
||||||
.map((poi) => toSgsRenderPoi(toMuseumPoiFromSgs(poi, manifest.floors)))
|
endpoint: 'pois' | 'spaces' | 'navigablePlaces',
|
||||||
|
loader: () => Promise<T[]>
|
||||||
|
) => {
|
||||||
|
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)
|
.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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user