diff --git a/.vscode/settings.json b/.vscode/settings.json index 9228e0e..72aadf4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,5 +17,7 @@ "**/internal/services/**/*.go": "${filename}.${dirname}.service", "**/internal/domain/**/*.go": "${filename}.${dirname}", "**/internal/repository/**/*.go": "${filename}.repo", + "**/internal/ports/**/*.go": "${filename}.ports", + "**/internal/web_server/handlers/**/*.go": "${filename}.handlers", }, } \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 8aded8f..af813b0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -52,6 +52,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/santimpay" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/stats" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/telebirr" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction" @@ -109,86 +110,170 @@ func main() { v := customvalidator.NewCustomValidator(validator.New()) // Initialize services - settingSvc := settings.NewService(store) + settingSvc := settings.NewService(repository.NewSettingStore(store)) messengerSvc := messenger.NewService(settingSvc, cfg) + statSvc := stats.NewService( + repository.NewCompanyStatStore(store), + repository.NewEventStatStore(store), + ) - authSvc := authentication.NewService(store, store, cfg.RefreshExpiry) - userSvc := user.NewService(store, store, messengerSvc, cfg) - eventSvc := event.New(cfg.Bet365Token, store, settingSvc, domain.MongoDBLogger, cfg) - oddsSvc := odds.New(store, cfg, eventSvc, logger, domain.MongoDBLogger) - notificationRepo := repository.NewNotificationRepository(store) - virtuaGamesRepo := repository.NewVirtualGameRepository(store) - // var userStore user.UserStore + authSvc := authentication.NewService( + repository.NewUserStore(store), + repository.NewTokenStore(store), + cfg.RefreshExpiry, + ) + userSvc := user.NewService( + repository.NewUserStore(store), + repository.NewOTPStore(store), + messengerSvc, + cfg, + ) + leagueSvc := league.New(repository.NewLeagueStore(store)) + eventSvc := event.New( + cfg.Bet365Token, + repository.NewEventStore(store), + repository.NewEventHistoryStore(store), + *leagueSvc, + settingSvc, + domain.MongoDBLogger, + cfg, + ) + + oddsSvc := odds.New( + repository.NewOddStore(store), + cfg, + eventSvc, + logger, + domain.MongoDBLogger, + ) + // virtuaGamesRepo := repository.NewVirtualGameRepository(store) // Initialize producer // topic := "wallet-balance-topic" // producer := kafka.NewProducer(cfg.KafkaBrokers, topic) - notificationSvc := notificationservice.New(notificationRepo, domain.MongoDBLogger, logger, cfg, messengerSvc, userSvc) + notificationSvc := notificationservice.New( + repository.NewNotificationStore(store), + domain.MongoDBLogger, + logger, + cfg, + messengerSvc, + userSvc, + ) walletSvc := wallet.NewService( - wallet.WalletStore(store), - wallet.TransferStore(store), - wallet.DirectDepositStore(store), + repository.NewWalletStore(store), + repository.NewTransferStore(store), + repository.NewDirectDepositStore(store), notificationSvc, userSvc, domain.MongoDBLogger, logger, ) - branchSvc := branch.NewService(store) - companySvc := company.NewService(store) - leagueSvc := league.New(store) - ticketSvc := ticket.NewService(store, eventSvc, *oddsSvc, domain.MongoDBLogger, settingSvc, notificationSvc) - betSvc := bet.NewService(store, eventSvc, *oddsSvc, *walletSvc, *branchSvc, *companySvc, *settingSvc, *userSvc, notificationSvc, logger, domain.MongoDBLogger) - resultSvc := result.NewService(store, cfg, logger, domain.MongoDBLogger, *betSvc, *oddsSvc, eventSvc, leagueSvc, notificationSvc, messengerSvc, *userSvc) - bonusSvc := bonus.NewService(store, walletSvc, settingSvc, notificationSvc, domain.MongoDBLogger) - referalRepo := repository.NewReferralRepository(store) + branchSvc := branch.NewService(repository.NewBranchStore(store)) + companySvc := company.NewService(repository.NewCompanyStore(store)) + + ticketSvc := ticket.NewService( + repository.NewTicketStore(store), + eventSvc, + *oddsSvc, + domain.MongoDBLogger, + settingSvc, + ) + betSvc := bet.NewService( + repository.NewBetStore(store), + eventSvc, + *oddsSvc, + *walletSvc, + *branchSvc, + *companySvc, + *settingSvc, + *userSvc, + notificationSvc, + logger, + domain.MongoDBLogger, + ) + + resultSvc := result.NewService( + repository.NewResultLogStore(store), + cfg, + logger, + domain.MongoDBLogger, + *betSvc, + *oddsSvc, + eventSvc, + leagueSvc, + notificationSvc, + messengerSvc, + *userSvc, + ) + + bonusSvc := bonus.NewService( + repository.NewBonusStore(store), + walletSvc, + settingSvc, + notificationSvc, + domain.MongoDBLogger, + ) vitualGameRepo := repository.NewVirtualGameRepository(store) recommendationRepo := repository.NewRecommendationRepository(store) - referalSvc := referralservice.New(referalRepo, *walletSvc, *settingSvc, cfg, logger, domain.MongoDBLogger) - raffleSvc := raffle.NewService(store) + referalSvc := referralservice.New( + repository.NewReferralStore(store), + *walletSvc, + *settingSvc, + cfg, + logger, + domain.MongoDBLogger, + ) + raffleSvc := raffle.NewService( + repository.NewRaffleStore(store), + ) virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger) aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger) veliCLient := veli.NewClient(cfg, walletSvc) - veliVirtualGameService := veli.New(virtualGameSvc, vitualGameRepo, veliCLient, walletSvc, wallet.TransferStore(store), domain.MongoDBLogger, cfg) + veliVirtualGameService := veli.New(virtualGameSvc, vitualGameRepo, veliCLient, walletSvc, repository.NewTransferStore(store), domain.MongoDBLogger, cfg) atlasClient := atlas.NewClient(cfg, walletSvc) - atlasVirtualGameService := atlas.New(virtualGameSvc, vitualGameRepo, atlasClient, walletSvc, wallet.TransferStore(store), cfg) + atlasVirtualGameService := atlas.New(virtualGameSvc, vitualGameRepo, atlasClient, walletSvc, repository.NewTransferStore(store), cfg) recommendationSvc := recommendation.NewService(recommendationRepo) chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY) chapaSvc := chapa.NewService( - wallet.TransferStore(store), + repository.NewTransferStore(store), *walletSvc, - user.UserStore(store), + repository.NewUserStore(store), cfg, chapaClient, ) - reportRepo := repository.NewReportRepo(store) currRepo := repository.NewCurrencyPostgresRepository(store) fixerFertcherSvc := currency.NewFixerFetcher( cfg.FIXER_API_KEY, cfg.FIXER_BASE_URL, ) - transactionSvc := transaction.NewService(store, *branchSvc, *betSvc, *walletSvc, *userSvc) + transactionSvc := transaction.NewService( + repository.NewTransactionStore(store), + *branchSvc, + *betSvc, + *walletSvc, + *userSvc, + ) reportSvc := report.NewService( - store, - bet.BetStore(store), - wallet.WalletStore(store), - transaction.TransactionStore(store), - branch.BranchStore(store), - user.UserStore(store), - reportRepo, - company.CompanyStore(store), - virtuaGamesRepo, - notificationRepo, + repository.NewReportStore(store), + repository.NewBetStore(store), + repository.NewWalletStore(store), + repository.NewTransactionStore(store), + repository.NewBranchStore(store), + repository.NewUserStore(store), + repository.NewOldRepositoryStore(store), + repository.NewCompanyStore(store), + repository.NewVirtualGameRepository(store), + repository.NewNotificationStore(store), notificationSvc, - eventSvc, - companySvc, + statSvc, logger, domain.MongoDBLogger, cfg, @@ -242,16 +327,16 @@ func main() { defer exchangeWorker.Stop() go walletMonitorSvc.Start() - httpserver.StartDataFetchingCrons(eventSvc, *oddsSvc, resultSvc, domain.MongoDBLogger) + httpserver.StartBetAPIDataFetchingCrons(eventSvc, *oddsSvc, resultSvc, domain.MongoDBLogger) httpserver.StartCleanupCrons(*ticketSvc, notificationSvc, domain.MongoDBLogger) - httpserver.StartStatCrons(*companySvc, eventSvc, domain.MongoDBLogger) + httpserver.StartStatCrons(statSvc, domain.MongoDBLogger) httpserver.StartReportCrons(reportSvc, domain.MongoDBLogger) issueReportingRepo := repository.NewReportedIssueRepository(store) issueReportingSvc := issuereporting.New(issueReportingRepo) - transferStore := wallet.TransferStore(store) + transferStore := repository.NewTransferStore(store) // walletStore := wallet.WalletStore(store) arifpaySvc := arifpay.NewArifpayService(cfg, transferStore, walletSvc, &http.Client{ @@ -302,7 +387,8 @@ func main() { aleaService, // veliService, recommendationSvc, - resultSvc, + resultSvc, + statSvc, cfg, domain.MongoDBLogger, ) diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index b820fbb..b8b687d 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -393,14 +393,6 @@ CREATE TABLE odd_history ( odd_value DOUBLE PRECISION NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -CREATE TABLE disabled_odd ( - id BIGSERIAL PRIMARY KEY, - company_id BIGINT NOT NULL, - odds_market_id BIGINT NOT NULL, - raw_odd_id BIGINT NOT NULL, - event_id BIGINT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); CREATE TABLE company_odd_settings ( id BIGSERIAL PRIMARY KEY, company_id BIGINT NOT NULL, diff --git a/db/query/disabled_odds.sql b/db/query/disabled_odds.sql deleted file mode 100644 index d890f7d..0000000 --- a/db/query/disabled_odds.sql +++ /dev/null @@ -1,26 +0,0 @@ --- name: InsertDisabledOdds :one -INSERT INTO disabled_odd ( - odds_market_id, - company_id, - event_id, - raw_odd_id - ) -VALUES ($1, $2, $3, $4) -RETURNING *; --- name: GetAllDisabledOdds :many -SELECT * -FROM disabled_odd; --- name: GetDisabledOddByRawOddID :one -SELECT * -FROM disabled_odd -WHERE raw_odd_id = $1; --- name: GetDisabledOddByID :one -SELECT * -FROM disabled_odd -WHERE raw_odd_id = $1; --- name: DeleteDisabledOddsByID :exec -DELETE FROM disabled_odd -WHERE raw_odd_id = $1; --- name: DeleteDisabledOddsByRawOddID :exec -DELETE FROM disabled_odd -WHERE raw_odd_id = $1; \ No newline at end of file diff --git a/gen/db/disabled_odds.sql.go b/gen/db/disabled_odds.sql.go deleted file mode 100644 index b9cc744..0000000 --- a/gen/db/disabled_odds.sql.go +++ /dev/null @@ -1,139 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: disabled_odds.sql - -package dbgen - -import ( - "context" -) - -const DeleteDisabledOddsByID = `-- name: DeleteDisabledOddsByID :exec -DELETE FROM disabled_odd -WHERE raw_odd_id = $1 -` - -func (q *Queries) DeleteDisabledOddsByID(ctx context.Context, rawOddID int64) error { - _, err := q.db.Exec(ctx, DeleteDisabledOddsByID, rawOddID) - return err -} - -const DeleteDisabledOddsByRawOddID = `-- name: DeleteDisabledOddsByRawOddID :exec -DELETE FROM disabled_odd -WHERE raw_odd_id = $1 -` - -func (q *Queries) DeleteDisabledOddsByRawOddID(ctx context.Context, rawOddID int64) error { - _, err := q.db.Exec(ctx, DeleteDisabledOddsByRawOddID, rawOddID) - return err -} - -const GetAllDisabledOdds = `-- name: GetAllDisabledOdds :many -SELECT id, company_id, odds_market_id, raw_odd_id, event_id, created_at -FROM disabled_odd -` - -func (q *Queries) GetAllDisabledOdds(ctx context.Context) ([]DisabledOdd, error) { - rows, err := q.db.Query(ctx, GetAllDisabledOdds) - if err != nil { - return nil, err - } - defer rows.Close() - var items []DisabledOdd - for rows.Next() { - var i DisabledOdd - if err := rows.Scan( - &i.ID, - &i.CompanyID, - &i.OddsMarketID, - &i.RawOddID, - &i.EventID, - &i.CreatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetDisabledOddByID = `-- name: GetDisabledOddByID :one -SELECT id, company_id, odds_market_id, raw_odd_id, event_id, created_at -FROM disabled_odd -WHERE raw_odd_id = $1 -` - -func (q *Queries) GetDisabledOddByID(ctx context.Context, rawOddID int64) (DisabledOdd, error) { - row := q.db.QueryRow(ctx, GetDisabledOddByID, rawOddID) - var i DisabledOdd - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.OddsMarketID, - &i.RawOddID, - &i.EventID, - &i.CreatedAt, - ) - return i, err -} - -const GetDisabledOddByRawOddID = `-- name: GetDisabledOddByRawOddID :one -SELECT id, company_id, odds_market_id, raw_odd_id, event_id, created_at -FROM disabled_odd -WHERE raw_odd_id = $1 -` - -func (q *Queries) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (DisabledOdd, error) { - row := q.db.QueryRow(ctx, GetDisabledOddByRawOddID, rawOddID) - var i DisabledOdd - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.OddsMarketID, - &i.RawOddID, - &i.EventID, - &i.CreatedAt, - ) - return i, err -} - -const InsertDisabledOdds = `-- name: InsertDisabledOdds :one -INSERT INTO disabled_odd ( - odds_market_id, - company_id, - event_id, - raw_odd_id - ) -VALUES ($1, $2, $3, $4) -RETURNING id, company_id, odds_market_id, raw_odd_id, event_id, created_at -` - -type InsertDisabledOddsParams struct { - OddsMarketID int64 `json:"odds_market_id"` - CompanyID int64 `json:"company_id"` - EventID int64 `json:"event_id"` - RawOddID int64 `json:"raw_odd_id"` -} - -func (q *Queries) InsertDisabledOdds(ctx context.Context, arg InsertDisabledOddsParams) (DisabledOdd, error) { - row := q.db.QueryRow(ctx, InsertDisabledOdds, - arg.OddsMarketID, - arg.CompanyID, - arg.EventID, - arg.RawOddID, - ) - var i DisabledOdd - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.OddsMarketID, - &i.RawOddID, - &i.EventID, - &i.CreatedAt, - ) - return i, err -} diff --git a/gen/db/models.go b/gen/db/models.go index 7e981bc..b828754 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -279,15 +279,6 @@ type DirectDeposit struct { VerifiedAt pgtype.Timestamp `json:"verified_at"` } -type DisabledOdd struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - OddsMarketID int64 `json:"odds_market_id"` - RawOddID int64 `json:"raw_odd_id"` - EventID int64 `json:"event_id"` - CreatedAt pgtype.Timestamp `json:"created_at"` -} - type EnetpulseSport struct { ID int64 `json:"id"` SportID string `json:"sport_id"` diff --git a/internal/domain/direct_deposit.go b/internal/domain/direct_deposit.go new file mode 100644 index 0000000..87c73e1 --- /dev/null +++ b/internal/domain/direct_deposit.go @@ -0,0 +1,113 @@ +package domain + +import ( + "time" + "math/big" + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/jackc/pgx/v5/pgtype" +) + +type DirectDepositStatus string + +const ( + DepositStatusPending DirectDepositStatus = "pending" + DepositStatusCompleted DirectDepositStatus = "completed" + DepositStatusRejected DirectDepositStatus = "rejected" +) + +type DirectDeposit struct { + ID int64 `json:"id"` + CustomerID int64 `json:"customer_id"` + WalletID int64 `json:"wallet_id"` + Wallet Wallet `json:"wallet"` + Amount Currency `json:"amount"` + BankReference string `json:"bank_reference"` + SenderAccount string `json:"sender_account"` + Status DirectDepositStatus `json:"status"` + CreatedAt time.Time `json:"created_at"` + VerifiedBy *int64 `json:"verified_by"` + VerificationNotes string `json:"verification_notes"` + VerifiedAt *time.Time `json:"verified_at"` +} + +type CreateDirectDeposit struct { + CustomerID int64 + WalletID int64 + Amount Currency + BankReference string + SenderAccount string + Status DirectDepositStatus +} + +type UpdateDirectDeposit struct { + ID int64 + Status DirectDepositStatus + VerifiedBy int64 + VerificationNotes string + VerifiedAt time.Time +} + +type DirectDepositRequest struct { + CustomerID int64 `json:"customer_id" binding:"required"` + Amount Currency `json:"amount" binding:"required,gt=0"` + BankReference string `json:"bank_reference" binding:"required"` + SenderAccount string `json:"sender_account" binding:"required"` +} + +type VerifyDirectDepositRequest struct { + DepositID int64 `json:"deposit_id" binding:"required"` + IsVerified bool `json:"is_verified" binding:"required"` + Notes string `json:"notes"` +} + + +func ConvertDBDirectDeposit(deposit dbgen.DirectDeposit) DirectDeposit { + return DirectDeposit{ + ID: deposit.ID, + CustomerID: deposit.CustomerID, + WalletID: deposit.WalletID, + Amount: Currency(deposit.Amount.Int.Int64()), + BankReference: deposit.BankReference, + SenderAccount: deposit.SenderAccount, + Status: DirectDepositStatus(deposit.Status), + CreatedAt: deposit.CreatedAt.Time, + VerifiedBy: convertPgInt64ToPtr(deposit.VerifiedBy), + VerificationNotes: deposit.VerificationNotes.String, + VerifiedAt: convertPgTimeToPtr(deposit.VerifiedAt), + } +} + +func ConvertCreateDirectDeposit(deposit CreateDirectDeposit) dbgen.CreateDirectDepositParams { + return dbgen.CreateDirectDepositParams{ + CustomerID: deposit.CustomerID, + WalletID: deposit.WalletID, + Amount: pgtype.Numeric{Int: big.NewInt(int64(deposit.Amount)), Valid: true}, + BankReference: deposit.BankReference, + SenderAccount: deposit.SenderAccount, + Status: string(deposit.Status), + } +} + +func ConvertUpdateDirectDeposit(deposit UpdateDirectDeposit) dbgen.UpdateDirectDepositParams { + return dbgen.UpdateDirectDepositParams{ + ID: deposit.ID, + Status: string(deposit.Status), + VerifiedBy: pgtype.Int8{Int64: deposit.VerifiedBy, Valid: true}, + VerificationNotes: pgtype.Text{String: deposit.VerificationNotes, Valid: deposit.VerificationNotes != ""}, + VerifiedAt: pgtype.Timestamp{Time: deposit.VerifiedAt, Valid: true}, + } +} + +func convertPgInt64ToPtr(i pgtype.Int8) *int64 { + if i.Valid { + return &i.Int64 + } + return nil +} + +func convertPgTimeToPtr(t pgtype.Timestamp) *time.Time { + if t.Valid { + return &t.Time + } + return nil +} \ No newline at end of file diff --git a/internal/domain/disabled_odds.go b/internal/domain/disabled_odds.go deleted file mode 100644 index 737c6b5..0000000 --- a/internal/domain/disabled_odds.go +++ /dev/null @@ -1,51 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type DisabledOdd struct { - ID int64 - OddMarketID int64 - RawOddID int64 - EventID int64 - CompanyID int64 - CreatedAt time.Time -} - -type CreateDisabledOdd struct { - OddMarketID int64 - RawOddID int64 - EventID int64 - CompanyID int64 -} - -func ConvertCreateDisabledOdd(odd CreateDisabledOdd) dbgen.InsertDisabledOddsParams { - return dbgen.InsertDisabledOddsParams{ - OddsMarketID: odd.OddMarketID, - EventID: odd.EventID, - CompanyID: odd.CompanyID, - RawOddID: odd.RawOddID, - } -} - -func ConvertDBDisabledOdd(dbDisabledOdd dbgen.DisabledOdd) DisabledOdd { - return DisabledOdd{ - ID: dbDisabledOdd.ID, - OddMarketID: dbDisabledOdd.OddsMarketID, - RawOddID: dbDisabledOdd.RawOddID, - EventID: dbDisabledOdd.EventID, - CompanyID: dbDisabledOdd.CompanyID, - CreatedAt: dbDisabledOdd.CreatedAt.Time, - } -} - -func ConvertDisabledOdds(list []dbgen.DisabledOdd) []DisabledOdd { - result := make([]DisabledOdd, 0, len(list)) - for _, item := range list { - result = append(result, ConvertDBDisabledOdd(item)) - } - return result -} diff --git a/internal/domain/raffle.go b/internal/domain/raffle.go index ebbd9bf..17ec892 100644 --- a/internal/domain/raffle.go +++ b/internal/domain/raffle.go @@ -1,6 +1,11 @@ package domain -import "time" +import ( + "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/jackc/pgx/v5/pgtype" +) type Raffle struct { ID int32 @@ -83,3 +88,62 @@ type UpdateRaffleParams struct { Name string `json:"name" validate:"required_without_all=ExpiresAt"` ExpiresAt *time.Time `json:"expires_at" validate:"required_without_all=Name"` } + + +func ConvertRaffleOutcome(raffle dbgen.Raffle) Raffle { + return Raffle{ + ID: raffle.ID, + CompanyID: raffle.CompanyID, + Name: raffle.Name, + CreatedAt: raffle.CreatedAt.Time, + ExpiresAt: raffle.ExpiresAt.Time, + TicketLimit: raffle.TicketLimit, + Type: raffle.Type, + Status: raffle.Status, + } +} + +func ConvertRaffleTicketOutcome(raffle dbgen.RaffleTicket) RaffleTicket { + return RaffleTicket{ + ID: raffle.ID, + RaffleID: raffle.RaffleID, + UserID: raffle.UserID, + IsActive: raffle.IsActive.Bool, + } +} + +func ConvertJoinedRaffleTicketOutcome(raffle dbgen.GetUserRaffleTicketsRow) RaffleTicketRes { + return RaffleTicketRes{ + TicketID: raffle.TicketID, + UserID: raffle.UserID, + Name: raffle.Name, + Type: raffle.Type, + ExpiresAt: raffle.ExpiresAt.Time, + Status: raffle.Status, + } +} + +func ConvertCreateRaffle(raffle CreateRaffle) dbgen.CreateRaffleParams { + return dbgen.CreateRaffleParams{ + CompanyID: raffle.CompanyID, + Name: raffle.Name, + ExpiresAt: pgtype.Timestamp{ + Time: *raffle.ExpiresAt, + Valid: true, + }, + TicketLimit: raffle.TicketLimit, + Type: raffle.Type, + } +} + +func ConvertRaffleStanding(raffleStanding dbgen.GetRaffleStandingRow) RaffleStanding { + return RaffleStanding{ + UserID: raffleStanding.UserID, + RaffleID: raffleStanding.RaffleID, + FirstName: raffleStanding.FirstName, + LastName: raffleStanding.LastName, + PhoneNumber: raffleStanding.PhoneNumber.String, + Email: raffleStanding.Email.String, + TicketCount: raffleStanding.TicketCount, + } +} \ No newline at end of file diff --git a/internal/domain/shop_transaction.go b/internal/domain/shop_transaction.go index 234d974..5574866 100644 --- a/internal/domain/shop_transaction.go +++ b/internal/domain/shop_transaction.go @@ -1,6 +1,10 @@ package domain -import "time" +import ( + "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" +) type ShopTransactionType int @@ -191,3 +195,121 @@ func ConvertShopTransactionDetail(transaction ShopTransactionDetail) ShopTransac type UpdateTransactionVerifiedReq struct { Verified bool `json:"verified" example:"true"` } + + +func ConvertDBShopTransaction(transaction dbgen.ShopTransaction) ShopTransaction { + return ShopTransaction{ + ID: transaction.ID, + Amount: Currency(transaction.Amount), + BranchID: transaction.BranchID, + UserID: transaction.UserID, + Type: ShopTransactionType(transaction.Type), + PaymentOption: PaymentOption(transaction.PaymentOption), + FullName: transaction.FullName, + PhoneNumber: transaction.PhoneNumber, + BankCode: ValidString{ + Value: transaction.BankCode.String, + Valid: transaction.BankCode.Valid, + }, + BeneficiaryName: ValidString{ + Value: transaction.BeneficiaryName.String, + Valid: transaction.BeneficiaryName.Valid, + }, + AccountName: ValidString{ + Value: transaction.AccountName.String, + Valid: transaction.AccountName.Valid, + }, + AccountNumber: ValidString{ + Value: transaction.AccountName.String, + Valid: transaction.AccountNumber.Valid, + }, + ReferenceNumber: ValidString{ + Value: transaction.ReferenceNumber.String, + Valid: transaction.ReferenceNumber.Valid, + }, + ApprovedBy: ValidInt64{ + Value: transaction.ApprovedBy.Int64, + Valid: transaction.ApprovedBy.Valid, + }, + CreatedAt: transaction.CreatedAt.Time, + UpdatedAt: transaction.UpdatedAt.Time, + Verified: transaction.Verified, + CompanyID: transaction.CompanyID, + } +} + +func ConvertDBShopTransactionDetail(transaction dbgen.ShopTransactionDetail) ShopTransactionDetail { + return ShopTransactionDetail{ + ID: transaction.ID, + Amount: Currency(transaction.Amount), + BranchID: transaction.BranchID, + UserID: transaction.UserID, + CreatorFirstName: transaction.CreatorFirstName.String, + CreatorLastName: transaction.CreatorLastName.String, + CreatorPhoneNumber: transaction.CreatorPhoneNumber.String, + Type: ShopTransactionType(transaction.Type), + PaymentOption: PaymentOption(transaction.PaymentOption), + FullName: transaction.FullName, + PhoneNumber: transaction.PhoneNumber, + BankCode: ValidString{ + Value: transaction.BankCode.String, + Valid: transaction.BankCode.Valid, + }, + BeneficiaryName: ValidString{ + Value: transaction.BeneficiaryName.String, + Valid: transaction.BeneficiaryName.Valid, + }, + AccountName: ValidString{ + Value: transaction.AccountName.String, + Valid: transaction.AccountName.Valid, + }, + AccountNumber: ValidString{ + Value: transaction.AccountName.String, + Valid: transaction.AccountNumber.Valid, + }, + ReferenceNumber: ValidString{ + Value: transaction.ReferenceNumber.String, + Valid: transaction.ReferenceNumber.Valid, + }, + ApprovedBy: ValidInt64{ + Value: transaction.ApprovedBy.Int64, + Valid: transaction.ApprovedBy.Valid, + }, + ApproverFirstName: ValidString{ + Value: transaction.ApproverFirstName.String, + Valid: transaction.ApproverFirstName.Valid, + }, + ApproverLastName: ValidString{ + Value: transaction.ApproverLastName.String, + Valid: transaction.ApproverLastName.Valid, + }, + ApproverPhoneNumber: ValidString{ + Value: transaction.ApproverPhoneNumber.String, + Valid: transaction.ApproverPhoneNumber.Valid, + }, + CreatedAt: transaction.CreatedAt.Time, + UpdatedAt: transaction.UpdatedAt.Time, + Verified: transaction.Verified, + CompanyID: transaction.CompanyID, + BranchName: transaction.BranchName.String, + BranchLocation: transaction.BranchLocation.String, + } +} + +func ConvertCreateShopTransaction(transaction CreateShopTransaction) dbgen.CreateShopTransactionParams { + return dbgen.CreateShopTransactionParams{ + Amount: int64(transaction.Amount), + BranchID: transaction.BranchID, + UserID: transaction.UserID, + Type: int64(transaction.Type), + PaymentOption: int64(transaction.PaymentOption), + FullName: transaction.FullName, + PhoneNumber: transaction.PhoneNumber, + CompanyID: transaction.CompanyID, + BankCode: transaction.BankCode.ToPG(), + BeneficiaryName: transaction.BeneficiaryName.ToPG(), + AccountName: transaction.AccountName.ToPG(), + AccountNumber: transaction.AccountNumber.ToPG(), + ReferenceNumber: transaction.ReferenceNumber.ToPG(), + } +} diff --git a/internal/domain/ticket.go b/internal/domain/ticket.go index aa724e3..b1ba771 100644 --- a/internal/domain/ticket.go +++ b/internal/domain/ticket.go @@ -1,6 +1,11 @@ package domain -import "time" +import ( + "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/jackc/pgx/v5/pgtype" +) type TicketOutcome struct { ID int64 `json:"id" example:"1"` @@ -89,3 +94,72 @@ type TicketRes struct { type TicketFilter struct { CompanyID ValidInt64 } + +func ConvertDBTicket(ticket dbgen.Ticket) Ticket { + return Ticket{ + ID: ticket.ID, + Amount: Currency(ticket.Amount), + TotalOdds: ticket.TotalOdds, + CompanyID: ticket.CompanyID, + } +} + +func ConvertDBTicketOutcomes(ticket dbgen.TicketWithOutcome) GetTicket { + + var outcomes []TicketOutcome = make([]TicketOutcome, 0, len(ticket.Outcomes)) + + for _, outcome := range ticket.Outcomes { + outcomes = append(outcomes, TicketOutcome{ + ID: outcome.ID, + TicketID: outcome.TicketID, + EventID: outcome.EventID, + OddID: outcome.OddID, + HomeTeamName: outcome.HomeTeamName, + AwayTeamName: outcome.AwayTeamName, + MarketID: outcome.MarketID, + MarketName: outcome.MarketName, + Odd: outcome.Odd, + OddName: outcome.OddName, + OddHeader: outcome.OddHeader, + OddHandicap: outcome.OddHandicap, + Status: OutcomeStatus(outcome.Status), + Expires: outcome.Expires.Time, + }) + } + return GetTicket{ + ID: ticket.ID, + CompanyID: ticket.CompanyID, + Amount: Currency(ticket.Amount), + TotalOdds: ticket.TotalOdds, + Outcomes: outcomes, + } +} + +func ConvertDBCreateTicketOutcome(ticketOutcome CreateTicketOutcome) dbgen.CreateTicketOutcomeParams { + return dbgen.CreateTicketOutcomeParams{ + TicketID: ticketOutcome.TicketID, + EventID: ticketOutcome.EventID, + OddID: ticketOutcome.OddID, + HomeTeamName: ticketOutcome.HomeTeamName, + AwayTeamName: ticketOutcome.AwayTeamName, + MarketID: ticketOutcome.MarketID, + MarketName: ticketOutcome.MarketName, + Odd: ticketOutcome.Odd, + OddName: ticketOutcome.OddName, + OddHeader: ticketOutcome.OddHeader, + OddHandicap: ticketOutcome.OddHandicap, + Expires: pgtype.Timestamp{ + Time: ticketOutcome.Expires, + Valid: true, + }, + } +} + +func ConvertCreateTicket(ticket CreateTicket) dbgen.CreateTicketParams { + return dbgen.CreateTicketParams{ + Amount: int64(ticket.Amount), + TotalOdds: ticket.TotalOdds, + Ip: ticket.IP, + CompanyID: ticket.CompanyID, + } +} diff --git a/internal/domain/transfer.go b/internal/domain/transfer.go index dd32c18..19ff813 100644 --- a/internal/domain/transfer.go +++ b/internal/domain/transfer.go @@ -1,6 +1,11 @@ package domain -import "time" +import ( + "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/jackc/pgx/v5/pgtype" +) type TransferType string @@ -112,3 +117,82 @@ type TransferStats struct { TotalWithdraws int64 TotalWalletToWallet int64 } + +func ConvertDBTransferDetail(transfer dbgen.WalletTransferDetail) TransferDetail { + return TransferDetail{ + ID: transfer.ID, + Amount: Currency(transfer.Amount.Int64), + Type: TransferType(transfer.Type.String), + Verified: transfer.Verified.Bool, + Message: transfer.Message, + ReceiverWalletID: ValidInt64{ + Value: transfer.ReceiverWalletID.Int64, + Valid: transfer.ReceiverWalletID.Valid, + }, + SenderWalletID: ValidInt64{ + Value: transfer.SenderWalletID.Int64, + Valid: transfer.SenderWalletID.Valid, + }, + DepositorID: ValidInt64{ + Value: transfer.CashierID.Int64, + Valid: transfer.CashierID.Valid, + }, + DepositorFirstName: transfer.FirstName.String, + DepositorLastName: transfer.LastName.String, + DepositorPhoneNumber: transfer.PhoneNumber.String, + PaymentMethod: PaymentMethod(transfer.PaymentMethod.String), + ReferenceNumber: transfer.ReferenceNumber, + SessionID: transfer.SessionID.String, + Status: transfer.Status.String, + CreatedAt: transfer.CreatedAt.Time, + UpdatedAt: transfer.UpdatedAt.Time, + } +} +func ConvertDBTransfer(transfer dbgen.WalletTransfer) Transfer { + return Transfer{ + ID: transfer.ID, + Amount: Currency(transfer.Amount.Int64), + Type: TransferType(transfer.Type.String), + Verified: transfer.Verified.Bool, + Message: transfer.Message, + ReceiverWalletID: ValidInt64{ + Value: transfer.ReceiverWalletID.Int64, + Valid: transfer.ReceiverWalletID.Valid, + }, + SenderWalletID: ValidInt64{ + Value: transfer.SenderWalletID.Int64, + Valid: transfer.SenderWalletID.Valid, + }, + DepositorID: ValidInt64{ + Value: transfer.CashierID.Int64, + Valid: transfer.CashierID.Valid, + }, + PaymentMethod: PaymentMethod(transfer.PaymentMethod.String), + ReferenceNumber: transfer.ReferenceNumber, + SessionID: transfer.SessionID.String, + Status: transfer.Status.String, + CreatedAt: transfer.CreatedAt.Time, + UpdatedAt: transfer.UpdatedAt.Time, + } +} + +func ConvertCreateTransfer(transfer CreateTransfer) dbgen.CreateTransferParams { + return dbgen.CreateTransferParams{ + Message: transfer.Message, + Amount: pgtype.Int8{Int64: int64(transfer.Amount), Valid: true}, + Type: pgtype.Text{String: string(transfer.Type), Valid: true}, + ReceiverWalletID: transfer.ReceiverWalletID.ToPG(), + SenderWalletID: transfer.SenderWalletID.ToPG(), + CashierID: transfer.CashierID.ToPG(), + ReferenceNumber: string(transfer.ReferenceNumber), + SessionID: pgtype.Text{ + String: transfer.SessionID, + Valid: true, + }, + PaymentMethod: pgtype.Text{String: string(transfer.PaymentMethod), Valid: true}, + Verified: pgtype.Bool{ + Bool: transfer.Verified, + Valid: true, + }, + } +} diff --git a/internal/domain/wallet.go b/internal/domain/wallet.go index 17d69d9..79170e5 100644 --- a/internal/domain/wallet.go +++ b/internal/domain/wallet.go @@ -3,6 +3,8 @@ package domain import ( "errors" "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" ) var ( @@ -89,57 +91,64 @@ const ( CompanyWalletType WalletType = "company_wallet" ) -// domain/wallet.go - -type DirectDepositStatus string - -const ( - DepositStatusPending DirectDepositStatus = "pending" - DepositStatusCompleted DirectDepositStatus = "completed" - DepositStatusRejected DirectDepositStatus = "rejected" -) - -type DirectDeposit struct { - ID int64 `json:"id"` - CustomerID int64 `json:"customer_id"` - WalletID int64 `json:"wallet_id"` - Wallet Wallet `json:"wallet"` - Amount Currency `json:"amount"` - BankReference string `json:"bank_reference"` - SenderAccount string `json:"sender_account"` - Status DirectDepositStatus `json:"status"` - CreatedAt time.Time `json:"created_at"` - VerifiedBy *int64 `json:"verified_by"` - VerificationNotes string `json:"verification_notes"` - VerifiedAt *time.Time `json:"verified_at"` +func ConvertDBWallet(wallet dbgen.Wallet) Wallet { + return Wallet{ + ID: wallet.ID, + Balance: Currency(wallet.Balance), + IsWithdraw: wallet.IsWithdraw, + IsBettable: wallet.IsBettable, + IsTransferable: wallet.IsTransferable, + IsActive: wallet.IsActive, + UserID: wallet.UserID, + Type: WalletType(wallet.Type), + UpdatedAt: wallet.UpdatedAt.Time, + CreatedAt: wallet.CreatedAt.Time, + } } -type CreateDirectDeposit struct { - CustomerID int64 - WalletID int64 - Amount Currency - BankReference string - SenderAccount string - Status DirectDepositStatus +func ConvertCreateWallet(wallet CreateWallet) dbgen.CreateWalletParams { + return dbgen.CreateWalletParams{ + IsWithdraw: wallet.IsWithdraw, + IsBettable: wallet.IsBettable, + IsTransferable: wallet.IsTransferable, + UserID: wallet.UserID, + Type: string(wallet.Type), + } } -type UpdateDirectDeposit struct { - ID int64 - Status DirectDepositStatus - VerifiedBy int64 - VerificationNotes string - VerifiedAt time.Time +func ConvertDBCustomerWallet(customerWallet dbgen.CustomerWallet) CustomerWallet { + return CustomerWallet{ + ID: customerWallet.ID, + RegularID: customerWallet.RegularWalletID, + StaticID: customerWallet.StaticWalletID, + CustomerID: customerWallet.CustomerID, + } +} +func ConvertCreateCustomerWallet(customerWallet CreateCustomerWallet) dbgen.CreateCustomerWalletParams { + return dbgen.CreateCustomerWalletParams{ + CustomerID: customerWallet.CustomerID, + RegularWalletID: customerWallet.RegularWalletID, + StaticWalletID: customerWallet.StaticWalletID, + } } -type DirectDepositRequest struct { - CustomerID int64 `json:"customer_id" binding:"required"` - Amount Currency `json:"amount" binding:"required,gt=0"` - BankReference string `json:"bank_reference" binding:"required"` - SenderAccount string `json:"sender_account" binding:"required"` +func ConvertDBGetCustomerWallet(customerWallet dbgen.CustomerWalletDetail) GetCustomerWallet { + return GetCustomerWallet{ + ID: customerWallet.ID, + RegularID: customerWallet.RegularID, + RegularBalance: Currency(customerWallet.RegularBalance), + StaticID: customerWallet.StaticID, + StaticBalance: Currency(customerWallet.StaticBalance), + CustomerID: customerWallet.CustomerID, + RegularIsActive: customerWallet.RegularIsActive, + StaticIsActive: customerWallet.StaticIsActive, + RegularUpdatedAt: customerWallet.RegularUpdatedAt.Time, + StaticUpdatedAt: customerWallet.StaticUpdatedAt.Time, + CreatedAt: customerWallet.CreatedAt.Time, + FirstName: customerWallet.FirstName, + LastName: customerWallet.LastName, + PhoneNumber: customerWallet.PhoneNumber.String, + } } -type VerifyDirectDepositRequest struct { - DepositID int64 `json:"deposit_id" binding:"required"` - IsVerified bool `json:"is_verified" binding:"required"` - Notes string `json:"notes"` -} + diff --git a/internal/services/authentication/port.go b/internal/ports/auth.go similarity index 71% rename from internal/services/authentication/port.go rename to internal/ports/auth.go index 0c313e3..57fdb5f 100644 --- a/internal/services/authentication/port.go +++ b/internal/ports/auth.go @@ -1,4 +1,4 @@ -package authentication +package ports import ( "context" @@ -6,9 +6,6 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) -type UserStore interface { - GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error) -} type TokenStore interface { CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error GetRefreshToken(ctx context.Context, token string) (domain.RefreshToken, error) diff --git a/internal/services/bet/port.go b/internal/ports/bet.go similarity index 99% rename from internal/services/bet/port.go rename to internal/ports/bet.go index 84b0fc2..953e388 100644 --- a/internal/services/bet/port.go +++ b/internal/ports/bet.go @@ -1,9 +1,7 @@ -package bet - +package ports import ( "context" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) @@ -51,3 +49,5 @@ type BetStore interface { GetBetsForCashback(ctx context.Context) ([]domain.GetBet, error) UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error } + + diff --git a/internal/services/bonus/port.go b/internal/ports/bonus.go similarity index 97% rename from internal/services/bonus/port.go rename to internal/ports/bonus.go index 4bbd877..a48c06f 100644 --- a/internal/services/bonus/port.go +++ b/internal/ports/bonus.go @@ -1,4 +1,5 @@ -package bonus +package ports + import ( "context" diff --git a/internal/services/branch/port.go b/internal/ports/branch.go similarity index 99% rename from internal/services/branch/port.go rename to internal/ports/branch.go index d23f91a..34eff13 100644 --- a/internal/services/branch/port.go +++ b/internal/ports/branch.go @@ -1,4 +1,4 @@ -package branch +package ports import ( "context" diff --git a/internal/services/company/port.go b/internal/ports/company.go similarity index 95% rename from internal/services/company/port.go rename to internal/ports/company.go index 848159c..d9342cc 100644 --- a/internal/services/company/port.go +++ b/internal/ports/company.go @@ -1,4 +1,4 @@ -package company +package ports import ( "context" @@ -14,9 +14,13 @@ type CompanyStore interface { GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) UpdateCompany(ctx context.Context, company domain.UpdateCompany) error DeleteCompany(ctx context.Context, id int64) error - + GetCompanyCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) +} + +type CompanyStatStore interface { UpdateCompanyStats(ctx context.Context) error GetCompanyStatByID(ctx context.Context, companyID int64) ([]domain.CompanyStat, error) GetCompanyStatsByInterval(ctx context.Context, filter domain.CompanyStatFilter) ([]domain.CompanyStat, error) } + diff --git a/internal/services/event/port.go b/internal/ports/event.go similarity index 65% rename from internal/services/event/port.go rename to internal/ports/event.go index e8a71bf..fca037b 100644 --- a/internal/services/event/port.go +++ b/internal/ports/event.go @@ -1,4 +1,4 @@ -package event +package ports import ( "context" @@ -7,25 +7,34 @@ import ( ) type EventStore interface { - // FetchLiveEvents(ctx context.Context) error - FetchUpcomingEvents(ctx context.Context) error + SaveEvent(ctx context.Context, e domain.CreateEvent) error GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) GetEventByID(ctx context.Context, ID int64) (domain.BaseEvent, error) - // GetAndStoreMatchResult(ctx context.Context, eventID int64) error UpdateFinalScore(ctx context.Context, eventID int64, fullScore string, status domain.EventStatus) error UpdateEventStatus(ctx context.Context, eventID int64, status domain.EventStatus) error IsEventMonitored(ctx context.Context, eventID int64) (bool, error) UpdateEventMonitored(ctx context.Context, eventID int64, IsMonitored bool) error GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error) - + + GetLiveEventIDs(ctx context.Context) ([]int64, error) + GetEventBySourceID(ctx context.Context, id string, source domain.EventSource) (domain.BaseEvent, error) + + DeleteEvent(ctx context.Context, eventID int64) error // Event Settings Views GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) GetEventWithSettingByID(ctx context.Context, ID int64, companyID int64) (domain.EventWithSettings, error) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error +} - // Stats +type EventHistoryStore interface { + InsertEventHistory(ctx context.Context, eventHistory domain.CreateEventHistory) (domain.EventHistory, error) + GetAllEventHistory(ctx context.Context, filter domain.EventHistoryFilter) ([]domain.EventHistory, error) + GetInitialEventPerDay(ctx context.Context, filter domain.EventHistoryFilter) ([]domain.EventHistory, error) +} + +type EventStatStore interface { GetTotalEventStats(ctx context.Context, filter domain.EventStatsFilter) (domain.EventStats, error) GetTotalEventStatsByInterval(ctx context.Context, filter domain.EventStatsByIntervalFilter) ([]domain.EventStatsByInterval, error) - UpdateEventBetStats(ctx context.Context) error + UpdateEventBetStats(ctx context.Context) error } diff --git a/internal/services/league/port.go b/internal/ports/league.go similarity index 98% rename from internal/services/league/port.go rename to internal/ports/league.go index eef87ce..8adbc79 100644 --- a/internal/services/league/port.go +++ b/internal/ports/league.go @@ -1,4 +1,4 @@ -package league +package ports import ( "context" diff --git a/internal/services/notification/port.go b/internal/ports/notification.go similarity index 51% rename from internal/services/notification/port.go rename to internal/ports/notification.go index 359148b..adcd3b5 100644 --- a/internal/services/notification/port.go +++ b/internal/ports/notification.go @@ -1,23 +1,20 @@ -package notificationservice +package ports import ( "context" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gorilla/websocket" ) type NotificationStore interface { - SendNotification(ctx context.Context, notification *domain.Notification) error - MarkAsRead(ctx context.Context, notificationID string, recipientID int64) error - GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) - ConnectWebSocket(ctx context.Context, recipientID int64, c *websocket.Conn) error - DisconnectWebSocket(recipientID int64) - SendSMS(ctx context.Context, recipientID int64, message string) error - SendEmail(ctx context.Context, recipientID int64, subject, message string) error + GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) // New method CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) - GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error) + + CreateNotification(ctx context.Context, notification *domain.Notification) (*domain.Notification, error) + UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error) + ListFailedNotifications(ctx context.Context, limit int) ([]domain.Notification, error) + DeleteOldNotifications(ctx context.Context) error } diff --git a/internal/ports/odds.go b/internal/ports/odds.go new file mode 100644 index 0000000..d85ddfc --- /dev/null +++ b/internal/ports/odds.go @@ -0,0 +1,31 @@ +package ports + +import ( + "context" + + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +) + +type OddStore interface { + SaveOddMarket(ctx context.Context, m domain.CreateOddMarket) error + GetAllOdds(ctx context.Context, filter domain.OddMarketFilter) ([]domain.OddMarket, error) + GetAllOddsWithSettings(ctx context.Context, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) + GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error) + GetOddsByMarketID(ctx context.Context, marketID int64, eventID int64) (domain.OddMarket, error) + GetOddsWithSettingsByMarketID(ctx context.Context, marketID int64, eventID int64, companyID int64) (domain.OddMarketWithSettings, error) + GetOddsByEventID(ctx context.Context, eventID int64, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) + GetOddsWithSettingsByEventID(ctx context.Context, eventID int64, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) + DeleteOddsForEvent(ctx context.Context, eventID int64) error + + // Settings + SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error + UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) error + GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error) + DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error + DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, companyID int64, oddMarketID int64) error + + // Odd History + InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error) + GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) + GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) +} diff --git a/internal/services/raffle/port.go b/internal/ports/raffle.go similarity index 98% rename from internal/services/raffle/port.go rename to internal/ports/raffle.go index edbe4d3..9ef6ea7 100644 --- a/internal/services/raffle/port.go +++ b/internal/ports/raffle.go @@ -1,4 +1,4 @@ -package raffle +package ports import ( "context" diff --git a/internal/ports/referral.go b/internal/ports/referral.go new file mode 100644 index 0000000..2e78c14 --- /dev/null +++ b/internal/ports/referral.go @@ -0,0 +1,19 @@ +package ports + +import ( + "context" + + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +) + +type ReferralStore interface { + CreateReferralCode(ctx context.Context, referralCode domain.CreateReferralCode) (domain.ReferralCode, error) + CreateUserReferral(ctx context.Context, referral domain.CreateUserReferrals) (domain.UserReferral, error) + GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error) + GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error) + UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error + GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) + GetUserReferral(ctx context.Context, referredID int64) (domain.UserReferral, error) + GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) + GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) +} diff --git a/internal/ports/report.go b/internal/ports/report.go new file mode 100644 index 0000000..88fb18a --- /dev/null +++ b/internal/ports/report.go @@ -0,0 +1,44 @@ +package ports + +import ( + "context" + "time" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +) + +type ReportStore interface { + // GetDashboardSummary(ctx context.Context, filter domain.ReportFilter) (domain.DashboardSummary, error) + // GetBetAnalysis(ctx context.Context, filter domain.ReportFilter) ([]domain.BetAnalysis, error) + // GetCustomerActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.CustomerActivity, error) + // GetBranchPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.BranchPerformance, error) + // GetSportPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.SportPerformance, error) + // GetNotificationReport(ctx context.Context, filter domain.ReportFilter) (domain.NotificationReport, error) + // GetCashierPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.CashierPerformance, error) + // GetCompanyPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.CompanyPerformance, error) + + CreateReportRequest(ctx context.Context, report domain.CreateReportRequest) (domain.ReportRequest, error) + GetAllReportRequests(ctx context.Context, filter domain.ReportRequestFilter) ([]domain.ReportRequestDetail, int64, error) + GetReportRequestByRequestedByID(ctx context.Context, requestedBy int64, filter domain.ReportRequestFilter) ([]domain.ReportRequestDetail, error) + GetReportRequestByID(ctx context.Context, ID int64) (domain.ReportRequestDetail, error) + UpdateReportRequest(ctx context.Context, report domain.UpdateRequestRequest) error +} + +// TODO: Move the code to new Report Store using report requests +// Auto-generated reports should also use a report request (but with no requested_by param) +type OldReportRepository interface { + GenerateReport(timeFrame domain.ReportTimeFrame, start, end time.Time) (*domain.Report, error) + SaveReport(report *domain.Report) error + FindReportsByTimeFrame(timeFrame domain.ReportTimeFrame, limit int) ([]*domain.Report, error) + + GetTotalCashOutInRange(ctx context.Context, from, to time.Time) (float64, error) + GetTotalCashMadeInRange(ctx context.Context, from, to time.Time) (float64, error) + GetTotalCashBacksInRange(ctx context.Context, from, to time.Time) (float64, error) + GetTotalBetsMadeInRange(ctx context.Context, from, to time.Time) (int64, error) + GetVirtualGameSummaryInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetVirtualGameSummaryInRangeRow, error) + GetAllTicketsInRange(ctx context.Context, from, to time.Time) (dbgen.GetAllTicketsInRangeRow, error) + GetWalletTransactionsInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetWalletTransactionsInRangeRow, error) + // GetCompanyWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetCompanyWiseReportRow, error) + // GetBranchWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetBranchWiseReportRow, error) +} diff --git a/internal/services/result/port.go b/internal/ports/result.go similarity index 65% rename from internal/services/result/port.go rename to internal/ports/result.go index b472c6d..b2e32c3 100644 --- a/internal/services/result/port.go +++ b/internal/ports/result.go @@ -1,4 +1,4 @@ -package result +package ports import ( "context" @@ -6,11 +6,6 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) -type ResultService interface { - FetchAndProcessResults(ctx context.Context) error - FetchAndStoreResult(ctx context.Context, eventID string) error -} - type ResultLogStore interface { CreateResultLog(ctx context.Context, result domain.CreateResultLog) (domain.ResultLog, error) GetAllResultLog(ctx context.Context, filter domain.ResultLogFilter) ([]domain.ResultLog, error) diff --git a/internal/services/settings/port.go b/internal/ports/settings.go similarity index 98% rename from internal/services/settings/port.go rename to internal/ports/settings.go index 9f37866..c5e3056 100644 --- a/internal/services/settings/port.go +++ b/internal/ports/settings.go @@ -1,4 +1,4 @@ -package settings +package ports import ( "context" diff --git a/internal/services/ticket/port.go b/internal/ports/ticket.go similarity index 97% rename from internal/services/ticket/port.go rename to internal/ports/ticket.go index 855d997..453c68a 100644 --- a/internal/services/ticket/port.go +++ b/internal/ports/ticket.go @@ -1,4 +1,4 @@ -package ticket +package ports import ( "context" diff --git a/internal/services/transaction/port.go b/internal/ports/transaction.go similarity index 99% rename from internal/services/transaction/port.go rename to internal/ports/transaction.go index 20aebcd..2c377e3 100644 --- a/internal/services/transaction/port.go +++ b/internal/ports/transaction.go @@ -1,8 +1,7 @@ -package transaction +package ports import ( "context" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) diff --git a/internal/services/user/port.go b/internal/ports/user.go similarity index 95% rename from internal/services/user/port.go rename to internal/ports/user.go index 321ee77..0a8b138 100644 --- a/internal/services/user/port.go +++ b/internal/ports/user.go @@ -1,4 +1,4 @@ -package user +package ports import ( "context" @@ -24,7 +24,8 @@ type UserStore interface { GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64, companyId int64) error - + GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error) + GetCustomerCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) GetCustomerDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.CustomerDetail, error) GetBranchCustomerCounts(ctx context.Context, filter domain.ReportFilter) (map[int64]int64, error) diff --git a/internal/services/wallet/port.go b/internal/ports/wallet.go similarity index 99% rename from internal/services/wallet/port.go rename to internal/ports/wallet.go index e5687d4..961e854 100644 --- a/internal/services/wallet/port.go +++ b/internal/ports/wallet.go @@ -1,4 +1,4 @@ -package wallet +package ports import ( "context" diff --git a/internal/repository/auth.go b/internal/repository/auth.go index 367d482..25ca516 100644 --- a/internal/repository/auth.go +++ b/internal/repository/auth.go @@ -7,49 +7,13 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" "github.com/jackc/pgx/v5/pgtype" ) -func (s *Store) GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error) { - user, err := s.queries.GetUserByEmailPhone(ctx, dbgen.GetUserByEmailPhoneParams{ - Email: pgtype.Text{ - String: email, - Valid: true, - }, - PhoneNumber: pgtype.Text{ - String: phone, - Valid: true, - }, - CompanyID: companyID.ToPG(), - }) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return domain.User{}, authentication.ErrUserNotFound - } - return domain.User{}, err - } - return domain.User{ - ID: user.ID, - FirstName: user.FirstName, - LastName: user.LastName, - Email: user.Email.String, - PhoneNumber: user.PhoneNumber.String, - Password: user.Password, - Role: domain.Role(user.Role), - EmailVerified: user.EmailVerified, - PhoneVerified: user.PhoneVerified, - CreatedAt: user.CreatedAt.Time, - UpdatedAt: user.UpdatedAt.Time, - SuspendedAt: user.SuspendedAt.Time, - Suspended: user.Suspended, - CompanyID: domain.ValidInt64{ - Value: user.CompanyID.Int64, - Valid: user.CompanyID.Valid, - }, - }, nil - -} +// Interface for creating new token store +func NewTokenStore(s *Store) ports.TokenStore { return s } func (s *Store) CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error { return s.queries.CreateRefreshToken(ctx, dbgen.CreateRefreshTokenParams{ diff --git a/internal/repository/bet.go b/internal/repository/bet.go index d8d3394..bde09f5 100644 --- a/internal/repository/bet.go +++ b/internal/repository/bet.go @@ -3,28 +3,22 @@ package repository import ( "context" "fmt" - "log/slog" "time" - // "fmt" - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" "go.uber.org/zap" ) -var ( - logger *slog.Logger - mongoLogger *zap.Logger -) +// Interface for creating new bet store +func NewBetStore(s *Store) ports.BetStore { return s } func (s *Store) CreateBet(ctx context.Context, bet domain.CreateBet) (domain.Bet, error) { newBet, err := s.queries.CreateBet(ctx, domain.ConvertCreateBet(bet)) if err != nil { - fmt.Println("We are here") - logger.Error("Failed to create bet", slog.String("error", err.Error()), slog.Any("bet", bet)) return domain.Bet{}, err } return domain.ConvertDBBet(newBet), err @@ -432,7 +426,7 @@ func (s *Store) UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, return result, nil } -func (s *Store) BulkUpdateBetOutcomeStatusForOddIds(ctx context.Context, oddID []int64, status domain.OutcomeStatus) (error) { +func (s *Store) BulkUpdateBetOutcomeStatusForOddIds(ctx context.Context, oddID []int64, status domain.OutcomeStatus) error { err := s.queries.BulkUpdateBetOutcomeStatusByOddIDs(ctx, dbgen.BulkUpdateBetOutcomeStatusByOddIDsParams{ Status: int32(status), OddIds: oddID, diff --git a/internal/repository/bonus.go b/internal/repository/bonus.go index 6b95b3e..a9b717c 100644 --- a/internal/repository/bonus.go +++ b/internal/repository/bonus.go @@ -5,8 +5,12 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) +// Interface for creating new bonus store +func NewBonusStore(s *Store) ports.BonusStore { return s } + func (s *Store) CreateUserBonus(ctx context.Context, bonus domain.CreateBonus) (domain.UserBonus, error) { newBonus, err := s.queries.CreateUserBonus(ctx, domain.ConvertCreateBonus(bonus)) diff --git a/internal/repository/branch.go b/internal/repository/branch.go index 4c5041d..b347d9a 100644 --- a/internal/repository/branch.go +++ b/internal/repository/branch.go @@ -6,9 +6,13 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) +// Interface for creating new branch store +func NewBranchStore(s *Store) ports.BranchStore { return s } + func (s *Store) CreateBranch(ctx context.Context, branch domain.CreateBranch) (domain.Branch, error) { dbBranch, err := s.queries.CreateBranch(ctx, domain.ConvertCreateBranch(branch)) diff --git a/internal/repository/company.go b/internal/repository/company.go index 33e5f16..a9625d8 100644 --- a/internal/repository/company.go +++ b/internal/repository/company.go @@ -5,9 +5,13 @@ import ( "fmt" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) +// Interface for creating new company store +func NewCompanyStore(s *Store) ports.CompanyStore { return s } + func (s *Store) CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error) { dbCompany, err := s.queries.CreateCompany(ctx, domain.ConvertCreateCompany(company)) @@ -81,8 +85,6 @@ func (s *Store) DeleteCompany(ctx context.Context, id int64) error { return s.queries.DeleteCompany(ctx, id) } - - func (s *Store) GetCompanyCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) { query := `SELECT COUNT(*) as total, diff --git a/internal/repository/company_stats.go b/internal/repository/company_stats.go index 6465786..03cbdd6 100644 --- a/internal/repository/company_stats.go +++ b/internal/repository/company_stats.go @@ -5,8 +5,12 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) +// Interface for creating new company stats store +func NewCompanyStatStore(s *Store) ports.CompanyStatStore { return s } + func (s *Store) UpdateCompanyStats(ctx context.Context) error { return s.queries.UpdateCompanyStats(ctx) } diff --git a/internal/repository/direct_deposit.go b/internal/repository/direct_deposit.go index 1fd5f1d..8d4a6d3 100644 --- a/internal/repository/direct_deposit.go +++ b/internal/repository/direct_deposit.go @@ -2,71 +2,21 @@ package repository import ( "context" - "math/big" - "time" - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) -func convertDBDirectDeposit(deposit dbgen.DirectDeposit) domain.DirectDeposit { - return domain.DirectDeposit{ - ID: deposit.ID, - CustomerID: deposit.CustomerID, - WalletID: deposit.WalletID, - Amount: domain.Currency(deposit.Amount.Int.Int64()), - BankReference: deposit.BankReference, - SenderAccount: deposit.SenderAccount, - Status: domain.DirectDepositStatus(deposit.Status), - CreatedAt: deposit.CreatedAt.Time, - VerifiedBy: convertPgInt64ToPtr(deposit.VerifiedBy), - VerificationNotes: deposit.VerificationNotes.String, - VerifiedAt: convertPgTimeToPtr(deposit.VerifiedAt), - } -} +// Interface for creating new wallet store +func NewDirectDepositStore(s *Store) ports.DirectDepositStore { return s } -func convertCreateDirectDeposit(deposit domain.CreateDirectDeposit) dbgen.CreateDirectDepositParams { - return dbgen.CreateDirectDepositParams{ - CustomerID: deposit.CustomerID, - WalletID: deposit.WalletID, - Amount: pgtype.Numeric{Int: big.NewInt(int64(deposit.Amount)), Valid: true}, - BankReference: deposit.BankReference, - SenderAccount: deposit.SenderAccount, - Status: string(deposit.Status), - } -} - -func convertUpdateDirectDeposit(deposit domain.UpdateDirectDeposit) dbgen.UpdateDirectDepositParams { - return dbgen.UpdateDirectDepositParams{ - ID: deposit.ID, - Status: string(deposit.Status), - VerifiedBy: pgtype.Int8{Int64: deposit.VerifiedBy, Valid: true}, - VerificationNotes: pgtype.Text{String: deposit.VerificationNotes, Valid: deposit.VerificationNotes != ""}, - VerifiedAt: pgtype.Timestamp{Time: deposit.VerifiedAt, Valid: true}, - } -} - -func convertPgInt64ToPtr(i pgtype.Int8) *int64 { - if i.Valid { - return &i.Int64 - } - return nil -} - -func convertPgTimeToPtr(t pgtype.Timestamp) *time.Time { - if t.Valid { - return &t.Time - } - return nil -} func (s *Store) CreateDirectDeposit(ctx context.Context, deposit domain.CreateDirectDeposit) (domain.DirectDeposit, error) { - newDeposit, err := s.queries.CreateDirectDeposit(ctx, convertCreateDirectDeposit(deposit)) + newDeposit, err := s.queries.CreateDirectDeposit(ctx, domain.ConvertCreateDirectDeposit(deposit)) if err != nil { return domain.DirectDeposit{}, err } - return convertDBDirectDeposit(newDeposit), nil + return domain.ConvertDBDirectDeposit(newDeposit), nil } func (s *Store) GetDirectDeposit(ctx context.Context, id int64) (domain.DirectDeposit, error) { @@ -74,15 +24,15 @@ func (s *Store) GetDirectDeposit(ctx context.Context, id int64) (domain.DirectDe if err != nil { return domain.DirectDeposit{}, err } - return convertDBDirectDeposit(deposit), nil + return domain.ConvertDBDirectDeposit(deposit), nil } func (s *Store) UpdateDirectDeposit(ctx context.Context, deposit domain.UpdateDirectDeposit) (domain.DirectDeposit, error) { - updatedDeposit, err := s.queries.UpdateDirectDeposit(ctx, convertUpdateDirectDeposit(deposit)) + updatedDeposit, err := s.queries.UpdateDirectDeposit(ctx, domain.ConvertUpdateDirectDeposit(deposit)) if err != nil { return domain.DirectDeposit{}, err } - return convertDBDirectDeposit(updatedDeposit), nil + return domain.ConvertDBDirectDeposit(updatedDeposit), nil } func (s *Store) GetDirectDepositsByStatus(ctx context.Context, status domain.DirectDepositStatus) ([]domain.DirectDeposit, error) { @@ -93,7 +43,7 @@ func (s *Store) GetDirectDepositsByStatus(ctx context.Context, status domain.Dir result := make([]domain.DirectDeposit, 0, len(deposits)) for _, deposit := range deposits { - result = append(result, convertDBDirectDeposit(deposit)) + result = append(result, domain.ConvertDBDirectDeposit(deposit)) } return result, nil } @@ -106,7 +56,7 @@ func (s *Store) GetCustomerDirectDeposits(ctx context.Context, customerID int64) result := make([]domain.DirectDeposit, 0, len(deposits)) for _, deposit := range deposits { - result = append(result, convertDBDirectDeposit(deposit)) + result = append(result, domain.ConvertDBDirectDeposit(deposit)) } return result, nil } diff --git a/internal/repository/disabled_odds.go b/internal/repository/disabled_odds.go deleted file mode 100644 index b94ba65..0000000 --- a/internal/repository/disabled_odds.go +++ /dev/null @@ -1,68 +0,0 @@ -package repository - -import ( - "context" - "fmt" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Store) InsertDisabledOdd(ctx context.Context, odd domain.CreateDisabledOdd) (domain.DisabledOdd, error) { - dbDisabledOdd, err := s.queries.InsertDisabledOdds(ctx, domain.ConvertCreateDisabledOdd(odd)) - - if err != nil { - return domain.DisabledOdd{}, fmt.Errorf("InsertDisabledOdd failed: %w", err) - } - - return domain.ConvertDBDisabledOdd(dbDisabledOdd), nil -} - -func (s *Store) GetAllDisabledOdds(ctx context.Context) ([]domain.DisabledOdd, error) { - dbDisabledOdds, err := s.queries.GetAllDisabledOdds(ctx) - - if err != nil { - return nil, fmt.Errorf("GetAllDisabledOdds failed: %w", err) - } - - return domain.ConvertDisabledOdds(dbDisabledOdds), nil -} - -func (s *Store) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (domain.DisabledOdd, error) { - dbDisabledOdd, err := s.queries.GetDisabledOddByRawOddID(ctx, rawOddID) - - if err != nil { - return domain.DisabledOdd{}, fmt.Errorf("GetDisabledOddByRawOddID failed: %w", err) - } - - return domain.ConvertDBDisabledOdd(dbDisabledOdd), nil -} - -func (s *Store) GetDisabledOddByID(ctx context.Context, id int64) (domain.DisabledOdd, error) { - dbDisabledOdd, err := s.queries.GetDisabledOddByID(ctx, id) - - if err != nil { - return domain.DisabledOdd{}, fmt.Errorf("GetDisabledOddByID failed: %w", err) - } - - return domain.ConvertDBDisabledOdd(dbDisabledOdd), nil -} - -func (s *Store) DeleteDisabledOddsByID(ctx context.Context, id int64) error { - - err := s.queries.DeleteDisabledOddsByID(ctx, id) - if err != nil { - return fmt.Errorf("DeleteDisabledOddsByID failed: %w", err) - } - - return nil -} - -func (s *Store) DeleteDisabledOddsByRawOddID(ctx context.Context, id int64) error { - - err := s.queries.DeleteDisabledOddsByRawOddID(ctx, id) - if err != nil { - return fmt.Errorf("DeleteDisabledOddsByRawOddID failed: %w", err) - } - - return nil -} diff --git a/internal/repository/event.go b/internal/repository/event.go index d318de0..8840e96 100644 --- a/internal/repository/event.go +++ b/internal/repository/event.go @@ -6,11 +6,15 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" // "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" "github.com/jackc/pgx/v5/pgtype" ) +// Interface for creating new event store +func NewEventStore(s *Store) ports.EventStore { return s } + func (s *Store) SaveEvent(ctx context.Context, e domain.CreateEvent) error { return s.queries.InsertEvent(ctx, domain.ConvertCreateEvent(e)) } diff --git a/internal/repository/event_history.go b/internal/repository/event_history.go index 0184581..cdff4b0 100644 --- a/internal/repository/event_history.go +++ b/internal/repository/event_history.go @@ -6,8 +6,12 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) +// Interface for creating new event history store +func NewEventHistoryStore(s *Store) ports.EventHistoryStore { return s } + func (s *Store) InsertEventHistory(ctx context.Context, eventHistory domain.CreateEventHistory) (domain.EventHistory, error) { dbEventHistory, err := s.queries.InsertEventHistory(ctx, domain.ConvertCreateEventHistory(eventHistory)) diff --git a/internal/repository/event_stats.go b/internal/repository/event_stats.go index 7e42858..380c887 100644 --- a/internal/repository/event_stats.go +++ b/internal/repository/event_stats.go @@ -5,8 +5,12 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) +// Interface for creating new event stats store +func NewEventStatStore(s *Store) ports.EventStatStore { return s } + func (s *Store) GetTotalEventStats(ctx context.Context, filter domain.EventStatsFilter) (domain.EventStats, error) { stats, err := s.queries.GetTotalEventStats(ctx, dbgen.GetTotalEventStatsParams{ LeagueID: filter.LeagueID.ToPG(), diff --git a/internal/repository/league.go b/internal/repository/league.go index e2e614e..b21c9d0 100644 --- a/internal/repository/league.go +++ b/internal/repository/league.go @@ -5,9 +5,13 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) +// Interface for creating new league store +func NewLeagueStore(s *Store) ports.LeagueStore { return s } + func (s *Store) SaveLeague(ctx context.Context, league domain.CreateLeague) error { return s.queries.InsertLeague(ctx, domain.ConvertCreateLeague(league)) } @@ -16,7 +20,7 @@ func (s *Store) SaveLeagueSettings(ctx context.Context, leagueSettings domain.Cr return s.queries.SaveLeagueSettings(ctx, domain.ConvertCreateLeagueSettings(leagueSettings)) } -func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague,int64, error) { +func (s *Store) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, int64, error) { l, err := s.queries.GetAllLeagues(ctx, dbgen.GetAllLeaguesParams{ Query: filter.Query.ToPG(), CountryCode: filter.CountryCode.ToPG(), diff --git a/internal/repository/notification.go b/internal/repository/notification.go index 0c91e82..62ceb3c 100644 --- a/internal/repository/notification.go +++ b/internal/repository/notification.go @@ -7,38 +7,13 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gorilla/websocket" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) -type NotificationRepository interface { - CreateNotification(ctx context.Context, notification *domain.Notification) (*domain.Notification, error) - UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error) - GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) - ListFailedNotifications(ctx context.Context, limit int) ([]domain.Notification, error) - ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) - CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) - GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) - GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error) - DeleteOldNotifications(ctx context.Context) error -} +func NewNotificationStore(s *Store) ports.NotificationStore { return s } -type Repository struct { - store *Store -} - -func NewNotificationRepository(store *Store) NotificationRepository { - return &Repository{store: store} -} - -func (s *Store) ConnectWebSocket(ctx context.Context, recipientID int64, c *websocket.Conn) error { - return nil -} - -func (s *Store) DisconnectWebSocket(recipientID int64) { -} - -func (r *Repository) CreateNotification(ctx context.Context, notification *domain.Notification) (*domain.Notification, error) { +func (r *Store) CreateNotification(ctx context.Context, notification *domain.Notification) (*domain.Notification, error) { var errorSeverity pgtype.Text if notification.ErrorSeverity != "" { errorSeverity.String = string(notification.ErrorSeverity) @@ -75,7 +50,7 @@ func (r *Repository) CreateNotification(ctx context.Context, notification *domai Metadata: notification.Metadata, } - dbNotification, err := r.store.queries.CreateNotification(ctx, params) + dbNotification, err := r.queries.CreateNotification(ctx, params) if err != nil { return nil, err } @@ -83,7 +58,7 @@ func (r *Repository) CreateNotification(ctx context.Context, notification *domai return r.mapDBToDomain(&dbNotification), nil } -func (r *Repository) UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error) { +func (r *Store) UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error) { params := dbgen.UpdateNotificationStatusParams{ ID: id, DeliveryStatus: status, @@ -91,7 +66,7 @@ func (r *Repository) UpdateNotificationStatus(ctx context.Context, id, status st Metadata: metadata, } - dbNotification, err := r.store.queries.UpdateNotificationStatus(ctx, params) + dbNotification, err := r.queries.UpdateNotificationStatus(ctx, params) if err != nil { return nil, err } @@ -99,19 +74,19 @@ func (r *Repository) UpdateNotificationStatus(ctx context.Context, id, status st return r.mapDBToDomain(&dbNotification), nil } -func (r *Repository) GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) { +func (r *Store) GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) { params := dbgen.GetUserNotificationsParams{ RecipientID: recipientID, Limit: int32(limit), Offset: int32(offset), } - dbNotifications, err := r.store.queries.GetUserNotifications(ctx, params) + dbNotifications, err := r.queries.GetUserNotifications(ctx, params) if err != nil { return nil, 0, err } - total, err := r.store.queries.GetUserNotificationCount(ctx, recipientID) + total, err := r.queries.GetUserNotificationCount(ctx, recipientID) if err != nil { return nil, 0, err @@ -126,9 +101,9 @@ func (r *Repository) GetUserNotifications(ctx context.Context, recipientID int64 return result, total, nil } -func (r *Repository) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) { +func (r *Store) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) { - dbNotifications, err := r.store.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{ + dbNotifications, err := r.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{ Limit: int32(limit), Offset: int32(offset), }) @@ -144,8 +119,8 @@ func (r *Repository) GetAllNotifications(ctx context.Context, limit, offset int) return result, nil } -func (r *Repository) ListFailedNotifications(ctx context.Context, limit int) ([]domain.Notification, error) { - dbNotifications, err := r.store.queries.ListFailedNotifications(ctx, int32(limit)) +func (r *Store) ListFailedNotifications(ctx context.Context, limit int) ([]domain.Notification, error) { + dbNotifications, err := r.queries.ListFailedNotifications(ctx, int32(limit)) if err != nil { return nil, err } @@ -159,15 +134,15 @@ func (r *Repository) ListFailedNotifications(ctx context.Context, limit int) ([] return result, nil } -func (r *Repository) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) { - return r.store.queries.ListRecipientIDsByReceiver(ctx, string(receiver)) +func (r *Store) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) { + return r.queries.ListRecipientIDsByReceiver(ctx, string(receiver)) } -func (s *Repository) DeleteOldNotifications(ctx context.Context) error { - return s.store.queries.DeleteOldNotifications(ctx) +func (s *Store) DeleteOldNotifications(ctx context.Context) error { + return s.queries.DeleteOldNotifications(ctx) } -func (r *Repository) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notification { +func (r *Store) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notification { var errorSeverity domain.NotificationErrorSeverity if dbNotif.ErrorSeverity.Valid { errorSeverity = domain.NotificationErrorSeverity(dbNotif.ErrorSeverity.String) @@ -225,12 +200,12 @@ func unmarshalPayload(data []byte) (domain.NotificationPayload, error) { return payload, nil } -func (r *Repository) CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) { - return r.store.queries.CountUnreadNotifications(ctx, recipient_id) +func (r *Store) CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) { + return r.queries.CountUnreadNotifications(ctx, recipient_id) } -func (r *Repository) GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error) { - rows, err := r.store.queries.GetNotificationCounts(ctx) +func (r *Store) GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error) { + rows, err := r.queries.GetNotificationCounts(ctx) if err != nil { return 0, 0, 0, fmt.Errorf("failed to get notification counts: %w", err) } @@ -326,14 +301,6 @@ func (s *Store) GetNotificationCountsByType(ctx context.Context, filter domain.R return counts, nil } -func (s *Store) CountUnreadNotifications(ctx context.Context, userID int64) (int64, error) { - count, err := s.queries.CountUnreadNotifications(ctx, userID) - if err != nil { - return 0, err - } - return count, nil -} - // func (s *Store) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) { // dbNotifications, err := s.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{ // Limit: int32(limit), diff --git a/internal/repository/odds.go b/internal/repository/odds.go index 8dd7c7f..e7740c5 100644 --- a/internal/repository/odds.go +++ b/internal/repository/odds.go @@ -9,9 +9,13 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) +// Interface for creating new event store +func NewOddStore(s *Store) ports.OddStore { return s } + func (s *Store) SaveOddMarket(ctx context.Context, m domain.CreateOddMarket) error { if len(m.Odds) == 0 { return nil diff --git a/internal/repository/old_report.go b/internal/repository/old_report.go index 365a4b7..1fb7af4 100644 --- a/internal/repository/old_report.go +++ b/internal/repository/old_report.go @@ -1,16 +1,22 @@ package repository import ( - // "context" - // "fmt" - // "time" + "context" + "fmt" + "time" - // dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - // "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - // "github.com/jackc/pgx/v5/pgtype" + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/jackc/pgx/v5/pgtype" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) -type ReportRepository interface { + +// Interface for creating new old repository repo interface +// We need to have an external definition of the repository or else we can't reuse the interface +func NewOldRepositoryStore(s *Store) ports.OldReportRepository { return s } + +// type ReportRepository interface { // GenerateReport(timeFrame domain.ReportTimeFrame, start, end time.Time) (*domain.Report, error) // SaveReport(report *domain.Report) error // FindReportsByTimeFrame(timeFrame domain.ReportTimeFrame, limit int) ([]*domain.Report, error) @@ -24,211 +30,211 @@ type ReportRepository interface { // GetWalletTransactionsInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetWalletTransactionsInRangeRow, error) // GetCompanyWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetCompanyWiseReportRow, error) // GetBranchWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetBranchWiseReportRow, error) +// } + +// type ReportRepo struct { +// store *Store +// } + +// func NewReportRepo(store *Store) ReportRepository { +// return &ReportRepo{store: store} +// } + +func (s *Store) GenerateReport(timeFrame domain.ReportTimeFrame, start, end time.Time) (*domain.Report, error) { + // Implement SQL queries to calculate metrics + var report domain.Report + + // Total Bets + err := s.conn.QueryRow( + context.Background(), + `SELECT COUNT(*) FROM bets + WHERE created_at BETWEEN $1 AND $2`, start, end).Scan(&report.TotalBets) + if err != nil { + return nil, err + } + + // Total Cash In + err = s.conn.QueryRow( + context.Background(), + `SELECT COALESCE(SUM(amount), 0) FROM transactions + WHERE type = 'stake' AND created_at BETWEEN $1 AND $2`, start, end).Scan(&report.TotalCashIn) + if err != nil { + return nil, err + } + + // Similar queries for Cash Out and Cash Back... + + report.TimeFrame = timeFrame + report.PeriodStart = start + report.PeriodEnd = end + report.GeneratedAt = time.Now() + + return &report, nil } -type ReportRepo struct { - store *Store +func (s *Store) SaveReport(report *domain.Report) error { + _, err := s.conn.Exec( + context.Background(), + `INSERT INTO reports + (id, time_frame, period_start, period_end, total_bets, total_cash_in, total_cash_out, total_cash_back, generated_at) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, + report.ID, report.TimeFrame, report.PeriodStart, report.PeriodEnd, + report.TotalBets, report.TotalCashIn, report.TotalCashOut, report.TotalCashBack, report.GeneratedAt) + return err } -func NewReportRepo(store *Store) ReportRepository { - return &ReportRepo{store: store} +func (s *Store) FindReportsByTimeFrame(timeFrame domain.ReportTimeFrame, limit int) ([]*domain.Report, error) { + rows, err := s.conn.Query( + context.Background(), + `SELECT id, time_frame, period_start, period_end, total_bets, + total_cash_in, total_cash_out, total_cash_back, generated_at + FROM reports + WHERE time_frame = $1 + ORDER BY generated_at DESC + LIMIT $2`, + timeFrame, limit) + if err != nil { + return nil, err + } + defer rows.Close() + + var reports []*domain.Report + for rows.Next() { + var report domain.Report + err := rows.Scan( + &report.ID, + &report.TimeFrame, + &report.PeriodStart, + &report.PeriodEnd, + &report.TotalBets, + &report.TotalCashIn, + &report.TotalCashOut, + &report.TotalCashBack, + &report.GeneratedAt, + ) + if err != nil { + return nil, err + } + reports = append(reports, &report) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return reports, nil } -// func (r *ReportRepo) GenerateReport(timeFrame domain.ReportTimeFrame, start, end time.Time) (*domain.Report, error) { -// // Implement SQL queries to calculate metrics -// var report domain.Report +func (s *Store) GetTotalBetsMadeInRange(ctx context.Context, from, to time.Time) (int64, error) { + params := dbgen.GetTotalBetsMadeInRangeParams{ + From: ToPgTimestamp(from), + To: ToPgTimestamp(to), + } + return s.queries.GetTotalBetsMadeInRange(ctx, params) +} -// // Total Bets -// err := r.store.conn.QueryRow( -// context.Background(), -// `SELECT COUNT(*) FROM bets -// WHERE created_at BETWEEN $1 AND $2`, start, end).Scan(&report.TotalBets) -// if err != nil { -// return nil, err -// } +func (s *Store) GetTotalCashBacksInRange(ctx context.Context, from, to time.Time) (float64, error) { + params := dbgen.GetTotalCashBacksInRangeParams{ + From: ToPgTimestamp(from), + To: ToPgTimestamp(to), + } + value, err := s.queries.GetTotalCashBacksInRange(ctx, params) + if err != nil { + return 0, err + } + return parseFloat(value) +} -// // Total Cash In -// err = r.store.conn.QueryRow( -// context.Background(), -// `SELECT COALESCE(SUM(amount), 0) FROM transactions -// WHERE type = 'stake' AND created_at BETWEEN $1 AND $2`, start, end).Scan(&report.TotalCashIn) -// if err != nil { -// return nil, err -// } +func (s *Store) GetTotalCashMadeInRange(ctx context.Context, from, to time.Time) (float64, error) { + params := dbgen.GetTotalCashMadeInRangeParams{ + From: ToPgTimestamp(from), + To: ToPgTimestamp(to), + } + value, err := s.queries.GetTotalCashMadeInRange(ctx, params) + if err != nil { + return 0, err + } + return parseFloat(value) +} -// // Similar queries for Cash Out and Cash Back... +func (s *Store) GetTotalCashOutInRange(ctx context.Context, from, to time.Time) (float64, error) { + params := dbgen.GetTotalCashOutInRangeParams{ + From: ToPgTimestamp(from), + To: ToPgTimestamp(to), + } + value, err := s.queries.GetTotalCashOutInRange(ctx, params) + if err != nil { + return 0, err + } + return parseFloat(value) +} -// report.TimeFrame = timeFrame -// report.PeriodStart = start -// report.PeriodEnd = end -// report.GeneratedAt = time.Now() +func (s *Store) GetWalletTransactionsInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetWalletTransactionsInRangeRow, error) { + params := dbgen.GetWalletTransactionsInRangeParams{ + CreatedAt: ToPgTimestamp(from), + CreatedAt_2: ToPgTimestamp(to), + } + return s.queries.GetWalletTransactionsInRange(ctx, params) +} -// return &report, nil -// } +func (s *Store) GetAllTicketsInRange(ctx context.Context, from, to time.Time) (dbgen.GetAllTicketsInRangeRow, error) { + params := dbgen.GetAllTicketsInRangeParams{ + CreatedAt: ToPgTimestamp(from), + CreatedAt_2: ToPgTimestamp(to), + } + return s.queries.GetAllTicketsInRange(ctx, params) +} -// func (r *ReportRepo) SaveReport(report *domain.Report) error { -// _, err := r.store.conn.Exec( -// context.Background(), -// `INSERT INTO reports -// (id, time_frame, period_start, period_end, total_bets, total_cash_in, total_cash_out, total_cash_back, generated_at) -// VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, -// report.ID, report.TimeFrame, report.PeriodStart, report.PeriodEnd, -// report.TotalBets, report.TotalCashIn, report.TotalCashOut, report.TotalCashBack, report.GeneratedAt) -// return err -// } +func (s *Store) GetVirtualGameSummaryInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetVirtualGameSummaryInRangeRow, error) { + params := dbgen.GetVirtualGameSummaryInRangeParams{ + CreatedAt: ToPgTimestamptz(from), + CreatedAt_2: ToPgTimestamptz(to), + } + return s.queries.GetVirtualGameSummaryInRange(ctx, params) +} -// func (r *ReportRepo) FindReportsByTimeFrame(timeFrame domain.ReportTimeFrame, limit int) ([]*domain.Report, error) { -// rows, err := r.store.conn.Query( -// context.Background(), -// `SELECT id, time_frame, period_start, period_end, total_bets, -// total_cash_in, total_cash_out, total_cash_back, generated_at -// FROM reports -// WHERE time_frame = $1 -// ORDER BY generated_at DESC -// LIMIT $2`, -// timeFrame, limit) -// if err != nil { -// return nil, err -// } -// defer rows.Close() +func ToPgTimestamp(t time.Time) pgtype.Timestamp { + return pgtype.Timestamp{Time: t, Valid: true} +} -// var reports []*domain.Report -// for rows.Next() { -// var report domain.Report -// err := rows.Scan( -// &report.ID, -// &report.TimeFrame, -// &report.PeriodStart, -// &report.PeriodEnd, -// &report.TotalBets, -// &report.TotalCashIn, -// &report.TotalCashOut, -// &report.TotalCashBack, -// &report.GeneratedAt, -// ) -// if err != nil { -// return nil, err -// } -// reports = append(reports, &report) -// } +func ToPgTimestamptz(t time.Time) pgtype.Timestamptz { + return pgtype.Timestamptz{Time: t, Valid: true} +} -// if err := rows.Err(); err != nil { -// return nil, err -// } +func parseFloat(value interface{}) (float64, error) { + switch v := value.(type) { + case float64: + return v, nil + case int64: + return float64(v), nil + case pgtype.Numeric: + if !v.Valid { + return 0, nil + } + f, err := v.Float64Value() + if err != nil { + return 0, fmt.Errorf("failed to convert pgtype.Numeric to float64: %w", err) + } + return f.Float64, nil + case nil: + return 0, nil + default: + return 0, fmt.Errorf("unexpected type %T for value: %+v", v, v) + } +} -// return reports, nil -// } - -// func (r *ReportRepo) GetTotalBetsMadeInRange(ctx context.Context, from, to time.Time) (int64, error) { -// params := dbgen.GetTotalBetsMadeInRangeParams{ -// From: ToPgTimestamp(from), -// To: ToPgTimestamp(to), -// } -// return r.store.queries.GetTotalBetsMadeInRange(ctx, params) -// } - -// func (r *ReportRepo) GetTotalCashBacksInRange(ctx context.Context, from, to time.Time) (float64, error) { -// params := dbgen.GetTotalCashBacksInRangeParams{ -// From: ToPgTimestamp(from), -// To: ToPgTimestamp(to), -// } -// value, err := r.store.queries.GetTotalCashBacksInRange(ctx, params) -// if err != nil { -// return 0, err -// } -// return parseFloat(value) -// } - -// func (r *ReportRepo) GetTotalCashMadeInRange(ctx context.Context, from, to time.Time) (float64, error) { -// params := dbgen.GetTotalCashMadeInRangeParams{ -// From: ToPgTimestamp(from), -// To: ToPgTimestamp(to), -// } -// value, err := r.store.queries.GetTotalCashMadeInRange(ctx, params) -// if err != nil { -// return 0, err -// } -// return parseFloat(value) -// } - -// func (r *ReportRepo) GetTotalCashOutInRange(ctx context.Context, from, to time.Time) (float64, error) { -// params := dbgen.GetTotalCashOutInRangeParams{ -// From: ToPgTimestamp(from), -// To: ToPgTimestamp(to), -// } -// value, err := r.store.queries.GetTotalCashOutInRange(ctx, params) -// if err != nil { -// return 0, err -// } -// return parseFloat(value) -// } - -// func (r *ReportRepo) GetWalletTransactionsInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetWalletTransactionsInRangeRow, error) { -// params := dbgen.GetWalletTransactionsInRangeParams{ -// CreatedAt: ToPgTimestamp(from), -// CreatedAt_2: ToPgTimestamp(to), -// } -// return r.store.queries.GetWalletTransactionsInRange(ctx, params) -// } - -// func (r *ReportRepo) GetAllTicketsInRange(ctx context.Context, from, to time.Time) (dbgen.GetAllTicketsInRangeRow, error) { -// params := dbgen.GetAllTicketsInRangeParams{ -// CreatedAt: ToPgTimestamp(from), -// CreatedAt_2: ToPgTimestamp(to), -// } -// return r.store.queries.GetAllTicketsInRange(ctx, params) -// } - -// func (r *ReportRepo) GetVirtualGameSummaryInRange(ctx context.Context, from, to time.Time) ([]dbgen.GetVirtualGameSummaryInRangeRow, error) { -// params := dbgen.GetVirtualGameSummaryInRangeParams{ -// CreatedAt: ToPgTimestamptz(from), -// CreatedAt_2: ToPgTimestamptz(to), -// } -// return r.store.queries.GetVirtualGameSummaryInRange(ctx, params) -// } - -// func ToPgTimestamp(t time.Time) pgtype.Timestamp { -// return pgtype.Timestamp{Time: t, Valid: true} -// } - -// func ToPgTimestamptz(t time.Time) pgtype.Timestamptz { -// return pgtype.Timestamptz{Time: t, Valid: true} -// } - -// func parseFloat(value interface{}) (float64, error) { -// switch v := value.(type) { -// case float64: -// return v, nil -// case int64: -// return float64(v), nil -// case pgtype.Numeric: -// if !v.Valid { -// return 0, nil -// } -// f, err := v.Float64Value() -// if err != nil { -// return 0, fmt.Errorf("failed to convert pgtype.Numeric to float64: %w", err) -// } -// return f.Float64, nil -// case nil: -// return 0, nil -// default: -// return 0, fmt.Errorf("unexpected type %T for value: %+v", v, v) -// } -// } - -// func (r *ReportRepo) GetCompanyWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetCompanyWiseReportRow, error) { +// func (s *Store) GetCompanyWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetCompanyWiseReportRow, error) { // params := dbgen.GetCompanyWiseReportParams{ // From: ToPgTimestamp(from), // To: ToPgTimestamp(to), // } -// return r.store.queries.GetCompanyWiseReport(ctx, params) +// return s.queries.GetCompanyWiseReport(ctx, params) // } -// func (r *ReportRepo) GetBranchWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetBranchWiseReportRow, error) { +// func (s *Store) GetBranchWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetBranchWiseReportRow, error) { // params := dbgen.GetBranchWiseReportParams{ // From: ToPgTimestamp(from), // To: ToPgTimestamp(to), // } -// return r.store.queries.GetBranchWiseReport(ctx, params) +// return s.queries.GetBranchWiseReport(ctx, params) // } diff --git a/internal/repository/otp.go b/internal/repository/otp.go index 29c3f81..93ceefa 100644 --- a/internal/repository/otp.go +++ b/internal/repository/otp.go @@ -7,9 +7,13 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) +// Interface for creating new otp store +func NewOTPStore(s *Store) ports.OtpStore { return s } + func (s *Store) CreateOtp(ctx context.Context, otp domain.Otp) error { return s.queries.CreateOtp(ctx, dbgen.CreateOtpParams{ SentTo: otp.SentTo, diff --git a/internal/repository/raffel.go b/internal/repository/raffle.go similarity index 67% rename from internal/repository/raffel.go rename to internal/repository/raffle.go index 298ed9e..01e495e 100644 --- a/internal/repository/raffel.go +++ b/internal/repository/raffle.go @@ -5,74 +5,20 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) -func convertRaffleOutcome(raffle dbgen.Raffle) domain.Raffle { - return domain.Raffle{ - ID: raffle.ID, - CompanyID: raffle.CompanyID, - Name: raffle.Name, - CreatedAt: raffle.CreatedAt.Time, - ExpiresAt: raffle.ExpiresAt.Time, - TicketLimit: raffle.TicketLimit, - Type: raffle.Type, - Status: raffle.Status, - } -} - -func convertRaffleTicketOutcome(raffle dbgen.RaffleTicket) domain.RaffleTicket { - return domain.RaffleTicket{ - ID: raffle.ID, - RaffleID: raffle.RaffleID, - UserID: raffle.UserID, - IsActive: raffle.IsActive.Bool, - } -} - -func convertJoinedRaffleTicketOutcome(raffle dbgen.GetUserRaffleTicketsRow) domain.RaffleTicketRes { - return domain.RaffleTicketRes{ - TicketID: raffle.TicketID, - UserID: raffle.UserID, - Name: raffle.Name, - Type: raffle.Type, - ExpiresAt: raffle.ExpiresAt.Time, - Status: raffle.Status, - } -} - -func convertCreateRaffle(raffle domain.CreateRaffle) dbgen.CreateRaffleParams { - return dbgen.CreateRaffleParams{ - CompanyID: raffle.CompanyID, - Name: raffle.Name, - ExpiresAt: pgtype.Timestamp{ - Time: *raffle.ExpiresAt, - Valid: true, - }, - TicketLimit: raffle.TicketLimit, - Type: raffle.Type, - } -} - -func convertRaffleStanding(raffleStanding dbgen.GetRaffleStandingRow) domain.RaffleStanding { - return domain.RaffleStanding{ - UserID: raffleStanding.UserID, - RaffleID: raffleStanding.RaffleID, - FirstName: raffleStanding.FirstName, - LastName: raffleStanding.LastName, - PhoneNumber: raffleStanding.PhoneNumber.String, - Email: raffleStanding.Email.String, - TicketCount: raffleStanding.TicketCount, - } -} +// Interface for creating new raffle store +func NewRaffleStore(s *Store) ports.RaffleStore { return s } func (s *Store) CreateRaffle(ctx context.Context, raffle domain.CreateRaffle) (domain.Raffle, error) { - raffleRes, err := s.queries.CreateRaffle(ctx, convertCreateRaffle(raffle)) + raffleRes, err := s.queries.CreateRaffle(ctx, domain.ConvertCreateRaffle(raffle)) if err != nil { return domain.Raffle{}, err } - return convertRaffleOutcome(raffleRes), nil + return domain.ConvertRaffleOutcome(raffleRes), nil } func (s *Store) DeleteRaffle(ctx context.Context, raffleID int32) (domain.Raffle, error) { @@ -81,7 +27,7 @@ func (s *Store) DeleteRaffle(ctx context.Context, raffleID int32) (domain.Raffle return domain.Raffle{}, err } - return convertRaffleOutcome(raffleRes), nil + return domain.ConvertRaffleOutcome(raffleRes), nil } func (s *Store) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]dbgen.Raffle, error) { @@ -102,7 +48,7 @@ func (s *Store) CreateRaffleTicket(ctx context.Context, raffleTicketParams domai return domain.RaffleTicket{}, err } - return convertRaffleTicketOutcome(raffleTicket), nil + return domain.ConvertRaffleTicketOutcome(raffleTicket), nil } func (s *Store) GetUserRaffleTickets(ctx context.Context, userID int32) ([]domain.RaffleTicketRes, error) { @@ -113,7 +59,7 @@ func (s *Store) GetUserRaffleTickets(ctx context.Context, userID int32) ([]domai res := []domain.RaffleTicketRes{} for _, raffle := range raffleTickets { - res = append(res, convertJoinedRaffleTicketOutcome(raffle)) + res = append(res, domain.ConvertJoinedRaffleTicketOutcome(raffle)) } return res, nil @@ -152,7 +98,7 @@ func (s *Store) GetRaffleStanding(ctx context.Context, raffleID, limit int32) ([ res := []domain.RaffleStanding{} for _, standing := range raffleStanding { - res = append(res, convertRaffleStanding(standing)) + res = append(res, domain.ConvertRaffleStanding(standing)) } return res, nil diff --git a/internal/repository/referal.go b/internal/repository/referal.go index cda22e2..7fd7a56 100644 --- a/internal/repository/referal.go +++ b/internal/repository/referal.go @@ -5,30 +5,14 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) -type ReferralRepository interface { - CreateReferralCode(ctx context.Context, referralCode domain.CreateReferralCode) (domain.ReferralCode, error) - CreateUserReferral(ctx context.Context, referral domain.CreateUserReferrals) (domain.UserReferral, error) - GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error) - GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error) - UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error - GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) - GetUserReferral(ctx context.Context, referredID int64) (domain.UserReferral, error) - GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) - GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) -} +// Interface for creating new referral store +func NewReferralStore(s *Store) ports.ReferralStore { return s } -type ReferralRepo struct { - store *Store -} - -func NewReferralRepository(store *Store) ReferralRepository { - return &ReferralRepo{store: store} -} - -func (r *ReferralRepo) CreateReferralCode(ctx context.Context, referralCode domain.CreateReferralCode) (domain.ReferralCode, error) { - newReferralCode, err := r.store.queries.CreateReferralCode(ctx, domain.ConvertCreateReferralCode(referralCode)) +func (s *Store) CreateReferralCode(ctx context.Context, referralCode domain.CreateReferralCode) (domain.ReferralCode, error) { + newReferralCode, err := s.queries.CreateReferralCode(ctx, domain.ConvertCreateReferralCode(referralCode)) if err != nil { return domain.ReferralCode{}, err @@ -36,8 +20,8 @@ func (r *ReferralRepo) CreateReferralCode(ctx context.Context, referralCode doma return domain.ConvertDBReferralCode(newReferralCode), nil } -func (r *ReferralRepo) CreateUserReferral(ctx context.Context, referral domain.CreateUserReferrals) (domain.UserReferral, error) { - newReferral, err := r.store.queries.CreateUserReferral(ctx, domain.ConvertCreateUserReferral(referral)) +func (s *Store) CreateUserReferral(ctx context.Context, referral domain.CreateUserReferrals) (domain.UserReferral, error) { + newReferral, err := s.queries.CreateUserReferral(ctx, domain.ConvertCreateUserReferral(referral)) if err != nil { return domain.UserReferral{}, err @@ -46,8 +30,8 @@ func (r *ReferralRepo) CreateUserReferral(ctx context.Context, referral domain.C return domain.ConvertDBUserReferral(newReferral), nil } -func (r *ReferralRepo) GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error) { - codes, err := r.store.queries.GetReferralCodeByUser(ctx, userID) +func (s *Store) GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error) { + codes, err := s.queries.GetReferralCodeByUser(ctx, userID) if err != nil { return nil, err @@ -56,8 +40,8 @@ func (r *ReferralRepo) GetReferralCodesByUser(ctx context.Context, userID int64) return domain.ConvertDBReferralCodes(codes), nil } -func (r *ReferralRepo) GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error) { - referralCode, err := r.store.queries.GetReferralCode(ctx, code) +func (s *Store) GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error) { + referralCode, err := s.queries.GetReferralCode(ctx, code) if err != nil { return domain.ReferralCode{}, err @@ -66,8 +50,8 @@ func (r *ReferralRepo) GetReferralCode(ctx context.Context, code string) (domain return domain.ConvertDBReferralCode(referralCode), nil } -func (r *ReferralRepo) UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error { - err := r.store.queries.UpdateReferralCode(ctx, domain.ConvertUpdateReferralCode(referral)) +func (s *Store) UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error { + err := s.queries.UpdateReferralCode(ctx, domain.ConvertUpdateReferralCode(referral)) if err != nil { return err @@ -76,8 +60,8 @@ func (r *ReferralRepo) UpdateReferralCode(ctx context.Context, referral domain.U return nil } -func (r *ReferralRepo) GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) { - stats, err := r.store.queries.GetReferralStats(ctx, dbgen.GetReferralStatsParams{ +func (s *Store) GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) { + stats, err := s.queries.GetReferralStats(ctx, dbgen.GetReferralStatsParams{ ReferrerID: userID, CompanyID: companyID, }) @@ -88,16 +72,16 @@ func (r *ReferralRepo) GetReferralStats(ctx context.Context, userID int64, compa return domain.ConvertDBReferralStats(stats), nil } -func (r *ReferralRepo) GetUserReferral(ctx context.Context, referredID int64) (domain.UserReferral, error) { - dbReferral, err := r.store.queries.GetUserReferral(ctx, referredID) +func (s *Store) GetUserReferral(ctx context.Context, referredID int64) (domain.UserReferral, error) { + dbReferral, err := s.queries.GetUserReferral(ctx, referredID) if err != nil { return domain.UserReferral{}, err } return domain.ConvertDBUserReferral(dbReferral), nil } -func (r *ReferralRepo) GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) { - dbReferrals, err := r.store.queries.GetUserReferralsByCode(ctx, code) +func (s *Store) GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) { + dbReferrals, err := s.queries.GetUserReferralsByCode(ctx, code) if err != nil { return nil, err @@ -106,80 +90,10 @@ func (r *ReferralRepo) GetUserReferralsByCode(ctx context.Context, code string) return domain.ConvertDBUserReferrals(dbReferrals), nil } -func (r *ReferralRepo) GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) { - count, err := r.store.queries.GetUserReferralsCount(ctx, referrerID) +func (s *Store) GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) { + count, err := s.queries.GetUserReferralsCount(ctx, referrerID) if err != nil { return 0, err } return count, nil } - -// func (r *ReferralRepo) mapToDomainReferral(dbRef *dbgen.Referral) *domain.Referral { -// var referredID *int64 -// if dbRef.ReferredID.Valid { -// referredID = &dbRef.ReferredID.Int64 -// } - -// rewardAmount := 0.0 -// if dbRef.RewardAmount.Valid { -// if f8, err := dbRef.RewardAmount.Float64Value(); err == nil { -// rewardAmount = f8.Float64 -// } -// } - -// cashbackAmount := 0.0 -// if dbRef.CashbackAmount.Valid { -// if f8, err := dbRef.CashbackAmount.Float64Value(); err == nil { -// cashbackAmount = f8.Float64 -// } -// } - -// return &domain.Referral{ -// ID: dbRef.ID, -// ReferralCode: dbRef.ReferralCode, -// ReferrerID: dbRef.ReferrerID, -// ReferredID: referredID, -// Status: domain.ReferralStatus(dbRef.Status), -// RewardAmount: rewardAmount, -// CashbackAmount: cashbackAmount, -// CreatedAt: dbRef.CreatedAt.Time, -// UpdatedAt: dbRef.UpdatedAt.Time, -// ExpiresAt: dbRef.ExpiresAt.Time, -// } -// } - -// func (r *ReferralRepo) mapToDomainSettings(dbSettings *dbgen.ReferralSetting) *domain.ReferralSettings { -// rewardAmount := 0.0 -// if dbSettings.ReferralRewardAmount.Valid { -// if f8, err := dbSettings.ReferralRewardAmount.Float64Value(); err == nil { -// rewardAmount = f8.Float64 -// } -// } - -// cashbackPercentage := 0.0 -// if dbSettings.CashbackPercentage.Valid { -// if f8, err := dbSettings.CashbackPercentage.Float64Value(); err == nil { -// cashbackPercentage = f8.Float64 -// } -// } - -// betReferralBonusPercentage := 0.0 -// if dbSettings.BetReferralBonusPercentage.Valid { -// if f8, err := dbSettings.BetReferralBonusPercentage.Float64Value(); err == nil { -// betReferralBonusPercentage = f8.Float64 -// } -// } - -// return &domain.ReferralSettings{ -// ID: dbSettings.ID, -// ReferralRewardAmount: rewardAmount, -// CashbackPercentage: cashbackPercentage, -// BetReferralBonusPercentage: betReferralBonusPercentage, // New field -// MaxReferrals: dbSettings.MaxReferrals, -// ExpiresAfterDays: dbSettings.ExpiresAfterDays, -// UpdatedBy: dbSettings.UpdatedBy, -// CreatedAt: dbSettings.CreatedAt.Time, -// UpdatedAt: dbSettings.UpdatedAt.Time, -// Version: dbSettings.Version, -// } -// } diff --git a/internal/repository/report.go b/internal/repository/report.go index 33a6165..b3f7f56 100644 --- a/internal/repository/report.go +++ b/internal/repository/report.go @@ -6,11 +6,14 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) -func (s *Store) CreateReportRequest(ctx context.Context, report domain.CreateReportRequest) (domain.ReportRequest, error) { +// Interface for ReportStore +func NewReportStore(s *Store) ports.ReportStore { return s } +func (s *Store) CreateReportRequest(ctx context.Context, report domain.CreateReportRequest) (domain.ReportRequest, error) { reportMetadata, err := report.Metadata.ToPG() if err != nil { return domain.ReportRequest{}, err diff --git a/internal/repository/result.go b/internal/repository/result.go index 2dda4bd..b11ea6a 100644 --- a/internal/repository/result.go +++ b/internal/repository/result.go @@ -5,9 +5,14 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) +// Interface for creating new result log store +func NewResultLogStore(s *Store) ports.ResultLogStore { return s } + + func (s *Store) CreateResultLog(ctx context.Context, result domain.CreateResultLog) (domain.ResultLog, error) { dbResult, err := s.queries.CreateResultLog(ctx, domain.ConvertCreateResultLog(result)) if err != nil { diff --git a/internal/repository/settings.go b/internal/repository/settings.go index f456bff..30371da 100644 --- a/internal/repository/settings.go +++ b/internal/repository/settings.go @@ -5,9 +5,13 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "go.uber.org/zap" ) +// Interface for creating new setting store +func NewSettingStore(s *Store) ports.SettingStore { return s } + func (s *Store) GetGlobalSettingList(ctx context.Context) (domain.SettingList, error) { settings, err := s.queries.GetGlobalSettings(ctx) if err != nil { diff --git a/internal/repository/shop_transaction.go b/internal/repository/shop_transaction.go index d3a2b9c..af22fd1 100644 --- a/internal/repository/shop_transaction.go +++ b/internal/repository/shop_transaction.go @@ -6,133 +6,20 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) -func convertDBShopTransaction(transaction dbgen.ShopTransaction) domain.ShopTransaction { - return domain.ShopTransaction{ - ID: transaction.ID, - Amount: domain.Currency(transaction.Amount), - BranchID: transaction.BranchID, - UserID: transaction.UserID, - Type: domain.ShopTransactionType(transaction.Type), - PaymentOption: domain.PaymentOption(transaction.PaymentOption), - FullName: transaction.FullName, - PhoneNumber: transaction.PhoneNumber, - BankCode: domain.ValidString{ - Value: transaction.BankCode.String, - Valid: transaction.BankCode.Valid, - }, - BeneficiaryName: domain.ValidString{ - Value: transaction.BeneficiaryName.String, - Valid: transaction.BeneficiaryName.Valid, - }, - AccountName: domain.ValidString{ - Value: transaction.AccountName.String, - Valid: transaction.AccountName.Valid, - }, - AccountNumber: domain.ValidString{ - Value: transaction.AccountName.String, - Valid: transaction.AccountNumber.Valid, - }, - ReferenceNumber: domain.ValidString{ - Value: transaction.ReferenceNumber.String, - Valid: transaction.ReferenceNumber.Valid, - }, - ApprovedBy: domain.ValidInt64{ - Value: transaction.ApprovedBy.Int64, - Valid: transaction.ApprovedBy.Valid, - }, - CreatedAt: transaction.CreatedAt.Time, - UpdatedAt: transaction.UpdatedAt.Time, - Verified: transaction.Verified, - CompanyID: transaction.CompanyID, - } -} - -func convertDBShopTransactionDetail(transaction dbgen.ShopTransactionDetail) domain.ShopTransactionDetail { - return domain.ShopTransactionDetail{ - ID: transaction.ID, - Amount: domain.Currency(transaction.Amount), - BranchID: transaction.BranchID, - UserID: transaction.UserID, - CreatorFirstName: transaction.CreatorFirstName.String, - CreatorLastName: transaction.CreatorLastName.String, - CreatorPhoneNumber: transaction.CreatorPhoneNumber.String, - Type: domain.ShopTransactionType(transaction.Type), - PaymentOption: domain.PaymentOption(transaction.PaymentOption), - FullName: transaction.FullName, - PhoneNumber: transaction.PhoneNumber, - BankCode: domain.ValidString{ - Value: transaction.BankCode.String, - Valid: transaction.BankCode.Valid, - }, - BeneficiaryName: domain.ValidString{ - Value: transaction.BeneficiaryName.String, - Valid: transaction.BeneficiaryName.Valid, - }, - AccountName: domain.ValidString{ - Value: transaction.AccountName.String, - Valid: transaction.AccountName.Valid, - }, - AccountNumber: domain.ValidString{ - Value: transaction.AccountName.String, - Valid: transaction.AccountNumber.Valid, - }, - ReferenceNumber: domain.ValidString{ - Value: transaction.ReferenceNumber.String, - Valid: transaction.ReferenceNumber.Valid, - }, - ApprovedBy: domain.ValidInt64{ - Value: transaction.ApprovedBy.Int64, - Valid: transaction.ApprovedBy.Valid, - }, - ApproverFirstName: domain.ValidString{ - Value: transaction.ApproverFirstName.String, - Valid: transaction.ApproverFirstName.Valid, - }, - ApproverLastName: domain.ValidString{ - Value: transaction.ApproverLastName.String, - Valid: transaction.ApproverLastName.Valid, - }, - ApproverPhoneNumber: domain.ValidString{ - Value: transaction.ApproverPhoneNumber.String, - Valid: transaction.ApproverPhoneNumber.Valid, - }, - CreatedAt: transaction.CreatedAt.Time, - UpdatedAt: transaction.UpdatedAt.Time, - Verified: transaction.Verified, - CompanyID: transaction.CompanyID, - BranchName: transaction.BranchName.String, - BranchLocation: transaction.BranchLocation.String, - } -} - -func convertCreateShopTransaction(transaction domain.CreateShopTransaction) dbgen.CreateShopTransactionParams { - return dbgen.CreateShopTransactionParams{ - Amount: int64(transaction.Amount), - BranchID: transaction.BranchID, - UserID: transaction.UserID, - Type: int64(transaction.Type), - PaymentOption: int64(transaction.PaymentOption), - FullName: transaction.FullName, - PhoneNumber: transaction.PhoneNumber, - CompanyID: transaction.CompanyID, - BankCode: pgtype.Text{String: transaction.BankCode.Value, Valid: transaction.BankCode.Valid}, - BeneficiaryName: pgtype.Text{String: transaction.BeneficiaryName.Value, Valid: transaction.BeneficiaryName.Valid}, - AccountName: pgtype.Text{String: transaction.AccountName.Value, Valid: transaction.AccountName.Valid}, - AccountNumber: pgtype.Text{String: transaction.AccountNumber.Value, Valid: transaction.AccountNumber.Valid}, - ReferenceNumber: pgtype.Text{String: transaction.ReferenceNumber.Value, Valid: transaction.ReferenceNumber.Valid}, - } -} +// Interface for creating new shop transaction store +func NewTransactionStore(s *Store) ports.TransactionStore { return s } func (s *Store) CreateShopTransaction(ctx context.Context, transaction domain.CreateShopTransaction) (domain.ShopTransaction, error) { - newTransaction, err := s.queries.CreateShopTransaction(ctx, convertCreateShopTransaction(transaction)) + newTransaction, err := s.queries.CreateShopTransaction(ctx, domain.ConvertCreateShopTransaction(transaction)) if err != nil { return domain.ShopTransaction{}, err } - return convertDBShopTransaction(newTransaction), err + return domain.ConvertDBShopTransaction(newTransaction), err } func (s *Store) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransactionDetail, error) { @@ -141,35 +28,17 @@ func (s *Store) GetShopTransactionByID(ctx context.Context, id int64) (domain.Sh return domain.ShopTransactionDetail{}, err } - return convertDBShopTransactionDetail(transaction), nil + return domain.ConvertDBShopTransactionDetail(transaction), nil } func (s *Store) GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransactionDetail, error) { transaction, err := s.queries.GetAllShopTransactions(ctx, dbgen.GetAllShopTransactionsParams{ - BranchID: pgtype.Int8{ - Int64: filter.BranchID.Value, - Valid: filter.BranchID.Valid, - }, - CompanyID: pgtype.Int8{ - Int64: filter.CompanyID.Value, - Valid: filter.CompanyID.Valid, - }, - UserID: pgtype.Int8{ - Int64: filter.UserID.Value, - Valid: filter.UserID.Valid, - }, - Query: pgtype.Text{ - String: filter.Query.Value, - Valid: filter.Query.Valid, - }, - CreatedBefore: pgtype.Timestamp{ - Time: filter.CreatedBefore.Value, - Valid: filter.CreatedBefore.Valid, - }, - CreatedAfter: pgtype.Timestamp{ - Time: filter.CreatedAfter.Value, - Valid: filter.CreatedAfter.Valid, - }, + BranchID: filter.BranchID.ToPG(), + CompanyID: filter.CompanyID.ToPG(), + UserID: filter.UserID.ToPG(), + Query: filter.Query.ToPG(), + CreatedBefore: filter.CreatedBefore.ToPG(), + CreatedAfter: filter.CreatedAfter.ToPG(), }) if err != nil { @@ -178,7 +47,7 @@ func (s *Store) GetAllShopTransactions(ctx context.Context, filter domain.ShopTr var result []domain.ShopTransactionDetail = make([]domain.ShopTransactionDetail, 0, len(transaction)) for _, tAction := range transaction { - result = append(result, convertDBShopTransactionDetail(tAction)) + result = append(result, domain.ConvertDBShopTransactionDetail(tAction)) } return result, nil } @@ -191,7 +60,7 @@ func (s *Store) GetShopTransactionByBranch(ctx context.Context, id int64) ([]dom var result []domain.ShopTransactionDetail = make([]domain.ShopTransactionDetail, 0, len(transaction)) for _, ticket := range transaction { - result = append(result, convertDBShopTransactionDetail(ticket)) + result = append(result, domain.ConvertDBShopTransactionDetail(ticket)) } return result, nil } @@ -208,7 +77,6 @@ func (s *Store) UpdateShopTransactionVerified(ctx context.Context, id int64, ver return err } - // GetTransactionTotals returns total deposits and withdrawals func (s *Store) GetTransactionTotals(ctx context.Context, filter domain.ReportFilter) (deposits, withdrawals domain.Currency, err error) { query := `SELECT diff --git a/internal/repository/ticket.go b/internal/repository/ticket.go index 1e2ef36..24afe8c 100644 --- a/internal/repository/ticket.go +++ b/internal/repository/ticket.go @@ -5,85 +5,19 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) -func convertDBTicket(ticket dbgen.Ticket) domain.Ticket { - return domain.Ticket{ - ID: ticket.ID, - Amount: domain.Currency(ticket.Amount), - TotalOdds: ticket.TotalOdds, - CompanyID: ticket.CompanyID, - } -} - -func convertDBTicketOutcomes(ticket dbgen.TicketWithOutcome) domain.GetTicket { - - var outcomes []domain.TicketOutcome = make([]domain.TicketOutcome, 0, len(ticket.Outcomes)) - - for _, outcome := range ticket.Outcomes { - outcomes = append(outcomes, domain.TicketOutcome{ - ID: outcome.ID, - TicketID: outcome.TicketID, - EventID: outcome.EventID, - OddID: outcome.OddID, - HomeTeamName: outcome.HomeTeamName, - AwayTeamName: outcome.AwayTeamName, - MarketID: outcome.MarketID, - MarketName: outcome.MarketName, - Odd: outcome.Odd, - OddName: outcome.OddName, - OddHeader: outcome.OddHeader, - OddHandicap: outcome.OddHandicap, - Status: domain.OutcomeStatus(outcome.Status), - Expires: outcome.Expires.Time, - }) - } - return domain.GetTicket{ - ID: ticket.ID, - CompanyID: ticket.CompanyID, - Amount: domain.Currency(ticket.Amount), - TotalOdds: ticket.TotalOdds, - Outcomes: outcomes, - } -} - -func convertDBCreateTicketOutcome(ticketOutcome domain.CreateTicketOutcome) dbgen.CreateTicketOutcomeParams { - return dbgen.CreateTicketOutcomeParams{ - TicketID: ticketOutcome.TicketID, - EventID: ticketOutcome.EventID, - OddID: ticketOutcome.OddID, - HomeTeamName: ticketOutcome.HomeTeamName, - AwayTeamName: ticketOutcome.AwayTeamName, - MarketID: ticketOutcome.MarketID, - MarketName: ticketOutcome.MarketName, - Odd: ticketOutcome.Odd, - OddName: ticketOutcome.OddName, - OddHeader: ticketOutcome.OddHeader, - OddHandicap: ticketOutcome.OddHandicap, - Expires: pgtype.Timestamp{ - Time: ticketOutcome.Expires, - Valid: true, - }, - } -} - -func convertCreateTicket(ticket domain.CreateTicket) dbgen.CreateTicketParams { - return dbgen.CreateTicketParams{ - Amount: int64(ticket.Amount), - TotalOdds: ticket.TotalOdds, - Ip: ticket.IP, - CompanyID: ticket.CompanyID, - } -} +// Interface for creating new user store +func NewTicketStore(s *Store) ports.TicketStore { return s } func (s *Store) CreateTicket(ctx context.Context, ticket domain.CreateTicket) (domain.Ticket, error) { - newTicket, err := s.queries.CreateTicket(ctx, convertCreateTicket(ticket)) + newTicket, err := s.queries.CreateTicket(ctx, domain.ConvertCreateTicket(ticket)) if err != nil { return domain.Ticket{}, err } - return convertDBTicket(newTicket), err + return domain.ConvertDBTicket(newTicket), err } @@ -91,7 +25,7 @@ func (s *Store) CreateTicketOutcome(ctx context.Context, outcomes []domain.Creat var dbParams []dbgen.CreateTicketOutcomeParams = make([]dbgen.CreateTicketOutcomeParams, 0, len(outcomes)) for _, outcome := range outcomes { - dbParams = append(dbParams, convertDBCreateTicketOutcome(outcome)) + dbParams = append(dbParams, domain.ConvertDBCreateTicketOutcome(outcome)) } rows, err := s.queries.CreateTicketOutcome(ctx, dbParams) @@ -110,7 +44,7 @@ func (s *Store) GetTicketByID(ctx context.Context, id int64) (domain.GetTicket, return domain.GetTicket{}, err } - return convertDBTicketOutcomes(ticket), nil + return domain.ConvertDBTicketOutcomes(ticket), nil } func (s *Store) GetAllTickets(ctx context.Context, filter domain.TicketFilter) ([]domain.GetTicket, error) { @@ -121,7 +55,7 @@ func (s *Store) GetAllTickets(ctx context.Context, filter domain.TicketFilter) ( var result []domain.GetTicket = make([]domain.GetTicket, 0, len(tickets)) for _, ticket := range tickets { - result = append(result, convertDBTicketOutcomes(ticket)) + result = append(result, domain.ConvertDBTicketOutcomes(ticket)) } return result, nil diff --git a/internal/repository/transfer.go b/internal/repository/transfer.go index 3b5e5a9..be23e52 100644 --- a/internal/repository/transfer.go +++ b/internal/repository/transfer.go @@ -5,104 +5,19 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) -func convertDBTransferDetail(transfer dbgen.WalletTransferDetail) domain.TransferDetail { - return domain.TransferDetail{ - ID: transfer.ID, - Amount: domain.Currency(transfer.Amount.Int64), - Type: domain.TransferType(transfer.Type.String), - Verified: transfer.Verified.Bool, - Message: transfer.Message, - ReceiverWalletID: domain.ValidInt64{ - Value: transfer.ReceiverWalletID.Int64, - Valid: transfer.ReceiverWalletID.Valid, - }, - SenderWalletID: domain.ValidInt64{ - Value: transfer.SenderWalletID.Int64, - Valid: transfer.SenderWalletID.Valid, - }, - DepositorID: domain.ValidInt64{ - Value: transfer.CashierID.Int64, - Valid: transfer.CashierID.Valid, - }, - DepositorFirstName: transfer.FirstName.String, - DepositorLastName: transfer.LastName.String, - DepositorPhoneNumber: transfer.PhoneNumber.String, - PaymentMethod: domain.PaymentMethod(transfer.PaymentMethod.String), - ReferenceNumber: transfer.ReferenceNumber, - SessionID: transfer.SessionID.String, - Status: transfer.Status.String, - CreatedAt: transfer.CreatedAt.Time, - UpdatedAt: transfer.UpdatedAt.Time, - } -} -func convertDBTransfer(transfer dbgen.WalletTransfer) domain.Transfer { - return domain.Transfer{ - ID: transfer.ID, - Amount: domain.Currency(transfer.Amount.Int64), - Type: domain.TransferType(transfer.Type.String), - Verified: transfer.Verified.Bool, - Message: transfer.Message, - ReceiverWalletID: domain.ValidInt64{ - Value: transfer.ReceiverWalletID.Int64, - Valid: transfer.ReceiverWalletID.Valid, - }, - SenderWalletID: domain.ValidInt64{ - Value: transfer.SenderWalletID.Int64, - Valid: transfer.SenderWalletID.Valid, - }, - DepositorID: domain.ValidInt64{ - Value: transfer.CashierID.Int64, - Valid: transfer.CashierID.Valid, - }, - PaymentMethod: domain.PaymentMethod(transfer.PaymentMethod.String), - ReferenceNumber: transfer.ReferenceNumber, - SessionID: transfer.SessionID.String, - Status: transfer.Status.String, - CreatedAt: transfer.CreatedAt.Time, - UpdatedAt: transfer.UpdatedAt.Time, - } -} - -func convertCreateTransfer(transfer domain.CreateTransfer) dbgen.CreateTransferParams { - return dbgen.CreateTransferParams{ - Message: transfer.Message, - Amount: pgtype.Int8{Int64: int64(transfer.Amount), Valid: true}, - Type: pgtype.Text{String: string(transfer.Type), Valid: true}, - ReceiverWalletID: pgtype.Int8{ - Int64: transfer.ReceiverWalletID.Value, - Valid: transfer.ReceiverWalletID.Valid, - }, - SenderWalletID: pgtype.Int8{ - Int64: transfer.SenderWalletID.Value, - Valid: transfer.SenderWalletID.Valid, - }, - CashierID: pgtype.Int8{ - Int64: transfer.CashierID.Value, - Valid: transfer.CashierID.Valid, - }, - ReferenceNumber: string(transfer.ReferenceNumber), - SessionID: pgtype.Text{ - String: transfer.SessionID, - Valid: true, - }, - - PaymentMethod: pgtype.Text{String: string(transfer.PaymentMethod), Valid: true}, - Verified: pgtype.Bool{ - Bool: transfer.Verified, - Valid: true, - }, - } -} +// Interface for creating new transfer store +func NewTransferStore(s *Store) ports.TransferStore { return s } func (s *Store) CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) { - newTransfer, err := s.queries.CreateTransfer(ctx, convertCreateTransfer(transfer)) + newTransfer, err := s.queries.CreateTransfer(ctx, domain.ConvertCreateTransfer(transfer)) if err != nil { return domain.Transfer{}, err } - return convertDBTransfer(newTransfer), nil + return domain.ConvertDBTransfer(newTransfer), nil } func (s *Store) GetAllTransfers(ctx context.Context) ([]domain.TransferDetail, error) { @@ -113,7 +28,7 @@ func (s *Store) GetAllTransfers(ctx context.Context) ([]domain.TransferDetail, e var result []domain.TransferDetail = make([]domain.TransferDetail, 0, len(transfers)) for _, transfer := range transfers { - result = append(result, convertDBTransferDetail(transfer)) + result = append(result, domain.ConvertDBTransferDetail(transfer)) } return result, nil } @@ -127,7 +42,7 @@ func (s *Store) GetTransfersByWallet(ctx context.Context, walletID int64) ([]dom var result []domain.TransferDetail = make([]domain.TransferDetail, 0, len(transfers)) for _, transfer := range transfers { - result = append(result, convertDBTransferDetail(transfer)) + result = append(result, domain.ConvertDBTransferDetail(transfer)) } return result, nil } @@ -137,7 +52,7 @@ func (s *Store) GetTransferByReference(ctx context.Context, reference string) (d if err != nil { return domain.TransferDetail{}, nil } - return convertDBTransferDetail(transfer), nil + return domain.ConvertDBTransferDetail(transfer), nil } func (s *Store) GetTransferByID(ctx context.Context, id int64) (domain.TransferDetail, error) { @@ -145,7 +60,7 @@ func (s *Store) GetTransferByID(ctx context.Context, id int64) (domain.TransferD if err != nil { return domain.TransferDetail{}, nil } - return convertDBTransferDetail(transfer), nil + return domain.ConvertDBTransferDetail(transfer), nil } func (s *Store) GetTransferStats(ctx context.Context, walletID int64) (domain.TransferStats, error) { diff --git a/internal/repository/user.go b/internal/repository/user.go index 40f620f..e3ee3a1 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -9,10 +9,15 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" ) +// Interface for creating new user store +func NewUserStore(s *Store) ports.UserStore { return s } + func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int64, is_company bool) (domain.User, error) { err := s.queries.MarkOtpAsUsed(ctx, dbgen.MarkOtpAsUsedParams{ ID: usedOtpId, @@ -529,6 +534,46 @@ func (s *Store) GetAdminByCompanyID(ctx context.Context, companyID int64) (domai }, nil } +func (s *Store) GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error) { + user, err := s.queries.GetUserByEmailPhone(ctx, dbgen.GetUserByEmailPhoneParams{ + Email: pgtype.Text{ + String: email, + Valid: true, + }, + PhoneNumber: pgtype.Text{ + String: phone, + Valid: true, + }, + CompanyID: companyID.ToPG(), + }) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return domain.User{}, authentication.ErrUserNotFound + } + return domain.User{}, err + } + return domain.User{ + ID: user.ID, + FirstName: user.FirstName, + LastName: user.LastName, + Email: user.Email.String, + PhoneNumber: user.PhoneNumber.String, + Password: user.Password, + Role: domain.Role(user.Role), + EmailVerified: user.EmailVerified, + PhoneVerified: user.PhoneVerified, + CreatedAt: user.CreatedAt.Time, + UpdatedAt: user.UpdatedAt.Time, + SuspendedAt: user.SuspendedAt.Time, + Suspended: user.Suspended, + CompanyID: domain.ValidInt64{ + Value: user.CompanyID.Int64, + Valid: user.CompanyID.Valid, + }, + }, nil + +} + // GetCustomerCounts returns total and active customer counts func (s *Store) GetCustomerCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) { query := `SELECT diff --git a/internal/repository/wallet.go b/internal/repository/wallet.go index 02767ef..8e113e5 100644 --- a/internal/repository/wallet.go +++ b/internal/repository/wallet.go @@ -6,86 +6,31 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) -func convertDBWallet(wallet dbgen.Wallet) domain.Wallet { - return domain.Wallet{ - ID: wallet.ID, - Balance: domain.Currency(wallet.Balance), - IsWithdraw: wallet.IsWithdraw, - IsBettable: wallet.IsBettable, - IsTransferable: wallet.IsTransferable, - IsActive: wallet.IsActive, - UserID: wallet.UserID, - Type: domain.WalletType(wallet.Type), - UpdatedAt: wallet.UpdatedAt.Time, - CreatedAt: wallet.CreatedAt.Time, - } -} +// Interface for creating new wallet store +func NewWalletStore(s *Store) ports.WalletStore { return s } -func convertCreateWallet(wallet domain.CreateWallet) dbgen.CreateWalletParams { - return dbgen.CreateWalletParams{ - IsWithdraw: wallet.IsWithdraw, - IsBettable: wallet.IsBettable, - IsTransferable: wallet.IsTransferable, - UserID: wallet.UserID, - Type: string(wallet.Type), - } -} - -func convertDBCustomerWallet(customerWallet dbgen.CustomerWallet) domain.CustomerWallet { - return domain.CustomerWallet{ - ID: customerWallet.ID, - RegularID: customerWallet.RegularWalletID, - StaticID: customerWallet.StaticWalletID, - CustomerID: customerWallet.CustomerID, - } -} -func convertCreateCustomerWallet(customerWallet domain.CreateCustomerWallet) dbgen.CreateCustomerWalletParams { - return dbgen.CreateCustomerWalletParams{ - CustomerID: customerWallet.CustomerID, - RegularWalletID: customerWallet.RegularWalletID, - StaticWalletID: customerWallet.StaticWalletID, - } -} - -func convertDBGetCustomerWallet(customerWallet dbgen.CustomerWalletDetail) domain.GetCustomerWallet { - return domain.GetCustomerWallet{ - ID: customerWallet.ID, - RegularID: customerWallet.RegularID, - RegularBalance: domain.Currency(customerWallet.RegularBalance), - StaticID: customerWallet.StaticID, - StaticBalance: domain.Currency(customerWallet.StaticBalance), - CustomerID: customerWallet.CustomerID, - RegularIsActive: customerWallet.RegularIsActive, - StaticIsActive: customerWallet.StaticIsActive, - RegularUpdatedAt: customerWallet.RegularUpdatedAt.Time, - StaticUpdatedAt: customerWallet.StaticUpdatedAt.Time, - CreatedAt: customerWallet.CreatedAt.Time, - FirstName: customerWallet.FirstName, - LastName: customerWallet.LastName, - PhoneNumber: customerWallet.PhoneNumber.String, - } -} func (s *Store) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) { - newWallet, err := s.queries.CreateWallet(ctx, convertCreateWallet(wallet)) + newWallet, err := s.queries.CreateWallet(ctx, domain.ConvertCreateWallet(wallet)) if err != nil { if IsUniqueViolation(err) { return domain.Wallet{}, domain.ErrWalletIDDuplicate } return domain.Wallet{}, err } - return convertDBWallet(newWallet), nil + return domain.ConvertDBWallet(newWallet), nil } func (s *Store) CreateCustomerWallet(ctx context.Context, customerWallet domain.CreateCustomerWallet) (domain.CustomerWallet, error) { - newCustomerWallet, err := s.queries.CreateCustomerWallet(ctx, convertCreateCustomerWallet(customerWallet)) + newCustomerWallet, err := s.queries.CreateCustomerWallet(ctx, domain.ConvertCreateCustomerWallet(customerWallet)) if err != nil { return domain.CustomerWallet{}, err } - return convertDBCustomerWallet(newCustomerWallet), nil + return domain.ConvertDBCustomerWallet(newCustomerWallet), nil } func (s *Store) GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error) { @@ -94,7 +39,7 @@ func (s *Store) GetWalletByID(ctx context.Context, id int64) (domain.Wallet, err if err != nil { return domain.Wallet{}, err } - return convertDBWallet(wallet), nil + return domain.ConvertDBWallet(wallet), nil } func (s *Store) GetAllWallets(ctx context.Context) ([]domain.Wallet, error) { @@ -106,7 +51,7 @@ func (s *Store) GetAllWallets(ctx context.Context) ([]domain.Wallet, error) { var result []domain.Wallet = make([]domain.Wallet, 0, len(wallets)) for _, wallet := range wallets { - result = append(result, convertDBWallet(wallet)) + result = append(result, domain.ConvertDBWallet(wallet)) } return result, nil } @@ -120,7 +65,7 @@ func (s *Store) GetWalletsByUser(ctx context.Context, userID int64) ([]domain.Wa var result []domain.Wallet = make([]domain.Wallet, 0, len(wallets)) for _, wallet := range wallets { - result = append(result, convertDBWallet(wallet)) + result = append(result, domain.ConvertDBWallet(wallet)) } return result, nil } @@ -133,7 +78,7 @@ func (s *Store) GetAllCustomerWallets(ctx context.Context) ([]domain.GetCustomer var result []domain.GetCustomerWallet = make([]domain.GetCustomerWallet, 0, len(customerWallets)) for _, wallet := range customerWallets { - result = append(result, convertDBGetCustomerWallet(wallet)) + result = append(result, domain.ConvertDBGetCustomerWallet(wallet)) } return result, nil } @@ -144,7 +89,7 @@ func (s *Store) GetCustomerWallet(ctx context.Context, customerID int64) (domain if err != nil { return domain.GetCustomerWallet{}, err } - return convertDBGetCustomerWallet(customerWallet), nil + return domain.ConvertDBGetCustomerWallet(customerWallet), nil } func (s *Store) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error) { diff --git a/internal/services/arifpay/service.go b/internal/services/arifpay/service.go index a074d98..80e5c2e 100644 --- a/internal/services/arifpay/service.go +++ b/internal/services/arifpay/service.go @@ -11,18 +11,19 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" "github.com/google/uuid" ) type ArifpayService struct { cfg *config.Config - transferStore wallet.TransferStore + transferStore ports.TransferStore walletSvc *wallet.Service httpClient *http.Client } -func NewArifpayService(cfg *config.Config, transferStore wallet.TransferStore, walletSvc *wallet.Service, httpClient *http.Client) *ArifpayService { +func NewArifpayService(cfg *config.Config, transferStore ports.TransferStore, walletSvc *wallet.Service, httpClient *http.Client) *ArifpayService { return &ArifpayService{ cfg: cfg, transferStore: transferStore, diff --git a/internal/services/authentication/interface.go b/internal/services/authentication/interface.go new file mode 100644 index 0000000..d2cf202 --- /dev/null +++ b/internal/services/authentication/interface.go @@ -0,0 +1,17 @@ +package authentication + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type UserStore interface { +// GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error) +// } +// type TokenStore interface { +// CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error +// GetRefreshToken(ctx context.Context, token string) (domain.RefreshToken, error) +// GetRefreshTokenByUserID(ctx context.Context, id int64) (domain.RefreshToken, error) +// RevokeRefreshToken(ctx context.Context, token string) error +// } diff --git a/internal/services/authentication/service.go b/internal/services/authentication/service.go index 577e9da..37b78e2 100644 --- a/internal/services/authentication/service.go +++ b/internal/services/authentication/service.go @@ -1,5 +1,7 @@ package authentication +import "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + // type EmailPhone struct { // Email ValidString // PhoneNumber ValidString @@ -14,12 +16,12 @@ type Tokens struct { RefreshToken string } type Service struct { - userStore UserStore - tokenStore TokenStore + userStore ports.UserStore + tokenStore ports.TokenStore RefreshExpiry int } -func NewService(userStore UserStore, tokenStore TokenStore, RefreshExpiry int) *Service { +func NewService(userStore ports.UserStore, tokenStore ports.TokenStore, RefreshExpiry int) *Service { return &Service{ userStore: userStore, tokenStore: tokenStore, diff --git a/internal/services/bet/interface.go b/internal/services/bet/interface.go new file mode 100644 index 0000000..19a8d78 --- /dev/null +++ b/internal/services/bet/interface.go @@ -0,0 +1,53 @@ +package bet + +// import ( +// "context" +// "time" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type BetStore interface { +// CreateBet(ctx context.Context, bet domain.CreateBet) (domain.Bet, error) +// CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBetOutcome) (int64, error) +// CreateFlag(ctx context.Context, flag domain.CreateFlagReq) (domain.Flag, error) +// GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) +// GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, int64, error) +// GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error) +// GetBetByFastCode(ctx context.Context, fastcode string) (domain.GetBet, error) +// GetBetOutcomeViewByEventID(ctx context.Context, eventID int64, filter domain.BetOutcomeViewFilter) ([]domain.BetOutcomeViewRes, int64, error) +// GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) +// GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error) +// GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (int64, error) +// GetBetCountByUserID(ctx context.Context, userID int64, outcomesHash string) (int64, error) +// GetBetCountByOutcomesHash(ctx context.Context, outcomesHash string) (int64, error) +// UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error +// UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error +// UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error) +// UpdateBetOutcomeStatusByBetID(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error) +// UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) +// UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) +// BulkUpdateBetOutcomeStatusForOddIds(ctx context.Context, oddID []int64, status domain.OutcomeStatus) error +// GetBetSummary(ctx context.Context, filter domain.ReportFilter) ( +// totalStakes domain.Currency, +// totalBets int64, +// activeBets int64, +// totalWins int64, +// totalLosses int64, +// winBalance domain.Currency, +// err error, +// ) +// GetBetStats(ctx context.Context, filter domain.ReportFilter) ([]domain.BetStat, error) +// GetSportPopularity(ctx context.Context, filter domain.ReportFilter) (map[time.Time]string, error) +// GetMarketPopularity(ctx context.Context, filter domain.ReportFilter) (map[time.Time]string, error) +// GetExtremeValues(ctx context.Context, filter domain.ReportFilter) (map[time.Time]domain.ExtremeValues, error) +// GetCustomerBetActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.CustomerBetActivity, error) +// GetCustomerPreferences(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.CustomerPreferences, error) +// GetBranchBetActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.BranchBetActivity, error) +// GetSportBetActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.SportBetActivity, error) +// GetSportDetails(ctx context.Context, filter domain.ReportFilter) (map[string]string, error) +// GetSportMarketPopularity(ctx context.Context, filter domain.ReportFilter) (map[string]string, error) + +// GetBetsForCashback(ctx context.Context) ([]domain.GetBet, error) +// UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error +// } diff --git a/internal/services/bet/service.go b/internal/services/bet/service.go index 6165e1c..ac6c3ef 100644 --- a/internal/services/bet/service.go +++ b/internal/services/bet/service.go @@ -19,6 +19,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/company" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" @@ -53,7 +54,7 @@ var ( ) type Service struct { - betStore BetStore + betStore ports.BetStore eventSvc *event.Service prematchSvc odds.ServiceImpl walletSvc wallet.Service @@ -67,7 +68,7 @@ type Service struct { } func NewService( - betStore BetStore, + betStore ports.BetStore, eventSvc *event.Service, prematchSvc odds.ServiceImpl, walletSvc wallet.Service, diff --git a/internal/services/bonus/interface.go b/internal/services/bonus/interface.go new file mode 100644 index 0000000..e7f0dc1 --- /dev/null +++ b/internal/services/bonus/interface.go @@ -0,0 +1,17 @@ +package bonus + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type BonusStore interface { +// CreateUserBonus(ctx context.Context, bonus domain.CreateBonus) (domain.UserBonus, error) +// GetAllUserBonuses(ctx context.Context, filter domain.BonusFilter) ([]domain.UserBonus, error) +// GetBonusCount(ctx context.Context, filter domain.BonusFilter) (int64, error) +// GetBonusByID(ctx context.Context, bonusID int64) (domain.UserBonus, error) +// GetBonusStats(ctx context.Context, filter domain.BonusFilter) (domain.BonusStats, error) +// UpdateUserBonus(ctx context.Context, bonusID int64, IsClaimed bool) error +// DeleteUserBonus(ctx context.Context, bonusID int64) error +// } diff --git a/internal/services/bonus/service.go b/internal/services/bonus/service.go index 3daaf71..e30c009 100644 --- a/internal/services/bonus/service.go +++ b/internal/services/bonus/service.go @@ -8,6 +8,7 @@ import ( "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" @@ -15,14 +16,20 @@ import ( ) type Service struct { - bonusStore BonusStore + bonusStore ports.BonusStore walletSvc *wallet.Service settingSvc *settings.Service notificationSvc *notificationservice.Service mongoLogger *zap.Logger } -func NewService(bonusStore BonusStore, walletSvc *wallet.Service, settingSvc *settings.Service, notificationSvc *notificationservice.Service, mongoLogger *zap.Logger) *Service { +func NewService( + bonusStore ports.BonusStore, + walletSvc *wallet.Service, + settingSvc *settings.Service, + notificationSvc *notificationservice.Service, + mongoLogger *zap.Logger, + ) *Service { return &Service{ bonusStore: bonusStore, walletSvc: walletSvc, diff --git a/internal/services/branch/interface.go b/internal/services/branch/interface.go new file mode 100644 index 0000000..4a15e7e --- /dev/null +++ b/internal/services/branch/interface.go @@ -0,0 +1,34 @@ +package branch + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type BranchStore interface { +// CreateBranch(ctx context.Context, branch domain.CreateBranch) (domain.Branch, error) +// GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error) +// GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error) +// GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error) +// GetAllBranches(ctx context.Context, filter domain.BranchFilter) ([]domain.BranchDetail, error) +// SearchBranchByName(ctx context.Context, name string, companyID domain.ValidInt64) ([]domain.BranchDetail, error) +// UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error) +// DeleteBranch(ctx context.Context, id int64) error +// CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error +// CreateSupportedOperation(ctx context.Context, supportedOperation domain.CreateSupportedOperation) (domain.SupportedOperation, error) +// GetAllSupportedOperations(ctx context.Context) ([]domain.SupportedOperation, error) +// GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error) +// DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error +// CreateBranchCashier(ctx context.Context, branchID int64, userID int64) error +// GetBranchByCashier(ctx context.Context, userID int64) (domain.Branch, error) +// DeleteBranchCashier(ctx context.Context, userID int64) error + +// GetBranchCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) +// GetBranchDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.BranchDetail, error) + +// GetAllCompaniesBranch(ctx context.Context) ([]domain.Company, error) +// GetBranchesByCompany(ctx context.Context, companyID int64) ([]domain.Branch, error) + +// GetAllBranchLocations(ctx context.Context, query domain.ValidString) ([]domain.BranchLocation, error) +// } diff --git a/internal/services/branch/service.go b/internal/services/branch/service.go index 1b44651..50f9889 100644 --- a/internal/services/branch/service.go +++ b/internal/services/branch/service.go @@ -1,16 +1,17 @@ - package branch +package branch import ( "context" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) type Service struct { - branchStore BranchStore + branchStore ports.BranchStore } -func NewService(branchStore BranchStore) *Service { +func NewService(branchStore ports.BranchStore) *Service { return &Service{ branchStore: branchStore, } @@ -78,4 +79,3 @@ func (s *Service) GetAllCompaniesBranch(ctx context.Context) ([]domain.Company, func (s *Service) GetBranchesByCompany(ctx context.Context, companyID int64) ([]domain.Branch, error) { return s.branchStore.GetBranchesByCompany(ctx, companyID) } - diff --git a/internal/services/chapa/service.go b/internal/services/chapa/service.go index 5822cb7..1ecd5f3 100644 --- a/internal/services/chapa/service.go +++ b/internal/services/chapa/service.go @@ -9,23 +9,24 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" "github.com/google/uuid" ) type Service struct { - transferStore wallet.TransferStore + transferStore ports.TransferStore walletStore wallet.Service - userStore user.UserStore + userStore ports.UserStore cfg *config.Config chapaClient *Client } func NewService( - transferStore wallet.TransferStore, + transferStore ports.TransferStore, walletStore wallet.Service, - userStore user.UserStore, + userStore ports.UserStore, cfg *config.Config, chapaClient *Client, diff --git a/internal/services/company/interface.go b/internal/services/company/interface.go new file mode 100644 index 0000000..5fff5aa --- /dev/null +++ b/internal/services/company/interface.go @@ -0,0 +1,23 @@ +package company + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type CompanyStore interface { +// CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error) +// GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error) +// SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) +// GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) +// GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) +// UpdateCompany(ctx context.Context, company domain.UpdateCompany) error +// DeleteCompany(ctx context.Context, id int64) error + +// GetCompanyCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) +// UpdateCompanyStats(ctx context.Context) error +// GetCompanyStatByID(ctx context.Context, companyID int64) ([]domain.CompanyStat, error) +// GetCompanyStatsByInterval(ctx context.Context, filter domain.CompanyStatFilter) ([]domain.CompanyStat, error) +// } + diff --git a/internal/services/company/service.go b/internal/services/company/service.go index 2dd6624..756a9c9 100644 --- a/internal/services/company/service.go +++ b/internal/services/company/service.go @@ -4,13 +4,14 @@ import ( "context" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) type Service struct { - companyStore CompanyStore + companyStore ports.CompanyStore } -func NewService(companyStore CompanyStore) *Service { +func NewService(companyStore ports.CompanyStore) *Service { return &Service{ companyStore: companyStore, } diff --git a/internal/services/event/interface.go b/internal/services/event/interface.go new file mode 100644 index 0000000..e84aa1e --- /dev/null +++ b/internal/services/event/interface.go @@ -0,0 +1,29 @@ +package event + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type EventStore interface { +// FetchUpcomingEvents(ctx context.Context) error +// GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) +// GetEventByID(ctx context.Context, ID int64) (domain.BaseEvent, error) +// UpdateFinalScore(ctx context.Context, eventID int64, fullScore string, status domain.EventStatus) error +// UpdateEventStatus(ctx context.Context, eventID int64, status domain.EventStatus) error +// IsEventMonitored(ctx context.Context, eventID int64) (bool, error) +// UpdateEventMonitored(ctx context.Context, eventID int64, IsMonitored bool) error +// GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error) + +// // Event Settings Views +// GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) +// GetEventWithSettingByID(ctx context.Context, ID int64, companyID int64) (domain.EventWithSettings, error) +// UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error +// UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error + +// // Stats +// GetTotalEventStats(ctx context.Context, filter domain.EventStatsFilter) (domain.EventStats, error) +// GetTotalEventStatsByInterval(ctx context.Context, filter domain.EventStatsByIntervalFilter) ([]domain.EventStatsByInterval, error) +// UpdateEventBetStats(ctx context.Context) error +// } diff --git a/internal/services/event/service.go b/internal/services/event/service.go index 9417c3f..5b72fe4 100644 --- a/internal/services/event/service.go +++ b/internal/services/event/service.go @@ -14,27 +14,41 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/league" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" "go.uber.org/zap" // "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" ) type Service struct { - token string - store *repository.Store - settingSvc *settings.Service - mongoLogger *zap.Logger - cfg *config.Config + token string + eventStore ports.EventStore + eventHistoryStore ports.EventHistoryStore + leagueSvc league.Service + settingSvc *settings.Service + mongoLogger *zap.Logger + cfg *config.Config } -func New(token string, store *repository.Store, settingSvc *settings.Service, mongoLogger *zap.Logger, cfg *config.Config) *Service { +func New( + token string, + eventStore ports.EventStore, + eventHistoryStore ports.EventHistoryStore, + leagueSvc league.Service, + settingSvc *settings.Service, + mongoLogger *zap.Logger, + cfg *config.Config, +) *Service { return &Service{ - token: token, - store: store, - settingSvc: settingSvc, - mongoLogger: mongoLogger, - cfg: cfg, + token: token, + eventStore: eventStore, + eventHistoryStore: eventHistoryStore, + leagueSvc: leagueSvc, + settingSvc: settingSvc, + mongoLogger: mongoLogger, + cfg: cfg, } } @@ -93,7 +107,7 @@ func New(token string, store *repository.Store, settingSvc *settings.Service, mo // } // for _, event := range events { -// if err := s.store.SaveEvent(ctx, event); err != nil { +// if err := s.eventStore.SaveEvent(ctx, event); err != nil { // fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) // } // } @@ -299,7 +313,7 @@ func (s *Service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur // no this its fine to keep it here // but change the league id to bet365 id later //Automatically feature the league if its in the list - err = s.store.SaveLeague(ctx, domain.CreateLeague{ + err = s.leagueSvc.SaveLeague(ctx, domain.CreateLeague{ ID: leagueID, Name: ev.League.Name, DefaultIsActive: true, @@ -313,7 +327,7 @@ func (s *Service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur } // Since the system is multi-vendor now, no events are going to be skipped - // if supported, err := s.store.CheckLeagueSupport(ctx, leagueID); !supported || err != nil { + // if supported, err := s.eventStore.CheckLeagueSupport(ctx, leagueID); !supported || err != nil { // dataLogger.Warn( // "Skipping league", // zap.Bool("is_supported", supported), @@ -356,7 +370,7 @@ func (s *Service) fetchUpcomingEventsFromProvider(ctx context.Context, source_ur dataLogger.Info("event history has been recorded") } - err = s.store.SaveEvent(ctx, event) + err = s.eventStore.SaveEvent(ctx, event) if err != nil { dataLogger.Error("failed to save upcoming event", zap.Error(err)) } @@ -401,7 +415,7 @@ func (s *Service) CheckAndInsertEventHistory(ctx context.Context, newEvent domai zap.Int32("sport_id", newEvent.SportID), ) - oldEvent, err := s.store.GetEventBySourceID(ctx, newEvent.SourceEventID, newEvent.Source) + oldEvent, err := s.eventStore.GetEventBySourceID(ctx, newEvent.SourceEventID, newEvent.Source) if err != nil { return false, err @@ -412,7 +426,7 @@ func (s *Service) CheckAndInsertEventHistory(ctx context.Context, newEvent domai } if oldEvent.Status != newEvent.Status { - _, err := s.store.InsertEventHistory(ctx, domain.CreateEventHistory{ + _, err := s.eventHistoryStore.InsertEventHistory(ctx, domain.CreateEventHistory{ EventID: oldEvent.ID, Status: string(newEvent.Status), }) @@ -462,27 +476,35 @@ func convertInt64(num string) int64 { return 0 } func (s *Service) GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) { - return s.store.GetAllEvents(ctx, filter) + return s.eventStore.GetAllEvents(ctx, filter) } func (s *Service) GetEventByID(ctx context.Context, ID int64) (domain.BaseEvent, error) { - return s.store.GetEventByID(ctx, ID) + return s.eventStore.GetEventByID(ctx, ID) } func (s *Service) UpdateFinalScore(ctx context.Context, eventID int64, fullScore string, status domain.EventStatus) error { - return s.store.UpdateFinalScore(ctx, eventID, fullScore, status) + return s.eventStore.UpdateFinalScore(ctx, eventID, fullScore, status) } func (s *Service) UpdateEventStatus(ctx context.Context, eventID int64, status domain.EventStatus) error { - return s.store.UpdateEventStatus(ctx, eventID, status) + return s.eventStore.UpdateEventStatus(ctx, eventID, status) } func (s *Service) IsEventMonitored(ctx context.Context, eventID int64) (bool, error) { - return s.store.IsEventMonitored(ctx, eventID) + return s.eventStore.IsEventMonitored(ctx, eventID) } func (s *Service) UpdateEventMonitored(ctx context.Context, eventID int64, IsMonitored bool) error { - return s.store.UpdateEventMonitored(ctx, eventID, IsMonitored) + return s.eventStore.UpdateEventMonitored(ctx, eventID, IsMonitored) } func (s *Service) GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error) { - return s.store.GetSportAndLeagueIDs(ctx, eventID) + return s.eventStore.GetSportAndLeagueIDs(ctx, eventID) +} + +func (s *Service) DeleteEvent(ctx context.Context, eventID int64) error { + return s.eventStore.DeleteEvent(ctx, eventID) +} + +func (s *Service) GetEventBySourceID(ctx context.Context, id string, source domain.EventSource) (domain.BaseEvent, error) { + return s.eventStore.GetEventBySourceID(ctx, id, source) } diff --git a/internal/services/event/settings.go b/internal/services/event/settings.go index 17ebec7..1044ef3 100644 --- a/internal/services/event/settings.go +++ b/internal/services/event/settings.go @@ -7,16 +7,16 @@ import ( ) func (s *Service) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) { - return s.store.GetEventsWithSettings(ctx, companyID, filter) + return s.eventStore.GetEventsWithSettings(ctx, companyID, filter) } func (s *Service) GetEventWithSettingByID(ctx context.Context, ID int64, companyID int64) (domain.EventWithSettings, error) { - return s.store.GetEventWithSettingByID(ctx, ID, companyID) + return s.eventStore.GetEventWithSettingByID(ctx, ID, companyID) } func (s *Service) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error { - return s.store.UpdateTenantEventSettings(ctx, event) + return s.eventStore.UpdateTenantEventSettings(ctx, event) } func (s *Service) UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error { - return s.store.UpdateGlobalEventSettings(ctx, event) + return s.eventStore.UpdateGlobalEventSettings(ctx, event) } diff --git a/internal/services/league/interface.go b/internal/services/league/interface.go new file mode 100644 index 0000000..ca4c4b8 --- /dev/null +++ b/internal/services/league/interface.go @@ -0,0 +1,17 @@ +package league + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type LeagueStore interface { +// SaveLeague(ctx context.Context, league domain.CreateLeague) error +// SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error +// GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, int64, error) +// GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error) +// CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error) +// UpdateLeague(ctx context.Context, league domain.UpdateLeague) error +// UpdateGlobalLeagueSettings(ctx context.Context, league domain.UpdateGlobalLeagueSettings) error +// } diff --git a/internal/services/league/service.go b/internal/services/league/service.go index d0b02b1..6c7f3cd 100644 --- a/internal/services/league/service.go +++ b/internal/services/league/service.go @@ -4,14 +4,15 @@ import ( "context" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + ) type Service struct { - store *repository.Store + store ports.LeagueStore } -func New(store *repository.Store) *Service { +func New(store ports.LeagueStore) *Service { return &Service{ store: store, } diff --git a/internal/services/notification/interface.go b/internal/services/notification/interface.go new file mode 100644 index 0000000..46fff84 --- /dev/null +++ b/internal/services/notification/interface.go @@ -0,0 +1,23 @@ +package notificationservice + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// "github.com/gorilla/websocket" +// ) + +// type NotificationStore interface { +// SendNotification(ctx context.Context, notification *domain.Notification) error +// MarkAsRead(ctx context.Context, notificationID string, recipientID int64) error +// GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) +// ConnectWebSocket(ctx context.Context, recipientID int64, c *websocket.Conn) error +// DisconnectWebSocket(recipientID int64) +// SendSMS(ctx context.Context, recipientID int64, message string) error +// SendEmail(ctx context.Context, recipientID int64, subject, message string) error +// ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) // New method +// CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) +// GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) + +// GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error) +// } diff --git a/internal/services/notification/service.go b/internal/services/notification/service.go index 283ed9a..2a07f1d 100644 --- a/internal/services/notification/service.go +++ b/internal/services/notification/service.go @@ -11,9 +11,9 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" // "github.com/segmentio/kafka-go" @@ -27,20 +27,20 @@ import ( ) type Service struct { - repo repository.NotificationRepository - Hub *ws.NotificationHub - notificationStore NotificationStore - connections sync.Map - notificationCh chan *domain.Notification - stopCh chan struct{} - config *config.Config - userSvc *user.Service - messengerSvc *messenger.Service - mongoLogger *zap.Logger - logger *slog.Logger + store ports.NotificationStore + Hub *ws.NotificationHub + connections sync.Map + notificationCh chan *domain.Notification + stopCh chan struct{} + config *config.Config + userSvc *user.Service + messengerSvc *messenger.Service + mongoLogger *zap.Logger + logger *slog.Logger } -func New(repo repository.NotificationRepository, +func New( + store ports.NotificationStore, mongoLogger *zap.Logger, logger *slog.Logger, cfg *config.Config, @@ -50,7 +50,7 @@ func New(repo repository.NotificationRepository, hub := ws.NewNotificationHub() svc := &Service{ - repo: repo, + store: store, Hub: hub, mongoLogger: mongoLogger, logger: logger, @@ -95,7 +95,7 @@ func (s *Service) SendNotification(ctx context.Context, notification *domain.Not notification.Timestamp = time.Now() notification.DeliveryStatus = domain.DeliveryStatusPending - created, err := s.repo.CreateNotification(ctx, notification) + created, err := s.store.CreateNotification(ctx, notification) if err != nil { s.mongoLogger.Error("[NotificationSvc.SendNotification] Failed to create notification", zap.String("id", notification.ID), @@ -129,7 +129,7 @@ func (s *Service) SendNotification(ctx context.Context, notification *domain.Not func (s *Service) MarkAsRead(ctx context.Context, notificationIDs []string, recipientID int64) error { for _, notificationID := range notificationIDs { - _, err := s.repo.UpdateNotificationStatus(ctx, notificationID, string(domain.DeliveryStatusSent), true, nil) + _, err := s.store.UpdateNotificationStatus(ctx, notificationID, string(domain.DeliveryStatusSent), true, nil) if err != nil { s.mongoLogger.Error("[NotificationSvc.MarkAsRead] Failed to mark notification as read", zap.String("notificationID", notificationID), @@ -140,7 +140,7 @@ func (s *Service) MarkAsRead(ctx context.Context, notificationIDs []string, reci return err } - // count, err := s.repo.CountUnreadNotifications(ctx, recipientID) + // count, err := s.store.CountUnreadNotifications(ctx, recipientID) // if err != nil { // s.logger.Error("[NotificationSvc.MarkAsRead] Failed to count unread notifications", "recipientID", recipientID, "error", err) // return err @@ -165,7 +165,7 @@ func (s *Service) MarkAsRead(ctx context.Context, notificationIDs []string, reci } func (s *Service) GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) { - notifications, total, err := s.repo.GetUserNotifications(ctx, recipientID, limit, offset) + notifications, total, err := s.store.GetUserNotifications(ctx, recipientID, limit, offset) if err != nil { s.mongoLogger.Error("[NotificationSvc.GetUserNotifications] Failed to list notifications", zap.Int64("recipientID", recipientID), @@ -186,7 +186,7 @@ func (s *Service) GetUserNotifications(ctx context.Context, recipientID int64, l } func (s *Service) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) { - notifications, err := s.repo.GetAllNotifications(ctx, limit, offset) + notifications, err := s.store.GetAllNotifications(ctx, limit, offset) if err != nil { s.mongoLogger.Error("[NotificationSvc.ListNotifications] Failed to get all notifications", zap.Int("limit", limit), @@ -282,7 +282,7 @@ func (s *Service) startWorker() { } func (s *Service) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) { - return s.repo.ListRecipientIDs(ctx, receiver) + return s.store.ListRecipientIDs(ctx, receiver) } func (s *Service) handleNotification(notification *domain.Notification) { @@ -314,7 +314,7 @@ func (s *Service) handleNotification(notification *domain.Notification) { } } - if _, err := s.repo.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil { + if _, err := s.store.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil { s.mongoLogger.Error("[NotificationSvc.HandleNotification] Failed to update notification status", zap.String("id", notification.ID), zap.Error(err), @@ -404,7 +404,7 @@ func (s *Service) startRetryWorker() { func (s *Service) retryFailedNotifications() { ctx := context.Background() - failedNotifications, err := s.repo.ListFailedNotifications(ctx, 100) + failedNotifications, err := s.store.ListFailedNotifications(ctx, 100) if err != nil { s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to list failed notifications", zap.Error(err), @@ -422,7 +422,7 @@ func (s *Service) retryFailedNotifications() { case domain.DeliveryChannelSMS: if err := s.SendNotificationSMS(ctx, notification.RecipientID, notification.Payload.Message); err == nil { notification.DeliveryStatus = domain.DeliveryStatusSent - if _, err := s.repo.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil { + if _, err := s.store.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil { s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to update after retry", zap.String("id", notification.ID), zap.Error(err), @@ -440,7 +440,7 @@ func (s *Service) retryFailedNotifications() { case domain.DeliveryChannelEmail: if err := s.SendNotificationEmail(ctx, notification.RecipientID, notification.Payload.Message, notification.Payload.Headline); err == nil { notification.DeliveryStatus = domain.DeliveryStatusSent - if _, err := s.repo.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil { + if _, err := s.store.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil { s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to update after retry", zap.String("id", notification.ID), zap.Error(err), @@ -467,15 +467,15 @@ func (s *Service) retryFailedNotifications() { } func (s *Service) CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) { - return s.repo.CountUnreadNotifications(ctx, recipient_id) + return s.store.CountUnreadNotifications(ctx, recipient_id) } func (s *Service) DeleteOldNotifications(ctx context.Context) error { - return s.repo.DeleteOldNotifications(ctx) + return s.store.DeleteOldNotifications(ctx) } // func (s *Service) GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error){ -// return s.repo.Get(ctx, filter) +// return s.store.Get(ctx, filter) // } // func (s *Service) RunRedisSubscriber(ctx context.Context) { diff --git a/internal/services/odds/disabled_odd.go b/internal/services/odds/disabled_odd.go deleted file mode 100644 index cd3dd06..0000000 --- a/internal/services/odds/disabled_odd.go +++ /dev/null @@ -1,28 +0,0 @@ -package odds - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *ServiceImpl) InsertDisabledOdd(ctx context.Context, odd domain.CreateDisabledOdd) (domain.DisabledOdd, error) { - return s.store.InsertDisabledOdd(ctx, odd) -} - -func (s *ServiceImpl) GetAllDisabledOdds(ctx context.Context) ([]domain.DisabledOdd, error) { - return s.store.GetAllDisabledOdds(ctx) -} -func (s *ServiceImpl) GetDisabledOddByRawOddID(ctx context.Context, rawOddID int64) (domain.DisabledOdd, error) { - return s.store.GetDisabledOddByRawOddID(ctx, rawOddID) -} -func (s *ServiceImpl) GetDisabledOddByID(ctx context.Context, id int64) (domain.DisabledOdd, error) { - return s.store.GetDisabledOddByID(ctx, id) -} -func (s *ServiceImpl) DeleteDisabledOddsByID(ctx context.Context, id int64) error { - return s.store.DeleteDisabledOddsByID(ctx, id) -} -func (s *ServiceImpl) DeleteDisabledOddsByRawOddID(ctx context.Context, id int64) error { - return s.store.DeleteDisabledOddsByRawOddID(ctx, id) -} - diff --git a/internal/services/odds/port.go b/internal/services/odds/interface.go similarity index 100% rename from internal/services/odds/port.go rename to internal/services/odds/interface.go diff --git a/internal/services/odds/service.go b/internal/services/odds/service.go index 3d8624e..9001d3c 100644 --- a/internal/services/odds/service.go +++ b/internal/services/odds/service.go @@ -15,13 +15,14 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" "go.uber.org/zap" ) type ServiceImpl struct { - store *repository.Store + store ports.OddStore config *config.Config eventSvc *event.Service logger *slog.Logger @@ -29,7 +30,7 @@ type ServiceImpl struct { client *http.Client } -func New(store *repository.Store, cfg *config.Config, eventSvc *event.Service, logger *slog.Logger, mongoLogger *zap.Logger) *ServiceImpl { +func New(store ports.OddStore, cfg *config.Config, eventSvc *event.Service, logger *slog.Logger, mongoLogger *zap.Logger) *ServiceImpl { return &ServiceImpl{ store: store, config: cfg, diff --git a/internal/services/raffle/interface.go b/internal/services/raffle/interface.go new file mode 100644 index 0000000..6f06cad --- /dev/null +++ b/internal/services/raffle/interface.go @@ -0,0 +1,27 @@ +package raffle + +// import ( +// "context" + +// dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type RaffleStore interface { +// CreateRaffle(ctx context.Context, raffle domain.CreateRaffle) (domain.Raffle, error) +// AddSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) error +// DeleteRaffle(ctx context.Context, raffleID int32) (domain.Raffle, error) +// GetRafflesOfCompany(ctx context.Context, companyID int32) ([]dbgen.Raffle, error) +// GetRaffleStanding(ctx context.Context, raffleID, limit int32) ([]domain.RaffleStanding, error) +// CreateRaffleWinner(ctx context.Context, raffleWinnerParams domain.RaffleWinnerParams) error +// SetRaffleComplete(ctx context.Context, raffleID int32) error +// CheckValidSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) (bool, error) +// CheckSportRaffleHasFilter(ctx context.Context, raffleID int32) (bool, error) + +// CreateRaffleTicket(ctx context.Context, raffleTicketParams domain.CreateRaffleTicket) (domain.RaffleTicket, error) +// GetUserRaffleTickets(ctx context.Context, userID int32) ([]domain.RaffleTicketRes, error) +// SuspendRaffleTicket(ctx context.Context, raffleTicketID int32) error +// UnSuspendRaffleTicket(ctx context.Context, raffleID int32) error +// GetRaffleTicketCount(ctx context.Context, raffleID, userID int32) (int64, error) +// GetRaffleTicketLimit(ctx context.Context, raffleID int32) (int32, error) +// } diff --git a/internal/services/raffle/service.go b/internal/services/raffle/service.go index 3483839..8326ddb 100644 --- a/internal/services/raffle/service.go +++ b/internal/services/raffle/service.go @@ -5,13 +5,14 @@ import ( dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) type Service struct { - raffleStore RaffleStore + raffleStore ports.RaffleStore } -func NewService(raffleStore RaffleStore) *Service { +func NewService(raffleStore ports.RaffleStore) *Service { return &Service{ raffleStore: raffleStore, } diff --git a/internal/services/referal/interface.go b/internal/services/referal/interface.go new file mode 100644 index 0000000..a192995 --- /dev/null +++ b/internal/services/referal/interface.go @@ -0,0 +1,17 @@ +package referralservice + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type ReferralStore interface { +// GenerateReferralCode() (string, error) +// CreateReferral(ctx context.Context, userID int64, companyID int64) error +// ProcessReferral(ctx context.Context, referredPhone, referralCode string, companyID int64) error +// ProcessDepositBonus(ctx context.Context, userPhone string, amount float64) error +// ProcessBetReferral(ctx context.Context, userId int64, betAmount float64) error +// GetReferralStats(ctx context.Context, userID int64, companyID int64) (*domain.ReferralStats, error) +// GetReferralCountByID(ctx context.Context, referrerID int64) (int64, error) +// } diff --git a/internal/services/referal/port.go b/internal/services/referal/port.go deleted file mode 100644 index 6930c0e..0000000 --- a/internal/services/referal/port.go +++ /dev/null @@ -1,17 +0,0 @@ -package referralservice - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type ReferralStore interface { - GenerateReferralCode() (string, error) - CreateReferral(ctx context.Context, userID int64, companyID int64) error - ProcessReferral(ctx context.Context, referredPhone, referralCode string, companyID int64) error - ProcessDepositBonus(ctx context.Context, userPhone string, amount float64) error - ProcessBetReferral(ctx context.Context, userId int64, betAmount float64) error - GetReferralStats(ctx context.Context, userID int64, companyID int64) (*domain.ReferralStats, error) - GetReferralCountByID(ctx context.Context, referrerID int64) (int64, error) -} diff --git a/internal/services/referal/service.go b/internal/services/referal/service.go index eb5b021..015c7e9 100644 --- a/internal/services/referal/service.go +++ b/internal/services/referal/service.go @@ -10,14 +10,15 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" "go.uber.org/zap" ) type Service struct { - repo repository.ReferralRepository + repo ports.ReferralStore walletSvc wallet.Service settingSvc settings.Service config *config.Config @@ -25,7 +26,14 @@ type Service struct { mongoLogger *zap.Logger } -func New(repo repository.ReferralRepository, walletSvc wallet.Service, settingSvc settings.Service, cfg *config.Config, logger *slog.Logger, mongoLogger *zap.Logger) *Service { +func New( + repo ports.ReferralStore, + walletSvc wallet.Service, + settingSvc settings.Service, + cfg *config.Config, + logger *slog.Logger, + mongoLogger *zap.Logger, + ) *Service { return &Service{ repo: repo, walletSvc: walletSvc, diff --git a/internal/services/report/event.go b/internal/services/report/event.go index 61a2cb6..9a30d66 100644 --- a/internal/services/report/event.go +++ b/internal/services/report/event.go @@ -29,7 +29,7 @@ func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domai return "", ErrInvalidInterval } - stats, err := s.eventSvc.GetTotalEventStatsByInterval(ctx, domain.EventStatsByIntervalFilter{ + stats, err := s.statService.GetTotalEventStatsByInterval(ctx, domain.EventStatsByIntervalFilter{ Interval: domain.ValidDateInterval{ Value: interval, Valid: true, diff --git a/internal/services/report/port.go b/internal/services/report/interface.go similarity index 67% rename from internal/services/report/port.go rename to internal/services/report/interface.go index 4d2e10d..4b7ee3a 100644 --- a/internal/services/report/port.go +++ b/internal/services/report/interface.go @@ -6,19 +6,23 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) -type ReportStore interface { +type ReportService interface { + registerGenerators() GetDashboardSummary(ctx context.Context, filter domain.ReportFilter) (domain.DashboardSummary, error) GetBetAnalysis(ctx context.Context, filter domain.ReportFilter) ([]domain.BetAnalysis, error) GetCustomerActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.CustomerActivity, error) GetBranchPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.BranchPerformance, error) GetSportPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.SportPerformance, error) - // GetNotificationReport(ctx context.Context, filter domain.ReportFilter) (domain.NotificationReport, error) - // GetCashierPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.CashierPerformance, error) - // GetCompanyPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.CompanyPerformance, error) - + // GenerateReport(ctx context.Context, from, to time.Time) error + // fetchReportData(ctx context.Context, from, to time.Time) CreateReportRequest(ctx context.Context, report domain.CreateReportRequest) (domain.ReportRequest, error) GetAllReportRequests(ctx context.Context, filter domain.ReportRequestFilter) ([]domain.ReportRequestDetail, int64, error) GetReportRequestByRequestedByID(ctx context.Context, requestedBy int64, filter domain.ReportRequestFilter) ([]domain.ReportRequestDetail, error) GetReportRequestByID(ctx context.Context, ID int64) (domain.ReportRequestDetail, error) UpdateReportRequest(ctx context.Context, report domain.UpdateRequestRequest) error + WriteCSV(rows [][]string, filePrefix string) (string, error) + CheckAndFetchReportFile(ctx context.Context, ID int64) (string, error) + ProcessReportRequests(ctx context.Context) error + processSingleReportRequest(ctx context.Context, req domain.ReportRequestDetail) error + GenerateEventIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) } diff --git a/internal/services/report/process.go b/internal/services/report/process.go index 98d4b9c..d209610 100644 --- a/internal/services/report/process.go +++ b/internal/services/report/process.go @@ -52,25 +52,19 @@ func (s *Service) processSingleReportRequest(ctx context.Context, req domain.Rep ) }() - switch req.Type { - case domain.EventIntervalReportRequest: - if req.Metadata.Interval == nil { + gen, ok := s.generators[req.Type] + if !ok { + status = domain.RejectReportRequest + rejectReason = fmt.Sprintf("unsupported report type: %s", req.Type) + s.mongoLogger.Warn("unsupported report type", zap.String("type", string(req.Type))) + } else { + fp, err := gen(ctx, req) + if err != nil { status = domain.RejectReportRequest - rejectReason = "invalid interval provided" - break - } - - fp, genErr := s.GenerateEventIntervalReport(ctx, req) - if genErr != nil { - status = domain.RejectReportRequest - rejectReason = fmt.Sprintf("failed to generate report: %v", genErr) + rejectReason = fmt.Sprintf("failed to generate report: %v", err) } else { filePath = fp } - - default: - status = domain.RejectReportRequest - rejectReason = fmt.Sprintf("unsupported report type: %s", req.Type) } update := domain.UpdateRequestRequest{ @@ -90,6 +84,7 @@ func (s *Service) processSingleReportRequest(ctx context.Context, req domain.Rep } if err := s.UpdateReportRequest(ctx, update); err != nil { + s.mongoLogger.Error("failed to update report request", zap.Int64("id", req.ID), zap.Error(err)) return fmt.Errorf("failed to update report request: %w", err) } diff --git a/internal/services/report/request.go b/internal/services/report/request.go index d3d51e4..89567ff 100644 --- a/internal/services/report/request.go +++ b/internal/services/report/request.go @@ -2,10 +2,10 @@ package report import ( "context" + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) - func (s *Service) CreateReportRequest(ctx context.Context, report domain.CreateReportRequest) (domain.ReportRequest, error) { return s.store.CreateReportRequest(ctx, report) } diff --git a/internal/services/report/service.go b/internal/services/report/service.go index 03484da..27a1748 100644 --- a/internal/services/report/service.go +++ b/internal/services/report/service.go @@ -14,19 +14,14 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/company" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" + + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/stats" "go.uber.org/zap" notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" - // virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" ) var ( @@ -35,43 +30,42 @@ var ( ) type Service struct { - store *repository.Store - betStore bet.BetStore - walletStore wallet.WalletStore - transactionStore transaction.TransactionStore - branchStore branch.BranchStore - userStore user.UserStore - repo repository.ReportRepository - companyStore company.CompanyStore + store ports.ReportStore + betStore ports.BetStore + walletStore ports.WalletStore + transactionStore ports.TransactionStore + branchStore ports.BranchStore + userStore ports.UserStore + repo ports.OldReportRepository + companyStore ports.CompanyStore + notificationStore ports.NotificationStore virtulaGamesStore repository.VirtualGameRepository - notificationStore repository.NotificationRepository notificationSvc *notificationservice.Service - eventSvc *event.Service - companySvc *company.Service + statService *stats.Service logger *slog.Logger mongoLogger *zap.Logger cfg *config.Config + generators map[domain.ReportRequestType]ReportGeneratorFunc } func NewService( - store *repository.Store, - betStore bet.BetStore, - walletStore wallet.WalletStore, - transactionStore transaction.TransactionStore, - branchStore branch.BranchStore, - userStore user.UserStore, - repo repository.ReportRepository, - companyStore company.CompanyStore, + store ports.ReportStore, + betStore ports.BetStore, + walletStore ports.WalletStore, + transactionStore ports.TransactionStore, + branchStore ports.BranchStore, + userStore ports.UserStore, + repo ports.OldReportRepository, + companyStore ports.CompanyStore, virtulaGamesStore repository.VirtualGameRepository, - notificationStore repository.NotificationRepository, + notificationStore ports.NotificationStore, notificationSvc *notificationservice.Service, - eventSvc *event.Service, - companySvc *company.Service, + statService *stats.Service, logger *slog.Logger, mongoLogger *zap.Logger, cfg *config.Config, -) *Service { - return &Service{ +) ReportService { + s := &Service{ store: store, betStore: betStore, walletStore: walletStore, @@ -83,12 +77,28 @@ func NewService( virtulaGamesStore: virtulaGamesStore, notificationStore: notificationStore, notificationSvc: notificationSvc, - eventSvc: eventSvc, - companySvc: companySvc, + statService: statService, logger: logger, mongoLogger: mongoLogger, cfg: cfg, } + + // Initialize the report generators + s.registerGenerators() + + return s +} + +// This registers all the report generators to make the processing cleaner and easier +// A report generator is a function (ReportGeneratorFunc) which takes a ReportRequestType and returns the path to file +type ReportGeneratorFunc func(ctx context.Context, req domain.ReportRequestDetail) (string, error) + +func (s *Service) registerGenerators() { + s.generators = map[domain.ReportRequestType]ReportGeneratorFunc{ + domain.EventIntervalReportRequest: s.GenerateEventIntervalReport, + // domain.CompanySummaryReportRequest: s.GenerateCompanySummaryReport, + // domain.BranchPerformanceReportRequest: s.GenerateBranchPerformanceReport, + } } // GetDashboardSummary returns comprehensive dashboard metrics @@ -480,7 +490,7 @@ func (s *Service) GetSportPerformance(ctx context.Context, filter domain.ReportF return performances, nil } -// func (s *Service) GenerateReport(ctx context.Context, from, to time.Time) error { +// func (s *service) GenerateReport(ctx context.Context, from, to time.Time) error { // // Hardcoded output directory // outputDir := "reports" @@ -630,7 +640,7 @@ func (s *Service) GetSportPerformance(ctx context.Context, filter domain.ReportF // return nil // } -// func (s *Service) fetchReportData(ctx context.Context, from, to time.Time) ( +// func (s *service) fetchReportData(ctx context.Context, from, to time.Time) ( // []domain.CompanyReport, map[int64][]domain.BranchReport, error, // ) { // // --- company level --- diff --git a/internal/services/result/interface.go b/internal/services/result/interface.go new file mode 100644 index 0000000..4796528 --- /dev/null +++ b/internal/services/result/interface.go @@ -0,0 +1,11 @@ +package result + +import ( + "context" +) + +type ResultService interface { + FetchAndProcessResults(ctx context.Context) error + FetchAndStoreResult(ctx context.Context, eventID string) error +} + diff --git a/internal/services/result/notification.go b/internal/services/result/notification.go index 3c88649..f98841b 100644 --- a/internal/services/result/notification.go +++ b/internal/services/result/notification.go @@ -13,7 +13,7 @@ import ( func (s *Service) CheckAndSendResultNotifications(ctx context.Context, createdAfter time.Time) error { - resultLog, err := s.repo.GetAllResultLog(ctx, domain.ResultLogFilter{ + resultLog, err := s.resultLogStore.GetAllResultLog(ctx, domain.ResultLogFilter{ CreatedAfter: domain.ValidTime{ Value: createdAfter, Valid: true, diff --git a/internal/services/result/service.go b/internal/services/result/service.go index 0a7315a..35ca5d0 100644 --- a/internal/services/result/service.go +++ b/internal/services/result/service.go @@ -13,7 +13,8 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/league" @@ -25,7 +26,7 @@ import ( ) type Service struct { - repo *repository.Store + resultLogStore ports.ResultLogStore config *config.Config logger *slog.Logger mongoLogger *zap.Logger @@ -40,7 +41,7 @@ type Service struct { } func NewService( - repo *repository.Store, + resultLogStore ports.ResultLogStore, cfg *config.Config, logger *slog.Logger, mongoLogger *zap.Logger, @@ -53,7 +54,7 @@ func NewService( userSvc user.Service, ) *Service { return &Service{ - repo: repo, + resultLogStore: resultLogStore, config: cfg, logger: logger, mongoLogger: mongoLogger, @@ -74,7 +75,7 @@ var ( func (s *Service) UpdateResultForOutcomes(ctx context.Context, eventID int64, resultRes json.RawMessage, sportID int64) error { // TODO: Optimize this since there could be many outcomes with the same event_id and market_id that could be updated the same time - outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID, true) + outcomes, err := s.betSvc.GetBetOutcomeByEventID(ctx, eventID, true) logger := s.mongoLogger.With( zap.Int64("eventID", eventID), zap.Int64("sportID", sportID), @@ -138,7 +139,7 @@ func (s *Service) UpdateResultForOutcomes(ctx context.Context, eventID int64, re } func (s *Service) GetTotalBetsForEvents(ctx context.Context, eventID int64) (map[int64]int64, error) { - outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID, false) + outcomes, err := s.betSvc.GetBetOutcomeByEventID(ctx, eventID, false) if err != nil { s.mongoLogger.Error( @@ -231,7 +232,7 @@ func (s *Service) RefundAllOutcomes(ctx context.Context, eventID int64) (map[int func (s *Service) FetchB365ResultAndUpdateBets(ctx context.Context) error { // TODO: Optimize this because there could be many bet outcomes for the same odd // Take market id and match result as param and update all the bet outcomes at the same time - events, _, err := s.repo.GetAllEvents(ctx, domain.EventFilter{ + events, _, err := s.eventSvc.GetAllEvents(ctx, domain.EventFilter{ LastStartTime: domain.ValidTime{ Value: time.Now(), Valid: true, @@ -318,12 +319,12 @@ func (s *Service) FetchB365ResultAndUpdateBets(ctx context.Context) error { commonRespLogger.Error("Failed to refund all outcomes", zap.Error(err)) continue } - err = s.repo.DeleteEvent(ctx, event.ID) + err = s.eventSvc.DeleteEvent(ctx, event.ID) if err != nil { commonRespLogger.Error("Failed to remove event", zap.Error(err)) continue } - err = s.repo.DeleteOddsForEvent(ctx, event.ID) + err = s.oddSvc.DeleteOddsForEvent(ctx, event.ID) if err != nil { commonRespLogger.Error("Failed to remove odds for event", zap.Error(err)) continue @@ -370,12 +371,12 @@ func (s *Service) FetchB365ResultAndUpdateBets(ctx context.Context) error { if err != nil { commonRespLogger.Error("Error while updating result for event", zap.Error(err)) } - err = s.repo.DeleteEvent(ctx, event.ID) + err = s.eventSvc.DeleteEvent(ctx, event.ID) if err != nil { commonRespLogger.Error("Failed to remove event", zap.Error(err)) continue } - err = s.repo.DeleteOddsForEvent(ctx, event.ID) + err = s.oddSvc.DeleteOddsForEvent(ctx, event.ID) if err != nil { commonRespLogger.Error("Failed to remove odds for event", zap.Error(err)) continue @@ -407,12 +408,12 @@ func (s *Service) FetchB365ResultAndUpdateBets(ctx context.Context) error { commonRespLogger.Error("Failed to refund outcomes", zap.Error(err)) } - err = s.repo.DeleteEvent(ctx, event.ID) + err = s.eventSvc.DeleteEvent(ctx, event.ID) if err != nil { commonRespLogger.Error("Failed to remove event", zap.Error(err)) continue } - err = s.repo.DeleteOddsForEvent(ctx, event.ID) + err = s.oddSvc.DeleteOddsForEvent(ctx, event.ID) if err != nil { commonRespLogger.Error("Failed to remove odds for event", zap.Error(err)) continue @@ -428,7 +429,7 @@ func (s *Service) FetchB365ResultAndUpdateBets(ctx context.Context) error { } // This will be used to send daily notifications, since events will be removed - _, err = s.repo.CreateResultLog(ctx, resultLog) + _, err = s.resultLogStore.CreateResultLog(ctx, resultLog) if err != nil { s.mongoLogger.Warn( "Failed to store result log", @@ -457,7 +458,7 @@ func (s *Service) FetchB365ResultAndUpdateBets(ctx context.Context) error { } func (s *Service) CheckAndUpdateExpiredB365Events(ctx context.Context) (int64, error) { - events, _, err := s.repo.GetAllEvents(ctx, domain.EventFilter{ + events, _, err := s.eventSvc.GetAllEvents(ctx, domain.EventFilter{ LastStartTime: domain.ValidTime{ Value: time.Now(), Valid: true, diff --git a/internal/services/santimpay/service.go b/internal/services/santimpay/service.go index 18a15b6..d5e87d1 100644 --- a/internal/services/santimpay/service.go +++ b/internal/services/santimpay/service.go @@ -11,6 +11,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" "github.com/google/uuid" ) @@ -22,11 +23,11 @@ import ( type SantimPayService struct { client SantimPayClient cfg *config.Config - transferStore wallet.TransferStore + transferStore ports.TransferStore walletSvc *wallet.Service } -func NewSantimPayService(client SantimPayClient, cfg *config.Config, transferStore wallet.TransferStore, walletSvc *wallet.Service) *SantimPayService { +func NewSantimPayService(client SantimPayClient, cfg *config.Config, transferStore ports.TransferStore, walletSvc *wallet.Service) *SantimPayService { return &SantimPayService{ client: client, cfg: cfg, @@ -237,7 +238,7 @@ func (s *SantimPayService) ProcessDirectPayment(ctx context.Context, req domain. if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } - + // 5. Save transfer in DB transfer := domain.CreateTransfer{ Amount: domain.Currency(req.Amount), diff --git a/internal/services/settings/interface.go b/internal/services/settings/interface.go new file mode 100644 index 0000000..22250e0 --- /dev/null +++ b/internal/services/settings/interface.go @@ -0,0 +1,24 @@ +package settings + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type SettingStore interface { +// GetGlobalSettingList(ctx context.Context) (domain.SettingList, error) +// GetGlobalSettings(ctx context.Context) ([]domain.Setting, error) +// GetGlobalSetting(ctx context.Context, key string) (domain.Setting, error) +// UpdateGlobalSetting(ctx context.Context, key, value string) error +// UpdateGlobalSettingList(ctx context.Context, settingList domain.ValidSettingList) error + +// InsertCompanySetting(ctx context.Context, key, value string, companyID int64) error +// InsertCompanySettingList(ctx context.Context, settingList domain.ValidSettingList, companyID int64) error +// GetAllCompanySettings(ctx context.Context) ([]domain.CompanySetting, error) +// GetCompanySettingsByKey(ctx context.Context, key string) ([]domain.CompanySetting, error) +// GetOverrideSettings(ctx context.Context, companyID int64) ([]domain.Setting, error) +// GetOverrideSettingsList(ctx context.Context, companyID int64) (domain.SettingList, error) +// DeleteCompanySetting(ctx context.Context, companyID int64, key string) error +// DeleteAllCompanySetting(ctx context.Context, companyID int64) error +// } diff --git a/internal/services/settings/service.go b/internal/services/settings/service.go index 5b5ea37..8b9339a 100644 --- a/internal/services/settings/service.go +++ b/internal/services/settings/service.go @@ -4,13 +4,14 @@ import ( "context" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) type Service struct { - settingStore SettingStore + settingStore ports.SettingStore } -func NewService(settingStore SettingStore) *Service { +func NewService(settingStore ports.SettingStore) *Service { return &Service{ settingStore: settingStore, } diff --git a/internal/services/company/stats.go b/internal/services/stats/company.go similarity index 67% rename from internal/services/company/stats.go rename to internal/services/stats/company.go index b483619..9ccaa1c 100644 --- a/internal/services/company/stats.go +++ b/internal/services/stats/company.go @@ -1,19 +1,16 @@ -package company +package stats import ( "context" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) func (s *Service) UpdateCompanyStats(ctx context.Context) error { - return s.companyStore.UpdateCompanyStats(ctx); + return s.UpdateCompanyStats(ctx) } - func (s *Service) GetCompanyStatByID(ctx context.Context, companyID int64) ([]domain.CompanyStat, error) { - return s.companyStore.GetCompanyStatByID(ctx, companyID); + return s.GetCompanyStatByID(ctx, companyID) } - func (s *Service) GetCompanyStatsByInterval(ctx context.Context, filter domain.CompanyStatFilter) ([]domain.CompanyStat, error) { - return s.companyStore.GetCompanyStatsByInterval(ctx, filter) -} \ No newline at end of file + return s.GetCompanyStatsByInterval(ctx, filter) +} diff --git a/internal/services/event/stats.go b/internal/services/stats/event.go similarity index 69% rename from internal/services/event/stats.go rename to internal/services/stats/event.go index 43735f4..32a78e5 100644 --- a/internal/services/event/stats.go +++ b/internal/services/stats/event.go @@ -1,4 +1,4 @@ -package event +package stats import ( "context" @@ -7,13 +7,12 @@ import ( ) func (s *Service) GetTotalEventStats(ctx context.Context, filter domain.EventStatsFilter) (domain.EventStats, error) { - return s.store.GetTotalEventStats(ctx, filter) + return s.eventStatStore.GetTotalEventStats(ctx, filter) } func (s *Service) GetTotalEventStatsByInterval(ctx context.Context, filter domain.EventStatsByIntervalFilter) ([]domain.EventStatsByInterval, error) { - return s.store.GetTotalEventStatsByInterval(ctx, filter) + return s.eventStatStore.GetTotalEventStatsByInterval(ctx, filter) } func (s *Service) UpdateEventBetStats(ctx context.Context) error { - return s.store.UpdateEventBetStats(ctx) - + return s.eventStatStore.UpdateEventBetStats(ctx) } \ No newline at end of file diff --git a/internal/services/stats/service.go b/internal/services/stats/service.go new file mode 100644 index 0000000..9da8d43 --- /dev/null +++ b/internal/services/stats/service.go @@ -0,0 +1,15 @@ +package stats + +import "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + +type Service struct { + companyStatStore ports.CompanyStatStore + eventStatStore ports.EventStatStore +} + +func NewService(companyStatStore ports.CompanyStatStore, eventStatStore ports.EventStatStore) *Service { + return &Service{ + companyStatStore: companyStatStore, + eventStatStore: eventStatStore, + } +} diff --git a/internal/services/telebirr/service.go b/internal/services/telebirr/service.go index ecca8d5..6258ecd 100644 --- a/internal/services/telebirr/service.go +++ b/internal/services/telebirr/service.go @@ -23,6 +23,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" ) @@ -31,11 +32,11 @@ import ( type TelebirrService struct { // client TelebirrClient cfg *config.Config - transferStore wallet.TransferStore + transferStore ports.TransferStore walletSvc *wallet.Service } -func NewTelebirrService(cfg *config.Config, transferStore wallet.TransferStore, walletSvc *wallet.Service) *TelebirrService { +func NewTelebirrService(cfg *config.Config, transferStore ports.TransferStore, walletSvc *wallet.Service) *TelebirrService { return &TelebirrService{ cfg: cfg, transferStore: transferStore, diff --git a/internal/services/ticket/interface.go b/internal/services/ticket/interface.go new file mode 100644 index 0000000..43ef339 --- /dev/null +++ b/internal/services/ticket/interface.go @@ -0,0 +1,2 @@ +package ticket + diff --git a/internal/services/ticket/service.go b/internal/services/ticket/service.go index 4c31b8c..efe0910 100644 --- a/internal/services/ticket/service.go +++ b/internal/services/ticket/service.go @@ -8,8 +8,8 @@ import ( "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" - notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" "go.uber.org/zap" @@ -30,29 +30,26 @@ var ( ) type Service struct { - ticketStore TicketStore - eventSvc *event.Service - prematchSvc odds.ServiceImpl - mongoLogger *zap.Logger - settingSvc *settings.Service - notificationSvc *notificationservice.Service + ticketStore ports.TicketStore + eventSvc *event.Service + prematchSvc odds.ServiceImpl + mongoLogger *zap.Logger + settingSvc *settings.Service } func NewService( - ticketStore TicketStore, + ticketStore ports.TicketStore, eventSvc *event.Service, prematchSvc odds.ServiceImpl, mongoLogger *zap.Logger, settingSvc *settings.Service, - notificationSvc *notificationservice.Service, ) *Service { return &Service{ - ticketStore: ticketStore, - eventSvc: eventSvc, - prematchSvc: prematchSvc, - mongoLogger: mongoLogger, - settingSvc: settingSvc, - notificationSvc: notificationSvc, + ticketStore: ticketStore, + eventSvc: eventSvc, + prematchSvc: prematchSvc, + mongoLogger: mongoLogger, + settingSvc: settingSvc, } } @@ -238,14 +235,6 @@ func (s *Service) CreateTicket(ctx context.Context, req domain.CreateTicketReq, return domain.Ticket{}, rows, err } - // updates := domain.MetricUpdates{ - // TotalLiveTicketsDelta: domain.PtrInt64(1), - // } - - // if err := s.notificationSvc.UpdateLiveMetrics(ctx, updates); err != nil { - // // handle error - // } - return ticket, rows, nil } diff --git a/internal/services/transaction/interface.go b/internal/services/transaction/interface.go new file mode 100644 index 0000000..055412e --- /dev/null +++ b/internal/services/transaction/interface.go @@ -0,0 +1,32 @@ +package transaction + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type TransactionStore interface { +// CreateShopTransaction(ctx context.Context, transaction domain.CreateShopTransaction) (domain.ShopTransaction, error) +// GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransactionDetail, error) +// GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransactionDetail, error) +// GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransactionDetail, error) +// UpdateShopTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64) error +// GetTransactionTotals(ctx context.Context, filter domain.ReportFilter) (deposits, withdrawals domain.Currency, err error) +// GetBranchTransactionTotals(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.BranchTransactions, error) + +// CreateShopBet(ctx context.Context, bet domain.CreateShopBet) (domain.ShopBet, error) +// GetAllShopBet(ctx context.Context, filter domain.ShopBetFilter) ([]domain.ShopBetDetail, error) +// GetShopBetByID(ctx context.Context, id int64) (domain.ShopBetDetail, error) +// GetShopBetByBetID(ctx context.Context, betID int64) (domain.ShopBetDetail, error) +// GetShopBetByCashoutID(ctx context.Context, cashoutID string) (domain.ShopBetDetail, error) +// GetShopBetByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopBetDetail, error) +// UpdateShopBetCashOut(ctx context.Context, id int64, cashedOut bool) error +// UpdateShopBetCashoutID(ctx context.Context, id int64, cashoutID string) error + +// CreateShopDeposit(ctx context.Context, deposit domain.CreateShopDeposit) (domain.ShopDeposit, error) +// GetAllShopDeposit(ctx context.Context, filter domain.ShopDepositFilter) ([]domain.ShopDepositDetail, error) +// GetShopDepositByID(ctx context.Context, id int64) (domain.ShopDepositDetail, error) +// GetShopDepositByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopDepositDetail, error) +// UpdateShopDepositTransferID(ctx context.Context, id int64, transferID domain.ValidInt64) error +// } diff --git a/internal/services/transaction/service.go b/internal/services/transaction/service.go index 1a10a78..9e71f8f 100644 --- a/internal/services/transaction/service.go +++ b/internal/services/transaction/service.go @@ -7,6 +7,7 @@ import ( "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" @@ -24,14 +25,20 @@ var ( ) type Service struct { - transactionStore TransactionStore + transactionStore ports.TransactionStore branchSvc branch.Service betSvc bet.Service walletSvc wallet.Service userSvc user.Service } -func NewService(transactionStore TransactionStore, branchSvc branch.Service, betSvc bet.Service, walletSvc wallet.Service, userSvc user.Service) *Service { +func NewService( + transactionStore ports.TransactionStore, + branchSvc branch.Service, + betSvc bet.Service, + walletSvc wallet.Service, + userSvc user.Service, +) *Service { return &Service{ transactionStore: transactionStore, branchSvc: branchSvc, diff --git a/internal/services/user/interface.go b/internal/services/user/interface.go new file mode 100644 index 0000000..a590a74 --- /dev/null +++ b/internal/services/user/interface.go @@ -0,0 +1,42 @@ +package user + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type UserStore interface { +// CreateUser(ctx context.Context, user domain.User, usedOtpId int64, is_company bool) (domain.User, error) +// CreateUserWithoutOtp(ctx context.Context, user domain.User, is_company bool) (domain.User, error) +// GetUserByID(ctx context.Context, id int64) (domain.User, error) +// GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error) +// GetAllCashiers(ctx context.Context, filter domain.UserFilter) ([]domain.GetCashier, int64, error) +// GetCashierByID(ctx context.Context, cashierID int64) (domain.GetCashier, error) +// GetCashiersByBranch(ctx context.Context, branchID int64) ([]domain.User, error) +// GetAdminByCompanyID(ctx context.Context, companyID int64) (domain.User, error) +// UpdateUser(ctx context.Context, user domain.UpdateUserReq) error +// UpdateUserCompany(ctx context.Context, id int64, companyID int64) error +// UpdateUserSuspend(ctx context.Context, id int64, status bool) error +// DeleteUser(ctx context.Context, id int64) error +// CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error) +// GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (domain.User, error) +// GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error) +// SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) +// UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64, companyId int64) error + +// GetCustomerCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) +// GetCustomerDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.CustomerDetail, error) +// GetBranchCustomerCounts(ctx context.Context, filter domain.ReportFilter) (map[int64]int64, error) +// GetRoleCounts(ctx context.Context, role string, filter domain.ReportFilter) (total, active, inactive int64, err error) +// } +// type SmsGateway interface { +// SendSMSOTP(ctx context.Context, phoneNumber, otp string) error +// } +// type EmailGateway interface { +// SendEmailOTP(ctx context.Context, email string, otp string) error +// } +// type OtpStore interface { +// CreateOtp(ctx context.Context, otp domain.Otp) error +// GetOtp(ctx context.Context, sentTo string, sentfor domain.OtpFor, medium domain.OtpMedium) (domain.Otp, error) +// } diff --git a/internal/services/user/service.go b/internal/services/user/service.go index 0ad970f..2e9fbb6 100644 --- a/internal/services/user/service.go +++ b/internal/services/user/service.go @@ -4,6 +4,7 @@ import ( "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/config" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger" ) @@ -12,15 +13,15 @@ const ( ) type Service struct { - userStore UserStore - otpStore OtpStore + userStore ports.UserStore + otpStore ports.OtpStore messengerSvc *messenger.Service config *config.Config } func NewService( - userStore UserStore, - otpStore OtpStore, + userStore ports.UserStore, + otpStore ports.OtpStore, messengerSvc *messenger.Service, cfg *config.Config, ) *Service { diff --git a/internal/services/virtualGame/atlas/service.go b/internal/services/virtualGame/atlas/service.go index 36c2c29..39a1c7b 100644 --- a/internal/services/virtualGame/atlas/service.go +++ b/internal/services/virtualGame/atlas/service.go @@ -8,6 +8,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" @@ -18,11 +19,11 @@ type Service struct { repo repository.VirtualGameRepository client *Client walletSvc *wallet.Service - transfetStore wallet.TransferStore + transfetStore ports.TransferStore cfg *config.Config } -func New(virtualGameSvc virtualgameservice.VirtualGameService, repo repository.VirtualGameRepository, client *Client, walletSvc *wallet.Service, transferStore wallet.TransferStore, cfg *config.Config) *Service { +func New(virtualGameSvc virtualgameservice.VirtualGameService, repo repository.VirtualGameRepository, client *Client, walletSvc *wallet.Service, transferStore ports.TransferStore, cfg *config.Config) *Service { return &Service{ virtualGameSvc: virtualGameSvc, repo: repo, diff --git a/internal/services/virtualGame/veli/service.go b/internal/services/virtualGame/veli/service.go index cea093e..fb2ece4 100644 --- a/internal/services/virtualGame/veli/service.go +++ b/internal/services/virtualGame/veli/service.go @@ -12,6 +12,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" @@ -30,7 +31,7 @@ type Service struct { repo repository.VirtualGameRepository client *Client walletSvc *wallet.Service - transfetStore wallet.TransferStore + transfetStore ports.TransferStore mongoLogger *zap.Logger cfg *config.Config } @@ -40,7 +41,7 @@ func New( repo repository.VirtualGameRepository, client *Client, walletSvc *wallet.Service, - transferStore wallet.TransferStore, + transferStore ports.TransferStore, mongoLogger *zap.Logger, cfg *config.Config, ) *Service { diff --git a/internal/services/wallet/interface.go b/internal/services/wallet/interface.go new file mode 100644 index 0000000..0a9c88b --- /dev/null +++ b/internal/services/wallet/interface.go @@ -0,0 +1,56 @@ +package wallet + +// import ( +// "context" + +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// ) + +// type WalletStore interface { +// GetCompanyByWalletID(ctx context.Context, walletID int64) (domain.Company, error) +// GetBranchByWalletID(ctx context.Context, walletID int64) (domain.Branch, error) +// CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) +// CreateCustomerWallet(ctx context.Context, customerWallet domain.CreateCustomerWallet) (domain.CustomerWallet, error) +// GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error) +// GetAllWallets(ctx context.Context) ([]domain.Wallet, error) +// GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wallet, error) +// GetAllCustomerWallets(ctx context.Context) ([]domain.GetCustomerWallet, error) +// GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) +// GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error) +// UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error +// UpdateWalletActive(ctx context.Context, id int64, isActive bool) error + +// GetBalanceSummary(ctx context.Context, filter domain.ReportFilter) (domain.BalanceSummary, error) +// GetTotalWallets(ctx context.Context, filter domain.ReportFilter) (int64, error) +// } + +// type TransferStore interface { +// CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) +// GetAllTransfers(ctx context.Context) ([]domain.TransferDetail, error) +// GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.TransferDetail, error) +// GetTransferByReference(ctx context.Context, reference string) (domain.TransferDetail, error) +// GetTransferByID(ctx context.Context, id int64) (domain.TransferDetail, error) +// GetTransferStats(ctx context.Context, walletID int64) (domain.TransferStats, error) +// UpdateTransferVerification(ctx context.Context, id int64, verified bool) error +// UpdateTransferStatus(ctx context.Context, id int64, status string) error +// // InitiateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) +// // ApproveTransfer(ctx context.Context, approval domain.ApprovalRequest) error +// // RejectTransfer(ctx context.Context, approval domain.ApprovalRequest) error +// // GetPendingApprovals(ctx context.Context) ([]domain.TransferDetail, error) +// // GetTransferApprovalHistory(ctx context.Context, transferID int64) ([]domain.TransactionApproval, error) +// } + +// type ApprovalStore interface { +// CreateApproval(ctx context.Context, approval domain.TransactionApproval) error +// UpdateApprovalStatus(ctx context.Context, approvalID int64, status string, comments string) error +// GetApprovalsByTransfer(ctx context.Context, transferID int64) ([]domain.TransactionApproval, error) +// GetPendingApprovals(ctx context.Context) ([]domain.TransferDetail, error) +// } + +// type DirectDepositStore interface { +// CreateDirectDeposit(ctx context.Context, deposit domain.CreateDirectDeposit) (domain.DirectDeposit, error) +// GetDirectDeposit(ctx context.Context, id int64) (domain.DirectDeposit, error) +// UpdateDirectDeposit(ctx context.Context, deposit domain.UpdateDirectDeposit) (domain.DirectDeposit, error) +// GetDirectDepositsByStatus(ctx context.Context, status domain.DirectDepositStatus) ([]domain.DirectDeposit, error) +// GetCustomerDirectDeposits(ctx context.Context, customerID int64) ([]domain.DirectDeposit, error) +// } diff --git a/internal/services/wallet/service.go b/internal/services/wallet/service.go index 27616cb..08d4e8f 100644 --- a/internal/services/wallet/service.go +++ b/internal/services/wallet/service.go @@ -4,6 +4,7 @@ import ( "log/slog" // "github.com/SamuelTariku/FortuneBet-Backend/internal/services/kafka" + "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" "go.uber.org/zap" @@ -11,9 +12,9 @@ import ( type Service struct { // approvalStore ApprovalStore - walletStore WalletStore - transferStore TransferStore - directDepositStore DirectDepositStore + walletStore ports.WalletStore + transferStore ports.TransferStore + directDepositStore ports.DirectDepositStore notificationSvc *notificationservice.Service userSvc *user.Service mongoLogger *zap.Logger @@ -21,9 +22,9 @@ type Service struct { } func NewService( - walletStore WalletStore, - transferStore TransferStore, - directDepositStore DirectDepositStore, + walletStore ports.WalletStore, + transferStore ports.TransferStore, + directDepositStore ports.DirectDepositStore, notificationSvc *notificationservice.Service, userSvc *user.Service, mongoLogger *zap.Logger, @@ -34,9 +35,9 @@ func NewService( transferStore: transferStore, directDepositStore: directDepositStore, // approvalStore: approvalStore, - notificationSvc: notificationSvc, - userSvc: userSvc, - mongoLogger: mongoLogger, - logger: logger, + notificationSvc: notificationSvc, + userSvc: userSvc, + mongoLogger: mongoLogger, + logger: logger, } } diff --git a/internal/web_server/app.go b/internal/web_server/app.go index 2752aee..776f580 100644 --- a/internal/web_server/app.go +++ b/internal/web_server/app.go @@ -26,6 +26,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/santimpay" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/stats" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/telebirr" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction" @@ -62,7 +63,7 @@ type App struct { logger *slog.Logger NotidicationStore *notificationservice.Service referralSvc *referralservice.Service - raffleSvc raffle.RaffleStore + raffleSvc *raffle.Service bonusSvc *bonus.Service port int settingSvc *settings.Service @@ -70,7 +71,7 @@ type App struct { userSvc *user.Service betSvc *bet.Service virtualGameSvc virtualgameservice.VirtualGameService - reportSvc *report.Service + reportSvc report.ReportService chapaSvc *chapa.Service walletSvc *wallet.Service transactionSvc *transaction.Service @@ -84,6 +85,7 @@ type App struct { eventSvc *event.Service leagueSvc *league.Service resultSvc *result.Service + statSvc *stats.Service mongoLoggerSvc *zap.Logger } @@ -105,7 +107,7 @@ func NewApp( userSvc *user.Service, ticketSvc *ticket.Service, betSvc *bet.Service, - reportSvc *report.Service, + reportSvc report.ReportService, chapaSvc *chapa.Service, walletSvc *wallet.Service, transactionSvc *transaction.Service, @@ -116,13 +118,14 @@ func NewApp( eventSvc *event.Service, leagueSvc *league.Service, referralSvc *referralservice.Service, - raffleSvc raffle.RaffleStore, + raffleSvc *raffle.Service, bonusSvc *bonus.Service, virtualGameSvc virtualgameservice.VirtualGameService, aleaVirtualGameService alea.AleaVirtualGameService, // veliVirtualGameService veli.VeliVirtualGameService, recommendationSvc recommendation.RecommendationService, resultSvc *result.Service, + statSvc *stats.Service, cfg *config.Config, mongoLoggerSvc *zap.Logger, ) *App { @@ -182,6 +185,7 @@ func NewApp( // veliVirtualGameService: veliVirtualGameService, recommendationSvc: recommendationSvc, resultSvc: resultSvc, + statSvc: statSvc, cfg: cfg, mongoLoggerSvc: mongoLoggerSvc, } diff --git a/internal/web_server/cron.go b/internal/web_server/cron.go index 3425e42..f5107de 100644 --- a/internal/web_server/cron.go +++ b/internal/web_server/cron.go @@ -3,6 +3,7 @@ package httpserver import ( "context" "os" + "time" // "time" @@ -10,20 +11,26 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" betSvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/company" + enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse" eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/report" resultsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/stats" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" "github.com/robfig/cron/v3" "go.uber.org/zap" ) -func StartDataFetchingCrons(eventService *eventsvc.Service, oddsService oddssvc.ServiceImpl, resultService *resultsvc.Service, mongoLogger *zap.Logger) { +func StartBetAPIDataFetchingCrons( + eventService *eventsvc.Service, + oddsService oddssvc.ServiceImpl, + resultService *resultsvc.Service, + mongoLogger *zap.Logger, +) { c := cron.New(cron.WithSeconds()) schedule := []struct { @@ -33,65 +40,98 @@ func StartDataFetchingCrons(eventService *eventsvc.Service, oddsService oddssvc. { spec: "0 0 * * * *", // Every 1 hour task: func() { - mongoLogger.Info("Began fetching upcoming events cron task") + start := time.Now() + mongoLogger.Info("[BetAPI Event Fetching Crons] Began fetching upcoming events cron task", zap.Time("timestamp", time.Now())) if err := eventService.FetchUpcomingEvents(context.Background()); err != nil { - mongoLogger.Error("Failed to fetch upcoming events", + mongoLogger.Error("[BetAPI Event Fetching Crons] Failed to fetch upcoming events", + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), zap.Error(err), ) } else { - mongoLogger.Info("Completed fetching upcoming events without errors") + mongoLogger.Info("[BetAPI Event Fetching Crons] Completed fetching upcoming events without errors", + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), + ) + } + }, + }, + { + spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events) + task: func() { + start := time.Now() + mongoLogger.Info("[BetAPI Pre-Match Odds Fetching Crons] Began fetching pre-match odds cron task", + zap.Time("timestamp", time.Now()), + ) + if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil { + mongoLogger.Error("[BetAPI Pre-Match Odds Fetching Crons] Failed to fetch pre-match odds", + zap.Error(err), + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), + ) + } else { + mongoLogger.Info("[BetAPI Pre-Match Odds Fetching Crons] Completed fetching pre-match odds without errors", + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), + ) + } + }, + }, + { + spec: "0 */5 * * * *", // Every 5 Minutes + task: func() { + start := time.Now() + mongoLogger.Info("[BetAPI Check And Update Expired Events Crons] Began update all expired events status cron task", + zap.Time("timestamp", time.Now()), + ) + if _, err := resultService.CheckAndUpdateExpiredB365Events(context.Background()); err != nil { + mongoLogger.Error("[BetAPI Check And Update Expired Events Crons] Failed to update expired events status", + zap.Error(err), + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), + ) + } else { + mongoLogger.Info("[BetAPI Check And Update Expired Events Crons] Completed expired events without errors", + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), + ) + } + }, + }, + { + spec: "0 */15 * * * *", // Every 15 Minutes + task: func() { + start := time.Now() + mongoLogger.Info("[BetAPI Fetch Result and Update Bets Crons] Began updating bets based on event results cron task") + if err := resultService.FetchB365ResultAndUpdateBets(context.Background()); err != nil { + mongoLogger.Error("[BetAPI Fetch Result and Update Bets Crons] Failed to process result", + zap.Error(err), + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), + ) + } else { + mongoLogger.Info("[BetAPI Fetch Result and Update Bets Crons] Completed processing all event result outcomes without errors", + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), + ) } }, }, // { - // spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events) - // task: func() { - // mongoLogger.Info("Began fetching non live odds cron task") - // if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil { - // mongoLogger.Error("Failed to fetch non live odds", - // zap.Error(err), - // ) - // } else { - // mongoLogger.Info("Completed fetching non live odds without errors") - // } - // }, - // }, - // { - // spec: "0 */5 * * * *", // Every 5 Minutes - // task: func() { - // mongoLogger.Info("Began update all expired events status cron task") - // if _, err := resultService.CheckAndUpdateExpiredB365Events(context.Background()); err != nil { - // mongoLogger.Error("Failed to update expired events status", - // zap.Error(err), - // ) - // } else { - // mongoLogger.Info("Completed expired events without errors") - // } - // }, - // }, - // { - // spec: "0 */15 * * * *", // Every 15 Minutes - // task: func() { - // mongoLogger.Info("Began updating bets based on event results cron task") - // if err := resultService.FetchB365ResultAndUpdateBets(context.Background()); err != nil { - // mongoLogger.Error("Failed to process result", - // zap.Error(err), - // ) - // } else { - // mongoLogger.Info("Completed processing all event result outcomes without errors") - // } - // }, - // }, - // { // spec: "0 0 0 * * 1", // Every Monday // task: func() { - // mongoLogger.Info("Began Send weekly result notification cron task") + // mongoLogger.Info("[BetAPI Send Result Notification Crons] Began Send weekly result notification cron task", + // zap.Time("timestamp", time.Now()), + // ) // if err := resultService.CheckAndSendResultNotifications(context.Background(), time.Now().Add(-7*24*time.Hour)); err != nil { - // mongoLogger.Error("Failed to process result", + // mongoLogger.Error("[BetAPI Send Result Notification Crons] Failed to process result", + // zap.Time("timestamp", time.Now()), // zap.Error(err), // ) // } else { - // mongoLogger.Info("Completed sending weekly result notification without errors") + // mongoLogger.Info("[BetAPI Send Result Notification Crons] Completed sending weekly result notification without errors", + // zap.Time("timestamp", time.Now()), + // ) // } // }, // }, @@ -100,14 +140,17 @@ func StartDataFetchingCrons(eventService *eventsvc.Service, oddsService oddssvc. for _, job := range schedule { job.task() if _, err := c.AddFunc(job.spec, job.task); err != nil { - mongoLogger.Error("Failed to schedule data fetching cron job", + mongoLogger.Error("[BetAPI Data Fetching Crons] Failed to schedule data fetching cron job", + zap.Time("timestamp", time.Now()), zap.Error(err), ) } } c.Start() - mongoLogger.Info("Data Fetching Cron jobs started") + mongoLogger.Info("[BetAPI Data Fetching Crons] Started Data Fetching Cron jobs", + zap.Time("timestamp", time.Now()), + ) } func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificationservice.Service, mongoLogger *zap.Logger) { @@ -120,26 +163,32 @@ func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificati { spec: "0 0 * * * *", // Every hour task: func() { - mongoLogger.Info("Deleting old tickets") + mongoLogger.Info("[Delete Old Ticket Crons] Deleting old tickets", zap.Time("timestamp", time.Now())) if err := ticketService.DeleteOldTickets(context.Background()); err != nil { - mongoLogger.Error("Failed to remove old ticket", + mongoLogger.Error("[Delete Old Ticket Crons] Failed to remove old ticket", zap.Error(err), + zap.Time("timestamp", time.Now()), ) } else { - mongoLogger.Info("Successfully deleted old tickets") + mongoLogger.Info("[Delete Old Ticket Crons] Successfully deleted old tickets", + zap.Time("timestamp", time.Now()), + ) } }, }, { spec: "0 0 0 * * 1", // Every Monday (Weekly) task: func() { - mongoLogger.Info("Deleting old notifications") + mongoLogger.Info("[Delete Old Notification Crons] Deleting old notifications") if err := notificationSvc.DeleteOldNotifications(context.Background()); err != nil { mongoLogger.Error("Failed to remove old notifications", + zap.Time("timestamp", time.Now()), zap.Error(err), ) } else { - mongoLogger.Info("Successfully deleted old notifications") + mongoLogger.Info("[Delete Old Notification Crons] Successfully deleted old notifications", + zap.Time("timestamp", time.Now()), + ) } }, }, @@ -147,17 +196,20 @@ func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificati for _, job := range schedule { if _, err := c.AddFunc(job.spec, job.task); err != nil { - mongoLogger.Error("Failed to schedule ticket cron job", + mongoLogger.Error("[Start Cleanup Crons] Failed to schedule cleanup cron job", + zap.Time("timestamp", time.Now()), zap.Error(err), ) } } c.Start() - mongoLogger.Info("Cron jobs started for ticket service") + mongoLogger.Info("[Start Cleanup Crons] Started cleanup cron jobs", + zap.Time("timestamp", time.Now()), + ) } -func StartStatCrons(companyService company.Service, eventService *eventsvc.Service, mongoLogger *zap.Logger) { +func StartStatCrons(statService *stats.Service, mongoLogger *zap.Logger) { c := cron.New(cron.WithSeconds()) schedule := []struct { @@ -167,26 +219,37 @@ func StartStatCrons(companyService company.Service, eventService *eventsvc.Servi { spec: "0 0 * * * *", // Every hour task: func() { - mongoLogger.Info("[Company Stats Crons] Updating company stats") - if err := companyService.UpdateCompanyStats(context.Background()); err != nil { + start := time.Now() + mongoLogger.Info("[Company Stats Crons] Updating company stats", zap.Time("timestamp", time.Now())) + if err := statService.UpdateCompanyStats(context.Background()); err != nil { mongoLogger.Error("[Company Stats Crons] Failed to update company stats", zap.Error(err), + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), ) } else { - mongoLogger.Info("[Company Stats Crons] Successfully updated company stats") + mongoLogger.Info("[Company Stats Crons] Successfully updated company stats", + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), + ) } }, }, { spec: "0 0 * * * *", // Hourly task: func() { + start := time.Now() mongoLogger.Info("[Event Stats Crons] Updating event stats") - if err := eventService.UpdateEventBetStats(context.Background()); err != nil { + if err := statService.UpdateEventBetStats(context.Background()); err != nil { mongoLogger.Error("[Event Stats Crons] Failed to update event bet stats", zap.Error(err), + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start)), ) } else { - mongoLogger.Info("[Event Stats Crons] Successfully updated event stats") + mongoLogger.Info("[Event Stats Crons] Successfully updated event stats", + zap.Time("timestamp", time.Now()), + zap.Duration("duration", time.Since(start))) } }, }, @@ -195,17 +258,18 @@ func StartStatCrons(companyService company.Service, eventService *eventsvc.Servi for _, job := range schedule { job.task() if _, err := c.AddFunc(job.spec, job.task); err != nil { - mongoLogger.Error("[Stats Crons] Failed to schedule stats cron job", + mongoLogger.Error("[Starting Stats Crons] Failed to schedule stats cron job", zap.Error(err), + zap.Time("timestamp", time.Now()), ) } } c.Start() - mongoLogger.Info("Cron jobs started for stats") + mongoLogger.Info("Starting Stats Crons] Started Stat Cron Jobs", zap.Time("timestamp", time.Now())) } -func StartReportCrons(reportService *report.Service, mongoLogger *zap.Logger) { +func StartReportCrons(reportService report.ReportService, mongoLogger *zap.Logger) { c := cron.New(cron.WithSeconds()) schedule := []struct { @@ -231,19 +295,20 @@ func StartReportCrons(reportService *report.Service, mongoLogger *zap.Logger) { job.task() if _, err := c.AddFunc(job.spec, job.task); err != nil { mongoLogger.Error("[Report Crons] Failed to schedule report cron job", + zap.Time("timestamp", time.Now()), zap.Error(err), ) } } c.Start() - mongoLogger.Info("[Report Crons] Cron jobs started for reports") + mongoLogger.Info("[Report Crons] Cron jobs started for reports", zap.Time("timestamp", time.Now())) } // SetupReportCronJobs schedules periodic report generation func SetupReportandVirtualGameCronJobs( ctx context.Context, - reportService *report.Service, + reportService report.ReportService, virtualGameService *veli.Service, // inject your virtual game service outputDir string, ) { diff --git a/internal/web_server/handlers/event_stats_handler.go b/internal/web_server/handlers/event_stats_handler.go index b75c300..db3d29c 100644 --- a/internal/web_server/handlers/event_stats_handler.go +++ b/internal/web_server/handlers/event_stats_handler.go @@ -44,7 +44,7 @@ func (h *Handler) GetTotalEventStats(c *fiber.Ctx) error { } } - stats, err := h.eventSvc.GetTotalEventStats(c.Context(), domain.EventStatsFilter{ + stats, err := h.statSvc.GetTotalEventStats(c.Context(), domain.EventStatsFilter{ LeagueID: leagueID, SportID: sportID, }) @@ -103,7 +103,7 @@ func (h *Handler) GetTotalEventStatsByInterval(c *fiber.Ctx) error { } } - stats, err := h.eventSvc.GetTotalEventStatsByInterval(c.Context(), domain.EventStatsByIntervalFilter{ + stats, err := h.statSvc.GetTotalEventStatsByInterval(c.Context(), domain.EventStatsByIntervalFilter{ Interval: domain.ValidDateInterval{ Value: interval, Valid: true, diff --git a/internal/web_server/handlers/handlers.go b/internal/web_server/handlers/handlers.go index e235e25..eade53c 100644 --- a/internal/web_server/handlers/handlers.go +++ b/internal/web_server/handlers/handlers.go @@ -26,6 +26,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/services/result" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/santimpay" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" + "github.com/SamuelTariku/FortuneBet-Backend/internal/services/stats" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/telebirr" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction" @@ -53,9 +54,9 @@ type Handler struct { notificationSvc *notificationservice.Service userSvc *user.Service referralSvc *referralservice.Service - raffleSvc raffle.RaffleStore + raffleSvc *raffle.Service bonusSvc *bonus.Service - reportSvc *report.Service + reportSvc report.ReportService chapaSvc *chapa.Service walletSvc *wallet.Service transactionSvc *transaction.Service @@ -73,6 +74,7 @@ type Handler struct { recommendationSvc recommendation.RecommendationService authSvc *authentication.Service resultSvc result.Service + statSvc *stats.Service jwtConfig jwtutil.JwtConfig validator *customvalidator.CustomValidator Cfg *config.Config @@ -91,11 +93,11 @@ func New( settingSvc *settings.Service, notificationSvc *notificationservice.Service, validator *customvalidator.CustomValidator, - reportSvc *report.Service, + reportSvc report.ReportService, chapaSvc *chapa.Service, walletSvc *wallet.Service, referralSvc *referralservice.Service, - raffleSvc raffle.RaffleStore, + raffleSvc *raffle.Service, bonusSvc *bonus.Service, virtualGameSvc virtualgameservice.VirtualGameService, aleaVirtualGameSvc alea.AleaVirtualGameService, @@ -114,6 +116,7 @@ func New( eventSvc *event.Service, leagueSvc *league.Service, resultSvc result.Service, + statSvc *stats.Service, cfg *config.Config, mongoLoggerSvc *zap.Logger, ) *Handler { @@ -151,6 +154,7 @@ func New( recommendationSvc: recommendationSvc, authSvc: authSvc, resultSvc: resultSvc, + statSvc: statSvc, jwtConfig: jwtConfig, Cfg: cfg, mongoLoggerSvc: mongoLoggerSvc, diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index 312b1e0..355c983 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -53,7 +53,8 @@ func (a *App) initAppRoutes() { a.prematchSvc, a.eventSvc, a.leagueSvc, - *a.resultSvc, + *a.resultSvc, + a.statSvc, a.cfg, a.mongoLoggerSvc, )