feat: add smt module
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<?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,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user