diff --git a/app/Http/Controllers/Module/HostController.php b/app/Http/Controllers/Module/HostController.php index fcf422d..3e587c0 100644 --- a/app/Http/Controllers/Module/HostController.php +++ b/app/Http/Controllers/Module/HostController.php @@ -37,7 +37,8 @@ public function store(Request $request): Response|JsonResponse { // 存储计费项目 $this->validate($request, [ - 'status' => 'required|in:running,stopped,error,suspended,pending', + 'status' => 'required|in:draft,running,stopped,error,suspended,pending', + 'billing_cycle' => 'nullable|in:monthly,quarterly,semi-annually,annually,biennially,triennially', 'price' => 'required|numeric', 'user_id' => 'required|integer|exists:users,id', ]); @@ -55,11 +56,12 @@ public function store(Request $request): Response|JsonResponse $data = [ 'name' => $name, - 'status' => $request->input('status'), - 'price' => $request->input('price'), - 'managed_price' => $request->input('managed_price'), 'user_id' => $user->id, 'module_id' => auth('module')->id(), + 'price' => $request->input('price'), + 'managed_price' => $request->input('managed_price'), + 'billing_cycle' => $request->input('billing_cycle'), + 'status' => $request->input('status'), ]; $host = (new Host)->create($data); diff --git a/app/Models/Host.php b/app/Models/Host.php index caf021d..7ce5add 100644 --- a/app/Models/Host.php +++ b/app/Models/Host.php @@ -2,7 +2,7 @@ namespace App\Models; -use App\Events\Users; +use App\Exceptions\User\BalanceNotEnoughException; use App\Jobs\Host\HostJob; use App\Jobs\Host\UpdateOrDeleteHostJob; use GeneaLabs\LaravelModelCaching\Traits\Cachable; @@ -22,17 +22,20 @@ class Host extends Model 'module_id', 'user_id', 'price', + 'managed_price', 'configuration', 'status', - 'managed_price', + 'billing_cycle', + 'next_due_at', 'suspended_at', ]; protected $casts = [ - // 'configuration' => 'array', - 'suspended_at' => 'datetime', 'price' => 'decimal:2', 'managed_price' => 'decimal:2', + 'configuration' => 'array', + 'next_due_at' => 'datetime', + 'suspended_at' => 'datetime', ]; /** @noinspection PhpUndefinedMethodInspection */ @@ -68,6 +71,21 @@ public function scopeActive($query) return $query->whereIn('status', ['running', 'stopped']); } + public function scopeDraft($query) + { + return $query->where('status', 'draft'); + } + + public function scopeExpiring($query) + { + return $query->where('status', 'running')->where('next_due_at', '<=', now()->addDays(7)); + } + + public function scopeSuspended($query) + { + return $query->where('status', 'suspended'); + } + public function scopeThisUser($query, $module = null) { if ($module) { @@ -77,6 +95,88 @@ public function scopeThisUser($query, $module = null) } } + public function isDraft(): bool + { + return $this->status === 'draft'; + } + + public function isRunning(): bool + { + return $this->status === 'running'; + } + + public function isStopped(): bool + { + return $this->status === 'stopped'; + } + + public function isSuspended(): bool + { + return $this->status === 'suspended'; + } + + 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 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 renew(): bool + { + $price = $this->getRenewPrice(); + + $description = '续费 '.$this->name.' 周期: '.$this->billing_cycle; + + try { + $this->user->reduce($price, $description, true, [ + 'host_id' => $this->id, + 'module_id' => $this->module_id, + ]); + } catch (BalanceNotEnoughException) { + return false; + } + + $this->addLog($price); + + $this->next_due_at = $this->getNewDueDate(); + + $this->save(); + + return true; + } + + public function isOverdue(): bool + { + return now()->gt($this->next_due_at); + } + + public function isCycle(): bool + { + return $this->billing_cycle !== null; + } + public function safeDelete(): bool { // 如果创建时间大于大于 1 小时 @@ -116,7 +216,7 @@ public function cost(string $amount = null, $auto = true, $description = null): $append_description = ''; if ($user_group) { if ($user_group->discount !== 100 && $user_group->discount !== null) { - $real_price = bcmul($real_price, bcdiv($user_group->discount, '100', 4), 4); + $real_price = $user_group->getCostPrice($real_price); $append_description = ' (折扣 '.$user_group->discount.'%)'; } @@ -181,8 +281,6 @@ public function cost(string $amount = null, $auto = true, $description = null): $this->addLog($real_price); - broadcast(new Users($this->user, 'balances.amount.reduced', $this->user)); - if ($left < 0) { $this->update([ 'status' => 'suspended', diff --git a/app/Observers/HostObserver.php b/app/Observers/HostObserver.php index 4604b2c..173d5aa 100644 --- a/app/Observers/HostObserver.php +++ b/app/Observers/HostObserver.php @@ -21,6 +21,10 @@ public function creating(Host $host): void if ($host->managed_price !== null) { $host->managed_price = bcdiv($host->managed_price, 1, 2); } + + if ($host->billing_cycle !== null) { + $host->next_due_at = $host->getNewDueDate(); + } } /** diff --git a/app/View/Components/BillingCycle.php b/app/View/Components/BillingCycle.php new file mode 100644 index 0000000..1a566d7 --- /dev/null +++ b/app/View/Components/BillingCycle.php @@ -0,0 +1,30 @@ +cycle = $cycle ?? 'dynamic'; + } + + /** + * Get the view / contents that represent the component. + * + * @return string + */ + public function render(): string + { + return trans('hosts.' . $this->cycle); + } +} diff --git a/lang/zh_CN/hosts.php b/lang/zh_CN/hosts.php new file mode 100644 index 0000000..51b30d5 --- /dev/null +++ b/lang/zh_CN/hosts.php @@ -0,0 +1,13 @@ + '每月', + 'quarterly' => '每季度', + 'semi-annually' => '每半年', + 'annually' => '每年', + 'biennially' => '每两年', + 'triennially' => '每三年', + 'dynamic' => '动态', +]; diff --git a/resources/views/components/billing-cycle.blade.php b/resources/views/components/billing-cycle.blade.php new file mode 100644 index 0000000..dbb0775 --- /dev/null +++ b/resources/views/components/billing-cycle.blade.php @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file