refactor: 重构数据层支持 SGS SDK 集成
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user