Initial backend repository commit.
Set up project files and add .gitignore to exclude local build/runtime artifacts. Made-with: Cursor
This commit is contained in:
216
internal/handler/vote_handler.go
Normal file
216
internal/handler/vote_handler.go
Normal file
@@ -0,0 +1,216 @@
|
||||
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})
|
||||
}
|
||||
Reference in New Issue
Block a user