Files
sub2api/backend/internal/model/error_passthrough_rule.go
T
SavitarCandClaude Opus 5.5 3b89cc08dd refactor(platform): 后端平台列表改由平台清单派生
新增 domain/platforms.go 作为具体平台的单一数据来源:每个平台一条登记,
写明展示名、所走网关(anthropic / openai / gemini / antigravity)、是否国产
单一厂商、定价目录 provider 名,顺序即展示顺序。

以下列表与判断改为查平台清单:
- "全部具体平台":用户平台额度白名单、错误透传规则平台列表、组合路由目标
  校验、上游计费探测资格、调度快照遍历、组合分组的定价 / 模型映射回退、
  组合分组默认模型列表;
- "OpenAI 网关族":IsOpenAICompatible、NormalizeOpenAICompatiblePlatform、
  Responses 兼容路由判断、count_tokens 分发、组合分组的 OpenAI 文本目标;
- 国产厂商判断、渠道定价同步的 provider 映射;
- 多协议 API Key 供应商相关的请求头覆盖资格、凭证测试、OpenAI 基址回退改用
  IsMultiProtocolAPIKeyProvider;
- 分组平台与组合路由目标的请求校验改为自定义校验标签 group_platform /
  concrete_platform,拒绝规则不变(大小写、首尾空格、别名一律拒绝)。

组合分组的逐平台回退是先命中者生效,CompositePrecedencePlatformIDs 保持
原有顺序 anthropic、gemini、openai,其余按登记顺序追加。依赖具体实现的
能力(OAuth、渠道监控、调度阈值、余额 / 用量窗口、Codex 模型目录等)仍在
各自代码中显式列出。

等价性测试把改动前的每个列表与 switch 原样保留为基准,逐一比较派生结果
(含顺序),谓词覆盖全部平台、composite 与非法值 × 全部账号类型。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:10:33 +08:00

87 lines
3.5 KiB
Go

// Package model 定义服务层使用的数据模型。
package model
import (
"time"
"github.com/Wei-Shaw/sub2api/internal/domain"
)
// ErrorPassthroughRule 全局错误透传规则
// 用于控制上游错误如何返回给客户端
type ErrorPassthroughRule struct {
ID int64 `json:"id"`
Name string `json:"name"` // 规则名称
Enabled bool `json:"enabled"` // 是否启用
Priority int `json:"priority"` // 优先级(数字越小优先级越高)
ErrorCodes []int `json:"error_codes"` // 匹配的错误码列表(OR关系)
Keywords []string `json:"keywords"` // 匹配的关键词列表(OR关系)
MatchMode string `json:"match_mode"` // "any"(任一条件) 或 "all"(所有条件)
Platforms []string `json:"platforms"` // 适用平台列表
PassthroughCode bool `json:"passthrough_code"` // 是否透传原始状态码
ResponseCode *int `json:"response_code"` // 自定义状态码(passthrough_code=false 时使用)
PassthroughBody bool `json:"passthrough_body"` // 是否透传原始错误信息
CustomMessage *string `json:"custom_message"` // 自定义错误信息(passthrough_body=false 时使用)
SkipMonitoring bool `json:"skip_monitoring"` // 是否跳过运维监控记录
Description *string `json:"description"` // 规则描述
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// MatchModeAny 表示任一条件匹配即可
const MatchModeAny = "any"
// MatchModeAll 表示所有条件都必须匹配
const MatchModeAll = "all"
// 支持的平台常量
const (
PlatformAnthropic = domain.PlatformAnthropic
PlatformOpenAI = domain.PlatformOpenAI
PlatformGemini = domain.PlatformGemini
PlatformAntigravity = domain.PlatformAntigravity
PlatformGrok = domain.PlatformGrok
PlatformKimi = domain.PlatformKimi
PlatformZhipu = domain.PlatformZhipu
PlatformDeepseek = domain.PlatformDeepseek
PlatformMiniMax = domain.PlatformMiniMax
PlatformOpenCodeGo = domain.PlatformOpenCodeGo
PlatformTypeSafe = domain.PlatformTypeSafe
)
// AllPlatforms 返回所有支持的平台列表(平台清单,按展示顺序)。
func AllPlatforms() []string {
return domain.ConcretePlatformIDs()
}
// Validate 验证规则配置的有效性
func (r *ErrorPassthroughRule) Validate() error {
if r.Name == "" {
return &ValidationError{Field: "name", Message: "name is required"}
}
if r.MatchMode != MatchModeAny && r.MatchMode != MatchModeAll {
return &ValidationError{Field: "match_mode", Message: "match_mode must be 'any' or 'all'"}
}
// 至少需要配置一个匹配条件(错误码或关键词)
if len(r.ErrorCodes) == 0 && len(r.Keywords) == 0 {
return &ValidationError{Field: "conditions", Message: "at least one error_code or keyword is required"}
}
if !r.PassthroughCode && (r.ResponseCode == nil || *r.ResponseCode <= 0) {
return &ValidationError{Field: "response_code", Message: "response_code is required when passthrough_code is false"}
}
if !r.PassthroughBody && (r.CustomMessage == nil || *r.CustomMessage == "") {
return &ValidationError{Field: "custom_message", Message: "custom_message is required when passthrough_body is false"}
}
return nil
}
// ValidationError 表示验证错误
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return e.Field + ": " + e.Message
}