feat(notify): add balance low & account quota notification system
- User balance low notification: email alert when balance drops below configurable threshold (user email + verified extra emails) - Account quota notification: broadcast email to admin-configured recipients when daily/weekly/total quota usage exceeds alert threshold - Admin settings: global enable/disable, default threshold, quota notification email list (Email Settings tab) - User profile: enable/disable, custom threshold, add/remove extra notification emails with verification code flow - Account quota: per-dimension alert toggle and threshold in quota control card - Trigger logic: first-crossing only (old >= threshold && new < threshold for balance; old < threshold && new >= threshold for quota), naturally prevents duplicate notifications without Redis dedup
This commit is contained in:
@@ -243,6 +243,61 @@ func (_u *UserUpdate) ClearTotpEnabledAt() *UserUpdate {
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetBalanceNotifyEnabled sets the "balance_notify_enabled" field.
|
||||
func (_u *UserUpdate) SetBalanceNotifyEnabled(v bool) *UserUpdate {
|
||||
_u.mutation.SetBalanceNotifyEnabled(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableBalanceNotifyEnabled sets the "balance_notify_enabled" field if the given value is not nil.
|
||||
func (_u *UserUpdate) SetNillableBalanceNotifyEnabled(v *bool) *UserUpdate {
|
||||
if v != nil {
|
||||
_u.SetBalanceNotifyEnabled(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetBalanceNotifyThreshold sets the "balance_notify_threshold" field.
|
||||
func (_u *UserUpdate) SetBalanceNotifyThreshold(v float64) *UserUpdate {
|
||||
_u.mutation.ResetBalanceNotifyThreshold()
|
||||
_u.mutation.SetBalanceNotifyThreshold(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableBalanceNotifyThreshold sets the "balance_notify_threshold" field if the given value is not nil.
|
||||
func (_u *UserUpdate) SetNillableBalanceNotifyThreshold(v *float64) *UserUpdate {
|
||||
if v != nil {
|
||||
_u.SetBalanceNotifyThreshold(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddBalanceNotifyThreshold adds value to the "balance_notify_threshold" field.
|
||||
func (_u *UserUpdate) AddBalanceNotifyThreshold(v float64) *UserUpdate {
|
||||
_u.mutation.AddBalanceNotifyThreshold(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearBalanceNotifyThreshold clears the value of the "balance_notify_threshold" field.
|
||||
func (_u *UserUpdate) ClearBalanceNotifyThreshold() *UserUpdate {
|
||||
_u.mutation.ClearBalanceNotifyThreshold()
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetBalanceNotifyExtraEmails sets the "balance_notify_extra_emails" field.
|
||||
func (_u *UserUpdate) SetBalanceNotifyExtraEmails(v string) *UserUpdate {
|
||||
_u.mutation.SetBalanceNotifyExtraEmails(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableBalanceNotifyExtraEmails sets the "balance_notify_extra_emails" field if the given value is not nil.
|
||||
func (_u *UserUpdate) SetNillableBalanceNotifyExtraEmails(v *string) *UserUpdate {
|
||||
if v != nil {
|
||||
_u.SetBalanceNotifyExtraEmails(*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...)
|
||||
@@ -746,6 +801,21 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
|
||||
if _u.mutation.TotpEnabledAtCleared() {
|
||||
_spec.ClearField(user.FieldTotpEnabledAt, field.TypeTime)
|
||||
}
|
||||
if value, ok := _u.mutation.BalanceNotifyEnabled(); ok {
|
||||
_spec.SetField(user.FieldBalanceNotifyEnabled, field.TypeBool, value)
|
||||
}
|
||||
if value, ok := _u.mutation.BalanceNotifyThreshold(); ok {
|
||||
_spec.SetField(user.FieldBalanceNotifyThreshold, field.TypeFloat64, value)
|
||||
}
|
||||
if value, ok := _u.mutation.AddedBalanceNotifyThreshold(); ok {
|
||||
_spec.AddField(user.FieldBalanceNotifyThreshold, field.TypeFloat64, value)
|
||||
}
|
||||
if _u.mutation.BalanceNotifyThresholdCleared() {
|
||||
_spec.ClearField(user.FieldBalanceNotifyThreshold, field.TypeFloat64)
|
||||
}
|
||||
if value, ok := _u.mutation.BalanceNotifyExtraEmails(); ok {
|
||||
_spec.SetField(user.FieldBalanceNotifyExtraEmails, field.TypeString, value)
|
||||
}
|
||||
if _u.mutation.APIKeysCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
@@ -1434,6 +1504,61 @@ func (_u *UserUpdateOne) ClearTotpEnabledAt() *UserUpdateOne {
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetBalanceNotifyEnabled sets the "balance_notify_enabled" field.
|
||||
func (_u *UserUpdateOne) SetBalanceNotifyEnabled(v bool) *UserUpdateOne {
|
||||
_u.mutation.SetBalanceNotifyEnabled(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableBalanceNotifyEnabled sets the "balance_notify_enabled" field if the given value is not nil.
|
||||
func (_u *UserUpdateOne) SetNillableBalanceNotifyEnabled(v *bool) *UserUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetBalanceNotifyEnabled(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetBalanceNotifyThreshold sets the "balance_notify_threshold" field.
|
||||
func (_u *UserUpdateOne) SetBalanceNotifyThreshold(v float64) *UserUpdateOne {
|
||||
_u.mutation.ResetBalanceNotifyThreshold()
|
||||
_u.mutation.SetBalanceNotifyThreshold(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableBalanceNotifyThreshold sets the "balance_notify_threshold" field if the given value is not nil.
|
||||
func (_u *UserUpdateOne) SetNillableBalanceNotifyThreshold(v *float64) *UserUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetBalanceNotifyThreshold(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddBalanceNotifyThreshold adds value to the "balance_notify_threshold" field.
|
||||
func (_u *UserUpdateOne) AddBalanceNotifyThreshold(v float64) *UserUpdateOne {
|
||||
_u.mutation.AddBalanceNotifyThreshold(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearBalanceNotifyThreshold clears the value of the "balance_notify_threshold" field.
|
||||
func (_u *UserUpdateOne) ClearBalanceNotifyThreshold() *UserUpdateOne {
|
||||
_u.mutation.ClearBalanceNotifyThreshold()
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetBalanceNotifyExtraEmails sets the "balance_notify_extra_emails" field.
|
||||
func (_u *UserUpdateOne) SetBalanceNotifyExtraEmails(v string) *UserUpdateOne {
|
||||
_u.mutation.SetBalanceNotifyExtraEmails(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableBalanceNotifyExtraEmails sets the "balance_notify_extra_emails" field if the given value is not nil.
|
||||
func (_u *UserUpdateOne) SetNillableBalanceNotifyExtraEmails(v *string) *UserUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetBalanceNotifyExtraEmails(*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...)
|
||||
@@ -1967,6 +2092,21 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
|
||||
if _u.mutation.TotpEnabledAtCleared() {
|
||||
_spec.ClearField(user.FieldTotpEnabledAt, field.TypeTime)
|
||||
}
|
||||
if value, ok := _u.mutation.BalanceNotifyEnabled(); ok {
|
||||
_spec.SetField(user.FieldBalanceNotifyEnabled, field.TypeBool, value)
|
||||
}
|
||||
if value, ok := _u.mutation.BalanceNotifyThreshold(); ok {
|
||||
_spec.SetField(user.FieldBalanceNotifyThreshold, field.TypeFloat64, value)
|
||||
}
|
||||
if value, ok := _u.mutation.AddedBalanceNotifyThreshold(); ok {
|
||||
_spec.AddField(user.FieldBalanceNotifyThreshold, field.TypeFloat64, value)
|
||||
}
|
||||
if _u.mutation.BalanceNotifyThresholdCleared() {
|
||||
_spec.ClearField(user.FieldBalanceNotifyThreshold, field.TypeFloat64)
|
||||
}
|
||||
if value, ok := _u.mutation.BalanceNotifyExtraEmails(); ok {
|
||||
_spec.SetField(user.FieldBalanceNotifyExtraEmails, field.TypeString, value)
|
||||
}
|
||||
if _u.mutation.APIKeysCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.O2M,
|
||||
|
||||
Reference in New Issue
Block a user