package postgres import ( "context" "time" "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" "git.gocasts.ir/ebhomengo/niki/pkg/types" "github.com/shopspring/decimal" ) func (db *DB) InsertTransaction(ctx context.Context, transaction entity.Transaction, currencyRate decimal.Decimal) (balance decimal.Decimal, currency types.Currency, err error) { const op = richerror.Op("wallet.repo.InsertTransaction") // TODO : USE TX INSTANT QUERY query := `INSERT INTO Transactions (user_id, amount ,currency ,action_type, timestamp) values ($1, $2, $3, $4, $5)` stmt, stErr := db.conn.PrepareStatement(ctx, postgres.StatementKeyWalletInsertTransaction, query) if stErr != nil { err = richerror.New(op).WithErr(stErr).WithKind(richerror.KindUnexpected) return } txHolder, newCtx := postgres.GetDBTxHolderFromContextOrNew(ctx) tx, txErr := txHolder.BeginTx(newCtx, db.conn.Conn()) if txErr != nil { err = richerror.New(op).WithErr(txErr).WithKind(richerror.KindUnexpected) return } defer func() { if tx != nil { if err != nil { if rbErr := tx.Rollback(); rbErr != nil { // log rbErr } } else if cErr := tx.Commit(); cErr != nil { // log cErr } } }() params := []any{transaction.UserID, transaction.Amount, transaction.Currency, transaction.ActionType, transaction.Timestamp} _, execErr := tx.StmtExecContext(newCtx, stmt, params...) if execErr != nil { err = richerror.New(op).WithErr(execErr).WithKind(richerror.KindUnexpected).WithMessage(errmsg.ErrorMsgFailedQuery) return } newBalance, walletCurrency, balanceErr := db.UpsertBalance(newCtx, transaction, currencyRate) if balanceErr != nil { err = richerror.New(op).WithErr(balanceErr) return } balance = newBalance currency = walletCurrency return } func (db *DB) UpsertBalance(newCtx context.Context, transaction entity.Transaction, currencyRate decimal.Decimal) (balance decimal.Decimal, currency types.Currency, err error) { const op = richerror.Op("wallet.repo.UpdateBalance") txHolder, _ := postgres.GetDBTxHolderFromContextOrNew(newCtx) tx, txErr := txHolder.Conn() if txErr != nil { err = richerror.New(op).WithMessage(errmsg.ErrorMsgFailedQuery).WithKind(richerror.KindUnexpected) return } if tx == nil { err = richerror.New(op).WithMessage(errmsg.ErrorMsgFailedQuery).WithKind(richerror.KindUnexpected) return } upsertQuery := `INSERT INTO wallets (user_id, balance , updated_at) VALUES ($1,$2,$3 ) ON CONFLICT (user_id) DO UPDATE SET balance = wallets.balance + $2 RETURNING balance , currency` upsertStmt, stErr := db.conn.PrepareStatement(newCtx, postgres.StatementKeyWalletUpsertBalance, upsertQuery) if stErr != nil { err = richerror.New(op).WithMessage(errmsg.ErrorMsgFailedQuery).WithKind(richerror.KindUnexpected) return } amount := transaction.Amount.Mul(currencyRate) row := tx.StmtQueryRowContext(newCtx, upsertStmt, transaction.UserID, amount, time.Now()) sErr := row.Scan(&balance, ¤cy) if sErr != nil { err = richerror.New(op).WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected) return } return }