86 lines
3.7 KiB
PHP
86 lines
3.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\User;
|
|
use App\Services\Supplier\Import\SupplierProjectImporter;
|
|
use Illuminate\Console\Command;
|
|
|
|
/**
|
|
* Разовый импорт активных проектов поставщика (аккаунт lkomega) как проектов
|
|
* Лидерры под тенантом владельца. По умолчанию dry-run (печатает план, ничего
|
|
* не пишет). С --commit пишет в БД через pgsql_supplier (BYPASSRLS), портал НЕ
|
|
* трогает. Идемпотентна.
|
|
*
|
|
* Plan: docs/superpowers/plans/2026-05-22-supplier-projects-import-lkomega.md
|
|
*/
|
|
class ImportSupplierProjectsCommand extends Command
|
|
{
|
|
protected $signature = 'supplier:import-projects
|
|
{--tenant= : email пользователя тенанта (напр. info@lkomega.ru)}
|
|
{--commit : выполнить запись (без флага — только dry-run)}';
|
|
|
|
protected $description = 'Усыновить активные проекты поставщика как проекты Лидерры под тенантом (dry-run по умолчанию)';
|
|
|
|
public function handle(SupplierProjectImporter $importer): int
|
|
{
|
|
$email = (string) $this->option('tenant');
|
|
if ($email === '') {
|
|
$this->error('Укажите --tenant=<email>');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$tenantId = User::on('pgsql_supplier')->where('email', $email)->value('tenant_id');
|
|
if ($tenantId === null) {
|
|
$this->error("Тенант для email '{$email}' не найден.");
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$plan = $importer->buildPlan((int) $tenantId);
|
|
|
|
$this->info(sprintf('Тенант %s (id=%d). К созданию: %d проектов. Пропущено строк/групп: %d.',
|
|
$email, $tenantId, count($plan['planned']), count($plan['skipped'])));
|
|
|
|
$this->table(
|
|
['Тип', 'Идентификатор', 'Тег', 'Регионы', 'Лимит', 'Площадки (external_id)'],
|
|
array_map(fn (array $p): array => [
|
|
$p['signal_type'],
|
|
$this->mask($p['signal_identifier'] ?? ($p['sms_senders'][0] ?? '')),
|
|
mb_substr((string) $p['tag'], 0, 30),
|
|
$p['regions'] === [] ? 'вся РФ' : implode(',', $p['regions']),
|
|
(string) $p['daily_limit_target'],
|
|
collect($p['platforms'])->map(fn (array $pl): string => $pl['platform'].':'.$pl['external_id'])->implode(' '),
|
|
], $plan['planned']),
|
|
);
|
|
|
|
if ($plan['skipped'] !== []) {
|
|
$this->warn('Пропуски:');
|
|
foreach ($plan['skipped'] as $s) {
|
|
$this->line(sprintf(' - [%s] %s', $s['reason'], $this->mask($s['label'])));
|
|
}
|
|
}
|
|
|
|
if (! $this->option('commit')) {
|
|
$this->comment('DRY-RUN: ничего не записано. Повторите с --commit для реальной записи.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$result = $importer->commit($plan, (int) $tenantId);
|
|
$this->info(sprintf('Создано: проектов=%d, supplier_projects=%d, связок=%d.',
|
|
$result['created_projects'], $result['created_supplier_projects'], $result['created_links']));
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
/** Маскирует цифровые хвосты (телефоны) для вывода (152-ФЗ). */
|
|
private function mask(string $value): string
|
|
{
|
|
return (string) preg_replace_callback('/\d{4,}/', static fn (array $m): string => substr($m[0], 0, 2).str_repeat('*', max(0, strlen($m[0]) - 4)).substr($m[0], -2), $value);
|
|
}
|
|
}
|