diff --git a/backend/internal/handler/admin/setting_handler.go b/backend/internal/handler/admin/setting_handler.go
index 3d587a21..49e7aeed 100644
--- a/backend/internal/handler/admin/setting_handler.go
+++ b/backend/internal/handler/admin/setting_handler.go
@@ -177,7 +177,6 @@ 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,
@@ -310,10 +309,9 @@ type UpdateSettingsRequest struct {
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"`
+ BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"`
+ 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"`
@@ -898,12 +896,6 @@ 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
@@ -1063,7 +1055,6 @@ 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/settings.go b/backend/internal/handler/dto/settings.go
index 8da7c6f2..e29f72da 100644
--- a/backend/internal/handler/dto/settings.go
+++ b/backend/internal/handler/dto/settings.go
@@ -150,10 +150,9 @@ type SystemSettings struct {
PaymentCancelRateLimitMode string `json:"payment_cancel_rate_limit_window_mode"`
// 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"`
+ BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"`
+ BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"`
+ AccountQuotaNotifyEmails []string `json:"account_quota_notify_emails"`
}
type DefaultSubscriptionSetting struct {
diff --git a/backend/internal/handler/user_handler.go b/backend/internal/handler/user_handler.go
index 48528d55..4fb72ce7 100644
--- a/backend/internal/handler/user_handler.go
+++ b/backend/internal/handler/user_handler.go
@@ -33,10 +33,9 @@ type ChangePasswordRequest struct {
// UpdateProfileRequest represents the update profile request payload
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"`
+ Username *string `json:"username"`
+ BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
+ BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
}
// GetProfile handles getting user profile
@@ -101,10 +100,9 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
}
svcReq := service.UpdateProfileRequest{
- Username: req.Username,
- BalanceNotifyEnabled: req.BalanceNotifyEnabled,
- BalanceNotifyThresholdType: req.BalanceNotifyThresholdType,
- BalanceNotifyThreshold: req.BalanceNotifyThreshold,
+ Username: req.Username,
+ BalanceNotifyEnabled: req.BalanceNotifyEnabled,
+ BalanceNotifyThreshold: req.BalanceNotifyThreshold,
}
updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq)
if err != nil {
diff --git a/backend/internal/service/balance_notify_service.go b/backend/internal/service/balance_notify_service.go
index e1f6bd8b..0d7e4c09 100644
--- a/backend/internal/service/balance_notify_service.go
+++ b/backend/internal/service/balance_notify_service.go
@@ -51,12 +51,16 @@ func (s *BalanceNotifyService) CheckBalanceAfterDeduction(ctx context.Context, u
return
}
- globalEnabled, globalThresholdType, globalThresholdValue := s.getBalanceNotifyConfig(ctx)
+ globalEnabled, globalThreshold := s.getBalanceNotifyConfig(ctx)
if !globalEnabled {
return
}
- threshold := s.resolveEffectiveThreshold(user, globalThresholdType, globalThresholdValue)
+ // User custom threshold overrides system default
+ threshold := globalThreshold
+ if user.BalanceNotifyThreshold != nil {
+ threshold = *user.BalanceNotifyThreshold
+ }
if threshold <= 0 {
return
}
@@ -76,30 +80,6 @@ func (s *BalanceNotifyService) CheckBalanceAfterDeduction(ctx context.Context, u
}
}
-// resolveEffectiveThreshold computes the actual USD threshold based on type and user settings.
-// When user sets a custom threshold, their type is used independently (defaults to "fixed" if unset).
-func (s *BalanceNotifyService) resolveEffectiveThreshold(user *User, globalType string, globalValue float64) float64 {
- if user.BalanceNotifyThreshold != nil {
- thresholdType := user.BalanceNotifyThresholdType
- if thresholdType == "" {
- thresholdType = ThresholdTypeFixed // user custom value defaults to fixed, not inherited
- }
- 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
@@ -154,21 +134,13 @@ func (s *BalanceNotifyService) asyncSendQuotaAlert(adminEmails []string, account
}
// getBalanceNotifyConfig reads global balance notification settings.
-func (s *BalanceNotifyService) getBalanceNotifyConfig(ctx context.Context) (enabled bool, thresholdType string, threshold float64) {
- keys := []string{
- SettingKeyBalanceLowNotifyEnabled,
- SettingKeyBalanceLowNotifyThresholdType,
- SettingKeyBalanceLowNotifyThreshold,
- }
+func (s *BalanceNotifyService) getBalanceNotifyConfig(ctx context.Context) (enabled bool, threshold float64) {
+ keys := []string{SettingKeyBalanceLowNotifyEnabled, SettingKeyBalanceLowNotifyThreshold}
settings, err := s.settingRepo.GetMultiple(ctx, keys)
if err != nil {
- return false, ThresholdTypeFixed, 0
+ return false, 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 3de0e343..2704e0d0 100644
--- a/backend/internal/service/domain_constants.go
+++ b/backend/internal/service/domain_constants.go
@@ -251,13 +251,8 @@ const (
SettingKeyEnableCCHSigning = "enable_cch_signing"
// Balance Low Notification
- 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"
+ SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关
+ SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD)
// 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 9b307426..f0cf750a 100644
--- a/backend/internal/service/setting_service.go
+++ b/backend/internal/service/setting_service.go
@@ -608,11 +608,6 @@ func (s *SettingService) UpdateSettings(ctx context.Context, settings *SystemSet
// Balance low notification
updates[SettingKeyBalanceLowNotifyEnabled] = strconv.FormatBool(settings.BalanceLowNotifyEnabled)
- thresholdType := settings.BalanceLowNotifyThresholdType
- if thresholdType != ThresholdTypeFixed && thresholdType != ThresholdTypePercentage {
- thresholdType = ThresholdTypeFixed
- }
- updates[SettingKeyBalanceLowNotifyThresholdType] = thresholdType
updates[SettingKeyBalanceLowNotifyThreshold] = strconv.FormatFloat(settings.BalanceLowNotifyThreshold, 'f', 8, 64)
accountQuotaNotifyEmailsJSON, err := json.Marshal(settings.AccountQuotaNotifyEmails)
if err != nil {
@@ -1252,10 +1247,6 @@ 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 b28d2247..debc2b19 100644
--- a/backend/internal/service/settings_view.go
+++ b/backend/internal/service/settings_view.go
@@ -108,9 +108,8 @@ type SystemSettings struct {
EnableCCHSigning bool // 是否对 billing header cch 进行签名(默认 false)
// Balance low notification
- BalanceLowNotifyEnabled bool
- BalanceLowNotifyThresholdType string // "fixed" (default) | "percentage"
- BalanceLowNotifyThreshold float64
+ BalanceLowNotifyEnabled bool
+ BalanceLowNotifyThreshold float64
// Account quota notification
AccountQuotaNotifyEmails []string
diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go
index 26021a9b..e6b9a210 100644
--- a/backend/internal/service/user_service.go
+++ b/backend/internal/service/user_service.go
@@ -62,12 +62,11 @@ 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"`
- BalanceNotifyThresholdType *string `json:"balance_notify_threshold_type"`
- BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
+ Email *string `json:"email"`
+ Username *string `json:"username"`
+ Concurrency *int `json:"concurrency"`
+ BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
+ BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
}
// ChangePasswordRequest 修改密码请求
@@ -144,11 +143,6 @@ func (s *UserService) UpdateProfile(ctx context.Context, userID int64, req Updat
if req.BalanceNotifyEnabled != nil {
user.BalanceNotifyEnabled = *req.BalanceNotifyEnabled
}
- if req.BalanceNotifyThresholdType != nil {
- if *req.BalanceNotifyThresholdType == ThresholdTypeFixed || *req.BalanceNotifyThresholdType == ThresholdTypePercentage {
- user.BalanceNotifyThresholdType = *req.BalanceNotifyThresholdType
- }
- }
if req.BalanceNotifyThreshold != nil {
if *req.BalanceNotifyThreshold <= 0 {
user.BalanceNotifyThreshold = nil // clear to system default
diff --git a/frontend/src/api/admin/settings.ts b/frontend/src/api/admin/settings.ts
index ec290be5..31284289 100644
--- a/frontend/src/api/admin/settings.ts
+++ b/frontend/src/api/admin/settings.ts
@@ -137,7 +137,6 @@ 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[]
}
@@ -241,7 +240,6 @@ 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 880a81ee..8e10bf2a 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -4633,12 +4633,8 @@ 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 41d94e06..1b82f419 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -4797,12 +4797,8 @@ export default {
title: '余额不足提醒',
description: '当用户余额低于阈值时发送邮件提醒',
enabled: '启用余额不足提醒',
- thresholdType: '阈值类型',
- typeFixed: '固定金额',
- typePercentage: '充值百分比',
- threshold: '提醒阈值',
+ threshold: '默认提醒阈值',
thresholdHint: '用户未自定义时使用此值',
- percentageHint: '当余额低于累计充值额的此百分比时提醒',
thresholdPlaceholder: '输入金额',
},
quotaNotify: {
diff --git a/frontend/src/views/admin/SettingsView.vue b/frontend/src/views/admin/SettingsView.vue
index 50b532fb..faab49fe 100644
--- a/frontend/src/views/admin/SettingsView.vue
+++ b/frontend/src/views/admin/SettingsView.vue
@@ -2675,43 +2675,13 @@
- {{ form.balance_low_notify_threshold_type === 'percentage' - ? t('admin.settings.balanceNotify.percentageHint') - : t('admin.settings.balanceNotify.thresholdHint') }} -
+{{ t('admin.settings.balanceNotify.thresholdHint') }}