更精确的计费 采用 bc ath
This commit is contained in:
parent
5214362404
commit
e0d8fe9cdd
10
app/Exceptions/Transaction/TransactionFailedException.php
Normal file
10
app/Exceptions/Transaction/TransactionFailedException.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Transaction;
|
||||
|
||||
use Exception;
|
||||
|
||||
class TransactionFailedException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Balance;
|
||||
use App\Models\Host;
|
||||
use App\Models\Transaction;
|
||||
use App\Models\User;
|
||||
use App\Models\UserGroup;
|
||||
use App\Models\WorkOrder\WorkOrder;
|
||||
@ -101,8 +100,6 @@ public function update(Request $request, User $user): RedirectResponse
|
||||
'id_card' => 'nullable|string|size:18',
|
||||
]);
|
||||
|
||||
$transaction = new Transaction();
|
||||
|
||||
if ($request->input('is_banned')) {
|
||||
$user->banned_at = Carbon::now();
|
||||
|
||||
@ -125,9 +122,13 @@ public function update(Request $request, User $user): RedirectResponse
|
||||
} else if ($one_time_action == 'stop_all_hosts') {
|
||||
$user->hosts()->update(['status' => 'stopped', 'suspended_at' => null]);
|
||||
} else if ($one_time_action == 'add_balance') {
|
||||
$transaction->addAmount($user->id, 'console', $request->balance ?? 0, '管理员添加。', true);
|
||||
$description = '管理员 ' . $request->user('admin')->name . " 增加。";
|
||||
|
||||
$user->charge($request->input('balance'), 'console', $description);
|
||||
} else if ($one_time_action == 'reduce_balance') {
|
||||
$transaction->reduceAmount($user->id, $request->balance ?? 0, '管理员扣除。');
|
||||
$description = '管理员 ' . $request->user('admin')->name . " 扣除。";
|
||||
|
||||
$user->reduce($request->input('balance'), $description);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,12 +6,14 @@
|
||||
use App\Models\Transaction;
|
||||
use App\Models\User;
|
||||
use App\Support\RealNameSupport;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RealNameController extends Controller
|
||||
{
|
||||
public function verify(Request $request)
|
||||
public function verify(Request $request): JsonResponse
|
||||
{
|
||||
$result = (new RealNameSupport())->verify($request->all());
|
||||
|
||||
@ -20,7 +22,7 @@ public function verify(Request $request)
|
||||
return $this->error('实名认证失败。');
|
||||
}
|
||||
|
||||
$user = User::find($result['user_id']);
|
||||
$user = (new User)->find($result['user_id']);
|
||||
$user->real_name = $result['name'];
|
||||
$user->id_card = $result['id_card'];
|
||||
$user->save();
|
||||
@ -31,7 +33,7 @@ public function verify(Request $request)
|
||||
return $this->success('实名认证成功。');
|
||||
}
|
||||
|
||||
public function process()
|
||||
public function process(): View
|
||||
{
|
||||
return view('real_name.process');
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ public function store(Request $request): RedirectResponse
|
||||
*/
|
||||
public function show(Request $request, Balance $balance): RedirectResponse|JsonResponse|View
|
||||
{
|
||||
|
||||
if ($balance->isPaid()) {
|
||||
if ($request->ajax()) {
|
||||
return $this->success($balance);
|
||||
@ -228,8 +227,7 @@ function notify(
|
||||
}
|
||||
|
||||
if ($is_paid) {
|
||||
(new Transaction)->addAmount($balance->user_id, $balance->payment, $balance->amount);
|
||||
|
||||
// $balance->user->charge($balance->amount, $balance->payment, $balance->order_id);
|
||||
$balance->update([
|
||||
'paid_at' => now()
|
||||
]);
|
||||
|
@ -4,13 +4,15 @@
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Support\RealNameSupport;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RealNameController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'real_name' => 'required|string',
|
||||
@ -54,7 +56,7 @@ public function store(Request $request)
|
||||
return redirect($output);
|
||||
}
|
||||
|
||||
public function create()
|
||||
public function create(): View
|
||||
{
|
||||
return view('real_name.create');
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Transaction;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@ -11,8 +10,6 @@
|
||||
|
||||
class TransferController extends Controller
|
||||
{
|
||||
//
|
||||
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
@ -24,7 +21,7 @@ public function index(Request $request): View
|
||||
public function transfer(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'amount' => 'numeric|min:1|max:100',
|
||||
'amount' => 'string|min:1|max:100',
|
||||
'description' => 'nullable|string|max:100',
|
||||
]);
|
||||
|
||||
@ -38,14 +35,15 @@ public function transfer(Request $request): RedirectResponse
|
||||
return back()->withErrors(['to' => '不能转给自己。']);
|
||||
}
|
||||
|
||||
$transaction = new Transaction();
|
||||
$amount = $request->input('amount');
|
||||
|
||||
if ($user->balance < $request->input('amount')) {
|
||||
return back()->withErrors(['amount' => '您的余额不足。']);
|
||||
} else {
|
||||
$transaction->transfer($user, $to, $request->input('amount'), $request->input('description'));
|
||||
// 使用 bc 判断金额是否足够
|
||||
if (bccomp($amount, $user->balance, 2) > 0) {
|
||||
return back()->withErrors(['amount' => '余额不足。']);
|
||||
}
|
||||
|
||||
$user->startTransfer($to, $amount, $request->input('description'));
|
||||
|
||||
return back()->with('success', '转账成功,已达对方账户。');
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
use App\Jobs\Job;
|
||||
use App\Models\Balance;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Yansongda\LaravelPay\Facades\Pay;
|
||||
use Yansongda\Pay\Exception\ContainerException;
|
||||
@ -62,8 +61,6 @@ public function checkAndCharge(Balance $balance, $check = false): bool
|
||||
return true;
|
||||
}
|
||||
|
||||
(new Transaction)->addAmount($balance->user_id, 'alipay', $balance->amount);
|
||||
|
||||
$balance->update([
|
||||
'paid_at' => now()
|
||||
]);
|
||||
|
@ -48,6 +48,8 @@ protected static function boot()
|
||||
if ($balance->paid_at) {
|
||||
$balance->notify(new UserCharged());
|
||||
broadcast(new Users($balance->user, 'balance.updated', $balance));
|
||||
|
||||
$balance->user->charge($balance->amount, $balance->payment, $balance->order_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -45,11 +45,11 @@ protected static function boot()
|
||||
$model->minute_at = now()->minute;
|
||||
|
||||
if ($model->price !== null) {
|
||||
$model->price = round($model->price, 2);
|
||||
$model->price = bcdiv($model->price, 1, 2);
|
||||
}
|
||||
|
||||
if ($model->managed_price !== null) {
|
||||
$model->managed_price = round($model->managed_price, 2);
|
||||
$model->managed_price = bcdiv($model->managed_price, 1, 2);
|
||||
}
|
||||
});
|
||||
|
||||
@ -153,7 +153,7 @@ public function safeDelete(): bool
|
||||
return true;
|
||||
}
|
||||
|
||||
public function cost($amount = null, $auto = true): bool
|
||||
public function cost(string $amount = null, $auto = true): bool
|
||||
{
|
||||
$this->load('user');
|
||||
$user = $this->user;
|
||||
@ -177,20 +177,23 @@ public function cost($amount = null, $auto = true): bool
|
||||
$append_description = '';
|
||||
if ($user_group) {
|
||||
if ($user_group->discount !== 100 && $user_group->discount !== null) {
|
||||
$real_price = $real_price * ($user_group->discount / 100);
|
||||
$real_price = bcmul($real_price, bcdiv($user_group->discount, "100", 2), 2);
|
||||
|
||||
$append_description = ' (折扣 ' . $user_group->discount . '%)';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($auto) {
|
||||
// 获取本月天数
|
||||
$days = now()->daysInMonth;
|
||||
// 本月每天的每小时的价格
|
||||
$real_price = $real_price / $days / 24;
|
||||
// 使用 bcmath 函数,解决浮点数计算精度问题
|
||||
$real_price = bcdiv($real_price, $days, 4);
|
||||
$real_price = bcdiv($real_price, 24, 4);
|
||||
}
|
||||
|
||||
if ($real_price == 0) {
|
||||
echo '价格为 0,不扣费';
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -199,9 +202,7 @@ public function cost($amount = null, $auto = true): bool
|
||||
$real_price = 0.0001;
|
||||
}
|
||||
|
||||
$real_price = round($real_price ?? 0, 4);
|
||||
|
||||
$transaction = new Transaction();
|
||||
$real_price = bcdiv($real_price, 1, 4);
|
||||
|
||||
$month = now()->month;
|
||||
|
||||
@ -215,7 +216,7 @@ public function cost($amount = null, $auto = true): bool
|
||||
$hosts_balances[$this->id] = $real_price;
|
||||
}
|
||||
|
||||
$hosts_balances[$this->id] = round($hosts_balances[$this->id], 4);
|
||||
$hosts_balances[$this->id] = bcdiv($hosts_balances[$this->id], 1, 4);
|
||||
|
||||
Cache::put($month_cache_key, $hosts_balances, 604800);
|
||||
|
||||
@ -229,7 +230,12 @@ public function cost($amount = null, $auto = true): bool
|
||||
$description .= $append_description;
|
||||
}
|
||||
|
||||
$left = $transaction->reduceHostAmount($this->user_id, $this->id, $this->module_id, $real_price, $description);
|
||||
$data = [
|
||||
'host_id' => $this->id,
|
||||
'module_id' => $this->module_id,
|
||||
];
|
||||
|
||||
$left = $user->reduce($real_price, $description, false, $data);
|
||||
|
||||
$this->addLog($real_price);
|
||||
|
||||
@ -244,9 +250,9 @@ public function cost($amount = null, $auto = true): bool
|
||||
return true;
|
||||
}
|
||||
|
||||
public function addLog(float|null $amount = 0): bool
|
||||
public function addLog(string $amount = "0"): bool
|
||||
{
|
||||
if ($amount === 0 || $amount === null) {
|
||||
if ($amount === "0") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -256,12 +262,12 @@ public function addLog(float|null $amount = 0): bool
|
||||
|
||||
$cache_key = 'module_earning_' . $this->module_id;
|
||||
|
||||
$commission = (float)config('billing.commission');
|
||||
$commission = config('billing.commission');
|
||||
|
||||
$should_amount = round($amount * $commission, 2);
|
||||
$should_amount = bcmul($amount, $commission, 2);
|
||||
|
||||
// 应得的余额
|
||||
$should_balance = $amount - $should_amount;
|
||||
$should_balance = bcsub($amount, $should_amount, 2);
|
||||
|
||||
$earnings = Cache::get($cache_key, []);
|
||||
|
||||
@ -270,8 +276,10 @@ public function addLog(float|null $amount = 0): bool
|
||||
}
|
||||
|
||||
if (isset($earnings[$current_year][$current_month])) {
|
||||
$earnings[$current_year][$current_month]['balance'] += $amount;
|
||||
$earnings[$current_year][$current_month]['should_balance'] += $should_balance;
|
||||
$earnings[$current_year][$current_month]['balance'] = bcadd($earnings[$current_year][$current_month]['balance'], $amount, 2);
|
||||
$earnings[$current_year][$current_month]['should_balance'] = bcadd($earnings[$current_year][$current_month]['should_balance'], $should_balance, 2);
|
||||
|
||||
|
||||
} else {
|
||||
$earnings[$current_year][$current_month] = [
|
||||
'balance' => $amount,
|
||||
|
@ -2,11 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Exceptions\User\BalanceNotEnoughException;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\HigherOrderBuilderProxy;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\HigherOrderCollectionProxy;
|
||||
use Jenssegers\Mongodb\Eloquent\Model;
|
||||
|
||||
class Transaction extends Model
|
||||
@ -27,7 +23,7 @@ class Transaction extends Model
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
// 交易类型
|
||||
// 类型
|
||||
'type',
|
||||
|
||||
// 交易渠道
|
||||
@ -36,15 +32,12 @@ class Transaction extends Model
|
||||
// 描述
|
||||
'description',
|
||||
|
||||
// 入账
|
||||
'income',
|
||||
// 交易金额,负数则是扣除
|
||||
'amount',
|
||||
|
||||
// 出账
|
||||
'outcome',
|
||||
|
||||
// 可用余额
|
||||
'balances',
|
||||
'balance',
|
||||
// 剩余余额
|
||||
'user_remain',
|
||||
'module_remain',
|
||||
|
||||
// 赠送金额
|
||||
'gift',
|
||||
@ -59,219 +52,32 @@ public function scopeThisUser($query)
|
||||
return $query->where('user_id', auth()->id());
|
||||
}
|
||||
|
||||
public function reduceAmount($user_id, $amount = 0, $description = '扣除费用请求。')
|
||||
// on create
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
$lock = Cache::lock("user_balance_lock_" . $user_id, 10);
|
||||
try {
|
||||
static::creating(function (self $transaction) {
|
||||
$user = null;
|
||||
$module = null;
|
||||
|
||||
$lock->block(5);
|
||||
|
||||
$user = (new User)->findOrFail($user_id);
|
||||
|
||||
$user->balance -= $amount;
|
||||
$user->save();
|
||||
|
||||
$this->addPayoutBalance($user_id, $amount, $description);
|
||||
} finally {
|
||||
optional($lock)->release();
|
||||
}
|
||||
|
||||
return $user->balance;
|
||||
}
|
||||
|
||||
public function addPayoutBalance($user_id, $amount, $description, $module_id = null)
|
||||
{
|
||||
$data = [
|
||||
'type' => 'payout',
|
||||
'payment' => 'balance',
|
||||
'description' => $description,
|
||||
'income' => 0,
|
||||
'outcome' => (float)$amount,
|
||||
];
|
||||
|
||||
if ($module_id) {
|
||||
$data['module_id'] = $module_id;
|
||||
}
|
||||
|
||||
return $this->addLog($user_id, $data);
|
||||
}
|
||||
|
||||
private function addLog($user_id, $data)
|
||||
{
|
||||
$user = (new User)->find($user_id);
|
||||
|
||||
$current = [
|
||||
'balance' => (float)$user->balance,
|
||||
'user_id' => intval($user_id),
|
||||
];
|
||||
|
||||
// merge
|
||||
$data = array_merge($data, $current);
|
||||
|
||||
// add expired at
|
||||
$data['expired_at'] = now()->addSeconds(7);
|
||||
|
||||
/** @noinspection PhpUndefinedMethodInspection */
|
||||
return $this->create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BalanceNotEnoughException
|
||||
*/
|
||||
public function reduceAmountModuleFail($user_id, $module_id, $amount = 0, $description = '扣除费用请求。')
|
||||
{
|
||||
|
||||
$lock = Cache::lock("user_balance_lock_" . $user_id, 10);
|
||||
try {
|
||||
|
||||
$lock->block(5);
|
||||
|
||||
$user = (new User)->findOrFail($user_id);
|
||||
|
||||
$user->balance -= $amount;
|
||||
|
||||
// if balance < 0
|
||||
if ($user->balance < 0) {
|
||||
throw new BalanceNotEnoughException('余额不足。');
|
||||
if ($transaction->user_id) {
|
||||
$user = (new User)->find($transaction->user_id);
|
||||
}
|
||||
|
||||
$user->save();
|
||||
|
||||
$this->addPayoutBalance($user_id, $amount, $description, $module_id);
|
||||
} finally {
|
||||
optional($lock)->release();
|
||||
}
|
||||
|
||||
return $user->balance;
|
||||
}
|
||||
|
||||
public function reduceHostAmount($user_id, $host_id, $module_id, $amount = 0, $description = '扣除费用请求。')
|
||||
{
|
||||
|
||||
$lock = Cache::lock("user_balance_lock_" . $user_id, 10);
|
||||
try {
|
||||
|
||||
$lock->block(5);
|
||||
|
||||
$user = (new User)->findOrFail($user_id);
|
||||
|
||||
$user->balance -= $amount;
|
||||
$user->save();
|
||||
|
||||
$this->addHostPayoutBalance($user_id, $host_id, $module_id, $amount, $description);
|
||||
} finally {
|
||||
optional($lock)->release();
|
||||
}
|
||||
|
||||
return $user->balance;
|
||||
}
|
||||
|
||||
public function addHostPayoutBalance($user_id, $host_id, $module_id, $amount, $description)
|
||||
{
|
||||
$data = [
|
||||
'type' => 'payout',
|
||||
'payment' => 'balance',
|
||||
'description' => $description,
|
||||
'income' => 0,
|
||||
'outcome' => (float)$amount,
|
||||
'host_id' => $host_id,
|
||||
'module_id' => $module_id,
|
||||
];
|
||||
|
||||
return $this->addLog($user_id, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $user_id
|
||||
* @param string $payment
|
||||
* @param int $amount
|
||||
* @param null $description
|
||||
* @param bool $add_charge_log
|
||||
*
|
||||
* @return float|HigherOrderBuilderProxy|HigherOrderCollectionProxy|int|mixed|string
|
||||
*/
|
||||
public function addAmount($user_id, string $payment = 'console', int $amount = 0, $description = null, bool $add_charge_log = false): mixed
|
||||
{
|
||||
$lock = Cache::lock("user_balance_lock_" . $user_id, 10);
|
||||
try {
|
||||
|
||||
$lock->block(5);
|
||||
|
||||
$user = (new User)->findOrFail($user_id);
|
||||
|
||||
$left_balance = $user->balance + $amount;
|
||||
|
||||
$user->increment('balance', $amount);
|
||||
|
||||
if (!$description) {
|
||||
$description = '充值 ' . $amount . ' 元';
|
||||
if ($transaction->module_id) {
|
||||
$module = (new Module)->find($transaction->module_id);
|
||||
}
|
||||
|
||||
if ($add_charge_log) {
|
||||
$data = [
|
||||
'user_id' => $user_id,
|
||||
'amount' => $amount,
|
||||
'payment' => $payment,
|
||||
'paid_at' => Carbon::now(),
|
||||
];
|
||||
|
||||
(new Balance)->create($data);
|
||||
if ($user) {
|
||||
$transaction->user_remain = $user->balance;
|
||||
}
|
||||
|
||||
$this->addIncomeBalance($user_id, $payment, $amount, $description);
|
||||
} finally {
|
||||
optional($lock)->release();
|
||||
}
|
||||
|
||||
return $left_balance;
|
||||
}
|
||||
|
||||
public function addIncomeBalance($user_id, $payment, $amount, $description)
|
||||
{
|
||||
$data = [
|
||||
'type' => 'income',
|
||||
'payment' => $payment,
|
||||
'description' => $description,
|
||||
'income' => (float)$amount,
|
||||
'outcome' => 0,
|
||||
];
|
||||
|
||||
return $this->addLog($user_id, $data);
|
||||
}
|
||||
|
||||
public function transfer(User $user, User $to, float $amount, string|null $description): float
|
||||
{
|
||||
$lock = Cache::lock("user_balance_lock_" . $user->id, 10);
|
||||
$lock_to = Cache::lock("user_balance_lock_" . $to->id, 10);
|
||||
try {
|
||||
|
||||
$lock->block(5);
|
||||
$lock_to->block(5);
|
||||
|
||||
$user->balance -= $amount;
|
||||
$user->save();
|
||||
|
||||
$to->balance += $amount;
|
||||
$to->save();
|
||||
|
||||
if (!$description) {
|
||||
$description = '完成。';
|
||||
if ($module) {
|
||||
$transaction->module_remain = $module->balance;
|
||||
}
|
||||
|
||||
$description_new = "转账给 $to->name($to->email) $amount 元,$description";
|
||||
|
||||
$this->addPayoutBalance($user->id, $amount, $description_new);
|
||||
|
||||
$description_new = "收到来自 $user->name($user->email) 转来的 $amount 元, $description";
|
||||
|
||||
$this->addIncomeBalance($to->id, 'transfer', $amount, $description_new);
|
||||
} finally {
|
||||
optional($lock)->release();
|
||||
optional($lock_to)->release();
|
||||
}
|
||||
|
||||
return $user->balance;
|
||||
$transaction->expired_at = Carbon::now()->addSeconds(7)->toString();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use App\Exceptions\User\BalanceNotEnoughException;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
@ -12,6 +13,7 @@
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
@ -111,9 +113,9 @@ private function getBirthdayFromIdCard(): string
|
||||
$idCard = $this->id_card;
|
||||
|
||||
$bir = substr($idCard, 6, 8);
|
||||
$year = (int) substr($bir, 0, 4);
|
||||
$month = (int) substr($bir, 4, 2);
|
||||
$day = (int) substr($bir, 6, 2);
|
||||
$year = (int)substr($bir, 0, 4);
|
||||
$month = (int)substr($bir, 4, 2);
|
||||
$day = (int)substr($bir, 6, 2);
|
||||
|
||||
return $year . '-' . $month . '-' . $day;
|
||||
}
|
||||
@ -164,4 +166,107 @@ public function selectPublic(): User
|
||||
// 过滤掉私有字段
|
||||
return $this->select(['id', 'name', 'email_md5', 'created_at']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 扣除费用
|
||||
*
|
||||
* @param string $amount
|
||||
* @param string $description
|
||||
* @param bool $fail
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function reduce(string $amount = "0", string $description = "消费", bool $fail = false, array $options = []): string
|
||||
{
|
||||
Cache::lock('user_balance_' . $this->id, 10)->block(10, function () use ($amount, $fail, $description, $options) {
|
||||
$this->refresh();
|
||||
|
||||
if ($this->balance < $amount) {
|
||||
if ($fail) {
|
||||
throw new BalanceNotEnoughException();
|
||||
}
|
||||
}
|
||||
|
||||
$this->balance = bcsub($this->balance, $amount, 2);
|
||||
$this->save();
|
||||
|
||||
$data = [
|
||||
'user_id' => $this->id,
|
||||
'amount' => $amount,
|
||||
'description' => $description,
|
||||
'payment' => 'balance',
|
||||
'type' => 'payout',
|
||||
];
|
||||
|
||||
if ($options) {
|
||||
$data = array_merge($data, $options);
|
||||
}
|
||||
|
||||
(new Transaction)->create($data);
|
||||
});
|
||||
|
||||
return $this->balance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加余额
|
||||
*
|
||||
* @param string $amount
|
||||
* @param string $payment
|
||||
* @param string $description
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function charge(string $amount = "0", string $payment = 'console', string $description = '充值', array $options = []): string
|
||||
{
|
||||
Cache::lock('user_balance_' . $this->id, 10)->block(10, function () use ($amount, $description, $payment, $options) {
|
||||
$this->refresh();
|
||||
$this->balance = bcadd($this->balance, $amount, 2);
|
||||
$this->save();
|
||||
|
||||
$data = [
|
||||
'user_id' => $this->id,
|
||||
'amount' => $amount,
|
||||
'payment' => $payment,
|
||||
'description' => $description,
|
||||
'type' => 'income',
|
||||
];
|
||||
|
||||
if ($options) {
|
||||
$data = array_merge($data, $options);
|
||||
}
|
||||
|
||||
(new Transaction)->create($data);
|
||||
|
||||
(new Balance)->create([
|
||||
'user_id' => $this->id,
|
||||
'amount' => $amount,
|
||||
'payment' => $payment,
|
||||
'description' => $description,
|
||||
'paid_at' => now(),
|
||||
]);
|
||||
});
|
||||
|
||||
return $this->balance;
|
||||
}
|
||||
|
||||
public function startTransfer(User $to, string $amount, string|null $description)
|
||||
{
|
||||
$description_from = "转账给 $to->name($to->email)";
|
||||
$description_to = "收到 $this->name($this->email) 的转账";
|
||||
|
||||
if ($description) {
|
||||
$description_from .= ",备注:$description";
|
||||
$description_to .= ",备注:$description";
|
||||
}
|
||||
|
||||
$this->reduce($amount, $description_from, true);
|
||||
|
||||
$to->charge($amount, 'transfer', $description_to);
|
||||
|
||||
return $this->balance;
|
||||
}
|
||||
}
|
||||
|
@ -7,14 +7,14 @@
|
||||
|
||||
class Payment extends Component
|
||||
{
|
||||
public string $payment = '';
|
||||
public string|null $payment = '';
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $payment)
|
||||
public function __construct(string|null $payment)
|
||||
{
|
||||
//
|
||||
$this->payment = $payment;
|
||||
|
@ -30,7 +30,8 @@
|
||||
"spatie/laravel-tags": "^4.3",
|
||||
"spiral/roadrunner": "^2.8.2",
|
||||
"symfony/psr-http-message-bridge": "^2.1",
|
||||
"yansongda/laravel-pay": "~3.2.0"
|
||||
"yansongda/laravel-pay": "~3.2.0",
|
||||
"ext-bcmath": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"beyondcode/laravel-query-detector": "^1.6",
|
||||
|
@ -13,13 +13,12 @@
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">类型与模块</th>
|
||||
<th scope="col">模块</th>
|
||||
<th scope="col">支付方式</th>
|
||||
<th scope="col">说明</th>
|
||||
<th scope="col">用户 ID</th>
|
||||
<th scope="col">主机 ID</th>
|
||||
<th scope="col">入账</th>
|
||||
<th scope="col">支出</th>
|
||||
<th scope="col">金额</th>
|
||||
<th scope="col">余额</th>
|
||||
<th scope="col">交易时间</th>
|
||||
</tr>
|
||||
@ -27,19 +26,8 @@
|
||||
<tbody>
|
||||
@foreach ($transactions as $t)
|
||||
<tr>
|
||||
<td>
|
||||
@if ($t->type === 'payout')
|
||||
<span class="text-danger">
|
||||
支出
|
||||
</span>
|
||||
@elseif($t->type === 'income')
|
||||
<span class="text-success">
|
||||
收入
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<td>
|
||||
<span class="module_name" module="{{ $t->module_id }}">{{ $t->module_id }}</span>
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<x-payment :payment="$t->payment"></x-payment>
|
||||
@ -61,16 +49,21 @@
|
||||
<a href="?host_id={{ $t->host_id }}">筛选</a>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-success">
|
||||
{{ $t->income }} 元
|
||||
</td>
|
||||
|
||||
<td class="text-danger">
|
||||
{{ $t->outcome }} 元
|
||||
<td>
|
||||
@if ($t->type === 'payout')
|
||||
<span class="text-danger">
|
||||
支出 {{ $t->amount }} 元
|
||||
</span>
|
||||
@elseif($t->type === 'income')
|
||||
<span class="text-success">
|
||||
收入 {{ $t->amount }} 元
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{ $t->balance ?? $t->balances }} 元
|
||||
{{ $t->user_remain ?? $t->balance }} 元
|
||||
</td>
|
||||
<td>
|
||||
{{ $t->created_at }}
|
||||
|
@ -13,11 +13,10 @@
|
||||
<table class="table table-hover">
|
||||
<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>
|
||||
<th scope="col">余额</th>
|
||||
<th scope="col">交易时间</th>
|
||||
</tr>
|
||||
@ -26,17 +25,7 @@
|
||||
@foreach ($transactions as $t)
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
@if ($t->type === 'payout')
|
||||
<span class="text-danger">
|
||||
支出
|
||||
</span>
|
||||
@elseif($t->type === 'income')
|
||||
<span class="text-success">
|
||||
收入
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<td>
|
||||
<span class="module_name" module="{{ $t->module_id }}">{{ $t->module_id }}</span>
|
||||
|
||||
</td>
|
||||
@ -46,16 +35,21 @@
|
||||
<td>
|
||||
{{ $t->description }}
|
||||
</td>
|
||||
<td class="text-success">
|
||||
{{ $t->income }} 元
|
||||
</td>
|
||||
|
||||
<td class="text-danger">
|
||||
{{ $t->outcome }} 元
|
||||
<td>
|
||||
@if ($t->type === 'payout')
|
||||
<span class="text-danger">
|
||||
支出 {{ $t->amount }} 元
|
||||
</span>
|
||||
@elseif($t->type === 'income')
|
||||
<span class="text-success">
|
||||
收入 {{ $t->amount }} 元
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{ $t->balance ?? $t->balances }} 元
|
||||
{{ $t->amount ?? $t->balance }} 元
|
||||
</td>
|
||||
<td>
|
||||
{{ $t->created_at }}
|
||||
|
Loading…
Reference in New Issue
Block a user