WB stats reliability: fix client apply-after-refetch, normalize ad dates, and add cache fallback\n\n- SalesTab: apply data immediately after refetch success to avoid empty state\n- Service: normalize advertising day dates to YYYY-MM-DD for correct range checks\n- Resolver: fallback to cached advertisingData when productsData is missing (429)\n\nHelps show data even when WB API rate-limits and fixes mixed-date aggregation.
This commit is contained in:
@ -232,7 +232,11 @@ const SalesTab = React.memo(({
|
|||||||
}
|
}
|
||||||
if (result.data?.getWildberriesStatistics?.success) {
|
if (result.data?.getWildberriesStatistics?.success) {
|
||||||
console.warn('Sales: Loading fresh data from API')
|
console.warn('Sales: Loading fresh data from API')
|
||||||
// Обрабатываем данные в существующем useEffect
|
const rows = result.data.getWildberriesStatistics.data || []
|
||||||
|
if (rows.length > 0) {
|
||||||
|
// Применяем данные сразу, не дожидаясь обновления wbData
|
||||||
|
applyData(rows)
|
||||||
|
}
|
||||||
lastLoadedKeyRef.current = loadKey
|
lastLoadedKeyRef.current = loadKey
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -8224,6 +8224,42 @@ const wildberriesQueries = {
|
|||||||
message: 'Данные возвращены из кеша из-за ошибки WB API',
|
message: 'Данные возвращены из кеша из-за ошибки WB API',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (cache?.advertisingData) {
|
||||||
|
// Fallback №2: если нет productsData, но есть advertisingData —
|
||||||
|
// формируем минимальный набор данных по дням на основе затрат на рекламу
|
||||||
|
try {
|
||||||
|
const adv = JSON.parse(cache.advertisingData as unknown as string) as {
|
||||||
|
dailyData?: Array<{
|
||||||
|
date: string
|
||||||
|
totalSum?: number
|
||||||
|
totalOrders?: number
|
||||||
|
totalRevenue?: number
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
const daily = adv.dailyData ?? []
|
||||||
|
const dataFromAdv = daily.map((d) => ({
|
||||||
|
date: d.date,
|
||||||
|
sales: 0,
|
||||||
|
orders: typeof d.totalOrders === 'number' ? d.totalOrders : 0,
|
||||||
|
advertising: typeof d.totalSum === 'number' ? d.totalSum : 0,
|
||||||
|
refusals: 0,
|
||||||
|
returns: 0,
|
||||||
|
revenue: typeof d.totalRevenue === 'number' ? d.totalRevenue : 0,
|
||||||
|
buyoutPercentage: 0,
|
||||||
|
}))
|
||||||
|
|
||||||
|
if (dataFromAdv.length > 0) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: dataFromAdv,
|
||||||
|
message:
|
||||||
|
'Данные по продажам недоступны из-за ошибки WB API. Показаны данные по рекламе из кеша.',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (parseErr) {
|
||||||
|
console.error('Failed to parse advertisingData from cache:', parseErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (fallbackErr) {
|
} catch (fallbackErr) {
|
||||||
@ -8980,6 +9016,17 @@ resolvers.Query = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Если кеш просрочен — не используем его, как и для склада WB (сервер решает, годен ли кеш)
|
||||||
|
const now = new Date()
|
||||||
|
if (cache.expiresAt && cache.expiresAt <= now) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'Кеш устарел, требуется загрузка из API',
|
||||||
|
cache: null,
|
||||||
|
fromCache: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Данные получены из кеша',
|
message: 'Данные получены из кеша',
|
||||||
@ -8990,6 +9037,8 @@ resolvers.Query = {
|
|||||||
dateTo: cache.dateTo ? cache.dateTo.toISOString().split('T')[0] : null,
|
dateTo: cache.dateTo ? cache.dateTo.toISOString().split('T')[0] : null,
|
||||||
productsTotalSales: cache.productsTotalSales ? Number(cache.productsTotalSales) : null,
|
productsTotalSales: cache.productsTotalSales ? Number(cache.productsTotalSales) : null,
|
||||||
advertisingTotalCost: cache.advertisingTotalCost ? Number(cache.advertisingTotalCost) : null,
|
advertisingTotalCost: cache.advertisingTotalCost ? Number(cache.advertisingTotalCost) : null,
|
||||||
|
// Возвращаем expiresAt в ISO, чтобы клиент корректно парсил дату
|
||||||
|
expiresAt: cache.expiresAt.toISOString(),
|
||||||
createdAt: cache.createdAt.toISOString(),
|
createdAt: cache.createdAt.toISOString(),
|
||||||
updatedAt: cache.updatedAt.toISOString(),
|
updatedAt: cache.updatedAt.toISOString(),
|
||||||
},
|
},
|
||||||
|
@ -851,7 +851,8 @@ class WildberriesService {
|
|||||||
// Обрабатываем статистику по дням для каждой кампании
|
// Обрабатываем статистику по дням для каждой кампании
|
||||||
if (advertStat.days && advertStat.days.length > 0) {
|
if (advertStat.days && advertStat.days.length > 0) {
|
||||||
advertStat.days.forEach((day) => {
|
advertStat.days.forEach((day) => {
|
||||||
const date = day.date
|
// Нормализуем дату рекламы до формата YYYY-MM-DD, чтобы совпадала с продажами/заказами
|
||||||
|
const date = (day.date.includes('T') ? day.date.split('T')[0] : day.date.split(' ')[0] || day.date) as string
|
||||||
|
|
||||||
console.warn(`WB API: Day ${date} - spent ${day.sum} rubles (campaign ${advertStat.advertId})`)
|
console.warn(`WB API: Day ${date} - spent ${day.sum} rubles (campaign ${advertStat.advertId})`)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user