chore: freeze guide and explain update
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
AudioPlayTargetType,
|
||||
ExplainTrack,
|
||||
MediaAsset,
|
||||
MuseumExhibit,
|
||||
@@ -13,6 +14,16 @@ import {
|
||||
mediaRepository,
|
||||
type MediaRepository
|
||||
} from '@/repositories/MediaRepository'
|
||||
import {
|
||||
audioPlayInfoRepository,
|
||||
audioReasonToText,
|
||||
type AudioPlayInfo,
|
||||
type AudioPlayInfoRepository
|
||||
} from '@/repositories/AudioPlayInfoRepository'
|
||||
import {
|
||||
publishedExhibitAudioRepository,
|
||||
type PublishedExhibitAudioRepository
|
||||
} from '@/repositories/PublishedExhibitAudioRepository'
|
||||
|
||||
export interface ExplainAudioSelection {
|
||||
exhibit: MuseumExhibit
|
||||
@@ -20,20 +31,83 @@ export interface ExplainAudioSelection {
|
||||
media: MediaAsset | null
|
||||
playable: boolean
|
||||
unavailableMessage?: string
|
||||
playInfo?: AudioPlayInfo
|
||||
}
|
||||
|
||||
export class ExplainUseCase {
|
||||
constructor(
|
||||
private readonly explain: ExplainRepository = explainRepository,
|
||||
private readonly media: MediaRepository = mediaRepository
|
||||
private readonly media: MediaRepository = mediaRepository,
|
||||
private readonly audioPlayInfo: AudioPlayInfoRepository = audioPlayInfoRepository,
|
||||
private readonly publishedAudio: PublishedExhibitAudioRepository = publishedExhibitAudioRepository
|
||||
) {}
|
||||
|
||||
listExhibits() {
|
||||
return this.explain.listExhibits()
|
||||
private applyTrackAudioState(exhibit: MuseumExhibit, track?: ExplainTrack | null): MuseumExhibit {
|
||||
const hasExhibitAudio = exhibit.audioAvailable === true || Boolean(exhibit.audioUrl)
|
||||
const hasTrackAudio = track?.available === true
|
||||
|
||||
if (!hasExhibitAudio && !hasTrackAudio) return exhibit
|
||||
|
||||
return {
|
||||
...exhibit,
|
||||
audioAvailable: true,
|
||||
playTargetType: track?.playTargetType || exhibit.playTargetType,
|
||||
playTargetId: track?.playTargetId || exhibit.playTargetId
|
||||
}
|
||||
}
|
||||
|
||||
getExhibitById(id: string) {
|
||||
return this.explain.getExhibitById(id)
|
||||
private async getTrackByExhibitIdMap() {
|
||||
const tracks = await this.explain.listTracks()
|
||||
const trackByExhibitId = new Map<string, ExplainTrack>()
|
||||
|
||||
tracks.forEach((track) => {
|
||||
if (track.exhibitId && !trackByExhibitId.has(track.exhibitId)) {
|
||||
trackByExhibitId.set(track.exhibitId, track)
|
||||
}
|
||||
})
|
||||
|
||||
return trackByExhibitId
|
||||
}
|
||||
|
||||
async listExhibits() {
|
||||
return this.listExplainExhibits()
|
||||
}
|
||||
|
||||
async listExplainExhibits() {
|
||||
const exhibits = await this.explain.listExplainExhibits()
|
||||
|
||||
try {
|
||||
const trackByExhibitId = await this.getTrackByExhibitIdMap()
|
||||
return exhibits.map((exhibit) => this.applyTrackAudioState(exhibit, trackByExhibitId.get(exhibit.id)))
|
||||
} catch (error) {
|
||||
console.warn('讲解音频状态加载失败,将使用讲解列表原始音频状态:', error)
|
||||
return exhibits
|
||||
}
|
||||
}
|
||||
|
||||
async listFullExhibits() {
|
||||
const exhibits = await this.explain.listExhibits()
|
||||
|
||||
try {
|
||||
const trackByExhibitId = await this.getTrackByExhibitIdMap()
|
||||
return exhibits.map((exhibit) => this.applyTrackAudioState(exhibit, trackByExhibitId.get(exhibit.id)))
|
||||
} catch (error) {
|
||||
console.warn('讲解音频状态加载失败,将使用展项原始音频状态:', error)
|
||||
return exhibits
|
||||
}
|
||||
}
|
||||
|
||||
async getExhibitById(id: string) {
|
||||
const exhibit = await this.explain.getExhibitById(id)
|
||||
if (!exhibit) return null
|
||||
|
||||
try {
|
||||
const track = await this.explain.getTrackByExhibitId(id)
|
||||
return this.applyTrackAudioState(exhibit, track)
|
||||
} catch (error) {
|
||||
console.warn('讲解详情音频状态加载失败,将使用展项原始音频状态:', error)
|
||||
return exhibit
|
||||
}
|
||||
}
|
||||
|
||||
listHalls(): Promise<MuseumHall[]> {
|
||||
@@ -45,7 +119,7 @@ export class ExplainUseCase {
|
||||
}
|
||||
|
||||
async listExhibitsByHallId(hallId: string) {
|
||||
const exhibits = await this.explain.listExhibits()
|
||||
const exhibits = await this.listExhibits()
|
||||
return exhibits.filter((exhibit) => exhibit.hallId === hallId)
|
||||
}
|
||||
|
||||
@@ -54,20 +128,123 @@ export class ExplainUseCase {
|
||||
}
|
||||
|
||||
async selectAudioForExhibit(exhibitId: string): Promise<ExplainAudioSelection | null> {
|
||||
const exhibit = await this.explain.getExhibitById(exhibitId)
|
||||
const track = await this.explain.getTrackByExhibitId(exhibitId)
|
||||
const explainExhibits = await this.explain.listExplainExhibits()
|
||||
const exhibit = explainExhibits.find((item) => item.id === exhibitId)
|
||||
|| await this.explain.getExhibitById(exhibitId)
|
||||
if (!exhibit) return null
|
||||
|
||||
const track = await this.explain.getTrackByExhibitId(exhibitId)
|
||||
const media = track?.mediaId
|
||||
const fallbackMedia = track?.mediaId
|
||||
? await this.media.getMediaById(track.mediaId)
|
||||
: null
|
||||
: exhibit.guideContentId
|
||||
? await this.media.getMediaById(`media-${exhibit.guideContentId}`)
|
||||
: null
|
||||
const targetType: AudioPlayTargetType = track?.playTargetType
|
||||
|| exhibit.playTargetType
|
||||
|| 'ITEM'
|
||||
const targetId = track?.playTargetId
|
||||
|| exhibit.playTargetId
|
||||
|| exhibit.id
|
||||
|
||||
return {
|
||||
exhibit,
|
||||
track,
|
||||
media,
|
||||
playable: media?.available === true && Boolean(media.url),
|
||||
unavailableMessage: media?.unavailableReason || '该展项暂无讲解音频'
|
||||
const toPlayableSelection = (
|
||||
playInfo: AudioPlayInfo,
|
||||
requestTargetType: AudioPlayTargetType,
|
||||
requestTargetId: string
|
||||
): ExplainAudioSelection => {
|
||||
const media: MediaAsset = {
|
||||
id: `play-${playInfo.audioId || `${requestTargetType}-${requestTargetId}`}`,
|
||||
type: 'audio',
|
||||
url: playInfo.playUrl || undefined,
|
||||
duration: typeof playInfo.duration === 'number' ? playInfo.duration : fallbackMedia?.duration,
|
||||
language: playInfo.lang,
|
||||
available: true
|
||||
}
|
||||
|
||||
return {
|
||||
exhibit,
|
||||
track,
|
||||
media,
|
||||
playable: true,
|
||||
playInfo
|
||||
}
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const initialPlayInfo = await this.audioPlayInfo.getPlayInfo({ targetType, targetId })
|
||||
|
||||
if (initialPlayInfo.playable && initialPlayInfo.playUrl) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const directMediaSelection = toDirectMediaSelection(initialPlayInfo)
|
||||
if (directMediaSelection) return directMediaSelection
|
||||
}
|
||||
|
||||
return {
|
||||
exhibit,
|
||||
track,
|
||||
media: null,
|
||||
playable: false,
|
||||
playInfo: initialPlayInfo,
|
||||
unavailableMessage: audioReasonToText(initialPlayInfo.reason)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('语音播放解析失败:', error)
|
||||
|
||||
return {
|
||||
exhibit,
|
||||
track,
|
||||
media: null,
|
||||
playable: false,
|
||||
unavailableMessage: '语音服务暂不可用,请稍后重试'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user