Files
backend/internal/pkg/cursor/pagination.go
lafay 92babe509f
All checks were successful
Build Backend / build (push) Successful in 4m42s
Build Backend / build-docker (push) Successful in 3m53s
feat(api): add cursor-based pagination for posts, comments, messages, groups, and notifications
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
2026-03-20 23:03:23 +08:00

139 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cursor
import (
"errors"
)
const (
// DefaultPageSize 默认页面大小
DefaultPageSize = 20
// MaxPageSize 最大页面大小
MaxPageSize = 100
)
// Direction 分页方向
type Direction string
const (
// Forward 向前分页(下一页)
Forward Direction = "forward"
// Backward 向后分页(上一页)
Backward Direction = "backward"
)
// IsValid 检查分页方向是否有效
func (d Direction) IsValid() bool {
return d == Forward || d == Backward
}
// PageRequest 游标分页请求参数
type PageRequest struct {
Cursor string // 游标字符串
Direction Direction // 分页方向
PageSize int // 每页数量
}
// NewPageRequest 创建新的游标请求
func NewPageRequest(cursor string, direction Direction, pageSize int) *PageRequest {
req := &PageRequest{
Cursor: cursor,
Direction: direction,
PageSize: pageSize,
}
req.Normalize()
return req
}
// Normalize 规范化分页参数
func (r *PageRequest) Normalize() {
if r.Direction == "" {
r.Direction = Forward
}
if r.PageSize <= 0 {
r.PageSize = DefaultPageSize
}
if r.PageSize > MaxPageSize {
r.PageSize = MaxPageSize
}
}
// GetPageSize 获取有效的页面大小默认20最大100
func (r *PageRequest) GetPageSize() int {
if r.PageSize <= 0 {
return DefaultPageSize
}
if r.PageSize > MaxPageSize {
return MaxPageSize
}
return r.PageSize
}
// Validate 验证分页请求
func (r *PageRequest) Validate() error {
if r.Direction != "" && !r.Direction.IsValid() {
return errors.New("invalid direction, must be 'forward' or 'backward'")
}
return nil
}
// GetCursor 解码游标
func (r *PageRequest) GetCursor() (*Cursor, error) {
return DecodeCursor(r.Cursor)
}
// IsForward 是否向前分页
func (r *PageRequest) IsForward() bool {
return r.Direction == Forward || r.Direction == ""
}
// IsBackward 是否向后分页
func (r *PageRequest) IsBackward() bool {
return r.Direction == Backward
}
// PageResponse 游标分页响应结构
type PageResponse struct {
Items interface{} `json:"items"`
NextCursor string `json:"next_cursor"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
// NewPageResponse 创建分页响应
func NewPageResponse(items interface{}, nextCursor, prevCursor string, hasMore bool) *PageResponse {
return &PageResponse{
Items: items,
NextCursor: nextCursor,
PrevCursor: prevCursor,
HasMore: hasMore,
}
}
// CursorPageResult 游标分页结果(泛型版本,供内部使用)
type CursorPageResult[T any] struct {
Items []T
NextCursor string
PrevCursor string
HasMore bool
}
// NewCursorPageResult 创建游标分页结果
func NewCursorPageResult[T any](items []T, nextCursor, prevCursor string, hasMore bool) *CursorPageResult[T] {
return &CursorPageResult[T]{
Items: items,
NextCursor: nextCursor,
PrevCursor: prevCursor,
HasMore: hasMore,
}
}
// ToPageResponse 转换为通用分页响应
func (r *CursorPageResult[T]) ToPageResponse() *PageResponse {
return &PageResponse{
Items: r.Items,
NextCursor: r.NextCursor,
PrevCursor: r.PrevCursor,
HasMore: r.HasMore,
}
}