Files
chatroom/app/Http/Controllers/Admin/UserManagerController.php
lkddi 0c5e218aa8 功能:新增用户积分流水系统
- 新建 user_currency_logs 流水表 (Migration)
- App\Enums\CurrencySource 来源枚举(可扩展)
- App\Models\UserCurrencyLog 流水模型
- App\Services\UserCurrencyService 统一积分变更服务
- FishingController:抛竿/收竿接入流水记录
- AutoSaveExp:自动存点接入流水记录
- Admin/UserManagerController:管理员调整接入流水记录
- LeaderboardController:新增今日三榜(经验/金币/魅力)+ 个人流水日志页
- Admin/CurrencyStatsController:后台活动统计页
- views:新增个人日志页、后台统计页;排行榜新增今日榜数据传递
- routes:新增个人日志路由 /my/currency-logs、后台路由 /admin/currency-stats
2026-02-28 12:49:26 +08:00

183 lines
7.0 KiB
PHP
Raw 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
/**
* 文件功能:后台用户大盘管理控制器
* (替代原版 gl/ 下的各种管理面)
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Enums\CurrencySource;
use App\Models\User;
use App\Services\UserCurrencyService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\View\View;
class UserManagerController extends Controller
{
/**
* 注入统一积分服务(用于管理员调整经验/金币/魅力时记录流水)
*/
public function __construct(
private readonly UserCurrencyService $currencyService,
) {}
/**
* 显示用户列表及搜索(支持按等级/经验/金币/魅力排序)
*/
public function index(Request $request): View
{
$query = User::query();
if ($request->filled('username')) {
$query->where('username', 'like', '%'.$request->input('username').'%');
}
// 排序:允许的字段白名单,防止 SQL 注入
$sortable = ['user_level', 'exp_num', 'jjb', 'meili', 'id'];
$sortBy = in_array($request->input('sort_by'), $sortable) ? $request->input('sort_by') : 'id';
$sortDir = $request->input('sort_dir') === 'asc' ? 'asc' : 'desc';
$users = $query->orderBy($sortBy, $sortDir)->paginate(20)->withQueryString();
// VIP 等级选项列表(供编辑弹窗使用)
$vipLevels = \App\Models\VipLevel::orderBy('sort_order')->get();
return view('admin.users.index', compact('users', 'vipLevels', 'sortBy', 'sortDir'));
}
/**
* 修改用户资料、等级或密码 (AJAX 或表单)
*/
public function update(Request $request, int $id): JsonResponse|RedirectResponse
{
$targetUser = User::findOrFail($id);
$currentUser = Auth::user();
// 越权防护:不能修改 等级大于或等于自己 的目标(除非修改自己)
if ($targetUser->id !== $currentUser->id && $targetUser->user_level >= $currentUser->user_level) {
return response()->json(['status' => 'error', 'message' => '权限不足:您无法修改同级或高级管理人员资料。'], 403);
}
// 管理员级别 = 最高等级 + 1后台编辑最高可设到管理员级别
$adminLevel = (int) \App\Models\Sysparam::getValue('maxlevel', '15') + 1;
$validated = $request->validate([
'sex' => 'sometimes|integer|in:0,1,2',
'user_level' => "sometimes|integer|min:0|max:{$adminLevel}",
'exp_num' => 'sometimes|integer|min:0',
'jjb' => 'sometimes|integer|min:0',
'meili' => 'sometimes|integer|min:0',
'qianming' => 'sometimes|nullable|string|max:255',
'headface' => 'sometimes|string|max:50',
'password' => 'nullable|string|min:6',
'vip_level_id' => 'sometimes|nullable|integer|exists:vip_levels,id',
'hy_time' => 'sometimes|nullable|date',
]);
// 如果传了且没超权,直接赋予
if (isset($validated['user_level'])) {
if ($currentUser->id !== $targetUser->id) {
// 修改别人:只有真正的创始人 (ID=1) 才能修改别人的等级
if ($currentUser->id !== 1) {
return response()->json(['status' => 'error', 'message' => '权限越界:只有星系创始人(站长)才能调整其他用户的行政等级!'], 403);
}
}
$targetUser->user_level = $validated['user_level'];
}
if (isset($validated['sex'])) {
$targetUser->sex = $validated['sex'];
}
if (isset($validated['exp_num'])) {
// 计算差值并通过统一服务记录流水(管理员手动调整)
$expDiff = $validated['exp_num'] - ($targetUser->exp_num ?? 0);
if ($expDiff !== 0) {
$this->currencyService->change(
$targetUser, 'exp', $expDiff, CurrencySource::ADMIN_ADJUST,
"管理员 {$currentUser->username} 手动调整经验",
);
$targetUser->refresh();
}
}
if (isset($validated['jjb'])) {
$jjbDiff = $validated['jjb'] - ($targetUser->jjb ?? 0);
if ($jjbDiff !== 0) {
$this->currencyService->change(
$targetUser, 'gold', $jjbDiff, CurrencySource::ADMIN_ADJUST,
"管理员 {$currentUser->username} 手动调整金币",
);
$targetUser->refresh();
}
}
if (isset($validated['meili'])) {
$meiliDiff = $validated['meili'] - ($targetUser->meili ?? 0);
if ($meiliDiff !== 0) {
$this->currencyService->change(
$targetUser, 'charm', $meiliDiff, CurrencySource::ADMIN_ADJUST,
"管理员 {$currentUser->username} 手动调整魅力",
);
$targetUser->refresh();
}
}
if (array_key_exists('qianming', $validated)) {
$targetUser->qianming = $validated['qianming'];
}
if (isset($validated['headface'])) {
$targetUser->headface = $validated['headface'];
}
// VIP 会员等级设置
if (array_key_exists('vip_level_id', $validated)) {
$targetUser->vip_level_id = $validated['vip_level_id'] ?: null;
}
if (array_key_exists('hy_time', $validated)) {
$targetUser->hy_time = $validated['hy_time'] ?: null;
}
if (! empty($validated['password'])) {
$targetUser->password = Hash::make($validated['password']);
}
$targetUser->save();
if ($request->wantsJson()) {
return response()->json(['status' => 'success', 'message' => '用户资料已强行更新完毕!']);
}
return back()->with('success', '用户资料已更新!');
}
/**
* 物理删除杀封用户
*/
public function destroy(Request $request, int $id): RedirectResponse
{
$targetUser = User::findOrFail($id);
$currentUser = Auth::user();
// 越权防护:不允许删除同级或更高等级的账号
if ($targetUser->id !== $currentUser->id && $targetUser->user_level >= $currentUser->user_level) {
abort(403, '权限不足:无法删除同级或高级账号!');
}
// 管理员保护:达到踢人等级(level_kick)的用户视为管理员,不可被强杀
$levelKick = (int) \App\Models\Sysparam::getValue('level_kick', '10');
if ($targetUser->user_level >= $levelKick) {
abort(403, '该用户为管理员,不允许强杀!请先在用户编辑中降低其等级。');
}
$targetUser->delete();
return back()->with('success', '目标已被物理删除。');
}
}