feat: improve traffic reset precision and admin features

- Increase traffic reset granularity to seconds
- Add next reset time display in admin pane
This commit is contained in:
xboard
2025-07-15 02:18:33 +08:00
parent 706ba5a7a9
commit b55a56d6a7
4 changed files with 29 additions and 53 deletions

View File

@@ -82,7 +82,8 @@ class TrafficResetService
if (
!$user->plan
|| $user->plan->reset_traffic_method === Plan::RESET_TRAFFIC_NEVER
|| ($user->plan->reset_traffic_method === Plan::RESET_TRAFFIC_FOLLOW_SYSTEM && (int) admin_setting('reset_traffic_method', Plan::RESET_TRAFFIC_MONTHLY) === Plan::RESET_TRAFFIC_NEVER)
|| ($user->plan->reset_traffic_method === Plan::RESET_TRAFFIC_FOLLOW_SYSTEM
&& (int) admin_setting('reset_traffic_method', Plan::RESET_TRAFFIC_MONTHLY) === Plan::RESET_TRAFFIC_NEVER)
|| $user->expired_at === NULL
) {
return null;
@@ -94,7 +95,7 @@ class TrafficResetService
$resetMethod = (int) admin_setting('reset_traffic_method', Plan::RESET_TRAFFIC_MONTHLY);
}
$now = Carbon::now();
$now = Carbon::now(config('app.timezone'));
return match ($resetMethod) {
Plan::RESET_TRAFFIC_FIRST_DAY_MONTH => $this->getNextMonthFirstDay($now),
@@ -124,38 +125,20 @@ class TrafficResetService
*/
private function getNextMonthlyReset(User $user, Carbon $from): Carbon
{
$expiredAt = Carbon::createFromTimestamp($user->expired_at);
$expiredAt = Carbon::createFromTimestamp($user->expired_at, config('app.timezone'));
$resetDay = $expiredAt->day;
return $this->getNextResetByDay($from, $resetDay);
}
/**
* Get the next reset time based on a specific day of the month.
*/
private function getNextResetByDay(Carbon $from, int $targetDay): Carbon
{
$currentMonthTarget = $this->getValidDayInMonth($from->copy(), $targetDay);
$resetTime = [$expiredAt->hour, $expiredAt->minute, $expiredAt->second];
// 当前月目标时间
$currentMonthTarget = $from->copy()->day($resetDay)
->setTime(...$resetTime);
if ($currentMonthTarget->timestamp > $from->timestamp) {
return $currentMonthTarget;
}
// 下月目标时间
$nextMonth = $from->copy()->addMonth();
return $this->getValidDayInMonth($nextMonth, $targetDay);
}
/**
* Get a valid day in a given month, handling non-existent dates.
*/
private function getValidDayInMonth(Carbon $month, int $targetDay): Carbon
{
$lastDayOfMonth = $month->copy()->endOfMonth()->day;
if ($targetDay > $lastDayOfMonth) {
return $month->endOfMonth()->startOfDay();
}
return $month->day($targetDay)->startOfDay();
$lastDayOfNextMonth = $nextMonth->copy()->endOfMonth()->day;
$targetDay = min($resetDay, $lastDayOfNextMonth);
return $nextMonth->copy()->day($targetDay)->setTime(...$resetTime);
}
/**
@@ -177,30 +160,21 @@ class TrafficResetService
*/
private function getNextYearlyReset(User $user, Carbon $from): Carbon
{
$expiredAt = Carbon::createFromTimestamp($user->expired_at);
$currentYearTarget = $this->getValidYearDate($from->copy(), $expiredAt);
$expiredAt = Carbon::createFromTimestamp($user->expired_at, config('app.timezone'));
$resetMonth = $expiredAt->month;
$resetDay = $expiredAt->day;
$resetTime = [$expiredAt->hour, $expiredAt->minute, $expiredAt->second];
$currentYearTarget = $from->copy()->month($resetMonth)->day($resetDay)->setTime(...$resetTime);
if ($currentYearTarget->timestamp > $from->timestamp) {
return $currentYearTarget;
}
return $this->getValidYearDate($from->copy()->addYear(), $expiredAt);
$nextYear = $from->copy()->addYear();
$lastDayOfMonth = $nextYear->copy()->month($resetMonth)->endOfMonth()->day;
$targetDay = min($resetDay, $lastDayOfMonth);
return $nextYear->copy()->month($resetMonth)->day($targetDay)->setTime(...$resetTime);
}
/**
* Get a valid date in a given year, handling leap year cases for Feb 29th.
*/
private function getValidYearDate(Carbon $year, Carbon $expiredAt): Carbon
{
$target = $year->month($expiredAt->month)->day($expiredAt->day)->startOfDay();
if ($expiredAt->month === 2 && $expiredAt->day === 29 && !$target->isLeapYear()) {
$target->day(28);
}
return $target;
}
/**
* Record the traffic reset log.

File diff suppressed because one or more lines are too long

View File

@@ -2310,6 +2310,7 @@ window.XBOARD_TRANSLATIONS['en-US'] = {
"commission": "Commission",
"register_time": "Register Time",
"actions": "Actions",
"next_reset_at": "Next Reset At",
"device_limit": {
"unlimited": "No device limit",
"limited": "Maximum {{count}} devices allowed"

View File

@@ -2383,6 +2383,7 @@ window.XBOARD_TRANSLATIONS['zh-CN'] = {
"commission": "佣金",
"register_time": "注册时间",
"actions": "操作",
"next_reset_at": "下次重置时间",
"device_limit": {
"unlimited": "无设备数限制",
"limited": "最多可同时在线 {{count}} 台设备"