This commit is contained in:
2026-10-19 10:23:38 +08:00
parent 4bb21641d9
commit 8ae1e5d0d4
18 changed files with 575 additions and 28 deletions
@@ -0,0 +1,51 @@
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
import { normalizeTripDetail } from './trips'
import type { TripDetail } from '../tripTypes'
export interface PickupSource { customerId: string; transferNo: string }
export interface PickupCandidate extends PickupSource { lastlegOrderNo: string; warehouseId: string; warehouseCode?: string | null; warehouseName?: string | null; pickupAddress: string; contactName: string; contactPhone: string; packageCount: number; expectedArrivalAt: string | null }
export interface PickupEvidence { fileId: string; fileName: string; contentType: string; sizeBytes: number }
export interface PickupTask extends Omit<PickupCandidate, 'expectedArrivalAt'> {
status: string; tripNo: string | null; vehiclePlate: string | null; driverName: string | null; driverPhone: string | null
plannedDepartureAt: string | null; actualPackages: number | null; pickedAt: string | null; arrivedAt: string | null; remark: string | null; evidence: PickupEvidence[]
}
export interface PickupPlan { sources: PickupSource[]; plannedDepartureAt: string; expectedDriverEndAt: string; remark?: string; idempotencyKey: string }
export interface PickupCommand { recordVersion: number; idempotencyKey: string; reason: string }
export interface PickupConfirmation { recordVersion: number; idempotencyKey: string; actualPackages: number; evidenceFileIds: string[]; remark?: string }
export const pickupStatusLabels: Record<string, string> = { PENDING_REVIEW: '待审核', WAITING: '待安排取货', ASSIGNED: '已安排取货', PICKED: '已取货', ARRIVED: '已送仓' }
const base = '/platform/v1/last-leg/fleet/pickups'
const tripPath = (tripNo: string) => `${base}/${encodeURIComponent(tripNo)}`
const taskPath = (tripNo: string, row: PickupSource) => `${tripPath(tripNo)}/tasks/${encodeURIComponent(row.customerId)}/${encodeURIComponent(row.transferNo)}`
type Page = { records: PickupCandidate[]; total: string | number }
type WireDetail = Parameters<typeof normalizeTripDetail>[0]
/** 待取货列表始终以服务端资格和总数为准。 */
export async function listPickupCandidates(page: number, size: number, keyword?: string) {
const value = unwrapApiResponse(await http.get<ApiEnvelope<Page>, ApiEnvelope<Page>>(base, { params: { page, size, keyword } }))
return { records: value.records, total: Number(value.total) }
}
/** 安排成功返回原有车次详情。 */
export async function createPickupTrip(body: PickupPlan): Promise<TripDetail> {
return normalizeTripDetail(unwrapApiResponse(await http.post<ApiEnvelope<WireDetail>, ApiEnvelope<WireDetail>>(base, body)))
}
/** 车次成员一次读取,避免逐行加载。 */
export async function getPickupTasks(tripNo: string): Promise<PickupTask[]> {
return unwrapApiResponse(await http.get<ApiEnvelope<PickupTask[]>, ApiEnvelope<PickupTask[]>>(`${tripPath(tripNo)}/tasks`))
}
/** 运输发车与结案沿取货专用规则执行。 */
export async function commandPickupTrip(tripNo: string, action: 'depart' | 'complete', body: PickupCommand): Promise<TripDetail> {
return normalizeTripDetail(unwrapApiResponse(await http.post<ApiEnvelope<WireDetail>, ApiEnvelope<WireDetail>>(`${tripPath(tripNo)}/${action}`, body)))
}
/** 取货和送仓逐票确认,保留请求键以支持未知结果重试。 */
export async function commandPickupTask(tripNo: string, row: PickupSource, action: 'picked' | 'arrive', body: PickupCommand | PickupConfirmation): Promise<PickupTask[]> {
return unwrapApiResponse(await http.post<ApiEnvelope<PickupTask[]>, ApiEnvelope<PickupTask[]>>(`${taskPath(tripNo, row)}/${action}`, body))
}
/** 文件上传绑定本票与当前车次。 */
export async function uploadPickupEvidence(tripNo: string, row: PickupSource, file: File): Promise<PickupEvidence> {
const body = new FormData(); body.append('file', file)
return unwrapApiResponse(await http.post<ApiEnvelope<PickupEvidence>, ApiEnvelope<PickupEvidence>>(`${taskPath(tripNo, row)}/files`, body))
}
/** 本票凭证通过授权入口读取,永不使用公共文件直链。 */
export async function downloadPickupEvidence(row: PickupSource, fileId: string): Promise<Blob> {
return http.get<Blob, Blob>(`/platform/v1/last-leg/warehouse-transfers/${encodeURIComponent(row.transferNo)}/pickup-evidence/${encodeURIComponent(fileId)}`, { params: { customerId: row.customerId }, responseType: 'blob' })
}