forked from ebhomengo/niki
Merge pull request 'init wallet service & postgres adapter #288' (#295) from feature/wallet into develop
Reviewed-on: ebhomengo/niki#295
This commit is contained in:
commit
631399313a
|
|
@ -0,0 +1,28 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Transaction struct {
|
||||||
|
ID uint64
|
||||||
|
UserID uint64
|
||||||
|
Amount float64
|
||||||
|
Currency Currency
|
||||||
|
ActionType TransactionType
|
||||||
|
Timestamp time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
TransactionTypeDeposit TransactionType = "deposit"
|
||||||
|
TransactionTypeWithdraw TransactionType = "withdraw"
|
||||||
|
TransactionTypeRefund TransactionType = "refund"
|
||||||
|
TransactionTypeDonate TransactionType = "donate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Currency string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IRR Currency = "IRR"
|
||||||
|
USD Currency = "USD"
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Wallet struct {
|
||||||
|
ID uint64
|
||||||
|
UserID uint64 // user unique ID
|
||||||
|
Balance float64
|
||||||
|
Currency Currency
|
||||||
|
UpdatedAt time.Time
|
||||||
|
Status WalletStatus // "active", "frozen", "closed"
|
||||||
|
}
|
||||||
|
|
||||||
|
type WalletStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Frozen WalletStatus = "frozen" // when need to check , approve ,validate , solve sth (but deposit is possible)
|
||||||
|
Active WalletStatus = "active" // when everything is ok
|
||||||
|
|
||||||
|
// ??
|
||||||
|
// Closed WalletStatus = "closed" // when need to check , approve ,validate , solve sth (exp : security problem)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package param
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateTransactionRequest struct {
|
||||||
|
UserID uint64 `json:"user_id"`
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
|
Currency entity.Currency `json:"currency"`
|
||||||
|
ActionType entity.TransactionType `json:"action_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InsertTransactionResponse struct {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package param
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TransactionRequest struct {
|
||||||
|
UserID uint64 `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionResponse struct {
|
||||||
|
Transaction []TransactionInfo `json:"transaction"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionInfo struct {
|
||||||
|
ID uint64 `json:"id"`
|
||||||
|
UserID uint64 `json:"user_id"`
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
|
Currency entity.Currency `json:"currency"`
|
||||||
|
ActionType entity.TransactionType `json:"action_type"`
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package param
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WalletRequest struct {
|
||||||
|
UserID uint64 `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WalletResponse struct {
|
||||||
|
Wallet WalletInfo `json:"wallet"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WalletInfo struct {
|
||||||
|
Balance float64 `json:"balance"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
Status entity.WalletStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import "git.gocasts.ir/ebhomengo/niki/pkg/database/postgres"
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
conn *postgres.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(conn *postgres.DB) *DB {
|
||||||
|
return &DB{conn: conn}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
production:
|
||||||
|
dialect: postgres
|
||||||
|
datasource: "host=127.0.0.1 port=5432 user=wallet password=wallet2123 dbname=wallet_db sslmode=disable"
|
||||||
|
dir: domain/wallet/repository/postgres/migrations
|
||||||
|
table: wallet_migrationsns
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/wallet/param"
|
||||||
|
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Service) CreateTransaction(ctx context.Context, request param.CreateTransactionRequest) (param.InsertTransactionResponse, error) {
|
||||||
|
|
||||||
|
const op = richerror.Op("wallet.service.CreateTransaction")
|
||||||
|
|
||||||
|
transaction := entity.Transaction{
|
||||||
|
ID: 0,
|
||||||
|
UserID: request.UserID,
|
||||||
|
Amount: request.Amount,
|
||||||
|
Currency: request.Currency,
|
||||||
|
ActionType: request.ActionType,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
}
|
||||||
|
err := s.repo.InsertTransaction(ctx, transaction)
|
||||||
|
if err != nil {
|
||||||
|
return param.InsertTransactionResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return param.InsertTransactionResponse{}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Repository interface {
|
||||||
|
GetTransactionListByUserID(ctx context.Context, UserID uint64) ([]entity.Transaction, error)
|
||||||
|
GetWalletByUserID(ctx context.Context, UserID uint64) (entity.Wallet, error)
|
||||||
|
InsertTransaction(ctx context.Context, transaction entity.Transaction) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
repo Repository
|
||||||
|
cfg Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(repo Repository, cfg Config) Service {
|
||||||
|
|
||||||
|
return Service{repo: repo, cfg: cfg}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/wallet/entity"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/wallet/param"
|
||||||
|
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Service) GetUserTransactionHistory(ctx context.Context, request param.TransactionRequest) (param.TransactionResponse, error) {
|
||||||
|
const op = richerror.Op("wallet.service.GetUserTransactionHistory")
|
||||||
|
|
||||||
|
transactionList, err := s.repo.GetTransactionListByUserID(ctx, request.UserID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return param.TransactionResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return param.TransactionResponse{Transaction: transactionEntityToTransactionInfo(transactionList)}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func transactionEntityToTransactionInfo(TransactionList []entity.Transaction) []param.TransactionInfo {
|
||||||
|
transactionInfoList := make([]param.TransactionInfo, len(TransactionList))
|
||||||
|
for i, transaction := range TransactionList {
|
||||||
|
transactionInfoList[i] = param.TransactionInfo{
|
||||||
|
ID: transaction.ID,
|
||||||
|
UserID: transaction.UserID,
|
||||||
|
Amount: transaction.Amount,
|
||||||
|
Currency: transaction.Currency,
|
||||||
|
ActionType: transaction.ActionType,
|
||||||
|
Timestamp: transaction.Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactionInfoList
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/wallet/param"
|
||||||
|
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Service) GetUserWallet(ctx context.Context, request param.WalletRequest) (param.WalletResponse, error) {
|
||||||
|
const op = richerror.Op("wallet.service.GetUserWallet")
|
||||||
|
|
||||||
|
wallet, err := s.repo.GetWalletByUserID(ctx, request.UserID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return param.WalletResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return param.WalletResponse{
|
||||||
|
Wallet: param.WalletInfo{
|
||||||
|
Balance: wallet.Balance,
|
||||||
|
UpdatedAt: wallet.UpdatedAt,
|
||||||
|
Status: wallet.Status,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
querier "git.gocasts.ir/ebhomengo/niki/pkg/query_transaction/sql"
|
||||||
|
_ "github.com/jackc/pgx/v5/stdlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Host string `koanf:"host"`
|
||||||
|
Port int `koanf:"port"`
|
||||||
|
User string `koanf:"user"`
|
||||||
|
Password string `koanf:"password"`
|
||||||
|
DbName string `koanf:"dbName"`
|
||||||
|
SSLMode string `koanf:"sslMode"`
|
||||||
|
MaxIdleConn int `koanf:"maxIdleConns"`
|
||||||
|
MaxOpenConn int `koanf:"maxOpenConns"`
|
||||||
|
ConnMaxLifetime int `koanf:"connMaxLifetime"`
|
||||||
|
PathOfMigrations string `koanf:"pathOfMigrations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
config Config
|
||||||
|
db *querier.SQLDB
|
||||||
|
mu sync.Mutex
|
||||||
|
statements map[statementKey]*sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Conn() *querier.SQLDB {
|
||||||
|
return db.db
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(config Config) *DB {
|
||||||
|
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
||||||
|
config.Host, config.Port, config.User, config.Password, config.DbName, config.SSLMode)
|
||||||
|
|
||||||
|
db, err := sql.Open("pgx", dsn)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("can't open postgres db: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
maxIdle := config.MaxIdleConn
|
||||||
|
|
||||||
|
maxOpen := config.MaxOpenConn
|
||||||
|
|
||||||
|
lifetime := time.Duration(config.ConnMaxLifetime) * time.Second
|
||||||
|
|
||||||
|
db.SetMaxIdleConns(maxIdle)
|
||||||
|
db.SetMaxOpenConns(maxOpen)
|
||||||
|
db.SetConnMaxLifetime(lifetime)
|
||||||
|
|
||||||
|
return &DB{
|
||||||
|
config: config,
|
||||||
|
db: &querier.SQLDB{DB: db},
|
||||||
|
statements: make(map[statementKey]*sql.Stmt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) PrepareStatement(ctx context.Context, key statementKey, query string) (*sql.Stmt, error) {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
|
||||||
|
if stmt, ok := db.statements[key]; ok {
|
||||||
|
return stmt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := db.db.PrepareContext(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("prepare statement %q: %w", key, err)
|
||||||
|
}
|
||||||
|
db.statements[key] = stmt
|
||||||
|
|
||||||
|
return stmt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) CloseStatements() error {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
|
||||||
|
var lastErr error
|
||||||
|
for key, stmt := range db.statements {
|
||||||
|
if err := stmt.Close(); err != nil {
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
delete(db.statements, key)
|
||||||
|
}
|
||||||
|
return lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Close() error {
|
||||||
|
return db.db.DB.Close()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
package migrator
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
type statementKey uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatementKeyAWalletGetTransactionHistory statementKey = iota + 1
|
||||||
|
StatementKeyWalletInsertTransaction
|
||||||
|
StatementKeyWalletGetUserWallet
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package walletapp
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetUp() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app Application) StartServer() {}
|
||||||
|
|
||||||
|
func (app Application) StopServer() {}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package walletapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/adapter/redis"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/domain/shoppingbasket/repository"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/pkg/httpserver"
|
||||||
|
logger "git.gocasts.ir/ebhomengo/niki/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Redis redis.Config `koanf:"redis" json:"redis"`
|
||||||
|
Repo repository.Config `koanf:"repo" json:"repo"`
|
||||||
|
HTTPServer httpserver.Config `koanf:"http_server" json:"http_server"`
|
||||||
|
Logger logger.Config `koanf:"logger" json:"logger"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
package httpserver
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
package httpserver
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
package transaction
|
||||||
Loading…
Reference in New Issue