153 lines
3.5 KiB
Go
153 lines
3.5 KiB
Go
|
|
package cursor
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"encoding/base64"
|
|||
|
|
"encoding/json"
|
|||
|
|
"errors"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// SortOrder 游标排序类型
|
|||
|
|
type SortOrder int
|
|||
|
|
|
|||
|
|
const (
|
|||
|
|
// SortByCreatedAtDesc 按创建时间降序
|
|||
|
|
SortByCreatedAtDesc SortOrder = iota
|
|||
|
|
// SortByCreatedAtAsc 按创建时间升序
|
|||
|
|
SortByCreatedAtAsc
|
|||
|
|
// SortByIDDesc 按ID降序
|
|||
|
|
SortByIDDesc
|
|||
|
|
// SortByIDAsc 按ID升序
|
|||
|
|
SortByIDAsc
|
|||
|
|
// SortBySeqDesc 按序列号降序(用于消息)
|
|||
|
|
SortBySeqDesc
|
|||
|
|
// SortBySeqAsc 按序列号升序
|
|||
|
|
SortBySeqAsc
|
|||
|
|
// SortByUpdatedAtDesc 按更新时间降序
|
|||
|
|
SortByUpdatedAtDesc
|
|||
|
|
// SortByUpdatedAtAsc 按更新时间升序
|
|||
|
|
SortByUpdatedAtAsc
|
|||
|
|
// SortByJoinTimeDesc 按加入时间降序(用于群成员)
|
|||
|
|
SortByJoinTimeDesc
|
|||
|
|
// SortByJoinTimeAsc 按加入时间升序(用于群成员)
|
|||
|
|
SortByJoinTimeAsc
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// String 返回排序类型的字符串表示
|
|||
|
|
func (s SortOrder) String() string {
|
|||
|
|
switch s {
|
|||
|
|
case SortByCreatedAtDesc:
|
|||
|
|
return "created_at DESC"
|
|||
|
|
case SortByCreatedAtAsc:
|
|||
|
|
return "created_at ASC"
|
|||
|
|
case SortByIDDesc:
|
|||
|
|
return "id DESC"
|
|||
|
|
case SortByIDAsc:
|
|||
|
|
return "id ASC"
|
|||
|
|
case SortBySeqDesc:
|
|||
|
|
return "seq DESC"
|
|||
|
|
case SortBySeqAsc:
|
|||
|
|
return "seq ASC"
|
|||
|
|
case SortByUpdatedAtDesc:
|
|||
|
|
return "updated_at DESC"
|
|||
|
|
case SortByUpdatedAtAsc:
|
|||
|
|
return "updated_at ASC"
|
|||
|
|
case SortByJoinTimeDesc:
|
|||
|
|
return "join_time DESC"
|
|||
|
|
case SortByJoinTimeAsc:
|
|||
|
|
return "join_time ASC"
|
|||
|
|
default:
|
|||
|
|
return "created_at DESC"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsDescending 是否为降序
|
|||
|
|
func (s SortOrder) IsDescending() bool {
|
|||
|
|
return s == SortByCreatedAtDesc || s == SortByIDDesc || s == SortBySeqDesc || s == SortByUpdatedAtDesc || s == SortByJoinTimeDesc
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SortField 返回排序字段名
|
|||
|
|
func (s SortOrder) SortField() string {
|
|||
|
|
switch s {
|
|||
|
|
case SortByCreatedAtDesc, SortByCreatedAtAsc:
|
|||
|
|
return "created_at"
|
|||
|
|
case SortByIDDesc, SortByIDAsc:
|
|||
|
|
return "id"
|
|||
|
|
case SortBySeqDesc, SortBySeqAsc:
|
|||
|
|
return "seq"
|
|||
|
|
case SortByUpdatedAtDesc, SortByUpdatedAtAsc:
|
|||
|
|
return "updated_at"
|
|||
|
|
case SortByJoinTimeDesc, SortByJoinTimeAsc:
|
|||
|
|
return "join_time"
|
|||
|
|
default:
|
|||
|
|
return "created_at"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// cursorJSON 游标 JSON 结构(用于序列化)
|
|||
|
|
type cursorJSON struct {
|
|||
|
|
SortValue string `json:"sv"` // 排序字段值(时间戳或ID)
|
|||
|
|
ID string `json:"id"` // 记录唯一ID
|
|||
|
|
Order SortOrder `json:"so"` // 排序方式
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Cursor 游标数据结构
|
|||
|
|
type Cursor struct {
|
|||
|
|
SortValue string // 排序字段值(时间戳或ID)
|
|||
|
|
ID string // 记录唯一ID
|
|||
|
|
Order SortOrder // 排序方式
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Encode 编码游标为 Base64URL 字符串
|
|||
|
|
func (c *Cursor) Encode() string {
|
|||
|
|
if c == nil {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
data, err := json.Marshal(&cursorJSON{
|
|||
|
|
SortValue: c.SortValue,
|
|||
|
|
ID: c.ID,
|
|||
|
|
Order: c.Order,
|
|||
|
|
})
|
|||
|
|
if err != nil {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return base64.URLEncoding.EncodeToString(data)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// DecodeCursor 解码 Base64URL 字符串为游标
|
|||
|
|
func DecodeCursor(encoded string) (*Cursor, error) {
|
|||
|
|
if encoded == "" {
|
|||
|
|
return nil, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
data, err := base64.URLEncoding.DecodeString(encoded)
|
|||
|
|
if err != nil {
|
|||
|
|
// 尝试使用 RawURLEncoding(不带填充的编码)
|
|||
|
|
data, err = base64.RawURLEncoding.DecodeString(encoded)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, errors.New("invalid cursor encoding: " + err.Error())
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var cj cursorJSON
|
|||
|
|
if err := json.Unmarshal(data, &cj); err != nil {
|
|||
|
|
return nil, errors.New("invalid cursor format: " + err.Error())
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return &Cursor{
|
|||
|
|
SortValue: cj.SortValue,
|
|||
|
|
ID: cj.ID,
|
|||
|
|
Order: cj.Order,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewCursor 创建新的游标
|
|||
|
|
func NewCursor(sortValue, id string, order SortOrder) *Cursor {
|
|||
|
|
return &Cursor{
|
|||
|
|
SortValue: sortValue,
|
|||
|
|
ID: id,
|
|||
|
|
Order: order,
|
|||
|
|
}
|
|||
|
|
}
|