/** 店铺数据任务列表/批量下载适配(任务 110/113)。 */ import { http } from '@/api/http' import { parseShopDataTaskPage, type ShopDataTaskPageResult } from './shop-data-model.ts' import { toShopDataQuery, type ShopDataFilter } from './shop-data-filter.ts' import { shopDataFilenameFromDisposition, toShopDataZipRequest } from './shop-data-download.ts' import { downloadZipHeaderCounts } from './image-video-download.ts' import { parseTaskPermissionPayload, toPermissionUserIdsPayload, type TaskPermissionItem } from './task-permission.ts' export const SHOP_DATA_CRAWL_TASKS_ENDPOINT = '/api/admin/shop-data-crawl-tasks' export const SHOP_DATA_TASK_PERMISSIONS_ENDPOINT = '/api/admin/shop-data-crawl-task-permissions' export const SHOP_DATA_SINGLE_ENDPOINT = '/api/admin/shop-data-crawl' export async function fetchShopDataTaskList( filter: ShopDataFilter, page: number, pageSize: number, ): Promise { const { data } = await http.get(SHOP_DATA_CRAWL_TASKS_ENDPOINT, { params: toShopDataQuery(filter, page, pageSize), }) return parseShopDataTaskPage(data) } export interface ShopDataZipDownloadResult { blob: Blob fileCount?: number errorCount?: number } /** 批量下载选中结果文件为 zip:POST /download-zip(blob);从响应头读成功/失败计数。 * onProgress: 可选下载进度回调(0-100,后端未回 Content-Length 时退化为不定进度)。 */ export async function requestShopDataZipDownload( resultIds: readonly (number | string)[], onProgress?: (percent: number) => void, ): Promise { const { data, headers } = await http.post(`${SHOP_DATA_CRAWL_TASKS_ENDPOINT}/download-zip`, toShopDataZipRequest(resultIds), { responseType: 'blob', ...(onProgress ? { onDownloadProgress: toAxiosProgress(onProgress) } : {}), }) const counts = downloadZipHeaderCounts(headers as Record) return { blob: data, fileCount: counts.fileCount, errorCount: counts.errorCount } } export interface ShopDataResultDownload { blob: Blob filename: string } /** 下载单店最新采集文件(按每日累计档 id):GET /api/admin/shop-data-crawl-tasks/daily-files/{id}/download。 * onProgress: 可选下载进度回调(0-100)。 */ export async function fetchShopDataResultDownload( resultId: number | string, onProgress?: (percent: number) => void, ): Promise { const { data, headers } = await http.get(`${SHOP_DATA_CRAWL_TASKS_ENDPOINT}/daily-files/${resultId}/download`, { responseType: 'blob', ...(onProgress ? { onDownloadProgress: toAxiosProgress(onProgress) } : {}), }) const disposition = (headers as Record | undefined)?.['content-disposition'] return { blob: data, filename: shopDataFilenameFromDisposition(disposition, `shop-data-task-${resultId}.xlsx`) } } /** 将 axios 进度事件转为 0-100 百分比;响应未回 Content-Length 时报 -1,前端显示不定进度。 */ function toAxiosProgress(onProgress: (percent: number) => void) { return (event: { loaded: number; total?: number }) => { if (event.total) { onProgress(Math.min(100, Math.round((event.loaded / event.total) * 100))) } else { onProgress(-1) } } } /** 删除该店这条店铺数据记录(按每日累计档 id,管理端显式操作):DELETE .../daily-files/{id}。 */ export async function deleteShopDataResultHistory(resultId: number | string): Promise { await http.delete(`${SHOP_DATA_CRAWL_TASKS_ENDPOINT}/daily-files/${resultId}`) } /** 加载店铺数据任务数据范围授权用户:GET /api/admin/shop-data-crawl-task-permissions。 */ export async function fetchShopDataTaskPermissionUsers(): Promise { const { data } = await http.get(SHOP_DATA_TASK_PERMISSIONS_ENDPOINT) return parseTaskPermissionPayload(data) } /** 保存店铺数据任务数据范围授权:PUT /api/admin/shop-data-crawl-task-permissions { user_ids }。 */ export async function saveShopDataTaskPermissions(userIds: Iterable): Promise { await http.put(SHOP_DATA_TASK_PERMISSIONS_ENDPOINT, toPermissionUserIdsPayload(userIds)) }