ADD | general instant queries & debug sth

This commit is contained in:
danialasadi 2026-05-08 16:06:54 +03:30
parent 44bfde3c65
commit 9a219730a0
10 changed files with 259 additions and 54 deletions

View File

@ -32,7 +32,7 @@ func (db *DB) GetTransactionListByUserID(ctx context.Context, userID uint64, dbP
scanTransaction, countParams, fetchParams, scanTransaction, countParams, fetchParams,
) )
if err != nil { if err != nil {
return nil, 0, err return nil, 0, richerror.New(op).WithErr(err)
} }
return transactionsList, totalItemsCount, nil return transactionsList, totalItemsCount, nil
@ -101,7 +101,7 @@ func (db *DB) GetTransactionListByUserID(ctx context.Context, userID uint64, dbP
} }
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, &transaction.CreatedAt) err = scanner.Scan(&transaction.ID, &transaction.UserID, &transaction.Amount, &transaction.Currency, &transaction.ActionType, &transaction.Timestamp, &transaction.CreatedAt)
if err != nil { if err != nil {
return return
} }

View File

@ -41,11 +41,11 @@ func (db *DB) InsertTransaction(ctx context.Context, transaction entity.Transact
}() }()
params := []any{transaction.UserID, transaction.Currency, transaction.Amount, transaction.ActionType, transaction.Timestamp} params := []any{transaction.UserID, transaction.Amount, transaction.Currency, transaction.ActionType, transaction.Timestamp}
_, execErr := tx.StmtExecContext(newCtx, stmt, params...) _, execErr := tx.StmtExecContext(newCtx, stmt, params...)
if execErr != nil { if execErr != nil {
err = richerror.New(op).WithErr(execErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantInsertTransaction) err = richerror.New(op).WithErr(execErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
return return
} }

View File

@ -0,0 +1,54 @@
package postgres
import (
"context"
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
"git.gocasts.ir/ebhomengo/niki/pkg/database/postgres"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
)
func (db *DB) GetWalletByUserID(ctx context.Context, UserID uint64) (entity.Wallet, error) {
const op = richerror.Op("Wallet.repo.GetWalletByUserID")
query := `SELECT * FROM wallets WHERE user_id = $1`
/////////////// use instant query
wallet, err := postgres.InstantQueryRowContext[entity.Wallet](ctx, postgres.StatementKeyWalletGetUserWallet, query, db.conn, scanWallet, UserID)
if err != nil {
return entity.Wallet{}, richerror.New(op).WithErr(err)
}
return wallet, nil
//////////////////////normal
//stmt, stErr := db.conn.PrepareStatement(ctx, postgres.StatementKeyWalletGetUserWallet, query)
//if stErr != nil {
// return entity.Wallet{}, richerror.New(op).WithErr(stErr)
//}
//walletRow := db.conn.StmtQueryRowContext(ctx, stmt, UserID)
//
//wallet, sErr := scanWallet(walletRow)
//
//if sErr != nil {
// return entity.Wallet{}, richerror.New(op).WithErr(sErr)
//}
//
//return wallet, nil
}
func scanWallet(scanner postgres.Scanner) (entity.Wallet, error) {
var wallet entity.Wallet
err := scanner.Scan(&wallet.ID, &wallet.UserID, &wallet.Balance, &wallet.Currency, &wallet.Status, &wallet.UpdatedAt)
if err != nil {
return entity.Wallet{}, err
}
return wallet, nil
}

View File

@ -1,40 +0,0 @@
package postgres
import (
"context"
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
"git.gocasts.ir/ebhomengo/niki/pkg/database/postgres"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
)
func (db *DB) GetWalletByUserID(ctx context.Context, UserID uint64) (entity.Wallet, error) {
const op = richerror.Op("Wallet.repo.GetWalletByUserID")
query := `SELECT * FROM wallets WHERE user_id = $1`
stmt, stErr := db.conn.PrepareStatement(ctx, postgres.StatementKeyWalletGetUserWallet, query)
if stErr != nil {
return entity.Wallet{}, richerror.New(op).WithErr(stErr)
}
walletRow := db.conn.StmtQueryRowContext(ctx, stmt, UserID)
wallet, sErr := scanWallet(walletRow)
if sErr != nil {
return entity.Wallet{}, richerror.New(op).WithErr(sErr)
}
return wallet, nil
}
func scanWallet(scanner postgres.Scanner) (entity.Wallet, error) {
var wallet entity.Wallet
err := scanner.Scan(&wallet.ID, &wallet.Balance, &wallet.Currency, &wallet.Status, &wallet.UpdatedAt)
if err != nil {
return entity.Wallet{}, err
}
return wallet, nil
}

View File

@ -22,11 +22,11 @@ func (s Service) CreateTransaction(ctx context.Context, request param.CreateTran
Timestamp: request.Timestamp, Timestamp: request.Timestamp,
CreatedAt: time.Now(), CreatedAt: time.Now(),
} }
err := s.repo.InsertTransaction(ctx, transaction) balance, err := s.repo.InsertTransaction(ctx, transaction)
if err != nil { if err != nil {
return param.InsertTransactionResponse{}, richerror.New(op).WithErr(err) return param.InsertTransactionResponse{}, richerror.New(op).WithErr(err)
} }
return param.InsertTransactionResponse{}, nil return param.InsertTransactionResponse{Balance: balance}, nil
} }

View File

@ -10,7 +10,7 @@ import (
type Repository interface { type Repository interface {
GetTransactionListByUserID(ctx context.Context, UserID uint64, DBPagination postgres.DBPagination) ([]entity.Transaction, int64, error) GetTransactionListByUserID(ctx context.Context, UserID uint64, DBPagination postgres.DBPagination) ([]entity.Transaction, int64, error)
GetWalletByUserID(ctx context.Context, UserID uint64) (entity.Wallet, error) GetWalletByUserID(ctx context.Context, UserID uint64) (entity.Wallet, error)
InsertTransaction(ctx context.Context, transaction entity.Transaction) error InsertTransaction(ctx context.Context, transaction entity.Transaction) (float64, error)
} }
type Config struct { type Config struct {

View File

@ -3,10 +3,13 @@ package postgres
import ( import (
"context" "context"
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"sync" "sync"
"time" "time"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
_ "github.com/jackc/pgx/v5/stdlib" _ "github.com/jackc/pgx/v5/stdlib"
) )
@ -111,3 +114,96 @@ func (db *DB) StmtExecContext(ctx context.Context, stmt *sql.Stmt, args ...any)
return result, nil return result, nil
} }
///////////////////////// generic query
type ScannerFunc[T any] func(scanner Scanner) (T, error)
func InstantQueryContext[T any](ctx context.Context, stmtKey statementKey, query string, conn *DB, scanner ScannerFunc[T], args ...any) ([]T, error) {
const op = richerror.Op("postgres.InstantQueryContext")
readyStmt, err := conn.PrepareStatement(ctx, stmtKey, query)
if err != nil {
return nil, richerror.New(op).WithMessage(errmsg.ErrorMsgFailedQuery)
}
rows, qErr := readyStmt.QueryContext(ctx, args...)
if qErr != nil {
return nil, richerror.New(op).WithMessage(errmsg.ErrorMsgFailedQuery)
}
defer rows.Close()
var itemsList []T
for rows.Next() {
item, sErr := scanner(rows)
if sErr != nil {
return nil, richerror.New(op).WithErr(sErr).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
}
itemsList = append(itemsList, item)
}
if rErr := rows.Err(); rErr != nil {
return nil, richerror.New(op).WithErr(rErr).WithMessage(errmsg.ErrorMsgFailedQuery)
}
return itemsList, nil
}
func InstantQueryRowContext[T any](ctx context.Context, stmtKey statementKey, query string, conn *DB, scanner ScannerFunc[T], args ...any) (item T, err error) {
const op = richerror.Op("postgres.InstantQueryRowContext")
readyStmt, sErr := conn.PrepareStatement(ctx, stmtKey, query)
if sErr != nil {
err = richerror.New(op).WithMessage(errmsg.ErrorMsgFailedQuery)
return
}
row := readyStmt.QueryRowContext(ctx, args...)
item, scErr := scanner(row)
if scErr != nil {
if errors.Is(scErr, sql.ErrNoRows) {
err = richerror.New(op).WithErr(scErr).WithKind(richerror.KindNotFound).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
return
}
err = richerror.New(op).WithErr(scErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
return
}
if rErr := row.Err(); rErr != nil {
err = richerror.New(op).WithErr(rErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
return
}
return
}
func InstantExecContext(ctx context.Context, stmtKey statementKey, query string, conn *DB, args ...any) (sql.Result, error) {
const op = richerror.Op("postgres.InstantExecContext")
readyStmt, err := conn.PrepareStatement(ctx, stmtKey, query)
if err != nil {
return nil, richerror.New(op)
}
result, err := readyStmt.ExecContext(ctx, args...)
if err != nil {
return nil, richerror.New(op)
}
return result, nil
}

View File

@ -24,8 +24,6 @@ type DBPagination struct {
PageSize int64 PageSize int64
} }
type ScannerFunc[T any] func(scanner Scanner) (T, error)
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) { 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 var totalCount int64
@ -78,7 +76,7 @@ func PageNumberPagination[T any](ctx context.Context, countQuery string, fetchQu
if qErr := queryRows.Err(); qErr != nil { if qErr := queryRows.Err(); qErr != nil {
return nil, 0, richerror.New(op).WithErr(qErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantScanQueryResult) return nil, 0, richerror.New(op).WithErr(qErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
} }
return itemsList, totalCount, nil return itemsList, totalCount, nil

View File

@ -3,9 +3,12 @@ package postgres
import ( import (
"context" "context"
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"sync" "sync"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
"github.com/lib/pq" "github.com/lib/pq"
) )
@ -176,3 +179,99 @@ func (tx *TxConn) StmtExecContext(ctx context.Context, stmt *sql.Stmt, args ...a
return result, nil return result, nil
} }
///////////////////////// generic query
func TXInstantQueryContext[T any](ctx context.Context, txConn *sql.Tx, stmtKey statementKey, query string, conn *DB, scanner ScannerFunc[T], args ...any) ([]T, error) {
const op = richerror.Op("postgres.TXInstantQueryContext")
stmt, err := conn.PrepareStatement(ctx, stmtKey, query)
if err != nil {
return nil, richerror.New(op).WithMessage(errmsg.ErrorMsgFailedQuery)
}
txStmt := txConn.StmtContext(ctx, stmt)
rows, qErr := txStmt.QueryContext(ctx, args...)
if qErr != nil {
return nil, richerror.New(op).WithMessage(errmsg.ErrorMsgFailedQuery)
}
defer rows.Close()
var itemsList []T
for rows.Next() {
item, sErr := scanner(rows)
if sErr != nil {
return nil, richerror.New(op).WithErr(sErr).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
}
itemsList = append(itemsList, item)
}
if rErr := rows.Err(); rErr != nil {
return nil, richerror.New(op).WithErr(rErr).WithMessage(errmsg.ErrorMsgFailedQuery)
}
return itemsList, nil
}
func TXInstantQueryRowContext[T any](ctx context.Context, txConn *sql.Tx, stmtKey statementKey, query string, conn *DB, scanner ScannerFunc[T], args ...any) (item T, err error) {
const op = richerror.Op("postgres.TXInstantQueryRowContext")
stmt, sErr := conn.PrepareStatement(ctx, stmtKey, query)
if sErr != nil {
err = richerror.New(op).WithMessage(errmsg.ErrorMsgFailedQuery)
return
}
txStmt := txConn.StmtContext(ctx, stmt)
row := txStmt.QueryRowContext(ctx, args...)
item, scErr := scanner(row)
if scErr != nil {
if errors.Is(scErr, sql.ErrNoRows) {
err = richerror.New(op).WithErr(scErr).WithKind(richerror.KindNotFound).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
return
}
err = richerror.New(op).WithErr(scErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgCantScanQueryResult)
return
}
if rErr := row.Err(); rErr != nil {
err = richerror.New(op).WithErr(rErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery)
return
}
return
}
func TXInstantExecContext(ctx context.Context, txConn *sql.Tx, stmtKey statementKey, query string, conn *DB, args ...any) (sql.Result, error) {
const op = richerror.Op("postgres.TXInstantExecContext")
stmt, err := conn.PrepareStatement(ctx, stmtKey, query)
if err != nil {
return nil, richerror.New(op)
}
txStmt := txConn.StmtContext(ctx, stmt)
result, err := txStmt.ExecContext(ctx, args...)
if err != nil {
return nil, richerror.New(op)
}
return result, nil
}

View File

@ -59,8 +59,6 @@ const (
ErrorMsgInvalidBenefactorStatus = "invalid benefactor status" ErrorMsgInvalidBenefactorStatus = "invalid benefactor status"
ErrorMsgInvalidAction = "action invalid" ErrorMsgInvalidAction = "action invalid"
ErrorMsgCantUpsertBalance = "cant update balance" // wallet ErrorMsgCantUpsertBalance = "cant update balance" // wallet
ErrorMsgCantGetBalance = "cant update balance" // wallet
ErrorMsgCantInsertTransaction = "cant insert transaction" // wallet
ErrorMsgFailedQuery = "query failed" // wallet ErrorMsgFailedQuery = "query failed" // wallet
) )