diff --git a/backend/internal/handler/admin/payment_handler.go b/backend/internal/handler/admin/payment_handler.go index f01917740b..1749d19670 100644 --- a/backend/internal/handler/admin/payment_handler.go +++ b/backend/internal/handler/admin/payment_handler.go @@ -288,25 +288,28 @@ func (h *PaymentHandler) ListPlans(c *gin.Context) { } type AdminSubscriptionPlanResult struct { - ID int64 `json:"id"` - GroupID int64 `json:"group_id"` - GroupPlatform string `json:"group_platform,omitempty"` - GroupName string `json:"group_name,omitempty"` - RateMultiplier float64 `json:"rate_multiplier,omitempty"` - DailyLimitUSD *float64 `json:"daily_limit_usd,omitempty"` - WeeklyLimitUSD *float64 `json:"weekly_limit_usd,omitempty"` - MonthlyLimitUSD *float64 `json:"monthly_limit_usd,omitempty"` - ModelScopes []string `json:"supported_model_scopes,omitempty"` - Name string `json:"name"` - Description string `json:"description"` - Price float64 `json:"price"` - OriginalPrice *float64 `json:"original_price,omitempty"` - ValidityDays int `json:"validity_days"` - ValidityUnit string `json:"validity_unit"` - Features string `json:"features"` - ProductName string `json:"product_name"` - ForSale bool `json:"for_sale"` - SortOrder int `json:"sort_order"` + ID int64 `json:"id"` + GroupID int64 `json:"group_id"` + GroupPlatform string `json:"group_platform,omitempty"` + GroupName string `json:"group_name,omitempty"` + RateMultiplier float64 `json:"rate_multiplier,omitempty"` + DailyLimitUSD *float64 `json:"daily_limit_usd,omitempty"` + WeeklyLimitUSD *float64 `json:"weekly_limit_usd,omitempty"` + MonthlyLimitUSD *float64 `json:"monthly_limit_usd,omitempty"` + ModelScopes []string `json:"supported_model_scopes,omitempty"` + Name string `json:"name"` + Description string `json:"description"` + Price float64 `json:"price"` + OriginalPrice *float64 `json:"original_price,omitempty"` + Currency string `json:"currency,omitempty"` + ValidityDays int `json:"validity_days"` + ValidityUnit string `json:"validity_unit"` + Features string `json:"features"` + ProductName string `json:"product_name"` + ForSale bool `json:"for_sale"` + SortOrder int `json:"sort_order"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` } func adminSubscriptionPlansForResponse(plans []*dbent.SubscriptionPlan, groupInfo map[int64]service.PlanGroupInfo) []AdminSubscriptionPlanResult { @@ -330,12 +333,15 @@ func adminSubscriptionPlansForResponse(plans []*dbent.SubscriptionPlan, groupInf Description: p.Description, Price: p.Price, OriginalPrice: p.OriginalPrice, + Currency: p.Currency, ValidityDays: p.ValidityDays, ValidityUnit: p.ValidityUnit, Features: p.Features, ProductName: p.ProductName, ForSale: p.ForSale, SortOrder: p.SortOrder, + CreatedAt: p.CreatedAt, + UpdatedAt: p.UpdatedAt, }) } return result diff --git a/backend/internal/handler/admin/payment_handler_test.go b/backend/internal/handler/admin/payment_handler_test.go index dc77e93f36..d0752ec09a 100644 --- a/backend/internal/handler/admin/payment_handler_test.go +++ b/backend/internal/handler/admin/payment_handler_test.go @@ -50,6 +50,7 @@ func TestSanitizeAdminPaymentOrderForResponseAddsCurrency(t *testing.T) { func TestAdminSubscriptionPlansForResponseIncludesCompositeGroupInfo(t *testing.T) { weekly := 25.0 + now := time.Now() plans := []*dbent.SubscriptionPlan{ { ID: 11, @@ -57,12 +58,15 @@ func TestAdminSubscriptionPlansForResponseIncludesCompositeGroupInfo(t *testing. Name: "All models", Description: "Composite access", Price: 19.99, + Currency: "CNY", ValidityDays: 30, ValidityUnit: "days", Features: "OpenAI\nClaude\nGemini\nGrok", ProductName: "Sub2API", ForSale: true, SortOrder: 1, + CreatedAt: now, + UpdatedAt: now, }, } groupInfo := map[int64]service.PlanGroupInfo{ @@ -92,4 +96,12 @@ func TestAdminSubscriptionPlansForResponseIncludesCompositeGroupInfo(t *testing. if strings.Join(got[0].ModelScopes, ",") != "openai,claude,gemini,grok" { t.Fatalf("expected model scopes to be preserved, got %#v", got[0].ModelScopes) } + // 投影必须保留 ent 原始响应的全部套餐字段:currency 丢失曾导致编辑保存时 + // 静默清空套餐货币(PlanEditDialog 回传空串 → SetCurrency(""))。 + if got[0].Currency != "CNY" { + t.Fatalf("expected currency to be preserved, got %q", got[0].Currency) + } + if !got[0].CreatedAt.Equal(now) || !got[0].UpdatedAt.Equal(now) { + t.Fatalf("expected created_at/updated_at to be preserved, got %v / %v", got[0].CreatedAt, got[0].UpdatedAt) + } }