mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
fix(admin): restore currency and timestamps in admin plan list response
The composite-groups PR (#3581) replaced the raw ent SubscriptionPlan response of GET /admin/payment/plans with a projection struct but dropped the currency field added by #4323. PlanEditDialog then read an undefined currency, sent an empty string on save, and silently wiped the stored plan currency. Restore currency plus created_at/updated_at so the projection preserves the full original response shape.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user