Files
backend/internal/handler/vote_handler.go
lan 4d8f2ec997 Initial backend repository commit.
Set up project files and add .gitignore to exclude local build/runtime artifacts.

Made-with: Cursor
2026-03-09 21:28:58 +08:00

217 lines
4.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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})
}