26999ca597
Три группы накопившихся auto-правок (НЕ ручные): 1. markdownlint --fix auto-format (~25 .md в docs/superpowers/, docs/security/marketing-vet.md, docs/adr/015, docs/deploy/lkomega-runbook): MD031/MD032 (blank lines around fence/list) + MD004 (bullet markers `+`→`-`). Содержательных текстовых правок 3: ADR-015 bullet, sprint5d-cleanup bullet, router-discipline trailing space. 2. lefthook 2.1.6 → 2.1.8 (package.json + lock): patch-bump, авто-резолвил npm. 3. Observer runtime (docs/observer/): episodes-2026-05.jsonl +420 строк (текущая активность мозга), STATUS.md regen, .pii-counters / .read-counter тики, +2026-05-24-brain-retro.md note. Цель — разблокировать merge feat/llm-first-router → main (этап 0 плана постановки в боевой). Содержание ветки не трогает.
100 lines
5.7 KiB
Markdown
100 lines
5.7 KiB
Markdown
# Runbook: импорт проектов lkomega → info@lkomega.ru
|
||
|
||
Разовая операция на боевом liderra.ru (`111.88.246.137`). Усыновляет активные
|
||
проекты поставщика crm.bp-gr.ru (аккаунт lkomega) как проекты Лидерры под
|
||
тенантом info@lkomega.ru. **Портал не трогается** (никаких save/update/delete).
|
||
|
||
Plan: `docs/superpowers/plans/2026-05-22-supplier-projects-import-lkomega.md`
|
||
Spec: `docs/superpowers/specs/2026-05-22-supplier-projects-import-lkomega-design.md`
|
||
|
||
## Деплой команды
|
||
|
||
Скопировать на сервер в `/var/www/liderra/app` (бэкап заменяемого `SupplierRegions.php`):
|
||
|
||
- `app/Support/SupplierRegions.php` (изменён — добавлен `mapFromSupplier`)
|
||
- `app/Services/Supplier/Import/SupplierImportMapper.php` (новый)
|
||
- `app/Services/Supplier/Import/SupplierProjectImporter.php` (новый)
|
||
- `app/Console/Commands/ImportSupplierProjectsCommand.php` (новый)
|
||
|
||
```bash
|
||
cp /var/www/liderra/app/app/Support/SupplierRegions.php /var/www/liderra/app/app/Support/SupplierRegions.php.bak-$(date +%Y%m%d-%H%M%S)
|
||
# scp 4 файла...
|
||
cd /var/www/liderra/app && php artisan optimize:clear # сброс кэша команд/конфига
|
||
```
|
||
|
||
Команда — не очередь/воркер, `queue:restart` не нужен.
|
||
|
||
## Шаг 1 — dry-run (показать план, ничего не пишет)
|
||
|
||
```bash
|
||
cd /var/www/liderra/app && php artisan supplier:import-projects --tenant=info@lkomega.ru
|
||
```
|
||
|
||
Вывод: число проектов к созданию, таблица (тип / идентификатор[маскирован] /
|
||
тег / регионы / лимит / площадки B1:id …), список пропусков (`unsupported_source`
|
||
для dop2, `regions_exclude`, `sms_unparseable`, `already_exists`).
|
||
**Показать заказчику → получить «ок».**
|
||
|
||
## Шаг 2 — реальный прогон
|
||
|
||
```bash
|
||
cd /var/www/liderra/app && php artisan supplier:import-projects --tenant=info@lkomega.ru --commit
|
||
```
|
||
|
||
Вывод: `Создано: проектов=N, supplier_projects=M, связок=K.`
|
||
|
||
## Шаг 3 — пост-проверка
|
||
|
||
```bash
|
||
# Число проектов под тенантом (подставить tenant_id info@lkomega.ru):
|
||
php artisan tinker --execute="echo App\Models\Project::on('pgsql_supplier')->where('tenant_id', <ID>)->count();"
|
||
```
|
||
|
||
- Выборочно сверить 2–3 проекта: `daily_limit_target` = сумме площадок; регионы корректны (ГИБДД→Лидерра).
|
||
- **Проверить целостность площадок каждого проекта** (см. оговорку ниже):
|
||
каждый проект должен иметь столько связок `project_supplier_links`, сколько площадок было в группе (обычно 3).
|
||
|
||
```bash
|
||
php artisan tinker --execute="App\Models\Project::on('pgsql_supplier')->where('tenant_id',<ID>)->get()->each(fn(\$p)=>print(\$p->id.': '.\$p->supplierProjects()->count().PHP_EOL));"
|
||
```
|
||
|
||
- Подтвердить, что на портале crm.bp-gr.ru **НЕ появилось новых проектов** (команда его не дёргает).
|
||
|
||
## Атомарность
|
||
|
||
`commit()` оборачивает запись **каждого проекта в отдельную транзакцию** на проде
|
||
(`DB::connection('pgsql_supplier')->transaction(...)` — Project + все `supplier_projects` +
|
||
все pivot-связки группы атомарно). Сбой посреди группы → транзакция откатывается → ни
|
||
проекта, ни partial-связок не остаётся, БД консистентна. Уже созданные ДО сбоя проекты
|
||
сохраняются (per-group, не per-run).
|
||
|
||
В прод-команде это включается автоматически: гейт `getPdo()->inTransaction()` — false на
|
||
проде → BEGIN/COMMIT per item; true только под тестовым харнессом `SharesSupplierPdo`
|
||
(общий PDO уже в транзакции) → внутренний BEGIN пропускается, чтобы избежать
|
||
«already active transaction» в Pest.
|
||
|
||
При ошибке посреди прогона — просто запустить `--commit` повторно: идемпотентность
|
||
(`already_exists` по tenant+signal + `firstOrCreate` по `(platform, unique_key,
|
||
subject_code)`) пропустит уже импортированные проекты и до-создаст оставшиеся.
|
||
|
||
## Откат
|
||
|
||
Импортированные проекты под тенантом — soft-archive через ЛК или:
|
||
|
||
```php
|
||
App\Models\Project::on('pgsql_supplier')->where('tenant_id', <ID>)
|
||
->update(['is_active' => false, 'archived_at' => now()]);
|
||
```
|
||
|
||
`supplier_projects`/pivot можно оставить (они указывают на реальные портальные проекты,
|
||
их используют и другие потоки).
|
||
|
||
## NB про среду
|
||
|
||
- На worktree-сборке 2 теста `SupplierPortalClient*Test` падают из-за отсутствия node-модуля
|
||
`playwright` — это известный worktree-only квирк (не регрессия), на боевом/основном
|
||
checkout с `node_modules` они зелёные.
|
||
- Larastan: production-код чист; test-only `TestCall`/Mockery (квирк #25) добавляются в
|
||
`phpstan-baseline.neon` на чистом checkout при интеграции (не из worktree — там дрейф
|
||
ide-helper искажает счётчики).
|