diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index d47f5d6..bcc53a9 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -5,7 +5,6 @@ use App\Jobs\Host\DeleteHostJob; use App\Jobs\Host\DispatchHostCostQueueJob; use App\Jobs\Host\ScanAllHostsJob; -use App\Jobs\Host\SuspendOverdueHosts; use App\Jobs\Module\DispatchFetchModuleJob; use App\Jobs\Module\SendModuleEarningsJob; 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 SuspendOverdueHosts())->dailyAt('00:00')->onOneServer()->name('暂停到期的循环计费主机'); } /** diff --git a/app/Http/Controllers/Module/HostController.php b/app/Http/Controllers/Module/HostController.php index c33cb23..3e587c0 100644 --- a/app/Http/Controllers/Module/HostController.php +++ b/app/Http/Controllers/Module/HostController.php @@ -66,12 +66,6 @@ public function store(Request $request): Response|JsonResponse $host = (new Host)->create($data); - if (! $user->hasBalance($host->getRenewPrice())) { - $host->delete(); - - return $this->error('此用户余额不足,无法开计费项目。'); - } - $host['host_id'] = $host->id; return $this->created($host); @@ -127,27 +121,6 @@ public function destroy($host): JsonResponse $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(); return $this->deleted(); diff --git a/app/Http/Controllers/Web/AuthController.php b/app/Http/Controllers/Web/AuthController.php index 9a25ba6..193a722 100644 --- a/app/Http/Controllers/Web/AuthController.php +++ b/app/Http/Controllers/Web/AuthController.php @@ -6,14 +6,14 @@ use App\Http\Controllers\Controller; use App\Notifications\User\UserNotification; +use function back; +use function config; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; use Illuminate\View\View; -use function back; -use function config; use function redirect; use function session; 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); if ($callbackHost === $dashboardHost) { - if (!$request->user('web')->isRealNamed()) { + if (! $request->user('web')->isRealNamed()) { return redirect()->route('real_name.create')->with('status', '重定向已被打断,需要先实人认证。'); } $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)]); @@ -121,7 +121,7 @@ public function exitSudo(): RedirectResponse public function showAuthRequest($token): View|RedirectResponse { - $data = Cache::get('auth_request:' . $token); + $data = Cache::get('auth_request:'.$token); if (empty($data)) { return redirect()->route('index')->with('error', '登录请求的 Token 不存在或已过期。'); @@ -145,7 +145,7 @@ public function storeAuthRequest(Request $request): RedirectResponse 'token' => 'required|string|max:128', ]); - $data = Cache::get('auth_request:' . $request->input('token')); + $data = Cache::get('auth_request:'.$request->input('token')); if (empty($data)) { 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; } - Cache::put('auth_request:' . $request->input('token'), $data, 60); + Cache::put('auth_request:'.$request->input('token'), $data, 60); return redirect()->route('index')->with('success', '登录请求已确认。'); } diff --git a/app/Http/Controllers/Web/HostController.php b/app/Http/Controllers/Web/HostController.php index 6b85e9d..3f21c20 100644 --- a/app/Http/Controllers/Web/HostController.php +++ b/app/Http/Controllers/Web/HostController.php @@ -6,7 +6,6 @@ use App\Models\Host; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Cache; use Illuminate\View\View; class HostController extends Controller @@ -19,9 +18,8 @@ class HostController extends Controller public function index(): View { $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 @@ -35,25 +33,6 @@ public function update(Request $request, Host $host): RedirectResponse 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. * diff --git a/app/Jobs/Host/DispatchHostCostQueueJob.php b/app/Jobs/Host/DispatchHostCostQueueJob.php index 5c2165a..04bfc63 100644 --- a/app/Jobs/Host/DispatchHostCostQueueJob.php +++ b/app/Jobs/Host/DispatchHostCostQueueJob.php @@ -43,7 +43,7 @@ public function handle(): void $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) { dispatch(new self($this->minute, $host)); } diff --git a/app/Jobs/Host/SuspendOverdueHosts.php b/app/Jobs/Host/SuspendOverdueHosts.php deleted file mode 100644 index 808dd41..0000000 --- a/app/Jobs/Host/SuspendOverdueHosts.php +++ /dev/null @@ -1,47 +0,0 @@ -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(); - } -} diff --git a/app/Models/Host.php b/app/Models/Host.php index c8c80b7..5670b25 100644 --- a/app/Models/Host.php +++ b/app/Models/Host.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Exceptions\User\BalanceNotEnoughException; use App\Jobs\Host\HostJob; use App\Jobs\Host\UpdateOrDeleteHostJob; use GeneaLabs\LaravelModelCaching\Traits\Cachable; @@ -47,26 +46,21 @@ public function getUserHosts($user_id = null): array|Collection })->get(); } - public function user(): BelongsToAlias - { - return $this->belongsTo(User::class); - } - public function module(): BelongsToAlias { return $this->belongsTo(Module::class); } + public function scopeActive($query) + { + return $query->whereIn('status', ['running', 'stopped']); + } + // public function workOrders(): HasManyAlias // { // return $this->hasMany(WorkOrder::class); // } - public function scopeActive($query) - { - return $query->whereIn('status', ['running', 'stopped']); - } - public function scopeDraft($query) { 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 { return $this->status === 'draft'; @@ -116,175 +105,20 @@ public function isUnavailable(): bool 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 { 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 { return $this->status === 'suspended'; } - public function run(): bool - { - $this->update([ - 'status' => 'running', - ]); - - return true; - } - 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 小时 - if (! $this->isCycle() && $this->created_at->diffInHours(now()) > 1) { + if ($this->created_at->diffInHours(now()) > 1) { // 如果当前时间比扣费时间小,则说明没有扣费。执行扣费。 if (now()->minute < $this->minute_at) { $this->cost(); @@ -396,6 +230,61 @@ public function cost( 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( string $status ): bool { @@ -406,14 +295,14 @@ public function changeStatus( return false; } - if (! $this->isCycle() && ! $user->hasBalance('0.5')) { + if (! $user->hasBalance('0.5')) { return false; } } if ($status === 'running') { return $this->run(); - } elseif (($status === 'suspended' || $status === 'suspend') && ! $this->isCycle()) { + } elseif (($status === 'suspended' || $status === 'suspend')) { return $this->suspend(); } elseif ($status === 'stopped') { return $this->stop(); @@ -422,6 +311,11 @@ public function changeStatus( return false; } + public function user(): BelongsToAlias + { + return $this->belongsTo(User::class); + } + public function isPending(): bool { return $this->status === 'pending'; @@ -432,6 +326,15 @@ public function isOverdue(): bool return now()->gt($this->next_due_at); } + public function run(): bool + { + $this->update([ + 'status' => 'running', + ]); + + return true; + } + public function suspend(): bool { $this->update([ diff --git a/app/Observers/HostObserver.php b/app/Observers/HostObserver.php index 09516b4..2305181 100644 --- a/app/Observers/HostObserver.php +++ b/app/Observers/HostObserver.php @@ -38,10 +38,6 @@ public function created(Host $host): void $host->user->notify(new WebNotification($host, 'hosts.created')); - if ($host->isCycle()) { - $host->renew(true); - } - $host->save(); } diff --git a/config/settings.php b/config/settings.php index 98743fa..1611d3c 100644 --- a/config/settings.php +++ b/config/settings.php @@ -3,7 +3,6 @@ return [ 'billing' => [ 'commission' => '0.1', - 'cycle_delete_times_every_month' => 2, ], 'wecom' => [ 'robot_hook' => [ diff --git a/database/migrations/2023_02_19_235923_remove_cycle_billing_to_hosts.php b/database/migrations/2023_02_19_235923_remove_cycle_billing_to_hosts.php new file mode 100644 index 0000000..d8cadda --- /dev/null +++ b/database/migrations/2023_02_19_235923_remove_cycle_billing_to_hosts.php @@ -0,0 +1,31 @@ +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; + } +}; diff --git a/resources/views/hosts/index.blade.php b/resources/views/hosts/index.blade.php index 9d4fa66..c472364 100644 --- a/resources/views/hosts/index.blade.php +++ b/resources/views/hosts/index.blade.php @@ -38,14 +38,6 @@ {{ $host->price }} 元 @endif
- @if($host->billing_cycle) - - 到期时间:{{ $host->next_due_at }} -
- 续费价格: {{ $host->getRenewPrice() }} 元 -
- 续费后到期时间: {{ $host->getNewDueDate() }} - @endif @@ -65,19 +57,6 @@ 操作