Xboard/app/Models/Plan.php

387 lines
10 KiB
PHP
Raw Normal View History

2023-11-17 01:44:01 -05:00
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
2025-01-06 12:20:11 -05:00
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use InvalidArgumentException;
use Carbon\Carbon;
2023-11-17 01:44:01 -05:00
class Plan extends Model
{
2025-01-06 12:20:11 -05:00
use HasFactory;
2023-11-17 01:44:01 -05:00
protected $table = 'v2_plan';
protected $dateFormat = 'U';
2025-01-06 12:20:11 -05:00
// 定义流量重置方式
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'
2025-01-06 12:20:11 -05:00
];
2023-11-17 01:44:01 -05:00
protected $casts = [
2025-01-06 12:20:11 -05:00
'show' => 'boolean',
'renew' => 'boolean',
2023-11-17 01:44:01 -05:00
'created_at' => 'timestamp',
2025-01-06 12:20:11 -05:00
'updated_at' => 'timestamp',
'group_id' => 'integer',
'prices' => 'array',
'reset_traffic_method' => 'integer',
2023-11-17 01:44:01 -05:00
];
2025-01-06 12:20:11 -05:00
/**
* 获取所有可用的流量重置方式
*
* @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;
}
}