feat(group): 订阅分组新增可选的高峰时段倍率,以支持智谱等coding plan的高峰时段

This commit is contained in:
xueshiji
2026-06-30 14:17:05 +08:00
parent 345d5c6b56
commit 915c60b150
24 changed files with 1699 additions and 55 deletions
+47 -3
View File
@@ -31,6 +31,14 @@ type Group struct {
Description *string `json:"description,omitempty"`
// RateMultiplier holds the value of the "rate_multiplier" field.
RateMultiplier float64 `json:"rate_multiplier,omitempty"`
// 是否启用高峰时段倍率
PeakRateEnabled bool `json:"peak_rate_enabled,omitempty"`
// 高峰开始时间 HH:MM(含),如 14:00;空表示未配置
PeakStart string `json:"peak_start,omitempty"`
// 高峰结束时间 HH:MM(不含),如 18:00
PeakEnd string `json:"peak_end,omitempty"`
// 高峰时段叠加倍率,仅在 peak_rate_enabled 且处于 [peak_start, peak_end) 时乘入文本倍率
PeakRateMultiplier float64 `json:"peak_rate_multiplier,omitempty"`
// IsExclusive holds the value of the "is_exclusive" field.
IsExclusive bool `json:"is_exclusive,omitempty"`
// Status holds the value of the "status" field.
@@ -197,13 +205,13 @@ func (*Group) scanValues(columns []string) ([]any, error) {
switch columns[i] {
case group.FieldModelRouting, group.FieldSupportedModelScopes, group.FieldMessagesDispatchModelConfig, group.FieldModelsListConfig:
values[i] = new([]byte)
case group.FieldIsExclusive, group.FieldAllowImageGeneration, group.FieldImageRateIndependent, group.FieldClaudeCodeOnly, group.FieldModelRoutingEnabled, group.FieldMcpXMLInject, group.FieldAllowMessagesDispatch, group.FieldRequireOauthOnly, group.FieldRequirePrivacySet:
case group.FieldPeakRateEnabled, group.FieldIsExclusive, group.FieldAllowImageGeneration, group.FieldImageRateIndependent, group.FieldClaudeCodeOnly, group.FieldModelRoutingEnabled, group.FieldMcpXMLInject, group.FieldAllowMessagesDispatch, group.FieldRequireOauthOnly, group.FieldRequirePrivacySet:
values[i] = new(sql.NullBool)
case group.FieldRateMultiplier, group.FieldDailyLimitUsd, group.FieldWeeklyLimitUsd, group.FieldMonthlyLimitUsd, group.FieldImageRateMultiplier, group.FieldImagePrice1k, group.FieldImagePrice2k, group.FieldImagePrice4k:
case group.FieldRateMultiplier, group.FieldPeakRateMultiplier, group.FieldDailyLimitUsd, group.FieldWeeklyLimitUsd, group.FieldMonthlyLimitUsd, group.FieldImageRateMultiplier, group.FieldImagePrice1k, group.FieldImagePrice2k, group.FieldImagePrice4k:
values[i] = new(sql.NullFloat64)
case group.FieldID, group.FieldDefaultValidityDays, group.FieldFallbackGroupID, group.FieldFallbackGroupIDOnInvalidRequest, group.FieldSortOrder, group.FieldRpmLimit:
values[i] = new(sql.NullInt64)
case group.FieldName, group.FieldDescription, group.FieldStatus, group.FieldPlatform, group.FieldSubscriptionType, group.FieldDefaultMappedModel:
case group.FieldName, group.FieldDescription, group.FieldPeakStart, group.FieldPeakEnd, group.FieldStatus, group.FieldPlatform, group.FieldSubscriptionType, group.FieldDefaultMappedModel:
values[i] = new(sql.NullString)
case group.FieldCreatedAt, group.FieldUpdatedAt, group.FieldDeletedAt:
values[i] = new(sql.NullTime)
@@ -266,6 +274,30 @@ func (_m *Group) assignValues(columns []string, values []any) error {
} else if value.Valid {
_m.RateMultiplier = value.Float64
}
case group.FieldPeakRateEnabled:
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field peak_rate_enabled", values[i])
} else if value.Valid {
_m.PeakRateEnabled = value.Bool
}
case group.FieldPeakStart:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field peak_start", values[i])
} else if value.Valid {
_m.PeakStart = value.String
}
case group.FieldPeakEnd:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field peak_end", values[i])
} else if value.Valid {
_m.PeakEnd = value.String
}
case group.FieldPeakRateMultiplier:
if value, ok := values[i].(*sql.NullFloat64); !ok {
return fmt.Errorf("unexpected type %T for field peak_rate_multiplier", values[i])
} else if value.Valid {
_m.PeakRateMultiplier = value.Float64
}
case group.FieldIsExclusive:
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field is_exclusive", values[i])
@@ -554,6 +586,18 @@ func (_m *Group) String() string {
builder.WriteString("rate_multiplier=")
builder.WriteString(fmt.Sprintf("%v", _m.RateMultiplier))
builder.WriteString(", ")
builder.WriteString("peak_rate_enabled=")
builder.WriteString(fmt.Sprintf("%v", _m.PeakRateEnabled))
builder.WriteString(", ")
builder.WriteString("peak_start=")
builder.WriteString(_m.PeakStart)
builder.WriteString(", ")
builder.WriteString("peak_end=")
builder.WriteString(_m.PeakEnd)
builder.WriteString(", ")
builder.WriteString("peak_rate_multiplier=")
builder.WriteString(fmt.Sprintf("%v", _m.PeakRateMultiplier))
builder.WriteString(", ")
builder.WriteString("is_exclusive=")
builder.WriteString(fmt.Sprintf("%v", _m.IsExclusive))
builder.WriteString(", ")
+44
View File
@@ -28,6 +28,14 @@ const (
FieldDescription = "description"
// FieldRateMultiplier holds the string denoting the rate_multiplier field in the database.
FieldRateMultiplier = "rate_multiplier"
// FieldPeakRateEnabled holds the string denoting the peak_rate_enabled field in the database.
FieldPeakRateEnabled = "peak_rate_enabled"
// FieldPeakStart holds the string denoting the peak_start field in the database.
FieldPeakStart = "peak_start"
// FieldPeakEnd holds the string denoting the peak_end field in the database.
FieldPeakEnd = "peak_end"
// FieldPeakRateMultiplier holds the string denoting the peak_rate_multiplier field in the database.
FieldPeakRateMultiplier = "peak_rate_multiplier"
// FieldIsExclusive holds the string denoting the is_exclusive field in the database.
FieldIsExclusive = "is_exclusive"
// FieldStatus holds the string denoting the status field in the database.
@@ -167,6 +175,10 @@ var Columns = []string{
FieldName,
FieldDescription,
FieldRateMultiplier,
FieldPeakRateEnabled,
FieldPeakStart,
FieldPeakEnd,
FieldPeakRateMultiplier,
FieldIsExclusive,
FieldStatus,
FieldPlatform,
@@ -235,6 +247,18 @@ var (
NameValidator func(string) error
// DefaultRateMultiplier holds the default value on creation for the "rate_multiplier" field.
DefaultRateMultiplier float64
// DefaultPeakRateEnabled holds the default value on creation for the "peak_rate_enabled" field.
DefaultPeakRateEnabled bool
// DefaultPeakStart holds the default value on creation for the "peak_start" field.
DefaultPeakStart string
// PeakStartValidator is a validator for the "peak_start" field. It is called by the builders before save.
PeakStartValidator func(string) error
// DefaultPeakEnd holds the default value on creation for the "peak_end" field.
DefaultPeakEnd string
// PeakEndValidator is a validator for the "peak_end" field. It is called by the builders before save.
PeakEndValidator func(string) error
// DefaultPeakRateMultiplier holds the default value on creation for the "peak_rate_multiplier" field.
DefaultPeakRateMultiplier float64
// DefaultIsExclusive holds the default value on creation for the "is_exclusive" field.
DefaultIsExclusive bool
// DefaultStatus holds the default value on creation for the "status" field.
@@ -323,6 +347,26 @@ func ByRateMultiplier(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldRateMultiplier, opts...).ToFunc()
}
// ByPeakRateEnabled orders the results by the peak_rate_enabled field.
func ByPeakRateEnabled(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPeakRateEnabled, opts...).ToFunc()
}
// ByPeakStart orders the results by the peak_start field.
func ByPeakStart(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPeakStart, opts...).ToFunc()
}
// ByPeakEnd orders the results by the peak_end field.
func ByPeakEnd(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPeakEnd, opts...).ToFunc()
}
// ByPeakRateMultiplier orders the results by the peak_rate_multiplier field.
func ByPeakRateMultiplier(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPeakRateMultiplier, opts...).ToFunc()
}
// ByIsExclusive orders the results by the is_exclusive field.
func ByIsExclusive(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldIsExclusive, opts...).ToFunc()
+200
View File
@@ -85,6 +85,26 @@ func RateMultiplier(v float64) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldRateMultiplier, v))
}
// PeakRateEnabled applies equality check predicate on the "peak_rate_enabled" field. It's identical to PeakRateEnabledEQ.
func PeakRateEnabled(v bool) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldPeakRateEnabled, v))
}
// PeakStart applies equality check predicate on the "peak_start" field. It's identical to PeakStartEQ.
func PeakStart(v string) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldPeakStart, v))
}
// PeakEnd applies equality check predicate on the "peak_end" field. It's identical to PeakEndEQ.
func PeakEnd(v string) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldPeakEnd, v))
}
// PeakRateMultiplier applies equality check predicate on the "peak_rate_multiplier" field. It's identical to PeakRateMultiplierEQ.
func PeakRateMultiplier(v float64) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldPeakRateMultiplier, v))
}
// IsExclusive applies equality check predicate on the "is_exclusive" field. It's identical to IsExclusiveEQ.
func IsExclusive(v bool) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldIsExclusive, v))
@@ -520,6 +540,186 @@ func RateMultiplierLTE(v float64) predicate.Group {
return predicate.Group(sql.FieldLTE(FieldRateMultiplier, v))
}
// PeakRateEnabledEQ applies the EQ predicate on the "peak_rate_enabled" field.
func PeakRateEnabledEQ(v bool) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldPeakRateEnabled, v))
}
// PeakRateEnabledNEQ applies the NEQ predicate on the "peak_rate_enabled" field.
func PeakRateEnabledNEQ(v bool) predicate.Group {
return predicate.Group(sql.FieldNEQ(FieldPeakRateEnabled, v))
}
// PeakStartEQ applies the EQ predicate on the "peak_start" field.
func PeakStartEQ(v string) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldPeakStart, v))
}
// PeakStartNEQ applies the NEQ predicate on the "peak_start" field.
func PeakStartNEQ(v string) predicate.Group {
return predicate.Group(sql.FieldNEQ(FieldPeakStart, v))
}
// PeakStartIn applies the In predicate on the "peak_start" field.
func PeakStartIn(vs ...string) predicate.Group {
return predicate.Group(sql.FieldIn(FieldPeakStart, vs...))
}
// PeakStartNotIn applies the NotIn predicate on the "peak_start" field.
func PeakStartNotIn(vs ...string) predicate.Group {
return predicate.Group(sql.FieldNotIn(FieldPeakStart, vs...))
}
// PeakStartGT applies the GT predicate on the "peak_start" field.
func PeakStartGT(v string) predicate.Group {
return predicate.Group(sql.FieldGT(FieldPeakStart, v))
}
// PeakStartGTE applies the GTE predicate on the "peak_start" field.
func PeakStartGTE(v string) predicate.Group {
return predicate.Group(sql.FieldGTE(FieldPeakStart, v))
}
// PeakStartLT applies the LT predicate on the "peak_start" field.
func PeakStartLT(v string) predicate.Group {
return predicate.Group(sql.FieldLT(FieldPeakStart, v))
}
// PeakStartLTE applies the LTE predicate on the "peak_start" field.
func PeakStartLTE(v string) predicate.Group {
return predicate.Group(sql.FieldLTE(FieldPeakStart, v))
}
// PeakStartContains applies the Contains predicate on the "peak_start" field.
func PeakStartContains(v string) predicate.Group {
return predicate.Group(sql.FieldContains(FieldPeakStart, v))
}
// PeakStartHasPrefix applies the HasPrefix predicate on the "peak_start" field.
func PeakStartHasPrefix(v string) predicate.Group {
return predicate.Group(sql.FieldHasPrefix(FieldPeakStart, v))
}
// PeakStartHasSuffix applies the HasSuffix predicate on the "peak_start" field.
func PeakStartHasSuffix(v string) predicate.Group {
return predicate.Group(sql.FieldHasSuffix(FieldPeakStart, v))
}
// PeakStartEqualFold applies the EqualFold predicate on the "peak_start" field.
func PeakStartEqualFold(v string) predicate.Group {
return predicate.Group(sql.FieldEqualFold(FieldPeakStart, v))
}
// PeakStartContainsFold applies the ContainsFold predicate on the "peak_start" field.
func PeakStartContainsFold(v string) predicate.Group {
return predicate.Group(sql.FieldContainsFold(FieldPeakStart, v))
}
// PeakEndEQ applies the EQ predicate on the "peak_end" field.
func PeakEndEQ(v string) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldPeakEnd, v))
}
// PeakEndNEQ applies the NEQ predicate on the "peak_end" field.
func PeakEndNEQ(v string) predicate.Group {
return predicate.Group(sql.FieldNEQ(FieldPeakEnd, v))
}
// PeakEndIn applies the In predicate on the "peak_end" field.
func PeakEndIn(vs ...string) predicate.Group {
return predicate.Group(sql.FieldIn(FieldPeakEnd, vs...))
}
// PeakEndNotIn applies the NotIn predicate on the "peak_end" field.
func PeakEndNotIn(vs ...string) predicate.Group {
return predicate.Group(sql.FieldNotIn(FieldPeakEnd, vs...))
}
// PeakEndGT applies the GT predicate on the "peak_end" field.
func PeakEndGT(v string) predicate.Group {
return predicate.Group(sql.FieldGT(FieldPeakEnd, v))
}
// PeakEndGTE applies the GTE predicate on the "peak_end" field.
func PeakEndGTE(v string) predicate.Group {
return predicate.Group(sql.FieldGTE(FieldPeakEnd, v))
}
// PeakEndLT applies the LT predicate on the "peak_end" field.
func PeakEndLT(v string) predicate.Group {
return predicate.Group(sql.FieldLT(FieldPeakEnd, v))
}
// PeakEndLTE applies the LTE predicate on the "peak_end" field.
func PeakEndLTE(v string) predicate.Group {
return predicate.Group(sql.FieldLTE(FieldPeakEnd, v))
}
// PeakEndContains applies the Contains predicate on the "peak_end" field.
func PeakEndContains(v string) predicate.Group {
return predicate.Group(sql.FieldContains(FieldPeakEnd, v))
}
// PeakEndHasPrefix applies the HasPrefix predicate on the "peak_end" field.
func PeakEndHasPrefix(v string) predicate.Group {
return predicate.Group(sql.FieldHasPrefix(FieldPeakEnd, v))
}
// PeakEndHasSuffix applies the HasSuffix predicate on the "peak_end" field.
func PeakEndHasSuffix(v string) predicate.Group {
return predicate.Group(sql.FieldHasSuffix(FieldPeakEnd, v))
}
// PeakEndEqualFold applies the EqualFold predicate on the "peak_end" field.
func PeakEndEqualFold(v string) predicate.Group {
return predicate.Group(sql.FieldEqualFold(FieldPeakEnd, v))
}
// PeakEndContainsFold applies the ContainsFold predicate on the "peak_end" field.
func PeakEndContainsFold(v string) predicate.Group {
return predicate.Group(sql.FieldContainsFold(FieldPeakEnd, v))
}
// PeakRateMultiplierEQ applies the EQ predicate on the "peak_rate_multiplier" field.
func PeakRateMultiplierEQ(v float64) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldPeakRateMultiplier, v))
}
// PeakRateMultiplierNEQ applies the NEQ predicate on the "peak_rate_multiplier" field.
func PeakRateMultiplierNEQ(v float64) predicate.Group {
return predicate.Group(sql.FieldNEQ(FieldPeakRateMultiplier, v))
}
// PeakRateMultiplierIn applies the In predicate on the "peak_rate_multiplier" field.
func PeakRateMultiplierIn(vs ...float64) predicate.Group {
return predicate.Group(sql.FieldIn(FieldPeakRateMultiplier, vs...))
}
// PeakRateMultiplierNotIn applies the NotIn predicate on the "peak_rate_multiplier" field.
func PeakRateMultiplierNotIn(vs ...float64) predicate.Group {
return predicate.Group(sql.FieldNotIn(FieldPeakRateMultiplier, vs...))
}
// PeakRateMultiplierGT applies the GT predicate on the "peak_rate_multiplier" field.
func PeakRateMultiplierGT(v float64) predicate.Group {
return predicate.Group(sql.FieldGT(FieldPeakRateMultiplier, v))
}
// PeakRateMultiplierGTE applies the GTE predicate on the "peak_rate_multiplier" field.
func PeakRateMultiplierGTE(v float64) predicate.Group {
return predicate.Group(sql.FieldGTE(FieldPeakRateMultiplier, v))
}
// PeakRateMultiplierLT applies the LT predicate on the "peak_rate_multiplier" field.
func PeakRateMultiplierLT(v float64) predicate.Group {
return predicate.Group(sql.FieldLT(FieldPeakRateMultiplier, v))
}
// PeakRateMultiplierLTE applies the LTE predicate on the "peak_rate_multiplier" field.
func PeakRateMultiplierLTE(v float64) predicate.Group {
return predicate.Group(sql.FieldLTE(FieldPeakRateMultiplier, v))
}
// IsExclusiveEQ applies the EQ predicate on the "is_exclusive" field.
func IsExclusiveEQ(v bool) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldIsExclusive, v))
+290
View File
@@ -105,6 +105,62 @@ func (_c *GroupCreate) SetNillableRateMultiplier(v *float64) *GroupCreate {
return _c
}
// SetPeakRateEnabled sets the "peak_rate_enabled" field.
func (_c *GroupCreate) SetPeakRateEnabled(v bool) *GroupCreate {
_c.mutation.SetPeakRateEnabled(v)
return _c
}
// SetNillablePeakRateEnabled sets the "peak_rate_enabled" field if the given value is not nil.
func (_c *GroupCreate) SetNillablePeakRateEnabled(v *bool) *GroupCreate {
if v != nil {
_c.SetPeakRateEnabled(*v)
}
return _c
}
// SetPeakStart sets the "peak_start" field.
func (_c *GroupCreate) SetPeakStart(v string) *GroupCreate {
_c.mutation.SetPeakStart(v)
return _c
}
// SetNillablePeakStart sets the "peak_start" field if the given value is not nil.
func (_c *GroupCreate) SetNillablePeakStart(v *string) *GroupCreate {
if v != nil {
_c.SetPeakStart(*v)
}
return _c
}
// SetPeakEnd sets the "peak_end" field.
func (_c *GroupCreate) SetPeakEnd(v string) *GroupCreate {
_c.mutation.SetPeakEnd(v)
return _c
}
// SetNillablePeakEnd sets the "peak_end" field if the given value is not nil.
func (_c *GroupCreate) SetNillablePeakEnd(v *string) *GroupCreate {
if v != nil {
_c.SetPeakEnd(*v)
}
return _c
}
// SetPeakRateMultiplier sets the "peak_rate_multiplier" field.
func (_c *GroupCreate) SetPeakRateMultiplier(v float64) *GroupCreate {
_c.mutation.SetPeakRateMultiplier(v)
return _c
}
// SetNillablePeakRateMultiplier sets the "peak_rate_multiplier" field if the given value is not nil.
func (_c *GroupCreate) SetNillablePeakRateMultiplier(v *float64) *GroupCreate {
if v != nil {
_c.SetPeakRateMultiplier(*v)
}
return _c
}
// SetIsExclusive sets the "is_exclusive" field.
func (_c *GroupCreate) SetIsExclusive(v bool) *GroupCreate {
_c.mutation.SetIsExclusive(v)
@@ -640,6 +696,22 @@ func (_c *GroupCreate) defaults() error {
v := group.DefaultRateMultiplier
_c.mutation.SetRateMultiplier(v)
}
if _, ok := _c.mutation.PeakRateEnabled(); !ok {
v := group.DefaultPeakRateEnabled
_c.mutation.SetPeakRateEnabled(v)
}
if _, ok := _c.mutation.PeakStart(); !ok {
v := group.DefaultPeakStart
_c.mutation.SetPeakStart(v)
}
if _, ok := _c.mutation.PeakEnd(); !ok {
v := group.DefaultPeakEnd
_c.mutation.SetPeakEnd(v)
}
if _, ok := _c.mutation.PeakRateMultiplier(); !ok {
v := group.DefaultPeakRateMultiplier
_c.mutation.SetPeakRateMultiplier(v)
}
if _, ok := _c.mutation.IsExclusive(); !ok {
v := group.DefaultIsExclusive
_c.mutation.SetIsExclusive(v)
@@ -742,6 +814,28 @@ func (_c *GroupCreate) check() error {
if _, ok := _c.mutation.RateMultiplier(); !ok {
return &ValidationError{Name: "rate_multiplier", err: errors.New(`ent: missing required field "Group.rate_multiplier"`)}
}
if _, ok := _c.mutation.PeakRateEnabled(); !ok {
return &ValidationError{Name: "peak_rate_enabled", err: errors.New(`ent: missing required field "Group.peak_rate_enabled"`)}
}
if _, ok := _c.mutation.PeakStart(); !ok {
return &ValidationError{Name: "peak_start", err: errors.New(`ent: missing required field "Group.peak_start"`)}
}
if v, ok := _c.mutation.PeakStart(); ok {
if err := group.PeakStartValidator(v); err != nil {
return &ValidationError{Name: "peak_start", err: fmt.Errorf(`ent: validator failed for field "Group.peak_start": %w`, err)}
}
}
if _, ok := _c.mutation.PeakEnd(); !ok {
return &ValidationError{Name: "peak_end", err: errors.New(`ent: missing required field "Group.peak_end"`)}
}
if v, ok := _c.mutation.PeakEnd(); ok {
if err := group.PeakEndValidator(v); err != nil {
return &ValidationError{Name: "peak_end", err: fmt.Errorf(`ent: validator failed for field "Group.peak_end": %w`, err)}
}
}
if _, ok := _c.mutation.PeakRateMultiplier(); !ok {
return &ValidationError{Name: "peak_rate_multiplier", err: errors.New(`ent: missing required field "Group.peak_rate_multiplier"`)}
}
if _, ok := _c.mutation.IsExclusive(); !ok {
return &ValidationError{Name: "is_exclusive", err: errors.New(`ent: missing required field "Group.is_exclusive"`)}
}
@@ -873,6 +967,22 @@ func (_c *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
_spec.SetField(group.FieldRateMultiplier, field.TypeFloat64, value)
_node.RateMultiplier = value
}
if value, ok := _c.mutation.PeakRateEnabled(); ok {
_spec.SetField(group.FieldPeakRateEnabled, field.TypeBool, value)
_node.PeakRateEnabled = value
}
if value, ok := _c.mutation.PeakStart(); ok {
_spec.SetField(group.FieldPeakStart, field.TypeString, value)
_node.PeakStart = value
}
if value, ok := _c.mutation.PeakEnd(); ok {
_spec.SetField(group.FieldPeakEnd, field.TypeString, value)
_node.PeakEnd = value
}
if value, ok := _c.mutation.PeakRateMultiplier(); ok {
_spec.SetField(group.FieldPeakRateMultiplier, field.TypeFloat64, value)
_node.PeakRateMultiplier = value
}
if value, ok := _c.mutation.IsExclusive(); ok {
_spec.SetField(group.FieldIsExclusive, field.TypeBool, value)
_node.IsExclusive = value
@@ -1223,6 +1333,60 @@ func (u *GroupUpsert) AddRateMultiplier(v float64) *GroupUpsert {
return u
}
// SetPeakRateEnabled sets the "peak_rate_enabled" field.
func (u *GroupUpsert) SetPeakRateEnabled(v bool) *GroupUpsert {
u.Set(group.FieldPeakRateEnabled, v)
return u
}
// UpdatePeakRateEnabled sets the "peak_rate_enabled" field to the value that was provided on create.
func (u *GroupUpsert) UpdatePeakRateEnabled() *GroupUpsert {
u.SetExcluded(group.FieldPeakRateEnabled)
return u
}
// SetPeakStart sets the "peak_start" field.
func (u *GroupUpsert) SetPeakStart(v string) *GroupUpsert {
u.Set(group.FieldPeakStart, v)
return u
}
// UpdatePeakStart sets the "peak_start" field to the value that was provided on create.
func (u *GroupUpsert) UpdatePeakStart() *GroupUpsert {
u.SetExcluded(group.FieldPeakStart)
return u
}
// SetPeakEnd sets the "peak_end" field.
func (u *GroupUpsert) SetPeakEnd(v string) *GroupUpsert {
u.Set(group.FieldPeakEnd, v)
return u
}
// UpdatePeakEnd sets the "peak_end" field to the value that was provided on create.
func (u *GroupUpsert) UpdatePeakEnd() *GroupUpsert {
u.SetExcluded(group.FieldPeakEnd)
return u
}
// SetPeakRateMultiplier sets the "peak_rate_multiplier" field.
func (u *GroupUpsert) SetPeakRateMultiplier(v float64) *GroupUpsert {
u.Set(group.FieldPeakRateMultiplier, v)
return u
}
// UpdatePeakRateMultiplier sets the "peak_rate_multiplier" field to the value that was provided on create.
func (u *GroupUpsert) UpdatePeakRateMultiplier() *GroupUpsert {
u.SetExcluded(group.FieldPeakRateMultiplier)
return u
}
// AddPeakRateMultiplier adds v to the "peak_rate_multiplier" field.
func (u *GroupUpsert) AddPeakRateMultiplier(v float64) *GroupUpsert {
u.Add(group.FieldPeakRateMultiplier, v)
return u
}
// SetIsExclusive sets the "is_exclusive" field.
func (u *GroupUpsert) SetIsExclusive(v bool) *GroupUpsert {
u.Set(group.FieldIsExclusive, v)
@@ -1833,6 +1997,69 @@ func (u *GroupUpsertOne) UpdateRateMultiplier() *GroupUpsertOne {
})
}
// SetPeakRateEnabled sets the "peak_rate_enabled" field.
func (u *GroupUpsertOne) SetPeakRateEnabled(v bool) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.SetPeakRateEnabled(v)
})
}
// UpdatePeakRateEnabled sets the "peak_rate_enabled" field to the value that was provided on create.
func (u *GroupUpsertOne) UpdatePeakRateEnabled() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.UpdatePeakRateEnabled()
})
}
// SetPeakStart sets the "peak_start" field.
func (u *GroupUpsertOne) SetPeakStart(v string) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.SetPeakStart(v)
})
}
// UpdatePeakStart sets the "peak_start" field to the value that was provided on create.
func (u *GroupUpsertOne) UpdatePeakStart() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.UpdatePeakStart()
})
}
// SetPeakEnd sets the "peak_end" field.
func (u *GroupUpsertOne) SetPeakEnd(v string) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.SetPeakEnd(v)
})
}
// UpdatePeakEnd sets the "peak_end" field to the value that was provided on create.
func (u *GroupUpsertOne) UpdatePeakEnd() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.UpdatePeakEnd()
})
}
// SetPeakRateMultiplier sets the "peak_rate_multiplier" field.
func (u *GroupUpsertOne) SetPeakRateMultiplier(v float64) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.SetPeakRateMultiplier(v)
})
}
// AddPeakRateMultiplier adds v to the "peak_rate_multiplier" field.
func (u *GroupUpsertOne) AddPeakRateMultiplier(v float64) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.AddPeakRateMultiplier(v)
})
}
// UpdatePeakRateMultiplier sets the "peak_rate_multiplier" field to the value that was provided on create.
func (u *GroupUpsertOne) UpdatePeakRateMultiplier() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.UpdatePeakRateMultiplier()
})
}
// SetIsExclusive sets the "is_exclusive" field.
func (u *GroupUpsertOne) SetIsExclusive(v bool) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
@@ -2688,6 +2915,69 @@ func (u *GroupUpsertBulk) UpdateRateMultiplier() *GroupUpsertBulk {
})
}
// SetPeakRateEnabled sets the "peak_rate_enabled" field.
func (u *GroupUpsertBulk) SetPeakRateEnabled(v bool) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.SetPeakRateEnabled(v)
})
}
// UpdatePeakRateEnabled sets the "peak_rate_enabled" field to the value that was provided on create.
func (u *GroupUpsertBulk) UpdatePeakRateEnabled() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.UpdatePeakRateEnabled()
})
}
// SetPeakStart sets the "peak_start" field.
func (u *GroupUpsertBulk) SetPeakStart(v string) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.SetPeakStart(v)
})
}
// UpdatePeakStart sets the "peak_start" field to the value that was provided on create.
func (u *GroupUpsertBulk) UpdatePeakStart() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.UpdatePeakStart()
})
}
// SetPeakEnd sets the "peak_end" field.
func (u *GroupUpsertBulk) SetPeakEnd(v string) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.SetPeakEnd(v)
})
}
// UpdatePeakEnd sets the "peak_end" field to the value that was provided on create.
func (u *GroupUpsertBulk) UpdatePeakEnd() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.UpdatePeakEnd()
})
}
// SetPeakRateMultiplier sets the "peak_rate_multiplier" field.
func (u *GroupUpsertBulk) SetPeakRateMultiplier(v float64) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.SetPeakRateMultiplier(v)
})
}
// AddPeakRateMultiplier adds v to the "peak_rate_multiplier" field.
func (u *GroupUpsertBulk) AddPeakRateMultiplier(v float64) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.AddPeakRateMultiplier(v)
})
}
// UpdatePeakRateMultiplier sets the "peak_rate_multiplier" field to the value that was provided on create.
func (u *GroupUpsertBulk) UpdatePeakRateMultiplier() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.UpdatePeakRateMultiplier()
})
}
// SetIsExclusive sets the "is_exclusive" field.
func (u *GroupUpsertBulk) SetIsExclusive(v bool) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
+176
View File
@@ -117,6 +117,69 @@ func (_u *GroupUpdate) AddRateMultiplier(v float64) *GroupUpdate {
return _u
}
// SetPeakRateEnabled sets the "peak_rate_enabled" field.
func (_u *GroupUpdate) SetPeakRateEnabled(v bool) *GroupUpdate {
_u.mutation.SetPeakRateEnabled(v)
return _u
}
// SetNillablePeakRateEnabled sets the "peak_rate_enabled" field if the given value is not nil.
func (_u *GroupUpdate) SetNillablePeakRateEnabled(v *bool) *GroupUpdate {
if v != nil {
_u.SetPeakRateEnabled(*v)
}
return _u
}
// SetPeakStart sets the "peak_start" field.
func (_u *GroupUpdate) SetPeakStart(v string) *GroupUpdate {
_u.mutation.SetPeakStart(v)
return _u
}
// SetNillablePeakStart sets the "peak_start" field if the given value is not nil.
func (_u *GroupUpdate) SetNillablePeakStart(v *string) *GroupUpdate {
if v != nil {
_u.SetPeakStart(*v)
}
return _u
}
// SetPeakEnd sets the "peak_end" field.
func (_u *GroupUpdate) SetPeakEnd(v string) *GroupUpdate {
_u.mutation.SetPeakEnd(v)
return _u
}
// SetNillablePeakEnd sets the "peak_end" field if the given value is not nil.
func (_u *GroupUpdate) SetNillablePeakEnd(v *string) *GroupUpdate {
if v != nil {
_u.SetPeakEnd(*v)
}
return _u
}
// SetPeakRateMultiplier sets the "peak_rate_multiplier" field.
func (_u *GroupUpdate) SetPeakRateMultiplier(v float64) *GroupUpdate {
_u.mutation.ResetPeakRateMultiplier()
_u.mutation.SetPeakRateMultiplier(v)
return _u
}
// SetNillablePeakRateMultiplier sets the "peak_rate_multiplier" field if the given value is not nil.
func (_u *GroupUpdate) SetNillablePeakRateMultiplier(v *float64) *GroupUpdate {
if v != nil {
_u.SetPeakRateMultiplier(*v)
}
return _u
}
// AddPeakRateMultiplier adds value to the "peak_rate_multiplier" field.
func (_u *GroupUpdate) AddPeakRateMultiplier(v float64) *GroupUpdate {
_u.mutation.AddPeakRateMultiplier(v)
return _u
}
// SetIsExclusive sets the "is_exclusive" field.
func (_u *GroupUpdate) SetIsExclusive(v bool) *GroupUpdate {
_u.mutation.SetIsExclusive(v)
@@ -921,6 +984,16 @@ func (_u *GroupUpdate) check() error {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Group.name": %w`, err)}
}
}
if v, ok := _u.mutation.PeakStart(); ok {
if err := group.PeakStartValidator(v); err != nil {
return &ValidationError{Name: "peak_start", err: fmt.Errorf(`ent: validator failed for field "Group.peak_start": %w`, err)}
}
}
if v, ok := _u.mutation.PeakEnd(); ok {
if err := group.PeakEndValidator(v); err != nil {
return &ValidationError{Name: "peak_end", err: fmt.Errorf(`ent: validator failed for field "Group.peak_end": %w`, err)}
}
}
if v, ok := _u.mutation.Status(); ok {
if err := group.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Group.status": %w`, err)}
@@ -980,6 +1053,21 @@ func (_u *GroupUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if value, ok := _u.mutation.AddedRateMultiplier(); ok {
_spec.AddField(group.FieldRateMultiplier, field.TypeFloat64, value)
}
if value, ok := _u.mutation.PeakRateEnabled(); ok {
_spec.SetField(group.FieldPeakRateEnabled, field.TypeBool, value)
}
if value, ok := _u.mutation.PeakStart(); ok {
_spec.SetField(group.FieldPeakStart, field.TypeString, value)
}
if value, ok := _u.mutation.PeakEnd(); ok {
_spec.SetField(group.FieldPeakEnd, field.TypeString, value)
}
if value, ok := _u.mutation.PeakRateMultiplier(); ok {
_spec.SetField(group.FieldPeakRateMultiplier, field.TypeFloat64, value)
}
if value, ok := _u.mutation.AddedPeakRateMultiplier(); ok {
_spec.AddField(group.FieldPeakRateMultiplier, field.TypeFloat64, value)
}
if value, ok := _u.mutation.IsExclusive(); ok {
_spec.SetField(group.FieldIsExclusive, field.TypeBool, value)
}
@@ -1530,6 +1618,69 @@ func (_u *GroupUpdateOne) AddRateMultiplier(v float64) *GroupUpdateOne {
return _u
}
// SetPeakRateEnabled sets the "peak_rate_enabled" field.
func (_u *GroupUpdateOne) SetPeakRateEnabled(v bool) *GroupUpdateOne {
_u.mutation.SetPeakRateEnabled(v)
return _u
}
// SetNillablePeakRateEnabled sets the "peak_rate_enabled" field if the given value is not nil.
func (_u *GroupUpdateOne) SetNillablePeakRateEnabled(v *bool) *GroupUpdateOne {
if v != nil {
_u.SetPeakRateEnabled(*v)
}
return _u
}
// SetPeakStart sets the "peak_start" field.
func (_u *GroupUpdateOne) SetPeakStart(v string) *GroupUpdateOne {
_u.mutation.SetPeakStart(v)
return _u
}
// SetNillablePeakStart sets the "peak_start" field if the given value is not nil.
func (_u *GroupUpdateOne) SetNillablePeakStart(v *string) *GroupUpdateOne {
if v != nil {
_u.SetPeakStart(*v)
}
return _u
}
// SetPeakEnd sets the "peak_end" field.
func (_u *GroupUpdateOne) SetPeakEnd(v string) *GroupUpdateOne {
_u.mutation.SetPeakEnd(v)
return _u
}
// SetNillablePeakEnd sets the "peak_end" field if the given value is not nil.
func (_u *GroupUpdateOne) SetNillablePeakEnd(v *string) *GroupUpdateOne {
if v != nil {
_u.SetPeakEnd(*v)
}
return _u
}
// SetPeakRateMultiplier sets the "peak_rate_multiplier" field.
func (_u *GroupUpdateOne) SetPeakRateMultiplier(v float64) *GroupUpdateOne {
_u.mutation.ResetPeakRateMultiplier()
_u.mutation.SetPeakRateMultiplier(v)
return _u
}
// SetNillablePeakRateMultiplier sets the "peak_rate_multiplier" field if the given value is not nil.
func (_u *GroupUpdateOne) SetNillablePeakRateMultiplier(v *float64) *GroupUpdateOne {
if v != nil {
_u.SetPeakRateMultiplier(*v)
}
return _u
}
// AddPeakRateMultiplier adds value to the "peak_rate_multiplier" field.
func (_u *GroupUpdateOne) AddPeakRateMultiplier(v float64) *GroupUpdateOne {
_u.mutation.AddPeakRateMultiplier(v)
return _u
}
// SetIsExclusive sets the "is_exclusive" field.
func (_u *GroupUpdateOne) SetIsExclusive(v bool) *GroupUpdateOne {
_u.mutation.SetIsExclusive(v)
@@ -2347,6 +2498,16 @@ func (_u *GroupUpdateOne) check() error {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Group.name": %w`, err)}
}
}
if v, ok := _u.mutation.PeakStart(); ok {
if err := group.PeakStartValidator(v); err != nil {
return &ValidationError{Name: "peak_start", err: fmt.Errorf(`ent: validator failed for field "Group.peak_start": %w`, err)}
}
}
if v, ok := _u.mutation.PeakEnd(); ok {
if err := group.PeakEndValidator(v); err != nil {
return &ValidationError{Name: "peak_end", err: fmt.Errorf(`ent: validator failed for field "Group.peak_end": %w`, err)}
}
}
if v, ok := _u.mutation.Status(); ok {
if err := group.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Group.status": %w`, err)}
@@ -2423,6 +2584,21 @@ func (_u *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error)
if value, ok := _u.mutation.AddedRateMultiplier(); ok {
_spec.AddField(group.FieldRateMultiplier, field.TypeFloat64, value)
}
if value, ok := _u.mutation.PeakRateEnabled(); ok {
_spec.SetField(group.FieldPeakRateEnabled, field.TypeBool, value)
}
if value, ok := _u.mutation.PeakStart(); ok {
_spec.SetField(group.FieldPeakStart, field.TypeString, value)
}
if value, ok := _u.mutation.PeakEnd(); ok {
_spec.SetField(group.FieldPeakEnd, field.TypeString, value)
}
if value, ok := _u.mutation.PeakRateMultiplier(); ok {
_spec.SetField(group.FieldPeakRateMultiplier, field.TypeFloat64, value)
}
if value, ok := _u.mutation.AddedPeakRateMultiplier(); ok {
_spec.AddField(group.FieldPeakRateMultiplier, field.TypeFloat64, value)
}
if value, ok := _u.mutation.IsExclusive(); ok {
_spec.SetField(group.FieldIsExclusive, field.TypeBool, value)
}
+9 -5
View File
@@ -644,6 +644,10 @@ var (
{Name: "name", Type: field.TypeString, Size: 100},
{Name: "description", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
{Name: "rate_multiplier", Type: field.TypeFloat64, Default: 1, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
{Name: "peak_rate_enabled", Type: field.TypeBool, Default: false},
{Name: "peak_start", Type: field.TypeString, Size: 5, Default: ""},
{Name: "peak_end", Type: field.TypeString, Size: 5, Default: ""},
{Name: "peak_rate_multiplier", Type: field.TypeFloat64, Default: 1, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
{Name: "is_exclusive", Type: field.TypeBool, Default: false},
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
{Name: "platform", Type: field.TypeString, Size: 50, Default: "anthropic"},
@@ -683,22 +687,22 @@ var (
{
Name: "group_status",
Unique: false,
Columns: []*schema.Column{GroupsColumns[8]},
Columns: []*schema.Column{GroupsColumns[12]},
},
{
Name: "group_platform",
Unique: false,
Columns: []*schema.Column{GroupsColumns[9]},
Columns: []*schema.Column{GroupsColumns[13]},
},
{
Name: "group_subscription_type",
Unique: false,
Columns: []*schema.Column{GroupsColumns[10]},
Columns: []*schema.Column{GroupsColumns[14]},
},
{
Name: "group_is_exclusive",
Unique: false,
Columns: []*schema.Column{GroupsColumns[7]},
Columns: []*schema.Column{GroupsColumns[11]},
},
{
Name: "group_deleted_at",
@@ -708,7 +712,7 @@ var (
{
Name: "group_sort_order",
Unique: false,
Columns: []*schema.Column{GroupsColumns[28]},
Columns: []*schema.Column{GroupsColumns[32]},
},
},
}
+250 -1
View File
@@ -15056,6 +15056,11 @@ type GroupMutation struct {
description *string
rate_multiplier *float64
addrate_multiplier *float64
peak_rate_enabled *bool
peak_start *string
peak_end *string
peak_rate_multiplier *float64
addpeak_rate_multiplier *float64
is_exclusive *bool
status *string
platform *string
@@ -15482,6 +15487,170 @@ func (m *GroupMutation) ResetRateMultiplier() {
m.addrate_multiplier = nil
}
// SetPeakRateEnabled sets the "peak_rate_enabled" field.
func (m *GroupMutation) SetPeakRateEnabled(b bool) {
m.peak_rate_enabled = &b
}
// PeakRateEnabled returns the value of the "peak_rate_enabled" field in the mutation.
func (m *GroupMutation) PeakRateEnabled() (r bool, exists bool) {
v := m.peak_rate_enabled
if v == nil {
return
}
return *v, true
}
// OldPeakRateEnabled returns the old "peak_rate_enabled" field's value of the Group entity.
// If the Group 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 *GroupMutation) OldPeakRateEnabled(ctx context.Context) (v bool, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldPeakRateEnabled is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldPeakRateEnabled requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldPeakRateEnabled: %w", err)
}
return oldValue.PeakRateEnabled, nil
}
// ResetPeakRateEnabled resets all changes to the "peak_rate_enabled" field.
func (m *GroupMutation) ResetPeakRateEnabled() {
m.peak_rate_enabled = nil
}
// SetPeakStart sets the "peak_start" field.
func (m *GroupMutation) SetPeakStart(s string) {
m.peak_start = &s
}
// PeakStart returns the value of the "peak_start" field in the mutation.
func (m *GroupMutation) PeakStart() (r string, exists bool) {
v := m.peak_start
if v == nil {
return
}
return *v, true
}
// OldPeakStart returns the old "peak_start" field's value of the Group entity.
// If the Group 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 *GroupMutation) OldPeakStart(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldPeakStart is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldPeakStart requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldPeakStart: %w", err)
}
return oldValue.PeakStart, nil
}
// ResetPeakStart resets all changes to the "peak_start" field.
func (m *GroupMutation) ResetPeakStart() {
m.peak_start = nil
}
// SetPeakEnd sets the "peak_end" field.
func (m *GroupMutation) SetPeakEnd(s string) {
m.peak_end = &s
}
// PeakEnd returns the value of the "peak_end" field in the mutation.
func (m *GroupMutation) PeakEnd() (r string, exists bool) {
v := m.peak_end
if v == nil {
return
}
return *v, true
}
// OldPeakEnd returns the old "peak_end" field's value of the Group entity.
// If the Group 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 *GroupMutation) OldPeakEnd(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldPeakEnd is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldPeakEnd requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldPeakEnd: %w", err)
}
return oldValue.PeakEnd, nil
}
// ResetPeakEnd resets all changes to the "peak_end" field.
func (m *GroupMutation) ResetPeakEnd() {
m.peak_end = nil
}
// SetPeakRateMultiplier sets the "peak_rate_multiplier" field.
func (m *GroupMutation) SetPeakRateMultiplier(f float64) {
m.peak_rate_multiplier = &f
m.addpeak_rate_multiplier = nil
}
// PeakRateMultiplier returns the value of the "peak_rate_multiplier" field in the mutation.
func (m *GroupMutation) PeakRateMultiplier() (r float64, exists bool) {
v := m.peak_rate_multiplier
if v == nil {
return
}
return *v, true
}
// OldPeakRateMultiplier returns the old "peak_rate_multiplier" field's value of the Group entity.
// If the Group 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 *GroupMutation) OldPeakRateMultiplier(ctx context.Context) (v float64, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldPeakRateMultiplier is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldPeakRateMultiplier requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldPeakRateMultiplier: %w", err)
}
return oldValue.PeakRateMultiplier, nil
}
// AddPeakRateMultiplier adds f to the "peak_rate_multiplier" field.
func (m *GroupMutation) AddPeakRateMultiplier(f float64) {
if m.addpeak_rate_multiplier != nil {
*m.addpeak_rate_multiplier += f
} else {
m.addpeak_rate_multiplier = &f
}
}
// AddedPeakRateMultiplier returns the value that was added to the "peak_rate_multiplier" field in this mutation.
func (m *GroupMutation) AddedPeakRateMultiplier() (r float64, exists bool) {
v := m.addpeak_rate_multiplier
if v == nil {
return
}
return *v, true
}
// ResetPeakRateMultiplier resets all changes to the "peak_rate_multiplier" field.
func (m *GroupMutation) ResetPeakRateMultiplier() {
m.peak_rate_multiplier = nil
m.addpeak_rate_multiplier = nil
}
// SetIsExclusive sets the "is_exclusive" field.
func (m *GroupMutation) SetIsExclusive(b bool) {
m.is_exclusive = &b
@@ -17264,7 +17433,7 @@ func (m *GroupMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *GroupMutation) Fields() []string {
fields := make([]string, 0, 35)
fields := make([]string, 0, 39)
if m.created_at != nil {
fields = append(fields, group.FieldCreatedAt)
}
@@ -17283,6 +17452,18 @@ func (m *GroupMutation) Fields() []string {
if m.rate_multiplier != nil {
fields = append(fields, group.FieldRateMultiplier)
}
if m.peak_rate_enabled != nil {
fields = append(fields, group.FieldPeakRateEnabled)
}
if m.peak_start != nil {
fields = append(fields, group.FieldPeakStart)
}
if m.peak_end != nil {
fields = append(fields, group.FieldPeakEnd)
}
if m.peak_rate_multiplier != nil {
fields = append(fields, group.FieldPeakRateMultiplier)
}
if m.is_exclusive != nil {
fields = append(fields, group.FieldIsExclusive)
}
@@ -17390,6 +17571,14 @@ func (m *GroupMutation) Field(name string) (ent.Value, bool) {
return m.Description()
case group.FieldRateMultiplier:
return m.RateMultiplier()
case group.FieldPeakRateEnabled:
return m.PeakRateEnabled()
case group.FieldPeakStart:
return m.PeakStart()
case group.FieldPeakEnd:
return m.PeakEnd()
case group.FieldPeakRateMultiplier:
return m.PeakRateMultiplier()
case group.FieldIsExclusive:
return m.IsExclusive()
case group.FieldStatus:
@@ -17469,6 +17658,14 @@ func (m *GroupMutation) OldField(ctx context.Context, name string) (ent.Value, e
return m.OldDescription(ctx)
case group.FieldRateMultiplier:
return m.OldRateMultiplier(ctx)
case group.FieldPeakRateEnabled:
return m.OldPeakRateEnabled(ctx)
case group.FieldPeakStart:
return m.OldPeakStart(ctx)
case group.FieldPeakEnd:
return m.OldPeakEnd(ctx)
case group.FieldPeakRateMultiplier:
return m.OldPeakRateMultiplier(ctx)
case group.FieldIsExclusive:
return m.OldIsExclusive(ctx)
case group.FieldStatus:
@@ -17578,6 +17775,34 @@ func (m *GroupMutation) SetField(name string, value ent.Value) error {
}
m.SetRateMultiplier(v)
return nil
case group.FieldPeakRateEnabled:
v, ok := value.(bool)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetPeakRateEnabled(v)
return nil
case group.FieldPeakStart:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetPeakStart(v)
return nil
case group.FieldPeakEnd:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetPeakEnd(v)
return nil
case group.FieldPeakRateMultiplier:
v, ok := value.(float64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetPeakRateMultiplier(v)
return nil
case group.FieldIsExclusive:
v, ok := value.(bool)
if !ok {
@@ -17792,6 +18017,9 @@ func (m *GroupMutation) AddedFields() []string {
if m.addrate_multiplier != nil {
fields = append(fields, group.FieldRateMultiplier)
}
if m.addpeak_rate_multiplier != nil {
fields = append(fields, group.FieldPeakRateMultiplier)
}
if m.adddaily_limit_usd != nil {
fields = append(fields, group.FieldDailyLimitUsd)
}
@@ -17838,6 +18066,8 @@ func (m *GroupMutation) AddedField(name string) (ent.Value, bool) {
switch name {
case group.FieldRateMultiplier:
return m.AddedRateMultiplier()
case group.FieldPeakRateMultiplier:
return m.AddedPeakRateMultiplier()
case group.FieldDailyLimitUsd:
return m.AddedDailyLimitUsd()
case group.FieldWeeklyLimitUsd:
@@ -17878,6 +18108,13 @@ func (m *GroupMutation) AddField(name string, value ent.Value) error {
}
m.AddRateMultiplier(v)
return nil
case group.FieldPeakRateMultiplier:
v, ok := value.(float64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddPeakRateMultiplier(v)
return nil
case group.FieldDailyLimitUsd:
v, ok := value.(float64)
if !ok {
@@ -18076,6 +18313,18 @@ func (m *GroupMutation) ResetField(name string) error {
case group.FieldRateMultiplier:
m.ResetRateMultiplier()
return nil
case group.FieldPeakRateEnabled:
m.ResetPeakRateEnabled()
return nil
case group.FieldPeakStart:
m.ResetPeakStart()
return nil
case group.FieldPeakEnd:
m.ResetPeakEnd()
return nil
case group.FieldPeakRateMultiplier:
m.ResetPeakRateMultiplier()
return nil
case group.FieldIsExclusive:
m.ResetIsExclusive()
return nil
+40 -20
View File
@@ -796,92 +796,112 @@ func init() {
groupDescRateMultiplier := groupFields[2].Descriptor()
// group.DefaultRateMultiplier holds the default value on creation for the rate_multiplier field.
group.DefaultRateMultiplier = groupDescRateMultiplier.Default.(float64)
// groupDescPeakRateEnabled is the schema descriptor for peak_rate_enabled field.
groupDescPeakRateEnabled := groupFields[3].Descriptor()
// group.DefaultPeakRateEnabled holds the default value on creation for the peak_rate_enabled field.
group.DefaultPeakRateEnabled = groupDescPeakRateEnabled.Default.(bool)
// groupDescPeakStart is the schema descriptor for peak_start field.
groupDescPeakStart := groupFields[4].Descriptor()
// group.DefaultPeakStart holds the default value on creation for the peak_start field.
group.DefaultPeakStart = groupDescPeakStart.Default.(string)
// group.PeakStartValidator is a validator for the "peak_start" field. It is called by the builders before save.
group.PeakStartValidator = groupDescPeakStart.Validators[0].(func(string) error)
// groupDescPeakEnd is the schema descriptor for peak_end field.
groupDescPeakEnd := groupFields[5].Descriptor()
// group.DefaultPeakEnd holds the default value on creation for the peak_end field.
group.DefaultPeakEnd = groupDescPeakEnd.Default.(string)
// group.PeakEndValidator is a validator for the "peak_end" field. It is called by the builders before save.
group.PeakEndValidator = groupDescPeakEnd.Validators[0].(func(string) error)
// groupDescPeakRateMultiplier is the schema descriptor for peak_rate_multiplier field.
groupDescPeakRateMultiplier := groupFields[6].Descriptor()
// group.DefaultPeakRateMultiplier holds the default value on creation for the peak_rate_multiplier field.
group.DefaultPeakRateMultiplier = groupDescPeakRateMultiplier.Default.(float64)
// groupDescIsExclusive is the schema descriptor for is_exclusive field.
groupDescIsExclusive := groupFields[3].Descriptor()
groupDescIsExclusive := groupFields[7].Descriptor()
// group.DefaultIsExclusive holds the default value on creation for the is_exclusive field.
group.DefaultIsExclusive = groupDescIsExclusive.Default.(bool)
// groupDescStatus is the schema descriptor for status field.
groupDescStatus := groupFields[4].Descriptor()
groupDescStatus := groupFields[8].Descriptor()
// group.DefaultStatus holds the default value on creation for the status field.
group.DefaultStatus = groupDescStatus.Default.(string)
// group.StatusValidator is a validator for the "status" field. It is called by the builders before save.
group.StatusValidator = groupDescStatus.Validators[0].(func(string) error)
// groupDescPlatform is the schema descriptor for platform field.
groupDescPlatform := groupFields[5].Descriptor()
groupDescPlatform := groupFields[9].Descriptor()
// group.DefaultPlatform holds the default value on creation for the platform field.
group.DefaultPlatform = groupDescPlatform.Default.(string)
// group.PlatformValidator is a validator for the "platform" field. It is called by the builders before save.
group.PlatformValidator = groupDescPlatform.Validators[0].(func(string) error)
// groupDescSubscriptionType is the schema descriptor for subscription_type field.
groupDescSubscriptionType := groupFields[6].Descriptor()
groupDescSubscriptionType := groupFields[10].Descriptor()
// group.DefaultSubscriptionType holds the default value on creation for the subscription_type field.
group.DefaultSubscriptionType = groupDescSubscriptionType.Default.(string)
// group.SubscriptionTypeValidator is a validator for the "subscription_type" field. It is called by the builders before save.
group.SubscriptionTypeValidator = groupDescSubscriptionType.Validators[0].(func(string) error)
// groupDescDefaultValidityDays is the schema descriptor for default_validity_days field.
groupDescDefaultValidityDays := groupFields[10].Descriptor()
groupDescDefaultValidityDays := groupFields[14].Descriptor()
// group.DefaultDefaultValidityDays holds the default value on creation for the default_validity_days field.
group.DefaultDefaultValidityDays = groupDescDefaultValidityDays.Default.(int)
// groupDescAllowImageGeneration is the schema descriptor for allow_image_generation field.
groupDescAllowImageGeneration := groupFields[11].Descriptor()
groupDescAllowImageGeneration := groupFields[15].Descriptor()
// group.DefaultAllowImageGeneration holds the default value on creation for the allow_image_generation field.
group.DefaultAllowImageGeneration = groupDescAllowImageGeneration.Default.(bool)
// groupDescImageRateIndependent is the schema descriptor for image_rate_independent field.
groupDescImageRateIndependent := groupFields[12].Descriptor()
groupDescImageRateIndependent := groupFields[16].Descriptor()
// group.DefaultImageRateIndependent holds the default value on creation for the image_rate_independent field.
group.DefaultImageRateIndependent = groupDescImageRateIndependent.Default.(bool)
// groupDescImageRateMultiplier is the schema descriptor for image_rate_multiplier field.
groupDescImageRateMultiplier := groupFields[13].Descriptor()
groupDescImageRateMultiplier := groupFields[17].Descriptor()
// group.DefaultImageRateMultiplier holds the default value on creation for the image_rate_multiplier field.
group.DefaultImageRateMultiplier = groupDescImageRateMultiplier.Default.(float64)
// groupDescClaudeCodeOnly is the schema descriptor for claude_code_only field.
groupDescClaudeCodeOnly := groupFields[17].Descriptor()
groupDescClaudeCodeOnly := groupFields[21].Descriptor()
// group.DefaultClaudeCodeOnly holds the default value on creation for the claude_code_only field.
group.DefaultClaudeCodeOnly = groupDescClaudeCodeOnly.Default.(bool)
// groupDescModelRoutingEnabled is the schema descriptor for model_routing_enabled field.
groupDescModelRoutingEnabled := groupFields[21].Descriptor()
groupDescModelRoutingEnabled := groupFields[25].Descriptor()
// group.DefaultModelRoutingEnabled holds the default value on creation for the model_routing_enabled field.
group.DefaultModelRoutingEnabled = groupDescModelRoutingEnabled.Default.(bool)
// groupDescMcpXMLInject is the schema descriptor for mcp_xml_inject field.
groupDescMcpXMLInject := groupFields[22].Descriptor()
groupDescMcpXMLInject := groupFields[26].Descriptor()
// group.DefaultMcpXMLInject holds the default value on creation for the mcp_xml_inject field.
group.DefaultMcpXMLInject = groupDescMcpXMLInject.Default.(bool)
// groupDescSupportedModelScopes is the schema descriptor for supported_model_scopes field.
groupDescSupportedModelScopes := groupFields[23].Descriptor()
groupDescSupportedModelScopes := groupFields[27].Descriptor()
// group.DefaultSupportedModelScopes holds the default value on creation for the supported_model_scopes field.
group.DefaultSupportedModelScopes = groupDescSupportedModelScopes.Default.([]string)
// groupDescSortOrder is the schema descriptor for sort_order field.
groupDescSortOrder := groupFields[24].Descriptor()
groupDescSortOrder := groupFields[28].Descriptor()
// group.DefaultSortOrder holds the default value on creation for the sort_order field.
group.DefaultSortOrder = groupDescSortOrder.Default.(int)
// groupDescAllowMessagesDispatch is the schema descriptor for allow_messages_dispatch field.
groupDescAllowMessagesDispatch := groupFields[25].Descriptor()
groupDescAllowMessagesDispatch := groupFields[29].Descriptor()
// group.DefaultAllowMessagesDispatch holds the default value on creation for the allow_messages_dispatch field.
group.DefaultAllowMessagesDispatch = groupDescAllowMessagesDispatch.Default.(bool)
// groupDescRequireOauthOnly is the schema descriptor for require_oauth_only field.
groupDescRequireOauthOnly := groupFields[26].Descriptor()
groupDescRequireOauthOnly := groupFields[30].Descriptor()
// group.DefaultRequireOauthOnly holds the default value on creation for the require_oauth_only field.
group.DefaultRequireOauthOnly = groupDescRequireOauthOnly.Default.(bool)
// groupDescRequirePrivacySet is the schema descriptor for require_privacy_set field.
groupDescRequirePrivacySet := groupFields[27].Descriptor()
groupDescRequirePrivacySet := groupFields[31].Descriptor()
// group.DefaultRequirePrivacySet holds the default value on creation for the require_privacy_set field.
group.DefaultRequirePrivacySet = groupDescRequirePrivacySet.Default.(bool)
// groupDescDefaultMappedModel is the schema descriptor for default_mapped_model field.
groupDescDefaultMappedModel := groupFields[28].Descriptor()
groupDescDefaultMappedModel := groupFields[32].Descriptor()
// group.DefaultDefaultMappedModel holds the default value on creation for the default_mapped_model field.
group.DefaultDefaultMappedModel = groupDescDefaultMappedModel.Default.(string)
// group.DefaultMappedModelValidator is a validator for the "default_mapped_model" field. It is called by the builders before save.
group.DefaultMappedModelValidator = groupDescDefaultMappedModel.Validators[0].(func(string) error)
// groupDescMessagesDispatchModelConfig is the schema descriptor for messages_dispatch_model_config field.
groupDescMessagesDispatchModelConfig := groupFields[29].Descriptor()
groupDescMessagesDispatchModelConfig := groupFields[33].Descriptor()
// group.DefaultMessagesDispatchModelConfig holds the default value on creation for the messages_dispatch_model_config field.
group.DefaultMessagesDispatchModelConfig = groupDescMessagesDispatchModelConfig.Default.(domain.OpenAIMessagesDispatchModelConfig)
// groupDescModelsListConfig is the schema descriptor for models_list_config field.
groupDescModelsListConfig := groupFields[30].Descriptor()
groupDescModelsListConfig := groupFields[34].Descriptor()
// group.DefaultModelsListConfig holds the default value on creation for the models_list_config field.
group.DefaultModelsListConfig = groupDescModelsListConfig.Default.(domain.GroupModelsListConfig)
// groupDescRpmLimit is the schema descriptor for rpm_limit field.
groupDescRpmLimit := groupFields[31].Descriptor()
groupDescRpmLimit := groupFields[35].Descriptor()
// group.DefaultRpmLimit holds the default value on creation for the rpm_limit field.
group.DefaultRpmLimit = groupDescRpmLimit.Default.(int)
idempotencyrecordMixin := schema.IdempotencyRecord{}.Mixin()
+16
View File
@@ -45,6 +45,22 @@ func (Group) Fields() []ent.Field {
field.Float("rate_multiplier").
SchemaType(map[string]string{dialect.Postgres: "decimal(10,4)"}).
Default(1.0),
// 高峰时段倍率(added by migration 156)
field.Bool("peak_rate_enabled").
Default(false).
Comment("是否启用高峰时段倍率"),
field.String("peak_start").
MaxLen(5).
Default("").
Comment("高峰开始时间 HH:MM(含),如 14:00;空表示未配置;不支持跨天"),
field.String("peak_end").
MaxLen(5).
Default("").
Comment("高峰结束时间 HH:MM(不含),必须大于 peak_start;不支持跨天,如 22:00-02:00"),
field.Float("peak_rate_multiplier").
SchemaType(map[string]string{dialect.Postgres: "decimal(10,4)"}).
Default(1.0).
Comment("高峰时段叠加倍率,仅在 peak_rate_enabled 且处于 [peak_start, peak_end) 时乘入文本倍率"),
field.Bool("is_exclusive").
Default(false),
field.String("status").
@@ -71,6 +71,13 @@ func (f optionalLimitField) ToServiceInput() *float64 {
return &zero
}
func derefFloat64Default(p *float64, def float64) float64 {
if p != nil {
return *p
}
return def
}
// NewGroupHandler creates a new admin group handler
func NewGroupHandler(adminService service.AdminService, dashboardService *service.DashboardService, groupCapacityService *service.GroupCapacityService) *GroupHandler {
return &GroupHandler{
@@ -95,6 +102,10 @@ type CreateGroupRequest struct {
AllowImageGeneration bool `json:"allow_image_generation"`
ImageRateIndependent bool `json:"image_rate_independent"`
ImageRateMultiplier *float64 `json:"image_rate_multiplier"`
PeakRateEnabled bool `json:"peak_rate_enabled"`
PeakStart string `json:"peak_start"`
PeakEnd string `json:"peak_end"`
PeakRateMultiplier *float64 `json:"peak_rate_multiplier"`
ImagePrice1K *float64 `json:"image_price_1k"`
ImagePrice2K *float64 `json:"image_price_2k"`
ImagePrice4K *float64 `json:"image_price_4k"`
@@ -136,6 +147,10 @@ type UpdateGroupRequest struct {
AllowImageGeneration *bool `json:"allow_image_generation"`
ImageRateIndependent *bool `json:"image_rate_independent"`
ImageRateMultiplier *float64 `json:"image_rate_multiplier"`
PeakRateEnabled *bool `json:"peak_rate_enabled"`
PeakStart *string `json:"peak_start"`
PeakEnd *string `json:"peak_end"`
PeakRateMultiplier *float64 `json:"peak_rate_multiplier"`
ImagePrice1K *float64 `json:"image_price_1k"`
ImagePrice2K *float64 `json:"image_price_2k"`
ImagePrice4K *float64 `json:"image_price_4k"`
@@ -277,6 +292,11 @@ func (h *GroupHandler) Create(c *gin.Context) {
return
}
if err := service.ValidatePeakRateConfig(req.SubscriptionType, req.PeakRateEnabled, req.PeakStart, req.PeakEnd, derefFloat64Default(req.PeakRateMultiplier, 1.0)); err != nil {
response.BadRequest(c, err.Error())
return
}
group, err := h.adminService.CreateGroup(c.Request.Context(), &service.CreateGroupInput{
Name: req.Name,
Description: req.Description,
@@ -290,6 +310,10 @@ func (h *GroupHandler) Create(c *gin.Context) {
AllowImageGeneration: req.AllowImageGeneration,
ImageRateIndependent: req.ImageRateIndependent,
ImageRateMultiplier: req.ImageRateMultiplier,
PeakRateEnabled: req.PeakRateEnabled,
PeakStart: req.PeakStart,
PeakEnd: req.PeakEnd,
PeakRateMultiplier: req.PeakRateMultiplier,
ImagePrice1K: req.ImagePrice1K,
ImagePrice2K: req.ImagePrice2K,
ImagePrice4K: req.ImagePrice4K,
@@ -346,6 +370,10 @@ func (h *GroupHandler) Update(c *gin.Context) {
AllowImageGeneration: req.AllowImageGeneration,
ImageRateIndependent: req.ImageRateIndependent,
ImageRateMultiplier: req.ImageRateMultiplier,
PeakRateEnabled: req.PeakRateEnabled,
PeakStart: req.PeakStart,
PeakEnd: req.PeakEnd,
PeakRateMultiplier: req.PeakRateMultiplier,
ImagePrice1K: req.ImagePrice1K,
ImagePrice2K: req.ImagePrice2K,
ImagePrice4K: req.ImagePrice4K,
+4
View File
@@ -181,6 +181,10 @@ func groupFromServiceBase(g *service.Group) Group {
AllowImageGeneration: g.AllowImageGeneration,
ImageRateIndependent: g.ImageRateIndependent,
ImageRateMultiplier: g.ImageRateMultiplier,
PeakRateEnabled: g.PeakRateEnabled,
PeakStart: g.PeakStart,
PeakEnd: g.PeakEnd,
PeakRateMultiplier: g.PeakRateMultiplier,
ImagePrice1K: g.ImagePrice1K,
ImagePrice2K: g.ImagePrice2K,
ImagePrice4K: g.ImagePrice4K,
+11 -6
View File
@@ -97,12 +97,17 @@ type Group struct {
MonthlyLimitUSD *float64 `json:"monthly_limit_usd"`
// 图片生成计费配置(仅 antigravity 平台使用)
AllowImageGeneration bool `json:"allow_image_generation"`
ImageRateIndependent bool `json:"image_rate_independent"`
ImageRateMultiplier float64 `json:"image_rate_multiplier"`
ImagePrice1K *float64 `json:"image_price_1k"`
ImagePrice2K *float64 `json:"image_price_2k"`
ImagePrice4K *float64 `json:"image_price_4k"`
AllowImageGeneration bool `json:"allow_image_generation"`
ImageRateIndependent bool `json:"image_rate_independent"`
ImageRateMultiplier float64 `json:"image_rate_multiplier"`
// 高峰时段倍率配置
PeakRateEnabled bool `json:"peak_rate_enabled"`
PeakStart string `json:"peak_start"`
PeakEnd string `json:"peak_end"`
PeakRateMultiplier float64 `json:"peak_rate_multiplier"`
ImagePrice1K *float64 `json:"image_price_1k"`
ImagePrice2K *float64 `json:"image_price_2k"`
ImagePrice4K *float64 `json:"image_price_4k"`
// Claude Code 客户端限制
ClaudeCodeOnly bool `json:"claude_code_only"`
@@ -194,6 +194,10 @@ func (r *apiKeyRepository) GetByKeyForAuth(ctx context.Context, key string) (*se
group.FieldMessagesDispatchModelConfig,
group.FieldModelsListConfig,
group.FieldRpmLimit,
group.FieldPeakRateEnabled,
group.FieldPeakStart,
group.FieldPeakEnd,
group.FieldPeakRateMultiplier,
)
}).
Only(ctx)
@@ -814,6 +818,10 @@ func groupEntityToService(g *dbent.Group) *service.Group {
MessagesDispatchModelConfig: g.MessagesDispatchModelConfig,
ModelsListConfig: g.ModelsListConfig,
RPMLimit: g.RpmLimit,
PeakRateEnabled: g.PeakRateEnabled,
PeakStart: g.PeakStart,
PeakEnd: g.PeakEnd,
PeakRateMultiplier: g.PeakRateMultiplier,
CreatedAt: g.CreatedAt,
UpdatedAt: g.UpdatedAt,
}
+10 -2
View File
@@ -67,7 +67,11 @@ func (r *groupRepository) Create(ctx context.Context, groupIn *service.Group) er
SetDefaultMappedModel(groupIn.DefaultMappedModel).
SetMessagesDispatchModelConfig(groupIn.MessagesDispatchModelConfig).
SetModelsListConfig(groupIn.ModelsListConfig).
SetRpmLimit(groupIn.RPMLimit)
SetRpmLimit(groupIn.RPMLimit).
SetPeakRateEnabled(groupIn.PeakRateEnabled).
SetPeakStart(groupIn.PeakStart).
SetPeakEnd(groupIn.PeakEnd).
SetPeakRateMultiplier(groupIn.PeakRateMultiplier)
// 设置模型路由配置
if groupIn.ModelRouting != nil {
@@ -143,7 +147,11 @@ func (r *groupRepository) Update(ctx context.Context, groupIn *service.Group) er
SetDefaultMappedModel(groupIn.DefaultMappedModel).
SetMessagesDispatchModelConfig(groupIn.MessagesDispatchModelConfig).
SetModelsListConfig(groupIn.ModelsListConfig).
SetRpmLimit(groupIn.RPMLimit)
SetRpmLimit(groupIn.RPMLimit).
SetPeakRateEnabled(groupIn.PeakRateEnabled).
SetPeakStart(groupIn.PeakStart).
SetPeakEnd(groupIn.PeakEnd).
SetPeakRateMultiplier(groupIn.PeakRateMultiplier)
// 显式处理可空字段:nil 需要 clear,非 nil 需要 set。
if groupIn.DailyLimitUSD != nil {
+49 -10
View File
@@ -209,11 +209,16 @@ type CreateGroupInput struct {
AllowImageGeneration bool
ImageRateIndependent bool
ImageRateMultiplier *float64
ImagePrice1K *float64
ImagePrice2K *float64
ImagePrice4K *float64
ClaudeCodeOnly bool // 仅允许 Claude Code 客户端
FallbackGroupID *int64 // 降级分组 ID
// 高峰时段倍率配置(PeakRateMultiplier 为 nil 时按 1.0 处理)
PeakRateEnabled bool
PeakStart string
PeakEnd string
PeakRateMultiplier *float64
ImagePrice1K *float64
ImagePrice2K *float64
ImagePrice4K *float64
ClaudeCodeOnly bool // 仅允许 Claude Code 客户端
FallbackGroupID *int64 // 降级分组 ID
// 无效请求兜底分组 ID(仅 anthropic 平台使用)
FallbackGroupIDOnInvalidRequest *int64
// 模型路由配置(仅 anthropic 平台使用)
@@ -250,11 +255,16 @@ type UpdateGroupInput struct {
AllowImageGeneration *bool
ImageRateIndependent *bool
ImageRateMultiplier *float64
ImagePrice1K *float64
ImagePrice2K *float64
ImagePrice4K *float64
ClaudeCodeOnly *bool // 仅允许 Claude Code 客户端
FallbackGroupID *int64 // 降级分组 ID
// 高峰时段倍率配置(nil 表示不修改)
PeakRateEnabled *bool
PeakStart *string
PeakEnd *string
PeakRateMultiplier *float64
ImagePrice1K *float64
ImagePrice2K *float64
ImagePrice4K *float64
ClaudeCodeOnly *bool // 仅允许 Claude Code 客户端
FallbackGroupID *int64 // 降级分组 ID
// 无效请求兜底分组 ID(仅 anthropic 平台使用)
FallbackGroupIDOnInvalidRequest *int64
// 模型路由配置(仅 anthropic 平台使用)
@@ -1824,6 +1834,14 @@ func (s *adminServiceImpl) CreateGroup(ctx context.Context, input *CreateGroupIn
imageRateMultiplier = *input.ImageRateMultiplier
}
peakRateMultiplier := 1.0
if input.PeakRateMultiplier != nil {
peakRateMultiplier = *input.PeakRateMultiplier
}
if err := ValidatePeakRateConfig(subscriptionType, input.PeakRateEnabled, input.PeakStart, input.PeakEnd, peakRateMultiplier); err != nil {
return nil, err
}
// 校验降级分组
if input.FallbackGroupID != nil {
if err := s.validateFallbackGroup(ctx, 0, *input.FallbackGroupID); err != nil {
@@ -1893,6 +1911,10 @@ func (s *adminServiceImpl) CreateGroup(ctx context.Context, input *CreateGroupIn
AllowImageGeneration: input.AllowImageGeneration,
ImageRateIndependent: input.ImageRateIndependent,
ImageRateMultiplier: imageRateMultiplier,
PeakRateEnabled: input.PeakRateEnabled,
PeakStart: input.PeakStart,
PeakEnd: input.PeakEnd,
PeakRateMultiplier: peakRateMultiplier,
ImagePrice1K: imagePrice1K,
ImagePrice2K: imagePrice2K,
ImagePrice4K: imagePrice4K,
@@ -2082,6 +2104,23 @@ func (s *adminServiceImpl) UpdateGroup(ctx context.Context, id int64, input *Upd
}
group.ImageRateMultiplier = *input.ImageRateMultiplier
}
if input.PeakRateEnabled != nil {
group.PeakRateEnabled = *input.PeakRateEnabled
}
if input.PeakStart != nil {
group.PeakStart = *input.PeakStart
}
if input.PeakEnd != nil {
group.PeakEnd = *input.PeakEnd
}
if input.PeakRateMultiplier != nil {
group.PeakRateMultiplier = *input.PeakRateMultiplier
}
// 收敛校验:Update 可能只传部分 peak 字段,需对合并后的最终配置统一校验,
// 防止单独修改 start/end 导致最终 start>=end 等非法配置入库。
if err := ValidatePeakRateConfig(group.SubscriptionType, group.PeakRateEnabled, group.PeakStart, group.PeakEnd, group.PeakRateMultiplier); err != nil {
return nil, err
}
if input.ImagePrice1K != nil {
group.ImagePrice1K = normalizePrice(input.ImagePrice1K)
}
@@ -93,6 +93,14 @@ type APIKeyAuthGroupSnapshot struct {
// RPMLimit 分组级每分钟请求数上限(0 = 不限制);用于 billing_cache_service.checkRPM 级联判断。
RPMLimit int `json:"rpm_limit"`
// 高峰时段倍率:PeakRateEnabled 为 true 且请求时刻处于 [PeakStart, PeakEnd) 时,
// 文本计费倍率额外乘以 PeakRateMultiplier(详见 Group.PeakMultiplierAt)。
// 必须随快照缓存,否则扣费路径拿到的 apiKey.Group 缺字段、高峰倍率失效。
PeakRateEnabled bool `json:"peak_rate_enabled"`
PeakStart string `json:"peak_start"`
PeakEnd string `json:"peak_end"`
PeakRateMultiplier float64 `json:"peak_rate_multiplier"`
}
// APIKeyAuthCacheEntry 缓存条目,支持负缓存
@@ -14,7 +14,7 @@ import (
"github.com/dgraph-io/ristretto"
)
const apiKeyAuthSnapshotVersion = 12 // v12: include exclusive group authorization fields
const apiKeyAuthSnapshotVersion = 13 // v13: include group peak rate fields
type apiKeyAuthCacheConfig struct {
l1Size int
@@ -276,6 +276,10 @@ func (s *APIKeyService) snapshotFromAPIKey(ctx context.Context, apiKey *APIKey)
MessagesDispatchModelConfig: apiKey.Group.MessagesDispatchModelConfig,
ModelsListConfig: apiKey.Group.ModelsListConfig,
RPMLimit: apiKey.Group.RPMLimit,
PeakRateEnabled: apiKey.Group.PeakRateEnabled,
PeakStart: apiKey.Group.PeakStart,
PeakEnd: apiKey.Group.PeakEnd,
PeakRateMultiplier: apiKey.Group.PeakRateMultiplier,
}
}
return snapshot
@@ -349,6 +353,10 @@ func (s *APIKeyService) snapshotToAPIKey(key string, snapshot *APIKeyAuthSnapsho
MessagesDispatchModelConfig: snapshot.Group.MessagesDispatchModelConfig,
ModelsListConfig: snapshot.Group.ModelsListConfig,
RPMLimit: snapshot.Group.RPMLimit,
PeakRateEnabled: snapshot.Group.PeakRateEnabled,
PeakStart: snapshot.Group.PeakStart,
PeakEnd: snapshot.Group.PeakEnd,
PeakRateMultiplier: snapshot.Group.PeakRateMultiplier,
}
}
s.compileAPIKeyIPRules(apiKey)
+4 -1
View File
@@ -30,6 +30,7 @@ import (
"github.com/Wei-Shaw/sub2api/internal/pkg/ctxkey"
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
"github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
"github.com/Wei-Shaw/sub2api/internal/pkg/usagestats"
"github.com/Wei-Shaw/sub2api/internal/util/responseheaders"
"github.com/Wei-Shaw/sub2api/internal/util/urlvalidator"
@@ -9436,7 +9437,9 @@ func (s *GatewayService) recordUsageCore(ctx context.Context, input *recordUsage
groupDefault := apiKey.Group.RateMultiplier
multiplier = s.getUserGroupRateMultiplier(ctx, user.ID, *apiKey.GroupID, groupDefault)
}
imageMultiplier := resolveImageRateMultiplier(apiKey, multiplier)
// 文本倍率叠加高峰因子(仅文本,图片倍率不受影响)。高峰因子按请求时刻现算,
// 不并入上面的 getUserGroupRateMultiplier,以免污染 user:group 倍率缓存。
multiplier, imageMultiplier := computePeakAwareMultipliers(apiKey, multiplier, timezone.Now())
// 确定计费模型
billingModel := forwardResultBillingModel(result.Model, result.UpstreamModel)
+85 -1
View File
@@ -1,10 +1,13 @@
package service
import (
"errors"
"fmt"
"strings"
"time"
"github.com/Wei-Shaw/sub2api/internal/domain"
"github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
)
type OpenAIMessagesDispatchModelConfig = domain.OpenAIMessagesDispatchModelConfig
@@ -16,7 +19,13 @@ type Group struct {
Description string
Platform string
RateMultiplier float64
IsExclusive bool
// 高峰时段倍率:peak_rate_enabled 为 true 且当前时刻处于 [PeakStart, PeakEnd) 时,
// 文本计费倍率额外乘以 PeakRateMultiplier。详见 PeakMultiplierAt。
PeakRateEnabled bool
PeakStart string
PeakEnd string
PeakRateMultiplier float64
IsExclusive bool
Status string
Hydrated bool // indicates the group was loaded from a trusted repository source
@@ -167,3 +176,78 @@ func matchModelPattern(pattern, model string) bool {
return false
}
// parseMinutes 把 "HH:MM" 解析为当日分钟数(0..1439)。格式非法返回 (0,false)。
func parseMinutes(hhmm string) (int, bool) {
t, err := time.Parse("15:04", hhmm)
if err != nil {
return 0, false
}
return t.Hour()*60 + t.Minute(), true
}
// PeakMultiplierAt 返回指定时刻 now 的高峰因子。
// - 未启用 / 未配置 / 配置非法(start>=end 或格式错误) / 非高峰时段 → 返回 1.0(安全降级)
// - 区间为左闭右开 [PeakStart, PeakEnd),仅支持当日区间,不支持跨天(如 22:00-次日02:00)
// - 时刻基于全局系统时区(timezone.Location)判定
// 该方法是纯函数,不读取任何外部状态,便于单测。
func (g *Group) PeakMultiplierAt(now time.Time) float64 {
if g == nil || !g.IsSubscriptionType() || !g.PeakRateEnabled || g.PeakStart == "" || g.PeakEnd == "" {
return 1.0
}
start, ok1 := parseMinutes(g.PeakStart)
end, ok2 := parseMinutes(g.PeakEnd)
if !ok1 || !ok2 || start >= end {
return 1.0
}
t := now.In(timezone.Location())
cur := t.Hour()*60 + t.Minute()
if cur >= start && cur < end {
return g.PeakRateMultiplier
}
return 1.0
}
// ValidatePeakRateConfig 是高峰倍率配置的唯一校验来源,供 handler 与 service 层共用。
// enabled=true 时仅允许订阅类型分组;并要求 start/end 合法且 end>start(不支持跨天),multiplier>=0。
// enabled=false 时放行(不关心类型)。subscriptionType 为空按 standard 处理。
func ValidatePeakRateConfig(subscriptionType string, enabled bool, start, end string, multiplier float64) error {
if !enabled {
return nil
}
if subscriptionType != SubscriptionTypeSubscription {
return errors.New("高峰时段倍率仅支持订阅类型分组")
}
if start == "" || end == "" {
return errors.New("peak_rate_enabled 为 true 时 peak_start 与 peak_end 必填")
}
st, err1 := time.Parse("15:04", start)
if err1 != nil {
return fmt.Errorf("peak_start 格式应为 HH:MM,got %q", start)
}
en, err2 := time.Parse("15:04", end)
if err2 != nil {
return fmt.Errorf("peak_end 格式应为 HH:MM,got %q", end)
}
if st.Hour()*60+st.Minute() >= en.Hour()*60+en.Minute() {
return errors.New("peak_end 必须大于 peak_start(不支持跨天区间,如 22:00-02:00)")
}
if multiplier < 0 {
return errors.New("peak_rate_multiplier 不能为负")
}
return nil
}
// computePeakAwareMultipliers 把"基础文本倍率 base"(已含系统/分组/用户级倍率,但不含高峰)
// 拆分为最终文本倍率与图片倍率:图片倍率基于 base 现算、不受高峰影响;文本倍率在 base 上叠加高峰因子。
// gateway_service.recordUsageCore 与 openai_gateway_service.RecordUsage 共用此函数,
// 锁死"高峰因子只乘入文本倍率、图片倍率不受影响"这一叠加顺序——任何调换都会被 group_peak_rate_test 覆盖。
func computePeakAwareMultipliers(apiKey *APIKey, base float64, now time.Time) (text, image float64) {
image = resolveImageRateMultiplier(apiKey, base)
peak := 1.0
if apiKey != nil && apiKey.Group != nil {
peak = apiKey.Group.PeakMultiplierAt(now)
}
text = base * peak
return
}
@@ -0,0 +1,238 @@
package service
import (
"context"
"math"
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
)
func init() {
// 测试固定全局时区为 UTC,确保判定可复现。
_ = timezone.Init("UTC")
}
func newPeakGroup(enabled bool, start, end string, mult float64) *Group {
return &Group{
SubscriptionType: "subscription",
PeakRateEnabled: enabled,
PeakStart: start,
PeakEnd: end,
PeakRateMultiplier: mult,
}
}
func at(hour, min int) time.Time {
return time.Date(2026, 6, 29, hour, min, 0, 0, time.UTC)
}
func TestPeakMultiplierAt_DisabledOrUnconfigured(t *testing.T) {
cases := []struct {
name string
g *Group
}{
{"disabled", newPeakGroup(false, "14:00", "18:00", 3.0)},
{"empty start", newPeakGroup(true, "", "18:00", 3.0)},
{"empty end", newPeakGroup(true, "14:00", "", 3.0)},
{"invalid start>=end", newPeakGroup(true, "18:00", "14:00", 3.0)},
{"equal start==end", newPeakGroup(true, "14:00", "14:00", 3.0)},
{"malformed start", newPeakGroup(true, "99:99", "18:00", 3.0)},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := c.g.PeakMultiplierAt(at(15, 0)); got != 1.0 {
t.Fatalf("expect 1.0, got %v", got)
}
})
}
}
func TestPeakMultiplierAt_NilReceiver(t *testing.T) {
var g *Group
if got := g.PeakMultiplierAt(at(15, 0)); got != 1.0 {
t.Fatalf("expect 1.0, got %v", got)
}
}
func TestPeakMultiplierAt_Boundaries(t *testing.T) {
g := newPeakGroup(true, "14:00", "18:00", 3.0)
cases := []struct {
t time.Time
want float64
}{
{at(13, 59), 1.0},
{at(14, 0), 3.0},
{at(15, 30), 3.0},
{at(17, 59), 3.0},
{at(18, 0), 1.0},
{at(23, 0), 1.0},
}
for _, c := range cases {
t.Run(c.t.Format("15:04"), func(t *testing.T) {
if got := g.PeakMultiplierAt(c.t); got != c.want {
t.Fatalf("at %s: expect %v, got %v", c.t.Format("15:04"), c.want, got)
}
})
}
}
func TestPeakMultiplierAt_RespectsTimezoneLocation(t *testing.T) {
// 全局时区为 UTC。北京 15:00 = UTC 07:00,不在 [14:00,18:00)。
nonUTC := time.Date(2026, 6, 29, 15, 0, 0, 0, mustLoad("Asia/Shanghai"))
g := newPeakGroup(true, "14:00", "18:00", 3.0)
if got := g.PeakMultiplierAt(nonUTC); got != 1.0 {
t.Fatalf("expect 1.0 (converted to UTC 07:00), got %v", got)
}
}
func mustLoad(name string) *time.Location {
loc, err := time.LoadLocation(name)
if err != nil {
panic(err)
}
return loc
}
func TestValidatePeakRateConfig(t *testing.T) {
cases := []struct {
name string
subType string
enabled bool
start string
end string
mult float64
wantErr bool
}{
{"disabled passes through", "subscription", false, "", "", 0, false},
{"subscription enabled valid", "subscription", true, "14:00", "18:00", 3.0, false},
{"standard enabled rejected", "standard", true, "14:00", "18:00", 3.0, true},
{"empty type treated as standard", "", true, "14:00", "18:00", 3.0, true},
{"standard disabled passes", "standard", false, "", "", 0, false},
{"enabled empty start", "subscription", true, "", "18:00", 1.0, true},
{"enabled empty end", "subscription", true, "14:00", "", 1.0, true},
{"enabled malformed start", "subscription", true, "99:99", "18:00", 1.0, true},
{"enabled malformed end", "subscription", true, "14:00", "25:00", 1.0, true},
{"enabled equal start==end", "subscription", true, "14:00", "14:00", 1.0, true},
{"enabled cross-day rejected", "subscription", true, "22:00", "02:00", 1.0, true},
{"enabled negative multiplier", "subscription", true, "14:00", "18:00", -0.5, true},
{"enabled zero multiplier allowed", "subscription", true, "14:00", "18:00", 0, false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
err := ValidatePeakRateConfig(c.subType, c.enabled, c.start, c.end, c.mult)
if c.wantErr && err == nil {
t.Fatalf("expect error, got nil")
}
if !c.wantErr && err != nil {
t.Fatalf("expect no error, got %v", err)
}
})
}
}
func TestPeakMultiplierAt_StandardTypeDegradesToOne(t *testing.T) {
g := newPeakGroup(true, "14:00", "18:00", 3.0)
g.SubscriptionType = "standard"
if got := g.PeakMultiplierAt(at(15, 30)); got != 1.0 {
t.Fatalf("standard group must degrade to 1.0, got %v", got)
}
sub := newPeakGroup(true, "14:00", "18:00", 3.0)
sub.SubscriptionType = "subscription"
if got := sub.PeakMultiplierAt(at(15, 30)); got != 3.0 {
t.Fatalf("subscription group peak multiplier: got %v, want 3.0", got)
}
}
// TestPeakMultiplier_GatewayBillingSequence 调用 gateway_service.recordUsageCore 与
// openai_gateway_service.RecordUsage 共用的 computePeakAwareMultipliers,验证计费叠加顺序:
// 图片倍率基于基础倍率算出且不受高峰影响,高峰因子只乘入文本倍率。
// 若有人调换叠加顺序或把高峰并入 imageMultiplier,此测试会失败。
func TestPeakMultiplier_GatewayBillingSequence(t *testing.T) {
const baseMultiplier = 0.8
apiKey := &APIKey{Group: newPeakGroup(true, "14:00", "18:00", 3.0)}
approxEq := func(a, b float64) bool { return math.Abs(a-b) < 1e-9 }
t.Run("peak hour amplifies text only", func(t *testing.T) {
now := at(15, 30) // 处于 [14:00, 18:00)
textMultiplier, imageMultiplier := computePeakAwareMultipliers(apiKey, baseMultiplier, now)
if !approxEq(imageMultiplier, baseMultiplier) {
t.Fatalf("image multiplier must not be affected by peak: got %v, want %v", imageMultiplier, baseMultiplier)
}
if want := baseMultiplier * 3.0; !approxEq(textMultiplier, want) {
t.Fatalf("text multiplier should include peak factor: got %v, want %v", textMultiplier, want)
}
})
t.Run("off-peak leaves both multipliers at base", func(t *testing.T) {
now := at(20, 0)
textMultiplier, imageMultiplier := computePeakAwareMultipliers(apiKey, baseMultiplier, now)
if !approxEq(imageMultiplier, baseMultiplier) {
t.Fatalf("image multiplier: got %v, want %v", imageMultiplier, baseMultiplier)
}
if !approxEq(textMultiplier, baseMultiplier) {
t.Fatalf("text multiplier should equal base off-peak: got %v, want %v", textMultiplier, baseMultiplier)
}
})
t.Run("image independent mode decoupled from peak", func(t *testing.T) {
indGroup := newPeakGroup(true, "14:00", "18:00", 3.0)
indGroup.ImageRateIndependent = true
indGroup.ImageRateMultiplier = 0.5
indKey := &APIKey{Group: indGroup}
now := at(15, 30)
textMultiplier, imageMultiplier := computePeakAwareMultipliers(indKey, baseMultiplier, now)
if !approxEq(imageMultiplier, 0.5) {
t.Fatalf("independent image multiplier: got %v, want 0.5", imageMultiplier)
}
if want := baseMultiplier * 3.0; !approxEq(textMultiplier, want) {
t.Fatalf("text multiplier should include peak factor: got %v, want %v", textMultiplier, want)
}
})
t.Run("nil api key degrades to base multipliers", func(t *testing.T) {
now := at(15, 30)
textMultiplier, imageMultiplier := computePeakAwareMultipliers(nil, baseMultiplier, now)
if !approxEq(textMultiplier, baseMultiplier) {
t.Fatalf("nil group text multiplier: got %v, want %v", textMultiplier, baseMultiplier)
}
if !approxEq(imageMultiplier, baseMultiplier) {
t.Fatalf("nil group image multiplier: got %v, want %v", imageMultiplier, baseMultiplier)
}
})
}
// TestPeakMultiplier_SnapshotRoundTrip 防回归:认证缓存快照(APIKeyAuthGroupSnapshot)
// 必须携带高峰倍率 4 字段,否则扣费路径拿到的 apiKey.Group 会缺字段、PeakMultiplierAt 恒降级为 1.0。
// 调用真实链路 snapshotFromAPIKey → snapshotToAPIKey,验证 peak 配置经快照往返后仍生效。
func TestPeakMultiplier_SnapshotRoundTrip(t *testing.T) {
apiKey := &APIKey{
User: &User{ID: 1, Status: StatusActive, Role: RoleUser},
Group: newPeakGroup(true, "14:00", "18:00", 3.0),
}
svc := &APIKeyService{}
snapshot := svc.snapshotFromAPIKey(context.Background(), apiKey)
if snapshot == nil || snapshot.Group == nil {
t.Fatalf("snapshot or snapshot.Group must not be nil")
}
restored := svc.snapshotToAPIKey("k", snapshot)
if restored.Group == nil {
t.Fatalf("restored.Group must not be nil")
}
if !restored.Group.PeakRateEnabled ||
restored.Group.PeakStart != "14:00" ||
restored.Group.PeakEnd != "18:00" ||
restored.Group.PeakRateMultiplier != 3.0 {
t.Fatalf("peak fields lost in snapshot round-trip: %+v", restored.Group)
}
if got := restored.Group.PeakMultiplierAt(at(15, 30)); got != 3.0 {
t.Fatalf("peak hour multiplier after round-trip: got %v, want 3.0", got)
}
if got := restored.Group.PeakMultiplierAt(at(20, 0)); got != 1.0 {
t.Fatalf("off-peak multiplier after round-trip: got %v, want 1.0", got)
}
}
@@ -24,6 +24,7 @@ import (
"github.com/Wei-Shaw/sub2api/internal/pkg/apicompat"
"github.com/Wei-Shaw/sub2api/internal/pkg/ip"
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
"github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
"github.com/Wei-Shaw/sub2api/internal/pkg/openai"
"github.com/Wei-Shaw/sub2api/internal/pkg/openai_compat"
"github.com/Wei-Shaw/sub2api/internal/pkg/xai"
@@ -6283,7 +6284,9 @@ func (s *OpenAIGatewayService) RecordUsage(ctx context.Context, input *OpenAIRec
}
multiplier = resolver.Resolve(ctx, user.ID, *apiKey.GroupID, apiKey.Group.RateMultiplier)
}
imageMultiplier := resolveImageRateMultiplier(apiKey, multiplier)
// 文本倍率叠加高峰因子(仅文本,图片倍率不受影响)。高峰因子按请求时刻现算,
// 不并入上面的 Resolve,以免污染 user:group 倍率缓存。
multiplier, imageMultiplier := computePeakAwareMultipliers(apiKey, multiplier, timezone.Now())
var cost *CostBreakdown
var err error
@@ -0,0 +1,4 @@
ALTER TABLE groups ADD COLUMN IF NOT EXISTS peak_rate_enabled BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE groups ADD COLUMN IF NOT EXISTS peak_start VARCHAR(5) NOT NULL DEFAULT '';
ALTER TABLE groups ADD COLUMN IF NOT EXISTS peak_end VARCHAR(5) NOT NULL DEFAULT '';
ALTER TABLE groups ADD COLUMN IF NOT EXISTS peak_rate_multiplier DECIMAL(10,4) NOT NULL DEFAULT 1.0;
+13
View File
@@ -518,6 +518,11 @@ export interface Group {
image_price_1k: number | null
image_price_2k: number | null
image_price_4k: number | null
// 高峰时段倍率配置
peak_rate_enabled: boolean
peak_start: string
peak_end: string
peak_rate_multiplier: number
// Claude Code 客户端限制
claude_code_only: boolean
fallback_group_id: number | null
@@ -636,6 +641,10 @@ export interface CreateGroupRequest {
image_price_1k?: number | null
image_price_2k?: number | null
image_price_4k?: number | null
peak_rate_enabled?: boolean
peak_start?: string
peak_end?: string
peak_rate_multiplier?: number
claude_code_only?: boolean
fallback_group_id?: number | null
fallback_group_id_on_invalid_request?: number | null
@@ -671,6 +680,10 @@ export interface UpdateGroupRequest {
image_price_1k?: number | null
image_price_2k?: number | null
image_price_4k?: number | null
peak_rate_enabled?: boolean
peak_start?: string
peak_end?: string
peak_rate_multiplier?: number
claude_code_only?: boolean
fallback_group_id?: number | null
fallback_group_id_on_invalid_request?: number | null
+152 -4
View File
@@ -853,6 +853,52 @@
</div>
</div>
<!-- 高峰时段倍率配置(仅订阅类型分组) -->
<div v-if="createForm.subscription_type === 'subscription'" class="border-t pt-4">
<div class="mb-4 grid grid-cols-1 gap-3 md:grid-cols-2">
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300">
<input
v-model="createForm.peak_rate_enabled"
type="checkbox"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
<span>启用高峰时段倍率</span>
</label>
</div>
<div
v-if="createForm.peak_rate_enabled"
class="mb-4 grid grid-cols-3 gap-3"
>
<div>
<label class="input-label">高峰开始</label>
<input
v-model="createForm.peak_start"
type="time"
class="input"
/>
</div>
<div>
<label class="input-label">高峰结束</label>
<input
v-model="createForm.peak_end"
type="time"
class="input"
/>
</div>
<div>
<label class="input-label">高峰倍率</label>
<input
v-model.number="createForm.peak_rate_multiplier"
type="number"
step="0.001"
min="0"
class="input"
placeholder="1"
/>
</div>
</div>
</div>
<!-- 支持的模型系列(仅 antigravity 平台) -->
<div v-if="createForm.platform === 'antigravity'" class="border-t pt-4">
<div class="mb-1.5 flex items-center gap-1">
@@ -2141,6 +2187,52 @@
</div>
</div>
<!-- 高峰时段倍率配置(仅订阅类型分组) -->
<div v-if="editForm.subscription_type === 'subscription'" class="border-t pt-4">
<div class="mb-4 grid grid-cols-1 gap-3 md:grid-cols-2">
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300">
<input
v-model="editForm.peak_rate_enabled"
type="checkbox"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
<span>启用高峰时段倍率</span>
</label>
</div>
<div
v-if="editForm.peak_rate_enabled"
class="mb-4 grid grid-cols-3 gap-3"
>
<div>
<label class="input-label">高峰开始</label>
<input
v-model="editForm.peak_start"
type="time"
class="input"
/>
</div>
<div>
<label class="input-label">高峰结束</label>
<input
v-model="editForm.peak_end"
type="time"
class="input"
/>
</div>
<div>
<label class="input-label">高峰倍率</label>
<input
v-model.number="editForm.peak_rate_multiplier"
type="number"
step="0.001"
min="0"
class="input"
placeholder="1"
/>
</div>
</div>
</div>
<!-- 支持的模型系列(仅 antigravity 平台) -->
<div v-if="editForm.platform === 'antigravity'" class="border-t pt-4">
<div class="mb-1.5 flex items-center gap-1">
@@ -3342,6 +3434,11 @@ const createForm = reactive({
image_price_1k: null as number | null,
image_price_2k: null as number | null,
image_price_4k: null as number | null,
// 高峰时段倍率配置
peak_rate_enabled: false,
peak_start: "",
peak_end: "",
peak_rate_multiplier: 1.0,
// Claude Code 客户端限制(仅 anthropic 平台使用)
claude_code_only: false,
fallback_group_id: null as number | null,
@@ -3673,6 +3770,11 @@ const editForm = reactive({
image_price_1k: null as number | null,
image_price_2k: null as number | null,
image_price_4k: null as number | null,
// 高峰时段倍率配置
peak_rate_enabled: false,
peak_start: "",
peak_end: "",
peak_rate_multiplier: 1.0,
// Claude Code 客户端限制(仅 anthropic 平台使用)
claude_code_only: false,
fallback_group_id: null as number | null,
@@ -3706,6 +3808,10 @@ type ImagePricingFormState = {
image_price_1k: number | string | null;
image_price_2k: number | string | null;
image_price_4k: number | string | null;
peak_rate_enabled: boolean;
peak_start: string;
peak_end: string;
peak_rate_multiplier: number;
};
const imagePricingTiers = [
@@ -3924,6 +4030,10 @@ const closeCreateModal = () => {
createForm.image_price_1k = null;
createForm.image_price_2k = null;
createForm.image_price_4k = null;
createForm.peak_rate_enabled = false;
createForm.peak_start = "";
createForm.peak_end = "";
createForm.peak_rate_multiplier = 1.0;
createForm.claude_code_only = false;
createForm.fallback_group_id = null;
createForm.fallback_group_id_on_invalid_request = null;
@@ -3957,7 +4067,7 @@ const normalizeOptionalLimit = (
return Number.isFinite(value) && value > 0 ? value : null;
};
const normalizeImageRateMultiplier = (
const normalizeRateMultiplier = (
value: number | string | null | undefined,
): number => {
if (value === null || value === undefined || value === "") {
@@ -4010,9 +4120,15 @@ const handleCreateGroup = async () => {
requestData.daily_limit_usd = emptyToNull(requestData.daily_limit_usd);
requestData.weekly_limit_usd = emptyToNull(requestData.weekly_limit_usd);
requestData.monthly_limit_usd = emptyToNull(requestData.monthly_limit_usd);
requestData.image_rate_multiplier = normalizeImageRateMultiplier(
requestData.image_rate_multiplier = normalizeRateMultiplier(
requestData.image_rate_multiplier,
);
requestData.peak_rate_enabled = createForm.peak_rate_enabled;
requestData.peak_start = createForm.peak_start;
requestData.peak_end = createForm.peak_end;
requestData.peak_rate_multiplier = normalizeRateMultiplier(
createForm.peak_rate_multiplier,
);
await adminAPI.groups.create(requestData);
appStore.showSuccess(t("admin.groups.groupCreated"));
closeCreateModal();
@@ -4050,6 +4166,10 @@ const handleEdit = async (group: AdminGroup) => {
editForm.image_price_1k = group.image_price_1k;
editForm.image_price_2k = group.image_price_2k;
editForm.image_price_4k = group.image_price_4k;
editForm.peak_rate_enabled = group.peak_rate_enabled ?? false;
editForm.peak_start = group.peak_start ?? "";
editForm.peak_end = group.peak_end ?? "";
editForm.peak_rate_multiplier = group.peak_rate_multiplier ?? 1.0;
editForm.claude_code_only = group.claude_code_only || false;
editForm.fallback_group_id = group.fallback_group_id;
editForm.fallback_group_id_on_invalid_request =
@@ -4094,6 +4214,10 @@ const closeEditModal = () => {
editingGroup.value = null;
editModelRoutingRules.value = [];
editForm.copy_accounts_from_group_ids = [];
editForm.peak_rate_enabled = false;
editForm.peak_start = "";
editForm.peak_end = "";
editForm.peak_rate_multiplier = 1.0;
resetMessagesDispatchFormState(editForm);
resetModelsListState(editModelsListState);
};
@@ -4149,9 +4273,15 @@ const handleUpdateGroup = async () => {
payload.daily_limit_usd = emptyToNull(payload.daily_limit_usd);
payload.weekly_limit_usd = emptyToNull(payload.weekly_limit_usd);
payload.monthly_limit_usd = emptyToNull(payload.monthly_limit_usd);
payload.image_rate_multiplier = normalizeImageRateMultiplier(
payload.image_rate_multiplier = normalizeRateMultiplier(
payload.image_rate_multiplier,
);
payload.peak_rate_enabled = editForm.peak_rate_enabled;
payload.peak_start = editForm.peak_start;
payload.peak_end = editForm.peak_end;
payload.peak_rate_multiplier = normalizeRateMultiplier(
editForm.peak_rate_multiplier,
);
await adminAPI.groups.update(editingGroup.value.id, payload);
appStore.showSuccess(t("admin.groups.groupUpdated"));
closeEditModal();
@@ -4222,13 +4352,31 @@ const confirmDelete = async () => {
}
};
// 监听 subscription_type 变化,订阅模式时 is_exclusive 默认为 true
// 监听 subscription_type 变化,订阅模式时 is_exclusive 默认为 true;标准模式清空高峰配置
watch(
() => createForm.subscription_type,
(newVal) => {
if (newVal === "subscription") {
createForm.is_exclusive = true;
createForm.fallback_group_id_on_invalid_request = null;
} else {
createForm.peak_rate_enabled = false;
createForm.peak_start = "";
createForm.peak_end = "";
createForm.peak_rate_multiplier = 1.0;
}
},
);
// 编辑表单:切回标准模式时清空高峰配置,避免残留随更新请求提交被后端拒绝
watch(
() => editForm.subscription_type,
(newVal) => {
if (newVal !== "subscription") {
editForm.peak_rate_enabled = false;
editForm.peak_start = "";
editForm.peak_end = "";
editForm.peak_rate_multiplier = 1.0;
}
},
);