增加 主机计费周期

This commit is contained in:
iVampireSP.com 2023-02-13 02:22:12 +08:00
parent 3abbc86951
commit 1424375a4b
No known key found for this signature in database
GPG Key ID: 2F7B001CA27A8132
6 changed files with 161 additions and 11 deletions

View File

@ -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);

View File

@ -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',

View File

@ -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();
}
}
/**

View File

@ -0,0 +1,30 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class BillingCycle extends Component
{
public string $cycle = 'dynamic';
/**
* Create a new component instance.
*
* @return void
*/
public function __construct(null|string $cycle = 'dynamic')
{
$this->cycle = $cycle ?? 'dynamic';
}
/**
* Get the view / contents that represent the component.
*
* @return string
*/
public function render(): string
{
return trans('hosts.' . $this->cycle);
}
}

13
lang/zh_CN/hosts.php Normal file
View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
return [
'monthly' => '每月',
'quarterly' => '每季度',
'semi-annually' => '每半年',
'annually' => '每年',
'biennially' => '每两年',
'triennially' => '每三年',
'dynamic' => '动态',
];

View File

@ -0,0 +1,3 @@
<div>
<!-- Simplicity is the consequence of refined emotions. - Jean D'Alembert -->
</div>