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

@@ -2,6 +2,7 @@ package service
import (
"cmp"
"context"
"errors"
"fmt"
"maps"
@@ -12,6 +13,7 @@ import (
"carrot_bbs/internal/cache"
apperrors "carrot_bbs/internal/errors"
"carrot_bbs/internal/model"
"carrot_bbs/internal/pkg/cursor"
"carrot_bbs/internal/pkg/sse"
"carrot_bbs/internal/pkg/utils"
"carrot_bbs/internal/repository"
@@ -88,6 +90,12 @@ type GroupService interface {
// 获取成员信息
GetMember(groupID string, userID string) (*model.GroupMember, error)
// 游标分页方法
GetGroupsByCursor(ctx context.Context, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Group], error)
GetUserGroupsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Group], error)
GetMembersByCursor(ctx context.Context, groupID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.GroupMember], error)
GetAnnouncementsByCursor(ctx context.Context, groupID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.GroupAnnouncement], error)
}
// GroupMembersResult 群组成员缓存结果
@@ -1614,3 +1622,25 @@ func (s *groupService) IsGroupOwner(userID string, groupID string) bool {
func (s *groupService) GetMember(groupID string, userID string) (*model.GroupMember, error) {
return s.groupRepo.GetMember(groupID, userID)
}
// ==================== 游标分页方法 ====================
// GetGroupsByCursor 游标分页获取群组列表
func (s *groupService) GetGroupsByCursor(ctx context.Context, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Group], error) {
return s.groupRepo.GetGroupsByCursor(ctx, req)
}
// GetUserGroupsByCursor 游标分页获取用户加入的群组列表
func (s *groupService) GetUserGroupsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Group], error) {
return s.groupRepo.GetUserGroupsByCursor(ctx, userID, req)
}
// GetMembersByCursor 游标分页获取群成员列表
func (s *groupService) GetMembersByCursor(ctx context.Context, groupID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.GroupMember], error) {
return s.groupRepo.GetMembersByCursor(ctx, groupID, req)
}
// GetAnnouncementsByCursor 游标分页获取群公告列表
func (s *groupService) GetAnnouncementsByCursor(ctx context.Context, groupID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.GroupAnnouncement], error) {
return s.groupRepo.GetAnnouncementsByCursor(ctx, groupID, req)
}