Files
sub2api/backend/internal/handler/subscription_handler.go
2025-12-18 13:50:39 +08:00

204 lines
5.7 KiB
Go

package handler
import (
"sub2api/internal/model"
"sub2api/internal/pkg/response"
"sub2api/internal/service"
"github.com/gin-gonic/gin"
)
// SubscriptionSummaryItem represents a subscription item in summary
type SubscriptionSummaryItem struct {
ID int64 `json:"id"`
GroupID int64 `json:"group_id"`
GroupName string `json:"group_name"`
Status string `json:"status"`
DailyUsedUSD float64 `json:"daily_used_usd,omitempty"`
DailyLimitUSD float64 `json:"daily_limit_usd,omitempty"`
WeeklyUsedUSD float64 `json:"weekly_used_usd,omitempty"`
WeeklyLimitUSD float64 `json:"weekly_limit_usd,omitempty"`
MonthlyUsedUSD float64 `json:"monthly_used_usd,omitempty"`
MonthlyLimitUSD float64 `json:"monthly_limit_usd,omitempty"`
ExpiresAt *string `json:"expires_at,omitempty"`
}
// SubscriptionProgressInfo represents subscription with progress info
type SubscriptionProgressInfo struct {
Subscription *model.UserSubscription `json:"subscription"`
Progress *service.SubscriptionProgress `json:"progress"`
}
// SubscriptionHandler handles user subscription operations
type SubscriptionHandler struct {
subscriptionService *service.SubscriptionService
}
// NewSubscriptionHandler creates a new user subscription handler
func NewSubscriptionHandler(subscriptionService *service.SubscriptionService) *SubscriptionHandler {
return &SubscriptionHandler{
subscriptionService: subscriptionService,
}
}
// List handles listing current user's subscriptions
// GET /api/v1/subscriptions
func (h *SubscriptionHandler) List(c *gin.Context) {
user, exists := c.Get("user")
if !exists {
response.Unauthorized(c, "User not found in context")
return
}
u, ok := user.(*model.User)
if !ok {
response.InternalError(c, "Invalid user in context")
return
}
subscriptions, err := h.subscriptionService.ListUserSubscriptions(c.Request.Context(), u.ID)
if err != nil {
response.InternalError(c, "Failed to list subscriptions: "+err.Error())
return
}
response.Success(c, subscriptions)
}
// GetActive handles getting current user's active subscriptions
// GET /api/v1/subscriptions/active
func (h *SubscriptionHandler) GetActive(c *gin.Context) {
user, exists := c.Get("user")
if !exists {
response.Unauthorized(c, "User not found in context")
return
}
u, ok := user.(*model.User)
if !ok {
response.InternalError(c, "Invalid user in context")
return
}
subscriptions, err := h.subscriptionService.ListActiveUserSubscriptions(c.Request.Context(), u.ID)
if err != nil {
response.InternalError(c, "Failed to get active subscriptions: "+err.Error())
return
}
response.Success(c, subscriptions)
}
// GetProgress handles getting subscription progress for current user
// GET /api/v1/subscriptions/progress
func (h *SubscriptionHandler) GetProgress(c *gin.Context) {
user, exists := c.Get("user")
if !exists {
response.Unauthorized(c, "User not found in context")
return
}
u, ok := user.(*model.User)
if !ok {
response.InternalError(c, "Invalid user in context")
return
}
// Get all active subscriptions with progress
subscriptions, err := h.subscriptionService.ListActiveUserSubscriptions(c.Request.Context(), u.ID)
if err != nil {
response.InternalError(c, "Failed to get subscriptions: "+err.Error())
return
}
result := make([]SubscriptionProgressInfo, 0, len(subscriptions))
for i := range subscriptions {
sub := &subscriptions[i]
progress, err := h.subscriptionService.GetSubscriptionProgress(c.Request.Context(), sub.ID)
if err != nil {
// Skip subscriptions with errors
continue
}
result = append(result, SubscriptionProgressInfo{
Subscription: sub,
Progress: progress,
})
}
response.Success(c, result)
}
// GetSummary handles getting a summary of current user's subscription status
// GET /api/v1/subscriptions/summary
func (h *SubscriptionHandler) GetSummary(c *gin.Context) {
user, exists := c.Get("user")
if !exists {
response.Unauthorized(c, "User not found in context")
return
}
u, ok := user.(*model.User)
if !ok {
response.InternalError(c, "Invalid user in context")
return
}
// Get all active subscriptions
subscriptions, err := h.subscriptionService.ListActiveUserSubscriptions(c.Request.Context(), u.ID)
if err != nil {
response.InternalError(c, "Failed to get subscriptions: "+err.Error())
return
}
var totalUsed float64
items := make([]SubscriptionSummaryItem, 0, len(subscriptions))
for _, sub := range subscriptions {
item := SubscriptionSummaryItem{
ID: sub.ID,
GroupID: sub.GroupID,
Status: sub.Status,
DailyUsedUSD: sub.DailyUsageUSD,
WeeklyUsedUSD: sub.WeeklyUsageUSD,
MonthlyUsedUSD: sub.MonthlyUsageUSD,
}
// Add group info if preloaded
if sub.Group != nil {
item.GroupName = sub.Group.Name
if sub.Group.DailyLimitUSD != nil {
item.DailyLimitUSD = *sub.Group.DailyLimitUSD
}
if sub.Group.WeeklyLimitUSD != nil {
item.WeeklyLimitUSD = *sub.Group.WeeklyLimitUSD
}
if sub.Group.MonthlyLimitUSD != nil {
item.MonthlyLimitUSD = *sub.Group.MonthlyLimitUSD
}
}
// Format expiration time
if !sub.ExpiresAt.IsZero() {
formatted := sub.ExpiresAt.Format("2006-01-02T15:04:05Z07:00")
item.ExpiresAt = &formatted
}
// Track total usage (use monthly as the most comprehensive)
totalUsed += sub.MonthlyUsageUSD
items = append(items, item)
}
summary := struct {
ActiveCount int `json:"active_count"`
TotalUsedUSD float64 `json:"total_used_usd"`
Subscriptions []SubscriptionSummaryItem `json:"subscriptions"`
}{
ActiveCount: len(subscriptions),
TotalUsedUSD: totalUsed,
Subscriptions: items,
}
response.Success(c, summary)
}