messages implemented
This commit is contained in:
147
app/Console/Commands/RecomputeUserStatsCommand.php
Normal file
147
app/Console/Commands/RecomputeUserStatsCommand.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\RecomputeUserStatsJob;
|
||||
use App\Services\UserStatsService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Recompute user_statistics counters from authoritative source tables.
|
||||
*
|
||||
* Usage:
|
||||
* # Recompute a single user (live)
|
||||
* php artisan skinbase:recompute-user-stats 42
|
||||
*
|
||||
* # Dry-run for a single user
|
||||
* php artisan skinbase:recompute-user-stats 42 --dry-run
|
||||
*
|
||||
* # Recompute all users in chunks of 500
|
||||
* php artisan skinbase:recompute-user-stats --all --chunk=500
|
||||
*
|
||||
* # Recompute all users via queue (one job per chunk)
|
||||
* php artisan skinbase:recompute-user-stats --all --queue
|
||||
*/
|
||||
class RecomputeUserStatsCommand extends Command
|
||||
{
|
||||
protected $signature = 'skinbase:recompute-user-stats
|
||||
{user_id? : The ID of a single user to recompute}
|
||||
{--all : Recompute stats for ALL non-deleted users}
|
||||
{--chunk=1000 : Chunk size when --all is used}
|
||||
{--dry-run : Show what would be written without saving}
|
||||
{--queue : Dispatch recompute jobs to the queue (--all mode only)}';
|
||||
|
||||
protected $description = 'Rebuild user_statistics counters from authoritative source tables';
|
||||
|
||||
public function handle(UserStatsService $statsService): int
|
||||
{
|
||||
$dryRun = (bool) $this->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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user