diff --git a/api/admin/v1/model.go b/api/admin/v1/model.go index ef3efdc..5fd9a07 100644 --- a/api/admin/v1/model.go +++ b/api/admin/v1/model.go @@ -21,6 +21,13 @@ type ModelListRes struct { PageSize int `json:"page_size"` } +// ModelChannelInfo 模型关联的渠道信息 +type ModelChannelInfo struct { + ChannelID int64 `json:"channel_id"` + ChannelName string `json:"channel_name"` + Type int `json:"type"` +} + // ModelItem 模型信息 type ModelItem struct { ID int64 `json:"id"` @@ -43,6 +50,8 @@ type ModelItem struct { InputPrice float64 `json:"input_price"` // $/1M tokens OutputPrice float64 `json:"output_price"` // $/1M tokens PerRequestPrice float64 `json:"per_request_price"` // $/request(按次计费模式) + // 可用渠道列表(来自 chn_abilities + chn_channels) + Channels []ModelChannelInfo `json:"channels"` } // ModelCreateReq 创建模型请求 diff --git a/internal/logic/admin/model.go b/internal/logic/admin/model.go index b101769..5c4ec11 100644 --- a/internal/logic/admin/model.go +++ b/internal/logic/admin/model.go @@ -137,6 +137,39 @@ func (s *sAdmin) ListModels(ctx context.Context, req *v1.ModelListReq) (*v1.Mode pricingMap[p.ModelId] = p } + // 批量查询渠道支持情况 + type channelAbilityRow struct { + ModelName string `json:"model_name"` + ChannelID int64 `json:"channel_id"` + ChannelName string `json:"channel_name"` + Type int `json:"type"` + } + modelIdStrs := make([]string, 0, len(models)) + for _, m := range models { + modelIdStrs = append(modelIdStrs, m.ModelId) + } + var channelRows []channelAbilityRow + if len(modelIdStrs) > 0 { + err = dao.ChnAbilities.Ctx(ctx). + LeftJoin("chn_channels", "chn_channels.id = chn_abilities.channel_id"). + Fields("chn_abilities.model_name, chn_abilities.channel_id, chn_channels.name AS channel_name, chn_channels.type"). + Where("chn_abilities.model_name IN (?)", modelIdStrs). + Where("chn_abilities.enabled", true). + Where("chn_channels.status", "active"). + Scan(&channelRows) + if err = common.IgnoreScanNoRows(err); err != nil { + return nil, err + } + } + channelMap := make(map[string][]v1.ModelChannelInfo, len(channelRows)) + for _, r := range channelRows { + channelMap[r.ModelName] = append(channelMap[r.ModelName], v1.ModelChannelInfo{ + ChannelID: r.ChannelID, + ChannelName: r.ChannelName, + Type: r.Type, + }) + } + list := make([]v1.ModelItem, 0, len(models)) for _, m := range models { item := v1.ModelItem{ @@ -171,6 +204,10 @@ func (s *sAdmin) ListModels(ctx context.Context, req *v1.ModelListReq) (*v1.Mode item.PerRequestPrice = *p.PerRequestPrice } } + // 填充可用渠道 + if chs, ok := channelMap[m.ModelId]; ok { + item.Channels = chs + } list = append(list, item) } diff --git a/web/admin/src/views/ai/ChannelsPage.vue b/web/admin/src/views/ai/ChannelsPage.vue index 6fadfe3..1ac161b 100644 --- a/web/admin/src/views/ai/ChannelsPage.vue +++ b/web/admin/src/views/ai/ChannelsPage.vue @@ -297,6 +297,8 @@ async function handleOAuthExchange() { finally { oauthLoading.value = false } } +const showSchedulingGuide = ref(false) + const { exporting, exportFile } = useExport({ url: '/admin/channels/export', getFilters: () => ({ @@ -323,6 +325,29 @@ const { exporting, exportFile } = useExport({ + +
请求到达时,调度器按以下规则从启用渠道中选择一个处理:
+两个同供应商渠道分别设 优先级=100(主力)和 优先级=50(备用),调度器始终优先使用主力渠道,仅在主力不可用时切换到备用。
同一优先级内两个渠道分别设 权重=70 和 权重=30,则流量按 7:3 比例分发。