完成后端架构重构等

This commit is contained in:
super
2026-04-23 15:25:41 +08:00
parent 6894f9cc57
commit 0391cb223f
86 changed files with 10843 additions and 1757 deletions

View File

@@ -312,6 +312,8 @@ const matchedItems = ref<PriceTrackShopQueueItem[]>([])
const adding = ref(false)
const matching = ref(false)
const pushing = ref(false)
const queueWorkerRunning = ref(false)
const autoQueueEnabled = ref(false)
const queuePushResult = ref('')
const queuePayloadText = ref('')
const matchAsinRowsByCountry = ref<Record<string, PriceTrackAsinParsedRow[]>>({})
@@ -787,6 +789,9 @@ async function runMatch() {
}
matchedItems.value = [...nextByShop.values()]
saveMatchedItemsToStorage()
if (autoQueueEnabled.value) {
void processMatchedQueue()
}
ElMessage.success(`匹配 ${batch.length} 个店铺`)
} catch (e) {
ElMessage.error(e instanceof Error ? e.message : '匹配失败')
@@ -813,6 +818,19 @@ function rowKeyForMatch(row: { shopName?: string; shopId?: number | string | nul
return `${(row.shopName || '').trim()}\u0001${row.shopId ?? ''}`
}
function buildSkipAsinDeletePolicy() {
const enabled = statusModeEnabled.value && !asinModeEnabled.value
return {
enabled,
mode: enabled ? 'DELETE_WHEN_PRICE_BELOW_MINIMUM' : 'NONE',
deleteWhenPriceBelowMinimum: enabled,
compareField: 'price',
thresholdField: 'minimumPrice',
target: 'skip_asin',
source: 'frontend-price-track',
}
}
function removeMatchedRowsLocally(rows: Array<{ shopName?: string; shopId?: number | string | null }>) {
if (!rows.length) return
const keys = new Set(rows.map((row) => rowKeyForMatch(row)))
@@ -903,6 +921,9 @@ async function pushToPythonQueueLegacy() {
// 店铺简要信息(用于展示)
shop_names: matchedRows.map((i) => i.shopName).filter(Boolean),
country_codes: [...orderedCountryCodes.value],
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)
@@ -930,6 +951,9 @@ function buildQueuePayload(taskVo: PriceTrackCreateTaskVo, row: PriceTrackShopQu
const taskItem = taskVo.items?.[0]
const shopName = (row.shopName || '').trim()
const skipAsinsByCountry = row.skipAsins || taskVo.skipAsinsByCountry || {}
const skipAsinDetailsByCountry = taskVo.skipAsinDetailsByCountry || {}
const minimumPriceByCountryAndAsin = taskVo.minimumPriceByCountryAndAsin || {}
const skipAsinDeletePolicy = buildSkipAsinDeletePolicy()
const resultId = taskItem?.resultId ?? null
const loopRunId = taskItem?.loopRunId ?? null
const roundIndex = taskItem?.roundIndex ?? null
@@ -963,6 +987,11 @@ function buildQueuePayload(taskVo: PriceTrackCreateTaskVo, row: PriceTrackShopQu
mode: statusModeEnabled.value ? 'status' : 'asin',
skip_asins: skipAsinsByCountry,
skip_asins_by_country: skipAsinsByCountry,
skip_asin_details_by_country: skipAsinDetailsByCountry,
minimum_price_by_country_and_asin: minimumPriceByCountryAndAsin,
skip_asin_delete_policy: skipAsinDeletePolicy,
delete_skip_asin_when_price_below_minimum: skipAsinDeletePolicy.deleteWhenPriceBelowMinimum,
deleteSkipAsinWhenPriceBelowMinimum: skipAsinDeletePolicy.deleteWhenPriceBelowMinimum,
asin_rows_by_country: Object.keys(matchAsinRowsByCountry.value).length
? matchAsinRowsByCountry.value
: (taskVo.asinRowsByCountry || {}),
@@ -1039,6 +1068,18 @@ async function waitForTaskTerminal(taskId: number) {
}
async function pushToPythonQueue() {
autoQueueEnabled.value = true
await processMatchedQueue()
}
function nextMatchedQueueItem() {
return matchedItems.value.find((item) => item.matched)
}
async function processMatchedQueue() {
if (queueWorkerRunning.value) {
return
}
if (!hasValidMode.value) {
ElMessage.warning('请至少选择一个跟价模式')
return
@@ -1057,13 +1098,19 @@ async function pushToPythonQueue() {
ElMessage.warning('无可用匹配店铺')
return
}
queueWorkerRunning.value = true
pushing.value = true
queuePayloadText.value = ''
let successCount = 0
let failedCount = 0
try {
for (let index = 0; index < matchedRows.length; index += 1) {
const row = matchedRows[index]
let index = 0
while (autoQueueEnabled.value) {
const row = nextMatchedQueueItem()
if (!row) {
break
}
index += 1
try {
let attempt = 0
const taskVo = await (async () => {
@@ -1107,37 +1154,38 @@ async function pushToPythonQueue() {
failedCount += 1
queuePushResult.value = '任务 ' + taskVo.taskId + ' 推送失败,已自动继续下一条:' + (pushResult?.error || '未知错误')
ElMessage.error(queuePushResult.value)
removeMatchedRowsLocally([row])
continue
}
addPollingTask(taskVo.taskId)
scheduleNextPoll(true)
removeMatchedRowsLocally([row])
queuePushResult.value = matchedRows.length > 1
? '任务 ' + taskVo.taskId + ' 已入队,等待完成后继续下一条(' + (index + 1) + '/' + matchedRows.length + ''
: '任务 ' + taskVo.taskId + ' 已入队,等待执行完成'
queuePushResult.value = '任务 ' + taskVo.taskId + ' 已入队,等待执行完成'
const finalStatus = await waitForTaskTerminal(taskVo.taskId)
if (finalStatus !== 'SUCCESS') {
failedCount += 1
queuePushResult.value = '任务 ' + taskVo.taskId + ' 执行失败,已自动继续下一条' + (index + 1) + '/' + matchedRows.length + ''
queuePushResult.value = '任务 ' + taskVo.taskId + ' 执行失败,已自动继续下一条'
ElMessage.error(queuePushResult.value)
continue
}
successCount += 1
queuePushResult.value = index + 1 < matchedRows.length
? '任务 ' + taskVo.taskId + ' 已完成,继续推送下一条(' + (index + 1) + '/' + matchedRows.length + ''
: '任务 ' + taskVo.taskId + ' 已完成'
queuePushResult.value = '任务 ' + taskVo.taskId + ' 已完成'
} catch (error) {
failedCount += 1
queuePushResult.value = '第 ' + (index + 1) + '/' + matchedRows.length + ' 条任务处理失败,已自动继续下一条:' + (error instanceof Error ? error.message : '未知错误')
queuePushResult.value = '第 ' + index + ' 条任务处理失败,已自动继续下一条:' + (error instanceof Error ? error.message : '未知错误')
ElMessage.error(queuePushResult.value)
removeMatchedRowsLocally([row])
continue
}
}
ElMessage.success('店铺任务推送已完成:成功 ' + successCount + ' 条,失败 ' + failedCount + ' 条')
if (successCount > 0 || failedCount > 0) {
ElMessage.success('店铺任务推送已完成:成功 ' + successCount + ' 条,失败 ' + failedCount + ' 条')
}
} catch (e) {
queuePushResult.value = e instanceof Error ? e.message : '推送异常'
ElMessage.error(queuePushResult.value)
} finally {
queueWorkerRunning.value = false
pushing.value = false
}
}