fix: 防止订阅过期时间超出 JSON 序列化范围
问题:当分配订阅天数过大时,expires_at 年份可能超过 9999, 导致 time.Time JSON 序列化失败(RFC 3339 要求年份 <= 9999), 使后台无法显示和删除异常数据。 修复: - handler 层添加 validity_days 最大值验证(max=36500,即100年) - service 层添加 MaxValidityDays 和 MaxExpiresAt 双重保护 - 启动时自动修复已存在的异常数据(expires_at > 2099年)
This commit is contained in:
@@ -10,6 +10,13 @@ import (
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
|
||||
)
|
||||
|
||||
// MaxExpiresAt is the maximum allowed expiration date (year 2099)
|
||||
// This prevents time.Time JSON serialization errors (RFC 3339 requires year <= 9999)
|
||||
var MaxExpiresAt = time.Date(2099, 12, 31, 23, 59, 59, 0, time.UTC)
|
||||
|
||||
// MaxValidityDays is the maximum allowed validity days for subscriptions (100 years)
|
||||
const MaxValidityDays = 36500
|
||||
|
||||
var (
|
||||
ErrSubscriptionNotFound = infraerrors.NotFound("SUBSCRIPTION_NOT_FOUND", "subscription not found")
|
||||
ErrSubscriptionExpired = infraerrors.Forbidden("SUBSCRIPTION_EXPIRED", "subscription has expired")
|
||||
@@ -111,6 +118,9 @@ func (s *SubscriptionService) AssignOrExtendSubscription(ctx context.Context, in
|
||||
if validityDays <= 0 {
|
||||
validityDays = 30
|
||||
}
|
||||
if validityDays > MaxValidityDays {
|
||||
validityDays = MaxValidityDays
|
||||
}
|
||||
|
||||
// 已有订阅,执行续期
|
||||
if existingSub != nil {
|
||||
@@ -125,6 +135,11 @@ func (s *SubscriptionService) AssignOrExtendSubscription(ctx context.Context, in
|
||||
newExpiresAt = now.AddDate(0, 0, validityDays)
|
||||
}
|
||||
|
||||
// 确保不超过最大过期时间
|
||||
if newExpiresAt.After(MaxExpiresAt) {
|
||||
newExpiresAt = MaxExpiresAt
|
||||
}
|
||||
|
||||
// 更新过期时间
|
||||
if err := s.userSubRepo.ExtendExpiry(ctx, existingSub.ID, newExpiresAt); err != nil {
|
||||
return nil, false, fmt.Errorf("extend subscription: %w", err)
|
||||
@@ -189,13 +204,21 @@ func (s *SubscriptionService) createSubscription(ctx context.Context, input *Ass
|
||||
if validityDays <= 0 {
|
||||
validityDays = 30
|
||||
}
|
||||
if validityDays > MaxValidityDays {
|
||||
validityDays = MaxValidityDays
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
expiresAt := now.AddDate(0, 0, validityDays)
|
||||
if expiresAt.After(MaxExpiresAt) {
|
||||
expiresAt = MaxExpiresAt
|
||||
}
|
||||
|
||||
sub := &UserSubscription{
|
||||
UserID: input.UserID,
|
||||
GroupID: input.GroupID,
|
||||
StartsAt: now,
|
||||
ExpiresAt: now.AddDate(0, 0, validityDays),
|
||||
ExpiresAt: expiresAt,
|
||||
Status: SubscriptionStatusActive,
|
||||
AssignedAt: now,
|
||||
Notes: input.Notes,
|
||||
@@ -291,8 +314,17 @@ func (s *SubscriptionService) ExtendSubscription(ctx context.Context, subscripti
|
||||
return nil, ErrSubscriptionNotFound
|
||||
}
|
||||
|
||||
// 限制延长天数
|
||||
if days > MaxValidityDays {
|
||||
days = MaxValidityDays
|
||||
}
|
||||
|
||||
// 计算新的过期时间
|
||||
newExpiresAt := sub.ExpiresAt.AddDate(0, 0, days)
|
||||
if newExpiresAt.After(MaxExpiresAt) {
|
||||
newExpiresAt = MaxExpiresAt
|
||||
}
|
||||
|
||||
if err := s.userSubRepo.ExtendExpiry(ctx, subscriptionID, newExpiresAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user