EDIT | Add & Use General Pagination Func

This commit is contained in:
danialasadi 2026-05-08 04:28:50 +03:30
parent bc24bcc686
commit 44bfde3c65
5 changed files with 158 additions and 78 deletions

View File

@ -5,66 +5,103 @@ import (
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity" "git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
"git.gocasts.ir/ebhomengo/niki/pkg/database/postgres" "git.gocasts.ir/ebhomengo/niki/pkg/database/postgres"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
) )
func (db *DB) GetTransactionListByUserID(ctx context.Context, userID uint64, dbPagination postgres.DBPagination) ([]entity.Transaction, int64, error) { func (db *DB) GetTransactionListByUserID(ctx context.Context, userID uint64, dbPagination postgres.DBPagination) ([]entity.Transaction, int64, error) {
const op = richerror.Op("Wallet.repo.GetTransactionListByUserID") const op = richerror.Op("Wallet.repo.GetTransactionListByUserID")
query := `SELECT * FROM Transactions WHERE user_id = $1 AND transaction_timestamp < $2 ORDER BY transaction_id DESC LIMIT $3` countQuery := "SELECT COUNT(*) FROM transactions WHERE user_id = $1"
fetchQuery := `SELECT * FROM Transactions WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3`
stmt, StErr := db.conn.PrepareStatement(ctx, postgres.StatementKeyAWalletGetTransactionHistory, query) countQueryStmt := postgres.StatementKeyAWalletGetTotalCountTransactionHistory
fetchQueryStmt := postgres.StatementKeyAWalletGetTransactionHistory
if StErr != nil { PageOffset := (dbPagination.PageNumber - 1) * dbPagination.PageSize // offset
return nil, 0, richerror.New(op).WithErr(StErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery) PageSize := dbPagination.PageSize //limit
} countParams := []any{userID} // $1
fetchParams := []any{userID, PageSize, PageOffset} // $1 , $2 , $3
totalRetrieveRecord := (dbPagination.MaxNextPages-1)*dbPagination.PageSize + 1 //
//
lastTimeStamp := dbPagination.LastTimeStamp /////////////////////////// with generic pagination package
queryRows, err := stmt.QueryContext(ctx, userID, lastTimeStamp, totalRetrieveRecord)
transactionsList, totalItemsCount, err := postgres.PageNumberPagination[entity.Transaction](ctx, countQuery, fetchQuery,
db.conn, countQueryStmt, fetchQueryStmt, op,
scanTransaction, countParams, fetchParams,
)
if err != nil { if err != nil {
return nil, 0, richerror.New(op).WithErr(StErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery) return nil, 0, err
} }
defer queryRows.Close() return transactionsList, totalItemsCount, nil
var transactions []entity.Transaction /////////////////////////// normal
var lenCounter int64 //var totalCount int64
//
for queryRows.Next() { //countStmt, CountStErr := db.conn.PrepareStatement(ctx, countQueryStmt, countQuery)
lenCounter++ //
//if CountStErr != nil {
if lenCounter < dbPagination.PageSize+1 { //
transaction, err := scanTransaction(queryRows) // return nil, 0, richerror.New(op).WithErr(CountStErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
//
if err != nil { //}
return nil, 0, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantScanQueryResult) //countErr := countStmt.QueryRowContext(ctx, userID).Scan(&totalCount)
} //
//if countErr != nil {
transactions = append(transactions, transaction) // return nil, 0, richerror.New(op).WithErr(CountStErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
//
} //}
//
} ////// get records
//
if qErr := queryRows.Err(); qErr != nil { //stmt, StErr := db.conn.PrepareStatement(ctx, fetchQueryStmt, fetchQuery)
//
return nil, 0, richerror.New(op).WithErr(qErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantScanQueryResult) //if StErr != nil {
} //
// return nil, 0, richerror.New(op).WithErr(StErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
return transactions, lenCounter, nil //
//}
//
//offset := (dbPagination.PageNumber - 1) * dbPagination.PageSize
//
//limit := dbPagination.PageSize
//
//queryRows, qrErr := stmt.QueryContext(ctx, userID, limit, offset)
//
//if qrErr != nil {
// return nil, 0, richerror.New(op).WithErr(qrErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
//}
//
//defer queryRows.Close()
//
//var transactions []entity.Transaction
//
//for queryRows.Next() {
//
// transaction, err := scanTransaction(queryRows)
//
// if err != nil {
// return nil, 0, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
// }
//
// transactions = append(transactions, transaction)
//
//}
//
//if qErr := queryRows.Err(); qErr != nil {
//
// return nil, 0, richerror.New(op).WithErr(qErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
//}
//
//return transactions, totalCount, nil
} }
func scanTransaction(scanner postgres.Scanner) (transaction entity.Transaction, err error) { func scanTransaction(scanner postgres.Scanner) (transaction entity.Transaction, err error) {
err = scanner.Scan(&transaction.UserID, &transaction.Currency, &transaction.Amount, &transaction.ActionType, &transaction.Timestamp) err = scanner.Scan(&transaction.UserID, &transaction.Currency, &transaction.Amount, &transaction.ActionType, &transaction.Timestamp, &transaction.CreatedAt)
if err != nil { if err != nil {
return return
} }

View File

@ -15,7 +15,6 @@ type Repository interface {
type Config struct { type Config struct {
PageSize int64 `koanf:"page_size"` PageSize int64 `koanf:"page_size"`
MaxNextPages int64 `koanf:"max_next_pages"`
} }
type Service struct { type Service struct {

View File

@ -2,7 +2,6 @@ package service
import ( import (
"context" "context"
"time"
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity" "git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
"git.gocasts.ir/ebhomengo/niki/domain/wallet/param" "git.gocasts.ir/ebhomengo/niki/domain/wallet/param"
@ -12,30 +11,24 @@ import (
func (s Service) GetUserTransactionHistory(ctx context.Context, request param.TransactionRequest) (param.TransactionResponse, error) { func (s Service) GetUserTransactionHistory(ctx context.Context, request param.TransactionRequest) (param.TransactionResponse, error) {
const op = richerror.Op("wallet.service.GetUserTransactionHistory") const op = richerror.Op("wallet.service.GetUserTransactionHistory")
lastTimeStamp := request.Pagination.LastTimeStamp
if lastTimeStamp.IsZero() {
lastTimeStamp = time.Now()
}
dbPagination := postgres.DBPagination{ dbPagination := postgres.DBPagination{
LastTimeStamp: lastTimeStamp,
PageNumber: request.Pagination.PageNumber, PageNumber: request.Pagination.PageNumber,
MaxNextPages: s.cfg.MaxNextPages,
PageSize: s.cfg.PageSize, PageSize: s.cfg.PageSize,
} }
transactionList, listLen, err := s.repo.GetTransactionListByUserID(ctx, request.UserID, dbPagination) transactionList, totalCount, err := s.repo.GetTransactionListByUserID(ctx, request.UserID, dbPagination)
showableNextPagesNum := postgres.ComputeNextPages(listLen, s.cfg.PageSize, s.cfg.MaxNextPages)
if err != nil { if err != nil {
return param.TransactionResponse{}, richerror.New(op).WithErr(err) return param.TransactionResponse{}, richerror.New(op).WithErr(err)
} }
totalPages := (totalCount + s.cfg.PageSize - 1) / s.cfg.PageSize
paginationInfo := postgres.ResponsePagination{ paginationInfo := postgres.ResponsePagination{
PageNumber: request.Pagination.PageNumber, PageNumber: request.Pagination.PageNumber,
PageSize: s.cfg.PageSize, PageSize: s.cfg.PageSize,
ShowableNextPagesNum: showableNextPagesNum, TotalPages: totalPages,
} }
return param.TransactionResponse{ return param.TransactionResponse{

View File

@ -1,35 +1,85 @@
package postgres package postgres
import "time" import (
"context"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
)
// page number pagination
type RequestPagination struct { type RequestPagination struct {
PageNumber int64 `json:"page_number"` PageNumber int64 `json:"page_number"`
LastTimeStamp time.Time `json:"last_time_stamp"`
} }
type ResponsePagination struct { type ResponsePagination struct {
PageNumber int64 `json:"page_number"` PageNumber int64 `json:"page_number"`
PageSize int64 `json:"page_size"` PageSize int64 `json:"page_size"`
ShowableNextPagesNum int64 `json:"showable_next_pages_num"` TotalPages int64 `json:"total_pages"`
} }
type DBPagination struct { type DBPagination struct {
LastTimeStamp time.Time
PageNumber int64 PageNumber int64
MaxNextPages int64
PageSize int64 PageSize int64
} }
func ComputeNextPages(listLen int64, pageSize int64, maxNextPages int64) int64 { type ScannerFunc[T any] func(scanner Scanner) (T, error)
pages := float64(listLen) / float64(pageSize) func PageNumberPagination[T any](ctx context.Context, countQuery string, fetchQuery string, conn *DB, countQueryStmt statementKey, fetchQueryStmt statementKey, op richerror.Op, scanner ScannerFunc[T], countParams []any, fetchParams []any) ([]T, int64, error) {
for i := maxNextPages - 1; i >= 0; i-- { var totalCount int64
if pages > float64(i) {
return i + 1
}
}
return 0 countStmt, CountStErr := conn.PrepareStatement(ctx, countQueryStmt, countQuery)
if CountStErr != nil {
return nil, 0, richerror.New(op).WithErr(CountStErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
} }
countErr := countStmt.QueryRowContext(ctx, countParams...).Scan(&totalCount)
if countErr != nil {
return nil, 0, richerror.New(op).WithErr(countErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
}
//// get records
stmt, StErr := conn.PrepareStatement(ctx, fetchQueryStmt, fetchQuery)
if StErr != nil {
return nil, 0, richerror.New(op).WithErr(StErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
}
queryRows, qrErr := stmt.QueryContext(ctx, fetchParams...)
if qrErr != nil {
return nil, 0, richerror.New(op).WithErr(qrErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
}
defer queryRows.Close()
var itemsList []T
for queryRows.Next() {
item, err := scanner(queryRows)
if err != nil {
return nil, 0, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
}
itemsList = append(itemsList, item)
}
if qErr := queryRows.Err(); qErr != nil {
return nil, 0, richerror.New(op).WithErr(qErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
}
return itemsList, totalCount, nil
}

View File

@ -4,6 +4,7 @@ type statementKey uint
const ( const (
StatementKeyAWalletGetTransactionHistory statementKey = iota + 1 //wallet StatementKeyAWalletGetTransactionHistory statementKey = iota + 1 //wallet
StatementKeyAWalletGetTotalCountTransactionHistory //wallet
StatementKeyWalletInsertTransaction //wallet StatementKeyWalletInsertTransaction //wallet
StatementKeyWalletGetUserWallet //wallet StatementKeyWalletGetUserWallet //wallet
StatementKeyWalletUpsertBalance //wallet StatementKeyWalletUpsertBalance //wallet