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

@@ -1,5 +1,8 @@
import type {
GuideDataIntegrityReport,
GuideFloorDetail,
GuideLocationPreview,
GuideMapDiagnostics,
GuideRouteReadiness,
MuseumFloor,
MuseumPoi
@@ -16,6 +19,20 @@ import {
defaultStaticNavAssetsProvider,
type StaticNavAssetsProvider
} from '@/data/providers/staticNavAssetsProvider'
import {
defaultSgsSdkApiProvider,
type SgsSdkApiProvider
} from '@/data/providers/sgsSdkApiProvider'
import {
buildSgsFloorAliases,
createGuideDataIntegrityReport,
toGuideFloorDetailFromSgs,
toGuideMapDiagnostics,
toLocationPreviewFromPoi,
toMuseumFloorFromSgs,
toMuseumPoiFromSgs,
toRouteReadinessFromSgsDiagnostics
} from '@/data/adapters/sgsSdkGuideAdapter'
const searchableCategories = new Set([
'touring_poi',
@@ -60,6 +77,9 @@ export interface GuideRepository {
searchPois(keyword?: string): Promise<MuseumPoi[]>
getLocationPreview(poiId: string): Promise<GuideLocationPreview | null>
getRouteReadiness(): Promise<GuideRouteReadiness>
getFloorDetail?(floorId: string): Promise<GuideFloorDetail | null>
getMapDiagnostics?(): Promise<GuideMapDiagnostics>
checkDataIntegrity?(): Promise<GuideDataIntegrityReport>
}
export class StaticGuideRepository implements GuideRepository {
@@ -120,6 +140,161 @@ export class StaticGuideRepository implements GuideRepository {
return this.provider.loadRouteReadiness()
.catch(() => NAV_ROUTE_READINESS)
}
async getFloorDetail(floorId: string) {
const floors = await this.getFloors()
const normalizedFloorId = this.normalizeFloorId(floorId)
const floor = floors.find((item) => item.id === normalizedFloorId)
return floor
? {
...floor,
warnings: []
}
: null
}
async getMapDiagnostics(): Promise<GuideMapDiagnostics> {
const [floors, pois] = await Promise.all([
this.getFloors(),
this.listPois()
])
return {
mapId: 'static',
mapName: '本地静态导览数据',
status: 'WARN',
floors: floors.map((floor) => ({
...floor,
poiCount: pois.filter((poi) => poi.floorId === floor.id).length,
warnings: []
})),
summary: {
floorCount: floors.length,
modelReadyFloorCount: floors.length,
poiCount: pois.length,
spaceCount: 0,
guideStopCount: 0,
navigablePlaceCount: 0,
routeNodeCount: 0,
routeEdgeCount: 0
},
warnings: ['static 模式仅提供本地导览资源基础检查']
}
}
async checkDataIntegrity() {
const [floors, pois, diagnostics] = await Promise.all([
this.getFloors(),
this.listPois(),
this.getMapDiagnostics()
])
return createGuideDataIntegrityReport(diagnostics, floors, pois)
}
}
export const guideRepository = new StaticGuideRepository()
export class SgsSdkGuideRepository implements GuideRepository {
private floorsCache: MuseumFloor[] | null = null
private poiCache: MuseumPoi[] | null = null
private floorAliases: Map<string, string> | null = null
constructor(private readonly provider: SgsSdkApiProvider = defaultSgsSdkApiProvider) {}
getAssetBaseUrl() {
return ''
}
async getFloors() {
if (this.floorsCache) return this.floorsCache
const manifest = await this.provider.getManifest()
this.floorAliases = buildSgsFloorAliases(manifest.floors)
this.floorsCache = [...manifest.floors]
.sort((a, b) => Number(a.sortOrder || 0) - Number(b.sortOrder || 0))
.map(toMuseumFloorFromSgs)
return this.floorsCache
}
normalizeFloorId(labelOrId: string) {
const normalized = labelOrId.trim()
const aliases = this.floorAliases
if (!aliases) return normalized
return aliases.get(normalized) || aliases.get(normalized.toLowerCase()) || normalized
}
async listPois() {
if (this.poiCache) return this.poiCache
const manifest = await this.provider.getManifest()
this.floorAliases = buildSgsFloorAliases(manifest.floors)
const poiGroups = await Promise.all(
manifest.floors.map((floor) => this.provider.getFloorPois(String(floor.floorId)))
)
this.poiCache = poiGroups
.flat()
.map((poi) => toMuseumPoiFromSgs(poi, manifest.floors))
.filter((poi) => poi.id && poi.name)
return this.poiCache
}
async getPoiById(id: string) {
const pois = await this.listPois()
return pois.find((poi) => poi.id === id) || null
}
async searchPois(keyword = '') {
const pois = await this.listPois()
const normalizedKeyword = keyword.trim().toLowerCase()
if (!normalizedKeyword) return pois
return pois.filter((poi) => toSearchText(poi).includes(normalizedKeyword))
}
async getLocationPreview(poiId: string) {
const poi = await this.getPoiById(poiId)
return poi ? toLocationPreviewFromPoi(poi) : null
}
async getRouteReadiness() {
return this.provider.getMapDiagnostics()
.then(toRouteReadinessFromSgsDiagnostics)
.catch(() => NAV_ROUTE_READINESS)
}
async getFloorDetail(floorId: string) {
await this.getFloors()
const normalizedFloorId = this.normalizeFloorId(floorId)
try {
const diagnostics = await this.provider.getFloorDiagnostics(normalizedFloorId)
return toGuideFloorDetailFromSgs(diagnostics)
} catch {
const manifest = await this.provider.getManifest()
const floor = manifest.floors.find((item) => String(item.floorId) === normalizedFloorId)
return floor ? toGuideFloorDetailFromSgs(floor) : null
}
}
async getMapDiagnostics() {
const [manifest, diagnostics] = await Promise.all([
this.provider.getManifest(),
this.provider.getMapDiagnostics()
])
return toGuideMapDiagnostics(diagnostics, manifest)
}
async checkDataIntegrity() {
const [floors, pois, diagnostics] = await Promise.all([
this.getFloors(),
this.listPois(),
this.getMapDiagnostics()
])
return createGuideDataIntegrityReport(diagnostics, floors, pois)
}
}