Xboard/app/Models/Plan.php

387 lines
10 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use InvalidArgumentException;
use Carbon\Carbon;
class Plan extends Model
{
use HasFactory;
protected $table = 'v2_plan';
protected $dateFormat = 'U';
// 定义流量重置方式
public const RESET_TRAFFIC_FOLLOW_SYSTEM = 0; // 跟随系统设置
public const RESET_TRAFFIC_FIRST_DAY_MONTH = 1; // 每月1号
public const RESET_TRAFFIC_MONTHLY = 2; // 按月重置
public const RESET_TRAFFIC_NEVER = 3; // 不重置
public const RESET_TRAFFIC_FIRST_DAY_YEAR = 4; // 每年1月1日
public const RESET_TRAFFIC_YEARLY = 5; // 按年重置
// 定义价格类型
public const PRICE_TYPE_RESET_TRAFFIC = 'reset_traffic'; // 重置流量价格
// 定义可用的订阅周期
public const PERIOD_MONTHLY = 'monthly';
public const PERIOD_QUARTERLY = 'quarterly';
public const PERIOD_HALF_YEARLY = 'half_yearly';
public const PERIOD_YEARLY = 'yearly';
public const PERIOD_TWO_YEARLY = 'two_yearly';
public const PERIOD_THREE_YEARLY = 'three_yearly';
public const PERIOD_ONETIME = 'onetime';
public const PERIOD_RESET_TRAFFIC = 'reset_traffic';
// 定义旧版周期映射
public const LEGACY_PERIOD_MAPPING = [
'month_price' => self::PERIOD_MONTHLY,
'quarter_price' => self::PERIOD_QUARTERLY,
'half_year_price' => self::PERIOD_HALF_YEARLY,
'year_price' => self::PERIOD_YEARLY,
'two_year_price' => self::PERIOD_TWO_YEARLY,
'three_year_price' => self::PERIOD_THREE_YEARLY,
'onetime_price' => self::PERIOD_ONETIME,
'reset_price' => self::PERIOD_RESET_TRAFFIC
];
protected $fillable = [
'group_id',
'transfer_enable',
'name',
'speed_limit',
'show',
'sort',
'renew',
'content',
'prices',
'reset_traffic_method',
'capacity_limit',
'sell',
'device_limit'
];
protected $casts = [
'show' => 'boolean',
'renew' => 'boolean',
'created_at' => 'timestamp',
'updated_at' => 'timestamp',
'group_id' => 'integer',
'prices' => 'array',
'reset_traffic_method' => 'integer',
];
/**
* 获取所有可用的流量重置方式
*
* @return array
*/
public static function getResetTrafficMethods(): array
{
return [
self::RESET_TRAFFIC_FOLLOW_SYSTEM => '跟随系统设置',
self::RESET_TRAFFIC_FIRST_DAY_MONTH => '每月1号',
self::RESET_TRAFFIC_MONTHLY => '按月重置',
self::RESET_TRAFFIC_NEVER => '不重置',
self::RESET_TRAFFIC_FIRST_DAY_YEAR => '每年1月1日',
self::RESET_TRAFFIC_YEARLY => '按年重置',
];
}
/**
* 获取下一次流量重置时间
*
* @param Carbon|null $from 计算起始时间,默认为当前时间
* @return Carbon|null 下次重置时间如果不重置则返回null
*/
public function getNextResetTime(?Carbon $from = null): ?Carbon
{
$from = $from ?? Carbon::now();
switch ($this->reset_traffic_method) {
case self::RESET_TRAFFIC_FIRST_DAY_MONTH:
return $from->copy()->addMonth()->startOfMonth();
case self::RESET_TRAFFIC_MONTHLY:
return $from->copy()->addMonth()->startOfDay();
case self::RESET_TRAFFIC_FIRST_DAY_YEAR:
return $from->copy()->addYear()->startOfYear();
case self::RESET_TRAFFIC_YEARLY:
return $from->copy()->addYear()->startOfDay();
case self::RESET_TRAFFIC_NEVER:
return null;
case self::RESET_TRAFFIC_FOLLOW_SYSTEM:
default:
// 这里需要实现获取系统设置的逻辑
// 可以通过系统配置或其他方式获取
return null;
}
}
/**
* 检查是否需要重置流量
*
* @param Carbon|null $checkTime 检查时间点,默认为当前时间
* @return bool
*/
public function shouldResetTraffic(?Carbon $checkTime = null): bool
{
if ($this->reset_traffic_method === self::RESET_TRAFFIC_NEVER) {
return false;
}
$checkTime = $checkTime ?? Carbon::now();
$nextResetTime = $this->getNextResetTime($checkTime);
if ($nextResetTime === null) {
return false;
}
return $checkTime->greaterThanOrEqualTo($nextResetTime);
}
/**
* 获取流量重置方式的描述
*
* @return string
*/
public function getResetTrafficMethodName(): string
{
return self::getResetTrafficMethods()[$this->reset_traffic_method] ?? '未知';
}
/**
* 获取所有可用的订阅周期
*
* @return array
*/
public static function getAvailablePeriods(): array
{
return [
self::PERIOD_MONTHLY => [
'name' => '月付',
'days' => 30,
'value' => 1
],
self::PERIOD_QUARTERLY => [
'name' => '季付',
'days' => 90,
'value' => 3
],
self::PERIOD_HALF_YEARLY => [
'name' => '半年付',
'days' => 180,
'value' => 6
],
self::PERIOD_YEARLY => [
'name' => '年付',
'days' => 365,
'value' => 12
],
self::PERIOD_TWO_YEARLY => [
'name' => '两年付',
'days' => 730,
'value' => 24
],
self::PERIOD_THREE_YEARLY => [
'name' => '三年付',
'days' => 1095,
'value' => 36
],
self::PERIOD_ONETIME => [
'name' => '一次性',
'days' => -1,
'value' => -1
],
self::PERIOD_RESET_TRAFFIC => [
'name' => '重置流量',
'days' => -1,
'value' => -1
],
];
}
/**
* 获取指定周期的价格
*
* @param string $period
* @return int|null
*/
public function getPriceByPeriod(string $period): ?int
{
return $this->prices[$period] ?? null;
}
/**
* 获取所有已设置价格的周期
*
* @return array
*/
public function getActivePeriods(): array
{
return array_filter(
self::getAvailablePeriods(),
fn($period) => isset($this->prices[$period])
&& $this->prices[$period] > 0,
ARRAY_FILTER_USE_KEY
);
}
/**
* 设置指定周期的价格
*
* @param string $period
* @param int $price
* @return void
* @throws InvalidArgumentException
*/
public function setPeriodPrice(string $period, int $price): void
{
if (!array_key_exists($period, self::getAvailablePeriods())) {
throw new InvalidArgumentException("Invalid period: {$period}");
}
$prices = $this->prices ?? [];
$prices[$period] = $price;
$this->prices = $prices;
}
/**
* 移除指定周期的价格
*
* @param string $period
* @return void
*/
public function removePeriodPrice(string $period): void
{
$prices = $this->prices ?? [];
unset($prices[$period]);
$this->prices = $prices;
}
/**
* 获取所有价格及其对应的周期信息
*
* @return array
*/
public function getPriceList(): array
{
$prices = $this->prices ?? [];
$periods = self::getAvailablePeriods();
$priceList = [];
foreach ($prices as $period => $price) {
if (isset($periods[$period]) && $price > 0) {
$priceList[$period] = [
'period' => $periods[$period],
'price' => $price,
'average_price' => $periods[$period]['value'] > 0
? round($price / $periods[$period]['value'], 2)
: $price
];
}
}
return $priceList;
}
/**
* 检查是否可以重置流量
*
* @return bool
*/
public function canResetTraffic(): bool
{
return $this->reset_traffic_method !== self::RESET_TRAFFIC_NEVER
&& $this->getResetTrafficPrice() > 0;
}
/**
* 获取重置流量的价格
*
* @return int
*/
public function getResetTrafficPrice(): int
{
return $this->prices[self::PRICE_TYPE_RESET_TRAFFIC] ?? 0;
}
/**
* 计算指定周期的有效天数
*
* @param string $period
* @return int -1表示永久有效
* @throws InvalidArgumentException
*/
public static function getPeriodDays(string $period): int
{
$periods = self::getAvailablePeriods();
if (!isset($periods[$period])) {
throw new InvalidArgumentException("Invalid period: {$period}");
}
return $periods[$period]['days'];
}
/**
* 检查周期是否有效
*
* @param string $period
* @return bool
*/
public static function isValidPeriod(string $period): bool
{
return array_key_exists($period, self::getAvailablePeriods());
}
public function users(): HasMany
{
return $this->hasMany(User::class);
}
public function group()
{
return $this->hasOne(ServerGroup::class, 'id', 'group_id');
}
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
/**
* 设置流量重置方式
*
* @param int $method
* @return void
* @throws InvalidArgumentException
*/
public function setResetTrafficMethod(int $method): void
{
if (!array_key_exists($method, self::getResetTrafficMethods())) {
throw new InvalidArgumentException("Invalid reset traffic method: {$method}");
}
$this->reset_traffic_method = $method;
}
/**
* 设置重置流量价格
*
* @param int $price
* @return void
*/
public function setResetTrafficPrice(int $price): void
{
$prices = $this->prices ?? [];
$prices[self::PRICE_TYPE_RESET_TRAFFIC] = max(0, $price);
$this->prices = $prices;
}
}