feat: 临时改造讲解页方案 B 链路

接入展厅列表、展厅讲解点分组生成临时业务单元,并迁移讲解详情播放正文到新接口链路。
This commit is contained in:
lyf
2026-07-02 00:51:27 +08:00
parent e0aeafe062
commit 9efcef5190
14 changed files with 1429 additions and 288 deletions

View File

@@ -1,5 +1,7 @@
import type {
AudioPlayTargetType,
ExplainBusinessUnit,
ExplainGuideStop,
ExplainTrack,
MediaAsset,
MuseumExhibit,
@@ -56,6 +58,35 @@ export interface BackendExhibit {
guideContents?: BackendGuideContent[] | null
}
export interface BackendHall {
id?: string | number | null
name?: string | null
hallCode?: string | null
nameEn?: string | null
subtitle?: string | null
description?: string | null
coverImageUrl?: string | null
exhibitCount?: number | null
}
export interface BackendGuideStop {
id?: string | number | null
name?: string | null
floorId?: string | number | null
targetType?: string | null
targetId?: string | number | null
coverImageUrl?: string | null
description?: string | null
hasAudio?: boolean | null
audioUrl?: string | null
poiId?: string | number | null
outlineId?: string | number | null
outlineName?: string | null
hallId?: string | number | null
hallName?: string | null
sort?: number | null
}
export interface BackendExplainAdapterResult {
exhibit: MuseumExhibit
track: ExplainTrack
@@ -180,7 +211,9 @@ export const toBackendMuseumExhibit = (
audioLanguage: supportedLanguage,
audioText: options.includeDetail && guideText ? guideText : undefined,
audioHasText: options.includeDetail ? Boolean(guideText) : undefined,
audioAvailable,
audioAvailable: audioAvailable || metadataSuggestsAudio,
audioStatus: source.audioStatus || undefined,
supportedLanguages: source.supportedLanguages || undefined,
audioUnavailableReason: audioAvailable || metadataSuggestsAudio
? undefined
: '该讲解暂无已发布音频,当前提供图文讲解。',
@@ -199,7 +232,7 @@ export const toBackendExplainTrack = (exhibit: MuseumExhibit): ExplainTrack => (
coverImage: exhibit.image,
poiId: exhibit.poiId,
floorId: exhibit.floorId,
available: Boolean(exhibit.audioUrl),
available: Boolean(exhibit.audioUrl) || exhibit.audioAvailable === true,
playTargetType: exhibit.playTargetType,
playTargetId: exhibit.playTargetId
})
@@ -236,3 +269,78 @@ export const toBackendHall = (
location: fallback?.location
}
}
export const toBackendHallFromList = (source: BackendHall, fallback?: MuseumHall | null): MuseumHall => {
const id = stringifyId(source.id) || fallback?.id || firstText(source.hallCode, source.name)
const name = firstText(source.name, fallback?.name, source.hallCode, '展厅')
return {
id,
name,
floorId: fallback?.floorId,
floorLabel: fallback?.floorLabel,
description: firstText(source.description, source.subtitle, fallback?.description, '该展厅暂无介绍。'),
image: normalizeSameOriginPublicUrl(firstText(source.coverImageUrl)) || fallback?.image || HALL_PLACEHOLDER_IMAGE,
exhibitCount: typeof source.exhibitCount === 'number'
? source.exhibitCount
: fallback?.exhibitCount || 0,
area: fallback?.area,
poiId: fallback?.poiId,
location: fallback?.location
}
}
export const toBackendGuideStop = (source: BackendGuideStop): ExplainGuideStop | null => {
const id = stringifyId(source.id)
if (!id) return null
const targetType = normalizeAudioTargetType(source.targetType) || 'STOP'
const targetId = stringifyId(source.targetId) || id
return {
id,
name: firstText(source.name, `讲解点${id}`),
hallId: stringifyId(source.hallId) || undefined,
hallName: firstText(source.hallName) || undefined,
floorId: stringifyId(source.floorId) || undefined,
targetType,
targetId,
coverImageUrl: normalizeSameOriginPublicUrl(firstText(source.coverImageUrl)) || undefined,
description: firstText(source.description) || undefined,
hasAudio: source.hasAudio === true,
poiId: stringifyId(source.poiId) || undefined,
outlineId: stringifyId(source.outlineId) || undefined,
outlineName: firstText(source.outlineName) || undefined,
sort: typeof source.sort === 'number' ? source.sort : undefined
}
}
export const groupGuideStopsByOutline = (
hallId: string,
stops: ExplainGuideStop[]
): ExplainBusinessUnit[] => {
const groupMap = new Map<string, ExplainGuideStop[]>()
const nameMap = new Map<string, string>()
stops
.slice()
.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0))
.forEach((stop) => {
const key = stop.outlineId || `ungrouped-${hallId}`
const name = stop.outlineName || '其他讲解'
const items = groupMap.get(key) || []
items.push(stop)
groupMap.set(key, items)
if (!nameMap.has(key)) {
nameMap.set(key, name)
}
})
return Array.from(groupMap.entries()).map(([id, groupStops]) => ({
id,
name: nameMap.get(id) || '其他讲解',
hallId,
guideStopCount: groupStops.length,
stops: groupStops
}))
}