后端优化修复问题
This commit is contained in:
@@ -849,9 +849,9 @@ function buildSkipAsinDeletePolicy() {
|
||||
return {
|
||||
enabled,
|
||||
mode: enabled ? 'DELETE_WHEN_PRICE_BELOW_MINIMUM' : 'NONE',
|
||||
deleteWhenPriceBelowMinimum: enabled,
|
||||
compareField: 'price',
|
||||
thresholdField: 'minimumPrice',
|
||||
delete_when_price_below_minimum: enabled,
|
||||
compare_field: 'price',
|
||||
threshold_field: 'minimum_price',
|
||||
target: 'skip_asin',
|
||||
source: 'frontend-price-track',
|
||||
}
|
||||
@@ -959,12 +959,14 @@ async function pushToPythonQueueLegacy() {
|
||||
ts: Date.now(),
|
||||
data: {
|
||||
task_id: taskVo.taskId,
|
||||
// 店铺简要信息(用于展示)
|
||||
shop_names: matchedRows.map((i) => i.shopName).filter(Boolean),
|
||||
country_codes: resolveCountryCodesForRequest(),
|
||||
use_skip_asin_check: statusModeEnabled.value && !asinModeEnabled.value,
|
||||
skip_asin_check_url: `/api/price-track/tasks/${taskVo.taskId}/skip-asin/check`,
|
||||
use_paginated_skip_asins: false,
|
||||
skip_asin_page_size: 0,
|
||||
skip_asin_delete_policy: buildSkipAsinDeletePolicy(),
|
||||
delete_skip_asin_when_price_below_minimum: statusModeEnabled.value && !asinModeEnabled.value,
|
||||
deleteSkipAsinWhenPriceBelowMinimum: statusModeEnabled.value && !asinModeEnabled.value,
|
||||
},
|
||||
}
|
||||
queuePayloadText.value = JSON.stringify(queuePayload, null, 2)
|
||||
@@ -992,6 +994,7 @@ function buildQueuePayload(taskVo: PriceTrackCreateTaskVo, row: PriceTrackShopQu
|
||||
const taskItem = taskVo.items?.[0]
|
||||
const shopName = (row.shopName || '').trim()
|
||||
const skipAsinDeletePolicy = buildSkipAsinDeletePolicy()
|
||||
const skipAsinCheckPath = `/api/price-track/tasks/${taskVo.taskId}/skip-asin/check`
|
||||
const resultId = taskItem?.resultId ?? null
|
||||
const loopRunId = taskItem?.loopRunId ?? null
|
||||
const roundIndex = taskItem?.roundIndex ?? null
|
||||
@@ -1024,28 +1027,12 @@ function buildQueuePayload(taskVo: PriceTrackCreateTaskVo, row: PriceTrackShopQu
|
||||
round_index: roundIndex,
|
||||
country_codes: resolveCountryCodesForRequest(),
|
||||
mode: statusModeEnabled.value ? 'status' : 'asin',
|
||||
// 分页拉取配置(Python 端统一处理)
|
||||
use_paginated_skip_asins: true,
|
||||
skip_asin_page_size: 1000,
|
||||
// 移除:不再传递任何 ASIN 数据,Python 端统一分页拉取
|
||||
use_skip_asin_check: statusModeEnabled.value && !asinModeEnabled.value,
|
||||
skip_asin_check_url: skipAsinCheckPath,
|
||||
use_paginated_skip_asins: false,
|
||||
skip_asin_page_size: 0,
|
||||
skip_asin_delete_policy: skipAsinDeletePolicy,
|
||||
delete_skip_asin_when_price_below_minimum: skipAsinDeletePolicy.deleteWhenPriceBelowMinimum,
|
||||
deleteSkipAsinWhenPriceBelowMinimum: skipAsinDeletePolicy.deleteWhenPriceBelowMinimum,
|
||||
// 兼容字段
|
||||
taskId: taskVo.taskId,
|
||||
resultId: resultId,
|
||||
shopName,
|
||||
shopId: row.shopId ?? null,
|
||||
platformName: row.platform || '',
|
||||
companyName: row.companyName || '',
|
||||
shopMallName: row.shopMallName || '',
|
||||
matchStatus: row.matchStatus || '',
|
||||
matchMessage: row.matchMessage || null,
|
||||
outputFilename: outputFilename,
|
||||
downloadUrl: downloadUrl,
|
||||
taskStatus,
|
||||
loopRunId: loopRunId,
|
||||
roundIndex: roundIndex,
|
||||
delete_skip_asin_when_price_below_minimum: skipAsinDeletePolicy.delete_when_price_below_minimum,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +227,18 @@ function isTaskTerminalItem(item: ShopMatchHistoryItem) {
|
||||
}
|
||||
async function loadCandidates() { candidates.value = await listShopMatchCandidates() }
|
||||
async function loadDashboard() { dashboard.value = await getShopMatchDashboard() }
|
||||
async function loadHistory() { const data = await getShopMatchHistory(); historyItems.value = data.items || [] }
|
||||
async function loadHistory() { const data = await getShopMatchHistory(); const items = data.items || []; historyItems.value = items; reconcileTerminalTasksFromHistory(items) }
|
||||
function reconcileTerminalTasksFromHistory(items: ShopMatchHistoryItem[]) {
|
||||
const terminalTaskIds = new Set<number>()
|
||||
for (const item of items) {
|
||||
const taskId = normalizeTaskId(item.taskId)
|
||||
if (!taskId) continue
|
||||
if (isTaskTerminalStatus(item.taskStatus) || item.success === true || (item.fileStatus === 'SUCCESS' && item.fileReady)) {
|
||||
terminalTaskIds.add(taskId)
|
||||
}
|
||||
}
|
||||
for (const taskId of terminalTaskIds) removePollingTask(taskId)
|
||||
}
|
||||
async function loadCountryPreference() { try { const data = await getShopMatchCountryPreference(); if (countryPrefUserTouched.value) return; const codes = (data.country_codes || []).filter((item) => COUNTRY_OPTIONS.some((option) => option.code === item)); if (codes.length) orderedCountryCodes.value = codes } catch {} }
|
||||
async function refreshTaskViewsBestEffort() { await Promise.allSettled([loadHistory(), loadDashboard()]) }
|
||||
function scheduleSaveCountryPreference() { if (countryPrefSaveTimer) timers.clearTimer('preference-save', countryPrefSaveTimer); countryPrefSaveTimer = timers.setTimeout('preference-save', () => { countryPrefSaveTimer = null; void saveCountryPreference() }, 450) }
|
||||
@@ -394,7 +405,18 @@ async function waitForScheduledTaskStageExit(taskId: number) {
|
||||
}
|
||||
}
|
||||
}
|
||||
function resolvedTaskStatus(item: ShopMatchHistoryItem) { const taskId = normalizeTaskId(item.taskId); if (!taskId) return item.taskStatus || ''; return taskDetails.value[taskId] || taskSnapshots.value[taskId]?.task?.status || item.taskStatus || '' }
|
||||
function resolvedTaskStatus(item: ShopMatchHistoryItem) {
|
||||
const itemStatus = item.taskStatus || ''
|
||||
if (isTaskTerminalStatus(itemStatus)) return itemStatus
|
||||
if (item.success === true || (item.fileStatus === 'SUCCESS' && item.fileReady)) return 'SUCCESS'
|
||||
const taskId = normalizeTaskId(item.taskId)
|
||||
if (!taskId) return itemStatus
|
||||
const cachedStatus = taskDetails.value[taskId] || ''
|
||||
const snapshotStatus = taskSnapshots.value[taskId]?.task?.status || ''
|
||||
if (isTaskTerminalStatus(cachedStatus)) return cachedStatus
|
||||
if (isTaskTerminalStatus(snapshotStatus)) return snapshotStatus
|
||||
return cachedStatus || snapshotStatus || itemStatus
|
||||
}
|
||||
function taskSnapshotOf(item: ShopMatchHistoryItem) { const taskId = normalizeTaskId(item.taskId); return taskId ? taskSnapshots.value[taskId] : undefined }
|
||||
function currentTaskStageText(item: ShopMatchHistoryItem) { const snapshot = taskSnapshotOf(item); const task = snapshot?.task; const stages = task?.scheduleStages || []; if (!stages.length) return ''; const total = stages.length; const activeIndex = typeof task?.activeStageIndex === 'number' ? task.activeStageIndex : undefined; const currentIndex = typeof task?.currentStageIndex === 'number' ? task.currentStageIndex : undefined; if (typeof activeIndex === 'number') return `执行进度: 第 ${activeIndex + 1}/${total} 次执行中`; if (typeof currentIndex === 'number') return `执行进度: 等待第 ${currentIndex + 1}/${total} 次`; return `执行进度: 共 ${total} 次`}
|
||||
function nextScheduledDisplay(item: ShopMatchHistoryItem) { const snapshot = taskSnapshotOf(item); const task = snapshot?.task; const stages = task?.scheduleStages || []; if (typeof task?.currentStageIndex === 'number') { const stage = stages.find((entry) => entry.stageIndex === task.currentStageIndex); if (stage?.scheduledAt) return formatMonthDayTime(stage.scheduledAt) } if (item.scheduledAt) return formatMonthDayTime(item.scheduledAt); return '' }
|
||||
|
||||
@@ -51,6 +51,7 @@ type ActiveNavKey =
|
||||
| 'pricing'
|
||||
| 'patrol-delete'
|
||||
| 'query-asin'
|
||||
| 'withdraw'
|
||||
| 'collect-data'
|
||||
| 'image-video'
|
||||
|
||||
@@ -111,7 +112,7 @@ const navGroups: ReadonlyArray<NavGroup> = [
|
||||
{ key: 'pricing', label: '跟价', href: '/new_web_source/price-track.html', aliases: ['price-track'] },
|
||||
{ key: 'patrol-delete', label: '巡店删除', href: '/new_web_source/patrol-delete.html' },
|
||||
{ key: 'query-asin', label: '查询ASIN', href: '/new_web_source/query-asin.html' },
|
||||
{ key: 'withdraw', label: '取款' },
|
||||
{ key: 'withdraw', label: '取款', href: '/new_web_source/withdraw.html' },
|
||||
{ key: 'shop-status', label: '店铺状态查询' },
|
||||
],
|
||||
},
|
||||
|
||||
1360
frontend-vue/src/pages/brand/components/BrandWithdrawTab.vue
Normal file
1360
frontend-vue/src/pages/brand/components/BrandWithdrawTab.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1485,6 +1485,180 @@ export function deleteQueryAsinHistory(resultId: number) {
|
||||
);
|
||||
}
|
||||
|
||||
// ========== 取款 ==========
|
||||
|
||||
export type WithdrawCandidateVo = QueryAsinCandidateVo;
|
||||
export type WithdrawDashboardVo = QueryAsinDashboardVo;
|
||||
export type WithdrawShopQueueItem = ProductRiskShopQueueItem;
|
||||
|
||||
export type WithdrawStatusCode =
|
||||
| "ZERO_AVAILABLE"
|
||||
| "SUCCESS"
|
||||
| "BALANCE_FORBIDDEN"
|
||||
| "NEGATIVE_WITHDRAW_BLANK";
|
||||
|
||||
export interface WithdrawRow {
|
||||
country?: string;
|
||||
shopAmount?: number | string | null;
|
||||
withdrawAmount?: number | string | null;
|
||||
status?: WithdrawStatusCode | string;
|
||||
}
|
||||
|
||||
export interface WithdrawTaskItem {
|
||||
shopName: string;
|
||||
matched: boolean;
|
||||
shopId?: string;
|
||||
platform?: string;
|
||||
companyName?: string;
|
||||
matchStatus?: string;
|
||||
matchMessage?: string;
|
||||
}
|
||||
|
||||
export interface WithdrawHistoryItem extends WithdrawTaskItem {
|
||||
resultId?: number;
|
||||
taskId?: number;
|
||||
taskStatus?: string;
|
||||
reservedAmount?: number | string | null;
|
||||
success?: boolean;
|
||||
error?: string;
|
||||
outputFilename?: string;
|
||||
downloadUrl?: string;
|
||||
fileJobId?: number;
|
||||
fileStatus?: string;
|
||||
fileError?: string;
|
||||
fileReady?: boolean;
|
||||
createdAt?: string;
|
||||
finishedAt?: string;
|
||||
rows?: WithdrawRow[];
|
||||
shops?: WithdrawHistoryItem[];
|
||||
}
|
||||
|
||||
export interface WithdrawHistoryVo {
|
||||
items: WithdrawHistoryItem[];
|
||||
}
|
||||
|
||||
export interface WithdrawTaskBatchVo {
|
||||
items: WithdrawHistoryItem[];
|
||||
missingTaskIds: number[];
|
||||
}
|
||||
|
||||
export interface WithdrawCreateTaskVo {
|
||||
taskId: number;
|
||||
items: WithdrawHistoryItem[];
|
||||
}
|
||||
|
||||
export function listWithdrawCandidates() {
|
||||
return unwrapJavaResponse(
|
||||
get<JavaApiResponse<WithdrawCandidateVo[]>>(`${JAVA_API_PREFIX}/withdraw/candidates`, {
|
||||
params: { user_id: getCurrentUserId() },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function addWithdrawCandidate(shopName: string) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<WithdrawCandidateVo>, { user_id: number; shop_name: string }>(
|
||||
`${JAVA_API_PREFIX}/withdraw/candidates`,
|
||||
{ user_id: getCurrentUserId(), shop_name: shopName },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteWithdrawCandidate(id: number) {
|
||||
return unwrapJavaResponse(
|
||||
del<JavaApiResponse<null>>(`${JAVA_API_PREFIX}/withdraw/candidates/${id}`, {
|
||||
params: { user_id: getCurrentUserId() },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function clearWithdrawCandidates(shopNames: string[]) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<null>, { user_id: number; shop_names: string[] }>(
|
||||
`${JAVA_API_PREFIX}/withdraw/candidates/clear`,
|
||||
{ user_id: getCurrentUserId(), shop_names: shopNames },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function matchWithdrawShops(shopNames: string[]) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<ProductRiskMatchShopsVo>, { user_id: number; shop_names: string[] }>(
|
||||
`${JAVA_API_PREFIX}/withdraw/match-shops`,
|
||||
{ user_id: getCurrentUserId(), shop_names: shopNames },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function getWithdrawDashboard() {
|
||||
return unwrapJavaResponse(
|
||||
get<JavaApiResponse<WithdrawDashboardVo>>(`${JAVA_API_PREFIX}/withdraw/dashboard`, {
|
||||
params: { user_id: getCurrentUserId() },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function getWithdrawHistory() {
|
||||
return unwrapJavaResponse(
|
||||
get<JavaApiResponse<WithdrawHistoryVo>>(`${JAVA_API_PREFIX}/withdraw/history`, {
|
||||
params: { user_id: getCurrentUserId() },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function getWithdrawTaskProgressBatch(taskIds: number[]) {
|
||||
return postTaskProgressBatch<WithdrawTaskBatchVo>(
|
||||
`${JAVA_API_PREFIX}/withdraw/tasks/progress/batch`,
|
||||
taskIds,
|
||||
);
|
||||
}
|
||||
|
||||
export function createWithdrawTask(items: WithdrawTaskItem[], reservedAmount: number | string | null) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<WithdrawCreateTaskVo>, { user_id: number; reserved_amount: number | string | null; items: WithdrawTaskItem[] }>(
|
||||
`${JAVA_API_PREFIX}/withdraw/tasks`,
|
||||
{ user_id: getCurrentUserId(), reserved_amount: reservedAmount, items },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function submitWithdrawTaskResult(
|
||||
taskId: number,
|
||||
payload: {
|
||||
shops: Array<{
|
||||
shopName: string;
|
||||
error?: string;
|
||||
rows?: WithdrawRow[];
|
||||
shopDone?: boolean;
|
||||
submissionId?: string;
|
||||
}>;
|
||||
},
|
||||
) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<null>, typeof payload>(`${JAVA_API_PREFIX}/withdraw/tasks/${taskId}/result`, payload),
|
||||
);
|
||||
}
|
||||
|
||||
export function getWithdrawResultDownloadUrl(resultId: number) {
|
||||
return getJavaDownloadUrl(`/withdraw/results/${resultId}/download`);
|
||||
}
|
||||
|
||||
export function deleteWithdrawTask(taskId: number) {
|
||||
return unwrapJavaResponse(
|
||||
del<JavaApiResponse<null>>(`${JAVA_API_PREFIX}/withdraw/tasks/${taskId}`, {
|
||||
params: { user_id: getCurrentUserId() },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteWithdrawHistory(resultId: number) {
|
||||
return unwrapJavaResponse(
|
||||
del<JavaApiResponse<null>>(`${JAVA_API_PREFIX}/withdraw/history/${resultId}`, {
|
||||
params: { user_id: getCurrentUserId() },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// ========== 外观专利检测 ==========
|
||||
|
||||
export interface AppearancePatentParsedRow {
|
||||
@@ -2235,6 +2409,27 @@ export function getTaskSkipPriceAsinsPaginated(
|
||||
);
|
||||
}
|
||||
|
||||
export function checkTaskSkipPriceAsin(taskId: number, country: string, asin: string) {
|
||||
return unwrapJavaResponse(
|
||||
get<JavaApiResponse<SkipPriceAsinCheckVo>>(
|
||||
`${JAVA_API_PREFIX}/price-track/tasks/${taskId}/skip-asin/check`,
|
||||
{
|
||||
params: {
|
||||
country,
|
||||
asin,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export interface SkipPriceAsinCheckVo {
|
||||
country: string;
|
||||
asin: string;
|
||||
exists: boolean;
|
||||
minimumPrice?: string;
|
||||
}
|
||||
|
||||
export interface SkipPriceAsinPageVo {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
|
||||
14
frontend-vue/src/withdraw-main.ts
Normal file
14
frontend-vue/src/withdraw-main.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { createApp } from 'vue'
|
||||
import ElementPlus from 'element-plus'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
import dayjs from 'dayjs'
|
||||
import 'dayjs/locale/zh-cn'
|
||||
import 'element-plus/dist/index.css'
|
||||
import '@/styles/main.css'
|
||||
import BrandWithdrawTab from '@/pages/brand/components/BrandWithdrawTab.vue'
|
||||
|
||||
dayjs.locale('zh-cn')
|
||||
|
||||
createApp(BrandWithdrawTab).use(ElementPlus, { locale: zhCn }).mount('#app')
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ export default defineConfig({
|
||||
'price-track': resolve(__dirname, 'price-track.html'),
|
||||
'patrol-delete': resolve(__dirname, 'patrol-delete.html'),
|
||||
'query-asin': resolve(__dirname, 'query-asin.html'),
|
||||
withdraw: resolve(__dirname, 'withdraw.html'),
|
||||
'collect-data': resolve(__dirname, 'collect-data.html'),
|
||||
'image-video': resolve(__dirname, 'image-video.html'),
|
||||
},
|
||||
|
||||
14
frontend-vue/withdraw.html
Normal file
14
frontend-vue/withdraw.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>取款 - 数富AI</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/withdraw-main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user