优化
This commit is contained in:
@@ -4,21 +4,107 @@ const inboundBasePath = '/platform/v1/inbounds'
|
||||
|
||||
export interface OutboundGateItem { code: string; name: string; passed: boolean; actual?: string | null; requirement?: string | null }
|
||||
export interface OutboundDispatchAggregate {
|
||||
orderId: number | string | null
|
||||
orderId: string | null
|
||||
orderNo: string | null
|
||||
inboundId: number | string | null
|
||||
inboundId: string | null
|
||||
inboundCode: string | null
|
||||
containerBookingId: number | string | null
|
||||
containerBookingId: string | null
|
||||
containerNo: string | null
|
||||
oceanBookingId: number | string | null
|
||||
loadingPlanId: string | null
|
||||
loadingPlanStatus: string | null
|
||||
oceanBookingId: string | null
|
||||
oceanBookingStatus: string | null
|
||||
truckAppointmentId: number | string | null
|
||||
truckAppointmentId: string | null
|
||||
truckAppointmentStatus: string | null
|
||||
customsStatus?: string | null
|
||||
sailingStatus?: string | null
|
||||
gate: { passed: boolean; items: OutboundGateItem[] } | null
|
||||
}
|
||||
|
||||
export interface ContainerBooking {
|
||||
id: string
|
||||
containerNo: string | null
|
||||
cabinetNo: string | null
|
||||
warehouseId: string | null
|
||||
warehouseName: string | null
|
||||
destinationWarehouseCode: string | null
|
||||
destinationWarehouseName: string | null
|
||||
capacityCbm: number | null
|
||||
maxWeightKg: number | null
|
||||
version: number
|
||||
}
|
||||
|
||||
export interface SmartLoadingCandidate {
|
||||
accountId: string
|
||||
inboundId: string
|
||||
inboundDetailId: string
|
||||
orderId: string
|
||||
orderNo: string | null
|
||||
inboundCode: string | null
|
||||
warehouseId: string
|
||||
destinationWarehouseId: string | null
|
||||
destinationWarehouseCode: string | null
|
||||
destinationWarehouseName: string | null
|
||||
customerName: string | null
|
||||
goodsName: string | null
|
||||
availablePackageCount: number
|
||||
unitWeightKg: number
|
||||
unitVolumeCbm: number
|
||||
}
|
||||
|
||||
export interface SmartLoadingPlanItem {
|
||||
id: string
|
||||
loadingPlanId: string
|
||||
accountId: string
|
||||
customerName: string | null
|
||||
inboundId: string
|
||||
inboundDetailId: string
|
||||
orderId: string
|
||||
orderNo: string | null
|
||||
inboundCode: string | null
|
||||
destinationWarehouseCode: string | null
|
||||
destinationWarehouseName: string | null
|
||||
skuCode: string | null
|
||||
productNameZh: string | null
|
||||
packageCount: number
|
||||
grossWeightKg: number
|
||||
volumeCbm: number
|
||||
loadSequence: number | null
|
||||
version: number
|
||||
}
|
||||
|
||||
export interface SmartLoadingPlan {
|
||||
id: string
|
||||
planNo: string | null
|
||||
status: string
|
||||
bookingId: string
|
||||
containerNo: string | null
|
||||
voyageNo: string | null
|
||||
version: number
|
||||
items: SmartLoadingPlanItem[]
|
||||
}
|
||||
|
||||
export interface CreateSmartLoadingPlanRequest { bookingId: string; remark?: string }
|
||||
export interface AssignSmartLoadingItemRequest {
|
||||
inboundId: string
|
||||
inboundDetailId: string
|
||||
packageCount: number
|
||||
expectedVersion: number
|
||||
operationId: string
|
||||
}
|
||||
export interface RecommendSmartLoadingRequest { expectedVersion: number; operationId: string }
|
||||
export interface AdjustSmartLoadingItemRequest {
|
||||
packageCount: number
|
||||
expectedPlanVersion: number
|
||||
expectedItemVersion: number
|
||||
operationId: string
|
||||
}
|
||||
export interface DeleteSmartLoadingItemRequest {
|
||||
expectedPlanVersion: number
|
||||
expectedItemVersion: number
|
||||
operationId: string
|
||||
}
|
||||
|
||||
export type PreloadStatus = 'PENDING' | 'CONFIRMING' | 'CONFIRMED' | string
|
||||
/** 五类装柜取证。旧接口仍使用 PHOTO_STEP_* / SEAL 编码,页面统一映射为业务名称。 */
|
||||
export type EvidenceType = 'PHOTO_STEP_1' | 'PHOTO_STEP_2' | 'PHOTO_STEP_3' | 'PHOTO_STEP_4' | 'SEAL'
|
||||
@@ -32,9 +118,9 @@ export interface PreloadItem {
|
||||
}
|
||||
|
||||
export interface PreloadPlan {
|
||||
id: number | string
|
||||
inboundId: number | string
|
||||
tenantId: number | string
|
||||
id: string
|
||||
inboundId: string
|
||||
tenantId: string
|
||||
status: PreloadStatus
|
||||
version: number
|
||||
items: PreloadItem[]
|
||||
@@ -139,24 +225,214 @@ function idempotencyKey(operation: string, inboundId: string | number) {
|
||||
return `admin-${operation}-${inboundId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||||
}
|
||||
|
||||
function operationId(operation: string, resourceId: string | number) {
|
||||
return `admin-${operation}-${resourceId}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`
|
||||
}
|
||||
|
||||
function nullableId(value: number | string | null | undefined) {
|
||||
return value === null || value === undefined || value === '' ? null : String(value)
|
||||
}
|
||||
|
||||
function numberValue(value: number | string | null | undefined, fallback = 0) {
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : fallback
|
||||
}
|
||||
|
||||
function normalizeContainer(raw: Record<string, unknown>): ContainerBooking {
|
||||
return {
|
||||
id: String(raw.id),
|
||||
containerNo: raw.containerNo == null ? null : String(raw.containerNo),
|
||||
cabinetNo: raw.cabinetNo == null ? null : String(raw.cabinetNo),
|
||||
warehouseId: nullableId(raw.warehouseId as number | string | null),
|
||||
warehouseName: raw.warehouseName == null ? null : String(raw.warehouseName),
|
||||
destinationWarehouseCode: raw.destinationWarehouseCode == null ? null : String(raw.destinationWarehouseCode),
|
||||
destinationWarehouseName: raw.destinationWarehouseName == null ? null : String(raw.destinationWarehouseName),
|
||||
capacityCbm: raw.capacityCbm == null ? null : numberValue(raw.capacityCbm as number | string),
|
||||
maxWeightKg: raw.maxWeightKg == null ? null : numberValue(raw.maxWeightKg as number | string),
|
||||
version: numberValue(raw.version as number | string),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeCandidate(raw: Record<string, unknown>): SmartLoadingCandidate {
|
||||
return {
|
||||
accountId: String(raw.accountId), inboundId: String(raw.inboundId), inboundDetailId: String(raw.inboundDetailId),
|
||||
orderId: String(raw.orderId), orderNo: raw.orderNo == null ? null : String(raw.orderNo),
|
||||
inboundCode: raw.inboundCode == null ? null : String(raw.inboundCode), warehouseId: String(raw.warehouseId),
|
||||
destinationWarehouseId: nullableId(raw.destinationWarehouseId as number | string | null),
|
||||
destinationWarehouseCode: raw.destinationWarehouseCode == null ? null : String(raw.destinationWarehouseCode),
|
||||
destinationWarehouseName: raw.destinationWarehouseName == null ? null : String(raw.destinationWarehouseName),
|
||||
customerName: raw.customerName == null ? null : String(raw.customerName),
|
||||
goodsName: raw.goodsName == null ? null : String(raw.goodsName),
|
||||
availablePackageCount: numberValue(raw.availablePackageCount as number | string),
|
||||
unitWeightKg: numberValue(raw.unitWeightKg as number | string), unitVolumeCbm: numberValue(raw.unitVolumeCbm as number | string),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeSmartLoadingItem(raw: Record<string, unknown>): SmartLoadingPlanItem {
|
||||
return {
|
||||
id: String(raw.id), loadingPlanId: String(raw.loadingPlanId), accountId: String(raw.accountId),
|
||||
customerName: raw.customerName == null ? null : String(raw.customerName), inboundId: String(raw.inboundId),
|
||||
inboundDetailId: String(raw.inboundDetailId), orderId: String(raw.orderId),
|
||||
orderNo: raw.orderNo == null ? null : String(raw.orderNo), inboundCode: raw.inboundCode == null ? null : String(raw.inboundCode),
|
||||
destinationWarehouseCode: raw.destinationWarehouseCode == null ? null : String(raw.destinationWarehouseCode),
|
||||
destinationWarehouseName: raw.destinationWarehouseName == null ? null : String(raw.destinationWarehouseName),
|
||||
skuCode: raw.skuCode == null ? null : String(raw.skuCode), productNameZh: raw.productNameZh == null ? null : String(raw.productNameZh),
|
||||
packageCount: numberValue(raw.packageCount as number | string), grossWeightKg: numberValue(raw.grossWeightKg as number | string),
|
||||
volumeCbm: numberValue(raw.volumeCbm as number | string), loadSequence: raw.loadSequence == null ? null : numberValue(raw.loadSequence as number | string),
|
||||
version: numberValue(raw.version as number | string),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeSmartLoadingPlan(raw: Record<string, unknown>): SmartLoadingPlan {
|
||||
return {
|
||||
id: String(raw.id), planNo: raw.planNo == null ? null : String(raw.planNo), status: String(raw.status || ''),
|
||||
bookingId: String(raw.bookingId), containerNo: raw.containerNo == null ? null : String(raw.containerNo),
|
||||
voyageNo: raw.voyageNo == null ? null : String(raw.voyageNo), version: numberValue(raw.version as number | string),
|
||||
items: Array.isArray(raw.items) ? raw.items.map((item) => normalizeSmartLoadingItem(item as Record<string, unknown>)) : [],
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeOutboundDispatch(raw: Record<string, unknown>): OutboundDispatchAggregate {
|
||||
return {
|
||||
orderId: nullableId(raw.orderId as number | string | null),
|
||||
orderNo: raw.orderNo == null ? null : String(raw.orderNo),
|
||||
inboundId: nullableId(raw.inboundId as number | string | null),
|
||||
inboundCode: raw.inboundCode == null ? null : String(raw.inboundCode),
|
||||
containerBookingId: nullableId(raw.containerBookingId as number | string | null),
|
||||
containerNo: raw.containerNo == null ? null : String(raw.containerNo),
|
||||
loadingPlanId: nullableId(raw.loadingPlanId as number | string | null),
|
||||
loadingPlanStatus: raw.loadingPlanStatus == null ? null : String(raw.loadingPlanStatus),
|
||||
oceanBookingId: nullableId(raw.oceanBookingId as number | string | null),
|
||||
oceanBookingStatus: raw.oceanBookingStatus == null ? null : String(raw.oceanBookingStatus),
|
||||
truckAppointmentId: nullableId(raw.truckAppointmentId as number | string | null),
|
||||
truckAppointmentStatus: raw.truckAppointmentStatus == null ? null : String(raw.truckAppointmentStatus),
|
||||
customsStatus: raw.customsStatus == null ? null : String(raw.customsStatus),
|
||||
sailingStatus: raw.sailingStatus == null ? null : String(raw.sailingStatus),
|
||||
gate: raw.gate == null ? null : raw.gate as OutboundDispatchAggregate['gate'],
|
||||
}
|
||||
}
|
||||
|
||||
function normalizePreloadPlan(raw: Record<string, unknown>): PreloadPlan {
|
||||
return {
|
||||
id: String(raw.id), inboundId: String(raw.inboundId), tenantId: String(raw.tenantId),
|
||||
status: String(raw.status || ''), version: numberValue(raw.version as number | string),
|
||||
items: Array.isArray(raw.items) ? raw.items.map((item) => ({ ...(item as PreloadItem) })) : [],
|
||||
remark: raw.remark == null ? null : String(raw.remark),
|
||||
createdAt: raw.createdAt == null ? null : String(raw.createdAt),
|
||||
updatedAt: raw.updatedAt == null ? null : String(raw.updatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeSplitAction(raw: Record<string, unknown>): SplitActionResponse {
|
||||
return {
|
||||
mainStatus: String(raw.mainStatus || ''),
|
||||
preloadPlan: raw.preloadPlan == null ? null : normalizePreloadPlan(raw.preloadPlan as Record<string, unknown>),
|
||||
}
|
||||
}
|
||||
|
||||
const smartLoadingBasePath = '/platform/v1/first-leg/smart-loading'
|
||||
|
||||
export async function getContainerBooking(bookingId: string | number) {
|
||||
const response = await http.get<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`/platform/v1/containers/${bookingId}`,
|
||||
)
|
||||
return normalizeContainer(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function listSmartLoadingCandidates(warehouseId: string | number) {
|
||||
const response = await http.get<ApiEnvelope<Record<string, unknown>[]>, ApiEnvelope<Record<string, unknown>[]>>(
|
||||
`${smartLoadingBasePath}/candidates`, { params: { warehouseId: String(warehouseId) } },
|
||||
)
|
||||
return unwrapApiResponse(response).map(normalizeCandidate)
|
||||
}
|
||||
|
||||
export async function getSmartLoadingPlan(planId: string | number) {
|
||||
const response = await http.get<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${smartLoadingBasePath}/plans/${planId}`,
|
||||
)
|
||||
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function getSmartLoadingPlanByBooking(bookingId: string | number) {
|
||||
const response = await http.get<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${smartLoadingBasePath}/plans/by-booking/${bookingId}`,
|
||||
)
|
||||
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function createSmartLoadingPlan(bookingId: string | number, remark?: string) {
|
||||
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${smartLoadingBasePath}/plans`, { bookingId: String(bookingId), remark: remark || undefined },
|
||||
)
|
||||
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function assignSmartLoadingItem(planId: string | number, request: Omit<AssignSmartLoadingItemRequest, 'operationId'>) {
|
||||
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${smartLoadingBasePath}/plans/${planId}/assignments`,
|
||||
{ ...request, operationId: operationId('smart-loading-assign', planId) },
|
||||
)
|
||||
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function recommendSmartLoading(planId: string | number, expectedVersion: number) {
|
||||
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${smartLoadingBasePath}/plans/${planId}/recommend`,
|
||||
{ expectedVersion, operationId: operationId('smart-loading-recommend', planId) },
|
||||
)
|
||||
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function adjustSmartLoadingItem(
|
||||
planId: string | number,
|
||||
itemId: string | number,
|
||||
request: Omit<AdjustSmartLoadingItemRequest, 'operationId'>,
|
||||
) {
|
||||
const response = await http.patch<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${smartLoadingBasePath}/plans/${planId}/items/${itemId}`,
|
||||
{ ...request, operationId: operationId('smart-loading-adjust', itemId) },
|
||||
)
|
||||
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function deleteSmartLoadingItem(
|
||||
planId: string | number,
|
||||
itemId: string | number,
|
||||
request: Omit<DeleteSmartLoadingItemRequest, 'operationId'>,
|
||||
) {
|
||||
const response = await http.delete<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${smartLoadingBasePath}/plans/${planId}/items/${itemId}`,
|
||||
{ data: { ...request, operationId: operationId('smart-loading-delete', itemId) } },
|
||||
)
|
||||
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function pushSmartLoadingPreload(planId: string | number, expectedVersion: number) {
|
||||
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${smartLoadingBasePath}/plans/${planId}/push-preload`, { expectedVersion },
|
||||
{ headers: { 'Idempotency-Key': idempotencyKey('smart-loading-push', planId) } },
|
||||
)
|
||||
return normalizeSmartLoadingPlan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
function writeOptions(operation: string, inboundId: string | number, tenantId: string | number) {
|
||||
return { params: query(tenantId), headers: { 'Idempotency-Key': idempotencyKey(operation, inboundId) } }
|
||||
}
|
||||
|
||||
export async function getPreload(tenantId: string | number, inboundId: string | number) {
|
||||
const response = await http.get<ApiEnvelope<PreloadPlan>, ApiEnvelope<PreloadPlan>>(
|
||||
const response = await http.get<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${inboundBasePath}/${inboundId}/preload`, { params: query(tenantId) },
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
return normalizePreloadPlan(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function getOutboundDispatch(tenantId: string | number, orderId?: string | number, inboundId?: string | number) {
|
||||
const response = await http.get<ApiEnvelope<OutboundDispatchAggregate[]>, ApiEnvelope<OutboundDispatchAggregate[]>>(
|
||||
const response = await http.get<ApiEnvelope<Record<string, unknown>[]>, ApiEnvelope<Record<string, unknown>[]>>(
|
||||
'/platform/v1/first-leg/outbound-dispatches',
|
||||
{ params: { tenantId: String(tenantId), ...(orderId ? { orderId: String(orderId) } : {}), ...(inboundId ? { inboundId: String(inboundId) } : {}) } },
|
||||
)
|
||||
const result = unwrapApiResponse(response)
|
||||
return result?.[0] || null
|
||||
return result?.[0] ? normalizeOutboundDispatch(result[0]) : null
|
||||
}
|
||||
|
||||
export async function smartSplit(
|
||||
@@ -164,10 +440,10 @@ export async function smartSplit(
|
||||
inboundId: string | number,
|
||||
request: SmartSplitRequest,
|
||||
) {
|
||||
const response = await http.post<ApiEnvelope<SplitActionResponse>, ApiEnvelope<SplitActionResponse>>(
|
||||
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${inboundBasePath}/${inboundId}/split/smart`, request, writeOptions('smart-split', inboundId, tenantId),
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
return normalizeSplitAction(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function confirmingPreload(
|
||||
@@ -175,11 +451,11 @@ export async function confirmingPreload(
|
||||
inboundId: string | number,
|
||||
request: ConfirmPreloadRequest,
|
||||
) {
|
||||
const response = await http.post<ApiEnvelope<SplitActionResponse>, ApiEnvelope<SplitActionResponse>>(
|
||||
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${inboundBasePath}/${inboundId}/preload/confirming`, request,
|
||||
writeOptions('preload-confirming', inboundId, tenantId),
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
return normalizeSplitAction(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function confirmPreload(
|
||||
@@ -187,11 +463,11 @@ export async function confirmPreload(
|
||||
inboundId: string | number,
|
||||
request: ConfirmPreloadRequest,
|
||||
) {
|
||||
const response = await http.post<ApiEnvelope<SplitActionResponse>, ApiEnvelope<SplitActionResponse>>(
|
||||
const response = await http.post<ApiEnvelope<Record<string, unknown>>, ApiEnvelope<Record<string, unknown>>>(
|
||||
`${inboundBasePath}/${inboundId}/preload/confirm`, request,
|
||||
writeOptions('preload-confirm', inboundId, tenantId),
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
return normalizeSplitAction(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function getPicking(tenantId: string | number, inboundId: string | number) {
|
||||
|
||||
Reference in New Issue
Block a user