Build composite subscription bucket two

This commit is contained in:
Heatherm Huang
2026-07-23 09:20:52 +08:00
parent 3a683fff55
commit ce3272c41b
6 changed files with 357 additions and 13 deletions
@@ -283,7 +283,62 @@ func (h *PaymentHandler) ListPlans(c *gin.Context) {
response.ErrorFrom(c, err)
return
}
response.Success(c, plans)
groupInfo := h.configService.GetGroupInfoMap(c.Request.Context(), plans)
response.Success(c, adminSubscriptionPlansForResponse(plans, groupInfo))
}
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"`
}
func adminSubscriptionPlansForResponse(plans []*dbent.SubscriptionPlan, groupInfo map[int64]service.PlanGroupInfo) []AdminSubscriptionPlanResult {
result := make([]AdminSubscriptionPlanResult, 0, len(plans))
for _, p := range plans {
if p == nil {
continue
}
gi := groupInfo[p.GroupID]
result = append(result, AdminSubscriptionPlanResult{
ID: int64(p.ID),
GroupID: p.GroupID,
GroupPlatform: gi.Platform,
GroupName: gi.Name,
RateMultiplier: gi.RateMultiplier,
DailyLimitUSD: gi.DailyLimitUSD,
WeeklyLimitUSD: gi.WeeklyLimitUSD,
MonthlyLimitUSD: gi.MonthlyLimitUSD,
ModelScopes: gi.ModelScopes,
Name: p.Name,
Description: p.Description,
Price: p.Price,
OriginalPrice: p.OriginalPrice,
ValidityDays: p.ValidityDays,
ValidityUnit: p.ValidityUnit,
Features: p.Features,
ProductName: p.ProductName,
ForSale: p.ForSale,
SortOrder: p.SortOrder,
})
}
return result
}
// CreatePlan creates a new subscription plan.
@@ -7,6 +7,7 @@ import (
"time"
dbent "github.com/Wei-Shaw/sub2api/ent"
"github.com/Wei-Shaw/sub2api/internal/service"
)
func TestSanitizeAdminPaymentOrderForResponseAddsCurrency(t *testing.T) {
@@ -46,3 +47,49 @@ func TestSanitizeAdminPaymentOrderForResponseAddsCurrency(t *testing.T) {
t.Fatalf("expected provider_snapshot to be omitted, got %s", string(body))
}
}
func TestAdminSubscriptionPlansForResponseIncludesCompositeGroupInfo(t *testing.T) {
weekly := 25.0
plans := []*dbent.SubscriptionPlan{
{
ID: 11,
GroupID: 7,
Name: "All models",
Description: "Composite access",
Price: 19.99,
ValidityDays: 30,
ValidityUnit: "days",
Features: "OpenAI\nClaude\nGemini\nGrok",
ProductName: "Sub2API",
ForSale: true,
SortOrder: 1,
},
}
groupInfo := map[int64]service.PlanGroupInfo{
7: {
Platform: service.PlatformComposite,
Name: "Bucket 2 composite",
RateMultiplier: 1.5,
WeeklyLimitUSD: &weekly,
ModelScopes: []string{"openai", "claude", "gemini", "grok"},
},
}
got := adminSubscriptionPlansForResponse(plans, groupInfo)
if len(got) != 1 {
t.Fatalf("expected one plan, got %d", len(got))
}
if got[0].GroupPlatform != service.PlatformComposite {
t.Fatalf("expected composite group platform, got %q", got[0].GroupPlatform)
}
if got[0].GroupName != "Bucket 2 composite" {
t.Fatalf("expected group name to be included, got %q", got[0].GroupName)
}
if got[0].WeeklyLimitUSD == nil || *got[0].WeeklyLimitUSD != weekly {
t.Fatalf("expected weekly limit to be included, got %#v", got[0].WeeklyLimitUSD)
}
if strings.Join(got[0].ModelScopes, ",") != "openai,claude,gemini,grok" {
t.Fatalf("expected model scopes to be preserved, got %#v", got[0].ModelScopes)
}
}