完成查询asin开发

This commit is contained in:
super
2026-04-23 20:48:57 +08:00
parent 72fdb1841c
commit 326f78705d
95 changed files with 6009 additions and 50 deletions

View File

@@ -0,0 +1,13 @@
<!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/query-asin-main.ts"></script>
</body>
</html>

View File

@@ -620,6 +620,11 @@ function clearActiveQueueTask() {
saveQueueState();
}
function isRecordMissingError(error: unknown) {
const message = error instanceof Error ? error.message : String(error || "");
return /记录不存在|任务不存在|不存在|已删除|not\s*found|404/i.test(message);
}
function removeMatchedRowLocally(row: PatrolDeleteShopQueueItem) {
matchedItems.value = matchedItems.value.filter(
(item) => rowKeyForMatch(item) !== rowKeyForMatch(row),
@@ -627,6 +632,14 @@ function removeMatchedRowLocally(row: PatrolDeleteShopQueueItem) {
saveMatchedItems();
}
function removeHistoryItemLocally(item: PatrolDeleteHistoryItem) {
historyItems.value = historyItems.value.filter((row) => {
if (item.resultId != null && row.resultId === item.resultId) return false;
if (item.taskId != null && row.taskId === item.taskId) return false;
return true;
});
}
function mergeQueueItems(
base: PatrolDeleteShopQueueItem[],
incoming: PatrolDeleteShopQueueItem[],
@@ -1013,6 +1026,15 @@ async function deleteTaskRecord(item: PatrolDeleteHistoryItem) {
await Promise.all([loadCandidates(), refreshTaskViews()]);
ElMessage.success("已删除");
} catch (error) {
if (isRecordMissingError(error)) {
if (item.taskId && activeTaskId.value === item.taskId) {
clearActiveQueueTask();
}
removeHistoryItemLocally(item);
await loadCandidates();
ElMessage.success("后端记录已不存在,已同步移除本地记录");
return;
}
ElMessage.error(error instanceof Error ? error.message : "删除失败");
}
}

View File

@@ -818,6 +818,11 @@ function rowKeyForMatch(row: { shopName?: string; shopId?: number | string | nul
return `${(row.shopName || '').trim()}\u0001${row.shopId ?? ''}`
}
function isRecordMissingError(error: unknown) {
const message = error instanceof Error ? error.message : String(error || '')
return /记录不存在|不存在|已删除|not\s*found|404/i.test(message)
}
function buildSkipAsinDeletePolicy() {
const enabled = statusModeEnabled.value && !asinModeEnabled.value
return {
@@ -843,6 +848,14 @@ function removeMatchedRowsLocally(rows: Array<{ shopName?: string; shopId?: numb
saveMatchedItemsToStorage()
}
function removeHistoryItemLocally(item: PriceTrackHistoryItem) {
historyItems.value = historyItems.value.filter((row) => {
if (item.resultId != null && row.resultId === item.resultId) return false
if (item.taskId != null && row.taskId === item.taskId) return false
return true
})
}
async function removeMatchedRow(row: PriceTrackShopQueueItem) {
const backup = [...matchedItems.value]
removeMatchedRowsLocally([row])
@@ -860,6 +873,13 @@ async function removeMatchedRow(row: PriceTrackShopQueueItem) {
}
ElMessage.success('已从匹配结果中移除')
} catch (e) {
if (isRecordMissingError(e)) {
await loadHistory()
await loadDashboard()
syncPollingIdsWithHistory()
ElMessage.success('后端记录已不存在,已同步移除本地记录')
return
}
matchedItems.value = backup
saveMatchedItemsToStorage()
ElMessage.error(e instanceof Error ? e.message : '同步后端失败')
@@ -1587,6 +1607,15 @@ async function deleteTaskRecord(item: PriceTrackHistoryItem) {
syncPollingIdsWithHistory()
ElMessage.success('已删除')
} catch (e) {
if (isRecordMissingError(e)) {
if (item.taskId != null) removePollingTask(item.taskId)
removeMatchedRowsLocally([item])
removeHistoryItemLocally(item)
await loadDashboard()
syncPollingIdsWithHistory()
ElMessage.success('后端记录已不存在,已同步移除本地记录')
return
}
ElMessage.error(e instanceof Error ? e.message : '删除失败')
}
}

View File

@@ -469,6 +469,11 @@ function rowKeyForMatch(row: { shopName?: string; shopId?: number | string | nul
return `${name}\u0001${id}`
}
function isRecordMissingError(error: unknown) {
const message = error instanceof Error ? error.message : String(error || '')
return /记录不存在|任务不存在|不存在|已删除|not\s*found|404/i.test(message)
}
function removeMatchedRowsLocally(rows: Array<{ shopName?: string; shopId?: number | string | null }>) {
if (!rows.length) return
const keys = new Set(rows.map((row) => rowKeyForMatch(row)))
@@ -476,6 +481,14 @@ function removeMatchedRowsLocally(rows: Array<{ shopName?: string; shopId?: numb
saveMatchedItemsToStorage()
}
function removeHistoryItemLocally(item: ProductRiskHistoryItem) {
historyItems.value = historyItems.value.filter((row) => {
if (item.resultId != null && row.resultId === item.resultId) return false
if (item.taskId != null && row.taskId === item.taskId) return false
return true
})
}
async function removeMatchedRow(row: ProductRiskShopQueueItem) {
const key = rowKeyForMatch(row)
const backup = [...matchedItems.value]
@@ -495,6 +508,13 @@ async function removeMatchedRow(row: ProductRiskShopQueueItem) {
}
ElMessage.success('已从匹配结果中移除')
} catch (e) {
if (isRecordMissingError(e)) {
await loadHistory()
await loadDashboard()
syncPollingIdsWithHistory()
ElMessage.success('后端记录已不存在,已同步移除本地记录')
return
}
matchedItems.value = backup
saveMatchedItemsToStorage()
ElMessage.error(e instanceof Error ? e.message : '同步后端失败')
@@ -530,6 +550,17 @@ async function deleteTaskRecord(item: ProductRiskHistoryItem) {
syncPollingIdsWithHistory()
ElMessage.success('已删除')
} catch (e) {
if (isRecordMissingError(e)) {
if (item.taskId != null && item.taskId > 0) {
removePollingTask(item.taskId)
}
removeMatchedRowsLocally([item])
removeHistoryItemLocally(item)
await loadDashboard()
syncPollingIdsWithHistory()
ElMessage.success('后端记录已不存在,已同步移除本地记录')
return
}
ElMessage.error(e instanceof Error ? e.message : '删除失败')
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -191,7 +191,7 @@ function loadTaskSnapshotsFromStorage() { try { const raw = window.localStorage.
function saveTaskSnapshotsToStorage() { setStorageJson(taskSnapshotsStorageKey(), taskSnapshots.value, Object.keys(taskSnapshots.value).length === 0) }
function loadMatchedItemsFromStorage() { try { const raw = window.localStorage.getItem(matchedItemsStorageKey()); matchedItems.value = raw ? JSON.parse(raw) : [] } catch { matchedItems.value = [] } }
function saveMatchedItemsToStorage() { setStorageJson(matchedItemsStorageKey(), matchedItems.value, matchedItems.value.length === 0) }
function isTaskMissingError(error: unknown) { const message = error instanceof Error ? error.message : String(error || ''); return message.includes('任务不存在') }
function isTaskMissingError(error: unknown) { const message = error instanceof Error ? error.message : String(error || ''); return /记录不存在|任务不存在|不存在|已删除|not\s*found|404/i.test(message) }
function syncPollingIdsWithTaskState() { const nextIds = pollingTaskIds.value.filter((taskId) => (taskDetails.value[taskId] || taskSnapshots.value[taskId]?.task?.status) === 'RUNNING'); if (nextIds.length === pollingTaskIds.value.length) return; pollingTaskIds.value = nextIds; savePollingIds() }
function addPollingTask(taskId: number) { if (!pollingTaskIds.value.includes(taskId)) { pollingTaskIds.value = [...pollingTaskIds.value, taskId]; savePollingIds() } }
function clearDispatchTimer(taskId: number) { const timer = dispatchTimers.get(taskId); if (timer) { window.clearTimeout(timer); dispatchTimers.delete(taskId) } }
@@ -334,10 +334,15 @@ async function deleteTaskRecord(item: ShopMatchHistoryItem) {
await Promise.allSettled([loadHistory(), loadCandidates(), loadDashboard()])
ElMessage.success('删除成功')
} catch (error) {
if (taskId > 0 && isTaskMissingError(error)) {
removeTaskLocally(taskId)
if (isTaskMissingError(error)) {
if (taskId > 0) {
removeTaskLocally(taskId)
} else if (resultId > 0) {
historyItems.value = historyItems.value.filter((row) => Number(row.resultId || 0) !== resultId)
}
removeMatchedRowsLocally([item])
await Promise.allSettled([loadHistory(), loadCandidates(), loadDashboard()])
ElMessage.success('任务已在服务端删除,本地记录已同步清理')
ElMessage.success('后端记录已不存在,本地记录已同步清理')
return
}
ElMessage.error(error instanceof Error ? error.message : '删除失败')

View File

@@ -53,6 +53,7 @@ type ActiveNavKey =
| 'shop-match'
| 'pricing'
| 'patrol-delete'
| 'query-asin'
type NavItem = {
key: string
@@ -95,6 +96,7 @@ const navGroups: ReadonlyArray<NavGroup> = [
{ key: 'shop-match', label: '定时匹配', href: '/new_web_source/shop-match.html' },
{ key: 'pricing', label: '跟价', href: '/new_web_source/price-track.html' },
{ 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: 'shop-status', label: '店铺状态查询' },
],

View File

@@ -0,0 +1,13 @@
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 BrandQueryAsinTab from '@/pages/brand/components/BrandQueryAsinTab.vue'
dayjs.locale('zh-cn')
createApp(BrandQueryAsinTab).use(ElementPlus, { locale: zhCn }).mount('#app')

View File

@@ -581,6 +581,7 @@ export interface ProductRiskShopQueueItem {
matchedUserId?: number;
matchStatus?: string;
matchMessage?: string;
queryAsins?: QueryAsinCountryAsins[];
}
export interface ProductRiskMatchShopsVo {
@@ -1189,6 +1190,165 @@ export function deletePatrolDeleteHistory(resultId: number) {
);
}
export type QueryAsinCandidateVo = ProductRiskCandidateVo;
export type QueryAsinDashboardVo = ProductRiskDashboardVo;
export type QueryAsinShopQueueItem = ProductRiskShopQueueItem;
export interface QueryAsinCountryAsins {
country: string;
asins: string[];
}
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;
queryAsins: QueryAsinCountryAsins[];
}
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<JavaApiResponse<QueryAsinCandidateVo[]>>(`${JAVA_API_PREFIX}/query-asin/candidates`, {
params: { user_id: getCurrentUserId() },
}),
);
}
export function addQueryAsinCandidate(shopName: string) {
return unwrapJavaResponse(
post<JavaApiResponse<QueryAsinCandidateVo>, { 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<JavaApiResponse<null>>(`${JAVA_API_PREFIX}/query-asin/candidates/${id}`, {
params: { user_id: getCurrentUserId() },
}),
);
}
export function matchQueryAsinShops(shopNames: string[]) {
return unwrapJavaResponse(
post<JavaApiResponse<ProductRiskMatchShopsVo>, { 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<JavaApiResponse<QueryAsinDashboardVo>>(`${JAVA_API_PREFIX}/query-asin/dashboard`, {
params: { user_id: getCurrentUserId() },
}),
);
}
export function getQueryAsinHistory() {
return unwrapJavaResponse(
get<JavaApiResponse<QueryAsinHistoryVo>>(`${JAVA_API_PREFIX}/query-asin/history`, {
params: { user_id: getCurrentUserId() },
}),
);
}
export function getQueryAsinTaskProgressBatch(taskIds: number[]) {
return postTaskProgressBatch<QueryAsinTaskBatchVo>(
`${JAVA_API_PREFIX}/query-asin/tasks/progress/batch`,
taskIds,
);
}
export function createQueryAsinTask(items: QueryAsinTaskItem[]) {
return unwrapJavaResponse(
post<JavaApiResponse<QueryAsinCreateTaskVo>, { 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;
queryAsins: QueryAsinCountryAsins[];
shopDone?: boolean;
submissionId?: string;
chunkIndex?: number;
chunkTotal?: number;
}>;
},
) {
return unwrapJavaResponse(
post<JavaApiResponse<null>, 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<JavaApiResponse<null>>(`${JAVA_API_PREFIX}/query-asin/tasks/${taskId}`, {
params: { user_id: getCurrentUserId() },
}),
);
}
export function deleteQueryAsinHistory(resultId: number) {
return unwrapJavaResponse(
del<JavaApiResponse<null>>(`${JAVA_API_PREFIX}/query-asin/history/${resultId}`, {
params: { user_id: getCurrentUserId() },
}),
);
}
// ========== 跟价 ==========
export interface PriceTrackCandidateVo {

View File

@@ -50,6 +50,7 @@ export default defineConfig({
'shop-match': resolve(__dirname, 'shop-match.html'),
'price-track': resolve(__dirname, 'price-track.html'),
'patrol-delete': resolve(__dirname, 'patrol-delete.html'),
'query-asin': resolve(__dirname, 'query-asin.html'),
},
output: {
entryFileNames: 'assets/[name].js',