feat(notif): 点赞通知延迟落库,短时取消不打扰作者
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user