mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-05-04 21:20:51 +08:00
feat(notify): add percentage threshold type for balance low notification
- Add threshold_type field (fixed/percentage) to system and user settings - Add total_recharged field to users table, auto-incremented on balance credit - Percentage mode: effective threshold = total_recharged × percentage / 100 - User-level threshold_type inherits from system default when not set - Update admin settings UI with radio selector (fixed amount / percentage) - Migration: 102_add_balance_notify_threshold_type.sql
This commit is contained in:
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -176,6 +176,10 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
|
||||
EnableMetadataPassthrough: settings.EnableMetadataPassthrough,
|
||||
EnableCCHSigning: settings.EnableCCHSigning,
|
||||
WebSearchEmulationEnabled: settings.WebSearchEmulationEnabled,
|
||||
BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
|
||||
BalanceLowNotifyThresholdType: settings.BalanceLowNotifyThresholdType,
|
||||
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
|
||||
AccountQuotaNotifyEmails: settings.AccountQuotaNotifyEmails,
|
||||
PaymentEnabled: paymentCfg.Enabled,
|
||||
PaymentMinAmount: paymentCfg.MinAmount,
|
||||
PaymentMaxAmount: paymentCfg.MaxAmount,
|
||||
@@ -305,6 +309,12 @@ type UpdateSettingsRequest struct {
|
||||
EnableMetadataPassthrough *bool `json:"enable_metadata_passthrough"`
|
||||
EnableCCHSigning *bool `json:"enable_cch_signing"`
|
||||
|
||||
// Balance low notification
|
||||
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"`
|
||||
PaymentMinAmount *float64 `json:"payment_min_amount"`
|
||||
@@ -882,6 +892,30 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
|
||||
}
|
||||
return previousSettings.EnableCCHSigning
|
||||
}(),
|
||||
BalanceLowNotifyEnabled: func() bool {
|
||||
if req.BalanceLowNotifyEnabled != nil {
|
||||
return *req.BalanceLowNotifyEnabled
|
||||
}
|
||||
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
|
||||
}
|
||||
return previousSettings.BalanceLowNotifyThreshold
|
||||
}(),
|
||||
AccountQuotaNotifyEmails: func() []string {
|
||||
if req.AccountQuotaNotifyEmails != nil {
|
||||
return *req.AccountQuotaNotifyEmails
|
||||
}
|
||||
return previousSettings.AccountQuotaNotifyEmails
|
||||
}(),
|
||||
}
|
||||
|
||||
if err := h.settingService.UpdateSettings(c.Request.Context(), settings); err != nil {
|
||||
@@ -1028,6 +1062,10 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
|
||||
EnableFingerprintUnification: updatedSettings.EnableFingerprintUnification,
|
||||
EnableMetadataPassthrough: updatedSettings.EnableMetadataPassthrough,
|
||||
EnableCCHSigning: updatedSettings.EnableCCHSigning,
|
||||
BalanceLowNotifyEnabled: updatedSettings.BalanceLowNotifyEnabled,
|
||||
BalanceLowNotifyThresholdType: updatedSettings.BalanceLowNotifyThresholdType,
|
||||
BalanceLowNotifyThreshold: updatedSettings.BalanceLowNotifyThreshold,
|
||||
AccountQuotaNotifyEmails: updatedSettings.AccountQuotaNotifyEmails,
|
||||
PaymentEnabled: updatedPaymentCfg.Enabled,
|
||||
PaymentMinAmount: updatedPaymentCfg.MinAmount,
|
||||
PaymentMaxAmount: updatedPaymentCfg.MaxAmount,
|
||||
|
||||
@@ -24,8 +24,10 @@ func UserFromServiceShallow(u *service.User) *User {
|
||||
CreatedAt: u.CreatedAt,
|
||||
UpdatedAt: u.UpdatedAt,
|
||||
BalanceNotifyEnabled: u.BalanceNotifyEnabled,
|
||||
BalanceNotifyThresholdType: u.BalanceNotifyThresholdType,
|
||||
BalanceNotifyThreshold: u.BalanceNotifyThreshold,
|
||||
BalanceNotifyExtraEmails: u.BalanceNotifyExtraEmails,
|
||||
TotalRecharged: u.TotalRecharged,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ type SystemSettings struct {
|
||||
|
||||
// Balance low notification
|
||||
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"`
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ type User struct {
|
||||
|
||||
// 余额不足通知
|
||||
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"`
|
||||
|
||||
@@ -35,6 +35,7 @@ type ChangePasswordRequest struct {
|
||||
type UpdateProfileRequest struct {
|
||||
Username *string `json:"username"`
|
||||
BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
|
||||
BalanceNotifyThresholdType *string `json:"balance_notify_threshold_type"`
|
||||
BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
|
||||
}
|
||||
|
||||
@@ -102,6 +103,7 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
|
||||
svcReq := service.UpdateProfileRequest{
|
||||
Username: req.Username,
|
||||
BalanceNotifyEnabled: req.BalanceNotifyEnabled,
|
||||
BalanceNotifyThresholdType: req.BalanceNotifyThresholdType,
|
||||
BalanceNotifyThreshold: req.BalanceNotifyThreshold,
|
||||
}
|
||||
updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq)
|
||||
|
||||
@@ -654,7 +654,9 @@ func userEntityToService(u *dbent.User) *service.User {
|
||||
TotpEnabled: u.TotpEnabled,
|
||||
TotpEnabledAt: u.TotpEnabledAt,
|
||||
BalanceNotifyEnabled: u.BalanceNotifyEnabled,
|
||||
BalanceNotifyThresholdType: u.BalanceNotifyThresholdType,
|
||||
BalanceNotifyThreshold: u.BalanceNotifyThreshold,
|
||||
TotalRecharged: u.TotalRecharged,
|
||||
CreatedAt: u.CreatedAt,
|
||||
UpdatedAt: u.UpdatedAt,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -252,7 +252,12 @@ const (
|
||||
|
||||
// Balance Low Notification
|
||||
SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关
|
||||
SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD)
|
||||
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 数组)
|
||||
|
||||
@@ -597,6 +597,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 {
|
||||
@@ -1228,6 +1233,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
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ type SystemSettings struct {
|
||||
|
||||
// Balance low notification
|
||||
BalanceLowNotifyEnabled bool
|
||||
BalanceLowNotifyThresholdType string // "fixed" (default) | "percentage"
|
||||
BalanceLowNotifyThreshold float64
|
||||
|
||||
// Account quota notification
|
||||
|
||||
@@ -32,8 +32,10 @@ type User struct {
|
||||
|
||||
// 余额不足通知
|
||||
BalanceNotifyEnabled bool
|
||||
BalanceNotifyThresholdType string // "fixed" (default) | "percentage"
|
||||
BalanceNotifyThreshold *float64
|
||||
BalanceNotifyExtraEmails []string
|
||||
TotalRecharged float64
|
||||
|
||||
APIKeys []APIKey
|
||||
Subscriptions []UserSubscription
|
||||
|
||||
@@ -66,6 +66,7 @@ type UpdateProfileRequest struct {
|
||||
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"`
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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[]
|
||||
}
|
||||
|
||||
@@ -4633,8 +4633,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: {
|
||||
|
||||
@@ -4797,8 +4797,12 @@ export default {
|
||||
title: '余额不足提醒',
|
||||
description: '当用户余额低于阈值时发送邮件提醒',
|
||||
enabled: '启用余额不足提醒',
|
||||
threshold: '默认提醒阈值',
|
||||
thresholdType: '阈值类型',
|
||||
typeFixed: '固定金额',
|
||||
typePercentage: '充值百分比',
|
||||
threshold: '提醒阈值',
|
||||
thresholdHint: '用户未自定义时使用此值',
|
||||
percentageHint: '当余额低于累计充值额的此百分比时提醒',
|
||||
thresholdPlaceholder: '输入金额',
|
||||
},
|
||||
quotaNotify: {
|
||||
|
||||
@@ -2660,6 +2660,90 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Balance Low Notification -->
|
||||
<div class="card">
|
||||
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
|
||||
<h3 class="text-base font-medium text-gray-900 dark:text-white">
|
||||
{{ t('admin.settings.balanceNotify.title') }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.settings.balanceNotify.description') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="px-6 py-6 space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="mb-0 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.enabled') }}</label>
|
||||
<Toggle v-model="form.balance_low_notify_enabled" />
|
||||
</div>
|
||||
<div v-if="form.balance_low_notify_enabled" class="space-y-3">
|
||||
<!-- Threshold type selector -->
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.thresholdType') }}</label>
|
||||
<div class="flex gap-4">
|
||||
<label class="flex items-center gap-1.5 text-sm cursor-pointer">
|
||||
<input type="radio" v-model="form.balance_low_notify_threshold_type" value="fixed" class="accent-primary-500" />
|
||||
{{ t('admin.settings.balanceNotify.typeFixed') }}
|
||||
</label>
|
||||
<label class="flex items-center gap-1.5 text-sm cursor-pointer">
|
||||
<input type="radio" v-model="form.balance_low_notify_threshold_type" value="percentage" class="accent-primary-500" />
|
||||
{{ t('admin.settings.balanceNotify.typePercentage') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Threshold value -->
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.threshold') }}</label>
|
||||
<div class="relative">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">
|
||||
{{ form.balance_low_notify_threshold_type === 'percentage' ? '%' : '$' }}
|
||||
</span>
|
||||
<input
|
||||
v-model.number="form.balance_low_notify_threshold"
|
||||
type="number"
|
||||
:min="0"
|
||||
:max="form.balance_low_notify_threshold_type === 'percentage' ? 100 : undefined"
|
||||
:step="form.balance_low_notify_threshold_type === 'percentage' ? 1 : 0.01"
|
||||
class="input pl-7"
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ form.balance_low_notify_threshold_type === 'percentage'
|
||||
? t('admin.settings.balanceNotify.percentageHint')
|
||||
: t('admin.settings.balanceNotify.thresholdHint') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Account Quota Notification -->
|
||||
<div class="card">
|
||||
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
|
||||
<h3 class="text-base font-medium text-gray-900 dark:text-white">
|
||||
{{ t('admin.settings.quotaNotify.title') }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.settings.quotaNotify.description') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="px-6 py-6 space-y-4">
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.quotaNotify.emails') }}</label>
|
||||
<div class="space-y-2">
|
||||
<div v-for="(_, index) in (form.account_quota_notify_emails || [])" :key="index" class="flex items-center gap-2">
|
||||
<input v-model="form.account_quota_notify_emails[index]" type="email" class="input flex-1" />
|
||||
<button @click="form.account_quota_notify_emails.splice(index, 1)" class="btn btn-secondary px-2" type="button">
|
||||
<Icon name="x" size="xs" class="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
<button @click="addQuotaNotifyEmail" class="btn btn-secondary btn-sm" type="button">
|
||||
+ {{ t('admin.settings.quotaNotify.addEmail') }}
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ t('admin.settings.quotaNotify.emailsHint') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /Tab: Email -->
|
||||
|
||||
<!-- Tab: Backup -->
|
||||
@@ -2939,7 +3023,12 @@ const form = reactive<SettingsForm>({
|
||||
// Gateway forwarding behavior
|
||||
enable_fingerprint_unification: true,
|
||||
enable_metadata_passthrough: false,
|
||||
enable_cch_signing: false
|
||||
enable_cch_signing: false,
|
||||
// Balance & quota notification
|
||||
balance_low_notify_enabled: false,
|
||||
balance_low_notify_threshold_type: 'fixed' as 'fixed' | 'percentage',
|
||||
balance_low_notify_threshold: 0,
|
||||
account_quota_notify_emails: [] as string[]
|
||||
})
|
||||
|
||||
// Proxies for web search emulation ProxySelector
|
||||
@@ -3149,6 +3238,14 @@ function handleRegistrationEmailSuffixWhitelistPaste(event: ClipboardEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
// Quota notify email helpers
|
||||
const addQuotaNotifyEmail = () => {
|
||||
if (!form.account_quota_notify_emails) {
|
||||
form.account_quota_notify_emails = []
|
||||
}
|
||||
form.account_quota_notify_emails.push('')
|
||||
}
|
||||
|
||||
// LinuxDo OAuth redirect URL suggestion
|
||||
const linuxdoRedirectUrlSuggestion = computed(() => {
|
||||
if (typeof window === 'undefined') return ''
|
||||
@@ -3488,6 +3585,11 @@ async function saveSettings() {
|
||||
payment_cancel_rate_limit_window: Number(form.payment_cancel_rate_limit_window) || 1,
|
||||
payment_cancel_rate_limit_unit: form.payment_cancel_rate_limit_unit,
|
||||
payment_cancel_rate_limit_window_mode: form.payment_cancel_rate_limit_window_mode,
|
||||
// Balance & quota notification
|
||||
balance_low_notify_enabled: form.balance_low_notify_enabled,
|
||||
balance_low_notify_threshold_type: form.balance_low_notify_threshold_type,
|
||||
balance_low_notify_threshold: Number(form.balance_low_notify_threshold) || 0,
|
||||
account_quota_notify_emails: (form.account_quota_notify_emails || []).filter((e: string) => e.trim() !== ''),
|
||||
}
|
||||
|
||||
const updated = await adminAPI.settings.updateSettings(payload)
|
||||
|
||||
Reference in New Issue
Block a user