package service import ( "strings" "time" ) type Group struct { ID int64 Name string Description string Platform string RateMultiplier float64 IsExclusive bool Status string Hydrated bool // indicates the group was loaded from a trusted repository source SubscriptionType string DailyLimitUSD *float64 WeeklyLimitUSD *float64 MonthlyLimitUSD *float64 DefaultValidityDays int // 图片生成计费配置(antigravity 和 gemini 平台使用) ImagePrice1K *float64 ImagePrice2K *float64 ImagePrice4K *float64 // Claude Code 客户端限制 ClaudeCodeOnly bool FallbackGroupID *int64 // 无效请求兜底分组(仅 anthropic 平台使用) FallbackGroupIDOnInvalidRequest *int64 // 模型路由配置 // key: 模型匹配模式(支持 * 通配符,如 "claude-opus-*") // value: 优先账号 ID 列表 ModelRouting map[string][]int64 ModelRoutingEnabled bool // MCP XML 协议注入开关(仅 antigravity 平台使用) MCPXMLInject bool // 支持的模型系列(仅 antigravity 平台使用) // 可选值: claude, gemini_text, gemini_image SupportedModelScopes []string // 分组排序 SortOrder int CreatedAt time.Time UpdatedAt time.Time AccountGroups []AccountGroup AccountCount int64 } func (g *Group) IsActive() bool { return g.Status == StatusActive } func (g *Group) IsSubscriptionType() bool { return g.SubscriptionType == SubscriptionTypeSubscription } func (g *Group) IsFreeSubscription() bool { return g.IsSubscriptionType() && g.RateMultiplier == 0 } func (g *Group) HasDailyLimit() bool { return g.DailyLimitUSD != nil && *g.DailyLimitUSD > 0 } func (g *Group) HasWeeklyLimit() bool { return g.WeeklyLimitUSD != nil && *g.WeeklyLimitUSD > 0 } func (g *Group) HasMonthlyLimit() bool { return g.MonthlyLimitUSD != nil && *g.MonthlyLimitUSD > 0 } // GetImagePrice 根据 image_size 返回对应的图片生成价格 // 如果分组未配置价格,返回 nil(调用方应使用默认值) func (g *Group) GetImagePrice(imageSize string) *float64 { switch imageSize { case "1K": return g.ImagePrice1K case "2K": return g.ImagePrice2K case "4K": return g.ImagePrice4K default: // 未知尺寸默认按 2K 计费 return g.ImagePrice2K } } // IsGroupContextValid reports whether a group from context has the fields required for routing decisions. func IsGroupContextValid(group *Group) bool { if group == nil { return false } if group.ID <= 0 { return false } if !group.Hydrated { return false } if group.Platform == "" || group.Status == "" { return false } return true } // GetRoutingAccountIDs 根据请求模型获取路由账号 ID 列表 // 返回匹配的优先账号 ID 列表,如果没有匹配规则则返回 nil func (g *Group) GetRoutingAccountIDs(requestedModel string) []int64 { if !g.ModelRoutingEnabled || len(g.ModelRouting) == 0 || requestedModel == "" { return nil } // 1. 精确匹配优先 if accountIDs, ok := g.ModelRouting[requestedModel]; ok && len(accountIDs) > 0 { return accountIDs } // 2. 通配符匹配(前缀匹配) for pattern, accountIDs := range g.ModelRouting { if matchModelPattern(pattern, requestedModel) && len(accountIDs) > 0 { return accountIDs } } return nil } // matchModelPattern 检查模型是否匹配模式 // 支持 * 通配符,如 "claude-opus-*" 匹配 "claude-opus-4-20250514" func matchModelPattern(pattern, model string) bool { if pattern == model { return true } // 处理 * 通配符(仅支持末尾通配符) if strings.HasSuffix(pattern, "*") { prefix := strings.TrimSuffix(pattern, "*") return strings.HasPrefix(model, prefix) } return false }