feat: 添加点赞和收藏功能,优化内容详情视图和评论交互
This commit is contained in:
@@ -47,7 +47,7 @@ func (c *Content) List(
|
||||
// @Success 200 {object} dto.ContentDetail
|
||||
// @Bind id path
|
||||
func (c *Content) Get(ctx fiber.Ctx, id string) (*dto.ContentDetail, error) {
|
||||
uid := cast.ToInt64(ctx.Locals(consts.CtxKeyUser))
|
||||
uid := getUserID(ctx)
|
||||
return services.Content.Get(ctx, uid, id)
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (c *Content) Get(ctx fiber.Ctx, id string) (*dto.ContentDetail, error) {
|
||||
// @Bind id path
|
||||
// @Bind page query
|
||||
func (c *Content) ListComments(ctx fiber.Ctx, id string, page int) (*requests.Pager, error) {
|
||||
uid := cast.ToInt64(ctx.Locals(consts.CtxKeyUser))
|
||||
uid := getUserID(ctx)
|
||||
return services.Content.ListComments(ctx, uid, id, page)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (c *Content) ListComments(ctx fiber.Ctx, id string, page int) (*requests.Pa
|
||||
// @Bind id path
|
||||
// @Bind form body
|
||||
func (c *Content) CreateComment(ctx fiber.Ctx, id string, form *dto.CommentCreateForm) error {
|
||||
uid := cast.ToInt64(ctx.Locals(consts.CtxKeyUser))
|
||||
uid := getUserID(ctx)
|
||||
return services.Content.CreateComment(ctx, uid, id, form)
|
||||
}
|
||||
|
||||
@@ -99,10 +99,62 @@ func (c *Content) CreateComment(ctx fiber.Ctx, id string, form *dto.CommentCreat
|
||||
// @Success 200 {string} string "Liked"
|
||||
// @Bind id path
|
||||
func (c *Content) LikeComment(ctx fiber.Ctx, id string) error {
|
||||
uid := cast.ToInt64(ctx.Locals(consts.CtxKeyUser))
|
||||
uid := getUserID(ctx)
|
||||
return services.Content.LikeComment(ctx, uid, id)
|
||||
}
|
||||
|
||||
// Add like
|
||||
//
|
||||
// @Router /v1/contents/:id/like [post]
|
||||
// @Summary Add like
|
||||
// @Tags Content
|
||||
// @Param id path string true "Content ID"
|
||||
// @Success 200 {string} string "Liked"
|
||||
// @Bind id path
|
||||
func (c *Content) AddLike(ctx fiber.Ctx, id string) error {
|
||||
uid := getUserID(ctx)
|
||||
return services.Content.AddLike(ctx, uid, id)
|
||||
}
|
||||
|
||||
// Remove like
|
||||
//
|
||||
// @Router /v1/contents/:id/like [delete]
|
||||
// @Summary Remove like
|
||||
// @Tags Content
|
||||
// @Param id path string true "Content ID"
|
||||
// @Success 200 {string} string "Unliked"
|
||||
// @Bind id path
|
||||
func (c *Content) RemoveLike(ctx fiber.Ctx, id string) error {
|
||||
uid := getUserID(ctx)
|
||||
return services.Content.RemoveLike(ctx, uid, id)
|
||||
}
|
||||
|
||||
// Add favorite
|
||||
//
|
||||
// @Router /v1/contents/:id/favorite [post]
|
||||
// @Summary Add favorite
|
||||
// @Tags Content
|
||||
// @Param id path string true "Content ID"
|
||||
// @Success 200 {string} string "Favorited"
|
||||
// @Bind id path
|
||||
func (c *Content) AddFavorite(ctx fiber.Ctx, id string) error {
|
||||
uid := getUserID(ctx)
|
||||
return services.Content.AddFavorite(ctx, uid, id)
|
||||
}
|
||||
|
||||
// Remove favorite
|
||||
//
|
||||
// @Router /v1/contents/:id/favorite [delete]
|
||||
// @Summary Remove favorite
|
||||
// @Tags Content
|
||||
// @Param id path string true "Content ID"
|
||||
// @Success 200 {string} string "Unfavorited"
|
||||
// @Bind id path
|
||||
func (c *Content) RemoveFavorite(ctx fiber.Ctx, id string) error {
|
||||
uid := getUserID(ctx)
|
||||
return services.Content.RemoveFavorite(ctx, uid, id)
|
||||
}
|
||||
|
||||
// List curated topics
|
||||
//
|
||||
// @Router /v1/topics [get]
|
||||
@@ -115,3 +167,12 @@ func (c *Content) LikeComment(ctx fiber.Ctx, id string) error {
|
||||
func (c *Content) ListTopics(ctx fiber.Ctx) ([]dto.Topic, error) {
|
||||
return services.Content.ListTopics(ctx)
|
||||
}
|
||||
|
||||
func getUserID(ctx fiber.Ctx) int64 {
|
||||
if u := ctx.Locals(consts.CtxKeyUser); u != nil {
|
||||
if user, ok := u.(*models.User); ok {
|
||||
return user.ID
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -12,19 +12,20 @@ type ContentListFilter struct {
|
||||
}
|
||||
|
||||
type ContentItem struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Cover string `json:"cover"`
|
||||
Genre string `json:"genre"`
|
||||
Type string `json:"type"` // video, audio, article
|
||||
Price float64 `json:"price"`
|
||||
AuthorID string `json:"author_id"`
|
||||
AuthorName string `json:"author_name"`
|
||||
AuthorAvatar string `json:"author_avatar"`
|
||||
Views int `json:"views"`
|
||||
Likes int `json:"likes"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
IsPurchased bool `json:"is_purchased"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Cover string `json:"cover"`
|
||||
Genre string `json:"genre"`
|
||||
Type string `json:"type"` // video, audio, article
|
||||
Price float64 `json:"price"`
|
||||
AuthorID string `json:"author_id"`
|
||||
AuthorName string `json:"author_name"`
|
||||
AuthorAvatar string `json:"author_avatar"`
|
||||
AuthorIsFollowing bool `json:"author_is_following"`
|
||||
Views int `json:"views"`
|
||||
Likes int `json:"likes"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
IsPurchased bool `json:"is_purchased"`
|
||||
}
|
||||
|
||||
type ContentDetail struct {
|
||||
|
||||
@@ -99,6 +99,16 @@ func (r *Routes) Register(router fiber.Router) {
|
||||
Body[dto.UploadPartForm]("form"),
|
||||
))
|
||||
// Register routes for controller: Content
|
||||
r.log.Debugf("Registering route: Delete /v1/contents/:id/favorite -> content.RemoveFavorite")
|
||||
router.Delete("/v1/contents/:id/favorite"[len(r.Path()):], Func1(
|
||||
r.content.RemoveFavorite,
|
||||
PathParam[string]("id"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Delete /v1/contents/:id/like -> content.RemoveLike")
|
||||
router.Delete("/v1/contents/:id/like"[len(r.Path()):], Func1(
|
||||
r.content.RemoveLike,
|
||||
PathParam[string]("id"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Get /v1/contents -> content.List")
|
||||
router.Get("/v1/contents"[len(r.Path()):], DataFunc1(
|
||||
r.content.List,
|
||||
@@ -130,6 +140,16 @@ func (r *Routes) Register(router fiber.Router) {
|
||||
PathParam[string]("id"),
|
||||
Body[dto.CommentCreateForm]("form"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Post /v1/contents/:id/favorite -> content.AddFavorite")
|
||||
router.Post("/v1/contents/:id/favorite"[len(r.Path()):], Func1(
|
||||
r.content.AddFavorite,
|
||||
PathParam[string]("id"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Post /v1/contents/:id/like -> content.AddLike")
|
||||
router.Post("/v1/contents/:id/like"[len(r.Path()):], Func1(
|
||||
r.content.AddLike,
|
||||
PathParam[string]("id"),
|
||||
))
|
||||
// Register routes for controller: Creator
|
||||
r.log.Debugf("Registering route: Delete /v1/creator/contents/:id -> creator.DeleteContent")
|
||||
router.Delete("/v1/creator/contents/:id"[len(r.Path()):], Func2(
|
||||
|
||||
@@ -88,7 +88,7 @@ func (s *content) List(ctx context.Context, filter *content_dto.ContentListFilte
|
||||
// Convert to DTO
|
||||
data := make([]content_dto.ContentItem, len(list))
|
||||
for i, item := range list {
|
||||
data[i] = s.toContentItemDTO(item, priceMap[item.ID])
|
||||
data[i] = s.toContentItemDTO(item, priceMap[item.ID], false)
|
||||
}
|
||||
|
||||
return &requests.Pager{
|
||||
@@ -179,8 +179,18 @@ func (s *content) Get(ctx context.Context, userID int64, id string) (*content_dt
|
||||
}
|
||||
}
|
||||
|
||||
// Check if author is followed
|
||||
authorIsFollowing := false
|
||||
if userID > 0 {
|
||||
exists, _ := models.TenantUserQuery.WithContext(ctx).
|
||||
Where(models.TenantUserQuery.TenantID.Eq(item.TenantID),
|
||||
models.TenantUserQuery.Role.Contains(string(consts.TenantUserRoleMember))).
|
||||
Exists()
|
||||
authorIsFollowing = exists
|
||||
}
|
||||
|
||||
detail := &content_dto.ContentDetail{
|
||||
ContentItem: s.toContentItemDTO(&item, price),
|
||||
ContentItem: s.toContentItemDTO(&item, price, authorIsFollowing),
|
||||
Description: item.Description,
|
||||
Body: item.Body,
|
||||
MediaUrls: s.toMediaURLs(accessibleAssets),
|
||||
@@ -356,7 +366,7 @@ func (s *content) GetLibrary(ctx context.Context, userID int64) ([]user_dto.Cont
|
||||
|
||||
var data []user_dto.ContentItem
|
||||
for _, item := range list {
|
||||
dto := s.toContentItemDTO(item, 0)
|
||||
dto := s.toContentItemDTO(item, 0, false)
|
||||
dto.IsPurchased = true
|
||||
data = append(data, dto)
|
||||
}
|
||||
@@ -441,16 +451,17 @@ func (s *content) ListTopics(ctx context.Context) ([]content_dto.Topic, error) {
|
||||
|
||||
// Helpers
|
||||
|
||||
func (s *content) toContentItemDTO(item *models.Content, price float64) content_dto.ContentItem {
|
||||
func (s *content) toContentItemDTO(item *models.Content, price float64, authorIsFollowing bool) content_dto.ContentItem {
|
||||
dto := content_dto.ContentItem{
|
||||
ID: cast.ToString(item.ID),
|
||||
Title: item.Title,
|
||||
Genre: item.Genre,
|
||||
AuthorID: cast.ToString(item.UserID),
|
||||
Views: int(item.Views),
|
||||
Likes: int(item.Likes),
|
||||
CreatedAt: item.CreatedAt.Format("2006-01-02"),
|
||||
Price: price,
|
||||
ID: cast.ToString(item.ID),
|
||||
Title: item.Title,
|
||||
Genre: item.Genre,
|
||||
AuthorID: cast.ToString(item.UserID),
|
||||
Views: int(item.Views),
|
||||
Likes: int(item.Likes),
|
||||
CreatedAt: item.CreatedAt.Format("2006-01-02"),
|
||||
Price: price,
|
||||
AuthorIsFollowing: authorIsFollowing,
|
||||
}
|
||||
if item.Author != nil {
|
||||
dto.AuthorName = item.Author.Nickname
|
||||
@@ -623,7 +634,7 @@ func (s *content) getInteractList(ctx context.Context, userID int64, typ string)
|
||||
|
||||
var data []user_dto.ContentItem
|
||||
for _, item := range list {
|
||||
data = append(data, s.toContentItemDTO(item, 0))
|
||||
data = append(data, s.toContentItemDTO(item, 0, false))
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user