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:
@@ -1406,6 +1406,19 @@ func (a *Account) getExtraTime(key string) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// getExtraBool 从 Extra 中读取指定 key 的 bool 值
|
||||
func (a *Account) getExtraBool(key string) bool {
|
||||
if a.Extra == nil {
|
||||
return false
|
||||
}
|
||||
if v, ok := a.Extra[key]; ok {
|
||||
if b, ok := v.(bool); ok {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getExtraString 从 Extra 中读取指定 key 的字符串值
|
||||
func (a *Account) getExtraString(key string) string {
|
||||
if a.Extra == nil {
|
||||
@@ -1475,6 +1488,32 @@ func (a *Account) GetQuotaResetTimezone() string {
|
||||
return "UTC"
|
||||
}
|
||||
|
||||
// --- Quota Notification Getters ---
|
||||
|
||||
func (a *Account) GetQuotaNotifyDailyEnabled() bool {
|
||||
return a.getExtraBool("quota_notify_daily_enabled")
|
||||
}
|
||||
|
||||
func (a *Account) GetQuotaNotifyDailyThreshold() float64 {
|
||||
return a.getExtraFloat64("quota_notify_daily_threshold")
|
||||
}
|
||||
|
||||
func (a *Account) GetQuotaNotifyWeeklyEnabled() bool {
|
||||
return a.getExtraBool("quota_notify_weekly_enabled")
|
||||
}
|
||||
|
||||
func (a *Account) GetQuotaNotifyWeeklyThreshold() float64 {
|
||||
return a.getExtraFloat64("quota_notify_weekly_threshold")
|
||||
}
|
||||
|
||||
func (a *Account) GetQuotaNotifyTotalEnabled() bool {
|
||||
return a.getExtraBool("quota_notify_total_enabled")
|
||||
}
|
||||
|
||||
func (a *Account) GetQuotaNotifyTotalThreshold() float64 {
|
||||
return a.getExtraFloat64("quota_notify_total_threshold")
|
||||
}
|
||||
|
||||
// nextFixedDailyReset 计算在 after 之后的下一个每日固定重置时间点
|
||||
func nextFixedDailyReset(hour int, tz *time.Location, after time.Time) time.Time {
|
||||
t := after.In(tz)
|
||||
|
||||
Reference in New Issue
Block a user