This commit is contained in:
@@ -12,10 +12,6 @@ import {
|
||||
explainRepository,
|
||||
type ExplainRepository
|
||||
} from '@/repositories/ExplainRepository'
|
||||
import {
|
||||
mediaRepository,
|
||||
type MediaRepository
|
||||
} from '@/repositories/MediaRepository'
|
||||
import {
|
||||
audioPlayInfoRepository,
|
||||
audioReasonToText,
|
||||
@@ -28,10 +24,6 @@ import {
|
||||
import type {
|
||||
GuideStopInfo
|
||||
} from '@/data/adapters/guideStopInfoAdapter'
|
||||
import {
|
||||
publishedExhibitAudioRepository,
|
||||
type PublishedExhibitAudioRepository
|
||||
} from '@/repositories/PublishedExhibitAudioRepository'
|
||||
import {
|
||||
dataSourceConfig
|
||||
} from '@/config/dataSource'
|
||||
@@ -75,9 +67,7 @@ export interface ExplainHallSummary {
|
||||
export class ExplainUseCase {
|
||||
constructor(
|
||||
private readonly explain: ExplainRepository = explainRepository,
|
||||
private readonly media: MediaRepository = mediaRepository,
|
||||
private readonly audioPlayInfo: AudioPlayInfoRepository = audioPlayInfoRepository,
|
||||
private readonly publishedAudio: PublishedExhibitAudioRepository = publishedExhibitAudioRepository
|
||||
private readonly audioPlayInfo: AudioPlayInfoRepository = audioPlayInfoRepository
|
||||
) {}
|
||||
|
||||
private applyTrackAudioState(exhibit: MuseumExhibit, track?: ExplainTrack | null): MuseumExhibit {
|
||||
@@ -134,7 +124,7 @@ export class ExplainUseCase {
|
||||
fallback?: MuseumExhibit | null
|
||||
): MuseumExhibit {
|
||||
const linkedPrimary = stopInfo.linkedExhibits[0]
|
||||
const coverImage = stopInfo.coverImageUrl || fallback?.image
|
||||
const coverImage = stopInfo.coverImageUrl || (stopInfo.imageStatus === 'READY' ? fallback?.image : undefined)
|
||||
const description = stopInfo.description || fallback?.description || '该讲解暂无简介。'
|
||||
const audioTarget = this.resolveStopInfoAudioTarget(stopInfo)
|
||||
const audioAvailable = stopInfo.audioStatus === 'READY'
|
||||
@@ -175,6 +165,8 @@ export class ExplainUseCase {
|
||||
imageStatus: stopInfo.imageStatus,
|
||||
imageSource: stopInfo.imageSource,
|
||||
galleryUrls: stopInfo.galleryUrls,
|
||||
linkedExhibitCount: stopInfo.linkedExhibitCount,
|
||||
isSharedStop: stopInfo.isSharedStop,
|
||||
linkedExhibits: stopInfo.linkedExhibits,
|
||||
stopInfoAvailable: stopInfo.available,
|
||||
stopInfoReason: stopInfo.reason,
|
||||
@@ -211,15 +203,10 @@ export class ExplainUseCase {
|
||||
|
||||
private applyPlayInfo(
|
||||
exhibit: MuseumExhibit,
|
||||
playInfo: AudioPlayInfo,
|
||||
options: { preserveStaticFallback?: boolean } = {}
|
||||
playInfo: AudioPlayInfo
|
||||
): MuseumExhibit {
|
||||
const apiPlayable = playInfo.playable === true && Boolean(playInfo.playUrl)
|
||||
const canUseStaticAudioFallback = !playInfo.reason || playInfo.reason === 'TARGET_NOT_FOUND'
|
||||
const fallbackAudioUrl = options.preserveStaticFallback && canUseStaticAudioFallback && exhibit.audioUrl
|
||||
? exhibit.audioUrl
|
||||
: undefined
|
||||
const nextAudioUrl = apiPlayable ? playInfo.playUrl || undefined : fallbackAudioUrl
|
||||
const nextAudioUrl = apiPlayable ? playInfo.playUrl || undefined : undefined
|
||||
const nextAudioDuration = typeof playInfo.duration === 'number'
|
||||
? playInfo.duration
|
||||
: exhibit.audioDuration
|
||||
@@ -234,7 +221,8 @@ export class ExplainUseCase {
|
||||
audioHasText: playInfo.hasText === true || exhibit.audioHasText,
|
||||
audioNarrationTier: playInfo.narrationTier || exhibit.audioNarrationTier,
|
||||
audioUnavailableReason: apiPlayable ? undefined : audioReasonToText(playInfo.reason),
|
||||
audioAvailable: apiPlayable || Boolean(fallbackAudioUrl)
|
||||
audioAvailable: apiPlayable,
|
||||
audioStatus: apiPlayable ? 'READY' : 'MISSING'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +246,6 @@ export class ExplainUseCase {
|
||||
exhibit: MuseumExhibit,
|
||||
options: {
|
||||
includeText?: boolean
|
||||
preserveStaticFallback?: boolean
|
||||
track?: ExplainTrack | null
|
||||
} = {}
|
||||
): Promise<MuseumExhibit> {
|
||||
@@ -268,9 +255,7 @@ export class ExplainUseCase {
|
||||
|
||||
try {
|
||||
const playInfo = await this.audioPlayInfo.getPlayInfo({ targetType, targetId })
|
||||
let nextExhibit = this.applyPlayInfo(baseExhibit, playInfo, {
|
||||
preserveStaticFallback: options.preserveStaticFallback
|
||||
})
|
||||
let nextExhibit = this.applyPlayInfo(baseExhibit, playInfo)
|
||||
|
||||
if (options.includeText && playInfo.hasText) {
|
||||
try {
|
||||
@@ -346,7 +331,6 @@ export class ExplainUseCase {
|
||||
|
||||
return this.enrichExhibitAudio(exhibit, {
|
||||
includeText: options.includeText,
|
||||
preserveStaticFallback: true,
|
||||
track
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -433,7 +417,18 @@ export class ExplainUseCase {
|
||||
|
||||
async loadExplainHallSummaries(hallIds: string[]): Promise<Record<string, ExplainHallSummary>> {
|
||||
const uniqueHallIds = Array.from(new Set(hallIds.map((id) => id.trim()).filter(Boolean)))
|
||||
const halls = await this.listHalls().catch(() => [])
|
||||
const summariesFromHallList = new Map(halls.map((hall) => [hall.id, hall]))
|
||||
const entries = await Promise.all(uniqueHallIds.map(async (hallId) => {
|
||||
const hall = summariesFromHallList.get(hallId)
|
||||
if (hall && (typeof hall.outlineCount === 'number' || typeof hall.stopCount === 'number')) {
|
||||
return [hallId, {
|
||||
hallId,
|
||||
businessUnitCount: hall.outlineCount || 0,
|
||||
guideStopCount: hall.stopCount || 0
|
||||
}] as const
|
||||
}
|
||||
|
||||
try {
|
||||
const units = await this.loadTemporaryBusinessUnitsByHall(hallId)
|
||||
const guideStopCount = units.reduce((total, unit) => total + unit.guideStopCount, 0)
|
||||
@@ -508,15 +503,6 @@ export class ExplainUseCase {
|
||||
const exhibit = detailExhibit || summaryExhibit
|
||||
if (!exhibit) return null
|
||||
|
||||
const fallbackMedia = await (track?.mediaId
|
||||
? this.media.getMediaById(track.mediaId)
|
||||
: exhibit.guideContentId
|
||||
? this.media.getMediaById(`media-${exhibit.guideContentId}`)
|
||||
: Promise.resolve(null)
|
||||
).catch((error) => {
|
||||
console.warn('静态讲解媒体读取失败,将继续尝试播放接口:', error)
|
||||
return null
|
||||
})
|
||||
const targetType: AudioPlayTargetType = track?.playTargetType
|
||||
|| exhibit.playTargetType
|
||||
|| 'ITEM'
|
||||
@@ -533,7 +519,7 @@ export class ExplainUseCase {
|
||||
id: `play-${playInfo.audioId || `${requestTargetType}-${requestTargetId}`}`,
|
||||
type: 'audio',
|
||||
url: playInfo.playUrl || undefined,
|
||||
duration: typeof playInfo.duration === 'number' ? playInfo.duration : fallbackMedia?.duration,
|
||||
duration: typeof playInfo.duration === 'number' ? playInfo.duration : undefined,
|
||||
language: playInfo.lang,
|
||||
available: true
|
||||
}
|
||||
@@ -547,49 +533,6 @@ export class ExplainUseCase {
|
||||
}
|
||||
}
|
||||
|
||||
const toDirectMediaSelection = (playInfo: AudioPlayInfo): ExplainAudioSelection | null => {
|
||||
const directMedia = fallbackMedia?.available && fallbackMedia.url
|
||||
? fallbackMedia
|
||||
: exhibit.audioUrl
|
||||
? {
|
||||
id: `media-${exhibit.guideContentId || exhibit.id}`,
|
||||
type: 'audio' as const,
|
||||
url: exhibit.audioUrl,
|
||||
duration: exhibit.audioDuration,
|
||||
language: playInfo.lang,
|
||||
available: true
|
||||
}
|
||||
: null
|
||||
|
||||
if (!directMedia?.url) return null
|
||||
|
||||
return {
|
||||
exhibit,
|
||||
track,
|
||||
media: directMedia,
|
||||
playable: true,
|
||||
playInfo: {
|
||||
...playInfo,
|
||||
playable: true,
|
||||
audioId: directMedia.id,
|
||||
title: track?.title || exhibit.guideTitle || exhibit.name,
|
||||
duration: directMedia.duration || playInfo.duration,
|
||||
playUrl: directMedia.url,
|
||||
fallback: true,
|
||||
fallbackReason: playInfo.reason || 'DIRECT_MEDIA_FALLBACK'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const directMediaSelection = toDirectMediaSelection({
|
||||
playable: false,
|
||||
targetType,
|
||||
targetId,
|
||||
lang: dataSourceConfig.audioLanguage as AudioPlayInfo['lang'],
|
||||
reason: 'DIRECT_MEDIA_FALLBACK'
|
||||
})
|
||||
if (directMediaSelection) return directMediaSelection
|
||||
|
||||
try {
|
||||
const initialPlayInfo = await this.audioPlayInfo.getPlayInfo({ targetType, targetId })
|
||||
|
||||
@@ -597,20 +540,6 @@ export class ExplainUseCase {
|
||||
return toPlayableSelection(initialPlayInfo, targetType, targetId)
|
||||
}
|
||||
|
||||
if (initialPlayInfo.reason === 'TARGET_NOT_FOUND') {
|
||||
const publishedResolution = await this.publishedAudio.resolveByExhibitName(exhibit.name, initialPlayInfo.lang)
|
||||
if (publishedResolution?.targets.length) {
|
||||
for (const candidate of publishedResolution.targets) {
|
||||
if (candidate.targetType === targetType && candidate.targetId === targetId) continue
|
||||
|
||||
const playInfo = await this.audioPlayInfo.getPlayInfo(candidate)
|
||||
if (playInfo.playable && playInfo.playUrl) {
|
||||
return toPlayableSelection(playInfo, candidate.targetType, candidate.targetId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
exhibit,
|
||||
track,
|
||||
@@ -626,11 +555,10 @@ export class ExplainUseCase {
|
||||
targetType,
|
||||
targetId,
|
||||
lang: dataSourceConfig.audioLanguage as AudioPlayInfo['lang'],
|
||||
hasText: false,
|
||||
fallback: false,
|
||||
reason: 'SERVICE_ERROR'
|
||||
}
|
||||
const directMediaSelection = toDirectMediaSelection(serviceErrorPlayInfo)
|
||||
if (directMediaSelection) return directMediaSelection
|
||||
|
||||
return {
|
||||
exhibit,
|
||||
track,
|
||||
@@ -642,7 +570,10 @@ export class ExplainUseCase {
|
||||
}
|
||||
}
|
||||
|
||||
async selectAudioForExplainDetail(exhibit: MuseumExhibit): Promise<ExplainAudioSelection> {
|
||||
async selectAudioForExplainDetail(
|
||||
exhibit: MuseumExhibit,
|
||||
options: { refreshPlayInfo?: boolean } = {}
|
||||
): Promise<ExplainAudioSelection> {
|
||||
const targetType = exhibit.playTargetType || 'ITEM'
|
||||
const targetId = exhibit.playTargetId || exhibit.id
|
||||
|
||||
@@ -660,7 +591,8 @@ export class ExplainUseCase {
|
||||
const playInfo = await this.audioPlayInfo.getPlayInfo({
|
||||
targetType,
|
||||
targetId,
|
||||
lang: (exhibit.audioLanguage as AudioLanguage) || (dataSourceConfig.audioLanguage as AudioLanguage)
|
||||
lang: (exhibit.audioLanguage as AudioLanguage) || (dataSourceConfig.audioLanguage as AudioLanguage),
|
||||
refresh: options.refreshPlayInfo === true
|
||||
})
|
||||
|
||||
if (!playInfo.playable || !playInfo.playUrl) {
|
||||
|
||||
Reference in New Issue
Block a user