package repository import ( "carrot_bbs/internal/model" "carrot_bbs/internal/pkg/cursor" "context" "gorm.io/gorm" ) // NotificationRepository 通知仓储接口 type NotificationRepository interface { Create(notification *model.Notification) error GetByID(id string) (*model.Notification, error) GetByUserID(userID string, page, pageSize int, unreadOnly bool) ([]*model.Notification, int64, error) MarkAsRead(id string) error MarkAllAsRead(userID string) error Delete(id string) error GetUnreadCount(userID string) (int64, error) DeleteAllByUserID(userID string) error GetNotificationsByCursor(ctx context.Context, userID string, unreadOnly bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Notification], error) } // notificationRepository 通知仓储实现 type notificationRepository struct { db *gorm.DB } // NewNotificationRepository 创建通知仓储 func NewNotificationRepository(db *gorm.DB) NotificationRepository { return ¬ificationRepository{db: db} } // Create 创建通知 func (r *notificationRepository) Create(notification *model.Notification) error { return r.db.Create(notification).Error } // GetByID 根据ID获取通知 func (r *notificationRepository) GetByID(id string) (*model.Notification, error) { var notification model.Notification err := r.db.First(¬ification, "id = ?", id).Error if err != nil { return nil, err } return ¬ification, nil } // GetByUserID 获取用户通知 func (r *notificationRepository) GetByUserID(userID string, page, pageSize int, unreadOnly bool) ([]*model.Notification, int64, error) { var notifications []*model.Notification var total int64 query := r.db.Model(&model.Notification{}).Where("user_id = ?", userID) if unreadOnly { query = query.Where("is_read = ?", false) } query.Count(&total) offset := (page - 1) * pageSize err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(¬ifications).Error return notifications, total, err } // MarkAsRead 标记为已读 func (r *notificationRepository) MarkAsRead(id string) error { return r.db.Model(&model.Notification{}).Where("id = ?", id).Update("is_read", true).Error } // MarkAllAsRead 标记所有为已读 func (r *notificationRepository) MarkAllAsRead(userID string) error { return r.db.Model(&model.Notification{}).Where("user_id = ?", userID).Update("is_read", true).Error } // Delete 删除通知 func (r *notificationRepository) Delete(id string) error { return r.db.Delete(&model.Notification{}, "id = ?", id).Error } // GetUnreadCount 获取未读数量 func (r *notificationRepository) GetUnreadCount(userID string) (int64, error) { var count int64 err := r.db.Model(&model.Notification{}).Where("user_id = ? AND is_read = ?", userID, false).Count(&count).Error return count, err } // DeleteAllByUserID 删除用户所有通知 func (r *notificationRepository) DeleteAllByUserID(userID string) error { return r.db.Where("user_id = ?", userID).Delete(&model.Notification{}).Error } // ========== Cursor Pagination Methods ========== // GetNotificationsByCursor 游标分页获取用户通知 // 排序方式:created_at DESC func (r *notificationRepository) GetNotificationsByCursor(ctx context.Context, userID string, unreadOnly bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Notification], error) { // 构建基础查询 query := r.db.WithContext(ctx).Model(&model.Notification{}).Where("user_id = ?", userID) if unreadOnly { query = query.Where("is_read = ?", false) } // 使用游标构建器 builder := cursor.NewBuilder(query, cursor.SortByCreatedAtDesc). WithCursor(req.Cursor, req.Direction). WithPageSize(req.PageSize) if builder.Error() != nil { // 无效游标,返回空列表 return cursor.NewCursorPageResult([]*model.Notification{}, "", "", false), nil } // 执行查询 var notifications []*model.Notification query = builder.Build() if err := query.Find(¬ifications).Error; err != nil { return nil, err } // 构建响应 pageSize := builder.GetPageSize() hasMore := cursor.HasMore(len(notifications), pageSize) if hasMore { notifications = notifications[:pageSize] } // 生成游标 var nextCursor, prevCursor string if len(notifications) > 0 { // 下一页游标 if hasMore { lastNotification := notifications[len(notifications)-1] nextCursor = cursor.NewCursor( cursor.FormatTime(lastNotification.CreatedAt), lastNotification.ID, cursor.SortByCreatedAtDesc, ).Encode() } // 上一页游标 firstNotification := notifications[0] prevCursor = cursor.NewCursor( cursor.FormatTime(firstNotification.CreatedAt), firstNotification.ID, cursor.SortByCreatedAtDesc, ).Encode() } return cursor.NewCursorPageResult(notifications, nextCursor, prevCursor, hasMore), nil }