fix(phase4): harden development acceptance adapters

This commit is contained in:
2026-08-24 16:57:25 +08:00
parent 964480a617
commit 37ec787bc1
13 changed files with 83 additions and 44 deletions
@@ -34,27 +34,52 @@ const normalizeQuoteDetail = (value: QuoteDetailWire): OverseasWarehouseQuoteDet
const normalizeExchangeRate = (value: ExchangeRateWire): MonthlyExchangeRate => ({ ...value, usdToCnyAverage: numeric(value.usdToCnyAverage), recordVersion: numeric(value.recordVersion) })
const normalizeVehicle = (value: VehicleWire): VehicleDetail => ({ ...value, fuelConsumptionPer100Km: numeric(value.fuelConsumptionPer100Km), mileageKm: numeric(value.mileageKm), recordVersion: numeric(value.recordVersion) })
async function csvError(blob: Blob, status: number) {
if (!blob.type.toLowerCase().includes('json')) return null
type DownloadPayload = Blob | ArrayBuffer | ArrayBufferView | string
async function downloadText(payload: DownloadPayload) {
if (typeof payload === 'string') return payload
if (payload instanceof Blob) return payload.text()
const bytes = payload instanceof ArrayBuffer
? new Uint8Array(payload)
: new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength)
return new TextDecoder().decode(bytes)
}
async function csvError(payload: DownloadPayload, status: number) {
const contentType = payload instanceof Blob ? payload.type.toLowerCase() : ''
const text = await downloadText(payload)
if (!contentType.includes('json') && !/^\s*[\[{]/.test(text)) return null
try {
const payload = JSON.parse(await blob.text()) as Partial<ApiEnvelope<unknown>>
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) throw new Error('invalid envelope')
return new ApiResponseError(payload.message || 'CSV 导出失败', payload.code || 'DOWNLOAD_FAILED', status,
payload.data, payload.requestId, payload.traceId)
const envelope = JSON.parse(text) as Partial<ApiEnvelope<unknown>>
if (!envelope || typeof envelope !== 'object' || Array.isArray(envelope)) throw new Error('invalid envelope')
return new ApiResponseError(envelope.message || 'CSV 导出失败', envelope.code || 'DOWNLOAD_FAILED', status,
envelope.data, envelope.requestId, envelope.traceId)
} catch (error) {
if (error instanceof ApiResponseError) return error
return new ApiResponseError('CSV 导出接口返回了无法识别的 JSON 响应', 'INVALID_DOWNLOAD_RESPONSE', status)
}
}
function csvBlob(payload: DownloadPayload) {
if (payload instanceof Blob) return payload
if (typeof payload === 'string') return new Blob([payload], { type: 'text/csv;charset=utf-8' })
const bytes = payload instanceof ArrayBuffer
? new Uint8Array(payload)
: new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength)
const copy = new Uint8Array(bytes.byteLength)
copy.set(bytes)
return new Blob([copy.buffer], { type: 'text/csv;charset=utf-8' })
}
async function download(path: string, params: object) {
try {
const blob = await overseasHttp.get<Blob, Blob>(path, { params, responseType: 'blob' })
const error = await csvError(blob, 200)
const payload = await overseasHttp.get<DownloadPayload, DownloadPayload>(path, { params, responseType: 'arraybuffer' })
const error = await csvError(payload, 200)
if (error) throw error
return blob
return csvBlob(payload)
} catch (error) {
if (axios.isAxiosError(error) && error.response?.data instanceof Blob) {
const responseError = await csvError(error.response.data, error.response.status)
const axiosError = axios.isAxiosError(error) ? error : null
const responsePayload = axiosError?.response?.data as DownloadPayload | undefined
if (responsePayload !== undefined) {
const responseError = await csvError(responsePayload, axiosError?.response?.status || 0)
if (responseError) throw responseError
}
throw error