增加 主机计费周期
This commit is contained in:
parent
3abbc86951
commit
1424375a4b
@ -37,7 +37,8 @@ public function store(Request $request): Response|JsonResponse
|
|||||||
{
|
{
|
||||||
// 存储计费项目
|
// 存储计费项目
|
||||||
$this->validate($request, [
|
$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',
|
'price' => 'required|numeric',
|
||||||
'user_id' => 'required|integer|exists:users,id',
|
'user_id' => 'required|integer|exists:users,id',
|
||||||
]);
|
]);
|
||||||
@ -55,11 +56,12 @@ public function store(Request $request): Response|JsonResponse
|
|||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'status' => $request->input('status'),
|
|
||||||
'price' => $request->input('price'),
|
|
||||||
'managed_price' => $request->input('managed_price'),
|
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'module_id' => auth('module')->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);
|
$host = (new Host)->create($data);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Events\Users;
|
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;
|
||||||
@ -22,17 +22,20 @@ class Host extends Model
|
|||||||
'module_id',
|
'module_id',
|
||||||
'user_id',
|
'user_id',
|
||||||
'price',
|
'price',
|
||||||
|
'managed_price',
|
||||||
'configuration',
|
'configuration',
|
||||||
'status',
|
'status',
|
||||||
'managed_price',
|
'billing_cycle',
|
||||||
|
'next_due_at',
|
||||||
'suspended_at',
|
'suspended_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
// 'configuration' => 'array',
|
|
||||||
'suspended_at' => 'datetime',
|
|
||||||
'price' => 'decimal:2',
|
'price' => 'decimal:2',
|
||||||
'managed_price' => 'decimal:2',
|
'managed_price' => 'decimal:2',
|
||||||
|
'configuration' => 'array',
|
||||||
|
'next_due_at' => 'datetime',
|
||||||
|
'suspended_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
/** @noinspection PhpUndefinedMethodInspection */
|
||||||
@ -68,6 +71,21 @@ public function scopeActive($query)
|
|||||||
return $query->whereIn('status', ['running', 'stopped']);
|
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)
|
public function scopeThisUser($query, $module = null)
|
||||||
{
|
{
|
||||||
if ($module) {
|
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
|
public function safeDelete(): bool
|
||||||
{
|
{
|
||||||
// 如果创建时间大于大于 1 小时
|
// 如果创建时间大于大于 1 小时
|
||||||
@ -116,7 +216,7 @@ public function cost(string $amount = null, $auto = true, $description = null):
|
|||||||
$append_description = '';
|
$append_description = '';
|
||||||
if ($user_group) {
|
if ($user_group) {
|
||||||
if ($user_group->discount !== 100 && $user_group->discount !== null) {
|
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.'%)';
|
$append_description = ' (折扣 '.$user_group->discount.'%)';
|
||||||
}
|
}
|
||||||
@ -181,8 +281,6 @@ public function cost(string $amount = null, $auto = true, $description = null):
|
|||||||
|
|
||||||
$this->addLog($real_price);
|
$this->addLog($real_price);
|
||||||
|
|
||||||
broadcast(new Users($this->user, 'balances.amount.reduced', $this->user));
|
|
||||||
|
|
||||||
if ($left < 0) {
|
if ($left < 0) {
|
||||||
$this->update([
|
$this->update([
|
||||||
'status' => 'suspended',
|
'status' => 'suspended',
|
||||||
|
@ -21,6 +21,10 @@ public function creating(Host $host): void
|
|||||||
if ($host->managed_price !== null) {
|
if ($host->managed_price !== null) {
|
||||||
$host->managed_price = bcdiv($host->managed_price, 1, 2);
|
$host->managed_price = bcdiv($host->managed_price, 1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($host->billing_cycle !== null) {
|
||||||
|
$host->next_due_at = $host->getNewDueDate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
30
app/View/Components/BillingCycle.php
Normal file
30
app/View/Components/BillingCycle.php
Normal 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
13
lang/zh_CN/hosts.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'monthly' => '每月',
|
||||||
|
'quarterly' => '每季度',
|
||||||
|
'semi-annually' => '每半年',
|
||||||
|
'annually' => '每年',
|
||||||
|
'biennially' => '每两年',
|
||||||
|
'triennially' => '每三年',
|
||||||
|
'dynamic' => '动态',
|
||||||
|
];
|
3
resources/views/components/billing-cycle.blade.php
Normal file
3
resources/views/components/billing-cycle.blade.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div>
|
||||||
|
<!-- Simplicity is the consequence of refined emotions. - Jean D'Alembert -->
|
||||||
|
</div>
|
Loading…
Reference in New Issue
Block a user