mirror of
https://github.com/lkddi/Xboard.git
synced 2026-04-24 03:57:27 +08:00
feat: enhance plan validation, traffic system and email verification
- feat: add plan price validation - feat: make traffic packages stackable - feat: add commission and invite info to admin order details - feat: apply email whitelist to verification code API - fix: subscription link copy compatibility for non-HTTPS - fix: resolve route editing 500 error in certain cases - refactor: restructure traffic reset logic
This commit is contained in:
@@ -9,76 +9,37 @@ use App\Models\Order;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Services\Plugin\HookManager;
|
||||
use App\Services\TrafficResetService;
|
||||
use App\Models\TrafficResetLog;
|
||||
|
||||
class UserService
|
||||
{
|
||||
private function calcResetDayByMonthFirstDay(): int
|
||||
{
|
||||
$today = (int) date('d');
|
||||
$lastDay = (int) date('d', strtotime('last day of +0 months'));
|
||||
return $lastDay - $today;
|
||||
}
|
||||
|
||||
private function calcResetDayByExpireDay(int $expiredAt)
|
||||
{
|
||||
$day = (int) date('d', $expiredAt);
|
||||
$today = (int) date('d');
|
||||
$lastDay = (int) date('d', strtotime('last day of +0 months'));
|
||||
if ($day >= $today && $day >= $lastDay) {
|
||||
return $lastDay - $today;
|
||||
}
|
||||
if ($day >= $today) {
|
||||
return $day - $today;
|
||||
}
|
||||
|
||||
return $lastDay - $today + $day;
|
||||
}
|
||||
|
||||
private function calcResetDayByYearFirstDay(): int
|
||||
{
|
||||
$nextYear = strtotime(date("Y-01-01", strtotime('+1 year')));
|
||||
return (int) (($nextYear - time()) / 86400);
|
||||
}
|
||||
|
||||
private function calcResetDayByYearExpiredAt(int $expiredAt): int
|
||||
{
|
||||
$md = date('m-d', $expiredAt);
|
||||
$nowYear = strtotime(date("Y-{$md}"));
|
||||
$nextYear = strtotime('+1 year', $nowYear);
|
||||
if ($nowYear > time()) {
|
||||
return (int) (($nowYear - time()) / 86400);
|
||||
}
|
||||
return (int) (($nextYear - time()) / 86400);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remaining days until the next traffic reset for a user.
|
||||
* This method reuses the TrafficResetService logic for consistency.
|
||||
*/
|
||||
public function getResetDay(User $user): ?int
|
||||
{
|
||||
// 前置条件检查
|
||||
if ($user->expired_at <= time() || $user->expired_at === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取重置方式逻辑统一
|
||||
$resetMethod = $user->plan->reset_traffic_method === Plan::RESET_TRAFFIC_FOLLOW_SYSTEM
|
||||
? (int)admin_setting('reset_traffic_method', 0)
|
||||
: $user->plan->reset_traffic_method;
|
||||
|
||||
// 验证重置方式有效性
|
||||
if (!in_array($resetMethod, array_keys(Plan::getResetTrafficMethods()), true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 方法映射表
|
||||
$methodHandlers = [
|
||||
Plan::RESET_TRAFFIC_FIRST_DAY_MONTH => fn() => $this->calcResetDayByMonthFirstDay(),
|
||||
Plan::RESET_TRAFFIC_MONTHLY => fn() => $this->calcResetDayByExpireDay($user->expired_at),
|
||||
Plan::RESET_TRAFFIC_FIRST_DAY_YEAR => fn() => $this->calcResetDayByYearFirstDay(),
|
||||
Plan::RESET_TRAFFIC_YEARLY => fn() => $this->calcResetDayByYearExpiredAt($user->expired_at),
|
||||
];
|
||||
|
||||
$handler = $methodHandlers[$resetMethod] ?? null;
|
||||
// Use TrafficResetService to calculate the next reset time
|
||||
$trafficResetService = app(TrafficResetService::class);
|
||||
$nextResetTime = $trafficResetService->calculateNextResetTime($user);
|
||||
|
||||
return $handler ? $handler() : null;
|
||||
if (!$nextResetTime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate the remaining days from now to the next reset time
|
||||
$now = time();
|
||||
$resetTimestamp = $nextResetTime->timestamp;
|
||||
|
||||
if ($resetTimestamp <= $now) {
|
||||
return 0; // Reset time has passed or is now
|
||||
}
|
||||
|
||||
// Calculate the difference in days (rounded up)
|
||||
$daysDifference = ceil(($resetTimestamp - $now) / 86400);
|
||||
|
||||
return (int) $daysDifference;
|
||||
}
|
||||
|
||||
public function isAvailable(User $user)
|
||||
@@ -165,4 +126,28 @@ class UserService
|
||||
StatServerJob::dispatch($server, $chunk->toArray(), $protocol, 'd');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户流量信息(增加重置检查)
|
||||
*/
|
||||
public function getUserTrafficInfo(User $user): array
|
||||
{
|
||||
// 检查是否需要重置流量
|
||||
app(TrafficResetService::class)->checkAndReset($user, TrafficResetLog::SOURCE_USER_ACCESS);
|
||||
|
||||
// 重新获取用户数据(可能已被重置)
|
||||
$user->refresh();
|
||||
|
||||
return [
|
||||
'upload' => $user->u ?? 0,
|
||||
'download' => $user->d ?? 0,
|
||||
'total_used' => $user->getTotalUsedTraffic(),
|
||||
'total_available' => $user->transfer_enable ?? 0,
|
||||
'remaining' => $user->getRemainingTraffic(),
|
||||
'usage_percentage' => $user->getTrafficUsagePercentage(),
|
||||
'next_reset_at' => $user->next_reset_at?->timestamp,
|
||||
'last_reset_at' => $user->last_reset_at?->timestamp,
|
||||
'reset_count' => $user->reset_count,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user