From 615e6901ea41530c5439679b57bf291e31948e95 Mon Sep 17 00:00:00 2001 From: Randark Date: Tue, 18 Aug 2026 03:50:01 +0000 Subject: [PATCH] feat(channel-monitor): add quota mode schema (migration 226 + ent) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 迁移 226:provider CHECK 扩到 8 平台(monitors + request_templates)、 channel_monitors 加 check_mode/account_id(FK ON DELETE SET NULL)、 channel_monitor_histories 加 quota JSONB、插入公开设置 channel_monitor_show_quota(默认 false) - ent schema 同步:provider 枚举 +4、endpoint 去 NotEmpty(quota 模式存空串)、 新增 check_mode/account_id 字段、history 加 quota JSON 快照 - domain 新增归一化配额快照类型 MonitorQuotaSnapshot/Tier/Balance (放 domain 是因为 ent schema 需要引用,service 会造成 import cycle) --- backend/ent/channelmonitor.go | 31 ++- backend/ent/channelmonitor/channelmonitor.go | 34 ++- backend/ent/channelmonitor/where.go | 125 +++++++++ backend/ent/channelmonitor_create.go | 168 +++++++++++++ backend/ent/channelmonitor_update.go | 116 +++++++++ backend/ent/channelmonitorhistory.go | 17 ++ .../channelmonitorhistory.go | 3 + backend/ent/channelmonitorhistory/where.go | 10 + backend/ent/channelmonitorhistory_create.go | 71 ++++++ backend/ent/channelmonitorhistory_update.go | 37 +++ .../channelmonitorrequesttemplate.go | 14 +- backend/ent/group.go | 2 +- backend/ent/migrate/schema.go | 28 ++- backend/ent/mutation.go | 238 +++++++++++++++++- backend/ent/runtime/runtime.go | 46 ++-- backend/ent/schema/channel_monitor.go | 24 +- backend/ent/schema/channel_monitor_history.go | 6 + .../channel_monitor_request_template.go | 3 +- .../internal/domain/channel_monitor_quota.go | 55 ++++ .../226_channel_monitor_quota_mode.sql | 78 ++++++ 20 files changed, 1049 insertions(+), 57 deletions(-) create mode 100644 backend/internal/domain/channel_monitor_quota.go create mode 100644 backend/migrations/226_channel_monitor_quota_mode.sql diff --git a/backend/ent/channelmonitor.go b/backend/ent/channelmonitor.go index e5697863f0..e74b6528cc 100644 --- a/backend/ent/channelmonitor.go +++ b/backend/ent/channelmonitor.go @@ -27,9 +27,13 @@ type ChannelMonitor struct { Name string `json:"name,omitempty"` // Provider holds the value of the "provider" field. Provider channelmonitor.Provider `json:"provider,omitempty"` + // probe = LLM probe (default); quota = account usage only; quota_probe = both + CheckMode string `json:"check_mode,omitempty"` + // AccountID holds the value of the "account_id" field. + AccountID *int64 `json:"account_id,omitempty"` // OpenAI request protocol: chat_completions or responses; non-OpenAI uses chat_completions APIMode string `json:"api_mode,omitempty"` - // Provider base origin, e.g. https://api.openai.com + // Provider base origin, e.g. https://api.openai.com; empty for quota-only monitors Endpoint string `json:"endpoint,omitempty"` // AES-256-GCM encrypted API key APIKeyEncrypted string `json:"-"` @@ -114,9 +118,9 @@ func (*ChannelMonitor) scanValues(columns []string) ([]any, error) { values[i] = new([]byte) case channelmonitor.FieldEnabled: values[i] = new(sql.NullBool) - case channelmonitor.FieldID, channelmonitor.FieldIntervalSeconds, channelmonitor.FieldJitterSeconds, channelmonitor.FieldCreatedBy, channelmonitor.FieldTemplateID: + case channelmonitor.FieldID, channelmonitor.FieldAccountID, channelmonitor.FieldIntervalSeconds, channelmonitor.FieldJitterSeconds, channelmonitor.FieldCreatedBy, channelmonitor.FieldTemplateID: values[i] = new(sql.NullInt64) - case channelmonitor.FieldName, channelmonitor.FieldProvider, channelmonitor.FieldAPIMode, channelmonitor.FieldEndpoint, channelmonitor.FieldAPIKeyEncrypted, channelmonitor.FieldPrimaryModel, channelmonitor.FieldGroupName, channelmonitor.FieldBodyOverrideMode: + case channelmonitor.FieldName, channelmonitor.FieldProvider, channelmonitor.FieldCheckMode, channelmonitor.FieldAPIMode, channelmonitor.FieldEndpoint, channelmonitor.FieldAPIKeyEncrypted, channelmonitor.FieldPrimaryModel, channelmonitor.FieldGroupName, channelmonitor.FieldBodyOverrideMode: values[i] = new(sql.NullString) case channelmonitor.FieldCreatedAt, channelmonitor.FieldUpdatedAt, channelmonitor.FieldLastCheckedAt: values[i] = new(sql.NullTime) @@ -165,6 +169,19 @@ func (_m *ChannelMonitor) assignValues(columns []string, values []any) error { } else if value.Valid { _m.Provider = channelmonitor.Provider(value.String) } + case channelmonitor.FieldCheckMode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field check_mode", values[i]) + } else if value.Valid { + _m.CheckMode = value.String + } + case channelmonitor.FieldAccountID: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field account_id", values[i]) + } else if value.Valid { + _m.AccountID = new(int64) + *_m.AccountID = value.Int64 + } case channelmonitor.FieldAPIMode: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field api_mode", values[i]) @@ -326,6 +343,14 @@ func (_m *ChannelMonitor) String() string { builder.WriteString("provider=") builder.WriteString(fmt.Sprintf("%v", _m.Provider)) builder.WriteString(", ") + builder.WriteString("check_mode=") + builder.WriteString(_m.CheckMode) + builder.WriteString(", ") + if v := _m.AccountID; v != nil { + builder.WriteString("account_id=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") builder.WriteString("api_mode=") builder.WriteString(_m.APIMode) builder.WriteString(", ") diff --git a/backend/ent/channelmonitor/channelmonitor.go b/backend/ent/channelmonitor/channelmonitor.go index 711e6217e2..3224c6eb04 100644 --- a/backend/ent/channelmonitor/channelmonitor.go +++ b/backend/ent/channelmonitor/channelmonitor.go @@ -23,6 +23,10 @@ const ( FieldName = "name" // FieldProvider holds the string denoting the provider field in the database. FieldProvider = "provider" + // FieldCheckMode holds the string denoting the check_mode field in the database. + FieldCheckMode = "check_mode" + // FieldAccountID holds the string denoting the account_id field in the database. + FieldAccountID = "account_id" // FieldAPIMode holds the string denoting the api_mode field in the database. FieldAPIMode = "api_mode" // FieldEndpoint holds the string denoting the endpoint field in the database. @@ -91,6 +95,8 @@ var Columns = []string{ FieldUpdatedAt, FieldName, FieldProvider, + FieldCheckMode, + FieldAccountID, FieldAPIMode, FieldEndpoint, FieldAPIKeyEncrypted, @@ -127,6 +133,10 @@ var ( UpdateDefaultUpdatedAt func() time.Time // NameValidator is a validator for the "name" field. It is called by the builders before save. NameValidator func(string) error + // DefaultCheckMode holds the default value on creation for the "check_mode" field. + DefaultCheckMode string + // CheckModeValidator is a validator for the "check_mode" field. It is called by the builders before save. + CheckModeValidator func(string) error // DefaultAPIMode holds the default value on creation for the "api_mode" field. DefaultAPIMode string // APIModeValidator is a validator for the "api_mode" field. It is called by the builders before save. @@ -164,10 +174,14 @@ type Provider string // Provider values. const ( - ProviderOpenai Provider = "openai" - ProviderAnthropic Provider = "anthropic" - ProviderGemini Provider = "gemini" - ProviderGrok Provider = "grok" + ProviderOpenai Provider = "openai" + ProviderAnthropic Provider = "anthropic" + ProviderGemini Provider = "gemini" + ProviderGrok Provider = "grok" + ProviderAntigravity Provider = "antigravity" + ProviderKimi Provider = "kimi" + ProviderZhipu Provider = "zhipu" + ProviderDeepseek Provider = "deepseek" ) func (pr Provider) String() string { @@ -177,7 +191,7 @@ func (pr Provider) String() string { // ProviderValidator is a validator for the "provider" field enum values. It is called by the builders before save. func ProviderValidator(pr Provider) error { switch pr { - case ProviderOpenai, ProviderAnthropic, ProviderGemini, ProviderGrok: + case ProviderOpenai, ProviderAnthropic, ProviderGemini, ProviderGrok, ProviderAntigravity, ProviderKimi, ProviderZhipu, ProviderDeepseek: return nil default: return fmt.Errorf("channelmonitor: invalid enum value for provider field: %q", pr) @@ -212,6 +226,16 @@ func ByProvider(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldProvider, opts...).ToFunc() } +// ByCheckMode orders the results by the check_mode field. +func ByCheckMode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCheckMode, opts...).ToFunc() +} + +// ByAccountID orders the results by the account_id field. +func ByAccountID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAccountID, opts...).ToFunc() +} + // ByAPIMode orders the results by the api_mode field. func ByAPIMode(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldAPIMode, opts...).ToFunc() diff --git a/backend/ent/channelmonitor/where.go b/backend/ent/channelmonitor/where.go index 6575aa5df6..9370a0b5ea 100644 --- a/backend/ent/channelmonitor/where.go +++ b/backend/ent/channelmonitor/where.go @@ -70,6 +70,16 @@ func Name(v string) predicate.ChannelMonitor { return predicate.ChannelMonitor(sql.FieldEQ(FieldName, v)) } +// CheckMode applies equality check predicate on the "check_mode" field. It's identical to CheckModeEQ. +func CheckMode(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldEQ(FieldCheckMode, v)) +} + +// AccountID applies equality check predicate on the "account_id" field. It's identical to AccountIDEQ. +func AccountID(v int64) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldEQ(FieldAccountID, v)) +} + // APIMode applies equality check predicate on the "api_mode" field. It's identical to APIModeEQ. func APIMode(v string) predicate.ChannelMonitor { return predicate.ChannelMonitor(sql.FieldEQ(FieldAPIMode, v)) @@ -295,6 +305,121 @@ func ProviderNotIn(vs ...Provider) predicate.ChannelMonitor { return predicate.ChannelMonitor(sql.FieldNotIn(FieldProvider, vs...)) } +// CheckModeEQ applies the EQ predicate on the "check_mode" field. +func CheckModeEQ(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldEQ(FieldCheckMode, v)) +} + +// CheckModeNEQ applies the NEQ predicate on the "check_mode" field. +func CheckModeNEQ(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldNEQ(FieldCheckMode, v)) +} + +// CheckModeIn applies the In predicate on the "check_mode" field. +func CheckModeIn(vs ...string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldIn(FieldCheckMode, vs...)) +} + +// CheckModeNotIn applies the NotIn predicate on the "check_mode" field. +func CheckModeNotIn(vs ...string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldNotIn(FieldCheckMode, vs...)) +} + +// CheckModeGT applies the GT predicate on the "check_mode" field. +func CheckModeGT(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldGT(FieldCheckMode, v)) +} + +// CheckModeGTE applies the GTE predicate on the "check_mode" field. +func CheckModeGTE(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldGTE(FieldCheckMode, v)) +} + +// CheckModeLT applies the LT predicate on the "check_mode" field. +func CheckModeLT(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldLT(FieldCheckMode, v)) +} + +// CheckModeLTE applies the LTE predicate on the "check_mode" field. +func CheckModeLTE(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldLTE(FieldCheckMode, v)) +} + +// CheckModeContains applies the Contains predicate on the "check_mode" field. +func CheckModeContains(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldContains(FieldCheckMode, v)) +} + +// CheckModeHasPrefix applies the HasPrefix predicate on the "check_mode" field. +func CheckModeHasPrefix(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldHasPrefix(FieldCheckMode, v)) +} + +// CheckModeHasSuffix applies the HasSuffix predicate on the "check_mode" field. +func CheckModeHasSuffix(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldHasSuffix(FieldCheckMode, v)) +} + +// CheckModeEqualFold applies the EqualFold predicate on the "check_mode" field. +func CheckModeEqualFold(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldEqualFold(FieldCheckMode, v)) +} + +// CheckModeContainsFold applies the ContainsFold predicate on the "check_mode" field. +func CheckModeContainsFold(v string) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldContainsFold(FieldCheckMode, v)) +} + +// AccountIDEQ applies the EQ predicate on the "account_id" field. +func AccountIDEQ(v int64) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldEQ(FieldAccountID, v)) +} + +// AccountIDNEQ applies the NEQ predicate on the "account_id" field. +func AccountIDNEQ(v int64) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldNEQ(FieldAccountID, v)) +} + +// AccountIDIn applies the In predicate on the "account_id" field. +func AccountIDIn(vs ...int64) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldIn(FieldAccountID, vs...)) +} + +// AccountIDNotIn applies the NotIn predicate on the "account_id" field. +func AccountIDNotIn(vs ...int64) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldNotIn(FieldAccountID, vs...)) +} + +// AccountIDGT applies the GT predicate on the "account_id" field. +func AccountIDGT(v int64) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldGT(FieldAccountID, v)) +} + +// AccountIDGTE applies the GTE predicate on the "account_id" field. +func AccountIDGTE(v int64) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldGTE(FieldAccountID, v)) +} + +// AccountIDLT applies the LT predicate on the "account_id" field. +func AccountIDLT(v int64) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldLT(FieldAccountID, v)) +} + +// AccountIDLTE applies the LTE predicate on the "account_id" field. +func AccountIDLTE(v int64) predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldLTE(FieldAccountID, v)) +} + +// AccountIDIsNil applies the IsNil predicate on the "account_id" field. +func AccountIDIsNil() predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldIsNull(FieldAccountID)) +} + +// AccountIDNotNil applies the NotNil predicate on the "account_id" field. +func AccountIDNotNil() predicate.ChannelMonitor { + return predicate.ChannelMonitor(sql.FieldNotNull(FieldAccountID)) +} + // APIModeEQ applies the EQ predicate on the "api_mode" field. func APIModeEQ(v string) predicate.ChannelMonitor { return predicate.ChannelMonitor(sql.FieldEQ(FieldAPIMode, v)) diff --git a/backend/ent/channelmonitor_create.go b/backend/ent/channelmonitor_create.go index 6c7e654917..cfd63437ff 100644 --- a/backend/ent/channelmonitor_create.go +++ b/backend/ent/channelmonitor_create.go @@ -65,6 +65,34 @@ func (_c *ChannelMonitorCreate) SetProvider(v channelmonitor.Provider) *ChannelM return _c } +// SetCheckMode sets the "check_mode" field. +func (_c *ChannelMonitorCreate) SetCheckMode(v string) *ChannelMonitorCreate { + _c.mutation.SetCheckMode(v) + return _c +} + +// SetNillableCheckMode sets the "check_mode" field if the given value is not nil. +func (_c *ChannelMonitorCreate) SetNillableCheckMode(v *string) *ChannelMonitorCreate { + if v != nil { + _c.SetCheckMode(*v) + } + return _c +} + +// SetAccountID sets the "account_id" field. +func (_c *ChannelMonitorCreate) SetAccountID(v int64) *ChannelMonitorCreate { + _c.mutation.SetAccountID(v) + return _c +} + +// SetNillableAccountID sets the "account_id" field if the given value is not nil. +func (_c *ChannelMonitorCreate) SetNillableAccountID(v *int64) *ChannelMonitorCreate { + if v != nil { + _c.SetAccountID(*v) + } + return _c +} + // SetAPIMode sets the "api_mode" field. func (_c *ChannelMonitorCreate) SetAPIMode(v string) *ChannelMonitorCreate { _c.mutation.SetAPIMode(v) @@ -303,6 +331,10 @@ func (_c *ChannelMonitorCreate) defaults() { v := channelmonitor.DefaultUpdatedAt() _c.mutation.SetUpdatedAt(v) } + if _, ok := _c.mutation.CheckMode(); !ok { + v := channelmonitor.DefaultCheckMode + _c.mutation.SetCheckMode(v) + } if _, ok := _c.mutation.APIMode(); !ok { v := channelmonitor.DefaultAPIMode _c.mutation.SetAPIMode(v) @@ -357,6 +389,14 @@ func (_c *ChannelMonitorCreate) check() error { return &ValidationError{Name: "provider", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitor.provider": %w`, err)} } } + if _, ok := _c.mutation.CheckMode(); !ok { + return &ValidationError{Name: "check_mode", err: errors.New(`ent: missing required field "ChannelMonitor.check_mode"`)} + } + if v, ok := _c.mutation.CheckMode(); ok { + if err := channelmonitor.CheckModeValidator(v); err != nil { + return &ValidationError{Name: "check_mode", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitor.check_mode": %w`, err)} + } + } if _, ok := _c.mutation.APIMode(); !ok { return &ValidationError{Name: "api_mode", err: errors.New(`ent: missing required field "ChannelMonitor.api_mode"`)} } @@ -473,6 +513,14 @@ func (_c *ChannelMonitorCreate) createSpec() (*ChannelMonitor, *sqlgraph.CreateS _spec.SetField(channelmonitor.FieldProvider, field.TypeEnum, value) _node.Provider = value } + if value, ok := _c.mutation.CheckMode(); ok { + _spec.SetField(channelmonitor.FieldCheckMode, field.TypeString, value) + _node.CheckMode = value + } + if value, ok := _c.mutation.AccountID(); ok { + _spec.SetField(channelmonitor.FieldAccountID, field.TypeInt64, value) + _node.AccountID = &value + } if value, ok := _c.mutation.APIMode(); ok { _spec.SetField(channelmonitor.FieldAPIMode, field.TypeString, value) _node.APIMode = value @@ -666,6 +714,42 @@ func (u *ChannelMonitorUpsert) UpdateProvider() *ChannelMonitorUpsert { return u } +// SetCheckMode sets the "check_mode" field. +func (u *ChannelMonitorUpsert) SetCheckMode(v string) *ChannelMonitorUpsert { + u.Set(channelmonitor.FieldCheckMode, v) + return u +} + +// UpdateCheckMode sets the "check_mode" field to the value that was provided on create. +func (u *ChannelMonitorUpsert) UpdateCheckMode() *ChannelMonitorUpsert { + u.SetExcluded(channelmonitor.FieldCheckMode) + return u +} + +// SetAccountID sets the "account_id" field. +func (u *ChannelMonitorUpsert) SetAccountID(v int64) *ChannelMonitorUpsert { + u.Set(channelmonitor.FieldAccountID, v) + return u +} + +// UpdateAccountID sets the "account_id" field to the value that was provided on create. +func (u *ChannelMonitorUpsert) UpdateAccountID() *ChannelMonitorUpsert { + u.SetExcluded(channelmonitor.FieldAccountID) + return u +} + +// AddAccountID adds v to the "account_id" field. +func (u *ChannelMonitorUpsert) AddAccountID(v int64) *ChannelMonitorUpsert { + u.Add(channelmonitor.FieldAccountID, v) + return u +} + +// ClearAccountID clears the value of the "account_id" field. +func (u *ChannelMonitorUpsert) ClearAccountID() *ChannelMonitorUpsert { + u.SetNull(channelmonitor.FieldAccountID) + return u +} + // SetAPIMode sets the "api_mode" field. func (u *ChannelMonitorUpsert) SetAPIMode(v string) *ChannelMonitorUpsert { u.Set(channelmonitor.FieldAPIMode, v) @@ -975,6 +1059,48 @@ func (u *ChannelMonitorUpsertOne) UpdateProvider() *ChannelMonitorUpsertOne { }) } +// SetCheckMode sets the "check_mode" field. +func (u *ChannelMonitorUpsertOne) SetCheckMode(v string) *ChannelMonitorUpsertOne { + return u.Update(func(s *ChannelMonitorUpsert) { + s.SetCheckMode(v) + }) +} + +// UpdateCheckMode sets the "check_mode" field to the value that was provided on create. +func (u *ChannelMonitorUpsertOne) UpdateCheckMode() *ChannelMonitorUpsertOne { + return u.Update(func(s *ChannelMonitorUpsert) { + s.UpdateCheckMode() + }) +} + +// SetAccountID sets the "account_id" field. +func (u *ChannelMonitorUpsertOne) SetAccountID(v int64) *ChannelMonitorUpsertOne { + return u.Update(func(s *ChannelMonitorUpsert) { + s.SetAccountID(v) + }) +} + +// AddAccountID adds v to the "account_id" field. +func (u *ChannelMonitorUpsertOne) AddAccountID(v int64) *ChannelMonitorUpsertOne { + return u.Update(func(s *ChannelMonitorUpsert) { + s.AddAccountID(v) + }) +} + +// UpdateAccountID sets the "account_id" field to the value that was provided on create. +func (u *ChannelMonitorUpsertOne) UpdateAccountID() *ChannelMonitorUpsertOne { + return u.Update(func(s *ChannelMonitorUpsert) { + s.UpdateAccountID() + }) +} + +// ClearAccountID clears the value of the "account_id" field. +func (u *ChannelMonitorUpsertOne) ClearAccountID() *ChannelMonitorUpsertOne { + return u.Update(func(s *ChannelMonitorUpsert) { + s.ClearAccountID() + }) +} + // SetAPIMode sets the "api_mode" field. func (u *ChannelMonitorUpsertOne) SetAPIMode(v string) *ChannelMonitorUpsertOne { return u.Update(func(s *ChannelMonitorUpsert) { @@ -1487,6 +1613,48 @@ func (u *ChannelMonitorUpsertBulk) UpdateProvider() *ChannelMonitorUpsertBulk { }) } +// SetCheckMode sets the "check_mode" field. +func (u *ChannelMonitorUpsertBulk) SetCheckMode(v string) *ChannelMonitorUpsertBulk { + return u.Update(func(s *ChannelMonitorUpsert) { + s.SetCheckMode(v) + }) +} + +// UpdateCheckMode sets the "check_mode" field to the value that was provided on create. +func (u *ChannelMonitorUpsertBulk) UpdateCheckMode() *ChannelMonitorUpsertBulk { + return u.Update(func(s *ChannelMonitorUpsert) { + s.UpdateCheckMode() + }) +} + +// SetAccountID sets the "account_id" field. +func (u *ChannelMonitorUpsertBulk) SetAccountID(v int64) *ChannelMonitorUpsertBulk { + return u.Update(func(s *ChannelMonitorUpsert) { + s.SetAccountID(v) + }) +} + +// AddAccountID adds v to the "account_id" field. +func (u *ChannelMonitorUpsertBulk) AddAccountID(v int64) *ChannelMonitorUpsertBulk { + return u.Update(func(s *ChannelMonitorUpsert) { + s.AddAccountID(v) + }) +} + +// UpdateAccountID sets the "account_id" field to the value that was provided on create. +func (u *ChannelMonitorUpsertBulk) UpdateAccountID() *ChannelMonitorUpsertBulk { + return u.Update(func(s *ChannelMonitorUpsert) { + s.UpdateAccountID() + }) +} + +// ClearAccountID clears the value of the "account_id" field. +func (u *ChannelMonitorUpsertBulk) ClearAccountID() *ChannelMonitorUpsertBulk { + return u.Update(func(s *ChannelMonitorUpsert) { + s.ClearAccountID() + }) +} + // SetAPIMode sets the "api_mode" field. func (u *ChannelMonitorUpsertBulk) SetAPIMode(v string) *ChannelMonitorUpsertBulk { return u.Update(func(s *ChannelMonitorUpsert) { diff --git a/backend/ent/channelmonitor_update.go b/backend/ent/channelmonitor_update.go index ccc6fa8cc2..f5b52e4e1d 100644 --- a/backend/ent/channelmonitor_update.go +++ b/backend/ent/channelmonitor_update.go @@ -66,6 +66,47 @@ func (_u *ChannelMonitorUpdate) SetNillableProvider(v *channelmonitor.Provider) return _u } +// SetCheckMode sets the "check_mode" field. +func (_u *ChannelMonitorUpdate) SetCheckMode(v string) *ChannelMonitorUpdate { + _u.mutation.SetCheckMode(v) + return _u +} + +// SetNillableCheckMode sets the "check_mode" field if the given value is not nil. +func (_u *ChannelMonitorUpdate) SetNillableCheckMode(v *string) *ChannelMonitorUpdate { + if v != nil { + _u.SetCheckMode(*v) + } + return _u +} + +// SetAccountID sets the "account_id" field. +func (_u *ChannelMonitorUpdate) SetAccountID(v int64) *ChannelMonitorUpdate { + _u.mutation.ResetAccountID() + _u.mutation.SetAccountID(v) + return _u +} + +// SetNillableAccountID sets the "account_id" field if the given value is not nil. +func (_u *ChannelMonitorUpdate) SetNillableAccountID(v *int64) *ChannelMonitorUpdate { + if v != nil { + _u.SetAccountID(*v) + } + return _u +} + +// AddAccountID adds value to the "account_id" field. +func (_u *ChannelMonitorUpdate) AddAccountID(v int64) *ChannelMonitorUpdate { + _u.mutation.AddAccountID(v) + return _u +} + +// ClearAccountID clears the value of the "account_id" field. +func (_u *ChannelMonitorUpdate) ClearAccountID() *ChannelMonitorUpdate { + _u.mutation.ClearAccountID() + return _u +} + // SetAPIMode sets the "api_mode" field. func (_u *ChannelMonitorUpdate) SetAPIMode(v string) *ChannelMonitorUpdate { _u.mutation.SetAPIMode(v) @@ -453,6 +494,11 @@ func (_u *ChannelMonitorUpdate) check() error { return &ValidationError{Name: "provider", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitor.provider": %w`, err)} } } + if v, ok := _u.mutation.CheckMode(); ok { + if err := channelmonitor.CheckModeValidator(v); err != nil { + return &ValidationError{Name: "check_mode", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitor.check_mode": %w`, err)} + } + } if v, ok := _u.mutation.APIMode(); ok { if err := channelmonitor.APIModeValidator(v); err != nil { return &ValidationError{Name: "api_mode", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitor.api_mode": %w`, err)} @@ -517,6 +563,18 @@ func (_u *ChannelMonitorUpdate) sqlSave(ctx context.Context) (_node int, err err if value, ok := _u.mutation.Provider(); ok { _spec.SetField(channelmonitor.FieldProvider, field.TypeEnum, value) } + if value, ok := _u.mutation.CheckMode(); ok { + _spec.SetField(channelmonitor.FieldCheckMode, field.TypeString, value) + } + if value, ok := _u.mutation.AccountID(); ok { + _spec.SetField(channelmonitor.FieldAccountID, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedAccountID(); ok { + _spec.AddField(channelmonitor.FieldAccountID, field.TypeInt64, value) + } + if _u.mutation.AccountIDCleared() { + _spec.ClearField(channelmonitor.FieldAccountID, field.TypeInt64) + } if value, ok := _u.mutation.APIMode(); ok { _spec.SetField(channelmonitor.FieldAPIMode, field.TypeString, value) } @@ -755,6 +813,47 @@ func (_u *ChannelMonitorUpdateOne) SetNillableProvider(v *channelmonitor.Provide return _u } +// SetCheckMode sets the "check_mode" field. +func (_u *ChannelMonitorUpdateOne) SetCheckMode(v string) *ChannelMonitorUpdateOne { + _u.mutation.SetCheckMode(v) + return _u +} + +// SetNillableCheckMode sets the "check_mode" field if the given value is not nil. +func (_u *ChannelMonitorUpdateOne) SetNillableCheckMode(v *string) *ChannelMonitorUpdateOne { + if v != nil { + _u.SetCheckMode(*v) + } + return _u +} + +// SetAccountID sets the "account_id" field. +func (_u *ChannelMonitorUpdateOne) SetAccountID(v int64) *ChannelMonitorUpdateOne { + _u.mutation.ResetAccountID() + _u.mutation.SetAccountID(v) + return _u +} + +// SetNillableAccountID sets the "account_id" field if the given value is not nil. +func (_u *ChannelMonitorUpdateOne) SetNillableAccountID(v *int64) *ChannelMonitorUpdateOne { + if v != nil { + _u.SetAccountID(*v) + } + return _u +} + +// AddAccountID adds value to the "account_id" field. +func (_u *ChannelMonitorUpdateOne) AddAccountID(v int64) *ChannelMonitorUpdateOne { + _u.mutation.AddAccountID(v) + return _u +} + +// ClearAccountID clears the value of the "account_id" field. +func (_u *ChannelMonitorUpdateOne) ClearAccountID() *ChannelMonitorUpdateOne { + _u.mutation.ClearAccountID() + return _u +} + // SetAPIMode sets the "api_mode" field. func (_u *ChannelMonitorUpdateOne) SetAPIMode(v string) *ChannelMonitorUpdateOne { _u.mutation.SetAPIMode(v) @@ -1155,6 +1254,11 @@ func (_u *ChannelMonitorUpdateOne) check() error { return &ValidationError{Name: "provider", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitor.provider": %w`, err)} } } + if v, ok := _u.mutation.CheckMode(); ok { + if err := channelmonitor.CheckModeValidator(v); err != nil { + return &ValidationError{Name: "check_mode", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitor.check_mode": %w`, err)} + } + } if v, ok := _u.mutation.APIMode(); ok { if err := channelmonitor.APIModeValidator(v); err != nil { return &ValidationError{Name: "api_mode", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitor.api_mode": %w`, err)} @@ -1236,6 +1340,18 @@ func (_u *ChannelMonitorUpdateOne) sqlSave(ctx context.Context) (_node *ChannelM if value, ok := _u.mutation.Provider(); ok { _spec.SetField(channelmonitor.FieldProvider, field.TypeEnum, value) } + if value, ok := _u.mutation.CheckMode(); ok { + _spec.SetField(channelmonitor.FieldCheckMode, field.TypeString, value) + } + if value, ok := _u.mutation.AccountID(); ok { + _spec.SetField(channelmonitor.FieldAccountID, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedAccountID(); ok { + _spec.AddField(channelmonitor.FieldAccountID, field.TypeInt64, value) + } + if _u.mutation.AccountIDCleared() { + _spec.ClearField(channelmonitor.FieldAccountID, field.TypeInt64) + } if value, ok := _u.mutation.APIMode(); ok { _spec.SetField(channelmonitor.FieldAPIMode, field.TypeString, value) } diff --git a/backend/ent/channelmonitorhistory.go b/backend/ent/channelmonitorhistory.go index 70dde5422a..fef5109528 100644 --- a/backend/ent/channelmonitorhistory.go +++ b/backend/ent/channelmonitorhistory.go @@ -3,6 +3,7 @@ package ent import ( + "encoding/json" "fmt" "strings" "time" @@ -11,6 +12,7 @@ import ( "entgo.io/ent/dialect/sql" "github.com/Wei-Shaw/sub2api/ent/channelmonitor" "github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory" + "github.com/Wei-Shaw/sub2api/internal/domain" ) // ChannelMonitorHistory is the model entity for the ChannelMonitorHistory schema. @@ -30,6 +32,8 @@ type ChannelMonitorHistory struct { PingLatencyMs *int `json:"ping_latency_ms,omitempty"` // Message holds the value of the "message" field. Message string `json:"message,omitempty"` + // Quota holds the value of the "quota" field. + Quota *domain.MonitorQuotaSnapshot `json:"quota,omitempty"` // CheckedAt holds the value of the "checked_at" field. CheckedAt time.Time `json:"checked_at,omitempty"` // Edges holds the relations/edges for other nodes in the graph. @@ -63,6 +67,8 @@ func (*ChannelMonitorHistory) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) for i := range columns { switch columns[i] { + case channelmonitorhistory.FieldQuota: + values[i] = new([]byte) case channelmonitorhistory.FieldID, channelmonitorhistory.FieldMonitorID, channelmonitorhistory.FieldLatencyMs, channelmonitorhistory.FieldPingLatencyMs: values[i] = new(sql.NullInt64) case channelmonitorhistory.FieldModel, channelmonitorhistory.FieldStatus, channelmonitorhistory.FieldMessage: @@ -128,6 +134,14 @@ func (_m *ChannelMonitorHistory) assignValues(columns []string, values []any) er } else if value.Valid { _m.Message = value.String } + case channelmonitorhistory.FieldQuota: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field quota", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &_m.Quota); err != nil { + return fmt.Errorf("unmarshal field quota: %w", err) + } + } case channelmonitorhistory.FieldCheckedAt: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field checked_at", values[i]) @@ -197,6 +211,9 @@ func (_m *ChannelMonitorHistory) String() string { builder.WriteString("message=") builder.WriteString(_m.Message) builder.WriteString(", ") + builder.WriteString("quota=") + builder.WriteString(fmt.Sprintf("%v", _m.Quota)) + builder.WriteString(", ") builder.WriteString("checked_at=") builder.WriteString(_m.CheckedAt.Format(time.ANSIC)) builder.WriteByte(')') diff --git a/backend/ent/channelmonitorhistory/channelmonitorhistory.go b/backend/ent/channelmonitorhistory/channelmonitorhistory.go index 6a9dc00670..ceb11cee67 100644 --- a/backend/ent/channelmonitorhistory/channelmonitorhistory.go +++ b/backend/ent/channelmonitorhistory/channelmonitorhistory.go @@ -27,6 +27,8 @@ const ( FieldPingLatencyMs = "ping_latency_ms" // FieldMessage holds the string denoting the message field in the database. FieldMessage = "message" + // FieldQuota holds the string denoting the quota field in the database. + FieldQuota = "quota" // FieldCheckedAt holds the string denoting the checked_at field in the database. FieldCheckedAt = "checked_at" // EdgeMonitor holds the string denoting the monitor edge name in mutations. @@ -51,6 +53,7 @@ var Columns = []string{ FieldLatencyMs, FieldPingLatencyMs, FieldMessage, + FieldQuota, FieldCheckedAt, } diff --git a/backend/ent/channelmonitorhistory/where.go b/backend/ent/channelmonitorhistory/where.go index afa73f35c8..f7afd7c319 100644 --- a/backend/ent/channelmonitorhistory/where.go +++ b/backend/ent/channelmonitorhistory/where.go @@ -365,6 +365,16 @@ func MessageContainsFold(v string) predicate.ChannelMonitorHistory { return predicate.ChannelMonitorHistory(sql.FieldContainsFold(FieldMessage, v)) } +// QuotaIsNil applies the IsNil predicate on the "quota" field. +func QuotaIsNil() predicate.ChannelMonitorHistory { + return predicate.ChannelMonitorHistory(sql.FieldIsNull(FieldQuota)) +} + +// QuotaNotNil applies the NotNil predicate on the "quota" field. +func QuotaNotNil() predicate.ChannelMonitorHistory { + return predicate.ChannelMonitorHistory(sql.FieldNotNull(FieldQuota)) +} + // CheckedAtEQ applies the EQ predicate on the "checked_at" field. func CheckedAtEQ(v time.Time) predicate.ChannelMonitorHistory { return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldCheckedAt, v)) diff --git a/backend/ent/channelmonitorhistory_create.go b/backend/ent/channelmonitorhistory_create.go index 71034865c9..4d4222d6d3 100644 --- a/backend/ent/channelmonitorhistory_create.go +++ b/backend/ent/channelmonitorhistory_create.go @@ -13,6 +13,7 @@ import ( "entgo.io/ent/schema/field" "github.com/Wei-Shaw/sub2api/ent/channelmonitor" "github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory" + "github.com/Wei-Shaw/sub2api/internal/domain" ) // ChannelMonitorHistoryCreate is the builder for creating a ChannelMonitorHistory entity. @@ -83,6 +84,12 @@ func (_c *ChannelMonitorHistoryCreate) SetNillableMessage(v *string) *ChannelMon return _c } +// SetQuota sets the "quota" field. +func (_c *ChannelMonitorHistoryCreate) SetQuota(v *domain.MonitorQuotaSnapshot) *ChannelMonitorHistoryCreate { + _c.mutation.SetQuota(v) + return _c +} + // SetCheckedAt sets the "checked_at" field. func (_c *ChannelMonitorHistoryCreate) SetCheckedAt(v time.Time) *ChannelMonitorHistoryCreate { _c.mutation.SetCheckedAt(v) @@ -226,6 +233,10 @@ func (_c *ChannelMonitorHistoryCreate) createSpec() (*ChannelMonitorHistory, *sq _spec.SetField(channelmonitorhistory.FieldMessage, field.TypeString, value) _node.Message = value } + if value, ok := _c.mutation.Quota(); ok { + _spec.SetField(channelmonitorhistory.FieldQuota, field.TypeJSON, value) + _node.Quota = value + } if value, ok := _c.mutation.CheckedAt(); ok { _spec.SetField(channelmonitorhistory.FieldCheckedAt, field.TypeTime, value) _node.CheckedAt = value @@ -401,6 +412,24 @@ func (u *ChannelMonitorHistoryUpsert) ClearMessage() *ChannelMonitorHistoryUpser return u } +// SetQuota sets the "quota" field. +func (u *ChannelMonitorHistoryUpsert) SetQuota(v *domain.MonitorQuotaSnapshot) *ChannelMonitorHistoryUpsert { + u.Set(channelmonitorhistory.FieldQuota, v) + return u +} + +// UpdateQuota sets the "quota" field to the value that was provided on create. +func (u *ChannelMonitorHistoryUpsert) UpdateQuota() *ChannelMonitorHistoryUpsert { + u.SetExcluded(channelmonitorhistory.FieldQuota) + return u +} + +// ClearQuota clears the value of the "quota" field. +func (u *ChannelMonitorHistoryUpsert) ClearQuota() *ChannelMonitorHistoryUpsert { + u.SetNull(channelmonitorhistory.FieldQuota) + return u +} + // SetCheckedAt sets the "checked_at" field. func (u *ChannelMonitorHistoryUpsert) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpsert { u.Set(channelmonitorhistory.FieldCheckedAt, v) @@ -572,6 +601,27 @@ func (u *ChannelMonitorHistoryUpsertOne) ClearMessage() *ChannelMonitorHistoryUp }) } +// SetQuota sets the "quota" field. +func (u *ChannelMonitorHistoryUpsertOne) SetQuota(v *domain.MonitorQuotaSnapshot) *ChannelMonitorHistoryUpsertOne { + return u.Update(func(s *ChannelMonitorHistoryUpsert) { + s.SetQuota(v) + }) +} + +// UpdateQuota sets the "quota" field to the value that was provided on create. +func (u *ChannelMonitorHistoryUpsertOne) UpdateQuota() *ChannelMonitorHistoryUpsertOne { + return u.Update(func(s *ChannelMonitorHistoryUpsert) { + s.UpdateQuota() + }) +} + +// ClearQuota clears the value of the "quota" field. +func (u *ChannelMonitorHistoryUpsertOne) ClearQuota() *ChannelMonitorHistoryUpsertOne { + return u.Update(func(s *ChannelMonitorHistoryUpsert) { + s.ClearQuota() + }) +} + // SetCheckedAt sets the "checked_at" field. func (u *ChannelMonitorHistoryUpsertOne) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpsertOne { return u.Update(func(s *ChannelMonitorHistoryUpsert) { @@ -909,6 +959,27 @@ func (u *ChannelMonitorHistoryUpsertBulk) ClearMessage() *ChannelMonitorHistoryU }) } +// SetQuota sets the "quota" field. +func (u *ChannelMonitorHistoryUpsertBulk) SetQuota(v *domain.MonitorQuotaSnapshot) *ChannelMonitorHistoryUpsertBulk { + return u.Update(func(s *ChannelMonitorHistoryUpsert) { + s.SetQuota(v) + }) +} + +// UpdateQuota sets the "quota" field to the value that was provided on create. +func (u *ChannelMonitorHistoryUpsertBulk) UpdateQuota() *ChannelMonitorHistoryUpsertBulk { + return u.Update(func(s *ChannelMonitorHistoryUpsert) { + s.UpdateQuota() + }) +} + +// ClearQuota clears the value of the "quota" field. +func (u *ChannelMonitorHistoryUpsertBulk) ClearQuota() *ChannelMonitorHistoryUpsertBulk { + return u.Update(func(s *ChannelMonitorHistoryUpsert) { + s.ClearQuota() + }) +} + // SetCheckedAt sets the "checked_at" field. func (u *ChannelMonitorHistoryUpsertBulk) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpsertBulk { return u.Update(func(s *ChannelMonitorHistoryUpsert) { diff --git a/backend/ent/channelmonitorhistory_update.go b/backend/ent/channelmonitorhistory_update.go index a85a8072a9..58d7214068 100644 --- a/backend/ent/channelmonitorhistory_update.go +++ b/backend/ent/channelmonitorhistory_update.go @@ -14,6 +14,7 @@ import ( "github.com/Wei-Shaw/sub2api/ent/channelmonitor" "github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory" "github.com/Wei-Shaw/sub2api/ent/predicate" + "github.com/Wei-Shaw/sub2api/internal/domain" ) // ChannelMonitorHistoryUpdate is the builder for updating ChannelMonitorHistory entities. @@ -145,6 +146,18 @@ func (_u *ChannelMonitorHistoryUpdate) ClearMessage() *ChannelMonitorHistoryUpda return _u } +// SetQuota sets the "quota" field. +func (_u *ChannelMonitorHistoryUpdate) SetQuota(v *domain.MonitorQuotaSnapshot) *ChannelMonitorHistoryUpdate { + _u.mutation.SetQuota(v) + return _u +} + +// ClearQuota clears the value of the "quota" field. +func (_u *ChannelMonitorHistoryUpdate) ClearQuota() *ChannelMonitorHistoryUpdate { + _u.mutation.ClearQuota() + return _u +} + // SetCheckedAt sets the "checked_at" field. func (_u *ChannelMonitorHistoryUpdate) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpdate { _u.mutation.SetCheckedAt(v) @@ -267,6 +280,12 @@ func (_u *ChannelMonitorHistoryUpdate) sqlSave(ctx context.Context) (_node int, if _u.mutation.MessageCleared() { _spec.ClearField(channelmonitorhistory.FieldMessage, field.TypeString) } + if value, ok := _u.mutation.Quota(); ok { + _spec.SetField(channelmonitorhistory.FieldQuota, field.TypeJSON, value) + } + if _u.mutation.QuotaCleared() { + _spec.ClearField(channelmonitorhistory.FieldQuota, field.TypeJSON) + } if value, ok := _u.mutation.CheckedAt(); ok { _spec.SetField(channelmonitorhistory.FieldCheckedAt, field.TypeTime, value) } @@ -435,6 +454,18 @@ func (_u *ChannelMonitorHistoryUpdateOne) ClearMessage() *ChannelMonitorHistoryU return _u } +// SetQuota sets the "quota" field. +func (_u *ChannelMonitorHistoryUpdateOne) SetQuota(v *domain.MonitorQuotaSnapshot) *ChannelMonitorHistoryUpdateOne { + _u.mutation.SetQuota(v) + return _u +} + +// ClearQuota clears the value of the "quota" field. +func (_u *ChannelMonitorHistoryUpdateOne) ClearQuota() *ChannelMonitorHistoryUpdateOne { + _u.mutation.ClearQuota() + return _u +} + // SetCheckedAt sets the "checked_at" field. func (_u *ChannelMonitorHistoryUpdateOne) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpdateOne { _u.mutation.SetCheckedAt(v) @@ -587,6 +618,12 @@ func (_u *ChannelMonitorHistoryUpdateOne) sqlSave(ctx context.Context) (_node *C if _u.mutation.MessageCleared() { _spec.ClearField(channelmonitorhistory.FieldMessage, field.TypeString) } + if value, ok := _u.mutation.Quota(); ok { + _spec.SetField(channelmonitorhistory.FieldQuota, field.TypeJSON, value) + } + if _u.mutation.QuotaCleared() { + _spec.ClearField(channelmonitorhistory.FieldQuota, field.TypeJSON) + } if value, ok := _u.mutation.CheckedAt(); ok { _spec.SetField(channelmonitorhistory.FieldCheckedAt, field.TypeTime, value) } diff --git a/backend/ent/channelmonitorrequesttemplate/channelmonitorrequesttemplate.go b/backend/ent/channelmonitorrequesttemplate/channelmonitorrequesttemplate.go index 5989d0e743..f0e9dbf16e 100644 --- a/backend/ent/channelmonitorrequesttemplate/channelmonitorrequesttemplate.go +++ b/backend/ent/channelmonitorrequesttemplate/channelmonitorrequesttemplate.go @@ -100,10 +100,14 @@ type Provider string // Provider values. const ( - ProviderOpenai Provider = "openai" - ProviderAnthropic Provider = "anthropic" - ProviderGemini Provider = "gemini" - ProviderGrok Provider = "grok" + ProviderOpenai Provider = "openai" + ProviderAnthropic Provider = "anthropic" + ProviderGemini Provider = "gemini" + ProviderGrok Provider = "grok" + ProviderAntigravity Provider = "antigravity" + ProviderKimi Provider = "kimi" + ProviderZhipu Provider = "zhipu" + ProviderDeepseek Provider = "deepseek" ) func (pr Provider) String() string { @@ -113,7 +117,7 @@ func (pr Provider) String() string { // ProviderValidator is a validator for the "provider" field enum values. It is called by the builders before save. func ProviderValidator(pr Provider) error { switch pr { - case ProviderOpenai, ProviderAnthropic, ProviderGemini, ProviderGrok: + case ProviderOpenai, ProviderAnthropic, ProviderGemini, ProviderGrok, ProviderAntigravity, ProviderKimi, ProviderZhipu, ProviderDeepseek: return nil default: return fmt.Errorf("channelmonitorrequesttemplate: invalid enum value for provider field: %q", pr) diff --git a/backend/ent/group.go b/backend/ent/group.go index 110f06742a..c9795fb58f 100644 --- a/backend/ent/group.go +++ b/backend/ent/group.go @@ -97,7 +97,7 @@ type Group struct { AudioTtsPricePerMillionChars *float64 `json:"audio_tts_price_per_million_chars,omitempty"` // STT 每小时价格(USD) AudioSttPricePerHour *float64 `json:"audio_stt_price_per_hour,omitempty"` - // 是否按上下文长度应用模型阶梯价格 + // 是否按上下文长度应用模型阶梯价格;默认开启以保持官方/渠道长上下文价 LongContextPricingEnabled bool `json:"long_context_pricing_enabled,omitempty"` // 分组逐模型定价;优先级高于渠道和内置定价 ModelPricing json.RawMessage `json:"model_pricing,omitempty"` diff --git a/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go index 316870e5a5..f667c3ffbf 100644 --- a/backend/ent/migrate/schema.go +++ b/backend/ent/migrate/schema.go @@ -623,7 +623,9 @@ var ( {Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}}, {Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}}, {Name: "name", Type: field.TypeString, Size: 100}, - {Name: "provider", Type: field.TypeEnum, Enums: []string{"openai", "anthropic", "gemini", "grok"}}, + {Name: "provider", Type: field.TypeEnum, Enums: []string{"openai", "anthropic", "gemini", "grok", "antigravity", "kimi", "zhipu", "deepseek"}}, + {Name: "check_mode", Type: field.TypeString, Size: 32, Default: "probe"}, + {Name: "account_id", Type: field.TypeInt64, Nullable: true}, {Name: "api_mode", Type: field.TypeString, Size: 32, Default: "chat_completions"}, {Name: "endpoint", Type: field.TypeString, Size: 500}, {Name: "api_key_encrypted", Type: field.TypeString}, @@ -648,7 +650,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "channel_monitors_channel_monitor_request_templates_request_template", - Columns: []*schema.Column{ChannelMonitorsColumns[19]}, + Columns: []*schema.Column{ChannelMonitorsColumns[21]}, RefColumns: []*schema.Column{ChannelMonitorRequestTemplatesColumns[0]}, OnDelete: schema.SetNull, }, @@ -657,7 +659,7 @@ var ( { Name: "channelmonitor_enabled_last_checked_at", Unique: false, - Columns: []*schema.Column{ChannelMonitorsColumns[11], ChannelMonitorsColumns[14]}, + Columns: []*schema.Column{ChannelMonitorsColumns[13], ChannelMonitorsColumns[16]}, }, { Name: "channelmonitor_provider", @@ -667,17 +669,22 @@ var ( { Name: "channelmonitor_provider_api_mode", Unique: false, - Columns: []*schema.Column{ChannelMonitorsColumns[4], ChannelMonitorsColumns[5]}, + Columns: []*schema.Column{ChannelMonitorsColumns[4], ChannelMonitorsColumns[7]}, }, { Name: "channelmonitor_group_name", Unique: false, - Columns: []*schema.Column{ChannelMonitorsColumns[10]}, + Columns: []*schema.Column{ChannelMonitorsColumns[12]}, }, { Name: "channelmonitor_template_id", Unique: false, - Columns: []*schema.Column{ChannelMonitorsColumns[19]}, + Columns: []*schema.Column{ChannelMonitorsColumns[21]}, + }, + { + Name: "channelmonitor_account_id", + Unique: false, + Columns: []*schema.Column{ChannelMonitorsColumns[6]}, }, }, } @@ -733,6 +740,7 @@ var ( {Name: "latency_ms", Type: field.TypeInt, Nullable: true}, {Name: "ping_latency_ms", Type: field.TypeInt, Nullable: true}, {Name: "message", Type: field.TypeString, Nullable: true, Size: 500, Default: ""}, + {Name: "quota", Type: field.TypeJSON, Nullable: true}, {Name: "checked_at", Type: field.TypeTime}, {Name: "monitor_id", Type: field.TypeInt64}, } @@ -744,7 +752,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "channel_monitor_histories_channel_monitors_history", - Columns: []*schema.Column{ChannelMonitorHistoriesColumns[7]}, + Columns: []*schema.Column{ChannelMonitorHistoriesColumns[8]}, RefColumns: []*schema.Column{ChannelMonitorsColumns[0]}, OnDelete: schema.Cascade, }, @@ -753,12 +761,12 @@ var ( { Name: "channelmonitorhistory_monitor_id_model_checked_at", Unique: false, - Columns: []*schema.Column{ChannelMonitorHistoriesColumns[7], ChannelMonitorHistoriesColumns[1], ChannelMonitorHistoriesColumns[6]}, + Columns: []*schema.Column{ChannelMonitorHistoriesColumns[8], ChannelMonitorHistoriesColumns[1], ChannelMonitorHistoriesColumns[7]}, }, { Name: "channelmonitorhistory_checked_at", Unique: false, - Columns: []*schema.Column{ChannelMonitorHistoriesColumns[6]}, + Columns: []*schema.Column{ChannelMonitorHistoriesColumns[7]}, }, }, } @@ -768,7 +776,7 @@ var ( {Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}}, {Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}}, {Name: "name", Type: field.TypeString, Size: 100}, - {Name: "provider", Type: field.TypeEnum, Enums: []string{"openai", "anthropic", "gemini", "grok"}}, + {Name: "provider", Type: field.TypeEnum, Enums: []string{"openai", "anthropic", "gemini", "grok", "antigravity", "kimi", "zhipu", "deepseek"}}, {Name: "api_mode", Type: field.TypeString, Size: 32, Default: "chat_completions"}, {Name: "description", Type: field.TypeString, Nullable: true, Size: 500, Default: ""}, {Name: "extra_headers", Type: field.TypeJSON}, diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go index da0500d9be..ddf50a1c42 100644 --- a/backend/ent/mutation.go +++ b/backend/ent/mutation.go @@ -14608,6 +14608,9 @@ type ChannelMonitorMutation struct { updated_at *time.Time name *string provider *channelmonitor.Provider + check_mode *string + account_id *int64 + addaccount_id *int64 api_mode *string endpoint *string api_key_encrypted *string @@ -14882,6 +14885,112 @@ func (m *ChannelMonitorMutation) ResetProvider() { m.provider = nil } +// SetCheckMode sets the "check_mode" field. +func (m *ChannelMonitorMutation) SetCheckMode(s string) { + m.check_mode = &s +} + +// CheckMode returns the value of the "check_mode" field in the mutation. +func (m *ChannelMonitorMutation) CheckMode() (r string, exists bool) { + v := m.check_mode + if v == nil { + return + } + return *v, true +} + +// OldCheckMode returns the old "check_mode" field's value of the ChannelMonitor entity. +// If the ChannelMonitor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ChannelMonitorMutation) OldCheckMode(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCheckMode is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCheckMode requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCheckMode: %w", err) + } + return oldValue.CheckMode, nil +} + +// ResetCheckMode resets all changes to the "check_mode" field. +func (m *ChannelMonitorMutation) ResetCheckMode() { + m.check_mode = nil +} + +// SetAccountID sets the "account_id" field. +func (m *ChannelMonitorMutation) SetAccountID(i int64) { + m.account_id = &i + m.addaccount_id = nil +} + +// AccountID returns the value of the "account_id" field in the mutation. +func (m *ChannelMonitorMutation) AccountID() (r int64, exists bool) { + v := m.account_id + if v == nil { + return + } + return *v, true +} + +// OldAccountID returns the old "account_id" field's value of the ChannelMonitor entity. +// If the ChannelMonitor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ChannelMonitorMutation) OldAccountID(ctx context.Context) (v *int64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAccountID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAccountID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAccountID: %w", err) + } + return oldValue.AccountID, nil +} + +// AddAccountID adds i to the "account_id" field. +func (m *ChannelMonitorMutation) AddAccountID(i int64) { + if m.addaccount_id != nil { + *m.addaccount_id += i + } else { + m.addaccount_id = &i + } +} + +// AddedAccountID returns the value that was added to the "account_id" field in this mutation. +func (m *ChannelMonitorMutation) AddedAccountID() (r int64, exists bool) { + v := m.addaccount_id + if v == nil { + return + } + return *v, true +} + +// ClearAccountID clears the value of the "account_id" field. +func (m *ChannelMonitorMutation) ClearAccountID() { + m.account_id = nil + m.addaccount_id = nil + m.clearedFields[channelmonitor.FieldAccountID] = struct{}{} +} + +// AccountIDCleared returns if the "account_id" field was cleared in this mutation. +func (m *ChannelMonitorMutation) AccountIDCleared() bool { + _, ok := m.clearedFields[channelmonitor.FieldAccountID] + return ok +} + +// ResetAccountID resets all changes to the "account_id" field. +func (m *ChannelMonitorMutation) ResetAccountID() { + m.account_id = nil + m.addaccount_id = nil + delete(m.clearedFields, channelmonitor.FieldAccountID) +} + // SetAPIMode sets the "api_mode" field. func (m *ChannelMonitorMutation) SetAPIMode(s string) { m.api_mode = &s @@ -15731,7 +15840,7 @@ func (m *ChannelMonitorMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ChannelMonitorMutation) Fields() []string { - fields := make([]string, 0, 19) + fields := make([]string, 0, 21) if m.created_at != nil { fields = append(fields, channelmonitor.FieldCreatedAt) } @@ -15744,6 +15853,12 @@ func (m *ChannelMonitorMutation) Fields() []string { if m.provider != nil { fields = append(fields, channelmonitor.FieldProvider) } + if m.check_mode != nil { + fields = append(fields, channelmonitor.FieldCheckMode) + } + if m.account_id != nil { + fields = append(fields, channelmonitor.FieldAccountID) + } if m.api_mode != nil { fields = append(fields, channelmonitor.FieldAPIMode) } @@ -15805,6 +15920,10 @@ func (m *ChannelMonitorMutation) Field(name string) (ent.Value, bool) { return m.Name() case channelmonitor.FieldProvider: return m.Provider() + case channelmonitor.FieldCheckMode: + return m.CheckMode() + case channelmonitor.FieldAccountID: + return m.AccountID() case channelmonitor.FieldAPIMode: return m.APIMode() case channelmonitor.FieldEndpoint: @@ -15852,6 +15971,10 @@ func (m *ChannelMonitorMutation) OldField(ctx context.Context, name string) (ent return m.OldName(ctx) case channelmonitor.FieldProvider: return m.OldProvider(ctx) + case channelmonitor.FieldCheckMode: + return m.OldCheckMode(ctx) + case channelmonitor.FieldAccountID: + return m.OldAccountID(ctx) case channelmonitor.FieldAPIMode: return m.OldAPIMode(ctx) case channelmonitor.FieldEndpoint: @@ -15919,6 +16042,20 @@ func (m *ChannelMonitorMutation) SetField(name string, value ent.Value) error { } m.SetProvider(v) return nil + case channelmonitor.FieldCheckMode: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCheckMode(v) + return nil + case channelmonitor.FieldAccountID: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAccountID(v) + return nil case channelmonitor.FieldAPIMode: v, ok := value.(string) if !ok { @@ -16032,6 +16169,9 @@ func (m *ChannelMonitorMutation) SetField(name string, value ent.Value) error { // this mutation. func (m *ChannelMonitorMutation) AddedFields() []string { var fields []string + if m.addaccount_id != nil { + fields = append(fields, channelmonitor.FieldAccountID) + } if m.addinterval_seconds != nil { fields = append(fields, channelmonitor.FieldIntervalSeconds) } @@ -16049,6 +16189,8 @@ func (m *ChannelMonitorMutation) AddedFields() []string { // was not set, or was not defined in the schema. func (m *ChannelMonitorMutation) AddedField(name string) (ent.Value, bool) { switch name { + case channelmonitor.FieldAccountID: + return m.AddedAccountID() case channelmonitor.FieldIntervalSeconds: return m.AddedIntervalSeconds() case channelmonitor.FieldJitterSeconds: @@ -16064,6 +16206,13 @@ func (m *ChannelMonitorMutation) AddedField(name string) (ent.Value, bool) { // type. func (m *ChannelMonitorMutation) AddField(name string, value ent.Value) error { switch name { + case channelmonitor.FieldAccountID: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddAccountID(v) + return nil case channelmonitor.FieldIntervalSeconds: v, ok := value.(int) if !ok { @@ -16093,6 +16242,9 @@ func (m *ChannelMonitorMutation) AddField(name string, value ent.Value) error { // mutation. func (m *ChannelMonitorMutation) ClearedFields() []string { var fields []string + if m.FieldCleared(channelmonitor.FieldAccountID) { + fields = append(fields, channelmonitor.FieldAccountID) + } if m.FieldCleared(channelmonitor.FieldGroupName) { fields = append(fields, channelmonitor.FieldGroupName) } @@ -16119,6 +16271,9 @@ func (m *ChannelMonitorMutation) FieldCleared(name string) bool { // error if the field is not defined in the schema. func (m *ChannelMonitorMutation) ClearField(name string) error { switch name { + case channelmonitor.FieldAccountID: + m.ClearAccountID() + return nil case channelmonitor.FieldGroupName: m.ClearGroupName() return nil @@ -16151,6 +16306,12 @@ func (m *ChannelMonitorMutation) ResetField(name string) error { case channelmonitor.FieldProvider: m.ResetProvider() return nil + case channelmonitor.FieldCheckMode: + m.ResetCheckMode() + return nil + case channelmonitor.FieldAccountID: + m.ResetAccountID() + return nil case channelmonitor.FieldAPIMode: m.ResetAPIMode() return nil @@ -17756,6 +17917,7 @@ type ChannelMonitorHistoryMutation struct { ping_latency_ms *int addping_latency_ms *int message *string + quota **domain.MonitorQuotaSnapshot checked_at *time.Time clearedFields map[string]struct{} monitor *int64 @@ -18160,6 +18322,55 @@ func (m *ChannelMonitorHistoryMutation) ResetMessage() { delete(m.clearedFields, channelmonitorhistory.FieldMessage) } +// SetQuota sets the "quota" field. +func (m *ChannelMonitorHistoryMutation) SetQuota(dqs *domain.MonitorQuotaSnapshot) { + m.quota = &dqs +} + +// Quota returns the value of the "quota" field in the mutation. +func (m *ChannelMonitorHistoryMutation) Quota() (r *domain.MonitorQuotaSnapshot, exists bool) { + v := m.quota + if v == nil { + return + } + return *v, true +} + +// OldQuota returns the old "quota" field's value of the ChannelMonitorHistory entity. +// If the ChannelMonitorHistory object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ChannelMonitorHistoryMutation) OldQuota(ctx context.Context) (v *domain.MonitorQuotaSnapshot, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldQuota is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldQuota requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldQuota: %w", err) + } + return oldValue.Quota, nil +} + +// ClearQuota clears the value of the "quota" field. +func (m *ChannelMonitorHistoryMutation) ClearQuota() { + m.quota = nil + m.clearedFields[channelmonitorhistory.FieldQuota] = struct{}{} +} + +// QuotaCleared returns if the "quota" field was cleared in this mutation. +func (m *ChannelMonitorHistoryMutation) QuotaCleared() bool { + _, ok := m.clearedFields[channelmonitorhistory.FieldQuota] + return ok +} + +// ResetQuota resets all changes to the "quota" field. +func (m *ChannelMonitorHistoryMutation) ResetQuota() { + m.quota = nil + delete(m.clearedFields, channelmonitorhistory.FieldQuota) +} + // SetCheckedAt sets the "checked_at" field. func (m *ChannelMonitorHistoryMutation) SetCheckedAt(t time.Time) { m.checked_at = &t @@ -18257,7 +18468,7 @@ func (m *ChannelMonitorHistoryMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ChannelMonitorHistoryMutation) Fields() []string { - fields := make([]string, 0, 7) + fields := make([]string, 0, 8) if m.monitor != nil { fields = append(fields, channelmonitorhistory.FieldMonitorID) } @@ -18276,6 +18487,9 @@ func (m *ChannelMonitorHistoryMutation) Fields() []string { if m.message != nil { fields = append(fields, channelmonitorhistory.FieldMessage) } + if m.quota != nil { + fields = append(fields, channelmonitorhistory.FieldQuota) + } if m.checked_at != nil { fields = append(fields, channelmonitorhistory.FieldCheckedAt) } @@ -18299,6 +18513,8 @@ func (m *ChannelMonitorHistoryMutation) Field(name string) (ent.Value, bool) { return m.PingLatencyMs() case channelmonitorhistory.FieldMessage: return m.Message() + case channelmonitorhistory.FieldQuota: + return m.Quota() case channelmonitorhistory.FieldCheckedAt: return m.CheckedAt() } @@ -18322,6 +18538,8 @@ func (m *ChannelMonitorHistoryMutation) OldField(ctx context.Context, name strin return m.OldPingLatencyMs(ctx) case channelmonitorhistory.FieldMessage: return m.OldMessage(ctx) + case channelmonitorhistory.FieldQuota: + return m.OldQuota(ctx) case channelmonitorhistory.FieldCheckedAt: return m.OldCheckedAt(ctx) } @@ -18375,6 +18593,13 @@ func (m *ChannelMonitorHistoryMutation) SetField(name string, value ent.Value) e } m.SetMessage(v) return nil + case channelmonitorhistory.FieldQuota: + v, ok := value.(*domain.MonitorQuotaSnapshot) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetQuota(v) + return nil case channelmonitorhistory.FieldCheckedAt: v, ok := value.(time.Time) if !ok { @@ -18448,6 +18673,9 @@ func (m *ChannelMonitorHistoryMutation) ClearedFields() []string { if m.FieldCleared(channelmonitorhistory.FieldMessage) { fields = append(fields, channelmonitorhistory.FieldMessage) } + if m.FieldCleared(channelmonitorhistory.FieldQuota) { + fields = append(fields, channelmonitorhistory.FieldQuota) + } return fields } @@ -18471,6 +18699,9 @@ func (m *ChannelMonitorHistoryMutation) ClearField(name string) error { case channelmonitorhistory.FieldMessage: m.ClearMessage() return nil + case channelmonitorhistory.FieldQuota: + m.ClearQuota() + return nil } return fmt.Errorf("unknown ChannelMonitorHistory nullable field %s", name) } @@ -18497,6 +18728,9 @@ func (m *ChannelMonitorHistoryMutation) ResetField(name string) error { case channelmonitorhistory.FieldMessage: m.ResetMessage() return nil + case channelmonitorhistory.FieldQuota: + m.ResetQuota() + return nil case channelmonitorhistory.FieldCheckedAt: m.ResetCheckedAt() return nil diff --git a/backend/ent/runtime/runtime.go b/backend/ent/runtime/runtime.go index 4be8d374d6..03a6894e0d 100644 --- a/backend/ent/runtime/runtime.go +++ b/backend/ent/runtime/runtime.go @@ -635,36 +635,28 @@ func init() { return nil } }() + // channelmonitorDescCheckMode is the schema descriptor for check_mode field. + channelmonitorDescCheckMode := channelmonitorFields[2].Descriptor() + // channelmonitor.DefaultCheckMode holds the default value on creation for the check_mode field. + channelmonitor.DefaultCheckMode = channelmonitorDescCheckMode.Default.(string) + // channelmonitor.CheckModeValidator is a validator for the "check_mode" field. It is called by the builders before save. + channelmonitor.CheckModeValidator = channelmonitorDescCheckMode.Validators[0].(func(string) error) // channelmonitorDescAPIMode is the schema descriptor for api_mode field. - channelmonitorDescAPIMode := channelmonitorFields[2].Descriptor() + channelmonitorDescAPIMode := channelmonitorFields[4].Descriptor() // channelmonitor.DefaultAPIMode holds the default value on creation for the api_mode field. channelmonitor.DefaultAPIMode = channelmonitorDescAPIMode.Default.(string) // channelmonitor.APIModeValidator is a validator for the "api_mode" field. It is called by the builders before save. channelmonitor.APIModeValidator = channelmonitorDescAPIMode.Validators[0].(func(string) error) // channelmonitorDescEndpoint is the schema descriptor for endpoint field. - channelmonitorDescEndpoint := channelmonitorFields[3].Descriptor() + channelmonitorDescEndpoint := channelmonitorFields[5].Descriptor() // channelmonitor.EndpointValidator is a validator for the "endpoint" field. It is called by the builders before save. - channelmonitor.EndpointValidator = func() func(string) error { - validators := channelmonitorDescEndpoint.Validators - fns := [...]func(string) error{ - validators[0].(func(string) error), - validators[1].(func(string) error), - } - return func(endpoint string) error { - for _, fn := range fns { - if err := fn(endpoint); err != nil { - return err - } - } - return nil - } - }() + channelmonitor.EndpointValidator = channelmonitorDescEndpoint.Validators[0].(func(string) error) // channelmonitorDescAPIKeyEncrypted is the schema descriptor for api_key_encrypted field. - channelmonitorDescAPIKeyEncrypted := channelmonitorFields[4].Descriptor() + channelmonitorDescAPIKeyEncrypted := channelmonitorFields[6].Descriptor() // channelmonitor.APIKeyEncryptedValidator is a validator for the "api_key_encrypted" field. It is called by the builders before save. channelmonitor.APIKeyEncryptedValidator = channelmonitorDescAPIKeyEncrypted.Validators[0].(func(string) error) // channelmonitorDescPrimaryModel is the schema descriptor for primary_model field. - channelmonitorDescPrimaryModel := channelmonitorFields[5].Descriptor() + channelmonitorDescPrimaryModel := channelmonitorFields[7].Descriptor() // channelmonitor.PrimaryModelValidator is a validator for the "primary_model" field. It is called by the builders before save. channelmonitor.PrimaryModelValidator = func() func(string) error { validators := channelmonitorDescPrimaryModel.Validators @@ -682,35 +674,35 @@ func init() { } }() // channelmonitorDescExtraModels is the schema descriptor for extra_models field. - channelmonitorDescExtraModels := channelmonitorFields[6].Descriptor() + channelmonitorDescExtraModels := channelmonitorFields[8].Descriptor() // channelmonitor.DefaultExtraModels holds the default value on creation for the extra_models field. channelmonitor.DefaultExtraModels = channelmonitorDescExtraModels.Default.([]string) // channelmonitorDescGroupName is the schema descriptor for group_name field. - channelmonitorDescGroupName := channelmonitorFields[7].Descriptor() + channelmonitorDescGroupName := channelmonitorFields[9].Descriptor() // channelmonitor.DefaultGroupName holds the default value on creation for the group_name field. channelmonitor.DefaultGroupName = channelmonitorDescGroupName.Default.(string) // channelmonitor.GroupNameValidator is a validator for the "group_name" field. It is called by the builders before save. channelmonitor.GroupNameValidator = channelmonitorDescGroupName.Validators[0].(func(string) error) // channelmonitorDescEnabled is the schema descriptor for enabled field. - channelmonitorDescEnabled := channelmonitorFields[8].Descriptor() + channelmonitorDescEnabled := channelmonitorFields[10].Descriptor() // channelmonitor.DefaultEnabled holds the default value on creation for the enabled field. channelmonitor.DefaultEnabled = channelmonitorDescEnabled.Default.(bool) // channelmonitorDescIntervalSeconds is the schema descriptor for interval_seconds field. - channelmonitorDescIntervalSeconds := channelmonitorFields[9].Descriptor() + channelmonitorDescIntervalSeconds := channelmonitorFields[11].Descriptor() // channelmonitor.IntervalSecondsValidator is a validator for the "interval_seconds" field. It is called by the builders before save. channelmonitor.IntervalSecondsValidator = channelmonitorDescIntervalSeconds.Validators[0].(func(int) error) // channelmonitorDescJitterSeconds is the schema descriptor for jitter_seconds field. - channelmonitorDescJitterSeconds := channelmonitorFields[10].Descriptor() + channelmonitorDescJitterSeconds := channelmonitorFields[12].Descriptor() // channelmonitor.DefaultJitterSeconds holds the default value on creation for the jitter_seconds field. channelmonitor.DefaultJitterSeconds = channelmonitorDescJitterSeconds.Default.(int) // channelmonitor.JitterSecondsValidator is a validator for the "jitter_seconds" field. It is called by the builders before save. channelmonitor.JitterSecondsValidator = channelmonitorDescJitterSeconds.Validators[0].(func(int) error) // channelmonitorDescExtraHeaders is the schema descriptor for extra_headers field. - channelmonitorDescExtraHeaders := channelmonitorFields[14].Descriptor() + channelmonitorDescExtraHeaders := channelmonitorFields[16].Descriptor() // channelmonitor.DefaultExtraHeaders holds the default value on creation for the extra_headers field. channelmonitor.DefaultExtraHeaders = channelmonitorDescExtraHeaders.Default.(map[string]string) // channelmonitorDescBodyOverrideMode is the schema descriptor for body_override_mode field. - channelmonitorDescBodyOverrideMode := channelmonitorFields[15].Descriptor() + channelmonitorDescBodyOverrideMode := channelmonitorFields[17].Descriptor() // channelmonitor.DefaultBodyOverrideMode holds the default value on creation for the body_override_mode field. channelmonitor.DefaultBodyOverrideMode = channelmonitorDescBodyOverrideMode.Default.(string) // channelmonitor.BodyOverrideModeValidator is a validator for the "body_override_mode" field. It is called by the builders before save. @@ -808,7 +800,7 @@ func init() { // channelmonitorhistory.MessageValidator is a validator for the "message" field. It is called by the builders before save. channelmonitorhistory.MessageValidator = channelmonitorhistoryDescMessage.Validators[0].(func(string) error) // channelmonitorhistoryDescCheckedAt is the schema descriptor for checked_at field. - channelmonitorhistoryDescCheckedAt := channelmonitorhistoryFields[6].Descriptor() + channelmonitorhistoryDescCheckedAt := channelmonitorhistoryFields[7].Descriptor() // channelmonitorhistory.DefaultCheckedAt holds the default value on creation for the checked_at field. channelmonitorhistory.DefaultCheckedAt = channelmonitorhistoryDescCheckedAt.Default.(func() time.Time) channelmonitorrequesttemplateMixin := schema.ChannelMonitorRequestTemplate{}.Mixin() diff --git a/backend/ent/schema/channel_monitor.go b/backend/ent/schema/channel_monitor.go index cb62079316..e14416b765 100644 --- a/backend/ent/schema/channel_monitor.go +++ b/backend/ent/schema/channel_monitor.go @@ -35,15 +35,32 @@ func (ChannelMonitor) Fields() []ent.Field { NotEmpty(). MaxLen(100), field.Enum("provider"). - Values("openai", "anthropic", "gemini", "grok"), + Values("openai", "anthropic", "gemini", "grok", + "antigravity", "kimi", "zhipu", "deepseek"), + // check_mode: 'probe' | 'quota' | 'quota_probe' + // probe - LLM 探活(默认,原有行为) + // quota - 仅查关联账号的用量/余额(零 LLM 成本;endpoint/api_key 可空) + // quota_probe - 探活 + 配额并存(配额快照挂到主模型历史行) + // antigravity 无探活 adapter,仅允许 quota。 + field.String("check_mode"). + Default("probe"). + MaxLen(32). + Comment("probe = LLM probe (default); quota = account usage only; quota_probe = both"), + // account_id: 配额模式的数据源账号(复用账号侧用量服务,不直接对接上游)。 + // 普通字段而非 edge(FK 由 SQL 迁移管理);账号删除时数据库置空, + // 监控保留并报「账号未关联」。 + field.Int64("account_id"). + Optional(). + Nillable(), field.String("api_mode"). Default("chat_completions"). MaxLen(32). Comment("OpenAI request protocol: chat_completions or responses; non-OpenAI uses chat_completions"), + // endpoint: 探活模式必填(service 层校验);quota 模式存空串 + // (列保持 NOT NULL,去掉 NotEmpty 校验器即可)。 field.String("endpoint"). - NotEmpty(). MaxLen(500). - Comment("Provider base origin, e.g. https://api.openai.com"), + Comment("Provider base origin, e.g. https://api.openai.com; empty for quota-only monitors"), field.String("api_key_encrypted"). NotEmpty(). Sensitive(). @@ -115,5 +132,6 @@ func (ChannelMonitor) Indexes() []ent.Index { index.Fields("provider", "api_mode"), index.Fields("group_name"), index.Fields("template_id"), + index.Fields("account_id"), } } diff --git a/backend/ent/schema/channel_monitor_history.go b/backend/ent/schema/channel_monitor_history.go index 4366e79a67..c2ec4dc25c 100644 --- a/backend/ent/schema/channel_monitor_history.go +++ b/backend/ent/schema/channel_monitor_history.go @@ -3,6 +3,8 @@ package schema import ( "time" + "github.com/Wei-Shaw/sub2api/internal/domain" + "entgo.io/ent" "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema" @@ -43,6 +45,10 @@ func (ChannelMonitorHistory) Fields() []ent.Field { Optional(). Default(""). MaxLen(500), + // quota: 配额模式(check_mode = quota / quota_probe)检测时附带的 + // 归一化配额快照(domain.MonitorQuotaSnapshot,JSONB);探活模式为 NULL。 + field.JSON("quota", &domain.MonitorQuotaSnapshot{}). + Optional(), field.Time("checked_at"). Default(time.Now), } diff --git a/backend/ent/schema/channel_monitor_request_template.go b/backend/ent/schema/channel_monitor_request_template.go index cf7fe05158..332a01007c 100644 --- a/backend/ent/schema/channel_monitor_request_template.go +++ b/backend/ent/schema/channel_monitor_request_template.go @@ -39,7 +39,8 @@ func (ChannelMonitorRequestTemplate) Fields() []ent.Field { NotEmpty(). MaxLen(100), field.Enum("provider"). - Values("openai", "anthropic", "gemini", "grok"), + Values("openai", "anthropic", "gemini", "grok", + "antigravity", "kimi", "zhipu", "deepseek"), field.String("api_mode"). Default("chat_completions"). MaxLen(32). diff --git a/backend/internal/domain/channel_monitor_quota.go b/backend/internal/domain/channel_monitor_quota.go new file mode 100644 index 0000000000..656163dddf --- /dev/null +++ b/backend/internal/domain/channel_monitor_quota.go @@ -0,0 +1,55 @@ +package domain + +import "time" + +// 渠道监控「配额模式」的归一化配额快照类型。 +// +// 配额模式监控不直接对接上游,而是关联一个已有账号,复用账号侧的用量服务 +// (AccountUsageService / CNProviderQuotaService / CNProviderBalanceService), +// 把各平台形态各异的用量数据归一成 MonitorQuotaSnapshot,随检测历史持久化 +// 到 channel_monitor_histories.quota(JSONB),供管理端与用户端渲染。 +// +// 类型放在 domain 包是因为 ent schema(internal/domain 的下游)需要引用它做 +// field.JSON 序列化;service 不能被 ent import(会造成循环依赖)。 + +// MonitorQuotaTier 单个用量窗口的快照。 +// +// Window 取值约定(与前端 monitorCommon.quota.windows.* 标签一一对应): +// - "5h" 5 小时滚动窗口(Claude/Codex/Kimi/Zhipu coding plan) +// - "7d" 7 天窗口(Claude/Codex) +// - "7d-sonnet" Claude 7 天 Sonnet 独立额度 +// - "7d-fable" Claude 7 天 Fable 独立额度 +// - "weekly" 周窗口(Kimi/Zhipu coding plan) +// - "30d" 30 天窗口(Grok 月度) +// - "total" 无窗口语义的总量额度(Gemini/Antigravity 等) +type MonitorQuotaTier struct { + Window string `json:"window"` + UsedPercent float64 `json:"used_percent"` // 0-100;仅有绝对值时按 used/limit 计算 + Used float64 `json:"used,omitempty"` + Limit float64 `json:"limit,omitempty"` + ResetAt string `json:"reset_at,omitempty"` // RFC3339;未知时留空 +} + +// MonitorQuotaSnapshot 一次配额查询的完整快照。 +// +// Source 取值: +// - "usage" 海外平台(AccountUsageService.GetUsage) +// - "cn_quota" 国产 Coding Plan(CNProviderQuotaService.QueryUsage) +// - "cn_balance" 国产按量付费余额(CNProviderBalanceService.QueryBalance) +type MonitorQuotaSnapshot struct { + Source string `json:"source"` + Success bool `json:"success"` + Tiers []MonitorQuotaTier `json:"tiers,omitempty"` + Balance *float64 `json:"balance,omitempty"` // cn_balance 主余额 + Balances []MonitorBalance `json:"balances,omitempty"` // 多币种余额(如 DeepSeek CNY+USD) + Currency string `json:"currency,omitempty"` // 主余额币种 + PlanLevel string `json:"plan_level,omitempty"` // 套餐等级(如智谱 level) + Error string `json:"error,omitempty"` // Success=false 时的错误摘要 + FetchedAt time.Time `json:"fetched_at"` +} + +// MonitorBalance 单币种余额条目。 +type MonitorBalance struct { + Currency string `json:"currency"` + Balance float64 `json:"balance"` +} diff --git a/backend/migrations/226_channel_monitor_quota_mode.sql b/backend/migrations/226_channel_monitor_quota_mode.sql new file mode 100644 index 0000000000..a0774e3dbf --- /dev/null +++ b/backend/migrations/226_channel_monitor_quota_mode.sql @@ -0,0 +1,78 @@ +-- Migration: 226_channel_monitor_quota_mode +-- 渠道监控配额模式: +-- 1. provider 扩容到全部 8 平台(antigravity/kimi/zhipu/deepseek) +-- (antigravity 仅支持配额模式,无探活 adapter;国产 3 家复用 OpenAI 兼容探活) +-- 2. check_mode:probe(默认,现状探活)/ quota(仅查关联账号用量,零 LLM 成本) +-- / quota_probe(探活 + 配额并存) +-- 3. account_id 关联已有账号(配额模式的数据源,复用账号侧用量服务); +-- 账号删除时置空,监控保留并报「账号未关联」 +-- 4. channel_monitor_histories.quota 持久化归一化配额快照(JSONB) +-- 5. 新增公开设置 channel_monitor_show_quota(默认关闭): +-- 控制用户端监控页是否展示配额/余额;管理端始终可见 + +DO $$ +DECLARE + monitor_constraint_def TEXT; + template_constraint_def TEXT; +BEGIN + SELECT pg_get_constraintdef(c.oid) + INTO monitor_constraint_def + FROM pg_constraint c + JOIN pg_class t ON t.oid = c.conrelid + WHERE t.relname = 'channel_monitors' + AND c.conname = 'channel_monitors_provider_check'; + + IF monitor_constraint_def IS NULL OR position('kimi' IN monitor_constraint_def) = 0 THEN + ALTER TABLE channel_monitors + DROP CONSTRAINT IF EXISTS channel_monitors_provider_check; + ALTER TABLE channel_monitors + ADD CONSTRAINT channel_monitors_provider_check + CHECK (provider IN ('openai', 'anthropic', 'gemini', 'grok', + 'antigravity', 'kimi', 'zhipu', 'deepseek')); + END IF; + + SELECT pg_get_constraintdef(c.oid) + INTO template_constraint_def + FROM pg_constraint c + JOIN pg_class t ON t.oid = c.conrelid + WHERE t.relname = 'channel_monitor_request_templates' + AND c.conname = 'channel_monitor_request_templates_provider_check'; + + IF template_constraint_def IS NULL OR position('kimi' IN template_constraint_def) = 0 THEN + ALTER TABLE channel_monitor_request_templates + DROP CONSTRAINT IF EXISTS channel_monitor_request_templates_provider_check; + ALTER TABLE channel_monitor_request_templates + ADD CONSTRAINT channel_monitor_request_templates_provider_check + CHECK (provider IN ('openai', 'anthropic', 'gemini', 'grok', + 'antigravity', 'kimi', 'zhipu', 'deepseek')); + END IF; +END $$; + +ALTER TABLE channel_monitors + ADD COLUMN IF NOT EXISTS check_mode VARCHAR(32) NOT NULL DEFAULT 'probe'; + +ALTER TABLE channel_monitors + ADD CONSTRAINT channel_monitors_check_mode_check + CHECK (check_mode IN ('probe', 'quota', 'quota_probe')); + +ALTER TABLE channel_monitors + ADD COLUMN IF NOT EXISTS account_id BIGINT REFERENCES accounts(id) ON DELETE SET NULL; + +CREATE INDEX IF NOT EXISTS idx_channel_monitors_account_id ON channel_monitors(account_id); + +COMMENT ON COLUMN channel_monitors.check_mode IS + 'probe = LLM 探活(默认);quota = 仅查关联账号用量;quota_probe = 探活 + 配额'; +COMMENT ON COLUMN channel_monitors.account_id IS + '配额模式关联的账号 ID(数据源);账号删除时置空'; + +ALTER TABLE channel_monitor_histories + ADD COLUMN IF NOT EXISTS quota JSONB; + +COMMENT ON COLUMN channel_monitor_histories.quota IS + '配额模式监控的归一化配额快照(domain.MonitorQuotaSnapshot);探活模式为 NULL'; + +-- 用户端是否展示配额/余额(默认关闭,fail-closed 解析:仅 "true" 视为开启)。 +-- 管理端不受此开关影响。 +INSERT INTO settings (key, value) +VALUES ('channel_monitor_show_quota', 'false') +ON CONFLICT (key) DO NOTHING;