121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
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<MuseumExhibit[]>
|
|
getExhibitById(id: string): Promise<MuseumExhibit | null>
|
|
listHalls(): Promise<MuseumHall[]>
|
|
getHallById(id: string): Promise<MuseumHall | null>
|
|
listTracks(): Promise<ExplainTrack[]>
|
|
getTrackByExhibitId(exhibitId: string): Promise<ExplainTrack | null>
|
|
searchExplain(keyword?: string): Promise<SearchIndexItem[]>
|
|
}
|
|
|
|
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<ExplainTrack> => {
|
|
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<SearchIndexItem>((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<SearchIndexItem>((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()
|