catalog prices fix

This commit is contained in:
54CHA
2025-07-17 21:22:20 +03:00
parent f5e8b4240c
commit 7fc55ab9c3
3 changed files with 97 additions and 3 deletions

View File

@ -3064,8 +3064,102 @@ export const resolvers = {
}
console.log('✅ Получены товары каталога:', entities.list.length)
console.log('🔍 Начинаем серверную фильтрацию по ценам...')
return entities
// Глобальный кэш для результатов проверки цен (персистентный между запросами)
if (!global.priceCache) {
global.priceCache = new Map<string, { hasPrice: boolean, timestamp: number }>()
}
const priceCache = global.priceCache as Map<string, { hasPrice: boolean, timestamp: number }>
const CACHE_TTL = 5 * 60 * 1000 // 5 минут
const getCachedPriceResult = (code: string, brand: string): boolean | null => {
const key = `${code}_${brand}`
const cached = priceCache.get(key)
if (cached && (Date.now() - cached.timestamp) < CACHE_TTL) {
return cached.hasPrice
}
return null
}
const cachePriceResult = (code: string, brand: string, hasPrice: boolean): void => {
const key = `${code}_${brand}`
priceCache.set(key, { hasPrice, timestamp: Date.now() })
}
// Фильтруем товары на сервере - проверяем наличие цен в AutoEuro
const filteredEntities: any[] = []
const batchSize = 20 // Увеличенный размер батча для скорости
for (let i = 0; i < entities.list.length; i += batchSize) {
const batch = entities.list.slice(i, i + batchSize)
// Проверяем цены для каждого товара в батче параллельно
const priceCheckPromises = batch.map(async (entity) => {
try {
// Сначала проверяем кэш
const cachedResult = getCachedPriceResult(entity.code, entity.brand.name);
if (cachedResult !== null) {
if (cachedResult) {
console.log(`💨 Кэш: товар ${entity.code} (${entity.brand.name}) имеет цену`);
return entity;
} else {
console.log(`💨 Кэш: товар ${entity.code} (${entity.brand.name}) не имеет цены`);
return null;
}
}
const searchResult = await autoEuroService.searchItems({
code: entity.code,
brand: entity.brand.name,
with_crosses: false,
with_offers: true
})
// Проверяем есть ли предложения с валидной ценой
const hasValidPrice: boolean = Boolean(searchResult.success &&
searchResult.data &&
searchResult.data.length > 0 &&
searchResult.data.some(offer =>
offer.price &&
parseFloat(offer.price.toString()) > 0
))
// Кэшируем результат
cachePriceResult(entity.code, entity.brand.name, hasValidPrice);
if (hasValidPrice) {
console.log(`✅ Товар ${entity.code} (${entity.brand.name}) имеет цену`);
return entity;
} else {
console.log(`❌ Товар ${entity.code} (${entity.brand.name}) не имеет цены`);
return null;
}
} catch (error) {
console.error(`❌ Ошибка проверки цены для ${entity.code}:`, error);
return null // Исключаем товары с ошибками
}
})
// Ждем результаты для текущего батча
const batchResults = await Promise.all(priceCheckPromises)
// Добавляем только товары с ценами
filteredEntities.push(...batchResults.filter(entity => entity !== null))
// Убираем задержку между батчами для максимальной скорости
// if (i + batchSize < entities.list.length) {
// await new Promise(resolve => setTimeout(resolve, 50))
// }
}
console.log(`✅ Серверная фильтрация завершена. Товаров с ценами: ${filteredEntities.length} из ${entities.list.length}`)
// Возвращаем отфильтрованный результат
return {
...entities,
list: filteredEntities
}
} catch (error) {
console.error('❌ Ошибка в GraphQL resolver partsIndexCatalogEntities:', error)
throw new Error('Не удалось получить товары каталога')

View File

@ -37,7 +37,7 @@ class PartsAPIService {
private mediaApiKey: string;
constructor() {
this.baseURL = 'https://api.partsapi.ru';
this.baseURL = process.env.PARTSAPI_URL || 'https://api.partsapi.ru';
// Получаем ключи API из переменных окружения
this.categoriesApiKey = process.env.PARTSAPI_CATEGORIES_KEY || '';

View File

@ -155,7 +155,7 @@ interface CacheEntry<T> {
}
class PartsIndexService {
private baseURL = 'https://api.parts-index.com/v1';
private baseURL = process.env.PARTSAPI_URL+"/v1" || 'https://api.parts-index.com/v1';
private apiKey = 'PI-E1C0ADB7-E4A8-4960-94A0-4D9C0A074DAE';
// Простой in-memory кэш