Files
portal/app/app/Console/Commands/PartitionsCreateMonths.php
T
Дмитрий ef4df2925f feat(import): сервис MonthlyPartitionManager + рефактор partitions:create-months
Выносит DDL-логику создания месячных RANGE-партиций из команды
PartitionsCreateMonths в переиспользуемый сервис MonthlyPartitionManager.
Сервис используется командой (DRY) и будет использован HistoricalImportService
для партиций под исторические даты CSV.

- MonthlyPartitionManager::ensureRange(table, from, to) — гарантирует партиции
  под диапазон дат, идемпотентно; отвергает незарегистрированные таблицы
- MonthlyPartitionManager::ensureMonth(table, monthStart) — одна партиция
- PartitionsCreateMonths рефакторена: убраны PARTITIONED_TABLES, partitionExists(),
  use DB; inject MonthlyPartitionManager через handle()
- Test: MonthlyPartitionManagerTest (3 теста, DatabaseTransactions — DDL откат)
- Regression: PartitionsCreateMonthsTest (4 теста) — зелёный, поведение не изменилось

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:37:29 +03:00

63 lines
2.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Console\Commands;
use App\Services\MonthlyPartitionManager;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
/**
* Создаёт ежемесячные партиции для `deals` и `supplier_lead_costs`
* на N месяцев вперёд от текущей даты.
*
* Замена `pg_partman` на native Windows-стеке (расширение недоступно
* без сборки из исходников). Запускается ежесуточно через Windows Task
* Scheduler / cron — идемпотентна (CREATE TABLE IF NOT EXISTS).
*
* По дефолту 2 месяца вперёд (паритет с инициализацией schema.sql:
* 6 партиций при `migrate:fresh`, последующие месяцы — этим cron'ом).
*
* Источник: db/schema.sql §5 (deals partition), §8.5 (supplier_lead_costs);
* project_phase1_strategy.md (pg_partman заменён ручным cron'ом).
*/
class PartitionsCreateMonths extends Command
{
/** @var string */
protected $signature = 'partitions:create-months {--ahead=2 : Сколько месяцев вперёд создать партиций}';
/** @var string */
protected $description = 'Создаёт ежемесячные партиции deals и supplier_lead_costs на N месяцев вперёд (idempotent)';
public function handle(MonthlyPartitionManager $manager): int
{
$ahead = max(1, (int) $this->option('ahead'));
$now = Carbon::now()->startOfMonth();
$created = 0;
$skipped = 0;
for ($i = 0; $i <= $ahead; $i++) {
$monthStart = $now->copy()->addMonths($i);
foreach (MonthlyPartitionManager::PARTITIONED_TABLES as $table) {
$partitionName = sprintf('%s_%s', $table, $monthStart->format('Y_m'));
if ($manager->ensureMonth($table, $monthStart)) {
$created++;
$this->info(" create <fg=green>{$partitionName}</>");
} else {
$skipped++;
$this->line(" skip <fg=gray>{$partitionName}</> (already exists)");
}
}
}
$this->newLine();
$this->info("Done: {$created} created, {$skipped} skipped (ahead={$ahead}).");
return self::SUCCESS;
}
}