optimizations
This commit is contained in:
131
app/Services/CollectionRankingService.php
Normal file
131
app/Services/CollectionRankingService.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Collection;
|
||||
use App\Models\CollectionRecommendationSnapshot;
|
||||
|
||||
class CollectionRankingService
|
||||
{
|
||||
public function explain(Collection $collection, string $context = 'default'): array
|
||||
{
|
||||
$signals = [
|
||||
'quality_score' => (float) ($collection->quality_score ?? 0),
|
||||
'health_score' => (float) ($collection->health_score ?? 0),
|
||||
'freshness_score' => (float) ($collection->freshness_score ?? 0),
|
||||
'engagement_score' => (float) ($collection->engagement_score ?? 0),
|
||||
'editorial_readiness_score' => (float) ($collection->editorial_readiness_score ?? 0),
|
||||
];
|
||||
|
||||
$score = ($signals['quality_score'] * 0.3)
|
||||
+ ($signals['health_score'] * 0.3)
|
||||
+ ($signals['freshness_score'] * 0.15)
|
||||
+ ($signals['engagement_score'] * 0.2)
|
||||
+ ($signals['editorial_readiness_score'] * 0.05);
|
||||
|
||||
if ($collection->is_featured) {
|
||||
$score += 5.0;
|
||||
}
|
||||
|
||||
if ($context === 'campaign' && filled($collection->campaign_key)) {
|
||||
$score += 7.5;
|
||||
}
|
||||
|
||||
if ($context === 'evergreen' && $signals['freshness_score'] < 35 && $signals['quality_score'] >= 70) {
|
||||
$score += 6.0;
|
||||
}
|
||||
|
||||
$score = round(max(0.0, min(150.0, $score)), 2);
|
||||
|
||||
return [
|
||||
'score' => $score,
|
||||
'context' => $context,
|
||||
'signals' => $signals,
|
||||
'bucket' => $this->rankingBucket($score),
|
||||
'recommendation_tier' => $this->recommendationTier($score),
|
||||
'search_boost_tier' => $this->searchBoostTier($collection, $score),
|
||||
'rationale' => [
|
||||
sprintf('Quality %.1f', $signals['quality_score']),
|
||||
sprintf('Health %.1f', $signals['health_score']),
|
||||
sprintf('Freshness %.1f', $signals['freshness_score']),
|
||||
sprintf('Engagement %.1f', $signals['engagement_score']),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function refresh(Collection $collection, string $context = 'default'): Collection
|
||||
{
|
||||
$explanation = $this->explain($collection->fresh(), $context);
|
||||
$snapshotDate = now()->toDateString();
|
||||
|
||||
$collection->forceFill([
|
||||
'ranking_bucket' => $explanation['bucket'],
|
||||
'recommendation_tier' => $explanation['recommendation_tier'],
|
||||
'search_boost_tier' => $explanation['search_boost_tier'],
|
||||
'last_recommendation_refresh_at' => now(),
|
||||
])->save();
|
||||
|
||||
$snapshot = CollectionRecommendationSnapshot::query()
|
||||
->where('collection_id', $collection->id)
|
||||
->where('context_key', $context)
|
||||
->whereDate('snapshot_date', $snapshotDate)
|
||||
->first();
|
||||
|
||||
if ($snapshot) {
|
||||
$snapshot->forceFill([
|
||||
'recommendation_score' => $explanation['score'],
|
||||
'rationale_json' => $explanation,
|
||||
])->save();
|
||||
} else {
|
||||
CollectionRecommendationSnapshot::query()->create([
|
||||
'collection_id' => $collection->id,
|
||||
'context_key' => $context,
|
||||
'recommendation_score' => $explanation['score'],
|
||||
'rationale_json' => $explanation,
|
||||
'snapshot_date' => $snapshotDate,
|
||||
]);
|
||||
}
|
||||
|
||||
return $collection->fresh();
|
||||
}
|
||||
|
||||
private function rankingBucket(float $score): string
|
||||
{
|
||||
return match (true) {
|
||||
$score >= 110 => 'elite',
|
||||
$score >= 85 => 'strong',
|
||||
$score >= 60 => 'steady',
|
||||
$score >= 35 => 'emerging',
|
||||
default => 'cold',
|
||||
};
|
||||
}
|
||||
|
||||
private function recommendationTier(float $score): string
|
||||
{
|
||||
return match (true) {
|
||||
$score >= 105 => 'premium',
|
||||
$score >= 80 => 'primary',
|
||||
$score >= 55 => 'secondary',
|
||||
default => 'fallback',
|
||||
};
|
||||
}
|
||||
|
||||
private function searchBoostTier(Collection $collection, float $score): string
|
||||
{
|
||||
if ($collection->type === Collection::TYPE_EDITORIAL && $score >= 80) {
|
||||
return 'editorial';
|
||||
}
|
||||
|
||||
if ($collection->placement_eligibility && $score >= 70) {
|
||||
return 'high';
|
||||
}
|
||||
|
||||
if ($score >= 45) {
|
||||
return 'standard';
|
||||
}
|
||||
|
||||
return 'low';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user