This commit is contained in:
Yvan
2026-09-22 12:04:22 +08:00
parent 829d75c657
commit bf33e87d72
75 changed files with 2299 additions and 276 deletions
+12
View File
@@ -1,7 +1,9 @@
package handler
import (
"errors"
"github.com/gin-gonic/gin"
bizgift "kra/internal/biz/gift"
"kra/internal/service/dto"
service "kra/internal/service/gift"
)
@@ -136,6 +138,16 @@ func (h *Gift) Send(c *gin.Context) {
}
v, e := h.s.Send(c.Request.Context(), claims.ID, id, in)
if e != nil {
var insufficient *bizgift.InsufficientBalanceError
if errors.As(e, &insufficient) {
Write(c, CodeError, gin.H{
"reason": "insufficient_balance",
"required": insufficient.Required,
"balance": insufficient.Balance,
"missing": insufficient.Missing,
}, e.Error())
return
}
businessFail(c, e)
return
}
+96 -15
View File
@@ -2,6 +2,7 @@ package handler
import (
"errors"
"fmt"
"net/http"
bizlive "kra/internal/biz/live"
@@ -14,10 +15,89 @@ import (
type Live struct {
service *liveservice.StreamService
catalog *liveservice.CatalogService
follows *liveservice.FollowService
}
func NewLive(service *liveservice.StreamService, catalog *liveservice.CatalogService) *Live {
return &Live{service: service, catalog: catalog}
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.GetRoom(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) {
@@ -114,17 +194,27 @@ func (h *Live) PublicRooms(c *gin.Context) {
in.PageSize = 20
}
if in.OrderBy == "" {
in.OrderBy = "sort_asc"
in.OrderBy = "viewer_count_desc"
}
enabled := true
in.Enabled = &enabled
items, total, e := h.catalog.ListRooms(c.Request.Context(), in)
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 {
@@ -203,15 +293,6 @@ func (h *Live) SaveRoomSort(c *gin.Context) {
OK(c)
}
func (h *Live) LegacyList(c *gin.Context) {
items, err := h.service.List(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"ok": false, "error": "直播列表获取失败"})
return
}
c.JSON(http.StatusOK, gin.H{"ok": true, "streams": items})
}
func (h *Live) Find(c *gin.Context) {
item, err := h.service.Get(c.Request.Context(), c.Param("streamId"))
if err != nil {
+6 -6
View File
@@ -15,14 +15,14 @@ func (h *LiveChat) History(c *gin.Context) {
if !ok {
return
}
in, ok := bindQuery[dto.LiveChatHistoryRequest](c)
if !ok {
return
}
items, err := h.service.History(c.Request.Context(), roomID, in.Limit)
// Chat is intentionally scoped to the current live connection. Do not
// replay rows persisted by an earlier viewer/session through HTTP. Still
// validate the room so callers receive a useful not-found response.
items, err := h.service.History(c.Request.Context(), roomID, 1)
if err != nil {
businessFail(c, err)
return
}
OKWithData(c, gin.H{"list": items})
_ = items
OKWithData(c, gin.H{"list": []*dto.LiveChatMessageResponse{}})
}
+14
View File
@@ -58,6 +58,20 @@ func (h *LiveClient) Me(c *gin.Context) {
OKWithData(c, result)
}
func (h *LiveClient) Balance(c *gin.Context) {
claims := Claims(c)
if claims == nil {
NoAuth(c, "请登录")
return
}
result, err := h.service.Balance(c.Request.Context(), claims.ID)
if err != nil {
businessFail(c, err)
return
}
OKWithData(c, result)
}
func (h *LiveClient) UpdateProfile(c *gin.Context) {
claims := Claims(c)
if claims == nil {
+36
View File
@@ -9,6 +9,14 @@ import (
type Recharge struct{ s *service.Service }
func NewRecharge(s *service.Service) *Recharge { return &Recharge{s: s} }
func (h *Recharge) Providers(c *gin.Context) {
v, e := h.s.Providers(c.Request.Context())
if e != nil {
businessFail(c, e)
return
}
OKWithData(c, gin.H{"list": v})
}
func (h *Recharge) Plans(c *gin.Context) {
v, e := h.s.Plans(c.Request.Context())
if e != nil {
@@ -48,6 +56,34 @@ func (h *Recharge) Get(c *gin.Context) {
}
OKWithData(c, v)
}
func (h *Recharge) GetMine(c *gin.Context) {
claims := Claims(c)
if claims == nil {
NoAuth(c, "请登录")
return
}
v, e := h.s.GetForUser(c.Request.Context(), claims.ID, c.Param("orderNo"))
if e != nil {
businessFail(c, e)
return
}
OKWithData(c, v)
}
func (h *Recharge) PaymentStatus(c *gin.Context) {
claims := Claims(c)
if claims == nil {
NoAuth(c, "请登录")
return
}
v, e := h.s.PaymentStatus(c.Request.Context(), claims.ID, c.Param("orderNo"))
if e != nil {
businessFail(c, e)
return
}
OKWithData(c, v)
}
func (h *Recharge) Create(c *gin.Context) {
in, ok := bindJSON[dto.RechargeOrderRequest](c)
if !ok {