feat: 图片生成计费功能

- 新增 Group 图片价格配置(image_price_1k/2k/4k)
- BillingService 新增 CalculateImageCost 方法
- AntigravityGatewayService 支持识别图片生成模型并按次计费
- UsageLog 新增 image_count 和 image_size 字段
- 前端分组管理支持配置图片价格(antigravity 和 gemini 平台)
- 图片计费复用通用计费能力(余额检查、扣费、倍率、订阅限额)
This commit is contained in:
song
2026-01-05 17:07:29 +08:00
parent e78c864650
commit d4c2b723a5
41 changed files with 2747 additions and 40 deletions

View File

@@ -45,6 +45,12 @@ type Group struct {
MonthlyLimitUsd *float64 `json:"monthly_limit_usd,omitempty"`
// DefaultValidityDays holds the value of the "default_validity_days" field.
DefaultValidityDays int `json:"default_validity_days,omitempty"`
// ImagePrice1k holds the value of the "image_price_1k" field.
ImagePrice1k *float64 `json:"image_price_1k,omitempty"`
// ImagePrice2k holds the value of the "image_price_2k" field.
ImagePrice2k *float64 `json:"image_price_2k,omitempty"`
// ImagePrice4k holds the value of the "image_price_4k" field.
ImagePrice4k *float64 `json:"image_price_4k,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the GroupQuery when eager-loading is set.
Edges GroupEdges `json:"edges"`
@@ -153,7 +159,7 @@ func (*Group) scanValues(columns []string) ([]any, error) {
switch columns[i] {
case group.FieldIsExclusive:
values[i] = new(sql.NullBool)
case group.FieldRateMultiplier, group.FieldDailyLimitUsd, group.FieldWeeklyLimitUsd, group.FieldMonthlyLimitUsd:
case group.FieldRateMultiplier, group.FieldDailyLimitUsd, group.FieldWeeklyLimitUsd, group.FieldMonthlyLimitUsd, group.FieldImagePrice1k, group.FieldImagePrice2k, group.FieldImagePrice4k:
values[i] = new(sql.NullFloat64)
case group.FieldID, group.FieldDefaultValidityDays:
values[i] = new(sql.NullInt64)
@@ -271,6 +277,27 @@ func (_m *Group) assignValues(columns []string, values []any) error {
} else if value.Valid {
_m.DefaultValidityDays = int(value.Int64)
}
case group.FieldImagePrice1k:
if value, ok := values[i].(*sql.NullFloat64); !ok {
return fmt.Errorf("unexpected type %T for field image_price_1k", values[i])
} else if value.Valid {
_m.ImagePrice1k = new(float64)
*_m.ImagePrice1k = value.Float64
}
case group.FieldImagePrice2k:
if value, ok := values[i].(*sql.NullFloat64); !ok {
return fmt.Errorf("unexpected type %T for field image_price_2k", values[i])
} else if value.Valid {
_m.ImagePrice2k = new(float64)
*_m.ImagePrice2k = value.Float64
}
case group.FieldImagePrice4k:
if value, ok := values[i].(*sql.NullFloat64); !ok {
return fmt.Errorf("unexpected type %T for field image_price_4k", values[i])
} else if value.Valid {
_m.ImagePrice4k = new(float64)
*_m.ImagePrice4k = value.Float64
}
default:
_m.selectValues.Set(columns[i], values[i])
}
@@ -398,6 +425,21 @@ func (_m *Group) String() string {
builder.WriteString(", ")
builder.WriteString("default_validity_days=")
builder.WriteString(fmt.Sprintf("%v", _m.DefaultValidityDays))
builder.WriteString(", ")
if v := _m.ImagePrice1k; v != nil {
builder.WriteString("image_price_1k=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
if v := _m.ImagePrice2k; v != nil {
builder.WriteString("image_price_2k=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
if v := _m.ImagePrice4k; v != nil {
builder.WriteString("image_price_4k=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteByte(')')
return builder.String()
}

View File

@@ -43,6 +43,12 @@ const (
FieldMonthlyLimitUsd = "monthly_limit_usd"
// FieldDefaultValidityDays holds the string denoting the default_validity_days field in the database.
FieldDefaultValidityDays = "default_validity_days"
// FieldImagePrice1k holds the string denoting the image_price_1k field in the database.
FieldImagePrice1k = "image_price_1k"
// FieldImagePrice2k holds the string denoting the image_price_2k field in the database.
FieldImagePrice2k = "image_price_2k"
// FieldImagePrice4k holds the string denoting the image_price_4k field in the database.
FieldImagePrice4k = "image_price_4k"
// EdgeAPIKeys holds the string denoting the api_keys edge name in mutations.
EdgeAPIKeys = "api_keys"
// EdgeRedeemCodes holds the string denoting the redeem_codes edge name in mutations.
@@ -132,6 +138,9 @@ var Columns = []string{
FieldWeeklyLimitUsd,
FieldMonthlyLimitUsd,
FieldDefaultValidityDays,
FieldImagePrice1k,
FieldImagePrice2k,
FieldImagePrice4k,
}
var (
@@ -267,6 +276,21 @@ func ByDefaultValidityDays(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldDefaultValidityDays, opts...).ToFunc()
}
// ByImagePrice1k orders the results by the image_price_1k field.
func ByImagePrice1k(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldImagePrice1k, opts...).ToFunc()
}
// ByImagePrice2k orders the results by the image_price_2k field.
func ByImagePrice2k(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldImagePrice2k, opts...).ToFunc()
}
// ByImagePrice4k orders the results by the image_price_4k field.
func ByImagePrice4k(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldImagePrice4k, opts...).ToFunc()
}
// ByAPIKeysCount orders the results by api_keys count.
func ByAPIKeysCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {

View File

@@ -125,6 +125,21 @@ func DefaultValidityDays(v int) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldDefaultValidityDays, v))
}
// ImagePrice1k applies equality check predicate on the "image_price_1k" field. It's identical to ImagePrice1kEQ.
func ImagePrice1k(v float64) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldImagePrice1k, v))
}
// ImagePrice2k applies equality check predicate on the "image_price_2k" field. It's identical to ImagePrice2kEQ.
func ImagePrice2k(v float64) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldImagePrice2k, v))
}
// ImagePrice4k applies equality check predicate on the "image_price_4k" field. It's identical to ImagePrice4kEQ.
func ImagePrice4k(v float64) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldImagePrice4k, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldCreatedAt, v))
@@ -830,6 +845,156 @@ func DefaultValidityDaysLTE(v int) predicate.Group {
return predicate.Group(sql.FieldLTE(FieldDefaultValidityDays, v))
}
// ImagePrice1kEQ applies the EQ predicate on the "image_price_1k" field.
func ImagePrice1kEQ(v float64) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldImagePrice1k, v))
}
// ImagePrice1kNEQ applies the NEQ predicate on the "image_price_1k" field.
func ImagePrice1kNEQ(v float64) predicate.Group {
return predicate.Group(sql.FieldNEQ(FieldImagePrice1k, v))
}
// ImagePrice1kIn applies the In predicate on the "image_price_1k" field.
func ImagePrice1kIn(vs ...float64) predicate.Group {
return predicate.Group(sql.FieldIn(FieldImagePrice1k, vs...))
}
// ImagePrice1kNotIn applies the NotIn predicate on the "image_price_1k" field.
func ImagePrice1kNotIn(vs ...float64) predicate.Group {
return predicate.Group(sql.FieldNotIn(FieldImagePrice1k, vs...))
}
// ImagePrice1kGT applies the GT predicate on the "image_price_1k" field.
func ImagePrice1kGT(v float64) predicate.Group {
return predicate.Group(sql.FieldGT(FieldImagePrice1k, v))
}
// ImagePrice1kGTE applies the GTE predicate on the "image_price_1k" field.
func ImagePrice1kGTE(v float64) predicate.Group {
return predicate.Group(sql.FieldGTE(FieldImagePrice1k, v))
}
// ImagePrice1kLT applies the LT predicate on the "image_price_1k" field.
func ImagePrice1kLT(v float64) predicate.Group {
return predicate.Group(sql.FieldLT(FieldImagePrice1k, v))
}
// ImagePrice1kLTE applies the LTE predicate on the "image_price_1k" field.
func ImagePrice1kLTE(v float64) predicate.Group {
return predicate.Group(sql.FieldLTE(FieldImagePrice1k, v))
}
// ImagePrice1kIsNil applies the IsNil predicate on the "image_price_1k" field.
func ImagePrice1kIsNil() predicate.Group {
return predicate.Group(sql.FieldIsNull(FieldImagePrice1k))
}
// ImagePrice1kNotNil applies the NotNil predicate on the "image_price_1k" field.
func ImagePrice1kNotNil() predicate.Group {
return predicate.Group(sql.FieldNotNull(FieldImagePrice1k))
}
// ImagePrice2kEQ applies the EQ predicate on the "image_price_2k" field.
func ImagePrice2kEQ(v float64) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldImagePrice2k, v))
}
// ImagePrice2kNEQ applies the NEQ predicate on the "image_price_2k" field.
func ImagePrice2kNEQ(v float64) predicate.Group {
return predicate.Group(sql.FieldNEQ(FieldImagePrice2k, v))
}
// ImagePrice2kIn applies the In predicate on the "image_price_2k" field.
func ImagePrice2kIn(vs ...float64) predicate.Group {
return predicate.Group(sql.FieldIn(FieldImagePrice2k, vs...))
}
// ImagePrice2kNotIn applies the NotIn predicate on the "image_price_2k" field.
func ImagePrice2kNotIn(vs ...float64) predicate.Group {
return predicate.Group(sql.FieldNotIn(FieldImagePrice2k, vs...))
}
// ImagePrice2kGT applies the GT predicate on the "image_price_2k" field.
func ImagePrice2kGT(v float64) predicate.Group {
return predicate.Group(sql.FieldGT(FieldImagePrice2k, v))
}
// ImagePrice2kGTE applies the GTE predicate on the "image_price_2k" field.
func ImagePrice2kGTE(v float64) predicate.Group {
return predicate.Group(sql.FieldGTE(FieldImagePrice2k, v))
}
// ImagePrice2kLT applies the LT predicate on the "image_price_2k" field.
func ImagePrice2kLT(v float64) predicate.Group {
return predicate.Group(sql.FieldLT(FieldImagePrice2k, v))
}
// ImagePrice2kLTE applies the LTE predicate on the "image_price_2k" field.
func ImagePrice2kLTE(v float64) predicate.Group {
return predicate.Group(sql.FieldLTE(FieldImagePrice2k, v))
}
// ImagePrice2kIsNil applies the IsNil predicate on the "image_price_2k" field.
func ImagePrice2kIsNil() predicate.Group {
return predicate.Group(sql.FieldIsNull(FieldImagePrice2k))
}
// ImagePrice2kNotNil applies the NotNil predicate on the "image_price_2k" field.
func ImagePrice2kNotNil() predicate.Group {
return predicate.Group(sql.FieldNotNull(FieldImagePrice2k))
}
// ImagePrice4kEQ applies the EQ predicate on the "image_price_4k" field.
func ImagePrice4kEQ(v float64) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldImagePrice4k, v))
}
// ImagePrice4kNEQ applies the NEQ predicate on the "image_price_4k" field.
func ImagePrice4kNEQ(v float64) predicate.Group {
return predicate.Group(sql.FieldNEQ(FieldImagePrice4k, v))
}
// ImagePrice4kIn applies the In predicate on the "image_price_4k" field.
func ImagePrice4kIn(vs ...float64) predicate.Group {
return predicate.Group(sql.FieldIn(FieldImagePrice4k, vs...))
}
// ImagePrice4kNotIn applies the NotIn predicate on the "image_price_4k" field.
func ImagePrice4kNotIn(vs ...float64) predicate.Group {
return predicate.Group(sql.FieldNotIn(FieldImagePrice4k, vs...))
}
// ImagePrice4kGT applies the GT predicate on the "image_price_4k" field.
func ImagePrice4kGT(v float64) predicate.Group {
return predicate.Group(sql.FieldGT(FieldImagePrice4k, v))
}
// ImagePrice4kGTE applies the GTE predicate on the "image_price_4k" field.
func ImagePrice4kGTE(v float64) predicate.Group {
return predicate.Group(sql.FieldGTE(FieldImagePrice4k, v))
}
// ImagePrice4kLT applies the LT predicate on the "image_price_4k" field.
func ImagePrice4kLT(v float64) predicate.Group {
return predicate.Group(sql.FieldLT(FieldImagePrice4k, v))
}
// ImagePrice4kLTE applies the LTE predicate on the "image_price_4k" field.
func ImagePrice4kLTE(v float64) predicate.Group {
return predicate.Group(sql.FieldLTE(FieldImagePrice4k, v))
}
// ImagePrice4kIsNil applies the IsNil predicate on the "image_price_4k" field.
func ImagePrice4kIsNil() predicate.Group {
return predicate.Group(sql.FieldIsNull(FieldImagePrice4k))
}
// ImagePrice4kNotNil applies the NotNil predicate on the "image_price_4k" field.
func ImagePrice4kNotNil() predicate.Group {
return predicate.Group(sql.FieldNotNull(FieldImagePrice4k))
}
// HasAPIKeys applies the HasEdge predicate on the "api_keys" edge.
func HasAPIKeys() predicate.Group {
return predicate.Group(func(s *sql.Selector) {

View File

@@ -216,6 +216,48 @@ func (_c *GroupCreate) SetNillableDefaultValidityDays(v *int) *GroupCreate {
return _c
}
// SetImagePrice1k sets the "image_price_1k" field.
func (_c *GroupCreate) SetImagePrice1k(v float64) *GroupCreate {
_c.mutation.SetImagePrice1k(v)
return _c
}
// SetNillableImagePrice1k sets the "image_price_1k" field if the given value is not nil.
func (_c *GroupCreate) SetNillableImagePrice1k(v *float64) *GroupCreate {
if v != nil {
_c.SetImagePrice1k(*v)
}
return _c
}
// SetImagePrice2k sets the "image_price_2k" field.
func (_c *GroupCreate) SetImagePrice2k(v float64) *GroupCreate {
_c.mutation.SetImagePrice2k(v)
return _c
}
// SetNillableImagePrice2k sets the "image_price_2k" field if the given value is not nil.
func (_c *GroupCreate) SetNillableImagePrice2k(v *float64) *GroupCreate {
if v != nil {
_c.SetImagePrice2k(*v)
}
return _c
}
// SetImagePrice4k sets the "image_price_4k" field.
func (_c *GroupCreate) SetImagePrice4k(v float64) *GroupCreate {
_c.mutation.SetImagePrice4k(v)
return _c
}
// SetNillableImagePrice4k sets the "image_price_4k" field if the given value is not nil.
func (_c *GroupCreate) SetNillableImagePrice4k(v *float64) *GroupCreate {
if v != nil {
_c.SetImagePrice4k(*v)
}
return _c
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func (_c *GroupCreate) AddAPIKeyIDs(ids ...int64) *GroupCreate {
_c.mutation.AddAPIKeyIDs(ids...)
@@ -516,6 +558,18 @@ func (_c *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
_spec.SetField(group.FieldDefaultValidityDays, field.TypeInt, value)
_node.DefaultValidityDays = value
}
if value, ok := _c.mutation.ImagePrice1k(); ok {
_spec.SetField(group.FieldImagePrice1k, field.TypeFloat64, value)
_node.ImagePrice1k = &value
}
if value, ok := _c.mutation.ImagePrice2k(); ok {
_spec.SetField(group.FieldImagePrice2k, field.TypeFloat64, value)
_node.ImagePrice2k = &value
}
if value, ok := _c.mutation.ImagePrice4k(); ok {
_spec.SetField(group.FieldImagePrice4k, field.TypeFloat64, value)
_node.ImagePrice4k = &value
}
if nodes := _c.mutation.APIKeysIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
@@ -888,6 +942,78 @@ func (u *GroupUpsert) AddDefaultValidityDays(v int) *GroupUpsert {
return u
}
// SetImagePrice1k sets the "image_price_1k" field.
func (u *GroupUpsert) SetImagePrice1k(v float64) *GroupUpsert {
u.Set(group.FieldImagePrice1k, v)
return u
}
// UpdateImagePrice1k sets the "image_price_1k" field to the value that was provided on create.
func (u *GroupUpsert) UpdateImagePrice1k() *GroupUpsert {
u.SetExcluded(group.FieldImagePrice1k)
return u
}
// AddImagePrice1k adds v to the "image_price_1k" field.
func (u *GroupUpsert) AddImagePrice1k(v float64) *GroupUpsert {
u.Add(group.FieldImagePrice1k, v)
return u
}
// ClearImagePrice1k clears the value of the "image_price_1k" field.
func (u *GroupUpsert) ClearImagePrice1k() *GroupUpsert {
u.SetNull(group.FieldImagePrice1k)
return u
}
// SetImagePrice2k sets the "image_price_2k" field.
func (u *GroupUpsert) SetImagePrice2k(v float64) *GroupUpsert {
u.Set(group.FieldImagePrice2k, v)
return u
}
// UpdateImagePrice2k sets the "image_price_2k" field to the value that was provided on create.
func (u *GroupUpsert) UpdateImagePrice2k() *GroupUpsert {
u.SetExcluded(group.FieldImagePrice2k)
return u
}
// AddImagePrice2k adds v to the "image_price_2k" field.
func (u *GroupUpsert) AddImagePrice2k(v float64) *GroupUpsert {
u.Add(group.FieldImagePrice2k, v)
return u
}
// ClearImagePrice2k clears the value of the "image_price_2k" field.
func (u *GroupUpsert) ClearImagePrice2k() *GroupUpsert {
u.SetNull(group.FieldImagePrice2k)
return u
}
// SetImagePrice4k sets the "image_price_4k" field.
func (u *GroupUpsert) SetImagePrice4k(v float64) *GroupUpsert {
u.Set(group.FieldImagePrice4k, v)
return u
}
// UpdateImagePrice4k sets the "image_price_4k" field to the value that was provided on create.
func (u *GroupUpsert) UpdateImagePrice4k() *GroupUpsert {
u.SetExcluded(group.FieldImagePrice4k)
return u
}
// AddImagePrice4k adds v to the "image_price_4k" field.
func (u *GroupUpsert) AddImagePrice4k(v float64) *GroupUpsert {
u.Add(group.FieldImagePrice4k, v)
return u
}
// ClearImagePrice4k clears the value of the "image_price_4k" field.
func (u *GroupUpsert) ClearImagePrice4k() *GroupUpsert {
u.SetNull(group.FieldImagePrice4k)
return u
}
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// Using this option is equivalent to using:
//
@@ -1185,6 +1311,90 @@ func (u *GroupUpsertOne) UpdateDefaultValidityDays() *GroupUpsertOne {
})
}
// SetImagePrice1k sets the "image_price_1k" field.
func (u *GroupUpsertOne) SetImagePrice1k(v float64) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.SetImagePrice1k(v)
})
}
// AddImagePrice1k adds v to the "image_price_1k" field.
func (u *GroupUpsertOne) AddImagePrice1k(v float64) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.AddImagePrice1k(v)
})
}
// UpdateImagePrice1k sets the "image_price_1k" field to the value that was provided on create.
func (u *GroupUpsertOne) UpdateImagePrice1k() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.UpdateImagePrice1k()
})
}
// ClearImagePrice1k clears the value of the "image_price_1k" field.
func (u *GroupUpsertOne) ClearImagePrice1k() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.ClearImagePrice1k()
})
}
// SetImagePrice2k sets the "image_price_2k" field.
func (u *GroupUpsertOne) SetImagePrice2k(v float64) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.SetImagePrice2k(v)
})
}
// AddImagePrice2k adds v to the "image_price_2k" field.
func (u *GroupUpsertOne) AddImagePrice2k(v float64) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.AddImagePrice2k(v)
})
}
// UpdateImagePrice2k sets the "image_price_2k" field to the value that was provided on create.
func (u *GroupUpsertOne) UpdateImagePrice2k() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.UpdateImagePrice2k()
})
}
// ClearImagePrice2k clears the value of the "image_price_2k" field.
func (u *GroupUpsertOne) ClearImagePrice2k() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.ClearImagePrice2k()
})
}
// SetImagePrice4k sets the "image_price_4k" field.
func (u *GroupUpsertOne) SetImagePrice4k(v float64) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.SetImagePrice4k(v)
})
}
// AddImagePrice4k adds v to the "image_price_4k" field.
func (u *GroupUpsertOne) AddImagePrice4k(v float64) *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.AddImagePrice4k(v)
})
}
// UpdateImagePrice4k sets the "image_price_4k" field to the value that was provided on create.
func (u *GroupUpsertOne) UpdateImagePrice4k() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.UpdateImagePrice4k()
})
}
// ClearImagePrice4k clears the value of the "image_price_4k" field.
func (u *GroupUpsertOne) ClearImagePrice4k() *GroupUpsertOne {
return u.Update(func(s *GroupUpsert) {
s.ClearImagePrice4k()
})
}
// Exec executes the query.
func (u *GroupUpsertOne) Exec(ctx context.Context) error {
if len(u.create.conflict) == 0 {
@@ -1648,6 +1858,90 @@ func (u *GroupUpsertBulk) UpdateDefaultValidityDays() *GroupUpsertBulk {
})
}
// SetImagePrice1k sets the "image_price_1k" field.
func (u *GroupUpsertBulk) SetImagePrice1k(v float64) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.SetImagePrice1k(v)
})
}
// AddImagePrice1k adds v to the "image_price_1k" field.
func (u *GroupUpsertBulk) AddImagePrice1k(v float64) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.AddImagePrice1k(v)
})
}
// UpdateImagePrice1k sets the "image_price_1k" field to the value that was provided on create.
func (u *GroupUpsertBulk) UpdateImagePrice1k() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.UpdateImagePrice1k()
})
}
// ClearImagePrice1k clears the value of the "image_price_1k" field.
func (u *GroupUpsertBulk) ClearImagePrice1k() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.ClearImagePrice1k()
})
}
// SetImagePrice2k sets the "image_price_2k" field.
func (u *GroupUpsertBulk) SetImagePrice2k(v float64) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.SetImagePrice2k(v)
})
}
// AddImagePrice2k adds v to the "image_price_2k" field.
func (u *GroupUpsertBulk) AddImagePrice2k(v float64) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.AddImagePrice2k(v)
})
}
// UpdateImagePrice2k sets the "image_price_2k" field to the value that was provided on create.
func (u *GroupUpsertBulk) UpdateImagePrice2k() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.UpdateImagePrice2k()
})
}
// ClearImagePrice2k clears the value of the "image_price_2k" field.
func (u *GroupUpsertBulk) ClearImagePrice2k() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.ClearImagePrice2k()
})
}
// SetImagePrice4k sets the "image_price_4k" field.
func (u *GroupUpsertBulk) SetImagePrice4k(v float64) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.SetImagePrice4k(v)
})
}
// AddImagePrice4k adds v to the "image_price_4k" field.
func (u *GroupUpsertBulk) AddImagePrice4k(v float64) *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.AddImagePrice4k(v)
})
}
// UpdateImagePrice4k sets the "image_price_4k" field to the value that was provided on create.
func (u *GroupUpsertBulk) UpdateImagePrice4k() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.UpdateImagePrice4k()
})
}
// ClearImagePrice4k clears the value of the "image_price_4k" field.
func (u *GroupUpsertBulk) ClearImagePrice4k() *GroupUpsertBulk {
return u.Update(func(s *GroupUpsert) {
s.ClearImagePrice4k()
})
}
// Exec executes the query.
func (u *GroupUpsertBulk) Exec(ctx context.Context) error {
if u.create.err != nil {

View File

@@ -273,6 +273,87 @@ func (_u *GroupUpdate) AddDefaultValidityDays(v int) *GroupUpdate {
return _u
}
// SetImagePrice1k sets the "image_price_1k" field.
func (_u *GroupUpdate) SetImagePrice1k(v float64) *GroupUpdate {
_u.mutation.ResetImagePrice1k()
_u.mutation.SetImagePrice1k(v)
return _u
}
// SetNillableImagePrice1k sets the "image_price_1k" field if the given value is not nil.
func (_u *GroupUpdate) SetNillableImagePrice1k(v *float64) *GroupUpdate {
if v != nil {
_u.SetImagePrice1k(*v)
}
return _u
}
// AddImagePrice1k adds value to the "image_price_1k" field.
func (_u *GroupUpdate) AddImagePrice1k(v float64) *GroupUpdate {
_u.mutation.AddImagePrice1k(v)
return _u
}
// ClearImagePrice1k clears the value of the "image_price_1k" field.
func (_u *GroupUpdate) ClearImagePrice1k() *GroupUpdate {
_u.mutation.ClearImagePrice1k()
return _u
}
// SetImagePrice2k sets the "image_price_2k" field.
func (_u *GroupUpdate) SetImagePrice2k(v float64) *GroupUpdate {
_u.mutation.ResetImagePrice2k()
_u.mutation.SetImagePrice2k(v)
return _u
}
// SetNillableImagePrice2k sets the "image_price_2k" field if the given value is not nil.
func (_u *GroupUpdate) SetNillableImagePrice2k(v *float64) *GroupUpdate {
if v != nil {
_u.SetImagePrice2k(*v)
}
return _u
}
// AddImagePrice2k adds value to the "image_price_2k" field.
func (_u *GroupUpdate) AddImagePrice2k(v float64) *GroupUpdate {
_u.mutation.AddImagePrice2k(v)
return _u
}
// ClearImagePrice2k clears the value of the "image_price_2k" field.
func (_u *GroupUpdate) ClearImagePrice2k() *GroupUpdate {
_u.mutation.ClearImagePrice2k()
return _u
}
// SetImagePrice4k sets the "image_price_4k" field.
func (_u *GroupUpdate) SetImagePrice4k(v float64) *GroupUpdate {
_u.mutation.ResetImagePrice4k()
_u.mutation.SetImagePrice4k(v)
return _u
}
// SetNillableImagePrice4k sets the "image_price_4k" field if the given value is not nil.
func (_u *GroupUpdate) SetNillableImagePrice4k(v *float64) *GroupUpdate {
if v != nil {
_u.SetImagePrice4k(*v)
}
return _u
}
// AddImagePrice4k adds value to the "image_price_4k" field.
func (_u *GroupUpdate) AddImagePrice4k(v float64) *GroupUpdate {
_u.mutation.AddImagePrice4k(v)
return _u
}
// ClearImagePrice4k clears the value of the "image_price_4k" field.
func (_u *GroupUpdate) ClearImagePrice4k() *GroupUpdate {
_u.mutation.ClearImagePrice4k()
return _u
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func (_u *GroupUpdate) AddAPIKeyIDs(ids ...int64) *GroupUpdate {
_u.mutation.AddAPIKeyIDs(ids...)
@@ -642,6 +723,33 @@ func (_u *GroupUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if value, ok := _u.mutation.AddedDefaultValidityDays(); ok {
_spec.AddField(group.FieldDefaultValidityDays, field.TypeInt, value)
}
if value, ok := _u.mutation.ImagePrice1k(); ok {
_spec.SetField(group.FieldImagePrice1k, field.TypeFloat64, value)
}
if value, ok := _u.mutation.AddedImagePrice1k(); ok {
_spec.AddField(group.FieldImagePrice1k, field.TypeFloat64, value)
}
if _u.mutation.ImagePrice1kCleared() {
_spec.ClearField(group.FieldImagePrice1k, field.TypeFloat64)
}
if value, ok := _u.mutation.ImagePrice2k(); ok {
_spec.SetField(group.FieldImagePrice2k, field.TypeFloat64, value)
}
if value, ok := _u.mutation.AddedImagePrice2k(); ok {
_spec.AddField(group.FieldImagePrice2k, field.TypeFloat64, value)
}
if _u.mutation.ImagePrice2kCleared() {
_spec.ClearField(group.FieldImagePrice2k, field.TypeFloat64)
}
if value, ok := _u.mutation.ImagePrice4k(); ok {
_spec.SetField(group.FieldImagePrice4k, field.TypeFloat64, value)
}
if value, ok := _u.mutation.AddedImagePrice4k(); ok {
_spec.AddField(group.FieldImagePrice4k, field.TypeFloat64, value)
}
if _u.mutation.ImagePrice4kCleared() {
_spec.ClearField(group.FieldImagePrice4k, field.TypeFloat64)
}
if _u.mutation.APIKeysCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
@@ -1195,6 +1303,87 @@ func (_u *GroupUpdateOne) AddDefaultValidityDays(v int) *GroupUpdateOne {
return _u
}
// SetImagePrice1k sets the "image_price_1k" field.
func (_u *GroupUpdateOne) SetImagePrice1k(v float64) *GroupUpdateOne {
_u.mutation.ResetImagePrice1k()
_u.mutation.SetImagePrice1k(v)
return _u
}
// SetNillableImagePrice1k sets the "image_price_1k" field if the given value is not nil.
func (_u *GroupUpdateOne) SetNillableImagePrice1k(v *float64) *GroupUpdateOne {
if v != nil {
_u.SetImagePrice1k(*v)
}
return _u
}
// AddImagePrice1k adds value to the "image_price_1k" field.
func (_u *GroupUpdateOne) AddImagePrice1k(v float64) *GroupUpdateOne {
_u.mutation.AddImagePrice1k(v)
return _u
}
// ClearImagePrice1k clears the value of the "image_price_1k" field.
func (_u *GroupUpdateOne) ClearImagePrice1k() *GroupUpdateOne {
_u.mutation.ClearImagePrice1k()
return _u
}
// SetImagePrice2k sets the "image_price_2k" field.
func (_u *GroupUpdateOne) SetImagePrice2k(v float64) *GroupUpdateOne {
_u.mutation.ResetImagePrice2k()
_u.mutation.SetImagePrice2k(v)
return _u
}
// SetNillableImagePrice2k sets the "image_price_2k" field if the given value is not nil.
func (_u *GroupUpdateOne) SetNillableImagePrice2k(v *float64) *GroupUpdateOne {
if v != nil {
_u.SetImagePrice2k(*v)
}
return _u
}
// AddImagePrice2k adds value to the "image_price_2k" field.
func (_u *GroupUpdateOne) AddImagePrice2k(v float64) *GroupUpdateOne {
_u.mutation.AddImagePrice2k(v)
return _u
}
// ClearImagePrice2k clears the value of the "image_price_2k" field.
func (_u *GroupUpdateOne) ClearImagePrice2k() *GroupUpdateOne {
_u.mutation.ClearImagePrice2k()
return _u
}
// SetImagePrice4k sets the "image_price_4k" field.
func (_u *GroupUpdateOne) SetImagePrice4k(v float64) *GroupUpdateOne {
_u.mutation.ResetImagePrice4k()
_u.mutation.SetImagePrice4k(v)
return _u
}
// SetNillableImagePrice4k sets the "image_price_4k" field if the given value is not nil.
func (_u *GroupUpdateOne) SetNillableImagePrice4k(v *float64) *GroupUpdateOne {
if v != nil {
_u.SetImagePrice4k(*v)
}
return _u
}
// AddImagePrice4k adds value to the "image_price_4k" field.
func (_u *GroupUpdateOne) AddImagePrice4k(v float64) *GroupUpdateOne {
_u.mutation.AddImagePrice4k(v)
return _u
}
// ClearImagePrice4k clears the value of the "image_price_4k" field.
func (_u *GroupUpdateOne) ClearImagePrice4k() *GroupUpdateOne {
_u.mutation.ClearImagePrice4k()
return _u
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func (_u *GroupUpdateOne) AddAPIKeyIDs(ids ...int64) *GroupUpdateOne {
_u.mutation.AddAPIKeyIDs(ids...)
@@ -1594,6 +1783,33 @@ func (_u *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error)
if value, ok := _u.mutation.AddedDefaultValidityDays(); ok {
_spec.AddField(group.FieldDefaultValidityDays, field.TypeInt, value)
}
if value, ok := _u.mutation.ImagePrice1k(); ok {
_spec.SetField(group.FieldImagePrice1k, field.TypeFloat64, value)
}
if value, ok := _u.mutation.AddedImagePrice1k(); ok {
_spec.AddField(group.FieldImagePrice1k, field.TypeFloat64, value)
}
if _u.mutation.ImagePrice1kCleared() {
_spec.ClearField(group.FieldImagePrice1k, field.TypeFloat64)
}
if value, ok := _u.mutation.ImagePrice2k(); ok {
_spec.SetField(group.FieldImagePrice2k, field.TypeFloat64, value)
}
if value, ok := _u.mutation.AddedImagePrice2k(); ok {
_spec.AddField(group.FieldImagePrice2k, field.TypeFloat64, value)
}
if _u.mutation.ImagePrice2kCleared() {
_spec.ClearField(group.FieldImagePrice2k, field.TypeFloat64)
}
if value, ok := _u.mutation.ImagePrice4k(); ok {
_spec.SetField(group.FieldImagePrice4k, field.TypeFloat64, value)
}
if value, ok := _u.mutation.AddedImagePrice4k(); ok {
_spec.AddField(group.FieldImagePrice4k, field.TypeFloat64, value)
}
if _u.mutation.ImagePrice4kCleared() {
_spec.ClearField(group.FieldImagePrice4k, field.TypeFloat64)
}
if _u.mutation.APIKeysCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,

View File

@@ -215,6 +215,9 @@ var (
{Name: "weekly_limit_usd", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
{Name: "monthly_limit_usd", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
{Name: "default_validity_days", Type: field.TypeInt, Default: 30},
{Name: "image_price_1k", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
{Name: "image_price_2k", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
{Name: "image_price_4k", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
}
// GroupsTable holds the schema information for the "groups" table.
GroupsTable = &schema.Table{
@@ -367,6 +370,8 @@ var (
{Name: "stream", Type: field.TypeBool, Default: false},
{Name: "duration_ms", Type: field.TypeInt, Nullable: true},
{Name: "first_token_ms", Type: field.TypeInt, Nullable: true},
{Name: "image_count", Type: field.TypeInt, Default: 0},
{Name: "image_size", Type: field.TypeString, Nullable: true, Size: 10},
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "api_key_id", Type: field.TypeInt64},
{Name: "account_id", Type: field.TypeInt64},
@@ -382,31 +387,31 @@ var (
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "usage_logs_api_keys_usage_logs",
Columns: []*schema.Column{UsageLogsColumns[21]},
Columns: []*schema.Column{UsageLogsColumns[23]},
RefColumns: []*schema.Column{APIKeysColumns[0]},
OnDelete: schema.NoAction,
},
{
Symbol: "usage_logs_accounts_usage_logs",
Columns: []*schema.Column{UsageLogsColumns[22]},
Columns: []*schema.Column{UsageLogsColumns[24]},
RefColumns: []*schema.Column{AccountsColumns[0]},
OnDelete: schema.NoAction,
},
{
Symbol: "usage_logs_groups_usage_logs",
Columns: []*schema.Column{UsageLogsColumns[23]},
Columns: []*schema.Column{UsageLogsColumns[25]},
RefColumns: []*schema.Column{GroupsColumns[0]},
OnDelete: schema.SetNull,
},
{
Symbol: "usage_logs_users_usage_logs",
Columns: []*schema.Column{UsageLogsColumns[24]},
Columns: []*schema.Column{UsageLogsColumns[26]},
RefColumns: []*schema.Column{UsersColumns[0]},
OnDelete: schema.NoAction,
},
{
Symbol: "usage_logs_user_subscriptions_usage_logs",
Columns: []*schema.Column{UsageLogsColumns[25]},
Columns: []*schema.Column{UsageLogsColumns[27]},
RefColumns: []*schema.Column{UserSubscriptionsColumns[0]},
OnDelete: schema.SetNull,
},
@@ -415,32 +420,32 @@ var (
{
Name: "usagelog_user_id",
Unique: false,
Columns: []*schema.Column{UsageLogsColumns[24]},
Columns: []*schema.Column{UsageLogsColumns[26]},
},
{
Name: "usagelog_api_key_id",
Unique: false,
Columns: []*schema.Column{UsageLogsColumns[21]},
Columns: []*schema.Column{UsageLogsColumns[23]},
},
{
Name: "usagelog_account_id",
Unique: false,
Columns: []*schema.Column{UsageLogsColumns[22]},
Columns: []*schema.Column{UsageLogsColumns[24]},
},
{
Name: "usagelog_group_id",
Unique: false,
Columns: []*schema.Column{UsageLogsColumns[23]},
Columns: []*schema.Column{UsageLogsColumns[25]},
},
{
Name: "usagelog_subscription_id",
Unique: false,
Columns: []*schema.Column{UsageLogsColumns[25]},
Columns: []*schema.Column{UsageLogsColumns[27]},
},
{
Name: "usagelog_created_at",
Unique: false,
Columns: []*schema.Column{UsageLogsColumns[20]},
Columns: []*schema.Column{UsageLogsColumns[22]},
},
{
Name: "usagelog_model",
@@ -455,12 +460,12 @@ var (
{
Name: "usagelog_user_id_created_at",
Unique: false,
Columns: []*schema.Column{UsageLogsColumns[24], UsageLogsColumns[20]},
Columns: []*schema.Column{UsageLogsColumns[26], UsageLogsColumns[22]},
},
{
Name: "usagelog_api_key_id_created_at",
Unique: false,
Columns: []*schema.Column{UsageLogsColumns[21], UsageLogsColumns[20]},
Columns: []*schema.Column{UsageLogsColumns[23], UsageLogsColumns[22]},
},
},
}

View File

@@ -3384,6 +3384,12 @@ type GroupMutation struct {
addmonthly_limit_usd *float64
default_validity_days *int
adddefault_validity_days *int
image_price_1k *float64
addimage_price_1k *float64
image_price_2k *float64
addimage_price_2k *float64
image_price_4k *float64
addimage_price_4k *float64
clearedFields map[string]struct{}
api_keys map[int64]struct{}
removedapi_keys map[int64]struct{}
@@ -4178,6 +4184,216 @@ func (m *GroupMutation) ResetDefaultValidityDays() {
m.adddefault_validity_days = nil
}
// SetImagePrice1k sets the "image_price_1k" field.
func (m *GroupMutation) SetImagePrice1k(f float64) {
m.image_price_1k = &f
m.addimage_price_1k = nil
}
// ImagePrice1k returns the value of the "image_price_1k" field in the mutation.
func (m *GroupMutation) ImagePrice1k() (r float64, exists bool) {
v := m.image_price_1k
if v == nil {
return
}
return *v, true
}
// OldImagePrice1k returns the old "image_price_1k" 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) OldImagePrice1k(ctx context.Context) (v *float64, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldImagePrice1k is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldImagePrice1k requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldImagePrice1k: %w", err)
}
return oldValue.ImagePrice1k, nil
}
// AddImagePrice1k adds f to the "image_price_1k" field.
func (m *GroupMutation) AddImagePrice1k(f float64) {
if m.addimage_price_1k != nil {
*m.addimage_price_1k += f
} else {
m.addimage_price_1k = &f
}
}
// AddedImagePrice1k returns the value that was added to the "image_price_1k" field in this mutation.
func (m *GroupMutation) AddedImagePrice1k() (r float64, exists bool) {
v := m.addimage_price_1k
if v == nil {
return
}
return *v, true
}
// ClearImagePrice1k clears the value of the "image_price_1k" field.
func (m *GroupMutation) ClearImagePrice1k() {
m.image_price_1k = nil
m.addimage_price_1k = nil
m.clearedFields[group.FieldImagePrice1k] = struct{}{}
}
// ImagePrice1kCleared returns if the "image_price_1k" field was cleared in this mutation.
func (m *GroupMutation) ImagePrice1kCleared() bool {
_, ok := m.clearedFields[group.FieldImagePrice1k]
return ok
}
// ResetImagePrice1k resets all changes to the "image_price_1k" field.
func (m *GroupMutation) ResetImagePrice1k() {
m.image_price_1k = nil
m.addimage_price_1k = nil
delete(m.clearedFields, group.FieldImagePrice1k)
}
// SetImagePrice2k sets the "image_price_2k" field.
func (m *GroupMutation) SetImagePrice2k(f float64) {
m.image_price_2k = &f
m.addimage_price_2k = nil
}
// ImagePrice2k returns the value of the "image_price_2k" field in the mutation.
func (m *GroupMutation) ImagePrice2k() (r float64, exists bool) {
v := m.image_price_2k
if v == nil {
return
}
return *v, true
}
// OldImagePrice2k returns the old "image_price_2k" 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) OldImagePrice2k(ctx context.Context) (v *float64, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldImagePrice2k is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldImagePrice2k requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldImagePrice2k: %w", err)
}
return oldValue.ImagePrice2k, nil
}
// AddImagePrice2k adds f to the "image_price_2k" field.
func (m *GroupMutation) AddImagePrice2k(f float64) {
if m.addimage_price_2k != nil {
*m.addimage_price_2k += f
} else {
m.addimage_price_2k = &f
}
}
// AddedImagePrice2k returns the value that was added to the "image_price_2k" field in this mutation.
func (m *GroupMutation) AddedImagePrice2k() (r float64, exists bool) {
v := m.addimage_price_2k
if v == nil {
return
}
return *v, true
}
// ClearImagePrice2k clears the value of the "image_price_2k" field.
func (m *GroupMutation) ClearImagePrice2k() {
m.image_price_2k = nil
m.addimage_price_2k = nil
m.clearedFields[group.FieldImagePrice2k] = struct{}{}
}
// ImagePrice2kCleared returns if the "image_price_2k" field was cleared in this mutation.
func (m *GroupMutation) ImagePrice2kCleared() bool {
_, ok := m.clearedFields[group.FieldImagePrice2k]
return ok
}
// ResetImagePrice2k resets all changes to the "image_price_2k" field.
func (m *GroupMutation) ResetImagePrice2k() {
m.image_price_2k = nil
m.addimage_price_2k = nil
delete(m.clearedFields, group.FieldImagePrice2k)
}
// SetImagePrice4k sets the "image_price_4k" field.
func (m *GroupMutation) SetImagePrice4k(f float64) {
m.image_price_4k = &f
m.addimage_price_4k = nil
}
// ImagePrice4k returns the value of the "image_price_4k" field in the mutation.
func (m *GroupMutation) ImagePrice4k() (r float64, exists bool) {
v := m.image_price_4k
if v == nil {
return
}
return *v, true
}
// OldImagePrice4k returns the old "image_price_4k" 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) OldImagePrice4k(ctx context.Context) (v *float64, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldImagePrice4k is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldImagePrice4k requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldImagePrice4k: %w", err)
}
return oldValue.ImagePrice4k, nil
}
// AddImagePrice4k adds f to the "image_price_4k" field.
func (m *GroupMutation) AddImagePrice4k(f float64) {
if m.addimage_price_4k != nil {
*m.addimage_price_4k += f
} else {
m.addimage_price_4k = &f
}
}
// AddedImagePrice4k returns the value that was added to the "image_price_4k" field in this mutation.
func (m *GroupMutation) AddedImagePrice4k() (r float64, exists bool) {
v := m.addimage_price_4k
if v == nil {
return
}
return *v, true
}
// ClearImagePrice4k clears the value of the "image_price_4k" field.
func (m *GroupMutation) ClearImagePrice4k() {
m.image_price_4k = nil
m.addimage_price_4k = nil
m.clearedFields[group.FieldImagePrice4k] = struct{}{}
}
// ImagePrice4kCleared returns if the "image_price_4k" field was cleared in this mutation.
func (m *GroupMutation) ImagePrice4kCleared() bool {
_, ok := m.clearedFields[group.FieldImagePrice4k]
return ok
}
// ResetImagePrice4k resets all changes to the "image_price_4k" field.
func (m *GroupMutation) ResetImagePrice4k() {
m.image_price_4k = nil
m.addimage_price_4k = nil
delete(m.clearedFields, group.FieldImagePrice4k)
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by ids.
func (m *GroupMutation) AddAPIKeyIDs(ids ...int64) {
if m.api_keys == nil {
@@ -4536,7 +4752,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, 14)
fields := make([]string, 0, 17)
if m.created_at != nil {
fields = append(fields, group.FieldCreatedAt)
}
@@ -4579,6 +4795,15 @@ func (m *GroupMutation) Fields() []string {
if m.default_validity_days != nil {
fields = append(fields, group.FieldDefaultValidityDays)
}
if m.image_price_1k != nil {
fields = append(fields, group.FieldImagePrice1k)
}
if m.image_price_2k != nil {
fields = append(fields, group.FieldImagePrice2k)
}
if m.image_price_4k != nil {
fields = append(fields, group.FieldImagePrice4k)
}
return fields
}
@@ -4615,6 +4840,12 @@ func (m *GroupMutation) Field(name string) (ent.Value, bool) {
return m.MonthlyLimitUsd()
case group.FieldDefaultValidityDays:
return m.DefaultValidityDays()
case group.FieldImagePrice1k:
return m.ImagePrice1k()
case group.FieldImagePrice2k:
return m.ImagePrice2k()
case group.FieldImagePrice4k:
return m.ImagePrice4k()
}
return nil, false
}
@@ -4652,6 +4883,12 @@ func (m *GroupMutation) OldField(ctx context.Context, name string) (ent.Value, e
return m.OldMonthlyLimitUsd(ctx)
case group.FieldDefaultValidityDays:
return m.OldDefaultValidityDays(ctx)
case group.FieldImagePrice1k:
return m.OldImagePrice1k(ctx)
case group.FieldImagePrice2k:
return m.OldImagePrice2k(ctx)
case group.FieldImagePrice4k:
return m.OldImagePrice4k(ctx)
}
return nil, fmt.Errorf("unknown Group field %s", name)
}
@@ -4759,6 +4996,27 @@ func (m *GroupMutation) SetField(name string, value ent.Value) error {
}
m.SetDefaultValidityDays(v)
return nil
case group.FieldImagePrice1k:
v, ok := value.(float64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetImagePrice1k(v)
return nil
case group.FieldImagePrice2k:
v, ok := value.(float64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetImagePrice2k(v)
return nil
case group.FieldImagePrice4k:
v, ok := value.(float64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetImagePrice4k(v)
return nil
}
return fmt.Errorf("unknown Group field %s", name)
}
@@ -4782,6 +5040,15 @@ func (m *GroupMutation) AddedFields() []string {
if m.adddefault_validity_days != nil {
fields = append(fields, group.FieldDefaultValidityDays)
}
if m.addimage_price_1k != nil {
fields = append(fields, group.FieldImagePrice1k)
}
if m.addimage_price_2k != nil {
fields = append(fields, group.FieldImagePrice2k)
}
if m.addimage_price_4k != nil {
fields = append(fields, group.FieldImagePrice4k)
}
return fields
}
@@ -4800,6 +5067,12 @@ func (m *GroupMutation) AddedField(name string) (ent.Value, bool) {
return m.AddedMonthlyLimitUsd()
case group.FieldDefaultValidityDays:
return m.AddedDefaultValidityDays()
case group.FieldImagePrice1k:
return m.AddedImagePrice1k()
case group.FieldImagePrice2k:
return m.AddedImagePrice2k()
case group.FieldImagePrice4k:
return m.AddedImagePrice4k()
}
return nil, false
}
@@ -4844,6 +5117,27 @@ func (m *GroupMutation) AddField(name string, value ent.Value) error {
}
m.AddDefaultValidityDays(v)
return nil
case group.FieldImagePrice1k:
v, ok := value.(float64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddImagePrice1k(v)
return nil
case group.FieldImagePrice2k:
v, ok := value.(float64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddImagePrice2k(v)
return nil
case group.FieldImagePrice4k:
v, ok := value.(float64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddImagePrice4k(v)
return nil
}
return fmt.Errorf("unknown Group numeric field %s", name)
}
@@ -4867,6 +5161,15 @@ func (m *GroupMutation) ClearedFields() []string {
if m.FieldCleared(group.FieldMonthlyLimitUsd) {
fields = append(fields, group.FieldMonthlyLimitUsd)
}
if m.FieldCleared(group.FieldImagePrice1k) {
fields = append(fields, group.FieldImagePrice1k)
}
if m.FieldCleared(group.FieldImagePrice2k) {
fields = append(fields, group.FieldImagePrice2k)
}
if m.FieldCleared(group.FieldImagePrice4k) {
fields = append(fields, group.FieldImagePrice4k)
}
return fields
}
@@ -4896,6 +5199,15 @@ func (m *GroupMutation) ClearField(name string) error {
case group.FieldMonthlyLimitUsd:
m.ClearMonthlyLimitUsd()
return nil
case group.FieldImagePrice1k:
m.ClearImagePrice1k()
return nil
case group.FieldImagePrice2k:
m.ClearImagePrice2k()
return nil
case group.FieldImagePrice4k:
m.ClearImagePrice4k()
return nil
}
return fmt.Errorf("unknown Group nullable field %s", name)
}
@@ -4946,6 +5258,15 @@ func (m *GroupMutation) ResetField(name string) error {
case group.FieldDefaultValidityDays:
m.ResetDefaultValidityDays()
return nil
case group.FieldImagePrice1k:
m.ResetImagePrice1k()
return nil
case group.FieldImagePrice2k:
m.ResetImagePrice2k()
return nil
case group.FieldImagePrice4k:
m.ResetImagePrice4k()
return nil
}
return fmt.Errorf("unknown Group field %s", name)
}
@@ -7713,6 +8034,9 @@ type UsageLogMutation struct {
addduration_ms *int
first_token_ms *int
addfirst_token_ms *int
image_count *int
addimage_count *int
image_size *string
created_at *time.Time
clearedFields map[string]struct{}
user *int64
@@ -9066,6 +9390,111 @@ func (m *UsageLogMutation) ResetFirstTokenMs() {
delete(m.clearedFields, usagelog.FieldFirstTokenMs)
}
// SetImageCount sets the "image_count" field.
func (m *UsageLogMutation) SetImageCount(i int) {
m.image_count = &i
m.addimage_count = nil
}
// ImageCount returns the value of the "image_count" field in the mutation.
func (m *UsageLogMutation) ImageCount() (r int, exists bool) {
v := m.image_count
if v == nil {
return
}
return *v, true
}
// OldImageCount returns the old "image_count" field's value of the UsageLog entity.
// If the UsageLog 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 *UsageLogMutation) OldImageCount(ctx context.Context) (v int, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldImageCount is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldImageCount requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldImageCount: %w", err)
}
return oldValue.ImageCount, nil
}
// AddImageCount adds i to the "image_count" field.
func (m *UsageLogMutation) AddImageCount(i int) {
if m.addimage_count != nil {
*m.addimage_count += i
} else {
m.addimage_count = &i
}
}
// AddedImageCount returns the value that was added to the "image_count" field in this mutation.
func (m *UsageLogMutation) AddedImageCount() (r int, exists bool) {
v := m.addimage_count
if v == nil {
return
}
return *v, true
}
// ResetImageCount resets all changes to the "image_count" field.
func (m *UsageLogMutation) ResetImageCount() {
m.image_count = nil
m.addimage_count = nil
}
// SetImageSize sets the "image_size" field.
func (m *UsageLogMutation) SetImageSize(s string) {
m.image_size = &s
}
// ImageSize returns the value of the "image_size" field in the mutation.
func (m *UsageLogMutation) ImageSize() (r string, exists bool) {
v := m.image_size
if v == nil {
return
}
return *v, true
}
// OldImageSize returns the old "image_size" field's value of the UsageLog entity.
// If the UsageLog 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 *UsageLogMutation) OldImageSize(ctx context.Context) (v *string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldImageSize is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldImageSize requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldImageSize: %w", err)
}
return oldValue.ImageSize, nil
}
// ClearImageSize clears the value of the "image_size" field.
func (m *UsageLogMutation) ClearImageSize() {
m.image_size = nil
m.clearedFields[usagelog.FieldImageSize] = struct{}{}
}
// ImageSizeCleared returns if the "image_size" field was cleared in this mutation.
func (m *UsageLogMutation) ImageSizeCleared() bool {
_, ok := m.clearedFields[usagelog.FieldImageSize]
return ok
}
// ResetImageSize resets all changes to the "image_size" field.
func (m *UsageLogMutation) ResetImageSize() {
m.image_size = nil
delete(m.clearedFields, usagelog.FieldImageSize)
}
// SetCreatedAt sets the "created_at" field.
func (m *UsageLogMutation) SetCreatedAt(t time.Time) {
m.created_at = &t
@@ -9271,7 +9700,7 @@ func (m *UsageLogMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *UsageLogMutation) Fields() []string {
fields := make([]string, 0, 25)
fields := make([]string, 0, 27)
if m.user != nil {
fields = append(fields, usagelog.FieldUserID)
}
@@ -9344,6 +9773,12 @@ func (m *UsageLogMutation) Fields() []string {
if m.first_token_ms != nil {
fields = append(fields, usagelog.FieldFirstTokenMs)
}
if m.image_count != nil {
fields = append(fields, usagelog.FieldImageCount)
}
if m.image_size != nil {
fields = append(fields, usagelog.FieldImageSize)
}
if m.created_at != nil {
fields = append(fields, usagelog.FieldCreatedAt)
}
@@ -9403,6 +9838,10 @@ func (m *UsageLogMutation) Field(name string) (ent.Value, bool) {
return m.DurationMs()
case usagelog.FieldFirstTokenMs:
return m.FirstTokenMs()
case usagelog.FieldImageCount:
return m.ImageCount()
case usagelog.FieldImageSize:
return m.ImageSize()
case usagelog.FieldCreatedAt:
return m.CreatedAt()
}
@@ -9462,6 +9901,10 @@ func (m *UsageLogMutation) OldField(ctx context.Context, name string) (ent.Value
return m.OldDurationMs(ctx)
case usagelog.FieldFirstTokenMs:
return m.OldFirstTokenMs(ctx)
case usagelog.FieldImageCount:
return m.OldImageCount(ctx)
case usagelog.FieldImageSize:
return m.OldImageSize(ctx)
case usagelog.FieldCreatedAt:
return m.OldCreatedAt(ctx)
}
@@ -9641,6 +10084,20 @@ func (m *UsageLogMutation) SetField(name string, value ent.Value) error {
}
m.SetFirstTokenMs(v)
return nil
case usagelog.FieldImageCount:
v, ok := value.(int)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetImageCount(v)
return nil
case usagelog.FieldImageSize:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetImageSize(v)
return nil
case usagelog.FieldCreatedAt:
v, ok := value.(time.Time)
if !ok {
@@ -9704,6 +10161,9 @@ func (m *UsageLogMutation) AddedFields() []string {
if m.addfirst_token_ms != nil {
fields = append(fields, usagelog.FieldFirstTokenMs)
}
if m.addimage_count != nil {
fields = append(fields, usagelog.FieldImageCount)
}
return fields
}
@@ -9744,6 +10204,8 @@ func (m *UsageLogMutation) AddedField(name string) (ent.Value, bool) {
return m.AddedDurationMs()
case usagelog.FieldFirstTokenMs:
return m.AddedFirstTokenMs()
case usagelog.FieldImageCount:
return m.AddedImageCount()
}
return nil, false
}
@@ -9865,6 +10327,13 @@ func (m *UsageLogMutation) AddField(name string, value ent.Value) error {
}
m.AddFirstTokenMs(v)
return nil
case usagelog.FieldImageCount:
v, ok := value.(int)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddImageCount(v)
return nil
}
return fmt.Errorf("unknown UsageLog numeric field %s", name)
}
@@ -9885,6 +10354,9 @@ func (m *UsageLogMutation) ClearedFields() []string {
if m.FieldCleared(usagelog.FieldFirstTokenMs) {
fields = append(fields, usagelog.FieldFirstTokenMs)
}
if m.FieldCleared(usagelog.FieldImageSize) {
fields = append(fields, usagelog.FieldImageSize)
}
return fields
}
@@ -9911,6 +10383,9 @@ func (m *UsageLogMutation) ClearField(name string) error {
case usagelog.FieldFirstTokenMs:
m.ClearFirstTokenMs()
return nil
case usagelog.FieldImageSize:
m.ClearImageSize()
return nil
}
return fmt.Errorf("unknown UsageLog nullable field %s", name)
}
@@ -9991,6 +10466,12 @@ func (m *UsageLogMutation) ResetField(name string) error {
case usagelog.FieldFirstTokenMs:
m.ResetFirstTokenMs()
return nil
case usagelog.FieldImageCount:
m.ResetImageCount()
return nil
case usagelog.FieldImageSize:
m.ResetImageSize()
return nil
case usagelog.FieldCreatedAt:
m.ResetCreatedAt()
return nil

View File

@@ -521,8 +521,16 @@ func init() {
usagelogDescStream := usagelogFields[21].Descriptor()
// usagelog.DefaultStream holds the default value on creation for the stream field.
usagelog.DefaultStream = usagelogDescStream.Default.(bool)
// usagelogDescImageCount is the schema descriptor for image_count field.
usagelogDescImageCount := usagelogFields[24].Descriptor()
// usagelog.DefaultImageCount holds the default value on creation for the image_count field.
usagelog.DefaultImageCount = usagelogDescImageCount.Default.(int)
// usagelogDescImageSize is the schema descriptor for image_size field.
usagelogDescImageSize := usagelogFields[25].Descriptor()
// usagelog.ImageSizeValidator is a validator for the "image_size" field. It is called by the builders before save.
usagelog.ImageSizeValidator = usagelogDescImageSize.Validators[0].(func(string) error)
// usagelogDescCreatedAt is the schema descriptor for created_at field.
usagelogDescCreatedAt := usagelogFields[24].Descriptor()
usagelogDescCreatedAt := usagelogFields[26].Descriptor()
// usagelog.DefaultCreatedAt holds the default value on creation for the created_at field.
usagelog.DefaultCreatedAt = usagelogDescCreatedAt.Default.(func() time.Time)
userMixin := schema.User{}.Mixin()

View File

@@ -72,6 +72,20 @@ func (Group) Fields() []ent.Field {
SchemaType(map[string]string{dialect.Postgres: "decimal(20,8)"}),
field.Int("default_validity_days").
Default(30),
// 图片生成计费配置antigravity 和 gemini 平台使用)
field.Float("image_price_1k").
Optional().
Nillable().
SchemaType(map[string]string{dialect.Postgres: "decimal(20,8)"}),
field.Float("image_price_2k").
Optional().
Nillable().
SchemaType(map[string]string{dialect.Postgres: "decimal(20,8)"}),
field.Float("image_price_4k").
Optional().
Nillable().
SchemaType(map[string]string{dialect.Postgres: "decimal(20,8)"}),
}
}

View File

@@ -97,6 +97,14 @@ func (UsageLog) Fields() []ent.Field {
Optional().
Nillable(),
// 图片生成字段(仅 gemini-3-pro-image 等图片模型使用)
field.Int("image_count").
Default(0),
field.String("image_size").
MaxLen(10).
Optional().
Nillable(),
// 时间戳(只有 created_at日志不可修改
field.Time("created_at").
Default(time.Now).

View File

@@ -70,6 +70,10 @@ type UsageLog struct {
DurationMs *int `json:"duration_ms,omitempty"`
// FirstTokenMs holds the value of the "first_token_ms" field.
FirstTokenMs *int `json:"first_token_ms,omitempty"`
// ImageCount holds the value of the "image_count" field.
ImageCount int `json:"image_count,omitempty"`
// ImageSize holds the value of the "image_size" field.
ImageSize *string `json:"image_size,omitempty"`
// CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
@@ -159,9 +163,9 @@ func (*UsageLog) scanValues(columns []string) ([]any, error) {
values[i] = new(sql.NullBool)
case usagelog.FieldInputCost, usagelog.FieldOutputCost, usagelog.FieldCacheCreationCost, usagelog.FieldCacheReadCost, usagelog.FieldTotalCost, usagelog.FieldActualCost, usagelog.FieldRateMultiplier:
values[i] = new(sql.NullFloat64)
case usagelog.FieldID, usagelog.FieldUserID, usagelog.FieldAPIKeyID, usagelog.FieldAccountID, usagelog.FieldGroupID, usagelog.FieldSubscriptionID, usagelog.FieldInputTokens, usagelog.FieldOutputTokens, usagelog.FieldCacheCreationTokens, usagelog.FieldCacheReadTokens, usagelog.FieldCacheCreation5mTokens, usagelog.FieldCacheCreation1hTokens, usagelog.FieldBillingType, usagelog.FieldDurationMs, usagelog.FieldFirstTokenMs:
case usagelog.FieldID, usagelog.FieldUserID, usagelog.FieldAPIKeyID, usagelog.FieldAccountID, usagelog.FieldGroupID, usagelog.FieldSubscriptionID, usagelog.FieldInputTokens, usagelog.FieldOutputTokens, usagelog.FieldCacheCreationTokens, usagelog.FieldCacheReadTokens, usagelog.FieldCacheCreation5mTokens, usagelog.FieldCacheCreation1hTokens, usagelog.FieldBillingType, usagelog.FieldDurationMs, usagelog.FieldFirstTokenMs, usagelog.FieldImageCount:
values[i] = new(sql.NullInt64)
case usagelog.FieldRequestID, usagelog.FieldModel:
case usagelog.FieldRequestID, usagelog.FieldModel, usagelog.FieldImageSize:
values[i] = new(sql.NullString)
case usagelog.FieldCreatedAt:
values[i] = new(sql.NullTime)
@@ -334,6 +338,19 @@ func (_m *UsageLog) assignValues(columns []string, values []any) error {
_m.FirstTokenMs = new(int)
*_m.FirstTokenMs = int(value.Int64)
}
case usagelog.FieldImageCount:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field image_count", values[i])
} else if value.Valid {
_m.ImageCount = int(value.Int64)
}
case usagelog.FieldImageSize:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field image_size", values[i])
} else if value.Valid {
_m.ImageSize = new(string)
*_m.ImageSize = value.String
}
case usagelog.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i])
@@ -481,6 +498,14 @@ func (_m *UsageLog) String() string {
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
builder.WriteString("image_count=")
builder.WriteString(fmt.Sprintf("%v", _m.ImageCount))
builder.WriteString(", ")
if v := _m.ImageSize; v != nil {
builder.WriteString("image_size=")
builder.WriteString(*v)
}
builder.WriteString(", ")
builder.WriteString("created_at=")
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
builder.WriteByte(')')

View File

@@ -62,6 +62,10 @@ const (
FieldDurationMs = "duration_ms"
// FieldFirstTokenMs holds the string denoting the first_token_ms field in the database.
FieldFirstTokenMs = "first_token_ms"
// FieldImageCount holds the string denoting the image_count field in the database.
FieldImageCount = "image_count"
// FieldImageSize holds the string denoting the image_size field in the database.
FieldImageSize = "image_size"
// FieldCreatedAt holds the string denoting the created_at field in the database.
FieldCreatedAt = "created_at"
// EdgeUser holds the string denoting the user edge name in mutations.
@@ -140,6 +144,8 @@ var Columns = []string{
FieldStream,
FieldDurationMs,
FieldFirstTokenMs,
FieldImageCount,
FieldImageSize,
FieldCreatedAt,
}
@@ -188,6 +194,10 @@ var (
DefaultBillingType int8
// DefaultStream holds the default value on creation for the "stream" field.
DefaultStream bool
// DefaultImageCount holds the default value on creation for the "image_count" field.
DefaultImageCount int
// ImageSizeValidator is a validator for the "image_size" field. It is called by the builders before save.
ImageSizeValidator func(string) error
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time
)
@@ -320,6 +330,16 @@ func ByFirstTokenMs(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldFirstTokenMs, opts...).ToFunc()
}
// ByImageCount orders the results by the image_count field.
func ByImageCount(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldImageCount, opts...).ToFunc()
}
// ByImageSize orders the results by the image_size field.
func ByImageSize(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldImageSize, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()

View File

@@ -175,6 +175,16 @@ func FirstTokenMs(v int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldEQ(FieldFirstTokenMs, v))
}
// ImageCount applies equality check predicate on the "image_count" field. It's identical to ImageCountEQ.
func ImageCount(v int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldEQ(FieldImageCount, v))
}
// ImageSize applies equality check predicate on the "image_size" field. It's identical to ImageSizeEQ.
func ImageSize(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldEQ(FieldImageSize, v))
}
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.UsageLog {
return predicate.UsageLog(sql.FieldEQ(FieldCreatedAt, v))
@@ -1100,6 +1110,121 @@ func FirstTokenMsNotNil() predicate.UsageLog {
return predicate.UsageLog(sql.FieldNotNull(FieldFirstTokenMs))
}
// ImageCountEQ applies the EQ predicate on the "image_count" field.
func ImageCountEQ(v int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldEQ(FieldImageCount, v))
}
// ImageCountNEQ applies the NEQ predicate on the "image_count" field.
func ImageCountNEQ(v int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldNEQ(FieldImageCount, v))
}
// ImageCountIn applies the In predicate on the "image_count" field.
func ImageCountIn(vs ...int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldIn(FieldImageCount, vs...))
}
// ImageCountNotIn applies the NotIn predicate on the "image_count" field.
func ImageCountNotIn(vs ...int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldNotIn(FieldImageCount, vs...))
}
// ImageCountGT applies the GT predicate on the "image_count" field.
func ImageCountGT(v int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldGT(FieldImageCount, v))
}
// ImageCountGTE applies the GTE predicate on the "image_count" field.
func ImageCountGTE(v int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldGTE(FieldImageCount, v))
}
// ImageCountLT applies the LT predicate on the "image_count" field.
func ImageCountLT(v int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldLT(FieldImageCount, v))
}
// ImageCountLTE applies the LTE predicate on the "image_count" field.
func ImageCountLTE(v int) predicate.UsageLog {
return predicate.UsageLog(sql.FieldLTE(FieldImageCount, v))
}
// ImageSizeEQ applies the EQ predicate on the "image_size" field.
func ImageSizeEQ(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldEQ(FieldImageSize, v))
}
// ImageSizeNEQ applies the NEQ predicate on the "image_size" field.
func ImageSizeNEQ(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldNEQ(FieldImageSize, v))
}
// ImageSizeIn applies the In predicate on the "image_size" field.
func ImageSizeIn(vs ...string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldIn(FieldImageSize, vs...))
}
// ImageSizeNotIn applies the NotIn predicate on the "image_size" field.
func ImageSizeNotIn(vs ...string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldNotIn(FieldImageSize, vs...))
}
// ImageSizeGT applies the GT predicate on the "image_size" field.
func ImageSizeGT(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldGT(FieldImageSize, v))
}
// ImageSizeGTE applies the GTE predicate on the "image_size" field.
func ImageSizeGTE(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldGTE(FieldImageSize, v))
}
// ImageSizeLT applies the LT predicate on the "image_size" field.
func ImageSizeLT(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldLT(FieldImageSize, v))
}
// ImageSizeLTE applies the LTE predicate on the "image_size" field.
func ImageSizeLTE(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldLTE(FieldImageSize, v))
}
// ImageSizeContains applies the Contains predicate on the "image_size" field.
func ImageSizeContains(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldContains(FieldImageSize, v))
}
// ImageSizeHasPrefix applies the HasPrefix predicate on the "image_size" field.
func ImageSizeHasPrefix(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldHasPrefix(FieldImageSize, v))
}
// ImageSizeHasSuffix applies the HasSuffix predicate on the "image_size" field.
func ImageSizeHasSuffix(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldHasSuffix(FieldImageSize, v))
}
// ImageSizeIsNil applies the IsNil predicate on the "image_size" field.
func ImageSizeIsNil() predicate.UsageLog {
return predicate.UsageLog(sql.FieldIsNull(FieldImageSize))
}
// ImageSizeNotNil applies the NotNil predicate on the "image_size" field.
func ImageSizeNotNil() predicate.UsageLog {
return predicate.UsageLog(sql.FieldNotNull(FieldImageSize))
}
// ImageSizeEqualFold applies the EqualFold predicate on the "image_size" field.
func ImageSizeEqualFold(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldEqualFold(FieldImageSize, v))
}
// ImageSizeContainsFold applies the ContainsFold predicate on the "image_size" field.
func ImageSizeContainsFold(v string) predicate.UsageLog {
return predicate.UsageLog(sql.FieldContainsFold(FieldImageSize, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.UsageLog {
return predicate.UsageLog(sql.FieldEQ(FieldCreatedAt, v))

View File

@@ -323,6 +323,34 @@ func (_c *UsageLogCreate) SetNillableFirstTokenMs(v *int) *UsageLogCreate {
return _c
}
// SetImageCount sets the "image_count" field.
func (_c *UsageLogCreate) SetImageCount(v int) *UsageLogCreate {
_c.mutation.SetImageCount(v)
return _c
}
// SetNillableImageCount sets the "image_count" field if the given value is not nil.
func (_c *UsageLogCreate) SetNillableImageCount(v *int) *UsageLogCreate {
if v != nil {
_c.SetImageCount(*v)
}
return _c
}
// SetImageSize sets the "image_size" field.
func (_c *UsageLogCreate) SetImageSize(v string) *UsageLogCreate {
_c.mutation.SetImageSize(v)
return _c
}
// SetNillableImageSize sets the "image_size" field if the given value is not nil.
func (_c *UsageLogCreate) SetNillableImageSize(v *string) *UsageLogCreate {
if v != nil {
_c.SetImageSize(*v)
}
return _c
}
// SetCreatedAt sets the "created_at" field.
func (_c *UsageLogCreate) SetCreatedAt(v time.Time) *UsageLogCreate {
_c.mutation.SetCreatedAt(v)
@@ -457,6 +485,10 @@ func (_c *UsageLogCreate) defaults() {
v := usagelog.DefaultStream
_c.mutation.SetStream(v)
}
if _, ok := _c.mutation.ImageCount(); !ok {
v := usagelog.DefaultImageCount
_c.mutation.SetImageCount(v)
}
if _, ok := _c.mutation.CreatedAt(); !ok {
v := usagelog.DefaultCreatedAt()
_c.mutation.SetCreatedAt(v)
@@ -535,6 +567,14 @@ func (_c *UsageLogCreate) check() error {
if _, ok := _c.mutation.Stream(); !ok {
return &ValidationError{Name: "stream", err: errors.New(`ent: missing required field "UsageLog.stream"`)}
}
if _, ok := _c.mutation.ImageCount(); !ok {
return &ValidationError{Name: "image_count", err: errors.New(`ent: missing required field "UsageLog.image_count"`)}
}
if v, ok := _c.mutation.ImageSize(); ok {
if err := usagelog.ImageSizeValidator(v); err != nil {
return &ValidationError{Name: "image_size", err: fmt.Errorf(`ent: validator failed for field "UsageLog.image_size": %w`, err)}
}
}
if _, ok := _c.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "UsageLog.created_at"`)}
}
@@ -650,6 +690,14 @@ func (_c *UsageLogCreate) createSpec() (*UsageLog, *sqlgraph.CreateSpec) {
_spec.SetField(usagelog.FieldFirstTokenMs, field.TypeInt, value)
_node.FirstTokenMs = &value
}
if value, ok := _c.mutation.ImageCount(); ok {
_spec.SetField(usagelog.FieldImageCount, field.TypeInt, value)
_node.ImageCount = value
}
if value, ok := _c.mutation.ImageSize(); ok {
_spec.SetField(usagelog.FieldImageSize, field.TypeString, value)
_node.ImageSize = &value
}
if value, ok := _c.mutation.CreatedAt(); ok {
_spec.SetField(usagelog.FieldCreatedAt, field.TypeTime, value)
_node.CreatedAt = value
@@ -1199,6 +1247,42 @@ func (u *UsageLogUpsert) ClearFirstTokenMs() *UsageLogUpsert {
return u
}
// SetImageCount sets the "image_count" field.
func (u *UsageLogUpsert) SetImageCount(v int) *UsageLogUpsert {
u.Set(usagelog.FieldImageCount, v)
return u
}
// UpdateImageCount sets the "image_count" field to the value that was provided on create.
func (u *UsageLogUpsert) UpdateImageCount() *UsageLogUpsert {
u.SetExcluded(usagelog.FieldImageCount)
return u
}
// AddImageCount adds v to the "image_count" field.
func (u *UsageLogUpsert) AddImageCount(v int) *UsageLogUpsert {
u.Add(usagelog.FieldImageCount, v)
return u
}
// SetImageSize sets the "image_size" field.
func (u *UsageLogUpsert) SetImageSize(v string) *UsageLogUpsert {
u.Set(usagelog.FieldImageSize, v)
return u
}
// UpdateImageSize sets the "image_size" field to the value that was provided on create.
func (u *UsageLogUpsert) UpdateImageSize() *UsageLogUpsert {
u.SetExcluded(usagelog.FieldImageSize)
return u
}
// ClearImageSize clears the value of the "image_size" field.
func (u *UsageLogUpsert) ClearImageSize() *UsageLogUpsert {
u.SetNull(usagelog.FieldImageSize)
return u
}
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// Using this option is equivalent to using:
//
@@ -1720,6 +1804,48 @@ func (u *UsageLogUpsertOne) ClearFirstTokenMs() *UsageLogUpsertOne {
})
}
// SetImageCount sets the "image_count" field.
func (u *UsageLogUpsertOne) SetImageCount(v int) *UsageLogUpsertOne {
return u.Update(func(s *UsageLogUpsert) {
s.SetImageCount(v)
})
}
// AddImageCount adds v to the "image_count" field.
func (u *UsageLogUpsertOne) AddImageCount(v int) *UsageLogUpsertOne {
return u.Update(func(s *UsageLogUpsert) {
s.AddImageCount(v)
})
}
// UpdateImageCount sets the "image_count" field to the value that was provided on create.
func (u *UsageLogUpsertOne) UpdateImageCount() *UsageLogUpsertOne {
return u.Update(func(s *UsageLogUpsert) {
s.UpdateImageCount()
})
}
// SetImageSize sets the "image_size" field.
func (u *UsageLogUpsertOne) SetImageSize(v string) *UsageLogUpsertOne {
return u.Update(func(s *UsageLogUpsert) {
s.SetImageSize(v)
})
}
// UpdateImageSize sets the "image_size" field to the value that was provided on create.
func (u *UsageLogUpsertOne) UpdateImageSize() *UsageLogUpsertOne {
return u.Update(func(s *UsageLogUpsert) {
s.UpdateImageSize()
})
}
// ClearImageSize clears the value of the "image_size" field.
func (u *UsageLogUpsertOne) ClearImageSize() *UsageLogUpsertOne {
return u.Update(func(s *UsageLogUpsert) {
s.ClearImageSize()
})
}
// Exec executes the query.
func (u *UsageLogUpsertOne) Exec(ctx context.Context) error {
if len(u.create.conflict) == 0 {
@@ -2407,6 +2533,48 @@ func (u *UsageLogUpsertBulk) ClearFirstTokenMs() *UsageLogUpsertBulk {
})
}
// SetImageCount sets the "image_count" field.
func (u *UsageLogUpsertBulk) SetImageCount(v int) *UsageLogUpsertBulk {
return u.Update(func(s *UsageLogUpsert) {
s.SetImageCount(v)
})
}
// AddImageCount adds v to the "image_count" field.
func (u *UsageLogUpsertBulk) AddImageCount(v int) *UsageLogUpsertBulk {
return u.Update(func(s *UsageLogUpsert) {
s.AddImageCount(v)
})
}
// UpdateImageCount sets the "image_count" field to the value that was provided on create.
func (u *UsageLogUpsertBulk) UpdateImageCount() *UsageLogUpsertBulk {
return u.Update(func(s *UsageLogUpsert) {
s.UpdateImageCount()
})
}
// SetImageSize sets the "image_size" field.
func (u *UsageLogUpsertBulk) SetImageSize(v string) *UsageLogUpsertBulk {
return u.Update(func(s *UsageLogUpsert) {
s.SetImageSize(v)
})
}
// UpdateImageSize sets the "image_size" field to the value that was provided on create.
func (u *UsageLogUpsertBulk) UpdateImageSize() *UsageLogUpsertBulk {
return u.Update(func(s *UsageLogUpsert) {
s.UpdateImageSize()
})
}
// ClearImageSize clears the value of the "image_size" field.
func (u *UsageLogUpsertBulk) ClearImageSize() *UsageLogUpsertBulk {
return u.Update(func(s *UsageLogUpsert) {
s.ClearImageSize()
})
}
// Exec executes the query.
func (u *UsageLogUpsertBulk) Exec(ctx context.Context) error {
if u.create.err != nil {

View File

@@ -504,6 +504,47 @@ func (_u *UsageLogUpdate) ClearFirstTokenMs() *UsageLogUpdate {
return _u
}
// SetImageCount sets the "image_count" field.
func (_u *UsageLogUpdate) SetImageCount(v int) *UsageLogUpdate {
_u.mutation.ResetImageCount()
_u.mutation.SetImageCount(v)
return _u
}
// SetNillableImageCount sets the "image_count" field if the given value is not nil.
func (_u *UsageLogUpdate) SetNillableImageCount(v *int) *UsageLogUpdate {
if v != nil {
_u.SetImageCount(*v)
}
return _u
}
// AddImageCount adds value to the "image_count" field.
func (_u *UsageLogUpdate) AddImageCount(v int) *UsageLogUpdate {
_u.mutation.AddImageCount(v)
return _u
}
// SetImageSize sets the "image_size" field.
func (_u *UsageLogUpdate) SetImageSize(v string) *UsageLogUpdate {
_u.mutation.SetImageSize(v)
return _u
}
// SetNillableImageSize sets the "image_size" field if the given value is not nil.
func (_u *UsageLogUpdate) SetNillableImageSize(v *string) *UsageLogUpdate {
if v != nil {
_u.SetImageSize(*v)
}
return _u
}
// ClearImageSize clears the value of the "image_size" field.
func (_u *UsageLogUpdate) ClearImageSize() *UsageLogUpdate {
_u.mutation.ClearImageSize()
return _u
}
// SetUser sets the "user" edge to the User entity.
func (_u *UsageLogUpdate) SetUser(v *User) *UsageLogUpdate {
return _u.SetUserID(v.ID)
@@ -603,6 +644,11 @@ func (_u *UsageLogUpdate) check() error {
return &ValidationError{Name: "model", err: fmt.Errorf(`ent: validator failed for field "UsageLog.model": %w`, err)}
}
}
if v, ok := _u.mutation.ImageSize(); ok {
if err := usagelog.ImageSizeValidator(v); err != nil {
return &ValidationError{Name: "image_size", err: fmt.Errorf(`ent: validator failed for field "UsageLog.image_size": %w`, err)}
}
}
if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "UsageLog.user"`)
}
@@ -738,6 +784,18 @@ func (_u *UsageLogUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if _u.mutation.FirstTokenMsCleared() {
_spec.ClearField(usagelog.FieldFirstTokenMs, field.TypeInt)
}
if value, ok := _u.mutation.ImageCount(); ok {
_spec.SetField(usagelog.FieldImageCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedImageCount(); ok {
_spec.AddField(usagelog.FieldImageCount, field.TypeInt, value)
}
if value, ok := _u.mutation.ImageSize(); ok {
_spec.SetField(usagelog.FieldImageSize, field.TypeString, value)
}
if _u.mutation.ImageSizeCleared() {
_spec.ClearField(usagelog.FieldImageSize, field.TypeString)
}
if _u.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
@@ -1375,6 +1433,47 @@ func (_u *UsageLogUpdateOne) ClearFirstTokenMs() *UsageLogUpdateOne {
return _u
}
// SetImageCount sets the "image_count" field.
func (_u *UsageLogUpdateOne) SetImageCount(v int) *UsageLogUpdateOne {
_u.mutation.ResetImageCount()
_u.mutation.SetImageCount(v)
return _u
}
// SetNillableImageCount sets the "image_count" field if the given value is not nil.
func (_u *UsageLogUpdateOne) SetNillableImageCount(v *int) *UsageLogUpdateOne {
if v != nil {
_u.SetImageCount(*v)
}
return _u
}
// AddImageCount adds value to the "image_count" field.
func (_u *UsageLogUpdateOne) AddImageCount(v int) *UsageLogUpdateOne {
_u.mutation.AddImageCount(v)
return _u
}
// SetImageSize sets the "image_size" field.
func (_u *UsageLogUpdateOne) SetImageSize(v string) *UsageLogUpdateOne {
_u.mutation.SetImageSize(v)
return _u
}
// SetNillableImageSize sets the "image_size" field if the given value is not nil.
func (_u *UsageLogUpdateOne) SetNillableImageSize(v *string) *UsageLogUpdateOne {
if v != nil {
_u.SetImageSize(*v)
}
return _u
}
// ClearImageSize clears the value of the "image_size" field.
func (_u *UsageLogUpdateOne) ClearImageSize() *UsageLogUpdateOne {
_u.mutation.ClearImageSize()
return _u
}
// SetUser sets the "user" edge to the User entity.
func (_u *UsageLogUpdateOne) SetUser(v *User) *UsageLogUpdateOne {
return _u.SetUserID(v.ID)
@@ -1487,6 +1586,11 @@ func (_u *UsageLogUpdateOne) check() error {
return &ValidationError{Name: "model", err: fmt.Errorf(`ent: validator failed for field "UsageLog.model": %w`, err)}
}
}
if v, ok := _u.mutation.ImageSize(); ok {
if err := usagelog.ImageSizeValidator(v); err != nil {
return &ValidationError{Name: "image_size", err: fmt.Errorf(`ent: validator failed for field "UsageLog.image_size": %w`, err)}
}
}
if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "UsageLog.user"`)
}
@@ -1639,6 +1743,18 @@ func (_u *UsageLogUpdateOne) sqlSave(ctx context.Context) (_node *UsageLog, err
if _u.mutation.FirstTokenMsCleared() {
_spec.ClearField(usagelog.FieldFirstTokenMs, field.TypeInt)
}
if value, ok := _u.mutation.ImageCount(); ok {
_spec.SetField(usagelog.FieldImageCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedImageCount(); ok {
_spec.AddField(usagelog.FieldImageCount, field.TypeInt, value)
}
if value, ok := _u.mutation.ImageSize(); ok {
_spec.SetField(usagelog.FieldImageSize, field.TypeString, value)
}
if _u.mutation.ImageSizeCleared() {
_spec.ClearField(usagelog.FieldImageSize, field.TypeString)
}
if _u.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,

View File

@@ -33,6 +33,10 @@ type CreateGroupRequest struct {
DailyLimitUSD *float64 `json:"daily_limit_usd"`
WeeklyLimitUSD *float64 `json:"weekly_limit_usd"`
MonthlyLimitUSD *float64 `json:"monthly_limit_usd"`
// 图片生成计费配置(仅 antigravity 平台使用)
ImagePrice1K *float64 `json:"image_price_1k"`
ImagePrice2K *float64 `json:"image_price_2k"`
ImagePrice4K *float64 `json:"image_price_4k"`
}
// UpdateGroupRequest represents update group request
@@ -47,6 +51,10 @@ type UpdateGroupRequest struct {
DailyLimitUSD *float64 `json:"daily_limit_usd"`
WeeklyLimitUSD *float64 `json:"weekly_limit_usd"`
MonthlyLimitUSD *float64 `json:"monthly_limit_usd"`
// 图片生成计费配置(仅 antigravity 平台使用)
ImagePrice1K *float64 `json:"image_price_1k"`
ImagePrice2K *float64 `json:"image_price_2k"`
ImagePrice4K *float64 `json:"image_price_4k"`
}
// List handles listing all groups with pagination
@@ -139,6 +147,9 @@ func (h *GroupHandler) Create(c *gin.Context) {
DailyLimitUSD: req.DailyLimitUSD,
WeeklyLimitUSD: req.WeeklyLimitUSD,
MonthlyLimitUSD: req.MonthlyLimitUSD,
ImagePrice1K: req.ImagePrice1K,
ImagePrice2K: req.ImagePrice2K,
ImagePrice4K: req.ImagePrice4K,
})
if err != nil {
response.ErrorFrom(c, err)
@@ -174,6 +185,9 @@ func (h *GroupHandler) Update(c *gin.Context) {
DailyLimitUSD: req.DailyLimitUSD,
WeeklyLimitUSD: req.WeeklyLimitUSD,
MonthlyLimitUSD: req.MonthlyLimitUSD,
ImagePrice1K: req.ImagePrice1K,
ImagePrice2K: req.ImagePrice2K,
ImagePrice4K: req.ImagePrice4K,
})
if err != nil {
response.ErrorFrom(c, err)

View File

@@ -78,6 +78,9 @@ func GroupFromServiceShallow(g *service.Group) *Group {
DailyLimitUSD: g.DailyLimitUSD,
WeeklyLimitUSD: g.WeeklyLimitUSD,
MonthlyLimitUSD: g.MonthlyLimitUSD,
ImagePrice1K: g.ImagePrice1K,
ImagePrice2K: g.ImagePrice2K,
ImagePrice4K: g.ImagePrice4K,
CreatedAt: g.CreatedAt,
UpdatedAt: g.UpdatedAt,
AccountCount: g.AccountCount,
@@ -246,6 +249,8 @@ func UsageLogFromService(l *service.UsageLog) *UsageLog {
Stream: l.Stream,
DurationMs: l.DurationMs,
FirstTokenMs: l.FirstTokenMs,
ImageCount: l.ImageCount,
ImageSize: l.ImageSize,
CreatedAt: l.CreatedAt,
User: UserFromServiceShallow(l.User),
APIKey: APIKeyFromService(l.APIKey),

View File

@@ -47,6 +47,11 @@ type Group struct {
WeeklyLimitUSD *float64 `json:"weekly_limit_usd"`
MonthlyLimitUSD *float64 `json:"monthly_limit_usd"`
// 图片生成计费配置(仅 antigravity 平台使用)
ImagePrice1K *float64 `json:"image_price_1k"`
ImagePrice2K *float64 `json:"image_price_2k"`
ImagePrice4K *float64 `json:"image_price_4k"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
@@ -168,6 +173,10 @@ type UsageLog struct {
DurationMs *int `json:"duration_ms"`
FirstTokenMs *int `json:"first_token_ms"`
// 图片生成字段
ImageCount int `json:"image_count"`
ImageSize *string `json:"image_size"`
CreatedAt time.Time `json:"created_at"`
User *User `json:"user,omitempty"`

View File

@@ -67,6 +67,13 @@ type GeminiGenerationConfig struct {
TopK *int `json:"topK,omitempty"`
ThinkingConfig *GeminiThinkingConfig `json:"thinkingConfig,omitempty"`
StopSequences []string `json:"stopSequences,omitempty"`
ImageConfig *GeminiImageConfig `json:"imageConfig,omitempty"`
}
// GeminiImageConfig Gemini 图片生成配置(仅 gemini-3-pro-image 支持)
type GeminiImageConfig struct {
AspectRatio string `json:"aspectRatio,omitempty"` // "1:1", "16:9", "9:16", "4:3", "3:4"
ImageSize string `json:"imageSize,omitempty"` // "1K", "2K", "4K"
}
// GeminiThinkingConfig Gemini thinking 配置

View File

@@ -321,6 +321,9 @@ func groupEntityToService(g *dbent.Group) *service.Group {
DailyLimitUSD: g.DailyLimitUsd,
WeeklyLimitUSD: g.WeeklyLimitUsd,
MonthlyLimitUSD: g.MonthlyLimitUsd,
ImagePrice1K: g.ImagePrice1k,
ImagePrice2K: g.ImagePrice2k,
ImagePrice4K: g.ImagePrice4k,
DefaultValidityDays: g.DefaultValidityDays,
CreatedAt: g.CreatedAt,
UpdatedAt: g.UpdatedAt,

View File

@@ -43,6 +43,9 @@ func (r *groupRepository) Create(ctx context.Context, groupIn *service.Group) er
SetNillableDailyLimitUsd(groupIn.DailyLimitUSD).
SetNillableWeeklyLimitUsd(groupIn.WeeklyLimitUSD).
SetNillableMonthlyLimitUsd(groupIn.MonthlyLimitUSD).
SetNillableImagePrice1k(groupIn.ImagePrice1K).
SetNillableImagePrice2k(groupIn.ImagePrice2K).
SetNillableImagePrice4k(groupIn.ImagePrice4K).
SetDefaultValidityDays(groupIn.DefaultValidityDays)
created, err := builder.Save(ctx)
@@ -80,6 +83,9 @@ func (r *groupRepository) Update(ctx context.Context, groupIn *service.Group) er
SetNillableDailyLimitUsd(groupIn.DailyLimitUSD).
SetNillableWeeklyLimitUsd(groupIn.WeeklyLimitUSD).
SetNillableMonthlyLimitUsd(groupIn.MonthlyLimitUSD).
SetNillableImagePrice1k(groupIn.ImagePrice1K).
SetNillableImagePrice2k(groupIn.ImagePrice2K).
SetNillableImagePrice4k(groupIn.ImagePrice4K).
SetDefaultValidityDays(groupIn.DefaultValidityDays).
Save(ctx)
if err != nil {

View File

@@ -22,7 +22,7 @@ import (
"github.com/lib/pq"
)
const usageLogSelectColumns = "id, user_id, api_key_id, account_id, request_id, model, group_id, subscription_id, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, cache_creation_5m_tokens, cache_creation_1h_tokens, input_cost, output_cost, cache_creation_cost, cache_read_cost, total_cost, actual_cost, rate_multiplier, billing_type, stream, duration_ms, first_token_ms, created_at"
const usageLogSelectColumns = "id, user_id, api_key_id, account_id, request_id, model, group_id, subscription_id, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, cache_creation_5m_tokens, cache_creation_1h_tokens, input_cost, output_cost, cache_creation_cost, cache_read_cost, total_cost, actual_cost, rate_multiplier, billing_type, stream, duration_ms, first_token_ms, image_count, image_size, created_at"
type usageLogRepository struct {
client *dbent.Client
@@ -109,6 +109,8 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
stream,
duration_ms,
first_token_ms,
image_count,
image_size,
created_at
) VALUES (
$1, $2, $3, $4, $5,
@@ -116,7 +118,8 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
$8, $9, $10, $11,
$12, $13,
$14, $15, $16, $17, $18, $19,
$20, $21, $22, $23, $24, $25
$20, $21, $22, $23, $24,
$25, $26, $27
)
ON CONFLICT (request_id, api_key_id) DO NOTHING
RETURNING id, created_at
@@ -126,6 +129,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
subscriptionID := nullInt64(log.SubscriptionID)
duration := nullInt(log.DurationMs)
firstToken := nullInt(log.FirstTokenMs)
imageSize := nullString(log.ImageSize)
var requestIDArg any
if requestID != "" {
@@ -157,6 +161,8 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
log.Stream,
duration,
firstToken,
log.ImageCount,
imageSize,
createdAt,
}
if err := scanSingleRow(ctx, sqlq, query, args, &log.ID, &log.CreatedAt); err != nil {
@@ -1789,6 +1795,8 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
stream bool
durationMs sql.NullInt64
firstTokenMs sql.NullInt64
imageCount int
imageSize sql.NullString
createdAt time.Time
)
@@ -1818,6 +1826,8 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
&stream,
&durationMs,
&firstTokenMs,
&imageCount,
&imageSize,
&createdAt,
); err != nil {
return nil, err
@@ -1844,6 +1854,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
RateMultiplier: rateMultiplier,
BillingType: int8(billingType),
Stream: stream,
ImageCount: imageCount,
CreatedAt: createdAt,
}
@@ -1866,6 +1877,9 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
value := int(firstTokenMs.Int64)
log.FirstTokenMs = &value
}
if imageSize.Valid {
log.ImageSize = &imageSize.String
}
return log, nil
}
@@ -1938,6 +1952,13 @@ func nullInt(v *int) sql.NullInt64 {
return sql.NullInt64{Int64: int64(*v), Valid: true}
}
func nullString(v *string) sql.NullString {
if v == nil || *v == "" {
return sql.NullString{}
}
return sql.NullString{String: *v, Valid: true}
}
func setToSlice(set map[int64]struct{}) []int64 {
out := make([]int64, 0, len(set))
for id := range set {

View File

@@ -98,6 +98,10 @@ type CreateGroupInput struct {
DailyLimitUSD *float64 // 日限额 (USD)
WeeklyLimitUSD *float64 // 周限额 (USD)
MonthlyLimitUSD *float64 // 月限额 (USD)
// 图片生成计费配置(仅 antigravity 平台使用)
ImagePrice1K *float64
ImagePrice2K *float64
ImagePrice4K *float64
}
type UpdateGroupInput struct {
@@ -111,6 +115,10 @@ type UpdateGroupInput struct {
DailyLimitUSD *float64 // 日限额 (USD)
WeeklyLimitUSD *float64 // 周限额 (USD)
MonthlyLimitUSD *float64 // 月限额 (USD)
// 图片生成计费配置(仅 antigravity 平台使用)
ImagePrice1K *float64
ImagePrice2K *float64
ImagePrice4K *float64
}
type CreateAccountInput struct {
@@ -507,6 +515,9 @@ func (s *adminServiceImpl) CreateGroup(ctx context.Context, input *CreateGroupIn
DailyLimitUSD: dailyLimit,
WeeklyLimitUSD: weeklyLimit,
MonthlyLimitUSD: monthlyLimit,
ImagePrice1K: input.ImagePrice1K,
ImagePrice2K: input.ImagePrice2K,
ImagePrice4K: input.ImagePrice4K,
}
if err := s.groupRepo.Create(ctx, group); err != nil {
return nil, err
@@ -561,6 +572,16 @@ func (s *adminServiceImpl) UpdateGroup(ctx context.Context, id int64, input *Upd
if input.MonthlyLimitUSD != nil {
group.MonthlyLimitUSD = normalizeLimit(input.MonthlyLimitUSD)
}
// 图片生成计费配置
if input.ImagePrice1K != nil {
group.ImagePrice1K = input.ImagePrice1K
}
if input.ImagePrice2K != nil {
group.ImagePrice2K = input.ImagePrice2K
}
if input.ImagePrice4K != nil {
group.ImagePrice4K = input.ImagePrice4K
}
if err := s.groupRepo.Update(ctx, group); err != nil {
return nil, err

View File

@@ -0,0 +1,197 @@
//go:build unit
package service
import (
"context"
"testing"
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
"github.com/stretchr/testify/require"
)
// groupRepoStubForAdmin 用于测试 AdminService 的 GroupRepository Stub
type groupRepoStubForAdmin struct {
created *Group // 记录 Create 调用的参数
updated *Group // 记录 Update 调用的参数
getByID *Group // GetByID 返回值
getErr error // GetByID 返回的错误
}
func (s *groupRepoStubForAdmin) Create(_ context.Context, g *Group) error {
s.created = g
return nil
}
func (s *groupRepoStubForAdmin) Update(_ context.Context, g *Group) error {
s.updated = g
return nil
}
func (s *groupRepoStubForAdmin) GetByID(_ context.Context, _ int64) (*Group, error) {
if s.getErr != nil {
return nil, s.getErr
}
return s.getByID, nil
}
func (s *groupRepoStubForAdmin) Delete(_ context.Context, _ int64) error {
panic("unexpected Delete call")
}
func (s *groupRepoStubForAdmin) DeleteCascade(_ context.Context, _ int64) ([]int64, error) {
panic("unexpected DeleteCascade call")
}
func (s *groupRepoStubForAdmin) List(_ context.Context, _ pagination.PaginationParams) ([]Group, *pagination.PaginationResult, error) {
panic("unexpected List call")
}
func (s *groupRepoStubForAdmin) ListWithFilters(_ context.Context, _ pagination.PaginationParams, _, _ string, _ *bool) ([]Group, *pagination.PaginationResult, error) {
panic("unexpected ListWithFilters call")
}
func (s *groupRepoStubForAdmin) ListActive(_ context.Context) ([]Group, error) {
panic("unexpected ListActive call")
}
func (s *groupRepoStubForAdmin) ListActiveByPlatform(_ context.Context, _ string) ([]Group, error) {
panic("unexpected ListActiveByPlatform call")
}
func (s *groupRepoStubForAdmin) ExistsByName(_ context.Context, _ string) (bool, error) {
panic("unexpected ExistsByName call")
}
func (s *groupRepoStubForAdmin) GetAccountCount(_ context.Context, _ int64) (int64, error) {
panic("unexpected GetAccountCount call")
}
func (s *groupRepoStubForAdmin) DeleteAccountGroupsByGroupID(_ context.Context, _ int64) (int64, error) {
panic("unexpected DeleteAccountGroupsByGroupID call")
}
// TestAdminService_CreateGroup_WithImagePricing 测试创建分组时 ImagePrice 字段正确传递
func TestAdminService_CreateGroup_WithImagePricing(t *testing.T) {
repo := &groupRepoStubForAdmin{}
svc := &adminServiceImpl{groupRepo: repo}
price1K := 0.10
price2K := 0.15
price4K := 0.30
input := &CreateGroupInput{
Name: "test-group",
Description: "Test group",
Platform: PlatformAntigravity,
RateMultiplier: 1.0,
ImagePrice1K: &price1K,
ImagePrice2K: &price2K,
ImagePrice4K: &price4K,
}
group, err := svc.CreateGroup(context.Background(), input)
require.NoError(t, err)
require.NotNil(t, group)
// 验证 repo 收到了正确的字段
require.NotNil(t, repo.created)
require.NotNil(t, repo.created.ImagePrice1K)
require.NotNil(t, repo.created.ImagePrice2K)
require.NotNil(t, repo.created.ImagePrice4K)
require.InDelta(t, 0.10, *repo.created.ImagePrice1K, 0.0001)
require.InDelta(t, 0.15, *repo.created.ImagePrice2K, 0.0001)
require.InDelta(t, 0.30, *repo.created.ImagePrice4K, 0.0001)
}
// TestAdminService_CreateGroup_NilImagePricing 测试 ImagePrice 为 nil 时正常创建
func TestAdminService_CreateGroup_NilImagePricing(t *testing.T) {
repo := &groupRepoStubForAdmin{}
svc := &adminServiceImpl{groupRepo: repo}
input := &CreateGroupInput{
Name: "test-group",
Description: "Test group",
Platform: PlatformAntigravity,
RateMultiplier: 1.0,
// ImagePrice 字段全部为 nil
}
group, err := svc.CreateGroup(context.Background(), input)
require.NoError(t, err)
require.NotNil(t, group)
// 验证 ImagePrice 字段为 nil
require.NotNil(t, repo.created)
require.Nil(t, repo.created.ImagePrice1K)
require.Nil(t, repo.created.ImagePrice2K)
require.Nil(t, repo.created.ImagePrice4K)
}
// TestAdminService_UpdateGroup_WithImagePricing 测试更新分组时 ImagePrice 字段正确更新
func TestAdminService_UpdateGroup_WithImagePricing(t *testing.T) {
existingGroup := &Group{
ID: 1,
Name: "existing-group",
Platform: PlatformAntigravity,
Status: StatusActive,
}
repo := &groupRepoStubForAdmin{getByID: existingGroup}
svc := &adminServiceImpl{groupRepo: repo}
price1K := 0.12
price2K := 0.18
price4K := 0.36
input := &UpdateGroupInput{
ImagePrice1K: &price1K,
ImagePrice2K: &price2K,
ImagePrice4K: &price4K,
}
group, err := svc.UpdateGroup(context.Background(), 1, input)
require.NoError(t, err)
require.NotNil(t, group)
// 验证 repo 收到了更新后的字段
require.NotNil(t, repo.updated)
require.NotNil(t, repo.updated.ImagePrice1K)
require.NotNil(t, repo.updated.ImagePrice2K)
require.NotNil(t, repo.updated.ImagePrice4K)
require.InDelta(t, 0.12, *repo.updated.ImagePrice1K, 0.0001)
require.InDelta(t, 0.18, *repo.updated.ImagePrice2K, 0.0001)
require.InDelta(t, 0.36, *repo.updated.ImagePrice4K, 0.0001)
}
// TestAdminService_UpdateGroup_PartialImagePricing 测试仅更新部分 ImagePrice 字段
func TestAdminService_UpdateGroup_PartialImagePricing(t *testing.T) {
oldPrice2K := 0.15
existingGroup := &Group{
ID: 1,
Name: "existing-group",
Platform: PlatformAntigravity,
Status: StatusActive,
ImagePrice2K: &oldPrice2K, // 已有 2K 价格
}
repo := &groupRepoStubForAdmin{getByID: existingGroup}
svc := &adminServiceImpl{groupRepo: repo}
// 只更新 1K 价格
price1K := 0.10
input := &UpdateGroupInput{
ImagePrice1K: &price1K,
// ImagePrice2K 和 ImagePrice4K 为 nil不更新
}
group, err := svc.UpdateGroup(context.Background(), 1, input)
require.NoError(t, err)
require.NotNil(t, group)
// 验证1K 被更新2K 保持原值4K 仍为 nil
require.NotNil(t, repo.updated)
require.NotNil(t, repo.updated.ImagePrice1K)
require.InDelta(t, 0.10, *repo.updated.ImagePrice1K, 0.0001)
require.NotNil(t, repo.updated.ImagePrice2K)
require.InDelta(t, 0.15, *repo.updated.ImagePrice2K, 0.0001) // 原值保持
require.Nil(t, repo.updated.ImagePrice4K)
}

View File

@@ -652,6 +652,9 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co
return nil, s.writeGoogleError(c, http.StatusBadRequest, "Request body is empty")
}
// 解析请求以获取 image_size用于图片计费
imageSize := s.extractImageSize(body)
switch action {
case "generateContent", "streamGenerateContent":
// ok
@@ -832,6 +835,13 @@ handleSuccess:
usage = &ClaudeUsage{}
}
// 判断是否为图片生成模型
imageCount := 0
if isImageGenerationModel(mappedModel) {
// 图片模型按次计费,默认 1 张图片
imageCount = 1
}
return &ForwardResult{
RequestID: requestID,
Usage: *usage,
@@ -839,6 +849,8 @@ handleSuccess:
Stream: stream,
Duration: time.Since(startTime),
FirstTokenMs: firstTokenMs,
ImageCount: imageCount,
ImageSize: imageSize,
}, nil
}
@@ -1161,3 +1173,27 @@ func (s *AntigravityGatewayService) handleClaudeStreamingResponse(c *gin.Context
return &antigravityStreamResult{usage: convertUsage(agUsage), firstTokenMs: firstTokenMs}, nil
}
// extractImageSize 从 Gemini 请求中提取 image_size 参数
func (s *AntigravityGatewayService) extractImageSize(body []byte) string {
var req antigravity.GeminiRequest
if err := json.Unmarshal(body, &req); err != nil {
return "2K" // 默认 2K
}
if req.GenerationConfig != nil && req.GenerationConfig.ImageConfig != nil {
size := strings.ToUpper(strings.TrimSpace(req.GenerationConfig.ImageConfig.ImageSize))
if size == "1K" || size == "2K" || size == "4K" {
return size
}
}
return "2K" // 默认 2K
}
// isImageGenerationModel 判断模型是否为图片生成模型
func isImageGenerationModel(model string) bool {
modelLower := strings.ToLower(model)
return strings.Contains(modelLower, "gemini-3-pro-image") ||
strings.Contains(modelLower, "gemini-2.5-flash-image")
}

View File

@@ -0,0 +1,120 @@
//go:build unit
package service
import (
"testing"
"github.com/stretchr/testify/require"
)
// TestIsImageGenerationModel_GeminiProImage 测试 gemini-3-pro-image 识别
func TestIsImageGenerationModel_GeminiProImage(t *testing.T) {
require.True(t, isImageGenerationModel("gemini-3-pro-image"))
require.True(t, isImageGenerationModel("gemini-3-pro-image-preview"))
require.True(t, isImageGenerationModel("models/gemini-3-pro-image"))
}
// TestIsImageGenerationModel_GeminiFlashImage 测试 gemini-2.5-flash-image 识别
func TestIsImageGenerationModel_GeminiFlashImage(t *testing.T) {
require.True(t, isImageGenerationModel("gemini-2.5-flash-image"))
require.True(t, isImageGenerationModel("gemini-2.5-flash-image-preview"))
}
// TestIsImageGenerationModel_RegularModel 测试普通模型不被识别为图片模型
func TestIsImageGenerationModel_RegularModel(t *testing.T) {
require.False(t, isImageGenerationModel("claude-3-opus"))
require.False(t, isImageGenerationModel("claude-sonnet-4-20250514"))
require.False(t, isImageGenerationModel("gpt-4o"))
require.False(t, isImageGenerationModel("gemini-2.5-pro")) // 非图片模型
require.False(t, isImageGenerationModel("gemini-2.5-flash"))
}
// TestIsImageGenerationModel_CaseInsensitive 测试大小写不敏感
func TestIsImageGenerationModel_CaseInsensitive(t *testing.T) {
require.True(t, isImageGenerationModel("GEMINI-3-PRO-IMAGE"))
require.True(t, isImageGenerationModel("Gemini-3-Pro-Image"))
require.True(t, isImageGenerationModel("GEMINI-2.5-FLASH-IMAGE"))
}
// TestExtractImageSize_ValidSizes 测试有效尺寸解析
func TestExtractImageSize_ValidSizes(t *testing.T) {
svc := &AntigravityGatewayService{}
// 1K
body := []byte(`{"generationConfig":{"imageConfig":{"imageSize":"1K"}}}`)
require.Equal(t, "1K", svc.extractImageSize(body))
// 2K
body = []byte(`{"generationConfig":{"imageConfig":{"imageSize":"2K"}}}`)
require.Equal(t, "2K", svc.extractImageSize(body))
// 4K
body = []byte(`{"generationConfig":{"imageConfig":{"imageSize":"4K"}}}`)
require.Equal(t, "4K", svc.extractImageSize(body))
}
// TestExtractImageSize_CaseInsensitive 测试大小写不敏感
func TestExtractImageSize_CaseInsensitive(t *testing.T) {
svc := &AntigravityGatewayService{}
body := []byte(`{"generationConfig":{"imageConfig":{"imageSize":"1k"}}}`)
require.Equal(t, "1K", svc.extractImageSize(body))
body = []byte(`{"generationConfig":{"imageConfig":{"imageSize":"4k"}}}`)
require.Equal(t, "4K", svc.extractImageSize(body))
}
// TestExtractImageSize_Default 测试无 imageConfig 返回默认 2K
func TestExtractImageSize_Default(t *testing.T) {
svc := &AntigravityGatewayService{}
// 无 generationConfig
body := []byte(`{"contents":[]}`)
require.Equal(t, "2K", svc.extractImageSize(body))
// 有 generationConfig 但无 imageConfig
body = []byte(`{"generationConfig":{"temperature":0.7}}`)
require.Equal(t, "2K", svc.extractImageSize(body))
// 有 imageConfig 但无 imageSize
body = []byte(`{"generationConfig":{"imageConfig":{}}}`)
require.Equal(t, "2K", svc.extractImageSize(body))
}
// TestExtractImageSize_InvalidJSON 测试非法 JSON 返回默认 2K
func TestExtractImageSize_InvalidJSON(t *testing.T) {
svc := &AntigravityGatewayService{}
body := []byte(`not valid json`)
require.Equal(t, "2K", svc.extractImageSize(body))
body = []byte(`{"broken":`)
require.Equal(t, "2K", svc.extractImageSize(body))
}
// TestExtractImageSize_EmptySize 测试空 imageSize 返回默认 2K
func TestExtractImageSize_EmptySize(t *testing.T) {
svc := &AntigravityGatewayService{}
body := []byte(`{"generationConfig":{"imageConfig":{"imageSize":""}}}`)
require.Equal(t, "2K", svc.extractImageSize(body))
// 空格
body = []byte(`{"generationConfig":{"imageConfig":{"imageSize":" "}}}`)
require.Equal(t, "2K", svc.extractImageSize(body))
}
// TestExtractImageSize_InvalidSize 测试无效尺寸返回默认 2K
func TestExtractImageSize_InvalidSize(t *testing.T) {
svc := &AntigravityGatewayService{}
body := []byte(`{"generationConfig":{"imageConfig":{"imageSize":"3K"}}}`)
require.Equal(t, "2K", svc.extractImageSize(body))
body = []byte(`{"generationConfig":{"imageConfig":{"imageSize":"8K"}}}`)
require.Equal(t, "2K", svc.extractImageSize(body))
body = []byte(`{"generationConfig":{"imageConfig":{"imageSize":"invalid"}}}`)
require.Equal(t, "2K", svc.extractImageSize(body))
}

View File

@@ -295,3 +295,88 @@ func (s *BillingService) ForceUpdatePricing() error {
}
return fmt.Errorf("pricing service not initialized")
}
// ImagePriceConfig 图片计费配置
type ImagePriceConfig struct {
Price1K *float64 // 1K 尺寸价格nil 表示使用默认值)
Price2K *float64 // 2K 尺寸价格nil 表示使用默认值)
Price4K *float64 // 4K 尺寸价格nil 表示使用默认值)
}
// CalculateImageCost 计算图片生成费用
// model: 请求的模型名称(用于获取 LiteLLM 默认价格)
// imageSize: 图片尺寸 "1K", "2K", "4K"
// imageCount: 生成的图片数量
// groupConfig: 分组配置的价格(可能为 nil表示使用默认值
// rateMultiplier: 费率倍数
func (s *BillingService) CalculateImageCost(model string, imageSize string, imageCount int, groupConfig *ImagePriceConfig, rateMultiplier float64) *CostBreakdown {
if imageCount <= 0 {
return &CostBreakdown{}
}
// 获取单价
unitPrice := s.getImageUnitPrice(model, imageSize, groupConfig)
// 计算总费用
totalCost := unitPrice * float64(imageCount)
// 应用倍率
if rateMultiplier <= 0 {
rateMultiplier = 1.0
}
actualCost := totalCost * rateMultiplier
return &CostBreakdown{
TotalCost: totalCost,
ActualCost: actualCost,
}
}
// getImageUnitPrice 获取图片单价
func (s *BillingService) getImageUnitPrice(model string, imageSize string, groupConfig *ImagePriceConfig) float64 {
// 优先使用分组配置的价格
if groupConfig != nil {
switch imageSize {
case "1K":
if groupConfig.Price1K != nil {
return *groupConfig.Price1K
}
case "2K":
if groupConfig.Price2K != nil {
return *groupConfig.Price2K
}
case "4K":
if groupConfig.Price4K != nil {
return *groupConfig.Price4K
}
}
}
// 回退到 LiteLLM 默认价格
return s.getDefaultImagePrice(model, imageSize)
}
// getDefaultImagePrice 获取 LiteLLM 默认图片价格
func (s *BillingService) getDefaultImagePrice(model string, imageSize string) float64 {
basePrice := 0.0
// 从 PricingService 获取 output_cost_per_image
if s.pricingService != nil {
pricing := s.pricingService.GetModelPricing(model)
if pricing != nil && pricing.OutputCostPerImage > 0 {
basePrice = pricing.OutputCostPerImage
}
}
// 如果没有找到价格,使用硬编码默认值($0.134,来自 gemini-3-pro-image-preview
if basePrice <= 0 {
basePrice = 0.134
}
// 4K 尺寸翻倍
if imageSize == "4K" {
return basePrice * 2
}
return basePrice
}

View File

@@ -0,0 +1,149 @@
//go:build unit
package service
import (
"testing"
"github.com/stretchr/testify/require"
)
// TestCalculateImageCost_DefaultPricing 测试无分组配置时使用默认价格
func TestCalculateImageCost_DefaultPricing(t *testing.T) {
svc := &BillingService{} // pricingService 为 nil使用硬编码默认值
// 2K 尺寸,默认价格 $0.134
cost := svc.CalculateImageCost("gemini-3-pro-image", "2K", 1, nil, 1.0)
require.InDelta(t, 0.134, cost.TotalCost, 0.0001)
require.InDelta(t, 0.134, cost.ActualCost, 0.0001)
// 多张图片
cost = svc.CalculateImageCost("gemini-3-pro-image", "2K", 3, nil, 1.0)
require.InDelta(t, 0.402, cost.TotalCost, 0.0001)
}
// TestCalculateImageCost_GroupCustomPricing 测试分组自定义价格
func TestCalculateImageCost_GroupCustomPricing(t *testing.T) {
svc := &BillingService{}
price1K := 0.10
price2K := 0.15
price4K := 0.30
groupConfig := &ImagePriceConfig{
Price1K: &price1K,
Price2K: &price2K,
Price4K: &price4K,
}
// 1K 使用分组价格
cost := svc.CalculateImageCost("gemini-3-pro-image", "1K", 2, groupConfig, 1.0)
require.InDelta(t, 0.20, cost.TotalCost, 0.0001)
// 2K 使用分组价格
cost = svc.CalculateImageCost("gemini-3-pro-image", "2K", 1, groupConfig, 1.0)
require.InDelta(t, 0.15, cost.TotalCost, 0.0001)
// 4K 使用分组价格
cost = svc.CalculateImageCost("gemini-3-pro-image", "4K", 1, groupConfig, 1.0)
require.InDelta(t, 0.30, cost.TotalCost, 0.0001)
}
// TestCalculateImageCost_4KDoublePrice 测试 4K 默认价格翻倍
func TestCalculateImageCost_4KDoublePrice(t *testing.T) {
svc := &BillingService{}
// 4K 尺寸,默认价格翻倍 $0.134 * 2 = $0.268
cost := svc.CalculateImageCost("gemini-3-pro-image", "4K", 1, nil, 1.0)
require.InDelta(t, 0.268, cost.TotalCost, 0.0001)
}
// TestCalculateImageCost_RateMultiplier 测试费率倍数
func TestCalculateImageCost_RateMultiplier(t *testing.T) {
svc := &BillingService{}
// 费率倍数 1.5x
cost := svc.CalculateImageCost("gemini-3-pro-image", "2K", 1, nil, 1.5)
require.InDelta(t, 0.134, cost.TotalCost, 0.0001) // TotalCost 不变
require.InDelta(t, 0.201, cost.ActualCost, 0.0001) // ActualCost = 0.134 * 1.5
// 费率倍数 2.0x
cost = svc.CalculateImageCost("gemini-3-pro-image", "2K", 2, nil, 2.0)
require.InDelta(t, 0.268, cost.TotalCost, 0.0001)
require.InDelta(t, 0.536, cost.ActualCost, 0.0001)
}
// TestCalculateImageCost_ZeroCount 测试 imageCount=0
func TestCalculateImageCost_ZeroCount(t *testing.T) {
svc := &BillingService{}
cost := svc.CalculateImageCost("gemini-3-pro-image", "2K", 0, nil, 1.0)
require.Equal(t, 0.0, cost.TotalCost)
require.Equal(t, 0.0, cost.ActualCost)
}
// TestCalculateImageCost_NegativeCount 测试 imageCount=-1
func TestCalculateImageCost_NegativeCount(t *testing.T) {
svc := &BillingService{}
cost := svc.CalculateImageCost("gemini-3-pro-image", "2K", -1, nil, 1.0)
require.Equal(t, 0.0, cost.TotalCost)
require.Equal(t, 0.0, cost.ActualCost)
}
// TestCalculateImageCost_ZeroRateMultiplier 测试费率倍数为 0 时默认使用 1.0
func TestCalculateImageCost_ZeroRateMultiplier(t *testing.T) {
svc := &BillingService{}
cost := svc.CalculateImageCost("gemini-3-pro-image", "2K", 1, nil, 0)
require.InDelta(t, 0.134, cost.TotalCost, 0.0001)
require.InDelta(t, 0.134, cost.ActualCost, 0.0001) // 0 倍率当作 1.0 处理
}
// TestGetImageUnitPrice_GroupPriorityOverDefault 测试分组价格优先于默认价格
func TestGetImageUnitPrice_GroupPriorityOverDefault(t *testing.T) {
svc := &BillingService{}
price2K := 0.20
groupConfig := &ImagePriceConfig{
Price2K: &price2K,
}
// 分组配置了 2K 价格,应该使用分组价格而不是默认的 $0.134
cost := svc.CalculateImageCost("gemini-3-pro-image", "2K", 1, groupConfig, 1.0)
require.InDelta(t, 0.20, cost.TotalCost, 0.0001)
}
// TestGetImageUnitPrice_PartialGroupConfig 测试分组部分配置时回退默认
func TestGetImageUnitPrice_PartialGroupConfig(t *testing.T) {
svc := &BillingService{}
// 只配置 1K 价格
price1K := 0.10
groupConfig := &ImagePriceConfig{
Price1K: &price1K,
}
// 1K 使用分组价格
cost := svc.CalculateImageCost("gemini-3-pro-image", "1K", 1, groupConfig, 1.0)
require.InDelta(t, 0.10, cost.TotalCost, 0.0001)
// 2K 回退默认价格 $0.134
cost = svc.CalculateImageCost("gemini-3-pro-image", "2K", 1, groupConfig, 1.0)
require.InDelta(t, 0.134, cost.TotalCost, 0.0001)
// 4K 回退默认价格 $0.268 (翻倍)
cost = svc.CalculateImageCost("gemini-3-pro-image", "4K", 1, groupConfig, 1.0)
require.InDelta(t, 0.268, cost.TotalCost, 0.0001)
}
// TestGetDefaultImagePrice_FallbackHardcoded 测试 PricingService 无数据时使用硬编码默认值
func TestGetDefaultImagePrice_FallbackHardcoded(t *testing.T) {
svc := &BillingService{} // pricingService 为 nil
// 1K 和 2K 使用相同的默认价格 $0.134
cost := svc.CalculateImageCost("gemini-3-pro-image", "1K", 1, nil, 1.0)
require.InDelta(t, 0.134, cost.TotalCost, 0.0001)
cost = svc.CalculateImageCost("gemini-3-pro-image", "2K", 1, nil, 1.0)
require.InDelta(t, 0.134, cost.TotalCost, 0.0001)
}

View File

@@ -100,6 +100,10 @@ type ForwardResult struct {
Stream bool
Duration time.Duration
FirstTokenMs *int // 首字时间(流式请求)
// 图片生成计费字段(仅 gemini-3-pro-image 使用)
ImageCount int // 生成的图片数量
ImageSize string // 图片尺寸 "1K", "2K", "4K"
}
// UpstreamFailoverError indicates an upstream error that should trigger account failover.
@@ -1794,25 +1798,40 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
account := input.Account
subscription := input.Subscription
// 计算费用
tokens := UsageTokens{
InputTokens: result.Usage.InputTokens,
OutputTokens: result.Usage.OutputTokens,
CacheCreationTokens: result.Usage.CacheCreationInputTokens,
CacheReadTokens: result.Usage.CacheReadInputTokens,
}
// 获取费率倍数
multiplier := s.cfg.Default.RateMultiplier
if apiKey.GroupID != nil && apiKey.Group != nil {
multiplier = apiKey.Group.RateMultiplier
}
cost, err := s.billingService.CalculateCost(result.Model, tokens, multiplier)
if err != nil {
log.Printf("Calculate cost failed: %v", err)
// 使用默认费用继续
cost = &CostBreakdown{ActualCost: 0}
var cost *CostBreakdown
// 根据请求类型选择计费方式
if result.ImageCount > 0 {
// 图片生成计费
var groupConfig *ImagePriceConfig
if apiKey.Group != nil {
groupConfig = &ImagePriceConfig{
Price1K: apiKey.Group.ImagePrice1K,
Price2K: apiKey.Group.ImagePrice2K,
Price4K: apiKey.Group.ImagePrice4K,
}
}
cost = s.billingService.CalculateImageCost(result.Model, result.ImageSize, result.ImageCount, groupConfig, multiplier)
} else {
// Token 计费
tokens := UsageTokens{
InputTokens: result.Usage.InputTokens,
OutputTokens: result.Usage.OutputTokens,
CacheCreationTokens: result.Usage.CacheCreationInputTokens,
CacheReadTokens: result.Usage.CacheReadInputTokens,
}
var err error
cost, err = s.billingService.CalculateCost(result.Model, tokens, multiplier)
if err != nil {
log.Printf("Calculate cost failed: %v", err)
cost = &CostBreakdown{ActualCost: 0}
}
}
// 判断计费方式:订阅模式 vs 余额模式
@@ -1824,6 +1843,10 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
// 创建使用日志
durationMs := int(result.Duration.Milliseconds())
var imageSize *string
if result.ImageSize != "" {
imageSize = &result.ImageSize
}
usageLog := &UsageLog{
UserID: user.ID,
APIKeyID: apiKey.ID,
@@ -1845,6 +1868,8 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
Stream: result.Stream,
DurationMs: &durationMs,
FirstTokenMs: result.FirstTokenMs,
ImageCount: result.ImageCount,
ImageSize: imageSize,
CreatedAt: time.Now(),
}

View File

@@ -17,6 +17,11 @@ type Group struct {
MonthlyLimitUSD *float64
DefaultValidityDays int
// 图片生成计费配置antigravity 和 gemini 平台使用)
ImagePrice1K *float64
ImagePrice2K *float64
ImagePrice4K *float64
CreatedAt time.Time
UpdatedAt time.Time
@@ -47,3 +52,19 @@ func (g *Group) HasWeeklyLimit() bool {
func (g *Group) HasMonthlyLimit() bool {
return g.MonthlyLimitUSD != nil && *g.MonthlyLimitUSD > 0
}
// GetImagePrice 根据 image_size 返回对应的图片生成价格
// 如果分组未配置价格,返回 nil调用方应使用默认值
func (g *Group) GetImagePrice(imageSize string) *float64 {
switch imageSize {
case "1K":
return g.ImagePrice1K
case "2K":
return g.ImagePrice2K
case "4K":
return g.ImagePrice4K
default:
// 未知尺寸默认按 2K 计费
return g.ImagePrice2K
}
}

View File

@@ -0,0 +1,92 @@
//go:build unit
package service
import (
"testing"
"github.com/stretchr/testify/require"
)
// TestGroup_GetImagePrice_1K 测试 1K 尺寸返回正确价格
func TestGroup_GetImagePrice_1K(t *testing.T) {
price := 0.10
group := &Group{
ImagePrice1K: &price,
}
result := group.GetImagePrice("1K")
require.NotNil(t, result)
require.InDelta(t, 0.10, *result, 0.0001)
}
// TestGroup_GetImagePrice_2K 测试 2K 尺寸返回正确价格
func TestGroup_GetImagePrice_2K(t *testing.T) {
price := 0.15
group := &Group{
ImagePrice2K: &price,
}
result := group.GetImagePrice("2K")
require.NotNil(t, result)
require.InDelta(t, 0.15, *result, 0.0001)
}
// TestGroup_GetImagePrice_4K 测试 4K 尺寸返回正确价格
func TestGroup_GetImagePrice_4K(t *testing.T) {
price := 0.30
group := &Group{
ImagePrice4K: &price,
}
result := group.GetImagePrice("4K")
require.NotNil(t, result)
require.InDelta(t, 0.30, *result, 0.0001)
}
// TestGroup_GetImagePrice_UnknownSize 测试未知尺寸回退 2K
func TestGroup_GetImagePrice_UnknownSize(t *testing.T) {
price2K := 0.15
group := &Group{
ImagePrice2K: &price2K,
}
// 未知尺寸 "3K" 应该回退到 2K
result := group.GetImagePrice("3K")
require.NotNil(t, result)
require.InDelta(t, 0.15, *result, 0.0001)
// 空字符串也回退到 2K
result = group.GetImagePrice("")
require.NotNil(t, result)
require.InDelta(t, 0.15, *result, 0.0001)
}
// TestGroup_GetImagePrice_NilValues 测试未配置时返回 nil
func TestGroup_GetImagePrice_NilValues(t *testing.T) {
group := &Group{
// 所有 ImagePrice 字段都是 nil
}
require.Nil(t, group.GetImagePrice("1K"))
require.Nil(t, group.GetImagePrice("2K"))
require.Nil(t, group.GetImagePrice("4K"))
require.Nil(t, group.GetImagePrice("unknown"))
}
// TestGroup_GetImagePrice_PartialConfig 测试部分配置
func TestGroup_GetImagePrice_PartialConfig(t *testing.T) {
price1K := 0.10
group := &Group{
ImagePrice1K: &price1K,
// ImagePrice2K 和 ImagePrice4K 未配置
}
result := group.GetImagePrice("1K")
require.NotNil(t, result)
require.InDelta(t, 0.10, *result, 0.0001)
// 2K 和 4K 返回 nil
require.Nil(t, group.GetImagePrice("2K"))
require.Nil(t, group.GetImagePrice("4K"))
}

View File

@@ -33,6 +33,7 @@ type LiteLLMModelPricing struct {
LiteLLMProvider string `json:"litellm_provider"`
Mode string `json:"mode"`
SupportsPromptCaching bool `json:"supports_prompt_caching"`
OutputCostPerImage float64 `json:"output_cost_per_image"` // 图片生成模型每张图片价格
}
// PricingRemoteClient 远程价格数据获取接口
@@ -50,6 +51,7 @@ type LiteLLMRawEntry struct {
LiteLLMProvider string `json:"litellm_provider"`
Mode string `json:"mode"`
SupportsPromptCaching bool `json:"supports_prompt_caching"`
OutputCostPerImage *float64 `json:"output_cost_per_image"`
}
// PricingService 动态价格服务
@@ -299,6 +301,9 @@ func (s *PricingService) parsePricingData(body []byte) (map[string]*LiteLLMModel
if entry.CacheReadInputTokenCost != nil {
pricing.CacheReadInputTokenCost = *entry.CacheReadInputTokenCost
}
if entry.OutputCostPerImage != nil {
pricing.OutputCostPerImage = *entry.OutputCostPerImage
}
result[modelName] = pricing
}

View File

@@ -39,6 +39,10 @@ type UsageLog struct {
DurationMs *int
FirstTokenMs *int
// 图片生成字段
ImageCount int
ImageSize *string
CreatedAt time.Time
User *User

View File

@@ -0,0 +1,10 @@
-- 为 Antigravity 分组添加图片生成计费配置
-- 支持 gemini-3-pro-image 模型的 1K/2K/4K 分辨率按次计费
ALTER TABLE groups ADD COLUMN IF NOT EXISTS image_price_1k DECIMAL(20,8);
ALTER TABLE groups ADD COLUMN IF NOT EXISTS image_price_2k DECIMAL(20,8);
ALTER TABLE groups ADD COLUMN IF NOT EXISTS image_price_4k DECIMAL(20,8);
COMMENT ON COLUMN groups.image_price_1k IS '1K 分辨率图片生成单价 (USD),仅 antigravity 平台使用';
COMMENT ON COLUMN groups.image_price_2k IS '2K 分辨率图片生成单价 (USD),仅 antigravity 平台使用';
COMMENT ON COLUMN groups.image_price_4k IS '4K 分辨率图片生成单价 (USD),仅 antigravity 平台使用';

View File

@@ -0,0 +1,5 @@
-- 为使用日志添加图片生成统计字段
-- 用于记录 gemini-3-pro-image 等图片生成模型的使用情况
ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS image_count INT DEFAULT 0;
ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS image_size VARCHAR(10);