Set up project files and add .gitignore to exclude local build/runtime artifacts. Made-with: Cursor
217 lines
4.8 KiB
Go
217 lines
4.8 KiB
Go
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})
|
||
}
|