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
|
|
|
|
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 {
|
2026-03-30 04:49:35 +08:00
|
|
|
|
Items any `json:"items"`
|
|
|
|
|
|
NextCursor string `json:"next_cursor"`
|
|
|
|
|
|
PrevCursor string `json:"prev_cursor,omitempty"`
|
|
|
|
|
|
HasMore bool `json:"has_more"`
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|