Xboard/app/Services/PlanService.php

194 lines
5.5 KiB
PHP
Raw Normal View History

2023-11-17 01:44:01 -05:00
<?php
namespace App\Services;
use App\Models\Plan;
use App\Models\User;
2025-01-06 12:20:11 -05:00
use App\Exceptions\ApiException;
use Illuminate\Database\Eloquent\Collection;
2023-11-17 01:44:01 -05:00
class PlanService
{
2025-01-06 12:20:11 -05:00
public Plan $plan;
2023-11-17 01:44:01 -05:00
2025-01-06 12:20:11 -05:00
public function __construct(Plan $plan)
2023-11-17 01:44:01 -05:00
{
2025-01-06 12:20:11 -05:00
$this->plan = $plan;
2023-11-17 01:44:01 -05:00
}
2025-01-06 12:20:11 -05:00
/**
* 获取所有可销售的订阅计划列表
* 条件show sell true,且容量充足
*
* @return Collection
*/
public function getAvailablePlans(): Collection
2023-11-17 01:44:01 -05:00
{
2025-01-06 12:20:11 -05:00
return Plan::where('show', true)
->where('sell', true)
->orderBy('sort')
->get()
->filter(function ($plan) {
return $this->hasCapacity($plan);
});
}
/**
* 获取指定订阅计划的可用状态
* 条件renew sell true
*
* @param int $planId
* @return Plan|null
*/
public function getAvailablePlan(int $planId): ?Plan
{
return Plan::where('id', $planId)
->where('sell', true)
->where('renew', true)
->first();
}
/**
* 检查指定计划是否可用于指定用户
*
* @param Plan $plan
* @param User $user
* @return bool
*/
public function isPlanAvailableForUser(Plan $plan, User $user): bool
{
// 如果是续费
if ($user->plan_id === $plan->id) {
2025-01-12 19:17:11 -05:00
return $plan->renew;
2025-01-06 12:20:11 -05:00
}
// 如果是新购
return $plan->show && $plan->sell && $this->hasCapacity($plan);
2023-11-17 01:44:01 -05:00
}
2025-01-06 12:20:11 -05:00
public function validatePurchase(User $user, string $period): void
2023-11-17 01:44:01 -05:00
{
2025-01-06 12:20:11 -05:00
if (!$this->plan) {
throw new ApiException(__('Subscription plan does not exist'));
}
// 转换周期格式为新版格式
$periodKey = self::getPeriodKey($period);
if ($periodKey === Plan::PERIOD_RESET_TRAFFIC) {
$this->validateResetTrafficPurchase($user);
return;
}
// 检查价格时使用新版格式
if (!isset($this->plan->prices[$periodKey])) {
throw new ApiException(__('This payment period cannot be purchased, please choose another period'));
}
if ($user->plan_id !== $this->plan->id && !$this->hasCapacity($this->plan)) {
throw new ApiException(__('Current product is sold out'));
}
$this->validatePlanAvailability($user);
}
/**
* 智能转换周期格式为新版格式
* 如果是新版格式直接返回,如果是旧版格式则转换为新版格式
*
* @param string $period
* @return string
*/
public static function getPeriodKey(string $period): string
{
// 如果是新版格式直接返回
if (in_array($period, self::getNewPeriods())) {
return $period;
}
// 如果是旧版格式则转换为新版格式
return Plan::LEGACY_PERIOD_MAPPING[$period] ?? $period;
}
/**
* 只能转换周期格式为旧版本
*/
public static function convertToLegacyPeriod(string $period): string
{
return Plan::LEGACY_PERIOD_MAPPING[$period] ?? $period;
}
/**
* 获取所有支持的新版周期格式
*
* @return array
*/
public static function getNewPeriods(): array
{
return array_values(Plan::LEGACY_PERIOD_MAPPING);
}
/**
* 获取旧版周期格式
*
* @param string $period
* @return string
*/
public static function getLegacyPeriod(string $period): string
{
$flipped = array_flip(Plan::LEGACY_PERIOD_MAPPING);
return $flipped[$period] ?? $period;
}
protected function validateResetTrafficPurchase(User $user): void
{
if (!app(UserService::class)->isAvailable($user) || $this->plan->id !== $user->plan_id) {
throw new ApiException(__('Subscription has expired or no active subscription, unable to purchase Data Reset Package'));
}
}
protected function validatePlanAvailability(User $user): void
{
if ((!$this->plan->show && !$this->plan->renew) || (!$this->plan->show && $user->plan_id !== $this->plan->id)) {
throw new ApiException(__('This subscription has been sold out, please choose another subscription'));
}
if (!$this->plan->renew && $user->plan_id == $this->plan->id) {
throw new ApiException(__('This subscription cannot be renewed, please change to another subscription'));
}
if (!$this->plan->show && $this->plan->renew && !app(UserService::class)->isAvailable($user)) {
throw new ApiException(__('This subscription has expired, please change to another subscription'));
}
}
public function hasCapacity(Plan $plan): bool
{
if ($plan->capacity_limit === null) {
return true;
}
$activeUserCount = User::where('plan_id', $plan->id)
2023-11-17 01:44:01 -05:00
->where(function ($query) {
$query->where('expired_at', '>=', time())
2025-01-06 12:20:11 -05:00
->orWhereNull('expired_at');
2023-11-17 01:44:01 -05:00
})
2025-01-06 12:20:11 -05:00
->count();
return ($plan->capacity_limit - $activeUserCount) > 0;
}
public function getAvailablePeriods(Plan $plan): array
{
return array_filter(
$plan->getActivePeriods(),
fn($period) => isset($plan->prices[$period]) && $plan->prices[$period] > 0
);
}
public function canResetTraffic(Plan $plan): bool
{
return $plan->reset_traffic_method !== Plan::RESET_TRAFFIC_NEVER
&& $plan->getResetTrafficPrice() > 0;
2023-11-17 01:44:01 -05:00
}
}