package handler import ( "encoding/json" "errors" "strconv" "github.com/gin-gonic/gin" "carrot_bbs/internal/dto" "carrot_bbs/internal/pkg/response" "carrot_bbs/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"` // 内容可选,允许只发图片 ParentID *string `json:"parent_id"` Images []string `json:"images"` // 图片URL列表 } var req CreateRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } // 验证:评论必须有内容或图片 if req.Content == "" && len(req.Images) == 0 { response.BadRequest(c, "评论内容或图片不能同时为空") return } // 将图片列表转换为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, req.Content, req.ParentID, imagesJSON, req.Images) if err != nil { var moderationErr *service.CommentModerationRejectedError if errors.As(err, &moderationErr) { response.BadRequest(c, moderationErr.UserMessage()) return } 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(), comment) if err != nil { response.InternalServerError(c, "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(), id) if err != nil { response.InternalServerError(c, "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) }