修复导览与讲解位置预览逻辑

This commit is contained in:
lyf
2026-06-30 11:00:49 +08:00
parent f2a33888b1
commit 1c2cc788d1
29 changed files with 1111 additions and 756 deletions

View File

@@ -36,12 +36,15 @@ import {
toGuideMapDiagnostics,
toLocationPreviewFromPoi,
toMuseumFloorFromSgs,
toMuseumHallPoisFromSgs,
toMuseumPoiFromSgs,
toRouteReadinessFromSgsDiagnostics
} from '@/data/adapters/sgsSdkGuideAdapter'
const searchableCategories = new Set([
'touring_poi',
'exhibition_hall',
'exhibition_hall_entrance',
'basic_service_facility',
'transport_circulation',
'accessibility_special_service',
@@ -50,6 +53,7 @@ const searchableCategories = new Set([
const toSearchText = (poi: MuseumPoi) => [
poi.name,
poi.hallName,
poi.floorId,
poi.floorLabel,
poi.primaryCategory.label,
@@ -71,7 +75,11 @@ const toLocationPreview = (poi: MuseumPoi): GuideLocationPreview => ({
floorLabel: poi.floorLabel,
primaryCategoryZh: poi.primaryCategory.label,
positionGltf: poi.positionGltf,
sourceObjectName: poi.sourceObjectName
sourceObjectName: poi.sourceObjectName,
kind: poi.kind,
hallId: poi.hallId,
hallName: poi.hallName,
entrances: poi.entrances
})
const isSearchableIndoorPoi = (poi: MuseumPoi) => (
@@ -79,6 +87,15 @@ const isSearchableIndoorPoi = (poi: MuseumPoi) => (
&& isPoiOnIndoorNavigableFloor(poi)
)
const dedupePoisById = (pois: MuseumPoi[]) => {
const seen = new Set<string>()
return pois.filter((poi) => {
if (!poi.id || seen.has(poi.id)) return false
seen.add(poi.id)
return true
})
}
export interface GuideRepository {
getAssetBaseUrl(): string
getFloors(): Promise<MuseumFloor[]>
@@ -245,16 +262,38 @@ export class SgsSdkGuideRepository implements GuideRepository {
const indoorFloors = manifest.floors.filter(isIndoorNavigableFloor)
const indoorFloorIds = new Set(indoorFloors.map((floor) => String(floor.floorId)))
this.floorAliases = buildSgsFloorAliases(indoorFloors)
const poiGroups = await Promise.all(
indoorFloors.map((floor) => this.provider.getFloorPois(String(floor.floorId)))
const floorDataGroups = await Promise.all(
indoorFloors.map(async (floor) => {
const floorId = String(floor.floorId)
const [pois, spaces, navigablePlaces] = await Promise.all([
this.provider.getFloorPois(floorId).catch(() => []),
this.provider.getFloorSpaces(floorId).catch(() => []),
this.provider.getNavigablePlaces(floorId).catch(() => [])
])
return {
floorId,
pois,
hallPois: toMuseumHallPoisFromSgs(spaces, navigablePlaces, manifest.floors, floorId)
}
})
)
this.poiCache = poiGroups
.flat()
const ordinaryPois = floorDataGroups
.flatMap((group) => group.pois)
.map((poi) => toMuseumPoiFromSgs(poi, manifest.floors))
.filter((poi) => poi.id && poi.name)
.filter((poi) => indoorFloorIds.has(String(poi.floorId)) || isPoiOnIndoorNavigableFloor(poi))
const hallPois = floorDataGroups
.flatMap((group) => group.hallPois)
.filter((poi) => indoorFloorIds.has(String(poi.floorId)) || isPoiOnIndoorNavigableFloor(poi))
this.poiCache = dedupePoisById([
...hallPois,
...ordinaryPois
])
return this.poiCache
}

View File

@@ -136,6 +136,10 @@ const createUnavailableReadiness = (message: string): GuideRouteReadiness => ({
requiredData: ['route_graph', 'nav_data']
})
const logRouteReadinessIssue = (message: string, payload: Record<string, unknown>) => {
console.error(`[RouteReadiness] ${message}`, payload)
}
export class StaticGuideRouteRepository implements GuideRouteRepository {
private datasetCache: NavRouteDataset | null = null
private datasetRequest: Promise<NavRouteDataset> | null = null
@@ -156,11 +160,17 @@ export class StaticGuideRouteRepository implements GuideRouteRepository {
try {
const dataset = await this.loadDataset()
if (!dataset.nodes.size || !dataset.edges.length || !dataset.anchorsByPoiId.size) {
return createUnavailableReadiness('路线示意数据不完整,当前暂不支持正式导航')
logRouteReadinessIssue('路线数据不完整', {
nodes: dataset.nodes.size,
edges: dataset.edges.length,
anchorsByPoiId: dataset.anchorsByPoiId.size
})
return createUnavailableReadiness('无法定位,当前仅支持点位位置预览')
}
return applyRouteReadinessGate(manifestReadiness)
} catch (error) {
console.error('[RouteReadiness] 静态路线数据加载失败', error)
return createUnavailableReadiness(error instanceof Error ? error.message : '路线示意数据加载失败')
}
}

View File

@@ -21,7 +21,6 @@ import {
toGuideRouteResultFromSgsPlan
} from '@/data/adapters/sgsSdkRouteAdapter'
import { stringifyId, formatSgsFloorLabel } from '@/data/adapters/sgsSdkGuideAdapter'
import { toAppFloorId } from '@/services/sgs/SgsMapEventAdapter'
const createUnavailableReadiness = (message: string): GuideRouteReadiness => ({
ready: false,
@@ -29,6 +28,10 @@ const createUnavailableReadiness = (message: string): GuideRouteReadiness => ({
requiredData: ['route_graph', 'nav_data']
})
const logSdkRouteReadinessIssue = (message: string, payload: Record<string, unknown>) => {
console.error(`[SDKRouteReadiness] ${message}`, payload)
}
const toNumber = (value: unknown) => (
Number.isFinite(Number(value)) ? Number(value) : undefined
)
@@ -51,11 +54,6 @@ const positionFromPlace = (place: SgsNavigablePlacePayload): [number, number, nu
]
}
const numericId = (value: unknown) => {
const numberValue = Number(value)
return Number.isFinite(numberValue) ? numberValue : undefined
}
export class SdkGuideRouteRepository {
private targetsCache: GuideRouteTarget[] | null = null
@@ -84,7 +82,19 @@ export class SdkGuideRouteRepository {
// 没有楼层有路线数据时不可用
if (readyFloors.length === 0) {
return createUnavailableReadiness('SDK 路线规划数据不完整,当前暂不支持正式导航')
logSdkRouteReadinessIssue('SDK 路线数据不完整', {
floorCount: floors.length,
readyFloorCount: readyFloors.length,
floors: floors.map((floor) => ({
floorId: floor.floorId,
floorName: floor.floorName,
floorCode: floor.floorCode,
routePlanningReady: floor.routePlanningReady,
routeNodeCount: floor.routeNodeCount,
routeEdgeCount: floor.routeEdgeCount
}))
})
return createUnavailableReadiness('无法定位,当前仅支持点位位置预览')
}
// 收集未就绪的楼层
@@ -97,19 +107,20 @@ export class SdkGuideRouteRepository {
.join('、')
return {
ready: true,
message: `部分楼层路线数据未就绪:${floorLabels},已就绪楼层可正常使用导览功能`,
message: `部分楼层位置关系数据未就绪:${floorLabels}可查看已就绪楼层的位置关系`,
requiredData: []
}
}
return {
ready: true,
message: 'SDK 路线数据已就绪',
message: 'SDK 位置关系数据已接入,可进行位置预览',
requiredData: []
}
} catch (error) {
console.error('[SDKRouteReadiness] 读取 SDK 路线状态失败', error)
return createUnavailableReadiness(
error instanceof Error ? error.message : 'SDK 路线数据加载失败'
error instanceof Error ? error.message : 'SDK 位置关系数据加载失败'
)
}
}
@@ -148,8 +159,8 @@ export class SdkGuideRouteRepository {
targets.push({
poiId: placeId,
name: placeName,
// 展示用标准化的 floorId(如 L1
floorId: toAppFloorId(place.floorId ?? rawFloorId),
// 保留后端原始 floorId,确保与 SDK 模型楼层和 ThreeMap 当前楼层一致。
floorId: stringifyId(place.floorId ?? rawFloorId),
floorLabel,
categoryLabel,
positionGltf,
@@ -188,7 +199,7 @@ export class SdkGuideRouteRepository {
const endTarget = targets.find((target) => target.poiId === endPoiId)
if (!startTarget || !endTarget) {
throw new GuideRouteError('ROUTE_TARGET_MISSING', '起点或终点不在 SDK 可导航目的地列表中')
throw new GuideRouteError('ROUTE_TARGET_MISSING', '起点或终点不在 SDK 可预览目的地列表中')
}
try {
@@ -196,24 +207,15 @@ export class SdkGuideRouteRepository {
throw new GuideRouteError('ROUTE_TARGET_UNANCHORED', '起点或终点缺少可规划坐标')
}
// 获取原始 floorId 用于 API 调用
const diagnostics = await this.sdkApiProvider.getMapDiagnostics()
const floorIdMap = new Map<string, string>()
for (const floor of (diagnostics.floors || []).filter(isIndoorNavigableFloor)) {
floorIdMap.set(toAppFloorId(floor.floorId), stringifyId(floor.floorId))
}
const rawStartFloorId = floorIdMap.get(startTarget.floorId) || startTarget.floorId
const rawEndFloorId = floorIdMap.get(endTarget.floorId) || endTarget.floorId
const route = await this.sdkApiProvider.planRoute({
startFloorId: rawStartFloorId,
startFloorId: startTarget.floorId,
startX: startTarget.positionGltf[0],
startY: startTarget.positionGltf[2],
startNodeId: numericId(startTarget.routeNodeId) || null,
endFloorId: rawEndFloorId,
startNodeId: startTarget.routeNodeId || null,
endFloorId: endTarget.floorId,
endX: endTarget.positionGltf[0],
endY: endTarget.positionGltf[2],
endNodeId: numericId(endTarget.routeNodeId) || null,
endNodeId: endTarget.routeNodeId || null,
wheelchair: 0
})
@@ -222,7 +224,7 @@ export class SdkGuideRouteRepository {
if (error instanceof GuideRouteError) throw error
throw new GuideRouteError(
'ROUTE_NOT_FOUND',
error instanceof Error ? error.message : 'SGS 路线接口规划失败'
error instanceof Error ? error.message : 'SGS 位置关系接口生成失败'
)
}
}