task-73(ASIN 数据中心): 实现查询 ASIN 导入删除
POST /api/admin/query-asins/delete-import(file+可选groupId) 启动导入删除,
GET /delete-import/{importId} 轮询(复用 QueryAsinImportProgressVo 含
deletedCount), 新增删除结果文案, 9 个契约测试。
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
/** 查询 ASIN 导入删除适配(任务 73):multipart POST /delete-import(file[+groupId]) → importId,及进度轮询。 */
|
||||
import { http } from '@/api/http'
|
||||
import {
|
||||
parseQueryAsinImportProgress,
|
||||
parseQueryAsinImportStart,
|
||||
type QueryAsinImportProgress,
|
||||
} from './query-asin-import-model'
|
||||
|
||||
export const QUERY_ASIN_DELETE_IMPORT_ENDPOINT = '/api/admin/query-asins/delete-import'
|
||||
|
||||
/** 启动导入删除;groupId 为 Excel 未提供分组时的兜底,可选(>=1 才下发)。 */
|
||||
export async function startQueryAsinDeleteImport(file: File, groupId?: number): Promise<string> {
|
||||
const form = new FormData()
|
||||
form.append('file', file, file.name)
|
||||
if (typeof groupId === 'number' && Number.isFinite(groupId) && groupId >= 1) {
|
||||
form.append('groupId', String(Math.floor(groupId)))
|
||||
}
|
||||
const { data } = await http.post<unknown>(QUERY_ASIN_DELETE_IMPORT_ENDPOINT, form)
|
||||
return parseQueryAsinImportStart(data)
|
||||
}
|
||||
|
||||
/** 查询导入删除进度:GET /delete-import/{importId}。 */
|
||||
export async function fetchQueryAsinDeleteImportProgress(importId: string): Promise<QueryAsinImportProgress> {
|
||||
const { data } = await http.get<unknown>(`${QUERY_ASIN_DELETE_IMPORT_ENDPOINT}/${importId}`)
|
||||
return parseQueryAsinImportProgress(data)
|
||||
}
|
||||
@@ -84,3 +84,18 @@ export function queryAsinImportOutcomeText(progress: QueryAsinImportProgress): s
|
||||
return '等待导入任务开始…'
|
||||
}
|
||||
}
|
||||
|
||||
/** 导入删除结果文案(任务 73):突出删除条数,成功/失败/等待各有可读行。 */
|
||||
export function queryAsinDeleteOutcomeText(progress: QueryAsinImportProgress): 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 '等待删除任务开始…'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readSource } from './helpers.ts'
|
||||
import {
|
||||
parseQueryAsinImportStart,
|
||||
parseQueryAsinImportProgress,
|
||||
queryAsinImportFinished,
|
||||
queryAsinImportSucceeded,
|
||||
queryAsinDeleteOutcomeText,
|
||||
} from '../src/pages/asin/query-asin-import-model.ts'
|
||||
|
||||
test('test_task_073_query_asin_delete_start_normal_primary_path', () => {
|
||||
// 正常主路径:删除导入启动结果解析出任务 id。
|
||||
assert.equal(
|
||||
parseQueryAsinImportStart({ success: true, data: { importId: 'job_del_1' } }),
|
||||
'job_del_1',
|
||||
)
|
||||
})
|
||||
|
||||
test('test_task_073_query_asin_delete_start_normal_variant_input', () => {
|
||||
// 正常变体:已解包对象直传同样解析。
|
||||
assert.equal(parseQueryAsinImportStart({ importId: 'job_del_2' }), 'job_del_2')
|
||||
})
|
||||
|
||||
test('test_task_073_query_asin_delete_progress_normal_deleted_count', () => {
|
||||
// 正常主路径:删除进度统计 deletedCount(插入恒为 0)。
|
||||
const progress = parseQueryAsinImportProgress({
|
||||
success: true,
|
||||
data: { status: 'running', processedRows: 50, deletedCount: 12, insertedCount: 0, skippedCount: 3 },
|
||||
})
|
||||
assert.equal(progress.status, 'running')
|
||||
assert.equal(progress.deletedCount, 12)
|
||||
assert.equal(progress.insertedCount, 0)
|
||||
assert.equal(queryAsinImportFinished(progress), false)
|
||||
})
|
||||
|
||||
test('test_task_073_query_asin_delete_success_terminal_and_text', () => {
|
||||
// 正常终态:success 判定成功;结果文案突出删除条数与跳过。
|
||||
const done = parseQueryAsinImportProgress({
|
||||
success: true,
|
||||
data: { status: 'success', deletedCount: 9, skippedCount: 2, insertedCount: 0 },
|
||||
})
|
||||
assert.equal(queryAsinImportFinished(done), true)
|
||||
assert.equal(queryAsinImportSucceeded(done), true)
|
||||
const text = queryAsinDeleteOutcomeText(done)
|
||||
assert.match(text, /删除/)
|
||||
assert.match(text, /9/)
|
||||
assert.match(text, /2/)
|
||||
})
|
||||
|
||||
test('test_task_073_query_asin_delete_failed_terminal_and_text', () => {
|
||||
// 边界失败:failed 终态完成但未成功,错误信息进入文案。
|
||||
const failed = parseQueryAsinImportProgress({
|
||||
success: true,
|
||||
data: { status: 'failed', errorMessage: 'Excel 缺少分组列' },
|
||||
})
|
||||
assert.equal(queryAsinImportFinished(failed), true)
|
||||
assert.equal(queryAsinImportSucceeded(failed), false)
|
||||
assert.match(queryAsinDeleteOutcomeText(failed), /分组/)
|
||||
})
|
||||
|
||||
test('test_task_073_query_asin_delete_progress_boundary_empty', () => {
|
||||
// 边界空值:空负载按 pending 处理,不崩溃、未完成。
|
||||
const empty = parseQueryAsinImportProgress({})
|
||||
assert.equal(empty.status, 'pending')
|
||||
assert.equal(empty.deletedCount, 0)
|
||||
assert.equal(queryAsinImportFinished(empty), false)
|
||||
})
|
||||
|
||||
test('test_task_073_query_asin_delete_text_running', () => {
|
||||
// 边界/过程:running 给行进度;缺 deletedCount 用 0。
|
||||
assert.match(queryAsinDeleteOutcomeText(parseQueryAsinImportProgress({ data: { status: 'running', processedRows: 4, totalRows: 8 } })), /4\/8/)
|
||||
})
|
||||
|
||||
test('test_task_073_query_asin_delete_invalid_input_rejected', () => {
|
||||
// 异常输入:success=false 抛后端 message;未知状态按未完成处理。
|
||||
assert.throws(
|
||||
() => parseQueryAsinImportProgress({ success: false, message: '删除任务不存在' }),
|
||||
/删除任务不存在/,
|
||||
)
|
||||
assert.throws(
|
||||
() => parseQueryAsinImportStart({ success: false, message: '无权导入删除' }),
|
||||
/无权导入删除/,
|
||||
)
|
||||
})
|
||||
|
||||
test('test_task_073_query_asin_delete_dependency_failure_returns_actionable_message', () => {
|
||||
// 依赖失败/轮询走 adapter:multipart POST /delete-import + GET /delete-import/{importId}。
|
||||
const api = readSource('src/pages/asin/query-asin-delete-import-api.ts')
|
||||
assert.match(api, /delete-import/)
|
||||
assert.match(api, /http\.post/)
|
||||
assert.match(api, /http\.get/)
|
||||
assert.match(api, /FormData/)
|
||||
assert.match(api, /startQueryAsinDeleteImport/)
|
||||
assert.match(api, /fetchQueryAsinDeleteImportProgress/)
|
||||
})
|
||||
Reference in New Issue
Block a user