This commit is contained in:
iVampireSP.com 2022-11-16 18:06:51 +08:00
parent 2537aa8431
commit 8c1e7497b4
No known key found for this signature in database
GPG Key ID: 2F7B001CA27A8132
22 changed files with 411 additions and 170 deletions

View File

@ -1,101 +0,0 @@
<?php
namespace App\Http\Controllers\Api;
use App\Exceptions\ChargeException;
use App\Http\Controllers\Controller;
use App\Models\Balance;
use App\Models\Transaction;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Yansongda\LaravelPay\Facades\Pay;
use Yansongda\Pay\Exception\InvalidResponseException;
use function auth;
use function config;
use function now;
class BalanceController extends Controller
{
//
public function index(): JsonResponse
{
$balances = Balance::thisUser()->simplePaginate(30);
return $this->success($balances);
}
public function checkAndCharge(Balance $balance, $check = false): JsonResponse|bool
{
if ($check) {
$alipay = Pay::alipay()->find(['out_trade_no' => $balance->order_id,]);
if ($alipay->trade_status !== 'TRADE_SUCCESS') {
return false;
}
}
if ($balance->paid_at !== null) {
return true;
}
try {
(new Transaction)->addAmount($balance->user_id, 'alipay', $balance->amount);
$balance->update([
'paid_at' => now()
]);
} catch (InvalidResponseException $e) {
Log::error($e->getMessage());
return $this->error('无法验证支付结果。');
} catch (ChargeException $e) {
Log::error($e->getMessage());
return $this->error('暂时无法处理充值。');
}
return true;
}
/**
* 获取交易记录
*
* @param mixed $request
*
* @return JsonResponse
*/
public function transactions(Request $request): JsonResponse
{
$transactions = Transaction::where('user_id', auth()->id());
if ($request->has('type')) {
$transactions = $transactions->where('type', $request->type);
}
if ($request->has('payment')) {
$transactions = $transactions->where('payment', $request->payment);
}
$transactions = $transactions->latest()->simplePaginate(30);
return $this->success($transactions);
}
/**
* 获取 Drops
*
* @return JsonResponse
*/
public function drops(): JsonResponse
{
$user_id = auth()->id();
$resp = [
'drops' => (new Transaction())->getDrops($user_id),
'rate' => config('drops.rate'),
];
return $this->success($resp);
}
}

View File

@ -19,6 +19,7 @@ public function index(Request $request)
$user['drops'] = $transaction->getDrops($user['id']); $user['drops'] = $transaction->getDrops($user['id']);
$user['drops_rate'] = config('drops.rate'); $user['drops_rate'] = config('drops.rate');
return $this->success($user); return $this->success($user);
} }
} }

View File

@ -39,14 +39,6 @@ public function index(Request $request)
} }
} }
if (Auth::check()) {
$user = Auth::user();
if ($user->banned_at !== null) {
// $user->tokens()->delete();
return redirect()->route('banned');
}
}
return view('index'); return view('index');
} }

View File

@ -4,6 +4,7 @@
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Balance; use App\Models\Balance;
use App\Models\Module;
use App\Models\Transaction; use App\Models\Transaction;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
@ -24,7 +25,11 @@ public function index(Request $request): View
$balance = $request->user()->balance; $balance = $request->user()->balance;
return view('balances.index', compact('drops', 'balance')); $balances = Balance::thisUser()->latest()->paginate(50);
$drops_rate = config('drops.rate');
return view('balances.index', compact('drops', 'balance', 'balances', 'drops_rate'));
} }
public function store(Request $request) public function store(Request $request)
@ -66,7 +71,6 @@ public function store(Request $request)
} }
/** /**
* @throws \Laravel\Octane\Exceptions\DdException
*/ */
public function show(Balance $balance) public function show(Balance $balance)
{ {
@ -105,7 +109,7 @@ public function notify(Request $request): View|JsonResponse
// 检测订单是否已支付 // 检测订单是否已支付
if ($balance->paid_at !== null) { if ($balance->paid_at !== null) {
// return $this->success('订单已支付'); // return $this->success('订单已支付');
return view('pay_process'); return view('balances.pay_process');
} }
// try { // try {
@ -128,10 +132,40 @@ public function notify(Request $request): View|JsonResponse
// throw new ChargeException('商户不匹配'); // throw new ChargeException('商户不匹配');
// } // }
if ((new \App\Http\Controllers\Api\BalanceController())->checkAndCharge($balance, true)) {
return view('pay_process'); return view('pay_process');
} else {
abort(500, '支付失败'); //
} // if ((new \App\Jobs\CheckAndChargeBalance())->checkAndCharge($balance, true)) {
// return view('pay_process');
// } else {
// abort(500, '支付失败');
// }
}
/**
* 获取交易记录
*
* @param mixed $request
*
* @return View
*/
public function transactions(Request $request): View
{
$modules = Module::all();
$transactions = Transaction::where('user_id', auth()->id());
if ($request->has('type')) {
$transactions = $transactions->where('type', $request->type);
}
if ($request->has('payment')) {
$transactions = $transactions->where('payment', $request->payment);
}
$transactions = $transactions->latest()->paginate(30);
return view('balances.transactions', compact('transactions', 'modules'));
} }
} }

View File

@ -0,0 +1,15 @@
<?php
namespace App\Http\Controllers\Web;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class TransferController extends Controller
{
//
public function index() {
return view('transfer.search');
}
}

View File

@ -64,5 +64,6 @@ class Kernel extends HttpKernel
'signed' => \App\Http\Middleware\ValidateSignature::class, 'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'banned' => \App\Http\Middleware\ValidateUserIfBanned::class,
]; ];
} }

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ValidateUserIfBanned
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
*
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$user = $request->user();
if ($user) if ($user->banned_at !== null) {
return redirect()->route('banned');
}
return $next($request);
}
}

View File

@ -2,8 +2,12 @@
namespace App\Jobs; namespace App\Jobs;
use App\Http\Controllers\Api\BalanceController; use App\Exceptions\ChargeException;
use App\Models\Balance; use App\Models\Balance;
use App\Models\Transaction;
use Illuminate\Support\Facades\Log;
use Yansongda\LaravelPay\Facades\Pay;
use Yansongda\Pay\Exception\InvalidResponseException;
class CheckAndChargeBalance extends Job class CheckAndChargeBalance extends Job
{ {
@ -24,13 +28,9 @@ public function __construct()
*/ */
public function handle() public function handle()
{ {
// Balance::where('paid_at', null)->chunk(100, function ($balances) {
$bc = new BalanceController();
Balance::where('paid_at', null)->chunk(100, function ($balances) use ($bc) {
foreach ($balances as $balance) { foreach ($balances as $balance) {
if (!$bc->checkAndCharge($balance, true)) { if (!$this->checkAndCharge($balance, true)) {
if (now()->diffInDays($balance->created_at) > 1) { if (now()->diffInDays($balance->created_at) > 1) {
$balance->delete(); $balance->delete();
} }
@ -40,4 +40,37 @@ public function handle()
Balance::where('paid_at', null)->where('created_at', '<', now()->subDays(2))->delete(); Balance::where('paid_at', null)->where('created_at', '<', now()->subDays(2))->delete();
} }
public function checkAndCharge(Balance $balance, $check = false): bool
{
if ($check) {
$alipay = Pay::alipay()->find(['out_trade_no' => $balance->order_id]);
if ($alipay->trade_status !== 'TRADE_SUCCESS') {
return false;
}
}
if ($balance->paid_at !== null) {
return true;
}
try {
(new Transaction)->addAmount($balance->user_id, 'alipay', $balance->amount);
$balance->update([
'paid_at' => now()
]);
} catch (InvalidResponseException $e) {
Log::error($e->getMessage());
return false;
} catch (ChargeException $e) {
Log::error($e->getMessage());
return false;
}
return true;
}
} }

View File

@ -0,0 +1,43 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class Payment extends Component
{
public $payment = null;
/**
* Create a new component instance.
*
* @return void
*/
public function __construct($payment)
{
//
$this->payment = $payment;
}
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
$this->payment = match ($this->payment) {
'alipay' => '支付宝',
'wechat', 'wepay' => '微信支付',
'drops' => 'Drops',
'balance' => '余额',
'unfreeze' => '解冻',
'freeze' => '冻结',
'console' => '控制台',
default => $this->payment,
};
return view('components.payment', ['payment' => $this->payment]);
}
}

View File

@ -8,7 +8,7 @@
], ],
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^8.0.2", "php": "^8.1",
"doctrine/dbal": "^3.4", "doctrine/dbal": "^3.4",
"fruitcake/laravel-cors": "^3.0", "fruitcake/laravel-cors": "^3.0",
"genealabs/laravel-model-caching": "^0.12.5", "genealabs/laravel-model-caching": "^0.12.5",

View File

@ -9,11 +9,100 @@
<p>您的余额: {{ $balance }} </p> <p>您的余额: {{ $balance }} </p>
<p>Drops: {{ $drops }} </p> <p>Drops: {{ $drops }} </p>
<form name="charge" method="POST" target="_blank" action="{{ route('balances.store') }}"> <h2>添加到余额</h2>
<form name="charge" method="POST" target="_blank" action="{{ route('balances.store') }}"
onsubmit="return confirm('请注意: 由于计费方式的特殊性,我们不支持退款,请合理充值。')">
@csrf @csrf
<input type="number" name="amount" value="1" min="1" max="1000"/> <input type="number" id="amount" name="amount" value="10" min="1" max="1000" />
<button type="submit" class="btn btn-primary">充值</button> <button type="submit" class="btn btn-primary">充值</button>
</form> </form>
<span> <span id="to_drops"></span> Drops</span>
<div class="mt-2">
<div>
请注意: 由于计费方式的特殊性,我们不支持退款,请合理充值。
<br/>
<a
target="_blank"
href="https://forum.laecloud.com/d/4-wo-chong-zhi-hou-jin-e-mei-you-li-ji-dao-zhang"
>
必看! 充值后金额没有立即到账的原因。
</a>
</div>
<div>
请注意: 由于计费方式的特殊性,我们不支持退款,请合理充值。
<br/>
<a
target="_blank"
href="https://forum.laecloud.com/d/4-wo-chong-zhi-hou-jin-e-mei-you-li-ji-dao-zhang"
>
必看! 充值后金额没有立即到账的原因。
</a>
</div>
<div>
请注意: 由于计费方式的特殊性,我们不支持退款,请合理充值。
<br/>
<a
target="_blank"
href="https://forum.laecloud.com/d/4-wo-chong-zhi-hou-jin-e-mei-you-li-ji-dao-zhang"
>
必看! 充值后金额没有立即到账的原因。
</a>
</div>
</div>
<h2 class="mt-3">充值记录</h2>
<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() }}
<script>
let rate = {{ $drops_rate }};
let to_drops = document.querySelector('#to_drops')
let amount = document.querySelector('#amount')
amount.addEventListener('change', (el) => calc(el.target))
function calc(el) {
to_drops.innerText = (el.value * rate)
}
calc(amount)
</script>
{{-- {{ }}--}}
@endsection @endsection

View File

@ -0,0 +1,11 @@
@extends('layouts.app')
@section('title', '支付处理')
@section('content')
<h2>正在处理</h2>
<p>我们正在处理,您的余额很快就到账。</p>
@endsection

View File

@ -0,0 +1,101 @@
@extends('layouts.app')
@section('title', '交易记录')
@section('content')
<h2>交易记录</h2>
<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>
<th scope="col">支出</th>
<th scope="col">余额</th>
<th scope="col">交易时间</th>
</tr>
</thead>
<tbody>
@foreach ($transactions as $t)
<tr>
<td>
@if ($t->type = 'payout')
<span class="text-danger">
支出
</span>
@else($t->type = 'payin')
<span class="text-success">
收入
</span>
@endif
&nbsp;
<span class="module_name" module="{{ $t->module_id }}">{{ $t->module_id }}</span>
</td>
<td>
<x-payment :payment="$t->payment"></x-payment>
</td>
<td>
{{ $t->description }}
</td>
<td class="text-success">
{{ $t->income }}
<br/>
{{ $t->income_drops }} Drops
</td>
<td class="text-danger">
{{ $t->outcome }}
<br/>
{{ $t->outcome_drops }} Drops
</td>
<td>
{{ $t->balance }}
<br/>
{{ $t->drops }} Drops
</td>
<td>
{{ $t->created_at }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
{{ $transactions->links() }}
<script>
let modules = {!! $modules !!},
display_name = "{{ config('app.display_name') }}"
let m = {}
modules.forEach((module) => {
// 转换成 key value
m[module.id] = module.name
})
window.onload = () => {
document.querySelectorAll('.module_name').forEach((node) => {
let module = node.getAttribute('module')
if (module == null || module === "") {
node.innerText = display_name
} else {
console.log(module)
node.innerText = m[module] ?? '模块'
}
})
}
</script>
@endsection

View File

@ -1,19 +1,21 @@
<!DOCTYPE html> @extends('layouts.app')
<html>
<head> @section('title', '您已被封禁')
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>您已被封禁</title>
</head>
<body> @section('content')
<h1>很抱歉,您可能违反了我们的规定。</h1>
@if (auth()->user()->banned_at)
<h3>很抱歉,您可能违反了我们的规定。</h3>
<p>{{ auth()->user()->banned_reason }}</p> <p>{{ auth()->user()->banned_reason }}</p>
<a href="{{ route('logout') }}">更换账号</a> <form id="logout-form" action="{{ route('logout') }}" method="POST">
</body> @csrf
<button class="btn btn-primary">退出登录</button>
</form>
@else
<h3>您的账号正常。</h3>
<a href="{{ route('index') }}">返回首页</a>
</html> @endif
@endsection

View File

@ -0,0 +1 @@
{{ $payment }}

View File

@ -21,7 +21,7 @@
<p>, {{ auth('web')->user()->name }} <p>, {{ auth('web')->user()->name }}
<p>在这里,你可以获取新的 Token 来对接其他应用程序</p> <p>在这里,你可以获取新的 Token 来对接其他应用程序或者访问 控制面板</p>
<form action="{{ route('newToken') }}" name="newToken" method="POST"> <form action="{{ route('newToken') }}" name="newToken" method="POST">
@csrf @csrf

View File

@ -35,11 +35,14 @@
<!-- Left Side Of Navbar --> <!-- Left Side Of Navbar -->
<ul class="navbar-nav me-auto"> <ul class="navbar-nav me-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('index') }}">首页</a> <a class="nav-link" href="{{ route('index') }}">密钥</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ route('balances.index') }}">余额</a> <a class="nav-link" href="{{ route('balances.index') }}">余额</a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="{{ route('transactions') }}">交易记录</a>
</li>
</ul> </ul>
<!-- Right Side Of Navbar --> <!-- Right Side Of Navbar -->

View File

@ -1,3 +1,3 @@
@if ($balance->paid_at !== null) @if ($balances->paid_at !== null)
{{ $user->name }} {{ $balance->paid_at->toDateTimeString() }} 充值了 {{ $balance->amount }} 元。 {{ $user->name }} {{ $balance->paid_at->toDateTimeString() }} 充值了 {{ $balances->amount }} 元。
@endif @endif

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>支付进度</title>
</head>
<body>
<p>非常感谢。我们正在处理您的支付,稍后您就可以在交易记录中看到了。</p>
</body>
</html>

View File

@ -1,6 +1,5 @@
<?php <?php
use App\Http\Controllers\Api\BalanceController;
use App\Http\Controllers\Api\ForumController; use App\Http\Controllers\Api\ForumController;
use App\Http\Controllers\Api\HostController; use App\Http\Controllers\Api\HostController;
use App\Http\Controllers\Api\ModuleController; use App\Http\Controllers\Api\ModuleController;
@ -24,8 +23,8 @@
Route::apiResource('hosts', HostController::class); Route::apiResource('hosts', HostController::class);
// Route::apiResource('balances', BalanceController::class)->only(['index', 'store']); // Route::apiResource('balances', BalanceController::class)->only(['index', 'store']);
Route::get('balances/transactions', [BalanceController::class, 'transactions']); // Route::get('balances/transactions', [BalanceController::class, 'transactions']);
Route::get('balances/drops', [BalanceController::class, 'drops']); // Route::get('balances/drops', [BalanceController::class, 'drops']);
Route::apiResource('work-orders', WorkOrderController::class)->only(['index', 'store', 'show', 'update']); Route::apiResource('work-orders', WorkOrderController::class)->only(['index', 'store', 'show', 'update']);

View File

@ -2,19 +2,22 @@
use App\Http\Controllers\Web\AuthController; use App\Http\Controllers\Web\AuthController;
use App\Http\Controllers\Web\BalanceController; use App\Http\Controllers\Web\BalanceController;
use App\Http\Controllers\Web\TransferController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
Route::middleware(['auth'])->group(function () { Route::middleware(['auth', 'banned'])->group(function () {
Route::view('banned', 'banned')->name('banned'); Route::view('banned', 'banned')->name('banned')->withoutMiddleware('banned');
Route::post('logout', [AuthController::class, 'logout'])->name('logout')->withoutMiddleware('banned');
Route::post('/newToken', [AuthController::class, 'newToken'])->name('newToken'); Route::post('newToken', [AuthController::class, 'newToken'])->name('newToken');
Route::delete('/deleteAll', [AuthController::class, 'deleteAll'])->name('deleteAll'); Route::delete('deleteAll', [AuthController::class, 'deleteAll'])->name('deleteAll');
// logout
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
Route::resource('/balances', BalanceController::class);
Route::get('transactions', [BalanceController::class, 'transactions'])->name('transactions');
Route::resource('balances', BalanceController::class);
Route::get('transfer', [TransferController::class, 'index'])->name('transfer');
}); });
@ -24,7 +27,7 @@
Route::get('callback', [AuthController::class, 'callback'])->name('callback'); Route::get('callback', [AuthController::class, 'callback'])->name('callback');
}); });
Route::get('/', [AuthController::class, 'index'])->name('index'); Route::get('/', [AuthController::class, 'index'])->name('index')->middleware('banned');
Route::view('not_verified', 'not_verified')->name('not_verified'); Route::view('not_verified', 'not_verified')->name('not_verified');