修复搜索与点位定位闭环
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
lyf
2026-07-12 03:07:49 +08:00
parent e006333c0a
commit d8420fc7c2
18 changed files with 2961 additions and 621 deletions

View File

@@ -15,9 +15,12 @@ import type {
import {
NAV_ROUTE_UNAVAILABLE_MESSAGE
} from '@/domain/guideReadiness'
import {
isIndoorNavigableFloor
} from '@/domain/guideFloor'
import {
isIndoorNavigableFloor
} from '@/domain/guideFloor'
import {
isPoiSearchCategorySupported
} from '@/domain/poiCategories'
import type {
SgsFloorDiagnosticsPayload,
SgsGuideStopPayload,
@@ -194,20 +197,22 @@ const toNumber = (value: number | null | undefined, fallback = 0) => (
Number.isFinite(Number(value)) ? Number(value) : fallback
)
const optionalNumber = (value: number | null | undefined) => (
Number.isFinite(Number(value)) ? Number(value) : undefined
)
const optionalNumber = (value: number | null | undefined) => (
value !== null && typeof value !== 'undefined' && Number.isFinite(Number(value))
? Number(value)
: undefined
)
const normalizedText = (value?: string | null) => (value || '').trim()
const normalizePositionSource = (source: SgsPositionPayload): [number, number, number] | undefined => {
const x = Number(source.x)
const y = Number(source.y)
const z = Number(source.z)
if (![x, y, z].every(Number.isFinite)) return undefined
return [x, y, z]
}
const normalizePositionSource = (source: SgsPositionPayload): [number, number, number] | undefined => {
const x = optionalNumber(source.x)
const y = optionalNumber(source.y)
const z = optionalNumber(source.z)
if (typeof x === 'undefined' || typeof y === 'undefined' || typeof z === 'undefined') return undefined
return [x, y, z]
}
const normalizeStatus = (status?: string): GuideDiagnosticsStatus => {
if (status === 'OK' || status === 'WARN' || status === 'ERROR') return status
@@ -354,7 +359,7 @@ const searchableSpaceText = (space: SgsSpacePayload) => [
const parseBoundaryWktCenter = (
boundaryWkt?: string | null,
fallbackY = 0
fallbackY?: number
): [number, number, number] | undefined => {
const matches = [...(boundaryWkt || '').matchAll(/(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)/g)]
const points = matches
@@ -377,12 +382,11 @@ const normalizePositionSourceWithFallbackY = (
): [number, number, number] | undefined => {
if (!source) return undefined
const x = Number(source.x)
const rawY = Number(source.y)
const y = Number.isFinite(rawY) ? rawY : Number(fallbackY)
const z = Number(source.z)
const x = optionalNumber(source.x)
const y = optionalNumber(source.y) ?? optionalNumber(fallbackY)
const z = optionalNumber(source.z)
if (![x, y, z].every(Number.isFinite)) return undefined
if (typeof x === 'undefined' || typeof y === 'undefined' || typeof z === 'undefined') return undefined
return [x, y, z]
}
@@ -513,13 +517,16 @@ const kindForSgsPoi = (poi: SgsPoiPayload, category: MuseumCategory) => {
return 'facility'
}
export const toMuseumPoiFromSgs = (
poi: SgsPoiPayload,
floors: SgsSdkFloorSummaryPayload[]
): MuseumPoi => {
const floorId = stringifyId(poi.floorId)
const matchedFloor = floors.find((floor) => stringifyId(floor.floorId) === floorId || floor.floorCode === poi.floorCode)
const category = categoryFor(poi)
export const toMuseumPoiFromSgs = (
poi: SgsPoiPayload,
floors: SgsSdkFloorSummaryPayload[]
): MuseumPoi => {
const sourceFloorId = stringifyId(poi.floorId)
const matchedFloor = floors.find((floor) => stringifyId(floor.floorId) === sourceFloorId || floor.floorCode === poi.floorCode)
const floorId = stringifyId(matchedFloor?.floorId)
|| sourceFloorId
|| stringifyId(poi.floorCode)
const category = categoryFor(poi)
return {
id: stringifyId(poi.id),
@@ -983,14 +990,19 @@ const addIssue = (
})
}
const missingFieldsForPoi = (poi: MuseumPoi) => {
const fields: string[] = []
if (!poi.id) fields.push('id')
if (!poi.name) fields.push('name')
if (!poi.floorId) fields.push('floorId')
if (!poi.positionGltf) fields.push('position')
return fields
}
const missingFieldsForPoi = (poi: MuseumPoi) => {
const fields: string[] = []
if (!poi.id) fields.push('id')
if (!poi.name) fields.push('name')
if (!poi.floorId) fields.push('floorId')
if (!poi.floorLabel) fields.push('floorLabel')
if (
!Array.isArray(poi.positionGltf)
|| poi.positionGltf.length !== 3
|| poi.positionGltf.some((value) => !Number.isFinite(value))
) fields.push('position')
return fields
}
export const createGuideDataIntegrityReport = (
diagnostics: GuideMapDiagnostics,
@@ -1065,7 +1077,7 @@ export const createGuideDataIntegrityReport = (
}
if (poi.id) poiIds.add(poi.id)
if (poi.floorId && !floorIds.has(poi.floorId)) {
if (poi.floorId && !floorIds.has(poi.floorId)) {
addIssue(issues, {
scope: 'poi',
severity: 'error',
@@ -1074,9 +1086,21 @@ export const createGuideDataIntegrityReport = (
floorLabel: poi.floorLabel,
poiId: poi.id,
fields: ['floorId']
})
}
})
})
}
if (!isPoiSearchCategorySupported(poi)) {
addIssue(issues, {
scope: 'poi',
severity: 'warn',
message: `${poi.name || poi.id} 缺少有效搜索分类`,
floorId: poi.floorId,
floorLabel: poi.floorLabel,
poiId: poi.id,
fields: ['primaryCategory']
})
}
})
const errorCount = issues.filter((issue) => issue.severity === 'error').length
const warningCount = issues.filter((issue) => issue.severity === 'warn').length