diff --git a/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go
index 4f31883b06..1fff61ba44 100644
--- a/backend/ent/migrate/schema.go
+++ b/backend/ent/migrate/schema.go
@@ -1079,8 +1079,10 @@ var (
{Name: "totp_enabled", Type: field.TypeBool, Default: false},
{Name: "totp_enabled_at", Type: field.TypeTime, Nullable: true},
{Name: "balance_notify_enabled", Type: field.TypeBool, Default: true},
+ {Name: "balance_notify_threshold_type", Type: field.TypeString, Default: "fixed"},
{Name: "balance_notify_threshold", Type: field.TypeFloat64, Nullable: true, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
{Name: "balance_notify_extra_emails", Type: field.TypeString, Default: "[]", SchemaType: map[string]string{"postgres": "text"}},
+ {Name: "total_recharged", Type: field.TypeFloat64, Default: 0, SchemaType: map[string]string{"postgres": "decimal(20,8)"}},
}
// UsersTable holds the schema information for the "users" table.
UsersTable = &schema.Table{
diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go
index cdaf363a8e..3bca248d2d 100644
--- a/backend/ent/mutation.go
+++ b/backend/ent/mutation.go
@@ -28211,9 +28211,12 @@ type UserMutation struct {
totp_enabled *bool
totp_enabled_at *time.Time
balance_notify_enabled *bool
+ balance_notify_threshold_type *string
balance_notify_threshold *float64
addbalance_notify_threshold *float64
balance_notify_extra_emails *string
+ total_recharged *float64
+ addtotal_recharged *float64
clearedFields map[string]struct{}
api_keys map[int64]struct{}
removedapi_keys map[int64]struct{}
@@ -28967,6 +28970,42 @@ func (m *UserMutation) ResetBalanceNotifyEnabled() {
m.balance_notify_enabled = nil
}
+// SetBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field.
+func (m *UserMutation) SetBalanceNotifyThresholdType(s string) {
+ m.balance_notify_threshold_type = &s
+}
+
+// BalanceNotifyThresholdType returns the value of the "balance_notify_threshold_type" field in the mutation.
+func (m *UserMutation) BalanceNotifyThresholdType() (r string, exists bool) {
+ v := m.balance_notify_threshold_type
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldBalanceNotifyThresholdType returns the old "balance_notify_threshold_type" field's value of the User entity.
+// If the User 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 *UserMutation) OldBalanceNotifyThresholdType(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldBalanceNotifyThresholdType is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldBalanceNotifyThresholdType requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldBalanceNotifyThresholdType: %w", err)
+ }
+ return oldValue.BalanceNotifyThresholdType, nil
+}
+
+// ResetBalanceNotifyThresholdType resets all changes to the "balance_notify_threshold_type" field.
+func (m *UserMutation) ResetBalanceNotifyThresholdType() {
+ m.balance_notify_threshold_type = nil
+}
+
// SetBalanceNotifyThreshold sets the "balance_notify_threshold" field.
func (m *UserMutation) SetBalanceNotifyThreshold(f float64) {
m.balance_notify_threshold = &f
@@ -29073,6 +29112,62 @@ func (m *UserMutation) ResetBalanceNotifyExtraEmails() {
m.balance_notify_extra_emails = nil
}
+// SetTotalRecharged sets the "total_recharged" field.
+func (m *UserMutation) SetTotalRecharged(f float64) {
+ m.total_recharged = &f
+ m.addtotal_recharged = nil
+}
+
+// TotalRecharged returns the value of the "total_recharged" field in the mutation.
+func (m *UserMutation) TotalRecharged() (r float64, exists bool) {
+ v := m.total_recharged
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldTotalRecharged returns the old "total_recharged" field's value of the User entity.
+// If the User 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 *UserMutation) OldTotalRecharged(ctx context.Context) (v float64, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldTotalRecharged is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldTotalRecharged requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldTotalRecharged: %w", err)
+ }
+ return oldValue.TotalRecharged, nil
+}
+
+// AddTotalRecharged adds f to the "total_recharged" field.
+func (m *UserMutation) AddTotalRecharged(f float64) {
+ if m.addtotal_recharged != nil {
+ *m.addtotal_recharged += f
+ } else {
+ m.addtotal_recharged = &f
+ }
+}
+
+// AddedTotalRecharged returns the value that was added to the "total_recharged" field in this mutation.
+func (m *UserMutation) AddedTotalRecharged() (r float64, exists bool) {
+ v := m.addtotal_recharged
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// ResetTotalRecharged resets all changes to the "total_recharged" field.
+func (m *UserMutation) ResetTotalRecharged() {
+ m.total_recharged = nil
+ m.addtotal_recharged = nil
+}
+
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by ids.
func (m *UserMutation) AddAPIKeyIDs(ids ...int64) {
if m.api_keys == nil {
@@ -29647,7 +29742,7 @@ func (m *UserMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *UserMutation) Fields() []string {
- fields := make([]string, 0, 17)
+ fields := make([]string, 0, 19)
if m.created_at != nil {
fields = append(fields, user.FieldCreatedAt)
}
@@ -29693,12 +29788,18 @@ func (m *UserMutation) Fields() []string {
if m.balance_notify_enabled != nil {
fields = append(fields, user.FieldBalanceNotifyEnabled)
}
+ if m.balance_notify_threshold_type != nil {
+ fields = append(fields, user.FieldBalanceNotifyThresholdType)
+ }
if m.balance_notify_threshold != nil {
fields = append(fields, user.FieldBalanceNotifyThreshold)
}
if m.balance_notify_extra_emails != nil {
fields = append(fields, user.FieldBalanceNotifyExtraEmails)
}
+ if m.total_recharged != nil {
+ fields = append(fields, user.FieldTotalRecharged)
+ }
return fields
}
@@ -29737,10 +29838,14 @@ func (m *UserMutation) Field(name string) (ent.Value, bool) {
return m.TotpEnabledAt()
case user.FieldBalanceNotifyEnabled:
return m.BalanceNotifyEnabled()
+ case user.FieldBalanceNotifyThresholdType:
+ return m.BalanceNotifyThresholdType()
case user.FieldBalanceNotifyThreshold:
return m.BalanceNotifyThreshold()
case user.FieldBalanceNotifyExtraEmails:
return m.BalanceNotifyExtraEmails()
+ case user.FieldTotalRecharged:
+ return m.TotalRecharged()
}
return nil, false
}
@@ -29780,10 +29885,14 @@ func (m *UserMutation) OldField(ctx context.Context, name string) (ent.Value, er
return m.OldTotpEnabledAt(ctx)
case user.FieldBalanceNotifyEnabled:
return m.OldBalanceNotifyEnabled(ctx)
+ case user.FieldBalanceNotifyThresholdType:
+ return m.OldBalanceNotifyThresholdType(ctx)
case user.FieldBalanceNotifyThreshold:
return m.OldBalanceNotifyThreshold(ctx)
case user.FieldBalanceNotifyExtraEmails:
return m.OldBalanceNotifyExtraEmails(ctx)
+ case user.FieldTotalRecharged:
+ return m.OldTotalRecharged(ctx)
}
return nil, fmt.Errorf("unknown User field %s", name)
}
@@ -29898,6 +30007,13 @@ func (m *UserMutation) SetField(name string, value ent.Value) error {
}
m.SetBalanceNotifyEnabled(v)
return nil
+ case user.FieldBalanceNotifyThresholdType:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetBalanceNotifyThresholdType(v)
+ return nil
case user.FieldBalanceNotifyThreshold:
v, ok := value.(float64)
if !ok {
@@ -29912,6 +30028,13 @@ func (m *UserMutation) SetField(name string, value ent.Value) error {
}
m.SetBalanceNotifyExtraEmails(v)
return nil
+ case user.FieldTotalRecharged:
+ v, ok := value.(float64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetTotalRecharged(v)
+ return nil
}
return fmt.Errorf("unknown User field %s", name)
}
@@ -29929,6 +30052,9 @@ func (m *UserMutation) AddedFields() []string {
if m.addbalance_notify_threshold != nil {
fields = append(fields, user.FieldBalanceNotifyThreshold)
}
+ if m.addtotal_recharged != nil {
+ fields = append(fields, user.FieldTotalRecharged)
+ }
return fields
}
@@ -29943,6 +30069,8 @@ func (m *UserMutation) AddedField(name string) (ent.Value, bool) {
return m.AddedConcurrency()
case user.FieldBalanceNotifyThreshold:
return m.AddedBalanceNotifyThreshold()
+ case user.FieldTotalRecharged:
+ return m.AddedTotalRecharged()
}
return nil, false
}
@@ -29973,6 +30101,13 @@ func (m *UserMutation) AddField(name string, value ent.Value) error {
}
m.AddBalanceNotifyThreshold(v)
return nil
+ case user.FieldTotalRecharged:
+ v, ok := value.(float64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.AddTotalRecharged(v)
+ return nil
}
return fmt.Errorf("unknown User numeric field %s", name)
}
@@ -30072,12 +30207,18 @@ func (m *UserMutation) ResetField(name string) error {
case user.FieldBalanceNotifyEnabled:
m.ResetBalanceNotifyEnabled()
return nil
+ case user.FieldBalanceNotifyThresholdType:
+ m.ResetBalanceNotifyThresholdType()
+ return nil
case user.FieldBalanceNotifyThreshold:
m.ResetBalanceNotifyThreshold()
return nil
case user.FieldBalanceNotifyExtraEmails:
m.ResetBalanceNotifyExtraEmails()
return nil
+ case user.FieldTotalRecharged:
+ m.ResetTotalRecharged()
+ return nil
}
return fmt.Errorf("unknown User field %s", name)
}
diff --git a/backend/ent/runtime/runtime.go b/backend/ent/runtime/runtime.go
index a288f5d963..951b5f99f8 100644
--- a/backend/ent/runtime/runtime.go
+++ b/backend/ent/runtime/runtime.go
@@ -1297,10 +1297,18 @@ func init() {
userDescBalanceNotifyEnabled := userFields[11].Descriptor()
// user.DefaultBalanceNotifyEnabled holds the default value on creation for the balance_notify_enabled field.
user.DefaultBalanceNotifyEnabled = userDescBalanceNotifyEnabled.Default.(bool)
+ // userDescBalanceNotifyThresholdType is the schema descriptor for balance_notify_threshold_type field.
+ userDescBalanceNotifyThresholdType := userFields[12].Descriptor()
+ // user.DefaultBalanceNotifyThresholdType holds the default value on creation for the balance_notify_threshold_type field.
+ user.DefaultBalanceNotifyThresholdType = userDescBalanceNotifyThresholdType.Default.(string)
// userDescBalanceNotifyExtraEmails is the schema descriptor for balance_notify_extra_emails field.
- userDescBalanceNotifyExtraEmails := userFields[13].Descriptor()
+ userDescBalanceNotifyExtraEmails := userFields[14].Descriptor()
// user.DefaultBalanceNotifyExtraEmails holds the default value on creation for the balance_notify_extra_emails field.
user.DefaultBalanceNotifyExtraEmails = userDescBalanceNotifyExtraEmails.Default.(string)
+ // userDescTotalRecharged is the schema descriptor for total_recharged field.
+ userDescTotalRecharged := userFields[15].Descriptor()
+ // user.DefaultTotalRecharged holds the default value on creation for the total_recharged field.
+ user.DefaultTotalRecharged = userDescTotalRecharged.Default.(float64)
userallowedgroupFields := schema.UserAllowedGroup{}.Fields()
_ = userallowedgroupFields
// userallowedgroupDescCreatedAt is the schema descriptor for created_at field.
diff --git a/backend/ent/schema/user.go b/backend/ent/schema/user.go
index bdaa45094b..ef52e985d9 100644
--- a/backend/ent/schema/user.go
+++ b/backend/ent/schema/user.go
@@ -76,6 +76,8 @@ func (User) Fields() []ent.Field {
// 余额不足通知
field.Bool("balance_notify_enabled").
Default(true),
+ field.String("balance_notify_threshold_type").
+ Default("fixed"), // "fixed" | "percentage"
field.Float("balance_notify_threshold").
SchemaType(map[string]string{dialect.Postgres: "decimal(20,8)"}).
Optional().
@@ -83,6 +85,9 @@ func (User) Fields() []ent.Field {
field.String("balance_notify_extra_emails").
SchemaType(map[string]string{dialect.Postgres: "text"}).
Default("[]"),
+ field.Float("total_recharged").
+ SchemaType(map[string]string{dialect.Postgres: "decimal(20,8)"}).
+ Default(0),
}
}
diff --git a/backend/ent/user.go b/backend/ent/user.go
index fc4ddb8f75..9fa91f74b9 100644
--- a/backend/ent/user.go
+++ b/backend/ent/user.go
@@ -47,10 +47,14 @@ type User struct {
TotpEnabledAt *time.Time `json:"totp_enabled_at,omitempty"`
// BalanceNotifyEnabled holds the value of the "balance_notify_enabled" field.
BalanceNotifyEnabled bool `json:"balance_notify_enabled,omitempty"`
+ // BalanceNotifyThresholdType holds the value of the "balance_notify_threshold_type" field.
+ BalanceNotifyThresholdType string `json:"balance_notify_threshold_type,omitempty"`
// BalanceNotifyThreshold holds the value of the "balance_notify_threshold" field.
BalanceNotifyThreshold *float64 `json:"balance_notify_threshold,omitempty"`
// BalanceNotifyExtraEmails holds the value of the "balance_notify_extra_emails" field.
BalanceNotifyExtraEmails string `json:"balance_notify_extra_emails,omitempty"`
+ // TotalRecharged holds the value of the "total_recharged" field.
+ TotalRecharged float64 `json:"total_recharged,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the UserQuery when eager-loading is set.
Edges UserEdges `json:"edges"`
@@ -192,11 +196,11 @@ func (*User) scanValues(columns []string) ([]any, error) {
switch columns[i] {
case user.FieldTotpEnabled, user.FieldBalanceNotifyEnabled:
values[i] = new(sql.NullBool)
- case user.FieldBalance, user.FieldBalanceNotifyThreshold:
+ case user.FieldBalance, user.FieldBalanceNotifyThreshold, user.FieldTotalRecharged:
values[i] = new(sql.NullFloat64)
case user.FieldID, user.FieldConcurrency:
values[i] = new(sql.NullInt64)
- case user.FieldEmail, user.FieldPasswordHash, user.FieldRole, user.FieldStatus, user.FieldUsername, user.FieldNotes, user.FieldTotpSecretEncrypted, user.FieldBalanceNotifyExtraEmails:
+ case user.FieldEmail, user.FieldPasswordHash, user.FieldRole, user.FieldStatus, user.FieldUsername, user.FieldNotes, user.FieldTotpSecretEncrypted, user.FieldBalanceNotifyThresholdType, user.FieldBalanceNotifyExtraEmails:
values[i] = new(sql.NullString)
case user.FieldCreatedAt, user.FieldUpdatedAt, user.FieldDeletedAt, user.FieldTotpEnabledAt:
values[i] = new(sql.NullTime)
@@ -314,6 +318,12 @@ func (_m *User) assignValues(columns []string, values []any) error {
} else if value.Valid {
_m.BalanceNotifyEnabled = value.Bool
}
+ case user.FieldBalanceNotifyThresholdType:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field balance_notify_threshold_type", values[i])
+ } else if value.Valid {
+ _m.BalanceNotifyThresholdType = value.String
+ }
case user.FieldBalanceNotifyThreshold:
if value, ok := values[i].(*sql.NullFloat64); !ok {
return fmt.Errorf("unexpected type %T for field balance_notify_threshold", values[i])
@@ -327,6 +337,12 @@ func (_m *User) assignValues(columns []string, values []any) error {
} else if value.Valid {
_m.BalanceNotifyExtraEmails = value.String
}
+ case user.FieldTotalRecharged:
+ if value, ok := values[i].(*sql.NullFloat64); !ok {
+ return fmt.Errorf("unexpected type %T for field total_recharged", values[i])
+ } else if value.Valid {
+ _m.TotalRecharged = value.Float64
+ }
default:
_m.selectValues.Set(columns[i], values[i])
}
@@ -469,6 +485,9 @@ func (_m *User) String() string {
builder.WriteString("balance_notify_enabled=")
builder.WriteString(fmt.Sprintf("%v", _m.BalanceNotifyEnabled))
builder.WriteString(", ")
+ builder.WriteString("balance_notify_threshold_type=")
+ builder.WriteString(_m.BalanceNotifyThresholdType)
+ builder.WriteString(", ")
if v := _m.BalanceNotifyThreshold; v != nil {
builder.WriteString("balance_notify_threshold=")
builder.WriteString(fmt.Sprintf("%v", *v))
@@ -476,6 +495,9 @@ func (_m *User) String() string {
builder.WriteString(", ")
builder.WriteString("balance_notify_extra_emails=")
builder.WriteString(_m.BalanceNotifyExtraEmails)
+ builder.WriteString(", ")
+ builder.WriteString("total_recharged=")
+ builder.WriteString(fmt.Sprintf("%v", _m.TotalRecharged))
builder.WriteByte(')')
return builder.String()
}
diff --git a/backend/ent/user/user.go b/backend/ent/user/user.go
index aff37013ca..d88a3a380b 100644
--- a/backend/ent/user/user.go
+++ b/backend/ent/user/user.go
@@ -45,10 +45,14 @@ const (
FieldTotpEnabledAt = "totp_enabled_at"
// FieldBalanceNotifyEnabled holds the string denoting the balance_notify_enabled field in the database.
FieldBalanceNotifyEnabled = "balance_notify_enabled"
+ // FieldBalanceNotifyThresholdType holds the string denoting the balance_notify_threshold_type field in the database.
+ FieldBalanceNotifyThresholdType = "balance_notify_threshold_type"
// FieldBalanceNotifyThreshold holds the string denoting the balance_notify_threshold field in the database.
FieldBalanceNotifyThreshold = "balance_notify_threshold"
// FieldBalanceNotifyExtraEmails holds the string denoting the balance_notify_extra_emails field in the database.
FieldBalanceNotifyExtraEmails = "balance_notify_extra_emails"
+ // FieldTotalRecharged holds the string denoting the total_recharged field in the database.
+ FieldTotalRecharged = "total_recharged"
// EdgeAPIKeys holds the string denoting the api_keys edge name in mutations.
EdgeAPIKeys = "api_keys"
// EdgeRedeemCodes holds the string denoting the redeem_codes edge name in mutations.
@@ -168,8 +172,10 @@ var Columns = []string{
FieldTotpEnabled,
FieldTotpEnabledAt,
FieldBalanceNotifyEnabled,
+ FieldBalanceNotifyThresholdType,
FieldBalanceNotifyThreshold,
FieldBalanceNotifyExtraEmails,
+ FieldTotalRecharged,
}
var (
@@ -228,8 +234,12 @@ var (
DefaultTotpEnabled bool
// DefaultBalanceNotifyEnabled holds the default value on creation for the "balance_notify_enabled" field.
DefaultBalanceNotifyEnabled bool
+ // DefaultBalanceNotifyThresholdType holds the default value on creation for the "balance_notify_threshold_type" field.
+ DefaultBalanceNotifyThresholdType string
// DefaultBalanceNotifyExtraEmails holds the default value on creation for the "balance_notify_extra_emails" field.
DefaultBalanceNotifyExtraEmails string
+ // DefaultTotalRecharged holds the default value on creation for the "total_recharged" field.
+ DefaultTotalRecharged float64
)
// OrderOption defines the ordering options for the User queries.
@@ -315,6 +325,11 @@ func ByBalanceNotifyEnabled(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldBalanceNotifyEnabled, opts...).ToFunc()
}
+// ByBalanceNotifyThresholdType orders the results by the balance_notify_threshold_type field.
+func ByBalanceNotifyThresholdType(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldBalanceNotifyThresholdType, opts...).ToFunc()
+}
+
// ByBalanceNotifyThreshold orders the results by the balance_notify_threshold field.
func ByBalanceNotifyThreshold(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldBalanceNotifyThreshold, opts...).ToFunc()
@@ -325,6 +340,11 @@ func ByBalanceNotifyExtraEmails(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldBalanceNotifyExtraEmails, opts...).ToFunc()
}
+// ByTotalRecharged orders the results by the total_recharged field.
+func ByTotalRecharged(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldTotalRecharged, opts...).ToFunc()
+}
+
// ByAPIKeysCount orders the results by api_keys count.
func ByAPIKeysCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
diff --git a/backend/ent/user/where.go b/backend/ent/user/where.go
index 11a0318f38..2788aa7adc 100644
--- a/backend/ent/user/where.go
+++ b/backend/ent/user/where.go
@@ -130,6 +130,11 @@ func BalanceNotifyEnabled(v bool) predicate.User {
return predicate.User(sql.FieldEQ(FieldBalanceNotifyEnabled, v))
}
+// BalanceNotifyThresholdType applies equality check predicate on the "balance_notify_threshold_type" field. It's identical to BalanceNotifyThresholdTypeEQ.
+func BalanceNotifyThresholdType(v string) predicate.User {
+ return predicate.User(sql.FieldEQ(FieldBalanceNotifyThresholdType, v))
+}
+
// BalanceNotifyThreshold applies equality check predicate on the "balance_notify_threshold" field. It's identical to BalanceNotifyThresholdEQ.
func BalanceNotifyThreshold(v float64) predicate.User {
return predicate.User(sql.FieldEQ(FieldBalanceNotifyThreshold, v))
@@ -140,6 +145,11 @@ func BalanceNotifyExtraEmails(v string) predicate.User {
return predicate.User(sql.FieldEQ(FieldBalanceNotifyExtraEmails, v))
}
+// TotalRecharged applies equality check predicate on the "total_recharged" field. It's identical to TotalRechargedEQ.
+func TotalRecharged(v float64) predicate.User {
+ return predicate.User(sql.FieldEQ(FieldTotalRecharged, v))
+}
+
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.User {
return predicate.User(sql.FieldEQ(FieldCreatedAt, v))
@@ -885,6 +895,71 @@ func BalanceNotifyEnabledNEQ(v bool) predicate.User {
return predicate.User(sql.FieldNEQ(FieldBalanceNotifyEnabled, v))
}
+// BalanceNotifyThresholdTypeEQ applies the EQ predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeEQ(v string) predicate.User {
+ return predicate.User(sql.FieldEQ(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeNEQ applies the NEQ predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeNEQ(v string) predicate.User {
+ return predicate.User(sql.FieldNEQ(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeIn applies the In predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeIn(vs ...string) predicate.User {
+ return predicate.User(sql.FieldIn(FieldBalanceNotifyThresholdType, vs...))
+}
+
+// BalanceNotifyThresholdTypeNotIn applies the NotIn predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeNotIn(vs ...string) predicate.User {
+ return predicate.User(sql.FieldNotIn(FieldBalanceNotifyThresholdType, vs...))
+}
+
+// BalanceNotifyThresholdTypeGT applies the GT predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeGT(v string) predicate.User {
+ return predicate.User(sql.FieldGT(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeGTE applies the GTE predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeGTE(v string) predicate.User {
+ return predicate.User(sql.FieldGTE(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeLT applies the LT predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeLT(v string) predicate.User {
+ return predicate.User(sql.FieldLT(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeLTE applies the LTE predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeLTE(v string) predicate.User {
+ return predicate.User(sql.FieldLTE(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeContains applies the Contains predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeContains(v string) predicate.User {
+ return predicate.User(sql.FieldContains(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeHasPrefix applies the HasPrefix predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeHasPrefix(v string) predicate.User {
+ return predicate.User(sql.FieldHasPrefix(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeHasSuffix applies the HasSuffix predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeHasSuffix(v string) predicate.User {
+ return predicate.User(sql.FieldHasSuffix(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeEqualFold applies the EqualFold predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeEqualFold(v string) predicate.User {
+ return predicate.User(sql.FieldEqualFold(FieldBalanceNotifyThresholdType, v))
+}
+
+// BalanceNotifyThresholdTypeContainsFold applies the ContainsFold predicate on the "balance_notify_threshold_type" field.
+func BalanceNotifyThresholdTypeContainsFold(v string) predicate.User {
+ return predicate.User(sql.FieldContainsFold(FieldBalanceNotifyThresholdType, v))
+}
+
// BalanceNotifyThresholdEQ applies the EQ predicate on the "balance_notify_threshold" field.
func BalanceNotifyThresholdEQ(v float64) predicate.User {
return predicate.User(sql.FieldEQ(FieldBalanceNotifyThreshold, v))
@@ -1000,6 +1075,46 @@ func BalanceNotifyExtraEmailsContainsFold(v string) predicate.User {
return predicate.User(sql.FieldContainsFold(FieldBalanceNotifyExtraEmails, v))
}
+// TotalRechargedEQ applies the EQ predicate on the "total_recharged" field.
+func TotalRechargedEQ(v float64) predicate.User {
+ return predicate.User(sql.FieldEQ(FieldTotalRecharged, v))
+}
+
+// TotalRechargedNEQ applies the NEQ predicate on the "total_recharged" field.
+func TotalRechargedNEQ(v float64) predicate.User {
+ return predicate.User(sql.FieldNEQ(FieldTotalRecharged, v))
+}
+
+// TotalRechargedIn applies the In predicate on the "total_recharged" field.
+func TotalRechargedIn(vs ...float64) predicate.User {
+ return predicate.User(sql.FieldIn(FieldTotalRecharged, vs...))
+}
+
+// TotalRechargedNotIn applies the NotIn predicate on the "total_recharged" field.
+func TotalRechargedNotIn(vs ...float64) predicate.User {
+ return predicate.User(sql.FieldNotIn(FieldTotalRecharged, vs...))
+}
+
+// TotalRechargedGT applies the GT predicate on the "total_recharged" field.
+func TotalRechargedGT(v float64) predicate.User {
+ return predicate.User(sql.FieldGT(FieldTotalRecharged, v))
+}
+
+// TotalRechargedGTE applies the GTE predicate on the "total_recharged" field.
+func TotalRechargedGTE(v float64) predicate.User {
+ return predicate.User(sql.FieldGTE(FieldTotalRecharged, v))
+}
+
+// TotalRechargedLT applies the LT predicate on the "total_recharged" field.
+func TotalRechargedLT(v float64) predicate.User {
+ return predicate.User(sql.FieldLT(FieldTotalRecharged, v))
+}
+
+// TotalRechargedLTE applies the LTE predicate on the "total_recharged" field.
+func TotalRechargedLTE(v float64) predicate.User {
+ return predicate.User(sql.FieldLTE(FieldTotalRecharged, v))
+}
+
// HasAPIKeys applies the HasEdge predicate on the "api_keys" edge.
func HasAPIKeys() predicate.User {
return predicate.User(func(s *sql.Selector) {
diff --git a/backend/ent/user_create.go b/backend/ent/user_create.go
index 955fde7269..fbc64f9c46 100644
--- a/backend/ent/user_create.go
+++ b/backend/ent/user_create.go
@@ -225,6 +225,20 @@ func (_c *UserCreate) SetNillableBalanceNotifyEnabled(v *bool) *UserCreate {
return _c
}
+// SetBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field.
+func (_c *UserCreate) SetBalanceNotifyThresholdType(v string) *UserCreate {
+ _c.mutation.SetBalanceNotifyThresholdType(v)
+ return _c
+}
+
+// SetNillableBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field if the given value is not nil.
+func (_c *UserCreate) SetNillableBalanceNotifyThresholdType(v *string) *UserCreate {
+ if v != nil {
+ _c.SetBalanceNotifyThresholdType(*v)
+ }
+ return _c
+}
+
// SetBalanceNotifyThreshold sets the "balance_notify_threshold" field.
func (_c *UserCreate) SetBalanceNotifyThreshold(v float64) *UserCreate {
_c.mutation.SetBalanceNotifyThreshold(v)
@@ -253,6 +267,20 @@ func (_c *UserCreate) SetNillableBalanceNotifyExtraEmails(v *string) *UserCreate
return _c
}
+// SetTotalRecharged sets the "total_recharged" field.
+func (_c *UserCreate) SetTotalRecharged(v float64) *UserCreate {
+ _c.mutation.SetTotalRecharged(v)
+ return _c
+}
+
+// SetNillableTotalRecharged sets the "total_recharged" field if the given value is not nil.
+func (_c *UserCreate) SetNillableTotalRecharged(v *float64) *UserCreate {
+ if v != nil {
+ _c.SetTotalRecharged(*v)
+ }
+ return _c
+}
+
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func (_c *UserCreate) AddAPIKeyIDs(ids ...int64) *UserCreate {
_c.mutation.AddAPIKeyIDs(ids...)
@@ -486,10 +514,18 @@ func (_c *UserCreate) defaults() error {
v := user.DefaultBalanceNotifyEnabled
_c.mutation.SetBalanceNotifyEnabled(v)
}
+ if _, ok := _c.mutation.BalanceNotifyThresholdType(); !ok {
+ v := user.DefaultBalanceNotifyThresholdType
+ _c.mutation.SetBalanceNotifyThresholdType(v)
+ }
if _, ok := _c.mutation.BalanceNotifyExtraEmails(); !ok {
v := user.DefaultBalanceNotifyExtraEmails
_c.mutation.SetBalanceNotifyExtraEmails(v)
}
+ if _, ok := _c.mutation.TotalRecharged(); !ok {
+ v := user.DefaultTotalRecharged
+ _c.mutation.SetTotalRecharged(v)
+ }
return nil
}
@@ -556,9 +592,15 @@ func (_c *UserCreate) check() error {
if _, ok := _c.mutation.BalanceNotifyEnabled(); !ok {
return &ValidationError{Name: "balance_notify_enabled", err: errors.New(`ent: missing required field "User.balance_notify_enabled"`)}
}
+ if _, ok := _c.mutation.BalanceNotifyThresholdType(); !ok {
+ return &ValidationError{Name: "balance_notify_threshold_type", err: errors.New(`ent: missing required field "User.balance_notify_threshold_type"`)}
+ }
if _, ok := _c.mutation.BalanceNotifyExtraEmails(); !ok {
return &ValidationError{Name: "balance_notify_extra_emails", err: errors.New(`ent: missing required field "User.balance_notify_extra_emails"`)}
}
+ if _, ok := _c.mutation.TotalRecharged(); !ok {
+ return &ValidationError{Name: "total_recharged", err: errors.New(`ent: missing required field "User.total_recharged"`)}
+ }
return nil
}
@@ -646,6 +688,10 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
_spec.SetField(user.FieldBalanceNotifyEnabled, field.TypeBool, value)
_node.BalanceNotifyEnabled = value
}
+ if value, ok := _c.mutation.BalanceNotifyThresholdType(); ok {
+ _spec.SetField(user.FieldBalanceNotifyThresholdType, field.TypeString, value)
+ _node.BalanceNotifyThresholdType = value
+ }
if value, ok := _c.mutation.BalanceNotifyThreshold(); ok {
_spec.SetField(user.FieldBalanceNotifyThreshold, field.TypeFloat64, value)
_node.BalanceNotifyThreshold = &value
@@ -654,6 +700,10 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
_spec.SetField(user.FieldBalanceNotifyExtraEmails, field.TypeString, value)
_node.BalanceNotifyExtraEmails = value
}
+ if value, ok := _c.mutation.TotalRecharged(); ok {
+ _spec.SetField(user.FieldTotalRecharged, field.TypeFloat64, value)
+ _node.TotalRecharged = value
+ }
if nodes := _c.mutation.APIKeysIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
@@ -1068,6 +1118,18 @@ func (u *UserUpsert) UpdateBalanceNotifyEnabled() *UserUpsert {
return u
}
+// SetBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field.
+func (u *UserUpsert) SetBalanceNotifyThresholdType(v string) *UserUpsert {
+ u.Set(user.FieldBalanceNotifyThresholdType, v)
+ return u
+}
+
+// UpdateBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field to the value that was provided on create.
+func (u *UserUpsert) UpdateBalanceNotifyThresholdType() *UserUpsert {
+ u.SetExcluded(user.FieldBalanceNotifyThresholdType)
+ return u
+}
+
// SetBalanceNotifyThreshold sets the "balance_notify_threshold" field.
func (u *UserUpsert) SetBalanceNotifyThreshold(v float64) *UserUpsert {
u.Set(user.FieldBalanceNotifyThreshold, v)
@@ -1104,6 +1166,24 @@ func (u *UserUpsert) UpdateBalanceNotifyExtraEmails() *UserUpsert {
return u
}
+// SetTotalRecharged sets the "total_recharged" field.
+func (u *UserUpsert) SetTotalRecharged(v float64) *UserUpsert {
+ u.Set(user.FieldTotalRecharged, v)
+ return u
+}
+
+// UpdateTotalRecharged sets the "total_recharged" field to the value that was provided on create.
+func (u *UserUpsert) UpdateTotalRecharged() *UserUpsert {
+ u.SetExcluded(user.FieldTotalRecharged)
+ return u
+}
+
+// AddTotalRecharged adds v to the "total_recharged" field.
+func (u *UserUpsert) AddTotalRecharged(v float64) *UserUpsert {
+ u.Add(user.FieldTotalRecharged, v)
+ return u
+}
+
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// Using this option is equivalent to using:
//
@@ -1380,6 +1460,20 @@ func (u *UserUpsertOne) UpdateBalanceNotifyEnabled() *UserUpsertOne {
})
}
+// SetBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field.
+func (u *UserUpsertOne) SetBalanceNotifyThresholdType(v string) *UserUpsertOne {
+ return u.Update(func(s *UserUpsert) {
+ s.SetBalanceNotifyThresholdType(v)
+ })
+}
+
+// UpdateBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field to the value that was provided on create.
+func (u *UserUpsertOne) UpdateBalanceNotifyThresholdType() *UserUpsertOne {
+ return u.Update(func(s *UserUpsert) {
+ s.UpdateBalanceNotifyThresholdType()
+ })
+}
+
// SetBalanceNotifyThreshold sets the "balance_notify_threshold" field.
func (u *UserUpsertOne) SetBalanceNotifyThreshold(v float64) *UserUpsertOne {
return u.Update(func(s *UserUpsert) {
@@ -1422,6 +1516,27 @@ func (u *UserUpsertOne) UpdateBalanceNotifyExtraEmails() *UserUpsertOne {
})
}
+// SetTotalRecharged sets the "total_recharged" field.
+func (u *UserUpsertOne) SetTotalRecharged(v float64) *UserUpsertOne {
+ return u.Update(func(s *UserUpsert) {
+ s.SetTotalRecharged(v)
+ })
+}
+
+// AddTotalRecharged adds v to the "total_recharged" field.
+func (u *UserUpsertOne) AddTotalRecharged(v float64) *UserUpsertOne {
+ return u.Update(func(s *UserUpsert) {
+ s.AddTotalRecharged(v)
+ })
+}
+
+// UpdateTotalRecharged sets the "total_recharged" field to the value that was provided on create.
+func (u *UserUpsertOne) UpdateTotalRecharged() *UserUpsertOne {
+ return u.Update(func(s *UserUpsert) {
+ s.UpdateTotalRecharged()
+ })
+}
+
// Exec executes the query.
func (u *UserUpsertOne) Exec(ctx context.Context) error {
if len(u.create.conflict) == 0 {
@@ -1864,6 +1979,20 @@ func (u *UserUpsertBulk) UpdateBalanceNotifyEnabled() *UserUpsertBulk {
})
}
+// SetBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field.
+func (u *UserUpsertBulk) SetBalanceNotifyThresholdType(v string) *UserUpsertBulk {
+ return u.Update(func(s *UserUpsert) {
+ s.SetBalanceNotifyThresholdType(v)
+ })
+}
+
+// UpdateBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field to the value that was provided on create.
+func (u *UserUpsertBulk) UpdateBalanceNotifyThresholdType() *UserUpsertBulk {
+ return u.Update(func(s *UserUpsert) {
+ s.UpdateBalanceNotifyThresholdType()
+ })
+}
+
// SetBalanceNotifyThreshold sets the "balance_notify_threshold" field.
func (u *UserUpsertBulk) SetBalanceNotifyThreshold(v float64) *UserUpsertBulk {
return u.Update(func(s *UserUpsert) {
@@ -1906,6 +2035,27 @@ func (u *UserUpsertBulk) UpdateBalanceNotifyExtraEmails() *UserUpsertBulk {
})
}
+// SetTotalRecharged sets the "total_recharged" field.
+func (u *UserUpsertBulk) SetTotalRecharged(v float64) *UserUpsertBulk {
+ return u.Update(func(s *UserUpsert) {
+ s.SetTotalRecharged(v)
+ })
+}
+
+// AddTotalRecharged adds v to the "total_recharged" field.
+func (u *UserUpsertBulk) AddTotalRecharged(v float64) *UserUpsertBulk {
+ return u.Update(func(s *UserUpsert) {
+ s.AddTotalRecharged(v)
+ })
+}
+
+// UpdateTotalRecharged sets the "total_recharged" field to the value that was provided on create.
+func (u *UserUpsertBulk) UpdateTotalRecharged() *UserUpsertBulk {
+ return u.Update(func(s *UserUpsert) {
+ s.UpdateTotalRecharged()
+ })
+}
+
// Exec executes the query.
func (u *UserUpsertBulk) Exec(ctx context.Context) error {
if u.create.err != nil {
diff --git a/backend/ent/user_update.go b/backend/ent/user_update.go
index 823df0b69b..6b35524765 100644
--- a/backend/ent/user_update.go
+++ b/backend/ent/user_update.go
@@ -257,6 +257,20 @@ func (_u *UserUpdate) SetNillableBalanceNotifyEnabled(v *bool) *UserUpdate {
return _u
}
+// SetBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field.
+func (_u *UserUpdate) SetBalanceNotifyThresholdType(v string) *UserUpdate {
+ _u.mutation.SetBalanceNotifyThresholdType(v)
+ return _u
+}
+
+// SetNillableBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field if the given value is not nil.
+func (_u *UserUpdate) SetNillableBalanceNotifyThresholdType(v *string) *UserUpdate {
+ if v != nil {
+ _u.SetBalanceNotifyThresholdType(*v)
+ }
+ return _u
+}
+
// SetBalanceNotifyThreshold sets the "balance_notify_threshold" field.
func (_u *UserUpdate) SetBalanceNotifyThreshold(v float64) *UserUpdate {
_u.mutation.ResetBalanceNotifyThreshold()
@@ -298,6 +312,27 @@ func (_u *UserUpdate) SetNillableBalanceNotifyExtraEmails(v *string) *UserUpdate
return _u
}
+// SetTotalRecharged sets the "total_recharged" field.
+func (_u *UserUpdate) SetTotalRecharged(v float64) *UserUpdate {
+ _u.mutation.ResetTotalRecharged()
+ _u.mutation.SetTotalRecharged(v)
+ return _u
+}
+
+// SetNillableTotalRecharged sets the "total_recharged" field if the given value is not nil.
+func (_u *UserUpdate) SetNillableTotalRecharged(v *float64) *UserUpdate {
+ if v != nil {
+ _u.SetTotalRecharged(*v)
+ }
+ return _u
+}
+
+// AddTotalRecharged adds value to the "total_recharged" field.
+func (_u *UserUpdate) AddTotalRecharged(v float64) *UserUpdate {
+ _u.mutation.AddTotalRecharged(v)
+ return _u
+}
+
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func (_u *UserUpdate) AddAPIKeyIDs(ids ...int64) *UserUpdate {
_u.mutation.AddAPIKeyIDs(ids...)
@@ -804,6 +839,9 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if value, ok := _u.mutation.BalanceNotifyEnabled(); ok {
_spec.SetField(user.FieldBalanceNotifyEnabled, field.TypeBool, value)
}
+ if value, ok := _u.mutation.BalanceNotifyThresholdType(); ok {
+ _spec.SetField(user.FieldBalanceNotifyThresholdType, field.TypeString, value)
+ }
if value, ok := _u.mutation.BalanceNotifyThreshold(); ok {
_spec.SetField(user.FieldBalanceNotifyThreshold, field.TypeFloat64, value)
}
@@ -816,6 +854,12 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if value, ok := _u.mutation.BalanceNotifyExtraEmails(); ok {
_spec.SetField(user.FieldBalanceNotifyExtraEmails, field.TypeString, value)
}
+ if value, ok := _u.mutation.TotalRecharged(); ok {
+ _spec.SetField(user.FieldTotalRecharged, field.TypeFloat64, value)
+ }
+ if value, ok := _u.mutation.AddedTotalRecharged(); ok {
+ _spec.AddField(user.FieldTotalRecharged, field.TypeFloat64, value)
+ }
if _u.mutation.APIKeysCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
@@ -1518,6 +1562,20 @@ func (_u *UserUpdateOne) SetNillableBalanceNotifyEnabled(v *bool) *UserUpdateOne
return _u
}
+// SetBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field.
+func (_u *UserUpdateOne) SetBalanceNotifyThresholdType(v string) *UserUpdateOne {
+ _u.mutation.SetBalanceNotifyThresholdType(v)
+ return _u
+}
+
+// SetNillableBalanceNotifyThresholdType sets the "balance_notify_threshold_type" field if the given value is not nil.
+func (_u *UserUpdateOne) SetNillableBalanceNotifyThresholdType(v *string) *UserUpdateOne {
+ if v != nil {
+ _u.SetBalanceNotifyThresholdType(*v)
+ }
+ return _u
+}
+
// SetBalanceNotifyThreshold sets the "balance_notify_threshold" field.
func (_u *UserUpdateOne) SetBalanceNotifyThreshold(v float64) *UserUpdateOne {
_u.mutation.ResetBalanceNotifyThreshold()
@@ -1559,6 +1617,27 @@ func (_u *UserUpdateOne) SetNillableBalanceNotifyExtraEmails(v *string) *UserUpd
return _u
}
+// SetTotalRecharged sets the "total_recharged" field.
+func (_u *UserUpdateOne) SetTotalRecharged(v float64) *UserUpdateOne {
+ _u.mutation.ResetTotalRecharged()
+ _u.mutation.SetTotalRecharged(v)
+ return _u
+}
+
+// SetNillableTotalRecharged sets the "total_recharged" field if the given value is not nil.
+func (_u *UserUpdateOne) SetNillableTotalRecharged(v *float64) *UserUpdateOne {
+ if v != nil {
+ _u.SetTotalRecharged(*v)
+ }
+ return _u
+}
+
+// AddTotalRecharged adds value to the "total_recharged" field.
+func (_u *UserUpdateOne) AddTotalRecharged(v float64) *UserUpdateOne {
+ _u.mutation.AddTotalRecharged(v)
+ return _u
+}
+
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func (_u *UserUpdateOne) AddAPIKeyIDs(ids ...int64) *UserUpdateOne {
_u.mutation.AddAPIKeyIDs(ids...)
@@ -2095,6 +2174,9 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
if value, ok := _u.mutation.BalanceNotifyEnabled(); ok {
_spec.SetField(user.FieldBalanceNotifyEnabled, field.TypeBool, value)
}
+ if value, ok := _u.mutation.BalanceNotifyThresholdType(); ok {
+ _spec.SetField(user.FieldBalanceNotifyThresholdType, field.TypeString, value)
+ }
if value, ok := _u.mutation.BalanceNotifyThreshold(); ok {
_spec.SetField(user.FieldBalanceNotifyThreshold, field.TypeFloat64, value)
}
@@ -2107,6 +2189,12 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
if value, ok := _u.mutation.BalanceNotifyExtraEmails(); ok {
_spec.SetField(user.FieldBalanceNotifyExtraEmails, field.TypeString, value)
}
+ if value, ok := _u.mutation.TotalRecharged(); ok {
+ _spec.SetField(user.FieldTotalRecharged, field.TypeFloat64, value)
+ }
+ if value, ok := _u.mutation.AddedTotalRecharged(); ok {
+ _spec.AddField(user.FieldTotalRecharged, field.TypeFloat64, value)
+ }
if _u.mutation.APIKeysCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
diff --git a/backend/internal/handler/admin/setting_handler.go b/backend/internal/handler/admin/setting_handler.go
index 49e7aeed4e..3d587a217b 100644
--- a/backend/internal/handler/admin/setting_handler.go
+++ b/backend/internal/handler/admin/setting_handler.go
@@ -177,6 +177,7 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
EnableCCHSigning: settings.EnableCCHSigning,
WebSearchEmulationEnabled: settings.WebSearchEmulationEnabled,
BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
+ BalanceLowNotifyThresholdType: settings.BalanceLowNotifyThresholdType,
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
AccountQuotaNotifyEmails: settings.AccountQuotaNotifyEmails,
PaymentEnabled: paymentCfg.Enabled,
@@ -309,9 +310,10 @@ type UpdateSettingsRequest struct {
EnableCCHSigning *bool `json:"enable_cch_signing"`
// Balance low notification
- BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"`
- BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"`
- AccountQuotaNotifyEmails *[]string `json:"account_quota_notify_emails"`
+ BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"`
+ BalanceLowNotifyThresholdType *string `json:"balance_low_notify_threshold_type"`
+ BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"`
+ AccountQuotaNotifyEmails *[]string `json:"account_quota_notify_emails"`
// Payment configuration (integrated into settings, full replace)
PaymentEnabled *bool `json:"payment_enabled"`
@@ -896,6 +898,12 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}
return previousSettings.BalanceLowNotifyEnabled
}(),
+ BalanceLowNotifyThresholdType: func() string {
+ if req.BalanceLowNotifyThresholdType != nil {
+ return *req.BalanceLowNotifyThresholdType
+ }
+ return previousSettings.BalanceLowNotifyThresholdType
+ }(),
BalanceLowNotifyThreshold: func() float64 {
if req.BalanceLowNotifyThreshold != nil {
return *req.BalanceLowNotifyThreshold
@@ -1055,6 +1063,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
EnableMetadataPassthrough: updatedSettings.EnableMetadataPassthrough,
EnableCCHSigning: updatedSettings.EnableCCHSigning,
BalanceLowNotifyEnabled: updatedSettings.BalanceLowNotifyEnabled,
+ BalanceLowNotifyThresholdType: updatedSettings.BalanceLowNotifyThresholdType,
BalanceLowNotifyThreshold: updatedSettings.BalanceLowNotifyThreshold,
AccountQuotaNotifyEmails: updatedSettings.AccountQuotaNotifyEmails,
PaymentEnabled: updatedPaymentCfg.Enabled,
diff --git a/backend/internal/handler/dto/mappers.go b/backend/internal/handler/dto/mappers.go
index a465c7fbc3..147072c35d 100644
--- a/backend/internal/handler/dto/mappers.go
+++ b/backend/internal/handler/dto/mappers.go
@@ -13,19 +13,21 @@ func UserFromServiceShallow(u *service.User) *User {
return nil
}
return &User{
- ID: u.ID,
- Email: u.Email,
- Username: u.Username,
- Role: u.Role,
- Balance: u.Balance,
- Concurrency: u.Concurrency,
- Status: u.Status,
- AllowedGroups: u.AllowedGroups,
- CreatedAt: u.CreatedAt,
- UpdatedAt: u.UpdatedAt,
- BalanceNotifyEnabled: u.BalanceNotifyEnabled,
- BalanceNotifyThreshold: u.BalanceNotifyThreshold,
- BalanceNotifyExtraEmails: u.BalanceNotifyExtraEmails,
+ ID: u.ID,
+ Email: u.Email,
+ Username: u.Username,
+ Role: u.Role,
+ Balance: u.Balance,
+ Concurrency: u.Concurrency,
+ Status: u.Status,
+ AllowedGroups: u.AllowedGroups,
+ CreatedAt: u.CreatedAt,
+ UpdatedAt: u.UpdatedAt,
+ BalanceNotifyEnabled: u.BalanceNotifyEnabled,
+ BalanceNotifyThresholdType: u.BalanceNotifyThresholdType,
+ BalanceNotifyThreshold: u.BalanceNotifyThreshold,
+ BalanceNotifyExtraEmails: u.BalanceNotifyExtraEmails,
+ TotalRecharged: u.TotalRecharged,
}
}
diff --git a/backend/internal/handler/dto/settings.go b/backend/internal/handler/dto/settings.go
index e29f72daaf..8da7c6f255 100644
--- a/backend/internal/handler/dto/settings.go
+++ b/backend/internal/handler/dto/settings.go
@@ -150,9 +150,10 @@ type SystemSettings struct {
PaymentCancelRateLimitMode string `json:"payment_cancel_rate_limit_window_mode"`
// Balance low notification
- BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"`
- BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"`
- AccountQuotaNotifyEmails []string `json:"account_quota_notify_emails"`
+ BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"`
+ BalanceLowNotifyThresholdType string `json:"balance_low_notify_threshold_type"`
+ BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"`
+ AccountQuotaNotifyEmails []string `json:"account_quota_notify_emails"`
}
type DefaultSubscriptionSetting struct {
diff --git a/backend/internal/handler/dto/types.go b/backend/internal/handler/dto/types.go
index 18522868a2..425d3df9e4 100644
--- a/backend/internal/handler/dto/types.go
+++ b/backend/internal/handler/dto/types.go
@@ -19,9 +19,11 @@ type User struct {
UpdatedAt time.Time `json:"updated_at"`
// 余额不足通知
- BalanceNotifyEnabled bool `json:"balance_notify_enabled"`
- BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
- BalanceNotifyExtraEmails []string `json:"balance_notify_extra_emails"`
+ BalanceNotifyEnabled bool `json:"balance_notify_enabled"`
+ BalanceNotifyThresholdType string `json:"balance_notify_threshold_type"`
+ BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
+ BalanceNotifyExtraEmails []string `json:"balance_notify_extra_emails"`
+ TotalRecharged float64 `json:"total_recharged"`
APIKeys []APIKey `json:"api_keys,omitempty"`
Subscriptions []UserSubscription `json:"subscriptions,omitempty"`
diff --git a/backend/internal/handler/user_handler.go b/backend/internal/handler/user_handler.go
index 4fb72ce776..48528d553d 100644
--- a/backend/internal/handler/user_handler.go
+++ b/backend/internal/handler/user_handler.go
@@ -33,9 +33,10 @@ type ChangePasswordRequest struct {
// UpdateProfileRequest represents the update profile request payload
type UpdateProfileRequest struct {
- Username *string `json:"username"`
- BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
- BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
+ Username *string `json:"username"`
+ BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
+ BalanceNotifyThresholdType *string `json:"balance_notify_threshold_type"`
+ BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
}
// GetProfile handles getting user profile
@@ -100,9 +101,10 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
}
svcReq := service.UpdateProfileRequest{
- Username: req.Username,
- BalanceNotifyEnabled: req.BalanceNotifyEnabled,
- BalanceNotifyThreshold: req.BalanceNotifyThreshold,
+ Username: req.Username,
+ BalanceNotifyEnabled: req.BalanceNotifyEnabled,
+ BalanceNotifyThresholdType: req.BalanceNotifyThresholdType,
+ BalanceNotifyThreshold: req.BalanceNotifyThreshold,
}
updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq)
if err != nil {
diff --git a/backend/internal/repository/api_key_repo.go b/backend/internal/repository/api_key_repo.go
index 752a5937f9..4ecab47abe 100644
--- a/backend/internal/repository/api_key_repo.go
+++ b/backend/internal/repository/api_key_repo.go
@@ -641,22 +641,24 @@ func userEntityToService(u *dbent.User) *service.User {
return nil
}
out := &service.User{
- ID: u.ID,
- Email: u.Email,
- Username: u.Username,
- Notes: u.Notes,
- PasswordHash: u.PasswordHash,
- Role: u.Role,
- Balance: u.Balance,
- Concurrency: u.Concurrency,
- Status: u.Status,
- TotpSecretEncrypted: u.TotpSecretEncrypted,
- TotpEnabled: u.TotpEnabled,
- TotpEnabledAt: u.TotpEnabledAt,
- BalanceNotifyEnabled: u.BalanceNotifyEnabled,
- BalanceNotifyThreshold: u.BalanceNotifyThreshold,
- CreatedAt: u.CreatedAt,
- UpdatedAt: u.UpdatedAt,
+ ID: u.ID,
+ Email: u.Email,
+ Username: u.Username,
+ Notes: u.Notes,
+ PasswordHash: u.PasswordHash,
+ Role: u.Role,
+ Balance: u.Balance,
+ Concurrency: u.Concurrency,
+ Status: u.Status,
+ TotpSecretEncrypted: u.TotpSecretEncrypted,
+ TotpEnabled: u.TotpEnabled,
+ TotpEnabledAt: u.TotpEnabledAt,
+ BalanceNotifyEnabled: u.BalanceNotifyEnabled,
+ BalanceNotifyThresholdType: u.BalanceNotifyThresholdType,
+ BalanceNotifyThreshold: u.BalanceNotifyThreshold,
+ TotalRecharged: u.TotalRecharged,
+ CreatedAt: u.CreatedAt,
+ UpdatedAt: u.UpdatedAt,
}
// Parse extra emails JSON array
if u.BalanceNotifyExtraEmails != "" && u.BalanceNotifyExtraEmails != "[]" {
diff --git a/backend/internal/repository/user_repo.go b/backend/internal/repository/user_repo.go
index 2c54485703..63168fb111 100644
--- a/backend/internal/repository/user_repo.go
+++ b/backend/internal/repository/user_repo.go
@@ -148,6 +148,7 @@ func (r *userRepository) Update(ctx context.Context, userIn *service.User) error
SetConcurrency(userIn.Concurrency).
SetStatus(userIn.Status).
SetBalanceNotifyEnabled(userIn.BalanceNotifyEnabled).
+ SetBalanceNotifyThresholdType(userIn.BalanceNotifyThresholdType).
SetNillableBalanceNotifyThreshold(userIn.BalanceNotifyThreshold).
SetBalanceNotifyExtraEmails(marshalExtraEmails(userIn.BalanceNotifyExtraEmails))
if userIn.BalanceNotifyThreshold == nil {
@@ -389,7 +390,12 @@ func (r *userRepository) filterUsersByAttributes(ctx context.Context, attrs map[
func (r *userRepository) UpdateBalance(ctx context.Context, id int64, amount float64) error {
client := clientFromContext(ctx, r.client)
- n, err := client.User.Update().Where(dbuser.IDEQ(id)).AddBalance(amount).Save(ctx)
+ update := client.User.Update().Where(dbuser.IDEQ(id)).AddBalance(amount)
+ // Track cumulative recharge amount for percentage-based notifications
+ if amount > 0 {
+ update = update.AddTotalRecharged(amount)
+ }
+ n, err := update.Save(ctx)
if err != nil {
return translatePersistenceError(err, service.ErrUserNotFound, nil)
}
diff --git a/backend/internal/service/balance_notify_service.go b/backend/internal/service/balance_notify_service.go
index 8dd56b8f91..7fbdd25403 100644
--- a/backend/internal/service/balance_notify_service.go
+++ b/backend/internal/service/balance_notify_service.go
@@ -47,30 +47,21 @@ func (s *BalanceNotifyService) CheckBalanceAfterDeduction(ctx context.Context, u
if user == nil || s.emailService == nil || s.settingRepo == nil {
return
}
-
- // Check user-level switch
if !user.BalanceNotifyEnabled {
return
}
- // Check global switch
- globalEnabled, threshold := s.getBalanceNotifyConfig(ctx)
+ globalEnabled, globalThresholdType, globalThresholdValue := s.getBalanceNotifyConfig(ctx)
if !globalEnabled {
return
}
- // User custom threshold overrides system default
- if user.BalanceNotifyThreshold != nil {
- threshold = *user.BalanceNotifyThreshold
- }
-
+ threshold := s.resolveEffectiveThreshold(user, globalThresholdType, globalThresholdValue)
if threshold <= 0 {
return
}
newBalance := oldBalance - cost
-
- // Only notify on first crossing
if oldBalance >= threshold && newBalance < threshold {
siteName := s.getSiteName(ctx)
recipients := s.collectBalanceNotifyRecipients(user)
@@ -85,6 +76,30 @@ func (s *BalanceNotifyService) CheckBalanceAfterDeduction(ctx context.Context, u
}
}
+// resolveEffectiveThreshold computes the actual USD threshold based on type and user settings.
+func (s *BalanceNotifyService) resolveEffectiveThreshold(user *User, globalType string, globalValue float64) float64 {
+ // User-level override takes full precedence
+ if user.BalanceNotifyThreshold != nil {
+ thresholdType := user.BalanceNotifyThresholdType
+ if thresholdType == "" {
+ thresholdType = globalType
+ }
+ return computeThreshold(thresholdType, *user.BalanceNotifyThreshold, user.TotalRecharged)
+ }
+ return computeThreshold(globalType, globalValue, user.TotalRecharged)
+}
+
+// computeThreshold converts a threshold value to USD based on type.
+func computeThreshold(thresholdType string, value, totalRecharged float64) float64 {
+ if thresholdType == ThresholdTypePercentage {
+ if totalRecharged <= 0 {
+ return 0 // no recharge history → skip percentage check
+ }
+ return totalRecharged * value / 100
+ }
+ return value // fixed USD amount
+}
+
// quotaDim describes one quota dimension for notification checking.
type quotaDim struct {
name string
@@ -139,13 +154,21 @@ func (s *BalanceNotifyService) asyncSendQuotaAlert(adminEmails []string, account
}
// getBalanceNotifyConfig reads global balance notification settings.
-func (s *BalanceNotifyService) getBalanceNotifyConfig(ctx context.Context) (enabled bool, threshold float64) {
- keys := []string{SettingKeyBalanceLowNotifyEnabled, SettingKeyBalanceLowNotifyThreshold}
+func (s *BalanceNotifyService) getBalanceNotifyConfig(ctx context.Context) (enabled bool, thresholdType string, threshold float64) {
+ keys := []string{
+ SettingKeyBalanceLowNotifyEnabled,
+ SettingKeyBalanceLowNotifyThresholdType,
+ SettingKeyBalanceLowNotifyThreshold,
+ }
settings, err := s.settingRepo.GetMultiple(ctx, keys)
if err != nil {
- return false, 0
+ return false, ThresholdTypeFixed, 0
}
enabled = settings[SettingKeyBalanceLowNotifyEnabled] == "true"
+ thresholdType = settings[SettingKeyBalanceLowNotifyThresholdType]
+ if thresholdType == "" {
+ thresholdType = ThresholdTypeFixed
+ }
if v := settings[SettingKeyBalanceLowNotifyThreshold]; v != "" {
if f, err := strconv.ParseFloat(v, 64); err == nil {
threshold = f
diff --git a/backend/internal/service/domain_constants.go b/backend/internal/service/domain_constants.go
index 4c2cfd5341..9bef1ebd67 100644
--- a/backend/internal/service/domain_constants.go
+++ b/backend/internal/service/domain_constants.go
@@ -255,8 +255,13 @@ const (
SettingKeyWebSearchEmulationConfig = "web_search_emulation_config"
// Balance Low Notification
- SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关
- SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD)
+ SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关
+ SettingKeyBalanceLowNotifyThresholdType = "balance_low_notify_threshold_type" // "fixed" | "percentage"
+ SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD 或百分比)
+
+ // Threshold type constants
+ ThresholdTypeFixed = "fixed"
+ ThresholdTypePercentage = "percentage"
// Account Quota Notification
SettingKeyAccountQuotaNotifyEmails = "account_quota_notify_emails" // 管理员通知邮箱列表(JSON 数组)
diff --git a/backend/internal/service/setting_service.go b/backend/internal/service/setting_service.go
index ab4d160c4a..a584b485f0 100644
--- a/backend/internal/service/setting_service.go
+++ b/backend/internal/service/setting_service.go
@@ -601,6 +601,11 @@ func (s *SettingService) UpdateSettings(ctx context.Context, settings *SystemSet
// Balance low notification
updates[SettingKeyBalanceLowNotifyEnabled] = strconv.FormatBool(settings.BalanceLowNotifyEnabled)
+ thresholdType := settings.BalanceLowNotifyThresholdType
+ if thresholdType == "" {
+ thresholdType = ThresholdTypeFixed
+ }
+ updates[SettingKeyBalanceLowNotifyThresholdType] = thresholdType
updates[SettingKeyBalanceLowNotifyThreshold] = strconv.FormatFloat(settings.BalanceLowNotifyThreshold, 'f', 8, 64)
accountQuotaNotifyEmailsJSON, err := json.Marshal(settings.AccountQuotaNotifyEmails)
if err != nil {
@@ -1240,6 +1245,10 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
// Balance low notification
result.BalanceLowNotifyEnabled = settings[SettingKeyBalanceLowNotifyEnabled] == "true"
+ result.BalanceLowNotifyThresholdType = settings[SettingKeyBalanceLowNotifyThresholdType]
+ if result.BalanceLowNotifyThresholdType == "" {
+ result.BalanceLowNotifyThresholdType = ThresholdTypeFixed
+ }
if v, err := strconv.ParseFloat(settings[SettingKeyBalanceLowNotifyThreshold], 64); err == nil && v >= 0 {
result.BalanceLowNotifyThreshold = v
}
diff --git a/backend/internal/service/settings_view.go b/backend/internal/service/settings_view.go
index 963215b64d..74ce9f421b 100644
--- a/backend/internal/service/settings_view.go
+++ b/backend/internal/service/settings_view.go
@@ -111,8 +111,9 @@ type SystemSettings struct {
WebSearchEmulationEnabled bool
// Balance low notification
- BalanceLowNotifyEnabled bool
- BalanceLowNotifyThreshold float64
+ BalanceLowNotifyEnabled bool
+ BalanceLowNotifyThresholdType string // "fixed" (default) | "percentage"
+ BalanceLowNotifyThreshold float64
// Account quota notification
AccountQuotaNotifyEmails []string
diff --git a/backend/internal/service/user.go b/backend/internal/service/user.go
index b4818223f7..4ca31adc63 100644
--- a/backend/internal/service/user.go
+++ b/backend/internal/service/user.go
@@ -31,9 +31,11 @@ type User struct {
TotpEnabledAt *time.Time // TOTP 启用时间
// 余额不足通知
- BalanceNotifyEnabled bool
- BalanceNotifyThreshold *float64
- BalanceNotifyExtraEmails []string
+ BalanceNotifyEnabled bool
+ BalanceNotifyThresholdType string // "fixed" (default) | "percentage"
+ BalanceNotifyThreshold *float64
+ BalanceNotifyExtraEmails []string
+ TotalRecharged float64
APIKeys []APIKey
Subscriptions []UserSubscription
diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go
index e6b9a2104d..4669cb2b7d 100644
--- a/backend/internal/service/user_service.go
+++ b/backend/internal/service/user_service.go
@@ -62,11 +62,12 @@ type UserRepository interface {
// UpdateProfileRequest 更新用户资料请求
type UpdateProfileRequest struct {
- Email *string `json:"email"`
- Username *string `json:"username"`
- Concurrency *int `json:"concurrency"`
- BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
- BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
+ Email *string `json:"email"`
+ Username *string `json:"username"`
+ Concurrency *int `json:"concurrency"`
+ BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
+ BalanceNotifyThresholdType *string `json:"balance_notify_threshold_type"`
+ BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
}
// ChangePasswordRequest 修改密码请求
@@ -143,6 +144,9 @@ func (s *UserService) UpdateProfile(ctx context.Context, userID int64, req Updat
if req.BalanceNotifyEnabled != nil {
user.BalanceNotifyEnabled = *req.BalanceNotifyEnabled
}
+ if req.BalanceNotifyThresholdType != nil {
+ user.BalanceNotifyThresholdType = *req.BalanceNotifyThresholdType
+ }
if req.BalanceNotifyThreshold != nil {
if *req.BalanceNotifyThreshold <= 0 {
user.BalanceNotifyThreshold = nil // clear to system default
diff --git a/backend/migrations/102_add_balance_notify_threshold_type.sql b/backend/migrations/102_add_balance_notify_threshold_type.sql
new file mode 100644
index 0000000000..7ad705520b
--- /dev/null
+++ b/backend/migrations/102_add_balance_notify_threshold_type.sql
@@ -0,0 +1,4 @@
+-- Add threshold type support (fixed / percentage) to balance notification
+ALTER TABLE users ADD COLUMN IF NOT EXISTS balance_notify_threshold_type VARCHAR(10) NOT NULL DEFAULT 'fixed';
+-- Track cumulative recharge amount for percentage threshold calculation
+ALTER TABLE users ADD COLUMN IF NOT EXISTS total_recharged DECIMAL(20,8) NOT NULL DEFAULT 0;
diff --git a/frontend/src/api/admin/settings.ts b/frontend/src/api/admin/settings.ts
index 31284289cd..ec290be56f 100644
--- a/frontend/src/api/admin/settings.ts
+++ b/frontend/src/api/admin/settings.ts
@@ -137,6 +137,7 @@ export interface SystemSettings {
// Balance & quota notification
balance_low_notify_enabled: boolean
+ balance_low_notify_threshold_type: 'fixed' | 'percentage'
balance_low_notify_threshold: number
account_quota_notify_emails: string[]
}
@@ -240,6 +241,7 @@ export interface UpdateSettingsRequest {
payment_cancel_rate_limit_window_mode?: string
// Balance & quota notification
balance_low_notify_enabled?: boolean
+ balance_low_notify_threshold_type?: 'fixed' | 'percentage'
balance_low_notify_threshold?: number
account_quota_notify_emails?: string[]
}
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index ca21b1cd68..d321be2c4c 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -4588,8 +4588,12 @@ export default {
title: 'Balance Low Notification',
description: 'Send email notification when user balance falls below threshold',
enabled: 'Enable Balance Low Notification',
+ thresholdType: 'Threshold Type',
+ typeFixed: 'Fixed Amount',
+ typePercentage: 'Percentage of Recharged',
threshold: 'Default Threshold',
thresholdHint: 'Used when user has not set a custom value',
+ percentageHint: 'Notify when balance falls below this percentage of total recharged amount',
thresholdPlaceholder: 'Enter amount',
},
quotaNotify: {
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index 97cbf5e7ac..b388e69c4b 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -4760,8 +4760,12 @@ export default {
title: '余额不足提醒',
description: '当用户余额低于阈值时发送邮件提醒',
enabled: '启用余额不足提醒',
- threshold: '默认提醒阈值',
+ thresholdType: '阈值类型',
+ typeFixed: '固定金额',
+ typePercentage: '充值百分比',
+ threshold: '提醒阈值',
thresholdHint: '用户未自定义时使用此值',
+ percentageHint: '当余额低于累计充值额的此百分比时提醒',
thresholdPlaceholder: '输入金额',
},
quotaNotify: {
diff --git a/frontend/src/views/admin/SettingsView.vue b/frontend/src/views/admin/SettingsView.vue
index 4f8caa9d9f..af84b67de4 100644
--- a/frontend/src/views/admin/SettingsView.vue
+++ b/frontend/src/views/admin/SettingsView.vue
@@ -2675,13 +2675,43 @@
+ {{ form.balance_low_notify_threshold_type === 'percentage' + ? t('admin.settings.balanceNotify.percentageHint') + : t('admin.settings.balanceNotify.thresholdHint') }} +
{{ t('admin.settings.balanceNotify.thresholdHint') }}