optimizations
This commit is contained in:
148
app/Services/CollectionBackgroundJobService.php
Normal file
148
app/Services/CollectionBackgroundJobService.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Jobs\RefreshCollectionHealthJob;
|
||||
use App\Jobs\RefreshCollectionQualityJob;
|
||||
use App\Jobs\RefreshCollectionRecommendationJob;
|
||||
use App\Jobs\ScanCollectionDuplicateCandidatesJob;
|
||||
use App\Models\Collection;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Collection as SupportCollection;
|
||||
|
||||
class CollectionBackgroundJobService
|
||||
{
|
||||
public function dispatchQualityRefresh(Collection $collection, ?User $actor = null): array
|
||||
{
|
||||
RefreshCollectionQualityJob::dispatch((int) $collection->id, $actor?->id)->afterCommit();
|
||||
|
||||
return [
|
||||
'status' => 'queued',
|
||||
'job' => 'quality_refresh',
|
||||
'scope' => 'single',
|
||||
'count' => 1,
|
||||
'collection_ids' => [(int) $collection->id],
|
||||
'message' => 'Quality refresh queued.',
|
||||
];
|
||||
}
|
||||
|
||||
public function dispatchHealthRefresh(?Collection $collection = null, ?User $actor = null): array
|
||||
{
|
||||
$targets = $collection ? collect([$collection]) : $this->healthTargets();
|
||||
|
||||
$targets->each(fn (Collection $item) => RefreshCollectionHealthJob::dispatch((int) $item->id, $actor?->id, 'programming-eligibility')->afterCommit());
|
||||
|
||||
return $this->queuedPayload('health_refresh', $targets, 'Health and eligibility refresh queued.');
|
||||
}
|
||||
|
||||
public function dispatchRecommendationRefresh(?Collection $collection = null, ?User $actor = null, string $context = 'default'): array
|
||||
{
|
||||
$targets = $collection ? collect([$collection]) : $this->recommendationTargets();
|
||||
|
||||
$targets->each(fn (Collection $item) => RefreshCollectionRecommendationJob::dispatch((int) $item->id, $actor?->id, $context)->afterCommit());
|
||||
|
||||
return $this->queuedPayload('recommendation_refresh', $targets, 'Recommendation refresh queued.');
|
||||
}
|
||||
|
||||
public function dispatchDuplicateScan(?Collection $collection = null, ?User $actor = null): array
|
||||
{
|
||||
$targets = $collection ? collect([$collection]) : $this->duplicateTargets();
|
||||
|
||||
$targets->each(fn (Collection $item) => ScanCollectionDuplicateCandidatesJob::dispatch((int) $item->id, $actor?->id)->afterCommit());
|
||||
|
||||
return $this->queuedPayload('duplicate_scan', $targets, 'Duplicate scan queued.');
|
||||
}
|
||||
|
||||
public function dispatchScheduledMaintenance(bool $health = true, bool $recommendations = true, bool $duplicates = true): array
|
||||
{
|
||||
$summary = [];
|
||||
|
||||
if ($health) {
|
||||
$summary['health'] = $this->dispatchHealthRefresh();
|
||||
}
|
||||
|
||||
if ($recommendations) {
|
||||
$summary['recommendations'] = $this->dispatchRecommendationRefresh();
|
||||
}
|
||||
|
||||
if ($duplicates) {
|
||||
$summary['duplicates'] = $this->dispatchDuplicateScan();
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/** @return SupportCollection<int, Collection> */
|
||||
private function healthTargets(): SupportCollection
|
||||
{
|
||||
$cutoff = now()->subHours(max(1, (int) config('collections.v5.queue.health_stale_after_hours', 24)));
|
||||
|
||||
return Collection::query()
|
||||
->where(function ($query) use ($cutoff): void {
|
||||
$query->whereNull('last_health_check_at')
|
||||
->orWhere('last_health_check_at', '<=', $cutoff)
|
||||
->orWhereColumn('updated_at', '>', 'last_health_check_at');
|
||||
})
|
||||
->orderBy('last_health_check_at')
|
||||
->orderByDesc('updated_at')
|
||||
->limit(max(1, (int) config('collections.v5.queue.health_batch_size', 40)))
|
||||
->get(['id']);
|
||||
}
|
||||
|
||||
/** @return SupportCollection<int, Collection> */
|
||||
private function recommendationTargets(): SupportCollection
|
||||
{
|
||||
$cutoff = now()->subHours(max(1, (int) config('collections.v5.queue.recommendation_stale_after_hours', 12)));
|
||||
|
||||
return Collection::query()
|
||||
->where('placement_eligibility', true)
|
||||
->where(function ($query) use ($cutoff): void {
|
||||
$query->whereNull('last_recommendation_refresh_at')
|
||||
->orWhere('last_recommendation_refresh_at', '<=', $cutoff)
|
||||
->orWhereColumn('updated_at', '>', 'last_recommendation_refresh_at');
|
||||
})
|
||||
->orderBy('last_recommendation_refresh_at')
|
||||
->orderByDesc('ranking_score')
|
||||
->limit(max(1, (int) config('collections.v5.queue.recommendation_batch_size', 40)))
|
||||
->get(['id']);
|
||||
}
|
||||
|
||||
/** @return SupportCollection<int, Collection> */
|
||||
private function duplicateTargets(): SupportCollection
|
||||
{
|
||||
$cutoff = now()->subHours(max(1, (int) config('collections.v5.queue.duplicate_stale_after_hours', 24)));
|
||||
|
||||
return Collection::query()
|
||||
->whereNull('canonical_collection_id')
|
||||
->where(function ($query) use ($cutoff): void {
|
||||
$query->where('updated_at', '>=', $cutoff)
|
||||
->orWhereDoesntHave('mergeActionsAsSource', function ($mergeQuery) use ($cutoff): void {
|
||||
$mergeQuery->where('action_type', 'suggested')
|
||||
->where('updated_at', '>=', $cutoff);
|
||||
});
|
||||
})
|
||||
->orderByDesc('updated_at')
|
||||
->limit(max(1, (int) config('collections.v5.queue.duplicate_batch_size', 30)))
|
||||
->get(['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SupportCollection<int, Collection> $targets
|
||||
*/
|
||||
private function queuedPayload(string $job, SupportCollection $targets, string $message): array
|
||||
{
|
||||
$ids = $targets->pluck('id')->map(static fn ($id): int => (int) $id)->values()->all();
|
||||
|
||||
return [
|
||||
'status' => 'queued',
|
||||
'job' => $job,
|
||||
'scope' => count($ids) === 1 ? 'single' : 'batch',
|
||||
'count' => count($ids),
|
||||
'collection_ids' => $ids,
|
||||
'items' => [],
|
||||
'message' => $ids === [] ? 'No collections needed this refresh.' : $message,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user