package handler import ( "encoding/json" "strconv" "github.com/gin-gonic/gin" "with_you/internal/dto" "with_you/internal/model" "with_you/internal/pkg/cursor" "with_you/internal/pkg/response" "with_you/internal/service" ) // CommentHandler 评论处理器 type CommentHandler struct { commentService *service.CommentService } // NewCommentHandler 创建评论处理器 func NewCommentHandler(commentService *service.CommentService) *CommentHandler { return &CommentHandler{ commentService: commentService, } } // Create 创建评论 func (h *CommentHandler) Create(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } type CreateRequest struct { PostID string `json:"post_id" binding:"required"` Content string `json:"content"` Segments model.MessageSegments `json:"segments"` ParentID *string `json:"parent_id"` Images []string `json:"images"` } var req CreateRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } // 验证:评论必须有内容或图片或segments if req.Content == "" && len(req.Images) == 0 && len(req.Segments) == 0 { response.BadRequest(c, "评论内容或图片不能同时为空") return } // 如果没有content但有segments,从segments提取文本 content := req.Content if content == "" && len(req.Segments) > 0 { content = dto.ExtractTextContent(req.Segments) } // 将图片列表转换为JSON字符串 var imagesJSON string if len(req.Images) > 0 { imagesBytes, _ := json.Marshal(req.Images) imagesJSON = string(imagesBytes) } comment, err := h.commentService.Create(c.Request.Context(), req.PostID, userID, content, req.Segments, req.ParentID, imagesJSON, req.Images) if err != nil { response.InternalServerError(c, "failed to create comment") return } response.Success(c, dto.ConvertCommentToResponse(comment, false)) } // GetByID 获取单条评论详情 func (h *CommentHandler) GetByID(c *gin.Context) { userID := c.GetString("user_id") id := c.Param("id") comment, err := h.commentService.GetByID(c.Request.Context(), id) if err != nil { response.NotFound(c, "comment not found") return } resp := dto.ConvertCommentToResponse(comment, h.commentService.IsLiked(c.Request.Context(), id, userID)) response.Success(c, resp) } // GetByPostID 获取帖子评论 func (h *CommentHandler) GetByPostID(c *gin.Context) { userID := c.GetString("user_id") postID := c.Param("id") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) comments, total, err := h.commentService.GetByPostID(c.Request.Context(), postID, page, pageSize) if err != nil { response.InternalServerError(c, "failed to get comments") return } // 转换为响应结构,检查每个评论的点赞状态 commentResponses := dto.ConvertCommentsToResponseWithUser(comments, userID, h.commentService) response.Paginated(c, commentResponses, total, page, pageSize) } // GetReplies 获取回复 func (h *CommentHandler) GetReplies(c *gin.Context) { userID := c.GetString("user_id") parentID := c.Param("id") comments, err := h.commentService.GetReplies(c.Request.Context(), parentID) if err != nil { response.InternalServerError(c, "failed to get replies") return } // 转换为响应结构,检查每个回复的点赞状态 commentResponses := dto.ConvertCommentsToResponseWithUser(comments, userID, h.commentService) response.Success(c, commentResponses) } // GetRepliesByRootID 根据根评论ID分页获取回复(扁平化) func (h *CommentHandler) GetRepliesByRootID(c *gin.Context) { userID := c.GetString("user_id") rootID := c.Param("id") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) replies, total, err := h.commentService.GetRepliesByRootID(c.Request.Context(), rootID, page, pageSize) if err != nil { response.InternalServerError(c, "failed to get replies") return } // 转换为响应结构,检查每个回复的点赞状态 replyResponses := dto.ConvertCommentsToResponseWithUser(replies, userID, h.commentService) response.Paginated(c, replyResponses, total, page, pageSize) } // Update 更新评论 func (h *CommentHandler) Update(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") comment, err := h.commentService.GetByID(c.Request.Context(), id) if err != nil { response.NotFound(c, "comment not found") return } if comment.UserID != userID { response.Forbidden(c, "cannot update others' comment") return } type UpdateRequest struct { Content string `json:"content" binding:"required"` } var req UpdateRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } comment.Content = req.Content err = h.commentService.Update(c.Request.Context(), userID, comment) if err != nil { response.HandleError(c, err, "failed to update comment") return } response.Success(c, dto.ConvertCommentToResponse(comment, false)) } // Delete 删除评论 func (h *CommentHandler) Delete(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") comment, err := h.commentService.GetByID(c.Request.Context(), id) if err != nil { response.NotFound(c, "comment not found") return } if comment.UserID != userID { response.Forbidden(c, "cannot delete others' comment") return } err = h.commentService.Delete(c.Request.Context(), userID, id) if err != nil { response.HandleError(c, err, "failed to delete comment") return } response.SuccessWithMessage(c, "comment deleted", nil) } // Like 点赞评论 func (h *CommentHandler) Like(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") err := h.commentService.Like(c.Request.Context(), id, userID) if err != nil { response.InternalServerError(c, "failed to like comment") return } response.SuccessWithMessage(c, "liked", nil) } // Unlike 取消点赞评论 func (h *CommentHandler) Unlike(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") err := h.commentService.Unlike(c.Request.Context(), id, userID) if err != nil { response.InternalServerError(c, "failed to unlike comment") return } response.SuccessWithMessage(c, "unliked", nil) } // GetByPostIDByCursor 游标分页获取帖子评论 func (h *CommentHandler) GetByPostIDByCursor(c *gin.Context) { userID := c.GetString("user_id") postID := c.Param("id") // 解析游标分页请求 req := h.parseCursorRequest(c) // 调用游标分页服务 result, err := h.commentService.GetCommentsByCursor(c.Request.Context(), postID, req) if err != nil { response.InternalServerError(c, "failed to get comments") return } // 转换为响应结构,检查每个评论的点赞状态 commentResponses := dto.ConvertCommentsToResponseWithUser(result.Items, userID, h.commentService) // 构建游标分页响应 cursorResp := &dto.CommentCursorPageResponse{ Items: commentResponses, NextCursor: result.NextCursor, PrevCursor: result.PrevCursor, HasMore: result.HasMore, } response.Success(c, cursorResp) } // GetRepliesByRootIDByCursor 游标分页获取根评论的回复 func (h *CommentHandler) GetRepliesByRootIDByCursor(c *gin.Context) { userID := c.GetString("user_id") rootID := c.Param("id") // 解析游标分页请求 req := h.parseCursorRequest(c) // 调用游标分页服务 result, err := h.commentService.GetRepliesByCursor(c.Request.Context(), rootID, req) if err != nil { response.InternalServerError(c, "failed to get replies") return } // 转换为响应结构,检查每个回复的点赞状态 replyResponses := dto.ConvertCommentsToResponseWithUser(result.Items, userID, h.commentService) // 构建游标分页响应 cursorResp := &dto.CommentCursorPageResponse{ Items: replyResponses, NextCursor: result.NextCursor, PrevCursor: result.PrevCursor, HasMore: result.HasMore, } response.Success(c, cursorResp) } // parseCursorRequest 解析游标分页请求参数 func (h *CommentHandler) parseCursorRequest(c *gin.Context) *cursor.PageRequest { cursorStr := c.Query("cursor") direction := cursor.Direction(c.DefaultQuery("direction", "forward")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) return cursor.NewPageRequest(cursorStr, direction, pageSize) }