From 5b1907fe61dde189fa203e793e5838bb2d5b8337 Mon Sep 17 00:00:00 2001 From: song Date: Mon, 5 Jan 2026 17:14:06 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=9B=BE=E7=89=87=E8=AE=A1=E8=B4=B9?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=AE=A1=E6=9F=A5=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - isImageGenerationModel 改为精确匹配/前缀匹配,避免误匹配 - 新增 normalizePrice 函数,支持负数清除价格配置 - 更新注释说明 Gemini API 每次请求只生成一张图片 - 添加测试用例验证不会误匹配自定义模型名 --- .../internal/handler/admin/group_handler.go | 4 +-- backend/internal/service/admin_service.go | 27 ++++++++++++++----- .../service/antigravity_gateway_service.go | 15 ++++++++--- .../service/antigravity_image_test.go | 3 +++ 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/backend/internal/handler/admin/group_handler.go b/backend/internal/handler/admin/group_handler.go index 1d318271..182d26d0 100644 --- a/backend/internal/handler/admin/group_handler.go +++ b/backend/internal/handler/admin/group_handler.go @@ -33,7 +33,7 @@ type CreateGroupRequest struct { DailyLimitUSD *float64 `json:"daily_limit_usd"` WeeklyLimitUSD *float64 `json:"weekly_limit_usd"` MonthlyLimitUSD *float64 `json:"monthly_limit_usd"` - // 图片生成计费配置(仅 antigravity 平台使用) + // 图片生成计费配置(antigravity 和 gemini 平台使用,负数表示清除配置) ImagePrice1K *float64 `json:"image_price_1k"` ImagePrice2K *float64 `json:"image_price_2k"` ImagePrice4K *float64 `json:"image_price_4k"` @@ -51,7 +51,7 @@ type UpdateGroupRequest struct { DailyLimitUSD *float64 `json:"daily_limit_usd"` WeeklyLimitUSD *float64 `json:"weekly_limit_usd"` MonthlyLimitUSD *float64 `json:"monthly_limit_usd"` - // 图片生成计费配置(仅 antigravity 平台使用) + // 图片生成计费配置(antigravity 和 gemini 平台使用,负数表示清除配置) ImagePrice1K *float64 `json:"image_price_1k"` ImagePrice2K *float64 `json:"image_price_2k"` ImagePrice4K *float64 `json:"image_price_4k"` diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go index 6f66f067..4f1c83e4 100644 --- a/backend/internal/service/admin_service.go +++ b/backend/internal/service/admin_service.go @@ -504,6 +504,11 @@ func (s *adminServiceImpl) CreateGroup(ctx context.Context, input *CreateGroupIn weeklyLimit := normalizeLimit(input.WeeklyLimitUSD) monthlyLimit := normalizeLimit(input.MonthlyLimitUSD) + // 图片价格:负数表示清除(使用默认价格),0 保留(表示免费) + imagePrice1K := normalizePrice(input.ImagePrice1K) + imagePrice2K := normalizePrice(input.ImagePrice2K) + imagePrice4K := normalizePrice(input.ImagePrice4K) + group := &Group{ Name: input.Name, Description: input.Description, @@ -515,9 +520,9 @@ func (s *adminServiceImpl) CreateGroup(ctx context.Context, input *CreateGroupIn DailyLimitUSD: dailyLimit, WeeklyLimitUSD: weeklyLimit, MonthlyLimitUSD: monthlyLimit, - ImagePrice1K: input.ImagePrice1K, - ImagePrice2K: input.ImagePrice2K, - ImagePrice4K: input.ImagePrice4K, + ImagePrice1K: imagePrice1K, + ImagePrice2K: imagePrice2K, + ImagePrice4K: imagePrice4K, } if err := s.groupRepo.Create(ctx, group); err != nil { return nil, err @@ -533,6 +538,14 @@ func normalizeLimit(limit *float64) *float64 { return limit } +// normalizePrice 将负数转换为 nil(表示使用默认价格),0 保留(表示免费) +func normalizePrice(price *float64) *float64 { + if price == nil || *price < 0 { + return nil + } + return price +} + func (s *adminServiceImpl) UpdateGroup(ctx context.Context, id int64, input *UpdateGroupInput) (*Group, error) { group, err := s.groupRepo.GetByID(ctx, id) if err != nil { @@ -572,15 +585,15 @@ func (s *adminServiceImpl) UpdateGroup(ctx context.Context, id int64, input *Upd if input.MonthlyLimitUSD != nil { group.MonthlyLimitUSD = normalizeLimit(input.MonthlyLimitUSD) } - // 图片生成计费配置 + // 图片生成计费配置:负数表示清除(使用默认价格) if input.ImagePrice1K != nil { - group.ImagePrice1K = input.ImagePrice1K + group.ImagePrice1K = normalizePrice(input.ImagePrice1K) } if input.ImagePrice2K != nil { - group.ImagePrice2K = input.ImagePrice2K + group.ImagePrice2K = normalizePrice(input.ImagePrice2K) } if input.ImagePrice4K != nil { - group.ImagePrice4K = input.ImagePrice4K + group.ImagePrice4K = normalizePrice(input.ImagePrice4K) } if err := s.groupRepo.Update(ctx, group); err != nil { diff --git a/backend/internal/service/antigravity_gateway_service.go b/backend/internal/service/antigravity_gateway_service.go index d77ffaa6..f390b313 100644 --- a/backend/internal/service/antigravity_gateway_service.go +++ b/backend/internal/service/antigravity_gateway_service.go @@ -838,7 +838,7 @@ handleSuccess: // 判断是否为图片生成模型 imageCount := 0 if isImageGenerationModel(mappedModel) { - // 图片模型按次计费,默认 1 张图片 + // Gemini 图片生成 API 每次请求只生成一张图片(API 限制) imageCount = 1 } @@ -1192,8 +1192,17 @@ func (s *AntigravityGatewayService) extractImageSize(body []byte) string { } // isImageGenerationModel 判断模型是否为图片生成模型 +// 支持的模型:gemini-3-pro-image, gemini-3-pro-image-preview, gemini-2.5-flash-image 等 func isImageGenerationModel(model string) bool { modelLower := strings.ToLower(model) - return strings.Contains(modelLower, "gemini-3-pro-image") || - strings.Contains(modelLower, "gemini-2.5-flash-image") + // 移除 models/ 前缀 + modelLower = strings.TrimPrefix(modelLower, "models/") + + // 精确匹配或前缀匹配 + return modelLower == "gemini-3-pro-image" || + modelLower == "gemini-3-pro-image-preview" || + strings.HasPrefix(modelLower, "gemini-3-pro-image-") || + modelLower == "gemini-2.5-flash-image" || + modelLower == "gemini-2.5-flash-image-preview" || + strings.HasPrefix(modelLower, "gemini-2.5-flash-image-") } diff --git a/backend/internal/service/antigravity_image_test.go b/backend/internal/service/antigravity_image_test.go index 1579c790..7fd2f843 100644 --- a/backend/internal/service/antigravity_image_test.go +++ b/backend/internal/service/antigravity_image_test.go @@ -28,6 +28,9 @@ func TestIsImageGenerationModel_RegularModel(t *testing.T) { require.False(t, isImageGenerationModel("gpt-4o")) require.False(t, isImageGenerationModel("gemini-2.5-pro")) // 非图片模型 require.False(t, isImageGenerationModel("gemini-2.5-flash")) + // 验证不会误匹配包含关键词的自定义模型名 + require.False(t, isImageGenerationModel("my-gemini-3-pro-image-test")) + require.False(t, isImageGenerationModel("custom-gemini-2.5-flash-image-wrapper")) } // TestIsImageGenerationModel_CaseInsensitive 测试大小写不敏感