package repository import ( "context" "strconv" "carrot_bbs/internal/model" "carrot_bbs/internal/pkg/cursor" "gorm.io/gorm" ) // SystemNotificationRepository 系统通知仓储接口 type SystemNotificationRepository interface { Create(notification *model.SystemNotification) error GetByID(id int64) (*model.SystemNotification, error) GetByReceiverID(receiverID string, page, pageSize int) ([]*model.SystemNotification, int64, error) GetUnreadByReceiverID(receiverID string, limit int) ([]*model.SystemNotification, error) GetUnreadCount(receiverID string) (int64, error) MarkAsRead(id int64, receiverID string) error MarkAllAsRead(receiverID string) error Delete(id int64, receiverID string) error GetByType(receiverID string, notifyType model.SystemNotificationType, page, pageSize int) ([]*model.SystemNotification, int64, error) GetByReceiverIDCursor(ctx context.Context, receiverID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.SystemNotification], error) GetByReceiverIDCursorLegacy(receiverID string, cursorStr string, pageSize int) ([]*model.SystemNotification, string, bool, error) } // systemNotificationRepository 系统通知仓储实现 type systemNotificationRepository struct { db *gorm.DB } // NewSystemNotificationRepository 创建系统通知仓储 func NewSystemNotificationRepository(db *gorm.DB) SystemNotificationRepository { return &systemNotificationRepository{db: db} } // Create 创建系统通知 func (r *systemNotificationRepository) Create(notification *model.SystemNotification) error { return r.db.Create(notification).Error } // GetByID 根据ID获取通知 func (r *systemNotificationRepository) GetByID(id int64) (*model.SystemNotification, error) { var notification model.SystemNotification err := r.db.First(¬ification, "id = ?", id).Error if err != nil { return nil, err } return ¬ification, nil } // GetByReceiverID 获取用户的通知列表 func (r *systemNotificationRepository) GetByReceiverID(receiverID string, page, pageSize int) ([]*model.SystemNotification, int64, error) { var notifications []*model.SystemNotification var total int64 query := r.db.Model(&model.SystemNotification{}).Where("receiver_id = ?", receiverID) query.Count(&total) offset := (page - 1) * pageSize err := query.Offset(offset). Limit(pageSize). Order("created_at DESC"). Find(¬ifications).Error return notifications, total, err } // GetUnreadByReceiverID 获取用户的未读通知列表 func (r *systemNotificationRepository) GetUnreadByReceiverID(receiverID string, limit int) ([]*model.SystemNotification, error) { var notifications []*model.SystemNotification err := r.db.Where("receiver_id = ? AND is_read = ?", receiverID, false). Order("created_at DESC"). Limit(limit). Find(¬ifications).Error return notifications, err } // GetUnreadCount 获取用户未读通知数量 func (r *systemNotificationRepository) GetUnreadCount(receiverID string) (int64, error) { var count int64 err := r.db.Model(&model.SystemNotification{}). Where("receiver_id = ? AND is_read = ?", receiverID, false). Count(&count).Error return count, err } // MarkAsRead 标记单条通知为已读 func (r *systemNotificationRepository) MarkAsRead(id int64, receiverID string) error { now := model.SystemNotification{}.UpdatedAt return r.db.Model(&model.SystemNotification{}). Where("id = ? AND receiver_id = ?", id, receiverID). Updates(map[string]any{ "is_read": true, "read_at": now, }).Error } // MarkAllAsRead 标记用户所有通知为已读 func (r *systemNotificationRepository) MarkAllAsRead(receiverID string) error { now := model.SystemNotification{}.UpdatedAt return r.db.Model(&model.SystemNotification{}). Where("receiver_id = ? AND is_read = ?", receiverID, false). Updates(map[string]any{ "is_read": true, "read_at": now, }).Error } // Delete 删除通知(软删除) func (r *systemNotificationRepository) Delete(id int64, receiverID string) error { return r.db.Where("id = ? AND receiver_id = ?", id, receiverID). Delete(&model.SystemNotification{}).Error } // GetByType 获取用户指定类型的通知 func (r *systemNotificationRepository) GetByType(receiverID string, notifyType model.SystemNotificationType, page, pageSize int) ([]*model.SystemNotification, int64, error) { var notifications []*model.SystemNotification var total int64 query := r.db.Model(&model.SystemNotification{}). Where("receiver_id = ? AND type = ?", receiverID, notifyType) query.Count(&total) offset := (page - 1) * pageSize err := query.Offset(offset). Limit(pageSize). Order("created_at DESC"). Find(¬ifications).Error return notifications, total, err } // GetByReceiverIDCursor 游标分页获取用户的通知列表 // 使用统一的 cursor 包进行游标分页 func (r *systemNotificationRepository) GetByReceiverIDCursor(ctx context.Context, receiverID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.SystemNotification], error) { // 构建基础查询 query := r.db.WithContext(ctx).Model(&model.SystemNotification{}).Where("receiver_id = ?", receiverID) // 使用 CursorBuilder 构建游标查询 builder := cursor.NewBuilder(query, cursor.SortByCreatedAtDesc). WithCursor(req.Cursor, cursor.Direction(req.Direction)). WithPageSize(req.PageSize) if builder.Error() != nil { return nil, builder.Error() } // 执行查询 var notifications []*model.SystemNotification if err := builder.Build().Find(¬ifications).Error; err != nil { return nil, err } // 构建响应 pageSize := builder.GetPageSize() hasMore := cursor.HasMore(len(notifications), pageSize) if hasMore { notifications = notifications[:pageSize] } // 生成下一页游标 var nextCursor string if hasMore && len(notifications) > 0 { lastNotification := notifications[len(notifications)-1] nextCursor = cursor.NewCursor( cursor.FormatTime(lastNotification.CreatedAt), strconv.FormatInt(lastNotification.ID, 10), cursor.SortByCreatedAtDesc, ).Encode() } return &cursor.CursorPageResult[*model.SystemNotification]{ Items: notifications, NextCursor: nextCursor, HasMore: hasMore, }, nil } // GetByReceiverIDCursorLegacy 游标分页获取用户的通知列表(旧版兼容接口) // 已废弃:请使用 GetByReceiverIDCursor func (r *systemNotificationRepository) GetByReceiverIDCursorLegacy(receiverID string, cursorStr string, pageSize int) ([]*model.SystemNotification, string, bool, error) { // 构建基础查询 query := r.db.Model(&model.SystemNotification{}).Where("receiver_id = ?", receiverID) // 如果有游标,解析游标并添加条件 if cursorStr != "" { decodedCursor, err := cursor.DecodeCursor(cursorStr) if err != nil { // 游标格式无效,返回空结果 return []*model.SystemNotification{}, "", false, nil } // 使用元组比较进行分页 condition := "(created_at, id) < (?, ?)" query = query.Where(condition, decodedCursor.SortValue, decodedCursor.ID) } // 多取一条用于判断是否还有下一页 var notifications []*model.SystemNotification err := query.Order("created_at DESC, id DESC"). Limit(pageSize + 1). Find(¬ifications).Error if err != nil { return nil, "", false, err } hasMore := len(notifications) > pageSize if hasMore { notifications = notifications[:pageSize] } // 生成下一页游标 var nextCursor string if hasMore && len(notifications) > 0 { lastNotification := notifications[len(notifications)-1] nextCursor = cursor.NewCursor( cursor.FormatTime(lastNotification.CreatedAt), strconv.FormatInt(lastNotification.ID, 10), cursor.SortByCreatedAtDesc, ).Encode() } return notifications, nextCursor, hasMore, nil }