package feed import ( "feedsystem_video_go/internal/middleware/jwt" "feedsystem_video_go/internal/apierror" "time" "github.com/gin-gonic/gin" ) type FeedHandler struct { service *FeedService } func NewFeedHandler(service *FeedService) *FeedHandler { return &FeedHandler{service: service} } func (f *FeedHandler) ListLatest(c *gin.Context) { var req ListLatestRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(apierror.ClassifyHTTPStatus(err), gin.H{"error": err.Error()}) return } if req.Limit <= 0 || req.Limit > 50 { req.Limit = 10 } var latestTime time.Time if req.LatestTime > 0 { latestTime = time.UnixMilli(req.LatestTime) } viewerAccountID, err := jwt.GetAccountID(c) if err != nil { viewerAccountID = 0 } feedItems, err := f.service.ListLatest(c.Request.Context(), req.Limit, latestTime, viewerAccountID) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } feedItems.VideoList = nonNilFeedVideoItems(feedItems.VideoList) c.JSON(200, feedItems) } func (f *FeedHandler) ListLikesCount(c *gin.Context) { var req ListLikesCountRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(apierror.ClassifyHTTPStatus(err), gin.H{"error": err.Error()}) return } if req.Limit <= 0 || req.Limit > 50 { req.Limit = 10 } var cursor *LikesCountCursor if req.LikesCountBefore != nil || req.IDBefore != nil { if req.LikesCountBefore == nil || req.IDBefore == nil { c.JSON(400, gin.H{"error": "likes_count_before and id_before must be provided together"}) return } likesCountBefore := *req.LikesCountBefore idBefore := *req.IDBefore if likesCountBefore < 0 { c.JSON(400, gin.H{"error": "invalid cursor: likes_count_before must be >= 0"}) return } if idBefore == 0 { if likesCountBefore != 0 { c.JSON(400, gin.H{"error": "invalid cursor: id_before must be > 0"}) return } } else { cursor = &LikesCountCursor{ LikesCount: likesCountBefore, ID: idBefore, } } } viewerAccountID, err := jwt.GetAccountID(c) if err != nil { viewerAccountID = 0 } feedItems, err := f.service.ListLikesCount(c.Request.Context(), req.Limit, cursor, viewerAccountID) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } feedItems.VideoList = nonNilFeedVideoItems(feedItems.VideoList) c.JSON(200, feedItems) } func (f *FeedHandler) ListByFollowing(c *gin.Context) { var req ListByFollowingRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(apierror.ClassifyHTTPStatus(err), gin.H{"error": err.Error()}) return } if req.Limit <= 0 || req.Limit > 50 { req.Limit = 10 } viewerAccountID, err := jwt.GetAccountID(c) if err != nil { viewerAccountID = 0 } var latestTime time.Time if req.LatestTime > 0 { latestTime = time.Unix(req.LatestTime, 0) } feedItems, err := f.service.ListByFollowing(c.Request.Context(), req.Limit, latestTime, viewerAccountID) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } feedItems.VideoList = nonNilFeedVideoItems(feedItems.VideoList) c.JSON(200, feedItems) } func (f *FeedHandler) ListByPopularity(c *gin.Context) { var req ListByPopularityRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(apierror.ClassifyHTTPStatus(err), gin.H{"error": err.Error()}) return } if req.Limit <= 0 || req.Limit > 50 { req.Limit = 10 } viewerAccountID, err := jwt.GetAccountID(c) if err != nil { viewerAccountID = 0 } var latestPopularity int64 var latestBefore time.Time var latestIDBefore uint if req.LatestPopularity < 0 { c.JSON(400, gin.H{"error": "latest_popularity must be >= 0"}) return } anyCursor := !req.LatestBefore.IsZero() || req.LatestIDBefore != nil if anyCursor { if req.LatestBefore.IsZero() || req.LatestIDBefore == nil || *req.LatestIDBefore == 0 { c.JSON(400, gin.H{"error": "latest_before and latest_id_before must be provided together"}) return } latestPopularity = req.LatestPopularity latestBefore = req.LatestBefore latestIDBefore = *req.LatestIDBefore } resp, err := f.service.ListByPopularity( c.Request.Context(), req.Limit, req.AsOf, req.Offset, viewerAccountID, latestPopularity, latestBefore, latestIDBefore, ) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } resp.VideoList = nonNilFeedVideoItems(resp.VideoList) c.JSON(200, resp) } func nonNilFeedVideoItems(items []FeedVideoItem) []FeedVideoItem { if items == nil { return []FeedVideoItem{} } return items } func (h *FeedHandler) ListByTag(c *gin.Context) { var req struct { TagName string `json:"tag_name"` Limit int `json:"limit"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } if req.TagName == "" { c.JSON(400, gin.H{"error": "tag_name is required"}) return } if req.Limit <= 0 || req.Limit > 50 { req.Limit = 10 } viewerAccountID, _ := jwt.GetAccountID(c) items, err := h.service.ListByTag(c.Request.Context(), req.TagName, req.Limit, viewerAccountID) if err != nil { c.JSON(apierror.ClassifyHTTPStatus(err), gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{"video_list": nonNilFeedVideoItems(items)}) }