Files
admin-web/src/modules/trucking/api/platformTrucking.ts
T

124 lines
6.4 KiB
TypeScript
Raw Normal View History

2026-08-08 13:32:27 +08:00
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
import type {
SaveTruckAppointmentRequest,
TruckAppointmentPage,
TruckAppointmentQuery,
TruckAppointmentResource,
2026-08-08 15:57:07 +08:00
TruckAppointmentStatusHistory,
TruckAttachmentReference,
2026-08-08 13:32:27 +08:00
} from '../types'
const basePath = '/platform/v1/truck-appointments'
2026-08-17 16:45:46 +08:00
function operationId(action: string, id: string | number) {
return `truck-appointment-${action.toLowerCase()}-${id}-${Date.now()}-${Math.random().toString(36).slice(2)}`
}
2026-08-08 13:32:27 +08:00
function dtoPayload(payload: SaveTruckAppointmentRequest) {
2026-08-08 14:13:32 +08:00
return Object.fromEntries(Object.entries(payload).map(([key, value]) => [key, value === '' ? null : value])) as SaveTruckAppointmentRequest
2026-08-08 13:32:27 +08:00
}
type PagePayload = Omit<TruckAppointmentPage, 'page' | 'size' | 'total' | 'pages'> & {
page: number | string
size: number | string
total: number | string
pages?: number | string
}
function normalizePage(payload: PagePayload): TruckAppointmentPage {
const page = Number(payload.page || 1)
const size = Number(payload.size || 20)
const total = Number(payload.total || 0)
return { records: (payload.records || []).map(normalizeResource), page, size, total, pages: Number(payload.pages || Math.max(Math.ceil(total / size), 1)) }
}
function normalizeResource(resource: TruckAppointmentResource): TruckAppointmentResource {
const idKeys = ['id', 'outboundOrderId', 'oceanBookingId', 'containerBookingId', 'loadingPlanId', 'warehouseId', 'customerId', 'carrierId']
const normalized = { ...resource } as Record<string, unknown>
for (const key of idKeys) {
if (normalized[key] !== null && normalized[key] !== undefined) normalized[key] = String(normalized[key])
}
return normalized as unknown as TruckAppointmentResource
}
export async function listTruckAppointments(query: TruckAppointmentQuery = {}) {
const response = await http.get<ApiEnvelope<PagePayload>, ApiEnvelope<PagePayload>>(basePath, {
params: {
...query,
keyword: query.keyword?.trim() || undefined,
status: query.status || undefined,
containerBookingId: query.containerBookingId || undefined,
2026-08-08 15:05:37 +08:00
containerType: query.containerType || undefined,
2026-08-08 13:32:27 +08:00
},
})
return normalizePage(unwrapApiResponse(response))
}
export async function getTruckAppointment(id: string | number) {
const response = await http.get<ApiEnvelope<TruckAppointmentResource>, ApiEnvelope<TruckAppointmentResource>>(`${basePath}/${id}`)
return normalizeResource(unwrapApiResponse(response))
}
2026-08-08 15:57:07 +08:00
export async function listTruckAppointmentHistory(id: string | number) {
const response = await http.get<ApiEnvelope<TruckAppointmentStatusHistory[] | { records?: TruckAppointmentStatusHistory[] }>, ApiEnvelope<TruckAppointmentStatusHistory[] | { records?: TruckAppointmentStatusHistory[] }>>(`${basePath}/${id}/history`)
const payload = unwrapApiResponse(response)
const records = Array.isArray(payload) ? payload : payload?.records || []
return records.map((item) => ({ ...item, id: item.id == null ? item.id : String(item.id), operatorId: item.operatorId == null ? item.operatorId : String(item.operatorId) }))
}
export async function listTruckAppointmentAttachments(id: string | number) {
const response = await http.get<ApiEnvelope<TruckAttachmentReference[]>, ApiEnvelope<TruckAttachmentReference[]>>(`${basePath}/${id}/attachments`)
return unwrapApiResponse(response).map((item) => ({ ...item, id: item.id == null ? item.id : String(item.id), fileId: String(item.fileId), uploadedBy: item.uploadedBy == null ? item.uploadedBy : String(item.uploadedBy) }))
}
export async function referenceTruckAppointmentAttachment(id: string | number, payload: { fileId: string; attachmentType?: string | null; fileName?: string | null; fileHash?: string | null }) {
const response = await http.post<ApiEnvelope<TruckAttachmentReference>, ApiEnvelope<TruckAttachmentReference>>(`${basePath}/${id}/attachments`, payload)
2026-08-08 15:57:07 +08:00
return unwrapApiResponse(response)
}
2026-08-14 14:27:03 +08:00
export async function uploadTruckAppointmentAttachment(id: string | number, file: File, attachmentType?: string | null) {
const form = new FormData()
form.append('file', file)
if (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)
}
2026-08-08 15:57:07 +08:00
export async function deleteTruckAppointmentAttachment(id: string | number, attachmentId: string | number) {
await http.delete<ApiEnvelope<unknown>, ApiEnvelope<unknown>>(`${basePath}/${id}/attachments/${attachmentId}`)
}
2026-08-08 13:32:27 +08:00
export async function createTruckAppointment(payload: SaveTruckAppointmentRequest) {
const response = await http.post<ApiEnvelope<TruckAppointmentResource>, ApiEnvelope<TruckAppointmentResource>>(basePath, dtoPayload(payload))
return normalizeResource(unwrapApiResponse(response))
}
export async function updateTruckAppointment(id: string | number, payload: SaveTruckAppointmentRequest) {
const response = await http.put<ApiEnvelope<TruckAppointmentResource>, ApiEnvelope<TruckAppointmentResource>>(`${basePath}/${id}`, dtoPayload(payload))
return unwrapApiResponse(response)
}
export type TruckAppointmentAction = 'SUBMIT' | 'DISPATCH' | 'PICKUP' | 'ARRIVE' | 'LOAD' | 'DEPART' | 'COMPLETE' | 'EXCEPTION' | 'CANCEL'
export async function performTruckAppointmentAction(
id: string | number,
action: TruckAppointmentAction,
expectedVersion: number,
reason?: string,
idempotencyKey?: string,
requestHash?: string,
) {
2026-08-17 16:45:46 +08:00
const operationKey = idempotencyKey || operationId(action, id)
2026-08-08 13:32:27 +08:00
const response = await http.post<ApiEnvelope<TruckAppointmentResource>, ApiEnvelope<TruckAppointmentResource>>(`${basePath}/${id}/${action.toLowerCase()}`, { expectedVersion, reason: reason || null }, {
2026-08-17 16:45:46 +08:00
headers: { 'Idempotency-Key': operationKey, ...(requestHash ? { 'X-Request-Hash': requestHash } : {}) },
2026-08-08 13:32:27 +08:00
})
return unwrapApiResponse(response)
}
export async function rescheduleTruckAppointment(id: string | number, plannedLoadingAt: string, expectedVersion: number, reason?: string) {
2026-08-17 16:45:46 +08:00
const response = await http.post<ApiEnvelope<TruckAppointmentResource>, ApiEnvelope<TruckAppointmentResource>>(`${basePath}/${id}/reschedule`, { expectedVersion, reason: reason || null }, {
params: { plannedLoadingAt },
headers: { 'Idempotency-Key': operationId('RESCHEDULE', id) },
})
2026-08-08 13:32:27 +08:00
return unwrapApiResponse(response)
}