|
|
@@ -2,6 +2,7 @@ package controller
|
|
|
|
|
|
import (
|
|
|
"encoding/json"
|
|
|
+ "sort"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
|
|
|
@@ -21,10 +22,8 @@ func GetAllModelsMeta(c *gin.Context) {
|
|
|
common.ApiError(c, err)
|
|
|
return
|
|
|
}
|
|
|
- // 填充附加字段
|
|
|
- for _, m := range modelsMeta {
|
|
|
- fillModelExtra(m)
|
|
|
- }
|
|
|
+ // 批量填充附加字段,提升列表接口性能
|
|
|
+ enrichModels(modelsMeta)
|
|
|
var total int64
|
|
|
model.DB.Model(&model.Model{}).Count(&total)
|
|
|
|
|
|
@@ -54,9 +53,8 @@ func SearchModelsMeta(c *gin.Context) {
|
|
|
common.ApiError(c, err)
|
|
|
return
|
|
|
}
|
|
|
- for _, m := range modelsMeta {
|
|
|
- fillModelExtra(m)
|
|
|
- }
|
|
|
+ // 批量填充附加字段,提升列表接口性能
|
|
|
+ enrichModels(modelsMeta)
|
|
|
pageInfo.SetTotal(int(total))
|
|
|
pageInfo.SetItems(modelsMeta)
|
|
|
common.ApiSuccess(c, pageInfo)
|
|
|
@@ -75,7 +73,7 @@ func GetModelMeta(c *gin.Context) {
|
|
|
common.ApiError(c, err)
|
|
|
return
|
|
|
}
|
|
|
- fillModelExtra(&m)
|
|
|
+ enrichModels([]*model.Model{&m})
|
|
|
common.ApiSuccess(c, &m)
|
|
|
}
|
|
|
|
|
|
@@ -162,104 +160,157 @@ func DeleteModelMeta(c *gin.Context) {
|
|
|
common.ApiSuccess(c, nil)
|
|
|
}
|
|
|
|
|
|
-// 辅助函数:填充 Endpoints 和 BoundChannels 和 EnableGroups
|
|
|
-func fillModelExtra(m *model.Model) {
|
|
|
- // 若为精确匹配,保持原有逻辑
|
|
|
- if m.NameRule == model.NameRuleExact {
|
|
|
- if m.Endpoints == "" {
|
|
|
- eps := model.GetModelSupportEndpointTypes(m.ModelName)
|
|
|
- if b, err := json.Marshal(eps); err == nil {
|
|
|
- m.Endpoints = string(b)
|
|
|
- }
|
|
|
+// enrichModels 批量填充附加信息:端点、渠道、分组、计费类型,避免 N+1 查询
|
|
|
+func enrichModels(models []*model.Model) {
|
|
|
+ if len(models) == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1) 拆分精确与规则匹配
|
|
|
+ exactNames := make([]string, 0)
|
|
|
+ exactIdx := make(map[string][]int) // modelName -> indices in models
|
|
|
+ ruleIndices := make([]int, 0)
|
|
|
+ for i, m := range models {
|
|
|
+ if m == nil {
|
|
|
+ continue
|
|
|
}
|
|
|
- if channels, err := model.GetBoundChannels(m.ModelName); err == nil {
|
|
|
- m.BoundChannels = channels
|
|
|
+ if m.NameRule == model.NameRuleExact {
|
|
|
+ exactNames = append(exactNames, m.ModelName)
|
|
|
+ exactIdx[m.ModelName] = append(exactIdx[m.ModelName], i)
|
|
|
+ } else {
|
|
|
+ ruleIndices = append(ruleIndices, i)
|
|
|
}
|
|
|
- m.EnableGroups = model.GetModelEnableGroups(m.ModelName)
|
|
|
- m.QuotaType = model.GetModelQuotaType(m.ModelName)
|
|
|
- return
|
|
|
}
|
|
|
|
|
|
- // 非精确匹配:计算并集
|
|
|
- pricings := model.GetPricing()
|
|
|
+ // 2) 批量查询精确模型的绑定渠道
|
|
|
+ channelsByModel, _ := model.GetBoundChannelsByModelsMap(exactNames)
|
|
|
+
|
|
|
+ // 3) 精确模型:端点从缓存、渠道批量映射、分组/计费类型从缓存
|
|
|
+ for name, indices := range exactIdx {
|
|
|
+ chs := channelsByModel[name]
|
|
|
+ for _, idx := range indices {
|
|
|
+ mm := models[idx]
|
|
|
+ if mm.Endpoints == "" {
|
|
|
+ eps := model.GetModelSupportEndpointTypes(mm.ModelName)
|
|
|
+ if b, err := json.Marshal(eps); err == nil {
|
|
|
+ mm.Endpoints = string(b)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mm.BoundChannels = chs
|
|
|
+ mm.EnableGroups = model.GetModelEnableGroups(mm.ModelName)
|
|
|
+ mm.QuotaTypes = model.GetModelQuotaTypes(mm.ModelName)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 匹配到的模型名称集合
|
|
|
- matchedNames := make([]string, 0)
|
|
|
+ if len(ruleIndices) == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
- // 端点去重集合
|
|
|
- endpointSet := make(map[constant.EndpointType]struct{})
|
|
|
+ // 4) 一次性读取定价缓存,内存匹配所有规则模型
|
|
|
+ pricings := model.GetPricing()
|
|
|
|
|
|
- // 已绑定渠道去重集合
|
|
|
- channelSet := make(map[string]model.BoundChannel)
|
|
|
- // 分组去重集合
|
|
|
- groupSet := make(map[string]struct{})
|
|
|
- // 计费类型(若有任意模型为 1,则返回 1)
|
|
|
- quotaTypeSet := make(map[int]struct{})
|
|
|
+ // 为全部规则模型收集匹配名集合、端点并集、分组并集、配额集合
|
|
|
+ matchedNamesByIdx := make(map[int][]string)
|
|
|
+ endpointSetByIdx := make(map[int]map[constant.EndpointType]struct{})
|
|
|
+ groupSetByIdx := make(map[int]map[string]struct{})
|
|
|
+ quotaSetByIdx := make(map[int]map[int]struct{})
|
|
|
|
|
|
for _, p := range pricings {
|
|
|
- var matched bool
|
|
|
- switch m.NameRule {
|
|
|
- case model.NameRulePrefix:
|
|
|
- matched = strings.HasPrefix(p.ModelName, m.ModelName)
|
|
|
- case model.NameRuleSuffix:
|
|
|
- matched = strings.HasSuffix(p.ModelName, m.ModelName)
|
|
|
- case model.NameRuleContains:
|
|
|
- matched = strings.Contains(p.ModelName, m.ModelName)
|
|
|
- }
|
|
|
- if !matched {
|
|
|
- continue
|
|
|
- }
|
|
|
+ for _, idx := range ruleIndices {
|
|
|
+ mm := models[idx]
|
|
|
+ var matched bool
|
|
|
+ switch mm.NameRule {
|
|
|
+ case model.NameRulePrefix:
|
|
|
+ matched = strings.HasPrefix(p.ModelName, mm.ModelName)
|
|
|
+ case model.NameRuleSuffix:
|
|
|
+ matched = strings.HasSuffix(p.ModelName, mm.ModelName)
|
|
|
+ case model.NameRuleContains:
|
|
|
+ matched = strings.Contains(p.ModelName, mm.ModelName)
|
|
|
+ }
|
|
|
+ if !matched {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ matchedNamesByIdx[idx] = append(matchedNamesByIdx[idx], p.ModelName)
|
|
|
|
|
|
- // 记录匹配到的模型名称
|
|
|
- matchedNames = append(matchedNames, p.ModelName)
|
|
|
+ es := endpointSetByIdx[idx]
|
|
|
+ if es == nil {
|
|
|
+ es = make(map[constant.EndpointType]struct{})
|
|
|
+ endpointSetByIdx[idx] = es
|
|
|
+ }
|
|
|
+ for _, et := range p.SupportedEndpointTypes {
|
|
|
+ es[et] = struct{}{}
|
|
|
+ }
|
|
|
|
|
|
- // 收集端点
|
|
|
- for _, et := range p.SupportedEndpointTypes {
|
|
|
- endpointSet[et] = struct{}{}
|
|
|
- }
|
|
|
+ gs := groupSetByIdx[idx]
|
|
|
+ if gs == nil {
|
|
|
+ gs = make(map[string]struct{})
|
|
|
+ groupSetByIdx[idx] = gs
|
|
|
+ }
|
|
|
+ for _, g := range p.EnableGroup {
|
|
|
+ gs[g] = struct{}{}
|
|
|
+ }
|
|
|
|
|
|
- // 收集分组
|
|
|
- for _, g := range p.EnableGroup {
|
|
|
- groupSet[g] = struct{}{}
|
|
|
+ qs := quotaSetByIdx[idx]
|
|
|
+ if qs == nil {
|
|
|
+ qs = make(map[int]struct{})
|
|
|
+ quotaSetByIdx[idx] = qs
|
|
|
+ }
|
|
|
+ qs[p.QuotaType] = struct{}{}
|
|
|
}
|
|
|
-
|
|
|
- // 收集计费类型
|
|
|
- quotaTypeSet[p.QuotaType] = struct{}{}
|
|
|
}
|
|
|
|
|
|
- // 序列化端点
|
|
|
- if len(endpointSet) > 0 && m.Endpoints == "" {
|
|
|
- eps := make([]constant.EndpointType, 0, len(endpointSet))
|
|
|
- for et := range endpointSet {
|
|
|
- eps = append(eps, et)
|
|
|
- }
|
|
|
- if b, err := json.Marshal(eps); err == nil {
|
|
|
- m.Endpoints = string(b)
|
|
|
+ // 5) 汇总所有匹配到的模型名称,批量查询一次渠道
|
|
|
+ allMatchedSet := make(map[string]struct{})
|
|
|
+ for _, names := range matchedNamesByIdx {
|
|
|
+ for _, n := range names {
|
|
|
+ allMatchedSet[n] = struct{}{}
|
|
|
}
|
|
|
}
|
|
|
+ allMatched := make([]string, 0, len(allMatchedSet))
|
|
|
+ for n := range allMatchedSet {
|
|
|
+ allMatched = append(allMatched, n)
|
|
|
+ }
|
|
|
+ matchedChannelsByModel, _ := model.GetBoundChannelsByModelsMap(allMatched)
|
|
|
+
|
|
|
+ // 6) 回填每个规则模型的并集信息
|
|
|
+ for _, idx := range ruleIndices {
|
|
|
+ mm := models[idx]
|
|
|
|
|
|
- // 序列化分组
|
|
|
- if len(groupSet) > 0 {
|
|
|
- groups := make([]string, 0, len(groupSet))
|
|
|
- for g := range groupSet {
|
|
|
- groups = append(groups, g)
|
|
|
+ // 端点并集 -> 序列化
|
|
|
+ if es, ok := endpointSetByIdx[idx]; ok && mm.Endpoints == "" {
|
|
|
+ eps := make([]constant.EndpointType, 0, len(es))
|
|
|
+ for et := range es {
|
|
|
+ eps = append(eps, et)
|
|
|
+ }
|
|
|
+ if b, err := json.Marshal(eps); err == nil {
|
|
|
+ mm.Endpoints = string(b)
|
|
|
+ }
|
|
|
}
|
|
|
- m.EnableGroups = groups
|
|
|
- }
|
|
|
|
|
|
- // 确定计费类型:仅当所有匹配模型计费类型一致时才返回该类型,否则返回 -1 表示未知/不确定
|
|
|
- if len(quotaTypeSet) == 1 {
|
|
|
- for k := range quotaTypeSet {
|
|
|
- m.QuotaType = k
|
|
|
+ // 分组并集
|
|
|
+ if gs, ok := groupSetByIdx[idx]; ok {
|
|
|
+ groups := make([]string, 0, len(gs))
|
|
|
+ for g := range gs {
|
|
|
+ groups = append(groups, g)
|
|
|
+ }
|
|
|
+ mm.EnableGroups = groups
|
|
|
}
|
|
|
- } else {
|
|
|
- m.QuotaType = -1
|
|
|
- }
|
|
|
|
|
|
- // 批量查询并序列化渠道
|
|
|
- if len(matchedNames) > 0 {
|
|
|
- if channels, err := model.GetBoundChannelsForModels(matchedNames); err == nil {
|
|
|
- for _, ch := range channels {
|
|
|
+ // 配额类型集合(保持去重并排序)
|
|
|
+ if qs, ok := quotaSetByIdx[idx]; ok {
|
|
|
+ arr := make([]int, 0, len(qs))
|
|
|
+ for k := range qs {
|
|
|
+ arr = append(arr, k)
|
|
|
+ }
|
|
|
+ sort.Ints(arr)
|
|
|
+ mm.QuotaTypes = arr
|
|
|
+ }
|
|
|
+
|
|
|
+ // 渠道并集
|
|
|
+ names := matchedNamesByIdx[idx]
|
|
|
+ channelSet := make(map[string]model.BoundChannel)
|
|
|
+ for _, n := range names {
|
|
|
+ for _, ch := range matchedChannelsByModel[n] {
|
|
|
key := ch.Name + "_" + strconv.Itoa(ch.Type)
|
|
|
channelSet[key] = ch
|
|
|
}
|
|
|
@@ -269,11 +320,11 @@ func fillModelExtra(m *model.Model) {
|
|
|
for _, ch := range channelSet {
|
|
|
chs = append(chs, ch)
|
|
|
}
|
|
|
- m.BoundChannels = chs
|
|
|
+ mm.BoundChannels = chs
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // 设置匹配信息
|
|
|
- m.MatchedModels = matchedNames
|
|
|
- m.MatchedCount = len(matchedNames)
|
|
|
+ // 匹配信息
|
|
|
+ mm.MatchedModels = names
|
|
|
+ mm.MatchedCount = len(names)
|
|
|
+ }
|
|
|
}
|