Files
SkinbaseNova/app/Services/Academy/AcademyPopularityService.php

60 lines
2.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Academy;
use App\Models\AcademyContentMetricDaily;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
final class AcademyPopularityService
{
/**
* @param array<string, int|float|null> $metrics
*/
public function calculatePopularityScore(array $metrics): float
{
return round(
((float) ($metrics['unique_visitors'] ?? 0) * 1)
+ ((float) ($metrics['engaged_views'] ?? 0) * 3)
+ ((float) ($metrics['likes'] ?? 0) * 5)
+ ((float) ($metrics['saves'] ?? 0) * 7)
+ ((float) ($metrics['prompt_copies'] ?? 0) * 8)
+ ((float) ($metrics['negative_prompt_copies'] ?? 0) * 4)
+ ((float) ($metrics['starts'] ?? 0) * 4)
+ ((float) ($metrics['completions'] ?? 0) * 10)
+ ((float) ($metrics['upgrade_clicks'] ?? 0) * 15)
+ ((float) ($metrics['premium_preview_views'] ?? 0) * 3)
- ((float) ($metrics['bounce_count'] ?? 0) * 2),
2,
);
}
/**
* @param array<string, int|float|null> $metrics
*/
public function calculateConversionScore(array $metrics): float
{
$uniqueVisitors = max(1, (int) ($metrics['unique_visitors'] ?? 0));
return round((((float) ($metrics['upgrade_clicks'] ?? 0) * 100) / $uniqueVisitors), 2);
}
public function queryBetween(Carbon $from, Carbon $to): Builder
{
return AcademyContentMetricDaily::query()
->whereBetween('date', [$from->toDateString(), $to->toDateString()]);
}
public function topContent(Carbon $from, Carbon $to, int $limit = 10): Collection
{
return $this->queryBetween($from, $to)
->selectRaw('content_type, content_id, sum(views) as views, sum(unique_visitors) as unique_visitors, sum(engaged_views) as engaged_views, sum(likes) as likes, sum(saves) as saves, sum(prompt_copies) as prompt_copies, sum(completions) as completions, sum(upgrade_clicks) as upgrade_clicks, sum(popularity_score) as popularity_score')
->groupBy('content_type', 'content_id')
->orderByDesc('popularity_score')
->limit($limit)
->get();
}
}