优化
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { createOceanBooking, deleteOceanBookingDraft, listOceanBookings, listWarehouseLogisticsChannels, releaseOceanBooking, updateOceanBookingDraft } from './platformOceanBookings'
|
||||
import { createOceanBooking, deleteOceanBookingDraft, listOceanBookingLinkCandidates, listOceanBookings, listWarehouseLogisticsChannels, releaseOceanBooking, updateOceanBookingDraft } from './platformOceanBookings'
|
||||
import { http } from '@/shared/api/http'
|
||||
|
||||
const detailResponse = (version = 0, status = 'DRAFT') => ({
|
||||
@@ -23,6 +23,16 @@ describe('ocean bookings api', () => {
|
||||
expect(result.records[0]).toMatchObject({ shipperName: '发货企业', consigneeName: '收货企业', containerCount: 3 })
|
||||
})
|
||||
|
||||
it('uses the frozen single-query link candidate contract and normalizes long ids', async () => {
|
||||
vi.spyOn(http, 'get').mockResolvedValueOnce({ code: '200', data: { records: [{ oceanBookingId: 9007199254740993, bookingNo: 'OB-1', requirementId: 9007199254740994, containerType: '40HC', capacityCbm: '76', maxWeightKg: '28000', requiredQuantity: '2', allocatedQuantity: '1', remainingQuantity: '1', customerName: '客户', warehouseName: '义乌仓', channelName: '美森正班' }], page: '1', size: '200', total: '1', pages: '1' } })
|
||||
const query = { warehouseId: '8', channelCode: 'ZBYHK', containerType: '40HC', capacityCbm: 76, maxWeightKg: 28000, page: 1, size: 200 }
|
||||
|
||||
const result = await listOceanBookingLinkCandidates(query)
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/platform/v1/ocean-bookings/link-candidates', { params: query })
|
||||
expect(result.records[0]).toMatchObject({ oceanBookingId: '9007199254740992', requirementId: '9007199254740994', capacityCbm: 76, maxWeightKg: 28000, requiredQuantity: 2, allocatedQuantity: 1, remainingQuantity: 1 })
|
||||
})
|
||||
|
||||
it('uses version zero for create and extracts the id from the returned Detail', async () => {
|
||||
const post = vi.spyOn(http, 'post').mockResolvedValue({ code: '200', message: 'ok', data: detailResponse() })
|
||||
const created = await createOceanBooking({ expectedVersion: 0, data: { customerId: null, customerName: null, channelCode: null, warehouseId: null, billOfLadingNo: '', vesselName: '', voyageNo: '', estimatedSailingAt: null, estimatedArrivalAt: null, serviceTerm: null, freightTerm: null, parties: [], containerRequirements: [{ containerType: '40HC', containerKind: 'FCL', quantity: 2, capacityCbm: 76, maxWeightKg: 28000 }] } })
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
|
||||
import type { OceanBookingDetail, OceanBookingPage, OceanBookingQuery, OceanBookingResource, OceanBookingSaveRequest, OceanBookingStatus, OceanBookingReleaseRequest } from '../types'
|
||||
import type { OceanBookingDetail, OceanBookingLinkCandidatePage, OceanBookingLinkCandidateQuery, OceanBookingPage, OceanBookingQuery, OceanBookingResource, OceanBookingSaveRequest, OceanBookingStatus, OceanBookingReleaseRequest } from '../types'
|
||||
|
||||
const basePath = '/platform/v1/ocean-bookings'
|
||||
|
||||
@@ -59,6 +59,27 @@ export async function listOceanBookings(query: OceanBookingQuery) {
|
||||
const result = unwrapApiResponse(response)
|
||||
return { ...result, records: (result.records || []).map(booking), page: pageNumber(result.page, query.page), size: pageNumber(result.size, query.size), total: pageNumber(result.total, 0), pages: pageNumber(result.pages, 0) } satisfies OceanBookingPage
|
||||
}
|
||||
export async function listOceanBookingLinkCandidates(query: OceanBookingLinkCandidateQuery) {
|
||||
const response = await http.get<ApiEnvelope<OceanBookingLinkCandidatePage>, ApiEnvelope<OceanBookingLinkCandidatePage>>(`${basePath}/link-candidates`, { params: query })
|
||||
const result = unwrapApiResponse(response)
|
||||
return {
|
||||
...result,
|
||||
records: (result.records || []).map((candidate) => ({
|
||||
...candidate,
|
||||
oceanBookingId: String(candidate.oceanBookingId),
|
||||
requirementId: String(candidate.requirementId),
|
||||
capacityCbm: Number(candidate.capacityCbm),
|
||||
maxWeightKg: Number(candidate.maxWeightKg),
|
||||
requiredQuantity: Number(candidate.requiredQuantity),
|
||||
allocatedQuantity: Number(candidate.allocatedQuantity),
|
||||
remainingQuantity: Number(candidate.remainingQuantity),
|
||||
})),
|
||||
page: pageNumber(result.page, query.page),
|
||||
size: pageNumber(result.size, query.size),
|
||||
total: pageNumber(result.total, 0),
|
||||
pages: pageNumber(result.pages, 0),
|
||||
} satisfies OceanBookingLinkCandidatePage
|
||||
}
|
||||
export async function getOceanBooking(id: string) { const response = await http.get<ApiEnvelope<OceanBookingDetail>, ApiEnvelope<OceanBookingDetail>>(`${basePath}/${encodeURIComponent(id)}`); return detail(unwrapApiResponse(response)) }
|
||||
function idempotencyKey() { return `ocean-booking-${Date.now()}-${Math.random().toString(36).slice(2)}` }
|
||||
export async function createOceanBooking(payload: OceanBookingSaveRequest) { const response = await http.post<ApiEnvelope<OceanBookingDetail>, ApiEnvelope<OceanBookingDetail>>(basePath, payload, { headers: { 'Idempotency-Key': idempotencyKey() } }); return detail(unwrapApiResponse(response)) }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { oceanBookingRoutes } from './routes'
|
||||
export { getOceanBooking, listOceanBookings, listWarehouseLogisticsChannels, type OceanLogisticsChannel } from './api/platformOceanBookings'
|
||||
export { getOceanBooking, listOceanBookingLinkCandidates, listOceanBookings, listWarehouseLogisticsChannels, type OceanLogisticsChannel } from './api/platformOceanBookings'
|
||||
export * from './status'
|
||||
export * from './types'
|
||||
|
||||
@@ -52,5 +52,29 @@ export interface OceanBookingDetail extends OceanBookingResource { oceanBooking:
|
||||
export interface OceanBookingStatusEvent { id: string; fromStatus: OceanBookingStatus | null; toStatus: OceanBookingStatus; action: string; reason?: string | null; operatorName: string | null; occurredAt: string }
|
||||
export interface OceanBookingPage { records: OceanBookingResource[]; page: number; size: number; total: number; pages: number }
|
||||
export interface OceanBookingQuery { page: number; size: number; keyword?: string; status?: OceanBookingStatus; customerId?: string }
|
||||
export interface OceanBookingLinkCandidate {
|
||||
oceanBookingId: string
|
||||
bookingNo: string
|
||||
requirementId: string
|
||||
containerType: string
|
||||
capacityCbm: number
|
||||
maxWeightKg: number
|
||||
requiredQuantity: number
|
||||
allocatedQuantity: number
|
||||
remainingQuantity: number
|
||||
customerName: string
|
||||
warehouseName: string
|
||||
channelName: string
|
||||
}
|
||||
export interface OceanBookingLinkCandidatePage { records: OceanBookingLinkCandidate[]; page: number; size: number; total: number; pages: number }
|
||||
export interface OceanBookingLinkCandidateQuery {
|
||||
warehouseId: string
|
||||
channelCode: string
|
||||
containerType: string
|
||||
capacityCbm: number
|
||||
maxWeightKg: number
|
||||
page: number
|
||||
size: number
|
||||
}
|
||||
export interface OceanBookingSaveRequest { expectedVersion: number; data: OceanBookingDraftData }
|
||||
export interface OceanBookingReleaseRequest { expectedVersion: number }
|
||||
|
||||
Reference in New Issue
Block a user