optimizations

This commit is contained in:
2026-03-28 19:15:39 +01:00
parent 0b25d9570a
commit cab4fbd83e
509 changed files with 1016804 additions and 1605 deletions

View 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';
}
}