import type { ExplainTrack, MuseumExhibit, MuseumHall, SearchIndexItem } from '@/domain/museum' import { mediaRepository, type MediaRepository } from '@/repositories/MediaRepository' import { museumContentRepository, type MuseumContentRepository } from '@/repositories/MuseumContentRepository' export interface ExplainRepository { listExhibits(): Promise getExhibitById(id: string): Promise listHalls(): Promise getHallById(id: string): Promise listTracks(): Promise getTrackByExhibitId(exhibitId: string): Promise searchExplain(keyword?: string): Promise } const trackTitleFor = (exhibit: MuseumExhibit) => `${exhibit.name}讲解` export class DefaultExplainRepository implements ExplainRepository { constructor( private readonly content: MuseumContentRepository = museumContentRepository, private readonly media: MediaRepository = mediaRepository ) {} listExhibits() { return this.content.listExhibits() } getExhibitById(id: string) { return this.content.getExhibitById(id) } listHalls() { return this.content.listHalls() } getHallById(id: string) { return this.content.getHallById(id) } async listTracks() { const exhibits = await this.content.listExhibits() return Promise.all(exhibits.map(async (exhibit): Promise => { const media = await this.media.getMediaForExplainTrack(exhibit.id) return { id: `track-${exhibit.id}`, exhibitId: exhibit.id, hallId: exhibit.hallId, title: trackTitleFor(exhibit), summary: exhibit.description, mediaId: media?.id, coverImage: exhibit.image, poiId: exhibit.poiId, floorId: exhibit.floorId, available: media?.available === true } })) } async getTrackByExhibitId(exhibitId: string) { const tracks = await this.listTracks() return tracks.find((track) => track.exhibitId === exhibitId) || null } async searchExplain(keyword = '') { const normalizedKeyword = keyword.trim().toLowerCase() const [exhibits, halls] = await Promise.all([ this.content.listExhibits(), this.content.listHalls() ]) const exhibitItems = exhibits .filter((exhibit) => !normalizedKeyword || [ exhibit.name, exhibit.hallName, exhibit.floorLabel, ...(exhibit.tags || []) ].filter(Boolean).join(' ').toLowerCase().includes(normalizedKeyword)) .map((exhibit) => ({ id: exhibit.id, name: exhibit.name, desc: `${exhibit.hallName || '展品讲解'} · ${exhibit.floorLabel || ''}`.trim(), type: 'exhibit', exhibitId: exhibit.id, hallId: exhibit.hallId, poiId: exhibit.poiId, floorId: exhibit.floorId })) const hallItems = halls .filter((hall) => !normalizedKeyword || [ hall.name, hall.floorLabel, hall.description ].filter(Boolean).join(' ').toLowerCase().includes(normalizedKeyword)) .map((hall) => ({ id: hall.id, name: hall.name, desc: `${hall.floorLabel || ''} · ${hall.exhibitCount || 0}个讲解内容`.trim(), type: 'hall', hallId: hall.id, poiId: hall.poiId, floorId: hall.floorId })) return [...exhibitItems, ...hallItems] } } export const explainRepository = new DefaultExplainRepository()