import axios from 'axios' import { ApiResponseError, http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http' export type ExportTaskStatus = 'PENDING' | 'RUNNING' | 'SUCCEEDED' | 'FAILED' | 'EXPIRED' export type KnownExportTaskType = | 'LAST_MILE_ORDER_LIST' | 'OVERSEAS_INVENTORY_BALANCE' | 'OVERSEAS_INVENTORY_LEDGER' | 'OVERSEAS_QUOTE_LIST' | 'MONTHLY_EXCHANGE_RATE_LIST' | 'VEHICLE_LIST' | 'FLEET_QUOTE_LIST' | 'TRIP_OPERATIONS_LIST' | 'SELFOP_INVENTORY_TURNOVER' | 'CUSTOMER_LIST' | 'DOMESTIC_WAREHOUSE_ORDER_LIST' | 'FINANCE_CUSTOMER_ACCOUNT_LIST' | 'FINANCE_CUSTOMER_ACCOUNT_LEDGER' | 'FINANCE_FEE_SOURCE_LIST' | 'FINANCE_RECONCILIATION_STATEMENT_LIST' | 'FINANCE_RECEIVABLE_SETTLEMENT_LIST' | 'FINANCE_RECEIVABLE_SETTLEMENT_DETAIL' | 'FINANCE_PAYABLE_SETTLEMENT_LIST' | 'FINANCE_PAYABLE_SETTLEMENT_DETAIL' export type ExportTaskType = KnownExportTaskType | (string & {}) export interface PlatformExportTask { id: string taskNo: string exportType: ExportTaskType format: 'XLSX' | string taskName: string status: ExportTaskStatus totalRows: number | null processedRows: number fileName: string | null contentType: string | null sizeBytes: number | null failureMessage: string | null attemptCount: number maxAttempts: number nextRetryAt: string | null requestedBy: string requestedUsername: string | null requestedAt: string startedAt: string | null completedAt: string | null expiresAt: string | null } export interface PlatformExportTaskPage { records: PlatformExportTask[] page: number size: number total: number pages: number } export interface PlatformExportTaskQuery { page: number size: number exportType?: string status?: ExportTaskStatus requestedFrom?: string requestedTo?: string } export interface CreatePlatformExportTaskRequest { exportType: ExportTaskType format: 'XLSX' taskName: string parameters: Record } const basePath = '/platform/v1/files/export-tasks' const downloadTimeoutMs = 10 * 60 * 1000 const maxErrorEnvelopeBytes = 1024 * 1024 async function parseDownloadErrorBlob(blob: Blob, status?: number): Promise { if (!blob.type.toLowerCase().includes('json')) return null if (blob.size > maxErrorEnvelopeBytes) { return new ApiResponseError( '下载接口返回了超出限制的 JSON 响应', 'INVALID_DOWNLOAD_RESPONSE', status, ) } try { const payload = JSON.parse(await blob.text()) as Partial> if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { return new ApiResponseError( '下载接口返回了无法识别的 JSON 响应', 'INVALID_DOWNLOAD_RESPONSE', status, ) } return new ApiResponseError( payload.code === '200' ? '下载接口未返回文件内容' : payload.message || '文件下载失败', payload.code === '200' ? 'INVALID_DOWNLOAD_RESPONSE' : payload.code || (status ? `HTTP_${status}` : 'DOWNLOAD_FAILED'), status, payload.data, payload.requestId, payload.traceId, ) } catch { return new ApiResponseError( '下载接口返回了无法识别的 JSON 响应', 'INVALID_DOWNLOAD_RESPONSE', status, ) } } export async function createPlatformExportTask(payload: CreatePlatformExportTaskRequest) { const response = await http.post, ApiEnvelope>(basePath, payload) return unwrapApiResponse(response) } export async function listPlatformExportTasks(query: PlatformExportTaskQuery) { const response = await http.get, ApiEnvelope>(basePath, { params: query, }) const page = unwrapApiResponse(response) return { ...page, page: Number(page.page), size: Number(page.size), total: Number(page.total), pages: Number(page.pages), } } export async function getPlatformExportTask(taskId: string) { const response = await http.get, ApiEnvelope>( `${basePath}/${encodeURIComponent(taskId)}`, ) return unwrapApiResponse(response) } export async function downloadPlatformExportTask(taskId: string) { try { const blob = await http.get(`${basePath}/${encodeURIComponent(taskId)}/download`, { responseType: 'blob', // 导出文件上限 50 MB,不能继承普通接口的 15 秒超时,也不能无限等待半断开的连接。 timeout: downloadTimeoutMs, }) const responseError = await parseDownloadErrorBlob(blob, 200) if (responseError) throw responseError return blob } catch (error) { if (axios.isAxiosError(error) && error.response?.data instanceof Blob) { const responseError = await parseDownloadErrorBlob(error.response.data, error.response.status) if (responseError) throw responseError } throw error } } export async function retryPlatformExportTask(taskId: string) { const response = await http.post, ApiEnvelope>( `${basePath}/${encodeURIComponent(taskId)}/retry`, {}, ) return unwrapApiResponse(response) }