移除 周期性计费
This commit is contained in:
parent
d8503a04a3
commit
ac538adc3a
@ -5,7 +5,6 @@
|
|||||||
use App\Jobs\Host\DeleteHostJob;
|
use App\Jobs\Host\DeleteHostJob;
|
||||||
use App\Jobs\Host\DispatchHostCostQueueJob;
|
use App\Jobs\Host\DispatchHostCostQueueJob;
|
||||||
use App\Jobs\Host\ScanAllHostsJob;
|
use App\Jobs\Host\ScanAllHostsJob;
|
||||||
use App\Jobs\Host\SuspendOverdueHosts;
|
|
||||||
use App\Jobs\Module\DispatchFetchModuleJob;
|
use App\Jobs\Module\DispatchFetchModuleJob;
|
||||||
use App\Jobs\Module\SendModuleEarningsJob;
|
use App\Jobs\Module\SendModuleEarningsJob;
|
||||||
use App\Jobs\User\CheckAndChargeBalanceJob;
|
use App\Jobs\User\CheckAndChargeBalanceJob;
|
||||||
@ -61,9 +60,6 @@ protected function schedule(Schedule $schedule): void
|
|||||||
|
|
||||||
// 设置生日用户组
|
// 设置生日用户组
|
||||||
$schedule->job(new SetBirthdayGroupJob())->dailyAt('00:00')->onOneServer()->name('设置生日用户组');
|
$schedule->job(new SetBirthdayGroupJob())->dailyAt('00:00')->onOneServer()->name('设置生日用户组');
|
||||||
|
|
||||||
// 暂停到期的循环计费主机
|
|
||||||
$schedule->job(new SuspendOverdueHosts())->dailyAt('00:00')->onOneServer()->name('暂停到期的循环计费主机');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,12 +66,6 @@ public function store(Request $request): Response|JsonResponse
|
|||||||
|
|
||||||
$host = (new Host)->create($data);
|
$host = (new Host)->create($data);
|
||||||
|
|
||||||
if (! $user->hasBalance($host->getRenewPrice())) {
|
|
||||||
$host->delete();
|
|
||||||
|
|
||||||
return $this->error('此用户余额不足,无法开计费项目。');
|
|
||||||
}
|
|
||||||
|
|
||||||
$host['host_id'] = $host->id;
|
$host['host_id'] = $host->id;
|
||||||
|
|
||||||
return $this->created($host);
|
return $this->created($host);
|
||||||
@ -127,27 +121,6 @@ public function destroy($host): JsonResponse
|
|||||||
$host = (new Host)->findOrFail($host);
|
$host = (new Host)->findOrFail($host);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($host?->isCycle()) {
|
|
||||||
$days = $host->next_due_at->diffInDays(now());
|
|
||||||
|
|
||||||
// 算出 1 天的价格
|
|
||||||
$price = bcdiv($host->getPrice(), 31, 4);
|
|
||||||
|
|
||||||
// 算出退还的金额
|
|
||||||
$amount = bcmul($price, $days, 4);
|
|
||||||
|
|
||||||
$host->user->charge($amount, 'balance', '删除主机退款。', [
|
|
||||||
'module_id' => $this->module_id,
|
|
||||||
'host_id' => $this->id,
|
|
||||||
'user_id' => $this->user_id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$host->module->reduce($amount, '删除主机退款。', false, [
|
|
||||||
'module_id' => $this->module_id,
|
|
||||||
'host_id' => $this->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$host?->delete();
|
$host?->delete();
|
||||||
|
|
||||||
return $this->deleted();
|
return $this->deleted();
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Notifications\User\UserNotification;
|
use App\Notifications\User\UserNotification;
|
||||||
|
use function back;
|
||||||
|
use function config;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use function back;
|
|
||||||
use function config;
|
|
||||||
use function redirect;
|
use function redirect;
|
||||||
use function session;
|
use function session;
|
||||||
use function view;
|
use function view;
|
||||||
@ -33,13 +33,13 @@ public function index(Request $request): View|RedirectResponse
|
|||||||
$dashboardHost = parse_url(config('settings.dashboard.base_url'), PHP_URL_HOST);
|
$dashboardHost = parse_url(config('settings.dashboard.base_url'), PHP_URL_HOST);
|
||||||
|
|
||||||
if ($callbackHost === $dashboardHost) {
|
if ($callbackHost === $dashboardHost) {
|
||||||
if (!$request->user('web')->isRealNamed()) {
|
if (! $request->user('web')->isRealNamed()) {
|
||||||
return redirect()->route('real_name.create')->with('status', '重定向已被打断,需要先实人认证。');
|
return redirect()->route('real_name.create')->with('status', '重定向已被打断,需要先实人认证。');
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = $request->user()->createToken('Dashboard')->plainTextToken;
|
$token = $request->user()->createToken('Dashboard')->plainTextToken;
|
||||||
|
|
||||||
return redirect($callback . '?token=' . $token);
|
return redirect($callback.'?token='.$token);
|
||||||
}
|
}
|
||||||
|
|
||||||
session(['referer.domain' => parse_url($request->header('referer'), PHP_URL_HOST)]);
|
session(['referer.domain' => parse_url($request->header('referer'), PHP_URL_HOST)]);
|
||||||
@ -121,7 +121,7 @@ public function exitSudo(): RedirectResponse
|
|||||||
|
|
||||||
public function showAuthRequest($token): View|RedirectResponse
|
public function showAuthRequest($token): View|RedirectResponse
|
||||||
{
|
{
|
||||||
$data = Cache::get('auth_request:' . $token);
|
$data = Cache::get('auth_request:'.$token);
|
||||||
|
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
return redirect()->route('index')->with('error', '登录请求的 Token 不存在或已过期。');
|
return redirect()->route('index')->with('error', '登录请求的 Token 不存在或已过期。');
|
||||||
@ -145,7 +145,7 @@ public function storeAuthRequest(Request $request): RedirectResponse
|
|||||||
'token' => 'required|string|max:128',
|
'token' => 'required|string|max:128',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$data = Cache::get('auth_request:' . $request->input('token'));
|
$data = Cache::get('auth_request:'.$request->input('token'));
|
||||||
|
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
return back()->with('error', '登录请求的 Token 不存在或已过期。');
|
return back()->with('error', '登录请求的 Token 不存在或已过期。');
|
||||||
@ -169,7 +169,7 @@ public function storeAuthRequest(Request $request): RedirectResponse
|
|||||||
$data['token'] = $user->createToken($data['meta']['description'] ?? Carbon::now()->toDateString(), $abilities)->plainTextToken;
|
$data['token'] = $user->createToken($data['meta']['description'] ?? Carbon::now()->toDateString(), $abilities)->plainTextToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cache::put('auth_request:' . $request->input('token'), $data, 60);
|
Cache::put('auth_request:'.$request->input('token'), $data, 60);
|
||||||
|
|
||||||
return redirect()->route('index')->with('success', '登录请求已确认。');
|
return redirect()->route('index')->with('success', '登录请求已确认。');
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
use App\Models\Host;
|
use App\Models\Host;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class HostController extends Controller
|
class HostController extends Controller
|
||||||
@ -19,9 +18,8 @@ class HostController extends Controller
|
|||||||
public function index(): View
|
public function index(): View
|
||||||
{
|
{
|
||||||
$hosts = (new Host)->thisUser()->with(['user', 'module'])->paginate(20);
|
$hosts = (new Host)->thisUser()->with(['user', 'module'])->paginate(20);
|
||||||
$times = config('settings.billing.cycle_delete_times_every_month') - Cache::get('host_delete_times:'.auth('web')->id(), 0);
|
|
||||||
|
|
||||||
return view('hosts.index', compact('hosts', 'times'));
|
return view('hosts.index', compact('hosts'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Request $request, Host $host): RedirectResponse
|
public function update(Request $request, Host $host): RedirectResponse
|
||||||
@ -35,25 +33,6 @@ public function update(Request $request, Host $host): RedirectResponse
|
|||||||
return $status ? back()->with('success', '修改成功。') : back()->with('error', '修改失败。');
|
return $status ? back()->with('success', '修改成功。') : back()->with('error', '修改失败。');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renew(Host $host): RedirectResponse
|
|
||||||
{
|
|
||||||
$price = $host->getRenewPrice();
|
|
||||||
|
|
||||||
if ($price > auth()->user()->balance) {
|
|
||||||
return back()->with('error', '余额不足,续费需要:'.$price.' 元,您还需要充值:'.($price - auth()->user()->balance).' 元');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $host->isCycle()) {
|
|
||||||
return back()->with('error', '该主机不是周期性付费,无法续费。');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($host->renew()) {
|
|
||||||
return back()->with('success', '续费成功,新的到期时间为:'.$host->next_due_at.'。');
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()->with('error', '续费失败,请检查是否有足够的余额。');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the specified resource from storage.
|
* Remove the specified resource from storage.
|
||||||
*
|
*
|
||||||
|
@ -43,7 +43,7 @@ public function handle(): void
|
|||||||
$host = $host->where('minute_at', $this->minute);
|
$host = $host->where('minute_at', $this->minute);
|
||||||
}
|
}
|
||||||
|
|
||||||
$host->whereIn('status', ['running', 'stopped'])->whereNull('billing_cycle')->with('user')->chunk(500, function ($hosts) {
|
$host->whereIn('status', ['running', 'stopped'])->with('user')->chunk(500, function ($hosts) {
|
||||||
foreach ($hosts as $host) {
|
foreach ($hosts as $host) {
|
||||||
dispatch(new self($this->minute, $host));
|
dispatch(new self($this->minute, $host));
|
||||||
}
|
}
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Jobs\Host;
|
|
||||||
|
|
||||||
use App\Models\Host;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class SuspendOverdueHosts implements ShouldQueue
|
|
||||||
{
|
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
||||||
|
|
||||||
protected ?Host $host;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new job instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(?Host $host = null)
|
|
||||||
{
|
|
||||||
$this->host = $host;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the job.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
if (! $this->host) {
|
|
||||||
(new Host)->where('next_due_at', '<', now())
|
|
||||||
->where('status', '!=', 'suspended')
|
|
||||||
->chunk(100, function ($hosts) {
|
|
||||||
foreach ($hosts as $host) {
|
|
||||||
dispatch(new self($host));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->host?->suspend();
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Exceptions\User\BalanceNotEnoughException;
|
|
||||||
use App\Jobs\Host\HostJob;
|
use App\Jobs\Host\HostJob;
|
||||||
use App\Jobs\Host\UpdateOrDeleteHostJob;
|
use App\Jobs\Host\UpdateOrDeleteHostJob;
|
||||||
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
|
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
|
||||||
@ -47,26 +46,21 @@ public function getUserHosts($user_id = null): array|Collection
|
|||||||
})->get();
|
})->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function user(): BelongsToAlias
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function module(): BelongsToAlias
|
public function module(): BelongsToAlias
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Module::class);
|
return $this->belongsTo(Module::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeActive($query)
|
||||||
|
{
|
||||||
|
return $query->whereIn('status', ['running', 'stopped']);
|
||||||
|
}
|
||||||
|
|
||||||
// public function workOrders(): HasManyAlias
|
// public function workOrders(): HasManyAlias
|
||||||
// {
|
// {
|
||||||
// return $this->hasMany(WorkOrder::class);
|
// return $this->hasMany(WorkOrder::class);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public function scopeActive($query)
|
|
||||||
{
|
|
||||||
return $query->whereIn('status', ['running', 'stopped']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeDraft($query)
|
public function scopeDraft($query)
|
||||||
{
|
{
|
||||||
return $query->where('status', 'draft');
|
return $query->where('status', 'draft');
|
||||||
@ -91,11 +85,6 @@ public function scopeThisUser($query, $module = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function scopeExpiringDays($query, $days)
|
|
||||||
{
|
|
||||||
return $query->where('status', 'running')->where('next_due_at', '<=', now()->addDays($days));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isDraft(): bool
|
public function isDraft(): bool
|
||||||
{
|
{
|
||||||
return $this->status === 'draft';
|
return $this->status === 'draft';
|
||||||
@ -116,175 +105,20 @@ public function isUnavailable(): bool
|
|||||||
return $this->status === 'unavailable';
|
return $this->status === 'unavailable';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renew($first = false): bool
|
|
||||||
{
|
|
||||||
if (! $this->isCycle()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$price = $this->getRenewPrice();
|
|
||||||
|
|
||||||
$description = ($first ? '新购' : '续费').' '.$this->name.',价格:'.$price.' 元。';
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->user->reduce($price, $description, true, [
|
|
||||||
'module_id' => $this->module_id,
|
|
||||||
'host_id' => $this->id,
|
|
||||||
'user_id' => $this->user_id,
|
|
||||||
]);
|
|
||||||
$this->module->charge($price, 'balance', '用户'.$description, [
|
|
||||||
'module_id' => $this->module_id,
|
|
||||||
'host_id' => $this->id,
|
|
||||||
]);
|
|
||||||
} catch (BalanceNotEnoughException) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addLog($price);
|
|
||||||
|
|
||||||
$this->next_due_at = $this->getNewDueDate();
|
|
||||||
$this->last_paid = $price;
|
|
||||||
|
|
||||||
if ($this->isSuspended()) {
|
|
||||||
$this->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->save();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isCycle(): bool
|
|
||||||
{
|
|
||||||
return $this->billing_cycle !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRenewPrice(): string
|
|
||||||
{
|
|
||||||
return match ($this->billing_cycle) {
|
|
||||||
'monthly' => $this->getPrice(),
|
|
||||||
'quarterly' => bcmul($this->getPrice(), 3),
|
|
||||||
'semi-annually' => bcmul($this->getPrice(), 6),
|
|
||||||
'annually' => bcmul($this->getPrice(), 12),
|
|
||||||
'biennially' => bcmul($this->getPrice(), 24),
|
|
||||||
'triennially' => bcmul($this->getPrice(), 36),
|
|
||||||
default => '0',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPrice(): float
|
public function getPrice(): float
|
||||||
{
|
{
|
||||||
return $this->managed_price ?? $this->price;
|
return $this->managed_price ?? $this->price;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addLog(string $amount = '0'): bool
|
|
||||||
{
|
|
||||||
if ($amount === '0') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 统计收益开始 */
|
|
||||||
$current_month = now()->month;
|
|
||||||
$current_year = now()->year;
|
|
||||||
|
|
||||||
$cache_key = 'module_earning_'.$this->module_id;
|
|
||||||
|
|
||||||
// 应支付的提成
|
|
||||||
$commission = config('settings.billing.commission');
|
|
||||||
$should_amount = bcmul($amount, $commission, 4);
|
|
||||||
|
|
||||||
// 应得的余额
|
|
||||||
$should_balance = bcsub($amount, $should_amount, 4);
|
|
||||||
// 如果太小,则重置为 0.0001
|
|
||||||
if ($should_balance < 0.0001) {
|
|
||||||
$should_balance = 0.0001;
|
|
||||||
}
|
|
||||||
|
|
||||||
$earnings = Cache::get($cache_key, []);
|
|
||||||
|
|
||||||
if (! isset($earnings[$current_year])) {
|
|
||||||
$earnings[$current_year] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($earnings[$current_year][$current_month])) {
|
|
||||||
$earnings[$current_year][$current_month]['balance'] = bcadd($earnings[$current_year][$current_month]['balance'], $amount, 4);
|
|
||||||
$earnings[$current_year][$current_month]['should_balance'] = bcadd($earnings[$current_year][$current_month]['should_balance'], $should_balance, 4);
|
|
||||||
} else {
|
|
||||||
$earnings[$current_year][$current_month] = [
|
|
||||||
'balance' => $amount,
|
|
||||||
// 应得(交了手续费)
|
|
||||||
'should_balance' => $should_balance,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除 前 3 年的数据
|
|
||||||
if (count($earnings) > 3) {
|
|
||||||
$earnings = array_slice($earnings, -3, 3, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->module->charge($amount, 'balance', null);
|
|
||||||
|
|
||||||
// 保存 1 年
|
|
||||||
Cache::forever($cache_key, $earnings);
|
|
||||||
|
|
||||||
/** 统计收益结束 */
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNewDueDate(): string
|
|
||||||
{
|
|
||||||
$this->next_due_at = $this->next_due_at ?? now();
|
|
||||||
|
|
||||||
return match ($this->billing_cycle) {
|
|
||||||
'monthly' => $this->next_due_at->addMonth(),
|
|
||||||
'quarterly' => $this->next_due_at->addMonths(3),
|
|
||||||
'semi-annually' => $this->next_due_at->addMonths(6),
|
|
||||||
'annually' => $this->next_due_at->addYear(),
|
|
||||||
'biennially' => $this->next_due_at->addYears(2),
|
|
||||||
'triennially' => $this->next_due_at->addYears(3),
|
|
||||||
default => null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isSuspended(): bool
|
public function isSuspended(): bool
|
||||||
{
|
{
|
||||||
return $this->status === 'suspended';
|
return $this->status === 'suspended';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(): bool
|
|
||||||
{
|
|
||||||
$this->update([
|
|
||||||
'status' => 'running',
|
|
||||||
]);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function safeDelete(): bool
|
public function safeDelete(): bool
|
||||||
{
|
{
|
||||||
$is_user = auth()->guard('sanctum')->check() || auth()->guard('web')->check();
|
|
||||||
|
|
||||||
if ($this->isCycle() && $is_user) {
|
|
||||||
// 周期性的,每个月只能删除固定次数
|
|
||||||
$times = Cache::remember('host_delete_times:'.$this->user_id, 60 * 24 * 30, function () {
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
if ($times >= config('settings.billing.cycle_delete_times_every_month')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cache::increment('host_delete_times:'.$this->user_id);
|
|
||||||
|
|
||||||
// 根据 next_due_at 来计算退还的金额
|
|
||||||
if ($this->next_due_at === null) {
|
|
||||||
$this->next_due_at = now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果创建时间大于 1 小时
|
// 如果创建时间大于 1 小时
|
||||||
if (! $this->isCycle() && $this->created_at->diffInHours(now()) > 1) {
|
if ($this->created_at->diffInHours(now()) > 1) {
|
||||||
// 如果当前时间比扣费时间小,则说明没有扣费。执行扣费。
|
// 如果当前时间比扣费时间小,则说明没有扣费。执行扣费。
|
||||||
if (now()->minute < $this->minute_at) {
|
if (now()->minute < $this->minute_at) {
|
||||||
$this->cost();
|
$this->cost();
|
||||||
@ -396,6 +230,61 @@ public function cost(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addLog(string $amount = '0'): bool
|
||||||
|
{
|
||||||
|
if ($amount === '0') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 统计收益开始 */
|
||||||
|
$current_month = now()->month;
|
||||||
|
$current_year = now()->year;
|
||||||
|
|
||||||
|
$cache_key = 'module_earning_'.$this->module_id;
|
||||||
|
|
||||||
|
// 应支付的提成
|
||||||
|
$commission = config('settings.billing.commission');
|
||||||
|
$should_amount = bcmul($amount, $commission, 4);
|
||||||
|
|
||||||
|
// 应得的余额
|
||||||
|
$should_balance = bcsub($amount, $should_amount, 4);
|
||||||
|
// 如果太小,则重置为 0.0001
|
||||||
|
if ($should_balance < 0.0001) {
|
||||||
|
$should_balance = 0.0001;
|
||||||
|
}
|
||||||
|
|
||||||
|
$earnings = Cache::get($cache_key, []);
|
||||||
|
|
||||||
|
if (! isset($earnings[$current_year])) {
|
||||||
|
$earnings[$current_year] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($earnings[$current_year][$current_month])) {
|
||||||
|
$earnings[$current_year][$current_month]['balance'] = bcadd($earnings[$current_year][$current_month]['balance'], $amount, 4);
|
||||||
|
$earnings[$current_year][$current_month]['should_balance'] = bcadd($earnings[$current_year][$current_month]['should_balance'], $should_balance, 4);
|
||||||
|
} else {
|
||||||
|
$earnings[$current_year][$current_month] = [
|
||||||
|
'balance' => $amount,
|
||||||
|
// 应得(交了手续费)
|
||||||
|
'should_balance' => $should_balance,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 前 3 年的数据
|
||||||
|
if (count($earnings) > 3) {
|
||||||
|
$earnings = array_slice($earnings, -3, 3, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->module->charge($amount, 'balance', null);
|
||||||
|
|
||||||
|
// 保存 1 年
|
||||||
|
Cache::forever($cache_key, $earnings);
|
||||||
|
|
||||||
|
/** 统计收益结束 */
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function changeStatus(
|
public function changeStatus(
|
||||||
string $status
|
string $status
|
||||||
): bool {
|
): bool {
|
||||||
@ -406,14 +295,14 @@ public function changeStatus(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $this->isCycle() && ! $user->hasBalance('0.5')) {
|
if (! $user->hasBalance('0.5')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($status === 'running') {
|
if ($status === 'running') {
|
||||||
return $this->run();
|
return $this->run();
|
||||||
} elseif (($status === 'suspended' || $status === 'suspend') && ! $this->isCycle()) {
|
} elseif (($status === 'suspended' || $status === 'suspend')) {
|
||||||
return $this->suspend();
|
return $this->suspend();
|
||||||
} elseif ($status === 'stopped') {
|
} elseif ($status === 'stopped') {
|
||||||
return $this->stop();
|
return $this->stop();
|
||||||
@ -422,6 +311,11 @@ public function changeStatus(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function user(): BelongsToAlias
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function isPending(): bool
|
public function isPending(): bool
|
||||||
{
|
{
|
||||||
return $this->status === 'pending';
|
return $this->status === 'pending';
|
||||||
@ -432,6 +326,15 @@ public function isOverdue(): bool
|
|||||||
return now()->gt($this->next_due_at);
|
return now()->gt($this->next_due_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function run(): bool
|
||||||
|
{
|
||||||
|
$this->update([
|
||||||
|
'status' => 'running',
|
||||||
|
]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function suspend(): bool
|
public function suspend(): bool
|
||||||
{
|
{
|
||||||
$this->update([
|
$this->update([
|
||||||
|
@ -38,10 +38,6 @@ public function created(Host $host): void
|
|||||||
|
|
||||||
$host->user->notify(new WebNotification($host, 'hosts.created'));
|
$host->user->notify(new WebNotification($host, 'hosts.created'));
|
||||||
|
|
||||||
if ($host->isCycle()) {
|
|
||||||
$host->renew(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$host->save();
|
$host->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
return [
|
return [
|
||||||
'billing' => [
|
'billing' => [
|
||||||
'commission' => '0.1',
|
'commission' => '0.1',
|
||||||
'cycle_delete_times_every_month' => 2,
|
|
||||||
],
|
],
|
||||||
'wecom' => [
|
'wecom' => [
|
||||||
'robot_hook' => [
|
'robot_hook' => [
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<?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(): void
|
||||||
|
{
|
||||||
|
Schema::table('hosts', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('next_due_at');
|
||||||
|
$table->dropColumn('billing_cycle');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
echo 'This migration cannot be reversed.'.PHP_EOL;
|
||||||
|
}
|
||||||
|
};
|
@ -38,14 +38,6 @@
|
|||||||
{{ $host->price }} 元
|
{{ $host->price }} 元
|
||||||
@endif
|
@endif
|
||||||
<br/>
|
<br/>
|
||||||
@if($host->billing_cycle)
|
|
||||||
<x-billing-cycle :cycle="$host->billing_cycle"/>
|
|
||||||
到期时间:{{ $host->next_due_at }}
|
|
||||||
<br />
|
|
||||||
续费价格: {{ $host->getRenewPrice() }} 元
|
|
||||||
<br />
|
|
||||||
续费后到期时间: {{ $host->getNewDueDate() }}
|
|
||||||
@endif
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<x-host-status :status="$host->status"/>
|
<x-host-status :status="$host->status"/>
|
||||||
@ -65,19 +57,6 @@
|
|||||||
操作
|
操作
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
|
||||||
@if($host->billing_cycle)
|
|
||||||
<a class="dropdown-item" href="#"
|
|
||||||
onclick="return confirm('确定续费此主机?') ? document.getElementById('renew-{{$host->id}}').submit() : false;">
|
|
||||||
续费此主机
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<form action="{{ route('hosts.renew', $host) }}" id="renew-{{$host->id}}"
|
|
||||||
method="post" class="d-none">
|
|
||||||
@csrf
|
|
||||||
</form>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if(!$host->isRunning())
|
@if(!$host->isRunning())
|
||||||
<a class="dropdown-item" href="#"
|
<a class="dropdown-item" href="#"
|
||||||
onclick="return confirm('确定执行此操作?') ? document.getElementById('start-{{$host->id}}').submit() : false;">
|
onclick="return confirm('确定执行此操作?') ? document.getElementById('start-{{$host->id}}').submit() : false;">
|
||||||
@ -92,7 +71,7 @@
|
|||||||
</form>
|
</form>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if(!$host->isSuspended() && !$host->isCycle())
|
@if(!$host->isSuspended())
|
||||||
<a class="dropdown-item" href="#"
|
<a class="dropdown-item" href="#"
|
||||||
onclick="return confirm('确定执行此操作?') ? document.getElementById('start-{{$host->id}}').submit() : false;">
|
onclick="return confirm('确定执行此操作?') ? document.getElementById('start-{{$host->id}}').submit() : false;">
|
||||||
暂停此主机
|
暂停此主机
|
||||||
@ -128,9 +107,4 @@
|
|||||||
|
|
||||||
{{-- 分页 --}}
|
{{-- 分页 --}}
|
||||||
{{ $hosts->links() }}
|
{{ $hosts->links() }}
|
||||||
|
|
||||||
<br/>
|
|
||||||
<p>还剩下周期性计费删除次数: {{ $times }}。当次数用完后,您的周期性计费主机只能在到期后删除。</p>
|
|
||||||
<p>当您的主机处于 "暂停" 状态时,计费会被终止。但是请注意,3 天后主机将会被删除,请合理使用。</p>
|
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
Loading…
Reference in New Issue
Block a user