feat(api): add cursor-based pagination for posts, comments, messages, groups, and notifications
All checks were successful
Build Backend / build (push) Successful in 4m42s
Build Backend / build-docker (push) Successful in 3m53s

Implement cursor-based pagination across multiple API endpoints to improve performance and enable efficient infinite scrolling. This replaces traditional offset-based pagination for high-volume data retrieval.

Changes:
- Add cursor pagination DTOs for all entity types (Post, Comment, Message, Conversation, Group, Notification, GroupMember, GroupAnnouncement)
- Implement cursor pagination methods in repositories with support for various sort orders (created_at, updated_at, join_time, seq)
- Add cursor pagination handlers that maintain backward compatibility with existing offset pagination
- Add new API routes for cursor-based endpoints (/cursor suffix)
- Add helper converter functions for pointer slice types in DTO conversions
This commit is contained in:
lafay
2026-03-20 23:03:23 +08:00
parent 98f0c9f2b6
commit 92babe509f
21 changed files with 2087 additions and 3 deletions

View File

@@ -10,6 +10,7 @@ import (
"carrot_bbs/internal/cache"
"carrot_bbs/internal/model"
"carrot_bbs/internal/pkg/cursor"
"carrot_bbs/internal/pkg/gorse"
"carrot_bbs/internal/pkg/hook"
"carrot_bbs/internal/repository"
@@ -40,6 +41,11 @@ type PostService interface {
GetFavorites(ctx context.Context, userID string, page, pageSize int) ([]*model.Post, int64, error)
Search(ctx context.Context, keyword string, page, pageSize int) ([]*model.Post, int64, error)
// 游标分页方法
ListByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
SearchByCursor(ctx context.Context, keyword string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
GetUserPostsByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
// 关注和推荐
GetFollowingPosts(ctx context.Context, userID string, page, pageSize int) ([]*model.Post, int64, error)
GetHotPosts(ctx context.Context, page, pageSize int) ([]*model.Post, int64, error)
@@ -760,3 +766,29 @@ func (s *postServiceImpl) DeletePostWithTransaction(ctx context.Context, postID
return s.postRepo.DeleteWithContext(ctx, postID)
})
}
// ========== 游标分页方法 ==========
// ListByCursor 游标分页获取帖子列表
func (s *postServiceImpl) ListByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
// 规范化请求参数
req.Normalize()
return s.postRepo.GetPostsByCursor(ctx, userID, includePending, req)
}
// SearchByCursor 游标分页搜索帖子
func (s *postServiceImpl) SearchByCursor(ctx context.Context, keyword string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
// 规范化请求参数
req.Normalize()
return s.postRepo.SearchPostsByCursor(ctx, keyword, req)
}
// GetUserPostsByCursor 游标分页获取用户帖子
func (s *postServiceImpl) GetUserPostsByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
// 规范化请求参数
req.Normalize()
return s.postRepo.GetUserPostsByCursor(ctx, userID, includePending, req)
}