mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-03 06:52:13 +08:00
feat(计费): 支持账号计费倍率快照与统计展示
- 新增 accounts.rate_multiplier(默认 1.0,允许 0) - 使用 usage_logs.account_rate_multiplier 记录倍率快照,避免历史回算 - 统计/导出/管理端展示账号口径费用(total_cost * account_rate_multiplier)
This commit is contained in:
@@ -43,6 +43,8 @@ type Account struct {
|
|||||||
Concurrency int `json:"concurrency,omitempty"`
|
Concurrency int `json:"concurrency,omitempty"`
|
||||||
// Priority holds the value of the "priority" field.
|
// Priority holds the value of the "priority" field.
|
||||||
Priority int `json:"priority,omitempty"`
|
Priority int `json:"priority,omitempty"`
|
||||||
|
// RateMultiplier holds the value of the "rate_multiplier" field.
|
||||||
|
RateMultiplier float64 `json:"rate_multiplier,omitempty"`
|
||||||
// Status holds the value of the "status" field.
|
// Status holds the value of the "status" field.
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
// ErrorMessage holds the value of the "error_message" field.
|
// ErrorMessage holds the value of the "error_message" field.
|
||||||
@@ -135,6 +137,8 @@ func (*Account) scanValues(columns []string) ([]any, error) {
|
|||||||
values[i] = new([]byte)
|
values[i] = new([]byte)
|
||||||
case account.FieldAutoPauseOnExpired, account.FieldSchedulable:
|
case account.FieldAutoPauseOnExpired, account.FieldSchedulable:
|
||||||
values[i] = new(sql.NullBool)
|
values[i] = new(sql.NullBool)
|
||||||
|
case account.FieldRateMultiplier:
|
||||||
|
values[i] = new(sql.NullFloat64)
|
||||||
case account.FieldID, account.FieldProxyID, account.FieldConcurrency, account.FieldPriority:
|
case account.FieldID, account.FieldProxyID, account.FieldConcurrency, account.FieldPriority:
|
||||||
values[i] = new(sql.NullInt64)
|
values[i] = new(sql.NullInt64)
|
||||||
case account.FieldName, account.FieldNotes, account.FieldPlatform, account.FieldType, account.FieldStatus, account.FieldErrorMessage, account.FieldSessionWindowStatus:
|
case account.FieldName, account.FieldNotes, account.FieldPlatform, account.FieldType, account.FieldStatus, account.FieldErrorMessage, account.FieldSessionWindowStatus:
|
||||||
@@ -241,6 +245,12 @@ func (_m *Account) assignValues(columns []string, values []any) error {
|
|||||||
} else if value.Valid {
|
} else if value.Valid {
|
||||||
_m.Priority = int(value.Int64)
|
_m.Priority = int(value.Int64)
|
||||||
}
|
}
|
||||||
|
case account.FieldRateMultiplier:
|
||||||
|
if value, ok := values[i].(*sql.NullFloat64); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field rate_multiplier", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.RateMultiplier = value.Float64
|
||||||
|
}
|
||||||
case account.FieldStatus:
|
case account.FieldStatus:
|
||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
return fmt.Errorf("unexpected type %T for field status", values[i])
|
return fmt.Errorf("unexpected type %T for field status", values[i])
|
||||||
@@ -420,6 +430,9 @@ func (_m *Account) String() string {
|
|||||||
builder.WriteString("priority=")
|
builder.WriteString("priority=")
|
||||||
builder.WriteString(fmt.Sprintf("%v", _m.Priority))
|
builder.WriteString(fmt.Sprintf("%v", _m.Priority))
|
||||||
builder.WriteString(", ")
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("rate_multiplier=")
|
||||||
|
builder.WriteString(fmt.Sprintf("%v", _m.RateMultiplier))
|
||||||
|
builder.WriteString(", ")
|
||||||
builder.WriteString("status=")
|
builder.WriteString("status=")
|
||||||
builder.WriteString(_m.Status)
|
builder.WriteString(_m.Status)
|
||||||
builder.WriteString(", ")
|
builder.WriteString(", ")
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ const (
|
|||||||
FieldConcurrency = "concurrency"
|
FieldConcurrency = "concurrency"
|
||||||
// FieldPriority holds the string denoting the priority field in the database.
|
// FieldPriority holds the string denoting the priority field in the database.
|
||||||
FieldPriority = "priority"
|
FieldPriority = "priority"
|
||||||
|
// FieldRateMultiplier holds the string denoting the rate_multiplier field in the database.
|
||||||
|
FieldRateMultiplier = "rate_multiplier"
|
||||||
// FieldStatus holds the string denoting the status field in the database.
|
// FieldStatus holds the string denoting the status field in the database.
|
||||||
FieldStatus = "status"
|
FieldStatus = "status"
|
||||||
// FieldErrorMessage holds the string denoting the error_message field in the database.
|
// FieldErrorMessage holds the string denoting the error_message field in the database.
|
||||||
@@ -116,6 +118,7 @@ var Columns = []string{
|
|||||||
FieldProxyID,
|
FieldProxyID,
|
||||||
FieldConcurrency,
|
FieldConcurrency,
|
||||||
FieldPriority,
|
FieldPriority,
|
||||||
|
FieldRateMultiplier,
|
||||||
FieldStatus,
|
FieldStatus,
|
||||||
FieldErrorMessage,
|
FieldErrorMessage,
|
||||||
FieldLastUsedAt,
|
FieldLastUsedAt,
|
||||||
@@ -174,6 +177,8 @@ var (
|
|||||||
DefaultConcurrency int
|
DefaultConcurrency int
|
||||||
// DefaultPriority holds the default value on creation for the "priority" field.
|
// DefaultPriority holds the default value on creation for the "priority" field.
|
||||||
DefaultPriority int
|
DefaultPriority int
|
||||||
|
// DefaultRateMultiplier holds the default value on creation for the "rate_multiplier" field.
|
||||||
|
DefaultRateMultiplier float64
|
||||||
// DefaultStatus holds the default value on creation for the "status" field.
|
// DefaultStatus holds the default value on creation for the "status" field.
|
||||||
DefaultStatus string
|
DefaultStatus string
|
||||||
// StatusValidator is a validator for the "status" field. It is called by the builders before save.
|
// StatusValidator is a validator for the "status" field. It is called by the builders before save.
|
||||||
@@ -244,6 +249,11 @@ func ByPriority(opts ...sql.OrderTermOption) OrderOption {
|
|||||||
return sql.OrderByField(FieldPriority, opts...).ToFunc()
|
return sql.OrderByField(FieldPriority, opts...).ToFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ByRateMultiplier orders the results by the rate_multiplier field.
|
||||||
|
func ByRateMultiplier(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldRateMultiplier, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
// ByStatus orders the results by the status field.
|
// ByStatus orders the results by the status field.
|
||||||
func ByStatus(opts ...sql.OrderTermOption) OrderOption {
|
func ByStatus(opts ...sql.OrderTermOption) OrderOption {
|
||||||
return sql.OrderByField(FieldStatus, opts...).ToFunc()
|
return sql.OrderByField(FieldStatus, opts...).ToFunc()
|
||||||
|
|||||||
@@ -105,6 +105,11 @@ func Priority(v int) predicate.Account {
|
|||||||
return predicate.Account(sql.FieldEQ(FieldPriority, v))
|
return predicate.Account(sql.FieldEQ(FieldPriority, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RateMultiplier applies equality check predicate on the "rate_multiplier" field. It's identical to RateMultiplierEQ.
|
||||||
|
func RateMultiplier(v float64) predicate.Account {
|
||||||
|
return predicate.Account(sql.FieldEQ(FieldRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
// Status applies equality check predicate on the "status" field. It's identical to StatusEQ.
|
// Status applies equality check predicate on the "status" field. It's identical to StatusEQ.
|
||||||
func Status(v string) predicate.Account {
|
func Status(v string) predicate.Account {
|
||||||
return predicate.Account(sql.FieldEQ(FieldStatus, v))
|
return predicate.Account(sql.FieldEQ(FieldStatus, v))
|
||||||
@@ -675,6 +680,46 @@ func PriorityLTE(v int) predicate.Account {
|
|||||||
return predicate.Account(sql.FieldLTE(FieldPriority, v))
|
return predicate.Account(sql.FieldLTE(FieldPriority, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RateMultiplierEQ applies the EQ predicate on the "rate_multiplier" field.
|
||||||
|
func RateMultiplierEQ(v float64) predicate.Account {
|
||||||
|
return predicate.Account(sql.FieldEQ(FieldRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateMultiplierNEQ applies the NEQ predicate on the "rate_multiplier" field.
|
||||||
|
func RateMultiplierNEQ(v float64) predicate.Account {
|
||||||
|
return predicate.Account(sql.FieldNEQ(FieldRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateMultiplierIn applies the In predicate on the "rate_multiplier" field.
|
||||||
|
func RateMultiplierIn(vs ...float64) predicate.Account {
|
||||||
|
return predicate.Account(sql.FieldIn(FieldRateMultiplier, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateMultiplierNotIn applies the NotIn predicate on the "rate_multiplier" field.
|
||||||
|
func RateMultiplierNotIn(vs ...float64) predicate.Account {
|
||||||
|
return predicate.Account(sql.FieldNotIn(FieldRateMultiplier, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateMultiplierGT applies the GT predicate on the "rate_multiplier" field.
|
||||||
|
func RateMultiplierGT(v float64) predicate.Account {
|
||||||
|
return predicate.Account(sql.FieldGT(FieldRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateMultiplierGTE applies the GTE predicate on the "rate_multiplier" field.
|
||||||
|
func RateMultiplierGTE(v float64) predicate.Account {
|
||||||
|
return predicate.Account(sql.FieldGTE(FieldRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateMultiplierLT applies the LT predicate on the "rate_multiplier" field.
|
||||||
|
func RateMultiplierLT(v float64) predicate.Account {
|
||||||
|
return predicate.Account(sql.FieldLT(FieldRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateMultiplierLTE applies the LTE predicate on the "rate_multiplier" field.
|
||||||
|
func RateMultiplierLTE(v float64) predicate.Account {
|
||||||
|
return predicate.Account(sql.FieldLTE(FieldRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
// StatusEQ applies the EQ predicate on the "status" field.
|
// StatusEQ applies the EQ predicate on the "status" field.
|
||||||
func StatusEQ(v string) predicate.Account {
|
func StatusEQ(v string) predicate.Account {
|
||||||
return predicate.Account(sql.FieldEQ(FieldStatus, v))
|
return predicate.Account(sql.FieldEQ(FieldStatus, v))
|
||||||
|
|||||||
@@ -153,6 +153,20 @@ func (_c *AccountCreate) SetNillablePriority(v *int) *AccountCreate {
|
|||||||
return _c
|
return _c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRateMultiplier sets the "rate_multiplier" field.
|
||||||
|
func (_c *AccountCreate) SetRateMultiplier(v float64) *AccountCreate {
|
||||||
|
_c.mutation.SetRateMultiplier(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableRateMultiplier sets the "rate_multiplier" field if the given value is not nil.
|
||||||
|
func (_c *AccountCreate) SetNillableRateMultiplier(v *float64) *AccountCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetRateMultiplier(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
// SetStatus sets the "status" field.
|
// SetStatus sets the "status" field.
|
||||||
func (_c *AccountCreate) SetStatus(v string) *AccountCreate {
|
func (_c *AccountCreate) SetStatus(v string) *AccountCreate {
|
||||||
_c.mutation.SetStatus(v)
|
_c.mutation.SetStatus(v)
|
||||||
@@ -429,6 +443,10 @@ func (_c *AccountCreate) defaults() error {
|
|||||||
v := account.DefaultPriority
|
v := account.DefaultPriority
|
||||||
_c.mutation.SetPriority(v)
|
_c.mutation.SetPriority(v)
|
||||||
}
|
}
|
||||||
|
if _, ok := _c.mutation.RateMultiplier(); !ok {
|
||||||
|
v := account.DefaultRateMultiplier
|
||||||
|
_c.mutation.SetRateMultiplier(v)
|
||||||
|
}
|
||||||
if _, ok := _c.mutation.Status(); !ok {
|
if _, ok := _c.mutation.Status(); !ok {
|
||||||
v := account.DefaultStatus
|
v := account.DefaultStatus
|
||||||
_c.mutation.SetStatus(v)
|
_c.mutation.SetStatus(v)
|
||||||
@@ -488,6 +506,9 @@ func (_c *AccountCreate) check() error {
|
|||||||
if _, ok := _c.mutation.Priority(); !ok {
|
if _, ok := _c.mutation.Priority(); !ok {
|
||||||
return &ValidationError{Name: "priority", err: errors.New(`ent: missing required field "Account.priority"`)}
|
return &ValidationError{Name: "priority", err: errors.New(`ent: missing required field "Account.priority"`)}
|
||||||
}
|
}
|
||||||
|
if _, ok := _c.mutation.RateMultiplier(); !ok {
|
||||||
|
return &ValidationError{Name: "rate_multiplier", err: errors.New(`ent: missing required field "Account.rate_multiplier"`)}
|
||||||
|
}
|
||||||
if _, ok := _c.mutation.Status(); !ok {
|
if _, ok := _c.mutation.Status(); !ok {
|
||||||
return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "Account.status"`)}
|
return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "Account.status"`)}
|
||||||
}
|
}
|
||||||
@@ -578,6 +599,10 @@ func (_c *AccountCreate) createSpec() (*Account, *sqlgraph.CreateSpec) {
|
|||||||
_spec.SetField(account.FieldPriority, field.TypeInt, value)
|
_spec.SetField(account.FieldPriority, field.TypeInt, value)
|
||||||
_node.Priority = value
|
_node.Priority = value
|
||||||
}
|
}
|
||||||
|
if value, ok := _c.mutation.RateMultiplier(); ok {
|
||||||
|
_spec.SetField(account.FieldRateMultiplier, field.TypeFloat64, value)
|
||||||
|
_node.RateMultiplier = value
|
||||||
|
}
|
||||||
if value, ok := _c.mutation.Status(); ok {
|
if value, ok := _c.mutation.Status(); ok {
|
||||||
_spec.SetField(account.FieldStatus, field.TypeString, value)
|
_spec.SetField(account.FieldStatus, field.TypeString, value)
|
||||||
_node.Status = value
|
_node.Status = value
|
||||||
@@ -893,6 +918,24 @@ func (u *AccountUpsert) AddPriority(v int) *AccountUpsert {
|
|||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRateMultiplier sets the "rate_multiplier" field.
|
||||||
|
func (u *AccountUpsert) SetRateMultiplier(v float64) *AccountUpsert {
|
||||||
|
u.Set(account.FieldRateMultiplier, v)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRateMultiplier sets the "rate_multiplier" field to the value that was provided on create.
|
||||||
|
func (u *AccountUpsert) UpdateRateMultiplier() *AccountUpsert {
|
||||||
|
u.SetExcluded(account.FieldRateMultiplier)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRateMultiplier adds v to the "rate_multiplier" field.
|
||||||
|
func (u *AccountUpsert) AddRateMultiplier(v float64) *AccountUpsert {
|
||||||
|
u.Add(account.FieldRateMultiplier, v)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
// SetStatus sets the "status" field.
|
// SetStatus sets the "status" field.
|
||||||
func (u *AccountUpsert) SetStatus(v string) *AccountUpsert {
|
func (u *AccountUpsert) SetStatus(v string) *AccountUpsert {
|
||||||
u.Set(account.FieldStatus, v)
|
u.Set(account.FieldStatus, v)
|
||||||
@@ -1325,6 +1368,27 @@ func (u *AccountUpsertOne) UpdatePriority() *AccountUpsertOne {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRateMultiplier sets the "rate_multiplier" field.
|
||||||
|
func (u *AccountUpsertOne) SetRateMultiplier(v float64) *AccountUpsertOne {
|
||||||
|
return u.Update(func(s *AccountUpsert) {
|
||||||
|
s.SetRateMultiplier(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRateMultiplier adds v to the "rate_multiplier" field.
|
||||||
|
func (u *AccountUpsertOne) AddRateMultiplier(v float64) *AccountUpsertOne {
|
||||||
|
return u.Update(func(s *AccountUpsert) {
|
||||||
|
s.AddRateMultiplier(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRateMultiplier sets the "rate_multiplier" field to the value that was provided on create.
|
||||||
|
func (u *AccountUpsertOne) UpdateRateMultiplier() *AccountUpsertOne {
|
||||||
|
return u.Update(func(s *AccountUpsert) {
|
||||||
|
s.UpdateRateMultiplier()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SetStatus sets the "status" field.
|
// SetStatus sets the "status" field.
|
||||||
func (u *AccountUpsertOne) SetStatus(v string) *AccountUpsertOne {
|
func (u *AccountUpsertOne) SetStatus(v string) *AccountUpsertOne {
|
||||||
return u.Update(func(s *AccountUpsert) {
|
return u.Update(func(s *AccountUpsert) {
|
||||||
@@ -1956,6 +2020,27 @@ func (u *AccountUpsertBulk) UpdatePriority() *AccountUpsertBulk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRateMultiplier sets the "rate_multiplier" field.
|
||||||
|
func (u *AccountUpsertBulk) SetRateMultiplier(v float64) *AccountUpsertBulk {
|
||||||
|
return u.Update(func(s *AccountUpsert) {
|
||||||
|
s.SetRateMultiplier(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRateMultiplier adds v to the "rate_multiplier" field.
|
||||||
|
func (u *AccountUpsertBulk) AddRateMultiplier(v float64) *AccountUpsertBulk {
|
||||||
|
return u.Update(func(s *AccountUpsert) {
|
||||||
|
s.AddRateMultiplier(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRateMultiplier sets the "rate_multiplier" field to the value that was provided on create.
|
||||||
|
func (u *AccountUpsertBulk) UpdateRateMultiplier() *AccountUpsertBulk {
|
||||||
|
return u.Update(func(s *AccountUpsert) {
|
||||||
|
s.UpdateRateMultiplier()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SetStatus sets the "status" field.
|
// SetStatus sets the "status" field.
|
||||||
func (u *AccountUpsertBulk) SetStatus(v string) *AccountUpsertBulk {
|
func (u *AccountUpsertBulk) SetStatus(v string) *AccountUpsertBulk {
|
||||||
return u.Update(func(s *AccountUpsert) {
|
return u.Update(func(s *AccountUpsert) {
|
||||||
|
|||||||
@@ -193,6 +193,27 @@ func (_u *AccountUpdate) AddPriority(v int) *AccountUpdate {
|
|||||||
return _u
|
return _u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRateMultiplier sets the "rate_multiplier" field.
|
||||||
|
func (_u *AccountUpdate) SetRateMultiplier(v float64) *AccountUpdate {
|
||||||
|
_u.mutation.ResetRateMultiplier()
|
||||||
|
_u.mutation.SetRateMultiplier(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableRateMultiplier sets the "rate_multiplier" field if the given value is not nil.
|
||||||
|
func (_u *AccountUpdate) SetNillableRateMultiplier(v *float64) *AccountUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetRateMultiplier(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRateMultiplier adds value to the "rate_multiplier" field.
|
||||||
|
func (_u *AccountUpdate) AddRateMultiplier(v float64) *AccountUpdate {
|
||||||
|
_u.mutation.AddRateMultiplier(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
// SetStatus sets the "status" field.
|
// SetStatus sets the "status" field.
|
||||||
func (_u *AccountUpdate) SetStatus(v string) *AccountUpdate {
|
func (_u *AccountUpdate) SetStatus(v string) *AccountUpdate {
|
||||||
_u.mutation.SetStatus(v)
|
_u.mutation.SetStatus(v)
|
||||||
@@ -629,6 +650,12 @@ func (_u *AccountUpdate) sqlSave(ctx context.Context) (_node int, err error) {
|
|||||||
if value, ok := _u.mutation.AddedPriority(); ok {
|
if value, ok := _u.mutation.AddedPriority(); ok {
|
||||||
_spec.AddField(account.FieldPriority, field.TypeInt, value)
|
_spec.AddField(account.FieldPriority, field.TypeInt, value)
|
||||||
}
|
}
|
||||||
|
if value, ok := _u.mutation.RateMultiplier(); ok {
|
||||||
|
_spec.SetField(account.FieldRateMultiplier, field.TypeFloat64, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.AddedRateMultiplier(); ok {
|
||||||
|
_spec.AddField(account.FieldRateMultiplier, field.TypeFloat64, value)
|
||||||
|
}
|
||||||
if value, ok := _u.mutation.Status(); ok {
|
if value, ok := _u.mutation.Status(); ok {
|
||||||
_spec.SetField(account.FieldStatus, field.TypeString, value)
|
_spec.SetField(account.FieldStatus, field.TypeString, value)
|
||||||
}
|
}
|
||||||
@@ -1005,6 +1032,27 @@ func (_u *AccountUpdateOne) AddPriority(v int) *AccountUpdateOne {
|
|||||||
return _u
|
return _u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRateMultiplier sets the "rate_multiplier" field.
|
||||||
|
func (_u *AccountUpdateOne) SetRateMultiplier(v float64) *AccountUpdateOne {
|
||||||
|
_u.mutation.ResetRateMultiplier()
|
||||||
|
_u.mutation.SetRateMultiplier(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableRateMultiplier sets the "rate_multiplier" field if the given value is not nil.
|
||||||
|
func (_u *AccountUpdateOne) SetNillableRateMultiplier(v *float64) *AccountUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetRateMultiplier(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRateMultiplier adds value to the "rate_multiplier" field.
|
||||||
|
func (_u *AccountUpdateOne) AddRateMultiplier(v float64) *AccountUpdateOne {
|
||||||
|
_u.mutation.AddRateMultiplier(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
// SetStatus sets the "status" field.
|
// SetStatus sets the "status" field.
|
||||||
func (_u *AccountUpdateOne) SetStatus(v string) *AccountUpdateOne {
|
func (_u *AccountUpdateOne) SetStatus(v string) *AccountUpdateOne {
|
||||||
_u.mutation.SetStatus(v)
|
_u.mutation.SetStatus(v)
|
||||||
@@ -1471,6 +1519,12 @@ func (_u *AccountUpdateOne) sqlSave(ctx context.Context) (_node *Account, err er
|
|||||||
if value, ok := _u.mutation.AddedPriority(); ok {
|
if value, ok := _u.mutation.AddedPriority(); ok {
|
||||||
_spec.AddField(account.FieldPriority, field.TypeInt, value)
|
_spec.AddField(account.FieldPriority, field.TypeInt, value)
|
||||||
}
|
}
|
||||||
|
if value, ok := _u.mutation.RateMultiplier(); ok {
|
||||||
|
_spec.SetField(account.FieldRateMultiplier, field.TypeFloat64, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.AddedRateMultiplier(); ok {
|
||||||
|
_spec.AddField(account.FieldRateMultiplier, field.TypeFloat64, value)
|
||||||
|
}
|
||||||
if value, ok := _u.mutation.Status(); ok {
|
if value, ok := _u.mutation.Status(); ok {
|
||||||
_spec.SetField(account.FieldStatus, field.TypeString, value)
|
_spec.SetField(account.FieldStatus, field.TypeString, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ var (
|
|||||||
{Name: "extra", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
{Name: "extra", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
|
||||||
{Name: "concurrency", Type: field.TypeInt, Default: 3},
|
{Name: "concurrency", Type: field.TypeInt, Default: 3},
|
||||||
{Name: "priority", Type: field.TypeInt, Default: 50},
|
{Name: "priority", Type: field.TypeInt, Default: 50},
|
||||||
|
{Name: "rate_multiplier", Type: field.TypeFloat64, Default: 1, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
|
||||||
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
|
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
|
||||||
{Name: "error_message", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
{Name: "error_message", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
||||||
{Name: "last_used_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
{Name: "last_used_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
||||||
@@ -101,7 +102,7 @@ var (
|
|||||||
ForeignKeys: []*schema.ForeignKey{
|
ForeignKeys: []*schema.ForeignKey{
|
||||||
{
|
{
|
||||||
Symbol: "accounts_proxies_proxy",
|
Symbol: "accounts_proxies_proxy",
|
||||||
Columns: []*schema.Column{AccountsColumns[24]},
|
Columns: []*schema.Column{AccountsColumns[25]},
|
||||||
RefColumns: []*schema.Column{ProxiesColumns[0]},
|
RefColumns: []*schema.Column{ProxiesColumns[0]},
|
||||||
OnDelete: schema.SetNull,
|
OnDelete: schema.SetNull,
|
||||||
},
|
},
|
||||||
@@ -120,12 +121,12 @@ var (
|
|||||||
{
|
{
|
||||||
Name: "account_status",
|
Name: "account_status",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{AccountsColumns[12]},
|
Columns: []*schema.Column{AccountsColumns[13]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "account_proxy_id",
|
Name: "account_proxy_id",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{AccountsColumns[24]},
|
Columns: []*schema.Column{AccountsColumns[25]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "account_priority",
|
Name: "account_priority",
|
||||||
@@ -135,27 +136,27 @@ var (
|
|||||||
{
|
{
|
||||||
Name: "account_last_used_at",
|
Name: "account_last_used_at",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{AccountsColumns[14]},
|
Columns: []*schema.Column{AccountsColumns[15]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "account_schedulable",
|
Name: "account_schedulable",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{AccountsColumns[17]},
|
Columns: []*schema.Column{AccountsColumns[18]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "account_rate_limited_at",
|
Name: "account_rate_limited_at",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{AccountsColumns[18]},
|
Columns: []*schema.Column{AccountsColumns[19]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "account_rate_limit_reset_at",
|
Name: "account_rate_limit_reset_at",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{AccountsColumns[19]},
|
Columns: []*schema.Column{AccountsColumns[20]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "account_overload_until",
|
Name: "account_overload_until",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{AccountsColumns[20]},
|
Columns: []*schema.Column{AccountsColumns[21]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "account_deleted_at",
|
Name: "account_deleted_at",
|
||||||
@@ -449,6 +450,7 @@ var (
|
|||||||
{Name: "total_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
{Name: "total_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
||||||
{Name: "actual_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
{Name: "actual_cost", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,10)"}},
|
||||||
{Name: "rate_multiplier", Type: field.TypeFloat64, Default: 1, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
|
{Name: "rate_multiplier", Type: field.TypeFloat64, Default: 1, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
|
||||||
|
{Name: "account_rate_multiplier", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(10,4)"}},
|
||||||
{Name: "billing_type", Type: field.TypeInt8, Default: 0},
|
{Name: "billing_type", Type: field.TypeInt8, Default: 0},
|
||||||
{Name: "stream", Type: field.TypeBool, Default: false},
|
{Name: "stream", Type: field.TypeBool, Default: false},
|
||||||
{Name: "duration_ms", Type: field.TypeInt, Nullable: true},
|
{Name: "duration_ms", Type: field.TypeInt, Nullable: true},
|
||||||
@@ -472,31 +474,31 @@ var (
|
|||||||
ForeignKeys: []*schema.ForeignKey{
|
ForeignKeys: []*schema.ForeignKey{
|
||||||
{
|
{
|
||||||
Symbol: "usage_logs_api_keys_usage_logs",
|
Symbol: "usage_logs_api_keys_usage_logs",
|
||||||
Columns: []*schema.Column{UsageLogsColumns[25]},
|
Columns: []*schema.Column{UsageLogsColumns[26]},
|
||||||
RefColumns: []*schema.Column{APIKeysColumns[0]},
|
RefColumns: []*schema.Column{APIKeysColumns[0]},
|
||||||
OnDelete: schema.NoAction,
|
OnDelete: schema.NoAction,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Symbol: "usage_logs_accounts_usage_logs",
|
Symbol: "usage_logs_accounts_usage_logs",
|
||||||
Columns: []*schema.Column{UsageLogsColumns[26]},
|
Columns: []*schema.Column{UsageLogsColumns[27]},
|
||||||
RefColumns: []*schema.Column{AccountsColumns[0]},
|
RefColumns: []*schema.Column{AccountsColumns[0]},
|
||||||
OnDelete: schema.NoAction,
|
OnDelete: schema.NoAction,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Symbol: "usage_logs_groups_usage_logs",
|
Symbol: "usage_logs_groups_usage_logs",
|
||||||
Columns: []*schema.Column{UsageLogsColumns[27]},
|
Columns: []*schema.Column{UsageLogsColumns[28]},
|
||||||
RefColumns: []*schema.Column{GroupsColumns[0]},
|
RefColumns: []*schema.Column{GroupsColumns[0]},
|
||||||
OnDelete: schema.SetNull,
|
OnDelete: schema.SetNull,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Symbol: "usage_logs_users_usage_logs",
|
Symbol: "usage_logs_users_usage_logs",
|
||||||
Columns: []*schema.Column{UsageLogsColumns[28]},
|
Columns: []*schema.Column{UsageLogsColumns[29]},
|
||||||
RefColumns: []*schema.Column{UsersColumns[0]},
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
||||||
OnDelete: schema.NoAction,
|
OnDelete: schema.NoAction,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Symbol: "usage_logs_user_subscriptions_usage_logs",
|
Symbol: "usage_logs_user_subscriptions_usage_logs",
|
||||||
Columns: []*schema.Column{UsageLogsColumns[29]},
|
Columns: []*schema.Column{UsageLogsColumns[30]},
|
||||||
RefColumns: []*schema.Column{UserSubscriptionsColumns[0]},
|
RefColumns: []*schema.Column{UserSubscriptionsColumns[0]},
|
||||||
OnDelete: schema.SetNull,
|
OnDelete: schema.SetNull,
|
||||||
},
|
},
|
||||||
@@ -505,32 +507,32 @@ var (
|
|||||||
{
|
{
|
||||||
Name: "usagelog_user_id",
|
Name: "usagelog_user_id",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{UsageLogsColumns[28]},
|
Columns: []*schema.Column{UsageLogsColumns[29]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "usagelog_api_key_id",
|
Name: "usagelog_api_key_id",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{UsageLogsColumns[25]},
|
Columns: []*schema.Column{UsageLogsColumns[26]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "usagelog_account_id",
|
Name: "usagelog_account_id",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{UsageLogsColumns[26]},
|
Columns: []*schema.Column{UsageLogsColumns[27]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "usagelog_group_id",
|
Name: "usagelog_group_id",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{UsageLogsColumns[27]},
|
Columns: []*schema.Column{UsageLogsColumns[28]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "usagelog_subscription_id",
|
Name: "usagelog_subscription_id",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{UsageLogsColumns[29]},
|
Columns: []*schema.Column{UsageLogsColumns[30]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "usagelog_created_at",
|
Name: "usagelog_created_at",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{UsageLogsColumns[24]},
|
Columns: []*schema.Column{UsageLogsColumns[25]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "usagelog_model",
|
Name: "usagelog_model",
|
||||||
@@ -545,12 +547,12 @@ var (
|
|||||||
{
|
{
|
||||||
Name: "usagelog_user_id_created_at",
|
Name: "usagelog_user_id_created_at",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{UsageLogsColumns[28], UsageLogsColumns[24]},
|
Columns: []*schema.Column{UsageLogsColumns[29], UsageLogsColumns[25]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "usagelog_api_key_id_created_at",
|
Name: "usagelog_api_key_id_created_at",
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Columns: []*schema.Column{UsageLogsColumns[25], UsageLogsColumns[24]},
|
Columns: []*schema.Column{UsageLogsColumns[26], UsageLogsColumns[25]},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1187,6 +1187,8 @@ type AccountMutation struct {
|
|||||||
addconcurrency *int
|
addconcurrency *int
|
||||||
priority *int
|
priority *int
|
||||||
addpriority *int
|
addpriority *int
|
||||||
|
rate_multiplier *float64
|
||||||
|
addrate_multiplier *float64
|
||||||
status *string
|
status *string
|
||||||
error_message *string
|
error_message *string
|
||||||
last_used_at *time.Time
|
last_used_at *time.Time
|
||||||
@@ -1822,6 +1824,62 @@ func (m *AccountMutation) ResetPriority() {
|
|||||||
m.addpriority = nil
|
m.addpriority = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetRateMultiplier sets the "rate_multiplier" field.
|
||||||
|
func (m *AccountMutation) SetRateMultiplier(f float64) {
|
||||||
|
m.rate_multiplier = &f
|
||||||
|
m.addrate_multiplier = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateMultiplier returns the value of the "rate_multiplier" field in the mutation.
|
||||||
|
func (m *AccountMutation) RateMultiplier() (r float64, exists bool) {
|
||||||
|
v := m.rate_multiplier
|
||||||
|
if v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return *v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// OldRateMultiplier returns the old "rate_multiplier" field's value of the Account entity.
|
||||||
|
// If the Account 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 *AccountMutation) OldRateMultiplier(ctx context.Context) (v float64, err error) {
|
||||||
|
if !m.op.Is(OpUpdateOne) {
|
||||||
|
return v, errors.New("OldRateMultiplier is only allowed on UpdateOne operations")
|
||||||
|
}
|
||||||
|
if m.id == nil || m.oldValue == nil {
|
||||||
|
return v, errors.New("OldRateMultiplier requires an ID field in the mutation")
|
||||||
|
}
|
||||||
|
oldValue, err := m.oldValue(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return v, fmt.Errorf("querying old value for OldRateMultiplier: %w", err)
|
||||||
|
}
|
||||||
|
return oldValue.RateMultiplier, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRateMultiplier adds f to the "rate_multiplier" field.
|
||||||
|
func (m *AccountMutation) AddRateMultiplier(f float64) {
|
||||||
|
if m.addrate_multiplier != nil {
|
||||||
|
*m.addrate_multiplier += f
|
||||||
|
} else {
|
||||||
|
m.addrate_multiplier = &f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddedRateMultiplier returns the value that was added to the "rate_multiplier" field in this mutation.
|
||||||
|
func (m *AccountMutation) AddedRateMultiplier() (r float64, exists bool) {
|
||||||
|
v := m.addrate_multiplier
|
||||||
|
if v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return *v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetRateMultiplier resets all changes to the "rate_multiplier" field.
|
||||||
|
func (m *AccountMutation) ResetRateMultiplier() {
|
||||||
|
m.rate_multiplier = nil
|
||||||
|
m.addrate_multiplier = nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetStatus sets the "status" field.
|
// SetStatus sets the "status" field.
|
||||||
func (m *AccountMutation) SetStatus(s string) {
|
func (m *AccountMutation) SetStatus(s string) {
|
||||||
m.status = &s
|
m.status = &s
|
||||||
@@ -2540,7 +2598,7 @@ func (m *AccountMutation) Type() string {
|
|||||||
// order to get all numeric fields that were incremented/decremented, call
|
// order to get all numeric fields that were incremented/decremented, call
|
||||||
// AddedFields().
|
// AddedFields().
|
||||||
func (m *AccountMutation) Fields() []string {
|
func (m *AccountMutation) Fields() []string {
|
||||||
fields := make([]string, 0, 24)
|
fields := make([]string, 0, 25)
|
||||||
if m.created_at != nil {
|
if m.created_at != nil {
|
||||||
fields = append(fields, account.FieldCreatedAt)
|
fields = append(fields, account.FieldCreatedAt)
|
||||||
}
|
}
|
||||||
@@ -2577,6 +2635,9 @@ func (m *AccountMutation) Fields() []string {
|
|||||||
if m.priority != nil {
|
if m.priority != nil {
|
||||||
fields = append(fields, account.FieldPriority)
|
fields = append(fields, account.FieldPriority)
|
||||||
}
|
}
|
||||||
|
if m.rate_multiplier != nil {
|
||||||
|
fields = append(fields, account.FieldRateMultiplier)
|
||||||
|
}
|
||||||
if m.status != nil {
|
if m.status != nil {
|
||||||
fields = append(fields, account.FieldStatus)
|
fields = append(fields, account.FieldStatus)
|
||||||
}
|
}
|
||||||
@@ -2645,6 +2706,8 @@ func (m *AccountMutation) Field(name string) (ent.Value, bool) {
|
|||||||
return m.Concurrency()
|
return m.Concurrency()
|
||||||
case account.FieldPriority:
|
case account.FieldPriority:
|
||||||
return m.Priority()
|
return m.Priority()
|
||||||
|
case account.FieldRateMultiplier:
|
||||||
|
return m.RateMultiplier()
|
||||||
case account.FieldStatus:
|
case account.FieldStatus:
|
||||||
return m.Status()
|
return m.Status()
|
||||||
case account.FieldErrorMessage:
|
case account.FieldErrorMessage:
|
||||||
@@ -2702,6 +2765,8 @@ func (m *AccountMutation) OldField(ctx context.Context, name string) (ent.Value,
|
|||||||
return m.OldConcurrency(ctx)
|
return m.OldConcurrency(ctx)
|
||||||
case account.FieldPriority:
|
case account.FieldPriority:
|
||||||
return m.OldPriority(ctx)
|
return m.OldPriority(ctx)
|
||||||
|
case account.FieldRateMultiplier:
|
||||||
|
return m.OldRateMultiplier(ctx)
|
||||||
case account.FieldStatus:
|
case account.FieldStatus:
|
||||||
return m.OldStatus(ctx)
|
return m.OldStatus(ctx)
|
||||||
case account.FieldErrorMessage:
|
case account.FieldErrorMessage:
|
||||||
@@ -2819,6 +2884,13 @@ func (m *AccountMutation) SetField(name string, value ent.Value) error {
|
|||||||
}
|
}
|
||||||
m.SetPriority(v)
|
m.SetPriority(v)
|
||||||
return nil
|
return nil
|
||||||
|
case account.FieldRateMultiplier:
|
||||||
|
v, ok := value.(float64)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||||
|
}
|
||||||
|
m.SetRateMultiplier(v)
|
||||||
|
return nil
|
||||||
case account.FieldStatus:
|
case account.FieldStatus:
|
||||||
v, ok := value.(string)
|
v, ok := value.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -2917,6 +2989,9 @@ func (m *AccountMutation) AddedFields() []string {
|
|||||||
if m.addpriority != nil {
|
if m.addpriority != nil {
|
||||||
fields = append(fields, account.FieldPriority)
|
fields = append(fields, account.FieldPriority)
|
||||||
}
|
}
|
||||||
|
if m.addrate_multiplier != nil {
|
||||||
|
fields = append(fields, account.FieldRateMultiplier)
|
||||||
|
}
|
||||||
return fields
|
return fields
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2929,6 +3004,8 @@ func (m *AccountMutation) AddedField(name string) (ent.Value, bool) {
|
|||||||
return m.AddedConcurrency()
|
return m.AddedConcurrency()
|
||||||
case account.FieldPriority:
|
case account.FieldPriority:
|
||||||
return m.AddedPriority()
|
return m.AddedPriority()
|
||||||
|
case account.FieldRateMultiplier:
|
||||||
|
return m.AddedRateMultiplier()
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
@@ -2952,6 +3029,13 @@ func (m *AccountMutation) AddField(name string, value ent.Value) error {
|
|||||||
}
|
}
|
||||||
m.AddPriority(v)
|
m.AddPriority(v)
|
||||||
return nil
|
return nil
|
||||||
|
case account.FieldRateMultiplier:
|
||||||
|
v, ok := value.(float64)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||||
|
}
|
||||||
|
m.AddRateMultiplier(v)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("unknown Account numeric field %s", name)
|
return fmt.Errorf("unknown Account numeric field %s", name)
|
||||||
}
|
}
|
||||||
@@ -3090,6 +3174,9 @@ func (m *AccountMutation) ResetField(name string) error {
|
|||||||
case account.FieldPriority:
|
case account.FieldPriority:
|
||||||
m.ResetPriority()
|
m.ResetPriority()
|
||||||
return nil
|
return nil
|
||||||
|
case account.FieldRateMultiplier:
|
||||||
|
m.ResetRateMultiplier()
|
||||||
|
return nil
|
||||||
case account.FieldStatus:
|
case account.FieldStatus:
|
||||||
m.ResetStatus()
|
m.ResetStatus()
|
||||||
return nil
|
return nil
|
||||||
@@ -10190,6 +10277,8 @@ type UsageLogMutation struct {
|
|||||||
addactual_cost *float64
|
addactual_cost *float64
|
||||||
rate_multiplier *float64
|
rate_multiplier *float64
|
||||||
addrate_multiplier *float64
|
addrate_multiplier *float64
|
||||||
|
account_rate_multiplier *float64
|
||||||
|
addaccount_rate_multiplier *float64
|
||||||
billing_type *int8
|
billing_type *int8
|
||||||
addbilling_type *int8
|
addbilling_type *int8
|
||||||
stream *bool
|
stream *bool
|
||||||
@@ -11323,6 +11412,76 @@ func (m *UsageLogMutation) ResetRateMultiplier() {
|
|||||||
m.addrate_multiplier = nil
|
m.addrate_multiplier = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAccountRateMultiplier sets the "account_rate_multiplier" field.
|
||||||
|
func (m *UsageLogMutation) SetAccountRateMultiplier(f float64) {
|
||||||
|
m.account_rate_multiplier = &f
|
||||||
|
m.addaccount_rate_multiplier = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplier returns the value of the "account_rate_multiplier" field in the mutation.
|
||||||
|
func (m *UsageLogMutation) AccountRateMultiplier() (r float64, exists bool) {
|
||||||
|
v := m.account_rate_multiplier
|
||||||
|
if v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return *v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// OldAccountRateMultiplier returns the old "account_rate_multiplier" 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) OldAccountRateMultiplier(ctx context.Context) (v *float64, err error) {
|
||||||
|
if !m.op.Is(OpUpdateOne) {
|
||||||
|
return v, errors.New("OldAccountRateMultiplier is only allowed on UpdateOne operations")
|
||||||
|
}
|
||||||
|
if m.id == nil || m.oldValue == nil {
|
||||||
|
return v, errors.New("OldAccountRateMultiplier requires an ID field in the mutation")
|
||||||
|
}
|
||||||
|
oldValue, err := m.oldValue(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return v, fmt.Errorf("querying old value for OldAccountRateMultiplier: %w", err)
|
||||||
|
}
|
||||||
|
return oldValue.AccountRateMultiplier, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccountRateMultiplier adds f to the "account_rate_multiplier" field.
|
||||||
|
func (m *UsageLogMutation) AddAccountRateMultiplier(f float64) {
|
||||||
|
if m.addaccount_rate_multiplier != nil {
|
||||||
|
*m.addaccount_rate_multiplier += f
|
||||||
|
} else {
|
||||||
|
m.addaccount_rate_multiplier = &f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddedAccountRateMultiplier returns the value that was added to the "account_rate_multiplier" field in this mutation.
|
||||||
|
func (m *UsageLogMutation) AddedAccountRateMultiplier() (r float64, exists bool) {
|
||||||
|
v := m.addaccount_rate_multiplier
|
||||||
|
if v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return *v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAccountRateMultiplier clears the value of the "account_rate_multiplier" field.
|
||||||
|
func (m *UsageLogMutation) ClearAccountRateMultiplier() {
|
||||||
|
m.account_rate_multiplier = nil
|
||||||
|
m.addaccount_rate_multiplier = nil
|
||||||
|
m.clearedFields[usagelog.FieldAccountRateMultiplier] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierCleared returns if the "account_rate_multiplier" field was cleared in this mutation.
|
||||||
|
func (m *UsageLogMutation) AccountRateMultiplierCleared() bool {
|
||||||
|
_, ok := m.clearedFields[usagelog.FieldAccountRateMultiplier]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetAccountRateMultiplier resets all changes to the "account_rate_multiplier" field.
|
||||||
|
func (m *UsageLogMutation) ResetAccountRateMultiplier() {
|
||||||
|
m.account_rate_multiplier = nil
|
||||||
|
m.addaccount_rate_multiplier = nil
|
||||||
|
delete(m.clearedFields, usagelog.FieldAccountRateMultiplier)
|
||||||
|
}
|
||||||
|
|
||||||
// SetBillingType sets the "billing_type" field.
|
// SetBillingType sets the "billing_type" field.
|
||||||
func (m *UsageLogMutation) SetBillingType(i int8) {
|
func (m *UsageLogMutation) SetBillingType(i int8) {
|
||||||
m.billing_type = &i
|
m.billing_type = &i
|
||||||
@@ -11963,7 +12122,7 @@ func (m *UsageLogMutation) Type() string {
|
|||||||
// order to get all numeric fields that were incremented/decremented, call
|
// order to get all numeric fields that were incremented/decremented, call
|
||||||
// AddedFields().
|
// AddedFields().
|
||||||
func (m *UsageLogMutation) Fields() []string {
|
func (m *UsageLogMutation) Fields() []string {
|
||||||
fields := make([]string, 0, 29)
|
fields := make([]string, 0, 30)
|
||||||
if m.user != nil {
|
if m.user != nil {
|
||||||
fields = append(fields, usagelog.FieldUserID)
|
fields = append(fields, usagelog.FieldUserID)
|
||||||
}
|
}
|
||||||
@@ -12024,6 +12183,9 @@ func (m *UsageLogMutation) Fields() []string {
|
|||||||
if m.rate_multiplier != nil {
|
if m.rate_multiplier != nil {
|
||||||
fields = append(fields, usagelog.FieldRateMultiplier)
|
fields = append(fields, usagelog.FieldRateMultiplier)
|
||||||
}
|
}
|
||||||
|
if m.account_rate_multiplier != nil {
|
||||||
|
fields = append(fields, usagelog.FieldAccountRateMultiplier)
|
||||||
|
}
|
||||||
if m.billing_type != nil {
|
if m.billing_type != nil {
|
||||||
fields = append(fields, usagelog.FieldBillingType)
|
fields = append(fields, usagelog.FieldBillingType)
|
||||||
}
|
}
|
||||||
@@ -12099,6 +12261,8 @@ func (m *UsageLogMutation) Field(name string) (ent.Value, bool) {
|
|||||||
return m.ActualCost()
|
return m.ActualCost()
|
||||||
case usagelog.FieldRateMultiplier:
|
case usagelog.FieldRateMultiplier:
|
||||||
return m.RateMultiplier()
|
return m.RateMultiplier()
|
||||||
|
case usagelog.FieldAccountRateMultiplier:
|
||||||
|
return m.AccountRateMultiplier()
|
||||||
case usagelog.FieldBillingType:
|
case usagelog.FieldBillingType:
|
||||||
return m.BillingType()
|
return m.BillingType()
|
||||||
case usagelog.FieldStream:
|
case usagelog.FieldStream:
|
||||||
@@ -12166,6 +12330,8 @@ func (m *UsageLogMutation) OldField(ctx context.Context, name string) (ent.Value
|
|||||||
return m.OldActualCost(ctx)
|
return m.OldActualCost(ctx)
|
||||||
case usagelog.FieldRateMultiplier:
|
case usagelog.FieldRateMultiplier:
|
||||||
return m.OldRateMultiplier(ctx)
|
return m.OldRateMultiplier(ctx)
|
||||||
|
case usagelog.FieldAccountRateMultiplier:
|
||||||
|
return m.OldAccountRateMultiplier(ctx)
|
||||||
case usagelog.FieldBillingType:
|
case usagelog.FieldBillingType:
|
||||||
return m.OldBillingType(ctx)
|
return m.OldBillingType(ctx)
|
||||||
case usagelog.FieldStream:
|
case usagelog.FieldStream:
|
||||||
@@ -12333,6 +12499,13 @@ func (m *UsageLogMutation) SetField(name string, value ent.Value) error {
|
|||||||
}
|
}
|
||||||
m.SetRateMultiplier(v)
|
m.SetRateMultiplier(v)
|
||||||
return nil
|
return nil
|
||||||
|
case usagelog.FieldAccountRateMultiplier:
|
||||||
|
v, ok := value.(float64)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||||
|
}
|
||||||
|
m.SetAccountRateMultiplier(v)
|
||||||
|
return nil
|
||||||
case usagelog.FieldBillingType:
|
case usagelog.FieldBillingType:
|
||||||
v, ok := value.(int8)
|
v, ok := value.(int8)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -12443,6 +12616,9 @@ func (m *UsageLogMutation) AddedFields() []string {
|
|||||||
if m.addrate_multiplier != nil {
|
if m.addrate_multiplier != nil {
|
||||||
fields = append(fields, usagelog.FieldRateMultiplier)
|
fields = append(fields, usagelog.FieldRateMultiplier)
|
||||||
}
|
}
|
||||||
|
if m.addaccount_rate_multiplier != nil {
|
||||||
|
fields = append(fields, usagelog.FieldAccountRateMultiplier)
|
||||||
|
}
|
||||||
if m.addbilling_type != nil {
|
if m.addbilling_type != nil {
|
||||||
fields = append(fields, usagelog.FieldBillingType)
|
fields = append(fields, usagelog.FieldBillingType)
|
||||||
}
|
}
|
||||||
@@ -12489,6 +12665,8 @@ func (m *UsageLogMutation) AddedField(name string) (ent.Value, bool) {
|
|||||||
return m.AddedActualCost()
|
return m.AddedActualCost()
|
||||||
case usagelog.FieldRateMultiplier:
|
case usagelog.FieldRateMultiplier:
|
||||||
return m.AddedRateMultiplier()
|
return m.AddedRateMultiplier()
|
||||||
|
case usagelog.FieldAccountRateMultiplier:
|
||||||
|
return m.AddedAccountRateMultiplier()
|
||||||
case usagelog.FieldBillingType:
|
case usagelog.FieldBillingType:
|
||||||
return m.AddedBillingType()
|
return m.AddedBillingType()
|
||||||
case usagelog.FieldDurationMs:
|
case usagelog.FieldDurationMs:
|
||||||
@@ -12597,6 +12775,13 @@ func (m *UsageLogMutation) AddField(name string, value ent.Value) error {
|
|||||||
}
|
}
|
||||||
m.AddRateMultiplier(v)
|
m.AddRateMultiplier(v)
|
||||||
return nil
|
return nil
|
||||||
|
case usagelog.FieldAccountRateMultiplier:
|
||||||
|
v, ok := value.(float64)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||||
|
}
|
||||||
|
m.AddAccountRateMultiplier(v)
|
||||||
|
return nil
|
||||||
case usagelog.FieldBillingType:
|
case usagelog.FieldBillingType:
|
||||||
v, ok := value.(int8)
|
v, ok := value.(int8)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -12639,6 +12824,9 @@ func (m *UsageLogMutation) ClearedFields() []string {
|
|||||||
if m.FieldCleared(usagelog.FieldSubscriptionID) {
|
if m.FieldCleared(usagelog.FieldSubscriptionID) {
|
||||||
fields = append(fields, usagelog.FieldSubscriptionID)
|
fields = append(fields, usagelog.FieldSubscriptionID)
|
||||||
}
|
}
|
||||||
|
if m.FieldCleared(usagelog.FieldAccountRateMultiplier) {
|
||||||
|
fields = append(fields, usagelog.FieldAccountRateMultiplier)
|
||||||
|
}
|
||||||
if m.FieldCleared(usagelog.FieldDurationMs) {
|
if m.FieldCleared(usagelog.FieldDurationMs) {
|
||||||
fields = append(fields, usagelog.FieldDurationMs)
|
fields = append(fields, usagelog.FieldDurationMs)
|
||||||
}
|
}
|
||||||
@@ -12674,6 +12862,9 @@ func (m *UsageLogMutation) ClearField(name string) error {
|
|||||||
case usagelog.FieldSubscriptionID:
|
case usagelog.FieldSubscriptionID:
|
||||||
m.ClearSubscriptionID()
|
m.ClearSubscriptionID()
|
||||||
return nil
|
return nil
|
||||||
|
case usagelog.FieldAccountRateMultiplier:
|
||||||
|
m.ClearAccountRateMultiplier()
|
||||||
|
return nil
|
||||||
case usagelog.FieldDurationMs:
|
case usagelog.FieldDurationMs:
|
||||||
m.ClearDurationMs()
|
m.ClearDurationMs()
|
||||||
return nil
|
return nil
|
||||||
@@ -12757,6 +12948,9 @@ func (m *UsageLogMutation) ResetField(name string) error {
|
|||||||
case usagelog.FieldRateMultiplier:
|
case usagelog.FieldRateMultiplier:
|
||||||
m.ResetRateMultiplier()
|
m.ResetRateMultiplier()
|
||||||
return nil
|
return nil
|
||||||
|
case usagelog.FieldAccountRateMultiplier:
|
||||||
|
m.ResetAccountRateMultiplier()
|
||||||
|
return nil
|
||||||
case usagelog.FieldBillingType:
|
case usagelog.FieldBillingType:
|
||||||
m.ResetBillingType()
|
m.ResetBillingType()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -177,22 +177,26 @@ func init() {
|
|||||||
accountDescPriority := accountFields[8].Descriptor()
|
accountDescPriority := accountFields[8].Descriptor()
|
||||||
// account.DefaultPriority holds the default value on creation for the priority field.
|
// account.DefaultPriority holds the default value on creation for the priority field.
|
||||||
account.DefaultPriority = accountDescPriority.Default.(int)
|
account.DefaultPriority = accountDescPriority.Default.(int)
|
||||||
|
// accountDescRateMultiplier is the schema descriptor for rate_multiplier field.
|
||||||
|
accountDescRateMultiplier := accountFields[9].Descriptor()
|
||||||
|
// account.DefaultRateMultiplier holds the default value on creation for the rate_multiplier field.
|
||||||
|
account.DefaultRateMultiplier = accountDescRateMultiplier.Default.(float64)
|
||||||
// accountDescStatus is the schema descriptor for status field.
|
// accountDescStatus is the schema descriptor for status field.
|
||||||
accountDescStatus := accountFields[9].Descriptor()
|
accountDescStatus := accountFields[10].Descriptor()
|
||||||
// account.DefaultStatus holds the default value on creation for the status field.
|
// account.DefaultStatus holds the default value on creation for the status field.
|
||||||
account.DefaultStatus = accountDescStatus.Default.(string)
|
account.DefaultStatus = accountDescStatus.Default.(string)
|
||||||
// account.StatusValidator is a validator for the "status" field. It is called by the builders before save.
|
// account.StatusValidator is a validator for the "status" field. It is called by the builders before save.
|
||||||
account.StatusValidator = accountDescStatus.Validators[0].(func(string) error)
|
account.StatusValidator = accountDescStatus.Validators[0].(func(string) error)
|
||||||
// accountDescAutoPauseOnExpired is the schema descriptor for auto_pause_on_expired field.
|
// accountDescAutoPauseOnExpired is the schema descriptor for auto_pause_on_expired field.
|
||||||
accountDescAutoPauseOnExpired := accountFields[13].Descriptor()
|
accountDescAutoPauseOnExpired := accountFields[14].Descriptor()
|
||||||
// account.DefaultAutoPauseOnExpired holds the default value on creation for the auto_pause_on_expired field.
|
// account.DefaultAutoPauseOnExpired holds the default value on creation for the auto_pause_on_expired field.
|
||||||
account.DefaultAutoPauseOnExpired = accountDescAutoPauseOnExpired.Default.(bool)
|
account.DefaultAutoPauseOnExpired = accountDescAutoPauseOnExpired.Default.(bool)
|
||||||
// accountDescSchedulable is the schema descriptor for schedulable field.
|
// accountDescSchedulable is the schema descriptor for schedulable field.
|
||||||
accountDescSchedulable := accountFields[14].Descriptor()
|
accountDescSchedulable := accountFields[15].Descriptor()
|
||||||
// account.DefaultSchedulable holds the default value on creation for the schedulable field.
|
// account.DefaultSchedulable holds the default value on creation for the schedulable field.
|
||||||
account.DefaultSchedulable = accountDescSchedulable.Default.(bool)
|
account.DefaultSchedulable = accountDescSchedulable.Default.(bool)
|
||||||
// accountDescSessionWindowStatus is the schema descriptor for session_window_status field.
|
// accountDescSessionWindowStatus is the schema descriptor for session_window_status field.
|
||||||
accountDescSessionWindowStatus := accountFields[20].Descriptor()
|
accountDescSessionWindowStatus := accountFields[21].Descriptor()
|
||||||
// account.SessionWindowStatusValidator is a validator for the "session_window_status" field. It is called by the builders before save.
|
// account.SessionWindowStatusValidator is a validator for the "session_window_status" field. It is called by the builders before save.
|
||||||
account.SessionWindowStatusValidator = accountDescSessionWindowStatus.Validators[0].(func(string) error)
|
account.SessionWindowStatusValidator = accountDescSessionWindowStatus.Validators[0].(func(string) error)
|
||||||
accountgroupFields := schema.AccountGroup{}.Fields()
|
accountgroupFields := schema.AccountGroup{}.Fields()
|
||||||
@@ -578,31 +582,31 @@ func init() {
|
|||||||
// usagelog.DefaultRateMultiplier holds the default value on creation for the rate_multiplier field.
|
// usagelog.DefaultRateMultiplier holds the default value on creation for the rate_multiplier field.
|
||||||
usagelog.DefaultRateMultiplier = usagelogDescRateMultiplier.Default.(float64)
|
usagelog.DefaultRateMultiplier = usagelogDescRateMultiplier.Default.(float64)
|
||||||
// usagelogDescBillingType is the schema descriptor for billing_type field.
|
// usagelogDescBillingType is the schema descriptor for billing_type field.
|
||||||
usagelogDescBillingType := usagelogFields[20].Descriptor()
|
usagelogDescBillingType := usagelogFields[21].Descriptor()
|
||||||
// usagelog.DefaultBillingType holds the default value on creation for the billing_type field.
|
// usagelog.DefaultBillingType holds the default value on creation for the billing_type field.
|
||||||
usagelog.DefaultBillingType = usagelogDescBillingType.Default.(int8)
|
usagelog.DefaultBillingType = usagelogDescBillingType.Default.(int8)
|
||||||
// usagelogDescStream is the schema descriptor for stream field.
|
// usagelogDescStream is the schema descriptor for stream field.
|
||||||
usagelogDescStream := usagelogFields[21].Descriptor()
|
usagelogDescStream := usagelogFields[22].Descriptor()
|
||||||
// usagelog.DefaultStream holds the default value on creation for the stream field.
|
// usagelog.DefaultStream holds the default value on creation for the stream field.
|
||||||
usagelog.DefaultStream = usagelogDescStream.Default.(bool)
|
usagelog.DefaultStream = usagelogDescStream.Default.(bool)
|
||||||
// usagelogDescUserAgent is the schema descriptor for user_agent field.
|
// usagelogDescUserAgent is the schema descriptor for user_agent field.
|
||||||
usagelogDescUserAgent := usagelogFields[24].Descriptor()
|
usagelogDescUserAgent := usagelogFields[25].Descriptor()
|
||||||
// usagelog.UserAgentValidator is a validator for the "user_agent" field. It is called by the builders before save.
|
// usagelog.UserAgentValidator is a validator for the "user_agent" field. It is called by the builders before save.
|
||||||
usagelog.UserAgentValidator = usagelogDescUserAgent.Validators[0].(func(string) error)
|
usagelog.UserAgentValidator = usagelogDescUserAgent.Validators[0].(func(string) error)
|
||||||
// usagelogDescIPAddress is the schema descriptor for ip_address field.
|
// usagelogDescIPAddress is the schema descriptor for ip_address field.
|
||||||
usagelogDescIPAddress := usagelogFields[25].Descriptor()
|
usagelogDescIPAddress := usagelogFields[26].Descriptor()
|
||||||
// usagelog.IPAddressValidator is a validator for the "ip_address" field. It is called by the builders before save.
|
// usagelog.IPAddressValidator is a validator for the "ip_address" field. It is called by the builders before save.
|
||||||
usagelog.IPAddressValidator = usagelogDescIPAddress.Validators[0].(func(string) error)
|
usagelog.IPAddressValidator = usagelogDescIPAddress.Validators[0].(func(string) error)
|
||||||
// usagelogDescImageCount is the schema descriptor for image_count field.
|
// usagelogDescImageCount is the schema descriptor for image_count field.
|
||||||
usagelogDescImageCount := usagelogFields[26].Descriptor()
|
usagelogDescImageCount := usagelogFields[27].Descriptor()
|
||||||
// usagelog.DefaultImageCount holds the default value on creation for the image_count field.
|
// usagelog.DefaultImageCount holds the default value on creation for the image_count field.
|
||||||
usagelog.DefaultImageCount = usagelogDescImageCount.Default.(int)
|
usagelog.DefaultImageCount = usagelogDescImageCount.Default.(int)
|
||||||
// usagelogDescImageSize is the schema descriptor for image_size field.
|
// usagelogDescImageSize is the schema descriptor for image_size field.
|
||||||
usagelogDescImageSize := usagelogFields[27].Descriptor()
|
usagelogDescImageSize := usagelogFields[28].Descriptor()
|
||||||
// usagelog.ImageSizeValidator is a validator for the "image_size" field. It is called by the builders before save.
|
// 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)
|
usagelog.ImageSizeValidator = usagelogDescImageSize.Validators[0].(func(string) error)
|
||||||
// usagelogDescCreatedAt is the schema descriptor for created_at field.
|
// usagelogDescCreatedAt is the schema descriptor for created_at field.
|
||||||
usagelogDescCreatedAt := usagelogFields[28].Descriptor()
|
usagelogDescCreatedAt := usagelogFields[29].Descriptor()
|
||||||
// usagelog.DefaultCreatedAt holds the default value on creation for the created_at field.
|
// usagelog.DefaultCreatedAt holds the default value on creation for the created_at field.
|
||||||
usagelog.DefaultCreatedAt = usagelogDescCreatedAt.Default.(func() time.Time)
|
usagelog.DefaultCreatedAt = usagelogDescCreatedAt.Default.(func() time.Time)
|
||||||
userMixin := schema.User{}.Mixin()
|
userMixin := schema.User{}.Mixin()
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ func (Account) Fields() []ent.Field {
|
|||||||
field.Int("priority").
|
field.Int("priority").
|
||||||
Default(50),
|
Default(50),
|
||||||
|
|
||||||
|
// rate_multiplier: 账号计费倍率(>=0,允许 0 表示该账号计费为 0)
|
||||||
|
// 仅影响账号维度计费口径,不影响用户/API Key 扣费(分组倍率)
|
||||||
|
field.Float("rate_multiplier").
|
||||||
|
SchemaType(map[string]string{dialect.Postgres: "decimal(10,4)"}).
|
||||||
|
Default(1.0),
|
||||||
|
|
||||||
// status: 账户状态,如 "active", "error", "disabled"
|
// status: 账户状态,如 "active", "error", "disabled"
|
||||||
field.String("status").
|
field.String("status").
|
||||||
MaxLen(20).
|
MaxLen(20).
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ func (UsageLog) Fields() []ent.Field {
|
|||||||
Default(1).
|
Default(1).
|
||||||
SchemaType(map[string]string{dialect.Postgres: "decimal(10,4)"}),
|
SchemaType(map[string]string{dialect.Postgres: "decimal(10,4)"}),
|
||||||
|
|
||||||
|
// account_rate_multiplier: 账号计费倍率快照(NULL 表示按 1.0 处理)
|
||||||
|
field.Float("account_rate_multiplier").
|
||||||
|
Optional().
|
||||||
|
Nillable().
|
||||||
|
SchemaType(map[string]string{dialect.Postgres: "decimal(10,4)"}),
|
||||||
|
|
||||||
// 其他字段
|
// 其他字段
|
||||||
field.Int8("billing_type").
|
field.Int8("billing_type").
|
||||||
Default(0),
|
Default(0),
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ type UsageLog struct {
|
|||||||
ActualCost float64 `json:"actual_cost,omitempty"`
|
ActualCost float64 `json:"actual_cost,omitempty"`
|
||||||
// RateMultiplier holds the value of the "rate_multiplier" field.
|
// RateMultiplier holds the value of the "rate_multiplier" field.
|
||||||
RateMultiplier float64 `json:"rate_multiplier,omitempty"`
|
RateMultiplier float64 `json:"rate_multiplier,omitempty"`
|
||||||
|
// AccountRateMultiplier holds the value of the "account_rate_multiplier" field.
|
||||||
|
AccountRateMultiplier *float64 `json:"account_rate_multiplier,omitempty"`
|
||||||
// BillingType holds the value of the "billing_type" field.
|
// BillingType holds the value of the "billing_type" field.
|
||||||
BillingType int8 `json:"billing_type,omitempty"`
|
BillingType int8 `json:"billing_type,omitempty"`
|
||||||
// Stream holds the value of the "stream" field.
|
// Stream holds the value of the "stream" field.
|
||||||
@@ -165,7 +167,7 @@ func (*UsageLog) scanValues(columns []string) ([]any, error) {
|
|||||||
switch columns[i] {
|
switch columns[i] {
|
||||||
case usagelog.FieldStream:
|
case usagelog.FieldStream:
|
||||||
values[i] = new(sql.NullBool)
|
values[i] = new(sql.NullBool)
|
||||||
case usagelog.FieldInputCost, usagelog.FieldOutputCost, usagelog.FieldCacheCreationCost, usagelog.FieldCacheReadCost, usagelog.FieldTotalCost, usagelog.FieldActualCost, usagelog.FieldRateMultiplier:
|
case usagelog.FieldInputCost, usagelog.FieldOutputCost, usagelog.FieldCacheCreationCost, usagelog.FieldCacheReadCost, usagelog.FieldTotalCost, usagelog.FieldActualCost, usagelog.FieldRateMultiplier, usagelog.FieldAccountRateMultiplier:
|
||||||
values[i] = new(sql.NullFloat64)
|
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, usagelog.FieldImageCount:
|
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)
|
values[i] = new(sql.NullInt64)
|
||||||
@@ -316,6 +318,13 @@ func (_m *UsageLog) assignValues(columns []string, values []any) error {
|
|||||||
} else if value.Valid {
|
} else if value.Valid {
|
||||||
_m.RateMultiplier = value.Float64
|
_m.RateMultiplier = value.Float64
|
||||||
}
|
}
|
||||||
|
case usagelog.FieldAccountRateMultiplier:
|
||||||
|
if value, ok := values[i].(*sql.NullFloat64); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field account_rate_multiplier", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.AccountRateMultiplier = new(float64)
|
||||||
|
*_m.AccountRateMultiplier = value.Float64
|
||||||
|
}
|
||||||
case usagelog.FieldBillingType:
|
case usagelog.FieldBillingType:
|
||||||
if value, ok := values[i].(*sql.NullInt64); !ok {
|
if value, ok := values[i].(*sql.NullInt64); !ok {
|
||||||
return fmt.Errorf("unexpected type %T for field billing_type", values[i])
|
return fmt.Errorf("unexpected type %T for field billing_type", values[i])
|
||||||
@@ -500,6 +509,11 @@ func (_m *UsageLog) String() string {
|
|||||||
builder.WriteString("rate_multiplier=")
|
builder.WriteString("rate_multiplier=")
|
||||||
builder.WriteString(fmt.Sprintf("%v", _m.RateMultiplier))
|
builder.WriteString(fmt.Sprintf("%v", _m.RateMultiplier))
|
||||||
builder.WriteString(", ")
|
builder.WriteString(", ")
|
||||||
|
if v := _m.AccountRateMultiplier; v != nil {
|
||||||
|
builder.WriteString("account_rate_multiplier=")
|
||||||
|
builder.WriteString(fmt.Sprintf("%v", *v))
|
||||||
|
}
|
||||||
|
builder.WriteString(", ")
|
||||||
builder.WriteString("billing_type=")
|
builder.WriteString("billing_type=")
|
||||||
builder.WriteString(fmt.Sprintf("%v", _m.BillingType))
|
builder.WriteString(fmt.Sprintf("%v", _m.BillingType))
|
||||||
builder.WriteString(", ")
|
builder.WriteString(", ")
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ const (
|
|||||||
FieldActualCost = "actual_cost"
|
FieldActualCost = "actual_cost"
|
||||||
// FieldRateMultiplier holds the string denoting the rate_multiplier field in the database.
|
// FieldRateMultiplier holds the string denoting the rate_multiplier field in the database.
|
||||||
FieldRateMultiplier = "rate_multiplier"
|
FieldRateMultiplier = "rate_multiplier"
|
||||||
|
// FieldAccountRateMultiplier holds the string denoting the account_rate_multiplier field in the database.
|
||||||
|
FieldAccountRateMultiplier = "account_rate_multiplier"
|
||||||
// FieldBillingType holds the string denoting the billing_type field in the database.
|
// FieldBillingType holds the string denoting the billing_type field in the database.
|
||||||
FieldBillingType = "billing_type"
|
FieldBillingType = "billing_type"
|
||||||
// FieldStream holds the string denoting the stream field in the database.
|
// FieldStream holds the string denoting the stream field in the database.
|
||||||
@@ -144,6 +146,7 @@ var Columns = []string{
|
|||||||
FieldTotalCost,
|
FieldTotalCost,
|
||||||
FieldActualCost,
|
FieldActualCost,
|
||||||
FieldRateMultiplier,
|
FieldRateMultiplier,
|
||||||
|
FieldAccountRateMultiplier,
|
||||||
FieldBillingType,
|
FieldBillingType,
|
||||||
FieldStream,
|
FieldStream,
|
||||||
FieldDurationMs,
|
FieldDurationMs,
|
||||||
@@ -320,6 +323,11 @@ func ByRateMultiplier(opts ...sql.OrderTermOption) OrderOption {
|
|||||||
return sql.OrderByField(FieldRateMultiplier, opts...).ToFunc()
|
return sql.OrderByField(FieldRateMultiplier, opts...).ToFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ByAccountRateMultiplier orders the results by the account_rate_multiplier field.
|
||||||
|
func ByAccountRateMultiplier(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldAccountRateMultiplier, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
// ByBillingType orders the results by the billing_type field.
|
// ByBillingType orders the results by the billing_type field.
|
||||||
func ByBillingType(opts ...sql.OrderTermOption) OrderOption {
|
func ByBillingType(opts ...sql.OrderTermOption) OrderOption {
|
||||||
return sql.OrderByField(FieldBillingType, opts...).ToFunc()
|
return sql.OrderByField(FieldBillingType, opts...).ToFunc()
|
||||||
|
|||||||
@@ -155,6 +155,11 @@ func RateMultiplier(v float64) predicate.UsageLog {
|
|||||||
return predicate.UsageLog(sql.FieldEQ(FieldRateMultiplier, v))
|
return predicate.UsageLog(sql.FieldEQ(FieldRateMultiplier, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplier applies equality check predicate on the "account_rate_multiplier" field. It's identical to AccountRateMultiplierEQ.
|
||||||
|
func AccountRateMultiplier(v float64) predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldEQ(FieldAccountRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
// BillingType applies equality check predicate on the "billing_type" field. It's identical to BillingTypeEQ.
|
// BillingType applies equality check predicate on the "billing_type" field. It's identical to BillingTypeEQ.
|
||||||
func BillingType(v int8) predicate.UsageLog {
|
func BillingType(v int8) predicate.UsageLog {
|
||||||
return predicate.UsageLog(sql.FieldEQ(FieldBillingType, v))
|
return predicate.UsageLog(sql.FieldEQ(FieldBillingType, v))
|
||||||
@@ -970,6 +975,56 @@ func RateMultiplierLTE(v float64) predicate.UsageLog {
|
|||||||
return predicate.UsageLog(sql.FieldLTE(FieldRateMultiplier, v))
|
return predicate.UsageLog(sql.FieldLTE(FieldRateMultiplier, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierEQ applies the EQ predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierEQ(v float64) predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldEQ(FieldAccountRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierNEQ applies the NEQ predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierNEQ(v float64) predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldNEQ(FieldAccountRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierIn applies the In predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierIn(vs ...float64) predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldIn(FieldAccountRateMultiplier, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierNotIn applies the NotIn predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierNotIn(vs ...float64) predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldNotIn(FieldAccountRateMultiplier, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierGT applies the GT predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierGT(v float64) predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldGT(FieldAccountRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierGTE applies the GTE predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierGTE(v float64) predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldGTE(FieldAccountRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierLT applies the LT predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierLT(v float64) predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldLT(FieldAccountRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierLTE applies the LTE predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierLTE(v float64) predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldLTE(FieldAccountRateMultiplier, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierIsNil applies the IsNil predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierIsNil() predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldIsNull(FieldAccountRateMultiplier))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountRateMultiplierNotNil applies the NotNil predicate on the "account_rate_multiplier" field.
|
||||||
|
func AccountRateMultiplierNotNil() predicate.UsageLog {
|
||||||
|
return predicate.UsageLog(sql.FieldNotNull(FieldAccountRateMultiplier))
|
||||||
|
}
|
||||||
|
|
||||||
// BillingTypeEQ applies the EQ predicate on the "billing_type" field.
|
// BillingTypeEQ applies the EQ predicate on the "billing_type" field.
|
||||||
func BillingTypeEQ(v int8) predicate.UsageLog {
|
func BillingTypeEQ(v int8) predicate.UsageLog {
|
||||||
return predicate.UsageLog(sql.FieldEQ(FieldBillingType, v))
|
return predicate.UsageLog(sql.FieldEQ(FieldBillingType, v))
|
||||||
|
|||||||
@@ -267,6 +267,20 @@ func (_c *UsageLogCreate) SetNillableRateMultiplier(v *float64) *UsageLogCreate
|
|||||||
return _c
|
return _c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAccountRateMultiplier sets the "account_rate_multiplier" field.
|
||||||
|
func (_c *UsageLogCreate) SetAccountRateMultiplier(v float64) *UsageLogCreate {
|
||||||
|
_c.mutation.SetAccountRateMultiplier(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableAccountRateMultiplier sets the "account_rate_multiplier" field if the given value is not nil.
|
||||||
|
func (_c *UsageLogCreate) SetNillableAccountRateMultiplier(v *float64) *UsageLogCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetAccountRateMultiplier(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
// SetBillingType sets the "billing_type" field.
|
// SetBillingType sets the "billing_type" field.
|
||||||
func (_c *UsageLogCreate) SetBillingType(v int8) *UsageLogCreate {
|
func (_c *UsageLogCreate) SetBillingType(v int8) *UsageLogCreate {
|
||||||
_c.mutation.SetBillingType(v)
|
_c.mutation.SetBillingType(v)
|
||||||
@@ -712,6 +726,10 @@ func (_c *UsageLogCreate) createSpec() (*UsageLog, *sqlgraph.CreateSpec) {
|
|||||||
_spec.SetField(usagelog.FieldRateMultiplier, field.TypeFloat64, value)
|
_spec.SetField(usagelog.FieldRateMultiplier, field.TypeFloat64, value)
|
||||||
_node.RateMultiplier = value
|
_node.RateMultiplier = value
|
||||||
}
|
}
|
||||||
|
if value, ok := _c.mutation.AccountRateMultiplier(); ok {
|
||||||
|
_spec.SetField(usagelog.FieldAccountRateMultiplier, field.TypeFloat64, value)
|
||||||
|
_node.AccountRateMultiplier = &value
|
||||||
|
}
|
||||||
if value, ok := _c.mutation.BillingType(); ok {
|
if value, ok := _c.mutation.BillingType(); ok {
|
||||||
_spec.SetField(usagelog.FieldBillingType, field.TypeInt8, value)
|
_spec.SetField(usagelog.FieldBillingType, field.TypeInt8, value)
|
||||||
_node.BillingType = value
|
_node.BillingType = value
|
||||||
@@ -1215,6 +1233,30 @@ func (u *UsageLogUpsert) AddRateMultiplier(v float64) *UsageLogUpsert {
|
|||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAccountRateMultiplier sets the "account_rate_multiplier" field.
|
||||||
|
func (u *UsageLogUpsert) SetAccountRateMultiplier(v float64) *UsageLogUpsert {
|
||||||
|
u.Set(usagelog.FieldAccountRateMultiplier, v)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAccountRateMultiplier sets the "account_rate_multiplier" field to the value that was provided on create.
|
||||||
|
func (u *UsageLogUpsert) UpdateAccountRateMultiplier() *UsageLogUpsert {
|
||||||
|
u.SetExcluded(usagelog.FieldAccountRateMultiplier)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccountRateMultiplier adds v to the "account_rate_multiplier" field.
|
||||||
|
func (u *UsageLogUpsert) AddAccountRateMultiplier(v float64) *UsageLogUpsert {
|
||||||
|
u.Add(usagelog.FieldAccountRateMultiplier, v)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAccountRateMultiplier clears the value of the "account_rate_multiplier" field.
|
||||||
|
func (u *UsageLogUpsert) ClearAccountRateMultiplier() *UsageLogUpsert {
|
||||||
|
u.SetNull(usagelog.FieldAccountRateMultiplier)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
// SetBillingType sets the "billing_type" field.
|
// SetBillingType sets the "billing_type" field.
|
||||||
func (u *UsageLogUpsert) SetBillingType(v int8) *UsageLogUpsert {
|
func (u *UsageLogUpsert) SetBillingType(v int8) *UsageLogUpsert {
|
||||||
u.Set(usagelog.FieldBillingType, v)
|
u.Set(usagelog.FieldBillingType, v)
|
||||||
@@ -1795,6 +1837,34 @@ func (u *UsageLogUpsertOne) UpdateRateMultiplier() *UsageLogUpsertOne {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAccountRateMultiplier sets the "account_rate_multiplier" field.
|
||||||
|
func (u *UsageLogUpsertOne) SetAccountRateMultiplier(v float64) *UsageLogUpsertOne {
|
||||||
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
|
s.SetAccountRateMultiplier(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccountRateMultiplier adds v to the "account_rate_multiplier" field.
|
||||||
|
func (u *UsageLogUpsertOne) AddAccountRateMultiplier(v float64) *UsageLogUpsertOne {
|
||||||
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
|
s.AddAccountRateMultiplier(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAccountRateMultiplier sets the "account_rate_multiplier" field to the value that was provided on create.
|
||||||
|
func (u *UsageLogUpsertOne) UpdateAccountRateMultiplier() *UsageLogUpsertOne {
|
||||||
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
|
s.UpdateAccountRateMultiplier()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAccountRateMultiplier clears the value of the "account_rate_multiplier" field.
|
||||||
|
func (u *UsageLogUpsertOne) ClearAccountRateMultiplier() *UsageLogUpsertOne {
|
||||||
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
|
s.ClearAccountRateMultiplier()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SetBillingType sets the "billing_type" field.
|
// SetBillingType sets the "billing_type" field.
|
||||||
func (u *UsageLogUpsertOne) SetBillingType(v int8) *UsageLogUpsertOne {
|
func (u *UsageLogUpsertOne) SetBillingType(v int8) *UsageLogUpsertOne {
|
||||||
return u.Update(func(s *UsageLogUpsert) {
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
@@ -2566,6 +2636,34 @@ func (u *UsageLogUpsertBulk) UpdateRateMultiplier() *UsageLogUpsertBulk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAccountRateMultiplier sets the "account_rate_multiplier" field.
|
||||||
|
func (u *UsageLogUpsertBulk) SetAccountRateMultiplier(v float64) *UsageLogUpsertBulk {
|
||||||
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
|
s.SetAccountRateMultiplier(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccountRateMultiplier adds v to the "account_rate_multiplier" field.
|
||||||
|
func (u *UsageLogUpsertBulk) AddAccountRateMultiplier(v float64) *UsageLogUpsertBulk {
|
||||||
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
|
s.AddAccountRateMultiplier(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAccountRateMultiplier sets the "account_rate_multiplier" field to the value that was provided on create.
|
||||||
|
func (u *UsageLogUpsertBulk) UpdateAccountRateMultiplier() *UsageLogUpsertBulk {
|
||||||
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
|
s.UpdateAccountRateMultiplier()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAccountRateMultiplier clears the value of the "account_rate_multiplier" field.
|
||||||
|
func (u *UsageLogUpsertBulk) ClearAccountRateMultiplier() *UsageLogUpsertBulk {
|
||||||
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
|
s.ClearAccountRateMultiplier()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SetBillingType sets the "billing_type" field.
|
// SetBillingType sets the "billing_type" field.
|
||||||
func (u *UsageLogUpsertBulk) SetBillingType(v int8) *UsageLogUpsertBulk {
|
func (u *UsageLogUpsertBulk) SetBillingType(v int8) *UsageLogUpsertBulk {
|
||||||
return u.Update(func(s *UsageLogUpsert) {
|
return u.Update(func(s *UsageLogUpsert) {
|
||||||
|
|||||||
@@ -415,6 +415,33 @@ func (_u *UsageLogUpdate) AddRateMultiplier(v float64) *UsageLogUpdate {
|
|||||||
return _u
|
return _u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAccountRateMultiplier sets the "account_rate_multiplier" field.
|
||||||
|
func (_u *UsageLogUpdate) SetAccountRateMultiplier(v float64) *UsageLogUpdate {
|
||||||
|
_u.mutation.ResetAccountRateMultiplier()
|
||||||
|
_u.mutation.SetAccountRateMultiplier(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableAccountRateMultiplier sets the "account_rate_multiplier" field if the given value is not nil.
|
||||||
|
func (_u *UsageLogUpdate) SetNillableAccountRateMultiplier(v *float64) *UsageLogUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetAccountRateMultiplier(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccountRateMultiplier adds value to the "account_rate_multiplier" field.
|
||||||
|
func (_u *UsageLogUpdate) AddAccountRateMultiplier(v float64) *UsageLogUpdate {
|
||||||
|
_u.mutation.AddAccountRateMultiplier(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAccountRateMultiplier clears the value of the "account_rate_multiplier" field.
|
||||||
|
func (_u *UsageLogUpdate) ClearAccountRateMultiplier() *UsageLogUpdate {
|
||||||
|
_u.mutation.ClearAccountRateMultiplier()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
// SetBillingType sets the "billing_type" field.
|
// SetBillingType sets the "billing_type" field.
|
||||||
func (_u *UsageLogUpdate) SetBillingType(v int8) *UsageLogUpdate {
|
func (_u *UsageLogUpdate) SetBillingType(v int8) *UsageLogUpdate {
|
||||||
_u.mutation.ResetBillingType()
|
_u.mutation.ResetBillingType()
|
||||||
@@ -807,6 +834,15 @@ func (_u *UsageLogUpdate) sqlSave(ctx context.Context) (_node int, err error) {
|
|||||||
if value, ok := _u.mutation.AddedRateMultiplier(); ok {
|
if value, ok := _u.mutation.AddedRateMultiplier(); ok {
|
||||||
_spec.AddField(usagelog.FieldRateMultiplier, field.TypeFloat64, value)
|
_spec.AddField(usagelog.FieldRateMultiplier, field.TypeFloat64, value)
|
||||||
}
|
}
|
||||||
|
if value, ok := _u.mutation.AccountRateMultiplier(); ok {
|
||||||
|
_spec.SetField(usagelog.FieldAccountRateMultiplier, field.TypeFloat64, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.AddedAccountRateMultiplier(); ok {
|
||||||
|
_spec.AddField(usagelog.FieldAccountRateMultiplier, field.TypeFloat64, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.AccountRateMultiplierCleared() {
|
||||||
|
_spec.ClearField(usagelog.FieldAccountRateMultiplier, field.TypeFloat64)
|
||||||
|
}
|
||||||
if value, ok := _u.mutation.BillingType(); ok {
|
if value, ok := _u.mutation.BillingType(); ok {
|
||||||
_spec.SetField(usagelog.FieldBillingType, field.TypeInt8, value)
|
_spec.SetField(usagelog.FieldBillingType, field.TypeInt8, value)
|
||||||
}
|
}
|
||||||
@@ -1406,6 +1442,33 @@ func (_u *UsageLogUpdateOne) AddRateMultiplier(v float64) *UsageLogUpdateOne {
|
|||||||
return _u
|
return _u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAccountRateMultiplier sets the "account_rate_multiplier" field.
|
||||||
|
func (_u *UsageLogUpdateOne) SetAccountRateMultiplier(v float64) *UsageLogUpdateOne {
|
||||||
|
_u.mutation.ResetAccountRateMultiplier()
|
||||||
|
_u.mutation.SetAccountRateMultiplier(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableAccountRateMultiplier sets the "account_rate_multiplier" field if the given value is not nil.
|
||||||
|
func (_u *UsageLogUpdateOne) SetNillableAccountRateMultiplier(v *float64) *UsageLogUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetAccountRateMultiplier(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccountRateMultiplier adds value to the "account_rate_multiplier" field.
|
||||||
|
func (_u *UsageLogUpdateOne) AddAccountRateMultiplier(v float64) *UsageLogUpdateOne {
|
||||||
|
_u.mutation.AddAccountRateMultiplier(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAccountRateMultiplier clears the value of the "account_rate_multiplier" field.
|
||||||
|
func (_u *UsageLogUpdateOne) ClearAccountRateMultiplier() *UsageLogUpdateOne {
|
||||||
|
_u.mutation.ClearAccountRateMultiplier()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
// SetBillingType sets the "billing_type" field.
|
// SetBillingType sets the "billing_type" field.
|
||||||
func (_u *UsageLogUpdateOne) SetBillingType(v int8) *UsageLogUpdateOne {
|
func (_u *UsageLogUpdateOne) SetBillingType(v int8) *UsageLogUpdateOne {
|
||||||
_u.mutation.ResetBillingType()
|
_u.mutation.ResetBillingType()
|
||||||
@@ -1828,6 +1891,15 @@ func (_u *UsageLogUpdateOne) sqlSave(ctx context.Context) (_node *UsageLog, err
|
|||||||
if value, ok := _u.mutation.AddedRateMultiplier(); ok {
|
if value, ok := _u.mutation.AddedRateMultiplier(); ok {
|
||||||
_spec.AddField(usagelog.FieldRateMultiplier, field.TypeFloat64, value)
|
_spec.AddField(usagelog.FieldRateMultiplier, field.TypeFloat64, value)
|
||||||
}
|
}
|
||||||
|
if value, ok := _u.mutation.AccountRateMultiplier(); ok {
|
||||||
|
_spec.SetField(usagelog.FieldAccountRateMultiplier, field.TypeFloat64, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.AddedAccountRateMultiplier(); ok {
|
||||||
|
_spec.AddField(usagelog.FieldAccountRateMultiplier, field.TypeFloat64, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.AccountRateMultiplierCleared() {
|
||||||
|
_spec.ClearField(usagelog.FieldAccountRateMultiplier, field.TypeFloat64)
|
||||||
|
}
|
||||||
if value, ok := _u.mutation.BillingType(); ok {
|
if value, ok := _u.mutation.BillingType(); ok {
|
||||||
_spec.SetField(usagelog.FieldBillingType, field.TypeInt8, value)
|
_spec.SetField(usagelog.FieldBillingType, field.TypeInt8, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ type CreateAccountRequest struct {
|
|||||||
ProxyID *int64 `json:"proxy_id"`
|
ProxyID *int64 `json:"proxy_id"`
|
||||||
Concurrency int `json:"concurrency"`
|
Concurrency int `json:"concurrency"`
|
||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
|
RateMultiplier *float64 `json:"rate_multiplier"`
|
||||||
GroupIDs []int64 `json:"group_ids"`
|
GroupIDs []int64 `json:"group_ids"`
|
||||||
ExpiresAt *int64 `json:"expires_at"`
|
ExpiresAt *int64 `json:"expires_at"`
|
||||||
AutoPauseOnExpired *bool `json:"auto_pause_on_expired"`
|
AutoPauseOnExpired *bool `json:"auto_pause_on_expired"`
|
||||||
@@ -101,6 +102,7 @@ type UpdateAccountRequest struct {
|
|||||||
ProxyID *int64 `json:"proxy_id"`
|
ProxyID *int64 `json:"proxy_id"`
|
||||||
Concurrency *int `json:"concurrency"`
|
Concurrency *int `json:"concurrency"`
|
||||||
Priority *int `json:"priority"`
|
Priority *int `json:"priority"`
|
||||||
|
RateMultiplier *float64 `json:"rate_multiplier"`
|
||||||
Status string `json:"status" binding:"omitempty,oneof=active inactive"`
|
Status string `json:"status" binding:"omitempty,oneof=active inactive"`
|
||||||
GroupIDs *[]int64 `json:"group_ids"`
|
GroupIDs *[]int64 `json:"group_ids"`
|
||||||
ExpiresAt *int64 `json:"expires_at"`
|
ExpiresAt *int64 `json:"expires_at"`
|
||||||
@@ -115,6 +117,7 @@ type BulkUpdateAccountsRequest struct {
|
|||||||
ProxyID *int64 `json:"proxy_id"`
|
ProxyID *int64 `json:"proxy_id"`
|
||||||
Concurrency *int `json:"concurrency"`
|
Concurrency *int `json:"concurrency"`
|
||||||
Priority *int `json:"priority"`
|
Priority *int `json:"priority"`
|
||||||
|
RateMultiplier *float64 `json:"rate_multiplier"`
|
||||||
Status string `json:"status" binding:"omitempty,oneof=active inactive error"`
|
Status string `json:"status" binding:"omitempty,oneof=active inactive error"`
|
||||||
Schedulable *bool `json:"schedulable"`
|
Schedulable *bool `json:"schedulable"`
|
||||||
GroupIDs *[]int64 `json:"group_ids"`
|
GroupIDs *[]int64 `json:"group_ids"`
|
||||||
@@ -199,6 +202,10 @@ func (h *AccountHandler) Create(c *gin.Context) {
|
|||||||
response.BadRequest(c, "Invalid request: "+err.Error())
|
response.BadRequest(c, "Invalid request: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if req.RateMultiplier != nil && *req.RateMultiplier < 0 {
|
||||||
|
response.BadRequest(c, "rate_multiplier must be >= 0")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 确定是否跳过混合渠道检查
|
// 确定是否跳过混合渠道检查
|
||||||
skipCheck := req.ConfirmMixedChannelRisk != nil && *req.ConfirmMixedChannelRisk
|
skipCheck := req.ConfirmMixedChannelRisk != nil && *req.ConfirmMixedChannelRisk
|
||||||
@@ -213,6 +220,7 @@ func (h *AccountHandler) Create(c *gin.Context) {
|
|||||||
ProxyID: req.ProxyID,
|
ProxyID: req.ProxyID,
|
||||||
Concurrency: req.Concurrency,
|
Concurrency: req.Concurrency,
|
||||||
Priority: req.Priority,
|
Priority: req.Priority,
|
||||||
|
RateMultiplier: req.RateMultiplier,
|
||||||
GroupIDs: req.GroupIDs,
|
GroupIDs: req.GroupIDs,
|
||||||
ExpiresAt: req.ExpiresAt,
|
ExpiresAt: req.ExpiresAt,
|
||||||
AutoPauseOnExpired: req.AutoPauseOnExpired,
|
AutoPauseOnExpired: req.AutoPauseOnExpired,
|
||||||
@@ -258,6 +266,10 @@ func (h *AccountHandler) Update(c *gin.Context) {
|
|||||||
response.BadRequest(c, "Invalid request: "+err.Error())
|
response.BadRequest(c, "Invalid request: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if req.RateMultiplier != nil && *req.RateMultiplier < 0 {
|
||||||
|
response.BadRequest(c, "rate_multiplier must be >= 0")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 确定是否跳过混合渠道检查
|
// 确定是否跳过混合渠道检查
|
||||||
skipCheck := req.ConfirmMixedChannelRisk != nil && *req.ConfirmMixedChannelRisk
|
skipCheck := req.ConfirmMixedChannelRisk != nil && *req.ConfirmMixedChannelRisk
|
||||||
@@ -271,6 +283,7 @@ func (h *AccountHandler) Update(c *gin.Context) {
|
|||||||
ProxyID: req.ProxyID,
|
ProxyID: req.ProxyID,
|
||||||
Concurrency: req.Concurrency, // 指针类型,nil 表示未提供
|
Concurrency: req.Concurrency, // 指针类型,nil 表示未提供
|
||||||
Priority: req.Priority, // 指针类型,nil 表示未提供
|
Priority: req.Priority, // 指针类型,nil 表示未提供
|
||||||
|
RateMultiplier: req.RateMultiplier,
|
||||||
Status: req.Status,
|
Status: req.Status,
|
||||||
GroupIDs: req.GroupIDs,
|
GroupIDs: req.GroupIDs,
|
||||||
ExpiresAt: req.ExpiresAt,
|
ExpiresAt: req.ExpiresAt,
|
||||||
@@ -652,6 +665,10 @@ func (h *AccountHandler) BulkUpdate(c *gin.Context) {
|
|||||||
response.BadRequest(c, "Invalid request: "+err.Error())
|
response.BadRequest(c, "Invalid request: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if req.RateMultiplier != nil && *req.RateMultiplier < 0 {
|
||||||
|
response.BadRequest(c, "rate_multiplier must be >= 0")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 确定是否跳过混合渠道检查
|
// 确定是否跳过混合渠道检查
|
||||||
skipCheck := req.ConfirmMixedChannelRisk != nil && *req.ConfirmMixedChannelRisk
|
skipCheck := req.ConfirmMixedChannelRisk != nil && *req.ConfirmMixedChannelRisk
|
||||||
@@ -660,6 +677,7 @@ func (h *AccountHandler) BulkUpdate(c *gin.Context) {
|
|||||||
req.ProxyID != nil ||
|
req.ProxyID != nil ||
|
||||||
req.Concurrency != nil ||
|
req.Concurrency != nil ||
|
||||||
req.Priority != nil ||
|
req.Priority != nil ||
|
||||||
|
req.RateMultiplier != nil ||
|
||||||
req.Status != "" ||
|
req.Status != "" ||
|
||||||
req.Schedulable != nil ||
|
req.Schedulable != nil ||
|
||||||
req.GroupIDs != nil ||
|
req.GroupIDs != nil ||
|
||||||
@@ -677,6 +695,7 @@ func (h *AccountHandler) BulkUpdate(c *gin.Context) {
|
|||||||
ProxyID: req.ProxyID,
|
ProxyID: req.ProxyID,
|
||||||
Concurrency: req.Concurrency,
|
Concurrency: req.Concurrency,
|
||||||
Priority: req.Priority,
|
Priority: req.Priority,
|
||||||
|
RateMultiplier: req.RateMultiplier,
|
||||||
Status: req.Status,
|
Status: req.Status,
|
||||||
Schedulable: req.Schedulable,
|
Schedulable: req.Schedulable,
|
||||||
GroupIDs: req.GroupIDs,
|
GroupIDs: req.GroupIDs,
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ func AccountFromServiceShallow(a *service.Account) *Account {
|
|||||||
ProxyID: a.ProxyID,
|
ProxyID: a.ProxyID,
|
||||||
Concurrency: a.Concurrency,
|
Concurrency: a.Concurrency,
|
||||||
Priority: a.Priority,
|
Priority: a.Priority,
|
||||||
|
RateMultiplier: a.BillingRateMultiplier(),
|
||||||
Status: a.Status,
|
Status: a.Status,
|
||||||
ErrorMessage: a.ErrorMessage,
|
ErrorMessage: a.ErrorMessage,
|
||||||
LastUsedAt: a.LastUsedAt,
|
LastUsedAt: a.LastUsedAt,
|
||||||
@@ -279,6 +280,7 @@ func usageLogFromServiceBase(l *service.UsageLog, account *AccountSummary, inclu
|
|||||||
TotalCost: l.TotalCost,
|
TotalCost: l.TotalCost,
|
||||||
ActualCost: l.ActualCost,
|
ActualCost: l.ActualCost,
|
||||||
RateMultiplier: l.RateMultiplier,
|
RateMultiplier: l.RateMultiplier,
|
||||||
|
AccountRateMultiplier: l.AccountRateMultiplier,
|
||||||
BillingType: l.BillingType,
|
BillingType: l.BillingType,
|
||||||
Stream: l.Stream,
|
Stream: l.Stream,
|
||||||
DurationMs: l.DurationMs,
|
DurationMs: l.DurationMs,
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ type Account struct {
|
|||||||
ProxyID *int64 `json:"proxy_id"`
|
ProxyID *int64 `json:"proxy_id"`
|
||||||
Concurrency int `json:"concurrency"`
|
Concurrency int `json:"concurrency"`
|
||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
|
RateMultiplier float64 `json:"rate_multiplier"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
ErrorMessage string `json:"error_message"`
|
ErrorMessage string `json:"error_message"`
|
||||||
LastUsedAt *time.Time `json:"last_used_at"`
|
LastUsedAt *time.Time `json:"last_used_at"`
|
||||||
@@ -169,13 +170,14 @@ type UsageLog struct {
|
|||||||
CacheCreation5mTokens int `json:"cache_creation_5m_tokens"`
|
CacheCreation5mTokens int `json:"cache_creation_5m_tokens"`
|
||||||
CacheCreation1hTokens int `json:"cache_creation_1h_tokens"`
|
CacheCreation1hTokens int `json:"cache_creation_1h_tokens"`
|
||||||
|
|
||||||
InputCost float64 `json:"input_cost"`
|
InputCost float64 `json:"input_cost"`
|
||||||
OutputCost float64 `json:"output_cost"`
|
OutputCost float64 `json:"output_cost"`
|
||||||
CacheCreationCost float64 `json:"cache_creation_cost"`
|
CacheCreationCost float64 `json:"cache_creation_cost"`
|
||||||
CacheReadCost float64 `json:"cache_read_cost"`
|
CacheReadCost float64 `json:"cache_read_cost"`
|
||||||
TotalCost float64 `json:"total_cost"`
|
TotalCost float64 `json:"total_cost"`
|
||||||
ActualCost float64 `json:"actual_cost"`
|
ActualCost float64 `json:"actual_cost"`
|
||||||
RateMultiplier float64 `json:"rate_multiplier"`
|
RateMultiplier float64 `json:"rate_multiplier"`
|
||||||
|
AccountRateMultiplier *float64 `json:"account_rate_multiplier"`
|
||||||
|
|
||||||
BillingType int8 `json:"billing_type"`
|
BillingType int8 `json:"billing_type"`
|
||||||
Stream bool `json:"stream"`
|
Stream bool `json:"stream"`
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
package usagestats
|
package usagestats
|
||||||
|
|
||||||
// AccountStats 账号使用统计
|
// AccountStats 账号使用统计
|
||||||
|
//
|
||||||
|
// cost: 账号口径费用(使用 total_cost * account_rate_multiplier)
|
||||||
|
// standard_cost: 标准费用(使用 total_cost,不含倍率)
|
||||||
|
// user_cost: 用户/API Key 口径费用(使用 actual_cost,受分组倍率影响)
|
||||||
type AccountStats struct {
|
type AccountStats struct {
|
||||||
Requests int64 `json:"requests"`
|
Requests int64 `json:"requests"`
|
||||||
Tokens int64 `json:"tokens"`
|
Tokens int64 `json:"tokens"`
|
||||||
Cost float64 `json:"cost"`
|
Cost float64 `json:"cost"`
|
||||||
|
StandardCost float64 `json:"standard_cost"`
|
||||||
|
UserCost float64 `json:"user_cost"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,14 +147,15 @@ type UsageLogFilters struct {
|
|||||||
|
|
||||||
// UsageStats represents usage statistics
|
// UsageStats represents usage statistics
|
||||||
type UsageStats struct {
|
type UsageStats struct {
|
||||||
TotalRequests int64 `json:"total_requests"`
|
TotalRequests int64 `json:"total_requests"`
|
||||||
TotalInputTokens int64 `json:"total_input_tokens"`
|
TotalInputTokens int64 `json:"total_input_tokens"`
|
||||||
TotalOutputTokens int64 `json:"total_output_tokens"`
|
TotalOutputTokens int64 `json:"total_output_tokens"`
|
||||||
TotalCacheTokens int64 `json:"total_cache_tokens"`
|
TotalCacheTokens int64 `json:"total_cache_tokens"`
|
||||||
TotalTokens int64 `json:"total_tokens"`
|
TotalTokens int64 `json:"total_tokens"`
|
||||||
TotalCost float64 `json:"total_cost"`
|
TotalCost float64 `json:"total_cost"`
|
||||||
TotalActualCost float64 `json:"total_actual_cost"`
|
TotalActualCost float64 `json:"total_actual_cost"`
|
||||||
AverageDurationMs float64 `json:"average_duration_ms"`
|
TotalAccountCost *float64 `json:"total_account_cost,omitempty"`
|
||||||
|
AverageDurationMs float64 `json:"average_duration_ms"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchUserUsageStats represents usage stats for a single user
|
// BatchUserUsageStats represents usage stats for a single user
|
||||||
@@ -177,25 +178,29 @@ type AccountUsageHistory struct {
|
|||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Requests int64 `json:"requests"`
|
Requests int64 `json:"requests"`
|
||||||
Tokens int64 `json:"tokens"`
|
Tokens int64 `json:"tokens"`
|
||||||
Cost float64 `json:"cost"`
|
Cost float64 `json:"cost"` // 标准计费(total_cost)
|
||||||
ActualCost float64 `json:"actual_cost"`
|
ActualCost float64 `json:"actual_cost"` // 账号口径费用(total_cost * account_rate_multiplier)
|
||||||
|
UserCost float64 `json:"user_cost"` // 用户口径费用(actual_cost,受分组倍率影响)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountUsageSummary represents summary statistics for an account
|
// AccountUsageSummary represents summary statistics for an account
|
||||||
type AccountUsageSummary struct {
|
type AccountUsageSummary struct {
|
||||||
Days int `json:"days"`
|
Days int `json:"days"`
|
||||||
ActualDaysUsed int `json:"actual_days_used"`
|
ActualDaysUsed int `json:"actual_days_used"`
|
||||||
TotalCost float64 `json:"total_cost"`
|
TotalCost float64 `json:"total_cost"` // 账号口径费用
|
||||||
|
TotalUserCost float64 `json:"total_user_cost"` // 用户口径费用
|
||||||
TotalStandardCost float64 `json:"total_standard_cost"`
|
TotalStandardCost float64 `json:"total_standard_cost"`
|
||||||
TotalRequests int64 `json:"total_requests"`
|
TotalRequests int64 `json:"total_requests"`
|
||||||
TotalTokens int64 `json:"total_tokens"`
|
TotalTokens int64 `json:"total_tokens"`
|
||||||
AvgDailyCost float64 `json:"avg_daily_cost"`
|
AvgDailyCost float64 `json:"avg_daily_cost"` // 账号口径日均
|
||||||
|
AvgDailyUserCost float64 `json:"avg_daily_user_cost"`
|
||||||
AvgDailyRequests float64 `json:"avg_daily_requests"`
|
AvgDailyRequests float64 `json:"avg_daily_requests"`
|
||||||
AvgDailyTokens float64 `json:"avg_daily_tokens"`
|
AvgDailyTokens float64 `json:"avg_daily_tokens"`
|
||||||
AvgDurationMs float64 `json:"avg_duration_ms"`
|
AvgDurationMs float64 `json:"avg_duration_ms"`
|
||||||
Today *struct {
|
Today *struct {
|
||||||
Date string `json:"date"`
|
Date string `json:"date"`
|
||||||
Cost float64 `json:"cost"`
|
Cost float64 `json:"cost"`
|
||||||
|
UserCost float64 `json:"user_cost"`
|
||||||
Requests int64 `json:"requests"`
|
Requests int64 `json:"requests"`
|
||||||
Tokens int64 `json:"tokens"`
|
Tokens int64 `json:"tokens"`
|
||||||
} `json:"today"`
|
} `json:"today"`
|
||||||
@@ -203,6 +208,7 @@ type AccountUsageSummary struct {
|
|||||||
Date string `json:"date"`
|
Date string `json:"date"`
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Cost float64 `json:"cost"`
|
Cost float64 `json:"cost"`
|
||||||
|
UserCost float64 `json:"user_cost"`
|
||||||
Requests int64 `json:"requests"`
|
Requests int64 `json:"requests"`
|
||||||
} `json:"highest_cost_day"`
|
} `json:"highest_cost_day"`
|
||||||
HighestRequestDay *struct {
|
HighestRequestDay *struct {
|
||||||
@@ -210,6 +216,7 @@ type AccountUsageSummary struct {
|
|||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Requests int64 `json:"requests"`
|
Requests int64 `json:"requests"`
|
||||||
Cost float64 `json:"cost"`
|
Cost float64 `json:"cost"`
|
||||||
|
UserCost float64 `json:"user_cost"`
|
||||||
} `json:"highest_request_day"`
|
} `json:"highest_request_day"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,10 @@ func (r *accountRepository) Create(ctx context.Context, account *service.Account
|
|||||||
SetSchedulable(account.Schedulable).
|
SetSchedulable(account.Schedulable).
|
||||||
SetAutoPauseOnExpired(account.AutoPauseOnExpired)
|
SetAutoPauseOnExpired(account.AutoPauseOnExpired)
|
||||||
|
|
||||||
|
if account.RateMultiplier != nil {
|
||||||
|
builder.SetRateMultiplier(*account.RateMultiplier)
|
||||||
|
}
|
||||||
|
|
||||||
if account.ProxyID != nil {
|
if account.ProxyID != nil {
|
||||||
builder.SetProxyID(*account.ProxyID)
|
builder.SetProxyID(*account.ProxyID)
|
||||||
}
|
}
|
||||||
@@ -291,6 +295,10 @@ func (r *accountRepository) Update(ctx context.Context, account *service.Account
|
|||||||
SetSchedulable(account.Schedulable).
|
SetSchedulable(account.Schedulable).
|
||||||
SetAutoPauseOnExpired(account.AutoPauseOnExpired)
|
SetAutoPauseOnExpired(account.AutoPauseOnExpired)
|
||||||
|
|
||||||
|
if account.RateMultiplier != nil {
|
||||||
|
builder.SetRateMultiplier(*account.RateMultiplier)
|
||||||
|
}
|
||||||
|
|
||||||
if account.ProxyID != nil {
|
if account.ProxyID != nil {
|
||||||
builder.SetProxyID(*account.ProxyID)
|
builder.SetProxyID(*account.ProxyID)
|
||||||
} else {
|
} else {
|
||||||
@@ -999,6 +1007,11 @@ func (r *accountRepository) BulkUpdate(ctx context.Context, ids []int64, updates
|
|||||||
args = append(args, *updates.Priority)
|
args = append(args, *updates.Priority)
|
||||||
idx++
|
idx++
|
||||||
}
|
}
|
||||||
|
if updates.RateMultiplier != nil {
|
||||||
|
setClauses = append(setClauses, "rate_multiplier = $"+itoa(idx))
|
||||||
|
args = append(args, *updates.RateMultiplier)
|
||||||
|
idx++
|
||||||
|
}
|
||||||
if updates.Status != nil {
|
if updates.Status != nil {
|
||||||
setClauses = append(setClauses, "status = $"+itoa(idx))
|
setClauses = append(setClauses, "status = $"+itoa(idx))
|
||||||
args = append(args, *updates.Status)
|
args = append(args, *updates.Status)
|
||||||
@@ -1347,6 +1360,8 @@ func accountEntityToService(m *dbent.Account) *service.Account {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rateMultiplier := m.RateMultiplier
|
||||||
|
|
||||||
return &service.Account{
|
return &service.Account{
|
||||||
ID: m.ID,
|
ID: m.ID,
|
||||||
Name: m.Name,
|
Name: m.Name,
|
||||||
@@ -1358,6 +1373,7 @@ func accountEntityToService(m *dbent.Account) *service.Account {
|
|||||||
ProxyID: m.ProxyID,
|
ProxyID: m.ProxyID,
|
||||||
Concurrency: m.Concurrency,
|
Concurrency: m.Concurrency,
|
||||||
Priority: m.Priority,
|
Priority: m.Priority,
|
||||||
|
RateMultiplier: &rateMultiplier,
|
||||||
Status: m.Status,
|
Status: m.Status,
|
||||||
ErrorMessage: derefString(m.ErrorMessage),
|
ErrorMessage: derefString(m.ErrorMessage),
|
||||||
LastUsedAt: m.LastUsedAt,
|
LastUsedAt: m.LastUsedAt,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/lib/pq"
|
"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, user_agent, ip_address, image_count, image_size, 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, account_rate_multiplier, billing_type, stream, duration_ms, first_token_ms, user_agent, ip_address, image_count, image_size, created_at"
|
||||||
|
|
||||||
type usageLogRepository struct {
|
type usageLogRepository struct {
|
||||||
client *dbent.Client
|
client *dbent.Client
|
||||||
@@ -105,6 +105,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
|
|||||||
total_cost,
|
total_cost,
|
||||||
actual_cost,
|
actual_cost,
|
||||||
rate_multiplier,
|
rate_multiplier,
|
||||||
|
account_rate_multiplier,
|
||||||
billing_type,
|
billing_type,
|
||||||
stream,
|
stream,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
@@ -120,7 +121,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
|
|||||||
$8, $9, $10, $11,
|
$8, $9, $10, $11,
|
||||||
$12, $13,
|
$12, $13,
|
||||||
$14, $15, $16, $17, $18, $19,
|
$14, $15, $16, $17, $18, $19,
|
||||||
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29
|
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30
|
||||||
)
|
)
|
||||||
ON CONFLICT (request_id, api_key_id) DO NOTHING
|
ON CONFLICT (request_id, api_key_id) DO NOTHING
|
||||||
RETURNING id, created_at
|
RETURNING id, created_at
|
||||||
@@ -160,6 +161,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
|
|||||||
log.TotalCost,
|
log.TotalCost,
|
||||||
log.ActualCost,
|
log.ActualCost,
|
||||||
rateMultiplier,
|
rateMultiplier,
|
||||||
|
log.AccountRateMultiplier,
|
||||||
log.BillingType,
|
log.BillingType,
|
||||||
log.Stream,
|
log.Stream,
|
||||||
duration,
|
duration,
|
||||||
@@ -835,7 +837,9 @@ func (r *usageLogRepository) GetAccountTodayStats(ctx context.Context, accountID
|
|||||||
SELECT
|
SELECT
|
||||||
COUNT(*) as requests,
|
COUNT(*) as requests,
|
||||||
COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as tokens,
|
COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as tokens,
|
||||||
COALESCE(SUM(actual_cost), 0) as cost
|
COALESCE(SUM(total_cost * COALESCE(account_rate_multiplier, 1)), 0) as cost,
|
||||||
|
COALESCE(SUM(total_cost), 0) as standard_cost,
|
||||||
|
COALESCE(SUM(actual_cost), 0) as user_cost
|
||||||
FROM usage_logs
|
FROM usage_logs
|
||||||
WHERE account_id = $1 AND created_at >= $2
|
WHERE account_id = $1 AND created_at >= $2
|
||||||
`
|
`
|
||||||
@@ -849,6 +853,8 @@ func (r *usageLogRepository) GetAccountTodayStats(ctx context.Context, accountID
|
|||||||
&stats.Requests,
|
&stats.Requests,
|
||||||
&stats.Tokens,
|
&stats.Tokens,
|
||||||
&stats.Cost,
|
&stats.Cost,
|
||||||
|
&stats.StandardCost,
|
||||||
|
&stats.UserCost,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -861,7 +867,9 @@ func (r *usageLogRepository) GetAccountWindowStats(ctx context.Context, accountI
|
|||||||
SELECT
|
SELECT
|
||||||
COUNT(*) as requests,
|
COUNT(*) as requests,
|
||||||
COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as tokens,
|
COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as tokens,
|
||||||
COALESCE(SUM(actual_cost), 0) as cost
|
COALESCE(SUM(total_cost * COALESCE(account_rate_multiplier, 1)), 0) as cost,
|
||||||
|
COALESCE(SUM(total_cost), 0) as standard_cost,
|
||||||
|
COALESCE(SUM(actual_cost), 0) as user_cost
|
||||||
FROM usage_logs
|
FROM usage_logs
|
||||||
WHERE account_id = $1 AND created_at >= $2
|
WHERE account_id = $1 AND created_at >= $2
|
||||||
`
|
`
|
||||||
@@ -875,6 +883,8 @@ func (r *usageLogRepository) GetAccountWindowStats(ctx context.Context, accountI
|
|||||||
&stats.Requests,
|
&stats.Requests,
|
||||||
&stats.Tokens,
|
&stats.Tokens,
|
||||||
&stats.Cost,
|
&stats.Cost,
|
||||||
|
&stats.StandardCost,
|
||||||
|
&stats.UserCost,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -1454,7 +1464,13 @@ func (r *usageLogRepository) GetUsageTrendWithFilters(ctx context.Context, start
|
|||||||
|
|
||||||
// GetModelStatsWithFilters returns model statistics with optional user/api_key filters
|
// GetModelStatsWithFilters returns model statistics with optional user/api_key filters
|
||||||
func (r *usageLogRepository) GetModelStatsWithFilters(ctx context.Context, startTime, endTime time.Time, userID, apiKeyID, accountID int64) (results []ModelStat, err error) {
|
func (r *usageLogRepository) GetModelStatsWithFilters(ctx context.Context, startTime, endTime time.Time, userID, apiKeyID, accountID int64) (results []ModelStat, err error) {
|
||||||
query := `
|
actualCostExpr := "COALESCE(SUM(actual_cost), 0) as actual_cost"
|
||||||
|
// 当仅按 account_id 聚合时,实际费用使用账号倍率(total_cost * account_rate_multiplier)。
|
||||||
|
if accountID > 0 && userID == 0 && apiKeyID == 0 {
|
||||||
|
actualCostExpr = "COALESCE(SUM(total_cost * COALESCE(account_rate_multiplier, 1)), 0) as actual_cost"
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(`
|
||||||
SELECT
|
SELECT
|
||||||
model,
|
model,
|
||||||
COUNT(*) as requests,
|
COUNT(*) as requests,
|
||||||
@@ -1462,10 +1478,10 @@ func (r *usageLogRepository) GetModelStatsWithFilters(ctx context.Context, start
|
|||||||
COALESCE(SUM(output_tokens), 0) as output_tokens,
|
COALESCE(SUM(output_tokens), 0) as output_tokens,
|
||||||
COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as total_tokens,
|
COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as total_tokens,
|
||||||
COALESCE(SUM(total_cost), 0) as cost,
|
COALESCE(SUM(total_cost), 0) as cost,
|
||||||
COALESCE(SUM(actual_cost), 0) as actual_cost
|
%s
|
||||||
FROM usage_logs
|
FROM usage_logs
|
||||||
WHERE created_at >= $1 AND created_at < $2
|
WHERE created_at >= $1 AND created_at < $2
|
||||||
`
|
`, actualCostExpr)
|
||||||
|
|
||||||
args := []any{startTime, endTime}
|
args := []any{startTime, endTime}
|
||||||
if userID > 0 {
|
if userID > 0 {
|
||||||
@@ -1587,12 +1603,14 @@ func (r *usageLogRepository) GetStatsWithFilters(ctx context.Context, filters Us
|
|||||||
COALESCE(SUM(cache_creation_tokens + cache_read_tokens), 0) as total_cache_tokens,
|
COALESCE(SUM(cache_creation_tokens + cache_read_tokens), 0) as total_cache_tokens,
|
||||||
COALESCE(SUM(total_cost), 0) as total_cost,
|
COALESCE(SUM(total_cost), 0) as total_cost,
|
||||||
COALESCE(SUM(actual_cost), 0) as total_actual_cost,
|
COALESCE(SUM(actual_cost), 0) as total_actual_cost,
|
||||||
|
COALESCE(SUM(total_cost * COALESCE(account_rate_multiplier, 1)), 0) as total_account_cost,
|
||||||
COALESCE(AVG(duration_ms), 0) as avg_duration_ms
|
COALESCE(AVG(duration_ms), 0) as avg_duration_ms
|
||||||
FROM usage_logs
|
FROM usage_logs
|
||||||
%s
|
%s
|
||||||
`, buildWhere(conditions))
|
`, buildWhere(conditions))
|
||||||
|
|
||||||
stats := &UsageStats{}
|
stats := &UsageStats{}
|
||||||
|
var totalAccountCost float64
|
||||||
if err := scanSingleRow(
|
if err := scanSingleRow(
|
||||||
ctx,
|
ctx,
|
||||||
r.sql,
|
r.sql,
|
||||||
@@ -1604,10 +1622,14 @@ func (r *usageLogRepository) GetStatsWithFilters(ctx context.Context, filters Us
|
|||||||
&stats.TotalCacheTokens,
|
&stats.TotalCacheTokens,
|
||||||
&stats.TotalCost,
|
&stats.TotalCost,
|
||||||
&stats.TotalActualCost,
|
&stats.TotalActualCost,
|
||||||
|
&totalAccountCost,
|
||||||
&stats.AverageDurationMs,
|
&stats.AverageDurationMs,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if filters.AccountID > 0 {
|
||||||
|
stats.TotalAccountCost = &totalAccountCost
|
||||||
|
}
|
||||||
stats.TotalTokens = stats.TotalInputTokens + stats.TotalOutputTokens + stats.TotalCacheTokens
|
stats.TotalTokens = stats.TotalInputTokens + stats.TotalOutputTokens + stats.TotalCacheTokens
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
@@ -1634,7 +1656,8 @@ func (r *usageLogRepository) GetAccountUsageStats(ctx context.Context, accountID
|
|||||||
COUNT(*) as requests,
|
COUNT(*) as requests,
|
||||||
COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as tokens,
|
COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as tokens,
|
||||||
COALESCE(SUM(total_cost), 0) as cost,
|
COALESCE(SUM(total_cost), 0) as cost,
|
||||||
COALESCE(SUM(actual_cost), 0) as actual_cost
|
COALESCE(SUM(total_cost * COALESCE(account_rate_multiplier, 1)), 0) as actual_cost,
|
||||||
|
COALESCE(SUM(actual_cost), 0) as user_cost
|
||||||
FROM usage_logs
|
FROM usage_logs
|
||||||
WHERE account_id = $1 AND created_at >= $2 AND created_at < $3
|
WHERE account_id = $1 AND created_at >= $2 AND created_at < $3
|
||||||
GROUP BY date
|
GROUP BY date
|
||||||
@@ -1661,7 +1684,8 @@ func (r *usageLogRepository) GetAccountUsageStats(ctx context.Context, accountID
|
|||||||
var tokens int64
|
var tokens int64
|
||||||
var cost float64
|
var cost float64
|
||||||
var actualCost float64
|
var actualCost float64
|
||||||
if err = rows.Scan(&date, &requests, &tokens, &cost, &actualCost); err != nil {
|
var userCost float64
|
||||||
|
if err = rows.Scan(&date, &requests, &tokens, &cost, &actualCost, &userCost); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t, _ := time.Parse("2006-01-02", date)
|
t, _ := time.Parse("2006-01-02", date)
|
||||||
@@ -1672,19 +1696,21 @@ func (r *usageLogRepository) GetAccountUsageStats(ctx context.Context, accountID
|
|||||||
Tokens: tokens,
|
Tokens: tokens,
|
||||||
Cost: cost,
|
Cost: cost,
|
||||||
ActualCost: actualCost,
|
ActualCost: actualCost,
|
||||||
|
UserCost: userCost,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if err = rows.Err(); err != nil {
|
if err = rows.Err(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalActualCost, totalStandardCost float64
|
var totalAccountCost, totalUserCost, totalStandardCost float64
|
||||||
var totalRequests, totalTokens int64
|
var totalRequests, totalTokens int64
|
||||||
var highestCostDay, highestRequestDay *AccountUsageHistory
|
var highestCostDay, highestRequestDay *AccountUsageHistory
|
||||||
|
|
||||||
for i := range history {
|
for i := range history {
|
||||||
h := &history[i]
|
h := &history[i]
|
||||||
totalActualCost += h.ActualCost
|
totalAccountCost += h.ActualCost
|
||||||
|
totalUserCost += h.UserCost
|
||||||
totalStandardCost += h.Cost
|
totalStandardCost += h.Cost
|
||||||
totalRequests += h.Requests
|
totalRequests += h.Requests
|
||||||
totalTokens += h.Tokens
|
totalTokens += h.Tokens
|
||||||
@@ -1711,11 +1737,13 @@ func (r *usageLogRepository) GetAccountUsageStats(ctx context.Context, accountID
|
|||||||
summary := AccountUsageSummary{
|
summary := AccountUsageSummary{
|
||||||
Days: daysCount,
|
Days: daysCount,
|
||||||
ActualDaysUsed: actualDaysUsed,
|
ActualDaysUsed: actualDaysUsed,
|
||||||
TotalCost: totalActualCost,
|
TotalCost: totalAccountCost,
|
||||||
|
TotalUserCost: totalUserCost,
|
||||||
TotalStandardCost: totalStandardCost,
|
TotalStandardCost: totalStandardCost,
|
||||||
TotalRequests: totalRequests,
|
TotalRequests: totalRequests,
|
||||||
TotalTokens: totalTokens,
|
TotalTokens: totalTokens,
|
||||||
AvgDailyCost: totalActualCost / float64(actualDaysUsed),
|
AvgDailyCost: totalAccountCost / float64(actualDaysUsed),
|
||||||
|
AvgDailyUserCost: totalUserCost / float64(actualDaysUsed),
|
||||||
AvgDailyRequests: float64(totalRequests) / float64(actualDaysUsed),
|
AvgDailyRequests: float64(totalRequests) / float64(actualDaysUsed),
|
||||||
AvgDailyTokens: float64(totalTokens) / float64(actualDaysUsed),
|
AvgDailyTokens: float64(totalTokens) / float64(actualDaysUsed),
|
||||||
AvgDurationMs: avgDuration,
|
AvgDurationMs: avgDuration,
|
||||||
@@ -1727,11 +1755,13 @@ func (r *usageLogRepository) GetAccountUsageStats(ctx context.Context, accountID
|
|||||||
summary.Today = &struct {
|
summary.Today = &struct {
|
||||||
Date string `json:"date"`
|
Date string `json:"date"`
|
||||||
Cost float64 `json:"cost"`
|
Cost float64 `json:"cost"`
|
||||||
|
UserCost float64 `json:"user_cost"`
|
||||||
Requests int64 `json:"requests"`
|
Requests int64 `json:"requests"`
|
||||||
Tokens int64 `json:"tokens"`
|
Tokens int64 `json:"tokens"`
|
||||||
}{
|
}{
|
||||||
Date: history[i].Date,
|
Date: history[i].Date,
|
||||||
Cost: history[i].ActualCost,
|
Cost: history[i].ActualCost,
|
||||||
|
UserCost: history[i].UserCost,
|
||||||
Requests: history[i].Requests,
|
Requests: history[i].Requests,
|
||||||
Tokens: history[i].Tokens,
|
Tokens: history[i].Tokens,
|
||||||
}
|
}
|
||||||
@@ -1744,11 +1774,13 @@ func (r *usageLogRepository) GetAccountUsageStats(ctx context.Context, accountID
|
|||||||
Date string `json:"date"`
|
Date string `json:"date"`
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Cost float64 `json:"cost"`
|
Cost float64 `json:"cost"`
|
||||||
|
UserCost float64 `json:"user_cost"`
|
||||||
Requests int64 `json:"requests"`
|
Requests int64 `json:"requests"`
|
||||||
}{
|
}{
|
||||||
Date: highestCostDay.Date,
|
Date: highestCostDay.Date,
|
||||||
Label: highestCostDay.Label,
|
Label: highestCostDay.Label,
|
||||||
Cost: highestCostDay.ActualCost,
|
Cost: highestCostDay.ActualCost,
|
||||||
|
UserCost: highestCostDay.UserCost,
|
||||||
Requests: highestCostDay.Requests,
|
Requests: highestCostDay.Requests,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1759,11 +1791,13 @@ func (r *usageLogRepository) GetAccountUsageStats(ctx context.Context, accountID
|
|||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Requests int64 `json:"requests"`
|
Requests int64 `json:"requests"`
|
||||||
Cost float64 `json:"cost"`
|
Cost float64 `json:"cost"`
|
||||||
|
UserCost float64 `json:"user_cost"`
|
||||||
}{
|
}{
|
||||||
Date: highestRequestDay.Date,
|
Date: highestRequestDay.Date,
|
||||||
Label: highestRequestDay.Label,
|
Label: highestRequestDay.Label,
|
||||||
Requests: highestRequestDay.Requests,
|
Requests: highestRequestDay.Requests,
|
||||||
Cost: highestRequestDay.ActualCost,
|
Cost: highestRequestDay.ActualCost,
|
||||||
|
UserCost: highestRequestDay.UserCost,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1994,36 +2028,37 @@ func (r *usageLogRepository) loadSubscriptions(ctx context.Context, ids []int64)
|
|||||||
|
|
||||||
func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, error) {
|
func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, error) {
|
||||||
var (
|
var (
|
||||||
id int64
|
id int64
|
||||||
userID int64
|
userID int64
|
||||||
apiKeyID int64
|
apiKeyID int64
|
||||||
accountID int64
|
accountID int64
|
||||||
requestID sql.NullString
|
requestID sql.NullString
|
||||||
model string
|
model string
|
||||||
groupID sql.NullInt64
|
groupID sql.NullInt64
|
||||||
subscriptionID sql.NullInt64
|
subscriptionID sql.NullInt64
|
||||||
inputTokens int
|
inputTokens int
|
||||||
outputTokens int
|
outputTokens int
|
||||||
cacheCreationTokens int
|
cacheCreationTokens int
|
||||||
cacheReadTokens int
|
cacheReadTokens int
|
||||||
cacheCreation5m int
|
cacheCreation5m int
|
||||||
cacheCreation1h int
|
cacheCreation1h int
|
||||||
inputCost float64
|
inputCost float64
|
||||||
outputCost float64
|
outputCost float64
|
||||||
cacheCreationCost float64
|
cacheCreationCost float64
|
||||||
cacheReadCost float64
|
cacheReadCost float64
|
||||||
totalCost float64
|
totalCost float64
|
||||||
actualCost float64
|
actualCost float64
|
||||||
rateMultiplier float64
|
rateMultiplier float64
|
||||||
billingType int16
|
accountRateMultiplier sql.NullFloat64
|
||||||
stream bool
|
billingType int16
|
||||||
durationMs sql.NullInt64
|
stream bool
|
||||||
firstTokenMs sql.NullInt64
|
durationMs sql.NullInt64
|
||||||
userAgent sql.NullString
|
firstTokenMs sql.NullInt64
|
||||||
ipAddress sql.NullString
|
userAgent sql.NullString
|
||||||
imageCount int
|
ipAddress sql.NullString
|
||||||
imageSize sql.NullString
|
imageCount int
|
||||||
createdAt time.Time
|
imageSize sql.NullString
|
||||||
|
createdAt time.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := scanner.Scan(
|
if err := scanner.Scan(
|
||||||
@@ -2048,6 +2083,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
|
|||||||
&totalCost,
|
&totalCost,
|
||||||
&actualCost,
|
&actualCost,
|
||||||
&rateMultiplier,
|
&rateMultiplier,
|
||||||
|
&accountRateMultiplier,
|
||||||
&billingType,
|
&billingType,
|
||||||
&stream,
|
&stream,
|
||||||
&durationMs,
|
&durationMs,
|
||||||
@@ -2080,6 +2116,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
|
|||||||
TotalCost: totalCost,
|
TotalCost: totalCost,
|
||||||
ActualCost: actualCost,
|
ActualCost: actualCost,
|
||||||
RateMultiplier: rateMultiplier,
|
RateMultiplier: rateMultiplier,
|
||||||
|
AccountRateMultiplier: nullFloat64Ptr(accountRateMultiplier),
|
||||||
BillingType: int8(billingType),
|
BillingType: int8(billingType),
|
||||||
Stream: stream,
|
Stream: stream,
|
||||||
ImageCount: imageCount,
|
ImageCount: imageCount,
|
||||||
@@ -2186,6 +2223,14 @@ func nullInt(v *int) sql.NullInt64 {
|
|||||||
return sql.NullInt64{Int64: int64(*v), Valid: true}
|
return sql.NullInt64{Int64: int64(*v), Valid: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nullFloat64Ptr(v sql.NullFloat64) *float64 {
|
||||||
|
if !v.Valid {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := v.Float64
|
||||||
|
return &out
|
||||||
|
}
|
||||||
|
|
||||||
func nullString(v *string) sql.NullString {
|
func nullString(v *string) sql.NullString {
|
||||||
if v == nil || *v == "" {
|
if v == nil || *v == "" {
|
||||||
return sql.NullString{}
|
return sql.NullString{}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
dbent "github.com/Wei-Shaw/sub2api/ent"
|
dbent "github.com/Wei-Shaw/sub2api/ent"
|
||||||
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
|
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
|
||||||
|
"github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
|
||||||
"github.com/Wei-Shaw/sub2api/internal/pkg/usagestats"
|
"github.com/Wei-Shaw/sub2api/internal/pkg/usagestats"
|
||||||
"github.com/Wei-Shaw/sub2api/internal/service"
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
@@ -95,6 +96,34 @@ func (s *UsageLogRepoSuite) TestGetByID_NotFound() {
|
|||||||
s.Require().Error(err, "expected error for non-existent ID")
|
s.Require().Error(err, "expected error for non-existent ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *UsageLogRepoSuite) TestGetByID_ReturnsAccountRateMultiplier() {
|
||||||
|
user := mustCreateUser(s.T(), s.client, &service.User{Email: "getbyid-mult@test.com"})
|
||||||
|
apiKey := mustCreateApiKey(s.T(), s.client, &service.APIKey{UserID: user.ID, Key: "sk-getbyid-mult", Name: "k"})
|
||||||
|
account := mustCreateAccount(s.T(), s.client, &service.Account{Name: "acc-getbyid-mult"})
|
||||||
|
|
||||||
|
m := 0.5
|
||||||
|
log := &service.UsageLog{
|
||||||
|
UserID: user.ID,
|
||||||
|
APIKeyID: apiKey.ID,
|
||||||
|
AccountID: account.ID,
|
||||||
|
RequestID: uuid.New().String(),
|
||||||
|
Model: "claude-3",
|
||||||
|
InputTokens: 10,
|
||||||
|
OutputTokens: 20,
|
||||||
|
TotalCost: 1.0,
|
||||||
|
ActualCost: 2.0,
|
||||||
|
AccountRateMultiplier: &m,
|
||||||
|
CreatedAt: timezone.Today().Add(2 * time.Hour),
|
||||||
|
}
|
||||||
|
_, err := s.repo.Create(s.ctx, log)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
got, err := s.repo.GetByID(s.ctx, log.ID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(got.AccountRateMultiplier)
|
||||||
|
s.Require().InEpsilon(0.5, *got.AccountRateMultiplier, 0.0001)
|
||||||
|
}
|
||||||
|
|
||||||
// --- Delete ---
|
// --- Delete ---
|
||||||
|
|
||||||
func (s *UsageLogRepoSuite) TestDelete() {
|
func (s *UsageLogRepoSuite) TestDelete() {
|
||||||
@@ -403,12 +432,49 @@ func (s *UsageLogRepoSuite) TestGetAccountTodayStats() {
|
|||||||
apiKey := mustCreateApiKey(s.T(), s.client, &service.APIKey{UserID: user.ID, Key: "sk-acctoday", Name: "k"})
|
apiKey := mustCreateApiKey(s.T(), s.client, &service.APIKey{UserID: user.ID, Key: "sk-acctoday", Name: "k"})
|
||||||
account := mustCreateAccount(s.T(), s.client, &service.Account{Name: "acc-today"})
|
account := mustCreateAccount(s.T(), s.client, &service.Account{Name: "acc-today"})
|
||||||
|
|
||||||
s.createUsageLog(user, apiKey, account, 10, 20, 0.5, time.Now())
|
createdAt := timezone.Today().Add(1 * time.Hour)
|
||||||
|
|
||||||
|
m1 := 1.5
|
||||||
|
m2 := 0.0
|
||||||
|
_, err := s.repo.Create(s.ctx, &service.UsageLog{
|
||||||
|
UserID: user.ID,
|
||||||
|
APIKeyID: apiKey.ID,
|
||||||
|
AccountID: account.ID,
|
||||||
|
RequestID: uuid.New().String(),
|
||||||
|
Model: "claude-3",
|
||||||
|
InputTokens: 10,
|
||||||
|
OutputTokens: 20,
|
||||||
|
TotalCost: 1.0,
|
||||||
|
ActualCost: 2.0,
|
||||||
|
AccountRateMultiplier: &m1,
|
||||||
|
CreatedAt: createdAt,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
_, err = s.repo.Create(s.ctx, &service.UsageLog{
|
||||||
|
UserID: user.ID,
|
||||||
|
APIKeyID: apiKey.ID,
|
||||||
|
AccountID: account.ID,
|
||||||
|
RequestID: uuid.New().String(),
|
||||||
|
Model: "claude-3",
|
||||||
|
InputTokens: 5,
|
||||||
|
OutputTokens: 5,
|
||||||
|
TotalCost: 0.5,
|
||||||
|
ActualCost: 1.0,
|
||||||
|
AccountRateMultiplier: &m2,
|
||||||
|
CreatedAt: createdAt,
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
stats, err := s.repo.GetAccountTodayStats(s.ctx, account.ID)
|
stats, err := s.repo.GetAccountTodayStats(s.ctx, account.ID)
|
||||||
s.Require().NoError(err, "GetAccountTodayStats")
|
s.Require().NoError(err, "GetAccountTodayStats")
|
||||||
s.Require().Equal(int64(1), stats.Requests)
|
s.Require().Equal(int64(2), stats.Requests)
|
||||||
s.Require().Equal(int64(30), stats.Tokens)
|
s.Require().Equal(int64(40), stats.Tokens)
|
||||||
|
// account cost = SUM(total_cost * account_rate_multiplier)
|
||||||
|
s.Require().InEpsilon(1.5, stats.Cost, 0.0001)
|
||||||
|
// standard cost = SUM(total_cost)
|
||||||
|
s.Require().InEpsilon(1.5, stats.StandardCost, 0.0001)
|
||||||
|
// user cost = SUM(actual_cost)
|
||||||
|
s.Require().InEpsilon(3.0, stats.UserCost, 0.0001)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UsageLogRepoSuite) TestDashboardAggregationConsistency() {
|
func (s *UsageLogRepoSuite) TestDashboardAggregationConsistency() {
|
||||||
@@ -416,8 +482,8 @@ func (s *UsageLogRepoSuite) TestDashboardAggregationConsistency() {
|
|||||||
// 使用固定的时间偏移确保 hour1 和 hour2 在同一天且都在过去
|
// 使用固定的时间偏移确保 hour1 和 hour2 在同一天且都在过去
|
||||||
// 选择当天 02:00 和 03:00 作为测试时间点(基于 now 的日期)
|
// 选择当天 02:00 和 03:00 作为测试时间点(基于 now 的日期)
|
||||||
dayStart := truncateToDayUTC(now)
|
dayStart := truncateToDayUTC(now)
|
||||||
hour1 := dayStart.Add(2 * time.Hour) // 当天 02:00
|
hour1 := dayStart.Add(2 * time.Hour) // 当天 02:00
|
||||||
hour2 := dayStart.Add(3 * time.Hour) // 当天 03:00
|
hour2 := dayStart.Add(3 * time.Hour) // 当天 03:00
|
||||||
// 如果当前时间早于 hour2,则使用昨天的时间
|
// 如果当前时间早于 hour2,则使用昨天的时间
|
||||||
if now.Before(hour2.Add(time.Hour)) {
|
if now.Before(hour2.Add(time.Hour)) {
|
||||||
dayStart = dayStart.Add(-24 * time.Hour)
|
dayStart = dayStart.Add(-24 * time.Hour)
|
||||||
|
|||||||
@@ -239,9 +239,10 @@ func TestAPIContracts(t *testing.T) {
|
|||||||
"cache_creation_cost": 0,
|
"cache_creation_cost": 0,
|
||||||
"cache_read_cost": 0,
|
"cache_read_cost": 0,
|
||||||
"total_cost": 0.5,
|
"total_cost": 0.5,
|
||||||
"actual_cost": 0.5,
|
"actual_cost": 0.5,
|
||||||
"rate_multiplier": 1,
|
"rate_multiplier": 1,
|
||||||
"billing_type": 0,
|
"account_rate_multiplier": null,
|
||||||
|
"billing_type": 0,
|
||||||
"stream": true,
|
"stream": true,
|
||||||
"duration_ms": 100,
|
"duration_ms": 100,
|
||||||
"first_token_ms": 50,
|
"first_token_ms": 50,
|
||||||
@@ -262,11 +263,11 @@ func TestAPIContracts(t *testing.T) {
|
|||||||
name: "GET /api/v1/admin/settings",
|
name: "GET /api/v1/admin/settings",
|
||||||
setup: func(t *testing.T, deps *contractDeps) {
|
setup: func(t *testing.T, deps *contractDeps) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
deps.settingRepo.SetAll(map[string]string{
|
deps.settingRepo.SetAll(map[string]string{
|
||||||
service.SettingKeyRegistrationEnabled: "true",
|
service.SettingKeyRegistrationEnabled: "true",
|
||||||
service.SettingKeyEmailVerifyEnabled: "false",
|
service.SettingKeyEmailVerifyEnabled: "false",
|
||||||
|
|
||||||
service.SettingKeySMTPHost: "smtp.example.com",
|
service.SettingKeySMTPHost: "smtp.example.com",
|
||||||
service.SettingKeySMTPPort: "587",
|
service.SettingKeySMTPPort: "587",
|
||||||
service.SettingKeySMTPUsername: "user",
|
service.SettingKeySMTPUsername: "user",
|
||||||
service.SettingKeySMTPPassword: "secret",
|
service.SettingKeySMTPPassword: "secret",
|
||||||
@@ -285,15 +286,15 @@ func TestAPIContracts(t *testing.T) {
|
|||||||
service.SettingKeyContactInfo: "support",
|
service.SettingKeyContactInfo: "support",
|
||||||
service.SettingKeyDocURL: "https://docs.example.com",
|
service.SettingKeyDocURL: "https://docs.example.com",
|
||||||
|
|
||||||
service.SettingKeyDefaultConcurrency: "5",
|
service.SettingKeyDefaultConcurrency: "5",
|
||||||
service.SettingKeyDefaultBalance: "1.25",
|
service.SettingKeyDefaultBalance: "1.25",
|
||||||
|
|
||||||
service.SettingKeyOpsMonitoringEnabled: "false",
|
service.SettingKeyOpsMonitoringEnabled: "false",
|
||||||
service.SettingKeyOpsRealtimeMonitoringEnabled: "true",
|
service.SettingKeyOpsRealtimeMonitoringEnabled: "true",
|
||||||
service.SettingKeyOpsQueryModeDefault: "auto",
|
service.SettingKeyOpsQueryModeDefault: "auto",
|
||||||
service.SettingKeyOpsMetricsIntervalSeconds: "60",
|
service.SettingKeyOpsMetricsIntervalSeconds: "60",
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
method: http.MethodGet,
|
method: http.MethodGet,
|
||||||
path: "/api/v1/admin/settings",
|
path: "/api/v1/admin/settings",
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
|
|||||||
@@ -9,16 +9,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Account struct {
|
type Account struct {
|
||||||
ID int64
|
ID int64
|
||||||
Name string
|
Name string
|
||||||
Notes *string
|
Notes *string
|
||||||
Platform string
|
Platform string
|
||||||
Type string
|
Type string
|
||||||
Credentials map[string]any
|
Credentials map[string]any
|
||||||
Extra map[string]any
|
Extra map[string]any
|
||||||
ProxyID *int64
|
ProxyID *int64
|
||||||
Concurrency int
|
Concurrency int
|
||||||
Priority int
|
Priority int
|
||||||
|
// RateMultiplier 账号计费倍率(>=0,允许 0 表示该账号计费为 0)。
|
||||||
|
// 使用指针用于兼容旧版本调度缓存(Redis)中缺字段的情况:nil 表示按 1.0 处理。
|
||||||
|
RateMultiplier *float64
|
||||||
Status string
|
Status string
|
||||||
ErrorMessage string
|
ErrorMessage string
|
||||||
LastUsedAt *time.Time
|
LastUsedAt *time.Time
|
||||||
@@ -57,6 +60,20 @@ func (a *Account) IsActive() bool {
|
|||||||
return a.Status == StatusActive
|
return a.Status == StatusActive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BillingRateMultiplier 返回账号计费倍率。
|
||||||
|
// - nil 表示未配置/旧缓存缺字段,按 1.0 处理
|
||||||
|
// - 允许 0,表示该账号计费为 0
|
||||||
|
// - 负数属于非法数据,出于安全考虑按 1.0 处理
|
||||||
|
func (a *Account) BillingRateMultiplier() float64 {
|
||||||
|
if a == nil || a.RateMultiplier == nil {
|
||||||
|
return 1.0
|
||||||
|
}
|
||||||
|
if *a.RateMultiplier < 0 {
|
||||||
|
return 1.0
|
||||||
|
}
|
||||||
|
return *a.RateMultiplier
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Account) IsSchedulable() bool {
|
func (a *Account) IsSchedulable() bool {
|
||||||
if !a.IsActive() || !a.Schedulable {
|
if !a.IsActive() || !a.Schedulable {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccount_BillingRateMultiplier_DefaultsToOneWhenNil(t *testing.T) {
|
||||||
|
var a Account
|
||||||
|
require.NoError(t, json.Unmarshal([]byte(`{"id":1,"name":"acc","status":"active"}`), &a))
|
||||||
|
require.Nil(t, a.RateMultiplier)
|
||||||
|
require.Equal(t, 1.0, a.BillingRateMultiplier())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccount_BillingRateMultiplier_AllowsZero(t *testing.T) {
|
||||||
|
v := 0.0
|
||||||
|
a := Account{RateMultiplier: &v}
|
||||||
|
require.Equal(t, 0.0, a.BillingRateMultiplier())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccount_BillingRateMultiplier_NegativeFallsBackToOne(t *testing.T) {
|
||||||
|
v := -1.0
|
||||||
|
a := Account{RateMultiplier: &v}
|
||||||
|
require.Equal(t, 1.0, a.BillingRateMultiplier())
|
||||||
|
}
|
||||||
@@ -63,14 +63,15 @@ type AccountRepository interface {
|
|||||||
// AccountBulkUpdate describes the fields that can be updated in a bulk operation.
|
// AccountBulkUpdate describes the fields that can be updated in a bulk operation.
|
||||||
// Nil pointers mean "do not change".
|
// Nil pointers mean "do not change".
|
||||||
type AccountBulkUpdate struct {
|
type AccountBulkUpdate struct {
|
||||||
Name *string
|
Name *string
|
||||||
ProxyID *int64
|
ProxyID *int64
|
||||||
Concurrency *int
|
Concurrency *int
|
||||||
Priority *int
|
Priority *int
|
||||||
Status *string
|
RateMultiplier *float64
|
||||||
Schedulable *bool
|
Status *string
|
||||||
Credentials map[string]any
|
Schedulable *bool
|
||||||
Extra map[string]any
|
Credentials map[string]any
|
||||||
|
Extra map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAccountRequest 创建账号请求
|
// CreateAccountRequest 创建账号请求
|
||||||
|
|||||||
@@ -96,10 +96,16 @@ func NewUsageCache() *UsageCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WindowStats 窗口期统计
|
// WindowStats 窗口期统计
|
||||||
|
//
|
||||||
|
// cost: 账号口径费用(total_cost * account_rate_multiplier)
|
||||||
|
// standard_cost: 标准费用(total_cost,不含倍率)
|
||||||
|
// user_cost: 用户/API Key 口径费用(actual_cost,受分组倍率影响)
|
||||||
type WindowStats struct {
|
type WindowStats struct {
|
||||||
Requests int64 `json:"requests"`
|
Requests int64 `json:"requests"`
|
||||||
Tokens int64 `json:"tokens"`
|
Tokens int64 `json:"tokens"`
|
||||||
Cost float64 `json:"cost"`
|
Cost float64 `json:"cost"`
|
||||||
|
StandardCost float64 `json:"standard_cost"`
|
||||||
|
UserCost float64 `json:"user_cost"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsageProgress 使用量进度
|
// UsageProgress 使用量进度
|
||||||
@@ -377,9 +383,11 @@ func (s *AccountUsageService) addWindowStats(ctx context.Context, account *Accou
|
|||||||
}
|
}
|
||||||
|
|
||||||
windowStats = &WindowStats{
|
windowStats = &WindowStats{
|
||||||
Requests: stats.Requests,
|
Requests: stats.Requests,
|
||||||
Tokens: stats.Tokens,
|
Tokens: stats.Tokens,
|
||||||
Cost: stats.Cost,
|
Cost: stats.Cost,
|
||||||
|
StandardCost: stats.StandardCost,
|
||||||
|
UserCost: stats.UserCost,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 缓存窗口统计(1 分钟)
|
// 缓存窗口统计(1 分钟)
|
||||||
@@ -403,9 +411,11 @@ func (s *AccountUsageService) GetTodayStats(ctx context.Context, accountID int64
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &WindowStats{
|
return &WindowStats{
|
||||||
Requests: stats.Requests,
|
Requests: stats.Requests,
|
||||||
Tokens: stats.Tokens,
|
Tokens: stats.Tokens,
|
||||||
Cost: stats.Cost,
|
Cost: stats.Cost,
|
||||||
|
StandardCost: stats.StandardCost,
|
||||||
|
UserCost: stats.UserCost,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ type CreateAccountInput struct {
|
|||||||
ProxyID *int64
|
ProxyID *int64
|
||||||
Concurrency int
|
Concurrency int
|
||||||
Priority int
|
Priority int
|
||||||
|
RateMultiplier *float64 // 账号计费倍率(>=0,允许 0)
|
||||||
GroupIDs []int64
|
GroupIDs []int64
|
||||||
ExpiresAt *int64
|
ExpiresAt *int64
|
||||||
AutoPauseOnExpired *bool
|
AutoPauseOnExpired *bool
|
||||||
@@ -151,8 +152,9 @@ type UpdateAccountInput struct {
|
|||||||
Credentials map[string]any
|
Credentials map[string]any
|
||||||
Extra map[string]any
|
Extra map[string]any
|
||||||
ProxyID *int64
|
ProxyID *int64
|
||||||
Concurrency *int // 使用指针区分"未提供"和"设置为0"
|
Concurrency *int // 使用指针区分"未提供"和"设置为0"
|
||||||
Priority *int // 使用指针区分"未提供"和"设置为0"
|
Priority *int // 使用指针区分"未提供"和"设置为0"
|
||||||
|
RateMultiplier *float64 // 账号计费倍率(>=0,允许 0)
|
||||||
Status string
|
Status string
|
||||||
GroupIDs *[]int64
|
GroupIDs *[]int64
|
||||||
ExpiresAt *int64
|
ExpiresAt *int64
|
||||||
@@ -162,16 +164,17 @@ type UpdateAccountInput struct {
|
|||||||
|
|
||||||
// BulkUpdateAccountsInput describes the payload for bulk updating accounts.
|
// BulkUpdateAccountsInput describes the payload for bulk updating accounts.
|
||||||
type BulkUpdateAccountsInput struct {
|
type BulkUpdateAccountsInput struct {
|
||||||
AccountIDs []int64
|
AccountIDs []int64
|
||||||
Name string
|
Name string
|
||||||
ProxyID *int64
|
ProxyID *int64
|
||||||
Concurrency *int
|
Concurrency *int
|
||||||
Priority *int
|
Priority *int
|
||||||
Status string
|
RateMultiplier *float64 // 账号计费倍率(>=0,允许 0)
|
||||||
Schedulable *bool
|
Status string
|
||||||
GroupIDs *[]int64
|
Schedulable *bool
|
||||||
Credentials map[string]any
|
GroupIDs *[]int64
|
||||||
Extra map[string]any
|
Credentials map[string]any
|
||||||
|
Extra map[string]any
|
||||||
// SkipMixedChannelCheck skips the mixed channel risk check when binding groups.
|
// SkipMixedChannelCheck skips the mixed channel risk check when binding groups.
|
||||||
// This should only be set when the caller has explicitly confirmed the risk.
|
// This should only be set when the caller has explicitly confirmed the risk.
|
||||||
SkipMixedChannelCheck bool
|
SkipMixedChannelCheck bool
|
||||||
@@ -817,6 +820,12 @@ func (s *adminServiceImpl) CreateAccount(ctx context.Context, input *CreateAccou
|
|||||||
} else {
|
} else {
|
||||||
account.AutoPauseOnExpired = true
|
account.AutoPauseOnExpired = true
|
||||||
}
|
}
|
||||||
|
if input.RateMultiplier != nil {
|
||||||
|
if *input.RateMultiplier < 0 {
|
||||||
|
return nil, errors.New("rate_multiplier must be >= 0")
|
||||||
|
}
|
||||||
|
account.RateMultiplier = input.RateMultiplier
|
||||||
|
}
|
||||||
if err := s.accountRepo.Create(ctx, account); err != nil {
|
if err := s.accountRepo.Create(ctx, account); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -869,6 +878,12 @@ func (s *adminServiceImpl) UpdateAccount(ctx context.Context, id int64, input *U
|
|||||||
if input.Priority != nil {
|
if input.Priority != nil {
|
||||||
account.Priority = *input.Priority
|
account.Priority = *input.Priority
|
||||||
}
|
}
|
||||||
|
if input.RateMultiplier != nil {
|
||||||
|
if *input.RateMultiplier < 0 {
|
||||||
|
return nil, errors.New("rate_multiplier must be >= 0")
|
||||||
|
}
|
||||||
|
account.RateMultiplier = input.RateMultiplier
|
||||||
|
}
|
||||||
if input.Status != "" {
|
if input.Status != "" {
|
||||||
account.Status = input.Status
|
account.Status = input.Status
|
||||||
}
|
}
|
||||||
@@ -942,6 +957,12 @@ func (s *adminServiceImpl) BulkUpdateAccounts(ctx context.Context, input *BulkUp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.RateMultiplier != nil {
|
||||||
|
if *input.RateMultiplier < 0 {
|
||||||
|
return nil, errors.New("rate_multiplier must be >= 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare bulk updates for columns and JSONB fields.
|
// Prepare bulk updates for columns and JSONB fields.
|
||||||
repoUpdates := AccountBulkUpdate{
|
repoUpdates := AccountBulkUpdate{
|
||||||
Credentials: input.Credentials,
|
Credentials: input.Credentials,
|
||||||
@@ -959,6 +980,9 @@ func (s *adminServiceImpl) BulkUpdateAccounts(ctx context.Context, input *BulkUp
|
|||||||
if input.Priority != nil {
|
if input.Priority != nil {
|
||||||
repoUpdates.Priority = input.Priority
|
repoUpdates.Priority = input.Priority
|
||||||
}
|
}
|
||||||
|
if input.RateMultiplier != nil {
|
||||||
|
repoUpdates.RateMultiplier = input.RateMultiplier
|
||||||
|
}
|
||||||
if input.Status != "" {
|
if input.Status != "" {
|
||||||
repoUpdates.Status = &input.Status
|
repoUpdates.Status = &input.Status
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2618,30 +2618,32 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
|
|||||||
if result.ImageSize != "" {
|
if result.ImageSize != "" {
|
||||||
imageSize = &result.ImageSize
|
imageSize = &result.ImageSize
|
||||||
}
|
}
|
||||||
|
accountRateMultiplier := account.BillingRateMultiplier()
|
||||||
usageLog := &UsageLog{
|
usageLog := &UsageLog{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
APIKeyID: apiKey.ID,
|
APIKeyID: apiKey.ID,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
RequestID: result.RequestID,
|
RequestID: result.RequestID,
|
||||||
Model: result.Model,
|
Model: result.Model,
|
||||||
InputTokens: result.Usage.InputTokens,
|
InputTokens: result.Usage.InputTokens,
|
||||||
OutputTokens: result.Usage.OutputTokens,
|
OutputTokens: result.Usage.OutputTokens,
|
||||||
CacheCreationTokens: result.Usage.CacheCreationInputTokens,
|
CacheCreationTokens: result.Usage.CacheCreationInputTokens,
|
||||||
CacheReadTokens: result.Usage.CacheReadInputTokens,
|
CacheReadTokens: result.Usage.CacheReadInputTokens,
|
||||||
InputCost: cost.InputCost,
|
InputCost: cost.InputCost,
|
||||||
OutputCost: cost.OutputCost,
|
OutputCost: cost.OutputCost,
|
||||||
CacheCreationCost: cost.CacheCreationCost,
|
CacheCreationCost: cost.CacheCreationCost,
|
||||||
CacheReadCost: cost.CacheReadCost,
|
CacheReadCost: cost.CacheReadCost,
|
||||||
TotalCost: cost.TotalCost,
|
TotalCost: cost.TotalCost,
|
||||||
ActualCost: cost.ActualCost,
|
ActualCost: cost.ActualCost,
|
||||||
RateMultiplier: multiplier,
|
RateMultiplier: multiplier,
|
||||||
BillingType: billingType,
|
AccountRateMultiplier: &accountRateMultiplier,
|
||||||
Stream: result.Stream,
|
BillingType: billingType,
|
||||||
DurationMs: &durationMs,
|
Stream: result.Stream,
|
||||||
FirstTokenMs: result.FirstTokenMs,
|
DurationMs: &durationMs,
|
||||||
ImageCount: result.ImageCount,
|
FirstTokenMs: result.FirstTokenMs,
|
||||||
ImageSize: imageSize,
|
ImageCount: result.ImageCount,
|
||||||
CreatedAt: time.Now(),
|
ImageSize: imageSize,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加 UserAgent
|
// 添加 UserAgent
|
||||||
|
|||||||
@@ -1432,28 +1432,30 @@ func (s *OpenAIGatewayService) RecordUsage(ctx context.Context, input *OpenAIRec
|
|||||||
|
|
||||||
// Create usage log
|
// Create usage log
|
||||||
durationMs := int(result.Duration.Milliseconds())
|
durationMs := int(result.Duration.Milliseconds())
|
||||||
|
accountRateMultiplier := account.BillingRateMultiplier()
|
||||||
usageLog := &UsageLog{
|
usageLog := &UsageLog{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
APIKeyID: apiKey.ID,
|
APIKeyID: apiKey.ID,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
RequestID: result.RequestID,
|
RequestID: result.RequestID,
|
||||||
Model: result.Model,
|
Model: result.Model,
|
||||||
InputTokens: actualInputTokens,
|
InputTokens: actualInputTokens,
|
||||||
OutputTokens: result.Usage.OutputTokens,
|
OutputTokens: result.Usage.OutputTokens,
|
||||||
CacheCreationTokens: result.Usage.CacheCreationInputTokens,
|
CacheCreationTokens: result.Usage.CacheCreationInputTokens,
|
||||||
CacheReadTokens: result.Usage.CacheReadInputTokens,
|
CacheReadTokens: result.Usage.CacheReadInputTokens,
|
||||||
InputCost: cost.InputCost,
|
InputCost: cost.InputCost,
|
||||||
OutputCost: cost.OutputCost,
|
OutputCost: cost.OutputCost,
|
||||||
CacheCreationCost: cost.CacheCreationCost,
|
CacheCreationCost: cost.CacheCreationCost,
|
||||||
CacheReadCost: cost.CacheReadCost,
|
CacheReadCost: cost.CacheReadCost,
|
||||||
TotalCost: cost.TotalCost,
|
TotalCost: cost.TotalCost,
|
||||||
ActualCost: cost.ActualCost,
|
ActualCost: cost.ActualCost,
|
||||||
RateMultiplier: multiplier,
|
RateMultiplier: multiplier,
|
||||||
BillingType: billingType,
|
AccountRateMultiplier: &accountRateMultiplier,
|
||||||
Stream: result.Stream,
|
BillingType: billingType,
|
||||||
DurationMs: &durationMs,
|
Stream: result.Stream,
|
||||||
FirstTokenMs: result.FirstTokenMs,
|
DurationMs: &durationMs,
|
||||||
CreatedAt: time.Now(),
|
FirstTokenMs: result.FirstTokenMs,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加 UserAgent
|
// 添加 UserAgent
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ type UsageLog struct {
|
|||||||
TotalCost float64
|
TotalCost float64
|
||||||
ActualCost float64
|
ActualCost float64
|
||||||
RateMultiplier float64
|
RateMultiplier float64
|
||||||
|
// AccountRateMultiplier 账号计费倍率快照(nil 表示历史数据,按 1.0 处理)
|
||||||
|
AccountRateMultiplier *float64
|
||||||
|
|
||||||
BillingType int8
|
BillingType int8
|
||||||
Stream bool
|
Stream bool
|
||||||
|
|||||||
14
backend/migrations/037_add_account_rate_multiplier.sql
Normal file
14
backend/migrations/037_add_account_rate_multiplier.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
-- Add account billing rate multiplier and per-usage snapshot.
|
||||||
|
--
|
||||||
|
-- accounts.rate_multiplier: 账号计费倍率(>=0,允许 0 表示该账号计费为 0)。
|
||||||
|
-- usage_logs.account_rate_multiplier: 每条 usage log 的账号倍率快照,用于实现
|
||||||
|
-- “倍率调整仅影响之后请求”,并支持同一天分段倍率加权统计。
|
||||||
|
--
|
||||||
|
-- 注意:usage_logs.account_rate_multiplier 不做回填、不设置 NOT NULL。
|
||||||
|
-- 老数据为 NULL 时,统计口径按 1.0 处理(COALESCE)。
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS accounts
|
||||||
|
ADD COLUMN IF NOT EXISTS rate_multiplier DECIMAL(10,4) NOT NULL DEFAULT 1.0;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS usage_logs
|
||||||
|
ADD COLUMN IF NOT EXISTS account_rate_multiplier DECIMAL(10,4);
|
||||||
@@ -16,6 +16,7 @@ export interface AdminUsageStatsResponse {
|
|||||||
total_tokens: number
|
total_tokens: number
|
||||||
total_cost: number
|
total_cost: number
|
||||||
total_actual_cost: number
|
total_actual_cost: number
|
||||||
|
total_account_cost?: number
|
||||||
average_duration_ms: number
|
average_duration_ms: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,11 +73,12 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
{{ t('admin.accounts.stats.accumulatedCost') }}
|
{{ t('admin.accounts.stats.accumulatedCost') }}
|
||||||
<span class="text-gray-400 dark:text-gray-500"
|
<span class="text-gray-400 dark:text-gray-500">
|
||||||
>({{ t('admin.accounts.stats.standardCost') }}: ${{
|
({{ t('usage.userBilled') }}: ${{ formatCost(stats.summary.total_user_cost) }} ·
|
||||||
|
{{ t('admin.accounts.stats.standardCost') }}: ${{
|
||||||
formatCost(stats.summary.total_standard_cost)
|
formatCost(stats.summary.total_standard_cost)
|
||||||
}})</span
|
}})
|
||||||
>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -121,12 +122,15 @@
|
|||||||
<p class="text-2xl font-bold text-gray-900 dark:text-white">
|
<p class="text-2xl font-bold text-gray-900 dark:text-white">
|
||||||
${{ formatCost(stats.summary.avg_daily_cost) }}
|
${{ formatCost(stats.summary.avg_daily_cost) }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
{{
|
{{
|
||||||
t('admin.accounts.stats.basedOnActualDays', {
|
t('admin.accounts.stats.basedOnActualDays', {
|
||||||
days: stats.summary.actual_days_used
|
days: stats.summary.actual_days_used
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
<span class="text-gray-400 dark:text-gray-500">
|
||||||
|
({{ t('usage.userBilled') }}: ${{ formatCost(stats.summary.avg_daily_user_cost) }})
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -189,13 +193,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.accountBilled') }}</span>
|
||||||
t('admin.accounts.stats.cost')
|
|
||||||
}}</span>
|
|
||||||
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
>${{ formatCost(stats.summary.today?.cost || 0) }}</span
|
>${{ formatCost(stats.summary.today?.cost || 0) }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.userBilled') }}</span>
|
||||||
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
|
>${{ formatCost(stats.summary.today?.user_cost || 0) }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
||||||
t('admin.accounts.stats.requests')
|
t('admin.accounts.stats.requests')
|
||||||
@@ -240,13 +248,17 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.accountBilled') }}</span>
|
||||||
t('admin.accounts.stats.cost')
|
|
||||||
}}</span>
|
|
||||||
<span class="text-sm font-semibold text-orange-600 dark:text-orange-400"
|
<span class="text-sm font-semibold text-orange-600 dark:text-orange-400"
|
||||||
>${{ formatCost(stats.summary.highest_cost_day?.cost || 0) }}</span
|
>${{ formatCost(stats.summary.highest_cost_day?.cost || 0) }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.userBilled') }}</span>
|
||||||
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
|
>${{ formatCost(stats.summary.highest_cost_day?.user_cost || 0) }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
||||||
t('admin.accounts.stats.requests')
|
t('admin.accounts.stats.requests')
|
||||||
@@ -291,13 +303,17 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.accountBilled') }}</span>
|
||||||
t('admin.accounts.stats.cost')
|
|
||||||
}}</span>
|
|
||||||
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
>${{ formatCost(stats.summary.highest_request_day?.cost || 0) }}</span
|
>${{ formatCost(stats.summary.highest_request_day?.cost || 0) }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.userBilled') }}</span>
|
||||||
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
|
>${{ formatCost(stats.summary.highest_request_day?.user_cost || 0) }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -397,13 +413,17 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.accountBilled') }}</span>
|
||||||
t('admin.accounts.stats.todayCost')
|
|
||||||
}}</span>
|
|
||||||
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
>${{ formatCost(stats.summary.today?.cost || 0) }}</span
|
>${{ formatCost(stats.summary.today?.cost || 0) }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.userBilled') }}</span>
|
||||||
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
|
>${{ formatCost(stats.summary.today?.user_cost || 0) }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -517,14 +537,24 @@ const trendChartData = computed(() => {
|
|||||||
labels: stats.value.history.map((h) => h.label),
|
labels: stats.value.history.map((h) => h.label),
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: t('admin.accounts.stats.cost') + ' (USD)',
|
label: t('usage.accountBilled') + ' (USD)',
|
||||||
data: stats.value.history.map((h) => h.cost),
|
data: stats.value.history.map((h) => h.actual_cost),
|
||||||
borderColor: '#3b82f6',
|
borderColor: '#3b82f6',
|
||||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||||||
fill: true,
|
fill: true,
|
||||||
tension: 0.3,
|
tension: 0.3,
|
||||||
yAxisID: 'y'
|
yAxisID: 'y'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t('usage.userBilled') + ' (USD)',
|
||||||
|
data: stats.value.history.map((h) => h.user_cost),
|
||||||
|
borderColor: '#10b981',
|
||||||
|
backgroundColor: 'rgba(16, 185, 129, 0.08)',
|
||||||
|
fill: false,
|
||||||
|
tension: 0.3,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
yAxisID: 'y'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t('admin.accounts.stats.requests'),
|
label: t('admin.accounts.stats.requests'),
|
||||||
data: stats.value.history.map((h) => h.requests),
|
data: stats.value.history.map((h) => h.requests),
|
||||||
@@ -602,7 +632,7 @@ const lineChartOptions = computed(() => ({
|
|||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: t('admin.accounts.stats.cost') + ' (USD)',
|
text: t('usage.accountBilled') + ' (USD)',
|
||||||
color: '#3b82f6',
|
color: '#3b82f6',
|
||||||
font: {
|
font: {
|
||||||
size: 11
|
size: 11
|
||||||
|
|||||||
@@ -32,15 +32,20 @@
|
|||||||
formatTokens(stats.tokens)
|
formatTokens(stats.tokens)
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- Cost -->
|
<!-- Cost (Account) -->
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<span class="text-gray-500 dark:text-gray-400"
|
<span class="text-gray-500 dark:text-gray-400">{{ t('usage.accountBilled') }}:</span>
|
||||||
>{{ t('admin.accounts.stats.cost') }}:</span
|
|
||||||
>
|
|
||||||
<span class="font-medium text-emerald-600 dark:text-emerald-400">{{
|
<span class="font-medium text-emerald-600 dark:text-emerald-400">{{
|
||||||
formatCurrency(stats.cost)
|
formatCurrency(stats.cost)
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Cost (User/API Key) -->
|
||||||
|
<div v-if="stats.user_cost != null" class="flex items-center gap-1">
|
||||||
|
<span class="text-gray-500 dark:text-gray-400">{{ t('usage.userBilled') }}:</span>
|
||||||
|
<span class="font-medium text-gray-700 dark:text-gray-300">{{
|
||||||
|
formatCurrency(stats.user_cost)
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- No data -->
|
<!-- No data -->
|
||||||
|
|||||||
@@ -459,7 +459,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Concurrency & Priority -->
|
<!-- Concurrency & Priority -->
|
||||||
<div class="grid grid-cols-2 gap-4 border-t border-gray-200 pt-4 dark:border-dark-600">
|
<div class="grid grid-cols-2 gap-4 border-t border-gray-200 pt-4 dark:border-dark-600 lg:grid-cols-3">
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-3 flex items-center justify-between">
|
<div class="mb-3 flex items-center justify-between">
|
||||||
<label
|
<label
|
||||||
@@ -516,6 +516,36 @@
|
|||||||
aria-labelledby="bulk-edit-priority-label"
|
aria-labelledby="bulk-edit-priority-label"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="mb-3 flex items-center justify-between">
|
||||||
|
<label
|
||||||
|
id="bulk-edit-rate-multiplier-label"
|
||||||
|
class="input-label mb-0"
|
||||||
|
for="bulk-edit-rate-multiplier-enabled"
|
||||||
|
>
|
||||||
|
{{ t('admin.accounts.billingRateMultiplier') }}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
v-model="enableRateMultiplier"
|
||||||
|
id="bulk-edit-rate-multiplier-enabled"
|
||||||
|
type="checkbox"
|
||||||
|
aria-controls="bulk-edit-rate-multiplier"
|
||||||
|
class="rounded border-gray-300 text-primary-600 focus:ring-primary-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
v-model.number="rateMultiplier"
|
||||||
|
id="bulk-edit-rate-multiplier"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
:disabled="!enableRateMultiplier"
|
||||||
|
class="input"
|
||||||
|
:class="!enableRateMultiplier && 'cursor-not-allowed opacity-50'"
|
||||||
|
aria-labelledby="bulk-edit-rate-multiplier-label"
|
||||||
|
/>
|
||||||
|
<p class="input-hint">{{ t('admin.accounts.billingRateMultiplierHint') }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Status -->
|
<!-- Status -->
|
||||||
@@ -655,6 +685,7 @@ const enableInterceptWarmup = ref(false)
|
|||||||
const enableProxy = ref(false)
|
const enableProxy = ref(false)
|
||||||
const enableConcurrency = ref(false)
|
const enableConcurrency = ref(false)
|
||||||
const enablePriority = ref(false)
|
const enablePriority = ref(false)
|
||||||
|
const enableRateMultiplier = ref(false)
|
||||||
const enableStatus = ref(false)
|
const enableStatus = ref(false)
|
||||||
const enableGroups = ref(false)
|
const enableGroups = ref(false)
|
||||||
|
|
||||||
@@ -670,6 +701,7 @@ const interceptWarmupRequests = ref(false)
|
|||||||
const proxyId = ref<number | null>(null)
|
const proxyId = ref<number | null>(null)
|
||||||
const concurrency = ref(1)
|
const concurrency = ref(1)
|
||||||
const priority = ref(1)
|
const priority = ref(1)
|
||||||
|
const rateMultiplier = ref(1)
|
||||||
const status = ref<'active' | 'inactive'>('active')
|
const status = ref<'active' | 'inactive'>('active')
|
||||||
const groupIds = ref<number[]>([])
|
const groupIds = ref<number[]>([])
|
||||||
|
|
||||||
@@ -863,6 +895,10 @@ const buildUpdatePayload = (): Record<string, unknown> | null => {
|
|||||||
updates.priority = priority.value
|
updates.priority = priority.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableRateMultiplier.value) {
|
||||||
|
updates.rate_multiplier = rateMultiplier.value
|
||||||
|
}
|
||||||
|
|
||||||
if (enableStatus.value) {
|
if (enableStatus.value) {
|
||||||
updates.status = status.value
|
updates.status = status.value
|
||||||
}
|
}
|
||||||
@@ -923,6 +959,7 @@ const handleSubmit = async () => {
|
|||||||
enableProxy.value ||
|
enableProxy.value ||
|
||||||
enableConcurrency.value ||
|
enableConcurrency.value ||
|
||||||
enablePriority.value ||
|
enablePriority.value ||
|
||||||
|
enableRateMultiplier.value ||
|
||||||
enableStatus.value ||
|
enableStatus.value ||
|
||||||
enableGroups.value
|
enableGroups.value
|
||||||
|
|
||||||
@@ -977,6 +1014,7 @@ watch(
|
|||||||
enableProxy.value = false
|
enableProxy.value = false
|
||||||
enableConcurrency.value = false
|
enableConcurrency.value = false
|
||||||
enablePriority.value = false
|
enablePriority.value = false
|
||||||
|
enableRateMultiplier.value = false
|
||||||
enableStatus.value = false
|
enableStatus.value = false
|
||||||
enableGroups.value = false
|
enableGroups.value = false
|
||||||
|
|
||||||
@@ -991,6 +1029,7 @@ watch(
|
|||||||
proxyId.value = null
|
proxyId.value = null
|
||||||
concurrency.value = 1
|
concurrency.value = 1
|
||||||
priority.value = 1
|
priority.value = 1
|
||||||
|
rateMultiplier.value = 1
|
||||||
status.value = 'active'
|
status.value = 'active'
|
||||||
groupIds.value = []
|
groupIds.value = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1196,7 +1196,7 @@
|
|||||||
<ProxySelector v-model="form.proxy_id" :proxies="proxies" />
|
<ProxySelector v-model="form.proxy_id" :proxies="proxies" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4 lg:grid-cols-3">
|
||||||
<div>
|
<div>
|
||||||
<label class="input-label">{{ t('admin.accounts.concurrency') }}</label>
|
<label class="input-label">{{ t('admin.accounts.concurrency') }}</label>
|
||||||
<input v-model.number="form.concurrency" type="number" min="1" class="input" />
|
<input v-model.number="form.concurrency" type="number" min="1" class="input" />
|
||||||
@@ -1212,6 +1212,11 @@
|
|||||||
/>
|
/>
|
||||||
<p class="input-hint">{{ t('admin.accounts.priorityHint') }}</p>
|
<p class="input-hint">{{ t('admin.accounts.priorityHint') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="input-label">{{ t('admin.accounts.billingRateMultiplier') }}</label>
|
||||||
|
<input v-model.number="form.rate_multiplier" type="number" min="0" step="0.01" class="input" />
|
||||||
|
<p class="input-hint">{{ t('admin.accounts.billingRateMultiplierHint') }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-t border-gray-200 pt-4 dark:border-dark-600">
|
<div class="border-t border-gray-200 pt-4 dark:border-dark-600">
|
||||||
<label class="input-label">{{ t('admin.accounts.expiresAt') }}</label>
|
<label class="input-label">{{ t('admin.accounts.expiresAt') }}</label>
|
||||||
@@ -1832,6 +1837,7 @@ const form = reactive({
|
|||||||
proxy_id: null as number | null,
|
proxy_id: null as number | null,
|
||||||
concurrency: 10,
|
concurrency: 10,
|
||||||
priority: 1,
|
priority: 1,
|
||||||
|
rate_multiplier: 1,
|
||||||
group_ids: [] as number[],
|
group_ids: [] as number[],
|
||||||
expires_at: null as number | null
|
expires_at: null as number | null
|
||||||
})
|
})
|
||||||
@@ -2119,6 +2125,7 @@ const resetForm = () => {
|
|||||||
form.proxy_id = null
|
form.proxy_id = null
|
||||||
form.concurrency = 10
|
form.concurrency = 10
|
||||||
form.priority = 1
|
form.priority = 1
|
||||||
|
form.rate_multiplier = 1
|
||||||
form.group_ids = []
|
form.group_ids = []
|
||||||
form.expires_at = null
|
form.expires_at = null
|
||||||
accountCategory.value = 'oauth-based'
|
accountCategory.value = 'oauth-based'
|
||||||
@@ -2272,6 +2279,7 @@ const createAccountAndFinish = async (
|
|||||||
proxy_id: form.proxy_id,
|
proxy_id: form.proxy_id,
|
||||||
concurrency: form.concurrency,
|
concurrency: form.concurrency,
|
||||||
priority: form.priority,
|
priority: form.priority,
|
||||||
|
rate_multiplier: form.rate_multiplier,
|
||||||
group_ids: form.group_ids,
|
group_ids: form.group_ids,
|
||||||
expires_at: form.expires_at,
|
expires_at: form.expires_at,
|
||||||
auto_pause_on_expired: autoPauseOnExpired.value
|
auto_pause_on_expired: autoPauseOnExpired.value
|
||||||
@@ -2490,6 +2498,7 @@ const handleCookieAuth = async (sessionKey: string) => {
|
|||||||
proxy_id: form.proxy_id,
|
proxy_id: form.proxy_id,
|
||||||
concurrency: form.concurrency,
|
concurrency: form.concurrency,
|
||||||
priority: form.priority,
|
priority: form.priority,
|
||||||
|
rate_multiplier: form.rate_multiplier,
|
||||||
group_ids: form.group_ids,
|
group_ids: form.group_ids,
|
||||||
expires_at: form.expires_at,
|
expires_at: form.expires_at,
|
||||||
auto_pause_on_expired: autoPauseOnExpired.value
|
auto_pause_on_expired: autoPauseOnExpired.value
|
||||||
|
|||||||
@@ -549,7 +549,7 @@
|
|||||||
<ProxySelector v-model="form.proxy_id" :proxies="proxies" />
|
<ProxySelector v-model="form.proxy_id" :proxies="proxies" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4 lg:grid-cols-3">
|
||||||
<div>
|
<div>
|
||||||
<label class="input-label">{{ t('admin.accounts.concurrency') }}</label>
|
<label class="input-label">{{ t('admin.accounts.concurrency') }}</label>
|
||||||
<input v-model.number="form.concurrency" type="number" min="1" class="input" />
|
<input v-model.number="form.concurrency" type="number" min="1" class="input" />
|
||||||
@@ -564,6 +564,11 @@
|
|||||||
data-tour="account-form-priority"
|
data-tour="account-form-priority"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="input-label">{{ t('admin.accounts.billingRateMultiplier') }}</label>
|
||||||
|
<input v-model.number="form.rate_multiplier" type="number" min="0" step="0.01" class="input" />
|
||||||
|
<p class="input-hint">{{ t('admin.accounts.billingRateMultiplierHint') }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-t border-gray-200 pt-4 dark:border-dark-600">
|
<div class="border-t border-gray-200 pt-4 dark:border-dark-600">
|
||||||
<label class="input-label">{{ t('admin.accounts.expiresAt') }}</label>
|
<label class="input-label">{{ t('admin.accounts.expiresAt') }}</label>
|
||||||
@@ -807,6 +812,7 @@ const form = reactive({
|
|||||||
proxy_id: null as number | null,
|
proxy_id: null as number | null,
|
||||||
concurrency: 1,
|
concurrency: 1,
|
||||||
priority: 1,
|
priority: 1,
|
||||||
|
rate_multiplier: 1,
|
||||||
status: 'active' as 'active' | 'inactive',
|
status: 'active' as 'active' | 'inactive',
|
||||||
group_ids: [] as number[],
|
group_ids: [] as number[],
|
||||||
expires_at: null as number | null
|
expires_at: null as number | null
|
||||||
@@ -834,6 +840,7 @@ watch(
|
|||||||
form.proxy_id = newAccount.proxy_id
|
form.proxy_id = newAccount.proxy_id
|
||||||
form.concurrency = newAccount.concurrency
|
form.concurrency = newAccount.concurrency
|
||||||
form.priority = newAccount.priority
|
form.priority = newAccount.priority
|
||||||
|
form.rate_multiplier = newAccount.rate_multiplier ?? 1
|
||||||
form.status = newAccount.status as 'active' | 'inactive'
|
form.status = newAccount.status as 'active' | 'inactive'
|
||||||
form.group_ids = newAccount.group_ids || []
|
form.group_ids = newAccount.group_ids || []
|
||||||
form.expires_at = newAccount.expires_at ?? null
|
form.expires_at = newAccount.expires_at ?? null
|
||||||
|
|||||||
@@ -15,7 +15,13 @@
|
|||||||
<span class="rounded bg-gray-100 px-1.5 py-0.5 dark:bg-gray-800">
|
<span class="rounded bg-gray-100 px-1.5 py-0.5 dark:bg-gray-800">
|
||||||
{{ formatTokens }}
|
{{ formatTokens }}
|
||||||
</span>
|
</span>
|
||||||
<span class="rounded bg-gray-100 px-1.5 py-0.5 dark:bg-gray-800"> ${{ formatCost }} </span>
|
<span class="rounded bg-gray-100 px-1.5 py-0.5 dark:bg-gray-800"> A ${{ formatAccountCost }} </span>
|
||||||
|
<span
|
||||||
|
v-if="windowStats?.user_cost != null"
|
||||||
|
class="rounded bg-gray-100 px-1.5 py-0.5 dark:bg-gray-800"
|
||||||
|
>
|
||||||
|
U ${{ formatUserCost }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -149,8 +155,13 @@ const formatTokens = computed(() => {
|
|||||||
return t.toString()
|
return t.toString()
|
||||||
})
|
})
|
||||||
|
|
||||||
const formatCost = computed(() => {
|
const formatAccountCost = computed(() => {
|
||||||
if (!props.windowStats) return '0.00'
|
if (!props.windowStats) return '0.00'
|
||||||
return props.windowStats.cost.toFixed(2)
|
return props.windowStats.cost.toFixed(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const formatUserCost = computed(() => {
|
||||||
|
if (!props.windowStats || props.windowStats.user_cost == null) return '0.00'
|
||||||
|
return props.windowStats.user_cost.toFixed(2)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -61,11 +61,12 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
{{ t('admin.accounts.stats.accumulatedCost') }}
|
{{ t('admin.accounts.stats.accumulatedCost') }}
|
||||||
<span class="text-gray-400 dark:text-gray-500"
|
<span class="text-gray-400 dark:text-gray-500">
|
||||||
>({{ t('admin.accounts.stats.standardCost') }}: ${{
|
({{ t('usage.userBilled') }}: ${{ formatCost(stats.summary.total_user_cost) }} ·
|
||||||
|
{{ t('admin.accounts.stats.standardCost') }}: ${{
|
||||||
formatCost(stats.summary.total_standard_cost)
|
formatCost(stats.summary.total_standard_cost)
|
||||||
}})</span
|
}})
|
||||||
>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -108,12 +109,15 @@
|
|||||||
<p class="text-2xl font-bold text-gray-900 dark:text-white">
|
<p class="text-2xl font-bold text-gray-900 dark:text-white">
|
||||||
${{ formatCost(stats.summary.avg_daily_cost) }}
|
${{ formatCost(stats.summary.avg_daily_cost) }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
{{
|
{{
|
||||||
t('admin.accounts.stats.basedOnActualDays', {
|
t('admin.accounts.stats.basedOnActualDays', {
|
||||||
days: stats.summary.actual_days_used
|
days: stats.summary.actual_days_used
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
<span class="text-gray-400 dark:text-gray-500">
|
||||||
|
({{ t('usage.userBilled') }}: ${{ formatCost(stats.summary.avg_daily_user_cost) }})
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -164,13 +168,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.accountBilled') }}</span>
|
||||||
t('admin.accounts.stats.cost')
|
|
||||||
}}</span>
|
|
||||||
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
>${{ formatCost(stats.summary.today?.cost || 0) }}</span
|
>${{ formatCost(stats.summary.today?.cost || 0) }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.userBilled') }}</span>
|
||||||
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
|
>${{ formatCost(stats.summary.today?.user_cost || 0) }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
||||||
t('admin.accounts.stats.requests')
|
t('admin.accounts.stats.requests')
|
||||||
@@ -210,13 +218,17 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.accountBilled') }}</span>
|
||||||
t('admin.accounts.stats.cost')
|
|
||||||
}}</span>
|
|
||||||
<span class="text-sm font-semibold text-orange-600 dark:text-orange-400"
|
<span class="text-sm font-semibold text-orange-600 dark:text-orange-400"
|
||||||
>${{ formatCost(stats.summary.highest_cost_day?.cost || 0) }}</span
|
>${{ formatCost(stats.summary.highest_cost_day?.cost || 0) }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.userBilled') }}</span>
|
||||||
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
|
>${{ formatCost(stats.summary.highest_cost_day?.user_cost || 0) }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
||||||
t('admin.accounts.stats.requests')
|
t('admin.accounts.stats.requests')
|
||||||
@@ -260,13 +272,17 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-xs text-gray-500 dark:text-gray-400">{{
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.accountBilled') }}</span>
|
||||||
t('admin.accounts.stats.cost')
|
|
||||||
}}</span>
|
|
||||||
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
>${{ formatCost(stats.summary.highest_request_day?.cost || 0) }}</span
|
>${{ formatCost(stats.summary.highest_request_day?.cost || 0) }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400">{{ t('usage.userBilled') }}</span>
|
||||||
|
<span class="text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
|
>${{ formatCost(stats.summary.highest_request_day?.user_cost || 0) }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -485,14 +501,24 @@ const trendChartData = computed(() => {
|
|||||||
labels: stats.value.history.map((h) => h.label),
|
labels: stats.value.history.map((h) => h.label),
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: t('admin.accounts.stats.cost') + ' (USD)',
|
label: t('usage.accountBilled') + ' (USD)',
|
||||||
data: stats.value.history.map((h) => h.cost),
|
data: stats.value.history.map((h) => h.actual_cost),
|
||||||
borderColor: '#3b82f6',
|
borderColor: '#3b82f6',
|
||||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||||||
fill: true,
|
fill: true,
|
||||||
tension: 0.3,
|
tension: 0.3,
|
||||||
yAxisID: 'y'
|
yAxisID: 'y'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t('usage.userBilled') + ' (USD)',
|
||||||
|
data: stats.value.history.map((h) => h.user_cost),
|
||||||
|
borderColor: '#10b981',
|
||||||
|
backgroundColor: 'rgba(16, 185, 129, 0.08)',
|
||||||
|
fill: false,
|
||||||
|
tension: 0.3,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
yAxisID: 'y'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t('admin.accounts.stats.requests'),
|
label: t('admin.accounts.stats.requests'),
|
||||||
data: stats.value.history.map((h) => h.requests),
|
data: stats.value.history.map((h) => h.requests),
|
||||||
@@ -570,7 +596,7 @@ const lineChartOptions = computed(() => ({
|
|||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: t('admin.accounts.stats.cost') + ' (USD)',
|
text: t('usage.accountBilled') + ' (USD)',
|
||||||
color: '#3b82f6',
|
color: '#3b82f6',
|
||||||
font: {
|
font: {
|
||||||
size: 11
|
size: 11
|
||||||
|
|||||||
@@ -27,9 +27,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
<p class="text-xs font-medium text-gray-500">{{ t('usage.totalCost') }}</p>
|
<p class="text-xs font-medium text-gray-500">{{ t('usage.totalCost') }}</p>
|
||||||
<p class="text-xl font-bold text-green-600">${{ (stats?.total_actual_cost || 0).toFixed(4) }}</p>
|
<p class="text-xl font-bold text-green-600">
|
||||||
<p class="text-xs text-gray-400">
|
${{ ((stats?.total_account_cost ?? stats?.total_actual_cost) || 0).toFixed(4) }}
|
||||||
{{ t('usage.standardCost') }}: <span class="line-through">${{ (stats?.total_cost || 0).toFixed(4) }}</span>
|
</p>
|
||||||
|
<p class="text-xs text-gray-400" v-if="stats?.total_account_cost != null">
|
||||||
|
{{ t('usage.userBilled') }}:
|
||||||
|
<span class="text-gray-300">${{ (stats?.total_actual_cost || 0).toFixed(4) }}</span>
|
||||||
|
· {{ t('usage.standardCost') }}:
|
||||||
|
<span class="text-gray-300">${{ (stats?.total_cost || 0).toFixed(4) }}</span>
|
||||||
|
</p>
|
||||||
|
<p class="text-xs text-gray-400" v-else>
|
||||||
|
{{ t('usage.standardCost') }}:
|
||||||
|
<span class="line-through">${{ (stats?.total_cost || 0).toFixed(4) }}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -81,18 +81,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #cell-cost="{ row }">
|
<template #cell-cost="{ row }">
|
||||||
<div class="flex items-center gap-1.5 text-sm">
|
<div class="text-sm">
|
||||||
<span class="font-medium text-green-600 dark:text-green-400">${{ row.actual_cost?.toFixed(6) || '0.000000' }}</span>
|
<div class="flex items-center gap-1.5">
|
||||||
<!-- Cost Detail Tooltip -->
|
<span class="font-medium text-green-600 dark:text-green-400">${{ row.actual_cost?.toFixed(6) || '0.000000' }}</span>
|
||||||
<div
|
<!-- Cost Detail Tooltip -->
|
||||||
class="group relative"
|
<div
|
||||||
@mouseenter="showTooltip($event, row)"
|
class="group relative"
|
||||||
@mouseleave="hideTooltip"
|
@mouseenter="showTooltip($event, row)"
|
||||||
>
|
@mouseleave="hideTooltip"
|
||||||
<div class="flex h-4 w-4 cursor-help items-center justify-center rounded-full bg-gray-100 transition-colors group-hover:bg-blue-100 dark:bg-gray-700 dark:group-hover:bg-blue-900/50">
|
>
|
||||||
<Icon name="infoCircle" size="xs" class="text-gray-400 group-hover:text-blue-500 dark:text-gray-500 dark:group-hover:text-blue-400" />
|
<div class="flex h-4 w-4 cursor-help items-center justify-center rounded-full bg-gray-100 transition-colors group-hover:bg-blue-100 dark:bg-gray-700 dark:group-hover:bg-blue-900/50">
|
||||||
|
<Icon name="infoCircle" size="xs" class="text-gray-400 group-hover:text-blue-500 dark:text-gray-500 dark:group-hover:text-blue-400" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="row.account_rate_multiplier != null" class="mt-0.5 text-[11px] text-gray-400">
|
||||||
|
A ${{ (row.total_cost * row.account_rate_multiplier).toFixed(6) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -202,14 +207,24 @@
|
|||||||
<span class="text-gray-400">{{ t('usage.rate') }}</span>
|
<span class="text-gray-400">{{ t('usage.rate') }}</span>
|
||||||
<span class="font-semibold text-blue-400">{{ (tooltipData?.rate_multiplier || 1).toFixed(2) }}x</span>
|
<span class="font-semibold text-blue-400">{{ (tooltipData?.rate_multiplier || 1).toFixed(2) }}x</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between gap-6">
|
||||||
|
<span class="text-gray-400">{{ t('usage.accountMultiplier') }}</span>
|
||||||
|
<span class="font-semibold text-blue-400">{{ (tooltipData?.account_rate_multiplier ?? 1).toFixed(2) }}x</span>
|
||||||
|
</div>
|
||||||
<div class="flex items-center justify-between gap-6">
|
<div class="flex items-center justify-between gap-6">
|
||||||
<span class="text-gray-400">{{ t('usage.original') }}</span>
|
<span class="text-gray-400">{{ t('usage.original') }}</span>
|
||||||
<span class="font-medium text-white">${{ tooltipData?.total_cost?.toFixed(6) || '0.000000' }}</span>
|
<span class="font-medium text-white">${{ tooltipData?.total_cost?.toFixed(6) || '0.000000' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between gap-6 border-t border-gray-700 pt-1.5">
|
<div class="flex items-center justify-between gap-6">
|
||||||
<span class="text-gray-400">{{ t('usage.billed') }}</span>
|
<span class="text-gray-400">{{ t('usage.userBilled') }}</span>
|
||||||
<span class="font-semibold text-green-400">${{ tooltipData?.actual_cost?.toFixed(6) || '0.000000' }}</span>
|
<span class="font-semibold text-green-400">${{ tooltipData?.actual_cost?.toFixed(6) || '0.000000' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between gap-6 border-t border-gray-700 pt-1.5">
|
||||||
|
<span class="text-gray-400">{{ t('usage.accountBilled') }}</span>
|
||||||
|
<span class="font-semibold text-green-400">
|
||||||
|
${{ (((tooltipData?.total_cost || 0) * (tooltipData?.account_rate_multiplier ?? 1)) || 0).toFixed(6) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute right-full top-1/2 h-0 w-0 -translate-y-1/2 border-b-[6px] border-r-[6px] border-t-[6px] border-b-transparent border-r-gray-900 border-t-transparent dark:border-r-gray-800"></div>
|
<div class="absolute right-full top-1/2 h-0 w-0 -translate-y-1/2 border-b-[6px] border-r-[6px] border-t-[6px] border-b-transparent border-r-gray-900 border-t-transparent dark:border-r-gray-800"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -429,6 +429,9 @@ export default {
|
|||||||
totalCost: 'Total Cost',
|
totalCost: 'Total Cost',
|
||||||
standardCost: 'Standard',
|
standardCost: 'Standard',
|
||||||
actualCost: 'Actual',
|
actualCost: 'Actual',
|
||||||
|
userBilled: 'User billed',
|
||||||
|
accountBilled: 'Account billed',
|
||||||
|
accountMultiplier: 'Account rate',
|
||||||
avgDuration: 'Avg Duration',
|
avgDuration: 'Avg Duration',
|
||||||
inSelectedRange: 'in selected range',
|
inSelectedRange: 'in selected range',
|
||||||
perRequest: 'per request',
|
perRequest: 'per request',
|
||||||
@@ -1058,6 +1061,7 @@ export default {
|
|||||||
concurrencyStatus: 'Concurrency',
|
concurrencyStatus: 'Concurrency',
|
||||||
notes: 'Notes',
|
notes: 'Notes',
|
||||||
priority: 'Priority',
|
priority: 'Priority',
|
||||||
|
billingRateMultiplier: 'Billing Rate',
|
||||||
weight: 'Weight',
|
weight: 'Weight',
|
||||||
status: 'Status',
|
status: 'Status',
|
||||||
schedulable: 'Schedulable',
|
schedulable: 'Schedulable',
|
||||||
@@ -1225,6 +1229,8 @@ export default {
|
|||||||
concurrency: 'Concurrency',
|
concurrency: 'Concurrency',
|
||||||
priority: 'Priority',
|
priority: 'Priority',
|
||||||
priorityHint: 'Lower value accounts are used first',
|
priorityHint: 'Lower value accounts are used first',
|
||||||
|
billingRateMultiplier: 'Billing Rate Multiplier',
|
||||||
|
billingRateMultiplierHint: '>=0, 0 means free. Affects account billing only',
|
||||||
expiresAt: 'Expires At',
|
expiresAt: 'Expires At',
|
||||||
expiresAtHint: 'Leave empty for no expiration',
|
expiresAtHint: 'Leave empty for no expiration',
|
||||||
higherPriorityFirst: 'Lower value means higher priority',
|
higherPriorityFirst: 'Lower value means higher priority',
|
||||||
|
|||||||
@@ -426,6 +426,9 @@ export default {
|
|||||||
totalCost: '总消费',
|
totalCost: '总消费',
|
||||||
standardCost: '标准',
|
standardCost: '标准',
|
||||||
actualCost: '实际',
|
actualCost: '实际',
|
||||||
|
userBilled: '用户扣费',
|
||||||
|
accountBilled: '账号计费',
|
||||||
|
accountMultiplier: '账号倍率',
|
||||||
avgDuration: '平均耗时',
|
avgDuration: '平均耗时',
|
||||||
inSelectedRange: '所选范围内',
|
inSelectedRange: '所选范围内',
|
||||||
perRequest: '每次请求',
|
perRequest: '每次请求',
|
||||||
@@ -1108,6 +1111,7 @@ export default {
|
|||||||
concurrencyStatus: '并发',
|
concurrencyStatus: '并发',
|
||||||
notes: '备注',
|
notes: '备注',
|
||||||
priority: '优先级',
|
priority: '优先级',
|
||||||
|
billingRateMultiplier: '账号倍率',
|
||||||
weight: '权重',
|
weight: '权重',
|
||||||
status: '状态',
|
status: '状态',
|
||||||
schedulable: '调度',
|
schedulable: '调度',
|
||||||
@@ -1359,6 +1363,8 @@ export default {
|
|||||||
concurrency: '并发数',
|
concurrency: '并发数',
|
||||||
priority: '优先级',
|
priority: '优先级',
|
||||||
priorityHint: '优先级越小的账号优先使用',
|
priorityHint: '优先级越小的账号优先使用',
|
||||||
|
billingRateMultiplier: '账号计费倍率',
|
||||||
|
billingRateMultiplierHint: '>=0,0 表示该账号计费为 0;仅影响账号计费口径',
|
||||||
expiresAt: '过期时间',
|
expiresAt: '过期时间',
|
||||||
expiresAtHint: '留空表示不过期',
|
expiresAtHint: '留空表示不过期',
|
||||||
higherPriorityFirst: '数值越小优先级越高',
|
higherPriorityFirst: '数值越小优先级越高',
|
||||||
|
|||||||
@@ -428,6 +428,7 @@ export interface Account {
|
|||||||
concurrency: number
|
concurrency: number
|
||||||
current_concurrency?: number // Real-time concurrency count from Redis
|
current_concurrency?: number // Real-time concurrency count from Redis
|
||||||
priority: number
|
priority: number
|
||||||
|
rate_multiplier?: number // Account billing multiplier (>=0, 0 means free)
|
||||||
status: 'active' | 'inactive' | 'error'
|
status: 'active' | 'inactive' | 'error'
|
||||||
error_message: string | null
|
error_message: string | null
|
||||||
last_used_at: string | null
|
last_used_at: string | null
|
||||||
@@ -457,7 +458,9 @@ export interface Account {
|
|||||||
export interface WindowStats {
|
export interface WindowStats {
|
||||||
requests: number
|
requests: number
|
||||||
tokens: number
|
tokens: number
|
||||||
cost: number
|
cost: number // Account cost (account multiplier)
|
||||||
|
standard_cost?: number
|
||||||
|
user_cost?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UsageProgress {
|
export interface UsageProgress {
|
||||||
@@ -522,6 +525,7 @@ export interface CreateAccountRequest {
|
|||||||
proxy_id?: number | null
|
proxy_id?: number | null
|
||||||
concurrency?: number
|
concurrency?: number
|
||||||
priority?: number
|
priority?: number
|
||||||
|
rate_multiplier?: number // Account billing multiplier (>=0, 0 means free)
|
||||||
group_ids?: number[]
|
group_ids?: number[]
|
||||||
expires_at?: number | null
|
expires_at?: number | null
|
||||||
auto_pause_on_expired?: boolean
|
auto_pause_on_expired?: boolean
|
||||||
@@ -537,6 +541,7 @@ export interface UpdateAccountRequest {
|
|||||||
proxy_id?: number | null
|
proxy_id?: number | null
|
||||||
concurrency?: number
|
concurrency?: number
|
||||||
priority?: number
|
priority?: number
|
||||||
|
rate_multiplier?: number // Account billing multiplier (>=0, 0 means free)
|
||||||
schedulable?: boolean
|
schedulable?: boolean
|
||||||
status?: 'active' | 'inactive'
|
status?: 'active' | 'inactive'
|
||||||
group_ids?: number[]
|
group_ids?: number[]
|
||||||
@@ -593,6 +598,7 @@ export interface UsageLog {
|
|||||||
total_cost: number
|
total_cost: number
|
||||||
actual_cost: number
|
actual_cost: number
|
||||||
rate_multiplier: number
|
rate_multiplier: number
|
||||||
|
account_rate_multiplier?: number | null
|
||||||
|
|
||||||
stream: boolean
|
stream: boolean
|
||||||
duration_ms: number
|
duration_ms: number
|
||||||
@@ -852,23 +858,27 @@ export interface AccountUsageHistory {
|
|||||||
requests: number
|
requests: number
|
||||||
tokens: number
|
tokens: number
|
||||||
cost: number
|
cost: number
|
||||||
actual_cost: number
|
actual_cost: number // Account cost (account multiplier)
|
||||||
|
user_cost: number // User/API key billed cost (group multiplier)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountUsageSummary {
|
export interface AccountUsageSummary {
|
||||||
days: number
|
days: number
|
||||||
actual_days_used: number
|
actual_days_used: number
|
||||||
total_cost: number
|
total_cost: number // Account cost (account multiplier)
|
||||||
|
total_user_cost: number
|
||||||
total_standard_cost: number
|
total_standard_cost: number
|
||||||
total_requests: number
|
total_requests: number
|
||||||
total_tokens: number
|
total_tokens: number
|
||||||
avg_daily_cost: number
|
avg_daily_cost: number // Account cost
|
||||||
|
avg_daily_user_cost: number
|
||||||
avg_daily_requests: number
|
avg_daily_requests: number
|
||||||
avg_daily_tokens: number
|
avg_daily_tokens: number
|
||||||
avg_duration_ms: number
|
avg_duration_ms: number
|
||||||
today: {
|
today: {
|
||||||
date: string
|
date: string
|
||||||
cost: number
|
cost: number
|
||||||
|
user_cost: number
|
||||||
requests: number
|
requests: number
|
||||||
tokens: number
|
tokens: number
|
||||||
} | null
|
} | null
|
||||||
@@ -876,6 +886,7 @@ export interface AccountUsageSummary {
|
|||||||
date: string
|
date: string
|
||||||
label: string
|
label: string
|
||||||
cost: number
|
cost: number
|
||||||
|
user_cost: number
|
||||||
requests: number
|
requests: number
|
||||||
} | null
|
} | null
|
||||||
highest_request_day: {
|
highest_request_day: {
|
||||||
@@ -883,6 +894,7 @@ export interface AccountUsageSummary {
|
|||||||
label: string
|
label: string
|
||||||
requests: number
|
requests: number
|
||||||
cost: number
|
cost: number
|
||||||
|
user_cost: number
|
||||||
} | null
|
} | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,11 @@
|
|||||||
<template #cell-usage="{ row }">
|
<template #cell-usage="{ row }">
|
||||||
<AccountUsageCell :account="row" />
|
<AccountUsageCell :account="row" />
|
||||||
</template>
|
</template>
|
||||||
|
<template #cell-rate_multiplier="{ row }">
|
||||||
|
<span class="text-sm font-mono text-gray-700 dark:text-gray-300">
|
||||||
|
{{ (row.rate_multiplier ?? 1).toFixed(2) }}x
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
<template #cell-priority="{ value }">
|
<template #cell-priority="{ value }">
|
||||||
<span class="text-sm text-gray-700 dark:text-gray-300">{{ value }}</span>
|
<span class="text-sm text-gray-700 dark:text-gray-300">{{ value }}</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -190,10 +195,11 @@ const cols = computed(() => {
|
|||||||
if (!authStore.isSimpleMode) {
|
if (!authStore.isSimpleMode) {
|
||||||
c.push({ key: 'groups', label: t('admin.accounts.columns.groups'), sortable: false })
|
c.push({ key: 'groups', label: t('admin.accounts.columns.groups'), sortable: false })
|
||||||
}
|
}
|
||||||
c.push(
|
c.push(
|
||||||
{ key: 'usage', label: t('admin.accounts.columns.usageWindows'), sortable: false },
|
{ key: 'usage', label: t('admin.accounts.columns.usageWindows'), sortable: false },
|
||||||
{ key: 'priority', label: t('admin.accounts.columns.priority'), sortable: true },
|
{ key: 'priority', label: t('admin.accounts.columns.priority'), sortable: true },
|
||||||
{ key: 'last_used_at', label: t('admin.accounts.columns.lastUsed'), sortable: true },
|
{ key: 'rate_multiplier', label: t('admin.accounts.columns.billingRateMultiplier'), sortable: true },
|
||||||
|
{ key: 'last_used_at', label: t('admin.accounts.columns.lastUsed'), sortable: true },
|
||||||
{ key: 'expires_at', label: t('admin.accounts.columns.expiresAt'), sortable: true },
|
{ key: 'expires_at', label: t('admin.accounts.columns.expiresAt'), sortable: true },
|
||||||
{ key: 'notes', label: t('admin.accounts.columns.notes'), sortable: false },
|
{ key: 'notes', label: t('admin.accounts.columns.notes'), sortable: false },
|
||||||
{ key: 'actions', label: t('admin.accounts.columns.actions'), sortable: false }
|
{ key: 'actions', label: t('admin.accounts.columns.actions'), sortable: false }
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ const exportToExcel = async () => {
|
|||||||
t('admin.usage.cacheReadTokens'), t('admin.usage.cacheCreationTokens'),
|
t('admin.usage.cacheReadTokens'), t('admin.usage.cacheCreationTokens'),
|
||||||
t('admin.usage.inputCost'), t('admin.usage.outputCost'),
|
t('admin.usage.inputCost'), t('admin.usage.outputCost'),
|
||||||
t('admin.usage.cacheReadCost'), t('admin.usage.cacheCreationCost'),
|
t('admin.usage.cacheReadCost'), t('admin.usage.cacheCreationCost'),
|
||||||
t('usage.rate'), t('usage.original'), t('usage.billed'),
|
t('usage.rate'), t('usage.accountMultiplier'), t('usage.original'), t('usage.userBilled'), t('usage.accountBilled'),
|
||||||
t('usage.firstToken'), t('usage.duration'),
|
t('usage.firstToken'), t('usage.duration'),
|
||||||
t('admin.usage.requestId'), t('usage.userAgent'), t('admin.usage.ipAddress')
|
t('admin.usage.requestId'), t('usage.userAgent'), t('admin.usage.ipAddress')
|
||||||
]
|
]
|
||||||
@@ -115,8 +115,10 @@ const exportToExcel = async () => {
|
|||||||
log.cache_read_cost?.toFixed(6) || '0.000000',
|
log.cache_read_cost?.toFixed(6) || '0.000000',
|
||||||
log.cache_creation_cost?.toFixed(6) || '0.000000',
|
log.cache_creation_cost?.toFixed(6) || '0.000000',
|
||||||
log.rate_multiplier?.toFixed(2) || '1.00',
|
log.rate_multiplier?.toFixed(2) || '1.00',
|
||||||
|
(log.account_rate_multiplier ?? 1).toFixed(2),
|
||||||
log.total_cost?.toFixed(6) || '0.000000',
|
log.total_cost?.toFixed(6) || '0.000000',
|
||||||
log.actual_cost?.toFixed(6) || '0.000000',
|
log.actual_cost?.toFixed(6) || '0.000000',
|
||||||
|
(log.total_cost * (log.account_rate_multiplier ?? 1)).toFixed(6),
|
||||||
log.first_token_ms ?? '',
|
log.first_token_ms ?? '',
|
||||||
log.duration_ms,
|
log.duration_ms,
|
||||||
log.request_id || '',
|
log.request_id || '',
|
||||||
|
|||||||
Reference in New Issue
Block a user