feat: wire guide data source and POI focus UI

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
lyf
2026-06-12 09:25:22 +08:00
parent a6bfda30e1
commit 2055b13b90
58 changed files with 5768 additions and 1898 deletions

View File

@@ -0,0 +1,75 @@
import type {
ExplainTrack,
MediaAsset,
MuseumExhibit,
MuseumHall,
SearchIndexItem
} from '@/domain/museum'
import {
explainRepository,
type ExplainRepository
} from '@/repositories/ExplainRepository'
import {
mediaRepository,
type MediaRepository
} from '@/repositories/MediaRepository'
export interface ExplainAudioSelection {
exhibit: MuseumExhibit
track: ExplainTrack | null
media: MediaAsset | null
playable: boolean
unavailableMessage?: string
}
export class ExplainUseCase {
constructor(
private readonly explain: ExplainRepository = explainRepository,
private readonly media: MediaRepository = mediaRepository
) {}
listExhibits() {
return this.explain.listExhibits()
}
getExhibitById(id: string) {
return this.explain.getExhibitById(id)
}
listHalls(): Promise<MuseumHall[]> {
return this.explain.listHalls()
}
getHallById(id: string) {
return this.explain.getHallById(id)
}
async listExhibitsByHallId(hallId: string) {
const exhibits = await this.explain.listExhibits()
return exhibits.filter((exhibit) => exhibit.hallId === hallId)
}
searchExplain(keyword?: string): Promise<SearchIndexItem[]> {
return this.explain.searchExplain(keyword)
}
async selectAudioForExhibit(exhibitId: string): Promise<ExplainAudioSelection | null> {
const exhibit = await this.explain.getExhibitById(exhibitId)
if (!exhibit) return null
const track = await this.explain.getTrackByExhibitId(exhibitId)
const media = track?.mediaId
? await this.media.getMediaById(track.mediaId)
: null
return {
exhibit,
track,
media,
playable: media?.available === true && Boolean(media.url),
unavailableMessage: media?.unavailableReason || '该展项暂无讲解音频'
}
}
}
export const explainUseCase = new ExplainUseCase()