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

@@ -1179,3 +1179,92 @@ type PendingContentAuthor struct {
type OnlineUsersResponse struct {
Count int64 `json:"count"` // 在线用户数
}
// ==================== Cursor Pagination DTOs ====================
// CursorPageRequest 游标分页请求
type CursorPageRequest struct {
Cursor string `json:"cursor" form:"cursor"` // 游标(首次请求为空)
Direction string `json:"direction" form:"direction"` // forward | backward
PageSize int `json:"page_size" form:"page_size"` // 每页数量
}
// CursorPageResponse 游标分页响应(泛型版本)
// 注意:由于 Go 的限制,这里使用 interface{} 作为 Items 类型
// 实际使用时可以创建特定类型的响应结构体
type CursorPageResponse struct {
Items interface{} `json:"items"`
NextCursor string `json:"next_cursor,omitempty"` // 下一页游标
PrevCursor string `json:"prev_cursor,omitempty"` // 上一页游标(可选)
HasMore bool `json:"has_more"` // 是否有更多数据
}
// CursorPageMeta 分页元信息
type CursorPageMeta struct {
PageSize int `json:"page_size"`
Direction string `json:"direction"`
}
// PostCursorPageResponse 帖子游标分页响应
type PostCursorPageResponse struct {
Items []*PostResponse `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
// CommentCursorPageResponse 评论游标分页响应
type CommentCursorPageResponse struct {
Items []*CommentResponse `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
// MessageCursorPageResponse 消息游标分页响应
type MessageCursorPageResponse struct {
Items []*MessageResponse `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
// ConversationCursorPageResponse 会话游标分页响应
type ConversationCursorPageResponse struct {
Items []*ConversationResponse `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
// GroupCursorPageResponse 群组游标分页响应
type GroupCursorPageResponse struct {
Items []*GroupResponse `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
// NotificationCursorPageResponse 通知游标分页响应
type NotificationCursorPageResponse struct {
Items []*NotificationResponse `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
// GroupMemberCursorPageResponse 群成员游标分页响应
type GroupMemberCursorPageResponse struct {
Items []*GroupMemberResponse `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
// GroupAnnouncementCursorPageResponse 群公告游标分页响应
type GroupAnnouncementCursorPageResponse struct {
Items []*GroupAnnouncementResponse `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}

View File

@@ -94,3 +94,30 @@ func GroupAnnouncementsToResponse(announcements []model.GroupAnnouncement) []*Gr
}
return result
}
// GroupsToResponseFromPtr 将Group指针切片转换为GroupResponse列表
func GroupsToResponseFromPtr(groups []*model.Group) []*GroupResponse {
result := make([]*GroupResponse, 0, len(groups))
for _, group := range groups {
result = append(result, GroupToResponse(group))
}
return result
}
// GroupMembersToResponseFromPtr 将GroupMember指针切片转换为GroupMemberResponse列表
func GroupMembersToResponseFromPtr(members []*model.GroupMember) []*GroupMemberResponse {
result := make([]*GroupMemberResponse, 0, len(members))
for _, member := range members {
result = append(result, GroupMemberToResponse(member))
}
return result
}
// GroupAnnouncementsToResponseFromPtr 将GroupAnnouncement指针切片转换为GroupAnnouncementResponse列表
func GroupAnnouncementsToResponseFromPtr(announcements []*model.GroupAnnouncement) []*GroupAnnouncementResponse {
result := make([]*GroupAnnouncementResponse, 0, len(announcements))
for _, announcement := range announcements {
result = append(result, GroupAnnouncementToResponse(announcement))
}
return result
}