diff --git a/backend/handler/like.go b/backend/handler/like.go index d81ca0d..1e69671 100644 --- a/backend/handler/like.go +++ b/backend/handler/like.go @@ -5,7 +5,6 @@ import ( "strconv" "github.com/freefire/jiang13-bbs/middleware" - "github.com/freefire/jiang13-bbs/model" "github.com/gin-gonic/gin" ) @@ -22,11 +21,6 @@ func (h *Handlers) ToggleLike(c *gin.Context) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - // 仅点赞时通知帖子作者(取消点赞不通知) - if liked { - if authorID, e := h.Post.GetAuthorID(uint(id)); e == nil { - h.Notification.Create(authorID, claims.ID, model.NotificationTypeLike, uint(id), 0, "") - } - } + // 点赞通知由后台延迟落库:误点后很快取消则不打扰作者 c.JSON(http.StatusOK, gin.H{"liked": liked, "like_count": count}) } diff --git a/backend/service/notification.go b/backend/service/notification.go index f0e254d..8660c60 100644 --- a/backend/service/notification.go +++ b/backend/service/notification.go @@ -1,10 +1,16 @@ package service import ( + "log" + "time" + "github.com/freefire/jiang13-bbs/model" "gorm.io/gorm" ) +// LikeNotifyDelay 点赞通知冷却:未满该时长即取消点赞则不通知作者 +const LikeNotifyDelay = time.Minute + // NotificationService 通知服务 type NotificationService struct { db *gorm.DB @@ -16,6 +22,57 @@ func NewNotificationService(db *gorm.DB) *NotificationService { return &NotificationService{db: db} } +// FlushPendingLikeNotifications 将已冷却且仍有效的点赞落为通知(误赞撤回不会进入此处)。 +func (s *NotificationService) FlushPendingLikeNotifications(limit int) { + if limit < 1 { + limit = 50 + } + cutoff := time.Now().Add(-LikeNotifyDelay) + type pendingLike struct { + PostID uint + UserID uint + AuthorID uint + } + var rows []pendingLike + err := s.db.Raw(` + SELECT l.post_id AS post_id, l.user_id AS user_id, p.user_id AS author_id + FROM likes l + INNER JOIN posts p ON p.id = l.post_id + WHERE l.created_at <= ? + AND p.deleted_at IS NULL + AND p.status = ? + AND l.user_id <> p.user_id + AND NOT EXISTS ( + SELECT 1 FROM notifications n + WHERE n.type = ? + AND n.actor_id = l.user_id + AND n.post_id = l.post_id + AND n.user_id = p.user_id + AND n.created_at >= l.created_at + ) + ORDER BY l.created_at ASC + LIMIT ? + `, cutoff, model.ContentStatusPublished, model.NotificationTypeLike, limit).Scan(&rows).Error + if err != nil { + log.Printf("[notif] 扫描待发点赞通知失败: %v", err) + return + } + for _, row := range rows { + s.Create(row.AuthorID, row.UserID, model.NotificationTypeLike, row.PostID, 0, "") + } +} + +// StartLikeNotifyFlusher 周期落库冷却后的点赞通知 +func StartLikeNotifyFlusher(s *NotificationService) { + go func() { + t := time.NewTicker(20 * time.Second) + defer t.Stop() + for range t.C { + s.FlushPendingLikeNotifications(50) + } + }() +} + // Create 创建通知(actorID == userID 时跳过,不通知自己) func (s *NotificationService) Create(userID, actorID uint, notifType string, postID, commentID uint, content string) { if userID == 0 || actorID == 0 || userID == actorID {