修复导览点位可见性与构建门禁
This commit is contained in:
@@ -5,7 +5,11 @@ import {
|
||||
} from '@/domain/poiCategories'
|
||||
import type { SgsSdkApiProvider, SgsSdkManifestPayload } from '@/data/providers/sgsSdkApiProvider'
|
||||
import type { StaticNavAssetsProvider } from '@/data/providers/staticNavAssetsProvider'
|
||||
import { SgsSdkGuideRepository, StaticGuideRepository } from '@/repositories/GuideRepository'
|
||||
import {
|
||||
canonicalizeVisitorSearchPois,
|
||||
SgsSdkGuideRepository,
|
||||
StaticGuideRepository
|
||||
} from '@/repositories/GuideRepository'
|
||||
import {
|
||||
SgsSdkGuideModelRepository,
|
||||
StaticGuideModelRepository
|
||||
@@ -40,7 +44,17 @@ const floorSpaces = [
|
||||
{ id: 'space-hall', name: '地球展厅', type: 'exhibition_hall', floorId: 'L1', center: { x: 1, y: 12, z: 1 } },
|
||||
{ id: 'space-cinema', name: '穹幕影院', type: 'theater', floorId: 'L1', center: { x: 2, y: 12, z: 2 } },
|
||||
{ id: 'space-dining', name: '餐饮区', type: 'restaurant', floorId: 'L1', center: { x: 3, y: 12, z: 3 } },
|
||||
{ id: 'space-shopping', name: '文创商店', type: 'commercial', floorId: 'L1', center: { x: 4, y: 12, z: 4 } }
|
||||
{ id: 'space-shopping', name: '文创商店', type: 'commercial', floorId: 'L1', center: { x: 4, y: 12, z: 4 } },
|
||||
{ id: 'space-private-tea', name: '茶水间', type: 'service_space', floorId: 'L1', center: { x: 5, y: 12, z: 5 } },
|
||||
{ id: 'space-private-vip', name: '贵宾接待区', type: 'service_space', floorId: 'L1', center: { x: 6, y: 12, z: 6 } },
|
||||
{
|
||||
id: 'space-public-rest',
|
||||
name: '游客休息区',
|
||||
type: 'service_space',
|
||||
floorId: 'L1',
|
||||
center: { x: 7, y: 12, z: 7 },
|
||||
visitorVisible: true
|
||||
}
|
||||
]
|
||||
|
||||
const floorPois = [
|
||||
@@ -120,17 +134,25 @@ describe('GuideRepository visitor search contracts', () => {
|
||||
const provider = createSgsProvider()
|
||||
const repository = new SgsSdkGuideRepository(provider)
|
||||
|
||||
const [defaultResults, availability] = await Promise.all([
|
||||
const [defaultResults, availability, privateKeyword, publicKeyword] = await Promise.all([
|
||||
repository.listDestinationPois('L1'),
|
||||
repository.getQuickFindCategoryAvailability('L1')
|
||||
repository.getQuickFindCategoryAvailability('L1'),
|
||||
repository.searchPois('茶水间', 'L1'),
|
||||
repository.searchPois('游客休息区', 'L1')
|
||||
])
|
||||
|
||||
expect(defaultResults.map((poi) => poi.id)).toEqual(expect.arrayContaining([
|
||||
'hall-space-hall',
|
||||
'hall-space-cinema',
|
||||
'space-space-dining',
|
||||
'space-space-shopping'
|
||||
'space-space-shopping',
|
||||
'space-space-public-rest'
|
||||
]))
|
||||
for (const privatePoiId of ['space-space-private-tea', 'space-space-private-vip']) {
|
||||
expect(defaultResults.map((poi) => poi.id)).not.toContain(privatePoiId)
|
||||
}
|
||||
expect(privateKeyword).toEqual([])
|
||||
expect(publicKeyword.map((poi) => poi.id)).toEqual(['space-space-public-rest'])
|
||||
expect(defaultResults.map((poi) => poi.id)).not.toEqual(expect.arrayContaining([
|
||||
'restroom',
|
||||
'elevator',
|
||||
@@ -160,9 +182,13 @@ describe('GuideRepository visitor search contracts', () => {
|
||||
// survive model marker deduplication.
|
||||
expect(renderPoiIds.has('restroom')).toBe(true)
|
||||
expect(renderPoiIds.has('elevator')).toBe(true)
|
||||
expect(renderPoiIds.has('space-space-public-rest')).toBe(true)
|
||||
for (const hiddenPoiId of ['door', 'anchor', 'route-node', 'guide-stop']) {
|
||||
expect(renderPoiIds.has(hiddenPoiId)).toBe(false)
|
||||
}
|
||||
for (const privatePoiId of ['space-space-private-tea', 'space-space-private-vip']) {
|
||||
expect(renderPoiIds.has(privatePoiId)).toBe(false)
|
||||
}
|
||||
const quickResultIds = (await Promise.all(allQuickCategoryIds.map((categoryId) => (
|
||||
repository.listQuickFindPois(categoryId, 'L1')
|
||||
)))).flat().map((poi) => poi.id)
|
||||
@@ -188,6 +214,115 @@ describe('GuideRepository visitor search contracts', () => {
|
||||
expect(hiddenKeyword).toEqual([])
|
||||
})
|
||||
|
||||
it('does not resolve private direct IDs outside the visitor search pool', async () => {
|
||||
const repository = new SgsSdkGuideRepository(createSgsProvider({
|
||||
queryPois: vi.fn().mockResolvedValue([{
|
||||
id: 'private-direct-space',
|
||||
name: '茶水间',
|
||||
type: 'service_space',
|
||||
floorId: 'L1',
|
||||
visitorVisible: false,
|
||||
position: { x: 30, y: 12, z: 30 }
|
||||
}])
|
||||
}))
|
||||
|
||||
await expect(repository.getPoiById('private-direct-space')).resolves.toBeNull()
|
||||
})
|
||||
|
||||
it('keeps private and structural space duplicates out of search lists and map markers', async () => {
|
||||
const provider = createSgsProvider({
|
||||
getFloorPois: vi.fn().mockResolvedValue([
|
||||
{
|
||||
id: 'poi-private-dining',
|
||||
name: '西侧餐饮区',
|
||||
type: 'restaurant',
|
||||
floorId: 'L1',
|
||||
spatialAreaId: 'space-private-dining',
|
||||
position: { x: 30, y: 12, z: 30 }
|
||||
},
|
||||
{
|
||||
id: 'poi-service-space',
|
||||
name: '服务空间',
|
||||
type: 'service_space',
|
||||
floorId: 'L1',
|
||||
position: { x: 31, y: 12, z: 31 }
|
||||
}
|
||||
]),
|
||||
getFloorBusinessPois: vi.fn().mockResolvedValue([]),
|
||||
getFloorSpaces: vi.fn().mockResolvedValue([
|
||||
{
|
||||
id: 'space-private-dining',
|
||||
name: '西侧餐饮区',
|
||||
type: 'restaurant',
|
||||
floorId: 'L1',
|
||||
visitorVisible: false,
|
||||
center: { x: 30, y: 12, z: 30 }
|
||||
},
|
||||
{
|
||||
id: 'space-private-cinema',
|
||||
name: '私享影院',
|
||||
type: 'theater',
|
||||
floorId: 'L1',
|
||||
visitorVisible: false,
|
||||
center: { x: 32, y: 12, z: 32 }
|
||||
},
|
||||
{
|
||||
id: 'space-route-node',
|
||||
name: '导航区域',
|
||||
type: 'navigable_place',
|
||||
floorId: 'L1',
|
||||
center: { x: 33, y: 12, z: 33 }
|
||||
}
|
||||
]),
|
||||
getNavigablePlaces: vi.fn().mockResolvedValue([])
|
||||
})
|
||||
const repository = new SgsSdkGuideRepository(provider)
|
||||
const modelRepository = new SgsSdkGuideModelRepository(provider, repository)
|
||||
|
||||
const [defaultResults, dining, cinema, privateKeyword, serviceKeyword, structuralKeyword, renderPois] = await Promise.all([
|
||||
repository.listDestinationPois('L1'),
|
||||
repository.listQuickFindPois('dining', 'L1'),
|
||||
repository.listQuickFindPois('cinema', 'L1'),
|
||||
repository.searchPois('西侧餐饮区', 'L1'),
|
||||
repository.searchPois('服务空间', 'L1'),
|
||||
repository.searchPois('导航区域', 'L1'),
|
||||
modelRepository.loadFloorPois('L1')
|
||||
])
|
||||
|
||||
expect(defaultResults).toEqual([])
|
||||
expect(dining).toEqual([])
|
||||
expect(cinema).toEqual([])
|
||||
expect(privateKeyword).toEqual([])
|
||||
expect(serviceKeyword).toEqual([])
|
||||
expect(structuralKeyword).toEqual([])
|
||||
expect(renderPois).toEqual([])
|
||||
})
|
||||
|
||||
it('merges source-place duplicates before returning canonical visitor IDs', () => {
|
||||
const basePoi = {
|
||||
floorId: 'L1',
|
||||
floorLabel: '1F',
|
||||
primaryCategory: { id: 'space_restaurant', label: '餐饮', iconType: 'restaurant' },
|
||||
categories: [{ id: 'space_restaurant', label: '餐饮', iconType: 'restaurant' }],
|
||||
positionGltf: [40, 12, 40] as [number, number, number],
|
||||
accessible: false,
|
||||
kind: 'facility' as const,
|
||||
sourcePlaceId: 'place-cafe'
|
||||
}
|
||||
|
||||
const results = canonicalizeVisitorSearchPois([
|
||||
{ ...basePoi, id: 'poi-cafe-a', name: '中庭咖啡' },
|
||||
{
|
||||
...basePoi,
|
||||
id: 'poi-cafe-b',
|
||||
name: '中庭咖啡取餐台',
|
||||
positionGltf: [41, 12, 41]
|
||||
}
|
||||
])
|
||||
|
||||
expect(results.map((poi) => poi.id)).toEqual(['poi-cafe-a'])
|
||||
})
|
||||
|
||||
it('deduplicates co-located sources when they do not provide a shared spatial area ID', async () => {
|
||||
const repository = new SgsSdkGuideRepository(createSgsProvider({
|
||||
getFloorPois: vi.fn().mockResolvedValue([{
|
||||
@@ -348,6 +483,24 @@ describe('GuideRepository visitor search contracts', () => {
|
||||
primaryCategoryZh: '基础服务设施',
|
||||
iconType: 'restroom',
|
||||
positionGltf: position(4)
|
||||
},
|
||||
{
|
||||
id: 'static-vip-reception',
|
||||
name: '贵宾接待区',
|
||||
floorId: 'L1',
|
||||
primaryCategory: 'basic_service_facility',
|
||||
primaryCategoryZh: '基础服务设施',
|
||||
iconType: 'restroom',
|
||||
positionGltf: position(5)
|
||||
},
|
||||
{
|
||||
id: 'static-vip-restroom',
|
||||
name: '贵宾卫生间',
|
||||
floorId: 'L1',
|
||||
primaryCategory: 'basic_service_facility',
|
||||
primaryCategoryZh: '基础服务设施',
|
||||
iconType: 'restroom',
|
||||
positionGltf: position(6)
|
||||
}
|
||||
])
|
||||
} as unknown as StaticNavAssetsProvider
|
||||
@@ -362,6 +515,10 @@ describe('GuideRepository visitor search contracts', () => {
|
||||
await expect(repository.searchPois('洗手间', 'L1')).resolves.toMatchObject([
|
||||
{ id: 'static-restroom' }
|
||||
])
|
||||
await expect(repository.searchPois('茶水间', 'L1')).resolves.toEqual([])
|
||||
await expect(repository.searchPois('贵宾', 'L1')).resolves.toEqual([])
|
||||
await expect(repository.getPoiById('static-tea-room')).resolves.toBeNull()
|
||||
await expect(repository.getPoiById('static-vip-restroom')).resolves.toBeNull()
|
||||
await expect(repository.searchPois('服务', 'L1')).resolves.toEqual([
|
||||
expect.objectContaining({ id: 'static-service-desk' })
|
||||
])
|
||||
@@ -397,6 +554,24 @@ describe('GuideRepository visitor search contracts', () => {
|
||||
primaryCategoryZh: '讲解点',
|
||||
iconType: 'guide',
|
||||
positionGltf: position(2)
|
||||
},
|
||||
{
|
||||
id: 'static-private-tea',
|
||||
name: '茶水间',
|
||||
floorId: 'L1',
|
||||
primaryCategory: 'basic_service_facility',
|
||||
primaryCategoryZh: '基础服务设施',
|
||||
iconType: 'restroom',
|
||||
positionGltf: position(3)
|
||||
},
|
||||
{
|
||||
id: 'static-vip-restroom',
|
||||
name: '贵宾卫生间',
|
||||
floorId: 'L1',
|
||||
primaryCategory: 'basic_service_facility',
|
||||
primaryCategoryZh: '基础服务设施',
|
||||
iconType: 'restroom',
|
||||
positionGltf: position(4)
|
||||
}
|
||||
])
|
||||
} as unknown as StaticNavAssetsProvider
|
||||
|
||||
Reference in New Issue
Block a user