Files
admin-web/src/modules/unified-orders/workflows/api.ts
T

290 lines
8.9 KiB
TypeScript
Raw Normal View History

2026-08-06 13:56:04 +08:00
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
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
orderNo: string | null
inboundId: number | string | null
inboundCode: string | null
containerBookingId: number | string | null
containerNo: string | null
oceanBookingId: number | string | null
oceanBookingStatus: string | null
truckAppointmentId: number | string | null
truckAppointmentStatus: string | null
customsStatus?: string | null
sailingStatus?: string | null
gate: { passed: boolean; items: OutboundGateItem[] } | null
}
2026-08-06 13:56:04 +08:00
export type PreloadStatus = 'PENDING' | 'CONFIRMING' | 'CONFIRMED' | string
/** 五类装柜取证。旧接口仍使用 PHOTO_STEP_* / SEAL 编码,页面统一映射为业务名称。 */
2026-08-06 13:56:04 +08:00
export type EvidenceType = 'PHOTO_STEP_1' | 'PHOTO_STEP_2' | 'PHOTO_STEP_3' | 'PHOTO_STEP_4' | 'SEAL'
export interface PreloadItem {
containerCode: string | null
cargoReference: string | null
packageCount: number | null
volumeCbm: number | null
remark: string | null
}
export interface PreloadPlan {
id: number | string
inboundId: number | string
tenantId: number | string
status: PreloadStatus
version: number
items: PreloadItem[]
remark: string | null
createdAt: string | null
updatedAt: string | null
}
export interface SplitActionResponse {
mainStatus: string
preloadPlan: PreloadPlan | null
}
export interface PickingTask {
id: number | string
inboundId: number | string
status: string
version: number
expectedCount: number | null
pickedCount: number | null
remark: string | null
}
export interface Evidence {
id: number | string
inboundId: number | string
tenantId: number | string
evidenceType: EvidenceType
fileId: number | string | null
fileName: string | null
sealNo: string | null
remark: string | null
version: number
status: 'ACTIVE' | 'VOIDED' | string
2026-08-06 13:56:04 +08:00
createdAt: string | null
updatedAt: string | null
voidReason?: string | null
voidedBy?: number | string | null
voidedByName?: string | null
voidedAt?: string | null
2026-08-06 13:56:04 +08:00
}
export interface EvidenceList {
inboundId: number | string
evidences: Evidence[]
}
export interface LoadingActionResponse {
mainStatus: string
evidences: EvidenceList | null
}
export interface PickingRequest {
expectedVersion: number
expectedCount: number
pickedCount: number
remark?: string
}
export interface SmartSplitRequest {
expectedVersion: number
items: PreloadItem[]
remark?: string
}
export interface ConfirmPreloadRequest {
expectedVersion: number
}
export interface UpsertEvidenceRequest {
expectedVersion: number
evidenceType: EvidenceType
fileId?: number | string
fileName?: string
sealNo?: string
remark?: string
}
export interface UploadEvidenceResult {
fileId: number | string
fileName: string | null
contentType?: string | null
sizeBytes?: number | null
}
export interface VoidEvidenceRequest {
expectedVersion: number
reason: string
}
2026-08-06 13:56:04 +08:00
export interface CompleteLoadingRequest {
expectedVersion: number
remark?: string
}
function query(tenantId: string | number) {
return { tenantId: String(tenantId) }
}
function idempotencyKey(operation: string, inboundId: string | number) {
return `admin-${operation}-${inboundId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
}
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>>(
`${inboundBasePath}/${inboundId}/preload`, { params: query(tenantId) },
)
return unwrapApiResponse(response)
}
export async function getOutboundDispatch(tenantId: string | number, orderId?: string | number, inboundId?: string | number) {
const response = await http.get<ApiEnvelope<OutboundDispatchAggregate[]>, ApiEnvelope<OutboundDispatchAggregate[]>>(
'/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
}
2026-08-06 13:56:04 +08:00
export async function smartSplit(
tenantId: string | number,
inboundId: string | number,
request: SmartSplitRequest,
) {
const response = await http.post<ApiEnvelope<SplitActionResponse>, ApiEnvelope<SplitActionResponse>>(
`${inboundBasePath}/${inboundId}/split/smart`, request, writeOptions('smart-split', inboundId, tenantId),
)
return unwrapApiResponse(response)
}
export async function confirmingPreload(
tenantId: string | number,
inboundId: string | number,
request: ConfirmPreloadRequest,
) {
const response = await http.post<ApiEnvelope<SplitActionResponse>, ApiEnvelope<SplitActionResponse>>(
`${inboundBasePath}/${inboundId}/preload/confirming`, request,
writeOptions('preload-confirming', inboundId, tenantId),
)
return unwrapApiResponse(response)
}
export async function confirmPreload(
tenantId: string | number,
inboundId: string | number,
request: ConfirmPreloadRequest,
) {
const response = await http.post<ApiEnvelope<SplitActionResponse>, ApiEnvelope<SplitActionResponse>>(
`${inboundBasePath}/${inboundId}/preload/confirm`, request,
writeOptions('preload-confirm', inboundId, tenantId),
)
return unwrapApiResponse(response)
}
export async function getPicking(tenantId: string | number, inboundId: string | number) {
const response = await http.get<ApiEnvelope<PickingTask>, ApiEnvelope<PickingTask>>(
`${inboundBasePath}/${inboundId}/loading/picking`, { params: query(tenantId) },
)
return unwrapApiResponse(response)
}
export async function startPicking(
tenantId: string | number,
inboundId: string | number,
request: PickingRequest,
) {
const response = await http.post<ApiEnvelope<PickingTask>, ApiEnvelope<PickingTask>>(
`${inboundBasePath}/${inboundId}/loading/picking/start`, request,
writeOptions('picking-start', inboundId, tenantId),
)
return unwrapApiResponse(response)
}
export async function completePicking(
tenantId: string | number,
inboundId: string | number,
request: PickingRequest,
) {
const response = await http.post<ApiEnvelope<PickingTask>, ApiEnvelope<PickingTask>>(
`${inboundBasePath}/${inboundId}/loading/picking/complete`, request,
writeOptions('picking-complete', inboundId, tenantId),
)
return unwrapApiResponse(response)
}
export async function getEvidence(tenantId: string | number, inboundId: string | number) {
const response = await http.get<ApiEnvelope<EvidenceList>, ApiEnvelope<EvidenceList>>(
`${inboundBasePath}/${inboundId}/loading/evidence`, { params: query(tenantId) },
)
return unwrapApiResponse(response)
}
export async function upsertEvidence(
tenantId: string | number,
inboundId: string | number,
request: UpsertEvidenceRequest,
) {
const response = await http.post<ApiEnvelope<LoadingActionResponse>, ApiEnvelope<LoadingActionResponse>>(
`${inboundBasePath}/${inboundId}/loading/evidence`, request,
writeOptions(`evidence-${request.evidenceType}`, inboundId, tenantId),
)
return unwrapApiResponse(response)
}
/** 真实 File 服务上传;返回的 fileId 才能提交证据,不接受本地文件名冒充。 */
export async function uploadEvidenceFile(
tenantId: string | number,
inboundId: string | number,
evidenceType: EvidenceType,
file: File,
) {
const body = new FormData()
body.append('evidenceType', evidenceType)
body.append('file', file)
const response = await http.post<ApiEnvelope<UploadEvidenceResult>, ApiEnvelope<UploadEvidenceResult>>(
`/platform/v1/files/loading-evidence/${tenantId}/${inboundId}`,
body,
{ headers: { 'Content-Type': 'multipart/form-data' } },
)
return unwrapApiResponse(response)
}
export async function voidEvidence(
tenantId: string | number,
inboundId: string | number,
evidenceId: string | number,
request: VoidEvidenceRequest,
) {
const response = await http.post<ApiEnvelope<LoadingActionResponse>, ApiEnvelope<LoadingActionResponse>>(
`${inboundBasePath}/${inboundId}/loading/evidence/${evidenceId}/void`,
request,
writeOptions('evidence-void', inboundId, tenantId),
)
return unwrapApiResponse(response)
}
2026-08-06 13:56:04 +08:00
export async function completeLoading(
tenantId: string | number,
inboundId: string | number,
request: CompleteLoadingRequest,
) {
const response = await http.post<ApiEnvelope<LoadingActionResponse>, ApiEnvelope<LoadingActionResponse>>(
`${inboundBasePath}/${inboundId}/loading/complete`, request,
writeOptions('loading-complete', inboundId, tenantId),
)
return unwrapApiResponse(response)
}