From 1e6912ea2e12beb052d1e0523aa321be619de57f Mon Sep 17 00:00:00 2001 From: erio Date: Tue, 14 Apr 2026 07:43:08 +0800 Subject: [PATCH] fix: gofmt formatting across all Go source files --- .../internal/handler/admin/setting_handler.go | 10 ++-- backend/internal/handler/dto/settings.go | 10 ++-- backend/internal/handler/dto/types.go | 8 +-- .../internal/payment/load_balancer_test.go | 2 +- .../internal/payment/provider/alipay_test.go | 6 +- backend/internal/service/account.go | 27 ++++++--- .../service/balance_notify_email_body_test.go | 18 +++--- backend/internal/service/domain_constants.go | 4 +- .../payment_config_plans_validation_test.go | 6 +- .../service/payment_config_providers.go | 59 +++++++++++++++---- .../service/payment_config_providers_test.go | 2 +- .../service/payment_config_service.go | 36 +++++------ .../service/setting_service_public_test.go | 2 +- .../service/setting_service_update_test.go | 4 +- backend/internal/service/settings_view.go | 12 ++-- backend/internal/service/usage_billing.go | 13 ++++ backend/internal/service/user.go | 2 +- backend/internal/service/user_service.go | 6 +- 18 files changed, 143 insertions(+), 84 deletions(-) diff --git a/backend/internal/handler/admin/setting_handler.go b/backend/internal/handler/admin/setting_handler.go index 2a87e95c..b50cad96 100644 --- a/backend/internal/handler/admin/setting_handler.go +++ b/backend/internal/handler/admin/setting_handler.go @@ -310,11 +310,11 @@ type UpdateSettingsRequest struct { EnableCCHSigning *bool `json:"enable_cch_signing"` // Balance low notification - BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"` - BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"` - BalanceLowNotifyRechargeURL *string `json:"balance_low_notify_recharge_url"` - AccountQuotaNotifyEnabled *bool `json:"account_quota_notify_enabled"` - AccountQuotaNotifyEmails *[]dto.NotifyEmailEntry `json:"account_quota_notify_emails"` + BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"` + BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"` + BalanceLowNotifyRechargeURL *string `json:"balance_low_notify_recharge_url"` + AccountQuotaNotifyEnabled *bool `json:"account_quota_notify_enabled"` + AccountQuotaNotifyEmails *[]dto.NotifyEmailEntry `json:"account_quota_notify_emails"` // Payment configuration (integrated into settings, full replace) PaymentEnabled *bool `json:"payment_enabled"` diff --git a/backend/internal/handler/dto/settings.go b/backend/internal/handler/dto/settings.go index d218490a..ef285a44 100644 --- a/backend/internal/handler/dto/settings.go +++ b/backend/internal/handler/dto/settings.go @@ -150,11 +150,11 @@ type SystemSettings struct { PaymentCancelRateLimitMode string `json:"payment_cancel_rate_limit_window_mode"` // Balance low notification - BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"` - BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"` - BalanceLowNotifyRechargeURL string `json:"balance_low_notify_recharge_url"` - AccountQuotaNotifyEnabled bool `json:"account_quota_notify_enabled"` - AccountQuotaNotifyEmails []NotifyEmailEntry `json:"account_quota_notify_emails"` + BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"` + BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"` + BalanceLowNotifyRechargeURL string `json:"balance_low_notify_recharge_url"` + AccountQuotaNotifyEnabled bool `json:"account_quota_notify_enabled"` + AccountQuotaNotifyEmails []NotifyEmailEntry `json:"account_quota_notify_emails"` } type DefaultSubscriptionSetting struct { diff --git a/backend/internal/handler/dto/types.go b/backend/internal/handler/dto/types.go index afb782b0..1aab1dbb 100644 --- a/backend/internal/handler/dto/types.go +++ b/backend/internal/handler/dto/types.go @@ -19,11 +19,11 @@ type User struct { UpdatedAt time.Time `json:"updated_at"` // 余额不足通知 - BalanceNotifyEnabled bool `json:"balance_notify_enabled"` - BalanceNotifyThresholdType string `json:"balance_notify_threshold_type"` - BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"` + BalanceNotifyEnabled bool `json:"balance_notify_enabled"` + BalanceNotifyThresholdType string `json:"balance_notify_threshold_type"` + BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"` BalanceNotifyExtraEmails []NotifyEmailEntry `json:"balance_notify_extra_emails"` - TotalRecharged float64 `json:"total_recharged"` + TotalRecharged float64 `json:"total_recharged"` APIKeys []APIKey `json:"api_keys,omitempty"` Subscriptions []UserSubscription `json:"subscriptions,omitempty"` diff --git a/backend/internal/payment/load_balancer_test.go b/backend/internal/payment/load_balancer_test.go index 568b56a3..04b3c25b 100644 --- a/backend/internal/payment/load_balancer_test.go +++ b/backend/internal/payment/load_balancer_test.go @@ -242,7 +242,7 @@ func TestFilterByLimits(t *testing.T) { wantIDs: nil, }, { - name: "empty candidates returns empty", + name: "empty candidates returns empty", candidates: nil, paymentType: "alipay", orderAmount: 10, diff --git a/backend/internal/payment/provider/alipay_test.go b/backend/internal/payment/provider/alipay_test.go index 1b9d66ba..7b0ce0d8 100644 --- a/backend/internal/payment/provider/alipay_test.go +++ b/backend/internal/payment/provider/alipay_test.go @@ -98,9 +98,9 @@ func TestNewAlipay(t *testing.T) { errSubstr: "privateKey", }, { - name: "nil config map returns error for appId", - config: map[string]string{}, - wantErr: true, + name: "nil config map returns error for appId", + config: map[string]string{}, + wantErr: true, errSubstr: "appId", }, } diff --git a/backend/internal/service/account.go b/backend/internal/service/account.go index cacfb240..52db3073 100644 --- a/backend/internal/service/account.go +++ b/backend/internal/service/account.go @@ -1533,39 +1533,48 @@ func (a *Account) QuotaNotifyConfig(dim string) (enabled bool, threshold float64 } func (a *Account) GetQuotaNotifyDailyEnabled() bool { - e, _, _ := a.QuotaNotifyConfig(quotaDimDaily); return e + e, _, _ := a.QuotaNotifyConfig(quotaDimDaily) + return e } func (a *Account) GetQuotaNotifyDailyThreshold() float64 { - _, t, _ := a.QuotaNotifyConfig(quotaDimDaily); return t + _, t, _ := a.QuotaNotifyConfig(quotaDimDaily) + return t } func (a *Account) GetQuotaNotifyDailyThresholdType() string { - _, _, tt := a.QuotaNotifyConfig(quotaDimDaily); return tt + _, _, tt := a.QuotaNotifyConfig(quotaDimDaily) + return tt } func (a *Account) GetQuotaNotifyWeeklyEnabled() bool { - e, _, _ := a.QuotaNotifyConfig(quotaDimWeekly); return e + e, _, _ := a.QuotaNotifyConfig(quotaDimWeekly) + return e } func (a *Account) GetQuotaNotifyWeeklyThreshold() float64 { - _, t, _ := a.QuotaNotifyConfig(quotaDimWeekly); return t + _, t, _ := a.QuotaNotifyConfig(quotaDimWeekly) + return t } func (a *Account) GetQuotaNotifyWeeklyThresholdType() string { - _, _, tt := a.QuotaNotifyConfig(quotaDimWeekly); return tt + _, _, tt := a.QuotaNotifyConfig(quotaDimWeekly) + return tt } func (a *Account) GetQuotaNotifyTotalEnabled() bool { - e, _, _ := a.QuotaNotifyConfig(quotaDimTotal); return e + e, _, _ := a.QuotaNotifyConfig(quotaDimTotal) + return e } func (a *Account) GetQuotaNotifyTotalThreshold() float64 { - _, t, _ := a.QuotaNotifyConfig(quotaDimTotal); return t + _, t, _ := a.QuotaNotifyConfig(quotaDimTotal) + return t } func (a *Account) GetQuotaNotifyTotalThresholdType() string { - _, _, tt := a.QuotaNotifyConfig(quotaDimTotal); return tt + _, _, tt := a.QuotaNotifyConfig(quotaDimTotal) + return tt } // nextFixedDailyReset 计算在 after 之后的下一个每日固定重置时间点 diff --git a/backend/internal/service/balance_notify_email_body_test.go b/backend/internal/service/balance_notify_email_body_test.go index 9baf164e..aee5a5bc 100644 --- a/backend/internal/service/balance_notify_email_body_test.go +++ b/backend/internal/service/balance_notify_email_body_test.go @@ -65,15 +65,15 @@ func TestBuildBalanceLowEmailBody_NoRechargeURLOmitsButton(t *testing.T) { func TestBuildQuotaAlertEmailBody_AllFieldsPresent(t *testing.T) { s := &BalanceNotifyService{} body := s.buildQuotaAlertEmailBody( - 42, // accountID - "acc-foo", // accountName - "anthropic", // platform - "日限额 / Daily", // dimLabel - 750.50, // used - 1000.0, // limit - 249.50, // remaining - "$249.50", // thresholdDisplay - "MySite", // siteName + 42, // accountID + "acc-foo", // accountName + "anthropic", // platform + "日限额 / Daily", // dimLabel + 750.50, // used + 1000.0, // limit + 249.50, // remaining + "$249.50", // thresholdDisplay + "MySite", // siteName ) require.Contains(t, body, "MySite") diff --git a/backend/internal/service/domain_constants.go b/backend/internal/service/domain_constants.go index 896ba59f..bdced29a 100644 --- a/backend/internal/service/domain_constants.go +++ b/backend/internal/service/domain_constants.go @@ -251,8 +251,8 @@ const ( SettingKeyEnableCCHSigning = "enable_cch_signing" // Balance Low Notification - SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关 - SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD) + SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关 + SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD) SettingKeyBalanceLowNotifyRechargeURL = "balance_low_notify_recharge_url" // 充值页面 URL // Account Quota Notification diff --git a/backend/internal/service/payment_config_plans_validation_test.go b/backend/internal/service/payment_config_plans_validation_test.go index efdbdb10..bcbe901f 100644 --- a/backend/internal/service/payment_config_plans_validation_test.go +++ b/backend/internal/service/payment_config_plans_validation_test.go @@ -131,9 +131,9 @@ func TestValidatePlanPatch_NilOriginalPrice(t *testing.T) { // --- validatePlanPatch: other fields --- -func ptrStr(s string) *string { return &s } -func ptrInt(i int) *int { return &i } -func ptrInt64(i int64) *int64 { return &i } +func ptrStr(s string) *string { return &s } +func ptrInt(i int) *int { return &i } +func ptrInt64(i int64) *int64 { return &i } func ptrFloat(f float64) *float64 { return &f } func TestValidatePlanPatch_EmptyName(t *testing.T) { diff --git a/backend/internal/service/payment_config_providers.go b/backend/internal/service/payment_config_providers.go index 10181914..0c71ab29 100644 --- a/backend/internal/service/payment_config_providers.go +++ b/backend/internal/service/payment_config_providers.go @@ -22,16 +22,17 @@ func (s *PaymentConfigService) ListProviderInstances(ctx context.Context) ([]*db // ProviderInstanceResponse is the API response for a provider instance. type ProviderInstanceResponse struct { - ID int64 `json:"id"` - ProviderKey string `json:"provider_key"` - Name string `json:"name"` - Config map[string]string `json:"config"` - SupportedTypes []string `json:"supported_types"` - Limits string `json:"limits"` - Enabled bool `json:"enabled"` - RefundEnabled bool `json:"refund_enabled"` - SortOrder int `json:"sort_order"` - PaymentMode string `json:"payment_mode"` + ID int64 `json:"id"` + ProviderKey string `json:"provider_key"` + Name string `json:"name"` + Config map[string]string `json:"config"` + SupportedTypes []string `json:"supported_types"` + Limits string `json:"limits"` + Enabled bool `json:"enabled"` + RefundEnabled bool `json:"refund_enabled"` + AllowUserRefund bool `json:"allow_user_refund"` + SortOrder int `json:"sort_order"` + PaymentMode string `json:"payment_mode"` } // ListProviderInstancesWithConfig returns provider instances with decrypted config. @@ -46,8 +47,8 @@ func (s *PaymentConfigService) ListProviderInstancesWithConfig(ctx context.Conte resp := ProviderInstanceResponse{ ID: int64(inst.ID), ProviderKey: inst.ProviderKey, Name: inst.Name, SupportedTypes: splitTypes(inst.SupportedTypes), Limits: inst.Limits, - Enabled: inst.Enabled, RefundEnabled: inst.RefundEnabled, SortOrder: inst.SortOrder, - PaymentMode: inst.PaymentMode, + Enabled: inst.Enabled, RefundEnabled: inst.RefundEnabled, AllowUserRefund: inst.AllowUserRefund, + SortOrder: inst.SortOrder, PaymentMode: inst.PaymentMode, } resp.Config, err = s.decryptAndMaskConfig(inst.Config) if err != nil { @@ -110,10 +111,12 @@ func (s *PaymentConfigService) CreateProviderInstance(ctx context.Context, req C if err != nil { return nil, err } + allowUserRefund := req.AllowUserRefund && req.RefundEnabled return s.entClient.PaymentProviderInstance.Create(). SetProviderKey(req.ProviderKey).SetName(req.Name).SetConfig(enc). SetSupportedTypes(typesStr).SetEnabled(req.Enabled).SetPaymentMode(req.PaymentMode). SetSortOrder(req.SortOrder).SetLimits(req.Limits).SetRefundEnabled(req.RefundEnabled). + SetAllowUserRefund(allowUserRefund). Save(ctx) } @@ -221,6 +224,21 @@ func (s *PaymentConfigService) UpdateProviderInstance(ctx context.Context, id in } if req.RefundEnabled != nil { u.SetRefundEnabled(*req.RefundEnabled) + // Cascade: turning off refund_enabled also disables allow_user_refund + if !*req.RefundEnabled { + u.SetAllowUserRefund(false) + } + } + if req.AllowUserRefund != nil { + // Only allow enabling when refund_enabled is true + if *req.AllowUserRefund { + inst, err := s.entClient.PaymentProviderInstance.Get(ctx, id) + if err == nil && inst.RefundEnabled { + u.SetAllowUserRefund(true) + } + } else { + u.SetAllowUserRefund(false) + } } if req.PaymentMode != nil { u.SetPaymentMode(*req.PaymentMode) @@ -228,6 +246,23 @@ func (s *PaymentConfigService) UpdateProviderInstance(ctx context.Context, id in return u.Save(ctx) } +// GetUserRefundEligibleInstanceIDs returns provider instance IDs that allow user refund. +func (s *PaymentConfigService) GetUserRefundEligibleInstanceIDs(ctx context.Context) ([]string, error) { + instances, err := s.entClient.PaymentProviderInstance.Query(). + Where( + paymentproviderinstance.AllowUserRefundEQ(true), + paymentproviderinstance.RefundEnabledEQ(true), + ).Select(paymentproviderinstance.FieldID).All(ctx) + if err != nil { + return nil, err + } + ids := make([]string, 0, len(instances)) + for _, inst := range instances { + ids = append(ids, strconv.FormatInt(int64(inst.ID), 10)) + } + return ids, nil +} + func (s *PaymentConfigService) mergeConfig(ctx context.Context, id int64, newConfig map[string]string) (map[string]string, error) { inst, err := s.entClient.PaymentProviderInstance.Get(ctx, id) if err != nil { diff --git a/backend/internal/service/payment_config_providers_test.go b/backend/internal/service/payment_config_providers_test.go index e71eb9f5..2aaa874f 100644 --- a/backend/internal/service/payment_config_providers_test.go +++ b/backend/internal/service/payment_config_providers_test.go @@ -101,7 +101,7 @@ func TestIsSensitiveConfigField(t *testing.T) { t.Parallel() tests := []struct { - field string + field string wantSen bool }{ // Sensitive fields (contain key/secret/private/password/pkey patterns) diff --git a/backend/internal/service/payment_config_service.go b/backend/internal/service/payment_config_service.go index 9042c3ab..cce31f4d 100644 --- a/backend/internal/service/payment_config_service.go +++ b/backend/internal/service/payment_config_service.go @@ -105,26 +105,28 @@ type MethodLimitsResponse struct { } type CreateProviderInstanceRequest struct { - ProviderKey string `json:"provider_key"` - Name string `json:"name"` - Config map[string]string `json:"config"` - SupportedTypes []string `json:"supported_types"` - Enabled bool `json:"enabled"` - PaymentMode string `json:"payment_mode"` - SortOrder int `json:"sort_order"` - Limits string `json:"limits"` - RefundEnabled bool `json:"refund_enabled"` + ProviderKey string `json:"provider_key"` + Name string `json:"name"` + Config map[string]string `json:"config"` + SupportedTypes []string `json:"supported_types"` + Enabled bool `json:"enabled"` + PaymentMode string `json:"payment_mode"` + SortOrder int `json:"sort_order"` + Limits string `json:"limits"` + RefundEnabled bool `json:"refund_enabled"` + AllowUserRefund bool `json:"allow_user_refund"` } type UpdateProviderInstanceRequest struct { - Name *string `json:"name"` - Config map[string]string `json:"config"` - SupportedTypes []string `json:"supported_types"` - Enabled *bool `json:"enabled"` - PaymentMode *string `json:"payment_mode"` - SortOrder *int `json:"sort_order"` - Limits *string `json:"limits"` - RefundEnabled *bool `json:"refund_enabled"` + Name *string `json:"name"` + Config map[string]string `json:"config"` + SupportedTypes []string `json:"supported_types"` + Enabled *bool `json:"enabled"` + PaymentMode *string `json:"payment_mode"` + SortOrder *int `json:"sort_order"` + Limits *string `json:"limits"` + RefundEnabled *bool `json:"refund_enabled"` + AllowUserRefund *bool `json:"allow_user_refund"` } type CreatePlanRequest struct { GroupID int64 `json:"group_id"` diff --git a/backend/internal/service/setting_service_public_test.go b/backend/internal/service/setting_service_public_test.go index 6dfa627c..5cf1e860 100644 --- a/backend/internal/service/setting_service_public_test.go +++ b/backend/internal/service/setting_service_public_test.go @@ -66,7 +66,7 @@ func TestSettingService_GetPublicSettings_ExposesRegistrationEmailSuffixWhitelis func TestSettingService_GetPublicSettings_ExposesTablePreferences(t *testing.T) { repo := &settingPublicRepoStub{ values: map[string]string{ - SettingKeyTableDefaultPageSize: "50", + SettingKeyTableDefaultPageSize: "50", SettingKeyTablePageSizeOptions: "[20,50,100]", }, } diff --git a/backend/internal/service/setting_service_update_test.go b/backend/internal/service/setting_service_update_test.go index 28c7ad02..e62218b4 100644 --- a/backend/internal/service/setting_service_update_test.go +++ b/backend/internal/service/setting_service_update_test.go @@ -208,7 +208,7 @@ func TestSettingService_UpdateSettings_TablePreferences(t *testing.T) { svc := NewSettingService(repo, &config.Config{}) err := svc.UpdateSettings(context.Background(), &SystemSettings{ - TableDefaultPageSize: 50, + TableDefaultPageSize: 50, TablePageSizeOptions: []int{20, 50, 100}, }) require.NoError(t, err) @@ -216,7 +216,7 @@ func TestSettingService_UpdateSettings_TablePreferences(t *testing.T) { require.Equal(t, "[20,50,100]", repo.updates[SettingKeyTablePageSizeOptions]) err = svc.UpdateSettings(context.Background(), &SystemSettings{ - TableDefaultPageSize: 1000, + TableDefaultPageSize: 1000, TablePageSizeOptions: []int{20, 100}, }) require.NoError(t, err) diff --git a/backend/internal/service/settings_view.go b/backend/internal/service/settings_view.go index 57f3746a..ec20fe0a 100644 --- a/backend/internal/service/settings_view.go +++ b/backend/internal/service/settings_view.go @@ -108,8 +108,8 @@ type SystemSettings struct { EnableCCHSigning bool // 是否对 billing header cch 进行签名(默认 false) // Balance low notification - BalanceLowNotifyEnabled bool - BalanceLowNotifyThreshold float64 + BalanceLowNotifyEnabled bool + BalanceLowNotifyThreshold float64 BalanceLowNotifyRechargeURL string // Account quota notification @@ -155,10 +155,10 @@ type PublicSettings struct { OIDCOAuthProviderName string Version string - BalanceLowNotifyEnabled bool - AccountQuotaNotifyEnabled bool - BalanceLowNotifyThreshold float64 - BalanceLowNotifyRechargeURL string + BalanceLowNotifyEnabled bool + AccountQuotaNotifyEnabled bool + BalanceLowNotifyThreshold float64 + BalanceLowNotifyRechargeURL string } // StreamTimeoutSettings 流超时处理配置(仅控制超时后的处理方式,超时判定由网关配置控制) diff --git a/backend/internal/service/usage_billing.go b/backend/internal/service/usage_billing.go index 73b05743..30495624 100644 --- a/backend/internal/service/usage_billing.go +++ b/backend/internal/service/usage_billing.go @@ -100,9 +100,22 @@ func valueOrZero(v *int64) int64 { return *v } +// AccountQuotaState holds the post-increment quota state returned by the DB transaction. +// All values are post-update (i.e., already include the increment). +type AccountQuotaState struct { + TotalUsed float64 + TotalLimit float64 + DailyUsed float64 + DailyLimit float64 + WeeklyUsed float64 + WeeklyLimit float64 +} + type UsageBillingApplyResult struct { Applied bool APIKeyQuotaExhausted bool + NewBalance *float64 // post-deduction balance (nil = no balance deduction) + QuotaState *AccountQuotaState // post-increment quota state (nil = no quota increment) } type UsageBillingRepository interface { diff --git a/backend/internal/service/user.go b/backend/internal/service/user.go index d3d8c954..59f8aa6b 100644 --- a/backend/internal/service/user.go +++ b/backend/internal/service/user.go @@ -32,7 +32,7 @@ type User struct { // 余额不足通知 BalanceNotifyEnabled bool - BalanceNotifyThresholdType string // "fixed" (default) | "percentage" + BalanceNotifyThresholdType string // "fixed" (default) | "percentage" BalanceNotifyThreshold *float64 BalanceNotifyExtraEmails []NotifyEmailEntry TotalRecharged float64 diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go index a7724a5a..3490e804 100644 --- a/backend/internal/service/user_service.go +++ b/backend/internal/service/user_service.go @@ -13,9 +13,9 @@ import ( ) var ( - ErrUserNotFound = infraerrors.NotFound("USER_NOT_FOUND", "user not found") - ErrPasswordIncorrect = infraerrors.BadRequest("PASSWORD_INCORRECT", "current password is incorrect") - ErrInsufficientPerms = infraerrors.Forbidden("INSUFFICIENT_PERMISSIONS", "insufficient permissions") + ErrUserNotFound = infraerrors.NotFound("USER_NOT_FOUND", "user not found") + ErrPasswordIncorrect = infraerrors.BadRequest("PASSWORD_INCORRECT", "current password is incorrect") + ErrInsufficientPerms = infraerrors.Forbidden("INSUFFICIENT_PERMISSIONS", "insufficient permissions") ErrNotifyCodeUserRateLimit = infraerrors.TooManyRequests("NOTIFY_CODE_USER_RATE_LIMIT", "too many verification codes requested, please try again later") )