This commit is contained in:
2026-08-04 09:05:19 +08:00
parent fa922ca96f
commit 815d4e8daf
21 changed files with 994 additions and 128 deletions
@@ -1,4 +1,5 @@
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
import axios from 'axios'
import { ApiResponseError, http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
export type ExportTaskStatus = 'PENDING' | 'RUNNING' | 'SUCCEEDED' | 'FAILED' | 'EXPIRED'
export type ExportTaskType = 'CUSTOMER_LIST' | string
@@ -49,6 +50,45 @@ export interface CreatePlatformExportTaskRequest {
}
const basePath = '/platform/v1/files/export-tasks'
const downloadTimeoutMs = 10 * 60 * 1000
const maxErrorEnvelopeBytes = 1024 * 1024
async function parseDownloadErrorBlob(blob: Blob, status?: number): Promise<ApiResponseError | null> {
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<ApiEnvelope<unknown>>
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<PlatformExportTask>, ApiEnvelope<PlatformExportTask>>(basePath, payload)
@@ -69,8 +109,30 @@ export async function listPlatformExportTasks(query: PlatformExportTaskQuery) {
}
}
export function downloadPlatformExportTask(taskId: string) {
return http.get<Blob, Blob>(`${basePath}/${encodeURIComponent(taskId)}/download`, { responseType: 'blob' })
export async function getPlatformExportTask(taskId: string) {
const response = await http.get<ApiEnvelope<PlatformExportTask>, ApiEnvelope<PlatformExportTask>>(
`${basePath}/${encodeURIComponent(taskId)}`,
)
return unwrapApiResponse(response)
}
export async function downloadPlatformExportTask(taskId: string) {
try {
const blob = await http.get<Blob, Blob>(`${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) {