package handler import ( "errors" "net/http" "github.com/gin-gonic/gin" "carrot_bbs/internal/dto" "carrot_bbs/internal/pkg/response" "carrot_bbs/internal/service" ) // VoteHandler 投票处理器 type VoteHandler struct { voteService *service.VoteService postService *service.PostService } // NewVoteHandler 创建投票处理器 func NewVoteHandler(voteService *service.VoteService, postService *service.PostService) *VoteHandler { return &VoteHandler{ voteService: voteService, postService: postService, } } // CreateVotePost 创建投票帖子 // POST /api/v1/posts/vote func (h *VoteHandler) CreateVotePost(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "请先登录") return } var req dto.CreateVotePostRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } post, err := h.voteService.CreateVotePost(c.Request.Context(), userID, &req) if err != nil { var moderationErr *service.PostModerationRejectedError if errors.As(err, &moderationErr) { response.BadRequest(c, moderationErr.UserMessage()) return } response.Error(c, http.StatusBadRequest, err.Error()) return } response.Success(c, post) } // GetVoteResult 获取投票结果 // GET /api/v1/posts/:id/vote func (h *VoteHandler) GetVoteResult(c *gin.Context) { postID := c.Param("id") if postID == "" { response.BadRequest(c, "帖子ID不能为空") return } // 验证帖子存在 _, err := h.postService.GetByID(c.Request.Context(), postID) if err != nil { response.NotFound(c, "帖子不存在") return } // 获取当前用户ID(可选登录) userID := c.GetString("user_id") // 如果用户未登录,返回不带has_voted的结果 if userID == "" { options, err := h.voteService.GetVoteOptions(postID) if err != nil { response.InternalServerError(c, "获取投票选项失败") return } // 计算总票数 totalVotes := 0 for _, option := range options { totalVotes += option.VotesCount } result := &dto.VoteResultDTO{ Options: options, TotalVotes: totalVotes, HasVoted: false, } response.Success(c, result) return } // 用户已登录,获取完整的投票结果 result, err := h.voteService.GetVoteResult(postID, userID) if err != nil { response.InternalServerError(c, "获取投票结果失败") return } response.Success(c, result) } // Vote 投票 // POST /api/v1/posts/:id/vote func (h *VoteHandler) Vote(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "请先登录") return } postID := c.Param("id") if postID == "" { response.BadRequest(c, "帖子ID不能为空") return } // 验证帖子存在 _, err := h.postService.GetByID(c.Request.Context(), postID) if err != nil { response.NotFound(c, "帖子不存在") return } // 解析请求体 var req struct { OptionID string `json:"option_id" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.voteService.Vote(c.Request.Context(), postID, userID, req.OptionID); err != nil { response.Error(c, http.StatusBadRequest, err.Error()) return } response.Success(c, gin.H{"success": true}) } // Unvote 取消投票 // DELETE /api/v1/posts/:id/vote func (h *VoteHandler) Unvote(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "请先登录") return } postID := c.Param("id") if postID == "" { response.BadRequest(c, "帖子ID不能为空") return } // 验证帖子存在 _, err := h.postService.GetByID(c.Request.Context(), postID) if err != nil { response.NotFound(c, "帖子不存在") return } if err := h.voteService.Unvote(c.Request.Context(), postID, userID); err != nil { response.Error(c, http.StatusBadRequest, err.Error()) return } response.Success(c, gin.H{"success": true}) } // UpdateVoteOption 更新投票选项(仅作者) // PUT /api/v1/vote-options/:id func (h *VoteHandler) UpdateVoteOption(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "请先登录") return } optionID := c.Param("id") if optionID == "" { response.BadRequest(c, "选项ID不能为空") return } // 解析请求体 var req struct { Content string `json:"content" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } // 获取帖子ID(从查询参数或请求体中获取) postID := c.Query("post_id") if postID == "" { response.BadRequest(c, "帖子ID不能为空") return } if err := h.voteService.UpdateVoteOption(c.Request.Context(), postID, optionID, userID, req.Content); err != nil { response.Error(c, http.StatusForbidden, err.Error()) return } response.Success(c, gin.H{"success": true}) }