65 lines
2.2 KiB
TypeScript
65 lines
2.2 KiB
TypeScript
/** 店铺数据结果批量下载任务(任务 113):打包请求体与下载 job 状态;纯逻辑。 */
|
|
|
|
export type ShopDataDownloadStatus = 'idle' | 'running' | 'done' | 'failed'
|
|
|
|
export interface ShopDataDownloadJob {
|
|
status: ShopDataDownloadStatus
|
|
message?: string
|
|
fileCount?: number
|
|
errorCount?: number
|
|
}
|
|
|
|
/** 从 Content-Disposition 解析单文件下载名(rfc5987 优先),失败回默认名。 */
|
|
export function shopDataFilenameFromDisposition(disposition: string | undefined, fallback: string): string {
|
|
const text = typeof disposition === 'string' ? disposition : ''
|
|
const encoded = /filename\*=UTF-8''([^;]+)/i.exec(text)
|
|
if (encoded) {
|
|
try {
|
|
const decoded = decodeURIComponent(encoded[1])
|
|
if (decoded.trim()) return decoded.trim()
|
|
} catch {
|
|
// 回退普通 filename
|
|
}
|
|
}
|
|
const plain = /filename="?([^";]+)"?/i.exec(text)
|
|
return plain && plain[1] ? plain[1].trim() : fallback
|
|
}
|
|
|
|
function finitePositive(value: unknown): number | null {
|
|
const number = typeof value === 'number' && Number.isFinite(value)
|
|
? value
|
|
: typeof value === 'string' && /^\d+$/.test(value.trim())
|
|
? Number(value.trim())
|
|
: null
|
|
return number === null || number < 1 ? null : Math.floor(number)
|
|
}
|
|
|
|
/** result_id → POST /download-zip 请求体(过滤非法 id,按序去重)。 */
|
|
export function toShopDataZipRequest(resultIds: readonly (number | string)[]): { result_ids: number[] } {
|
|
const ids: number[] = []
|
|
const seen = new Set<number>()
|
|
for (const id of resultIds) {
|
|
const parsed = finitePositive(id)
|
|
if (parsed === null || seen.has(parsed)) continue
|
|
seen.add(parsed)
|
|
ids.push(parsed)
|
|
}
|
|
return { result_ids: ids }
|
|
}
|
|
|
|
export function createShopDataDownloadJob(): ShopDataDownloadJob {
|
|
return { status: 'idle' }
|
|
}
|
|
|
|
export function startShopDataDownload(): ShopDataDownloadJob {
|
|
return { status: 'running' }
|
|
}
|
|
|
|
export function succeedShopDataDownload(_job: ShopDataDownloadJob, fileCount: number, errorCount: number): ShopDataDownloadJob {
|
|
return { status: 'done', fileCount, errorCount }
|
|
}
|
|
|
|
export function failShopDataDownload(_job: ShopDataDownloadJob, message: string): ShopDataDownloadJob {
|
|
return { status: 'failed', message }
|
|
}
|