You've already forked Atomcms-edit
256 lines
8.4 KiB
PHP
Executable File
256 lines
8.4 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Miscellaneous\WebsiteSetting;
|
|
use App\Models\RadioListenerPoint;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
readonly class PointsService
|
|
{
|
|
private const string POINTS_CACHE_KEY = 'user_points_';
|
|
|
|
private const string LEADERBOARD_CACHE_KEY = 'radio_leaderboard';
|
|
|
|
private const int LEADERBOARD_CACHE_DURATION = 300;
|
|
|
|
private const string SETTINGS_CACHE_KEY = 'points_service_settings';
|
|
|
|
private const int SETTINGS_CACHE_DURATION = 300;
|
|
|
|
private int $pointsPerMinute;
|
|
|
|
private int $maxPointsPerDay;
|
|
|
|
private int $requestPoints;
|
|
|
|
private int $votePoints;
|
|
|
|
private int $giveawayWinPoints;
|
|
|
|
private int $contestWinPoints;
|
|
|
|
public function __construct()
|
|
{
|
|
$settings = $this->getSettings();
|
|
$this->pointsPerMinute = (int) ($settings['points_per_minute'] ?? 1);
|
|
$this->maxPointsPerDay = (int) ($settings['max_points_per_day'] ?? 100);
|
|
$this->requestPoints = (int) ($settings['points_for_request'] ?? 5);
|
|
$this->votePoints = (int) ($settings['points_for_vote'] ?? 2);
|
|
$this->giveawayWinPoints = (int) ($settings['points_for_giveaway_win'] ?? 50);
|
|
$this->contestWinPoints = (int) ($settings['points_for_contest_win'] ?? 100);
|
|
}
|
|
|
|
public function awardPoints(User $user, int $points, string $reason): RadioListenerPoint
|
|
{
|
|
$pointRecord = RadioListenerPoint::awardPoints($user, $points, $reason);
|
|
|
|
$this->clearUserCache($user->id);
|
|
$this->clearLeaderboardCache();
|
|
|
|
return $pointRecord;
|
|
}
|
|
|
|
public function deductPoints(User $user, int $points, string $reason): RadioListenerPoint
|
|
{
|
|
$pointRecord = RadioListenerPoint::deductPoints($user, $points, $reason);
|
|
|
|
$this->clearUserCache($user->id);
|
|
$this->clearLeaderboardCache();
|
|
|
|
return $pointRecord;
|
|
}
|
|
|
|
public function getUserPoints(User $user): int
|
|
{
|
|
return Cache::remember(self::POINTS_CACHE_KEY . $user->id, 3600, fn (): int => (int) ($user->radio_points ?? 0));
|
|
}
|
|
|
|
/**
|
|
* @return array<int, array<string, mixed>>
|
|
*/
|
|
public function getUserHistory(User $user, int $limit = 50): array
|
|
{
|
|
return RadioListenerPoint::where('user_id', $user->id)
|
|
->orderBy('earned_at', 'desc')
|
|
->limit($limit)
|
|
->get()
|
|
->toArray();
|
|
}
|
|
|
|
/**
|
|
* @return array<int, array{rank: int, user_id: int, username: string, avatar: string|null, points: int}>
|
|
*/
|
|
public function getLeaderboard(int $limit = 100): array
|
|
{
|
|
return Cache::remember(self::LEADERBOARD_CACHE_KEY, self::LEADERBOARD_CACHE_DURATION, function () use ($limit): array {
|
|
/** @var Collection $collection */
|
|
$collection = User::where('radio_points', '>', 0)
|
|
->orderBy('radio_points', 'desc')
|
|
->limit($limit)
|
|
->get(['id', 'username', 'avatar', 'radio_points']);
|
|
|
|
return $collection->map(fn (User $user, int $index): array => [
|
|
'rank' => $index + 1,
|
|
'user_id' => $user->id,
|
|
'username' => $user->username,
|
|
'avatar' => $user->avatar ?? null,
|
|
'points' => (int) $user->radio_points,
|
|
])->toArray();
|
|
});
|
|
}
|
|
|
|
public function getWeeklyLeaderboard(int $limit = 50): array
|
|
{
|
|
return Cache::remember(self::LEADERBOARD_CACHE_KEY . '_weekly', self::LEADERBOARD_CACHE_DURATION, function () use ($limit) {
|
|
$weeklyPoints = RadioListenerPoint::where('earned_at', '>=', now()->subWeek())
|
|
->selectRaw('user_id, SUM(points) as total_points')
|
|
->groupBy('user_id')
|
|
->orderBy('total_points', 'desc')
|
|
->limit($limit)
|
|
->get();
|
|
|
|
$userIds = $weeklyPoints->pluck('user_id')->unique()->values();
|
|
$users = User::whereIn('id', $userIds)
|
|
->get(['id', 'username', 'avatar'])
|
|
->keyBy('id');
|
|
|
|
return $weeklyPoints->map(fn (object $item, int $index): array => [
|
|
'rank' => $index + 1,
|
|
'user_id' => $item->user_id,
|
|
'username' => $users[$item->user_id]?->username ?? 'Unknown',
|
|
'avatar' => $users[$item->user_id]?->avatar ?? null,
|
|
'points' => $item->total_points,
|
|
])->toArray();
|
|
});
|
|
}
|
|
|
|
public function getMonthlyLeaderboard(int $limit = 50): array
|
|
{
|
|
return Cache::remember(self::LEADERBOARD_CACHE_KEY . '_monthly', self::LEADERBOARD_CACHE_DURATION, function () use ($limit) {
|
|
$monthlyPoints = RadioListenerPoint::where('earned_at', '>=', now()->subMonth())
|
|
->selectRaw('user_id, SUM(points) as total_points')
|
|
->groupBy('user_id')
|
|
->orderBy('total_points', 'desc')
|
|
->limit($limit)
|
|
->get();
|
|
|
|
$userIds = $monthlyPoints->pluck('user_id')->unique()->values();
|
|
$users = User::whereIn('id', $userIds)
|
|
->get(['id', 'username', 'avatar'])
|
|
->keyBy('id');
|
|
|
|
return $monthlyPoints->map(fn (object $item, int $index): array => [
|
|
'rank' => $index + 1,
|
|
'user_id' => $item->user_id,
|
|
'username' => $users[$item->user_id]?->username ?? 'Unknown',
|
|
'avatar' => $users[$item->user_id]?->avatar ?? null,
|
|
'points' => $item->total_points,
|
|
])->toArray();
|
|
});
|
|
}
|
|
|
|
public function getUserRank(User $user): int
|
|
{
|
|
/** @var int $count */
|
|
$count = User::query()
|
|
->where('radio_points', '>', $user->radio_points ?? 0)
|
|
->count();
|
|
|
|
return $count + 1;
|
|
}
|
|
|
|
public function awardListeningPoints(User $user, int $minutes): int
|
|
{
|
|
$todayPoints = RadioListenerPoint::where('user_id', $user->id)
|
|
->where('earned_at', '>=', now()->startOfDay())
|
|
->sum('points');
|
|
|
|
$availablePoints = max(0, $this->maxPointsPerDay - $todayPoints);
|
|
$pointsToAward = min($minutes * $this->pointsPerMinute, $availablePoints);
|
|
|
|
if ($pointsToAward > 0) {
|
|
$this->awardPoints($user, $pointsToAward, 'Luisteren');
|
|
}
|
|
|
|
return $pointsToAward;
|
|
}
|
|
|
|
public function awardRequestPoints(User $user): int
|
|
{
|
|
$this->awardPoints($user, $this->requestPoints, 'Song Request');
|
|
|
|
return $this->requestPoints;
|
|
}
|
|
|
|
public function awardVotePoints(User $user): int
|
|
{
|
|
$this->awardPoints($user, $this->votePoints, 'Stemmen op Request');
|
|
|
|
return $this->votePoints;
|
|
}
|
|
|
|
public function awardWinPoints(User $user, string $type): int
|
|
{
|
|
$winPoints = match ($type) {
|
|
'giveaway' => $this->giveawayWinPoints,
|
|
'contest' => $this->contestWinPoints,
|
|
default => 10,
|
|
};
|
|
|
|
$this->awardPoints($user, $winPoints, 'Winst: ' . $type);
|
|
|
|
return $winPoints;
|
|
}
|
|
|
|
private function clearUserCache(int $userId): void
|
|
{
|
|
Cache::forget(self::POINTS_CACHE_KEY . $userId);
|
|
}
|
|
|
|
public function clearLeaderboardCache(): void
|
|
{
|
|
Cache::forget(self::LEADERBOARD_CACHE_KEY);
|
|
Cache::forget(self::LEADERBOARD_CACHE_KEY . '_weekly');
|
|
Cache::forget(self::LEADERBOARD_CACHE_KEY . '_monthly');
|
|
}
|
|
|
|
public function clearSettingsCache(): void
|
|
{
|
|
Cache::forget(self::SETTINGS_CACHE_KEY);
|
|
CacheService::clearPointsSettings();
|
|
}
|
|
|
|
public function getStats(): array
|
|
{
|
|
$totalPoints = RadioListenerPoint::sum('points');
|
|
$totalUsers = User::where('radio_points', '>', 0)->count();
|
|
|
|
return [
|
|
'total_points_awarded' => $totalPoints,
|
|
'total_active_users' => $totalUsers,
|
|
'points_per_minute' => $this->pointsPerMinute,
|
|
'max_points_per_day' => $this->maxPointsPerDay,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array<string, string|null>
|
|
*/
|
|
private function getSettings(): array
|
|
{
|
|
return Cache::remember(self::SETTINGS_CACHE_KEY, self::SETTINGS_CACHE_DURATION, fn (): array => WebsiteSetting::whereIn('key', [
|
|
'points_per_minute',
|
|
'max_points_per_day',
|
|
'points_for_request',
|
|
'points_for_vote',
|
|
'points_for_giveaway_win',
|
|
'points_for_contest_win',
|
|
])->pluck('value', 'key')->toArray());
|
|
}
|
|
}
|