mirror of
https://github.com/cedar2025/Xboard.git
synced 2025-01-23 02:58:14 -05:00
387 lines
10 KiB
PHP
Executable File
387 lines
10 KiB
PHP
Executable File
<?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;
|
||
}
|
||
} |