改进 扣费

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

View File

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

View File

@ -3,10 +3,14 @@
namespace App\Models;
// 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\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
@ -40,5 +44,38 @@ class User extends Authenticatable
*/
protected $casts = [
'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);
$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

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\TaskController;
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\WorkOrderController;