task-119(任务与重复分析中心): 实现重复检查导入/导出状态

新增 duplicate-import-export.ts:导入结果摘要/导入参数归一与导出查询构建。

TDD: task-119.test.ts 8 用例先 RED 后 GREEN。
This commit is contained in:
2026-09-05 17:27:50 +08:00
parent 9b21e1f22e
commit 1acb93ff1c
2 changed files with 141 additions and 0 deletions
@@ -0,0 +1,71 @@
/** 重复检查导入/导出(任务 119):导入结果摘要、导入参数与导出查询构建;纯逻辑。
* 导出走 GET /duplicate-check-export 直接下载 CSV,导入 POST /duplicate-check-import(multipart)。 */
import { unwrap } from '../../api/envelope.ts'
import type { DuplicateFilter } from './duplicate-filter.ts'
/** 导出端点路径(供下载 URL 构造)。 */
export const DUPLICATE_EXPORT_PATH = '/duplicate-check-export'
function count(value: unknown): number {
return typeof value === 'number' && Number.isFinite(value) ? Math.floor(value) : 0
}
export type DuplicateImportMode = 'add' | 'delete'
/** 导入模式归一:delete/del/remove → delete,其余按 add。 */
export function normalizeImportMode(mode: unknown): DuplicateImportMode {
const text = typeof mode === 'string' ? mode.trim().toLowerCase() : ''
return text === 'delete' || text === 'del' || text === 'remove' ? 'delete' : 'add'
}
export interface DuplicateImportResult {
totalRows: number
asinCount: number
mode: DuplicateImportMode
}
/** 导入结果摘要解析(data: {total_rows, asin_count, ...})。 */
export function parseDuplicateImportResult(payload: unknown): DuplicateImportResult {
const data = unwrap<unknown>(payload)
const record = data && typeof data === 'object' ? (data as Record<string, unknown>) : {}
return {
totalRows: count(record.total_rows ?? record.totalRows),
asinCount: count(record.asin_count ?? record.asinCount),
mode: normalizeImportMode(record.mode),
}
}
/** 导入查询参数(multipart 外的固定参数)。 */
export function toDuplicateImportQuery(input: {
mode?: unknown
shopName?: string
countryCodes?: readonly string[]
}): { mode: DuplicateImportMode; shop_name?: string; country_codes?: string } {
const query: { mode: DuplicateImportMode; shop_name?: string; country_codes?: string } = {
mode: normalizeImportMode(input.mode),
}
const shopName = typeof input.shopName === 'string' ? input.shopName.trim() : ''
if (shopName) query.shop_name = shopName
const countryCodes = Array.isArray(input.countryCodes)
? input.countryCodes.map((code) => String(code).trim()).filter(Boolean)
: []
if (countryCodes.length) query.country_codes = countryCodes.join(',')
return query
}
/** 导出查询参数(不含分页,直接下载 CSV)。 */
export function toDuplicateExportQuery(filter: DuplicateFilter): Record<string, string> {
const query: Record<string, string> = {}
const put = (key: string, value: string) => {
const text = value.trim()
if (text) query[key] = text
}
put('view', filter.view)
put('asin', filter.asin)
put('shop_name', filter.shopName)
put('country', filter.country)
put('site', filter.site)
put('date_from', filter.dateFrom)
put('date_to', filter.dateTo)
return query
}