Files
chatroom/app/Http/Controllers/Admin/VipController.php

230 lines
7.1 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 文件功能:后台 VIP 会员等级管理控制器
* 提供会员等级的 CRUD增删改查功能
* 后台可自由创建、修改、删除会员等级
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\VipLevel;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
/**
* 后台 VIP 会员等级管理控制器
* 负责会员等级维护,以及查看各等级下的会员名单。
*/
class VipController extends Controller
{
/**
* 会员主题支持的特效下拉选项。
*
* @var array<string, string>
*/
private const EFFECT_LABELS = [
'none' => '无特效',
'fireworks' => '烟花',
'rain' => '下雨',
'lightning' => '闪电',
'snow' => '下雪',
'sakura' => '樱花飘落',
'meteors' => '流星',
'gold-rain' => '金币雨',
'hearts' => '爱心飘落',
'confetti' => '彩带庆典',
'fireflies' => '萤火虫',
];
/**
* 会员主题支持的横幅风格下拉选项。
*
* @var array<string, string>
*/
private const BANNER_STYLE_LABELS = [
'aurora' => '鎏光星幕',
'storm' => '雷霆风暴',
'royal' => '王者金辉',
'cosmic' => '星穹幻彩',
'farewell' => '告别暮光',
];
/**
* 会员等级管理列表页
*/
public function index(): View
{
$levels = VipLevel::query()
->withCount('users')
->orderBy('sort_order')
->get();
return view('admin.vip.index', [
'levels' => $levels,
'effectOptions' => self::EFFECT_LABELS,
'bannerStyleOptions' => self::BANNER_STYLE_LABELS,
]);
}
/**
* 查看某个会员等级下的会员名单。
*
* @param Request $request 当前筛选请求
* @param VipLevel $vip 当前会员等级
*/
public function members(Request $request, VipLevel $vip): View
{
$query = User::query()->where('vip_level_id', $vip->id);
$now = now();
if ($request->filled('keyword')) {
$keyword = trim((string) $request->input('keyword'));
// 支持后台按用户名快速筛选某个等级下的会员。
$query->where('username', 'like', '%'.$keyword.'%');
}
if ($request->input('status') === 'active') {
// 当前有效会员:永久会员或到期时间仍在未来。
$query->where(function ($builder) use ($now): void {
$builder->whereNull('hy_time')->orWhere('hy_time', '>', $now);
});
}
if ($request->input('status') === 'expired') {
// 已过期会员:到期时间存在且已经早于当前时间。
$query->whereNotNull('hy_time')->where('hy_time', '<=', $now);
}
$members = $query
->select(['id', 'username', 'sex', 'vip_level_id', 'hy_time', 'created_at'])
->orderByRaw('CASE WHEN hy_time IS NULL THEN 0 WHEN hy_time > ? THEN 1 ELSE 2 END', [$now])
->orderByRaw('hy_time IS NULL DESC')
->orderByDesc('hy_time')
->orderBy('username')
->paginate(20)
->withQueryString();
$totalAssignedCount = User::query()
->where('vip_level_id', $vip->id)
->count();
$activeCount = User::query()
->where('vip_level_id', $vip->id)
->where(function ($builder) use ($now): void {
$builder->whereNull('hy_time')->orWhere('hy_time', '>', $now);
})
->count();
return view('admin.vip.members', [
'vip' => $vip,
'members' => $members,
'totalAssignedCount' => $totalAssignedCount,
'activeCount' => $activeCount,
]);
}
/**
* 新增会员等级
*/
public function store(Request $request): RedirectResponse
{
$data = $this->validatedPayload($request);
VipLevel::create($data);
return redirect()->route('admin.vip.index')->with('success', '会员等级创建成功!');
}
/**
* 更新会员等级
*
* @param VipLevel $vip 路由模型自动注入
*/
public function update(Request $request, VipLevel $vip): RedirectResponse
{
$level = $vip;
$data = $this->validatedPayload($request);
$level->update($data);
return redirect()->route('admin.vip.index')->with('success', '会员等级更新成功!');
}
/**
* 删除会员等级(关联用户的 vip_level_id 会自动置 null
*
* @param VipLevel $vip 路由模型自动注入
*/
public function destroy(VipLevel $vip): RedirectResponse
{
$vip->delete();
return redirect()->route('admin.vip.index')->with('success', '会员等级已删除!');
}
/**
* 将多行文本转为 JSON 数组字符串
* 每行一个模板,空行忽略
*
* @param string $text 多行文本
* @return string|null JSON 字符串
*/
private function textToJson(string $text): ?string
{
$lines = array_filter(
array_map('trim', explode("\n", $text)),
fn ($line) => $line !== ''
);
if (empty($lines)) {
return null;
}
return json_encode(array_values($lines), JSON_UNESCAPED_UNICODE);
}
/**
* 统一整理后台提交的会员等级主题配置数据。
*
* @return array<string, mixed>
*/
private function validatedPayload(Request $request): array
{
$data = $request->validate([
'name' => 'required|string|max:50',
'icon' => 'required|string|max:20',
'color' => 'required|string|max:10',
'exp_multiplier' => 'required|numeric|min:1|max:99',
'jjb_multiplier' => 'required|numeric|min:1|max:99',
'sort_order' => 'required|integer|min:0',
'price' => 'required|integer|min:0',
'duration_days' => 'required|integer|min:0',
'join_templates' => 'nullable|string',
'leave_templates' => 'nullable|string',
'join_effect' => ['required', 'string', Rule::in(VipLevel::EFFECT_OPTIONS)],
'leave_effect' => ['required', 'string', Rule::in(VipLevel::EFFECT_OPTIONS)],
'join_banner_style' => 'required|in:aurora,storm,royal,cosmic,farewell',
'leave_banner_style' => 'required|in:aurora,storm,royal,cosmic,farewell',
'allow_custom_messages' => 'nullable|boolean',
]);
// 将多行文本框内容转为 JSON 数组,便于后续随机抽取模板。
$data['join_templates'] = $this->textToJson($data['join_templates'] ?? '');
$data['leave_templates'] = $this->textToJson($data['leave_templates'] ?? '');
$data['allow_custom_messages'] = $request->boolean('allow_custom_messages');
return $data;
}
}