From 91bdcf8994877acf5b271aa5385598cef6382054 Mon Sep 17 00:00:00 2001 From: erio Date: Tue, 31 Mar 2026 01:34:16 +0800 Subject: [PATCH] =?UTF-8?q?fix(channel):=20=E6=A8=A1=E5=9E=8B=E9=99=90?= =?UTF-8?q?=E5=88=B6=E7=94=A8=E6=98=A0=E5=B0=84=E5=90=8E=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=20+=20=E5=B9=B3=E5=8F=B0=E5=BC=80=E5=85=B3?= =?UTF-8?q?=E4=BF=9D=E7=95=99=E9=85=8D=E7=BD=AE=E4=B8=8D=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OpenAI 网关三处 IsModelRestricted 改用 channelMapping.MappedModel - 前端平台勾选改为 enabled 开关,取消勾选不清空配置数据 - formToAPI/校验只处理 enabled 的平台 --- backend/internal/handler/gateway_handler.go | 4 +- .../handler/openai_gateway_handler.go | 14 +++--- frontend/src/views/admin/ChannelsView.vue | 43 ++++++++----------- 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/backend/internal/handler/gateway_handler.go b/backend/internal/handler/gateway_handler.go index b46c86a3..dff922d1 100644 --- a/backend/internal/handler/gateway_handler.go +++ b/backend/internal/handler/gateway_handler.go @@ -164,9 +164,9 @@ func (h *GatewayHandler) Messages(c *gin.Context) { channelMapping = h.gatewayService.ResolveChannelMapping(c.Request.Context(), *apiKey.GroupID, reqModel) } - // 渠道模型限制检查:使用原始请求模型名,因为定价列表中注册的是用户请求的模型名 + // 渠道模型限制检查:先映射再判断,映射后的模型在定价列表中即放行 if apiKey.GroupID != nil { - if h.gatewayService.IsModelRestricted(c.Request.Context(), *apiKey.GroupID, reqModel) { + if h.gatewayService.IsModelRestricted(c.Request.Context(), *apiKey.GroupID, channelMapping.MappedModel) { h.errorResponse(c, http.StatusServiceUnavailable, "api_error", "No available accounts") return } diff --git a/backend/internal/handler/openai_gateway_handler.go b/backend/internal/handler/openai_gateway_handler.go index 7f68a56b..c8b90e14 100644 --- a/backend/internal/handler/openai_gateway_handler.go +++ b/backend/internal/handler/openai_gateway_handler.go @@ -191,10 +191,9 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) { channelMapping = h.gatewayService.ResolveChannelMapping(c.Request.Context(), *apiKey.GroupID, reqModel) } - // 渠道模型限制检查 + // 渠道模型限制检查:先映射再判断 if apiKey.GroupID != nil { - if h.gatewayService.IsModelRestricted(c.Request.Context(), *apiKey.GroupID, reqModel) { - h.errorResponse(c, http.StatusServiceUnavailable, "api_error", "No available accounts") + if h.gatewayService.IsModelRestricted(c.Request.Context(), *apiKey.GroupID, channelMapping.MappedModel) { return } } @@ -584,10 +583,9 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) { channelMappingMsg = h.gatewayService.ResolveChannelMapping(c.Request.Context(), *apiKey.GroupID, reqModel) } - // 渠道模型限制检查 + // 渠道模型限制检查:先映射再判断 if apiKey.GroupID != nil { - if h.gatewayService.IsModelRestricted(c.Request.Context(), *apiKey.GroupID, reqModel) { - h.anthropicErrorResponse(c, http.StatusServiceUnavailable, "api_error", "No available accounts") + if h.gatewayService.IsModelRestricted(c.Request.Context(), *apiKey.GroupID, channelMappingMsg.MappedModel) { return } } @@ -1165,9 +1163,9 @@ func (h *OpenAIGatewayHandler) ResponsesWebSocket(c *gin.Context) { channelMappingWS = h.gatewayService.ResolveChannelMapping(ctx, *apiKey.GroupID, reqModel) } - // 渠道模型限制检查 + // 渠道模型限制检查:先映射再判断 if apiKey.GroupID != nil { - if h.gatewayService.IsModelRestricted(ctx, *apiKey.GroupID, reqModel) { + if h.gatewayService.IsModelRestricted(ctx, *apiKey.GroupID, channelMappingWS.MappedModel) { closeOpenAIClientWS(wsConn, coderws.StatusPolicyViolation, "model not allowed") return } diff --git a/frontend/src/views/admin/ChannelsView.vue b/frontend/src/views/admin/ChannelsView.vue index c26a6fcf..6c57b269 100644 --- a/frontend/src/views/admin/ChannelsView.vue +++ b/frontend/src/views/admin/ChannelsView.vue @@ -155,9 +155,9 @@ > {{ t('admin.channels.form.basicSettings', '基础设置') }} - + @@ -261,7 +255,7 @@
@@ -449,6 +443,7 @@ const appStore = useAppStore() // ── Platform Section type ── interface PlatformSection { platform: GroupPlatform + enabled: boolean collapsed: boolean group_ids: number[] model_mapping: Record @@ -549,11 +544,12 @@ function formatDate(value: string): string { } // ── Platform section helpers ── -const activePlatforms = computed(() => form.platforms.map(s => s.platform)) +const activePlatforms = computed(() => form.platforms.filter(s => s.enabled).map(s => s.platform)) function addPlatformSection(platform: GroupPlatform) { form.platforms.push({ platform, + enabled: true, collapsed: false, group_ids: [], model_mapping: {}, @@ -562,22 +558,17 @@ function addPlatformSection(platform: GroupPlatform) { } function togglePlatform(platform: GroupPlatform) { - const idx = form.platforms.findIndex(s => s.platform === platform) - if (idx >= 0) { - removePlatformSection(idx) + const section = form.platforms.find(s => s.platform === platform) + if (section) { + section.enabled = !section.enabled + if (!section.enabled && activeTab.value === platform) { + activeTab.value = 'basic' + } } else { addPlatformSection(platform) } } -function removePlatformSection(idx: number) { - const removed = form.platforms[idx] - form.platforms.splice(idx, 1) - if (activeTab.value === removed.platform) { - activeTab.value = 'basic' - } -} - function getGroupsForPlatform(platform: GroupPlatform): AdminGroup[] { return allGroups.value.filter(g => g.platform === platform) } @@ -682,6 +673,7 @@ function formToAPI(): { group_ids: number[], model_pricing: ChannelModelPricing[ const model_mapping: Record> = {} for (const section of form.platforms) { + if (!section.enabled) continue group_ids.push(...section.group_ids) // Model mapping per platform @@ -755,6 +747,7 @@ function apiToForm(channel: Channel): PlatformSection[] { sections.push({ platform, + enabled: true, collapsed: false, group_ids: groupIds, model_mapping: { ...mapping }, @@ -868,16 +861,16 @@ async function handleSubmit() { return } - // Check duplicate models across all platform sections - const allModels = form.platforms.flatMap(s => s.model_pricing.flatMap(e => e.models.map(m => m.toLowerCase()))) + // Check duplicate models across all enabled platform sections + const allModels = form.platforms.filter(s => s.enabled).flatMap(s => s.model_pricing.flatMap(e => e.models.map(m => m.toLowerCase()))) const duplicates = allModels.filter((m, i) => allModels.indexOf(m) !== i) if (duplicates.length > 0) { appStore.showError(t('admin.channels.duplicateModels', `模型 "${duplicates[0]}" 在多个定价条目中重复`)) return } - // 校验 per_request/image 模式必须有价格 - for (const section of form.platforms) { + // 校验 per_request/image 模式必须有价格 (只校验启用的平台) + for (const section of form.platforms.filter(s => s.enabled)) { for (const entry of section.model_pricing) { if (entry.models.length === 0) continue if ((entry.billing_mode === 'per_request' || entry.billing_mode === 'image') &&