From 1550ef3cb1bc0904cff2f4578dd5661be803eced Mon Sep 17 00:00:00 2001 From: Break Date: Thu, 28 May 2026 15:19:54 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=B6=85=E9=A2=9D=E6=B5=81?= =?UTF-8?q?=E9=87=8F=E4=B8=8D=E8=AE=B0=E5=BD=95=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/package/usage_service.go | 32 ++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/internal/service/package/usage_service.go b/internal/service/package/usage_service.go index 82850d2..5cedfd0 100644 --- a/internal/service/package/usage_service.go +++ b/internal/service/package/usage_service.go @@ -65,7 +65,8 @@ func (s *UsageService) SetStopResumeCallback(callback StopResumeCallback) { // DeductDataUsage 任务 10.2-10.6: 按优先级扣减流量 // 扣减顺序:加油包(按 priority ASC) → 主套餐 -// 流量用完时自动标记 status=2,所有套餐用完时触发停机 +// 真实用量允许超过套餐阈值,超额部分记入最后一个生效套餐,用于后续虚流量阈值评估。 +// 流量用完时自动标记 status=2,所有套餐用完时触发停机。 func (s *UsageService) DeductDataUsage(ctx context.Context, carrierType string, carrierID uint, usageMB float64) error { if usageMB <= 0 { return errors.New(errors.CodeInvalidParam, "扣减流量必须大于0") @@ -94,7 +95,7 @@ func (s *UsageService) DeductDataUsage(ctx context.Context, carrierType string, remainingUsage := deductUsageMB today := time.Now().Format("2006-01-02") - for _, pkg := range packages { + for index, pkg := range packages { if remainingUsage <= 0 { break } @@ -102,7 +103,8 @@ func (s *UsageService) DeductDataUsage(ctx context.Context, carrierType string, // 计算当前套餐剩余额度 effectiveThresholdMB := pkg.EffectiveThresholdMB() remainingQuota := pkg.RemainingQuotaMB() - if remainingQuota <= 0 { + isLastPackage := index == len(packages)-1 + if remainingQuota <= 0 && !isLastPackage { // 套餐已用完,标记为已用完 if err := tx.Model(pkg).Update("status", constants.PackageUsageStatusDepleted).Error; err != nil { return errors.Wrap(errors.CodeDatabaseError, err, "更新套餐状态失败") @@ -112,11 +114,19 @@ func (s *UsageService) DeductDataUsage(ctx context.Context, carrierType string, // 本次从该套餐扣减的流量 var deductFromPkg int64 - if remainingUsage <= remainingQuota { + if isLastPackage { + deductFromPkg = remainingUsage + } else if remainingUsage <= remainingQuota { deductFromPkg = remainingUsage } else { deductFromPkg = remainingQuota } + if deductFromPkg <= 0 { + if err := tx.Model(pkg).Update("status", constants.PackageUsageStatusDepleted).Error; err != nil { + return errors.Wrap(errors.CodeDatabaseError, err, "更新套餐状态失败") + } + continue + } // 更新套餐使用量 newUsage := pkg.DataUsageMB + deductFromPkg @@ -145,17 +155,9 @@ func (s *UsageService) DeductDataUsage(ctx context.Context, carrierType string, zap.Int64("deduct_mb", deductFromPkg), zap.Int64("new_usage_mb", newUsage), zap.Int64("effective_threshold_mb", effectiveThresholdMB), - zap.Int64("data_limit_mb", pkg.DataLimitMB)) - } - - // 如果流量扣减未完成,说明所有套餐都不够 - if remainingUsage > 0 { - s.logger.Warn("流量不足", - zap.String("carrier_type", targetCarrierType), - zap.Uint("carrier_id", targetCarrierID), - zap.Int64("requested_mb", deductUsageMB), - zap.Int64("remaining_mb", remainingUsage)) - return errors.New(errors.CodeInsufficientQuota, "流量不足") + zap.Int64("data_limit_mb", pkg.DataLimitMB), + zap.Int64("remaining_quota_mb", remainingQuota), + zap.Bool("over_threshold", effectiveThresholdMB > 0 && newUsage > effectiveThresholdMB)) } // 任务 10.5: 检查是否所有套餐都用完(触发停机)