# MASTER-ТЗ ДЛЯ CLAUDE CODE

# Маркетплейс TEEU

Ты выступаешь как senior Laravel architect, senior PHP developer, database architect, security engineer и frontend engineer.

Нужно разработать полноценный современный маркетплейс **teeu**.

Это не демо, не прототип и не набор статических страниц. Нужен реальный расширяемый production-ready проект с корректной архитектурой, миграциями, бизнес-логикой, очередями, безопасностью, тестами, SEO и документацией.

---

# 0. ГЛАВНОЕ ПРАВИЛО РАБОТЫ

Не начинай разработку вслепую.

Перед написанием основной функциональности обязательно:

1. Проанализируй текущий проект.
2. Проанализируй дизайн.
3. Проанализируй референсный проект Placeo.
4. Составь краткий технический аудит.
5. Только после этого начинай реализацию.

Не переписывай уже существующую работающую архитектуру без необходимости.

Не создавай второй параллельный механизм, если аналогичный качественный механизм уже есть в проекте или может быть аккуратно адаптирован из Placeo.

Не копируй код Placeo вслепую. Сначала пойми:

* зависимости;
* модели;
* middleware;
* contracts;
* services;
* routes;
* jobs;
* frontend-зависимости;
* формат данных;
* возможную привязку к доменной модели Placeo.

---

# 1. ИСХОДНЫЕ ДАННЫЕ

## 1.1. Название проекта

Маркетплейс:

**teeu**

---

## 1.2. Основной стек

Целевая конфигурация нового проекта:

* Laravel 13;
* PHP 8.3+;
* MySQL 8+ или MariaDB;
* исключительно InnoDB;
* UTF8MB4;
* Vite;
* server-side rendered frontend для публичных SEO-страниц;
* Blade как безопасная базовая технология публичного frontend;
* JavaScript только там, где он нужен;
* допускается Alpine.js, если он уже используется в проекте или оправдан UI;
* не превращать публичный каталог в SPA;
* очереди Laravel;
* Laravel Scheduler;
* Laravel Notifications.

Если фактическое окружение поддерживает только PHP 8.2:

* не ломать окружение;
* использовать Laravel 12;
* зафиксировать решение в `docs/architecture.md`.

Redis не считать обязательной частью исходного стека.

Проект должен уметь работать:

* с database queue;
* с database/cache-compatible инфраструктурой.

Но архитектура очередей должна позволять позднее переключиться на:

* Redis;
* Laravel Horizon.

Не привязывать бизнес-логику напрямую к Redis.

---

# 2. ОБЯЗАТЕЛЬНЫЙ АУДИТ ПЕРЕД РАЗРАБОТКОЙ

## 2.1. Дизайн

Наработки дизайна находятся:

`/tmp/design`

Нужно:

1. Рекурсивно просмотреть папку.
2. Найти:

   * логотипы;
   * SVG;
   * PNG/WebP;
   * шрифты;
   * CSS;
   * HTML;
   * UI-kit;
   * макеты;
   * компоненты;
   * цветовые переменные;
   * иконки;
   * состояния элементов.
3. Определить дизайн-систему:

   * основные цвета;
   * акцентные цвета;
   * типографику;
   * border-radius;
   * тени;
   * размеры контейнеров;
   * сетку;
   * кнопки;
   * формы;
   * карточки;
   * мобильную навигацию.
4. Не придумывать новый визуальный стиль, если стиль уже определён.
5. Не заменять существующий логотип.
6. Не уничтожать исходники `/tmp/design`.

Создать:

`docs/design-inventory.md`

Зафиксировать:

* найденные файлы;
* используемые компоненты;
* палитру;
* типографику;
* решения по адаптации дизайна.

---

## 2.2. Референсный проект Placeo

Референс:

`D:\laragon\www\placeo`

Если Claude Code работает через WSL/Linux, проверить также:

`/mnt/d/laragon/www/placeo`

В Placeo уже работают:

* авторизация через Яндекс;
* авторизация через дозвон;
* механизм чатов;
* геотаргетинг;
* OpenStreetMap;
* нейромодерация:

  * отзывов;
  * описания продавца;
  * карточек объявлений.

Нужно сначала найти реальные реализации этих механизмов.

Проверить:

* routes;
* controllers;
* services;
* models;
* migrations;
* jobs;
* commands;
* JS;
* Blade;
* API clients;
* configuration;
* env variables;
* moderation prompts;
* chat transport;
* authentication callbacks;
* geocoding;
* city storage;
* authorization policies.

Создать:

`docs/placeo-reference-audit.md`

Для каждого механизма написать:

* где найден;
* какие файлы участвуют;
* можно ли переиспользовать;
* что можно адаптировать;
* что нельзя копировать;
* какие зависимости потребуются.

Особенно тщательно исследовать:

1. Яндекс OAuth.
2. Дозвон.
3. Чаты.
4. Геотаргетинг.
5. OpenStreetMap.
6. Нейромодерацию.

Не начинать параллельную реализацию этих функций до окончания аудита.

Если путь Placeo недоступен:

* не останавливать весь проект;
* зафиксировать это в документации;
* реализовать механизмы через abstractions/interfaces, чтобы позднее можно было заменить адаптерами Placeo.

---

# 3. ОБЩАЯ АРХИТЕКТУРА TEEU

Система состоит из четырёх визуально и логически разделённых зон:

## 3.1. Публичный маркетплейс

Примерные URL:

* `/`
* `/catalog`
* `/catalog/{categoryPath}`
* `/product/{slug}-{id}`
* `/seller/{slug}`

Здесь находятся:

* главная;
* каталог;
* категории;
* товары;
* карточки продавцов;
* отзывы;
* выбор города;
* авторизация.

---

## 3.2. Кабинет покупателя

Префикс:

`/account`

Разделы:

* профиль;
* корзина;
* избранное;
* заказы;
* переписка;
* уведомления;
* уведомления о снижении цены;
* переход к регистрации продавца;
* переход в кабинет продавца, если seller account уже создан.

---

## 3.3. Кабинет продавца

Использовать отдельный prefix.

По умолчанию:

`/merchant`

Prefix вынести в config/env, например:

`SELLER_PANEL_PREFIX=merchant`

Кабинет продавца должен:

* визуально отличаться от основного сайта;
* иметь отдельный layout;
* иметь собственную минималистичную шапку;
* иметь понятную кнопку возврата на основной teeu;
* не использовать публичную шапку маркетплейса как основную навигацию.

Разделы:

* dashboard;
* товары;
* YML;
* заказы;
* профиль продавца;
* адреса;
* сообщения/диалоги;
* настройки;
* возврат на teeu.

---

## 3.4. Админ-панель разработчика

Использовать отдельный prefix.

Например:

`/developer`

Prefix вынести в config/env:

`ADMIN_PANEL_PREFIX=developer`

Важно:

Секретный slug не является системой безопасности.

Доступ обязательно защищается:

* authentication;
* authorization;
* admin role;
* Policies/Gates;
* аудитом действий.

---

# 4. РОЛИ И ДОСТУП

Предусмотреть:

1. Guest.
2. Buyer/User.
3. Seller owner.
4. Admin/Developer.

Архитектуру продавцов сделать расширяемой.

Даже если в первой версии один пользователь управляет одним продавцом, не привязывать всё через:

`sellers.user_id`

как единственную нерасширяемую связь.

Использовать:

* `sellers`;
* `seller_memberships`.

Например роли:

* owner;
* manager — резерв на будущее.

В первой версии UI управления сотрудниками продавца можно не реализовывать.

Но схема БД не должна блокировать эту возможность.

---

# 5. ПОЛЬЗОВАТЕЛИ И АВТОРИЗАЦИЯ

## 5.1. Главный принцип

В teeu нет обычной регистрации:

* email + password;
* login + password.

Основной уникальный пользовательский идентификатор:

**подтверждённый телефон**.

Телефон хранить в нормализованном формате.

Для РФ:

`+7XXXXXXXXXX`

Перед сохранением:

* удалить пробелы;
* скобки;
* дефисы;
* нормализовать `8XXXXXXXXXX` в `+7XXXXXXXXXX`;
* валидировать.

Не создавать несколько пользователей из-за разного форматирования одного телефона.

---

## 5.2. Методы входа

Поддержать:

1. Яндекс ID OAuth.
2. VK ID — только при возможности надёжно получить телефон.
3. Сбер ID.
4. Авторизация по дозвону с вводом последних 4 символов/цифр номера.

---

## 5.3. Яндекс ID

Сначала изучить реализацию в Placeo.

По возможности адаптировать существующий механизм.

После OAuth:

1. Получить provider user id.
2. Получить разрешённый номер телефона.
3. Нормализовать телефон.
4. Найти существующего пользователя.
5. Если существует:

   * привязать Yandex identity к нему.
6. Если нет:

   * создать пользователя.
7. Не плодить дубли.

---

## 5.4. VK ID

До полноценной реализации выполнить отдельный technical spike.

Цель spike:

Определить, возвращает ли реальная текущая конфигурация VK ID для приложения teeu:

* пригодный номер телефона;
* с достаточной гарантией идентификации;
* в доступном scope;
* после необходимых разрешений приложения.

Правило:

Если подтверждённый телефон не получен:

* не создавать отдельного пользователя только по VK ID;
* не нарушать phone-centric модель аккаунтов.

Допускается UI-сценарий:

«Не удалось получить подтверждённый телефон из VK ID. Используйте другой способ входа».

Всю интеграцию делать через provider adapter.

---

## 5.5. Сбер ID

Сделать отдельный adapter/service.

Учесть:

* OAuth/OIDC flow;
* state;
* PKCE, если требуется фактической схемой;
* callback;
* token validation;
* provider user identifier;
* получение разрешённых user attributes;
* возможную необходимость сертификатов.

Не хранить интеграционную логику в controller.

---

## 5.6. Авторизация по дозвону

Сначала найти рабочий механизм в Placeo.

Использовать его как референс.

Сценарий:

1. Пользователь вводит телефон.
2. Backend инициирует flash-call/dosvon через внешний API.
3. Сервис выполняет звонок.
4. Пользователь видит номер.
5. Пользователь вводит последние 4 символа/цифры номера.
6. Backend проверяет код.
7. Телефон считается подтверждённым.
8. Выполняется login/create account.

Требования:

* cooldown между запросами;
* лимит попыток;
* срок действия challenge;
* защита от brute force;
* IP rate limit;
* phone rate limit;
* challenge нельзя использовать повторно;
* successful challenge закрывается;
* ошибки внешнего API логируются.

---

## 5.7. Объединение OAuth-идентичностей

Создать таблицу типа:

`auth_identities`

Поля:

* id;
* user_id;
* provider;
* provider_user_id;
* provider_phone nullable;
* metadata JSON nullable;
* created_at;
* updated_at.

Unique:

`provider + provider_user_id`

Если телефон OAuth-провайдера совпадает с существующим пользователем:

* не создавать дубль;
* привязать identity к существующему пользователю.

Не хранить access token без необходимости.

Если refresh/access token действительно необходим:

* хранить шифрованно;
* никогда не логировать.

---

# 6. КАБИНЕТ ПРОДАВЦА И РЕГИСТРАЦИЯ ПРОДАВЦА

## 6.1. Общая логика

Обычный пользователь сначала существует как покупатель.

При желании он нажимает:

**Стать продавцом**

После этого заполняет анкету организации.

---

## 6.2. Анкета продавца

Предусмотреть как минимум:

* тип организации;
* публичное название магазина;
* юридическое название;
* ИНН;
* КПП nullable;
* ОГРН/ОГРНИП;
* юридический адрес;
* фактический адрес nullable;
* контактное лицо;
* контактный телефон;
* email продавца;
* согласия с правилами площадки.

Не придумывать автоматическое внешнее KYC, если оно отдельно не подключено.

---

## 6.3. Email продавца

Продавец обязан добавить email и подтвердить его.

Подтверждение:

* код длиной ровно 4 символа;
* отправляется на email.

Рекомендуемый alphabet:

* цифры;
* латинские uppercase;
* исключить визуально неоднозначные символы, например `O/0`, `I/1`, если это не конфликтует с дизайном.

Требования:

* код хранить только в hash;
* TTL по умолчанию 10 минут;
* max attempts по умолчанию 5;
* resend cooldown по умолчанию 60 секунд;
* старый код инвалидируется при выпуске нового;
* success challenge нельзя использовать повторно.

Параметры вынести в config.

Продавец не получает полноценный active status, пока email не подтверждён.

---

## 6.4. Статусы продавца

Использовать Enum.

Например:

* `draft`
* `pending_email`
* `active`
* `suspended`
* `blocked`

Не использовать магические строки по проекту.

---

## 6.5. После активации

Пользователь получает:

* переход в `/merchant`;
* переключатель/ссылку «К кабинету продавца»;
* seller dashboard.

Новый seller вызывает Telegram-уведомление администратору.

---

# 7. ПУБЛИЧНАЯ КАРТОЧКА ПРОДАВЦА

URL:

`/seller/{slug}`

Поля:

* логотип;
* публичное название;
* описание;
* рейтинг;
* количество одобренных отзывов;
* сайт;
* контакты;
* телефон;
* VK;
* Telegram;
* товары продавца;
* отзывы.

---

## 7.1. Описание продавца

Описание обязательно проходит AI-модерацию.

До approval:

* новый текст публично не показывать.

При редактировании уже одобренного текста:

* старый approved текст можно продолжить показывать;
* новая версия получает `pending`;
* после approval заменить старую.

Не создавать ситуацию, когда продавец изменил одну букву и вся публичная карточка исчезла.

---

## 7.2. Контакты продавца

Контакты хранятся структурированно:

* website;
* phone;
* VK;
* Telegram.

Валидировать URL.

Не разрешать произвольный JavaScript URL.

---

## 7.3. Логотип

Обработать изображение:

* проверить реальный MIME;
* исправить EXIF orientation;
* сжать;
* сохранить WebP;
* не увеличивать маленький оригинал;
* удалить опасные/лишние metadata.

---

# 8. YML-ФИДЫ ПРОДАВЦОВ

Это один из ключевых модулей проекта.

Один продавец может иметь:

* 0;
* 1;
* много YML-фидов.

Каждый YML-фид независим.

---

## 8.1. YML source

Продавец добавляет:

* название источника;
* URL YML;
* один или несколько адресов отправки;
* период обновления;
* active/paused state.

Каждый товар обязан помнить:

* что он импортирован;
* seller;
* конкретный YML feed;
* внешний `offer id`.

Unique constraint:

`seller feed + external offer id`

---

## 8.2. Формат

Поддерживать стандартный YML/XML.

Не рассчитывать только на один тип `<offer>`.

Импортёр должен аккуратно читать распространённые поля:

* `id`;
* `available`;
* `name`;
* `model`;
* `vendor`;
* `vendorCode`;
* `url`;
* `price`;
* `oldprice`;
* `currencyId`;
* `categoryId`;
* `description`;
* `picture`;
* `barcode`;
* `param`;
* `weight`;
* `dimensions`;
* прочие безопасные поддерживаемые значения.

Не падать целиком от неизвестного XML element.

Не использовать YML category tree как дерево teeu.

YML category/categoryId допускается:

* сохранить в raw metadata;
* использовать для диагностики;
* при желании использовать как дополнительный текстовый сигнал AI.

Но финальная категория teeu выбирается только из собственного дерева teeu.

---

## 8.3. Потоковый XML parser

Не загружать огромный XML целиком в память.

Использовать потоковый разбор:

* XMLReader или эквивалентный безопасный streaming подход.

Обязательно:

* запрет external entities;
* запрет XXE;
* отсутствие внешних сетевых entity resolution;
* лимиты;
* безопасная обработка malformed XML.

---

## 8.4. Защита URL загрузки YML

YML URL является потенциальным SSRF-вектором.

Разрешать только:

* HTTP;
* HTTPS.

Запретить обращения к:

* localhost;
* `127.0.0.0/8`;
* private IPv4 ranges;
* link-local;
* metadata endpoints;
* внутренним IPv6;
* loopback IPv6;
* Unix socket tricks;
* нестандартным опасным схемам.

Учитывать:

* redirects;
* DNS resolution;
* DNS rebinding.

Ограничить:

* количество redirects;
* connect timeout;
* total timeout;
* максимальный размер файла.

Максимальный размер вынести в config.

Стартовое значение можно принять:

`512 MB`

Но не хардкодить в бизнес-логике.

---

## 8.5. Staging import

Нельзя делать наивно:

1. скачать YML;
2. сразу удалить отсутствующие товары;
3. начать парсить.

Нужен staging process.

Пример:

1. Создать `yml_import_run`.
2. Получить lock на feed.
3. Скачать файл.
4. Проверить базовую валидность.
5. Безопасно распарсить во временную staging-структуру.
6. Убедиться, что parsing завершён.
7. Обработать offers chunks.
8. Классифицировать категории.
9. Запустить moderation.
10. Upsert products.
11. Запустить image jobs.
12. Выполнить finalization.
13. Только после успешного authoritative run определить исчезнувшие offers.
14. Снять lock.

Если файл:

* не скачался;
* оборвался;
* XML повреждён;
* парсинг завершился критической ошибкой;

запрещено массово деактивировать старые товары.

---

## 8.6. Импорт должен быть идемпотентным

Повтор одного и того же YML не должен:

* создавать дубли;
* плодить изображения;
* плодить товары;
* создавать бесконечную price history без изменения цены.

Использовать:

* external id;
* content hash;
* source hash;
* upsert;
* idempotency.

---

## 8.7. Исчезнувшие из YML товары

Если offer отсутствует в новом успешно завершённом authoritative import:

* товар не удалять физически;
* пометить unavailable/source_missing;
* убрать возможность заказа;
* исключить из публичной активной выдачи согласно статусу;
* сохранить историю;
* сохранить старые order references.

Если offer позднее вернулся:

* восстановить availability, если feed и seller активны.

---

## 8.8. Ошибка одного товара

Некорректный offer не должен всегда уничтожать весь импорт.

Нужно:

* записать item-level error;
* пропустить проблемный offer;
* продолжить, если ошибка некритическая;
* импорт пометить `partial`, если есть ошибки.

Критические ошибки parsing всего XML:

* `failed`.

---

## 8.9. История импортов

Для каждого YML feed показывать:

* последний запуск;
* статус;
* начало;
* окончание;
* длительность;
* размер файла;
* найдено offers;
* создано;
* обновлено;
* без изменений;
* деактивировано;
* ошибки;
* AI pending;
* изображения pending.

Статусы run:

* queued;
* downloading;
* parsing;
* processing;
* classifying;
* moderating;
* images_processing;
* completed;
* partial;
* failed.

---

## 8.10. Параллельный импорт

Один и тот же feed не импортировать одновременно.

Использовать distributed/cache/database lock abstraction.

Если previous run активен:

* новый не запускать параллельно;
* записать skipped/locked состояние.

---

# 9. АДРЕСА ОТПРАВКИ YML

У каждого YML feed может быть несколько адресов отправки.

UI:

* карта OpenStreetMap;
* строка поиска;
* autocomplete;
* выбор результата;
* marker;
* возможность скорректировать точку.

Хранить:

* original input;
* normalized/formatted address;
* latitude;
* longitude;
* city_id;
* geocoder provider;
* provider object id nullable;
* status.

---

## 9.1. Выделение города

После сохранения адреса:

1. Не блокировать HTTP request долгой геообработкой.
2. Создать background job.
3. Геокодировать/нормализовать.
4. Извлечь город.
5. Сопоставить с `cities`.
6. Сохранить `city_id`.

Если город не определён:

* status `needs_review`;
* позволить продавцу выбрать город вручную;
* записать проблему.

---

## 9.2. GeoProvider abstraction

Создать interface:

`GeoProviderInterface`

Реализация:

`NominatimGeoProvider`

Но код приложения не должен зависеть напрямую от публичного Nominatim.

Нужно уметь заменить provider.

Обязательно:

* debounce search;
* server-side proxy;
* cache;
* rate limiting;
* User-Agent приложения;
* корректная attribution OpenStreetMap;
* не выполнять API request на каждый keypress без контроля.

---

# 10. СОБСТВЕННОЕ ДЕРЕВО КАТЕГОРИЙ TEEU

Создать собственное дерево.

Поля категории:

* id;
* parent_id nullable;
* name;
* slug;
* full_path или вычисляемый path;
* depth;
* sort_order;
* is_active;
* products_allowed;
* seo_title;
* seo_description;
* seo_h1;
* seo_text nullable;
* timestamps.

Использовать adjacency list с корректной сервисной логикой перемещения.

Если потребуется materialized path:

* реализовать осознанно;
* поддерживать консистентность.

---

## 10.1. Slug

Slug:

* человекопонятный;
* SEO-friendly;
* уникальный в выбранной модели маршрутов.

При смене slug существующей индексируемой категории:

* создать 301 redirect;
* не обрывать старый URL.

---

## 10.2. Перемещение категории

Нельзя:

* переместить категорию внутрь самой себя;
* переместить parent внутрь descendant;
* создать цикл.

Обязательно тестировать.

---

# 11. AI-КЛАССИФИКАЦИЯ ТОВАРОВ ПО КАТЕГОРИЯМ

YML category tree не использовать как итоговое дерево.

Нужен:

`CategoryClassifierInterface`

Реализация:

`DeepSeekCategoryClassifier`

---

## 11.1. Входные данные

Для товара можно передавать:

* title;
* vendor;
* model;
* короткое description;
* params;
* raw YML category text как дополнительную подсказку;
* список разрешённых category candidates.

Не отправлять в AI ненужные персональные данные.

---

## 11.2. Нельзя позволять AI придумать category id

AI выбирает только из существующих category IDs, переданных системой.

Ответ валидировать.

Пример JSON-ответа:

```json
{
  "external_id": "12345",
  "category_id": 381,
  "confidence": 0.93,
  "reason_code": "title_and_params_match"
}
```

Если category id не существует:

* ответ invalid;
* retry/fallback;
* не сохранять выдуманную категорию.

---

## 11.3. Масштабирование классификации

Не делать бездумно один HTTP request на каждый товар.

Предусмотреть:

* chunking;
* batch requests;
* candidate narrowing;
* retries;
* queue.

Если category tree большой:

* не отправлять тысячи категорий целиком для каждого продукта.

Реализовать двухэтапный подход:

1. Получить ограниченный набор candidates:

   * по parent branch;
   * lexical matching;
   * existing mappings;
   * ранее подтверждённым результатам;
   * локальному поиску.
2. Передать AI только допустимые candidates.

Допускается кэширование стабильных классификаций по нормализованным признакам.

---

## 11.4. Confidence

Вынести threshold в config.

Стартовое значение:

`0.75`

Если confidence ниже:

* статус `needs_category_review`;
* товар не публиковать без валидной категории;
* показать проблему в admin/feed details.

Не назначать случайную категорию.

---

# 12. DEEPSEEK API — ЕДИНАЯ АРХИТЕКТУРА

Не создавать отдельный не связанный HTTP-клиент для:

* категорий;
* отзывов;
* чатов;
* товаров;
* продавцов.

Создать единый слой:

* `AiProviderInterface`;
* `DeepSeekAiProvider`;
* domain services поверх него.

Например:

* `ProductModerationService`;
* `ChatModerationService`;
* `ReviewModerationService`;
* `SellerDescriptionModerationService`;
* `CategoryClassificationService`.

---

## 12.1. Настройки DeepSeek

В admin:

* API key;
* base URL;
* model;
* timeout;
* max attempts;
* enabled;
* confidence thresholds.

Ключ хранить:

* encrypted at rest;
* masked в UI;
* не возвращать frontend;
* не логировать.

Модель никогда не хардкодить в domain services.

Стартовое значение модели:

`deepseek-v4-flash`

Но model всегда должна быть изменяемой из настроек.

---

## 12.2. Structured JSON

Для автоматических решений использовать JSON response mode.

Каждый ответ:

1. parse;
2. schema validate;
3. semantic validate.

Недостаточно проверить только:

`json_decode() !== null`

Проверять:

* обязательные keys;
* enum values;
* types;
* ranges;
* существование IDs.

---

## 12.3. AI request log

Создать общую таблицу, например:

`ai_requests`

Поля:

* id;
* provider;
* model;
* purpose;
* target_type;
* target_id;
* prompt_version;
* request_hash;
* status;
* decision nullable;
* response_json nullable;
* error_code nullable;
* attempts;
* latency_ms nullable;
* input_tokens nullable;
* output_tokens nullable;
* created_at;
* completed_at.

Не писать API key.

Не писать в обычный application log полный sensitive payload без необходимости.

---

## 12.4. Ошибки API

Обработать:

* insufficient balance;
* rate limit;
* timeout;
* 4xx;
* 5xx;
* overload;
* malformed response;
* empty response;
* invalid JSON.

Использовать:

* exponential backoff;
* jitter;
* max attempts;
* dead-letter/failed jobs;
* повторную обработку из admin.

---

## 12.5. AI unavailable policy

Важно.

Если нейромодерация не работает:

### Новый product text

* остаётся pending;
* не публикуется.

### Новая seller description

* новая версия pending;
* старая approved версия продолжает показываться.

### Новый review

* pending;
* публично не показывается.

### Chat message

* pending;
* получателю не показывается до approval.

### Новая AI category classification

* pending;
* товар без подтверждённой категории не публикуется.

### Уже approved content

* не скрывать автоматически только из-за временной недоступности AI.

---

## 12.6. Telegram alert о проблеме AI

При системных ошибках:

* закончился баланс;
* массовый rate limit;
* API недоступен;
* invalid credentials;
* длительный outage;

отправлять Telegram alert администратору.

Не спамить на каждый товар.

Использовать deduplication/cooldown.

Например:

* одна одинаковая alert-группа не чаще одного раза в 30 минут.

---

# 13. НЕЙРОМОДЕРАЦИЯ

Все policies должны быть versioned.

Например:

* `product_text_v1`;
* `seller_description_v1`;
* `chat_v1`;
* `review_v1`.

---

## 13.1. Модерация товаров

Модерировать не только ручное описание.

Модерировать также изменившийся публичный текст, пришедший из YML.

Проверять:

* title;
* description.

Запрещать:

* телефоны и контакты в product description;
* email;
* призывы связаться вне площадки;
* явно контактные messenger identifiers;
* грубую ненормативную лексику;
* оскорбления;
* непристойный контент;
* призывы к противоправным действиям;
* продажу явно запрещённых товаров/веществ;
* инструкции по незаконной деятельности.

Decision schema:

```json
{
  "decision": "allow",
  "confidence": 0.96,
  "categories": [],
  "reason_code": "clean"
}
```

Allowed decision:

* allow;
* block;
* review.

---

## 13.2. Модерация seller description

Проверять:

* obscene;
* hate/insults;
* illegal offers;
* prohibited goods;
* unlawful calls to action;
* spam.

Контакты рекомендуется хранить в структурированных полях.

---

## 13.3. Модерация чатов

Проверять:

* оскорбления;
* угрозы;
* продажу запрещённых веществ;
* незаконные сделки;
* призывы к противоправным действиям;
* тяжёлую непристойность.

Важно:

Не вводить запрет на любой телефон/email в чате без отдельного business requirement.

Пользователь исходно запретил противоправный контент, а не любое общение вне площадки.

Политику обмена контактами сделать отдельно конфигурируемой на будущее.

---

## 13.4. Модерация отзывов

Проверять:

* мат;
* оскорбления;
* угрозы;
* непристойности;
* незаконный контент;
* spam;
* явные контакты/рекламу.

---

## 13.5. Не переписывать пользовательский текст молча

AI не должен самовольно публиковать «исправленный» текст вместо автора.

Решение:

* allow;
* block;
* review.

При block показать пользователю понятную нейтральную ошибку.

Не показывать внутренний prompt.

---

# 14. ТОВАРЫ

## 14.1. Источники товара

Товар может быть:

* `manual`;
* `yml`.

Для YML обязательно:

* `yml_feed_id`;
* `external_id`.

---

## 14.2. Основные поля

Предусмотреть:

* id;
* seller_id;
* source_type;
* yml_feed_id nullable;
* external_id nullable;
* category_id;
* title;
* slug;
* description;
* price;
* old_price nullable;
* currency;
* vendor nullable;
* vendor_code nullable;
* barcode nullable;
* availability;
* stock_quantity nullable;
* moderation_status;
* category_status;
* publication_status;
* source_missing_at nullable;
* published_at nullable;
* timestamps;
* soft deletes при необходимости.

Деньги:

* DECIMAL;
* никогда float.

---

## 14.3. Статусы товара

Разделять причины.

Не использовать один boolean `active`.

Например:

Publication:

* draft;
* pending;
* active;
* paused;
* archived.

Moderation:

* pending;
* approved;
* rejected;
* needs_review.

Availability:

* available;
* unavailable;
* source_missing;
* out_of_stock.

Feed blocking учитывать отдельно.

Публичен только товар, удовлетворяющий всем правилам.

---

# 15. КРИТИЧЕСКАЯ ЛОГИКА OVERRIDE ДЛЯ YML

Это обязательно.

Проблема:

Если продавец вручную исправил описание YML-товара, следующий импорт не должен молча уничтожить его изменения.

Предусмотреть field override.

Минимум:

* description override;
* images override.

Допускается:

`manual_overrides JSON`

Например:

```json
{
  "description": true,
  "images": true
}
```

Правило:

Если `description=true`:

* YML refresh не перезаписывает public description.

Если `images=true`:

* YML refresh не заменяет ручной набор изображений.

В UI дать действие:

**Вернуть управление полем YML-фиду**

После снятия override:

* следующий import снова управляет полем.

Для price/availability в первой версии YML остаётся authoritative source, если отдельно не задан иной бизнес-сценарий.

---

# 16. РУЧНОЕ СОЗДАНИЕ ТОВАРА

В seller panel реализовать:

* создать товар;
* редактировать;
* категория;
* название;
* цена;
* старая цена nullable;
* описание;
* характеристики;
* до 10 фотографий;
* availability.

После изменения moderation-sensitive text:

* новая версия проходит moderation.

Не публиковать запрещённый текст до approval.

---

# 17. ХАРАКТЕРИСТИКИ ТОВАРОВ

Не хранить все характеристики одной HTML-строкой.

Создать нормализованную структуру.

Например:

`product_characteristics`

Поля:

* id;
* product_id;
* name;
* value;
* unit nullable;
* sort_order.

YML `<param>` импортировать сюда.

Для будущих фильтров предусмотреть возможность развития category attributes.

Не переусложнять первую версию полноценным PIM, если этого нет в исходных требованиях.

---

# 18. ИЗОБРАЖЕНИЯ ТОВАРОВ

Максимум:

**10 изображений на товар**

Применяется:

* manual;
* YML.

---

## 18.1. Форматы

Принимать распространённые безопасные raster formats:

* JPEG;
* PNG;
* WebP.

Другие форматы только при явной поддержке безопасного image pipeline.

Не доверять расширению файла.

Проверять реальный content/MIME.

---

## 18.2. Конвертация

Все public product images привести к WebP.

Создавать минимум:

### Preview

Максимальная рамка:

`400 x 400`

### Large

Максимальная рамка:

`1200 x 1200`

Сохранять aspect ratio.

Запрещено увеличивать маленькие исходники.

Пример:

Исходник:
`600 x 400`

Large:
не превращать в `1200 x 800`.

Оставить максимум исходного разрешения.

---

## 18.3. Дополнительная обработка

* EXIF orientation;
* metadata strip;
* configurable quality;
* защита от decompression bomb;
* max source bytes;
* max dimensions/pixels;
* timeout remote image download;
* content hash;
* deduplication.

---

## 18.4. YML remote images

Скачивание делать через queue.

URL изображения тоже считать SSRF surface.

Применять ограничения аналогично внешнему fetch:

* HTTP/HTTPS;
* private IP protection;
* redirect limits;
* timeout;
* max bytes.

---

## 18.5. Хранение

Использовать Laravel Storage abstraction.

Проект не должен быть намертво привязан к:

`public/uploads`

Поддержать возможность позднего перехода на S3-compatible storage.

---

# 19. КАТАЛОГ

Публичный каталог должен быть рабочим, а не только набором карточек товара.

---

## 19.1. Страница категории

Показывать:

* breadcrumbs;
* H1;
* дочерние категории;
* список товаров;
* pagination;
* SEO text в разумном месте;
* selected city context.

Минимальные сортировки:

* по актуальности/default;
* сначала новые;
* цена по возрастанию;
* цена по убыванию.

Не загружать бесконечный массив товаров одной страницей.

---

## 19.2. Карточка товара в листинге

Минимум:

* главное фото;
* title;
* актуальная цена;
* старая цена, если есть;
* продавец;
* favorite action;
* city context, если нужен дизайном.

Следовать `/tmp/design`.

---

# 20. КАРТОЧКА ТОВАРА

URL:

`/product/{slug}-{id}`

ID допускается использовать для стабильного поиска объекта.

Slug нужен для:

* читаемости;
* SEO.

Если slug в URL устарел:

* сделать canonical/301 на актуальный URL.

---

## 20.1. Основные элементы

Обязательно:

* изображения;
* название;
* цена;
* старая цена;
* availability;
* положить в корзину;
* написать продавцу;
* уведомить о снижении цены;
* описание;
* характеристики;
* продавец;
* логотип продавца;
* название продавца;
* рейтинг;
* ссылка на seller card;
* другие товары продавца;
* похожие товары других продавцов той же категории.

---

## 20.2. Корзина

Кнопка добавления доступна только авторизованному пользователю.

Для guest:

* открыть login flow;
* после успешной авторизации желательно вернуть к товару.

---

## 20.3. Похожие товары

Минимальная логика:

* active;
* approved;
* доступны в выбранном городе согласно geo rules;
* та же категория;
* другие продавцы;
* исключить текущий товар.

Не показывать blocked feed products.

---

## 20.4. Другие товары продавца

* active;
* approved;
* не текущий товар;
* seller active.

---

# 21. ГЕОТАРГЕТИНГ

В шапке публичного сайта:

* выбранный город;
* возможность изменить.

---

## 21.1. Окно выбора города

Должно содержать:

* поиск по городам РФ;
* быстрый блок городов-миллионников;
* список результатов.

Не выполнять внешний geocoder request на каждый символ.

Для выбора города использовать собственную таблицу `cities`.

---

## 21.2. Таблица cities

Предусмотреть:

* id;
* name;
* normalized_name;
* region;
* latitude nullable;
* longitude nullable;
* external code nullable;
* is_featured;
* population nullable;
* sort_order.

Список featured cities управляемый.

Не зашивать список миллионников навсегда в Blade.

---

## 21.3. Приоритет определения города

1. Явный выбор пользователя.
2. Сохранённый profile city.
3. Cookie/session.
4. Дополнительное автоопределение — только если будет отдельно реализовано.

Явный выбор пользователя всегда имеет приоритет.

---

## 21.4. Связь товаров с городами

YML product наследует географию от своего YML feed addresses.

Один feed может иметь:

* несколько адресов;
* несколько city IDs.

Следовательно товар может быть доступен в нескольких городах.

Для manual product продавец должен выбрать доступные seller/shipping addresses или city coverage.

Не хранить один `products.city_id`, если источник реально может иметь несколько городов.

---

# 22. ИЗБРАННОЕ

Только зарегистрированные пользователи.

Таблица:

`favorites`

Unique:

`user_id + product_id`

Функции:

* добавить;
* удалить;
* список.

Если товар стал недоступен:

* favorite не обязательно физически удалять;
* UI показывает недоступность.

---

# 23. УВЕДОМЛЕНИЕ О СНИЖЕНИИ ЦЕНЫ

На product page:

**Уведомить о снижении цены**

Только зарегистрированные.

Создать:

`price_drop_subscriptions`

Поля:

* user_id;
* product_id;
* baseline_price;
* last_notified_price nullable;
* active;
* timestamps.

Unique:

* user + product.

---

## 23.1. Price history

Создать:

`product_price_history`

Записывать только реальное изменение цены.

Не создавать запись при каждом одинаковом YML import.

---

## 23.2. Trigger

Когда цена реально снизилась:

* найти active subscriptions;
* создать in-app notification;
* Web Push при наличии подписки и включённом feature;
* дополнительные каналы — через notification architecture.

Не уведомлять бесконечно об одной цене.

Обновлять `last_notified_price`.

---

# 24. КОРЗИНА

Корзина хранится в БД.

Не localStorage как основной источник.

---

## 24.1. Таблицы

`carts`

* id;
* user_id;
* timestamps.

`cart_items`

* id;
* cart_id;
* product_id;
* quantity;
* created_at;
* updated_at.

Unique:

* cart + product.

---

## 24.2. Постоянство

Корзина:

* не стирается после logout;
* восстанавливается после login;
* сохраняется между устройствами для одного user account.

После оформления выбранных items:

* удалить только успешно оформленные items;
* остальные оставить.

---

## 24.3. Проверка доступности

Проверять:

* при открытии cart;
* при изменении quantity;
* непосредственно перед checkout;
* внутри transaction при создании заказа.

Проверить:

* seller active;
* feed not blocked;
* product published;
* moderation approved;
* availability;
* price.

---

## 24.4. Изменение цены

Если цена изменилась после добавления:

* не использовать старую цену молча;
* показать актуальную;
* перед checkout использовать актуальную серверную цену;
* frontend total не считать источником истины.

---

## 24.5. Выбор товаров

Каждый item имеет checkbox.

Пользователь может оформить:

* один;
* несколько;
* все.

На backend передавать IDs выбранных cart items.

Backend повторно проверяет ownership.

Не доверять seller_id/price из frontend.

---

# 25. ОФОРМЛЕНИЕ ЗАКАЗА

В корзине могут находиться товары разных продавцов.

При checkout выбранных товаров:

1. Проверить выбранные cart items.
2. Заблокировать необходимые записи для консистентности.
3. Повторно проверить товары.
4. Получить seller IDs с backend.
5. Сгруппировать по seller.
6. Создать общий `order_group`.
7. Для каждого seller создать отдельный `order`.
8. Создать snapshots order items.
9. Commit transaction.
10. После commit отправить notifications.

Если 3 продавца:

* создаются 3 orders.

---

## 25.1. Не реализовывать несуществующую платёжную систему

В исходном scope нет:

* online acquiring;
* marketplace payouts;
* комиссии;
* seller balance;
* escrow.

Не придумывать их.

Order в текущем scope — заказ/заявка продавцу.

Архитектура не должна мешать добавить payment позже.

---

## 25.2. Order group

Таблица:

`order_groups`

Нужна, чтобы покупатель понимал, что одно оформление split на несколько seller orders.

---

## 25.3. Order snapshot

Нельзя строить историю заказа только из текущего product.

`order_items` должны хранить snapshot:

* product_id nullable;
* product title;
* product slug/url snapshot;
* quantity;
* unit price;
* total price;
* vendor code nullable;
* primary image snapshot/reference nullable.

Если product позже удалён:

* order остаётся читаемым.

---

# 26. СТАТУСЫ ЗАКАЗА

Использовать Enum и transition service.

Предлагаемая первая матрица:

* `new`
* `confirmed`
* `processing`
* `ready`
* `shipped`
* `completed`
* `cancelled_by_seller`
* `cancelled_by_buyer`
* `cancelled_by_admin`

Если физическая доставка отсутствует у конкретного продавца, статус `shipped` можно не использовать в UI, но domain enum должен быть осмысленным.

Не позволять произвольную смену:

`completed -> new`

---

## 26.1. Seller transitions

Пример:

`new -> confirmed`

`new -> cancelled_by_seller`

`confirmed -> processing`

`confirmed -> cancelled_by_seller`

`processing -> ready`

`processing -> cancelled_by_seller`

`ready -> shipped`

`ready -> completed`

`shipped -> completed`

Финальные cancelled/completed:

* обычным seller UI назад не двигаются.

Admin override:

* отдельный action;
* обязательный audit log;
* reason.

---

## 26.2. История статусов

Создать:

`order_status_history`

Поля:

* order_id;
* from_status;
* to_status;
* actor_type;
* actor_id nullable;
* reason nullable;
* created_at.

---

# 27. КАБИНЕТ ПРОДАВЦА — ЗАКАЗЫ

Продавец видит только свои orders.

Функции:

* список;
* фильтр по status;
* поиск по номеру;
* order detail;
* товары;
* каждый товар имеет ссылку;
* buyer contact согласно разрешённой модели;
* смена status только по transition rules.

Защита от IDOR обязательна.

Seller A никогда не получает order Seller B через изменение URL.

---

# 28. КАБИНЕТ ПОКУПАТЕЛЯ — ЗАКАЗЫ

Раздел:

`/account/orders`

Показывать:

* order group;
* отдельные seller orders;
* номер;
* дата;
* seller;
* products;
* total;
* current status;
* status history.

Пользователь видит только собственные заказы.

---

# 29. УВЕДОМЛЕНИЯ

Использовать Laravel Notification architecture.

Каналы:

* database/in-app;
* email;
* Telegram для admin system events;
* web push как расширяемый канал.

---

## 29.1. Новый заказ

Продавцу:

* in-app;
* email.

После commit order transaction.

Не отправлять email до успешного commit.

---

## 29.2. Order больше 2 дней в New

Если order находится:

`new`

больше 48 часов:

Продавцу:

* in-app reminder;
* email.

Не спамить каждую минуту.

Правило:

* первый reminder после 48 часов;
* повторный максимум раз в 24 часа, пока order остаётся new.

Хранить факт отправки.

---

## 29.3. Покупателю при смене статуса

При реальном status transition:

* in-app notification;
* web push при доступности/согласии.

Не отправлять duplicate, если status фактически не изменился.

---

## 29.4. Notification center

В кабинете:

* список;
* unread count;
* mark read;
* mark all read.

---

# 30. ЧАТЫ

Сначала изучить Placeo.

По возможности использовать его рабочую архитектуру.

---

## 30.1. Модель conversation

Рекомендуемая базовая логика:

Один buyer-seller dialogue на пару:

* buyer;
* seller.

Чтобы не плодить 20 отдельных чатов по 20 товарам одного seller.

При переходе с product page:

* conversation открывается;
* UI может добавить product context card.

---

## 30.2. Conversation

Поля:

* id;
* buyer_user_id;
* seller_id;
* status;
* last_message_at;
* timestamps.

Unique pair:

* buyer + seller.

---

## 30.3. Messages

Поля:

* id;
* conversation_id;
* sender_kind;
* sender_user_id nullable;
* portal_admin_id nullable;
* body;
* moderation_status;
* client_uuid;
* created_at;
* approved_at nullable.

`client_uuid` нужен против duplicate send при повторе запроса.

---

## 30.4. Sender kinds

* buyer;
* seller;
* portal.

Admin не должен притворяться конкретным покупателем.

При intervention показывать:

**Команда teeu**

или иной текст из дизайна.

---

## 30.5. AI moderation chat flow

При отправке:

1. Валидировать.
2. Сохранить message как pending.
3. Запустить moderation.
4. До approval recipient не видит message.
5. При allow:

   * approved;
   * broadcast/update UI.
6. При block:

   * sender получает понятный статус;
   * recipient message не видит.

Если Placeo уже имеет подходящий механизм:

* адаптировать.

---

## 30.6. Realtime

Не изобретать второй realtime stack до аудита Placeo.

Если Placeo использует рабочий:

* websocket;
* polling;
* long polling;
* другой transport;

оценить возможность адаптации.

Domain chat service не должен зависеть от конкретного realtime transport.

---

## 30.7. Безопасность

* escape output;
* не рендерить raw HTML;
* rate limit;
* message max length;
* authorization;
* user cannot access чужой conversation;
* seller cannot access чужой seller conversation.

---

# 31. ADMIN — ДАШБОРД

Главная admin page.

Показать минимум:

* всего users;
* новых users за 24h/7d/30d;
* всего sellers;
* active sellers;
* suspended sellers;
* orders;
* orders by status;
* order amount sum;
* active products;
* pending products;
* YML feeds;
* failed imports;
* AI pending;
* AI failed;
* pending reviews;
* chat activity.

Графики:

* registrations;
* orders;
* sellers;
* imports/errors.

Периоды:

* 7 дней;
* 30 дней.

Следовать современному UI.

---

# 32. ADMIN — ПРОДАВЦЫ

Список:

* ID;
* public name;
* legal name;
* INN;
* owner;
* phone;
* email;
* email verified;
* status;
* products count;
* orders count;
* created.

Фильтры:

* status;
* date;
* verified;
* search.

Detail:

* анкета;
* contacts;
* addresses;
* feeds;
* products;
* orders;
* reviews;
* membership;
* audit history.

Actions:

* suspend;
* unblock;
* block.

Dangerous actions:

* confirmation;
* audit.

---

# 33. ADMIN — ПОЛЬЗОВАТЕЛИ

Список:

* ID;
* phone;
* name;
* OAuth providers;
* seller yes/no;
* orders count;
* created;
* last activity;
* status.

Detail:

* profile;
* identities;
* orders;
* favorites;
* conversations;
* seller profile, если есть.

Не показывать secrets/tokens.

---

# 34. ADMIN — ЗАКАЗЫ

Список:

* order number;
* buyer;
* seller;
* status;
* total;
* items;
* created;
* updated.

Detail:

* snapshots;
* status history;
* linked products;
* actors;
* notifications.

Admin status override:

* отдельное действие;
* reason;
* audit.

---

# 35. ADMIN — YML ВЫГРУЗКИ

Список:

* seller;
* feed name;
* URL в безопасно отображаемом виде;
* status;
* addresses;
* cities;
* products;
* last run;
* last success;
* errors.

Action:

**Block feed**

Следствие:

Все товары этого feed:

* становятся недоступны публично;
* нельзя добавить в cart;
* нельзя оформить;
* не удаляются;
* orders history сохраняется.

При unblock:

* товары не должны автоматически нарушить moderation/availability rules;
* применяется полная visibility policy.

---

## 35.1. Feed detail

Показать:

* runs;
* errors;
* item errors;
* pending classification;
* pending moderation;
* products;
* addresses;
* statistics.

Нужны actions:

* run import now;
* pause;
* activate;
* block;
* unblock;
* reprocess failed AI;
* retry failed images.

---

# 36. ADMIN — ДЕРЕВО КАТЕГОРИЙ

Функции:

* create;
* edit;
* move;
* deactivate;
* sort.

Поля:

* name;
* parent;
* slug;
* SEO title;
* meta description;
* H1;
* SEO text;
* active.

При изменении slug:

* 301 redirect.

При перемещении:

* пересчитать path;
* обновить descendants;
* не создать цикл.

---

# 37. ADMIN — ДИАЛОГИ

Администратор может:

* смотреть conversations;
* искать;
* фильтровать;
* читать messages;
* видеть moderation statuses;
* видеть blocked messages;
* вмешиваться.

Intervention:

* message sender = portal;
* показывается как сообщение teeu;
* хранить конкретного admin actor;
* audit.

---

# 38. ADMIN — НАСТРОЙКИ

Разделы:

## DeepSeek

* enabled;
* API key;
* base URL;
* model;
* timeout;
* attempts;
* classification threshold.

## Telegram

* bot token;
* chat ID;
* enabled.

Event toggles:

* new user;
* new seller;
* new YML feed;
* YML import critical error;
* AI unavailable;
* AI insufficient balance;
* system critical errors.

---

## 38.1. Secrets

API keys:

* encrypted in DB;
* masked in UI;
* не возвращать полное значение после сохранения;
* empty edit field не должен стирать существующий secret без явного action.

Добавить:

* Test DeepSeek;
* Test Telegram.

Test action:

* rate limited;
* audit logged.

---

# 39. TELEGRAM ADMIN EVENTS

Отправлять:

1. Новый user.
2. Новый seller.
3. Новый YML feed.
4. Критическая ошибка YML.
5. AI unavailable.
6. AI balance/auth error.

Сообщение должно быть кратким.

Не отправлять пользовательские секреты.

Не отправлять полный private chat content.

---

# 40. ОТЗЫВЫ

Отзыв можно оставить только seller, у которого есть успешно завершённый order.

---

## 40.1. Eligibility

Лучшее правило первой версии:

**Один review на один completed order.**

Требования:

* order принадлежит текущему buyer;
* order seller совпадает;
* status = completed;
* order не cancelled;
* review для order ещё не создан.

Unique:

* `order_id`

Это предотвращает бесконечные отзывы по одной покупке.

---

## 40.2. Поля

* order_id;
* seller_id;
* user_id;
* rating;
* text;
* moderation_status;
* published_at;
* timestamps.

Rating:

* integer 1–5.

---

## 40.3. Moderation

До approval:

* не участвует в public rating;
* не виден публично.

При редактировании:

* повторная moderation.

---

## 40.4. Seller rating

Рейтинг рассчитывается только по approved reviews.

Хранить cache:

* `rating_avg`;
* `reviews_count`.

Но source of truth:

* reviews.

Пересчитывать безопасным сервисом.

---

# 41. SEO

Проект изначально строить под SEO.

Публичный каталог не делать client-only SPA.

---

## 41.1. Meta

Для category:

* title;
* description;
* canonical;
* H1.

Для product:

* title;
* description;
* canonical;
* OG.

Для seller:

* title;
* description;
* canonical;
* OG.

---

## 41.2. OpenGraph

Product:

* `og:title`;
* `og:description`;
* `og:image`;
* `og:url`;
* `og:type`.

Seller:

* соответствующие поля.

---

## 41.3. Schema.org product

На product page JSON-LD.

Использовать релевантные свойства:

* `@type: Product`
* name
* description
* image
* sku, если есть
* brand, если есть
* offers
* price
* priceCurrency
* availability
* url
* seller
* aggregateRating только при реальных approved reviews

Не выводить fake rating.

---

## 41.4. BreadcrumbList

Для:

* category;
* product.

JSON-LD:

`BreadcrumbList`

Путь должен совпадать с логикой каталога.

---

## 41.5. Sitemap

Создать:

* sitemap index;
* category sitemap;
* product sitemap;
* seller sitemap.

Не включать:

* blocked;
* pending;
* rejected;
* private;
* seller panel;
* account;
* admin.

---

## 41.6. Robots

Noindex:

* `/account`;
* `/merchant`;
* `/developer`;
* cart;
* private dialogs;
* auth callbacks.

---

## 41.7. Canonical

Особенно:

* pagination;
* stale slug;
* duplicated query params.

Не создавать тысячи индексируемых дублей из:

* sorting;
* tracking params.

---

## 41.8. 301 redirect history

Для изменённых category slugs хранить redirects.

Для product stale slug:

* определять product по ID;
* 301 на текущий URL.

---

# 42. АДАПТИВНАЯ ВЁРСТКА

Все страницы адаптивные.

Главное правило:

На телефоне сайт должен восприниматься как современное мобильное приложение.

---

## 42.1. Запрещено

* horizontal scroll;
* элементы за viewport;
* таблицы, ломающие экран;
* fixed-width desktop blocks;
* огромные модальные окна;
* кнопки без touch usability.

Не решать проблемы только:

`body { overflow-x: hidden; }`

Нужно исправлять реальную причину overflow.

---

## 42.2. Mobile UX

Использовать дизайн `/tmp/design`.

Предусмотреть:

* мобильную шапку;
* удобную нижнюю навигацию, если соответствует UI;
* sticky actions там, где оправдано;
* safe-area;
* touch-friendly controls;
* mobile cart;
* mobile product gallery.

---

## 42.3. Тестовые ширины

Минимум проверить:

* 320;
* 360;
* 375;
* 390;
* 430;
* 768;
* 1024;
* 1280;
* 1440+.

---

# 43. PWA

Реализовать:

* Web App Manifest;
* app name;
* short name;
* icons;
* theme color;
* background color;
* standalone display;
* service worker;
* installability.

Использовать реальные teeu assets из design.

---

## 43.1. Service Worker cache

Очень важно.

Не кэшировать бездумно приватные страницы:

* account;
* seller panel;
* admin;
* conversations;
* sensitive API.

Использовать разные strategies:

Static assets:

* cache first/versioned.

Public pages:

* network first или осознанная strategy.

Private data:

* network only по умолчанию.

Добавить offline fallback page.

---

# 44. WEB PUSH

Заложить архитектуру.

Минимум:

* `push_subscriptions`;
* user_id;
* endpoint;
* keys;
* browser metadata;
* active;
* timestamps.

Создать:

`WebPushChannel` abstraction.

Использовать feature flag.

Начальные use cases:

* order status changed;
* price drop.

Если полноценный push provider не подключён:

* не создавать fake push;
* подготовить реальную архитектуру и отключённый feature flag.

---

# 45. БАЗА ДАННЫХ — ОСНОВНЫЕ ТАБЛИЦЫ

Обязательно проработать минимум следующие сущности.

## Identity

* users
* auth_identities

## Sellers

* sellers
* seller_memberships
* seller_email_verification_codes
* seller_addresses

## Geography

* cities

## Categories

* categories
* category_redirects

## YML

* yml_feeds
* yml_feed_addresses
* yml_import_runs
* yml_import_offers или staging equivalent
* yml_import_errors

## Products

* products
* product_images
* product_characteristics
* product_price_history
* product category/classification data where needed

## Buyer

* favorites
* carts
* cart_items
* price_drop_subscriptions

## Orders

* order_groups
* orders
* order_items
* order_status_history

## Chats

* conversations
* messages

## Reviews

* reviews

## AI

* ai_requests
* moderation records/version data if separated

## Notifications

* notifications
* push_subscriptions

## System

* app_settings
* audit_logs
* jobs
* failed_jobs

Не воспринимать список как запрет на дополнительные необходимые таблицы.

---

# 46. ИНДЕКСЫ И CONSTRAINTS

Обязательно продумать индексы.

Примеры:

Users:

* unique normalized phone.

Auth:

* unique provider + provider_user_id.

Products:

* seller_id;
* category_id;
* yml_feed_id;
* publication_status;
* moderation_status;
* availability;
* published_at.

YML:

* unique feed + external_id;
* feed status;
* next_sync_at.

Orders:

* user_id;
* seller_id;
* status;
* created_at.

Messages:

* conversation_id + created_at.

Reviews:

* seller_id + moderation_status;
* unique order_id.

Favorites:

* unique user + product.

Не создавать 30 случайных индексов.

Проверять реальные query patterns.

---

# 47. БЕЗОПАСНОСТЬ

Обязательно:

* CSRF;
* XSS protection;
* authorization policies;
* IDOR protection;
* Form Requests;
* server-side validation;
* rate limiting;
* safe uploads;
* SSRF protection;
* XXE protection;
* secret encryption;
* safe redirects;
* transaction safety.

---

## 47.1. Никакой авторизации только через frontend

Недостаточно спрятать кнопку.

Каждый seller/admin endpoint:

* backend authorization.

---

## 47.2. Mass assignment

Явно определить fillable/DTO mapping.

Не принимать:

`request()->all()`

для sensitive models.

---

## 47.3. HTML

По умолчанию descriptions — plain text либо строго безопасный allowlist.

Не выводить raw seller/user HTML.

---

## 47.4. Audit

Логировать важные действия:

* seller block;
* feed block;
* category change;
* order admin override;
* admin chat intervention;
* settings change;
* secret change без записи secret value.

---

# 48. QUEUES

Тяжёлые задачи только background jobs.

Отдельные logical queues:

* `yml-download`
* `yml-processing`
* `ai`
* `images`
* `notifications`
* `default`

Если database queue:

* они всё равно должны логически разделяться.

---

## 48.1. Jobs

Минимум продумать:

* FetchYmlFeedJob
* ParseYmlFeedJob
* ProcessYmlOfferChunkJob
* ClassifyProductsJob
* ModerateProductTextJob
* DownloadProductImageJob
* ProcessProductImageJob
* GeocodeAddressJob
* ModerateChatMessageJob
* ModerateReviewJob
* ModerateSellerDescriptionJob
* SendTelegramEventJob
* SendNotificationJob

Названия можно адаптировать.

---

## 48.2. Retry

Для каждого внешнего API:

* timeout;
* tries;
* backoff.

Не использовать одинаковую retry policy для:

* Telegram;
* image download;
* AI;
* YML.

---

# 49. SCHEDULER

Использовать Laravel Scheduler.

Нужны задачи:

## YML

* запуск due feeds.

## Orders

* поиск new orders старше 48h;
* reminders.

## Notifications

* retry/cleanup where needed.

## AI

* controlled retry pending requests.

## System

* cleanup expired email codes;
* cleanup expired auth challenges;
* cleanup old temp YML files.

На сервере должен быть документирован один scheduler cron.

---

# 50. YML AUTO-UPDATE

Каждый feed:

* manual sync;
* auto sync.

Параметр:

`update_interval_minutes`

Стартовый default:

6 часов.

Но значение конфигурируемое.

Хранить:

* last_attempt_at;
* last_success_at;
* next_sync_at.

После failed run:

* не сбрасывать старые товары.

---

# 51. КОДОВАЯ АРХИТЕКТУРА LARAVEL

Не делать giant controllers.

Использовать:

* Controllers;
* Form Requests;
* Policies;
* Actions/Services;
* Jobs;
* Events;
* Listeners;
* Notifications;
* Enums;
* DTO там, где оправдано;
* Contracts для внешних интеграций.

Пример:

```text
app/
  Actions/
  Contracts/
  DTO/
  Enums/
  Events/
  Http/
  Jobs/
  Models/
  Notifications/
  Policies/
  Services/
    Ai/
    Auth/
    Catalog/
    Geo/
    Images/
    Orders/
    Yml/
```

Не обязательно копировать структуру буквально, но сохранить separation of concerns.

---

# 52. TRANSACTIONS

Использовать DB transactions там, где требуется атомарность.

Особенно:

* checkout;
* order split;
* status transitions;
* seller activation;
* critical aggregate updates.

Не держать DB transaction открытой во время:

* DeepSeek HTTP call;
* YML download;
* email;
* Telegram;
* image download.

External effects запускать после commit.

---

# 53. EVENTS

Рекомендуемые domain/application events:

* UserRegistered
* SellerActivated
* YmlFeedCreated
* YmlImportFailed
* OrderCreated
* OrderStatusChanged
* ProductPriceDecreased
* AiProviderUnavailable
* ReviewApproved

Notification side effects вынести в listeners/jobs.

---

# 54. CONFIG И ENV

Создать понятный `.env.example`.

Минимум:

```text
APP_NAME=teeu

SELLER_PANEL_PREFIX=merchant
ADMIN_PANEL_PREFIX=developer

DEEPSEEK_API_KEY=
DEEPSEEK_BASE_URL=
DEEPSEEK_MODEL=deepseek-v4-flash

TELEGRAM_BOT_TOKEN=
TELEGRAM_ADMIN_CHAT_ID=

YML_MAX_FILE_SIZE=
YML_DEFAULT_SYNC_INTERVAL=

IMAGE_MAX_SOURCE_BYTES=
IMAGE_PREVIEW_MAX=400
IMAGE_LARGE_MAX=1200

GEO_PROVIDER=nominatim
NOMINATIM_BASE_URL=

PUSH_ENABLED=false
VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY=
```

Если secrets управляются из admin DB:

* env может быть fallback/bootstrap source.

Определить понятный priority.

---

# 55. ЛОГИРОВАНИЕ

Использовать structured context.

Логировать IDs:

* user_id;
* seller_id;
* feed_id;
* import_run_id;
* order_id;
* ai_request_id.

Не логировать:

* API keys;
* OAuth tokens;
* полные verification codes;
* passwords;
* unnecessary private messages.

---

# 56. ТЕСТЫ

Проект нельзя считать завершённым только потому, что «страница открывается».

---

## 56.1. Auth tests

* нормализация телефона;
* duplicate phone merge;
* OAuth identity attach;
* invalid callback;
* replay state;
* flash-call attempts;
* expired challenge;
* reused challenge.

---

## 56.2. Seller tests

* buyer becomes seller;
* email required;
* wrong code;
* expired code;
* max attempts;
* successful verification;
* suspended seller access.

---

## 56.3. Category tests

* add;
* nested add;
* move;
* prevent cycle;
* slug redirect;
* inactive category.

---

## 56.4. YML tests

Создать fixtures:

1. Valid simple YML.
2. Vendor-model offers.
3. Multiple pictures.
4. Params.
5. Missing optional fields.
6. Broken XML.
7. Duplicate offer IDs.
8. Huge streamed fixture.
9. XXE attempt.
10. External entity.
11. Redirect URL.
12. Private IP URL.
13. Removed offer.
14. Reappeared offer.
15. Failed import.

Критический test:

**Failed import must not mass-deactivate existing products.**

---

## 56.5. Override tests

1. Import YML description A.
2. Seller manually changes to B.
3. Set override.
4. YML changes to C.
5. Public description remains B.
6. Remove override.
7. Next import applies source value.

---

## 56.6. Image tests

* large JPEG;
* small JPEG not upscaled;
* PNG;
* fake extension;
* oversized file;
* duplicate;
* > 10;
* EXIF orientation;
* invalid image.

---

## 56.7. AI tests

Mock provider.

Cases:

* allow;
* block;
* review;
* invalid JSON;
* empty response;
* nonexistent category id;
* timeout;
* insufficient balance;
* rate limit;
* 500;
* retry exhausted.

---

## 56.8. Cart tests

* persistence;
* multiple sellers;
* selected items;
* unavailable product;
* blocked feed;
* changed price;
* foreign cart item ID;
* duplicate add.

---

## 56.9. Checkout tests

Cart:

* Seller A item 1;
* Seller A item 2;
* Seller B item 3.

Expected:

* 1 order_group;
* 2 orders;
* correct items;
* correct totals;
* processed cart items removed;
* unselected cart items remain.

---

## 56.10. Order tests

* valid transitions;
* invalid transitions;
* seller cannot see other order;
* buyer cannot see foreign order;
* status history;
* notifications.

---

## 56.11. Review tests

* no completed order => forbidden;
* cancelled => forbidden;
* completed => allowed;
* duplicate review same order => forbidden;
* pending review not in rating;
* approved review changes rating.

---

## 56.12. Chat tests

* unauthorized access;
* seller isolation;
* pending moderation hidden;
* approved visible;
* blocked hidden;
* portal intervention correctly identified;
* duplicate client UUID.

---

## 56.13. SEO tests

* canonical;
* metadata;
* Product JSON-LD;
* BreadcrumbList;
* no fake AggregateRating;
* sitemap excludes blocked;
* old slug 301.

---

# 57. PERFORMANCE

Обязательно:

* eager loading;
* no obvious N+1;
* pagination;
* chunk processing;
* streaming YML;
* indexes;
* queues.

Не загружать:

* все products seller;
* все orders;
* все messages;
* все reviews;

одним запросом.

---

# 58. ADMIN OPERATIONAL SCREENS

Кроме основных CRUD, предусмотреть operational visibility.

Нужно видеть:

## AI

* pending;
* failed;
* recent errors.

## YML

* failed runs;
* partial runs;
* item errors.

## Category classification

* needs review.

## Images

* failed processing.

Это можно встроить:

* dashboard;
* feed details;
* product details.

Не обязательно делать четыре отдельных огромных раздела.

Но проблемы не должны существовать только в server logs.

---

# 59. COMMANDS ДЛЯ ОБСЛУЖИВАНИЯ

Предусмотреть Artisan commands, где оправдано.

Например:

```text
teeu:yml:sync-due
teeu:yml:sync {feedId}
teeu:ai:retry-failed
teeu:orders:send-stale-reminders
teeu:products:rebuild-rating
teeu:sitemap:generate
```

Названия можно изменить.

Каждая command:

* корректные exit codes;
* logging;
* безопасна к повторному запуску.

---

# 60. PUBLIC VISIBILITY POLICY

Создать единый service/scope.

Не размазывать по 20 controllers условия:

```php
product.active = 1
```

Публичный товар видим только если:

* seller active;
* product publication active;
* moderation approved;
* category valid/active;
* feed не blocked, если YML;
* product availability разрешает показ;
* geo rules соответствуют контексту.

Создать reusable:

* Eloquent scope;
* service;
* specification/query object.

Cart и checkout используют более строгую purchase eligibility проверку.

---

# 61. PRODUCT PURCHASE ELIGIBILITY

Создать единый сервис, например:

`ProductPurchaseEligibilityService`

Проверяет:

* существует;
* seller active;
* feed active/not blocked;
* product active;
* moderation approved;
* available;
* geo availability при обязательности;
* quantity.

Использовать:

* add to cart;
* cart validation;
* checkout.

Не дублировать разные правила.

---

# 62. SLUG STRATEGY

## Product

URL:

`/product/{slug}-{id}`

Пример:

`/product/besprovodnye-naushniki-sony-12345`

ID обеспечивает стабильность.

## Seller

`/seller/{slug}`

Slug unique.

## Category

Иерархический path:

`/catalog/electronics/smartphones`

Поддержать redirects при изменении.

---

# 63. UI СОСТОЯНИЯ

Для всех async функций предусмотреть:

* loading;
* empty;
* success;
* partial;
* error.

Особенно:

* YML import;
* AI moderation;
* image processing;
* geocoding;
* chat pending.

Не оставлять пользователя с вечным spinner.

---

# 64. SELLER DASHBOARD

Показать:

* active products;
* unavailable;
* YML feeds;
* last import status;
* new orders;
* orders requiring action;
* unread messages.

Если есть order new >48h:

* заметный warning.

---

# 65. BUYER DASHBOARD

Показать:

* recent orders;
* current statuses;
* favorites;
* unread notifications;
* unread conversations.

Следовать дизайну.

---

# 66. EMAIL

Email обязателен минимум для:

1. Seller email verification.
2. New order seller notification.
3. Stale new order reminder.

Все email:

* queued;
* branded teeu;
* responsive;
* без утечки unnecessary data.

---

# 67. OUT OF SCOPE ПЕРВОЙ ВЕРСИИ

Не реализовывать без отдельного требования:

* online payments;
* seller payouts;
* marketplace commission;
* escrow;
* delivery carrier integrations;
* return/dispute center;
* native iOS;
* native Android;
* external KYC;
* automatic bank requisites verification;
* AI image moderation;
* multi-currency settlement;
* international cities.

Но архитектура не должна искусственно блокировать развитие.

---

# 68. ВАЖНОЕ ОГРАНИЧЕНИЕ AI-МОДЕРАЦИИ

Текстовая AI-модерация не гарантирует обнаружение незаконного товара, если нарушение видно только:

* на фотографии;
* в завуалированном изображении;
* вне текста.

Не создавать ложного утверждения в документации, что DeepSeek обеспечивает абсолютную безопасность.

В текущем scope:

* text moderation.

Image moderation:

* отдельная будущая интеграция.

---

# 69. МИГРАЦИИ

Требования:

* foreign keys;
* appropriate delete behavior;
* indexes;
* rollback;
* InnoDB;
* utf8mb4.

Не использовать cascade delete там, где это уничтожит:

* orders;
* order history;
* reviews;
* audit.

Для исторических сущностей использовать:

* restrict;
* set null;
* soft deletion;
  в зависимости от смысла.

---

# 70. DOCUMENTATION

Создать минимум:

`README.md`

`docs/architecture.md`

`docs/database.md`

`docs/erd.md`

`docs/design-inventory.md`

`docs/placeo-reference-audit.md`

`docs/yml-import.md`

`docs/ai-moderation.md`

`docs/authentication.md`

`docs/deployment.md`

`docs/queues.md`

`docs/seo.md`

В ERD использовать Mermaid, если удобно.

---

# 71. README

README должен содержать реальные команды:

* install;
* env;
* key generate;
* migrations;
* seed;
* npm install/build;
* storage link;
* queue worker;
* scheduler;
* tests.

Не писать абстрактно:

«Настройте сервер».

---

# 72. DEPLOYMENT

Документировать:

* PHP extensions;
* writable dirs;
* queue worker;
* scheduler;
* storage;
* mail;
* DB;
* external integrations.

Если используется Supervisor/systemd:

* дать пример.

Не привязывать production к `php artisan serve`.

---

# 73. ПОРЯДОК РЕАЛИЗАЦИИ

Работать этапами.

---

## ЭТАП 0 — Аудит

1. Inspect project.
2. Inspect `/tmp/design`.
3. Inspect Placeo.
4. Создать audit docs.
5. Зафиксировать Laravel/PHP versions.
6. Зафиксировать архитектурные решения.

Результат:

* документация;
* план;
* никаких слепых переписываний.

---

## ЭТАП 1 — Foundation

* базовый Laravel;
* layouts;
* design tokens;
* users;
* auth identities;
* roles;
* policies;
* settings;
* audit;
* queues.

---

## ЭТАП 2 — Authentication

* Yandex;
* flash call;
* VK spike/adapter;
* Sber adapter;
* account merge.

---

## ЭТАП 3 — Geography и categories

* cities;
* city selector;
* OSM;
* geocoder abstraction;
* category tree;
* admin category management.

---

## ЭТАП 4 — Seller

* become seller;
* legal form;
* email verification;
* seller panel;
* public seller card;
* AI seller description moderation.

---

## ЭТАП 5 — Products

* manual products;
* images;
* characteristics;
* moderation;
* public product card;
* listing.

---

## ЭТАП 6 — YML

* feeds;
* addresses;
* staging;
* parser;
* import runs;
* idempotency;
* overrides;
* AI classification;
* AI text moderation;
* images;
* scheduler.

---

## ЭТАП 7 — Buyer commerce

* favorites;
* cart;
* persistent DB storage;
* selected checkout;
* split orders;
* status history.

---

## ЭТАП 8 — Chats

* Placeo reuse/adaptation;
* conversations;
* AI moderation;
* portal intervention.

---

## ЭТАП 9 — Reviews и rating

* eligibility;
* moderation;
* aggregation.

---

## ЭТАП 10 — Notifications

* in-app;
* seller email;
* stale orders;
* status updates;
* price drop;
* Telegram admin events.

---

## ЭТАП 11 — Admin

* dashboard;
* sellers;
* users;
* orders;
* YML;
* categories;
* dialogs;
* AI/system visibility;
* settings.

---

## ЭТАП 12 — SEO

* meta;
* OG;
* schema;
* breadcrumbs;
* sitemap;
* canonical;
* redirects.

---

## ЭТАП 13 — PWA

* manifest;
* service worker;
* offline;
* push foundation.

---

## ЭТАП 14 — Hardening

* security tests;
* performance;
* mobile audit;
* no horizontal overflow;
* N+1;
* queue failures;
* external API failures.

---

# 74. DEFINITION OF DONE

Функция считается готовой только когда:

1. Backend реализован.
2. Frontend реализован.
3. Mobile state реализован.
4. Authorization проверена.
5. Validation есть.
6. Errors обработаны.
7. Loading state есть.
8. Empty state есть.
9. Tests есть.
10. Documentation обновлена.
11. Нет obvious N+1.
12. Нет секретов в коде.
13. Нет horizontal overflow.
14. Нет placeholder logic вместо интеграции.

---

# 75. ЗАПРЕТЫ ДЛЯ CLAUDE CODE

Нельзя:

* писать весь marketplace одним giant controller;
* хранить cart только в localStorage;
* использовать float для денег;
* импортировать YML синхронно в HTTP request;
* скачивать 1000 изображений в одном request;
* загружать весь XML в память;
* доверять внешним URLs;
* использовать YML categories как категории teeu;
* позволять AI придумать category ID;
* публиковать pending moderation content;
* создавать duplicate users при разных OAuth;
* хранить DeepSeek key открытым;
* хардкодить AI model по всему проекту;
* удалять orders вместе с product;
* массово деактивировать товары после failed YML;
* перезаписывать manual description override следующим YML;
* показывать чужие orders/chats по изменению ID;
* делать публичный каталог client-only SPA;
* считать hidden admin URL защитой;
* кэшировать private PWA pages бездумно;
* скрывать CSS-проблемы через глобальный `overflow-x:hidden`;
* использовать fake API response в production code;
* оставлять критические TODO вместо реализации.

---

# 76. ПЕРВЫЙ ОТВЕТ/ПЕРВОЕ ДЕЙСТВИЕ CLAUDE CODE

Не начинай с генерации десятков файлов.

Сначала:

1. Покажи структуру текущего проекта.
2. Определи версии:

   * PHP;
   * Laravel;
   * Node;
   * DB assumptions.
3. Проанализируй `/tmp/design`.
4. Найди Placeo.
5. Проанализируй перечисленные reference mechanisms.
6. Создай audit docs.
7. Составь конкретный implementation plan по существующему repository.
8. После этого приступай к Этапу 1.

При этом не останавливай работу из-за мелких неоднозначностей.

Используй решения, явно зафиксированные в этом ТЗ.

Если обнаружен реальный архитектурный конфликт:

* зафиксируй его;
* выбери безопасное решение;
* не уничтожай существующую рабочую функциональность.

---

# 77. ИТОГОВАЯ ЦЕЛЬ

Результатом должен быть не «сайт с несколькими страницами», а полноценная база маркетплейса teeu:

* покупатели;
* продавцы;
* seller panel;
* YML-фиды;
* несколько feeds одного seller;
* собственное category tree;
* AI-классификация;
* AI-модерация;
* безопасный image pipeline;
* persistent cart;
* multi-seller split checkout;
* orders;
* chats;
* reviews;
* ratings;
* favorites;
* price-drop subscriptions;
* geo targeting;
* SEO;
* PWA;
* notifications;
* admin dashboard;
* Telegram operational alerts;
* audit;
* queues;
* scheduler;
* tests;
* production documentation.

Все решения должны быть реализованы последовательно, безопасно и расширяемо.
