61 خطوط
2.9 KiB
PHP
61 خطوط
2.9 KiB
PHP
<?php
|
|
|
|
use App\Modules\Collaboration\Application\NotificationOrchestrator;
|
|
use App\Modules\Courses\Domain\CourseVersion;
|
|
use App\Modules\Courses\Domain\Enums\CourseVersionStatus;
|
|
use App\Modules\Publishing\Application\CoursePublication;
|
|
use App\Modules\System\Application\QueueHeartbeat;
|
|
use Illuminate\Foundation\Inspiring;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schedule;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
Artisan::command('inspire', function () {
|
|
$this->comment(Inspiring::quote());
|
|
})->purpose('Display an inspiring quote');
|
|
|
|
Artisan::command('learning:process-schedules', function (CoursePublication $publication) {
|
|
$published = 0;
|
|
$unpublished = 0;
|
|
CourseVersion::query()->where('status', CourseVersionStatus::InReview)->whereNotNull('scheduled_publish_at')->where('scheduled_publish_at', '<=', now())->each(function (CourseVersion $version) use ($publication, &$published) {
|
|
$publication->publish($version);
|
|
$published++;
|
|
});
|
|
CourseVersion::query()->where('status', CourseVersionStatus::Published)->whereNull('unpublished_at')->whereNotNull('scheduled_unpublish_at')->where('scheduled_unpublish_at', '<=', now())->each(function (CourseVersion $version) use ($publication, &$unpublished) {
|
|
$publication->unpublish($version);
|
|
$unpublished++;
|
|
});
|
|
$this->info("Published {$published}; unpublished {$unpublished}.");
|
|
})->purpose('Process scheduled course publication lifecycle');
|
|
|
|
Schedule::command('learning:process-schedules')->everyMinute()->withoutOverlapping();
|
|
|
|
Artisan::command('notifications:process-schedules', function (NotificationOrchestrator $notifications) {
|
|
$result = $notifications->deliverDue();
|
|
$this->info("Notification schedules: {$result['sent']} sent, {$result['skipped']} skipped, {$result['failed']} failed.");
|
|
})->purpose('Deliver due in-app notification schedules');
|
|
|
|
Schedule::command('notifications:process-schedules')->everyMinute()->withoutOverlapping();
|
|
|
|
Artisan::command('system:heartbeat', function () {
|
|
Cache::put('system:scheduler-heartbeat', now(), now()->addMinutes(5));
|
|
$this->info('Scheduler heartbeat recorded.');
|
|
});
|
|
|
|
Artisan::command('exports:prune', function () {
|
|
$cutoff = now()->subDays((int) config('exports.retention_days', 30));
|
|
DB::table('export_jobs')->where('created_at', '<', $cutoff)->whereIn('status', ['completed', 'failed', 'cancelled'])->orderBy('created_at')->each(function ($row): void {
|
|
if ($row->path && $row->disk) {
|
|
Storage::disk($row->disk)->delete($row->path);
|
|
}
|
|
DB::table('export_jobs')->where('id', $row->id)->delete();
|
|
});
|
|
$this->info('Expired export artifacts pruned.');
|
|
});
|
|
|
|
Schedule::command('system:heartbeat')->everyMinute();
|
|
Schedule::job(new QueueHeartbeat)->everyMinute()->withoutOverlapping();
|
|
Schedule::command('exports:prune')->dailyAt('02:30')->withoutOverlapping();
|