实现酒店上下文单酒店收口

This commit is contained in:
andy
2026-07-10 15:31:51 +08:00
parent 7492c21350
commit d69f3975ef
52 changed files with 1183 additions and 188 deletions

View File

@@ -1,6 +1,6 @@
const configuredReservationHotelId = import.meta.env.VITE_RESERVATION_HOTEL_ID?.trim()
export const reservationHotelId = configuredReservationHotelId || 'HOTEL-TEST'
export const reservationHotelId = configuredReservationHotelId || null
let reservationHotelIdProvider: (() => string | null | undefined) | null = null
@@ -8,7 +8,7 @@ export function setReservationHotelIdProvider(provider: (() => string | null | u
reservationHotelIdProvider = provider
}
export function getReservationHotelId(): string {
export function getReservationHotelId(): string | null {
const providedHotelId = reservationHotelIdProvider?.()?.trim()
return providedHotelId || reservationHotelId
}

View File

@@ -29,7 +29,10 @@ export async function uploadDebugEmlSuperAgentRun(
): Promise<DebugEmlSuperAgentRunResult> {
const form = new FormData()
form.append('file', input.file)
form.append('hotel_id', input.hotelId.trim())
const hotelId = input.hotelId?.trim()
if (hotelId) {
form.append('hotel_id', hotelId)
}
const runLabel = input.runLabel?.trim()
if (runLabel) {
form.append('run_label', runLabel)

View File

@@ -152,7 +152,7 @@ function withQuery(path: string, params: object): string {
function withReservationHotel<T extends { hotel_id?: string }>(filters: T): T {
return {
hotel_id: getReservationHotelId(),
hotel_id: getReservationHotelId() ?? undefined,
...filters,
}
}

View File

@@ -77,6 +77,29 @@ describe('debugEmlService', () => {
expect(result.debug_run_id).toBe('90001')
})
it('omits hotel id when debug upload does not choose a hotel', async () => {
const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
mockJsonResponse({
debug_run_id: '90002',
source_message_id: null,
uploaded_media: [],
warnings: [],
status: 'CREATED',
}),
)
const file = new File(['From: guest@example.test'], 'booking.eml', { type: 'message/rfc822' })
await uploadDebugEmlSuperAgentRun({
debugUploadKey: 'manual-debug-key',
file,
})
const [, init] = fetchMock.mock.calls[0]!
const formData = init?.body as FormData
expect(formData.has('hotel_id')).toBe(false)
expect(formData.get('file')).toBe(file)
})
it('throws a typed safe error for backend error_code responses', async () => {
const unauthorizedHandler = vi.fn()
setUnauthorizedHandler(unauthorizedHandler)

View File

@@ -72,7 +72,7 @@ describe('reservationService real API mode', () => {
})
expect(fetchMock).toHaveBeenCalledWith(
'/api/reservation/tasks?hotel_id=HOTEL-TEST&task_status=PENDING_CONFIRM&order_status=ACTIVE&page_num=1&page_size=20',
'/api/reservation/tasks?task_status=PENDING_CONFIRM&order_status=ACTIVE&page_num=1&page_size=20',
expect.objectContaining({ method: 'GET' }),
)
expect(result.items).toHaveLength(1)
@@ -99,12 +99,12 @@ describe('reservationService real API mode', () => {
})
expect(fetchMock).toHaveBeenCalledWith(
'/api/reservation/tasks?hotel_id=HOTEL-TEST&task_subtype=RATE_CHANGE&task_status=FAILED&page_num=1&page_size=20',
'/api/reservation/tasks?task_subtype=RATE_CHANGE&task_status=FAILED&page_num=1&page_size=20',
expect.objectContaining({ method: 'GET' }),
)
})
it('uses the selected auth hotel before falling back to the environment hotel id', async () => {
it('uses the selected auth hotel when available', async () => {
const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
mockJsonResponse({
items: [],
@@ -162,7 +162,7 @@ describe('reservationService real API mode', () => {
})
expect(fetchMock).toHaveBeenCalledWith(
'/api/reservation/orders?hotel_id=HOTEL-TEST&keyword=GRP-001&page_num=1&page_size=20',
'/api/reservation/orders?keyword=GRP-001&page_num=1&page_size=20',
expect.objectContaining({ method: 'GET' }),
)
expect(result.items[0]?.display_order_key).toBe('GRP-001')
@@ -191,7 +191,7 @@ describe('reservationService real API mode', () => {
})
expect(fetchMock).toHaveBeenCalledWith(
'/api/reservation/orders?hotel_id=HOTEL-TEST&order_status=LOGIC_DELETED&group_code=GRP-001&confirmation_number=CNF-001&keyword=VIP&page_num=2&page_size=10',
'/api/reservation/orders?order_status=LOGIC_DELETED&group_code=GRP-001&confirmation_number=CNF-001&keyword=VIP&page_num=2&page_size=10',
expect.objectContaining({ method: 'GET' }),
)
})
@@ -257,7 +257,7 @@ describe('reservationService real API mode', () => {
const result = await fetchReservationOrderDetail('20001')
expect(fetchMock).toHaveBeenCalledWith(
'/api/reservation/orders/20001?hotel_id=HOTEL-TEST&include_tasks=true&include_source_summary=true',
'/api/reservation/orders/20001?include_tasks=true&include_source_summary=true',
expect.objectContaining({ method: 'GET' }),
)
expect(result.order.display_name).toBe('GRP-001')

View File

@@ -11,7 +11,7 @@ export type DebugEmlErrorCode =
| string
export interface DebugEmlUploadInput {
hotelId: string
hotelId?: string | null
debugUploadKey: string
file: File
runLabel?: string

View File

@@ -348,7 +348,7 @@ type DebugEmlStatus = 'idle' | 'uploading' | 'succeeded' | 'failed'
const { t } = useI18n()
const hotelId = ref(reservationHotelId)
const hotelId = ref(reservationHotelId ?? '')
const debugUploadKey = ref('')
const runLabel = ref('')
const selectedFile = ref<File | null>(null)
@@ -359,7 +359,7 @@ const errorMessage = ref('')
const busy = computed(() => status.value === 'uploading')
const submitDisabled = computed(
() => busy.value || !hotelId.value.trim() || !debugUploadKey.value.trim() || !selectedFile.value,
() => busy.value || !debugUploadKey.value.trim() || !selectedFile.value,
)
const statusLabel = computed(() => t(`debugEml.statuses.${status.value}`))
const errorDisplayMessage = computed(() => {