refactor: 重构数据层支持 SGS SDK 集成

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
lyf
2026-06-26 04:21:12 +08:00
parent a7413ee037
commit 15fbbec12d
16 changed files with 762 additions and 54 deletions

View File

@@ -4,6 +4,18 @@ import type {
GuideModelSource,
GuideRenderPoi
} from '@/domain/guideModel'
import {
dataSourceConfig
} from '@/config/dataSource'
import {
formatSgsFloorLabel,
toMuseumPoiFromSgs
} from '@/data/adapters/sgsSdkGuideAdapter'
import {
defaultSgsSdkApiProvider,
type SgsSdkApiProvider,
type SgsSdkFloorSummaryPayload
} from '@/data/providers/sgsSdkApiProvider'
import {
formatNavFloorLabel
} from '@/data/adapters/navAssetsAdapter'
@@ -24,6 +36,38 @@ const toRenderPoi = (poi: StaticNavPoiPayload): GuideRenderPoi => ({
sourceObjectName: poi.sourceObjectName
})
const toSgsRenderPoi = (
poi: ReturnType<typeof toMuseumPoiFromSgs>
): GuideRenderPoi => ({
id: poi.id,
name: poi.name,
floorId: poi.floorId,
primaryCategory: poi.primaryCategory.id,
primaryCategoryZh: poi.primaryCategory.label,
iconType: poi.primaryCategory.iconType || poi.primaryCategory.id,
positionGltf: poi.positionGltf,
sourceObjectName: poi.sourceObjectName
})
const trimTrailingSlash = (value: string) => value.replace(/\/+$/, '')
const resolveSgsAssetUrl = (url?: string | null) => {
const normalizedUrl = url?.trim()
if (!normalizedUrl) return ''
if (/^https?:\/\//i.test(normalizedUrl)) return normalizedUrl
if (!normalizedUrl.startsWith('/')) return normalizedUrl
const baseUrl = trimTrailingSlash(dataSourceConfig.sgsApiBaseUrl || dataSourceConfig.apiBaseUrl || '')
const origin = baseUrl.endsWith('/app-api')
? baseUrl.slice(0, -'/app-api'.length)
: baseUrl
return origin ? `${origin}${normalizedUrl}` : normalizedUrl
}
const sortSgsFloors = (floors: SgsSdkFloorSummaryPayload[]) => [...floors]
.sort((a, b) => Number(a.sortOrder || 0) - Number(b.sortOrder || 0))
export interface GuideModelRepository extends GuideModelSource {}
export class StaticGuideModelRepository implements GuideModelRepository {
@@ -63,4 +107,67 @@ export class StaticGuideModelRepository implements GuideModelRepository {
}
}
export const guideModelRepository = new StaticGuideModelRepository()
export class SgsSdkGuideModelRepository implements GuideModelRepository {
private floorsCache: SgsSdkFloorSummaryPayload[] | null = null
constructor(private readonly provider: SgsSdkApiProvider = defaultSgsSdkApiProvider) {}
async loadPackage(): Promise<GuideModelRenderPackage> {
const manifest = await this.provider.getManifest()
const floors = sortSgsFloors(manifest.floors)
this.floorsCache = floors
const bundles = await Promise.all(
floors.map((floor) => this.provider.getFloorBundle(String(floor.floorId)))
)
const floorAssets: GuideModelFloorAsset[] = floors
.map((floor, index) => {
const bundle = bundles[index]
const modelUrl = resolveSgsAssetUrl(bundle.model?.modelUrl || bundle.model?.fallbackModelUrl)
return {
floorId: String(floor.floorId),
label: formatSgsFloorLabel(floor.floorCode, floor.floorName),
order: Number(floor.sortOrder || 0),
modelUrl,
sharedModelAsset: false
}
})
.filter((asset) => asset.modelUrl)
const overviewFloorIndex = floors.findIndex((floor) => floor.floorCode === 'EXTERIOR')
const overviewModelUrl = floorAssets[overviewFloorIndex]?.modelUrl || floorAssets[0]?.modelUrl || ''
return {
overviewModelUrl,
floors: floorAssets
}
}
async loadFloorPois(floorId: string) {
const manifest = await this.provider.getManifest()
this.floorsCache = sortSgsFloors(manifest.floors)
const matchedFloor = this.floorsCache.find((floor) => (
String(floor.floorId) === floorId
|| floor.floorCode === floorId
|| formatSgsFloorLabel(floor.floorCode, floor.floorName) === floorId
))
if (!matchedFloor) return []
const pois = await this.provider.getFloorPois(String(matchedFloor.floorId))
return pois
.map((poi) => toSgsRenderPoi(toMuseumPoiFromSgs(poi, manifest.floors)))
.filter((poi) => Array.isArray(poi.positionGltf) && poi.positionGltf.length === 3)
}
}
export const createGuideModelRepository = (): GuideModelRepository => {
if (dataSourceConfig.mode === 'api' || dataSourceConfig.mode === 'sdk') {
return new SgsSdkGuideModelRepository()
}
return new StaticGuideModelRepository()
}
export const guideModelRepository = createGuideModelRepository()