feat(notif): 点赞通知延迟落库,短时取消不打扰作者

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-16 08:33:52 +08:00
parent 44fdcace4c
commit 6b772380c9
2 changed files with 58 additions and 7 deletions

View File

@@ -5,7 +5,6 @@ import (
"strconv" "strconv"
"github.com/freefire/jiang13-bbs/middleware" "github.com/freefire/jiang13-bbs/middleware"
"github.com/freefire/jiang13-bbs/model"
"github.com/gin-gonic/gin" "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()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return 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}) c.JSON(http.StatusOK, gin.H{"liked": liked, "like_count": count})
} }

View File

@@ -1,10 +1,16 @@
package service package service
import ( import (
"log"
"time"
"github.com/freefire/jiang13-bbs/model" "github.com/freefire/jiang13-bbs/model"
"gorm.io/gorm" "gorm.io/gorm"
) )
// LikeNotifyDelay 点赞通知冷却:未满该时长即取消点赞则不通知作者
const LikeNotifyDelay = time.Minute
// NotificationService 通知服务 // NotificationService 通知服务
type NotificationService struct { type NotificationService struct {
db *gorm.DB db *gorm.DB
@@ -16,6 +22,57 @@ func NewNotificationService(db *gorm.DB) *NotificationService {
return &NotificationService{db: db} 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 时跳过,不通知自己) // Create 创建通知(actorID == userID 时跳过,不通知自己)
func (s *NotificationService) Create(userID, actorID uint, notifType string, postID, commentID uint, content string) { func (s *NotificationService) Create(userID, actorID uint, notifType string, postID, commentID uint, content string) {
if userID == 0 || actorID == 0 || userID == actorID { if userID == 0 || actorID == 0 || userID == actorID {