Files

164 lines
3.1 KiB
Go
Raw Permalink Normal View History

package cursor
import (
"fmt"
"time"
"gorm.io/gorm"
)
// CursorBuilder 游标查询构建器
type CursorBuilder struct {
db *gorm.DB
cursor *Cursor
direction Direction
pageSize int
order SortOrder
err error
}
// NewBuilder 创建新的构建器
func NewBuilder(db *gorm.DB, order SortOrder) *CursorBuilder {
return &CursorBuilder{
db: db,
order: order,
pageSize: DefaultPageSize,
direction: Forward,
}
}
// WithCursor 设置游标
func (b *CursorBuilder) WithCursor(cursor string, direction Direction) *CursorBuilder {
if b.err != nil {
return b
}
if cursor == "" {
return b
}
decoded, err := DecodeCursor(cursor)
if err != nil {
b.err = err
return b
}
b.cursor = decoded
b.direction = direction
return b
}
// WithPageSize 设置页面大小
func (b *CursorBuilder) WithPageSize(size int) *CursorBuilder {
if b.err != nil {
return b
}
if size <= 0 {
b.pageSize = DefaultPageSize
} else if size > MaxPageSize {
b.pageSize = MaxPageSize
} else {
b.pageSize = size
}
return b
}
// Error 返回构建过程中的错误
func (b *CursorBuilder) Error() error {
return b.err
}
// Build 构建查询(返回 GORM 查询)
// 注意:调用者需要自己执行 Find 并处理结果
func (b *CursorBuilder) Build() *gorm.DB {
if b.err != nil {
return b.db.Where("1 = 0") // 返回空结果
}
query := b.db
// 应用游标条件
if b.cursor != nil {
query = b.applyCursorCondition(query)
}
// 应用排序
query = b.applyOrder(query)
// 多取一条用于判断是否有更多数据
query = query.Limit(b.pageSize + 1)
return query
}
// applyCursorCondition 应用游标条件
func (b *CursorBuilder) applyCursorCondition(query *gorm.DB) *gorm.DB {
if b.cursor == nil {
return query
}
sortField := b.order.SortField()
sortValue := b.cursor.SortValue
id := b.cursor.ID
// 根据分页方向和排序类型决定比较运算符
var op string
if b.direction == Forward {
// 向前分页:降序用 <,升序用 >
if b.order.IsDescending() {
op = "<"
} else {
op = ">"
}
} else {
// 向后分页:降序用 >,升序用 <
if b.order.IsDescending() {
op = ">"
} else {
op = "<"
}
}
// 使用元组比较:(sort_field, id) op (sort_value, id)
// 这样可以确保排序稳定且唯一
condition := fmt.Sprintf("(%s, id) %s (?, ?)", sortField, op)
return query.Where(condition, sortValue, id)
}
// applyOrder 应用排序
func (b *CursorBuilder) applyOrder(query *gorm.DB) *gorm.DB {
// 主排序字段
orderClause := b.order.String()
// 添加 ID 作为第二排序字段,确保排序稳定
if b.order.IsDescending() {
orderClause += ", id DESC"
} else {
orderClause += ", id ASC"
}
return query.Order(orderClause)
}
// GetPageSize 获取页面大小
func (b *CursorBuilder) GetPageSize() int {
return b.pageSize
}
// HasMore 判断是否有更多数据
// items 是查询结果切片,需要传入指针切片
func HasMore(itemsLen int, pageSize int) bool {
return itemsLen > pageSize
}
// FormatTime 格式化时间为字符串(用于游标)
func FormatTime(t time.Time) string {
return t.Format(time.RFC3339Nano)
}