initial commit

This commit is contained in:
Jiao77
2026-03-01 09:13:24 +08:00
commit 72baa341cc
43 changed files with 12560 additions and 0 deletions

View File

@@ -0,0 +1,169 @@
package handlers
import (
"crypto/sha256"
"encoding/hex"
"net/http"
"github.com/gin-gonic/gin"
"github.com/novablog/server/internal/database"
"github.com/novablog/server/internal/middleware"
"github.com/novablog/server/internal/models"
"gorm.io/gorm"
)
// LikeHandler 点赞处理器
type LikeHandler struct{}
// NewLikeHandler 创建点赞处理器
func NewLikeHandler() *LikeHandler {
return &LikeHandler{}
}
// LikeRequest 点赞请求
type LikeRequest struct {
PostID string `json:"post_id" binding:"required"`
}
// LikeResponse 点赞响应
type LikeResponse struct {
Liked bool `json:"liked"`
LikeCount int `json:"like_count"`
}
// ToggleLike 切换点赞状态(点赞/取消点赞)
func (h *LikeHandler) ToggleLike(c *gin.Context) {
var req LikeRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 获取用户 ID可选支持未登录用户
userID, isLoggedIn := middleware.GetUserID(c)
// 获取访客 IP Hash用于未登录用户的防刷
ipHash := ""
if !isLoggedIn {
ip := c.ClientIP()
hash := sha256.Sum256([]byte(ip + "novablog-salt")) // 加盐防止反向推导
ipHash = hex.EncodeToString(hash[:])[:64]
}
// 检查是否已点赞
var existingLike models.Like
var err error
if isLoggedIn {
err = database.DB.Where("post_id = ? AND user_id = ?", req.PostID, userID).First(&existingLike).Error
} else {
err = database.DB.Where("post_id = ? AND ip_hash = ?", req.PostID, ipHash).First(&existingLike).Error
}
liked := false
if err == gorm.ErrRecordNotFound {
// 未点赞,创建点赞记录
like := models.Like{
PostID: req.PostID,
IPHash: ipHash,
}
if isLoggedIn {
like.UserID = &userID
}
if err := database.DB.Create(&like).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to like"})
return
}
liked = true
// 更新点赞计数
h.updateLikeCount(req.PostID, 1)
} else if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database error"})
return
} else {
// 已点赞,取消点赞
if err := database.DB.Delete(&existingLike).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to unlike"})
return
}
liked = false
// 更新点赞计数
h.updateLikeCount(req.PostID, -1)
}
// 获取当前点赞数
likeCount := h.getLikeCount(req.PostID)
c.JSON(http.StatusOK, LikeResponse{
Liked: liked,
LikeCount: likeCount,
})
}
// GetLikeStatus 获取点赞状态
func (h *LikeHandler) GetLikeStatus(c *gin.Context) {
postID := c.Query("post_id")
if postID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "post_id is required"})
return
}
// 获取用户 ID可选
userID, isLoggedIn := middleware.GetUserID(c)
// 获取访客 IP Hash
ipHash := ""
if !isLoggedIn {
ip := c.ClientIP()
hash := sha256.Sum256([]byte(ip + "novablog-salt"))
ipHash = hex.EncodeToString(hash[:])[:64]
}
// 检查是否已点赞
var existingLike models.Like
var err error
if isLoggedIn {
err = database.DB.Where("post_id = ? AND user_id = ?", postID, userID).First(&existingLike).Error
} else {
err = database.DB.Where("post_id = ? AND ip_hash = ?", postID, ipHash).First(&existingLike).Error
}
liked := err == nil
// 获取点赞数
likeCount := h.getLikeCount(postID)
c.JSON(http.StatusOK, LikeResponse{
Liked: liked,
LikeCount: likeCount,
})
}
// updateLikeCount 更新点赞计数
func (h *LikeHandler) updateLikeCount(postID string, delta int) {
var likeCount models.LikeCount
result := database.DB.FirstOrCreate(&likeCount, models.LikeCount{PostID: postID})
if result.Error != nil {
return
}
likeCount.Count += delta
if likeCount.Count < 0 {
likeCount.Count = 0
}
database.DB.Save(&likeCount)
}
// getLikeCount 获取点赞数
func (h *LikeHandler) getLikeCount(postID string) int {
var likeCount models.LikeCount
if err := database.DB.Where("post_id = ?", postID).First(&likeCount).Error; err != nil {
return 0
}
return likeCount.Count
}