package handler import ( "errors" "fmt" "net/http" bizlive "kra/internal/biz/live" "kra/internal/service/dto" liveservice "kra/internal/service/live" "github.com/gin-gonic/gin" ) type Live struct { service *liveservice.StreamService catalog *liveservice.CatalogService follows *liveservice.FollowService } func NewLive(service *liveservice.StreamService, catalog *liveservice.CatalogService, follows *liveservice.FollowService) *Live { return &Live{service: service, catalog: catalog, follows: follows} } func (h *Live) FollowStatus(c *gin.Context) { id, ok := businessID(c) if !ok { return } claims := Claims(c) if claims == nil { NoAuth(c, "请登录") return } followed, err := h.follows.Status(c.Request.Context(), claims.ID, id) if err != nil { businessFail(c, err) return } OKWithData(c, gin.H{"followed": followed}) } func (h *Live) Follow(c *gin.Context) { id, ok := businessID(c) if !ok { return } claims := Claims(c) if claims == nil { NoAuth(c, "请登录") return } followed, err := h.follows.Follow(c.Request.Context(), claims.ID, id) if err != nil { businessFail(c, err) return } OKWithData(c, gin.H{"followed": followed}) } func (h *Live) Unfollow(c *gin.Context) { id, ok := businessID(c) if !ok { return } claims := Claims(c) if claims == nil { NoAuth(c, "请登录") return } if err := h.follows.Unfollow(c.Request.Context(), claims.ID, id); err != nil { businessFail(c, err) return } OKWithData(c, gin.H{"followed": false}) } func (h *Live) Like(c *gin.Context) { id, ok := businessID(c) if !ok { return } // Do not create runtime stream state for arbitrary room IDs. The stream // repository is process-local, so validating the catalog first also keeps // bogus likes from accumulating forever. room, err := h.catalog.GetPublicRoom(c.Request.Context(), id) if err != nil { businessFail(c, err) return } if !room.Enabled || room.Status != bizlive.RoomStatusLive { businessFail(c, bizlive.ErrRoomNotLive) return } count, err := h.service.Like(c.Request.Context(), fmt.Sprintf("room-%d", id)) if err != nil { businessFail(c, err) return } OKWithData(c, gin.H{"likeCount": count}) } func (h *Live) List(c *gin.Context) { items, err := h.service.List(c.Request.Context()) if err != nil { Fail(c, "直播列表获取失败") return } Write(c, CodeSuccess, gin.H{"streams": items}, "获取成功") } func (h *Live) ListCategories(c *gin.Context) { in, ok := bindQuery[dto.LiveCategoryListRequest](c) if !ok { return } items, total, e := h.catalog.ListCategories(c.Request.Context(), in) if e != nil { businessFail(c, e) return } OKWithData(c, gin.H{"list": items, "total": total}) } func (h *Live) PublicCategories(c *gin.Context) { enabled := true in := &dto.LiveCategoryListRequest{LiveListRequest: dto.LiveListRequest{Page: 1, PageSize: 100, OrderBy: "sort_asc"}, Enabled: &enabled} items, _, e := h.catalog.ListCategories(c.Request.Context(), in) if e != nil { businessFail(c, e) return } OKWithData(c, gin.H{"list": items}) } func (h *Live) CreateCategory(c *gin.Context) { in, ok := bindJSON[dto.LiveCategoryRequest](c) if !ok { return } v, e := h.catalog.CreateCategory(c.Request.Context(), in) if e != nil { businessFail(c, e) return } OKWithData(c, v) } func (h *Live) UpdateCategory(c *gin.Context) { id, ok := businessID(c) if !ok { return } in, ok := bindJSON[dto.LiveCategoryRequest](c) if !ok { return } v, e := h.catalog.UpdateCategory(c.Request.Context(), id, in) if e != nil { businessFail(c, e) return } OKWithData(c, v) } func (h *Live) DeleteCategory(c *gin.Context) { id, ok := businessID(c) if !ok { return } if e := h.catalog.DeleteCategory(c.Request.Context(), id); e != nil { businessFail(c, e) return } OK(c) } func (h *Live) ListRooms(c *gin.Context) { in, ok := bindQuery[dto.LiveRoomListRequest](c) if !ok { return } items, total, e := h.catalog.ListRooms(c.Request.Context(), in) if e != nil { businessFail(c, e) return } OKWithData(c, gin.H{"list": items, "total": total}) } func (h *Live) PublicRooms(c *gin.Context) { in, ok := bindQuery[dto.LiveRoomListRequest](c) if !ok { return } if in.Page < 1 { in.Page = 1 } if in.PageSize < 1 { in.PageSize = 20 } if in.OrderBy == "" { in.OrderBy = "viewer_count_desc" } items, total, e := h.catalog.ListPublicRooms(c.Request.Context(), in) if e != nil { businessFail(c, e) return } OKWithData(c, gin.H{"list": items, "total": total, "page": in.Page, "pageSize": in.PageSize}) } func (h *Live) GetPublicRoom(c *gin.Context) { id, ok := businessID(c) if !ok { return } v, e := h.catalog.GetPublicRoom(c.Request.Context(), id) if e != nil { businessFail(c, e) return } OKWithData(c, v) } func (h *Live) GetRoom(c *gin.Context) { id, ok := businessID(c) if !ok { return } v, e := h.catalog.GetRoom(c.Request.Context(), id) if e != nil { businessFail(c, e) return } OKWithData(c, v) } func (h *Live) CreateRoom(c *gin.Context) { in, ok := bindJSON[dto.LiveRoomRequest](c) if !ok { return } v, e := h.catalog.CreateRoom(c.Request.Context(), in) if e != nil { businessFail(c, e) return } OKWithData(c, v) } func (h *Live) UpdateRoom(c *gin.Context) { id, ok := businessID(c) if !ok { return } in, ok := bindJSON[dto.LiveRoomRequest](c) if !ok { return } v, e := h.catalog.UpdateRoom(c.Request.Context(), id, in) if e != nil { businessFail(c, e) return } OKWithData(c, v) } func (h *Live) DeleteRoom(c *gin.Context) { id, ok := businessID(c) if !ok { return } if e := h.catalog.DeleteRoom(c.Request.Context(), id); e != nil { businessFail(c, e) return } OK(c) } func (h *Live) SetRoomStatus(c *gin.Context) { id, ok := businessID(c) if !ok { return } in, ok := bindJSON[dto.LiveStatusRequest](c) if !ok { return } if e := h.catalog.SetRoomEnabled(c.Request.Context(), id, in.Enabled); e != nil { businessFail(c, e) return } OK(c) } func (h *Live) SaveRoomSort(c *gin.Context) { in, ok := bindJSON[dto.LiveSortRequest](c) if !ok { return } if e := h.catalog.SaveRoomSort(c.Request.Context(), in.Values); e != nil { businessFail(c, e) return } OK(c) } func (h *Live) Find(c *gin.Context) { item, err := h.service.Get(c.Request.Context(), c.Param("streamId")) if err != nil { if errors.Is(err, bizlive.ErrInvalidStreamID) { c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "data": nil, "msg": "streamId 无效"}) return } Fail(c, "直播间获取失败") return } Write(c, CodeSuccess, gin.H{"stream": item}, "获取成功") }