option('dry-run'); $all = (bool) $this->option('all'); $userId = $this->argument('user_id'); $chunk = max(1, (int) $this->option('chunk')); $queue = (bool) $this->option('queue'); if ($userId !== null && $all) { $this->error('Provide either a user_id OR --all, not both.'); return self::FAILURE; } if ($userId !== null) { return $this->recomputeSingle((int) $userId, $statsService, $dryRun); } if ($all) { return $this->recomputeAll($statsService, $chunk, $dryRun, $queue); } $this->error('Provide a user_id or use --all.'); return self::FAILURE; } // ─── Single user ───────────────────────────────────────────────────────── private function recomputeSingle(int $userId, UserStatsService $statsService, bool $dryRun): int { $exists = DB::table('users')->where('id', $userId)->exists(); if (! $exists) { $this->error("User {$userId} not found."); return self::FAILURE; } $label = $dryRun ? '[DRY-RUN]' : '[LIVE]'; $this->line("{$label} Recomputing stats for user #{$userId}…"); $computed = $statsService->recomputeUser($userId, $dryRun); $rows = []; foreach ($computed as $col => $val) { $rows[] = [$col, $val ?? '(null)']; } $this->table(['Column', 'Value'], $rows); if ($dryRun) { $this->warn('Dry-run: no changes written.'); } else { $this->info("Stats saved for user #{$userId}."); } return self::SUCCESS; } // ─── All users ──────────────────────────────────────────────────────────── private function recomputeAll( UserStatsService $statsService, int $chunk, bool $dryRun, bool $useQueue ): int { $total = DB::table('users')->whereNull('deleted_at')->count(); $label = $dryRun ? '[DRY-RUN]' : ($useQueue ? '[QUEUE]' : '[LIVE]'); $this->info("{$label} Recomputing stats for {$total} users (chunk={$chunk})…"); if ($useQueue && ! $dryRun) { $dispatched = 0; DB::table('users') ->whereNull('deleted_at') ->orderBy('id') ->chunkById($chunk, function ($users) use (&$dispatched) { $ids = $users->pluck('id')->all(); RecomputeUserStatsJob::dispatch($ids); $dispatched += count($ids); $this->line(" Queued chunk of " . count($ids) . " users (total dispatched: {$dispatched})"); }); $this->info("Done – {$dispatched} users queued for recompute."); return self::SUCCESS; } $processed = 0; $bar = $this->output->createProgressBar($total); $bar->start(); DB::table('users') ->whereNull('deleted_at') ->orderBy('id') ->chunkById($chunk, function ($users) use ($statsService, $dryRun, &$processed, $bar) { foreach ($users as $user) { $statsService->recomputeUser((int) $user->id, $dryRun); $processed++; $bar->advance(); } }); $bar->finish(); $this->newLine(); $suffix = $dryRun ? ' (no changes written – dry-run)' : ''; $this->info("Done – {$processed} users recomputed{$suffix}."); return self::SUCCESS; } }