Files
frontend-miniapp/src/repositories/MuseumContentRepository.ts
2026-06-12 09:25:22 +08:00

125 lines
3.5 KiB
TypeScript

import type {
MuseumExhibit,
MuseumHall,
MuseumPoi,
SearchIndexItem
} from '@/domain/museum'
import {
guideRepository,
type GuideRepository
} from '@/repositories/GuideRepository'
import {
staticMuseumContentProvider,
type MuseumContentProvider
} from '@/data/providers/staticMuseumContentProvider'
export interface MuseumContentRepository {
listFacilities(): Promise<MuseumPoi[]>
getFacilityById(id: string): Promise<MuseumPoi | null>
searchFacilities(keyword?: string): Promise<MuseumPoi[]>
listExhibits(): Promise<MuseumExhibit[]>
getExhibitById(id: string): Promise<MuseumExhibit | null>
listHalls(): Promise<MuseumHall[]>
getHallById(id: string): Promise<MuseumHall | null>
searchContent(keyword?: string): Promise<SearchIndexItem[]>
}
const normalize = (value: string) => value.trim().toLowerCase()
const includesKeyword = (values: Array<string | undefined>, keyword: string) => (
values.filter(Boolean).join(' ').toLowerCase().includes(keyword)
)
export class DefaultMuseumContentRepository implements MuseumContentRepository {
constructor(
private readonly contentProvider: MuseumContentProvider = staticMuseumContentProvider,
private readonly guide: GuideRepository = guideRepository
) {}
listFacilities() {
return this.guide.listPois()
}
getFacilityById(id: string) {
return this.guide.getPoiById(id)
}
searchFacilities(keyword = '') {
return this.guide.searchPois(keyword)
}
listExhibits() {
return this.contentProvider.listExhibits()
}
async getExhibitById(id: string) {
const exhibits = await this.contentProvider.listExhibits()
return exhibits.find((exhibit) => exhibit.id === id) || null
}
listHalls() {
return this.contentProvider.listHalls()
}
async getHallById(id: string) {
const halls = await this.contentProvider.listHalls()
return halls.find((hall) => hall.id === id) || null
}
async searchContent(keyword = '') {
const normalizedKeyword = normalize(keyword)
const facilities = await this.searchFacilities(keyword)
const exhibits = await this.listExhibits()
const halls = await this.listHalls()
const exhibitItems = exhibits
.filter((exhibit) => !normalizedKeyword || includesKeyword([
exhibit.name,
exhibit.hallName,
exhibit.floorLabel,
...(exhibit.tags || [])
], normalizedKeyword))
.map<SearchIndexItem>((exhibit) => ({
id: exhibit.id,
name: exhibit.name,
desc: `${exhibit.hallName || '展项'} · ${exhibit.floorLabel || ''}`.trim(),
type: 'exhibit',
exhibitId: exhibit.id,
poiId: exhibit.poiId,
floorId: exhibit.floorId
}))
const hallItems = halls
.filter((hall) => !normalizedKeyword || includesKeyword([
hall.name,
hall.floorLabel,
hall.description
], 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
}))
const facilityItems = facilities.map<SearchIndexItem>((poi) => ({
id: poi.id,
name: poi.name,
desc: `${poi.floorLabel} · ${poi.primaryCategory.label}`,
type: 'facility',
poiId: poi.id,
floorId: poi.floorId
}))
return [
...exhibitItems,
...hallItems,
...facilityItems
]
}
}
export const museumContentRepository = new DefaultMuseumContentRepository()