forked from ebhomengo/niki
EDIT | Add & Use General Pagination Func
This commit is contained in:
parent
bc24bcc686
commit
44bfde3c65
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ 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 {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
PageSize: s.cfg.PageSize,
|
||||||
MaxNextPages: s.cfg.MaxNextPages,
|
|
||||||
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{
|
||||||
|
|
|
||||||
|
|
@ -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
|
PageSize 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)
|
||||||
|
|
||||||
|
}
|
||||||
|
countErr := countStmt.QueryRowContext(ctx, countParams...).Scan(&totalCount)
|
||||||
|
|
||||||
|
if countErr != nil {
|
||||||
|
return nil, 0, richerror.New(op).WithErr(countErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
|
||||||
|
|
||||||
for i := maxNextPages - 1; i >= 0; i-- {
|
|
||||||
if pages > float64(i) {
|
|
||||||
return i + 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ package postgres
|
||||||
type statementKey uint
|
type statementKey uint
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StatementKeyAWalletGetTransactionHistory statementKey = iota + 1 //wallet
|
StatementKeyAWalletGetTransactionHistory statementKey = iota + 1 //wallet
|
||||||
StatementKeyWalletInsertTransaction //wallet
|
StatementKeyAWalletGetTotalCountTransactionHistory //wallet
|
||||||
StatementKeyWalletGetUserWallet //wallet
|
StatementKeyWalletInsertTransaction //wallet
|
||||||
StatementKeyWalletUpsertBalance //wallet
|
StatementKeyWalletGetUserWallet //wallet
|
||||||
|
StatementKeyWalletUpsertBalance //wallet
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue