Files
SingBox-Gopanel/internal/service/traffic_reset.go
CN-JS-HuiBai 28f94c5418
Some checks failed
build / build (api, amd64, linux) (push) Failing after -50s
build / build (api, arm64, linux) (push) Failing after -51s
build / build (api.exe, amd64, windows) (push) Failing after -51s
修复无法编译的错误
2026-04-18 02:01:06 +08:00

118 lines
3.3 KiB
Go

package service
import (
"time"
"xboard-go/internal/database"
"xboard-go/internal/model"
)
const (
ResetTrafficFirstDayMonth = 0
ResetTrafficMonthly = 1
ResetTrafficNever = 2
ResetTrafficFirstDayYear = 3
ResetTrafficYearly = 4
)
func GetResetDay(user *model.User) *int {
nextResetTime := CalculateNextResetTime(user)
if nextResetTime == nil {
return nil
}
now := time.Now()
if nextResetTime.Before(now) {
zero := 0
return &zero
}
days := int(nextResetTime.Sub(now).Hours()/24) + 1
return &days
}
func CalculateNextResetTime(user *model.User) *time.Time {
if user.PlanID == nil {
return nil
}
// We need to fetch the plan's reset method.
// For simplicity in this service, we assume the caller has access or we fetch it here.
// However, usually it's better to pass the plan or the reset method.
// Let's implement it carefully.
// If the user has no plan or reset method is never, return nil.
// Since we don't have the plan object here, we'll need to fetch it if not provided.
// For now, let's look at how Xboard does it (it uses $user->plan).
// Wait, I should check if I can get the plan easily.
var resetMethod int
if err := database.DB.Model(&model.Plan{}).Select("reset_traffic_method").Where("id = ?", *user.PlanID).Scan(&resetMethod).Error; err != nil {
return nil
}
if resetMethod == ResetTrafficNever || user.ExpiredAt == nil {
return nil
}
now := time.Now()
expiredAt := time.Unix(*user.ExpiredAt, 0)
switch resetMethod {
case ResetTrafficFirstDayMonth:
return getNextMonthFirstDay(now)
case ResetTrafficMonthly:
return getNextMonthlyReset(user, now, expiredAt)
case ResetTrafficFirstDayYear:
return getNextYearFirstDay(now)
case ResetTrafficYearly:
return getNextYearlyReset(user, now, expiredAt)
default:
return nil
}
}
func getNextMonthFirstDay(from time.Time) *time.Time {
t := time.Date(from.Year(), from.Month(), 1, 0, 0, 0, 0, from.Location()).AddDate(0, 1, 0)
return &t
}
func getNextMonthlyReset(user *model.User, from time.Time, expiredAt time.Time) *time.Time {
resetDay := expiredAt.Day()
// Try current month
target := time.Date(from.Year(), from.Month(), resetDay, expiredAt.Hour(), expiredAt.Minute(), expiredAt.Second(), 0, from.Location())
if target.After(from) {
return &target
}
// Try next month
target = target.AddDate(0, 1, 0)
// Handle cases where next month doesn't have that day (e.g. Feb 30)
if target.Day() != resetDay {
// Go back to last day of intended month
target = time.Date(target.Year(), target.Month(), 0, expiredAt.Hour(), expiredAt.Minute(), expiredAt.Second(), 0, from.Location())
}
return &target
}
func getNextYearFirstDay(from time.Time) *time.Time {
t := time.Date(from.Year()+1, 1, 1, 0, 0, 0, 0, from.Location())
return &t
}
func getNextYearlyReset(user *model.User, from time.Time, expiredAt time.Time) *time.Time {
resetMonth := expiredAt.Month()
resetDay := expiredAt.Day()
// Try current year
target := time.Date(from.Year(), resetMonth, resetDay, expiredAt.Hour(), expiredAt.Minute(), expiredAt.Second(), 0, from.Location())
if target.After(from) {
return &target
}
// Try next year
target = time.Date(from.Year()+1, resetMonth, resetDay, expiredAt.Hour(), expiredAt.Minute(), expiredAt.Second(), 0, from.Location())
return &target
}