From 2491e9b5ad632f483ad5781d1b705616b0bad046 Mon Sep 17 00:00:00 2001 From: QTom Date: Sat, 28 Feb 2026 10:46:34 +0800 Subject: [PATCH] fix: round-3 review fixes for RPM limiting - Add sanitizeExtraBaseRPM to BulkUpdate handler (was missing) - Add WindowCost scheduling checks to legacy non-sticky selection paths (4 sites), matching existing sticky + load-aware coverage - Export ParseExtraInt from service package, remove duplicate parseExtraIntForValidation from admin handler --- .../internal/handler/admin/account_handler.go | 25 +++---------------- backend/internal/service/account.go | 6 +++++ backend/internal/service/gateway_service.go | 12 +++++++++ 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/backend/internal/handler/admin/account_handler.go b/backend/internal/handler/admin/account_handler.go index 382d62c1..dadecbc0 100644 --- a/backend/internal/handler/admin/account_handler.go +++ b/backend/internal/handler/admin/account_handler.go @@ -1082,6 +1082,8 @@ func (h *AccountHandler) BulkUpdate(c *gin.Context) { response.BadRequest(c, "rate_multiplier must be >= 0") return } + // base_rpm 输入校验:负值归零,超过 10000 截断 + sanitizeExtraBaseRPM(req.Extra) // 确定是否跳过混合渠道检查 skipCheck := req.ConfirmMixedChannelRisk != nil && *req.ConfirmMixedChannelRisk @@ -1751,7 +1753,7 @@ func sanitizeExtraBaseRPM(extra map[string]any) { if !ok { return } - v := parseExtraIntForValidation(raw) + v := service.ParseExtraInt(raw) if v < 0 { v = 0 } else if v > 10000 { @@ -1760,24 +1762,3 @@ func sanitizeExtraBaseRPM(extra map[string]any) { extra["base_rpm"] = v } -// parseExtraIntForValidation 从 extra 字段的 any 值解析为 int,用于输入校验。 -// 支持 int, int64, float64, json.Number, string 类型。 -func parseExtraIntForValidation(value any) int { - switch v := value.(type) { - case int: - return v - case int64: - return int(v) - case float64: - return int(v) - case json.Number: - if i, err := v.Int64(); err == nil { - return int(i) - } - case string: - if i, err := strconv.Atoi(strings.TrimSpace(v)); err == nil { - return i - } - } - return 0 -} diff --git a/backend/internal/service/account.go b/backend/internal/service/account.go index 7f33c61d..c76c817e 100644 --- a/backend/internal/service/account.go +++ b/backend/internal/service/account.go @@ -1274,6 +1274,12 @@ func parseExtraFloat64(value any) float64 { } // parseExtraInt 从 extra 字段解析 int 值 +// ParseExtraInt 从 extra 字段的 any 值解析为 int。 +// 支持 int, int64, float64, json.Number, string 类型,无法解析时返回 0。 +func ParseExtraInt(value any) int { + return parseExtraInt(value) +} + func parseExtraInt(value any) int { switch v := value.(type) { case int: diff --git a/backend/internal/service/gateway_service.go b/backend/internal/service/gateway_service.go index 04e37f68..3323f868 100644 --- a/backend/internal/service/gateway_service.go +++ b/backend/internal/service/gateway_service.go @@ -2641,6 +2641,9 @@ func (s *GatewayService) selectAccountForModelWithPlatform(ctx context.Context, if !s.isAccountSchedulableForModelSelection(ctx, acc, requestedModel) { continue } + if !s.isAccountSchedulableForWindowCost(ctx, acc, false) { + continue + } if !s.isAccountSchedulableForRPM(ctx, acc, false) { continue } @@ -2737,6 +2740,9 @@ func (s *GatewayService) selectAccountForModelWithPlatform(ctx context.Context, if !s.isAccountSchedulableForModelSelection(ctx, acc, requestedModel) { continue } + if !s.isAccountSchedulableForWindowCost(ctx, acc, false) { + continue + } if !s.isAccountSchedulableForRPM(ctx, acc, false) { continue } @@ -2865,6 +2871,9 @@ func (s *GatewayService) selectAccountWithMixedScheduling(ctx context.Context, g if !s.isAccountSchedulableForModelSelection(ctx, acc, requestedModel) { continue } + if !s.isAccountSchedulableForWindowCost(ctx, acc, false) { + continue + } if !s.isAccountSchedulableForRPM(ctx, acc, false) { continue } @@ -2963,6 +2972,9 @@ func (s *GatewayService) selectAccountWithMixedScheduling(ctx context.Context, g if !s.isAccountSchedulableForModelSelection(ctx, acc, requestedModel) { continue } + if !s.isAccountSchedulableForWindowCost(ctx, acc, false) { + continue + } if !s.isAccountSchedulableForRPM(ctx, acc, false) { continue }