Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
// Package pagination provides types and helpers for paginated responses.
|
||||
package pagination
|
||||
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
SortOrderAsc = "asc"
|
||||
SortOrderDesc = "desc"
|
||||
)
|
||||
|
||||
// PaginationParams 分页参数
|
||||
type PaginationParams struct {
|
||||
Page int
|
||||
PageSize int
|
||||
SortBy string
|
||||
SortOrder string
|
||||
}
|
||||
|
||||
// PaginationResult 分页结果
|
||||
type PaginationResult struct {
|
||||
Total int64
|
||||
Page int
|
||||
PageSize int
|
||||
Pages int
|
||||
}
|
||||
|
||||
// DefaultPagination 默认分页参数
|
||||
func DefaultPagination() PaginationParams {
|
||||
return PaginationParams{
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
SortOrder: SortOrderDesc,
|
||||
}
|
||||
}
|
||||
|
||||
// Offset 计算偏移量
|
||||
func (p PaginationParams) Offset() int {
|
||||
if p.Page < 1 {
|
||||
p.Page = 1
|
||||
}
|
||||
return (p.Page - 1) * p.Limit()
|
||||
}
|
||||
|
||||
// Limit 获取限制数
|
||||
func (p PaginationParams) Limit() int {
|
||||
if p.PageSize < 1 {
|
||||
return 20
|
||||
}
|
||||
if p.PageSize > 1000 {
|
||||
return 1000
|
||||
}
|
||||
return p.PageSize
|
||||
}
|
||||
|
||||
// NormalizeSortOrder normalizes sort order to asc/desc and falls back to defaultOrder.
|
||||
func NormalizeSortOrder(order string, defaultOrder string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(defaultOrder)) {
|
||||
case SortOrderAsc:
|
||||
defaultOrder = SortOrderAsc
|
||||
default:
|
||||
defaultOrder = SortOrderDesc
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(order)) {
|
||||
case SortOrderAsc:
|
||||
return SortOrderAsc
|
||||
case SortOrderDesc:
|
||||
return SortOrderDesc
|
||||
default:
|
||||
return defaultOrder
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizedSortOrder returns the normalized sort order using defaultOrder as fallback.
|
||||
func (p PaginationParams) NormalizedSortOrder(defaultOrder string) string {
|
||||
return NormalizeSortOrder(p.SortOrder, defaultOrder)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package pagination
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNormalizeSortOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
defaultOrder string
|
||||
want string
|
||||
}{
|
||||
{name: "asc", input: "asc", defaultOrder: "desc", want: "asc"},
|
||||
{name: "uppercase asc", input: "ASC", defaultOrder: "desc", want: "asc"},
|
||||
{name: "desc", input: "desc", defaultOrder: "asc", want: "desc"},
|
||||
{name: "trim spaces", input: " desc ", defaultOrder: "asc", want: "desc"},
|
||||
{name: "invalid falls back", input: "sideways", defaultOrder: "asc", want: "asc"},
|
||||
{name: "empty falls back", input: "", defaultOrder: "desc", want: "desc"},
|
||||
{name: "invalid default falls back to desc", input: "", defaultOrder: "wat", want: "desc"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if got := NormalizeSortOrder(tt.input, tt.defaultOrder); got != tt.want {
|
||||
t.Fatalf("NormalizeSortOrder(%q, %q) = %q, want %q", tt.input, tt.defaultOrder, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaginationParamsNormalizedSortOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
params := PaginationParams{SortOrder: "ASC"}
|
||||
if got := params.NormalizedSortOrder("desc"); got != "asc" {
|
||||
t.Fatalf("NormalizedSortOrder = %q, want asc", got)
|
||||
}
|
||||
|
||||
params = PaginationParams{SortOrder: "bad"}
|
||||
if got := params.NormalizedSortOrder("asc"); got != "asc" {
|
||||
t.Fatalf("NormalizedSortOrder invalid fallback = %q, want asc", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaginationParamsLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pageSize int
|
||||
want int
|
||||
}{
|
||||
{name: "non-positive falls back to default", pageSize: 0, want: 20},
|
||||
{name: "negative falls back to default", pageSize: -1, want: 20},
|
||||
{name: "normal value keeps", pageSize: 50, want: 50},
|
||||
{name: "max value keeps", pageSize: 1000, want: 1000},
|
||||
{name: "beyond max clamps to 1000", pageSize: 1500, want: 1000},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := PaginationParams{PageSize: tt.pageSize}
|
||||
if got := p.Limit(); got != tt.want {
|
||||
t.Fatalf("Limit() for PageSize=%d = %d, want %d", tt.pageSize, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaginationParamsOffsetUsesNormalizedLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
page int
|
||||
pageSize int
|
||||
want int
|
||||
}{
|
||||
{name: "invalid page uses first page", page: 0, pageSize: 50, want: 0},
|
||||
{name: "zero page size uses default", page: 2, pageSize: 0, want: 20},
|
||||
{name: "negative page size uses default", page: 2, pageSize: -1, want: 20},
|
||||
{name: "normal values", page: 3, pageSize: 50, want: 100},
|
||||
{name: "page size beyond max is clamped", page: 2, pageSize: 1500, want: 1000},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
params := PaginationParams{Page: tt.page, PageSize: tt.pageSize}
|
||||
if got := params.Offset(); got != tt.want {
|
||||
t.Fatalf("Offset() for Page=%d, PageSize=%d = %d, want %d", tt.page, tt.pageSize, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user