d9b1b980ca
POST /api/admin/skip-price-asins/import、/delete-import(file+group_id 必填) 启动 + GET 进度轮询, 复用新增通用导入进度模型(含 deletedCount), 8 个契约测试。
102 lines
3.9 KiB
TypeScript
102 lines
3.9 KiB
TypeScript
/** ASIN 中心导入/轮询通用模型(任务 76):启动结果、Excel 校验与导入进度归一(含 deletedCount),纯逻辑。 */
|
|
import { unwrap } from '../../api/envelope.ts'
|
|
|
|
export const EXCEL_IMPORT_ALLOWED_EXT = /\.(xlsx|xls)$/i
|
|
|
|
/** 是否为允许导入的 Excel 文件。 */
|
|
export function isAllowedExcelImportFile(fileName: unknown): boolean {
|
|
return typeof fileName === 'string' && EXCEL_IMPORT_ALLOWED_EXT.test(fileName.trim())
|
|
}
|
|
|
|
/** 解包导入启动结果并取回 importId;缺省抛可读错误。 */
|
|
export function parseImportStart(payload: unknown): string {
|
|
const data = unwrap<unknown>(payload)
|
|
const record = data && typeof data === 'object' ? (data as Record<string, unknown>) : null
|
|
const importId = typeof record?.importId === 'string' ? record.importId.trim() : ''
|
|
if (!importId) throw new Error('导入任务响应异常:缺少任务 ID')
|
|
return importId
|
|
}
|
|
|
|
export type ImportStatus = 'pending' | 'running' | 'success' | 'failed'
|
|
|
|
export interface ImportProgress {
|
|
status: ImportStatus
|
|
totalRows: number
|
|
processedRows: number
|
|
asinCount: number
|
|
insertedCount: number
|
|
deletedCount: number
|
|
skippedCount: number
|
|
errorMessage?: string
|
|
}
|
|
|
|
function intOrZero(value: unknown): number {
|
|
return typeof value === 'number' && Number.isFinite(value) ? Math.floor(value) : 0
|
|
}
|
|
|
|
/** 归一化导入进度负载;缺省按未开始(pending)处理,未知状态不改。 */
|
|
export function parseImportProgress(payload: unknown): ImportProgress {
|
|
const data = unwrap<unknown>(payload)
|
|
const record = data && typeof data === 'object' ? (data as Record<string, unknown>) : null
|
|
const rawStatus = typeof record?.status === 'string' ? record.status.trim().toLowerCase() : ''
|
|
const status: ImportStatus =
|
|
rawStatus === 'running' || rawStatus === 'success' || rawStatus === 'failed' || rawStatus === 'pending'
|
|
? rawStatus
|
|
: 'pending'
|
|
const progress: ImportProgress = {
|
|
status,
|
|
totalRows: intOrZero(record?.totalRows),
|
|
processedRows: intOrZero(record?.processedRows),
|
|
asinCount: intOrZero(record?.asinCount),
|
|
insertedCount: intOrZero(record?.insertedCount),
|
|
deletedCount: intOrZero(record?.deletedCount),
|
|
skippedCount: intOrZero(record?.skippedCount),
|
|
}
|
|
const errorMessage =
|
|
typeof record?.errorMessage === 'string' && record.errorMessage.trim()
|
|
? record.errorMessage.trim()
|
|
: undefined
|
|
if (errorMessage) progress.errorMessage = errorMessage
|
|
return progress
|
|
}
|
|
|
|
/** 是否到达终态(success/failed)。 */
|
|
export function importFinished(progress: Pick<ImportProgress, 'status'>): boolean {
|
|
return progress.status === 'success' || progress.status === 'failed'
|
|
}
|
|
|
|
/** 是否成功完成。 */
|
|
export function importSucceeded(progress: Pick<ImportProgress, 'status'>): boolean {
|
|
return progress.status === 'success'
|
|
}
|
|
|
|
/** 导入添加结果文案。 */
|
|
export function importOutcomeText(progress: ImportProgress): string {
|
|
switch (progress.status) {
|
|
case 'running':
|
|
return `导入处理中:已处理 ${progress.processedRows}/${progress.totalRows} 行`
|
|
case 'success':
|
|
return `导入完成:处理 ${progress.insertedCount} 条,跳过 ${progress.skippedCount} 条`
|
|
case 'failed':
|
|
return progress.errorMessage ? `导入失败:${progress.errorMessage}` : '导入失败,请重试'
|
|
case 'pending':
|
|
default:
|
|
return '等待导入任务开始…'
|
|
}
|
|
}
|
|
|
|
/** 导入删除结果文案。 */
|
|
export function deleteOutcomeText(progress: ImportProgress): string {
|
|
switch (progress.status) {
|
|
case 'running':
|
|
return `删除处理中:已处理 ${progress.processedRows}/${progress.totalRows} 行`
|
|
case 'success':
|
|
return `删除完成:删除 ${progress.deletedCount} 条,跳过 ${progress.skippedCount} 条`
|
|
case 'failed':
|
|
return progress.errorMessage ? `删除失败:${progress.errorMessage}` : '删除失败,请重试'
|
|
case 'pending':
|
|
default:
|
|
return '等待删除任务开始…'
|
|
}
|
|
}
|