2026-07-30 16:04:34 +08:00
|
|
|
import type {
|
|
|
|
|
Poi,
|
|
|
|
|
PoiCategory,
|
|
|
|
|
PoiDataset,
|
|
|
|
|
PoiFilter,
|
|
|
|
|
PoiImageAsset,
|
|
|
|
|
PoiOpeningHours,
|
|
|
|
|
PoiResolved,
|
|
|
|
|
PoiSummary,
|
|
|
|
|
PoiTag,
|
|
|
|
|
RecommendationSource,
|
|
|
|
|
} from './types'
|
|
|
|
|
|
|
|
|
|
export function formatOpeningHours(openingHours: PoiOpeningHours): string {
|
|
|
|
|
if (openingHours.status === 'unknown')
|
|
|
|
|
return '开放时间待确认'
|
|
|
|
|
|
|
|
|
|
return openingHours.displayText?.trim() || '开放时间待确认'
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 15:01:44 +08:00
|
|
|
export function formatRecommendationLabel(_source: RecommendationSource, index: number): string {
|
|
|
|
|
return `推荐指数 ${index}/5`
|
2026-07-30 16:04:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function filterPois(pois: readonly Poi[], filter: PoiFilter = {}): Poi[] {
|
|
|
|
|
const requiredTags = Array.from(new Set(filter.tagCodes ?? []))
|
|
|
|
|
|
|
|
|
|
return pois.filter((poi) => {
|
|
|
|
|
if (filter.categoryCode && poi.categoryCode !== filter.categoryCode)
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
return requiredTags.every(tagCode => poi.tagCodes.includes(tagCode))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function findPoiById(pois: readonly Poi[], poiId: string): Poi | null {
|
|
|
|
|
const normalizedId = poiId.trim()
|
|
|
|
|
if (!normalizedId)
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
|
|
return pois.find(poi => poi.id === normalizedId) ?? null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function sortCategories(categories: readonly PoiCategory[]): PoiCategory[] {
|
|
|
|
|
return categories
|
|
|
|
|
.filter(category => category.status === 'enabled')
|
|
|
|
|
.slice()
|
|
|
|
|
.sort((left, right) => left.sortOrder - right.sortOrder || left.name.localeCompare(right.name, 'zh-CN'))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function sortTags(tags: readonly PoiTag[]): PoiTag[] {
|
|
|
|
|
return tags
|
|
|
|
|
.filter(tag => tag.status === 'enabled')
|
|
|
|
|
.slice()
|
|
|
|
|
.sort((left, right) => left.sortOrder - right.sortOrder || left.name.localeCompare(right.name, 'zh-CN'))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resolvePoi(
|
|
|
|
|
poi: Poi,
|
|
|
|
|
categories: readonly PoiCategory[],
|
|
|
|
|
tags: readonly PoiTag[],
|
|
|
|
|
images: readonly PoiImageAsset[],
|
|
|
|
|
): PoiResolved | null {
|
|
|
|
|
const category = categories.find(item => item.code === poi.categoryCode && item.status === 'enabled')
|
|
|
|
|
const coverImage = images.find(item => item.id === poi.coverImageId)
|
|
|
|
|
if (!category || !coverImage)
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
|
|
const tagMap = new Map(tags.map(tag => [tag.code, tag]))
|
|
|
|
|
const imageMap = new Map(images.map(image => [image.id, image]))
|
|
|
|
|
const resolvedTags = poi.tagCodes
|
|
|
|
|
.map(tagCode => tagMap.get(tagCode))
|
|
|
|
|
.filter((tag): tag is PoiTag => Boolean(tag && tag.status === 'enabled'))
|
|
|
|
|
const resolvedImages = poi.imageIds
|
|
|
|
|
.map(imageId => imageMap.get(imageId))
|
|
|
|
|
.filter((image): image is PoiImageAsset => Boolean(image))
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...poi,
|
|
|
|
|
category,
|
|
|
|
|
tags: resolvedTags,
|
|
|
|
|
coverImage,
|
|
|
|
|
images: [coverImage, ...resolvedImages],
|
|
|
|
|
openingHoursText: formatOpeningHours(poi.openingHours),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function toPoiSummary(poi: PoiResolved): PoiSummary {
|
|
|
|
|
return {
|
|
|
|
|
id: poi.id,
|
|
|
|
|
name: poi.name,
|
|
|
|
|
categoryCode: poi.categoryCode,
|
|
|
|
|
categoryName: poi.category.name,
|
|
|
|
|
tagCodes: [...poi.tagCodes],
|
|
|
|
|
tags: poi.tags.map(tag => ({ ...tag, synonyms: tag.synonyms ? [...tag.synonyms] : undefined })),
|
|
|
|
|
longitude: poi.longitude,
|
|
|
|
|
latitude: poi.latitude,
|
|
|
|
|
coordinateSystem: poi.coordinateSystem,
|
|
|
|
|
coverImage: { ...poi.coverImage },
|
|
|
|
|
summary: poi.summary,
|
|
|
|
|
description: poi.description,
|
|
|
|
|
address: poi.address,
|
|
|
|
|
recommendationIndex: poi.recommendationIndex,
|
|
|
|
|
recommendationSource: poi.recommendationSource,
|
|
|
|
|
openingHoursText: poi.openingHoursText,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resolveDatasetPois(dataset: PoiDataset, pois: readonly Poi[]): PoiResolved[] {
|
|
|
|
|
return pois
|
|
|
|
|
.map(poi => resolvePoi(poi, dataset.categories, dataset.tags, dataset.images))
|
|
|
|
|
.filter((poi): poi is PoiResolved => Boolean(poi))
|
|
|
|
|
}
|