GWPTD Analytics — mp.hyp.ru
Аналитический дашборд продаж Global Watch Parts Trading на маркетплейсах.
Laravel 12 + Filament 3 + MySQL 8.0, два интерфейса на одном репозитории:
/new (целевой) — Python Data API + kernel ETL, и /admin (legacy, заморожен).
⛔ Перед любой правкой кода — AGENTS.md: пайплайн local→s3→s2-по-OK, канон «всё через 1С», где что лежит. Этот README — только навигация; детали архитектуры и стратегии см. по ссылкам ниже.
Маркетплейсы
Реестр площадок, их идентификаторов и схем работы порождается из кода — docs/backend/generated/MARKETPLACES-AND-IDENTITY.md. Там же порядок приведения внешних идентификаторов к нашему артикулу.
Полный перечень эндпоинтов сбора (81 спека: метод, путь, окно, постраничность, ограничения по частоте) — docs/backend/generated/ENDPOINTS-CATALOG.md.
Здесь список не дублируется намеренно: рукописная копия расходится с кодом —
прежняя таблица в этом файле показывала одиннадцать площадок, тогда как в ядре
их десять, а mp_id=7 отсутствует вовсе.
Стек
- Laravel 12 + Filament 3 — один Laravel-репозиторий, два деплоя интерфейса
(
INTERFACE_VARIANT=admin|new,config/gwptd.php). - PHP 8.4/8.5, PhpSpreadsheet, Guzzle
- Python 3.11 (
data_api/, FastAPI) — HTTP API поверх kernel-слоя, читаетgwptd_kernel.* - Python (
marketplace-collector-v3/) — универсальный ProductSpec runtime:next_collector.py→gwptd_intake→kernel/etl/(build_fact_*) →gwptd_kernel - MySQL 8.0 —
lamoda_reports(legacy-схема, общая для обоих интерфейсов) +gwptd_intake/gwptd_kernel/gwptd_monitoring(новый контур, ADR-0008) - Docker (PHP-FPM + Nginx + Supervisor)
- Google Drive API — синхронизация xlsx-отчётов (legacy, консервируется — не покрыто API)
Целевая архитектура — new-first (стратегия владельца, 2026-07-02)
Инвестируем в новое, старое замораживаем и в итоге выключаем.
/new— целевой интерфейс: 59 PageSpec и специализированные workflow-страницы читают данные черезApp\Services\DataApi\DataApiClient→ HTTP → Python Data API (data_api/) →gwptd_kernel.*, наполняемый ETL изmarketplace-collector-v3/kernel/etl/. Единственное текущее исключение —gwptd_kernel.shipmentsпишется из Laravel напрямую (осознанно, см. ADR-0014 (выведен в архив:docs/_archive/docs-archive-2026-08-legacy.zip))./admin— старый интерфейс (RatingService,TurnoverCalculationService,BitmapService, legacy-коллекторapp/Services/Marketplace/*+marketplace-collector/, старые*Resource/*Page) — заморожен: не развивается, не чинится сверх P0-безопасности, оставлен только для сверки «было→стало». Старые страницы и Resources гейчены трейтомApp\Filament\Concerns\OldInterfaceOnly(canAccess()только приINTERFACE_VARIANT=admin) — физически недоступны в/new.- Дальше: после контрольного окна сверки kernel↔legacy (ADR-0006: расхождение <0.5% × 14 дней) —
выключение старого интерфейса и коллектора; в следующих итерациях — удаление старого кода из
кодовой базы. База данных
lamoda_reports(auth/users,warehouse_limits, часть shared-сервисов) переживает выключение интерфейса — cutover БД отдельный, не одномоментный с UI. - Полная стратегия, порядок волн и что уже сделано → docs/remediation/README.md
- docs/remediation/_execution-log.md (выведен в архив:
docs/_archive/docs-archive-2026-08-history.zip).
- docs/remediation/_execution-log.md (выведен в архив:
Быстрый старт (локально)
# 1. SSH-туннель к MySQL (Hetzner)
ssh -f -N -L 3307:127.0.0.1:3306 root@10.8.0.1
# 2. Установка
composer install && cp .env.example .env
php artisan key:generate && php artisan migrate
# 3. Создать пользователя
php artisan make:filament-user
# 4. Запуск
php artisan serve --port=8501
# Дашборд: http://localhost:8501/admin
Деплой S3 Next
Новый интерфейс опубликован через https://mp.hyp.ru/new* на S3. Перед любым
действием сверять точный live SHA через mcp__gwptd__repo_status; S2 остаётся
read-only production oracle, пока владелец отдельно не разрешит изменение.
# S3: только root-owned deploy wrapper и SHA, уже опубликованный в origin.
ssh s3-int /usr/local/sbin/gwptd-next-deploy --expected-sha <40-hex-commit>
# После deploy: read-only проверка checkout.
ssh s3-int 'cd /opt/gwptd-analytics/repo && git status --short --branch && git rev-parse HEAD'
Не использовать ручной git pull, bash deploy.sh, docker compose up,
artisan migrate или ручную замену cron на S3. Эти действия обходят
rollback, deploy lock и MCP reindex. S2 deploy возможен только по отдельному
явному OK владельца.
Artisan-команды
php artisan mp:collect # Остатки + заказы (все МП)
php artisan mp:collect --all # Всё: 8 типов данных
php artisan mp:collect --mp=ozon # Один МП
php artisan rating:calculate # Рейтинг
php artisan shipment:calculate --mp=NAME # Расчёт отгрузки
php artisan turnover:calculate # Оборачиваемость
php artisan import:reports [path] # Импорт XLSX
php artisan sync:supplier # Каталог gwptd.com
Документация
Вход — docs/README.md. Там документация разделена по роду: что порождается из кода, что написано человеком и охраняется проверкой, что выведено в архив. От рода зависит, можно ли файл править руками.
| файл | когда читать |
|---|---|
| AGENTS.md | всегда первым, до любого действия в коде — правила, границы, что заморожено |
| docs/README.md | вход в документацию — три рода, канон, генераторы, архив |
| for-ai-agents/START-HERE.md | первая сессия ИИ-агента: порядок поиска, куда класть правку, грабли |
| docs/backend/generated/COLUMN-REGISTRY.md + generate_column_registry.py | реестр колонок и их происхождения — когда нужен точный разбор схемы |
| docs/remediation/HANDOFF-2026-08-04.md | первым в новой сессии — состояние работ |
| docs/remediation/PLAN-SVEDENIE-2026-08-03.md | что осталось сделать и что ждёт решения владельца |
| docs/remediation/POSTDEPLOY-CHECKLIST.md | девять проверок после выкладки — после каждого S3 deploy |
| docs/remediation/OWNER-DECISIONS-PENDING.md | 16 решений, ожидающих владельца — перед owner-gated работой |
| docs/domain/ | до правки бизнес-логики — формулы, запреты, семантика статусов |
| docs/domain/METHOD-TABLE-REVIEW.md | метод проверки таблиц с данными — перед разбором качества и смысла данных |
| CLAUDE.md | общая инфраструктура Laravel и прежний /admin-слой |
| data_api/README.md | работа с Python Data API |
| marketplace-collector-v3/README.md | коллектор и kernel ETL |
| scripts/docs/check_doc_links.py | сторож локальных Markdown-ссылок — после добавления или переноса документа |
| marketplace-collector-v3/scripts/check_intake_schema_contract.py | контракт ProductSpec ↔ intake-схема — при изменении ProductSpec или миграций, с доступом к БД |
| marketplace-collector-v3/tests/test_returns_sign_invariant.py | сторож знака возвратов, включая известные xfail-пути — при правке проекции возвратов |
Остальное перечислять здесь незачем: список файлов сам устаревает — это и была причина, по которой таблица разрослась до тридцати строк с мёртвыми ссылками.
Архитектура (кратко)
/new (целевой контур):
Маркетплейсы API
↓
specs/products/*.yaml → next_collector.py → gwptd_intake.* (сырой слой)
↓
marketplace-collector-v3/kernel/etl/* → gwptd_kernel.* (dim_*/fact_*, build_fact_rating/turnover/...)
↓
data_api/ (FastAPI, HTTP/JSON) ← единственная дверь чтения kernel-данных
↓
app/Services/DataApi/DataApiClient → PageSpec/SpecDrivenApiPage (Filament) → /new
↓
https://mp.hyp.ru/new
Исключение из границы «PHP читает kernel только через Data API»: ShipmentCalcApiPage::saveBatch()
пишет напрямую в gwptd_kernel.shipments (узаконено ADR-0014 (выведен в архив: docs/_archive/docs-archive-2026-08-legacy.zip)).
/admin (legacy, заморожен):
Маркетплейсы API
↓
MarketplaceCollectorService (PHP artisan mp:collect) + Python marketplace-collector/
↓
MySQL lamoda_reports: mp_stocks_daily, mp_orders_daily, mp_finances, ...
↓
Services: RatingService, ShipmentCalculator, TurnoverCalculationService
↓
Filament Dashboard (OldInterfaceOnly-гейт) + REST API (/api/*)
↓
https://mp.hyp.ru/admin (nginx + Basic Auth + HSTS)
Override-паттерн (legacy): файлы в overrides/ перекрывают app/ через Docker volume mount.
Изменения в логике старого коллектора вносить также в Python-коллектор (/opt/marketplace-collector/).
Новый коллектор (marketplace-collector-v3/) от этого паттерна не зависит.
Безопасность
- Nginx Basic Auth: задаётся через
.env.production(не коммитятся в репо) - HSTS:
max-age=31536000; includeSubDomains - Все credentials в
.env(не в коде); прод-connection'ы безroot/root-дефолтов (epic-09) - Старые SQL-инъекционные
*Resourceзакрыты гейтомOldInterfaceOnly(недостижимы в/new) - Session UUID валидация в ChatController
Обновлено: 2026-07-03 | Стратегия: new-first (см. docs/remediation/)