Кейс · Laravel · маркетплейсы · 2026

Книжный конвейер

Система, которая превращает стопку букинистических книг и несколько фотографий в живые карточки на Ozon, Wildberries и Яндекс Маркете — с распознаванием, оценкой цены по рынку, публикацией и сквозным учётом остатков.

55 500строк PHP
3маркетплейса
60сервисов
30фоновых задач
45CLI-команд
302теста

Задача

Букинистика ломает обычную логику электронной торговли: у товара нет партии и нет повторной поставки. Одна книга — одна единица, со своим состоянием, своей ценой и своим набором фотографий. Завести её вручную на три площадки — это перепечатать выходные данные, найти аналоги, прикинуть цену, подобрать категорию и атрибуты, а после продажи не забыть обнулить остаток на двух других маркетплейсах.

Проект убирает эту рутину целиком. Оператор фотографирует книгу — всё остальное система делает сама и показывает человеку только то, что требует решения.

Ключевое ограничение архитектуры: товар физически один. Продажа на одной площадке обязана мгновенно погасить остаток на двух других — иначе магазин продаёт книгу, которой уже нет.

Семь стадий конвейера

  1. Приёмка и съёмка

    Партия заводится одним экраном массового создания: артикул присваивается автоматически, фотографии грузятся пачкой. Роль «фотограф» намеренно урезана до этого единственного экрана — человек на приёмке не может ничего сломать в каталоге.

    Blade + Alpine · загрузка и поворот изображений · Intervention Image
  2. Распознавание по фотографиям

    Vision-модель читает обложку и выходные данные: название, автор, издательство, ISBN, год, серия, тип обложки, возрастной рейтинг. Отдельным проходом проверяются названия, набранные капсом, — модель возвращает корректный регистр, но только если текст совпадает с исходным без учёта регистра и пунктуации.

    Габариты и вес подставляются по шаблонам размеров: модель определяет класс книги, шаблон даёт миллиметры и граммы, пригодные для карточки маркетплейса.

    OpenAI Vision · драйверы openai / mock · очередь с heartbeat и авто-рестартом
  3. Поиск аналогов на площадках

    Два отдельных Node-сервиса на Playwright ходят в поиск Wildberries и Ozon настоящим браузером — с сессионными куками, прокси и health-проверками. Яндекс Маркет опрашивается по API. Найденные карточки сопоставляются с книгой по названию, автору и ISBN с числовой оценкой похожести.

    Express + Playwright в отдельных контейнерах · Shadowsocks-прокси · таймауты и фолбэки
  4. Оценка цены

    Цена считается по правилам товароведа, перенесённым в код: учитываются только аналоги с такой же обложкой, электронные издания отбрасываются, явные выбросы отсекаются по коридору отношений к медиане, действует минимальный ценник. Итоговая цена продажи выводится из ценника со скидкой, автограф автора даёт наценку.

    Если разброс цен по аналогам слишком велик, подключается AI-арбитр: он выбирает опорную группу и объясняет решение текстом, который видит оператор.

    Медиана и среднее · фильтр выбросов · ручная цена защищена от перезаписи
  5. Карточка и публикация

    Генерируются описание и хэштеги, подбирается категория Ozon вместе с её обязательными атрибутами, собирается payload под формат каждой площадки. Публикация идёт фоновой задачей с повторами, режимом dry-run и человеческой расшифровкой ошибок площадки вместо сырого кода ответа.

    Отдельный слой следит за дублями: одна и та же книга не уедет второй карточкой, а уже проданная карточка Ozon может быть переиспользована вместо создания новой.

    Журнал публикаций с диагностикой · защита от дублей · кросс-публикация между площадками
  6. Заказы

    Заказы приходят двумя путями — вебхуками площадок и регулярным опросом кабинетов. По каждому строится таймлайн событий, отслеживаются возвраты и подтверждение возврата товара на склад.

    Вебхуки Ozon / WB / ЯМ · поллинг FBS · таймлайн и возвраты
  7. Остатки

    Единый складской учёт: продажа на любой площадке проводится движением по складу и сразу гасит остаток на остальных. Ежечасно собирается снимок рассинхрона — где локальный остаток разошёлся с маркетплейсом, — и его можно вылечить массовой синхронизацией из интерфейса.

    Движения склада с аудитом · версии выгрузок · защита от двойного списания

Три площадки, три разных API

Каждый маркетплейс подключается несколькими кабинетами, у каждого свой формат карточки, своя модель складов и свои правила модерации. Общая доменная модель одна, различия изолированы в сервисах площадок.

Ozon

  • Синхронизация дерева категорий и атрибутов, подбор книжной категории
  • Контент-рейтинг карточек и его регулярное обновление
  • Склады, остатки, импорт товаров и сверка каталога
  • Переиспользование проданных карточек и починка ложного статуса «продано»
  • Собственный поисковый микросервис через прокси

Wildberries

  • Карточки, цены и отчёты по кабинетам
  • Остатки с «прогревом» новых карточек и починкой зависших
  • Контроль длины названия под лимиты площадки
  • Поиск аналогов браузером с куками и опциональным прокси
  • Полный лог API-запросов для разбора инцидентов

Яндекс Маркет

  • Кабинеты с проверкой доступа и подбором книжной категории
  • Публикация карточек и финализация статуса
  • Импорт товаров, ежечасная синхронизация остатков
  • Опрос заказов и участие в общем складском учёте

Что держит систему на ногах

Большая часть кода написана не ради счастливого сценария, а ради того, что происходит после него: площадка ответила ошибкой, задача упала, остаток разошёлся, габариты не прошли модерацию.

  • Детектор рассинхрона — ежечасный снимок расхождений между локальными остатками и данными площадок, с массовой починкой и публикацией недостающих карточек.
  • Аудит габаритов — находит карточки с некорректными ВГХ, строит план исправления и применяет его фоновой задачей.
  • Паритет каталогов — сверка того, что опубликовано на трёх площадках: дедупликация товаров, поиск «пропавших» карточек, кросс-публикация недостающих.
  • Health-панель пайплайна — показывает книги, застрявшие между стадиями, позволяет перезапустить распознавание без похода в консоль.
  • Резервное копирование — дамп базы на FTP каждые два часа в рабочее время, фотографии — раз в сутки инкрементально.
  • Тесты и защита боевой базы — 302 теста на доменной логике публикации, остатков и заказов; отдельный guard не даёт прогону тестов уйти в продакшен-базу.

Архитектура

Backend
PHP 8.3 — строгая доменная модель на 32 сущностях
Laravel 13 — Eloquent, очереди, планировщик, Sanctum
MySQL — 72 миграции, индексы под каталожные выборки
PhpSpreadsheet — импорт остатков из таблиц поставщика
Frontend
Blade — 78 шаблонов админ-интерфейса
Alpine.js — точечная интерактивность без SPA
Tailwind 4 — сборка через Vite
Роли — админ, менеджер, модератор, фотограф
Инфраструктура
Docker — nginx, php-fpm, MySQL, воркеры под supervisor
Node · Playwright — два поисковых микросервиса в контейнерах
Certbot — TLS с автопродлением
Прокси — Shadowsocks-контейнер для поиска площадки
Интеграции
Ozon Seller API — каталог, карточки, склады, заказы
Wildberries API — карточки, цены, остатки, отчёты
Яндекс Маркет API — карточки, остатки, заказы
OpenAI — распознавание, описания, арбитраж цены

Результат

Продакшен-система, в которой оператор занимается только тем, что нельзя автоматизировать: оценивает состояние книги и подтверждает спорные решения. Всё остальное — распознавание, поиск аналогов, ценообразование, публикация на три площадки, приём заказов и сведение остатков — идёт фоном по расписанию и не требует ежедневного вмешательства.

32модели
31контроллер
72миграции
78экранов
14 100строк тестов
20+задач в планировщике

← Краткое описание этой работы в портфолио

Laravel 13 · PHP 8.3 · MySQL · Docker · Playwright · OpenAI · Ozon · Wildberries · Яндекс Маркет


Сделать заказ

| необходим для связи с вами
В кротчайшие сроки я свяжусь с вами.

Также вы можетете связать со мной:
telegram: @ifwcom