Yimaru-BackEnd/internal/repository/virtual_game.go

551 lines
20 KiB
Go

package repository
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
type VirtualGameRepository interface {
CountVirtualGameProviders(ctx context.Context) (int64, error)
CreateVirtualGameProvider(ctx context.Context, arg dbgen.CreateVirtualGameProviderParams) (dbgen.VirtualGameProvider, error)
DeleteVirtualGameProvider(ctx context.Context, providerID string) error
DeleteAllVirtualGameProviders(ctx context.Context) error
GetVirtualGameProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error)
ListVirtualGameProviders(ctx context.Context, limit, offset int32) ([]dbgen.VirtualGameProvider, error)
UpdateVirtualGameProviderEnabled(ctx context.Context, providerID string, enabled bool) (dbgen.VirtualGameProvider, error)
CreateVirtualGameSession(ctx context.Context, session *domain.VirtualGameSession) error
GetVirtualGameSessionByUserID(ctx context.Context, userID int64) (*domain.VirtualGameSession, error)
GetVirtualGameSessionByToken(ctx context.Context, token string) (*domain.VirtualGameSession, error)
// UpdateVirtualGameSessionStatus(ctx context.Context, id int64, status string) error
CreateVirtualGameTransaction(ctx context.Context, tx *domain.VirtualGameTransaction) error
GetVirtualGameTransactionByExternalID(ctx context.Context, externalID string) (*domain.VirtualGameTransaction, error)
UpdateVirtualGameTransactionStatus(ctx context.Context, id int64, status string) error
// WithTransaction(ctx context.Context, fn func(ctx context.Context) error) error
AddFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error
RemoveFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error
ListFavoriteGames(
ctx context.Context,
userID int64,
providerID *string,
limit, offset int32,
) ([]dbgen.VirtualGame, error)
// GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error)
CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error
CreateVirtualGame(ctx context.Context, arg dbgen.CreateVirtualGameParams) (dbgen.VirtualGame, error)
ListAllVirtualGames(ctx context.Context, arg dbgen.GetAllVirtualGamesParams) ([]dbgen.GetAllVirtualGamesRow, error)
RemoveAllVirtualGames(ctx context.Context) error
CreateVirtualGameProviderReport(ctx context.Context, report domain.CreateVirtualGameProviderReport) (domain.VirtualGameProviderReport, error)
CreateVirtualGameReport(ctx context.Context, report domain.CreateVirtualGameReport) (domain.VirtualGameReport, error)
GetVirtualGameProviderReportByProviderAndDate(ctx context.Context, providerID string, createdAt time.Time, reportType string) (domain.VirtualGameProviderReport, error)
UpdateVirtualGameProviderReportByDate(ctx context.Context, providerID string, reportDate time.Time, reportType string, totalGamesPlayed int64, totalBets float64, totalPayouts float64, totalPlayers int64) error
ListVirtualGameProviderReportsByGamesPlayedAsc(ctx context.Context) ([]domain.VirtualGameProviderReport, error)
ListVirtualGameProviderReportsByGamesPlayedDesc(ctx context.Context) ([]domain.VirtualGameProviderReport, error)
}
type VirtualGameRepo struct {
store *Store
}
// GetGameCounts implements VirtualGameRepository.
// func (r *VirtualGameRepo) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total int64, active int64, inactive int64, err error) {
// panic("unimplemented")
// }
// func convertDBVirtualGameProvider(p dbgen.VirtualGameProvider) domain.VirtualGameProvider {
// var logoDark *string
// if p.LogoDark.Valid {
// logoDark = &p.LogoDark.String
// }
// var logoLight *string
// if p.LogoLight.Valid {
// logoLight = &p.LogoLight.String
// }
// return domain.VirtualGameProvider{
// // ID: p.ID,
// ProviderID: p.ProviderID,
// ProviderName: p.ProviderName,
// LogoDark: logoDark,
// LogoLight: logoLight,
// Enabled: p.Enabled,
// CreatedAt: p.CreatedAt.Time,
// UpdatedAt: &p.UpdatedAt.Time,
// }
// }
func ConvertCreateVirtualGameProvider(p domain.VirtualGameProvider) dbgen.CreateVirtualGameProviderParams {
return dbgen.CreateVirtualGameProviderParams{
ProviderID: p.ProviderID,
ProviderName: p.ProviderName,
LogoDark: pgtype.Text{String: func() string {
if p.LogoDark != nil {
return *p.LogoDark
}
return ""
}(), Valid: p.LogoDark != nil},
LogoLight: pgtype.Text{String: func() string {
if p.LogoLight != nil {
return *p.LogoLight
}
return ""
}(), Valid: p.LogoLight != nil},
Enabled: p.Enabled,
// CreatedAt: time.Now(),
}
}
func NewVirtualGameRepository(store *Store) VirtualGameRepository {
return &VirtualGameRepo{store: store}
}
func (r *VirtualGameRepo) CreateVirtualGameProvider(ctx context.Context, arg dbgen.CreateVirtualGameProviderParams) (dbgen.VirtualGameProvider, error) {
return r.store.queries.CreateVirtualGameProvider(ctx, arg)
}
func (r *VirtualGameRepo) RemoveVirtualGameProvider(ctx context.Context, providerID string) error {
return r.store.queries.DeleteVirtualGameProvider(ctx, providerID)
}
func (r *VirtualGameRepo) DeleteAllVirtualGameProviders(ctx context.Context) error {
return r.store.queries.DeleteAllVirtualGameProviders(ctx)
}
func (r *VirtualGameRepo) DeleteVirtualGameProvider(ctx context.Context, providerID string) error {
return r.store.queries.DeleteVirtualGameProvider(ctx, providerID)
}
func (r *VirtualGameRepo) GetVirtualGameProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) {
return r.store.queries.GetVirtualGameProviderByID(ctx, providerID)
}
func (r *VirtualGameRepo) ListVirtualGameProviders(ctx context.Context, limit, offset int32) ([]dbgen.VirtualGameProvider, error) {
args := dbgen.ListVirtualGameProvidersParams{
Limit: limit,
Offset: offset,
}
return r.store.queries.ListVirtualGameProviders(ctx, args)
}
func (r *VirtualGameRepo) UpdateVirtualGameProviderEnabled(ctx context.Context, providerID string, enabled bool) (dbgen.VirtualGameProvider, error) {
params := dbgen.UpdateVirtualGameProviderEnabledParams{
ProviderID: providerID,
Enabled: enabled,
}
return r.store.queries.UpdateVirtualGameProviderEnabled(ctx, params)
}
func (r *VirtualGameRepo) AddFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error {
params := dbgen.AddFavoriteGameParams{
UserID: userID,
GameID: gameID,
ProviderID: providerID,
}
return r.store.queries.AddFavoriteGame(ctx, params)
}
func (r *VirtualGameRepo) RemoveFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error {
params := dbgen.RemoveFavoriteGameParams{
UserID: userID,
GameID: gameID,
ProviderID: providerID,
}
return r.store.queries.RemoveFavoriteGame(ctx, params)
}
func (r *VirtualGameRepo) ListFavoriteGames(
ctx context.Context,
userID int64,
providerID *string,
limit, offset int32,
) ([]dbgen.VirtualGame, error) {
params := dbgen.GetUserFavoriteGamesPaginatedParams{
UserID: userID,
Limit: limit,
Offset: offset,
}
if providerID != nil {
params.Column2 = *providerID
} else {
params.Column2 = ""
}
return r.store.queries.GetUserFavoriteGamesPaginated(ctx, params)
}
func (r *VirtualGameRepo) CountVirtualGameProviders(ctx context.Context) (int64, error) {
return r.store.queries.CountVirtualGameProviders(ctx)
}
func (r *VirtualGameRepo) CreateVirtualGameSession(ctx context.Context, session *domain.VirtualGameSession) error {
params := dbgen.CreateVirtualGameSessionParams{
UserID: session.UserID,
GameID: session.GameID,
SessionToken: session.SessionToken,
// Currency: session.Currency,
// Status: session.Status,
// ExpiresAt: pgtype.Timestamptz{Time: session.ExpiresAt, Valid: true},
}
_, err := r.store.queries.CreateVirtualGameSession(ctx, params)
return err
}
func (r *VirtualGameRepo) GetVirtualGameSessionByUserID(ctx context.Context, userID int64) (*domain.VirtualGameSession, error) {
dbSession, err := r.store.queries.GetVirtualGameSessionByUserID(ctx, userID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
return &domain.VirtualGameSession{
ID: dbSession.ID,
UserID: dbSession.UserID,
GameID: dbSession.GameID,
SessionToken: dbSession.SessionToken,
CreatedAt: dbSession.CreatedAt.Time,
UpdatedAt: dbSession.UpdatedAt.Time,
}, nil
}
func (r *VirtualGameRepo) GetVirtualGameSessionByToken(ctx context.Context, token string) (*domain.VirtualGameSession, error) {
dbSession, err := r.store.queries.GetVirtualGameSessionByToken(ctx, token)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
return &domain.VirtualGameSession{
ID: dbSession.ID,
UserID: dbSession.UserID,
GameID: dbSession.GameID,
SessionToken: dbSession.SessionToken,
// Currency: dbSession.Currency,
// Status: dbSession.Status,
CreatedAt: dbSession.CreatedAt.Time,
UpdatedAt: dbSession.UpdatedAt.Time,
// ExpiresAt: dbSession.ExpiresAt.Time,
}, nil
}
// func (r *VirtualGameRepo) UpdateVirtualGameSessionStatus(ctx context.Context, id int64, status string) error {
// return r.store.queries.UpdateVirtualGameSessionStatus(ctx, dbgen.UpdateVirtualGameSessionStatusParams{
// ID: id,
// Status: status,
// })
// }
func (r *VirtualGameRepo) CreateVirtualGameTransaction(ctx context.Context, tx *domain.VirtualGameTransaction) error {
params := dbgen.CreateVirtualGameTransactionParams{
// SessionID: tx.SessionID,
UserID: tx.UserID,
WalletID: tx.WalletID,
TransactionType: tx.TransactionType,
Amount: tx.Amount,
Currency: tx.Currency,
ExternalTransactionID: tx.ExternalTransactionID,
Status: tx.Status,
}
_, err := r.store.queries.CreateVirtualGameTransaction(ctx, params)
return err
}
func (r *VirtualGameRepo) CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error {
params := dbgen.CreateVirtualGameHistoryParams{
// SessionID: pgtype.Text{String: his.SessionID, Valid: true},
UserID: his.UserID,
// WalletID: pgtype.Int8{Int64: *his.WalletID, Valid: true},
TransactionType: his.TransactionType,
Amount: his.Amount,
Currency: his.Currency,
ExternalTransactionID: his.ExternalTransactionID,
Status: his.Status,
}
_, err := r.store.queries.CreateVirtualGameHistory(ctx, params)
return err
}
func (r *VirtualGameRepo) GetVirtualGameTransactionByExternalID(ctx context.Context, externalID string) (*domain.VirtualGameTransaction, error) {
dbTx, err := r.store.queries.GetVirtualGameTransactionByExternalID(ctx, externalID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
return &domain.VirtualGameTransaction{
ID: dbTx.ID,
// SessionID: dbTx.SessionID,
UserID: dbTx.UserID,
WalletID: dbTx.WalletID,
TransactionType: dbTx.TransactionType,
Amount: dbTx.Amount,
Currency: dbTx.Currency,
ExternalTransactionID: dbTx.ExternalTransactionID,
Status: dbTx.Status,
CreatedAt: dbTx.CreatedAt.Time,
UpdatedAt: dbTx.UpdatedAt.Time,
}, nil
}
func (r *VirtualGameRepo) UpdateVirtualGameTransactionStatus(ctx context.Context, id int64, status string) error {
return r.store.queries.UpdateVirtualGameTransactionStatus(ctx, dbgen.UpdateVirtualGameTransactionStatusParams{
ID: id,
Status: status,
})
}
// func (r *VirtualGameRepo) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) {
// query := `SELECT
// COUNT(*) as total,
// COUNT(CASE WHEN is_active = true THEN 1 END) as active,
// COUNT(CASE WHEN is_active = false THEN 1 END) as inactive
// FROM virtual_games`
// args := []interface{}{}
// argPos := 1
// // Add filters if provided
// if filter.StartTime.Valid {
// query += fmt.Sprintf(" WHERE created_at >= $%d", argPos)
// args = append(args, filter.StartTime.Value)
// argPos++
// }
// if filter.EndTime.Valid {
// query += fmt.Sprintf(" AND created_at <= $%d", argPos)
// args = append(args, filter.EndTime.Value)
// argPos++
// }
// row := r.store.conn.QueryRow(ctx, query, args...)
// err = row.Scan(&total, &active, &inactive)
// if err != nil {
// return 0, 0, 0, fmt.Errorf("failed to get game counts: %w", err)
// }
// return total, active, inactive, nil
// }
func (r *VirtualGameRepo) GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error) {
query := `SELECT game_id FROM virtual_game_histories WHERE user_id = $1 AND transaction_type = 'BET' ORDER BY created_at DESC LIMIT 100`
rows, err := r.store.conn.Query(ctx, query, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var history []domain.VirtualGameHistory
for rows.Next() {
var tx domain.VirtualGameHistory
if err := rows.Scan(&tx.GameID); err == nil {
history = append(history, tx)
}
}
return history, nil
}
func (r *VirtualGameRepo) CreateVirtualGame(ctx context.Context, arg dbgen.CreateVirtualGameParams) (dbgen.VirtualGame, error) {
return r.store.queries.CreateVirtualGame(ctx, arg)
}
func (r *VirtualGameRepo) ListAllVirtualGames(ctx context.Context, arg dbgen.GetAllVirtualGamesParams) ([]dbgen.GetAllVirtualGamesRow, error) {
return r.store.queries.GetAllVirtualGames(ctx, arg)
}
func (r *VirtualGameRepo) RemoveAllVirtualGames(ctx context.Context) error {
return r.store.queries.DeleteAllVirtualGames(ctx)
}
func (r *VirtualGameRepo) CreateVirtualGameProviderReport(
ctx context.Context,
report domain.CreateVirtualGameProviderReport,
) (domain.VirtualGameProviderReport, error) {
dbReport, err := r.store.queries.CreateVirtualGameProviderReport(
ctx,
ConvertCreateVirtualGameProviderReport(report),
)
if err != nil {
return domain.VirtualGameProviderReport{}, err
}
return ConvertDBVirtualGameProviderReport(dbReport), nil
}
func (r *VirtualGameRepo) CreateVirtualGameReport(
ctx context.Context,
report domain.CreateVirtualGameReport,
) (domain.VirtualGameReport, error) {
dbReport, err := r.store.queries.CreateVirtualGameReport(
ctx,
ConvertCreateVirtualGameReport(report),
)
if err != nil {
return domain.VirtualGameReport{}, err
}
return ConvertDBVirtualGameReport(dbReport), nil
}
func (r *VirtualGameRepo) GetVirtualGameProviderReportByProviderAndDate(
ctx context.Context,
providerID string,
reportDate time.Time,
reportType string,
) (domain.VirtualGameProviderReport, error) {
arg := dbgen.GetVirtualGameProviderReportByProviderAndDateParams{
ProviderID: providerID,
ReportDate: pgtype.Date{Time: reportDate, Valid: true},
ReportType: pgtype.Text{String: reportType, Valid: true},
}
dbReport, err := r.store.queries.GetVirtualGameProviderReportByProviderAndDate(ctx, arg)
if err != nil {
return domain.VirtualGameProviderReport{}, err
}
return ConvertDBVirtualGameProviderReport(dbReport), nil
}
func (r *VirtualGameRepo) UpdateVirtualGameProviderReportByDate(
ctx context.Context,
providerID string,
reportDate time.Time,
reportType string,
totalGamesPlayed int64,
totalBets float64,
totalPayouts float64,
totalPlayers int64,
) error {
arg := dbgen.UpdateVirtualGameProviderReportByDateParams{
ProviderID: providerID,
ReportDate: pgtype.Date{Time: reportDate, Valid: true},
ReportType: pgtype.Text{String: reportType, Valid: true},
TotalGamesPlayed: pgtype.Int8{Int64: totalGamesPlayed, Valid: true},
TotalBets: pgtype.Numeric{},
TotalPayouts: pgtype.Numeric{},
TotalPlayers: pgtype.Int8{Int64: totalPlayers, Valid: true},
}
// Safely convert float64 → pgtype.Numeric
if err := arg.TotalBets.Scan(totalBets); err != nil {
return fmt.Errorf("failed to set total_bets: %w", err)
}
if err := arg.TotalPayouts.Scan(totalPayouts); err != nil {
return fmt.Errorf("failed to set total_payouts: %w", err)
}
if err := r.store.queries.UpdateVirtualGameProviderReportByDate(ctx, arg); err != nil {
return fmt.Errorf("failed to update provider report for %s: %w", providerID, err)
}
return nil
}
func (r *VirtualGameRepo) ListVirtualGameProviderReportsByGamesPlayedAsc(
ctx context.Context,
) ([]domain.VirtualGameProviderReport, error) {
dbReports, err := r.store.queries.ListVirtualGameProviderReportsByGamesPlayedAsc(ctx)
if err != nil {
return nil, err
}
reports := make([]domain.VirtualGameProviderReport, len(dbReports))
for i, r := range dbReports {
reports[i] = ConvertDBVirtualGameProviderReport(r)
}
return reports, nil
}
func (r *VirtualGameRepo) ListVirtualGameProviderReportsByGamesPlayedDesc(
ctx context.Context,
) ([]domain.VirtualGameProviderReport, error) {
dbReports, err := r.store.queries.ListVirtualGameProviderReportsByGamesPlayedDesc(ctx)
if err != nil {
return nil, err
}
reports := make([]domain.VirtualGameProviderReport, len(dbReports))
for i, r := range dbReports {
reports[i] = ConvertDBVirtualGameProviderReport(r)
}
return reports, nil
}
func ConvertCreateVirtualGameProviderReport(r domain.CreateVirtualGameProviderReport) dbgen.CreateVirtualGameProviderReportParams {
// var totalBets, totalPayouts pgtype.Numeric
// _ = r.TotalBets.
// _ = totalPayouts.Set(r.TotalPayouts)
return dbgen.CreateVirtualGameProviderReportParams{
ProviderID: r.ProviderID,
ReportDate: pgtype.Date{Time: r.ReportDate, Valid: true},
TotalGamesPlayed: pgtype.Int8{Int64: r.TotalGamesPlayed, Valid: true},
TotalBets: pgtype.Numeric{Exp: int32(r.TotalBets)},
TotalPayouts: pgtype.Numeric{Exp: int32(r.TotalPayouts)},
TotalPlayers: pgtype.Int8{Int64: r.TotalPlayers, Valid: true},
Column7: pgtype.Text{String: r.ReportType, Valid: true},
}
}
func ConvertDBVirtualGameProviderReport(db dbgen.VirtualGameProviderReport) domain.VirtualGameProviderReport {
return domain.VirtualGameProviderReport{
ID: db.ID,
ProviderID: db.ProviderID,
ReportDate: db.ReportDate.Time,
TotalGamesPlayed: db.TotalGamesPlayed.Int64,
TotalBets: float64(db.TotalBets.Exp),
TotalPayouts: float64(db.TotalPayouts.Exp),
TotalProfit: float64(db.TotalProfit.Exp),
TotalPlayers: db.TotalPlayers.Int64,
ReportType: db.ReportType.String,
CreatedAt: db.CreatedAt.Time,
UpdatedAt: db.UpdatedAt.Time,
}
}
func ConvertCreateVirtualGameReport(r domain.CreateVirtualGameReport) dbgen.CreateVirtualGameReportParams {
return dbgen.CreateVirtualGameReportParams{
GameID: r.GameID,
ProviderID: r.ProviderID,
ReportDate: pgtype.Date{Time: r.ReportDate},
TotalRounds: pgtype.Int8{Int64: r.TotalRounds},
TotalBets: pgtype.Numeric{Exp: int32(r.TotalBets)},
TotalPayouts: pgtype.Numeric{Exp: int32(r.TotalPayouts)},
TotalPlayers: pgtype.Int8{Int64: r.TotalPlayers},
Column8: r.ReportType,
}
}
func ConvertDBVirtualGameReport(db dbgen.VirtualGameReport) domain.VirtualGameReport {
return domain.VirtualGameReport{
ID: db.ID,
GameID: db.GameID,
ProviderID: db.ProviderID,
ReportDate: db.ReportDate.Time,
TotalRounds: db.TotalRounds.Int64,
TotalBets: float64(db.TotalBets.Exp),
TotalPayouts: float64(db.TotalPayouts.Exp),
TotalProfit: float64(db.TotalProfit.Exp),
TotalPlayers: db.TotalPlayers.Int64,
ReportType: db.ReportType.String,
CreatedAt: db.CreatedAt.Time,
UpdatedAt: db.UpdatedAt.Time,
}
}