增加 周期扣费(月)
This commit is contained in:
parent
d1fad94644
commit
727c1bf07d
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Console;
|
namespace App\Console;
|
||||||
|
|
||||||
|
use App\Jobs\Host\CancelExpiredHostJob;
|
||||||
use App\Jobs\Host\DeleteHostJob;
|
use App\Jobs\Host\DeleteHostJob;
|
||||||
use App\Jobs\Host\DispatchHostCostQueueJob;
|
use App\Jobs\Host\DispatchHostCostQueueJob;
|
||||||
use App\Jobs\Host\ScanErrorHostsJob;
|
use App\Jobs\Host\ScanErrorHostsJob;
|
||||||
@ -30,7 +31,9 @@ protected function schedule(Schedule $schedule): void
|
|||||||
$schedule->command('sanctum:prune-expired --hours=24')->daily()->runInBackground()->onOneServer()->name('清理过期的 Token。');
|
$schedule->command('sanctum:prune-expired --hours=24')->daily()->runInBackground()->onOneServer()->name('清理过期的 Token。');
|
||||||
|
|
||||||
// 扣费
|
// 扣费
|
||||||
$schedule->job(new DispatchHostCostQueueJob(now()->minute))->everyMinute()->withoutOverlapping()->onOneServer()->name('部署扣费任务');
|
$schedule->job(new DispatchHostCostQueueJob(now(), null, 'hourly'))->everyMinute()->withoutOverlapping()->onOneServer()->name('部署扣费任务 (小时)');
|
||||||
|
$schedule->job(new DispatchHostCostQueueJob(now(), null, 'monthly'))->hourly()->withoutOverlapping()->onOneServer()->name('部署扣费任务 (月度)');
|
||||||
|
$schedule->job(new CancelExpiredHostJob())->hourly()->withoutOverlapping()->onOneServer()->name('部署清理到期主机任务');
|
||||||
|
|
||||||
// 获取模块暴露的信息(服务器等,检查模块状态)
|
// 获取模块暴露的信息(服务器等,检查模块状态)
|
||||||
$schedule->job(new DispatchFetchModuleJob())->withoutOverlapping()->everyMinute()->name('获取模块暴露的信息(服务器等,检查模块状态)');
|
$schedule->job(new DispatchFetchModuleJob())->withoutOverlapping()->everyMinute()->name('获取模块暴露的信息(服务器等,检查模块状态)');
|
||||||
|
@ -38,13 +38,23 @@ public function store(Request $request): Response|JsonResponse
|
|||||||
'status' => 'required|in:draft,running,stopped,error,suspended,pending',
|
'status' => 'required|in:draft,running,stopped,error,suspended,pending',
|
||||||
'price' => 'required|numeric',
|
'price' => 'required|numeric',
|
||||||
'user_id' => 'required|integer|exists:users,id',
|
'user_id' => 'required|integer|exists:users,id',
|
||||||
|
'managed_price' => 'nullable|numeric',
|
||||||
|
'billing_cycle' => 'nullable|in:hourly,monthly',
|
||||||
|
'trial_ends_at' => 'nullable|date|after:now',
|
||||||
|
'configuration' => 'nullable|array',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = (new User)->findOrFail($request->input('user_id'));
|
$user = (new User)->findOrFail($request->input('user_id'));
|
||||||
|
|
||||||
if ($request->input('price') > 0) {
|
if ($request->input('price') > 0) {
|
||||||
if ($user->balance < 1) {
|
if ($request->billing_cycle === 'hourly') {
|
||||||
return $this->error('此用户余额不足,无法开设计费项目。');
|
if (! $user->hasBalance(1)) {
|
||||||
|
return $this->error('此用户余额不足,无法开设计费项目。');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (! $user->hasBalance($request->input('managed_price', $request->input('price')))) {
|
||||||
|
return $this->error('此用户余额不足,无法开计月费项目。');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +68,8 @@ public function store(Request $request): Response|JsonResponse
|
|||||||
'price' => $request->input('price'),
|
'price' => $request->input('price'),
|
||||||
'managed_price' => $request->input('managed_price'),
|
'managed_price' => $request->input('managed_price'),
|
||||||
'status' => $request->input('status'),
|
'status' => $request->input('status'),
|
||||||
|
'billing_cycle' => $request->input('billing_cycle', 'hourly'),
|
||||||
|
'trial_ends_at' => $request->input('trial_ends_at'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$host = (new Host)->create($data);
|
$host = (new Host)->create($data);
|
||||||
@ -73,9 +85,6 @@ public function store(Request $request): Response|JsonResponse
|
|||||||
public function show(Host $host): JsonResponse
|
public function show(Host $host): JsonResponse
|
||||||
{
|
{
|
||||||
return $this->success($host);
|
return $this->success($host);
|
||||||
//
|
|
||||||
|
|
||||||
// dd($host->cost());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,10 +97,21 @@ public function update(Request $request, Host $host): JsonResponse
|
|||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'status' => 'sometimes|in:running,stopped,error,suspended,pending',
|
'status' => 'sometimes|in:running,stopped,error,suspended,pending',
|
||||||
|
'price' => 'sometimes|nullable|numeric',
|
||||||
'managed_price' => 'sometimes|numeric|nullable',
|
'managed_price' => 'sometimes|numeric|nullable',
|
||||||
|
'configuration' => 'nullable|array',
|
||||||
|
'trial_ends_at' => 'nullable|date|after:now',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$update = $request->except(['module_id', 'user_id']);
|
$update = $request->only([
|
||||||
|
'name',
|
||||||
|
'status',
|
||||||
|
'price',
|
||||||
|
'managed_price',
|
||||||
|
'billing_cycle',
|
||||||
|
'trial_ends_at',
|
||||||
|
'configuration',
|
||||||
|
]);
|
||||||
|
|
||||||
$host->update($update);
|
$host->update($update);
|
||||||
|
|
||||||
|
@ -23,12 +23,29 @@ public function index(): View
|
|||||||
public function update(Request $request, Host $host): RedirectResponse
|
public function update(Request $request, Host $host): RedirectResponse
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'status' => 'required|in:running,stopped,suspended',
|
'status' => 'nullable|in:running,stopped,suspended',
|
||||||
|
'cancel_at_period_end' => 'nullable|boolean',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$status = $host->changeStatus($request->input('status'));
|
if ($request->filled('status')) {
|
||||||
|
$status = $host->changeStatus($request->input('status'));
|
||||||
|
|
||||||
return $status ? back()->with('success', '修改成功。') : back()->with('error', '修改失败。');
|
if (! $status) {
|
||||||
|
return back()->with('error', '在修改主机状态时发生错误。');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->filled('cancel_at_period_end')) {
|
||||||
|
if ($host->isHourly()) {
|
||||||
|
return back()->with('error', '按小时计费的主机无法进行此操作。');
|
||||||
|
}
|
||||||
|
|
||||||
|
$host->update([
|
||||||
|
'cancel_at_period_end' => $request->boolean('cancel_at_period_end'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return back()->with('info', '更改已应用。');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Jobs\Host;
|
namespace App\Jobs\Host;
|
||||||
|
|
||||||
use App\Models\Host;
|
use App\Models\Host;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
@ -12,19 +13,22 @@ class DispatchHostCostQueueJob implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use InteractsWithQueue, Queueable, SerializesModels;
|
use InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected int $minute;
|
protected Carbon $now;
|
||||||
|
|
||||||
protected ?Host $host;
|
protected ?Host $host;
|
||||||
|
|
||||||
|
protected string $type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct($minute, Host $host = null)
|
public function __construct(Carbon $now, Host $host = null, $type = 'hourly')
|
||||||
{
|
{
|
||||||
$this->minute = $minute;
|
$this->now = $now;
|
||||||
$this->host = $host;
|
$this->host = $host;
|
||||||
|
$this->type = $type;
|
||||||
|
|
||||||
$this->onQueue('host-cost');
|
$this->onQueue('host-cost');
|
||||||
}
|
}
|
||||||
@ -37,18 +41,28 @@ public function handle(): void
|
|||||||
if (! $this->host) {
|
if (! $this->host) {
|
||||||
$host = new Host();
|
$host = new Host();
|
||||||
|
|
||||||
if (app()->environment() != 'local') {
|
if ($this->type == 'monthly') {
|
||||||
|
// 月度计费,需要精确到天和小时
|
||||||
|
$host = $host->where('day_at', $this->now->day);
|
||||||
|
$host = $host->where('hour_at', $this->now->hour);
|
||||||
|
$host = $host->where('cancel_at_period_end', false);
|
||||||
|
} elseif (app()->environment() != 'local') {
|
||||||
$host = $host->where('minute_at', $this->minute);
|
$host = $host->where('minute_at', $this->minute);
|
||||||
}
|
}
|
||||||
$host->whereIn('status', ['running', 'stopped'])->with(['user', 'module'])->chunk(500, function ($hosts) {
|
|
||||||
|
$host->where('billing_cycle', $this->type)->whereIn('status', ['running', 'stopped'])->with(['user', 'module'])->chunk(500, function ($hosts) {
|
||||||
$hosts->each(function ($host) {
|
$hosts->each(function ($host) {
|
||||||
|
/* @var Host $host */
|
||||||
|
|
||||||
if ($host->module->isUp()) {
|
if ($host->module->isUp()) {
|
||||||
dispatch(new self($this->minute, $host));
|
dispatch(new self($this->now, $host, $this->type));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$this->host->cost($this->host->getPrice());
|
if (! $this->host->isHourly() && ! $this->host->isNextMonthCancel() && ! $this->host->isTrial()) {
|
||||||
|
$this->host->cost($this->host->getPrice());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
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;
|
||||||
@ -25,13 +26,23 @@ class Host extends Model
|
|||||||
'configuration',
|
'configuration',
|
||||||
'status',
|
'status',
|
||||||
'suspended_at',
|
'suspended_at',
|
||||||
|
'trial_ends_at',
|
||||||
|
'billing_cycle',
|
||||||
|
'cancel_at_period_end',
|
||||||
|
'last_paid',
|
||||||
|
'last_paid_at',
|
||||||
|
'expired_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'price' => 'decimal:2',
|
'price' => 'decimal:2',
|
||||||
|
'last_paid' => 'decimal:2',
|
||||||
'managed_price' => 'decimal:2',
|
'managed_price' => 'decimal:2',
|
||||||
'configuration' => 'array',
|
'configuration' => 'array',
|
||||||
'suspended_at' => 'datetime',
|
'suspended_at' => 'datetime',
|
||||||
|
'last_paid_at' => 'datetime',
|
||||||
|
'trial_ends_at' => 'datetime',
|
||||||
|
'expired_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
/** @noinspection PhpUndefinedMethodInspection */
|
||||||
@ -111,13 +122,41 @@ public function isSuspended(): bool
|
|||||||
return $this->status === 'suspended';
|
return $this->status === 'suspended';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isExpired(): bool
|
||||||
|
{
|
||||||
|
return $this->expired_at && $this->expired_at->isPast();
|
||||||
|
}
|
||||||
|
|
||||||
public function safeDelete(): bool
|
public function safeDelete(): bool
|
||||||
{
|
{
|
||||||
// 如果创建时间大于 1 小时
|
if ($this->isHourly()) {
|
||||||
if ($this->created_at->diffInHours(now()) > 1) {
|
// 如果创建时间大于 1 小时
|
||||||
// 如果当前时间比扣费时间小,则说明没有扣费。执行扣费。
|
if ($this->created_at->diffInHours(now()) > 1) {
|
||||||
if (now()->minute < $this->minute_at) {
|
// 如果当前时间比扣费时间小,则说明没有扣费。执行扣费。
|
||||||
$this->cost();
|
if (now()->minute < $this->minute_at) {
|
||||||
|
$this->cost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($this->isMonthly() && $this->last_paid && ! $this->isExpired()) {
|
||||||
|
// 根据扣费时间,计算出退款金额
|
||||||
|
$refund = $this->getRefundAmount();
|
||||||
|
|
||||||
|
if ($refund) {
|
||||||
|
// 如果有退款金额,则退款
|
||||||
|
$this->module?->reduce($refund, 'module_balance', '主机 '.$this->name.' 退款。', [
|
||||||
|
'host_id' => $this->id,
|
||||||
|
'module_id' => $this->module_id,
|
||||||
|
]);
|
||||||
|
$this->user->charge($refund, 'balance', '主机 '.$this->name.' 退款。', [
|
||||||
|
'host_id' => $this->id,
|
||||||
|
'module_id' => $this->module_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 退款后,更新扣费时间
|
||||||
|
$this->update([
|
||||||
|
'last_paid_at' => null,
|
||||||
|
'last_paid' => 0,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,9 +165,52 @@ public function safeDelete(): bool
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRefundAmount(): string|null
|
||||||
|
{
|
||||||
|
if (! $this->last_paid_at) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是月付,则按比例
|
||||||
|
$days = $this->last_paid_at->daysInMonth;
|
||||||
|
|
||||||
|
// 本月已经过的天数
|
||||||
|
$passed_days = $this->last_paid_at->day;
|
||||||
|
|
||||||
|
// 本月还剩下的天数
|
||||||
|
$left_days = $days - $passed_days;
|
||||||
|
|
||||||
|
// 计算
|
||||||
|
return bcmul($this->last_paid, bcdiv($left_days, $days, 2), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isTrial(): bool
|
||||||
|
{
|
||||||
|
return $this->trial_ends_at !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isMonthly(): bool
|
||||||
|
{
|
||||||
|
return $this->billing_cycle === 'monthly';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isHourly(): bool
|
||||||
|
{
|
||||||
|
return $this->billing_cycle === 'hourly';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isNextMonthCancel(): bool
|
||||||
|
{
|
||||||
|
return $this->cancel_at_period_end;
|
||||||
|
}
|
||||||
|
|
||||||
public function cost(
|
public function cost(
|
||||||
string $amount = null, $auto = true, $description = null
|
string $amount = null, $auto = true, $description = null
|
||||||
): bool {
|
): bool {
|
||||||
|
if ($this->isTrial() && ! $this->trial_ends_at->isPast()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$this->load('user');
|
$this->load('user');
|
||||||
$user = $this->user;
|
$user = $this->user;
|
||||||
$user->load('user_group');
|
$user->load('user_group');
|
||||||
@ -157,7 +239,7 @@ public function cost(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($auto) {
|
if ($auto && $this->isHourly()) {
|
||||||
// 获取本月天数
|
// 获取本月天数
|
||||||
$days = now()->daysInMonth;
|
$days = now()->daysInMonth;
|
||||||
// 本月每天的每小时的价格
|
// 本月每天的每小时的价格
|
||||||
@ -195,12 +277,19 @@ public function cost(
|
|||||||
|
|
||||||
Cache::put($month_cache_key, $hosts_balances, 604800);
|
Cache::put($month_cache_key, $hosts_balances, 604800);
|
||||||
|
|
||||||
if (! $description) {
|
$description = '主机: '.$this->name.', '.$description;
|
||||||
$description = '模块发起的扣费。';
|
|
||||||
|
if ($auto && $this->isHourly()) {
|
||||||
|
$description .= '小时计费。';
|
||||||
|
} elseif ($auto && $this->isMonthly()) {
|
||||||
|
$description .= '月度计费。';
|
||||||
|
} else {
|
||||||
|
$description .= '扣费。';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($auto) {
|
if ($this->isTrial() && $this->trial_ends_at->isPast()) {
|
||||||
$description = '自动扣费。';
|
$description .= '试用已过期。';
|
||||||
|
$this->trial_ends_at = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($append_description) {
|
if ($append_description) {
|
||||||
@ -212,17 +301,29 @@ public function cost(
|
|||||||
'module_id' => $this->module_id,
|
'module_id' => $this->module_id,
|
||||||
];
|
];
|
||||||
|
|
||||||
$left = $user->reduce($real_price, $description, false, $data)->user_remain;
|
$this->last_paid = $real_price;
|
||||||
|
$this->last_paid_at = now();
|
||||||
|
|
||||||
|
if ($this->isMonthly()) {
|
||||||
|
$this->expired_at = now()->addMonth();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$left = $user->reduce($real_price, $description, ! $this->isHourly(), $data)->user_remain;
|
||||||
|
} catch (BalanceNotEnoughException) {
|
||||||
|
$this->changeStatus('suspended');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$this->addLog($real_price);
|
$this->addLog($real_price);
|
||||||
|
|
||||||
if ($left < 0) {
|
if ($left < 0) {
|
||||||
$this->changeStatus('suspended');
|
$this->changeStatus('suspended');
|
||||||
|
|
||||||
$this->last_paid = $real_price;
|
|
||||||
$this->save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +392,11 @@ public function changeStatus(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $user->hasBalance('0.5')) {
|
if ($this->isMonthly()) {
|
||||||
|
if (! $user->hasBalance($this->price)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} elseif (! $user->hasBalance('0.5')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
use App\Exceptions\User\BalanceNotEnoughException;
|
use App\Exceptions\User\BalanceNotEnoughException;
|
||||||
use App\Models\Affiliate\Affiliates;
|
use App\Models\Affiliate\Affiliates;
|
||||||
use App\Models\Affiliate\AffiliateUser;
|
use App\Models\Affiliate\AffiliateUser;
|
||||||
|
use App\Notifications\User\BalanceNotEnough;
|
||||||
|
use App\Notifications\User\LowBalance;
|
||||||
use GeneaLabs\LaravelModelCaching\CachedBuilder;
|
use GeneaLabs\LaravelModelCaching\CachedBuilder;
|
||||||
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
|
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
|
||||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
@ -208,6 +210,9 @@ public function reduce(string|null $amount = '0', string $description = '消费'
|
|||||||
|
|
||||||
if ($this->balance < $amount) {
|
if ($this->balance < $amount) {
|
||||||
if ($fail) {
|
if ($fail) {
|
||||||
|
// 发送邮件通知
|
||||||
|
$this->notify(new BalanceNotEnough());
|
||||||
|
|
||||||
throw new BalanceNotEnoughException();
|
throw new BalanceNotEnoughException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,6 +234,12 @@ public function reduce(string|null $amount = '0', string $description = '消费'
|
|||||||
|
|
||||||
broadcast(new Users($this, 'balances.amount.reduced', $this));
|
broadcast(new Users($this, 'balances.amount.reduced', $this));
|
||||||
|
|
||||||
|
// 如果用户的余额小于 5 元,则发送邮件提醒(一天只发送一次,使用缓存)
|
||||||
|
if (! $this->hasBalance(5) && ! Cache::has('user_balance_less_than_5_'.$this->id)) {
|
||||||
|
$this->notify(new LowBalance());
|
||||||
|
Cache::put('user_balance_less_than_5_'.$this->id, true, now()->addDay());
|
||||||
|
}
|
||||||
|
|
||||||
return (new Transaction)->create($data);
|
return (new Transaction)->create($data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
45
app/Notifications/User/LowBalance.php
Normal file
45
app/Notifications/User/LowBalance.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications\User;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Notifications\Channels\WebChannel;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class LowBalance extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification's delivery channels.
|
||||||
|
*/
|
||||||
|
public function via(): array
|
||||||
|
{
|
||||||
|
return [WebChannel::class, 'mail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the mail representation of the notification.
|
||||||
|
*/
|
||||||
|
public function toMail(User $user): MailMessage
|
||||||
|
{
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject('账户余额过低')
|
||||||
|
->line('您的账户余额过低。还剩下 '.$user->balance.' 元。')
|
||||||
|
->action('充值', route('balances.index'))
|
||||||
|
->line('当您的账户欠费时,您的服务将会被暂停。');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the array representation of the notification.
|
||||||
|
*/
|
||||||
|
public function toArray(User $user): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'title' => '账户余额过低',
|
||||||
|
'message' => '您的账户余额过低。还剩下'.$user->balance.'元。当您的账户欠费时,您的服务将会被暂停。',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ public function creating(Host $host): void
|
|||||||
{
|
{
|
||||||
$host->hour_at = now()->hour;
|
$host->hour_at = now()->hour;
|
||||||
$host->minute_at = now()->minute;
|
$host->minute_at = now()->minute;
|
||||||
|
$host->day_at = now()->day;
|
||||||
|
|
||||||
if ($host->price !== null) {
|
if ($host->price !== null) {
|
||||||
$host->price = bcdiv($host->price, 1, 2);
|
$host->price = bcdiv($host->price, 1, 2);
|
||||||
@ -22,6 +23,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) {
|
||||||
|
$host->billing_cycle = 'hourly';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,6 +42,10 @@ public function created(Host $host): void
|
|||||||
$host->user->notify(new WebNotification($host, 'hosts.created'));
|
$host->user->notify(new WebNotification($host, 'hosts.created'));
|
||||||
|
|
||||||
$host->save();
|
$host->save();
|
||||||
|
|
||||||
|
if ($host->isMonthly()) {
|
||||||
|
$host->cost(null, true, '预');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,15 +38,29 @@
|
|||||||
{{ $host->price }} 元
|
{{ $host->price }} 元
|
||||||
@endif
|
@endif
|
||||||
<br/>
|
<br/>
|
||||||
|
@if ($host->isMonthly())
|
||||||
|
<span class="text-muted">月付</span>
|
||||||
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<x-host-status :status="$host->status"/>
|
<x-host-status :status="$host->status"/>
|
||||||
|
@if ($host->isNextMonthCancel())
|
||||||
|
<br/>
|
||||||
|
<small>
|
||||||
|
<span class="text-danger">不自动续费</span>
|
||||||
|
</small>
|
||||||
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="small">
|
<span class="small">
|
||||||
{{ $host->updated_at }}
|
{{ $host->updated_at }}
|
||||||
<br/>
|
<br/>
|
||||||
{{ $host->created_at }}
|
{{ $host->created_at }}
|
||||||
|
|
||||||
|
@if ($host->isTrial())
|
||||||
|
<br/>
|
||||||
|
<span class="text-danger">试用到 {{ $host->trial_ends_at }}</span>
|
||||||
|
@endif
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@ -85,6 +99,22 @@
|
|||||||
</form>
|
</form>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@if (!$host->isHourly())
|
||||||
|
<a class="dropdown-item" href="#"
|
||||||
|
onclick="document.getElementById('update-{{$host->id}}').submit()">
|
||||||
|
{{ $host->isNextMonthCancel() ? '启用自动续订' : '取消自动续订'}}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<form action="{{ route('hosts.update', $host) }}"
|
||||||
|
id="update-{{$host->id}}"
|
||||||
|
method="post" class="d-none">
|
||||||
|
@csrf
|
||||||
|
@method('PATCH')
|
||||||
|
<input type="hidden" name="cancel_at_period_end"
|
||||||
|
value="{{ !$host->isNextMonthCancel() ? '1' : '0'}}">
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
|
||||||
<a class="dropdown-item" href="#"
|
<a class="dropdown-item" href="#"
|
||||||
onclick="return confirm('删除操作将不可恢复,确定吗?') ? document.getElementById('delete-{{$host->id}}').submit() : false;">
|
onclick="return confirm('删除操作将不可恢复,确定吗?') ? document.getElementById('delete-{{$host->id}}').submit() : false;">
|
||||||
删除
|
删除
|
||||||
|
Loading…
Reference in New Issue
Block a user