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