修复后端BUG
This commit is contained in:
@@ -108,7 +108,7 @@ import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import BrandTopBar from '@/pages/brand/components/BrandTopBar.vue'
|
||||
import { LISTING_FILTER_OPTIONS, type ListingFilterValue } from '@/pages/brand/components/listingFilters'
|
||||
import { activateShopMatchTask, addShopMatchCandidate, createShopMatchTask, deleteShopMatchCandidate, deleteShopMatchHistory, deleteShopMatchTask, getShopMatchCountryPreference, getShopMatchDashboard, getShopMatchHistory, getShopMatchResultDownloadUrl, getShopMatchTaskProgressBatch, getShopMatchTasksBatch, listShopMatchCandidates, matchShopMatchShops, putShopMatchCountryPreference, type ShopMatchCandidateVo, type ShopMatchDashboardVo, type ShopMatchHistoryItem, type ShopMatchShopQueueItem, type ShopMatchTaskDetailVo } from '@/shared/api/java-modules'
|
||||
import { activateShopMatchTask, addShopMatchCandidate, createShopMatchTask, deleteShopMatchCandidate, deleteShopMatchHistory, deleteShopMatchTask, getShopMatchCountryPreference, getShopMatchDashboard, getShopMatchHistory, getShopMatchResultDownloadUrl, getShopMatchTaskProgressBatch, getShopMatchTasksBatch, listShopMatchCandidates, matchShopMatchShops, putShopMatchCountryPreference, type ShopMatchCandidateVo, type ShopMatchCreateTaskItem, type ShopMatchDashboardVo, type ShopMatchHistoryItem, type ShopMatchShopQueueItem, type ShopMatchTaskDetailVo } from '@/shared/api/java-modules'
|
||||
import { getPywebviewApi } from '@/shared/bridges/pywebview'
|
||||
import { getTaskPollIntervalMs } from '@/shared/task-progress-config'
|
||||
import { createCategorizedTimers } from '@/shared/utils/categorized-timers'
|
||||
@@ -303,9 +303,47 @@ function sanitizeSchedulePickerValues(values: string[], ensureOne: boolean) {
|
||||
if (!deduped.length && ensureOne) return [getDefaultScheduleValue()]
|
||||
return deduped
|
||||
}
|
||||
function readSkipAsinsByCountry(item: ShopMatchHistoryItem) { return item.skipAsinsByCountry || item.skip_asins_by_country || {} }
|
||||
function readSkipAsinDetailsByCountry(item: ShopMatchHistoryItem) { return item.skipAsinDetailsByCountry || item.skip_asin_details_by_country || {} }
|
||||
function buildQueuePayload(taskId: number, item: Pick<ShopMatchHistoryItem, 'shopName' | 'shopId' | 'platform' | 'companyName' | 'matchStatus' | 'matchMessage' | 'skipAsinsByCountry' | 'skip_asins_by_country' | 'skipAsinDetailsByCountry' | 'skip_asin_details_by_country'>, countryCodes: string[], stageIndex?: number, finalStage = true) { const skipAsinsByCountry = readSkipAsinsByCountry(item as ShopMatchHistoryItem); const skipAsinDetailsByCountry = readSkipAsinDetailsByCountry(item as ShopMatchHistoryItem); return { type: 'shop-match-run', ts: Date.now(), data: { taskId, user_id: currentUserId(), items: [{ shopName: item.shopName, shopId: item.shopId, platform: item.platform, companyName: item.companyName, matched: true, matchStatus: item.matchStatus, matchMessage: item.matchMessage, skip_asins_by_country: skipAsinsByCountry, skipAsinsByCountry, skip_asin_details_by_country: skipAsinDetailsByCountry, skipAsinDetailsByCountry }], country_codes: [...countryCodes], skip_asins_by_country: skipAsinsByCountry, skipAsinsByCountry, skip_asin_details_by_country: skipAsinDetailsByCountry, skipAsinDetailsByCountry, risk_listing_filter: shopMatchListingFilter.value, stage_index: stageIndex, final_stage: finalStage } } }
|
||||
function getJavaApiBaseUrl() { return `${window.location.origin}/newApi/api` }
|
||||
function buildSkipAsinsPageUrl(taskId: number) { return `${getJavaApiBaseUrl()}/shop-match/tasks/${taskId}/skip-asins/paginated` }
|
||||
function buildTaskCreateItem(item: ShopMatchShopQueueItem): ShopMatchCreateTaskItem {
|
||||
return {
|
||||
shopName: item.shopName,
|
||||
matched: item.matched,
|
||||
shopId: item.shopId,
|
||||
platform: item.platform,
|
||||
companyName: item.companyName,
|
||||
openStoreUrl: item.openStoreUrl,
|
||||
matchedUserId: item.matchedUserId,
|
||||
matchStatus: item.matchStatus,
|
||||
matchMessage: item.matchMessage,
|
||||
}
|
||||
}
|
||||
function buildQueuePayload(taskId: number, item: Pick<ShopMatchHistoryItem, 'shopName' | 'shopId' | 'platform' | 'companyName' | 'matchStatus' | 'matchMessage'>, countryCodes: string[], stageIndex?: number, finalStage = true) {
|
||||
const skipAsinsPageUrl = buildSkipAsinsPageUrl(taskId)
|
||||
return {
|
||||
type: 'shop-match-run',
|
||||
ts: Date.now(),
|
||||
data: {
|
||||
taskId,
|
||||
user_id: currentUserId(),
|
||||
items: [{
|
||||
shopName: item.shopName,
|
||||
shopId: item.shopId,
|
||||
platform: item.platform,
|
||||
companyName: item.companyName,
|
||||
matched: true,
|
||||
matchStatus: item.matchStatus,
|
||||
matchMessage: item.matchMessage,
|
||||
}],
|
||||
country_codes: [...countryCodes],
|
||||
skip_asins_page_url: skipAsinsPageUrl,
|
||||
skip_asins_page_size: 1000,
|
||||
risk_listing_filter: shopMatchListingFilter.value,
|
||||
stage_index: stageIndex,
|
||||
final_stage: finalStage,
|
||||
},
|
||||
}
|
||||
}
|
||||
function nextScheduleStage(taskId: number) { const task = taskSnapshots.value[taskId]?.task; const stages = task?.scheduleStages || []; const currentStageIndex = task?.currentStageIndex; if (typeof currentStageIndex !== 'number') return null; const stage = stages.find((item) => item.stageIndex === currentStageIndex); if (!stage?.scheduledAt) return null; return { stageIndex: currentStageIndex, scheduledAt: stage.scheduledAt, finalStage: currentStageIndex === stages.length - 1 } }
|
||||
async function dispatchScheduledTask(taskId: number, item: ShopMatchHistoryItem) { const api = getPywebviewApi(); if (!api?.enqueue_json) throw new Error('当前客户端未提供 enqueue_json'); const stage = nextScheduleStage(taskId); const snapshot = taskSnapshots.value[taskId]; const countryCodes = snapshot?.task?.countryCodes || orderedCountryCodes.value; if (!stage) throw new Error(`任务 ${taskId} 缺少待执行阶段`); clearDispatchTimer(taskId); await withTransientRetry(() => activateShopMatchTask(taskId, stage.stageIndex), (attempt, maxAttempts) => { queuePushResult.value = `任务 ${taskId} 等待后端恢复后再激活执行(${attempt}/${maxAttempts})...` }); taskDetails.value = { ...taskDetails.value, [taskId]: 'RUNNING' }; if (snapshot?.task) taskSnapshots.value = { ...taskSnapshots.value, [taskId]: { ...snapshot, task: { ...snapshot.task, status: 'RUNNING', activeStageIndex: stage.stageIndex } } }; saveTaskSnapshotsToStorage(); saveTaskDetailsToStorage(); const payload = buildQueuePayload(taskId, item, countryCodes, stage.stageIndex, stage.finalStage); queuePayloadText.value = JSON.stringify(payload, null, 2); const result = await api.enqueue_json(payload); if (!result?.success) throw new Error(result?.error || `任务 ${taskId} 推送失败`); addPollingTask(taskId); queuePushResult.value = `任务 ${taskId} 已推送第 ${stage.stageIndex + 1} 轮`; ensurePolling(true) }
|
||||
function scheduledTimestamp(value?: string) { const date = parseScheduleDateTime(value); return date ? date.getTime() : Number.NaN }
|
||||
@@ -459,7 +497,7 @@ function formatMatchStatus(status?: string) { const value = (status || '').trim(
|
||||
function formatMatchRemark(row: ShopMatchShopQueueItem) { const message = (row.matchMessage || '').trim(); if (message) return message; if (row.matched && row.matchStatus === 'MATCHED') return '紫鸟索引已命中,可推送队列'; if (row.matched) return '已关联索引,请结合状态列查看是否可推送'; return '未命中或未就绪,请检查店铺名与索引刷新' }
|
||||
async function pushToPythonQueue() { autoQueueEnabled.value = true; await processMatchedQueue() }
|
||||
function nextMatchedQueueItem() { return matchedItems.value.find((item) => item.matched) }
|
||||
async function processMatchedQueue() { if (disposed || queueWorkerRunning.value) return; const api = getPywebviewApi(); if (!api?.enqueue_json) { ElMessage.error('当前客户端未提供 enqueue_json'); return } const matched = matchedItems.value.filter((item) => item.matched); if (!matched.length) { ElMessage.warning('请先匹配可用店铺'); return } if (!orderedCountryCodes.value.length) { ElMessage.warning('请至少勾选一个国家'); return } let scheduleValues: string[] | undefined; try { scheduleValues = parseScheduleValues() } catch (error) { ElMessage.error(error instanceof Error ? error.message : '定时配置无效'); return } queueWorkerRunning.value = true; pushing.value = true; queuePayloadText.value = ''; let successCount = 0; let failedCount = 0; let index = 0; try { while (!disposed && autoQueueEnabled.value) { const item = nextMatchedQueueItem(); if (!item) break; index += 1; try { const created = await withTransientRetry(() => createShopMatchTask([item], orderedCountryCodes.value, scheduleValues), (attempt, maxAttempts) => { queuePushResult.value = `后端服务暂时不可用,正在重试创建任务(${attempt}/${maxAttempts})...` }); const taskId = created.taskId; const initialStatus = scheduleValues?.length ? 'SCHEDULED' : 'RUNNING'; const snapshot: ShopMatchTaskDetailVo = { task: { id: taskId, status: initialStatus, scheduledAt: scheduleValues?.[0], countryCodes: [...orderedCountryCodes.value], currentStageIndex: scheduleValues?.length ? 0 : undefined, activeStageIndex: undefined, scheduleStages: (scheduleValues || []).map((value, stageIndex) => ({ stageIndex, scheduledAt: value, status: stageIndex === 0 ? 'SCHEDULED' : 'PENDING' })) }, items: created.items }; taskSnapshots.value = { ...taskSnapshots.value, [taskId]: snapshot }; taskDetails.value = { ...taskDetails.value, [taskId]: initialStatus }; saveTaskSnapshotsToStorage(); saveTaskDetailsToStorage(); const createdItem = created.items?.[0]; if (!createdItem) { removeMatchedRowsLocally([item]); continue } if (scheduleValues?.length) { successCount += 1; restoreScheduledDispatches(); queuePushResult.value = `任务 ${taskId} 已创建,共 ${scheduleValues.length} 个执行时间点`; removeMatchedRowsLocally([item]); continue } const payload = buildQueuePayload(taskId, item, orderedCountryCodes.value); queuePayloadText.value = JSON.stringify(payload, null, 2); const result = await api.enqueue_json(payload); if (!result?.success) { failedCount += 1; queuePushResult.value = `任务 ${taskId} 推送失败,已自动继续下一条:${result?.error || '未知错误'}`; ElMessage.error(queuePushResult.value); removeMatchedRowsLocally([item]); continue } addPollingTask(taskId); ensurePolling(true); removeMatchedRowsLocally([item]); queuePushResult.value = `任务 ${taskId} 已入队,等待执行完成`; const finalStatus = await waitForTaskTerminal(taskId); if (finalStatus !== 'SUCCESS') { failedCount += 1; queuePushResult.value = `任务 ${taskId} 执行失败,已自动继续下一条`; ElMessage.error(queuePushResult.value); continue } successCount += 1; queuePushResult.value = `任务 ${taskId} 已完成` } catch (error) { if (disposed) break; failedCount += 1; queuePushResult.value = `第 ${index} 条任务处理失败,已自动继续下一条:${error instanceof Error ? error.message : '未知错误'}`; ElMessage.error(queuePushResult.value); removeMatchedRowsLocally([item]); continue } } restoreScheduledDispatches(); if (!scheduleValues?.length) ensurePolling(true); if (!disposed && (successCount > 0 || failedCount > 0)) ElMessage.success(scheduleValues?.length ? `定时任务已创建:成功 ${successCount} 条,失败 ${failedCount} 条` : `店铺推送已完成:成功 ${successCount} 条,失败 ${failedCount} 条`) } catch (error) { if (disposed) return; const message = error instanceof Error ? error.message : '推送失败'; queuePushResult.value = message; ElMessage.error(message) } finally { queueWorkerRunning.value = false; pushing.value = false } }
|
||||
async function processMatchedQueue() { if (disposed || queueWorkerRunning.value) return; const api = getPywebviewApi(); if (!api?.enqueue_json) { ElMessage.error('当前客户端未提供 enqueue_json'); return } const matched = matchedItems.value.filter((item) => item.matched); if (!matched.length) { ElMessage.warning('请先匹配可用店铺'); return } if (!orderedCountryCodes.value.length) { ElMessage.warning('请至少勾选一个国家'); return } let scheduleValues: string[] | undefined; try { scheduleValues = parseScheduleValues() } catch (error) { ElMessage.error(error instanceof Error ? error.message : '定时配置无效'); return } queueWorkerRunning.value = true; pushing.value = true; queuePayloadText.value = ''; let successCount = 0; let failedCount = 0; let index = 0; try { while (!disposed && autoQueueEnabled.value) { const item = nextMatchedQueueItem(); if (!item) break; index += 1; try { const created = await withTransientRetry(() => createShopMatchTask([buildTaskCreateItem(item)], orderedCountryCodes.value, scheduleValues), (attempt, maxAttempts) => { queuePushResult.value = `后端服务暂时不可用,正在重试创建任务(${attempt}/${maxAttempts})...` }); const taskId = created.taskId; const initialStatus = scheduleValues?.length ? 'SCHEDULED' : 'RUNNING'; const snapshot: ShopMatchTaskDetailVo = { task: { id: taskId, status: initialStatus, scheduledAt: scheduleValues?.[0], countryCodes: [...orderedCountryCodes.value], currentStageIndex: scheduleValues?.length ? 0 : undefined, activeStageIndex: undefined, scheduleStages: (scheduleValues || []).map((value, stageIndex) => ({ stageIndex, scheduledAt: value, status: stageIndex === 0 ? 'SCHEDULED' : 'PENDING' })) }, items: created.items }; taskSnapshots.value = { ...taskSnapshots.value, [taskId]: snapshot }; taskDetails.value = { ...taskDetails.value, [taskId]: initialStatus }; saveTaskSnapshotsToStorage(); saveTaskDetailsToStorage(); const createdItem = created.items?.[0]; if (!createdItem) { removeMatchedRowsLocally([item]); continue } if (scheduleValues?.length) { successCount += 1; restoreScheduledDispatches(); queuePushResult.value = `任务 ${taskId} 已创建,共 ${scheduleValues.length} 个执行时间点`; removeMatchedRowsLocally([item]); continue } const payload = buildQueuePayload(taskId, item, orderedCountryCodes.value); queuePayloadText.value = JSON.stringify(payload, null, 2); const result = await api.enqueue_json(payload); if (!result?.success) { failedCount += 1; queuePushResult.value = `任务 ${taskId} 推送失败,已自动继续下一条:${result?.error || '未知错误'}`; ElMessage.error(queuePushResult.value); removeMatchedRowsLocally([item]); continue } addPollingTask(taskId); ensurePolling(true); removeMatchedRowsLocally([item]); queuePushResult.value = `任务 ${taskId} 已入队,等待执行完成`; const finalStatus = await waitForTaskTerminal(taskId); if (finalStatus !== 'SUCCESS') { failedCount += 1; queuePushResult.value = `任务 ${taskId} 执行失败,已自动继续下一条`; ElMessage.error(queuePushResult.value); continue } successCount += 1; queuePushResult.value = `任务 ${taskId} 已完成` } catch (error) { if (disposed) break; failedCount += 1; queuePushResult.value = `第 ${index} 条任务处理失败,已自动继续下一条:${error instanceof Error ? error.message : '未知错误'}`; ElMessage.error(queuePushResult.value); removeMatchedRowsLocally([item]); continue } } restoreScheduledDispatches(); if (!scheduleValues?.length) ensurePolling(true); if (!disposed && (successCount > 0 || failedCount > 0)) ElMessage.success(scheduleValues?.length ? `定时任务已创建:成功 ${successCount} 条,失败 ${failedCount} 条` : `店铺推送已完成:成功 ${successCount} 条,失败 ${failedCount} 条`) } catch (error) { if (disposed) return; const message = error instanceof Error ? error.message : '推送失败'; queuePushResult.value = message; ElMessage.error(message) } finally { queueWorkerRunning.value = false; pushing.value = false } }
|
||||
onMounted(async () => { loadPollingIdsFromStorage(); loadTaskDetailsFromStorage(); loadTaskSnapshotsFromStorage(); loadMatchedItemsFromStorage(); schedulePickerValues.value = sanitizeSchedulePickerValues(schedulePickerValues.value, scheduleEnabled.value); syncPollingIdsWithTaskState(); await Promise.allSettled([loadCandidates(), loadCountryPreference(), loadHistory(), loadDashboard()]); window.addEventListener('focus', handleScheduleRecovery); document.addEventListener('visibilitychange', handleScheduleRecovery); restoreScheduledDispatches(); if (pollingTaskIds.value.length) ensurePolling(true) })
|
||||
onUnmounted(() => { disposed = true; stopPolling(); stopScheduleHeartbeat(); clearSleepTimers(); window.removeEventListener('focus', handleScheduleRecovery); document.removeEventListener('visibilitychange', handleScheduleRecovery); if (countryPrefSaveTimer) timers.clearTimer('preference-save', countryPrefSaveTimer); for (const timer of dispatchTimers.values()) timers.clearTimer('scheduled-dispatch', timer); dispatchTimers.clear(); timers.clearScope() })
|
||||
</script>
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">整体风格 / 视频提示词</span>
|
||||
<span class="delivery-field__label">视频风格/动作引导</span>
|
||||
<el-input v-model="currentWorkspace.videoPrompt" type="textarea" :rows="4" resize="none"
|
||||
placeholder="例如:视频画面保持高清,运镜平稳,整体风格干净明亮。" />
|
||||
</div>
|
||||
@@ -138,8 +138,8 @@
|
||||
<el-input v-model="currentWorkspace.categoryName" clearable placeholder="输入产品类目,例如:服饰鞋包 / 男鞋 / 运动鞋" />
|
||||
</div>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">产品名称</span>
|
||||
<el-input v-model="currentWorkspace.productName" clearable placeholder="例如:新款复古运动鞋" />
|
||||
<span class="delivery-field__label">产品名称 *</span>
|
||||
<el-input v-model="currentWorkspace.productName" clearable aria-required="true" placeholder="例如:新款复古运动鞋" />
|
||||
</div>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">核心卖点 / 产品属性</span>
|
||||
@@ -198,8 +198,8 @@
|
||||
<el-input v-model="currentWorkspace.categoryName" clearable placeholder="输入产品类目,例如:服饰鞋包 / 男鞋 / 运动鞋" />
|
||||
</div>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">产品名称</span>
|
||||
<el-input v-model="currentWorkspace.productName" clearable placeholder="例如:新款复古运动鞋" />
|
||||
<span class="delivery-field__label">产品名称 *</span>
|
||||
<el-input v-model="currentWorkspace.productName" clearable aria-required="true" placeholder="例如:新款复古运动鞋" />
|
||||
</div>
|
||||
<div class="delivery-field">
|
||||
<span class="delivery-field__label">核心卖点 / 产品属性</span>
|
||||
@@ -398,7 +398,8 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="T8密钥(国内)">
|
||||
<el-input v-model="secretForm.t8VideoKey" type="password" show-password placeholder="不填写则保留当前有效值" />
|
||||
<div v-if="secretStatus?.t8VideoKeyMasked" class="secret-form__hint">当前:{{ secretStatus.t8VideoKeyMasked }}</div>
|
||||
<div v-if="secretStatus?.t8VideoKeyMasked" class="secret-form__hint">当前:{{ secretStatus.t8VideoKeyMasked }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="音频密钥">
|
||||
<el-input v-model="secretForm.voiceApiKey" type="password" show-password placeholder="不填写则保留当前有效值" />
|
||||
@@ -408,7 +409,7 @@
|
||||
<el-form-item label="音频团队ID">
|
||||
<el-input v-model="secretForm.voiceGroupId" type="password" show-password placeholder="不填写则保留当前有效值" />
|
||||
<div v-if="secretStatus?.voiceGroupIdMasked" class="secret-form__hint">当前:{{ secretStatus.voiceGroupIdMasked
|
||||
}}</div>
|
||||
}}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期">
|
||||
<el-select v-model="secretForm.expireDays" class="delivery-select">
|
||||
@@ -1016,6 +1017,11 @@ async function rewriteScriptFromSource() {
|
||||
ElMessage.warning('请粘贴视频链接或文案来源链接,不要粘贴接口返回内容')
|
||||
return
|
||||
}
|
||||
const productName = currentWorkspace.value.productName.trim()
|
||||
if (!productName) {
|
||||
ElMessage.warning('请输入产品名称')
|
||||
return
|
||||
}
|
||||
if (activeTab.value === 'remake') currentWorkspace.value.scriptSourceUrl = url
|
||||
copyLearningLoading.value = true
|
||||
try {
|
||||
@@ -1024,7 +1030,7 @@ async function rewriteScriptFromSource() {
|
||||
duration: Math.max(0, Math.round(Number(currentWorkspace.value.durationSeconds) || 0)),
|
||||
proc_info: {
|
||||
type: currentWorkspace.value.categoryName.trim(),
|
||||
name: currentWorkspace.value.productName.trim(),
|
||||
name: productName,
|
||||
proc_image: resolveProductImageUrls(currentWorkspace.value),
|
||||
properties: currentWorkspace.value.productFocus.trim(),
|
||||
},
|
||||
|
||||
@@ -833,7 +833,45 @@ export type ShopMatchHistoryItem = ProductRiskHistoryItem;
|
||||
export type ShopMatchHistoryVo = ProductRiskHistoryVo;
|
||||
export type ShopMatchTaskDetailVo = ProductRiskTaskDetailVo;
|
||||
export type ShopMatchTaskBatchVo = ProductRiskTaskBatchVo;
|
||||
export type ShopMatchCreateTaskVo = ProductRiskCreateTaskVo;
|
||||
|
||||
export interface ShopMatchCreateTaskItem {
|
||||
shopName: string;
|
||||
matched: boolean;
|
||||
shopId?: string;
|
||||
platform?: string;
|
||||
companyName?: string;
|
||||
openStoreUrl?: string;
|
||||
matchedUserId?: number;
|
||||
matchStatus?: string;
|
||||
matchMessage?: string;
|
||||
}
|
||||
|
||||
export interface ShopMatchCreateTaskResultItem {
|
||||
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 ShopMatchCreateTaskVo {
|
||||
taskId: number;
|
||||
items: ShopMatchCreateTaskResultItem[];
|
||||
}
|
||||
|
||||
export interface ShopMatchTaskStage {
|
||||
stageIndex?: number;
|
||||
@@ -1020,14 +1058,14 @@ export function deleteShopMatchHistory(resultId: number) {
|
||||
}
|
||||
|
||||
export function createShopMatchTask(
|
||||
items: ShopMatchShopQueueItem[],
|
||||
items: ShopMatchCreateTaskItem[],
|
||||
countryCodes: string[],
|
||||
scheduleTimes?: string[],
|
||||
) {
|
||||
return unwrapJavaResponse(
|
||||
post<
|
||||
JavaApiResponse<ShopMatchCreateTaskVo>,
|
||||
{ user_id: number; items: ShopMatchShopQueueItem[]; country_codes: string[]; schedule_times?: string[] }
|
||||
{ user_id: number; items: ShopMatchCreateTaskItem[]; country_codes: string[]; schedule_times?: string[] }
|
||||
>(`${JAVA_API_PREFIX}/shop-match/tasks`, {
|
||||
user_id: getCurrentUserId(),
|
||||
items,
|
||||
@@ -1076,6 +1114,30 @@ export function getShopMatchResultDownloadUrl(resultId: number) {
|
||||
return getJavaDownloadUrl(`/shop-match/results/${resultId}/download`);
|
||||
}
|
||||
|
||||
export function getShopMatchTaskSkipAsinsPaginated(
|
||||
taskId: number,
|
||||
page: number = 1,
|
||||
pageSize: number = 1000,
|
||||
options: {
|
||||
shopName?: string | string[];
|
||||
countryCode?: string | string[];
|
||||
} = {},
|
||||
) {
|
||||
return unwrapJavaResponse(
|
||||
get<JavaApiResponse<ShopMatchSkipAsinPageVo>>(
|
||||
`${JAVA_API_PREFIX}/shop-match/tasks/${taskId}/skip-asins/paginated`,
|
||||
{
|
||||
params: {
|
||||
page,
|
||||
page_size: pageSize,
|
||||
...(options.shopName ? { shop_name: options.shopName } : {}),
|
||||
...(options.countryCode ? { country_code: options.countryCode } : {}),
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export type PatrolDeleteCandidateVo = ProductRiskCandidateVo;
|
||||
export type PatrolDeleteDashboardVo = ProductRiskDashboardVo;
|
||||
export type PatrolDeleteShopQueueItem = ProductRiskShopQueueItem;
|
||||
@@ -2474,6 +2536,16 @@ export interface SkipPriceAsinPageVo {
|
||||
minimum_price_by_country_and_asin?: Record<string, Record<string, string>>;
|
||||
}
|
||||
|
||||
export interface ShopMatchSkipAsinPageVo {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
queryAsins: QueryAsinCountryAsins[];
|
||||
skipAsinsByCountry: Record<string, string[]>;
|
||||
skipAsinDetailsByCountry: Record<string, Array<{ asin?: string; minimumPrice?: string }>>;
|
||||
}
|
||||
|
||||
export function deletePendingPriceTrackShopResult(shopName: string) {
|
||||
return unwrapJavaResponse(
|
||||
del<JavaApiResponse<PriceTrackPendingDeleteVo>>(
|
||||
|
||||
Reference in New Issue
Block a user