diff --git a/Feed.md b/Feed.md new file mode 100644 index 0000000..6c92297 --- /dev/null +++ b/Feed.md @@ -0,0 +1,86 @@ +# Фид товаров + +## Обзор + +YML-фид каталога товаров загружается с URL из настроек (`feedUrl`). Данные используются для подстановки в Mindbox-теги при рендере превью. + +## Загрузка фида + +Функция `_fetchFeedProducts(feedUrl)`: + +1. Проверка `isPublicUrl(feedUrl)` — блокирует localhost, private IP, IPv6, hex IP +2. HTTP GET с таймаутом 90 сек, User-Agent: `AspekterVA-FeedReader/1.0` +3. Автодетект кодировки windows-1251 из Content-Type или XML declaration +4. Парсинг regex-ом (не DOM) — извлекает `` элементы + +## Кэширование + +### В памяти +- `feedCache` — Map `{url: {ts, products: Map}}` +- TTL: 3 часа (`FEED_CACHE_TTL = 3 * 60 * 60 * 1000`) + +### На диске +- `data/feed-cache.json` — `{url, ts, products: {id: {...}}}` +- Восстанавливается при старте сервера + +### Дедупликация запросов +- `feedPending` — Map текущих in-flight запросов +- Если запрос уже выполняется, новый подключается к Promise +- Stale timeout: 2 минуты (если Promise завис) + +## Поля товара из фида + +```javascript +{ + id, available, // идентификатор и наличие + name, price, oldPrice, // название, цены + image, url, // картинка и ссылка + description, // описание + categoryId, vendor, // категория и бренд + typePrefix, model, // тип товара и модель + gender, color, // гендер и цвет + discountPercent, // процент скидки + // Кастомные поля (из param): + denomination, year, dia, material, country, + condition, weight, assay, vendorCode, + reverseImage, salePercent, series +} +``` + +## API эндпоинты фида + +### POST /api/project/:name/feed-refresh +Принудительная перезагрузка фида: +- Очищает кэш +- Загружает заново +- Возвращает diff: `{added, removed, total}` + +### POST /api/project/:name/feed-lookup +Поиск товаров по ID: +- Body: `{ids: ["123", "456"]}` +- Возвращает: `{found: [{id, name, price, ...}], notFound: ["789"]}` + +### POST /api/project/:name/feed-suggest +Замена недоступного товара: +- Body: `{productId: "123", limit: 10}` +- Алгоритм скоринга: + | Фактор | Баллы | + |--------|-------| + | Тот же бренд (vendor) | +25 | + | Тот же тип (typePrefix) | +20 | + | Та же категория (categoryId) | +15 | + | Тот же гендер | +10 | + | Близкая цена | 0-10 | + | Тот же цвет | +5 | +- Исключаются товары с ценой >3x от оригинала +- Возвращает топ-N по скору + +## "Нет в наличии" + +При рендере: +1. Mindbox-теги подставляются из фида +2. Если товар `available === false` → на картинку накладывается CSS-оверлей +3. Список `unavailableProducts[]` возвращается клиенту +4. В UI показывается warning: "⚠ Нет в наличии (N) · фид DD.MM HH:MM" +5. Клик раскрывает список с кнопками "Заменить" для каждого товара +6. "Заменить" → показывает модал с рекомендациями из `feed-suggest`