ef4df2925f
Выносит 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>
63 lines
2.4 KiB
PHP
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;
|
|
}
|
|
}
|