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/pkg/database/postgres"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
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) {
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
}
totalRetrieveRecord := (dbPagination.MaxNextPages-1)*dbPagination.PageSize + 1
lastTimeStamp := dbPagination.LastTimeStamp
queryRows, err := stmt.QueryContext(ctx, userID, lastTimeStamp, totalRetrieveRecord)
countParams := []any{userID} // $1
fetchParams := []any{userID, PageSize, PageOffset} // $1 , $2 , $3
//
//
/////////////////////////// with generic pagination package
transactionsList, totalItemsCount, err := postgres.PageNumberPagination[entity.Transaction](ctx, countQuery, fetchQuery,
db.conn, countQueryStmt, fetchQueryStmt, op,
scanTransaction, countParams, fetchParams,
)
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
for queryRows.Next() {
lenCounter++
if lenCounter < dbPagination.PageSize+1 {
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, lenCounter, nil
//var totalCount int64
//
//countStmt, CountStErr := db.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, userID).Scan(&totalCount)
//
//if countErr != nil {
// return nil, 0, richerror.New(op).WithErr(CountStErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
//
//}
//
////// get records
//
//stmt, StErr := db.conn.PrepareStatement(ctx, fetchQueryStmt, fetchQuery)
//
//if StErr != nil {
//
// return nil, 0, richerror.New(op).WithErr(StErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
//
//}
//
//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) {
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 {
return
}

View File

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

View File

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

View File

@ -1,35 +1,85 @@
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 {
PageNumber int64 `json:"page_number"`
LastTimeStamp time.Time `json:"last_time_stamp"`
}
type ResponsePagination struct {
PageNumber int64 `json:"page_number"`
PageSize int64 `json:"page_size"`
ShowableNextPagesNum int64 `json:"showable_next_pages_num"`
TotalPages int64 `json:"total_pages"`
}
type DBPagination struct {
LastTimeStamp time.Time
PageNumber int64
MaxNextPages 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) {
var totalCount int64
countStmt, CountStErr := conn.PrepareStatement(ctx, countQueryStmt, countQuery)
if CountStErr != nil {
return nil, 0, richerror.New(op).WithErr(CountStErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
for i := maxNextPages - 1; i >= 0; i-- {
if pages > float64(i) {
return i + 1
}
countErr := countStmt.QueryRowContext(ctx, countParams...).Scan(&totalCount)
if countErr != nil {
return nil, 0, richerror.New(op).WithErr(countErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
}
return 0
//// 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 (
StatementKeyAWalletGetTransactionHistory statementKey = iota + 1 //wallet
StatementKeyAWalletGetTotalCountTransactionHistory //wallet
StatementKeyWalletInsertTransaction //wallet
StatementKeyWalletGetUserWallet //wallet
StatementKeyWalletUpsertBalance //wallet