fix(notify): remove percentage threshold from balance notification
Balance low notification only supports fixed USD amount threshold. Percentage threshold is a quota concept, not applicable to balance. Reverted threshold_type from admin settings, user profile, and all backend/frontend layers. DB fields (balance_notify_threshold_type, total_recharged) retained for potential future quota use.
This commit is contained in:
parent
9e33d0c4c0
commit
cef22c70ab
@ -177,7 +177,6 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
|
|||||||
EnableCCHSigning: settings.EnableCCHSigning,
|
EnableCCHSigning: settings.EnableCCHSigning,
|
||||||
WebSearchEmulationEnabled: settings.WebSearchEmulationEnabled,
|
WebSearchEmulationEnabled: settings.WebSearchEmulationEnabled,
|
||||||
BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
|
BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
|
||||||
BalanceLowNotifyThresholdType: settings.BalanceLowNotifyThresholdType,
|
|
||||||
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
|
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
|
||||||
AccountQuotaNotifyEmails: settings.AccountQuotaNotifyEmails,
|
AccountQuotaNotifyEmails: settings.AccountQuotaNotifyEmails,
|
||||||
PaymentEnabled: paymentCfg.Enabled,
|
PaymentEnabled: paymentCfg.Enabled,
|
||||||
@ -310,10 +309,9 @@ type UpdateSettingsRequest struct {
|
|||||||
EnableCCHSigning *bool `json:"enable_cch_signing"`
|
EnableCCHSigning *bool `json:"enable_cch_signing"`
|
||||||
|
|
||||||
// Balance low notification
|
// Balance low notification
|
||||||
BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"`
|
BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"`
|
||||||
BalanceLowNotifyThresholdType *string `json:"balance_low_notify_threshold_type"`
|
BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"`
|
||||||
BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"`
|
AccountQuotaNotifyEmails *[]string `json:"account_quota_notify_emails"`
|
||||||
AccountQuotaNotifyEmails *[]string `json:"account_quota_notify_emails"`
|
|
||||||
|
|
||||||
// Payment configuration (integrated into settings, full replace)
|
// Payment configuration (integrated into settings, full replace)
|
||||||
PaymentEnabled *bool `json:"payment_enabled"`
|
PaymentEnabled *bool `json:"payment_enabled"`
|
||||||
@ -898,12 +896,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
return previousSettings.BalanceLowNotifyEnabled
|
return previousSettings.BalanceLowNotifyEnabled
|
||||||
}(),
|
}(),
|
||||||
BalanceLowNotifyThresholdType: func() string {
|
|
||||||
if req.BalanceLowNotifyThresholdType != nil {
|
|
||||||
return *req.BalanceLowNotifyThresholdType
|
|
||||||
}
|
|
||||||
return previousSettings.BalanceLowNotifyThresholdType
|
|
||||||
}(),
|
|
||||||
BalanceLowNotifyThreshold: func() float64 {
|
BalanceLowNotifyThreshold: func() float64 {
|
||||||
if req.BalanceLowNotifyThreshold != nil {
|
if req.BalanceLowNotifyThreshold != nil {
|
||||||
return *req.BalanceLowNotifyThreshold
|
return *req.BalanceLowNotifyThreshold
|
||||||
@ -1063,7 +1055,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
|
|||||||
EnableMetadataPassthrough: updatedSettings.EnableMetadataPassthrough,
|
EnableMetadataPassthrough: updatedSettings.EnableMetadataPassthrough,
|
||||||
EnableCCHSigning: updatedSettings.EnableCCHSigning,
|
EnableCCHSigning: updatedSettings.EnableCCHSigning,
|
||||||
BalanceLowNotifyEnabled: updatedSettings.BalanceLowNotifyEnabled,
|
BalanceLowNotifyEnabled: updatedSettings.BalanceLowNotifyEnabled,
|
||||||
BalanceLowNotifyThresholdType: updatedSettings.BalanceLowNotifyThresholdType,
|
|
||||||
BalanceLowNotifyThreshold: updatedSettings.BalanceLowNotifyThreshold,
|
BalanceLowNotifyThreshold: updatedSettings.BalanceLowNotifyThreshold,
|
||||||
AccountQuotaNotifyEmails: updatedSettings.AccountQuotaNotifyEmails,
|
AccountQuotaNotifyEmails: updatedSettings.AccountQuotaNotifyEmails,
|
||||||
PaymentEnabled: updatedPaymentCfg.Enabled,
|
PaymentEnabled: updatedPaymentCfg.Enabled,
|
||||||
|
|||||||
@ -150,10 +150,9 @@ type SystemSettings struct {
|
|||||||
PaymentCancelRateLimitMode string `json:"payment_cancel_rate_limit_window_mode"`
|
PaymentCancelRateLimitMode string `json:"payment_cancel_rate_limit_window_mode"`
|
||||||
|
|
||||||
// Balance low notification
|
// Balance low notification
|
||||||
BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"`
|
BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"`
|
||||||
BalanceLowNotifyThresholdType string `json:"balance_low_notify_threshold_type"`
|
BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"`
|
||||||
BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"`
|
AccountQuotaNotifyEmails []string `json:"account_quota_notify_emails"`
|
||||||
AccountQuotaNotifyEmails []string `json:"account_quota_notify_emails"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultSubscriptionSetting struct {
|
type DefaultSubscriptionSetting struct {
|
||||||
|
|||||||
@ -33,10 +33,9 @@ type ChangePasswordRequest struct {
|
|||||||
|
|
||||||
// UpdateProfileRequest represents the update profile request payload
|
// UpdateProfileRequest represents the update profile request payload
|
||||||
type UpdateProfileRequest struct {
|
type UpdateProfileRequest struct {
|
||||||
Username *string `json:"username"`
|
Username *string `json:"username"`
|
||||||
BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
|
BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
|
||||||
BalanceNotifyThresholdType *string `json:"balance_notify_threshold_type"`
|
BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
|
||||||
BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProfile handles getting user profile
|
// GetProfile handles getting user profile
|
||||||
@ -101,10 +100,9 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
svcReq := service.UpdateProfileRequest{
|
svcReq := service.UpdateProfileRequest{
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
BalanceNotifyEnabled: req.BalanceNotifyEnabled,
|
BalanceNotifyEnabled: req.BalanceNotifyEnabled,
|
||||||
BalanceNotifyThresholdType: req.BalanceNotifyThresholdType,
|
BalanceNotifyThreshold: req.BalanceNotifyThreshold,
|
||||||
BalanceNotifyThreshold: req.BalanceNotifyThreshold,
|
|
||||||
}
|
}
|
||||||
updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq)
|
updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -51,12 +51,16 @@ func (s *BalanceNotifyService) CheckBalanceAfterDeduction(ctx context.Context, u
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
globalEnabled, globalThresholdType, globalThresholdValue := s.getBalanceNotifyConfig(ctx)
|
globalEnabled, globalThreshold := s.getBalanceNotifyConfig(ctx)
|
||||||
if !globalEnabled {
|
if !globalEnabled {
|
||||||
return
|
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 {
|
if threshold <= 0 {
|
||||||
return
|
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.
|
// quotaDim describes one quota dimension for notification checking.
|
||||||
type quotaDim struct {
|
type quotaDim struct {
|
||||||
name string
|
name string
|
||||||
@ -154,21 +134,13 @@ func (s *BalanceNotifyService) asyncSendQuotaAlert(adminEmails []string, account
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getBalanceNotifyConfig reads global balance notification settings.
|
// getBalanceNotifyConfig reads global balance notification settings.
|
||||||
func (s *BalanceNotifyService) getBalanceNotifyConfig(ctx context.Context) (enabled bool, thresholdType string, threshold float64) {
|
func (s *BalanceNotifyService) getBalanceNotifyConfig(ctx context.Context) (enabled bool, threshold float64) {
|
||||||
keys := []string{
|
keys := []string{SettingKeyBalanceLowNotifyEnabled, SettingKeyBalanceLowNotifyThreshold}
|
||||||
SettingKeyBalanceLowNotifyEnabled,
|
|
||||||
SettingKeyBalanceLowNotifyThresholdType,
|
|
||||||
SettingKeyBalanceLowNotifyThreshold,
|
|
||||||
}
|
|
||||||
settings, err := s.settingRepo.GetMultiple(ctx, keys)
|
settings, err := s.settingRepo.GetMultiple(ctx, keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, ThresholdTypeFixed, 0
|
return false, 0
|
||||||
}
|
}
|
||||||
enabled = settings[SettingKeyBalanceLowNotifyEnabled] == "true"
|
enabled = settings[SettingKeyBalanceLowNotifyEnabled] == "true"
|
||||||
thresholdType = settings[SettingKeyBalanceLowNotifyThresholdType]
|
|
||||||
if thresholdType == "" {
|
|
||||||
thresholdType = ThresholdTypeFixed
|
|
||||||
}
|
|
||||||
if v := settings[SettingKeyBalanceLowNotifyThreshold]; v != "" {
|
if v := settings[SettingKeyBalanceLowNotifyThreshold]; v != "" {
|
||||||
if f, err := strconv.ParseFloat(v, 64); err == nil {
|
if f, err := strconv.ParseFloat(v, 64); err == nil {
|
||||||
threshold = f
|
threshold = f
|
||||||
|
|||||||
@ -251,13 +251,8 @@ const (
|
|||||||
SettingKeyEnableCCHSigning = "enable_cch_signing"
|
SettingKeyEnableCCHSigning = "enable_cch_signing"
|
||||||
|
|
||||||
// Balance Low Notification
|
// Balance Low Notification
|
||||||
SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关
|
SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关
|
||||||
SettingKeyBalanceLowNotifyThresholdType = "balance_low_notify_threshold_type" // "fixed" | "percentage"
|
SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD)
|
||||||
SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD 或百分比)
|
|
||||||
|
|
||||||
// Threshold type constants
|
|
||||||
ThresholdTypeFixed = "fixed"
|
|
||||||
ThresholdTypePercentage = "percentage"
|
|
||||||
|
|
||||||
// Account Quota Notification
|
// Account Quota Notification
|
||||||
SettingKeyAccountQuotaNotifyEmails = "account_quota_notify_emails" // 管理员通知邮箱列表(JSON 数组)
|
SettingKeyAccountQuotaNotifyEmails = "account_quota_notify_emails" // 管理员通知邮箱列表(JSON 数组)
|
||||||
|
|||||||
@ -608,11 +608,6 @@ func (s *SettingService) UpdateSettings(ctx context.Context, settings *SystemSet
|
|||||||
|
|
||||||
// Balance low notification
|
// Balance low notification
|
||||||
updates[SettingKeyBalanceLowNotifyEnabled] = strconv.FormatBool(settings.BalanceLowNotifyEnabled)
|
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)
|
updates[SettingKeyBalanceLowNotifyThreshold] = strconv.FormatFloat(settings.BalanceLowNotifyThreshold, 'f', 8, 64)
|
||||||
accountQuotaNotifyEmailsJSON, err := json.Marshal(settings.AccountQuotaNotifyEmails)
|
accountQuotaNotifyEmailsJSON, err := json.Marshal(settings.AccountQuotaNotifyEmails)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1252,10 +1247,6 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
|
|||||||
|
|
||||||
// Balance low notification
|
// Balance low notification
|
||||||
result.BalanceLowNotifyEnabled = settings[SettingKeyBalanceLowNotifyEnabled] == "true"
|
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 {
|
if v, err := strconv.ParseFloat(settings[SettingKeyBalanceLowNotifyThreshold], 64); err == nil && v >= 0 {
|
||||||
result.BalanceLowNotifyThreshold = v
|
result.BalanceLowNotifyThreshold = v
|
||||||
}
|
}
|
||||||
|
|||||||
@ -108,9 +108,8 @@ type SystemSettings struct {
|
|||||||
EnableCCHSigning bool // 是否对 billing header cch 进行签名(默认 false)
|
EnableCCHSigning bool // 是否对 billing header cch 进行签名(默认 false)
|
||||||
|
|
||||||
// Balance low notification
|
// Balance low notification
|
||||||
BalanceLowNotifyEnabled bool
|
BalanceLowNotifyEnabled bool
|
||||||
BalanceLowNotifyThresholdType string // "fixed" (default) | "percentage"
|
BalanceLowNotifyThreshold float64
|
||||||
BalanceLowNotifyThreshold float64
|
|
||||||
|
|
||||||
// Account quota notification
|
// Account quota notification
|
||||||
AccountQuotaNotifyEmails []string
|
AccountQuotaNotifyEmails []string
|
||||||
|
|||||||
@ -62,12 +62,11 @@ type UserRepository interface {
|
|||||||
|
|
||||||
// UpdateProfileRequest 更新用户资料请求
|
// UpdateProfileRequest 更新用户资料请求
|
||||||
type UpdateProfileRequest struct {
|
type UpdateProfileRequest struct {
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email"`
|
||||||
Username *string `json:"username"`
|
Username *string `json:"username"`
|
||||||
Concurrency *int `json:"concurrency"`
|
Concurrency *int `json:"concurrency"`
|
||||||
BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
|
BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
|
||||||
BalanceNotifyThresholdType *string `json:"balance_notify_threshold_type"`
|
BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
|
||||||
BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangePasswordRequest 修改密码请求
|
// ChangePasswordRequest 修改密码请求
|
||||||
@ -144,11 +143,6 @@ func (s *UserService) UpdateProfile(ctx context.Context, userID int64, req Updat
|
|||||||
if req.BalanceNotifyEnabled != nil {
|
if req.BalanceNotifyEnabled != nil {
|
||||||
user.BalanceNotifyEnabled = *req.BalanceNotifyEnabled
|
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 != nil {
|
||||||
if *req.BalanceNotifyThreshold <= 0 {
|
if *req.BalanceNotifyThreshold <= 0 {
|
||||||
user.BalanceNotifyThreshold = nil // clear to system default
|
user.BalanceNotifyThreshold = nil // clear to system default
|
||||||
|
|||||||
@ -137,7 +137,6 @@ export interface SystemSettings {
|
|||||||
|
|
||||||
// Balance & quota notification
|
// Balance & quota notification
|
||||||
balance_low_notify_enabled: boolean
|
balance_low_notify_enabled: boolean
|
||||||
balance_low_notify_threshold_type: 'fixed' | 'percentage'
|
|
||||||
balance_low_notify_threshold: number
|
balance_low_notify_threshold: number
|
||||||
account_quota_notify_emails: string[]
|
account_quota_notify_emails: string[]
|
||||||
}
|
}
|
||||||
@ -241,7 +240,6 @@ export interface UpdateSettingsRequest {
|
|||||||
payment_cancel_rate_limit_window_mode?: string
|
payment_cancel_rate_limit_window_mode?: string
|
||||||
// Balance & quota notification
|
// Balance & quota notification
|
||||||
balance_low_notify_enabled?: boolean
|
balance_low_notify_enabled?: boolean
|
||||||
balance_low_notify_threshold_type?: 'fixed' | 'percentage'
|
|
||||||
balance_low_notify_threshold?: number
|
balance_low_notify_threshold?: number
|
||||||
account_quota_notify_emails?: string[]
|
account_quota_notify_emails?: string[]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4633,12 +4633,8 @@ export default {
|
|||||||
title: 'Balance Low Notification',
|
title: 'Balance Low Notification',
|
||||||
description: 'Send email notification when user balance falls below threshold',
|
description: 'Send email notification when user balance falls below threshold',
|
||||||
enabled: 'Enable Balance Low Notification',
|
enabled: 'Enable Balance Low Notification',
|
||||||
thresholdType: 'Threshold Type',
|
|
||||||
typeFixed: 'Fixed Amount',
|
|
||||||
typePercentage: 'Percentage of Recharged',
|
|
||||||
threshold: 'Default Threshold',
|
threshold: 'Default Threshold',
|
||||||
thresholdHint: 'Used when user has not set a custom value',
|
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',
|
thresholdPlaceholder: 'Enter amount',
|
||||||
},
|
},
|
||||||
quotaNotify: {
|
quotaNotify: {
|
||||||
|
|||||||
@ -4797,12 +4797,8 @@ export default {
|
|||||||
title: '余额不足提醒',
|
title: '余额不足提醒',
|
||||||
description: '当用户余额低于阈值时发送邮件提醒',
|
description: '当用户余额低于阈值时发送邮件提醒',
|
||||||
enabled: '启用余额不足提醒',
|
enabled: '启用余额不足提醒',
|
||||||
thresholdType: '阈值类型',
|
threshold: '默认提醒阈值',
|
||||||
typeFixed: '固定金额',
|
|
||||||
typePercentage: '充值百分比',
|
|
||||||
threshold: '提醒阈值',
|
|
||||||
thresholdHint: '用户未自定义时使用此值',
|
thresholdHint: '用户未自定义时使用此值',
|
||||||
percentageHint: '当余额低于累计充值额的此百分比时提醒',
|
|
||||||
thresholdPlaceholder: '输入金额',
|
thresholdPlaceholder: '输入金额',
|
||||||
},
|
},
|
||||||
quotaNotify: {
|
quotaNotify: {
|
||||||
|
|||||||
@ -2675,43 +2675,13 @@
|
|||||||
<label class="mb-0 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.enabled') }}</label>
|
<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" />
|
<Toggle v-model="form.balance_low_notify_enabled" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="form.balance_low_notify_enabled" class="space-y-3">
|
<div v-if="form.balance_low_notify_enabled">
|
||||||
<!-- Threshold type selector -->
|
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.threshold') }}</label>
|
||||||
<div>
|
<div class="relative">
|
||||||
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.thresholdType') }}</label>
|
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">$</span>
|
||||||
<div class="flex gap-4">
|
<input v-model.number="form.balance_low_notify_threshold" type="number" min="0" step="0.01" class="input pl-7" />
|
||||||
<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>
|
||||||
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ t('admin.settings.balanceNotify.thresholdHint') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -3026,7 +2996,6 @@ const form = reactive<SettingsForm>({
|
|||||||
enable_cch_signing: false,
|
enable_cch_signing: false,
|
||||||
// Balance & quota notification
|
// Balance & quota notification
|
||||||
balance_low_notify_enabled: false,
|
balance_low_notify_enabled: false,
|
||||||
balance_low_notify_threshold_type: 'fixed' as 'fixed' | 'percentage',
|
|
||||||
balance_low_notify_threshold: 0,
|
balance_low_notify_threshold: 0,
|
||||||
account_quota_notify_emails: [] as string[]
|
account_quota_notify_emails: [] as string[]
|
||||||
})
|
})
|
||||||
@ -3591,7 +3560,6 @@ async function saveSettings() {
|
|||||||
payment_cancel_rate_limit_window_mode: form.payment_cancel_rate_limit_window_mode,
|
payment_cancel_rate_limit_window_mode: form.payment_cancel_rate_limit_window_mode,
|
||||||
// Balance & quota notification
|
// Balance & quota notification
|
||||||
balance_low_notify_enabled: form.balance_low_notify_enabled,
|
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,
|
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() !== ''),
|
account_quota_notify_emails: (form.account_quota_notify_emails || []).filter((e: string) => e.trim() !== ''),
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user