Files
mini_tp/app/smt/service/AchievementService.php
2026-04-26 09:24:08 +08:00

117 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace app\smt\service;
use app\smt\model\AchievementLevel;
use app\smt\model\AchievementTheme;
class AchievementService
{
public function listActiveThemes(): array
{
$themes = AchievementTheme::where('is_active', 1)
->whereNull('deleted_at')
->order('sort_order', 'asc')
->order('id', 'asc')
->select()
->all();
if (empty($themes)) {
return [];
}
$themeIds = array_map(static function ($item) {
return (int) $item->id;
}, $themes);
$levels = AchievementLevel::whereIn('theme_id', $themeIds)
->whereNull('deleted_at')
->order('required_days', 'asc')
->order('sort_order', 'asc')
->select()
->all();
$groupedLevels = [];
foreach ($levels as $level) {
$groupedLevels[(int) $level->theme_id][] = [
'id' => (int) $level->id,
'theme_id' => (int) $level->theme_id,
'name' => (string) $level->name,
'icon' => (string) $level->icon,
'required_days' => (int) $level->required_days,
'sort_order' => (int) $level->sort_order,
];
}
return array_map(static function ($theme) use ($groupedLevels) {
return [
'id' => (int) $theme->id,
'name' => (string) $theme->name,
'key' => (string) $theme->key,
'icon' => (string) $theme->icon,
'sort_order' => (int) $theme->sort_order,
'is_active' => (bool) $theme->is_active,
'levels' => $groupedLevels[(int) $theme->id] ?? [],
];
}, $themes);
}
public function getUserAchievement(int $themeId, int $days): ?array
{
$theme = AchievementTheme::where('id', $themeId)->whereNull('deleted_at')->find();
if (!$theme) {
return null;
}
$levels = AchievementLevel::where('theme_id', $themeId)
->whereNull('deleted_at')
->order('required_days', 'asc')
->order('sort_order', 'asc')
->select()
->all();
$current = null;
$next = null;
foreach ($levels as $index => $level) {
if ($days >= (int) $level->required_days) {
$current = $level;
$next = $levels[$index + 1] ?? null;
}
}
$progress = 0.0;
if ($current && $next) {
$rangeTotal = (int) $next->required_days - (int) $current->required_days;
if ($rangeTotal > 0) {
$progress = max(0, min(1, ($days - (int) $current->required_days) / $rangeTotal));
}
} elseif ($current) {
$progress = 1.0;
}
return [
'theme_id' => (int) $theme->id,
'theme_name' => (string) $theme->name,
'theme_key' => (string) $theme->key,
'theme_icon' => (string) $theme->icon,
'days' => max(0, $days),
'current' => $current ? $this->formatLevel($current) : null,
'next' => $next ? $this->formatLevel($next) : null,
'progress' => $progress,
];
}
private function formatLevel(AchievementLevel $level): array
{
return [
'id' => (int) $level->id,
'theme_id' => (int) $level->theme_id,
'name' => (string) $level->name,
'icon' => (string) $level->icon,
'required_days' => (int) $level->required_days,
'sort_order' => (int) $level->sort_order,
];
}
}