fix(admin): preserve tail-leg references

This commit is contained in:
2026-09-01 11:03:45 +08:00
parent 0cf4a93f9c
commit 4711bcc4e6
10 changed files with 80 additions and 16 deletions
@@ -69,4 +69,16 @@ describe('truck appointment API', () => {
expect(form.get('file')).toBe(file)
expect(form.get('attachmentType')).toBe('DISPATCH_ORDER')
})
it('never forwards a legacy booking document type into truck attachments', async () => {
vi.mocked(http.post).mockResolvedValue({ data: { id: '1', fileId: '2', fileName: 'legacy.pdf' } })
await referenceTruckAppointmentAttachment('77', { fileId: '2', fileName: 'legacy.pdf', attachmentType: 'BILL_OF_LADING' as never })
expect(http.post).toHaveBeenLastCalledWith('/platform/v1/truck-appointments/77/attachments', { fileId: '2', fileName: 'legacy.pdf' })
const file = new File(['legacy'], 'legacy.pdf', { type: 'application/pdf' })
await uploadTruckAppointmentAttachment('77', file, 'BILL_OF_LADING' as never)
const form = vi.mocked(http.post).mock.calls.at(-1)?.[1] as FormData
expect(form.get('file')).toBe(file)
expect(form.has('attachmentType')).toBe(false)
})
})
+5 -3
View File
@@ -1,5 +1,5 @@
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
import { TRUCK_APPOINTMENT_SAVE_FIELDS, normalizeTruckAppointmentCurrency } from '../types'
import { TRUCK_APPOINTMENT_SAVE_FIELDS, isTruckAttachmentType, normalizeTruckAppointmentCurrency } from '../types'
import type {
SaveTruckAppointmentRequest,
TruckAppointmentPage,
@@ -77,14 +77,16 @@ export async function listTruckAppointmentAttachments(id: string | number) {
}
export async function referenceTruckAppointmentAttachment(id: string | number, payload: { fileId: string; attachmentType?: TruckAttachmentType | null; fileName?: string | null; fileHash?: string | null }) {
const response = await http.post<ApiEnvelope<TruckAttachmentReference>, ApiEnvelope<TruckAttachmentReference>>(`${basePath}/${id}/attachments`, payload)
const { attachmentType, ...rest } = payload
const body = { ...rest, ...(isTruckAttachmentType(attachmentType) ? { attachmentType } : {}) }
const response = await http.post<ApiEnvelope<TruckAttachmentReference>, ApiEnvelope<TruckAttachmentReference>>(`${basePath}/${id}/attachments`, body)
return unwrapApiResponse(response)
}
export async function uploadTruckAppointmentAttachment(id: string | number, file: File, attachmentType?: TruckAttachmentType | null) {
const form = new FormData()
form.append('file', file)
if (attachmentType) form.append('attachmentType', attachmentType)
if (isTruckAttachmentType(attachmentType)) form.append('attachmentType', attachmentType)
const response = await http.post<ApiEnvelope<TruckAttachmentReference>, ApiEnvelope<TruckAttachmentReference>>(`${basePath}/${id}/attachments/upload`, form, { headers: { 'Content-Type': 'multipart/form-data' } })
return unwrapApiResponse(response)
}
+5 -1
View File
@@ -52,7 +52,11 @@ export function truckAppointmentActionLabel(action: string | null | undefined) {
return truckAppointmentActionLabels[action] || '状态更新'
}
export type TruckAttachmentType = 'DISPATCH_ORDER' | 'DRIVER_DOCUMENT' | 'VEHICLE_DOCUMENT' | 'OTHER'
export const TRUCK_ATTACHMENT_TYPES = ['DISPATCH_ORDER', 'DRIVER_DOCUMENT', 'VEHICLE_DOCUMENT', 'OTHER'] as const
export type TruckAttachmentType = typeof TRUCK_ATTACHMENT_TYPES[number]
export function isTruckAttachmentType(value: unknown): value is TruckAttachmentType {
return TRUCK_ATTACHMENT_TYPES.includes(value as TruckAttachmentType)
}
export const TRUCK_APPOINTMENT_CURRENCIES = ['RMB', 'USD'] as const
export type TruckAppointmentCurrency = typeof TRUCK_APPOINTMENT_CURRENCIES[number]
export function normalizeTruckAppointmentCurrency(value: unknown): TruckAppointmentCurrency {