*/ protected $commands = [ ImportLegacyUsers::class, \App\Console\Commands\EnforceUsernamePolicy::class, ImportCategories::class, MigrateFeaturedWorks::class, \App\Console\Commands\AvatarsMigrate::class, \App\Console\Commands\AvatarsBulkUpdate::class, \App\Console\Commands\ResetAllUserPasswords::class, CleanupUploadsCommand::class, PublishScheduledArtworksCommand::class, BackfillArtworkEmbeddingsCommand::class, AggregateSimilarArtworkAnalyticsCommand::class, AggregateFeedAnalyticsCommand::class, AggregateTagInteractionAnalyticsCommand::class, SeedTagInteractionDemoCommand::class, EvaluateFeedWeightsCommand::class, CompareFeedAbCommand::class, AiTagArtworksCommand::class, SyncCountriesCommand::class, \App\Console\Commands\MigrateFollows::class, RecalculateTrendingCommand::class, RecalculateRankingsCommand::class, MetricsSnapshotHourlyCommand::class, RecalculateHeatCommand::class, ]; /** * Define the application's command schedule. */ protected function schedule(\Illuminate\Console\Scheduling\Schedule $schedule): void { $schedule->command('uploads:cleanup')->dailyAt('03:00'); // Publish artworks whose scheduled publish_at has passed $schedule->command('artworks:publish-scheduled') ->everyMinute() ->name('publish-scheduled-artworks') ->withoutOverlapping(2) // prevent overlap up to 2 minutes ->runInBackground(); $schedule->command('analytics:aggregate-similar-artworks')->dailyAt('03:10'); $schedule->command('analytics:aggregate-feed')->dailyAt('03:20'); $schedule->command('analytics:aggregate-tag-interactions')->dailyAt('03:30'); // Recalculate trending scores every 30 minutes (staggered to reduce peak load) $schedule->command('skinbase:recalculate-trending --period=24h')->everyThirtyMinutes(); $schedule->command('skinbase:recalculate-trending --period=7d --skip-index')->everyThirtyMinutes()->runInBackground(); // ── Ranking system (rank_v1) ──────────────────────────────────────── // Step 1: compute per-artwork scores every hour at :05 $schedule->job(new RankComputeArtworkScoresJob)->hourlyAt(5)->runInBackground(); // Step 2: build ranked lists every hour at :15 (after scores are ready) $schedule->job(new RankBuildListsJob)->hourlyAt(15)->runInBackground(); // ── Ranking Engine V2 — runs every 30 min ────────────────────────── $schedule->command('nova:recalculate-rankings --sync-rank-scores') ->everyThirtyMinutes() ->name('ranking-v2') ->withoutOverlapping() ->runInBackground(); $schedule->job(new UpdateLeaderboardsJob) ->hourlyAt(20) ->name('leaderboards-refresh') ->withoutOverlapping() ->runInBackground(); // ── Rising Engine (Heat / Momentum) ───────────────────────────────── // Step 1: snapshot metric totals every hour at :00 $schedule->command('nova:metrics-snapshot-hourly') ->hourly() ->name('metrics-snapshot-hourly') ->withoutOverlapping() ->runInBackground(); // Step 2: recalculate heat scores every 15 minutes $schedule->command('nova:recalculate-heat') ->everyFifteenMinutes() ->name('recalculate-heat') ->withoutOverlapping() ->runInBackground(); // Step 3: prune old snapshots daily at 04:00 $schedule->command('nova:prune-metric-snapshots --keep-days=7') ->dailyAt('04:00'); $schedule->command('skinbase:sync-countries') ->monthlyOn(1, '03:40') ->name('sync-countries') ->withoutOverlapping() ->runInBackground(); } /** * Register the commands for the application. */ protected function commands(): void { $this->load(__DIR__ . '/Commands'); require base_path('routes/console.php'); } }