管理员基本功能

身份切换
This commit is contained in:
iVampireSP.com 2022-11-18 17:16:30 +08:00
parent 20e35ad632
commit d2d6e21ca5
No known key found for this signature in database
GPG Key ID: 2F7B001CA27A8132
15 changed files with 751 additions and 40 deletions

View File

@ -0,0 +1,94 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class HostController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
// $price = 0.01;
// // 一个月多少个 5 分钟
// $month = 60 * 24 * 30 / 5;
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ModuleController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ReplyController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}

View File

@ -3,24 +3,38 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Balance;
use App\Models\Host;
use App\Models\Transaction;
use App\Models\User;
use App\Models\WorkOrder\WorkOrder;
use Carbon\Carbon;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function index()
{
//
$users = User::paginate(100);
return view('admin.users.index', compact('users'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function create()
{
@ -32,7 +46,7 @@ public function create()
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function store(Request $request)
{
@ -42,38 +56,94 @@ public function store(Request $request)
/**
* Display the specified resource.
*
* @param int $id
* @param User $user
*
* @return \Illuminate\Http\Response
* @return RedirectResponse
*/
public function show($id)
public function show(User $user): RedirectResponse
{
//
Auth::guard('web')->login($user);
return back()->with('success', '您已切换到用户 ' . $user->name . ' 的身份。');
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @param User $user
*
* @return \Illuminate\Http\Response
* @return View
*/
public function edit($id)
public function edit(User $user)
{
//
$transaction = new Transaction();
$drops = $transaction->getDrops($user->id);
$hosts = Host::where('user_id', $user->id)->latest()->paginate(50, ['*'], 'hosts_page');
$workOrders = WorkOrder::where('user_id', $user->id)->latest()->paginate(50, ['*'], 'workOrders_page');
$balances = Balance::where('user_id', $user->id)->latest()->paginate(50, ['*'], 'balances_page');
return view('admin.users.edit', compact('user', 'drops', 'hosts', 'workOrders', 'balances'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @param User $user
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse|Response
*/
public function update(Request $request, $id)
public function update(Request $request, User $user)
{
//
$transaction = new Transaction();
// 检测是否为空
if ($request->filled('balance')) {
$transaction->addAmount($user->id, 'console', $request->balance, '管理员汇入', true);
}
if ($request->filled('drops')) {
$transaction->increaseDrops($user->id, $request->drops, '管理员汇入', 'console');
}
if ($request->is_banned) {
$user->banned_at = Carbon::now();
if ($request->filled('banned_reason')) {
$user->banned_reason = $request->banned_reason;
}
} else {
if ($user->banned_at) {
$user->banned_at = null;
}
}
if ($request->filled('one_time_action')) {
if ($request->one_time_action == 'clear_all_keys') {
$user->tokens()->delete();
} else if ($request->one_time_action == 'suspend_all_hosts') {
$user->hosts()->update(['status' => 'suspended', 'suspended_at' => now()]);
} else if ($request->one_time_action == 'stop_all_hosts') {
$user->hosts()->update(['status' => 'stopped', 'suspended_at' => null]);
}
}
$user->save();
// if dirty, save
if ($user->isDirty()) {
$user->save();
}
return back()->with('success', '已完成所有更改。');
}
/**
@ -81,9 +151,10 @@ public function update(Request $request, $id)
*
* @param int $id
*
* @return \Illuminate\Http\Response
* @return Response
*/
public function destroy($id)
public
function destroy($id)
{
//
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class WorkOrderController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}

View File

@ -28,6 +28,7 @@ class Host extends Model
'configuration',
'status',
'managed_price',
'suspended_at',
];
protected $casts = [
@ -48,10 +49,13 @@ protected static function boot()
});
static::updating(function ($model) {
if ($model->status == 'suspended') {
$model->suspended_at = now();
} else if ($model->status == 'running') {
$model->suspended_at = null;
if ($model->isDirty('status')) {
if ($model->status == 'suspended') {
$model->suspended_at = now();
} else {
$model->suspended_at = null;
}
}
broadcast(new UserEvent($model->user_id, 'hosts.updating', $model));

View File

@ -4,6 +4,7 @@
use App\Exceptions\ChargeException;
use App\Exceptions\User\BalanceNotEnoughException;
use Carbon\Carbon;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
@ -302,6 +303,7 @@ public function addAmount($user_id, $payment = 'console', $amount = 0, $descript
'user_id' => $user_id,
'amount' => $amount,
'payment' => $payment,
'paid_at' => Carbon::now(),
];
Balance::create($data);

View File

@ -8,6 +8,7 @@
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Cache;
@ -44,6 +45,11 @@ class User extends Authenticatable
'banned_at' => 'datetime',
];
public function hosts(): HasMany
{
return $this->hasMany(Host::class);
}
protected static function boot()
{
parent::boot();
@ -51,17 +57,19 @@ protected static function boot()
static::updating(function ($model) {
// balance 四舍五入
$model->balance = round($model->balance, 2);
if ($model->banned_at) {
Host::where('user_id', $model->id)->update([
'status' => 'suspended',
'suspended_at' => now()
]);
$model->tokens()->delete();
if ($model->isDirty('balance')) {
$model->balance = round($model->balance, 2);
}
if ($model->isDirty('banned_at')) {
if ($model->banned_at) {
$model->tokens()->delete();
$model->hosts()->update(['status' => 'suspended', 'suspended_at' => now()]);
} else {
$model->hosts()->update(['status' => 'stopped']);
}
}
});
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class WorkOrderStatus extends Component
{
public $status = null;
/**
* Create a new component instance.
*
* @return void
*/
public function __construct($status)
{
//
$this->status = $status;
}
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
$this->status = match ($this->status) {
'pending' => '推送中',
'open' => '开启',
'user_read' => '用户已读',
'user_replied' => '用户已回复',
'replied' => '已回复',
'read' => '已读',
'on_hold' => '挂起',
'in_progress' => '处理中',
'closed' => '结单',
default => $this->status,
};
return view('components.work-order-status', ['status' => $this->status]);
}
}

View File

@ -0,0 +1,163 @@
@extends('layouts.admin')
@section('title', $user->name)
@section('content')
<h3>{{ $user->name }}</h3>
<p>余额: {{ $user->balance }} , {{ $drops }} Drops</p>
<p>注册时间: {{ $user->created_at }}</p>
<p>邮箱: {{ $user->email }}</p>
{{-- hosts --}}
<h3>主机列表</h3>
<table class="table table-hover">
<thead>
<th>ID</th>
<th>名称</th>
<th>价格 / 5 分钟</th>
<th>操作</th>
</thead>
<tbody>
@php($drops_rage = config('drops.rate'))
@foreach($hosts as $host)
<tr>
<td>{{ $host->id }}</td>
<td>{{ $host->name }}</td>
<td>
<span>{{ $host->price }} Drops</span>
<span>{{ round($host->price / $drops_rage * (30 * 24 * 60 / 5), 2) }} / </span>
</td>
<td>
<a href="{{ route('admin.hosts.show', $host) }}" class="btn btn-primary btn-sm">查看</a>
</td>
</tr>
</tbody>
@endforeach
</table>
{{ $hosts->links() }}
{{-- Work Orders --}}
<h3>工单列表</h3>
<table class="table table-hover">
<thead>
<th>ID</th>
<th>标题</th>
<th>状态</th>
<th>操作</th>
</thead>
<tbody>
@foreach($workOrders as $workOrder)
<tr>
<td>{{ $workOrder->id }}</td>
<td>{{ $workOrder->title }}</td>
<td>
<x-work-order-status :status="$workOrder->status"/>
</td>
<td>
<a href="{{ route('admin.work-orders.show', $host) }}" class="btn btn-primary btn-sm">查看</a>
</td>
</tr>
@endforeach
</tbody>
</table>
{{ $workOrders->links() }}
<h3 class="mt-3">充值记录</h3>
<div class="overflow-auto">
<table class="table">
<thead>
<tr>
<th scope="col">订单号</th>
<th scope="col">支付方式</th>
<th scope="col">金额</th>
<th scope="col">完成时间</th>
</tr>
</thead>
<tbody>
@foreach($balances as $b)
<tr>
<td>{{ $b->order_id }}</td>
<td>
<x-payment :payment="$b->payment"></x-payment>
</td>
<td>
{{ $b->amount }}
</td>
<td>
{{ $b->paid_at }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
{{ $balances->links() }}
{{-- transactions_page--}}
{{-- 账号操作 --}}
<h3 class="mt-3">账号操作</h3>
<form action="{{ route('admin.users.update', $user) }}" method="post">
@csrf
@method('PATCH')
<div class="form-group">
<label for="balance">充值余额()</label>
<input type="number" class="form-control" id="balance" name="balance" placeholder="充值金额">
</div>
<div class="form-group">
<label for="drops">充值 Drops</label>
<input type="number" class="form-control" id="drops" name="drops" placeholder="充值 Drops">
</div>
{{-- 封禁 --}}
<div class="form-group">
<label for="is_banned">封禁</label>
<select class="form-control" id="is_banned" name="is_banned">
<option value="0"></option>
<option value="1" @if ($user->banned_at) selected @endif>(将会暂停所有主机,清除所有密钥。)</option>
</select>
</div>
{{-- 原因 --}}
<div class="form-group">
<label for="banned_reason">封禁原因</label>
<input type="text" class="form-control" id="banned_reason" name="banned_reason" placeholder="封禁原因"
value="{{ $user->banned_reason }}">
</div>
{{-- 一次性操作 --}}
<div class="form-group">
<label for="one_time_action">一次性操作</label>
<select class="form-control" id="one_time_action" name="one_time_action">
<option value=""></option>
<option value="clear_all_keys">清除所有密钥</option>
<option value="suspend_all_hosts">暂停所有主机(3天后不恢复,将会自动删除)</option>
<option value="stop_all_hosts">停止所有主机(从暂停中恢复或者将其设置为 停止,需要用户手动启动)</option>
</select>
</div>
<button type="submit" class="btn btn-primary mt-3">提交</button>
</form>
@endsection

View File

@ -4,6 +4,46 @@
@section('content')
欢迎来到后台管理系统
{{-- 用户列表 --}}
<div class="overflow-auto">
<table class="table table-hover">
<thead>
<th>用户名</th>
<th>邮箱</th>
<th>金额</th>
<th>注册时间</th>
<th>操作</th>
</thead>
<tbody>
@foreach ($users as $user)
<tr>
<td>
<a href="{{ route('admin.users.show', $user) }}">
{{ $user->name }}
</a>
</td>
<td>
{{ $user->email }}
</td>
<td>
{{ $user->balance }}
</td>
<td>
{{ $user->created_at }}
</td>
<td>
<a href="{{ route('admin.users.show', $user) }}" class="btn btn-primary btn-sm">编辑</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
{{-- 分页 --}}
{{ $users->links() }}
@endsection

View File

@ -0,0 +1 @@
<span>{{ $status }}</span>

View File

@ -11,10 +11,10 @@
<title>@yield('title', '管理员')</title>
<!-- Fonts -->
{{-- <link rel="dns-prefetch" href="//fonts.gstatic.com"> --}}
{{-- <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet"> --}}
{{-- <link rel="dns-prefetch" href="//fonts.gstatic.com"> --}}
{{-- <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet"> --}}
<!-- Scripts -->
<!-- Scripts -->
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
</head>
@ -37,6 +37,15 @@
<li class="nav-item">
<a class="nav-link" href="{{ route('admin.users.index') }}">用户</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('admin.hosts.index') }}">主机</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('admin.modules.index') }}">模块</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('admin.work-orders.index') }}">工单</a>
</li>
</ul>
<!-- Right Side Of Navbar -->
@ -49,6 +58,11 @@
</li>
@endif
@else
@if (Auth::guard('web')->check())
<li class="nav-item">
<a class="nav-link" href="{{ route('index') }}">切换到 {{ Auth::guard('web')->user()->name }}</a>
</li>
@endif
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button"
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

View File

@ -11,10 +11,10 @@
<title>@yield('title', '莱云')</title>
<!-- Fonts -->
{{-- <link rel="dns-prefetch" href="//fonts.gstatic.com"> --}}
{{-- <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet"> --}}
{{-- <link rel="dns-prefetch" href="//fonts.gstatic.com"> --}}
{{-- <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet"> --}}
<!-- Scripts -->
<!-- Scripts -->
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
</head>
@ -51,13 +51,19 @@
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ms-auto">
@if (Auth::guard('admin')->check())
<li class="nav-item">
<a class="nav-link"
href="{{ route('admin.index') }}">切换到 {{ Auth::guard('admin')->user()->email }}</a>
</li>
@endif
<!-- Authentication Links -->
@guest
@if (Route::has('admin.login'))
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@endif
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button"

View File

@ -1,7 +1,11 @@
<?php
use App\Http\Controllers\Admin\AuthController;
use App\Http\Controllers\Admin\HostController;
use App\Http\Controllers\Admin\ModuleController;
use App\Http\Controllers\Admin\ReplyController;
use App\Http\Controllers\Admin\UserController;
use App\Http\Controllers\Admin\WorkOrderController;
use Illuminate\Support\Facades\Route;
Route::withoutMiddleware(['auth'])->group(function () {
@ -17,5 +21,9 @@
Route::group([
'middleware' => 'auth:admin',
], function () {
Route::resource('users', UserController::class);
Route::resource('users', UserController::class)->only(['index', 'show', 'edit', 'update']);
Route::resource('modules', ModuleController::class);
Route::resource('hosts', HostController::class);
Route::resource('work-orders', WorkOrderController::class);
Route::resource('work-orders.replies', ReplyController::class);
});