提交H5业务闭环修复代码

This commit is contained in:
lyf
2026-07-01 16:02:21 +08:00
parent c43aa00038
commit e0aeafe062
26 changed files with 396 additions and 95 deletions

View File

@@ -52,14 +52,14 @@ export class ExplainUseCase {
) {}
private applyTrackAudioState(exhibit: MuseumExhibit, track?: ExplainTrack | null): MuseumExhibit {
const hasExhibitAudio = exhibit.audioAvailable === true || Boolean(exhibit.audioUrl)
const hasTrackAudio = track?.available === true
const hasPlayableAudioUrl = Boolean(exhibit.audioUrl?.trim())
const hasTrackTarget = Boolean(track?.playTargetType || track?.playTargetId)
if (!hasExhibitAudio && !hasTrackAudio) return exhibit
if (!hasPlayableAudioUrl && !hasTrackTarget) return exhibit
return {
...exhibit,
audioAvailable: true,
audioAvailable: hasPlayableAudioUrl,
playTargetType: track?.playTargetType || exhibit.playTargetType,
playTargetId: track?.playTargetId || exhibit.playTargetId
}

View File

@@ -1,6 +1,7 @@
import type {
GuideDataIntegrityReport,
GuideFloorDetail,
GuideLocationResolution,
GuideRouteReadiness,
GuideLocationPreviewPlan,
GuideMapDiagnostics,
@@ -42,6 +43,21 @@ const formatStartStepText = (startLocation: GuideStartLocation) => {
return `起点:${startLocation.label}${floorSuffix}${sourceSuffix}`
}
const normalizeLocationText = (value: string) => value
.trim()
.replace(/[\s()【】[\]_-]/g, '')
.toLowerCase()
const isHallLikePoi = (poi: MuseumPoi) => (
poi.kind === 'hall'
|| poi.primaryCategory.id === 'exhibition_hall'
|| poi.primaryCategory.id === 'touring_poi'
)
const uniqueStrings = (values: Array<string | undefined>) => Array.from(new Set(
values.map((value) => value?.trim()).filter(Boolean) as string[]
))
const createPreviewSteps = (
poi: MuseumPoi | null,
startLocation: GuideStartLocation | null,
@@ -74,6 +90,14 @@ const createPreviewSteps = (
]
}
export interface GuideContentLocationRequest {
directPoiId?: string
location?: GuideLocationResolution | null
hallId?: string
hallName?: string
targetName?: string
}
export class GuideUseCase {
private routeReadinessCache: GuideRouteReadiness | null = null
@@ -119,6 +143,71 @@ export class GuideUseCase {
return this.guide.getPoiById(id)
}
private async getPoiByIdIfResolvable(id: string, requireHall = false) {
const poi = await this.guide.getPoiById(id)
if (!poi) return null
if (requireHall && !isHallLikePoi(poi)) return null
return poi
}
private async searchBestPoiByName(name: string, requireHall = false) {
const normalizedTarget = normalizeLocationText(name)
if (!normalizedTarget) return null
const candidates = await this.guide.searchPois(name)
const allowedCandidates = requireHall
? candidates.filter(isHallLikePoi)
: candidates
return allowedCandidates.find((candidate) => {
const candidateName = normalizeLocationText(candidate.name)
const candidateHallName = normalizeLocationText(candidate.hallName || '')
return candidateName === normalizedTarget
|| candidateHallName === normalizedTarget
|| candidateName.includes(normalizedTarget)
|| normalizedTarget.includes(candidateName)
|| candidateHallName.includes(normalizedTarget)
|| normalizedTarget.includes(candidateHallName)
}) || allowedCandidates[0] || null
}
async resolveContentLocationPreviewTarget(request: GuideContentLocationRequest) {
const exactLocationPoiId = request.location?.status === 'exact'
? request.location.poiId
: undefined
const directIds = uniqueStrings([exactLocationPoiId, request.directPoiId])
for (const poiId of directIds) {
const poi = await this.getPoiByIdIfResolvable(poiId)
if (poi) return poi
}
const bridgedIds = uniqueStrings([request.location?.poiId])
for (const poiId of bridgedIds) {
const poi = await this.getPoiByIdIfResolvable(poiId)
if (poi) return poi
}
const hallIds = uniqueStrings([request.hallId])
for (const hallId of hallIds) {
const poi = await this.getPoiByIdIfResolvable(hallId, true)
if (poi) return poi
}
const hallNames = uniqueStrings([request.hallName])
for (const hallName of hallNames) {
const poi = await this.searchBestPoiByName(hallName, true)
if (poi) return poi
}
const targetNames = uniqueStrings([request.targetName])
for (const targetName of targetNames) {
const poi = await this.searchBestPoiByName(targetName)
if (poi) return poi
}
return null
}
getFloorDetail(floorId: string): Promise<GuideFloorDetail | null> {
return this.guide.getFloorDetail
? this.guide.getFloorDetail(floorId)