import { del, get, http, post, put, type JavaApiResponse, unwrapJavaResponse, } from "@/shared/api/http"; import { getTaskProgressCacheTtlMs } from "@/shared/task-progress-config"; const JAVA_API_PREFIX = "/newApi/api"; type TaskProgressCacheEntry = { expiresAt: number; data: unknown; }; interface TaskProgressBatchOptions { force?: boolean; } const taskProgressResponseCache = new Map(); const taskProgressInflightRequests = new Map>(); function getCurrentUserId() { const raw = typeof window === "undefined" ? "" : window.localStorage.getItem("uid") || ""; const value = Number(raw); if (!Number.isFinite(value) || value <= 0) { throw new Error("未获取到用户ID"); } return value; } function normalizeTaskIds(taskIds: number[]) { return Array.from( new Set( (taskIds || []) .map((taskId) => Number(taskId)) .filter((taskId) => Number.isFinite(taskId) && taskId > 0), ), ).sort((left, right) => left - right); } function buildTaskProgressRequestKey(path: string, taskIds: number[]) { return `${path}::${taskIds.join(",")}`; } async function postTaskProgressBatch( path: string, taskIds: number[], options: TaskProgressBatchOptions = {}, ) { const normalizedTaskIds = normalizeTaskIds(taskIds); if (!normalizedTaskIds.length) { return { items: [], missingTaskIds: [] } as T; } const cacheKey = buildTaskProgressRequestKey(path, normalizedTaskIds); const now = Date.now(); const cached = taskProgressResponseCache.get(cacheKey); if (!options.force && cached && cached.expiresAt > now) { return cached.data as T; } const inflight = taskProgressInflightRequests.get(cacheKey); if (!options.force && inflight) { return (await inflight) as T; } const requestPromise = unwrapJavaResponse( post, { taskIds: number[] }>(path, { taskIds: normalizedTaskIds }), ) .then((data) => { taskProgressResponseCache.set(cacheKey, { data, expiresAt: Date.now() + getTaskProgressCacheTtlMs(), }); return data; }) .finally(() => { taskProgressInflightRequests.delete(cacheKey); }); taskProgressInflightRequests.set(cacheKey, requestPromise); return requestPromise; } export interface UploadedFileRef { fileKey: string; originalFilename?: string; relativePath?: string; } export interface UploadFileVo { fileKey: string; originalFilename: string; localPath: string; size: number; relativePath?: string; } export interface DedupeResultItem { resultId?: number; sourceFilename: string; outputFilename?: string; success: boolean; error?: string; downloadUrl?: string; } export interface DedupeRunVo { total: number; successCount: number; failedCount: number; items: DedupeResultItem[]; } export interface DedupeHistoryVo { items: DedupeResultItem[]; } export interface DedupeRunRequest { files: UploadedFileRef[]; selectedColumns: string[]; keepIntegerIds: boolean; keepUnderscoreIds: boolean; keepIntegerMainIdsWhenNoSubIds: boolean; archiveName?: string; user_id: number; } export interface SplitArchiveEntry { filename: string; rowCount?: number; } export interface SplitResultItem { resultId?: number; sourceFilename: string; outputFilename?: string; success: boolean; error?: string; rowCount?: number; downloadUrl?: string; entryCount?: number; entries?: SplitArchiveEntry[]; } export interface SplitRunVo { total: number; successCount: number; failedCount: number; items: SplitResultItem[]; } export interface SplitHistoryVo { items: SplitResultItem[]; } export interface SplitRunRequest { files: UploadedFileRef[]; selectedColumns: string[]; splitMode: "rows_per_file" | "parts"; rowsPerFile?: number; parts?: number; archiveName?: string; user_id: number; } export interface ConvertResultItem { resultId?: number; sourceFilename: string; outputFilename?: string; success: boolean; error?: string; downloadUrl?: string; } export interface ConvertRunVo { total: number; successCount: number; failedCount: number; items: ConvertResultItem[]; } export interface ConvertHistoryVo { items: ConvertResultItem[]; } export interface ConvertRunRequest { files: UploadedFileRef[]; templateId: string; archiveName?: string; user_id: number; } export interface DeleteBrandPreviewRow { rowIndex: number; country: string; asin: string; status: string; } export interface DeleteBrandCountryAsinItem { rowIndex: number; asin: string; status: string; } export interface DeleteBrandCountryGroup { country: string; asinCount: number; items: DeleteBrandCountryAsinItem[]; } export interface DeleteBrandCountryResultItem { asin: string; status?: string; } export interface DeleteBrandProcessedCountry { country: string; items: DeleteBrandCountryResultItem[]; } export interface DeleteBrandResultFile { fileKey?: string; sourceFilename: string; fileIndex?: number; fileTotal?: number; chunkIndex?: number; chunkTotal?: number; processedRows?: number; totalRows?: number; currentCountry?: string; currentAsin?: string; countries: DeleteBrandProcessedCountry[]; } export interface DeleteBrandSubmitResultRequest { submissionId?: string; files: DeleteBrandResultFile[]; } export interface DeleteBrandLineProgressInfo { file_index?: number; file_total?: number; file_name?: string; current_line?: number; total_lines?: number; current_country?: string; current_asin?: string; finished_files?: number; phase?: string; } export interface DeleteBrandLineProgress { has_progress: boolean; info?: DeleteBrandLineProgressInfo; } export interface DeleteBrandTaskItem { id: number; taskNo?: string; status?: string; sourceFileCount?: number; successFileCount?: number; failedFileCount?: number; errorMessage?: string; createdAt?: string; updatedAt?: string; finishedAt?: string; downloadUrl?: string; downloadFilename?: string; } export interface DeleteBrandTaskFileProgress { fileKey?: string; sourceFilename?: string; processedRows?: number; totalRows?: number; percent?: number; status?: "PENDING" | "RUNNING" | "COMPLETED" | "FAILED"; } export interface DeleteBrandTaskDetailVo { task: DeleteBrandTaskItem; line_progress: DeleteBrandLineProgress; items?: DeleteBrandResultItem[]; fileProgress?: DeleteBrandTaskFileProgress[]; } export interface DeleteBrandTaskBatchVo { items: DeleteBrandTaskDetailVo[]; missingTaskIds: number[]; } export type DeleteBrandMatchStatus = | "MATCHED" | "PENDING" | "CONFLICT" | "INDEX_STALE"; export interface DeleteBrandResultItem { resultId?: number; fileKey?: string; sourceFilename: string; shopName?: string; companyName?: string; matched?: boolean; matchStatus?: DeleteBrandMatchStatus; matchMessage?: string; shopId?: string; platform?: string; openStoreUrl?: string; outputFilename?: string; downloadUrl?: string; fileJobId?: number; fileStatus?: string; fileError?: string; fileReady?: boolean; taskId?: number; countryCount?: number; countries?: DeleteBrandCountryGroup[]; totalRows?: number; truncated?: boolean; previewRows?: DeleteBrandPreviewRow[]; success: boolean; error?: string; taskStatus?: string; } export interface DeleteBrandRunVo { total: number; successCount: number; failedCount: number; items: DeleteBrandResultItem[]; } export interface DeleteBrandHistoryVo { items: DeleteBrandResultItem[]; } export interface DeleteBrandRunRequest { files: UploadedFileRef[]; user_id: number; } export interface ConvertTemplateVo { id: string; templateCode: string; templateName: string; outputFilename: string; isDefault?: boolean; builtIn?: boolean; } export interface ConvertTemplateImportRequest { templateName: string; templateContent: string; } export interface ExcelInfoVo { headers: string[]; totalRows: number; } export function getExcelInfo(fileKey: string) { return unwrapJavaResponse( get>(`${JAVA_API_PREFIX}/files/excel-info`, { params: { fileKey }, }), ); } export function runDedupe( request: Omit | DedupeRunRequest, ) { return unwrapJavaResponse( post, DedupeRunRequest>( `${JAVA_API_PREFIX}/dedupe/run`, { ...request, user_id: getCurrentUserId(), }, ), ); } export function getDedupeHistory() { return unwrapJavaResponse( get>(`${JAVA_API_PREFIX}/dedupe/history`, { params: { user_id: getCurrentUserId() }, }), ); } export function deleteDedupeHistory(resultId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/dedupe/history/${resultId}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function getDedupeResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/dedupe/results/${resultId}/download`); } export function runSplit( request: Omit | SplitRunRequest, ) { return unwrapJavaResponse( post, SplitRunRequest>( `${JAVA_API_PREFIX}/split/run`, { ...request, user_id: getCurrentUserId(), }, ), ); } export function getSplitHistory() { return unwrapJavaResponse( get>(`${JAVA_API_PREFIX}/split/history`, { params: { user_id: getCurrentUserId() }, }), ); } export function deleteSplitHistory(resultId: number) { return unwrapJavaResponse( del>(`${JAVA_API_PREFIX}/split/history/${resultId}`, { params: { user_id: getCurrentUserId() }, }), ); } export function getSplitResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/split/results/${resultId}/download`); } export function getConvertTemplates() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/convert/templates`, ), ); } export function importConvertTemplate(request: ConvertTemplateImportRequest) { return unwrapJavaResponse( post, ConvertTemplateImportRequest>( `${JAVA_API_PREFIX}/convert/templates/import`, request, ), ); } export function setDefaultConvertTemplate(templateCode: string) { return unwrapJavaResponse( post>( `${JAVA_API_PREFIX}/convert/templates/${templateCode}/default`, ), ); } export function deleteConvertTemplate(templateCode: string) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/convert/templates/${templateCode}`, ), ); } export function runConvert( request: Omit | ConvertRunRequest, ) { return unwrapJavaResponse( post, ConvertRunRequest>( `${JAVA_API_PREFIX}/convert/run`, { ...request, user_id: getCurrentUserId(), }, ), ); } export function getConvertHistory() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/convert/history`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function deleteConvertHistory(resultId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/convert/history/${resultId}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function getConvertResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/convert/results/${resultId}/download`); } export function runDeleteBrand( request: Omit | DeleteBrandRunRequest, ) { return unwrapJavaResponse( post, DeleteBrandRunRequest>( `${JAVA_API_PREFIX}/delete-brand/run`, { ...request, user_id: getCurrentUserId(), }, { timeout: 180000 }, ), ); } export function getDeleteBrandHistory() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/delete-brand/history`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function deleteDeleteBrandHistory(resultId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/delete-brand/history/${resultId}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function getDeleteBrandTaskDetail(taskId: number) { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/delete-brand/tasks/${taskId}`, ), ); } export function getDeleteBrandTaskDetails(taskIds: number[]) { return unwrapJavaResponse( post, { taskIds: number[] }>( `${JAVA_API_PREFIX}/delete-brand/tasks/batch`, { taskIds }, ), ); } export function getDeleteBrandTaskDownloadUrl(taskId: number) { return getJavaDownloadUrl(`/delete-brand/tasks/${taskId}/download`); } export function getDeleteBrandResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/delete-brand/results/${resultId}/download`); } export function submitDeleteBrandResult( taskId: number, request: DeleteBrandSubmitResultRequest, ) { return unwrapJavaResponse( post, DeleteBrandSubmitResultRequest>( `${JAVA_API_PREFIX}/delete-brand/tasks/${taskId}/result`, request, ), ); } /** 商品风险处理:备选店铺与匹配相关数据结构。 */ export interface ProductRiskCandidateVo { id: number; shop_name: string; created_at?: string; } export interface ProductRiskShopQueueItem { shopName: string; matched: boolean; shopId?: string; platform?: string; companyName?: string; openStoreUrl?: string; matchedUserId?: number; matchStatus?: string; matchMessage?: string; queryAsins?: QueryAsinCountryAsins[]; } export interface ProductRiskMatchShopsVo { items: ProductRiskShopQueueItem[]; } export function listProductRiskCandidates() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/product-risk-resolve/candidates`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function addProductRiskCandidate(shopName: string) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; shop_name: string } >(`${JAVA_API_PREFIX}/product-risk-resolve/candidates`, { user_id: getCurrentUserId(), shop_name: shopName, }), ); } export function deleteProductRiskCandidate(id: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/product-risk-resolve/candidates/${id}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export interface ProductRiskCountryPreferenceVo { country_codes: string[]; } export function getProductRiskCountryPreference() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/product-risk-resolve/country-preference`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function putProductRiskCountryPreference(countryCodes: string[]) { return unwrapJavaResponse( put< JavaApiResponse, { user_id: number; country_codes: string[] } >(`${JAVA_API_PREFIX}/product-risk-resolve/country-preference`, { user_id: getCurrentUserId(), country_codes: countryCodes, }), ); } export function matchProductRiskShops(shopNames: string[]) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; shop_names: string[] } >(`${JAVA_API_PREFIX}/product-risk-resolve/match-shops`, { user_id: getCurrentUserId(), shop_names: shopNames, }), ); } export interface ProductRiskDashboardVo { candidateCount: number; processedTaskCount: number; successTaskCount: number; failedTaskCount: number; } export interface ProductRiskHistoryItem { resultId?: number; taskId?: number; shopName?: string; shopId?: string; platform?: string; companyName?: string; matched?: boolean; matchStatus?: string; matchMessage?: string; taskStatus?: string; success?: boolean; error?: string; outputFilename?: string; downloadUrl?: string; fileJobId?: number; fileStatus?: string; fileError?: string; fileReady?: boolean; scheduledAt?: string; } export interface ProductRiskHistoryVo { items: ProductRiskHistoryItem[]; } export interface ProductRiskTaskSummary { id?: number; taskNo?: string; status?: string; errorMessage?: string; createdAt?: string; updatedAt?: string; finishedAt?: string; scheduledAt?: string; countryCodes?: string[]; currentStageIndex?: number; activeStageIndex?: number; scheduleStages?: ShopMatchTaskStage[]; } export interface ProductRiskTaskDetailVo { task?: ProductRiskTaskSummary; items?: ProductRiskHistoryItem[]; } export interface ProductRiskTaskBatchVo { items: ProductRiskTaskDetailVo[]; missingTaskIds?: number[]; } export interface ProductRiskPendingDeleteVo { removed: boolean; } export interface ProductRiskCreateTaskVo { taskId: number; items: ProductRiskHistoryItem[]; } export type ShopMatchCandidateVo = ProductRiskCandidateVo; export type ShopMatchCountryPreferenceVo = ProductRiskCountryPreferenceVo; export type ShopMatchShopQueueItem = ProductRiskShopQueueItem; export type ShopMatchDashboardVo = ProductRiskDashboardVo; export type ShopMatchHistoryItem = ProductRiskHistoryItem; export type ShopMatchHistoryVo = ProductRiskHistoryVo; export type ShopMatchTaskDetailVo = ProductRiskTaskDetailVo; export type ShopMatchTaskBatchVo = ProductRiskTaskBatchVo; export type ShopMatchCreateTaskVo = ProductRiskCreateTaskVo; export interface ShopMatchTaskStage { stageIndex?: number; scheduledAt?: string; status?: string; } export function getProductRiskDashboard() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/product-risk-resolve/dashboard`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function getProductRiskHistory() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/product-risk-resolve/history`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function deleteProductRiskHistory(resultId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/product-risk-resolve/history/${resultId}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function createProductRiskTask(items: ProductRiskShopQueueItem[]) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; items: ProductRiskShopQueueItem[] } >(`${JAVA_API_PREFIX}/product-risk-resolve/tasks`, { user_id: getCurrentUserId(), items, }), ); } export function deleteProductRiskTask(taskId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/product-risk-resolve/tasks/${taskId}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function deletePendingProductRiskShopResult(shopName: string) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/product-risk-resolve/pending-shop-result`, { params: { user_id: getCurrentUserId(), shop_name: shopName }, }, ), ); } export function getProductRiskTasksBatch(taskIds: number[]) { return unwrapJavaResponse( post, { taskIds: number[] }>( `${JAVA_API_PREFIX}/product-risk-resolve/tasks/batch`, { taskIds }, ), ); } export function getProductRiskResultDownloadUrl(resultId: number) { return getJavaDownloadUrl( `/product-risk-resolve/results/${resultId}/download`, ); } export function listShopMatchCandidates() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/shop-match/candidates`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function addShopMatchCandidate(shopName: string) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; shop_name: string } >(`${JAVA_API_PREFIX}/shop-match/candidates`, { user_id: getCurrentUserId(), shop_name: shopName, }), ); } export function deleteShopMatchCandidate(id: number) { return unwrapJavaResponse( del>(`${JAVA_API_PREFIX}/shop-match/candidates/${id}`, { params: { user_id: getCurrentUserId() }, }), ); } export function getShopMatchCountryPreference() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/shop-match/country-preference`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function putShopMatchCountryPreference(countryCodes: string[]) { return unwrapJavaResponse( put< JavaApiResponse, { user_id: number; country_codes: string[] } >(`${JAVA_API_PREFIX}/shop-match/country-preference`, { user_id: getCurrentUserId(), country_codes: countryCodes, }), ); } export function matchShopMatchShops(shopNames: string[]) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; shop_names: string[] } >(`${JAVA_API_PREFIX}/shop-match/match-shops`, { user_id: getCurrentUserId(), shop_names: shopNames, }), ); } export function getShopMatchDashboard() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/shop-match/dashboard`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function getShopMatchHistory() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/shop-match/history`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function deleteShopMatchHistory(resultId: number) { return unwrapJavaResponse( del>(`${JAVA_API_PREFIX}/shop-match/history/${resultId}`, { params: { user_id: getCurrentUserId() }, }), ); } export function createShopMatchTask( items: ShopMatchShopQueueItem[], countryCodes: string[], scheduleTimes?: string[], ) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; items: ShopMatchShopQueueItem[]; country_codes: string[]; schedule_times?: string[] } >(`${JAVA_API_PREFIX}/shop-match/tasks`, { user_id: getCurrentUserId(), items, country_codes: countryCodes, schedule_times: scheduleTimes, }), ); } export function deleteShopMatchTask(taskId: number) { return unwrapJavaResponse( del>(`${JAVA_API_PREFIX}/shop-match/tasks/${taskId}`, { params: { user_id: getCurrentUserId() }, }), ); } export function activateShopMatchTask(taskId: number, stageIndex: number) { return unwrapJavaResponse( post, undefined>( `${JAVA_API_PREFIX}/shop-match/tasks/${taskId}/activate?user_id=${encodeURIComponent(String(getCurrentUserId()))}&stage_index=${encodeURIComponent(String(stageIndex))}`, undefined, ), ); } export function completeShopMatchTaskStage(taskId: number, stageIndex: number) { return unwrapJavaResponse( post, { stage_index: number }>( `${JAVA_API_PREFIX}/shop-match/tasks/${taskId}/stage-finished?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, { stage_index: stageIndex }, ), ); } export function getShopMatchTasksBatch(taskIds: number[]) { return unwrapJavaResponse( post, { taskIds: number[] }>( `${JAVA_API_PREFIX}/shop-match/tasks/batch`, { taskIds }, ), ); } export function getShopMatchResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/shop-match/results/${resultId}/download`); } export type PatrolDeleteCandidateVo = ProductRiskCandidateVo; export type PatrolDeleteDashboardVo = ProductRiskDashboardVo; export type PatrolDeleteShopQueueItem = ProductRiskShopQueueItem; export interface PatrolDeleteConditionVo { id: number; conditionText: string; createdAt?: string; } export interface PatrolDeleteCountryMetricRow { status: string; quantity: string; deleteQuantity: string; processStatus: string; } export interface PatrolDeleteCountrySection { country: string; rows: PatrolDeleteCountryMetricRow[]; } export interface PatrolDeleteCartRatio { country: string; ratio: string; } export interface PatrolDeleteTaskItem { shopName?: string; matched?: boolean; shopId?: string; platform?: string; companyName?: string; matchStatus?: string; matchMessage?: string; countrySections: PatrolDeleteCountrySection[]; cartRatios: PatrolDeleteCartRatio[]; } export interface PatrolDeleteHistoryItem { resultId?: number; taskId?: number; shopName?: string; shopId?: string; platform?: string; companyName?: string; matched?: boolean; matchStatus?: string; matchMessage?: string; taskStatus?: string; success?: boolean; error?: string; createdAt?: string; finishedAt?: string; outputFilename?: string; downloadUrl?: string; fileJobId?: number; fileStatus?: string; fileError?: string; fileReady?: boolean; countrySections: PatrolDeleteCountrySection[]; cartRatios: PatrolDeleteCartRatio[]; } export interface PatrolDeleteHistoryVo { items: PatrolDeleteHistoryItem[]; } export interface PatrolDeleteTaskBatchVo { items: PatrolDeleteHistoryItem[]; missingTaskIds: number[]; } export interface PatrolDeleteCreateTaskVo { taskId: number; items: PatrolDeleteHistoryItem[]; } export function listPatrolDeleteCandidates() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/patrol-delete/candidates`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function addPatrolDeleteCandidate(shopName: string) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; shop_name: string } >(`${JAVA_API_PREFIX}/patrol-delete/candidates`, { user_id: getCurrentUserId(), shop_name: shopName, }), ); } export function deletePatrolDeleteCandidate(id: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/patrol-delete/candidates/${id}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function listPatrolDeleteConditions() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/patrol-delete/conditions`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function addPatrolDeleteCondition(conditionText: string) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; condition_text: string } >(`${JAVA_API_PREFIX}/patrol-delete/conditions`, { user_id: getCurrentUserId(), condition_text: conditionText, }), ); } export function deletePatrolDeleteCondition(id: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/patrol-delete/conditions/${id}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function matchPatrolDeleteShops(shopNames: string[]) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; shop_names: string[] } >(`${JAVA_API_PREFIX}/patrol-delete/match-shops`, { user_id: getCurrentUserId(), shop_names: shopNames, }), ); } export function getPatrolDeleteDashboard() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/patrol-delete/dashboard`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function getPatrolDeleteHistory() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/patrol-delete/history`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function getPatrolDeleteTaskProgressBatch(taskIds: number[]) { return postTaskProgressBatch( `${JAVA_API_PREFIX}/patrol-delete/tasks/progress/batch`, taskIds, ); } export function createPatrolDeleteTask(items: PatrolDeleteTaskItem[]) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; items: PatrolDeleteTaskItem[] } >(`${JAVA_API_PREFIX}/patrol-delete/tasks`, { user_id: getCurrentUserId(), items, }), ); } export function submitPatrolDeleteTaskResult( taskId: number, payload: { shops: Array<{ shopName: string; error?: string; countrySections: PatrolDeleteCountrySection[]; cartRatios: PatrolDeleteCartRatio[]; shopDone?: boolean; submissionId?: string; chunkIndex?: number; chunkTotal?: number; }>; }, ) { return unwrapJavaResponse( post< JavaApiResponse, { shops: Array<{ shopName: string; error?: string; countrySections: PatrolDeleteCountrySection[]; cartRatios: PatrolDeleteCartRatio[]; shopDone?: boolean; submissionId?: string; chunkIndex?: number; chunkTotal?: number; }>; } >(`${JAVA_API_PREFIX}/patrol-delete/tasks/${taskId}/result`, payload), ); } export function getPatrolDeleteResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/patrol-delete/results/${resultId}/download`); } export function deletePatrolDeleteTask(taskId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/patrol-delete/tasks/${taskId}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export function deletePatrolDeleteHistory(resultId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/patrol-delete/history/${resultId}`, { params: { user_id: getCurrentUserId() }, }, ), ); } export type QueryAsinCandidateVo = ProductRiskCandidateVo; export type QueryAsinDashboardVo = ProductRiskDashboardVo; export type QueryAsinShopQueueItem = ProductRiskShopQueueItem; export interface QueryAsinCountryAsins { country: string; asins: string[]; } export interface QueryAsinStatusItem { asin?: string; status?: string; } export interface QueryAsinCountryResult { country: string; items: QueryAsinStatusItem[]; } export interface QueryAsinTaskItem { shopName?: string; matched?: boolean; shopId?: string; platform?: string; companyName?: string; matchStatus?: string; matchMessage?: string; queryAsins: QueryAsinCountryAsins[]; } export interface QueryAsinHistoryItem { resultId?: number; taskId?: number; shopName?: string; shopId?: string; platform?: string; companyName?: string; matched?: boolean; matchStatus?: string; matchMessage?: string; taskStatus?: string; success?: boolean; error?: string; createdAt?: string; finishedAt?: string; outputFilename?: string; downloadUrl?: string; fileJobId?: number; fileStatus?: string; fileError?: string; fileReady?: boolean; queryAsins: QueryAsinCountryAsins[]; countryResults?: QueryAsinCountryResult[]; } export interface QueryAsinHistoryVo { items: QueryAsinHistoryItem[]; } export interface QueryAsinTaskBatchVo { items: QueryAsinHistoryItem[]; missingTaskIds: number[]; } export interface QueryAsinCreateTaskVo { taskId: number; items: QueryAsinHistoryItem[]; } export function listQueryAsinCandidates() { return unwrapJavaResponse( get>(`${JAVA_API_PREFIX}/query-asin/candidates`, { params: { user_id: getCurrentUserId() }, }), ); } export function addQueryAsinCandidate(shopName: string) { return unwrapJavaResponse( post, { user_id: number; shop_name: string }>( `${JAVA_API_PREFIX}/query-asin/candidates`, { user_id: getCurrentUserId(), shop_name: shopName }, ), ); } export function deleteQueryAsinCandidate(id: number) { return unwrapJavaResponse( del>(`${JAVA_API_PREFIX}/query-asin/candidates/${id}`, { params: { user_id: getCurrentUserId() }, }), ); } export function matchQueryAsinShops(shopNames: string[]) { return unwrapJavaResponse( post, { user_id: number; shop_names: string[] }>( `${JAVA_API_PREFIX}/query-asin/match-shops`, { user_id: getCurrentUserId(), shop_names: shopNames }, ), ); } export function getQueryAsinDashboard() { return unwrapJavaResponse( get>(`${JAVA_API_PREFIX}/query-asin/dashboard`, { params: { user_id: getCurrentUserId() }, }), ); } export function getQueryAsinHistory() { return unwrapJavaResponse( get>(`${JAVA_API_PREFIX}/query-asin/history`, { params: { user_id: getCurrentUserId() }, }), ); } export function getQueryAsinTaskProgressBatch(taskIds: number[]) { return postTaskProgressBatch( `${JAVA_API_PREFIX}/query-asin/tasks/progress/batch`, taskIds, ); } export function createQueryAsinTask(items: QueryAsinTaskItem[]) { return unwrapJavaResponse( post, { user_id: number; items: QueryAsinTaskItem[] }>( `${JAVA_API_PREFIX}/query-asin/tasks`, { user_id: getCurrentUserId(), items }, ), ); } export function submitQueryAsinTaskResult( taskId: number, payload: { shops: Array<{ shopName: string; error?: string; countryResults?: QueryAsinCountryResult[]; shopDone?: boolean; submissionId?: string; }>; }, ) { return unwrapJavaResponse( post, typeof payload>(`${JAVA_API_PREFIX}/query-asin/tasks/${taskId}/result`, payload), ); } export function getQueryAsinResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/query-asin/results/${resultId}/download`); } export function deleteQueryAsinTask(taskId: number) { return unwrapJavaResponse( del>(`${JAVA_API_PREFIX}/query-asin/tasks/${taskId}`, { params: { user_id: getCurrentUserId() }, }), ); } export function deleteQueryAsinHistory(resultId: number) { return unwrapJavaResponse( del>(`${JAVA_API_PREFIX}/query-asin/history/${resultId}`, { params: { user_id: getCurrentUserId() }, }), ); } // ========== 外观专利检测 ========== export interface AppearancePatentParsedRow { rowIndex: number; sourceFileKey?: string; sourceFilename?: string; sourceId: string; displayId: string; rowToken?: string; groupKey?: string; asin: string; country: string; url?: string; title?: string; } export interface AppearancePatentParsedGroup { sourceFileKey?: string; sourceFilename?: string; groupKey?: string; baseId?: string; displayId?: string; itemCount?: number; items: AppearancePatentParsedRow[]; } export interface AppearancePatentParseVo { taskId: number; sourceFilename?: string; sourceFileCount?: number; totalRows: number; acceptedRows: number; droppedRows: number; groupCount?: number; aiPrompt?: string; items: AppearancePatentParsedRow[]; groups?: AppearancePatentParsedGroup[]; } export interface AppearancePatentDashboardVo { pendingTaskCount: number; processedTaskCount: number; successTaskCount: number; failedTaskCount: number; } export interface AppearancePatentHistoryItem { resultId?: number; taskId?: number; sourceFilename?: string; resultFilename?: string; downloadUrl?: string; fileJobId?: number; fileStatus?: string; fileError?: string; fileReady?: boolean; fileProgressPercent?: number; fileProgressCurrent?: number; fileProgressTotal?: number; fileProgressMessage?: string; taskStatus?: string; success?: boolean; error?: string; rowCount?: number; createdAt?: string; } export interface AppearancePatentHistoryVo { items: AppearancePatentHistoryItem[]; } export interface AppearancePatentTaskSummary { id?: number; taskNo?: string; status?: string; errorMessage?: string; createdAt?: string; updatedAt?: string; finishedAt?: string; } export interface AppearancePatentTaskDetailVo { task?: AppearancePatentTaskSummary; items?: AppearancePatentHistoryItem[]; } export interface AppearancePatentTaskBatchVo { items: AppearancePatentTaskDetailVo[]; missingTaskIds?: number[]; } export function parseAppearancePatent(files: UploadedFileRef[], aiPrompt: string, apiKey?: string) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; files: UploadedFileRef[]; ai_prompt: string; api_key?: string } >(`${JAVA_API_PREFIX}/appearance-patent/parse`, { user_id: getCurrentUserId(), files, ai_prompt: aiPrompt, api_key: apiKey, }), ); } export function getAppearancePatentDashboard() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/appearance-patent/dashboard`, { params: { user_id: getCurrentUserId() } }, ), ); } export function getAppearancePatentHistory() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/appearance-patent/history`, { params: { user_id: getCurrentUserId(), limit: 50 } }, ), ); } export function getAppearancePatentTaskProgressBatch( taskIds: number[], options: TaskProgressBatchOptions = {}, ) { return postTaskProgressBatch( `${JAVA_API_PREFIX}/appearance-patent/tasks/progress/batch`, taskIds, options, ); } export function activateAppearancePatentTask(taskId: number) { return unwrapJavaResponse( post, undefined>( `${JAVA_API_PREFIX}/appearance-patent/tasks/${taskId}/activate?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, undefined, ), ); } export function deleteAppearancePatentTask(taskId: number) { return unwrapJavaResponse( del>(`${JAVA_API_PREFIX}/appearance-patent/tasks/${taskId}`, { params: { user_id: getCurrentUserId() }, }), ); } export function deleteAppearancePatentHistory(resultId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/appearance-patent/history/${resultId}`, { params: { user_id: getCurrentUserId() } }, ), ); } export function getAppearancePatentResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/appearance-patent/results/${resultId}/download`); } // ========== 相似ASIN检测 ========== export interface SimilarAsinParsedRow { rowIndex: number; sourceFileKey?: string; sourceFilename?: string; sourceId: string; displayId: string; rowToken?: string; groupKey?: string; asin: string; country: string; price?: string; url?: string; title?: string; } export interface SimilarAsinParsedGroup { sourceFileKey?: string; sourceFilename?: string; groupKey?: string; baseId?: string; displayId?: string; itemCount?: number; items: SimilarAsinParsedRow[]; } export interface SimilarAsinParseVo { taskId: number; sourceFilename?: string; sourceFileCount?: number; totalRows: number; acceptedRows: number; droppedRows: number; groupCount?: number; aiPrompt?: string; items: SimilarAsinParsedRow[]; groups?: SimilarAsinParsedGroup[]; } export interface SimilarAsinDashboardVo { pendingTaskCount: number; processedTaskCount: number; successTaskCount: number; failedTaskCount: number; } export interface SimilarAsinHistoryItem { resultId?: number; taskId?: number; sourceFilename?: string; resultFilename?: string; downloadUrl?: string; fileJobId?: number; fileStatus?: string; fileError?: string; fileReady?: boolean; fileProgressPercent?: number; fileProgressCurrent?: number; fileProgressTotal?: number; fileProgressMessage?: string; taskStatus?: string; success?: boolean; error?: string; rowCount?: number; createdAt?: string; } export interface SimilarAsinHistoryVo { items: SimilarAsinHistoryItem[]; } export interface SimilarAsinTaskSummary { id?: number; taskNo?: string; status?: string; errorMessage?: string; createdAt?: string; updatedAt?: string; finishedAt?: string; } export interface SimilarAsinTaskDetailVo { task?: SimilarAsinTaskSummary; items?: SimilarAsinHistoryItem[]; } export interface SimilarAsinTaskBatchVo { items: SimilarAsinTaskDetailVo[]; missingTaskIds?: number[]; } export function parseSimilarAsin(files: UploadedFileRef[], aiPrompt: string, apiKey?: string) { return unwrapJavaResponse( post< JavaApiResponse, { user_id: number; files: UploadedFileRef[]; ai_prompt: string; api_key?: string } >(`${JAVA_API_PREFIX}/similar-asin/parse`, { user_id: getCurrentUserId(), files, ai_prompt: aiPrompt, api_key: apiKey, }), ); } export function getSimilarAsinDashboard() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/similar-asin/dashboard`, { params: { user_id: getCurrentUserId() } }, ), ); } export function getSimilarAsinHistory() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/similar-asin/history`, { params: { user_id: getCurrentUserId(), limit: 50 } }, ), ); } export function getSimilarAsinTaskProgressBatch( taskIds: number[], options: TaskProgressBatchOptions = {}, ) { return postTaskProgressBatch( `${JAVA_API_PREFIX}/similar-asin/tasks/progress/batch`, taskIds, options, ); } export function activateSimilarAsinTask(taskId: number) { return unwrapJavaResponse( post, undefined>( `${JAVA_API_PREFIX}/similar-asin/tasks/${taskId}/activate?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, undefined, ), ); } export function deleteSimilarAsinTask(taskId: number) { return unwrapJavaResponse( del>(`${JAVA_API_PREFIX}/similar-asin/tasks/${taskId}`, { params: { user_id: getCurrentUserId() }, }), ); } export function deleteSimilarAsinHistory(resultId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/similar-asin/history/${resultId}`, { params: { user_id: getCurrentUserId() } }, ), ); } export function getSimilarAsinResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/similar-asin/results/${resultId}/download`); } // ========== 跟价 ========== export interface PriceTrackCandidateVo { id: number; shopName: string; } export interface PriceTrackShopQueueItem { shopName?: string; shopMallName?: string; shopId?: number | string | null; matched?: boolean; matchStatus?: string; matchMessage?: string; platform?: string; companyName?: string; skipAsins?: Record; } export interface PriceTrackMatchShopsVo { items: PriceTrackShopQueueItem[]; skipAsinsByCountry?: Record; skipAsinDetailsByCountry?: Record>; asinRowsByCountry?: Record; minimumPriceByCountryAndAsin?: Record>; } export interface PriceTrackCountryPreferenceVo { userId: number; countryCodes: string[]; } export interface PriceTrackDashboardVo { candidateCount: number; processedTaskCount: number; successTaskCount: number; failedTaskCount: number; } export interface PriceTrackHistoryItem { resultId?: number; taskId?: number; loopRunId?: number; roundIndex?: number; shopName?: string; shopMallName?: string; shopId?: number | string | null; platform?: string; companyName?: string; matched?: boolean; matchStatus?: string; matchMessage?: string; taskStatus?: string; outputFilename?: string; downloadUrl?: string; fileJobId?: number; fileStatus?: string; fileError?: string; fileReady?: boolean; error?: string; success?: boolean; } export interface PriceTrackHistoryVo { items: PriceTrackHistoryItem[]; } export interface PriceTrackAsinParsedRow { shopMallName?: string; asin?: string; price?: string; recommendedPrice?: string; shippingFee?: string; minimumPrice?: string; firstPlace?: string; secondPlace?: string; cartShopName?: string; priceChangeStatus?: string; modifyCount?: string; status?: string; } export interface PriceTrackCreateTaskVo { taskId: number; items: PriceTrackHistoryItem[]; skipAsinsByCountry?: Record; skipAsinDetailsByCountry?: Record>; asinRowsByCountry?: Record; minimumPriceByCountryAndAsin?: Record>; } export interface PriceTrackTaskSummary { id?: number; taskNo?: string; status?: string; loopRunId?: number; roundIndex?: number; errorMessage?: string; createdAt?: string; updatedAt?: string; finishedAt?: string; } export interface PriceTrackTaskDetailVo { task?: PriceTrackTaskSummary; items?: PriceTrackHistoryItem[]; } export interface PriceTrackTaskBatchVo { items: PriceTrackTaskDetailVo[]; missingTaskIds?: number[]; } export interface PriceTrackPendingDeleteVo { removed: boolean; } export type PriceTrackCreateTaskPayload = Omit< PriceTrackCreateTaskRequest, "userId" >; export function listPriceTrackCandidates() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/price-track/candidates?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, ), ); } export function addPriceTrackCandidate(shopName: string) { return unwrapJavaResponse( post, { userId: number; shopName: string }>( `${JAVA_API_PREFIX}/price-track/candidates`, { userId: getCurrentUserId(), shopName }, ), ); } export function deletePriceTrackCandidate(id: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/price-track/candidates/${id}?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, ), ); } export function getPriceTrackCountryPreference() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/price-track/country-preference?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, ), ); } export function putPriceTrackCountryPreference(countryCodes: string[]) { return unwrapJavaResponse( put, { userId: number; countryCodes: string[] }>( `${JAVA_API_PREFIX}/price-track/country-preference`, { userId: getCurrentUserId(), countryCodes }, ), ); } export function matchPriceTrackShops( shopNames: string[], options?: { asinFiles?: string[]; countryCodes?: string[]; }, ) { return unwrapJavaResponse( post< JavaApiResponse, { userId: number; shopNames: string[]; asinFiles?: string[]; countryCodes?: string[] } >( `${JAVA_API_PREFIX}/price-track/match-shops`, { userId: getCurrentUserId(), shopNames, asinFiles: options?.asinFiles || [], countryCodes: options?.countryCodes || [], }, ), ); } export function getShopMatchTaskProgressBatch(taskIds: number[]) { return postTaskProgressBatch( `${JAVA_API_PREFIX}/shop-match/tasks/progress/batch`, taskIds, ); } export function getProductRiskTaskProgressBatch(taskIds: number[]) { return postTaskProgressBatch( `${JAVA_API_PREFIX}/product-risk-resolve/tasks/progress/batch`, taskIds, ); } export function getDeleteBrandTaskProgress(taskIds: number[]) { return postTaskProgressBatch( `${JAVA_API_PREFIX}/delete-brand/tasks/progress/batch`, taskIds, ); } export function getPriceTrackDashboard() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/price-track/dashboard?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, ), ); } export function getPriceTrackHistory() { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/price-track/history?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, ), ); } export function deletePriceTrackHistory(resultId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/price-track/history/${resultId}?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, ), ); } export interface PriceTrackCreateTaskRequest { userId: number; statusMode: boolean; asinMode: boolean; items: PriceTrackShopQueueItem[]; asinFiles: string[]; countryCodes: string[]; loopRunId?: number; roundIndex?: number; shopIndex?: number; } export type PriceTrackExecutionMode = "FINITE" | "INFINITE"; export interface PriceTrackLoopRunCreateRequest { userId: number; statusMode: boolean; asinMode: boolean; items: PriceTrackShopQueueItem[]; asinFiles: string[]; countryCodes: string[]; executionMode: PriceTrackExecutionMode; targetRounds?: number; } export interface PriceTrackLoopRunVo { id: number; status?: string; executionMode?: PriceTrackExecutionMode; targetRounds?: number; currentRound?: number; currentShopIndex?: number; totalShopCount?: number; activeTaskId?: number; activeTaskStatus?: string; stopRequested?: boolean; errorMessage?: string; statusMode?: boolean; asinMode?: boolean; asinFiles?: string[]; countryCodes?: string[]; } export interface PriceTrackLoopRunDispatchVo { loopRun?: PriceTrackLoopRunVo; childTaskRequest?: PriceTrackCreateTaskRequest; } export function createPriceTrackTask( request: PriceTrackCreateTaskPayload | PriceTrackCreateTaskRequest, ) { return unwrapJavaResponse( post, PriceTrackCreateTaskRequest>( `${JAVA_API_PREFIX}/price-track/tasks`, { ...request, userId: getCurrentUserId() }, ), ); } export function deletePriceTrackTask(taskId: number) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/price-track/tasks/${taskId}?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, ), ); } export function getPriceTrackTasksBatch(taskIds: number[]) { return unwrapJavaResponse( post, { taskIds: number[] }>( `${JAVA_API_PREFIX}/price-track/tasks/batch`, { taskIds }, ), ); } export function createPriceTrackLoopRun( request: Omit | PriceTrackLoopRunCreateRequest, ) { return unwrapJavaResponse( post, PriceTrackLoopRunCreateRequest>( `${JAVA_API_PREFIX}/price-track/loop-runs`, { ...request, userId: getCurrentUserId() }, ), ); } export function getPriceTrackLoopRun(loopRunId: number) { return unwrapJavaResponse( get>( `${JAVA_API_PREFIX}/price-track/loop-runs/${loopRunId}?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, ), ); } export function dispatchNextPriceTrackLoopRun(loopRunId: number) { return unwrapJavaResponse( post, undefined>( `${JAVA_API_PREFIX}/price-track/loop-runs/${loopRunId}/dispatch-next?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, undefined, ), ); } export function completePriceTrackLoopChild(loopRunId: number, childTaskId: number) { return unwrapJavaResponse( post, { childTaskId: number }>( `${JAVA_API_PREFIX}/price-track/loop-runs/${loopRunId}/child-finished?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, { childTaskId }, ), ); } export function stopPriceTrackLoopRun(loopRunId: number) { return unwrapJavaResponse( post, undefined>( `${JAVA_API_PREFIX}/price-track/loop-runs/${loopRunId}/stop?user_id=${encodeURIComponent(String(getCurrentUserId()))}`, undefined, ), ); } export function getPriceTrackTaskProgressBatch(taskIds: number[]) { return postTaskProgressBatch( `${JAVA_API_PREFIX}/price-track/tasks/progress/batch`, taskIds, ); } export function getPriceTrackResultDownloadUrl(resultId: number) { return getJavaDownloadUrl(`/price-track/results/${resultId}/download`); } export function deletePendingPriceTrackShopResult(shopName: string) { return unwrapJavaResponse( del>( `${JAVA_API_PREFIX}/price-track/pending-shop-result?user_id=${encodeURIComponent(String(getCurrentUserId()))}&shop_name=${encodeURIComponent(shopName)}`, ), ); } export function getJavaDownloadUrl(path: string) { let raw = path.startsWith("http://") || path.startsWith("https://") ? path : `${JAVA_API_PREFIX}${path}`; // pywebview 的 save_file_from_url_new 需要完整且带 schema 的 URL if ( !raw.startsWith("http://") && !raw.startsWith("https://") && typeof window !== "undefined" ) { raw = `${window.location.origin}${raw}`; } const separator = raw.includes("?") ? "&" : "?"; return `${raw}${separator}user_id=${encodeURIComponent(String(getCurrentUserId()))}`; } export { JAVA_API_PREFIX, http };