改进 扣费

This commit is contained in:
iVampireSP.com 2022-08-30 17:20:45 +08:00
parent 373ca437b9
commit 5a1356e26f
No known key found for this signature in database
GPG Key ID: 2F7B001CA27A8132
10 changed files with 225 additions and 29 deletions

View File

@ -22,7 +22,7 @@ protected function schedule(Schedule $schedule)
// dispatch HostCost job // dispatch HostCost job
$schedule->job(new HostCost())->everyFiveMinutes(); $schedule->job(new HostCost())->everyFiveMinutes();
$schedule->job(new UserSave())->everyTenMinutes(); // $schedule->job(new UserSave())->everyTenMinutes();
$schedule->job(new Remote\FetchModule())->everyMinute()->onOneServer(); $schedule->job(new Remote\FetchModule())->everyMinute()->onOneServer();
$schedule->job(new Remote\PushHost())->everyMinute()->onOneServer(); $schedule->job(new Remote\PushHost())->everyMinute()->onOneServer();
$schedule->job(new Remote\PushWorkOrder())->everyMinute()->onOneServer(); $schedule->job(new Remote\PushWorkOrder())->everyMinute()->onOneServer();

View File

@ -0,0 +1,10 @@
<?php
namespace App\Exceptions\User;
use Exception;
class BalanceNotEnoughException extends Exception
{
//
}

View File

@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class BalanceController extends Controller
{
//
public function index(Request $request)
{
//
$balance = $request->user();
return $this->success($balance);
}
public function store(Request $request)
{
// 充值
$request->validate([
'amount' => 'required|numeric',
]);
$balance = $request->user();
// 启用事物
\DB::beginTransaction();
try {
$balance->increment('amount', $request->amount);
\DB::commit();
} catch (\Exception $e) {
\DB::rollBack();
return $this->error($e->getMessage());
}
return $this->success($balance);
}
// // 转换为 drops
// public function transfer($amount = 1)
// {
// $balance = auth('sanctum')->user();
// $balance->decrement('amount', $request->amount);
// return $this->success($balance);
// }
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers;
use Cache;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function index(Request $request)
{
$user = $request->user();
$user['drops'] = (float) Cache::get('user_drops_' . $user['id'], 0);
return $this->success($user);
}
}

View File

@ -11,6 +11,7 @@
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Log;
class UserSave implements ShouldQueue class UserSave implements ShouldQueue
{ {
@ -34,7 +35,11 @@ public function __construct()
*/ */
public function handle() public function handle()
{ {
Host::active()->chunk(100, function ($hosts) {
// 弃用
return false;
Host::all()->chunk(100, function ($hosts) {
foreach ($hosts as $host) { foreach ($hosts as $host) {
$this->cache_key = 'user_' . $host->user_id; $this->cache_key = 'user_' . $host->user_id;
@ -43,11 +48,18 @@ public function handle()
if (Cache::has($this->cache_key)) { if (Cache::has($this->cache_key)) {
// if user is not instances of Model // if user is not instances of Model
$user = Cache::get($this->cache_key); $user = Cache::get($this->cache_key);
Log::debug($user);
if ($user instanceof User) { if ($user instanceof User) {
$this->await($this->cache_key, function () use ($user, $host) { $this->await($this->cache_key, function () use ($user) {
$user->save(); $user->save();
}); });
} }
} else {
// save cache
$this->cache->put($this->cache_key, $host->user, now()->addDay());
} }
} }
}); });

View File

@ -2,12 +2,14 @@
namespace App\Models; namespace App\Models;
use Log;
use App\Models\Module\Module; use App\Models\Module\Module;
use App\Exceptions\CommonException; use App\Exceptions\CommonException;
use App\Models\WorkOrder\WorkOrder; use App\Models\WorkOrder\WorkOrder;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Model;
// use Illuminate\Database\Eloquent\SoftDeletes; // use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Model;
use App\Exceptions\User\BalanceNotEnoughException;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
class Host extends Model class Host extends Model
@ -77,21 +79,26 @@ public function scopeThisUser($query, $module = null)
public function cost($price = null) public function cost($price = null)
{ {
$cache_key = 'user_' . $this->user_id; $this->load('user');
// if cache has user
if (Cache::has($cache_key)) { if ($this->user->balance < 10) {
// if user is not instances of Model $amount = 1;
$user = Cache::get($cache_key); } else if ($this->user->balance < 100) {
$amount = 10;
if (!($user instanceof User)) { } else if ($this->user->balance < 1000) {
$user = Cache::put($cache_key, $this->user, now()->addDay()); $amount = 100;
} } else if ($this->user->balance < 10000) {
$amount = 1000;
} else { } else {
$user = Cache::put($cache_key, $this->user, now()->addDay()); $amount = 10000;
} }
$cache_key = 'user_drops_' . $this->user_id;
$drops = Cache::get($cache_key);
// Log::debug($user); // Log::debug($user);
@ -103,24 +110,25 @@ public function cost($price = null)
$this->price = $this->managed_price; $this->price = $this->managed_price;
} }
// if drops <= price
if ($drops < $this->price) {
try {
$this->user->toDrops($amount);
} catch (BalanceNotEnoughException) {
$this->update([
'status' => 'suspended',
]);
$user->drops -= (int) $this->price; return false;
}
// update cache
Cache::put($cache_key, $user, now()->addDay());
// if $user->drops <= 0
if ($user->drops <= 0) {
$this->update([
'status' => 'suspended',
]);
} else if ($this->status == 'suspended') { } else if ($this->status == 'suspended') {
$this->update([ $this->update([
'status' => 'running', 'status' => 'stopped',
]); ]);
} }
Cache::decrement($cache_key, $this->price);
return true; return true;
} }

View File

@ -3,10 +3,14 @@
namespace App\Models; namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail; // use Illuminate\Contracts\Auth\MustVerifyEmail;
use Laravel\Sanctum\HasApiTokens;
use App\Exceptions\CommonException;
use App\Exceptions\User\BalanceNotEnoughException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable class User extends Authenticatable
{ {
@ -40,5 +44,38 @@ class User extends Authenticatable
*/ */
protected $casts = [ protected $casts = [
'email_verified_at' => 'datetime', 'email_verified_at' => 'datetime',
'balance' => 'float',
]; ];
public function toDrops($amount = 1)
{
$rate = Cache::get('drops_rate', 100);
$total = $amount * $rate;
$cache_key = 'user_drops_' . $this->id;
$lock = Cache::lock("lock_" . $cache_key, 5);
try {
$lock->block(5);
// if user balance <= 0
if ($this->balance < $amount) {
throw new BalanceNotEnoughException('余额不足');
}
$this->balance -= $amount;
$this->save();
// increment user drops
Cache::increment($cache_key, $total);
} catch (LockTimeoutException) {
throw new CommonException('暂时无法处理此请求,请稍后再试。');
} finally {
optional($lock)->release();
}
return $this;
}
} }

View File

@ -46,6 +46,23 @@ public static function boot()
$rate = Cache::get('drops_rate', 100); $rate = Cache::get('drops_rate', 100);
$drops->total = $drops->amount * $rate; $drops->total = $drops->amount * $rate;
$this->cache_key = 'user_' . $drops->user_id;
// if cache has user
// if (Cache::has($this->cache_key)) {
// // if user is not instances of Model
// $user = Cache::get($this->cache_key);
// if ($user instanceof User) {
// $this->await($this->cache_key, function () use ($user) {
// $user->save();
// });
// }
// // delete cache
// Cache::forget($this->cache_key);
// }
}); });
// created // created

View File

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
//
$table->decimal('balance', 10, 2)->default(0)->after('password');
// drop column if exists
if (Schema::hasColumn('users', 'drops')) {
$table->dropColumn('drops');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
//
$table->dropColumn('balance');
});
}
};

View File

@ -4,7 +4,7 @@
use App\Http\Controllers\User\DropController; use App\Http\Controllers\User\DropController;
use App\Http\Controllers\User\TaskController; use App\Http\Controllers\User\TaskController;
use App\Http\Controllers\Remote\ModuleController; use App\Http\Controllers\Remote\ModuleController;
use App\Http\Controllers\Admin\User\UserController; use App\Http\Controllers\UserController;
use App\Http\Controllers\User\WorkOrder\ReplyController; use App\Http\Controllers\User\WorkOrder\ReplyController;
use App\Http\Controllers\User\WorkOrder\WorkOrderController; use App\Http\Controllers\User\WorkOrder\WorkOrderController;