From 36748d534e559845fb2993e998dd38c556086e7d Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Fri, 1 May 2026 09:06:13 +0300 Subject: [PATCH] refactor: Replace offset pagination with cursor-based pagination for subscriber listing - Replace Page and PageSize with Cursor and Limit in ListSubscribersRequest - Replace Total, Page, and PageSize with NextCursor and HasMore in ListSubscribersResponse - Add default limit of 20 with max cap of 100 in ListSubscribers - Replace ListSubscribers call with ListSubscribersCursor in subscriber_crud.go - Remove unused database import from subscriber_crud.go - Replace context.Background() with context.Without --- .../internal/services/subscriber.go | 11 ++++---- .../internal/services/subscriber_crud.go | 26 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/apps/api-server/internal/services/subscriber.go b/apps/api-server/internal/services/subscriber.go index 34e6934..20ecb97 100644 --- a/apps/api-server/internal/services/subscriber.go +++ b/apps/api-server/internal/services/subscriber.go @@ -53,17 +53,16 @@ type UpdateSubscriberRequest struct { // ListSubscribersRequest is the query-filter payload for listing subscribers. type ListSubscribersRequest struct { - Page int `json:"page" query:"page"` - PageSize int `json:"page_size" query:"page_size"` + Cursor string `json:"cursor" query:"cursor"` + Limit int `json:"limit" query:"limit"` Status models.SubscriberStatus `json:"status" query:"status"` OrganizationID string `json:"organization_id" query:"organization_id"` Search string `json:"search" query:"search"` } -// ListSubscribersResponse is the paginated subscriber list result. +// ListSubscribersResponse is the cursor-paginated subscriber list result. type ListSubscribersResponse struct { Subscribers []models.Subscriber `json:"subscribers"` - Total int64 `json:"total"` - Page int `json:"page"` - PageSize int `json:"page_size"` + NextCursor string `json:"next_cursor,omitempty"` + HasMore bool `json:"has_more"` } diff --git a/apps/api-server/internal/services/subscriber_crud.go b/apps/api-server/internal/services/subscriber_crud.go index 19ca5ef..1cac167 100644 --- a/apps/api-server/internal/services/subscriber_crud.go +++ b/apps/api-server/internal/services/subscriber_crud.go @@ -4,7 +4,6 @@ import ( "context" "fmt" - "github.com/nutcas3/telecom-platform/apps/api-server/internal/database" "github.com/nutcas3/telecom-platform/apps/api-server/internal/models" "github.com/sirupsen/logrus" ) @@ -44,7 +43,7 @@ func (s *SubscriberService) CreateSubscriber(ctx context.Context, req *CreateSub subscriber.EUICCID = req.EUICCID go func() { // Use derived context for goroutine to respect parent cancellation - bgCtx := context.Background() + bgCtx := context.WithoutCancel(ctx) if err := s.provisionESIMProfile(bgCtx, subscriber.ID); err != nil { logrus.WithError(err).WithField("subscriber_id", subscriber.ID).Warn("Failed to provision eSIM profile") } @@ -106,24 +105,25 @@ func (s *SubscriberService) UpdateSubscriber(ctx context.Context, id uint, req * return subscriber, nil } -// ListSubscribers lists subscribers with pagination and filtering. +// ListSubscribers lists subscribers with cursor-based pagination and filtering. func (s *SubscriberService) ListSubscribers(ctx context.Context, req *ListSubscribersRequest) (*ListSubscribersResponse, error) { - dbReq := &database.ListSubscribersRequest{ - Page: req.Page, - PageSize: req.PageSize, - Status: string(req.Status), - OrganizationID: req.OrganizationID, - Search: req.Search, + // Set default limit if not provided + limit := req.Limit + if limit <= 0 { + limit = 20 } - subscribers, total, err := s.db.ListSubscribers(ctx, dbReq) + if limit > 100 { + limit = 100 + } + + subscribers, nextCursor, hasMore, err := s.db.ListSubscribersCursor(ctx, req.Cursor, limit, string(req.Status), req.OrganizationID, req.Search) if err != nil { return nil, fmt.Errorf("failed to list subscribers: %w", err) } return &ListSubscribersResponse{ Subscribers: subscribers, - Total: total, - Page: req.Page, - PageSize: req.PageSize, + NextCursor: nextCursor, + HasMore: hasMore, }, nil }