118 lines
3.3 KiB
Go
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
|
|
}
|