diff --git a/README.md b/README.md index 59486f8..5668932 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# FortuneBet Backend +# Yimaru Backend -FortuneBet Backend is the server-side application that powers the FortuneBet system. It fetches and processes odds and results for various sports (e.g. football, basketball, ice hockey), manages upcoming events, evaluates bet outcomes, as well as managing the different branches and companies that will be using the system. +Yimaru Backend is the server-side application that powers the Yimaru online learning system. It manages courses, lessons, quizzes, student progress, instructor content, and administrative operations for institutions and users on the platform. ## Table of Contents @@ -16,6 +16,7 @@ FortuneBet Backend is the server-side application that powers the FortuneBet sys - [Code Organization Guidelines](#code-organization-guidelines) - [Further Details](#further-details) +--- ## Installation Before running the application, ensure you have the following installed: @@ -30,223 +31,41 @@ Before running the application, ensure you have the following installed: Clone the repository: ```bash -git clone https://github.com/your-org/fortunebet-backend.git -cd fortunebet-backend -``` -Then you will need to setup the database, which you can do using: -```bash -make db-up -``` +git clone https://github.com/your-org/Yimaru-backend.git +cd Yimaru-backend -You will also need to setup the necessary seed_data using: -```bash -make seed_data -``` - -## Environment Configuration -Create a .env file in the root directory. This file is critical for running the application as it contains database credentials, secret keys, third-party integrations, and configuration flags. - -Sample variables you might include: -```ini -ENV=development -PORT=8080 -DB_URL=postgresql://root:secret@localhost:5422/gh?sslmode=disable -REFRESH_EXPIRY= -JWT_KEY= -ACCESS_EXPIRY= -LOG_LEVEL=debug -AFRO_SMS_API_KEY= -AFRO_SMS_SENDER_NAME= -AFRO_SMS_RECEIVER_PHONE_NUMBER= -BET365_TOKEN= -POPOK_CLIENT_ID= -POPOK_SECRET_KEY= -POPOK_BASE_URL= -POPOK_CALLBACK_URL= -``` - -## Running the Application -To run the application: -```bash -make run -``` - -To start the server with live reload (using air): -```bash -make air -``` - -To build the binary: -```bash -make build -``` - -## Database Management -Start the database using Docker Compose: -```bash -make db-up -``` - -Stop it: -```bash -make db-down -``` - -Create a new migration: -```bash -make migrations/new name=add_new_table -``` -Apply all pending migrations: -```bash -make migrations/up -``` - -## Testing and Code Quality -Run all tests: - -```bash -make test -``` - -Generate a coverage report: -```bash -make coverage -``` - -## API Documentation -Generate Swagger docs: - -```bash -make swagger -``` -This will output files to the docs/ directory. - - -## Project Structure - -```bash ├── cmd/ │ └── main.go # Application entry point ├── internal/ │ ├── config/ # Configuration and environment loading │ │ └── config.go │ ├── domain/ # Domain models, constants, and roles -│ │ ├── sportmarket.go # Market types (e.g., FootballMarket) -│ │ └── roles.go # Role definitions (e.g., RoleSuperAdmin, RoleAdmin, etc.) +│ │ ├── course.go # Course and lesson structures +│ │ └── roles.go # Role definitions (e.g., RoleAdmin, RoleInstructor) │ ├── repository/ # Database interaction layer -│ │ ├── branch.go # Branch related queries and conversion functions -│ │ ├── company.go # Company-related database functions +│ │ ├── course.go # Course-related queries +│ │ ├── lesson.go # Lesson-related database functions │ │ ├── user.go # User repository methods │ │ └── ... # Other repository files │ ├── services/ # Business logic and core services -│ │ ├── odds/ -│ │ │ └── service.go # Fetching odds from external APIs -│ │ └── result/ -│ │ ├── service.go # Parsing and storing results -│ │ └── eval.go # Evaluation logic for bet outcomes +│ │ ├── course/ +│ │ │ └── service.go # Course management logic +│ │ └── quiz/ +│ │ ├── service.go # Quiz management and evaluation logic +│ │ └── eval.go # Evaluation logic for student progress │ └── web_server/ # HTTP handlers, routes, middleware, and cron jobs │ ├── cron.go # Scheduled background tasks │ └── handlers/ -│ ├── branch_handler.go # Branch-related API endpoints -│ ├── manager.go # Manager-related endpoints -│ └── ... # Additional handlers (e.g., for companies, users, etc.) +│ ├── course_handler.go # Course-related API endpoints +│ ├── user_handler.go # User-related endpoints +│ └── ... # Additional handlers ├── db/ │ └── migrations/ # SQL migration files -│ ├── 000001_fortune.up.sql -│ └── ... # Further migration files ├── docs/ │ ├── swagger/ # Swagger/OpenAPI documentation files -│ │ ├── swagger.yaml -│ │ └── swagger.json -│ └── docs.go # Swaggo-generated docs (do not edit manually) +│ └── docs.go # Swaggo-generated docs ├── gen/ │ └── db/ # SQLC-generated Go code for database access -│ └── ... # Generated files (e.g., queries.go) ├── makefile # Development and operations commands -├── .env # Environment configuration file (not checked in) +├── .env # Environment configuration file └── README.md # Project documentation -``` - - -## Core Services Overview - -FortuneBet includes a wide suite of services for a modern betting platform: - -- **Notification Service**: Sends alerts to users/admins about odds changes, results, etc. -- **Ticket System**: Manages bet tickets, results, and statuses. -- **Temporary Ticket QR/Shortcode System**: Fast bet placement and redemption. -- **Fraud Prevention**: Detects suspicious activity and prevents account abuse. -- **Bet Expiration**: Bets expire after one year; claims must be handled manually. -- **Self-Protection**: Limits bets based on user behavior to promote responsible betting. -- **Betting System**: Core logic for placing, modifying, and canceling bets. -- **Bet365 Odds Integration**: Real-time odds sync with Bet365. -- **Event Disabling**: Admins can disable events (e.g. suspicious or restricted). -- **Odds Management**: Admins can dynamically adjust odds. -- **Odds Update Log**: Track and audit all odds changes. -- **Game Management**: Remove games or leagues from the market. -- **Wallet System**: Handles transactions, deposits, and withdrawals. -- **Super Admin Panel**: Master control for the whole system. -- **Branch Manager Dashboard**: Local control for branches and terminals. -- **User Management**: User roles, activity tracking, limits. -- **Live Betting**: Real-time betting during live matches. -- **Cashout System**: Enables early bet redemption. -- **Bonus & Promotions**: Incentivize users with custom promotions. -- **Risk Analysis**: Detect high-risk bets and patterns. -- **Affiliate & Referrals**: Referral tracking and commission. -- **Reports & Analytics**: Generate operational insights. -- **Compliance Tools**: Support for KYC, AML, and legal requirements. - - -## Roles and Access Control -FortuneBet uses role-based access control to secure routes and platform features. The defined roles are: -```go -package domain - -type Role string - -const ( - RoleSuperAdmin Role = "super_admin" - RoleAdmin Role = "admin" - RoleBranchManager Role = "branch_manager" - RoleCustomer Role = "customer" - RoleCashier Role = "cashier" -) -``` - -Super Admin — Full system control - -Admin — Represents a company admin that controls everything for company - -Branch Manager — Handles local branch users and operations - -Customer — Places and tracks bets - -Cashier — Handles customer funds and transactions in branches - -These roles determine route access and permissions in the admin interface. - - -## Code Organization Guidelines - -- **Services**: - Add new business logic or functionality under the `/internal/services` directory. - For example, create a new service in `/internal/services/your_service/service.go`. - -- **Routes and Handlers**: - Define new HTTP routes and API endpoint logic inside `/internal/web_server/handlers`. - Create separate files for different modules (e.g., users, companies, branches, etc.). - -- **SQL Queries and Code Generation (SQLC)**: - Write your raw SQL queries in the `/db/query` directory. - Then generate type-safe Go code with SQLC by running: - ```bash - make sqlc-gen - ``` - -## Further Details -Our long-term goal is to become a trusted betting provider, offering white-label sportsbook solutions for other operators to use in their shops and platforms. - -Planned Features: -- Multi-language and currency support -- Full virtual games integration -- Regulatory audit logging diff --git a/cmd/main.go b/cmd/main.go index af32dba..8b70fe8 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -4,7 +4,22 @@ import ( // "context" // "context" - "context" + "Yimaru-Backend/internal/config" + "Yimaru-Backend/internal/domain" + customlogger "Yimaru-Backend/internal/logger" + "Yimaru-Backend/internal/logger/mongoLogger" + "Yimaru-Backend/internal/repository" + "Yimaru-Backend/internal/services/arifpay" + "Yimaru-Backend/internal/services/authentication" + "Yimaru-Backend/internal/services/currency" + notificationservice "Yimaru-Backend/internal/services/notification" + "Yimaru-Backend/internal/services/recommendation" + referralservice "Yimaru-Backend/internal/services/referal" + "Yimaru-Backend/internal/services/transaction" + "Yimaru-Backend/internal/services/user" + httpserver "Yimaru-Backend/internal/web_server" + jwtutil "Yimaru-Backend/internal/web_server/jwt" + customvalidator "Yimaru-Backend/internal/web_server/validator" "fmt" "log" "log/slog" @@ -14,66 +29,11 @@ import ( "github.com/go-playground/validator/v10" "go.uber.org/zap" - - // "github.com/gofiber/fiber/v2" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - customlogger "github.com/SamuelTariku/FortuneBet-Backend/internal/logger" - "github.com/SamuelTariku/FortuneBet-Backend/internal/logger/mongoLogger" - - // mongologger "github.com/SamuelTariku/FortuneBet-Backend/internal/logger/mongoLogger" - - // "github.com/SamuelTariku/FortuneBet-Backend/internal/logger/mongoLogger" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" - - // "github.com/SamuelTariku/FortuneBet-Backend/internal/router" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/arifpay" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bonus" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/company" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/currency" - directdeposit "github.com/SamuelTariku/FortuneBet-Backend/internal/services/direct_deposit" - enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions" - issuereporting "github.com/SamuelTariku/FortuneBet-Backend/internal/services/issue_reporting" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/league" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger" - notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/raffle" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/recommendation" - referralservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/referal" - "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" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" - virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" - alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/atlas" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/orchestration" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet/monitor" - - // "github.com/SamuelTariku/FortuneBet-Backend/internal/utils" - httpserver "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server" - jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" - customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator" ) -// @title FortuneBet API +// @title Yimaru API // @version 1.0.1 -// @description This is server for FortuneBet. +// @description This is server for Yimaru. // @termsOfService http://swagger.io/terms/ // @contact.name API Support // @contact.url http://www.swagger.io/support @@ -111,21 +71,18 @@ func main() { v := customvalidator.NewCustomValidator(validator.New()) // Initialize services - settingRepo := repository.NewSettingStore(store) + // settingRepo := repository.NewSettingStore(store) - if err := settingRepo.EnsureAllSettingsExist(context.Background()); err != nil { - log.Fatalf("failed to ensure settings: %v", err) - } - settingSvc := settings.NewService(settingRepo) + // if err := settingRepo.EnsureAllSettingsExist(context.Background()); err != nil { + // log.Fatalf("failed to ensure settings: %v", err) + // } + // settingSvc := settings.NewService(settingRepo) - messengerSvc := messenger.NewService(settingSvc, cfg) - statSvc := stats.NewService( - repository.NewCompanyStatStore(store), - repository.NewBranchStatStore(store), - repository.NewEventStatStore(store), - repository.NewBetStatStore(store), - repository.NewWalletStatStore(store), - ) + // messengerSvc := messenger.NewService(settingSvc, cfg) + // statSvc := stats.NewService( + // repository.NewCompanyStatStore(store), + // repository.NewBranchStatStore(store), + // ) authSvc := authentication.NewService( repository.NewUserStore(store), @@ -135,34 +92,34 @@ func main() { 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, + // messengerSvc, cfg, ) + // leagueSvc := league.New(repository.NewLeagueStore(store)) + // eventSvc := event.New( + // cfg.Bet365Token, + // repository.NewEventStore(store), + // repository.NewEventHistoryStore(store), + // *leagueSvc, + // settingSvc, + // domain.MongoDBLogger, + // cfg, + // ) - marketSettingRepo := repository.NewMarketSettingStore(store) + // marketSettingRepo := repository.NewMarketSettingStore(store) - if err := marketSettingRepo.EnsureAllMarketSettingsExist(context.Background()); err != nil { - log.Fatalf("failed to ensure market settings: %v", err) - } + // if err := marketSettingRepo.EnsureAllMarketSettingsExist(context.Background()); err != nil { + // log.Fatalf("failed to ensure market settings: %v", err) + // } - oddsSvc := odds.New( - repository.NewOddStore(store), - marketSettingRepo, - cfg, - eventSvc, - logger, - domain.MongoDBLogger, - ) + // oddsSvc := odds.New( + // repository.NewOddStore(store), + // marketSettingRepo, + // cfg, + // eventSvc, + // logger, + // domain.MongoDBLogger, + // ) // virtuaGamesRepo := repository.NewVirtualGameRepository(store) @@ -179,67 +136,65 @@ func main() { userSvc, ) - walletSvc := wallet.NewService( - repository.NewWalletStore(store), - repository.NewTransferStore(store), - // repository.NewDirectDepositStore(store), - notificationSvc, - userSvc, - domain.MongoDBLogger, - logger, - ) + // / := wallet.NewService( + // repository.NewWalletStore(store), + // repository.NewTransferStore(store), + // // repository.NewDirectDepositStore(store), + // notificationSvc, + // userSvc, + // domain.MongoDBLogger, + // logger, + // ) 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, - ) + // ticketSvc := ticke.NewService( + // repository.NewTicketStore(store), + // // eventSvc, + // // *oddsSvc, + // domain.MongoDBLogger, + // settingSvc, + // ) + // betSvc := bet.NewService( + // repository.NewBetStore(store), + // eventSvc, + // *oddsSvc, + // , + // *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, - ) + // 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, ) - virtualGamesRepo := repository.NewVirtualGameRepository(store) + // virtualGamesRepo := repository.NewVirtualGameRepository(store) recommendationRepo := repository.NewRecommendationRepository(store) referalSvc := referralservice.New( repository.NewReferralStore(store), - *walletSvc, *settingSvc, cfg, logger, @@ -248,28 +203,26 @@ func main() { raffleSvc := raffle.NewService( repository.NewRaffleStore(store), ) - virtualGameSvc := virtualgameservice.New(virtualGamesRepo, *walletSvc, store, cfg, logger) - aleaService := alea.NewAleaPlayService(virtualGamesRepo, *walletSvc, cfg, logger) - veliCLient := veli.NewClient(cfg, walletSvc) - veliVirtualGameService := veli.New(virtualGameSvc, virtualGamesRepo, *store, veliCLient, walletSvc, repository.NewTransferStore(store), domain.MongoDBLogger, cfg) - orchestrationSvc := orchestration.New( - virtualGameSvc, - virtualGamesRepo, - cfg, - veliCLient, - ) - atlasClient := atlas.NewClient(cfg, walletSvc) - atlasVirtualGameService := atlas.New(virtualGameSvc, virtualGamesRepo, atlasClient, walletSvc, repository.NewTransferStore(store), cfg) + // virtualGameSvc := virtualgameservice.New(virtualGamesRepo,, store, cfg, logger) + // aleaService := alea.NewAleaPlayService(virtualGamesRepo,, cfg, logger) + // veliCLient := veli.NewClient(cfg) + // veliVirtualGameService := veli.New(virtualGameSvc, virtualGamesRepo, *store, veliCLient, repository.NewTransferStore(store), domain.MongoDBLogger, cfg) + // orchestrationSvc := orchestration.New( + // virtualGameSvc, + // virtualGamesRepo, + // cfg, + // veliCLient, + // ) + // atlasClient := atlas.NewClient(cfg) + // atlasVirtualGameService := atlas.New(virtualGameSvc, virtualGamesRepo, atlasClient, repository.NewTransferStore(store), cfg) recommendationSvc := recommendation.NewService(recommendationRepo) - chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY) + // chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY) - chapaSvc := chapa.NewService( - repository.NewTransferStore(store), - *walletSvc, - repository.NewUserStore(store), - cfg, - chapaClient, - ) + // chapaSvc := chapa.NewService( + // repository.NewUserStore(store), + // cfg, + // chapaClient, + // ) currRepo := repository.NewCurrencyPostgresRepository(store) @@ -280,8 +233,6 @@ func main() { transactionSvc := transaction.NewService( repository.NewTransactionStore(store), *branchSvc, - *betSvc, - *walletSvc, *userSvc, ) @@ -303,14 +254,14 @@ func main() { // cfg, // ) - enePulseSvc := enetpulse.New( - *cfg, - store, - ) + // enePulseSvc := enetpulse.New( + // *cfg, + // store, + // ) - go httpserver.StartEnetPulseCron(enePulseSvc, domain.MongoDBLogger) + // go httpserver.StartEnetPulseCron(enePulseSvc, domain.MongoDBLogger) // go httpserver.SetupReportandVirtualGameCronJobs(context.Background(), reportSvc, orchestrationSvc, "C:/Users/User/Desktop") - go httpserver.ProcessBetCashback(context.TODO(), betSvc) + // go httpserver.ProcessBetCashback(context.TODO(), betSvc) bankRepository := repository.NewBankRepository(store) instSvc := institutions.New(bankRepository) @@ -326,27 +277,25 @@ func main() { // Start cron jobs for automated reporting - directdeposit := directdeposit.NewService( - *walletSvc, - repository.NewTransferStore(store), - repository.NewDirectDepositRepository(store), - notificationSvc, - userSvc, - ) + // directdeposit := directdeposit.NewService( + // repository.NewDirectDepositRepository(store), + // notificationSvc, + // userSvc, + // ) - enetPulseSvc := enetpulse.New( - *cfg, - store, - ) + // enetPulseSvc := enetpulse.New( + // *cfg, + // store, + // ) // Initialize wallet monitoring service - walletMonitorSvc := monitor.NewService( - *walletSvc, - *branchSvc, - notificationSvc, - logger, - 5*time.Minute, - ) + // walletMonitorSvc := monitor.NewService( + // , + // *branchSvc, + // notificationSvc, + // logger, + // 5*time.Minute, + // ) currSvc := currency.NewService( currRepo, @@ -357,38 +306,34 @@ func main() { // exchangeWorker := currency.NewExchangeRateWorker(fixerFertcherSvc, logger, cfg) // exchangeWorker.Start(context.Background()) // defer exchangeWorker.Stop() - go walletMonitorSvc.Start() + // go walletMonitorSvc.Start() - httpserver.StartBetAPIDataFetchingCrons(eventSvc, *oddsSvc, resultSvc, domain.MongoDBLogger) - httpserver.StartCleanupCrons(*ticketSvc, notificationSvc, domain.MongoDBLogger) - httpserver.StartStatCrons(statSvc, domain.MongoDBLogger) + // // httpserver.StartBetAPIDataFetchingCrons(eventSvc, *oddsSvc, resultSvc, domain.MongoDBLogger) + // httpserver.StartCleanupCrons(*ticketSvc, notificationSvc, domain.MongoDBLogger) + // httpserver.StartStatCrons(statSvc, domain.MongoDBLogger) // httpserver.StartReportCrons(reportSvc, domain.MongoDBLogger) issueReportingRepo := repository.NewReportedIssueRepository(store) issueReportingSvc := issuereporting.New(issueReportingRepo) - transferStore := repository.NewTransferStore(store) + // transferStore := repository.NewTransferStore(store) // walletStore := wallet.WalletStore(store) - arifpaySvc := arifpay.NewArifpayService(cfg, transferStore, walletSvc, &http.Client{ + arifpaySvc := arifpay.NewArifpayService(cfg, *transactionSvc, &http.Client{ Timeout: 30 * time.Second}) - santimpayClient := santimpay.NewSantimPayClient(cfg) + // santimpayClient := santimpay.NewSantimPayClient(cfg) - santimpaySvc := santimpay.NewSantimPayService(santimpayClient, cfg, transferStore, walletSvc) - telebirrSvc := telebirr.NewTelebirrService(cfg, transferStore, walletSvc) + // santimpaySvc := santimpay.NewSantimPayService(santimpayClient, cfg, transferStore) + // telebirrSvc := telebirr.NewTelebirrService(cfg, transferStore) // Initialize and start HTTP server app := httpserver.NewApp( - directdeposit, - enetPulseSvc, - atlasVirtualGameService, - veliVirtualGameService, - orchestrationSvc, - telebirrSvc, + // directdeposit, + // telebirrSvc, arifpaySvc, - santimpaySvc, + // santimpaySvc, issueReportingSvc, instSvc, currSvc, @@ -402,26 +347,15 @@ func main() { JwtAccessExpiry: cfg.AccessExpiry, }, userSvc, - ticketSvc, - betSvc, - // reportSvc, // Make sure httpserver.NewApp accepts this parameter - chapaSvc, - walletSvc, + // chapaSvc, transactionSvc, branchSvc, companySvc, notificationSvc, - oddsSvc, - eventSvc, - leagueSvc, referalSvc, raffleSvc, bonusSvc, - virtualGameSvc, - aleaService, - // veliService, recommendationSvc, - resultSvc, statSvc, cfg, domain.MongoDBLogger, diff --git a/db/data/001_initial_seed_data.sql b/db/data/001_initial_seed_data.sql index 1671b2b..0ac7167 100644 --- a/db/data/001_initial_seed_data.sql +++ b/db/data/001_initial_seed_data.sql @@ -1,381 +1,260 @@ CREATE EXTENSION IF NOT EXISTS pgcrypto; --- Locations Initial Data -INSERT INTO branch_locations (key, value) -VALUES ('addis_ababa', 'Addis Ababa'), - ('dire_dawa', 'Dire Dawa'), - ('mekelle', 'Mekelle'), - ('adama', 'Adama'), - ('awassa', 'Awassa'), - ('bahir_dar', 'Bahir Dar'), - ('gonder', 'Gonder'), - ('dessie', 'Dessie'), - ('jimma', 'Jimma'), - ('jijiga', 'Jijiga'), - ('shashamane', 'Shashamane'), - ('bishoftu', 'Bishoftu'), - ('sodo', 'Sodo'), - ('arba_minch', 'Arba Minch'), - ('hosaena', 'Hosaena'), - ('harar', 'Harar'), - ('dilla', 'Dilla'), - ('nekemte', 'Nekemte'), - ('debre_birhan', 'Debre Birhan'), - ('asella', 'Asella'), - ('debre_markos', 'Debre Markos'), - ('kombolcha', 'Kombolcha'), - ('debre_tabor', 'Debre Tabor'), - ('adigrat', 'Adigrat'), - ('areka', 'Areka'), - ('weldiya', 'Weldiya'), - ('sebeta', 'Sebeta'), - ('burayu', 'Burayu'), - ('shire', 'Shire'), - ('ambo', 'Ambo'), - ('arsi_negele', 'Arsi Negele'), - ('aksum', 'Aksum'), - ('gambela', 'Gambela'), - ('bale_robe', 'Bale Robe'), - ('butajira', 'Butajira'), - ('batu', 'Batu'), - ('boditi', 'Boditi'), - ('adwa', 'Adwa'), - ('yirgalem', 'Yirgalem'), - ('waliso', 'Waliso'), - ('welkite', 'Welkite'), - ('gode', 'Gode'), - ('meki', 'Meki'), - ('negele_borana', 'Negele Borana'), - ('alaba_kulito', 'Alaba Kulito'), - ('alamata,', 'Alamata,'), - ('chiro', 'Chiro'), - ('tepi', 'Tepi'), - ('durame', 'Durame'), - ('goba', 'Goba'), - ('assosa', 'Assosa'), - ('gimbi', 'Gimbi'), - ('wukro', 'Wukro'), - ('haramaya', 'Haramaya'), - ('mizan_teferi', 'Mizan Teferi'), - ('sawla', 'Sawla'), - ('mojo', 'Mojo'), - ('dembi_dolo', 'Dembi Dolo'), - ('aleta_wendo', 'Aleta Wendo'), - ('metu', 'Metu'), - ('mota', 'Mota'), - ('fiche', 'Fiche'), - ('finote_selam', 'Finote Selam'), - ('bule_hora_town', 'Bule Hora Town'), - ('bonga', 'Bonga'), - ('kobo', 'Kobo'), - ('jinka', 'Jinka'), - ('dangila', 'Dangila'), - ('degehabur', 'Degehabur'), - ('bedessa', 'Bedessa'), - ('agaro', 'Agaro') ON CONFLICT (key) DO -UPDATE -SET value = EXCLUDED.value; --- Settings Initial Data + +-- ====================================================== +-- Global Settings (LMS) +-- ====================================================== INSERT INTO global_settings (key, value) -VALUES ('sms_provider', 'afro_message'), - ('max_number_of_outcomes', '30'), - ('max_unsettled_bets', '100'), - ('bet_amount_limit', '10000000'), - ('daily_ticket_limit', '50'), - ('total_winnings_limit', '100000000'), - ('total_winnings_notify', '10000000'), - ('amount_for_bet_referral', '1000000'), - ('cashback_amount_cap', '1000'), - ('default_winning_limit', '5000000'), - ('referral_reward_amount', '10000'), - ('cashback_percentage', '0.2'), - ('default_max_referrals', '15'), - ('minimum_bet_amount', '100'), - ('bet_duplicate_limit', '5'), - ('send_email_on_bet_finish', 'true'), - ('send_sms_on_bet_finish', 'false'), - ('welcome_bonus_active', 'false'), - ('welcome_bonus_multiplier', '1.5'), - ('welcome_bonus_cap', '100000'), - ('welcome_bonus_count', '3'), - ('welcome_bonus_expiry', '10') ON CONFLICT (key) DO NOTHING; --- Users -INSERT INTO users ( - id, - first_name, - last_name, - email, - phone_number, - password, - role, - email_verified, - phone_verified, - created_at, - updated_at, - suspended, - company_id - ) +VALUES + ('platform_name', 'Yimaru LMS'), + ('default_language', 'en'), + ('allow_self_signup', 'true'), + ('otp_expiry_minutes', '5'), + ('certificate_enabled', 'true'), + ('max_courses_per_instructor', '50') +ON CONFLICT (key) DO NOTHING; + +-- ====================================================== +-- Organizations (Tenants) +-- ====================================================== +INSERT INTO organizations ( + id, + name, + slug, + owner_id, + is_active, + created_at, + updated_at +) VALUES ( - 1, - 'John', - 'Doe', - 'john.doe@example.com', - NULL, - crypt('password@123', gen_salt('bf'))::bytea, - 'customer', - TRUE, - FALSE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP, - FALSE, - 1 - ), - ( - 2, - 'Test', - 'Admin', - 'test.admin@gmail.com', - '0988554466', - crypt('password@123', gen_salt('bf'))::bytea, - 'admin', - TRUE, - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP, - FALSE, - 1 - ), - ( - 3, - 'Samuel', - 'Tariku', - 'cybersamt@gmail.com', - '0911111111', - crypt('password@123', gen_salt('bf'))::bytea, - 'super_admin', - TRUE, - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP, - FALSE, - NULL - ), - ( - 4, - 'Kirubel', - 'Kibru', - 'kirubel.jkl679@gmail.com', - '0911554486', - crypt('password@123', gen_salt('bf'))::bytea, - 'super_admin', - TRUE, - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP, - FALSE, - NULL - ) ON CONFLICT (id) DO -UPDATE + 1, + 'Yimaru Academy', + 'yimaru-academy', + 1, + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP +) +ON CONFLICT (id) DO UPDATE +SET name = EXCLUDED.name, + slug = EXCLUDED.slug, + is_active = EXCLUDED.is_active, + updated_at = CURRENT_TIMESTAMP; + +-- ====================================================== +-- Users +-- Roles: SUPER_ADMIN, ORG_ADMIN, INSTRUCTOR, STUDENT +-- ====================================================== +INSERT INTO users ( + id, + first_name, + last_name, + nick_name, + email, + phone_number, + password, + role, + age, + education_level, + country, + region, + email_verified, + phone_verified, + suspended, + organization_id, + created_at, + updated_at +) +VALUES +( + 1, + 'Sarah', + 'Connor', + 'SarahC', + 'sarah.connor@yimaru.com', + NULL, + crypt('password@123', gen_salt('bf'))::bytea, + 'SUPER_ADMIN', + 35, + 'Masters', + 'USA', + 'California', + TRUE, + FALSE, + FALSE, + NULL, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP +), +( + 2, + 'Test', + 'Instructor', + 'InstructorT', + 'instructor@yimaru.com', + '0988554466', + crypt('password@123', gen_salt('bf'))::bytea, + 'INSTRUCTOR', + 30, + 'Bachelors', + 'USA', + 'New York', + TRUE, + TRUE, + FALSE, + 1, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP +), +( + 3, + 'Demo', + 'Student', + 'DemoS', + 'student@yimaru.com', + NULL, + crypt('password@123', gen_salt('bf'))::bytea, + 'STUDENT', + 22, + 'High School', + 'USA', + 'Texas', + TRUE, + FALSE, + FALSE, + 1, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP +) +ON CONFLICT (id) DO UPDATE SET first_name = EXCLUDED.first_name, last_name = EXCLUDED.last_name, + nick_name = EXCLUDED.nick_name, email = EXCLUDED.email, phone_number = EXCLUDED.phone_number, password = EXCLUDED.password, role = EXCLUDED.role, + age = EXCLUDED.age, + education_level = EXCLUDED.education_level, + country = EXCLUDED.country, + region = EXCLUDED.region, email_verified = EXCLUDED.email_verified, phone_verified = EXCLUDED.phone_verified, - created_at = EXCLUDED.created_at, - updated_at = EXCLUDED.updated_at, suspended = EXCLUDED.suspended, - company_id = EXCLUDED.company_id; --- Supported Operations -INSERT INTO supported_operations (id, name, description) -VALUES (1, 'SportBook', 'Sportbook operations'), - (2, 'Virtual', 'Virtual operations') ON CONFLICT (id) DO -UPDATE -SET name = EXCLUDED.name, - description = EXCLUDED.description; --- Wallets -INSERT INTO wallets ( - id, - balance, - is_withdraw, - is_bettable, - is_transferable, - user_id, - type, - currency, - is_active, - created_at, - updated_at - ) + organization_id = EXCLUDED.organization_id, + updated_at = CURRENT_TIMESTAMP; + +-- ====================================================== +-- Courses +-- ====================================================== +INSERT INTO courses ( + id, + organization_id, + instructor_id, + title, + description, + level, + language, + is_published, + created_at, + updated_at +) VALUES ( - 1, - 10000, - TRUE, - TRUE, - TRUE, - 1, - 'regular_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ), - ( - 2, - 5000, - FALSE, - TRUE, - TRUE, - 1, - 'static_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ), - ( - 3, - 100000000, - TRUE, - TRUE, - TRUE, - 2, - 'company_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ), - ( - 4, - 50000000, - TRUE, - TRUE, - TRUE, - 2, - 'branch_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ) ON CONFLICT (id) DO -UPDATE -SET balance = EXCLUDED.balance, - is_withdraw = EXCLUDED.is_withdraw, - is_bettable = EXCLUDED.is_bettable, - is_transferable = EXCLUDED.is_transferable, - user_id = EXCLUDED.user_id, - type = EXCLUDED.type, - currency = EXCLUDED.currency, - is_active = EXCLUDED.is_active, - created_at = EXCLUDED.created_at, - updated_at = EXCLUDED.updated_at; --- Customer Wallets -INSERT INTO customer_wallets ( - id, - customer_id, - regular_wallet_id, - static_wallet_id - ) -VALUES (1, 1, 1, 2) ON CONFLICT (id) DO -UPDATE -SET customer_id = EXCLUDED.customer_id, - regular_wallet_id = EXCLUDED.regular_wallet_id, - static_wallet_id = EXCLUDED.static_wallet_id; --- Company -INSERT INTO companies ( - id, - name, - slug, - admin_id, - wallet_id, - deducted_percentage, - is_active, - created_at, - updated_at - ) -VALUES ( - 1, - 'FortuneBets', - 'fortunebets', - 2, - 3, - 0.10, - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ) ON CONFLICT (id) DO -UPDATE -SET name = EXCLUDED.name, - slug = EXCLUDED.slug, - admin_id = EXCLUDED.admin_id, - wallet_id = EXCLUDED.wallet_id, - deducted_percentage = EXCLUDED.deducted_percentage, - is_active = EXCLUDED.is_active, - created_at = EXCLUDED.created_at, - updated_at = EXCLUDED.updated_at; --- Branch -INSERT INTO branches ( - id, - name, - location, - wallet_id, - branch_manager_id, - company_id, - is_self_owned, - profit_percent, - is_active, - created_at, - updated_at - ) -VALUES ( - 1, - 'Test Branch', - 'addis_ababa', - 4, - 2, - 1, - TRUE, - 0.10, - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ) ON CONFLICT (id) DO -UPDATE -SET name = EXCLUDED.name, - location = EXCLUDED.location, - wallet_id = EXCLUDED.wallet_id, - branch_manager_id = EXCLUDED.branch_manager_id, - company_id = EXCLUDED.company_id, - is_self_owned = EXCLUDED.is_self_owned, - profit_percent = EXCLUDED.profit_percent, - is_active = EXCLUDED.is_active, - created_at = EXCLUDED.created_at, - updated_at = EXCLUDED.updated_at; --- Bonus -INSERT INTO user_bonuses ( - id, - name, - description, - type, - user_id, - reward_amount, - expires_at - ) -VALUES ( - 1, - 'Welcome Bonus', - 'Awarded for deposit number (1 / 3)', - 'welcome_bonus', - 1, - 1000, - now() + INTERVAL '1 day' - ) ON CONFLICT (id) DO -UPDATE -SET name = EXCLUDED.name, + 1, + 1, + 2, + 'Introduction to Go Programming', + 'Learn the fundamentals of Go for backend development.', + 'beginner', + 'en', + TRUE, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP +) +ON CONFLICT (id) DO UPDATE +SET title = EXCLUDED.title, description = EXCLUDED.description, - type = EXCLUDED.type, - user_id = EXCLUDED.user_id, - reward_amount = EXCLUDED.reward_amount, - expires_at = EXCLUDED.expires_at; \ No newline at end of file + is_published = EXCLUDED.is_published, + updated_at = CURRENT_TIMESTAMP; + +-- ====================================================== +-- Course Modules +-- ====================================================== +INSERT INTO course_modules ( + id, + course_id, + title, + position, + created_at +) +VALUES ( + 1, + 1, + 'Getting Started', + 1, + CURRENT_TIMESTAMP +) +ON CONFLICT (id) DO NOTHING; + +-- ====================================================== +-- Lessons +-- ====================================================== +INSERT INTO lessons ( + id, + module_id, + title, + content_type, + content_url, + duration_minutes, + position, + created_at +) +VALUES ( + 1, + 1, + 'What is Go?', + 'video', + 'https://example.com/go-intro', + 15, + 1, + CURRENT_TIMESTAMP +) +ON CONFLICT (id) DO NOTHING; + +-- ====================================================== +-- Enrollments +-- ====================================================== +INSERT INTO enrollments ( + id, + course_id, + student_id, + enrolled_at +) +VALUES ( + 1, + 1, + 3, + CURRENT_TIMESTAMP +) +ON CONFLICT (id) DO NOTHING; + +-- ====================================================== +-- Notifications (Sample) +-- ====================================================== +INSERT INTO notifications ( + user_id, + type, + level, + channel, + title, + message, + created_at +) +VALUES ( + 3, + 'course_enrolled', + 'info', + 'in_app', + 'Welcome to your course', + 'You have successfully enrolled in Introduction to Go Programming.', + CURRENT_TIMESTAMP +); diff --git a/db/data/002_veli_user.sql b/db/data/002_veli_user.sql deleted file mode 100644 index 25a429b..0000000 --- a/db/data/002_veli_user.sql +++ /dev/null @@ -1,148 +0,0 @@ --- Users -INSERT INTO users ( - id, - first_name, - last_name, - email, - phone_number, - password, - role, - email_verified, - phone_verified, - created_at, - updated_at, - suspended, - company_id - ) -VALUES ( - 5, - 'Test', - 'Veli', - 'test.veli@example.com', - NULL, - crypt('password@123', gen_salt('bf'))::bytea, - 'customer', - TRUE, - FALSE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP, - FALSE, - 1 - ), - ( - 6, - 'Kirubel', - 'Kibru', - 'modernkibru@gmail.com', - NULL, - crypt('password@123', gen_salt('bf'))::bytea, - 'customer', - TRUE, - FALSE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP, - FALSE, - 1 - ) ON CONFLICT (id) DO -UPDATE -SET first_name = EXCLUDED.first_name, - last_name = EXCLUDED.last_name, - email = EXCLUDED.email, - phone_number = EXCLUDED.phone_number, - password = EXCLUDED.password, - role = EXCLUDED.role, - email_verified = EXCLUDED.email_verified, - phone_verified = EXCLUDED.phone_verified, - created_at = EXCLUDED.created_at, - updated_at = EXCLUDED.updated_at, - suspended = EXCLUDED.suspended, - company_id = EXCLUDED.company_id; -INSERT INTO wallets ( - id, - balance, - is_withdraw, - is_bettable, - is_transferable, - user_id, - type, - currency, - is_active, - created_at, - updated_at - ) -VALUES ( - 5, - 10000, - TRUE, - TRUE, - TRUE, - 5, - 'regular_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ), - ( - 6, - 5000, - FALSE, - TRUE, - TRUE, - 5, - 'static_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ), - ( - 7, - 1000000, - TRUE, - TRUE, - TRUE, - 6, - 'regular_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ), - ( - 8, - 5000, - FALSE, - TRUE, - TRUE, - 6, - 'static_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ) ON CONFLICT (id) DO -UPDATE -SET balance = EXCLUDED.balance, - is_withdraw = EXCLUDED.is_withdraw, - is_bettable = EXCLUDED.is_bettable, - is_transferable = EXCLUDED.is_transferable, - user_id = EXCLUDED.user_id, - type = EXCLUDED.type, - currency = EXCLUDED.currency, - is_active = EXCLUDED.is_active, - created_at = EXCLUDED.created_at, - updated_at = EXCLUDED.updated_at; --- Customer Wallets -INSERT INTO customer_wallets ( - id, - customer_id, - regular_wallet_id, - static_wallet_id - ) -VALUES (2, 5, 5, 6), - (3, 6, 7, 8) ON CONFLICT (id) DO -UPDATE -SET customer_id = EXCLUDED.customer_id, - regular_wallet_id = EXCLUDED.regular_wallet_id, - static_wallet_id = EXCLUDED.static_wallet_id; \ No newline at end of file diff --git a/db/data/003_fix_autoincrement_desync.sql b/db/data/003_fix_autoincrement_desync.sql index 835e10e..cfa2ab9 100644 --- a/db/data/003_fix_autoincrement_desync.sql +++ b/db/data/003_fix_autoincrement_desync.sql @@ -1,31 +1,69 @@ --- For each table with an id sequence +-- ====================================================== +-- Reset sequences for LMS tables +-- ====================================================== + SELECT setval( - pg_get_serial_sequence('users', 'id'), - COALESCE(MAX(id), 1) - ) + pg_get_serial_sequence('users', 'id'), + COALESCE(MAX(id), 1) +) FROM users; + SELECT setval( - pg_get_serial_sequence('wallets', 'id'), - COALESCE(MAX(id), 1) - ) -FROM wallets; + pg_get_serial_sequence('organizations', 'id'), + COALESCE(MAX(id), 1) +) +FROM organizations; + SELECT setval( - pg_get_serial_sequence('customer_wallets', 'id'), - COALESCE(MAX(id), 1) - ) -FROM customer_wallets; + pg_get_serial_sequence('courses', 'id'), + COALESCE(MAX(id), 1) +) +FROM courses; + SELECT setval( - pg_get_serial_sequence('companies', 'id'), - COALESCE(MAX(id), 1) - ) -FROM companies; + pg_get_serial_sequence('course_modules', 'id'), + COALESCE(MAX(id), 1) +) +FROM course_modules; + SELECT setval( - pg_get_serial_sequence('branches', 'id'), - COALESCE(MAX(id), 1) - ) -FROM branches; + pg_get_serial_sequence('lessons', 'id'), + COALESCE(MAX(id), 1) +) +FROM lessons; + SELECT setval( - pg_get_serial_sequence('supported_operations', 'id'), - COALESCE(MAX(id), 1) - ) -FROM supported_operations; \ No newline at end of file + pg_get_serial_sequence('enrollments', 'id'), + COALESCE(MAX(id), 1) +) +FROM enrollments; + +SELECT setval( + pg_get_serial_sequence('assessments', 'id'), + COALESCE(MAX(id), 1) +) +FROM assessments; + +SELECT setval( + pg_get_serial_sequence('assessment_submissions', 'id'), + COALESCE(MAX(id), 1) +) +FROM assessment_submissions; + +SELECT setval( + pg_get_serial_sequence('notifications', 'id'), + COALESCE(MAX(id), 1) +) +FROM notifications; + +SELECT setval( + pg_get_serial_sequence('referral_codes', 'id'), + COALESCE(MAX(id), 1) +) +FROM referral_codes; + +SELECT setval( + pg_get_serial_sequence('user_referrals', 'id'), + COALESCE(MAX(id), 1) +) +FROM user_referrals; diff --git a/db/dev_data/betfidel_data.sql b/db/dev_data/betfidel_data.sql deleted file mode 100644 index 376a0fe..0000000 --- a/db/dev_data/betfidel_data.sql +++ /dev/null @@ -1,217 +0,0 @@ -CREATE EXTENSION IF NOT EXISTS pgcrypto; -DO $$ -DECLARE _admin_id bigint; -_manager_id bigint; -_company_wallet_id bigint; -_company_id bigint; -_branch_id bigint; -_branch_wallet_id bigint; -_cashier_id bigint; -BEGIN -INSERT INTO users ( - first_name, - last_name, - email, - phone_number, - password, - role, - email_verified, - phone_verified, - created_at, - updated_at, - suspended - ) -VALUES ( - 'Admin', - 'BetFidel', - 'admin.betfidel@gmail.com', - NULL, - crypt('password@123', gen_salt('bf'))::bytea, - 'admin', - TRUE, - FALSE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP, - FALSE - ) ON CONFLICT (email) DO -UPDATE -SET updated_at = EXCLUDED.updated_at -RETURNING id INTO STRICT _admin_id; -INSERT INTO users ( - first_name, - last_name, - email, - phone_number, - password, - role, - email_verified, - phone_verified, - created_at, - updated_at, - suspended, - company_id - ) -VALUES ( - 'Manager', - 'BetFidel', - 'manager.betfidel@gmail.com', - NULL, - crypt('password@123', gen_salt('bf'))::bytea, - 'branch_manager', - TRUE, - FALSE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP, - FALSE, - _company_id - ) ON CONFLICT (email) DO -UPDATE -SET updated_at = EXCLUDED.updated_at -RETURNING id INTO STRICT _manager_id; -INSERT INTO wallets ( - balance, - is_withdraw, - is_bettable, - is_transferable, - user_id, - type, - currency, - is_active, - created_at, - updated_at - ) -VALUES ( - 10000, - TRUE, - TRUE, - TRUE, - _admin_id, - 'company_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ) -RETURNING id INTO STRICT _company_wallet_id; -INSERT INTO companies ( - name, - slug, - admin_id, - wallet_id, - deducted_percentage, - is_active, - created_at, - updated_at - ) -VALUES ( - 'FidelBet', - 'betfidel', - _admin_id, - _company_wallet_id, - 0.15, - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ) ON CONFLICT (slug) DO -UPDATE -SET updated_at = EXCLUDED.updated_at -RETURNING id INTO STRICT _company_id; -UPDATE users -SET company_id = _company_id -WHERE id = _admin_id; -INSERT INTO wallets ( - balance, - is_withdraw, - is_bettable, - is_transferable, - user_id, - type, - currency, - is_active, - created_at, - updated_at - ) -VALUES ( - 10000, - TRUE, - TRUE, - TRUE, - _admin_id, - 'branch_wallet', - 'ETB', - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ) -RETURNING id INTO STRICT _branch_wallet_id; -INSERT INTO branches ( - name, - location, - wallet_id, - branch_manager_id, - company_id, - is_self_owned, - profit_percent, - is_active, - created_at, - updated_at - ) -VALUES ( - 'Test Branch', - 'addis_ababa', - _branch_wallet_id, - _manager_id, - _company_id, - TRUE, - 0.10, - TRUE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP - ) ON CONFLICT (wallet_id) DO -UPDATE -SET updated_at = EXCLUDED.updated_at -RETURNING id INTO STRICT _branch_id; -INSERT INTO users ( - first_name, - last_name, - email, - phone_number, - password, - role, - email_verified, - phone_verified, - created_at, - updated_at, - suspended, - company_id - ) -VALUES ( - 'Cashier', - 'BetFidel', - 'cashier.betfidel@gmail.com', - NULL, - crypt('password@123', gen_salt('bf'))::bytea, - 'cashier', - TRUE, - FALSE, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP, - FALSE, - _company_id - ) ON CONFLICT (email) DO -UPDATE -SET updated_at = EXCLUDED.updated_at -RETURNING id INTO STRICT _cashier_id; -INSERT INTO branch_cashiers (user_id, branch_id) -VALUES (_cashier_id, _branch_id); -RAISE NOTICE 'BETFIDEL_DEV_DATA (Admin ID: %, Company Wallet ID: %, Company ID: %)', -_admin_id, -_company_wallet_id, -_company_id; -RAISE NOTICE 'BETFIDEL_DEV_DATA (Branch ID: %, Branch Wallet ID: %, Manager ID: %)', -_branch_id, -_branch_wallet_id, -_manager_id; -RAISE NOTICE 'BETFIDEL_DEV_DATA (Cashier ID: %)', -_cashier_id; -END $$; \ No newline at end of file diff --git a/db/migrations/000001_fortune.down.sql b/db/migrations/000001_fortune.down.sql deleted file mode 100644 index 0870ac6..0000000 --- a/db/migrations/000001_fortune.down.sql +++ /dev/null @@ -1,85 +0,0 @@ -DROP TABLE IF EXISTS virtual_game_providers; --- Drop tables that depend on service_type_setting -DROP TABLE IF EXISTS service_type_setting; --- Drop product-related tables and types -DROP TABLE IF EXISTS product; -DROP TYPE IF EXISTS tier_group; --- Drop onboarding-related tables and types -DROP TABLE IF EXISTS verification_key; -DROP TABLE IF EXISTS onboarding_user; -DROP TYPE IF EXISTS verification_status; -DROP TYPE IF EXISTS onboarding_status; --- Drop staff-related tables and types -DROP TABLE IF EXISTS staff_session; -DROP TABLE IF EXISTS user_agent; -DROP TABLE IF EXISTS staff; -DROP TYPE IF EXISTS password_status; --- Drop mobile app-related tables and types -DROP TABLE IF EXISTS user_devices; -DROP TABLE IF EXISTS user_session; -DROP TABLE IF EXISTS linked_accounts; -DROP TABLE IF EXISTS users; -DROP TYPE IF EXISTS device_type; -DROP TYPE IF EXISTS registeration_type; -DROP TYPE IF EXISTS customer_group; --- Drop linked accounts and beneficiary tables and types -DROP TABLE IF EXISTS beneficiary; -DROP TYPE IF EXISTS fund_destination; --- Drop maker checker-related tables and types -DROP TABLE IF EXISTS workflow; -DROP TYPE IF EXISTS approval_status; -DROP TYPE IF EXISTS action_type; -DROP TYPE IF EXISTS context_type; --- Drop authorization-related tables and types -DROP TRIGGER IF EXISTS enforce_unique_array ON policy; -DROP FUNCTION IF EXISTS check_unique_array; -DROP TABLE IF EXISTS policy; -DROP TABLE IF EXISTS roles; -DROP TYPE IF EXISTS policy_action; -DROP TYPE IF EXISTS policy_object; --- Drop bank-related tables and types -DROP TABLE IF EXISTS bank; -DROP TABLE IF EXISTS flagged_users; --- Drop transaction-related tables and types -DROP TABLE IF EXISTS transaction_daily; -DROP TABLE IF EXISTS system_limits; -DROP TABLE IF EXISTS transactions; -DROP TYPE IF EXISTS payment_status; -DROP TYPE IF EXISTS service_type; -DROP TYPE IF EXISTS channel; -DROP TYPE IF EXISTS transaction_category; -DROP TYPE IF EXISTS registration_type; --- Drop branches and related tables -DROP TABLE IF EXISTS branches; -DROP TABLE IF EXISTS cities; -DROP TABLE IF EXISTS districts; -DROP TABLE IF EXISTS regions; --- Drop activity logs -DROP TABLE IF EXISTS activity; --- Drop ussd account and related enums -DROP TABLE IF EXISTS ussd_account; -DROP TYPE IF EXISTS ua_pin_status; -DROP TYPE IF EXISTS ua_status; -DROP TYPE IF EXISTS ua_registaration_type; --- Drop FortuneBet -DROP TABLE IF EXISTS tickets; -DROP TABLE IF EXISTS ticket_outcomes; -DROP TABLE IF EXISTS bets; -DROP TABLE IF EXISTS bet_outcomes; -DROP TABLE IF EXISTS wallets; -DROP TABLE IF EXISTS customer_wallets; -DROP TABLE IF EXISTS wallet_transfer; -DROP TABLE IF EXISTS transactions; -DROP TABLE IF EXISTS branches; -DROP TABLE IF EXISTS companies; -DROP TABLE IF EXISTS supported_operations; -DROP TABLE IF EXISTS refresh_tokens; -DROP TABLE IF EXISTS otps; -DROP TABLE IF EXISTS odds; -DROP TABLE IF EXISTS events; -DROP TABLE IF EXISTS leagues; -DROP TABLE IF EXISTS teams; -DROP TABLE IF EXISTS settings; -DROP TABLE IF EXISTS bonus; -DROP TABLE IF EXISTS flags; --- DELETE FROM wallet_transfer; \ No newline at end of file diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql deleted file mode 100644 index 90f5219..0000000 --- a/db/migrations/000001_fortune.up.sql +++ /dev/null @@ -1,1011 +0,0 @@ -CREATE TABLE IF NOT EXISTS users ( - id BIGSERIAL PRIMARY KEY, - first_name VARCHAR(255) NOT NULL, - last_name VARCHAR(255) NOT NULL, - email VARCHAR(255) UNIQUE, - phone_number VARCHAR(20) UNIQUE, - role VARCHAR(50) NOT NULL, - password BYTEA NOT NULL, - email_verified BOOLEAN NOT NULL DEFAULT FALSE, - phone_verified BOOLEAN NOT NULL DEFAULT FALSE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ, - company_id BIGINT, - suspended_at TIMESTAMPTZ NULL, - -- this can be NULL if the user is not suspended - suspended BOOLEAN NOT NULL DEFAULT FALSE, - CHECK ( - email IS NOT NULL - OR phone_number IS NOT NULL - ), - UNIQUE (email, company_id), - UNIQUE (phone_number, company_id) -); -CREATE TABLE IF NOT EXISTS virtual_game_providers ( - id BIGSERIAL PRIMARY KEY, - provider_id VARCHAR(100) UNIQUE NOT NULL, - -- providerId from Veli Games - provider_name VARCHAR(255) NOT NULL, - logo_dark TEXT, - logo_light TEXT, - -- logoForLight (URL) - enabled BOOLEAN NOT NULL DEFAULT TRUE, - -- allow enabling/disabling providers - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE TABLE IF NOT EXISTS virtual_game_provider_reports ( - id BIGSERIAL PRIMARY KEY, - provider_id BIGINT NOT NULL REFERENCES virtual_game_providers(id) ON DELETE CASCADE, - report_date DATE NOT NULL, - total_games_played BIGINT DEFAULT 0, - total_bets NUMERIC(18,2) DEFAULT 0, - total_payouts NUMERIC(18,2) DEFAULT 0, - total_profit NUMERIC(18,2) GENERATED ALWAYS AS (total_bets - total_payouts) STORED, - total_players BIGINT DEFAULT 0, - report_type VARCHAR(50) DEFAULT 'daily', - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE UNIQUE INDEX IF NOT EXISTS idx_unique_provider_report -ON virtual_game_provider_reports (provider_id, report_date, report_type); - -CREATE TABLE IF NOT EXISTS virtual_games ( - id BIGSERIAL PRIMARY KEY, - game_id VARCHAR(150) UNIQUE NOT NULL, - provider_id BIGINT NOT NULL REFERENCES virtual_game_providers(id) ON DELETE CASCADE, - name VARCHAR(255) NOT NULL, - category VARCHAR(100), - device_type VARCHAR(100), - volatility VARCHAR(50), - rtp NUMERIC(5, 2), - has_demo BOOLEAN DEFAULT FALSE, - has_free_bets BOOLEAN DEFAULT FALSE, - bets NUMERIC [] DEFAULT '{}', - thumbnail TEXT, - status INT, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); -CREATE UNIQUE INDEX IF NOT EXISTS ux_virtual_games_provider_game ON virtual_games (provider_id, game_id); - -CREATE TABLE IF NOT EXISTS virtual_game_favourites ( - id BIGSERIAL PRIMARY KEY, - game_id BIGINT NOT NULL REFERENCES virtual_games(id) ON DELETE CASCADE, - user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - provider_id BIGINT NOT NULL REFERENCES virtual_game_providers(id) ON DELETE CASCADE, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE (game_id, user_id) -); - -CREATE TABLE IF NOT EXISTS virtual_game_reports ( - id BIGSERIAL PRIMARY KEY, - game_id VARCHAR(150) NOT NULL REFERENCES virtual_games(game_id) ON DELETE CASCADE, - provider_id BIGINT NOT NULL REFERENCES virtual_game_providers(id) ON DELETE CASCADE, - report_date DATE NOT NULL, - total_rounds BIGINT DEFAULT 0, - total_bets NUMERIC(18,2) DEFAULT 0, - total_payouts NUMERIC(18,2) DEFAULT 0, - total_profit NUMERIC(18,2) GENERATED ALWAYS AS (total_bets - total_payouts) STORED, - total_players BIGINT DEFAULT 0, - report_type VARCHAR(50) DEFAULT 'daily', - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE UNIQUE INDEX IF NOT EXISTS idx_unique_game_report -ON virtual_game_reports (game_id, report_date, report_type); - -CREATE TABLE IF NOT EXISTS wallets ( - id BIGSERIAL PRIMARY KEY, - balance BIGINT NOT NULL DEFAULT 0, - currency VARCHAR(3) NOT NULL DEFAULT 'ETB', - is_withdraw BOOLEAN NOT NULL, - is_bettable BOOLEAN NOT NULL, - is_transferable BOOLEAN NOT NULL, - user_id BIGINT NOT NULL, - type TEXT NOT NULL CHECK ( - type IN ( - 'regular_wallet', - 'static_wallet', - 'branch_wallet', - 'company_wallet' - ) - ), - is_active BOOLEAN NOT NULL DEFAULT true, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT balance_positve CHECK (balance >= 0) -); -CREATE TABLE wallet_stats ( - wallet_id BIGINT NOT NULL, - wallet_user_id BIGINT NOT NULL, - wallet_user_first_name TEXT NOT NULL, - wallet_user_last_name TEXT NOT NULL, - wallet_type TEXT NOT NULL, - interval_start TIMESTAMP NOT NULL, - number_of_transactions BIGINT NOT NULL, - total_transactions BIGINT NOT NULL, - number_of_deposits BIGINT NOT NULL, - total_deposits_amount BIGINT NOT NULL, - number_of_withdraws BIGINT NOT NULL, - total_withdraws_amount BIGINT NOT NULL, - number_of_transfers BIGINT NOT NULL, - total_transfers_amount BIGINT NOT NULL, - updated_at TIMESTAMP DEFAULT now(), - UNIQUE(wallet_id, interval_start) -); -CREATE TABLE refresh_tokens ( - id BIGSERIAL PRIMARY KEY, - user_id BIGINT NOT NULL, - token TEXT NOT NULL UNIQUE, - expires_at TIMESTAMPTZ NOT NULL, - created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL, - revoked BOOLEAN DEFAULT FALSE NOT NULL, - CONSTRAINT unique_token UNIQUE (token) -); ------ -CREATE TABLE otps ( - id BIGSERIAL PRIMARY KEY, - sent_to VARCHAR(255) NOT NULL, - medium VARCHAR(50) NOT NULL, - otp_for VARCHAR(50) NOT NULL, - otp VARCHAR(10) NOT NULL, - used BOOLEAN NOT NULL DEFAULT FALSE, - used_at TIMESTAMPTZ, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - expires_at TIMESTAMPTZ NOT NULL -); -CREATE TABLE IF NOT EXISTS bets ( - id BIGSERIAL PRIMARY KEY, - company_id BIGINT NOT NULL, - amount BIGINT NOT NULL, - total_odds REAL NOT NULL, - potential_win BIGINT GENERATED ALWAYS AS (amount * total_odds) STORED, - status INT NOT NULL, - user_id BIGINT NOT NULL, - is_shop_bet BOOLEAN NOT NULL, - cashed_out BOOLEAN NOT NULL DEFAULT false, - outcomes_hash TEXT NOT NULL, - fast_code VARCHAR(10) NOT NULL, - processed BOOLEAN DEFAULT FALSE NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE IF NOT EXISTS tickets ( - id BIGSERIAL PRIMARY KEY, - company_id BIGINT NOT NULL, - amount BIGINT NOT NULL, - total_odds REAL NOT NULL, - IP VARCHAR(255) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE exchange_rates ( - id SERIAL PRIMARY KEY, - from_currency VARCHAR(3) NOT NULL, - to_currency VARCHAR(3) NOT NULL, - rate DECIMAL(19, 6) NOT NULL, - valid_until TIMESTAMP NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT NOW (), - UNIQUE (from_currency, to_currency) -); -CREATE TABLE IF NOT EXISTS bet_outcomes ( - id BIGSERIAL PRIMARY KEY, - bet_id BIGINT NOT NULL, - sport_id BIGINT NOT NULL, - event_id BIGINT NOT null, - odd_id BIGINT NOT NULL, - home_team_name TEXT NOT NULL, - away_team_name TEXT NOT NULL, - market_id BIGINT NOT NULL, - market_name TEXT NOT NULL, - odd REAL NOT NULL, - odd_name TEXT NOT NULL, - odd_header TEXT NOT NULL, - odd_handicap TEXT NOT NULL, - status INT NOT NULL DEFAULT 0, - expires TIMESTAMP NOT NULL -); -CREATE TABLE IF NOT EXISTS ticket_outcomes ( - id BIGSERIAL PRIMARY KEY, - ticket_id BIGINT NOT NULL, - event_id BIGINT NOT null, - odd_id BIGINT NOT NULL, - home_team_name TEXT NOT NULL, - away_team_name TEXT NOT NULL, - market_id BIGINT NOT NULL, - market_name TEXT NOT NULL, - odd REAL NOT NULL, - odd_name TEXT NOT NULL, - odd_header TEXT NOT NULL, - odd_handicap TEXT NOT NULL, - status INT NOT NULL DEFAULT 0, - expires TIMESTAMP NOT NULL -); -CREATE TABLE IF NOT EXISTS banks ( - id BIGSERIAL PRIMARY KEY, - slug VARCHAR(255) NOT NULL UNIQUE, - swift VARCHAR(20) NOT NULL, - name VARCHAR(255) NOT NULL, - acct_length INT NOT NULL, - country_id INT NOT NULL, - is_mobilemoney INT, - -- nullable integer (0 or 1) - is_active INT NOT NULL, - -- 0 or 1 - is_rtgs INT NOT NULL, - -- 0 or 1 - active INT NOT NULL, - -- 0 or 1 - is_24hrs INT, - -- nullable integer (0 or 1) - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, - updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, - currency VARCHAR(10) NOT NULL, - bank_logo TEXT -- URL or base64 string -); --- CREATE TABLE IF NOT EXISTS wallets ( --- id BIGSERIAL PRIMARY KEY, --- balance BIGINT NOT NULL DEFAULT 0, --- is_withdraw BOOLEAN NOT NULL, --- is_bettable BOOLEAN NOT NULL, --- is_transferable BOOLEAN NOT NULL, --- user_id BIGINT NOT NULL, --- type VARCHAR(255) NOT NULL, --- is_active BOOLEAN NOT NULL DEFAULT true, --- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, --- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, --- CONSTRAINT balance_positve CHECK (balance >= 0) --- ); -CREATE TABLE IF NOT EXISTS customer_wallets ( - id BIGSERIAL PRIMARY KEY, - customer_id BIGINT NOT NULL, - regular_wallet_id BIGINT NOT NULL, - static_wallet_id BIGINT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE IF NOT EXISTS wallet_transfer ( - id BIGSERIAL PRIMARY KEY, - amount BIGINT, - message TEXT NOT NULL, - type VARCHAR(255), - receiver_wallet_id BIGINT, - sender_wallet_id BIGINT, - cashier_id BIGINT, - verified BOOLEAN DEFAULT false, - reference_number VARCHAR(255) NOT NULL, - ext_reference_number VARCHAR(255), - session_id VARCHAR(255), - status VARCHAR(255), - payment_method VARCHAR(255), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE IF NOT EXISTS shop_transactions ( - id BIGSERIAL PRIMARY KEY, - amount BIGINT NOT NULL, - branch_id BIGINT NOT NULL, - company_id BIGINT NOT NULL, - user_id BIGINT NOT NULL, - type BIGINT NOT NULL, - full_name VARCHAR(255) NOT NULL, - phone_number VARCHAR(255) NOT NULL, - payment_option BIGINT NOT NULL, - bank_code VARCHAR(255), - beneficiary_name VARCHAR(255), - account_name VARCHAR(255), - account_number VARCHAR(255), - reference_number VARCHAR(255), - approved_by BIGINT, - verified BOOLEAN DEFAULT false NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE IF NOT EXISTS shop_bets ( - id BIGSERIAL PRIMARY KEY, - shop_transaction_id BIGINT NOT NULL, - cashout_id VARCHAR(255) NOT NULL, - cashed_out_by BIGINT, - bet_id BIGINT NOT NULL, - number_of_outcomes BIGINT NOT NULL, - cashed_out BOOLEAN DEFAULT FALSE NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE (shop_transaction_id), - UNIQUE (bet_id), - UNIQUE (cashout_id) -); -CREATE TABLE IF NOT EXISTS shop_deposits ( - id BIGSERIAL PRIMARY KEY, - shop_transaction_id BIGINT NOT NULL, - customer_id BIGINT NOT NULL, - wallet_transfer_id BIGINT, - branch_wallet_id BIGINT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE (shop_transaction_id) -); -CREATE TABLE IF NOT EXISTS branches ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - location TEXT NOT NULL, - profit_percent REAL NOT NULL, - is_active BOOLEAN NOT NULL DEFAULT true, - wallet_id BIGINT NOT NULL, - branch_manager_id BIGINT NOT NULL, - company_id BIGINT NOT NULL, - is_self_owned BOOLEAN NOT NULL DEFAULT false, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE (wallet_id), - CONSTRAINT profit_percentage_check CHECK ( - profit_percent >= 0 - AND profit_percent < 1 - ) -); -CREATE TABLE IF NOT EXISTS branch_operations ( - id BIGSERIAL PRIMARY KEY, - operation_id BIGINT NOT NULL, - branch_id BIGINT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE IF NOT EXISTS branch_cashiers ( - id BIGSERIAL PRIMARY KEY, - user_id BIGINT NOT NULL, - branch_id BIGINT NOT NULL, - UNIQUE (user_id, branch_id) -); -CREATE TABLE IF NOT EXISTS branch_locations (key TEXT PRIMARY KEY, value TEXT NOT NULL); -CREATE TABLE branch_stats ( - branch_id BIGINT NOT NULL, - branch_name TEXT NOT NULL, - company_id BIGINT NOT NULL, - company_name TEXT NOT NULL, - company_slug TEXT NOT NULL, - interval_start TIMESTAMP NOT NULL, - total_bets BIGINT NOT NULL, - total_stake BIGINT NOT NULL, - deducted_stake BIGINT NOT NULL, - total_cash_out BIGINT NOT NULL, - total_cash_backs BIGINT NOT NULL, - number_of_unsettled BIGINT NOT NULL, - total_unsettled_amount BIGINT NOT NULL, - total_cashiers BIGINT NOT NULL, - updated_at TIMESTAMP DEFAULT now(), - UNIQUE(branch_id, interval_start) -); -CREATE TABLE events ( - id BIGSERIAL PRIMARY KEY, - source_event_id TEXT NOT NULL, - sport_id INT NOT NULL, - match_name TEXT NOT NULL, - home_team TEXT NOT NULL, - away_team TEXT NOT NULL, - home_team_id BIGINT NOT NULL, - away_team_id BIGINT NOT NULL, - home_kit_image TEXT NOT NULL, - away_kit_image TEXT NOT NULL, - league_id BIGINT NOT NULL, - league_name TEXT NOT NULL, - start_time TIMESTAMP NOT NULL, - score TEXT, - match_minute INT, - timer_status TEXT, - added_time INT, - match_period INT, - is_live BOOLEAN NOT NULL DEFAULT false, - status TEXT NOT NULL, - fetched_at TIMESTAMP DEFAULT now (), - updated_at TIMESTAMP DEFAULT now (), - source TEXT NOT NULL DEFAULT 'b365api' CHECK ( - source IN ('b365api', 'bfair', '1xbet', 'bwin', 'enetpulse') - ), - default_is_active BOOLEAN NOT NULL DEFAULT true, - default_is_featured BOOLEAN NOT NULL DEFAULT false, - default_winning_upper_limit BIGINT NOT NULL, - is_monitored BOOLEAN NOT NULL DEFAULT FALSE, - UNIQUE(source_event_id, source) -); -CREATE INDEX idx_events_league_start_time ON events(league_id, start_time); -CREATE INDEX IF NOT EXISTS idx_events_league_id ON events (league_id); -CREATE EXTENSION IF NOT EXISTS pg_trgm; -CREATE INDEX idx_events_search_trgm ON events USING GIN ( - match_name gin_trgm_ops, - league_name gin_trgm_ops -); -CREATE INDEX idx_events_core_filter ON events ( - source, - sport_id, - league_id, - status, - is_live, - start_time DESC -); -CREATE TABLE event_bet_stats ( - event_id BIGINT PRIMARY KEY, - number_of_bets BIGINT, - total_amount BIGINT, - avg_bet_amount DOUBLE PRECISION, - total_potential_winnings BIGINT, - updated_at TIMESTAMP DEFAULT now() -); -CREATE TABLE event_history ( - id BIGSERIAL PRIMARY KEY, - event_id BIGINT NOT NULL, - status TEXT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE company_event_settings ( - id BIGSERIAL PRIMARY KEY, - company_id BIGINT NOT NULL, - event_id BIGINT NOT NULL, - is_active BOOLEAN, - is_featured BOOLEAN, - winning_upper_limit BIGINT, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE (company_id, event_id) -); -CREATE TABLE odds_market ( - id BIGSERIAL PRIMARY KEY, - event_id BIGINT NOT NULL, - market_type TEXT NOT NULL, - market_name TEXT NOT NULL, - market_category TEXT NOT NULL, - market_id BIGINT NOT NULL, - raw_odds JSONB NOT NULL, - number_of_outcomes BIGINT NOT NULL, - default_is_active BOOLEAN NOT NULL DEFAULT true, - fetched_at TIMESTAMP DEFAULT now (), - expires_at TIMESTAMP NOT NULL, - UNIQUE (event_id, market_id) -); -CREATE INDEX IF NOT EXISTS idx_odds_market_event_id ON odds_market (event_id); -CREATE TABLE odd_history ( - id BIGSERIAL PRIMARY KEY, - odds_market_id BIGINT NOT NULL REFERENCES odds_market (id), - raw_odd_id BIGINT NOT NULL, - market_id BIGINT NOT NULL, - event_id BIGINT NOT NULL, - odd_value DOUBLE PRECISION NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE company_odd_settings ( - id BIGSERIAL PRIMARY KEY, - company_id BIGINT NOT NULL, - odds_market_id BIGINT NOT NULL, - is_active BOOLEAN, - custom_raw_odds JSONB, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE (company_id, odds_market_id) -); -CREATE TABLE global_odd_market_settings ( - market_id BIGINT NOT NULL, - market_name TEXT NOT NULL, - is_active BOOLEAN NOT NULL DEFAULT true, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE (market_id) -); -CREATE TABLE company_odd_market_settings ( - market_id BIGINT NOT NULL, - market_name TEXT NOT NULL, - company_id BIGINT NOT NULL, - is_active BOOLEAN, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE (company_id, market_id) -); -CREATE TABLE result_log ( - id BIGSERIAL PRIMARY KEY, - status_not_finished_count INT NOT NULL, - status_not_finished_bets INT NOT NULL, - status_to_be_fixed_count INT NOT NULL, - status_to_be_fixed_bets INT NOT NULL, - status_postponed_count INT NOT NULL, - status_postponed_bets INT NOT NULL, - status_ended_count INT NOT NULL, - status_ended_bets INT NOT NULL, - status_removed_count INT NOT NULL, - status_removed_bets INT NOT NULL, - removed_count INT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE companies ( - id BIGSERIAL PRIMARY KEY, - name TEXT NOT NULL, - slug TEXT UNIQUE NOT NULL, - admin_id BIGINT NOT NULL, - wallet_id BIGINT NOT NULL, - deducted_percentage REAL NOT NULL, - is_active BOOLEAN NOT NULL DEFAULT true, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT deducted_percentage_check CHECK ( - deducted_percentage > 0 - AND deducted_percentage < 1 - ) -); -CREATE TABLE company_stats ( - company_id BIGINT NOT NULL, - company_name TEXT NOT NULL, - company_slug TEXT NOT NULL, - interval_start TIMESTAMP NOT NULL, - total_bets BIGINT NOT NULL, - total_stake BIGINT NOT NULL, - deducted_stake BIGINT NOT NULL, - total_cash_out BIGINT NOT NULL, - total_cash_backs BIGINT NOT NULL, - number_of_unsettled BIGINT NOT NULL, - total_unsettled_amount BIGINT NOT NULL, - total_admins BIGINT NOT NULL, - total_managers BIGINT NOT NULL, - total_cashiers BIGINT NOT NULL, - total_customers BIGINT NOT NULL, - total_approvers BIGINT NOT NULL, - total_branches BIGINT NOT NULL, - updated_at TIMESTAMP DEFAULT now(), - UNIQUE(company_id, interval_start) -); -CREATE TABLE leagues ( - id BIGINT PRIMARY KEY, - name TEXT NOT NULL, - img_url TEXT, - country_code TEXT, - bet365_id INT, - sport_id INT NOT NULL, - default_is_active BOOLEAN NOT NULL DEFAULT true, - default_is_featured BOOLEAN NOT NULL DEFAULT false -); -CREATE TABLE company_league_settings ( - id BIGSERIAL PRIMARY KEY, - company_id BIGINT NOT NULL, - league_id BIGINT NOT NULL, - is_active BOOLEAN, - is_featured BOOLEAN, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE (league_id, company_id) -); -CREATE TABLE teams ( - id BIGSERIAL PRIMARY KEY, - team_name TEXT NOT NULL, - country_code TEXT NOT NULL, - bet365_id BIGINT, - img_url TEXT -); -CREATE TABLE IF NOT EXISTS global_settings ( - key TEXT PRIMARY KEY, - value TEXT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); --- Tenant/Company-specific overrides -CREATE TABLE IF NOT EXISTS company_settings ( - company_id BIGINT NOT NULL REFERENCES companies (id) ON DELETE CASCADE, - key TEXT NOT NULL, - value TEXT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (company_id, key) -); -CREATE TABLE user_bonuses ( - id BIGSERIAL PRIMARY KEY, - name TEXT NOT NULL, - description TEXT NOT NULL, - type TEXT NOT NULL, - user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - reward_amount BIGINT NOT NULL, - is_claimed BOOLEAN NOT NULL DEFAULT false, - expires_at TIMESTAMP NOT NULL, - claimed_at TIMESTAMP, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE flags ( - id BIGSERIAL PRIMARY KEY, - bet_id BIGINT REFERENCES bets (id) ON DELETE CASCADE, - odds_market_id BIGINT REFERENCES odds_market (id), - reason TEXT, - flagged_at TIMESTAMP DEFAULT NOW (), - resolved BOOLEAN DEFAULT FALSE, - -- either bet or odd is flagged (not at the same time) - CHECK ( - ( - bet_id IS NOT NULL - AND odds_market_id IS NULL - ) - OR ( - bet_id IS NULL - AND odds_market_id IS NOT NULL - ) - ) -); -CREATE TABLE direct_deposits ( - id BIGSERIAL PRIMARY KEY, - customer_id BIGINT REFERENCES users(id), - wallet_id BIGINT REFERENCES wallets(id), - bank_name TEXT, - account_number TEXT, - account_holder TEXT, - amount NUMERIC(18,2), - reference_number TEXT, - transfer_screenshot TEXT, - status TEXT CHECK(status IN ('PENDING', 'APPROVED', 'REJECTED')), - created_at TIMESTAMPTZ, - approved_by BIGINT NULL REFERENCES users(id), - approved_at TIMESTAMPTZ NULL, - rejection_reason TEXT NULL -); --- CREATE INDEX idx_direct_deposits_status ON direct_deposits (status); --- CREATE INDEX idx_direct_deposits_customer ON direct_deposits (customer_id); --- CREATE INDEX idx_direct_deposits_reference ON direct_deposits (bank_reference); -CREATE TABLE IF NOT EXISTS raffles ( - id SERIAL PRIMARY KEY, - company_id INT NOT NULL, - name VARCHAR(255) NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - expires_at TIMESTAMP NOT NULL, - -- -1 means there is no limit for the raffle - ticket_limit INT NOT NULL DEFAULT -1, - type VARCHAR(50) NOT NULL CHECK (type IN ('virtual', 'sport')), - status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'completed')) -); -CREATE TABLE IF NOT EXISTS raffle_tickets ( - id SERIAL PRIMARY KEY, - raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE, - user_id INT NOT NULL, - is_active BOOL DEFAULT true -); -CREATE TABLE IF NOT EXISTS raffle_winners ( - id SERIAL PRIMARY KEY, - raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE, - user_id INT NOT NULL, - rank INT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT NOW() -); -CREATE TABLE IF NOT EXISTS raffle_sport_filters ( - id SERIAL PRIMARY KEY, - raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE, - sport_id BIGINT NOT NULL, - league_id BIGINT NOT NULL, - CONSTRAINT unique_raffle_sport_league UNIQUE (raffle_id, sport_id, league_id) -); -CREATE TABLE IF NOT EXISTS raffle_game_filters ( - id SERIAL PRIMARY KEY, - raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE, - game_id VARCHAR(150) NOT NULL, - CONSTRAINT unique_raffle_game UNIQUE (raffle_id, game_id) -); -CREATE TABLE IF NOT EXISTS company_accumulator ( - id SERIAL PRIMARY KEY, - company_id BIGINT NOT NULL, - outcome_count BIGINT NOT NULL, - multiplier REAL NOT NULL -); -CREATE TABLE report_requests ( - id BIGSERIAL PRIMARY KEY, - company_id BIGINT, - requested_by BIGINT, - file_path TEXT, - type TEXT NOT NULL, - status TEXT NOT NULL DEFAULT 'pending', - metadata JSONB NOT NULL, - reject_reason TEXT, - created_at TIMESTAMP DEFAULT now(), - completed_at TIMESTAMP -); ------- Views -CREATE VIEW companies_details AS -SELECT companies.*, - wallets.balance, - wallets.is_active as wallet_is_active, - users.first_name AS admin_first_name, - users.last_name AS admin_last_name, - users.phone_number AS admin_phone_number, - COALESCE(cs.total_bets, 0) AS total_bets, - COALESCE(cs.total_stake, 0) AS total_stake, - COALESCE(cs.deducted_stake, 0) AS deducted_stake, - COALESCE(cs.total_cash_out, 0) AS total_cash_out, - COALESCE(cs.total_cash_backs, 0) AS total_cash_backs, - COALESCE(cs.number_of_unsettled, 0) AS number_of_unsettled, - COALESCE(cs.total_unsettled_amount, 0) AS total_unsettled_amount, - COALESCE(cs.total_admins, 0) AS total_admins, - COALESCE(cs.total_managers, 0) AS total_managers, - COALESCE(cs.total_cashiers, 0) AS total_cashiers, - COALESCE(cs.total_customers, 0) AS total_customers, - COALESCE(cs.total_approvers, 0) AS total_approvers, - COALESCE(cs.total_branches, 0) AS total_branches, - cs.updated_at AS stats_updated_at -FROM companies - JOIN wallets ON wallets.id = companies.wallet_id - JOIN users ON users.id = companies.admin_id - LEFT JOIN LATERAL ( - SELECT * - FROM company_stats s - WHERE s.company_id = companies.id - ORDER BY s.interval_start DESC - LIMIT 1 - ) cs ON true; -CREATE VIEW branch_details AS -SELECT branches.*, - CONCAT (users.first_name, ' ', users.last_name) AS manager_name, - users.phone_number AS manager_phone_number, - wallets.balance, - wallets.is_active AS wallet_is_active, - companies.name AS company_name, - COALESCE(bs.total_bets, 0) AS total_bets, - COALESCE(bs.total_stake, 0) AS total_stake, - COALESCE(bs.deducted_stake, 0) AS deducted_stake, - COALESCE(bs.total_cash_out, 0) AS total_cash_out, - COALESCE(bs.total_cash_backs, 0) AS total_cash_backs, - COALESCE(bs.number_of_unsettled, 0) AS number_of_unsettled, - COALESCE(bs.total_unsettled_amount, 0) AS total_unsettled_amount, - COALESCE(bs.total_cashiers, 0) AS total_cashiers, - bs.updated_at AS stats_updated_at -FROM branches - LEFT JOIN users ON branches.branch_manager_id = users.id - LEFT JOIN wallets ON wallets.id = branches.wallet_id - JOIN companies ON companies.id = branches.company_id - LEFT JOIN LATERAL ( - SELECT * - FROM branch_stats s - WHERE s.branch_id = branches.id - ORDER BY s.interval_start DESC - LIMIT 1 - ) bs ON true; -CREATE TABLE IF NOT EXISTS supported_operations ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - description VARCHAR(255) NOT NULL -); -CREATE VIEW bet_with_outcomes AS -SELECT bets.*, - CONCAT (users.first_name, ' ', users.last_name) AS full_name, - users.phone_number, - JSON_AGG (bet_outcomes.*) AS outcomes, - companies.slug as company_slug -FROM bets - LEFT JOIN bet_outcomes ON bets.id = bet_outcomes.bet_id - LEFT JOIN users ON bets.user_id = users.id - JOIN companies ON bets.company_id = companies.id -GROUP BY bets.id, - users.first_name, - users.last_name, - users.phone_number, - companies.slug; -CREATE VIEW ticket_with_outcomes AS -SELECT tickets.*, - JSON_AGG (ticket_outcomes.*) AS outcomes -FROM tickets - LEFT JOIN ticket_outcomes ON tickets.id = ticket_outcomes.ticket_id -GROUP BY tickets.id; -CREATE VIEW customer_wallet_details AS -SELECT cw.id, - cw.customer_id, - rw.id AS regular_id, - rw.balance AS regular_balance, - sw.id AS static_id, - sw.balance AS static_balance, - rw.is_active as regular_is_active, - sw.is_active as static_is_active, - rw.updated_at as regular_updated_at, - sw.updated_at as static_updated_at, - cw.created_at, - users.first_name, - users.last_name, - users.phone_number, - COALESCE(cs.number_of_transactions, 0) AS number_of_transactions, - COALESCE(cs.total_transactions, 0) AS total_transactions, - COALESCE(cs.number_of_deposits, 0) AS number_of_deposits, - COALESCE(cs.total_deposits_amount, 0) AS total_deposits_amount, - COALESCE(cs.number_of_withdraws, 0) AS number_of_withdraws, - COALESCE(cs.total_withdraws_amount, 0) AS total_withdraws_amount, - COALESCE(cs.number_of_transfers, 0) AS number_of_transfers, - COALESCE(cs.total_transfers_amount, 0) AS total_transfers_amount, - cs.updated_at AS stats_updated_at -FROM customer_wallets cw - JOIN wallets rw ON cw.regular_wallet_id = rw.id - JOIN wallets sw ON cw.static_wallet_id = sw.id - JOIN users ON users.id = cw.customer_id - LEFT JOIN LATERAL ( - SELECT * - FROM wallet_stats s - WHERE s.wallet_id = cw.regular_wallet_id - ORDER BY s.interval_start DESC - LIMIT 1 - ) cs ON true; -CREATE VIEW wallet_transfer_details AS -SELECT wt.*, - users.first_name, - users.last_name, - users.phone_number -FROM wallet_transfer wt - LEFT JOIN users ON users.id = wt.cashier_id; -CREATE VIEW shop_transaction_detail AS -SELECT st.*, - cr.first_name AS creator_first_name, - cr.last_name AS creator_last_name, - cr.phone_number AS creator_phone_number, - ap.first_name AS approver_first_name, - ap.last_name AS approver_last_name, - ap.phone_number AS approver_phone_number, - branches.name AS branch_name, - branches.location AS branch_location -FROM shop_transactions st - LEFT JOIN users cr ON cr.id = st.user_id - LEFT JOIN users ap ON ap.id = st.approved_by - LEFT JOIN branches ON branches.id = st.branch_id; -CREATE VIEW shop_bet_detail AS -SELECT sb.*, - st.full_name AS customer_full_name, - st.phone_number AS customer_phone_number, - st.branch_id, - st.company_id, - st.amount, - st.verified AS transaction_verified, - bets.status, - bets.total_odds, - bets.fast_code, - JSON_AGG (bet_outcomes.*) AS outcomes -FROM shop_bets AS sb - JOIN shop_transactions st ON st.id = sb.shop_transaction_id - JOIN bets ON bets.id = sb.bet_id - LEFT JOIN bet_outcomes ON bet_outcomes.bet_id = sb.bet_id -GROUP BY sb.id, - st.full_name, - st.phone_number, - st.branch_id, - st.company_id, - st.amount, - st.verified, - bets.status, - bets.total_odds, - bets.fast_code; -CREATE VIEW shop_deposit_detail AS -SELECT sd.*, - st.full_name, - st.phone_number, - st.branch_id, - st.company_id, - st.amount, - st.verified AS transaction_verified -FROM shop_deposits AS sd - JOIN shop_transactions st ON st.id = sd.shop_transaction_id; -CREATE OR REPLACE VIEW event_detailed AS -SELECT events.*, - leagues.country_code as league_cc, - COALESCE(om.total_outcomes, 0) AS total_outcomes, - COALESCE(ebs.number_of_bets, 0) AS number_of_bets, - COALESCE(ebs.total_amount, 0) AS total_amount, - COALESCE(ebs.avg_bet_amount, 0) AS avg_bet_amount, - COALESCE(ebs.total_potential_winnings, 0) AS total_potential_winnings -FROM events - LEFT JOIN event_bet_stats ebs ON ebs.event_id = events.id - LEFT JOIN leagues ON leagues.id = events.league_id - LEFT JOIN ( - SELECT event_id, - SUM(number_of_outcomes) AS total_outcomes - FROM odds_market - GROUP BY event_id - ) om ON om.event_id = events.id; -CREATE VIEW odds_market_with_event AS -SELECT o.*, - e.is_monitored, - e.is_live, - e.status, - e.source -FROM odds_market o - JOIN events e ON o.event_id = e.id; --- Views only for SQLC to generate structs for them (so that we can reuse those structs) -CREATE VIEW league_with_settings AS -SELECT l.*, - cls.company_id, - COALESCE(cls.is_active, l.default_is_active) AS is_active, - COALESCE(cls.is_featured, l.default_is_featured) AS is_featured, - cls.updated_at -FROM leagues l - LEFT JOIN company_league_settings cls ON l.id = cls.league_id; -CREATE VIEW event_with_settings AS -SELECT e.*, - ces.company_id, - COALESCE(ces.is_active, e.default_is_active) AS is_active, - COALESCE(ces.is_featured, e.default_is_featured) AS is_featured, - COALESCE( - ces.winning_upper_limit, - e.default_winning_upper_limit - ) AS winning_upper_limit, - ces.updated_at as company_updated_at, - l.country_code as league_cc, - COALESCE(om.total_outcomes, 0) AS total_outcomes, - COALESCE(ebs.number_of_bets, 0) AS number_of_bets, - COALESCE(ebs.total_amount, 0) AS total_amount, - COALESCE(ebs.avg_bet_amount, 0) AS avg_bet_amount, - COALESCE(ebs.total_potential_winnings, 0) AS total_potential_winnings -FROM events e - LEFT JOIN company_event_settings ces ON e.id = ces.event_id - JOIN leagues l ON l.id = e.league_id - LEFT JOIN event_bet_stats ebs ON ebs.event_id = e.id - LEFT JOIN ( - SELECT event_id, - SUM(number_of_outcomes) AS total_outcomes - FROM odds_market - GROUP BY event_id - ) om ON om.event_id = e.id; -CREATE VIEW odds_market_with_settings AS -SELECT o.id, - o.event_id, - o.market_type, - o.market_name, - o.market_category, - o.market_id, - o.number_of_outcomes, - o.default_is_active, - o.fetched_at, - o.expires_at, - cos.company_id, - COALESCE(cos.is_active, o.default_is_active) AS is_active, - COALESCE(cms.is_active, TRUE) AS is_market_active, - COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, - cos.updated_at -FROM odds_market o - LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id - LEFT JOIN company_odd_market_settings cms ON o.id = cms.market_id; -CREATE VIEW report_request_detail AS -SELECT r.*, - c.name AS company_name, - c.slug AS company_slug, - u.first_name AS requester_first_name, - u.last_name AS requester_last_name, - u.role AS requester_role -FROM report_requests r - LEFT JOIN companies c ON c.id = r.company_id - LEFT JOIN users u ON u.id = r.requested_by; --- Foreign Keys -ALTER TABLE refresh_tokens -ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users (id); -ALTER TABLE bets -ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users (id); -ALTER TABLE wallets -ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users (id); -ALTER TABLE customer_wallets -ADD CONSTRAINT fk_customer_wallets_customers FOREIGN KEY (customer_id) REFERENCES users (id), - ADD CONSTRAINT fk_customer_wallets_regular_wallet FOREIGN KEY (regular_wallet_id) REFERENCES wallets (id), - ADD CONSTRAINT fk_customer_wallets_static_wallet FOREIGN KEY (static_wallet_id) REFERENCES wallets (id); -ALTER TABLE wallet_transfer -ADD CONSTRAINT fk_wallet_transfer_receiver_wallet FOREIGN KEY (receiver_wallet_id) REFERENCES wallets (id), - ADD CONSTRAINT fk_wallet_transfer_sender_wallet FOREIGN KEY (sender_wallet_id) REFERENCES wallets (id), - ADD CONSTRAINT fk_wallet_transfer_cashier FOREIGN KEY (cashier_id) REFERENCES users (id); -ALTER TABLE shop_transactions -ADD CONSTRAINT fk_shop_transactions_branches FOREIGN KEY (branch_id) REFERENCES branches (id), - ADD CONSTRAINT fk_shop_transactions_users FOREIGN KEY (user_id) REFERENCES users (id); -ALTER TABLE shop_bets -ADD CONSTRAINT fk_shop_bet_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions (id), - ADD CONSTRAINT fk_shop_bet_bets FOREIGN KEY (bet_id) REFERENCES bets (id); -ALTER TABLE shop_deposits -ADD CONSTRAINT fk_shop_deposit_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions (id), - ADD CONSTRAINT fk_shop_deposit_customers FOREIGN KEY (customer_id) REFERENCES users (id); -ALTER TABLE branches -ADD CONSTRAINT fk_branches_wallet FOREIGN KEY (wallet_id) REFERENCES wallets (id), - ADD CONSTRAINT fk_branches_manager FOREIGN KEY (branch_manager_id) REFERENCES users (id), - ADD CONSTRAINT fk_branches_location FOREIGN KEY (location) REFERENCES branch_locations (key); -ALTER TABLE branch_operations -ADD CONSTRAINT fk_branch_operations_operations FOREIGN KEY (operation_id) REFERENCES supported_operations (id) ON DELETE CASCADE, - ADD CONSTRAINT fk_branch_operations_branches FOREIGN KEY (branch_id) REFERENCES branches (id) ON DELETE CASCADE; -ALTER TABLE branch_cashiers -ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, - ADD CONSTRAINT fk_branch_cashiers_branches FOREIGN KEY (branch_id) REFERENCES branches (id) ON DELETE CASCADE; -ALTER TABLE companies -ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users (id), - ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets (id) ON DELETE CASCADE; -ALTER TABLE company_league_settings -ADD CONSTRAINT fk_league_settings_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE, - ADD CONSTRAINT fk_league_settings_league FOREIGN KEY (league_id) REFERENCES leagues (id) ON DELETE CASCADE; -ALTER TABLE company_event_settings -ADD CONSTRAINT fk_event_settings_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE, - ADD CONSTRAINT fk_event_settings_event FOREIGN KEY (event_id) REFERENCES events (id) ON DELETE CASCADE; -ALTER TABLE company_odd_settings -ADD CONSTRAINT fk_odds_settings_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE, - ADD CONSTRAINT fk_odds_settings_odds_market FOREIGN KEY (odds_market_id) REFERENCES odds_market (id) ON DELETE CASCADE; \ No newline at end of file diff --git a/db/migrations/000001_yimaru.down.sql b/db/migrations/000001_yimaru.down.sql new file mode 100644 index 0000000..a29d002 --- /dev/null +++ b/db/migrations/000001_yimaru.down.sql @@ -0,0 +1,51 @@ +-- ========================================= +-- Notifications +-- ========================================= +DROP TABLE IF EXISTS notifications; + + +-- ========================================= +-- Issue Reporting +-- ========================================= +DROP TABLE IF EXISTS reported_issues; + +-- ========================================= +-- Assessments +-- ========================================= +DROP TABLE IF EXISTS assessment_submissions; +DROP TABLE IF EXISTS assessments; + +-- ========================================= +-- Progress & Enrollment +-- ========================================= +DROP TABLE IF EXISTS lesson_progress; +DROP TABLE IF EXISTS enrollments; + +-- ========================================= +-- Course Content Structure +-- ========================================= +DROP TABLE IF EXISTS lessons; +DROP TABLE IF EXISTS course_modules; +DROP TABLE IF EXISTS courses; + +-- ========================================= +-- Organization Settings +-- ========================================= +DROP TABLE IF EXISTS organization_settings; +DROP TABLE IF EXISTS global_settings; + +-- ========================================= +-- Organizations (Tenants) +-- ========================================= +DROP TABLE IF EXISTS organizations; + +-- ========================================= +-- Authentication & Security +-- ========================================= +DROP TABLE IF EXISTS refresh_tokens; +DROP TABLE IF EXISTS otps; + +-- ========================================= +-- Users +-- ========================================= +DROP TABLE IF EXISTS users; diff --git a/db/migrations/000001_yimaru.up.sql b/db/migrations/000001_yimaru.up.sql new file mode 100644 index 0000000..f2a24ab --- /dev/null +++ b/db/migrations/000001_yimaru.up.sql @@ -0,0 +1,195 @@ +CREATE TABLE IF NOT EXISTS users ( + id BIGSERIAL PRIMARY KEY, + first_name VARCHAR(255) NOT NULL, + last_name VARCHAR(255) NOT NULL, + nick_name VARCHAR(100), + email VARCHAR(255) UNIQUE, + phone_number VARCHAR(20) UNIQUE, + role VARCHAR(50) NOT NULL, -- SUPER_ADMIN, ORG_ADMIN, INSTRUCTOR, STUDENT, SUPPORT + password BYTEA NOT NULL, + age INT, + education_level VARCHAR(100), + country VARCHAR(100), + region VARCHAR(100), + email_verified BOOLEAN NOT NULL DEFAULT FALSE, + phone_verified BOOLEAN NOT NULL DEFAULT FALSE, + suspended BOOLEAN NOT NULL DEFAULT FALSE, + suspended_at TIMESTAMPTZ, + organization_id BIGINT, + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ, + CHECK (email IS NOT NULL OR phone_number IS NOT NULL), + UNIQUE (email, organization_id), + UNIQUE (phone_number, organization_id) +); + +CREATE TABLE refresh_tokens ( + id BIGSERIAL PRIMARY KEY, + user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + token TEXT NOT NULL UNIQUE, + expires_at TIMESTAMPTZ NOT NULL, + revoked BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE otps ( + id BIGSERIAL PRIMARY KEY, + sent_to VARCHAR(255) NOT NULL, + medium VARCHAR(50) NOT NULL, -- email, sms + otp_for VARCHAR(50) NOT NULL, -- login, reset_password, verify + otp VARCHAR(10) NOT NULL, + used BOOLEAN NOT NULL DEFAULT FALSE, + used_at TIMESTAMPTZ, + expires_at TIMESTAMPTZ NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE organizations ( + id BIGSERIAL PRIMARY KEY, + name TEXT NOT NULL, + slug TEXT UNIQUE NOT NULL, + owner_id BIGINT NOT NULL REFERENCES users(id), + is_active BOOLEAN NOT NULL DEFAULT TRUE, + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE courses ( + id BIGSERIAL PRIMARY KEY, + organization_id BIGINT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, + instructor_id BIGINT NOT NULL REFERENCES users(id), + title TEXT NOT NULL, + description TEXT, + level TEXT, + language TEXT, + is_published BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE course_modules ( + id BIGSERIAL PRIMARY KEY, + course_id BIGINT NOT NULL REFERENCES courses(id) ON DELETE CASCADE, + title TEXT NOT NULL, + position INT NOT NULL, + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE lessons ( + id BIGSERIAL PRIMARY KEY, + module_id BIGINT NOT NULL REFERENCES course_modules(id) ON DELETE CASCADE, + title TEXT NOT NULL, + content_type TEXT NOT NULL, -- video, article, quiz + content_url TEXT, + duration_minutes INT, + position INT NOT NULL, + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE enrollments ( + id BIGSERIAL PRIMARY KEY, + course_id BIGINT NOT NULL REFERENCES courses(id) ON DELETE CASCADE, + student_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + enrolled_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + completed_at TIMESTAMPTZ, + UNIQUE (course_id, student_id) +); + +CREATE TABLE lesson_progress ( + id BIGSERIAL PRIMARY KEY, + lesson_id BIGINT NOT NULL REFERENCES lessons(id) ON DELETE CASCADE, + student_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + completed BOOLEAN NOT NULL DEFAULT FALSE, + completed_at TIMESTAMPTZ, + UNIQUE (lesson_id, student_id) +); + +CREATE TABLE assessments ( + id BIGSERIAL PRIMARY KEY, + course_id BIGINT NOT NULL REFERENCES courses(id) ON DELETE CASCADE, + title TEXT NOT NULL, + type TEXT NOT NULL, -- quiz, assignment + total_score INT NOT NULL, + due_date TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE assessment_submissions ( + id BIGSERIAL PRIMARY KEY, + assessment_id BIGINT NOT NULL REFERENCES assessments(id) ON DELETE CASCADE, + student_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + score INT, + feedback TEXT, + submitted_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + graded_at TIMESTAMPTZ, + UNIQUE (assessment_id, student_id) +); + +CREATE TABLE IF NOT EXISTS notifications ( + id BIGSERIAL PRIMARY KEY, + user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + + type TEXT NOT NULL CHECK ( + type IN ( + 'course_enrolled', + 'lesson_completed', + 'assessment_assigned', + 'assessment_submitted', + 'assessment_graded', + 'course_completed', + 'certificate_issued', + 'announcement', + 'otp_sent', + 'signup_welcome', + 'system_alert' + ) + ), + + level TEXT NOT NULL CHECK ( + level IN ('info', 'warning', 'success', 'error') + ), + + channel TEXT CHECK ( + channel IN ('email', 'sms', 'push', 'in_app') + ), + + title TEXT NOT NULL, + message TEXT NOT NULL, + + payload JSONB, + is_read BOOLEAN NOT NULL DEFAULT FALSE, + + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + read_at TIMESTAMPTZ +); + +CREATE TABLE global_settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE organization_settings ( + organization_id BIGINT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, + key TEXT NOT NULL, + value TEXT NOT NULL, + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (organization_id, key) +); + +CREATE TABLE IF NOT EXISTS reported_issues ( + id BIGSERIAL PRIMARY KEY, + user_id BIGINT NOT NULL REFERENCES users(id), + user_role VARCHAR(255) NOT NULL, + subject TEXT NOT NULL, + description TEXT NOT NULL, + issue_type TEXT NOT NULL, + -- e.g., "deposit", "withdrawal", "bet", "technical" + status TEXT NOT NULL DEFAULT 'pending', + -- pending, in_progress, resolved, rejected + metadata JSONB, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); diff --git a/db/migrations/000002_notification.down.sql b/db/migrations/000002_notification.down.sql deleted file mode 100644 index 5e492c9..0000000 --- a/db/migrations/000002_notification.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -DROP TABLE notifications; -DROP TABLE wallet_threshold_notifications; diff --git a/db/migrations/000002_notification.up.sql b/db/migrations/000002_notification.up.sql deleted file mode 100644 index 66ac72d..0000000 --- a/db/migrations/000002_notification.up.sql +++ /dev/null @@ -1,54 +0,0 @@ -CREATE TABLE IF NOT EXISTS notifications ( - id VARCHAR(255) NOT NULL PRIMARY KEY, - recipient_id BIGSERIAL NOT NULL, - type TEXT NOT NULL CHECK ( - type IN ( - 'cash_out_success', - 'deposit_success', - 'withdraw_success', - 'bet_placed', - 'daily_report', - 'report_request', - 'high_loss_on_bet', - 'bet_overload', - 'signup_welcome', - 'otp_sent', - 'wallet_threshold', - 'wallet_updated', - 'transfer_failed', - 'transfer_success', - 'admin_alert', - 'bet_result', - 'transfer_rejected', - 'approval_required', - 'bonus_awarded' - ) - ), - level TEXT NOT NULL CHECK (level IN ('info', 'error', 'warning', 'success')), - error_severity TEXT CHECK ( - error_severity IN ('low', 'medium', 'high', 'critical', 'fatal') - ), - reciever TEXT NOT NULL CHECK (reciever IN ('admin', 'customer')), - is_read BOOLEAN NOT NULL DEFAULT FALSE, - delivery_status TEXT NOT NULL CHECK (delivery_status IN ('pending', 'sent', 'failed')), - delivery_channel TEXT CHECK ( - delivery_channel IN ('email', 'sms', 'push', 'in-app') - ), - payload JSONB NOT NULL, - priority INTEGER, - version INTEGER NOT NULL DEFAULT 0, - timestamp TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - img TEXT, - expires TIMESTAMPTZ NOT NULL, - metadata JSONB -); -CREATE TABLE IF NOT EXISTS wallet_threshold_notifications ( - company_id BIGINT NOT NULL, - threshold FLOAT NOT NULL, - notified_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (company_id, threshold) -); -CREATE INDEX idx_wallet_threshold_notifications_company ON wallet_threshold_notifications(company_id); -CREATE INDEX idx_notifications_recipient_id ON notifications (recipient_id); -CREATE INDEX idx_notifications_timestamp ON notifications (timestamp); -CREATE INDEX idx_notifications_type ON notifications (type); \ No newline at end of file diff --git a/db/migrations/000003_referal.down.sql b/db/migrations/000002_referal.down.sql similarity index 100% rename from db/migrations/000003_referal.down.sql rename to db/migrations/000002_referal.down.sql diff --git a/db/migrations/000002_referal.up.sql b/db/migrations/000002_referal.up.sql new file mode 100644 index 0000000..566a7b4 --- /dev/null +++ b/db/migrations/000002_referal.up.sql @@ -0,0 +1,45 @@ +CREATE TABLE IF NOT EXISTS referral_codes ( + id BIGSERIAL PRIMARY KEY, + code VARCHAR(12) NOT NULL UNIQUE, + + referrer_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + + is_active BOOLEAN NOT NULL DEFAULT TRUE, + max_uses INT, + current_uses INT NOT NULL DEFAULT 0, + + incentive_type TEXT NOT NULL CHECK ( + incentive_type IN ( + 'course_access', + 'discount', + 'certificate_unlock', + 'feature_unlock' + ) + ), + + incentive_value TEXT, -- e.g. course_id, percentage, feature_key + + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_referral_codes_referrer_id + ON referral_codes (referrer_id); + +CREATE INDEX idx_referral_codes_code + ON referral_codes (code); + + +CREATE TABLE IF NOT EXISTS user_referrals ( + id BIGSERIAL PRIMARY KEY, + + referrer_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + referred_user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + + referral_code_id BIGINT NOT NULL REFERENCES referral_codes(id) ON DELETE CASCADE, + + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + + UNIQUE (referred_user_id), + UNIQUE (referrer_id, referred_user_id) +); diff --git a/db/migrations/000007_issue_reporting.down.sql b/db/migrations/000003_issue_reporting.down.sql similarity index 100% rename from db/migrations/000007_issue_reporting.down.sql rename to db/migrations/000003_issue_reporting.down.sql diff --git a/db/migrations/000007_issue_reporting.up.sql b/db/migrations/000003_issue_reporting.up.sql similarity index 100% rename from db/migrations/000007_issue_reporting.up.sql rename to db/migrations/000003_issue_reporting.up.sql diff --git a/db/migrations/000003_referal.up.sql b/db/migrations/000003_referal.up.sql deleted file mode 100644 index badf443..0000000 --- a/db/migrations/000003_referal.up.sql +++ /dev/null @@ -1,37 +0,0 @@ --- CREATE TYPE ReferralStatus AS ENUM ('PENDING', 'COMPLETED', 'EXPIRED', 'CANCELLED'); --- CREATE TABLE IF NOT EXISTS referral_settings ( --- id BIGSERIAL PRIMARY KEY, --- referral_reward_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00, --- cashback_percentage DECIMAL(5, 2) NOT NULL DEFAULT 0.00, --- bet_referral_bonus_percentage NUMERIC DEFAULT 5.0, --- max_referrals INTEGER NOT NULL DEFAULT 0, --- expires_after_days INTEGER NOT NULL DEFAULT 30, --- updated_by VARCHAR(255) NOT NULL, --- created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, --- updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, --- version INTEGER NOT NULL DEFAULT 0, --- CONSTRAINT referral_reward_amount_positive CHECK (referral_reward_amount >= 0), --- CONSTRAINT cashback_percentage_range CHECK ( --- cashback_percentage >= 0 --- AND cashback_percentage <= 100 --- ) --- ); -CREATE TABLE IF NOT EXISTS referral_codes ( - id BIGSERIAL PRIMARY KEY, - referral_code VARCHAR(10) NOT NULL UNIQUE, - referrer_id BIGINT NOT NULL UNIQUE REFERENCES users (id), - company_id BIGINT NOT NULL REFERENCES companies (id), - is_active BOOLEAN NOT NULL DEFAULT true, - number_of_referrals BIGINT NOT NULL, - reward_amount BIGINT NOT NULL, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT reward_amount_positive CHECK (reward_amount >= 0) -); -CREATE INDEX idx_referrals_referrer_id ON referral_codes (referrer_id); -CREATE TABLE IF NOT EXISTS user_referrals ( - id BIGSERIAL PRIMARY KEY, - referred_id BIGINT UNIQUE NOT NULL REFERENCES users (id), - referral_code_id BIGINT NOT NULL REFERENCES referral_codes (id), - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP -); \ No newline at end of file diff --git a/db/migrations/000004_virtual_game_Session.down.sql b/db/migrations/000004_virtual_game_Session.down.sql deleted file mode 100644 index 714c2e5..0000000 --- a/db/migrations/000004_virtual_game_Session.down.sql +++ /dev/null @@ -1,5 +0,0 @@ -DROP TABLE IF EXISTS vitrual_games; - -DROP TABLE IF EXISTS virtual_game_transactions; - -DROP TABLE IF EXISTS virtual_game_sessions; diff --git a/db/migrations/000004_virtual_game_Session.up.sql b/db/migrations/000004_virtual_game_Session.up.sql deleted file mode 100644 index de60ae0..0000000 --- a/db/migrations/000004_virtual_game_Session.up.sql +++ /dev/null @@ -1,64 +0,0 @@ -CREATE TABLE virtual_game_sessions ( - id BIGSERIAL PRIMARY KEY, - user_id BIGINT NOT NULL REFERENCES users(id), - game_id VARCHAR(50) NOT NULL, - session_token VARCHAR(255) NOT NULL UNIQUE, - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE virtual_game_transactions ( - id BIGSERIAL PRIMARY KEY, - -- session_id BIGINT NOT NULL REFERENCES virtual_game_sessions(id), - user_id BIGINT NOT NULL REFERENCES users(id), - company_id BIGINT, - provider VARCHAR(100), - game_id VARCHAR(100), - wallet_id BIGINT NOT NULL REFERENCES wallets(id), - transaction_type VARCHAR(20) NOT NULL, - amount BIGINT NOT NULL, - currency VARCHAR(3) NOT NULL, - external_transaction_id VARCHAR(100) NOT NULL UNIQUE, -- PopOK transaction ID - status VARCHAR(20) NOT NULL DEFAULT 'PENDING', -- PENDING, COMPLETED, FAILED - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE virtual_game_histories ( - id BIGSERIAL PRIMARY KEY, - -- session_id VARCHAR(100), -- nullable - user_id BIGINT NOT NULL, - company_id BIGINT, - provider VARCHAR(100), - wallet_id BIGINT, -- nullable - game_id BIGINT, -- nullable - transaction_type VARCHAR(20) NOT NULL, -- e.g., BET, WIN, CANCEL - amount BIGINT NOT NULL, -- in cents or smallest currency unit - currency VARCHAR(10) NOT NULL, - external_transaction_id VARCHAR(100) NOT NULL, - reference_transaction_id VARCHAR(100), -- nullable, for cancel/refund - status VARCHAR(20) NOT NULL DEFAULT 'COMPLETED', -- transaction status - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT NOW() -); - -CREATE TABLE IF NOT EXISTS favorite_games ( - id BIGSERIAL PRIMARY KEY, - user_id BIGINT NOT NULL, - game_id BIGINT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - --- Optional: Indexes for performance -CREATE INDEX idx_virtual_game_user_id ON virtual_game_histories(user_id); -CREATE INDEX idx_virtual_game_transaction_type ON virtual_game_histories(transaction_type); -CREATE INDEX idx_virtual_game_game_id ON virtual_game_histories(game_id); -CREATE INDEX idx_virtual_game_external_transaction_id ON virtual_game_histories(external_transaction_id); - -CREATE INDEX idx_virtual_game_sessions_user_id ON virtual_game_sessions(user_id); --- CREATE INDEX idx_virtual_game_transactions_session_id ON virtual_game_transactions(session_id); -CREATE INDEX idx_virtual_game_transactions_user_id ON virtual_game_transactions(user_id); - -ALTER TABLE favorite_games -ADD CONSTRAINT unique_user_game_favorite UNIQUE (user_id, game_id); - diff --git a/db/migrations/000005_result_checker.down.sql b/db/migrations/000005_result_checker.down.sql deleted file mode 100644 index e2882ff..0000000 --- a/db/migrations/000005_result_checker.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS results; diff --git a/db/migrations/000005_result_checker.up.sql b/db/migrations/000005_result_checker.up.sql deleted file mode 100644 index 2cbc8fb..0000000 --- a/db/migrations/000005_result_checker.up.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE TABLE IF NOT EXISTS results ( - id BIGSERIAL PRIMARY KEY, - bet_outcome_id BIGINT NOT NULL, - event_id BIGINT NOT NULL, - odd_id BIGINT NOT NULL, - market_id BIGINT NOT NULL, - status INT NOT NULL, - score VARCHAR(255), - full_time_score VARCHAR(255), - half_time_score VARCHAR(255), - ss VARCHAR(255), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (bet_outcome_id) REFERENCES bet_outcomes (id) -); diff --git a/db/migrations/000006_recommendation.down.sql b/db/migrations/000006_recommendation.down.sql deleted file mode 100644 index 10493be..0000000 --- a/db/migrations/000006_recommendation.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -DROP TABLE IF EXISTS virtual_games; -DROP TABLE IF EXISTS user_game_interactions; \ No newline at end of file diff --git a/db/migrations/000006_recommendation.up.sql b/db/migrations/000006_recommendation.up.sql deleted file mode 100644 index e0900fa..0000000 --- a/db/migrations/000006_recommendation.up.sql +++ /dev/null @@ -1,28 +0,0 @@ -CREATE TABLE IF NOT EXISTS virtual_games ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - provider VARCHAR(100) NOT NULL, - category VARCHAR(100) NOT NULL, - min_bet DECIMAL(15,2) NOT NULL, - max_bet DECIMAL(15,2) NOT NULL, - volatility VARCHAR(50) NOT NULL, - rtp DECIMAL(5,2) NOT NULL, - is_featured BOOLEAN DEFAULT false, - popularity_score INTEGER DEFAULT 0, - thumbnail_url TEXT, - created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE user_game_interactions ( - id BIGSERIAL PRIMARY KEY, - user_id BIGINT NOT NULL REFERENCES users(id), - game_id BIGINT NOT NULL REFERENCES virtual_games(id), - interaction_type VARCHAR(50) NOT NULL, - -- 'view', 'play', 'bet', 'favorite' - amount DECIMAL(15, 2), - duration_seconds INTEGER, - created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP -); -CREATE INDEX idx_user_game_interactions_user ON user_game_interactions(user_id); -CREATE INDEX idx_user_game_interactions_game ON user_game_interactions(game_id); \ No newline at end of file diff --git a/db/migrations/00008_enet_pulse.down.sql b/db/migrations/00008_enet_pulse.down.sql deleted file mode 100644 index 43196f1..0000000 --- a/db/migrations/00008_enet_pulse.down.sql +++ /dev/null @@ -1,11 +0,0 @@ -DROP TABLE IF EXISTS enetpulse_sports; -DROP TABLE IF EXISTS enetpulse_tournament_templates; -DROP TABLE IF EXISTS enetpulse_tournaments; -DROP TABLE IF EXISTS enetpulse_tournament_stages; -DROP TABLE IF EXISTS enetpulse_fixtures; -DROP TABLE IF EXISTS enetpulse_results; -DROP TABLE IF EXISTS enetpulse_result_participants; -DROP TABLE IF EXISTS enetpulse_result_referees; -DROP TABLE IF EXISTS enetpulse_outcome_types; -DROP TABLE IF EXISTS enetpulse_preodds; -DROP TABLE IF EXISTS enetpulse_preodds_bettingoffers; \ No newline at end of file diff --git a/db/migrations/00008_enet_pulse.up.sql b/db/migrations/00008_enet_pulse.up.sql deleted file mode 100644 index 416bddf..0000000 --- a/db/migrations/00008_enet_pulse.up.sql +++ /dev/null @@ -1,227 +0,0 @@ -CREATE TABLE IF NOT EXISTS enetpulse_sports ( - id BIGSERIAL PRIMARY KEY, - sport_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" - name VARCHAR(255) NOT NULL, -- from API "name" - updates_count INT DEFAULT 0, -- from API "n" - last_updated_at TIMESTAMPTZ, -- from API "ut" - status INT DEFAULT 1, -- optional status (active/inactive) - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE TABLE IF NOT EXISTS enetpulse_tournament_templates ( - id BIGSERIAL PRIMARY KEY, - template_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" - name VARCHAR(255) NOT NULL, -- from API "name" - sport_fk VARCHAR(50) NOT NULL REFERENCES enetpulse_sports(sport_id) ON DELETE CASCADE, - gender VARCHAR(20) DEFAULT 'unknown', -- from API "gender" {male, female, mixed, unknown} - updates_count INT DEFAULT 0, -- from API "n" - last_updated_at TIMESTAMPTZ, -- from API "ut" - status INT DEFAULT 1, -- optional status (active/inactive) - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE TABLE IF NOT EXISTS enetpulse_tournaments ( - id BIGSERIAL PRIMARY KEY, - tournament_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" - name VARCHAR(255) NOT NULL, -- from API "name" - - -- Link to the template it belongs to: - tournament_template_fk VARCHAR(50) NOT NULL - REFERENCES enetpulse_tournament_templates(template_id) ON DELETE CASCADE, - - updates_count INT DEFAULT 0, -- from API "n" - last_updated_at TIMESTAMPTZ, -- from API "ut" - status INT DEFAULT 1, -- optional active/inactive flag - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE TABLE IF NOT EXISTS enetpulse_tournament_stages ( - id BIGSERIAL PRIMARY KEY, - stage_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" - name VARCHAR(255) NOT NULL, -- from API "name" - tournament_fk VARCHAR(50) NOT NULL REFERENCES enetpulse_tournaments(tournament_id) ON DELETE CASCADE, - -- from API "tournamentFK" - gender VARCHAR(20) DEFAULT 'unknown', -- from API "gender" {male, female, mixed, unknown} - country_fk VARCHAR(50), -- from API "countryFK" - country_name VARCHAR(255), -- from API "country_name" - start_date TIMESTAMPTZ, -- from API "startdate" - end_date TIMESTAMPTZ, -- from API "enddate" - updates_count INT DEFAULT 0, -- from API "n" - last_updated_at TIMESTAMPTZ, -- from API "ut" - status INT DEFAULT 1, -- optional status (active/inactive) - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE INDEX IF NOT EXISTS idx_enetpulse_tournament_stages_tournament_fk - ON enetpulse_tournament_stages (tournament_fk); - -CREATE TABLE IF NOT EXISTS enetpulse_fixtures ( - id BIGSERIAL PRIMARY KEY, - fixture_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" - name VARCHAR(255) NOT NULL, -- fixture name (e.g. 12 de Junio-Sol de America) - - sport_fk VARCHAR(50) NOT NULL REFERENCES enetpulse_sports(sport_id) ON DELETE CASCADE, - tournament_fk VARCHAR(50), -- raw tournamentFK (optional) - tournament_template_fk VARCHAR(50) REFERENCES enetpulse_tournament_templates(template_id) ON DELETE CASCADE, - -- tournament_stage_fk VARCHAR(50) REFERENCES enetpulse_tournament_stages(stage_id) ON DELETE CASCADE, - - -- tournament_stage_name VARCHAR(255), - tournament_name VARCHAR(255), - tournament_template_name VARCHAR(255), - sport_name VARCHAR(255), - gender VARCHAR(20), - - start_date TIMESTAMPTZ NOT NULL, -- startdate - status_type VARCHAR(50), -- Not started, Live, Finished - status_desc_fk VARCHAR(50), -- status_descFK - round_type_fk VARCHAR(50), -- round_typeFK - updates_count INT DEFAULT 0, -- n - last_updated_at TIMESTAMPTZ, -- ut - - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE TABLE IF NOT EXISTS enetpulse_results ( - id BIGSERIAL PRIMARY KEY, - result_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" - name VARCHAR(255) NOT NULL, -- event name (e.g. Brentford-Manchester City) - - sport_fk VARCHAR(50) NOT NULL REFERENCES enetpulse_sports(sport_id) ON DELETE CASCADE, - tournament_fk VARCHAR(50), - tournament_template_fk VARCHAR(50) REFERENCES enetpulse_tournament_templates(template_id) ON DELETE CASCADE, - -- tournament_stage_fk VARCHAR(50) REFERENCES enetpulse_tournament_stages(stage_id) ON DELETE CASCADE, - - -- tournament_stage_name VARCHAR(255), - tournament_name VARCHAR(255), - tournament_template_name VARCHAR(255), - sport_name VARCHAR(255), - - start_date TIMESTAMPTZ NOT NULL, -- startdate - status_type VARCHAR(50), -- e.g. Finished - status_desc_fk VARCHAR(50), -- status_descFK - round_type_fk VARCHAR(50), -- round_typeFK - updates_count INT DEFAULT 0, -- n - last_updated_at TIMESTAMPTZ, -- ut - - -- Optional metadata (dynamic but common fields are included) - round VARCHAR(50), - live VARCHAR(10), - venue_name VARCHAR(255), - livestats_plus VARCHAR(10), - livestats_type VARCHAR(50), - commentary VARCHAR(50), - lineup_confirmed BOOLEAN, - verified BOOLEAN, - spectators INT, - - -- Time-related metadata - game_started TIMESTAMPTZ, - first_half_ended TIMESTAMPTZ, - second_half_started TIMESTAMPTZ, - second_half_ended TIMESTAMPTZ, - game_ended TIMESTAMPTZ, - - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - --- Event Participants (teams involved in the result) -CREATE TABLE IF NOT EXISTS enetpulse_result_participants ( - id BIGSERIAL PRIMARY KEY, - participant_map_id VARCHAR(50) NOT NULL UNIQUE, -- from event_participants.*.id - result_fk VARCHAR(50) NOT NULL REFERENCES enetpulse_results(result_id) ON DELETE CASCADE, - participant_fk VARCHAR(50) NOT NULL, -- team/player FK (from API participantFK) - number INT, -- 1 or 2 (home/away indicator) - name VARCHAR(255), -- team/player name - gender VARCHAR(20), - type VARCHAR(50), - country_fk VARCHAR(50), - country_name VARCHAR(100), - - -- Result details - ordinary_time VARCHAR(10), - running_score VARCHAR(10), - halftime VARCHAR(10), - final_result VARCHAR(10), - - last_updated_at TIMESTAMPTZ, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP -); - --- Referees / Match officials -CREATE TABLE IF NOT EXISTS enetpulse_result_referees ( - id BIGSERIAL PRIMARY KEY, - result_fk VARCHAR(50) NOT NULL REFERENCES enetpulse_results(result_id) ON DELETE CASCADE, - referee_fk VARCHAR(50), - assistant1_referee_fk VARCHAR(50), - assistant2_referee_fk VARCHAR(50), - fourth_referee_fk VARCHAR(50), - var1_referee_fk VARCHAR(50), - var2_referee_fk VARCHAR(50), - last_updated_at TIMESTAMPTZ, - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE IF NOT EXISTS enetpulse_outcome_types ( - id BIGSERIAL PRIMARY KEY, - outcome_type_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" - name VARCHAR(100) NOT NULL, -- e.g. "1x2" - description VARCHAR(255), -- e.g. "1x2 - 3Way" - - updates_count INT DEFAULT 0, -- maps to "n" - last_updated_at TIMESTAMPTZ, -- maps to "ut" - - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - --- Table for pre-match odds (main outcome entries) -CREATE TABLE IF NOT EXISTS enetpulse_preodds ( - id BIGSERIAL PRIMARY KEY, - preodds_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" - event_fk BIGINT NOT NULL, -- maps to objectFK/event - outcome_type_fk INT, -- outcome_typeFK - outcome_scope_fk INT, -- outcome_scopeFK - outcome_subtype_fk INT, -- outcome_subtypeFK - event_participant_number INT, -- event_participant_number - iparam VARCHAR(50), - iparam2 VARCHAR(50), - dparam VARCHAR(50), - dparam2 VARCHAR(50), - sparam VARCHAR(50), - - updates_count INT DEFAULT 0, -- maps to "n" - last_updated_at TIMESTAMPTZ, -- maps to "ut" - - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - --- Table for nested betting offers within preodds -CREATE TABLE IF NOT EXISTS enetpulse_preodds_bettingoffers ( - id BIGSERIAL PRIMARY KEY, - bettingoffer_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" - preodds_fk VARCHAR(50) NOT NULL REFERENCES enetpulse_preodds(preodds_id) ON DELETE CASCADE, - bettingoffer_status_fk INT, -- bettingoffer_statusFK - odds_provider_fk INT, -- odds_providerFK - odds NUMERIC(10,4), -- current odds - odds_old NUMERIC(10,4), -- previous odds - active BOOLEAN, -- maps "yes"/"no" to boolean - coupon_key VARCHAR(255), -- couponKey - updates_count INT DEFAULT 0, -- maps to "n" - last_updated_at TIMESTAMPTZ, -- maps to "ut" - - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - - - - - - diff --git a/db/migrations/00009_virtual_reports.down.sql b/db/migrations/00009_virtual_reports.down.sql deleted file mode 100644 index fa7e51e..0000000 --- a/db/migrations/00009_virtual_reports.down.sql +++ /dev/null @@ -1,4 +0,0 @@ -DROP TABLE IF EXISTS virtual_game_financial_reports; -DROP TABLE IF EXISTS virtual_game_company_reports; -DROP TABLE IF EXISTS virtual_game_player_cashflow_reports; -DROP TABLE IF EXISTS virtual_game_player_session_reports; \ No newline at end of file diff --git a/db/migrations/00009_virtual_reports.up.sql b/db/migrations/00009_virtual_reports.up.sql deleted file mode 100644 index f72ebcb..0000000 --- a/db/migrations/00009_virtual_reports.up.sql +++ /dev/null @@ -1,73 +0,0 @@ -CREATE TABLE IF NOT EXISTS virtual_game_financial_reports ( - id BIGSERIAL PRIMARY KEY, - game_id VARCHAR(150) NOT NULL REFERENCES virtual_games(game_id) ON DELETE CASCADE, - provider_id VARCHAR(100) NOT NULL REFERENCES virtual_game_providers(provider_id) ON DELETE CASCADE, - - report_date DATE NOT NULL, - report_type VARCHAR(50) NOT NULL DEFAULT 'daily', - - total_bets NUMERIC(18,2) DEFAULT 0, - total_wins NUMERIC(18,2) DEFAULT 0, - - ggr NUMERIC(18,2) GENERATED ALWAYS AS (total_bets - total_wins) STORED, - rtp NUMERIC(5,2) GENERATED ALWAYS AS ( - CASE WHEN total_bets > 0 THEN (total_wins / total_bets) * 100 ELSE 0 END - ) STORED, - - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE TABLE IF NOT EXISTS virtual_game_company_reports ( - id BIGSERIAL PRIMARY KEY, - - company_id BIGINT NOT NULL REFERENCES companies(id) ON DELETE CASCADE, - provider_id VARCHAR(100) NOT NULL REFERENCES virtual_game_providers(provider_id) ON DELETE CASCADE, - - report_date DATE NOT NULL, - report_type VARCHAR(50) NOT NULL DEFAULT 'daily', - - total_bet_amount NUMERIC(18,2) DEFAULT 0, - total_win_amount NUMERIC(18,2) DEFAULT 0, - - net_profit NUMERIC(18,2) GENERATED ALWAYS AS (total_bet_amount - total_win_amount) STORED, - profit_margin NUMERIC(6,3) GENERATED ALWAYS AS ( - CASE WHEN total_bet_amount > 0 THEN (total_bet_amount - total_win_amount) / total_bet_amount ELSE 0 END - ) STORED, - - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - -CREATE TABLE IF NOT EXISTS virtual_game_player_activity_reports ( - id BIGSERIAL PRIMARY KEY, - - user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - - -- Reporting scope - report_date DATE NOT NULL, - report_type VARCHAR(50) NOT NULL DEFAULT 'daily', - - -- Cashflow information - total_deposits NUMERIC(18,2) DEFAULT 0, - total_withdrawals NUMERIC(18,2) DEFAULT 0, - net_contribution NUMERIC(18,2) GENERATED ALWAYS AS ( - total_deposits - total_withdrawals - ) STORED, - - -- Betting and win/loss analytics - total_bet_amount NUMERIC(18,2) DEFAULT 0, - total_win_amount NUMERIC(18,2) DEFAULT 0, - net_result NUMERIC(18,2) GENERATED ALWAYS AS ( - total_bet_amount - total_win_amount - ) STORED, - - rounds_played BIGINT DEFAULT 0, - avg_bet_size NUMERIC(18,4) GENERATED ALWAYS AS ( - CASE WHEN rounds_played > 0 THEN total_bet_amount / rounds_played ELSE 0 END - ) STORED, - - created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMPTZ -); - diff --git a/db/query/auth.sql b/db/query/auth.sql index 3bd28bb..43a207e 100644 --- a/db/query/auth.sql +++ b/db/query/auth.sql @@ -6,8 +6,8 @@ WHERE ( OR phone_number = $2 ) AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL + organization_id = sqlc.narg('organization_id') + OR sqlc.narg('organization_id') IS NULL ); -- name: CreateRefreshToken :exec INSERT INTO refresh_tokens (user_id, token, expires_at, created_at, revoked) diff --git a/db/query/bet.sql b/db/query/bet.sql deleted file mode 100644 index 4a5307a..0000000 --- a/db/query/bet.sql +++ /dev/null @@ -1,240 +0,0 @@ --- name: CreateBet :one -INSERT INTO bets ( - amount, - total_odds, - status, - user_id, - is_shop_bet, - outcomes_hash, - fast_code, - company_id - ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8) -RETURNING *; --- name: CreateBetOutcome :copyfrom -INSERT INTO bet_outcomes ( - bet_id, - sport_id, - event_id, - odd_id, - home_team_name, - away_team_name, - market_id, - market_name, - odd, - odd_name, - odd_header, - odd_handicap, - expires - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13 - ); --- name: GetAllBets :many -SELECT * -FROM bet_with_outcomes -wHERE ( - user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ) - AND ( - is_shop_bet = sqlc.narg('is_shop_bet') - OR sqlc.narg('is_shop_bet') IS NULL - ) - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - status = sqlc.narg('status') - OR sqlc.narg('status') IS NULL - ) - AND ( - cashed_out = sqlc.narg('cashed_out') - OR sqlc.narg('cashed_out') IS NULL - ) - AND ( - full_name ILIKE '%' || sqlc.narg('query') || '%' - OR phone_number ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ) -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetTotalBets :one -SELECT COUNT(*) -FROM bets -wHERE ( - user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ) - AND ( - is_shop_bet = sqlc.narg('is_shop_bet') - OR sqlc.narg('is_shop_bet') IS NULL - ) - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - status = sqlc.narg('status') - OR sqlc.narg('status') IS NULL - ) - AND ( - cashed_out = sqlc.narg('cashed_out') - OR sqlc.narg('cashed_out') IS NULL - ) - AND ( - full_name ILIKE '%' || sqlc.narg('query') || '%' - OR phone_number ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetBetByID :one -SELECT * -FROM bet_with_outcomes -WHERE id = $1; --- name: GetBetByUserID :many -SELECT * -FROM bet_with_outcomes -WHERE user_id = $1; --- name: GetBetByFastCode :one -SELECT * -FROM bet_with_outcomes -WHERE fast_code = $1 -LIMIT 1; --- name: GetBetsForCashback :many -SELECT * -FROM bet_with_outcomes -WHERE status = 2 - AND processed = false; --- name: GetBetOutcomeViewByEventID :many -SELECT bet_outcomes.*, - users.first_name, - users.last_name, - bets.amount, - bets.total_odds, - companies.name as company_name -FROM bet_outcomes - JOIN bets ON bets.id = bet_outcomes.bet_id - JOIN users ON bets.user_id = users.id - JOIN companies ON bets.company_id = companies.id -WHERE bet_outcomes.event_id = $1 - AND ( - bets.company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - bet_outcomes.status = sqlc.narg('filter_status') - OR sqlc.narg('filter_status') IS NULL - ) -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: TotalBetOutcomeViewByEventID :one -SELECT count(*) -FROM bet_outcomes - JOIN bets ON bets.id = bet_outcomes.bet_id -WHERE bet_outcomes.event_id = $1 - AND ( - bets.company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - bet_outcomes.status = sqlc.narg('filter_status') - OR sqlc.narg('filter_status') IS NULL - ); --- name: GetBetOutcomeByEventID :many -SELECT * -FROM bet_outcomes -WHERE (event_id = $1) - AND ( - status = sqlc.narg('filter_status') - OR sqlc.narg('filter_status') IS NULL - OR status = sqlc.narg('filter_status_2') - OR sqlc.narg('filter_status_2') IS NULL - ); --- name: GetBetOutcomeByBetID :many -SELECT * -FROM bet_outcomes -WHERE bet_id = $1; --- name: GetBetOutcomeCountByOddID :one -SELECT COUNT(*) -FROM bet_outcomes -WHERE odd_id = $1; --- name: GetBetCountByUserID :one -SELECT COUNT(*) -FROM bets -WHERE user_id = $1 - AND outcomes_hash = $2; --- name: GetBetCountByOutcomesHash :one -SELECT COUNT(*) -FROM bets -WHERE outcomes_hash = $1; --- name: UpdateCashOut :exec -UPDATE bets -SET cashed_out = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1; --- name: UpdateBetOutcomeStatus :one -UPDATE bet_outcomes -SET status = $1 -WHERE id = $2 -RETURNING *; --- name: UpdateBetOutcomeStatusByBetID :one -UPDATE bet_outcomes -SET status = $1 -WHERE bet_id = $2 -RETURNING *; --- name: UpdateBetOutcomeStatusForEvent :many -UPDATE bet_outcomes -SEt status = $1 -WHERE event_id = $2 -RETURNING *; --- name: UpdateBetOutcomeStatusForOddID :many -UPDATE bet_outcomes -SEt status = $1 -WHERE odd_id = $2 -RETURNING *; --- name: BulkUpdateBetOutcomeStatusByOddIDs :exec -UPDATE bet_outcomes -SET status = $1 -WHERE odd_id = ANY(sqlc.arg('odd_ids')::BIGINT []); --- name: UpdateStatus :exec -UPDATE bets -SET status = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2; --- name: UpdateBetWithCashback :exec -UPDATE bets -SET processed = $1 -WHERE id = $2; --- name: DeleteBet :exec -DELETE FROM bets -WHERE id = $1; --- name: DeleteBetOutcome :exec -DELETE FROM bet_outcomes -WHERE bet_id = $1; \ No newline at end of file diff --git a/db/query/bet_stat.sql b/db/query/bet_stat.sql deleted file mode 100644 index ef9a378..0000000 --- a/db/query/bet_stat.sql +++ /dev/null @@ -1,191 +0,0 @@ --- name: GetBetStatsByInterval :many -SELECT DATE_TRUNC(sqlc.narg('interval'), created_at)::timestamp AS date, - COUNT(*) as total_bets, - SUM(amount) AS total_stake, - SUM( - CASE - WHEN status = 0 THEN 1 - ELSE 0 - END - ) as active_bets, - SUM( - CASE - WHEN status = 1 THEN 1 - ELSE 0 - END - ) as total_wins, - SUM( - CASE - WHEN status = 2 THEN 1 - ELSE 0 - END - ) as total_losses, - SUM( - CASE - WHEN status = 1 THEN amount * total_odds - ELSE 0 - END - ) as win_balance, - COUNT(*) FILTER ( - WHERE status = 5 - ) AS number_of_unsettled, - SUM( - CASE - WHEN status = 5 THEN amount - ELSE 0 - END - ) AS total_unsettled_amount, - COUNT(*) FILTER ( - WHERE is_shop_bet = TRUE - ) AS total_shop_bets -FROM bets -WHERE ( - bets.company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ); --- name: GetBetSummary :one -SELECT SUM(amount) as total_stakes, - COUNT(*) as total_bets, - SUM( - CASE - WHEN status = 0 THEN 1 - ELSE 0 - END - ) as active_bets, - SUM( - CASE - WHEN status = 1 THEN 1 - ELSE 0 - END - ) as total_wins, - SUM( - CASE - WHEN status = 2 THEN 1 - ELSE 0 - END - ) as total_losses, - SUM( - CASE - WHEN status = 1 THEN amount * total_odds - ELSE 0 - END - ) as win_balance -FROM bets -wHERE ( - user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ) - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetBetStats :many -SELECT DATE(created_at) as date, - COUNT(*) as total_bets, - SUM(amount) as total_stakes, - SUM( - CASE - WHEN status = 1 THEN 1 - ELSE 0 - END - ) as total_wins, - SUM( - CASE - WHEN status = 1 THEN amount * total_odds - ELSE 0 - END - ) as total_payouts, - AVG(total_odds) as average_odds -FROM bets -wHERE ( - user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ) - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - is_shop_bet = sqlc.narg('is_shop_bet') - OR sqlc.narg('is_shop_bet') IS NULL - ) - AND ( - cashed_out = sqlc.narg('cashed_out') - OR sqlc.narg('cashed_out') IS NULL - ) - AND ( - full_name ILIKE '%' || sqlc.narg('query') || '%' - OR phone_number ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ) -GROUP BY DATE(created_at) -ORDER BY DATE(created_at); --- name: GetTotalBetsMadeInRange :one -SELECT COUNT(*) AS total_bets -FROM bets -WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to'); --- name: GetTotalCashMadeInRange :one -SELECT COALESCE(SUM(amount), 0) AS total_cash_made -FROM bets -WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to'); --- name: GetTotalCashOutInRange :one -SELECT COALESCE(SUM(amount), 0) AS total_cash_out -FROM bets -WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to') - AND cashed_out = true; --- name: GetTotalCashBacksInRange :one -SELECT COALESCE(SUM(amount), 0) AS total_cash_backs -FROM bets -WHERE created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to') - AND status = 5; --- name: GetMarketPopularity :one -WITH market_counts AS ( - SELECT DATE(b.created_at) as date, - bo.market_name, - COUNT(*) as bet_count, - ROW_NUMBER() OVER ( - PARTITION BY DATE(b.created_at) - ORDER BY COUNT(*) DESC - ) as rank - FROM bets b - JOIN bet_outcomes bo ON b.id = bo.bet_id - WHERE bo.market_name IS NOT NULL - AND ( - user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ) - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ) - GROUP BY DATE(b.created_at), - bo.market_name -) -SELECT date, - market_name -FROM market_counts -WHERE rank = 1; \ No newline at end of file diff --git a/db/query/bonus.sql b/db/query/bonus.sql deleted file mode 100644 index 216528a..0000000 --- a/db/query/bonus.sql +++ /dev/null @@ -1,61 +0,0 @@ --- name: CreateUserBonus :one -INSERT INTO user_bonuses ( - name, - description, - type, - user_id, - reward_amount, - expires_at - ) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING *; --- name: GetAllUserBonuses :many -SELECT * -FROM user_bonuses -WHERE ( - user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ) -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetUserBonusByID :one -SELECT * -FROM user_bonuses -WHERE id = $1; - --- name: GetBonusCount :one -SELECT COUNT(*) -FROM user_bonuses -WHERE ( - user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ); --- name: GetBonusStats :one -SELECT COUNT(*) AS total_bonuses, - COALESCE(SUM(reward_amount), 0)::bigint AS total_reward_earned, - COUNT( - CASE - WHEN is_claimed = true THEN 1 - END - ) AS claimed_bonuses, - COUNT( - CASE - WHEN expires_at < now() THEN 1 - END - ) AS expired_bonuses -FROM user_bonuses - JOIN users ON users.id = user_bonuses.user_id -WHERE ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ); --- name: UpdateUserBonus :exec -UPDATE user_bonuses -SET is_claimed = $2 -WHERE id = $1; --- name: DeleteUserBonus :exec -DELETE FROM user_bonuses -WHERE id = $1; \ No newline at end of file diff --git a/db/query/branch.sql b/db/query/branch.sql index 14fd768..3a9d0ec 100644 --- a/db/query/branch.sql +++ b/db/query/branch.sql @@ -1,109 +1,109 @@ --- name: CreateBranch :one -INSERT INTO branches ( - name, - location, - wallet_id, - branch_manager_id, - company_id, - is_self_owned, - profit_percent - ) -VALUES ($1, $2, $3, $4, $5, $6, $7) -RETURNING *; --- name: CreateSupportedOperation :one -INSERT INTO supported_operations (name, description) -VALUES ($1, $2) -RETURNING *; --- name: CreateBranchOperation :one -INSERT INTO branch_operations (operation_id, branch_id) -VALUES ($1, $2) -RETURNING *; --- name: CreateBranchCashier :one -INSERT INTO branch_cashiers (user_id, branch_id) -VALUES ($1, $2) -RETURNING *; --- name: GetAllBranches :many -SELECT * -FROM branch_details -WHERE ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - is_active = sqlc.narg('is_active') - OR sqlc.narg('is_active') IS NULL - ) - AND ( - branch_manager_id = sqlc.narg('branch_manager_id') - OR sqlc.narg('branch_manager_id') IS NULL - ) - AND ( - name ILIKE '%' || sqlc.narg('query') || '%' - OR location ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetBranchByID :one -SELECT * -FROM branch_details -WHERE id = $1; --- name: GetBranchByCompanyID :many -SELECT * -FROM branch_details -WHERE company_id = $1; --- name: GetBranchByManagerID :many -SELECT * -FROM branch_details -WHERE branch_manager_id = $1; --- name: SearchBranchByName :many -SELECT * -FROM branch_details -WHERE name ILIKE '%' || $1 || '%' - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ); --- name: GetAllSupportedOperations :many -SELECT * -FROM supported_operations; --- name: GetBranchOperations :many -SELECT branch_operations.*, - supported_operations.name, - supported_operations.description -FROM branch_operations - JOIN supported_operations ON branch_operations.operation_id = supported_operations.id -WHERE branch_operations.branch_id = $1; --- name: GetBranchByCashier :one -SELECT branches.* -FROM branch_cashiers - JOIN branches ON branch_cashiers.branch_id = branches.id -WHERE branch_cashiers.user_id = $1; --- name: UpdateBranch :one -UPDATE branches -SET name = COALESCE(sqlc.narg(name), name), - location = COALESCE(sqlc.narg(location), location), - branch_manager_id = COALESCE(sqlc.narg(branch_manager_id), branch_manager_id), - company_id = COALESCE(sqlc.narg(company_id), company_id), - is_self_owned = COALESCE(sqlc.narg(is_self_owned), is_self_owned), - is_active = COALESCE(sqlc.narg(is_active), is_active), - profit_percent = COALESCE(sqlc.narg(profit_percent), profit_percent), - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -RETURNING *; --- name: DeleteBranch :exec -DELETE FROM branches -WHERE id = $1; --- name: DeleteBranchOperation :exec -DELETE FROM branch_operations -WHERE operation_id = $1 - AND branch_id = $2; --- name: DeleteBranchCashier :exec -DELETE FROM branch_cashiers -WHERE user_id = $1; \ No newline at end of file +-- -- name: CreateBranch :one +-- INSERT INTO branches ( +-- name, +-- location, +-- wallet_id, +-- branch_manager_id, +-- company_id, +-- is_self_owned, +-- profit_percent +-- ) +-- VALUES ($1, $2, $3, $4, $5, $6, $7) +-- RETURNING *; +-- -- name: CreateSupportedOperation :one +-- INSERT INTO supported_operations (name, description) +-- VALUES ($1, $2) +-- RETURNING *; +-- -- name: CreateBranchOperation :one +-- INSERT INTO branch_operations (operation_id, branch_id) +-- VALUES ($1, $2) +-- RETURNING *; +-- -- name: CreateBranchCashier :one +-- INSERT INTO branch_cashiers (user_id, branch_id) +-- VALUES ($1, $2) +-- RETURNING *; +-- -- name: GetAllBranches :many +-- SELECT * +-- FROM branch_details +-- WHERE ( +-- company_id = sqlc.narg('company_id') +-- OR sqlc.narg('company_id') IS NULL +-- ) +-- AND ( +-- is_active = sqlc.narg('is_active') +-- OR sqlc.narg('is_active') IS NULL +-- ) +-- AND ( +-- branch_manager_id = sqlc.narg('branch_manager_id') +-- OR sqlc.narg('branch_manager_id') IS NULL +-- ) +-- AND ( +-- name ILIKE '%' || sqlc.narg('query') || '%' +-- OR location ILIKE '%' || sqlc.narg('query') || '%' +-- OR sqlc.narg('query') IS NULL +-- ) +-- AND ( +-- created_at > sqlc.narg('created_before') +-- OR sqlc.narg('created_before') IS NULL +-- ) +-- AND ( +-- created_at < sqlc.narg('created_after') +-- OR sqlc.narg('created_after') IS NULL +-- ); +-- -- name: GetBranchByID :one +-- SELECT * +-- FROM branch_details +-- WHERE id = $1; +-- -- name: GetBranchByCompanyID :many +-- SELECT * +-- FROM branch_details +-- WHERE company_id = $1; +-- -- name: GetBranchByManagerID :many +-- SELECT * +-- FROM branch_details +-- WHERE branch_manager_id = $1; +-- -- name: SearchBranchByName :many +-- SELECT * +-- FROM branch_details +-- WHERE name ILIKE '%' || $1 || '%' +-- AND ( +-- company_id = sqlc.narg('company_id') +-- OR sqlc.narg('company_id') IS NULL +-- ); +-- -- name: GetAllSupportedOperations :many +-- SELECT * +-- FROM supported_operations; +-- -- name: GetBranchOperations :many +-- SELECT branch_operations.*, +-- supported_operations.name, +-- supported_operations.description +-- FROM branch_operations +-- JOIN supported_operations ON branch_operations.operation_id = supported_operations.id +-- WHERE branch_operations.branch_id = $1; +-- -- name: GetBranchByCashier :one +-- SELECT branches.* +-- FROM branch_cashiers +-- JOIN branches ON branch_cashiers.branch_id = branches.id +-- WHERE branch_cashiers.user_id = $1; +-- -- name: UpdateBranch :one +-- UPDATE branches +-- SET name = COALESCE(sqlc.narg(name), name), +-- location = COALESCE(sqlc.narg(location), location), +-- branch_manager_id = COALESCE(sqlc.narg(branch_manager_id), branch_manager_id), +-- company_id = COALESCE(sqlc.narg(company_id), company_id), +-- is_self_owned = COALESCE(sqlc.narg(is_self_owned), is_self_owned), +-- is_active = COALESCE(sqlc.narg(is_active), is_active), +-- profit_percent = COALESCE(sqlc.narg(profit_percent), profit_percent), +-- updated_at = CURRENT_TIMESTAMP +-- WHERE id = $1 +-- RETURNING *; +-- -- name: DeleteBranch :exec +-- DELETE FROM branches +-- WHERE id = $1; +-- -- name: DeleteBranchOperation :exec +-- DELETE FROM branch_operations +-- WHERE operation_id = $1 +-- AND branch_id = $2; +-- -- name: DeleteBranchCashier :exec +-- DELETE FROM branch_cashiers +-- WHERE user_id = $1; \ No newline at end of file diff --git a/db/query/branch_stats.sql b/db/query/branch_stats.sql index 23c42d7..044dcc0 100644 --- a/db/query/branch_stats.sql +++ b/db/query/branch_stats.sql @@ -1,128 +1,128 @@ --- name: UpdateBranchStats :exec -WITH -- Aggregate bet data per branch -bet_stats AS ( - SELECT branch_id, - COUNT(*) AS total_bets, - COALESCE(SUM(amount), 0) AS total_stake, - COALESCE( - SUM(amount) * MAX(profit_percent), - 0 - ) AS deducted_stake, - COALESCE( - SUM( - CASE - WHEN cashed_out THEN amount - ELSE 0 - END - ), - 0 - ) AS total_cash_out, - COALESCE( - SUM( - CASE - WHEN status = 3 THEN amount - ELSE 0 - END - ), - 0 - ) AS total_cash_backs, - COUNT(*) FILTER ( - WHERE status = 5 - ) AS number_of_unsettled, - COALESCE( - SUM( - CASE - WHEN status = 5 THEN amount - ELSE 0 - END - ), - 0 - ) AS total_unsettled_amount - FROM shop_bet_detail - LEFT JOIN branches ON branches.id = shop_bet_detail.branch_id - GROUP BY branch_id -), -cashier_stats AS ( - SELECT branch_id, - COUNT(*) AS total_cashiers - FROM branch_cashiers - GROUP BY branch_id -) -INSERT INTO branch_stats ( - branch_id, - branch_name, - company_id, - company_name, - company_slug, - interval_start, - total_bets, - total_stake, - deducted_stake, - total_cash_out, - total_cash_backs, - number_of_unsettled, - total_unsettled_amount, - total_cashiers, - updated_at - ) -SELECT br.id AS branch_id, - br.name AS branch_name, - c.id AS company_id, - c.name AS company_name, - c.slug AS company_slug, - DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start, - COALESCE(bs.total_bets, 0) AS total_bets, - COALESCE(bs.total_stake, 0) AS total_stake, - COALESCE(bs.deducted_stake, 0) AS deducted_stake, - COALESCE(bs.total_cash_out, 0) AS total_cash_out, - COALESCE(bs.total_cash_backs, 0) AS total_cash_backs, - COALESCE(bs.number_of_unsettled, 0) AS number_of_unsettled, - COALESCE(bs.total_unsettled_amount, 0) AS total_unsettled_amount, - COALESCE(bc.total_cashiers, 0) AS total_cashiers, - NOW() AS updated_at -FROM branches br - LEFT JOIN companies c ON c.id = br.company_id - LEFT JOIN bet_stats bs ON bs.branch_id = br.id - LEFT JOIN cashier_stats bc ON bc.branch_id = br.id ON CONFLICT (branch_id, interval_start) DO -UPDATE -SET total_bets = EXCLUDED.total_bets, - total_stake = EXCLUDED.total_stake, - deducted_stake = EXCLUDED.deducted_stake, - total_cash_out = EXCLUDED.total_cash_out, - total_cash_backs = EXCLUDED.total_cash_backs, - number_of_unsettled = EXCLUDED.number_of_unsettled, - total_unsettled_amount = EXCLUDED.total_unsettled_amount, - total_cashiers = EXCLUDED.total_cashiers, - updated_at = EXCLUDED.updated_at; --- name: GetBranchStatsByID :many -SELECt * -FROM branch_stats -WHERE branch_id = $1 -ORDER BY interval_start DESC; --- name: GetBranchStats :many -SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start, - branch_stats.branch_id, - branch_stats.branch_name, - branch_stats.company_id, - branch_stats.company_name, - branch_stats.company_slug, - branch_stats.total_bets, - branch_stats.total_stake, - branch_stats.deducted_stake, - branch_stats.total_cash_out, - branch_stats.total_cash_backs, - branch_stats.number_of_unsettled, - branch_stats.total_unsettled_amount, - branch_stats.total_cashiers, - branch_stats.updated_at -FROM branch_stats -WHERE ( - branch_stats.branch_id = sqlc.narg('branch_id') - OR sqlc.narg('branch_id') IS NULL - ) - AND ( - branch_stats.company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) -GROUP BY interval_start -ORDER BY interval_start DESC; \ No newline at end of file +-- -- name: UpdateBranchStats :exec +-- WITH -- Aggregate bet data per branch +-- bet_stats AS ( +-- SELECT branch_id, +-- COUNT(*) AS total_bets, +-- COALESCE(SUM(amount), 0) AS total_stake, +-- COALESCE( +-- SUM(amount) * MAX(profit_percent), +-- 0 +-- ) AS deducted_stake, +-- COALESCE( +-- SUM( +-- CASE +-- WHEN cashed_out THEN amount +-- ELSE 0 +-- END +-- ), +-- 0 +-- ) AS total_cash_out, +-- COALESCE( +-- SUM( +-- CASE +-- WHEN status = 3 THEN amount +-- ELSE 0 +-- END +-- ), +-- 0 +-- ) AS total_cash_backs, +-- COUNT(*) FILTER ( +-- WHERE status = 5 +-- ) AS number_of_unsettled, +-- COALESCE( +-- SUM( +-- CASE +-- WHEN status = 5 THEN amount +-- ELSE 0 +-- END +-- ), +-- 0 +-- ) AS total_unsettled_amount +-- FROM shop_bet_detail +-- LEFT JOIN branches ON branches.id = shop_bet_detail.branch_id +-- GROUP BY branch_id +-- ), +-- cashier_stats AS ( +-- SELECT branch_id, +-- COUNT(*) AS total_cashiers +-- FROM branch_cashiers +-- GROUP BY branch_id +-- ) +-- INSERT INTO branch_stats ( +-- branch_id, +-- branch_name, +-- company_id, +-- company_name, +-- company_slug, +-- interval_start, +-- total_bets, +-- total_stake, +-- deducted_stake, +-- total_cash_out, +-- total_cash_backs, +-- number_of_unsettled, +-- total_unsettled_amount, +-- total_cashiers, +-- updated_at +-- ) +-- SELECT br.id AS branch_id, +-- br.name AS branch_name, +-- c.id AS company_id, +-- c.name AS company_name, +-- c.slug AS company_slug, +-- DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start, +-- COALESCE(bs.total_bets, 0) AS total_bets, +-- COALESCE(bs.total_stake, 0) AS total_stake, +-- COALESCE(bs.deducted_stake, 0) AS deducted_stake, +-- COALESCE(bs.total_cash_out, 0) AS total_cash_out, +-- COALESCE(bs.total_cash_backs, 0) AS total_cash_backs, +-- COALESCE(bs.number_of_unsettled, 0) AS number_of_unsettled, +-- COALESCE(bs.total_unsettled_amount, 0) AS total_unsettled_amount, +-- COALESCE(bc.total_cashiers, 0) AS total_cashiers, +-- NOW() AS updated_at +-- FROM branches br +-- LEFT JOIN companies c ON c.id = br.company_id +-- LEFT JOIN bet_stats bs ON bs.branch_id = br.id +-- LEFT JOIN cashier_stats bc ON bc.branch_id = br.id ON CONFLICT (branch_id, interval_start) DO +-- UPDATE +-- SET total_bets = EXCLUDED.total_bets, +-- total_stake = EXCLUDED.total_stake, +-- deducted_stake = EXCLUDED.deducted_stake, +-- total_cash_out = EXCLUDED.total_cash_out, +-- total_cash_backs = EXCLUDED.total_cash_backs, +-- number_of_unsettled = EXCLUDED.number_of_unsettled, +-- total_unsettled_amount = EXCLUDED.total_unsettled_amount, +-- total_cashiers = EXCLUDED.total_cashiers, +-- updated_at = EXCLUDED.updated_at; +-- -- name: GetBranchStatsByID :many +-- SELECt * +-- FROM branch_stats +-- WHERE branch_id = $1 +-- ORDER BY interval_start DESC; +-- -- name: GetBranchStats :many +-- SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start, +-- branch_stats.branch_id, +-- branch_stats.branch_name, +-- branch_stats.company_id, +-- branch_stats.company_name, +-- branch_stats.company_slug, +-- branch_stats.total_bets, +-- branch_stats.total_stake, +-- branch_stats.deducted_stake, +-- branch_stats.total_cash_out, +-- branch_stats.total_cash_backs, +-- branch_stats.number_of_unsettled, +-- branch_stats.total_unsettled_amount, +-- branch_stats.total_cashiers, +-- branch_stats.updated_at +-- FROM branch_stats +-- WHERE ( +-- branch_stats.branch_id = sqlc.narg('branch_id') +-- OR sqlc.narg('branch_id') IS NULL +-- ) +-- AND ( +-- branch_stats.company_id = sqlc.narg('company_id') +-- OR sqlc.narg('company_id') IS NULL +-- ) +-- GROUP BY interval_start +-- ORDER BY interval_start DESC; \ No newline at end of file diff --git a/db/query/cashier.sql b/db/query/cashier.sql deleted file mode 100644 index 0b0c294..0000000 --- a/db/query/cashier.sql +++ /dev/null @@ -1,39 +0,0 @@ --- name: GetCashiersByBranch :many -SELECT users.* -FROM branch_cashiers - JOIN users ON branch_cashiers.user_id = users.id - JOIN branches ON branches.id = branch_id -WHERE branch_cashiers.branch_id = $1; --- name: GetAllCashiers :many -SELECT users.*, - branch_id, - branches.name AS branch_name, - branches.wallet_id AS branch_wallet, - branches.location As branch_location -FROM branch_cashiers - JOIN users ON branch_cashiers.user_id = users.id - JOIN branches ON branches.id = branch_id -WHERE ( - first_name ILIKE '%' || sqlc.narg('query') || '%' - OR last_name ILIKE '%' || sqlc.narg('query') || '%' - OR phone_number ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - users.created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - users.created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetCashierByID :one -SELECT users.*, - branch_id, - branches.name AS branch_name, - branches.wallet_id AS branch_wallet, - branches.location As branch_location -FROM branch_cashiers - JOIN users ON branch_cashiers.user_id = users.id - JOIN branches ON branches.id = branch_id -WHERE users.id = $1; \ No newline at end of file diff --git a/db/query/company.sql b/db/query/company.sql index 4f5952c..bfead0f 100644 --- a/db/query/company.sql +++ b/db/query/company.sql @@ -1,56 +1,56 @@ --- name: CreateCompany :one -INSERT INTO companies ( - name, - slug, - admin_id, - wallet_id, - deducted_percentage, - is_active - ) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING *; --- name: GetAllCompanies :many -SELECT * -FROM companies_details -WHERE ( - name ILIKE '%' || sqlc.narg('query') || '%' - OR admin_first_name ILIKE '%' || sqlc.narg('query') || '%' - OR admin_last_name ILIKE '%' || sqlc.narg('query') || '%' - OR admin_phone_number ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetCompanyByID :one -SELECT * -FROM companies_details -WHERE id = $1; --- name: GetCompanyUsingSlug :one -SELECT * -FROM companies -WHERE slug = $1; --- name: SearchCompanyByName :many -SELECT * -FROM companies_details -WHERE name ILIKE '%' || $1 || '%'; --- name: UpdateCompany :exec -UPDATE companies -SET name = COALESCE(sqlc.narg(name), name), - admin_id = COALESCE(sqlc.narg(admin_id), admin_id), - is_active = COALESCE(sqlc.narg(is_active), is_active), - deducted_percentage = COALESCE( - sqlc.narg(deducted_percentage), - deducted_percentage - ), - slug = COALESCE(sqlc.narg(slug), slug), - updated_at = CURRENT_TIMESTAMP -WHERE id = $1; --- name: DeleteCompany :exec -DELETE FROM companies -WHERE id = $1; \ No newline at end of file +-- -- name: CreateCompany :one +-- INSERT INTO companies ( +-- name, +-- slug, +-- admin_id, +-- wallet_id, +-- deducted_percentage, +-- is_active +-- ) +-- VALUES ($1, $2, $3, $4, $5, $6) +-- RETURNING *; +-- -- name: GetAllCompanies :many +-- SELECT * +-- FROM companies_details +-- WHERE ( +-- name ILIKE '%' || sqlc.narg('query') || '%' +-- OR admin_first_name ILIKE '%' || sqlc.narg('query') || '%' +-- OR admin_last_name ILIKE '%' || sqlc.narg('query') || '%' +-- OR admin_phone_number ILIKE '%' || sqlc.narg('query') || '%' +-- OR sqlc.narg('query') IS NULL +-- ) +-- AND ( +-- created_at > sqlc.narg('created_before') +-- OR sqlc.narg('created_before') IS NULL +-- ) +-- AND ( +-- created_at < sqlc.narg('created_after') +-- OR sqlc.narg('created_after') IS NULL +-- ); +-- -- name: GetCompanyByID :one +-- SELECT * +-- FROM companies_details +-- WHERE id = $1; +-- -- name: GetCompanyUsingSlug :one +-- SELECT * +-- FROM companies +-- WHERE slug = $1; +-- -- name: SearchCompanyByName :many +-- SELECT * +-- FROM companies_details +-- WHERE name ILIKE '%' || $1 || '%'; +-- -- name: UpdateCompany :exec +-- UPDATE companies +-- SET name = COALESCE(sqlc.narg(name), name), +-- admin_id = COALESCE(sqlc.narg(admin_id), admin_id), +-- is_active = COALESCE(sqlc.narg(is_active), is_active), +-- deducted_percentage = COALESCE( +-- sqlc.narg(deducted_percentage), +-- deducted_percentage +-- ), +-- slug = COALESCE(sqlc.narg(slug), slug), +-- updated_at = CURRENT_TIMESTAMP +-- WHERE id = $1; +-- -- name: DeleteCompany :exec +-- DELETE FROM companies +-- WHERE id = $1; \ No newline at end of file diff --git a/db/query/company_stats.sql b/db/query/company_stats.sql index df202ab..56b4fd9 100644 --- a/db/query/company_stats.sql +++ b/db/query/company_stats.sql @@ -1,160 +1,160 @@ --- name: UpdateCompanyStats :exec -WITH -- Aggregate bet data per company -bet_stats AS ( - SELECT company_id, - COUNT(*) AS total_bets, - COALESCE(SUM(amount), 0) AS total_stake, - COALESCE( - SUM(amount) * MAX(companies.deducted_percentage), - 0 - ) AS deducted_stake, - COALESCE( - SUM( - CASE - WHEN cashed_out THEN amount - ELSE 0 - END - ), - 0 - ) AS total_cash_out, - COALESCE( - SUM( - CASE - WHEN status = 3 THEN amount - ELSE 0 - END - ), - 0 - ) AS total_cash_backs, - COUNT(*) FILTER ( - WHERE status = 5 - ) AS number_of_unsettled, - COALESCE( - SUM( - CASE - WHEN status = 5 THEN amount - ELSE 0 - END - ), - 0 - ) AS total_unsettled_amount - FROM shop_bet_detail - LEFT JOIN companies ON companies.id = shop_bet_detail.company_id - GROUP BY company_id -), --- Aggregate user counts per company -user_stats AS ( - SELECT company_id, - COUNT(*) FILTER ( - WHERE role = 'admin' - ) AS total_admins, - COUNT(*) FILTER ( - WHERE role = 'branch_manager' - ) AS total_managers, - COUNT(*) FILTER ( - WHERE role = 'cashier' - ) AS total_cashiers, - COUNT(*) FILTER ( - WHERE role = 'customer' - ) AS total_customers, - COUNT(*) FILTER ( - WHERE role = 'transaction_approver' - ) AS total_approvers - FROM users - GROUP BY company_id -), --- Aggregate branch counts per company -branch_stats AS ( - SELECT company_id, - COUNT(*) AS total_branches - FROM branches - GROUP BY company_id -) -- Final combined aggregation -INSERT INTO company_stats ( - company_id, - company_name, - company_slug, - interval_start, - total_bets, - total_stake, - deducted_stake, - total_cash_out, - total_cash_backs, - number_of_unsettled, - total_unsettled_amount, - total_admins, - total_managers, - total_cashiers, - total_customers, - total_approvers, - total_branches, - updated_at - ) -SELECT c.id AS company_id, - c.name AS company_name, - c.slug AS company_slug, - DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start, - COALESCE(b.total_bets, 0) AS total_bets, - COALESCE(b.total_stake, 0) AS total_stake, - COALESCE(b.deducted_stake, 0) AS deducted_stake, - COALESCE(b.total_cash_out, 0) AS total_cash_out, - COALESCE(b.total_cash_backs, 0) AS total_cash_backs, - COALESCE(b.number_of_unsettled, 0) AS number_of_unsettled, - COALESCE(b.total_unsettled_amount, 0) AS total_unsettled_amount, - COALESCE(u.total_admins, 0) AS total_admins, - COALESCE(u.total_managers, 0) AS total_managers, - COALESCE(u.total_cashiers, 0) AS total_cashiers, - COALESCE(u.total_customers, 0) AS total_customers, - COALESCE(u.total_approvers, 0) AS total_approvers, - COALESCE(br.total_branches, 0) AS total_branches, - NOW() AS updated_at -FROM companies c - LEFT JOIN bet_stats b ON b.company_id = c.id - LEFT JOIN user_stats u ON u.company_id = c.id - LEFT JOIN branch_stats br ON br.company_id = c.id ON CONFLICT (company_id, interval_start) DO -UPDATE -SET total_bets = EXCLUDED.total_bets, - total_stake = EXCLUDED.total_stake, - deducted_stake = EXCLUDED.deducted_stake, - total_cash_out = EXCLUDED.total_cash_out, - total_cash_backs = EXCLUDED.total_cash_backs, - number_of_unsettled = EXCLUDED.number_of_unsettled, - total_unsettled_amount = EXCLUDED.total_unsettled_amount, - total_admins = EXCLUDED.total_admins, - total_managers = EXCLUDED.total_managers, - total_cashiers = EXCLUDED.total_cashiers, - total_customers = EXCLUDED.total_customers, - total_approvers = EXCLUDED.total_approvers, - total_branches = EXCLUDED.total_branches, - updated_at = EXCLUDED.updated_at; --- name: GetCompanyStatsByID :many -SELECT * -FROM company_stats -WHERE company_id = $1 -ORDER BY interval_start DESC; --- name: GetCompanyStats :many -SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start, - company_stats.company_id, - company_stats.company_name, - company_stats.company_slug, - company_stats.total_bets, - company_stats.total_stake, - company_stats.deducted_stake, - company_stats.total_cash_out, - company_stats.total_cash_backs, - company_stats.number_of_unsettled, - company_stats.total_unsettled_amount, - company_stats.total_admins, - company_stats.total_managers, - company_stats.total_cashiers, - company_stats.total_customers, - company_stats.total_approvers, - company_stats.total_branches, - company_stats.updated_at -FROM company_stats -WHERE ( - company_stats.company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) -GROUP BY interval_start -ORDER BY interval_start DESC; \ No newline at end of file +-- -- name: UpdateCompanyStats :exec +-- WITH -- Aggregate bet data per company +-- bet_stats AS ( +-- SELECT company_id, +-- COUNT(*) AS total_bets, +-- COALESCE(SUM(amount), 0) AS total_stake, +-- COALESCE( +-- SUM(amount) * MAX(companies.deducted_percentage), +-- 0 +-- ) AS deducted_stake, +-- COALESCE( +-- SUM( +-- CASE +-- WHEN cashed_out THEN amount +-- ELSE 0 +-- END +-- ), +-- 0 +-- ) AS total_cash_out, +-- COALESCE( +-- SUM( +-- CASE +-- WHEN status = 3 THEN amount +-- ELSE 0 +-- END +-- ), +-- 0 +-- ) AS total_cash_backs, +-- COUNT(*) FILTER ( +-- WHERE status = 5 +-- ) AS number_of_unsettled, +-- COALESCE( +-- SUM( +-- CASE +-- WHEN status = 5 THEN amount +-- ELSE 0 +-- END +-- ), +-- 0 +-- ) AS total_unsettled_amount +-- FROM shop_bet_detail +-- LEFT JOIN companies ON companies.id = shop_bet_detail.company_id +-- GROUP BY company_id +-- ), +-- -- Aggregate user counts per company +-- user_stats AS ( +-- SELECT company_id, +-- COUNT(*) FILTER ( +-- WHERE role = 'admin' +-- ) AS total_admins, +-- COUNT(*) FILTER ( +-- WHERE role = 'branch_manager' +-- ) AS total_managers, +-- COUNT(*) FILTER ( +-- WHERE role = 'cashier' +-- ) AS total_cashiers, +-- COUNT(*) FILTER ( +-- WHERE role = 'customer' +-- ) AS total_customers, +-- COUNT(*) FILTER ( +-- WHERE role = 'transaction_approver' +-- ) AS total_approvers +-- FROM users +-- GROUP BY company_id +-- ), +-- -- Aggregate branch counts per company +-- branch_stats AS ( +-- SELECT company_id, +-- COUNT(*) AS total_branches +-- FROM branches +-- GROUP BY company_id +-- ) -- Final combined aggregation +-- INSERT INTO company_stats ( +-- company_id, +-- company_name, +-- company_slug, +-- interval_start, +-- total_bets, +-- total_stake, +-- deducted_stake, +-- total_cash_out, +-- total_cash_backs, +-- number_of_unsettled, +-- total_unsettled_amount, +-- total_admins, +-- total_managers, +-- total_cashiers, +-- total_customers, +-- total_approvers, +-- total_branches, +-- updated_at +-- ) +-- SELECT c.id AS company_id, +-- c.name AS company_name, +-- c.slug AS company_slug, +-- DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start, +-- COALESCE(b.total_bets, 0) AS total_bets, +-- COALESCE(b.total_stake, 0) AS total_stake, +-- COALESCE(b.deducted_stake, 0) AS deducted_stake, +-- COALESCE(b.total_cash_out, 0) AS total_cash_out, +-- COALESCE(b.total_cash_backs, 0) AS total_cash_backs, +-- COALESCE(b.number_of_unsettled, 0) AS number_of_unsettled, +-- COALESCE(b.total_unsettled_amount, 0) AS total_unsettled_amount, +-- COALESCE(u.total_admins, 0) AS total_admins, +-- COALESCE(u.total_managers, 0) AS total_managers, +-- COALESCE(u.total_cashiers, 0) AS total_cashiers, +-- COALESCE(u.total_customers, 0) AS total_customers, +-- COALESCE(u.total_approvers, 0) AS total_approvers, +-- COALESCE(br.total_branches, 0) AS total_branches, +-- NOW() AS updated_at +-- FROM companies c +-- LEFT JOIN bet_stats b ON b.company_id = c.id +-- LEFT JOIN user_stats u ON u.company_id = c.id +-- LEFT JOIN branch_stats br ON br.company_id = c.id ON CONFLICT (company_id, interval_start) DO +-- UPDATE +-- SET total_bets = EXCLUDED.total_bets, +-- total_stake = EXCLUDED.total_stake, +-- deducted_stake = EXCLUDED.deducted_stake, +-- total_cash_out = EXCLUDED.total_cash_out, +-- total_cash_backs = EXCLUDED.total_cash_backs, +-- number_of_unsettled = EXCLUDED.number_of_unsettled, +-- total_unsettled_amount = EXCLUDED.total_unsettled_amount, +-- total_admins = EXCLUDED.total_admins, +-- total_managers = EXCLUDED.total_managers, +-- total_cashiers = EXCLUDED.total_cashiers, +-- total_customers = EXCLUDED.total_customers, +-- total_approvers = EXCLUDED.total_approvers, +-- total_branches = EXCLUDED.total_branches, +-- updated_at = EXCLUDED.updated_at; +-- -- name: GetCompanyStatsByID :many +-- SELECT * +-- FROM company_stats +-- WHERE company_id = $1 +-- ORDER BY interval_start DESC; +-- -- name: GetCompanyStats :many +-- SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start, +-- company_stats.company_id, +-- company_stats.company_name, +-- company_stats.company_slug, +-- company_stats.total_bets, +-- company_stats.total_stake, +-- company_stats.deducted_stake, +-- company_stats.total_cash_out, +-- company_stats.total_cash_backs, +-- company_stats.number_of_unsettled, +-- company_stats.total_unsettled_amount, +-- company_stats.total_admins, +-- company_stats.total_managers, +-- company_stats.total_cashiers, +-- company_stats.total_customers, +-- company_stats.total_approvers, +-- company_stats.total_branches, +-- company_stats.updated_at +-- FROM company_stats +-- WHERE ( +-- company_stats.company_id = sqlc.narg('company_id') +-- OR sqlc.narg('company_id') IS NULL +-- ) +-- GROUP BY interval_start +-- ORDER BY interval_start DESC; \ No newline at end of file diff --git a/db/query/custom_odds.sql b/db/query/custom_odds.sql deleted file mode 100644 index e4357e9..0000000 --- a/db/query/custom_odds.sql +++ /dev/null @@ -1,35 +0,0 @@ --- -- name: InsertCustomOddsMarket :one --- INSERT INTO custom_odds_market ( --- odds_market_id, --- company_id, --- event_id, --- raw_odds --- ) --- VALUES ($1, $2, $3, $4) --- RETURNING *; --- -- name: GetAllCustomOdds :many --- SELECT * --- FROM custom_odds_market --- WHERE ( --- company_id = sqlc.narg('company_id') --- OR sqlc.narg('company_id') IS NULL --- ); --- -- name: GetCustomOddByID :one --- SELECT * --- FROM custom_odds_market --- WHERE id = $1; --- -- name: GetCustomOddByOddID :one --- SELECT * --- FROM custom_odds_market --- WHERE odds_market_id = $1 --- AND company_id = $2; --- -- name: DeleteCustomOddsByID :exec --- DELETE FROM custom_odds_market --- WHERE id = $1; --- -- name: DeleteCustomOddsByOddID :exec --- DELETE FROM custom_odds_market --- WHERE odds_market_id = $1 --- AND company_id = $2; --- -- name: DeleteCustomOddByEventID :exec --- DELETE FROM custom_odds_market --- WHERE event_id = $1; \ No newline at end of file diff --git a/db/query/direct_deposit.sql b/db/query/direct_deposit.sql deleted file mode 100644 index f0b6e6a..0000000 --- a/db/query/direct_deposit.sql +++ /dev/null @@ -1,64 +0,0 @@ --- name: CreateDirectDeposit :one -INSERT INTO direct_deposits ( - customer_id, wallet_id, bank_name, account_number, - account_holder, amount, reference_number, - transfer_screenshot, status -) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'PENDING') -RETURNING *; - --- name: GetDirectDepositByID :one -SELECT * -FROM direct_deposits -WHERE id = $1; - --- name: DeleteDirectDeposit :exec -DELETE FROM direct_deposits -WHERE id = $1; - --- name: GetDirectDepositsByStatus :many -SELECT - id, - customer_id, - wallet_id, - bank_name, - account_number, - account_holder, - amount, - reference_number, - transfer_screenshot, - status, - created_at, - approved_by, - approved_at, - rejection_reason -FROM direct_deposits -WHERE status = $1 -ORDER BY created_at DESC -LIMIT $2 OFFSET $3; - --- name: CountDirectDepositsByStatus :one -SELECT COUNT(*) -FROM direct_deposits -WHERE status = $1; - --- name: ApproveDirectDeposit :exec -UPDATE direct_deposits -SET - status = 'APPROVED', - approved_by = $2, - approved_at = NOW() -WHERE - id = $1 - AND status = 'PENDING'; - --- name: RejectDirectDeposit :exec -UPDATE direct_deposits -SET - status = 'REJECTED', - approved_by = $2, -- still track the admin who took final action - approved_at = NOW(), - rejection_reason = $3 -WHERE - id = $1 - AND status = 'PENDING'; - diff --git a/db/query/enet_pulse.sql b/db/query/enet_pulse.sql deleted file mode 100644 index f586d46..0000000 --- a/db/query/enet_pulse.sql +++ /dev/null @@ -1,537 +0,0 @@ --- name: CreateEnetpulseSport :one -INSERT INTO enetpulse_sports ( - sport_id, - name, - updates_count, - last_updated_at, - status, - updated_at -) VALUES ( - $1, $2, $3, $4, $5, NOW() -) -ON CONFLICT (sport_id) DO UPDATE -SET - name = EXCLUDED.name, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - status = EXCLUDED.status, - updated_at = NOW() -RETURNING *; - --- name: GetAllEnetpulseSports :many -SELECT - id, - sport_id, - name, - updates_count, - last_updated_at, - status, - created_at, - updated_at -FROM enetpulse_sports -ORDER BY name; - --- name: CreateEnetpulseTournamentTemplate :one -INSERT INTO enetpulse_tournament_templates ( - template_id, - name, - sport_fk, - gender, - updates_count, - last_updated_at, - status, - updated_at -) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW()) -ON CONFLICT (template_id) DO UPDATE -SET - name = EXCLUDED.name, - sport_fk = EXCLUDED.sport_fk, - gender = EXCLUDED.gender, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - status = EXCLUDED.status, - updated_at = NOW() -RETURNING *; - --- name: GetAllEnetpulseTournamentTemplates :many -SELECT - id, - template_id, - name, - sport_fk, - gender, - updates_count, - last_updated_at, - status, - created_at, - updated_at -FROM enetpulse_tournament_templates -ORDER BY name; - --- -- name: DeleteEnetpulseTournamentTemplateByID :exec --- DELETE FROM enetpulse_tournament_templates WHERE template_id = $1; - --- name: CreateEnetpulseTournament :one -INSERT INTO enetpulse_tournaments ( - tournament_id, - name, - tournament_template_fk, - updates_count, - last_updated_at, - status -) VALUES ($1, $2, $3, $4, $5, $6) -ON CONFLICT (tournament_id) DO UPDATE -SET - name = EXCLUDED.name, - tournament_template_fk = EXCLUDED.tournament_template_fk, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - status = EXCLUDED.status -RETURNING *; - --- name: GetAllEnetpulseTournaments :many -SELECT * -FROM enetpulse_tournaments -ORDER BY created_at DESC; - --- name: CreateEnetpulseTournamentStage :one -INSERT INTO enetpulse_tournament_stages ( - stage_id, - name, - tournament_fk, - gender, - country_fk, - country_name, - start_date, - end_date, - updates_count, - last_updated_at, - status -) -VALUES ( - $1, -- stage_id - $2, -- name - $3, -- tournament_fk - $4, -- gender - $5, -- country_fk - $6, -- country_name - $7, -- start_date - $8, -- end_date - $9, -- updates_count - $10, -- last_updated_at - $11 -- status -) -ON CONFLICT (stage_id) DO UPDATE -SET - name = EXCLUDED.name, - tournament_fk = EXCLUDED.tournament_fk, - gender = EXCLUDED.gender, - country_fk = EXCLUDED.country_fk, - country_name = EXCLUDED.country_name, - start_date = EXCLUDED.start_date, - end_date = EXCLUDED.end_date, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - status = EXCLUDED.status, - updated_at = NOW() -RETURNING *; - --- name: GetAllEnetpulseTournamentStages :many -SELECT * -FROM enetpulse_tournament_stages -ORDER BY created_at DESC; - --- name: GetTournamentStagesByTournamentFK :many -SELECT * -FROM enetpulse_tournament_stages -WHERE tournament_fk = $1 -ORDER BY created_at DESC; - --- name: CreateEnetpulseFixture :one -INSERT INTO enetpulse_fixtures ( - fixture_id, - name, - sport_fk, - tournament_fk, - tournament_template_fk, - tournament_name, - tournament_template_name, - sport_name, - gender, - start_date, - status_type, - status_desc_fk, - round_type_fk, - updates_count, - last_updated_at, - created_at, - updated_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP -) -ON CONFLICT (fixture_id) DO UPDATE -SET - name = EXCLUDED.name, - sport_fk = EXCLUDED.sport_fk, - tournament_fk = EXCLUDED.tournament_fk, - tournament_template_fk = EXCLUDED.tournament_template_fk, - tournament_name = EXCLUDED.tournament_name, - tournament_template_name = EXCLUDED.tournament_template_name, - sport_name = EXCLUDED.sport_name, - gender = EXCLUDED.gender, - start_date = EXCLUDED.start_date, - status_type = EXCLUDED.status_type, - status_desc_fk = EXCLUDED.status_desc_fk, - round_type_fk = EXCLUDED.round_type_fk, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - updated_at = CURRENT_TIMESTAMP -RETURNING *; - - --- name: GetAllEnetpulseFixtures :many -SELECT * -FROM enetpulse_fixtures -ORDER BY created_at DESC; - --- name: CreateEnetpulseResult :one -INSERT INTO enetpulse_results ( - result_id, - name, - sport_fk, - tournament_fk, - tournament_template_fk, - tournament_name, - tournament_template_name, - sport_name, - start_date, - status_type, - status_desc_fk, - round_type_fk, - updates_count, - last_updated_at, - round, - live, - venue_name, - livestats_plus, - livestats_type, - commentary, - lineup_confirmed, - verified, - spectators, - game_started, - first_half_ended, - second_half_started, - second_half_ended, - game_ended, - created_at, - updated_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10, - $11,$12,$13,$14,$15,$16,$17,$18, - $19,$20,$21,$22,$23,$24,$25,$26, - $27,$28,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP -) -ON CONFLICT (result_id) DO UPDATE -SET - name = EXCLUDED.name, - sport_fk = EXCLUDED.sport_fk, - tournament_fk = EXCLUDED.tournament_fk, - tournament_template_fk = EXCLUDED.tournament_template_fk, - tournament_name = EXCLUDED.tournament_name, - tournament_template_name = EXCLUDED.tournament_template_name, - sport_name = EXCLUDED.sport_name, - start_date = EXCLUDED.start_date, - status_type = EXCLUDED.status_type, - status_desc_fk = EXCLUDED.status_desc_fk, - round_type_fk = EXCLUDED.round_type_fk, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - round = EXCLUDED.round, - live = EXCLUDED.live, - venue_name = EXCLUDED.venue_name, - livestats_plus = EXCLUDED.livestats_plus, - livestats_type = EXCLUDED.livestats_type, - commentary = EXCLUDED.commentary, - lineup_confirmed = EXCLUDED.lineup_confirmed, - verified = EXCLUDED.verified, - spectators = EXCLUDED.spectators, - game_started = EXCLUDED.game_started, - first_half_ended = EXCLUDED.first_half_ended, - second_half_started = EXCLUDED.second_half_started, - second_half_ended = EXCLUDED.second_half_ended, - game_ended = EXCLUDED.game_ended, - updated_at = CURRENT_TIMESTAMP -RETURNING *; - --- name: GetAllEnetpulseResults :many -SELECT * -FROM enetpulse_results -ORDER BY created_at DESC; - --- name: CreateEnetpulseResultParticipant :one -INSERT INTO enetpulse_result_participants ( - participant_map_id, - result_fk, - participant_fk, - number, - name, - gender, - type, - country_fk, - country_name, - ordinary_time, - running_score, - halftime, - final_result, - last_updated_at, - created_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,CURRENT_TIMESTAMP -) -ON CONFLICT (participant_map_id) DO UPDATE -SET - result_fk = EXCLUDED.result_fk, - participant_fk = EXCLUDED.participant_fk, - number = EXCLUDED.number, - name = EXCLUDED.name, - gender = EXCLUDED.gender, - type = EXCLUDED.type, - country_fk = EXCLUDED.country_fk, - country_name = EXCLUDED.country_name, - ordinary_time = EXCLUDED.ordinary_time, - running_score = EXCLUDED.running_score, - halftime = EXCLUDED.halftime, - final_result = EXCLUDED.final_result, - last_updated_at = EXCLUDED.last_updated_at -RETURNING *; - --- name: GetEnetpulseResultParticipantsByResultFK :many -SELECT * -FROM enetpulse_result_participants -WHERE result_fk = $1 -ORDER BY created_at DESC; - --- name: CreateEnetpulseResultReferee :one -INSERT INTO enetpulse_result_referees ( - result_fk, - referee_fk, - assistant1_referee_fk, - assistant2_referee_fk, - fourth_referee_fk, - var1_referee_fk, - var2_referee_fk, - last_updated_at, - created_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,CURRENT_TIMESTAMP -) -ON CONFLICT (result_fk) DO UPDATE -SET - referee_fk = EXCLUDED.referee_fk, - assistant1_referee_fk = EXCLUDED.assistant1_referee_fk, - assistant2_referee_fk = EXCLUDED.assistant2_referee_fk, - fourth_referee_fk = EXCLUDED.fourth_referee_fk, - var1_referee_fk = EXCLUDED.var1_referee_fk, - var2_referee_fk = EXCLUDED.var2_referee_fk, - last_updated_at = EXCLUDED.last_updated_at -RETURNING *; - --- name: GetEnetpulseResultRefereesByResultFK :many -SELECT * -FROM enetpulse_result_referees -WHERE result_fk = $1 -ORDER BY created_at DESC; - --- name: CreateEnetpulseOutcomeType :one -INSERT INTO enetpulse_outcome_types ( - outcome_type_id, - name, - description, - updates_count, - last_updated_at, - created_at, - updated_at -) VALUES ( - $1, $2, $3, $4, $5, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP -) -ON CONFLICT (outcome_type_id) DO UPDATE -SET - name = EXCLUDED.name, - description = EXCLUDED.description, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - updated_at = CURRENT_TIMESTAMP -RETURNING *; - --- name: GetAllEnetpulseOutcomeTypes :many -SELECT * -FROM enetpulse_outcome_types -ORDER BY created_at DESC; - --- name: CreateEnetpulsePreodds :one -INSERT INTO enetpulse_preodds ( - preodds_id, - event_fk, - outcome_type_fk, - outcome_scope_fk, - outcome_subtype_fk, - event_participant_number, - iparam, - iparam2, - dparam, - dparam2, - sparam, - updates_count, - last_updated_at, - created_at, - updated_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP -) -ON CONFLICT (preodds_id) DO UPDATE -SET - event_fk = EXCLUDED.event_fk, - outcome_type_fk = EXCLUDED.outcome_type_fk, - outcome_scope_fk = EXCLUDED.outcome_scope_fk, - outcome_subtype_fk = EXCLUDED.outcome_subtype_fk, - event_participant_number = EXCLUDED.event_participant_number, - iparam = EXCLUDED.iparam, - iparam2 = EXCLUDED.iparam2, - dparam = EXCLUDED.dparam, - dparam2 = EXCLUDED.dparam2, - sparam = EXCLUDED.sparam, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - updated_at = CURRENT_TIMESTAMP -RETURNING *; - --- name: GetAllEnetpulsePreodds :many -SELECT * -FROM enetpulse_preodds -ORDER BY created_at DESC; - - --- name: CreateEnetpulsePreoddsBettingOffer :one -INSERT INTO enetpulse_preodds_bettingoffers ( - bettingoffer_id, - preodds_fk, - bettingoffer_status_fk, - odds_provider_fk, - odds, - odds_old, - active, - coupon_key, - updates_count, - last_updated_at, - created_at, - updated_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP -) -ON CONFLICT (bettingoffer_id) DO UPDATE -SET - preodds_fk = EXCLUDED.preodds_fk, - bettingoffer_status_fk = EXCLUDED.bettingoffer_status_fk, - odds_provider_fk = EXCLUDED.odds_provider_fk, - odds = EXCLUDED.odds, - odds_old = EXCLUDED.odds_old, - active = EXCLUDED.active, - coupon_key = EXCLUDED.coupon_key, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - updated_at = CURRENT_TIMESTAMP -RETURNING *; - --- name: GetAllEnetpulsePreoddsBettingOffers :many -SELECT * -FROM enetpulse_preodds_bettingoffers -ORDER BY created_at DESC; - --- name: GetAllEnetpulsePreoddsWithBettingOffers :many -SELECT - p.id AS preodds_db_id, - p.preodds_id, - p.event_fk, - p.outcome_type_fk, - p.outcome_scope_fk, - p.outcome_subtype_fk, - p.event_participant_number, - p.iparam, - p.iparam2, - p.dparam, - p.dparam2, - p.sparam, - p.updates_count AS preodds_updates_count, - p.last_updated_at AS preodds_last_updated_at, - p.created_at AS preodds_created_at, - p.updated_at AS preodds_updated_at, - - -- Betting offer fields - bo.id AS bettingoffer_db_id, - bo.bettingoffer_id, - bo.preodds_fk, -- ✅ ensure alias matches struct field - bo.bettingoffer_status_fk, - bo.odds_provider_fk, - bo.odds, - bo.odds_old, - bo.active, - bo.coupon_key, - bo.updates_count AS bettingoffer_updates_count, - bo.last_updated_at AS bettingoffer_last_updated_at, - bo.created_at AS bettingoffer_created_at, - bo.updated_at AS bettingoffer_updated_at - -FROM enetpulse_preodds p -LEFT JOIN enetpulse_preodds_bettingoffers bo - ON bo.preodds_fk = p.preodds_id -ORDER BY p.created_at DESC, bo.created_at DESC; - - --- name: GetFixturesWithPreodds :many -SELECT - f.fixture_id AS id, - f.fixture_id AS fixture_id, - f.name AS fixture_name, - f.sport_fk, - f.tournament_fk, - f.tournament_template_fk, - f.start_date, - f.status_type, - f.status_desc_fk, - f.round_type_fk, - f.updates_count AS fixture_updates_count, - f.last_updated_at AS fixture_last_updated_at, - f.created_at AS fixture_created_at, - f.updated_at AS fixture_updated_at, - - -- Preodds fields - p.id AS preodds_db_id, - p.preodds_id, - p.event_fk, - p.outcome_type_fk, - p.outcome_scope_fk, - p.outcome_subtype_fk, - p.event_participant_number, - p.iparam, - p.iparam2, - p.dparam, - p.dparam2, - p.sparam, - p.updates_count AS preodds_updates_count, - p.last_updated_at AS preodds_last_updated_at, - p.created_at AS preodds_created_at, - p.updated_at AS preodds_updated_at - -FROM enetpulse_fixtures f -LEFT JOIN enetpulse_preodds p - ON p.event_fk = f.id -ORDER BY f.start_date DESC; - - - - - - - - diff --git a/db/query/event_history.sql b/db/query/event_history.sql deleted file mode 100644 index 3739838..0000000 --- a/db/query/event_history.sql +++ /dev/null @@ -1,36 +0,0 @@ --- name: InsertEventHistory :one -INSERT INTO event_history (event_id, status) -VALUES ($1, $2) -RETURNING *; --- name: GetAllEventHistory :many -SELECT * -FROM event_history -WHERE ( - event_id = sqlc.narg('event_id') - OR sqlc.narg('event_id') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetInitialEventPerDay :many -SELECT DISTINCT ON (DATE_TRUNC('day', created_at)) * -FROM event_history -WHERE ( - event_id = sqlc.narg('event_id') - OR sqlc.narg('event_id') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ) -ORDER BY DATE_TRUNC('day', created_at), - created_at ASC; \ No newline at end of file diff --git a/db/query/events.sql b/db/query/events.sql deleted file mode 100644 index 63017c9..0000000 --- a/db/query/events.sql +++ /dev/null @@ -1,191 +0,0 @@ --- name: InsertEvent :exec -INSERT INTO events ( - source_event_id, - sport_id, - match_name, - home_team, - away_team, - home_team_id, - away_team_id, - home_kit_image, - away_kit_image, - league_id, - league_name, - start_time, - is_live, - status, - source, - default_winning_upper_limit - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15, - $16 - ) ON CONFLICT (source_event_id, source) DO -UPDATE -SET sport_id = EXCLUDED.sport_id, - match_name = EXCLUDED.match_name, - home_team = EXCLUDED.home_team, - away_team = EXCLUDED.away_team, - home_team_id = EXCLUDED.home_team_id, - away_team_id = EXCLUDED.away_team_id, - home_kit_image = EXCLUDED.home_kit_image, - away_kit_image = EXCLUDED.away_kit_image, - league_id = EXCLUDED.league_id, - league_name = EXCLUDED.league_name, - start_time = EXCLUDED.start_time, - score = EXCLUDED.score, - match_minute = EXCLUDED.match_minute, - timer_status = EXCLUDED.timer_status, - added_time = EXCLUDED.added_time, - match_period = EXCLUDED.match_period, - is_live = EXCLUDED.is_live, - source = EXCLUDED.source, - default_winning_upper_limit = EXCLUDED.default_winning_upper_limit, - fetched_at = now(); --- name: ListLiveEvents :many -SELECT id -FROM event_detailed -WHERE is_live = true; --- TODO: Figure out how to make this code reusable. SQLC prohibits CTEs within multiple queries --- name: GetAllEvents :many -SELECT * -FROM event_detailed -WHERE ( - is_live = sqlc.narg('is_live') - OR sqlc.narg('is_live') IS NULL - ) - AND ( - status = sqlc.narg('status') - OR sqlc.narg('status') IS NULL - ) - AND ( - league_id = sqlc.narg('league_id') - OR sqlc.narg('league_id') IS NULL - ) - AND ( - sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ) - AND ( - match_name ILIKE '%' || sqlc.narg('query') || '%' - OR league_name ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - start_time < sqlc.narg('last_start_time') - OR sqlc.narg('last_start_time') IS NULL - ) - AND ( - start_time > sqlc.narg('first_start_time') - OR sqlc.narg('first_start_time') IS NULL - ) - AND ( - league_cc = sqlc.narg('country_code') - OR sqlc.narg('country_code') IS NULL - ) - AND ( - source = sqlc.narg('source') - OR sqlc.narg('source') IS NULL - ) -ORDER BY start_time ASC -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetTotalEvents :one -SELECT COUNT(*) -FROM event_detailed -WHERE ( - is_live = sqlc.narg('is_live') - OR sqlc.narg('is_live') IS NULL - ) - AND ( - status = sqlc.narg('status') - OR sqlc.narg('status') IS NULL - ) - AND ( - league_id = sqlc.narg('league_id') - OR sqlc.narg('league_id') IS NULL - ) - AND ( - sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ) - AND ( - match_name ILIKE '%' || sqlc.narg('query') || '%' - OR league_name ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - start_time < sqlc.narg('last_start_time') - OR sqlc.narg('last_start_time') IS NULL - ) - AND ( - start_time > sqlc.narg('first_start_time') - OR sqlc.narg('first_start_time') IS NULL - ) - AND ( - league_cc = sqlc.narg('country_code') - OR sqlc.narg('country_code') IS NULL - ) - AND ( - source = sqlc.narg('source') - OR sqlc.narg('source') IS NULL - ); - --- name: GetEventByID :one -SELECT * -FROM event_detailed -WHERE id = $1 -LIMIT 1; --- name: GetEventBySourceID :one -SELECT * -FROM event_detailed -WHERE source_event_id = $1 - AND source = $2; --- name: GetSportAndLeagueIDs :one -SELECT sport_id, - league_id -FROM events -WHERE id = $1; --- name: UpdateMatchResult :exec -UPDATE events -SET score = $1, - status = $2 -WHERE id = $3; --- name: IsEventMonitored :one -SELECT is_monitored -FROM events -WHERE id = $1; --- name: UpdateEventMonitored :exec -UPDATE events -SET is_monitored = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2; --- name: UpdateGlobalEventSettings :exec -UPDATE events -SET default_is_active = COALESCE(sqlc.narg(default_is_active), default_is_active), - default_is_featured = COALESCE( - sqlc.narg(default_is_featured), - default_is_featured - ), - default_winning_upper_limit = COALESCE( - sqlc.narg(default_winning_upper_limit), - default_winning_upper_limit - ), - updated_at = CURRENT_TIMESTAMP -WHERE id = $1; --- name: DeleteEvent :exec -DELETE FROM events -WHERE id = $1; \ No newline at end of file diff --git a/db/query/events_bet_stats.sql b/db/query/events_bet_stats.sql deleted file mode 100644 index 3773293..0000000 --- a/db/query/events_bet_stats.sql +++ /dev/null @@ -1,25 +0,0 @@ --- Aggregate bet stats per event --- name: UpdateEventBetStats :exec -INSERT INTO event_bet_stats ( - event_id, - number_of_bets, - total_amount, - avg_bet_amount, - total_potential_winnings, - updated_at - ) -SELECT bo.event_id, - COUNT(DISTINCT b.id) AS number_of_bets, - COALESCE(SUM(b.amount), 0) AS total_amount, - COALESCE(AVG(b.amount), 0) AS avg_bet_amount, - COALESCE(SUM(b.potential_win), 0) AS total_potential_winnings, - NOW() AS updated_at -FROM bet_outcomes bo - JOIN bets b ON bo.bet_id = b.id -GROUP BY bo.event_id ON CONFLICT (event_id) DO -UPDATE -SET number_of_bets = EXCLUDED.number_of_bets, - total_amount = EXCLUDED.total_amount, - avg_bet_amount = EXCLUDED.avg_bet_amount, - total_potential_winnings = EXCLUDED.total_potential_winnings, - updated_at = EXCLUDED.updated_at; \ No newline at end of file diff --git a/db/query/events_stat.sql b/db/query/events_stat.sql deleted file mode 100644 index 384e0f5..0000000 --- a/db/query/events_stat.sql +++ /dev/null @@ -1,123 +0,0 @@ --- name: GetTotalEventStats :one -SELECT COUNT(*) AS event_count, - COUNT(*) FILTER ( - WHERE events.default_is_active = TRUE - ) AS total_active_events, - COUNT(*) FILTER ( - WHERE events.default_is_active = FALSE - ) AS total_inactive_events, - COUNT(*) FILTER ( - WHERE events.default_is_featured = TRUE - ) AS total_featured_events, - COUNT(DISTINCT league_id) as total_leagues, - COUNT(*) FILTER ( - WHERE events.status = 'upcoming' - ) AS pending, - COUNT(*) FILTER ( - WHERE events.status = 'in_play' - ) AS in_play, - COUNT(*) FILTER ( - WHERE events.status = 'to_be_fixed' - ) AS to_be_fixed, - COUNT(*) FILTER ( - WHERE events.status = 'ended' - ) AS ended, - COUNT(*) FILTER ( - WHERE events.status = 'postponed' - ) AS postponed, - COUNT(*) FILTER ( - WHERE events.status = 'cancelled' - ) AS cancelled, - COUNT(*) FILTER ( - WHERE events.status = 'walkover' - ) AS walkover, - COUNT(*) FILTER ( - WHERE events.status = 'interrupted' - ) AS interrupted, - COUNT(*) FILTER ( - WHERE events.status = 'abandoned' - ) AS abandoned, - COUNT(*) FILTER ( - WHERE events.status = 'retired' - ) AS retired, - COUNT(*) FILTER ( - WHERE events.status = 'suspended' - ) AS suspended, - COUNT(*) FILTER ( - WHERE events.status = 'decided_by_fa' - ) AS decided_by_fa, - COUNT(*) FILTER ( - WHERE events.status = 'removed' - ) AS removed -FROM events -WHERE ( - events.league_id = sqlc.narg('league_id') - OR sqlc.narg('league_id') IS NULL - ) - AND ( - events.sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ); --- name: GetTotalEventStatsByInterval :many -SELECT DATE_TRUNC(sqlc.narg('interval'), start_time)::timestamp AS date, - COUNT(*) AS event_count, - COUNT(*) FILTER ( - WHERE events.default_is_active = TRUE - ) AS total_active_events, - COUNT(*) FILTER ( - WHERE events.default_is_active = FALSE - ) AS total_inactive_events, - COUNT(*) FILTER ( - WHERE events.default_is_featured = TRUE - ) AS total_featured_events, - COUNT(DISTINCT league_id) as total_leagues, - COUNT(*) FILTER ( - WHERE events.status = 'upcoming' - ) AS pending, - COUNT(*) FILTER ( - WHERE events.status = 'in_play' - ) AS in_play, - COUNT(*) FILTER ( - WHERE events.status = 'to_be_fixed' - ) AS to_be_fixed, - COUNT(*) FILTER ( - WHERE events.status = 'ended' - ) AS ended, - COUNT(*) FILTER ( - WHERE events.status = 'postponed' - ) AS postponed, - COUNT(*) FILTER ( - WHERE events.status = 'cancelled' - ) AS cancelled, - COUNT(*) FILTER ( - WHERE events.status = 'walkover' - ) AS walkover, - COUNT(*) FILTER ( - WHERE events.status = 'interrupted' - ) AS interrupted, - COUNT(*) FILTER ( - WHERE events.status = 'abandoned' - ) AS abandoned, - COUNT(*) FILTER ( - WHERE events.status = 'retired' - ) AS retired, - COUNT(*) FILTER ( - WHERE events.status = 'suspended' - ) AS suspended, - COUNT(*) FILTER ( - WHERE events.status = 'decided_by_fa' - ) AS decided_by_fa, - COUNT(*) FILTER ( - WHERE events.status = 'removed' - ) AS removed -FROM events -WHERE ( - events.league_id = sqlc.narg('league_id') - OR sqlc.narg('league_id') IS NULL - ) - AND ( - events.sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ) -GROUP BY date -ORDER BY date; \ No newline at end of file diff --git a/db/query/events_with_settings.sql b/db/query/events_with_settings.sql deleted file mode 100644 index 8444ee2..0000000 --- a/db/query/events_with_settings.sql +++ /dev/null @@ -1,171 +0,0 @@ --- name: SaveTenantEventSettings :exec -INSERT INTO company_event_settings ( - company_id, - event_id, - is_active, - is_featured, - winning_upper_limit - ) -VALUES ($1, $2, $3, $4, $5) ON CONFLICT(company_id, event_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - is_featured = EXCLUDED.is_featured, - winning_upper_limit = EXCLUDED.winning_upper_limit; --- name: GetTotalCompanyEvents :one -SELECT COUNT(*) -FROM events e - LEFT JOIN company_event_settings ces ON e.id = ces.event_id - AND ces.company_id = $1 - JOIN leagues l ON l.id = e.league_id -WHERE ( - is_live = sqlc.narg('is_live') - OR sqlc.narg('is_live') IS NULL - ) - AND ( - status = sqlc.narg('status') - OR sqlc.narg('status') IS NULL - ) - AND( - league_id = sqlc.narg('league_id') - OR sqlc.narg('league_id') IS NULL - ) - AND ( - e.sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ) - AND ( - match_name ILIKE '%' || sqlc.narg('query') || '%' - OR league_name ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - start_time < sqlc.narg('last_start_time') - OR sqlc.narg('last_start_time') IS NULL - ) - AND ( - start_time > sqlc.narg('first_start_time') - OR sqlc.narg('first_start_time') IS NULL - ) - AND ( - l.country_code = sqlc.narg('country_code') - OR sqlc.narg('country_code') IS NULL - ) - AND ( - ces.is_featured = sqlc.narg('is_featured') - OR e.default_is_featured = sqlc.narg('is_featured') - OR sqlc.narg('is_featured') IS NULL - ) - AND ( - ces.is_active = sqlc.narg('is_active') - OR e.default_is_active = sqlc.narg('is_active') - OR sqlc.narg('is_active') IS NULL - ) - AND ( - source = sqlc.narg('source') - OR sqlc.narg('source') IS NULL - ); --- name: GetEventsWithSettings :many -SELECT e.*, - ces.company_id, - COALESCE(ces.is_active, e.default_is_active) AS is_active, - COALESCE(ces.is_featured, e.default_is_featured) AS is_featured, - COALESCE( - ces.winning_upper_limit, - e.default_winning_upper_limit - ) AS winning_upper_limit, - ces.updated_at, - l.country_code as league_cc, - COALESCE(om.total_outcomes, 0) AS total_outcomes, - COALESCE(ebs.number_of_bets, 0) AS number_of_bets, - COALESCE(ebs.total_amount, 0) AS total_amount, - COALESCE(ebs.avg_bet_amount, 0) AS avg_bet_amount, - COALESCE(ebs.total_potential_winnings, 0) AS total_potential_winnings -FROM events e - LEFT JOIN company_event_settings ces ON e.id = ces.event_id - AND ces.company_id = $1 - LEFT JOIN event_bet_stats ebs ON ebs.event_id = e.id - JOIN leagues l ON l.id = e.league_id - LEFT JOIN ( - SELECT event_id, - SUM(number_of_outcomes) AS total_outcomes - FROM odds_market - GROUP BY event_id - ) om ON om.event_id = e.id -WHERE ( - is_live = sqlc.narg('is_live') - OR sqlc.narg('is_live') IS NULL - ) - AND ( - status = sqlc.narg('status') - OR sqlc.narg('status') IS NULL - ) - AND( - league_id = sqlc.narg('league_id') - OR sqlc.narg('league_id') IS NULL - ) - AND ( - e.sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ) - AND ( - match_name ILIKE '%' || sqlc.narg('query') || '%' - OR league_name ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - start_time < sqlc.narg('last_start_time') - OR sqlc.narg('last_start_time') IS NULL - ) - AND ( - start_time > sqlc.narg('first_start_time') - OR sqlc.narg('first_start_time') IS NULL - ) - AND ( - l.country_code = sqlc.narg('country_code') - OR sqlc.narg('country_code') IS NULL - ) - AND ( - ces.is_featured = sqlc.narg('is_featured') - OR e.default_is_featured = sqlc.narg('is_featured') - OR sqlc.narg('is_featured') IS NULL - ) - AND ( - ces.is_active = sqlc.narg('is_active') - OR e.default_is_active = sqlc.narg('is_active') - OR sqlc.narg('is_active') IS NULL - ) - AND ( - source = sqlc.narg('source') - OR sqlc.narg('source') IS NULL - ) -ORDER BY start_time ASC -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetEventWithSettingByID :one -SELECT e.*, - ces.company_id, - COALESCE(ces.is_active, e.default_is_active) AS is_active, - COALESCE(ces.is_featured, e.default_is_featured) AS is_featured, - COALESCE( - ces.winning_upper_limit, - e.default_winning_upper_limit - ) AS winning_upper_limit, - ces.updated_at, - l.country_code as league_cc, - COALESCE(om.total_outcomes, 0) AS total_outcomes, - COALESCE(ebs.number_of_bets, 0) AS number_of_bets, - COALESCE(ebs.total_amount, 0) AS total_amount, - COALESCE(ebs.avg_bet_amount, 0) AS avg_bet_amount, - COALESCE(ebs.total_potential_winnings, 0) AS total_potential_winnings -FROM events e - LEFT JOIN company_event_settings ces ON e.id = ces.event_id - AND ces.company_id = $2 - LEFT JOIN event_bet_stats ebs ON ebs.event_id = e.id - JOIN leagues l ON l.id = e.league_id - LEFT JOIN ( - SELECT event_id, - SUM(number_of_outcomes) AS total_outcomes - FROM odds_market - GROUP BY event_id - ) om ON om.event_id = e.id -WHERE e.id = $1 -LIMIT 1; \ No newline at end of file diff --git a/db/query/flags.sql b/db/query/flags.sql index bb36e8b..f11cdbf 100644 --- a/db/query/flags.sql +++ b/db/query/flags.sql @@ -1,8 +1,8 @@ --- name: CreateFlag :one -INSERT INTO flags ( - bet_id, - odds_market_id, - reason -) VALUES ( - $1, $2, $3 -) RETURNING *; \ No newline at end of file +-- -- name: CreateFlag :one +-- INSERT INTO flags ( +-- bet_id, +-- odds_market_id, +-- reason +-- ) VALUES ( +-- $1, $2, $3 +-- ) RETURNING *; \ No newline at end of file diff --git a/db/query/institutions.sql b/db/query/institutions.sql index 865524b..67b2290 100644 --- a/db/query/institutions.sql +++ b/db/query/institutions.sql @@ -1,87 +1,87 @@ --- name: CreateBank :one -INSERT INTO banks ( - slug, - swift, - name, - acct_length, - country_id, - is_mobilemoney, - is_active, - is_rtgs, - active, - is_24hrs, - created_at, - updated_at, - currency, - bank_logo -) -VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, $11, $12 -) -RETURNING *; +-- -- name: CreateBank :one +-- INSERT INTO banks ( +-- slug, +-- swift, +-- name, +-- acct_length, +-- country_id, +-- is_mobilemoney, +-- is_active, +-- is_rtgs, +-- active, +-- is_24hrs, +-- created_at, +-- updated_at, +-- currency, +-- bank_logo +-- ) +-- VALUES ( +-- $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, $11, $12 +-- ) +-- RETURNING *; --- name: GetBankByID :one -SELECT * -FROM banks -WHERE id = $1; +-- -- name: GetBankByID :one +-- SELECT * +-- FROM banks +-- WHERE id = $1; --- name: GetAllBanks :many -SELECT * -FROM banks -WHERE ( - country_id = sqlc.narg('country_id') - OR sqlc.narg('country_id') IS NULL - ) - AND ( - is_active = sqlc.narg('is_active') - OR sqlc.narg('is_active') IS NULL - ) - AND ( - name ILIKE '%' || sqlc.narg('search_term') || '%' - OR sqlc.narg('search_term') IS NULL - ) - AND ( - code ILIKE '%' || sqlc.narg('search_term') || '%' - OR sqlc.narg('search_term') IS NULL - ) -ORDER BY name ASC -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); +-- -- name: GetAllBanks :many +-- SELECT * +-- FROM banks +-- WHERE ( +-- country_id = sqlc.narg('country_id') +-- OR sqlc.narg('country_id') IS NULL +-- ) +-- AND ( +-- is_active = sqlc.narg('is_active') +-- OR sqlc.narg('is_active') IS NULL +-- ) +-- AND ( +-- name ILIKE '%' || sqlc.narg('search_term') || '%' +-- OR sqlc.narg('search_term') IS NULL +-- ) +-- AND ( +-- code ILIKE '%' || sqlc.narg('search_term') || '%' +-- OR sqlc.narg('search_term') IS NULL +-- ) +-- ORDER BY name ASC +-- LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: CountBanks :one -SELECT COUNT(*) -FROM banks -WHERE ( - country_id = $1 - OR $1 IS NULL - ) - AND ( - is_active = $2 - OR $2 IS NULL - ) - AND ( - name ILIKE '%' || $3 || '%' - OR code ILIKE '%' || $3 || '%' - OR $3 IS NULL - ); +-- -- name: CountBanks :one +-- SELECT COUNT(*) +-- FROM banks +-- WHERE ( +-- country_id = $1 +-- OR $1 IS NULL +-- ) +-- AND ( +-- is_active = $2 +-- OR $2 IS NULL +-- ) +-- AND ( +-- name ILIKE '%' || $3 || '%' +-- OR code ILIKE '%' || $3 || '%' +-- OR $3 IS NULL +-- ); --- name: UpdateBank :one -UPDATE banks -SET slug = COALESCE(sqlc.narg(slug), slug), - swift = COALESCE(sqlc.narg(swift), swift), - name = COALESCE(sqlc.narg(name), name), - acct_length = COALESCE(sqlc.narg(acct_length), acct_length), - country_id = COALESCE(sqlc.narg(country_id), country_id), - is_mobilemoney = COALESCE(sqlc.narg(is_mobilemoney), is_mobilemoney), - is_active = COALESCE(sqlc.narg(is_active), is_active), - is_rtgs = COALESCE(sqlc.narg(is_rtgs), is_rtgs), - active = COALESCE(sqlc.narg(active), active), - is_24hrs = COALESCE(sqlc.narg(is_24hrs), is_24hrs), - updated_at = CURRENT_TIMESTAMP, - currency = COALESCE(sqlc.narg(currency), currency), - bank_logo = COALESCE(sqlc.narg(bank_logo), bank_logo) -WHERE id = $1 -RETURNING *; +-- -- name: UpdateBank :one +-- UPDATE banks +-- SET slug = COALESCE(sqlc.narg(slug), slug), +-- swift = COALESCE(sqlc.narg(swift), swift), +-- name = COALESCE(sqlc.narg(name), name), +-- acct_length = COALESCE(sqlc.narg(acct_length), acct_length), +-- country_id = COALESCE(sqlc.narg(country_id), country_id), +-- is_mobilemoney = COALESCE(sqlc.narg(is_mobilemoney), is_mobilemoney), +-- is_active = COALESCE(sqlc.narg(is_active), is_active), +-- is_rtgs = COALESCE(sqlc.narg(is_rtgs), is_rtgs), +-- active = COALESCE(sqlc.narg(active), active), +-- is_24hrs = COALESCE(sqlc.narg(is_24hrs), is_24hrs), +-- updated_at = CURRENT_TIMESTAMP, +-- currency = COALESCE(sqlc.narg(currency), currency), +-- bank_logo = COALESCE(sqlc.narg(bank_logo), bank_logo) +-- WHERE id = $1 +-- RETURNING *; --- name: DeleteBank :exec -DELETE FROM banks -WHERE id = $1; +-- -- name: DeleteBank :exec +-- DELETE FROM banks +-- WHERE id = $1; diff --git a/db/query/issue_reporting.sql b/db/query/issue_reporting.sql index 1906ffe..12263d5 100644 --- a/db/query/issue_reporting.sql +++ b/db/query/issue_reporting.sql @@ -1,37 +1,37 @@ --- name: CreateReportedIssue :one -INSERT INTO reported_issues ( - user_id, - user_role, - subject, - description, - issue_type, - metadata - ) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING *; --- name: ListReportedIssues :many -SELECT * -FROM reported_issues -ORDER BY created_at DESC -LIMIT $1 OFFSET $2; --- name: ListReportedIssuesByUser :many -SELECT * -FROM reported_issues -WHERE user_id = $1 -ORDER BY created_at DESC -LIMIT $2 OFFSET $3; --- name: CountReportedIssues :one -SELECT COUNT(*) -FROM reported_issues; --- name: CountReportedIssuesByUser :one -SELECT COUNT(*) -FROM reported_issues -WHERE user_id = $1; --- name: UpdateReportedIssueStatus :exec -UPDATE reported_issues -SET status = $2, - updated_at = NOW() -WHERE id = $1; --- name: DeleteReportedIssue :exec -DELETE FROM reported_issues -WHERE id = $1; \ No newline at end of file +-- -- name: CreateReportedIssue :one +-- INSERT INTO reported_issues ( +-- user_id, +-- user_role, +-- subject, +-- description, +-- issue_type, +-- metadata +-- ) +-- VALUES ($1, $2, $3, $4, $5, $6) +-- RETURNING *; +-- -- name: ListReportedIssues :many +-- SELECT * +-- FROM reported_issues +-- ORDER BY created_at DESC +-- LIMIT $1 OFFSET $2; +-- -- name: ListReportedIssuesByUser :many +-- SELECT * +-- FROM reported_issues +-- WHERE user_id = $1 +-- ORDER BY created_at DESC +-- LIMIT $2 OFFSET $3; +-- -- name: CountReportedIssues :one +-- SELECT COUNT(*) +-- FROM reported_issues; +-- -- name: CountReportedIssuesByUser :one +-- SELECT COUNT(*) +-- FROM reported_issues +-- WHERE user_id = $1; +-- -- name: UpdateReportedIssueStatus :exec +-- UPDATE reported_issues +-- SET status = $2, +-- updated_at = NOW() +-- WHERE id = $1; +-- -- name: DeleteReportedIssue :exec +-- DELETE FROM reported_issues +-- WHERE id = $1; \ No newline at end of file diff --git a/db/query/league_stats.sql b/db/query/league_stats.sql deleted file mode 100644 index 669529d..0000000 --- a/db/query/league_stats.sql +++ /dev/null @@ -1,47 +0,0 @@ --- name: GetLeagueEventStat :many -SELECT leagues.id, - leagues.name, - COUNT(*) AS total_events, - COUNT(*) FILTER ( - WHERE events.status = 'upcoming' - ) AS pending, - COUNT(*) FILTER ( - WHERE events.status = 'in_play' - ) AS in_play, - COUNT(*) FILTER ( - WHERE events.status = 'to_be_fixed' - ) AS to_be_fixed, - COUNT(*) FILTER ( - WHERE events.status = 'ended' - ) AS ended, - COUNT(*) FILTER ( - WHERE events.status = 'postponed' - ) AS postponed, - COUNT(*) FILTER ( - WHERE events.status = 'cancelled' - ) AS cancelled, - COUNT(*) FILTER ( - WHERE events.status = 'walkover' - ) AS walkover, - COUNT(*) FILTER ( - WHERE events.status = 'interrupted' - ) AS interrupted, - COUNT(*) FILTER ( - WHERE events.status = 'abandoned' - ) AS abandoned, - COUNT(*) FILTER ( - WHERE events.status = 'retired' - ) AS retired, - COUNT(*) FILTER ( - WHERE events.status = 'suspended' - ) AS suspended, - COUNT(*) FILTER ( - WHERE events.status = 'decided_by_fa' - ) AS decided_by_fa, - COUNT(*) FILTER ( - WHERE events.status = 'removed' - ) AS removed -FROM leagues - JOIN events ON leagues.id = events.league_id -GROUP BY leagues.id, - leagues.name; \ No newline at end of file diff --git a/db/query/leagues.sql b/db/query/leagues.sql deleted file mode 100644 index 047ce5d..0000000 --- a/db/query/leagues.sql +++ /dev/null @@ -1,157 +0,0 @@ --- name: InsertLeague :exec -INSERT INTO leagues ( - id, - name, - country_code, - bet365_id, - sport_id, - default_is_active, - default_is_featured - ) -VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO -UPDATE -SET name = EXCLUDED.name, - country_code = EXCLUDED.country_code, - bet365_id = EXCLUDED.bet365_id, - sport_id = EXCLUDED.sport_id; --- name: SaveLeagueSettings :exec -INSERT INTO company_league_settings ( - company_id, - league_id, - is_active, - is_featured - ) -VALUES ($1, $2, $3, $4) ON CONFLICT(company_id, league_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - is_featured = EXCLUDED.is_featured; --- name: GetAllLeagues :many -SELECT * -FROM leagues -WHERE ( - country_code = sqlc.narg('country_code') - OR sqlc.narg('country_code') IS NULL - ) - AND ( - sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ) - AND ( - name ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - default_is_active = sqlc.narg('is_active') - OR sqlc.narg('is_active') IS NULL - ) -ORDER BY name ASC -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetTotalLeagues :one -SELECT COUNT(*) -FROM leagues -WHERE ( - country_code = sqlc.narg('country_code') - OR sqlc.narg('country_code') IS NULL - ) - AND ( - sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ) - AND ( - name ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - default_is_active = sqlc.narg('is_active') - OR sqlc.narg('is_active') IS NULL - ); --- name: GetTotalLeaguesWithSettings :one -SELECT COUNT(*) -FROM leagues l - LEFT JOIN company_league_settings cls ON l.id = cls.league_id - AND company_id = $1 -WHERE ( - country_code = sqlc.narg('country_code') - OR sqlc.narg('country_code') IS NULL - ) - AND ( - sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ) - AND ( - is_active = sqlc.narg('is_active') - OR default_is_active = sqlc.narg('is_active') - OR sqlc.narg('is_active') IS NULL - ) - AND ( - is_featured = sqlc.narg('is_featured') - OR default_is_featured = sqlc.narg('is_featured') - OR sqlc.narg('is_featured') IS NULL - ) - AND ( - name ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ); --- name: GetAllLeaguesWithSettings :many -SELECT l.*, - cls.company_id, - COALESCE(cls.is_active, l.default_is_active) AS is_active, - COALESCE(cls.is_featured, l.default_is_featured) AS is_featured, - cls.updated_at -FROM leagues l - LEFT JOIN company_league_settings cls ON l.id = cls.league_id - AND company_id = $1 -WHERE ( - country_code = sqlc.narg('country_code') - OR sqlc.narg('country_code') IS NULL - ) - AND ( - sport_id = sqlc.narg('sport_id') - OR sqlc.narg('sport_id') IS NULL - ) - AND ( - is_active = sqlc.narg('is_active') - OR default_is_active = sqlc.narg('is_active') - OR sqlc.narg('is_active') IS NULL - ) - AND ( - is_featured = sqlc.narg('is_featured') - OR default_is_featured = sqlc.narg('is_featured') - OR sqlc.narg('is_featured') IS NULL - ) - AND ( - name ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) -ORDER BY is_featured DESC, - name ASC -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: CheckLeagueSupport :one -SELECT EXISTS( - SELECT 1 - FROM company_league_settings - WHERE league_id = $1 - AND company_id = $2 - AND is_active = true - ); --- name: UpdateLeague :exec -UPDATE leagues -SET name = COALESCE(sqlc.narg('name'), name), - country_code = COALESCE(sqlc.narg('country_code'), country_code), - bet365_id = COALESCE(sqlc.narg('bet365_id'), bet365_id), - sport_id = COALESCE(sqlc.narg('sport_id'), sport_id) -WHERE id = $1; --- name: UpdateCompanyLeagueSettings :exec -UPDATE company_league_settings -SET is_active = COALESCE(sqlc.narg('is_active'), is_active), - is_featured = COALESCE( - sqlc.narg('is_featured'), - is_featured - ) -WHERE league_id = $1 - AND company_id = $2; --- name: UpdateGlobalLeagueSettings :exec -UPDATE leagues -SET default_is_active = COALESCE(sqlc.narg('is_active'), default_is_active), - default_is_featured = COALESCE(sqlc.narg('is_featured'), default_is_featured) -WHERE id = $1; \ No newline at end of file diff --git a/db/query/location.sql b/db/query/location.sql index 01d117d..6bb8595 100644 --- a/db/query/location.sql +++ b/db/query/location.sql @@ -1,7 +1,7 @@ --- name: GetAllBranchLocations :many -SELECT * -FROM branch_locations -WHERE ( - value ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ); \ No newline at end of file +-- -- name: GetAllBranchLocations :many +-- SELECT * +-- FROM branch_locations +-- WHERE ( +-- value ILIKE '%' || sqlc.narg('query') || '%' +-- OR sqlc.narg('query') IS NULL +-- ); \ No newline at end of file diff --git a/db/query/market_settings.sql b/db/query/market_settings.sql deleted file mode 100644 index da8e138..0000000 --- a/db/query/market_settings.sql +++ /dev/null @@ -1,57 +0,0 @@ --- name: InsertGlobalMarketSettings :exec -INSERT INTO global_odd_market_settings (market_id, market_name, is_active) -VALUES ($1, $2, $3) ON CONFLICT (market_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - updated_at = CURRENT_TIMESTAMP; --- name: InsertCompanyMarketSettings :exec -INSERT INTO company_odd_market_settings (company_id, market_id, market_name, is_active) -VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, market_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - updated_at = CURRENT_TIMESTAMP; --- name: GetAllGlobalMarketSettings :many -SELECT * -FROM global_odd_market_settings -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetGlobalMarketSettingsByID :one -SELECT * -FROM global_odd_market_settings -WHERE market_id = $1; --- name: GetAllCompanyMarketSettings :many -SELECT * -FROM company_odd_market_settings -WHERE ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetCompanyMarketSettingsByID :one -SELECT * -FROM company_odd_market_settings -WHERE market_id = $1; --- name: GetAllOverrideMarketSettings :many -SELECT gdm.market_id, - gdm.market_name, - COALESCE(cdm.is_active, gdm.is_active) AS is_active, - COALESCE(cdm.updated_at, gdm.updated_at) AS updated_at -FROM global_odd_market_settings gdm - LEFT JOIN company_odd_market_settings cdm ON cdm.market_id = gdm.market_id - AND company_id = $1 -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetOverrideMarketSettingByID :one -SELECT gdm.market_id, - gdm.market_name, - COALESCE(cdm.is_active, gdm.is_active) AS is_active, - COALESCE(cdm.updated_at, gdm.updated_at) AS updated_at -FROM global_odd_market_settings gdm - LEFT JOIN company_odd_market_settings cdm ON cdm.market_id = gdm.market_id - AND company_id = $1 -WHERE gdm.market_id = $2; --- name: DeleteAllMarketSettingsForCompany :exec -DELETE FROM company_odd_market_settings -WHERE company_id = $1; --- name: DeleteCompanyMarketSettings :exec -DELETE FROM company_odd_market_settings -WHERE market_id = $1 - AND company_id = $2; \ No newline at end of file diff --git a/db/query/monitor.sql b/db/query/monitor.sql deleted file mode 100644 index a206539..0000000 --- a/db/query/monitor.sql +++ /dev/null @@ -1,24 +0,0 @@ --- name: GetAllCompaniesBranch :many -SELECT id, name, wallet_id, admin_id -FROM companies; - --- name: GetBranchesByCompanyID :many -SELECT - id, - name, - location, - wallet_id, - branch_manager_id, - company_id, - is_self_owned -FROM branches -WHERE company_id = $1; - --- name: CountThresholdNotifications :one -SELECT COUNT(*) -FROM wallet_threshold_notifications -WHERE company_id = $1 AND threshold = $2; - --- name: CreateThresholdNotification :exec -INSERT INTO wallet_threshold_notifications (company_id, threshold) -VALUES ($1, $2); \ No newline at end of file diff --git a/db/query/notification.sql b/db/query/notification.sql index c1dfa06..1dbf5df 100644 --- a/db/query/notification.sql +++ b/db/query/notification.sql @@ -1,99 +1,99 @@ --- name: CreateNotification :one -INSERT INTO notifications ( - id, - recipient_id, - type, - level, - error_severity, - reciever, - is_read, - delivery_status, - delivery_channel, - payload, - priority, - timestamp, - expires, - img, - metadata - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15 - ) -RETURNING *; --- name: GetNotification :one -SELECT * -FROM notifications -WHERE id = $1 -LIMIT 1; --- name: GetAllNotifications :many -SELECT * -FROM notifications -ORDER BY timestamp DESC -LIMIT $1 OFFSET $2; --- name: GetTotalNotificationCount :one -SELECT COUNT(*) -FROM notifications; --- name: GetUserNotifications :many -SELECT * -FROM notifications -WHERE recipient_id = $1 -ORDER BY timestamp DESC -LIMIT $2 OFFSET $3; --- name: GetUserNotificationCount :one -SELECT COUNT(*) -FROM notifications -WHERE recipient_id = $1; --- name: CountUnreadNotifications :one -SELECT count(id) -FROM notifications -WHERE recipient_id = $1 - AND is_read = false; --- name: UpdateNotificationStatus :one -UPDATE notifications -SET delivery_status = $2, - is_read = $3, - metadata = $4 -WHERE id = $1 -RETURNING *; --- name: ListFailedNotifications :many -SELECT * -FROM notifications -WHERE delivery_status = 'failed' - AND timestamp < NOW() - INTERVAL '1 hour' -ORDER BY timestamp ASC -LIMIT $1; --- name: ListRecipientIDsByReceiver :many -SELECT recipient_id -FROM notifications -WHERE reciever = $1; --- name: GetNotificationCounts :many -SELECT COUNT(*) as total, - COUNT( - CASE - WHEN is_read = true THEN 1 - END - ) as read, - COUNT( - CASE - WHEN is_read = false THEN 1 - END - ) as unread -FROM notifications; +-- -- name: CreateNotification :one +-- INSERT INTO notifications ( +-- id, +-- recipient_id, +-- type, +-- level, +-- error_severity, +-- reciever, +-- is_read, +-- delivery_status, +-- delivery_channel, +-- payload, +-- priority, +-- timestamp, +-- expires, +-- img, +-- metadata +-- ) +-- VALUES ( +-- $1, +-- $2, +-- $3, +-- $4, +-- $5, +-- $6, +-- $7, +-- $8, +-- $9, +-- $10, +-- $11, +-- $12, +-- $13, +-- $14, +-- $15 +-- ) +-- RETURNING *; +-- -- name: GetNotification :one +-- SELECT * +-- FROM notifications +-- WHERE id = $1 +-- LIMIT 1; +-- -- name: GetAllNotifications :many +-- SELECT * +-- FROM notifications +-- ORDER BY timestamp DESC +-- LIMIT $1 OFFSET $2; +-- -- name: GetTotalNotificationCount :one +-- SELECT COUNT(*) +-- FROM notifications; +-- -- name: GetUserNotifications :many +-- SELECT * +-- FROM notifications +-- WHERE recipient_id = $1 +-- ORDER BY timestamp DESC +-- LIMIT $2 OFFSET $3; +-- -- name: GetUserNotificationCount :one +-- SELECT COUNT(*) +-- FROM notifications +-- WHERE recipient_id = $1; +-- -- name: CountUnreadNotifications :one +-- SELECT count(id) +-- FROM notifications +-- WHERE recipient_id = $1 +-- AND is_read = false; +-- -- name: UpdateNotificationStatus :one +-- UPDATE notifications +-- SET delivery_status = $2, +-- is_read = $3, +-- metadata = $4 +-- WHERE id = $1 +-- RETURNING *; +-- -- name: ListFailedNotifications :many +-- SELECT * +-- FROM notifications +-- WHERE delivery_status = 'failed' +-- AND timestamp < NOW() - INTERVAL '1 hour' +-- ORDER BY timestamp ASC +-- LIMIT $1; +-- -- name: ListRecipientIDsByReceiver :many +-- SELECT recipient_id +-- FROM notifications +-- WHERE reciever = $1; +-- -- name: GetNotificationCounts :many +-- SELECT COUNT(*) as total, +-- COUNT( +-- CASE +-- WHEN is_read = true THEN 1 +-- END +-- ) as read, +-- COUNT( +-- CASE +-- WHEN is_read = false THEN 1 +-- END +-- ) as unread +-- FROM notifications; --- name: DeleteOldNotifications :exec -DELETE FROM notifications -WHERE expires < now(); \ No newline at end of file +-- -- name: DeleteOldNotifications :exec +-- DELETE FROM notifications +-- WHERE expires < now(); \ No newline at end of file diff --git a/db/query/odd_history.sql b/db/query/odd_history.sql deleted file mode 100644 index b040c2e..0000000 --- a/db/query/odd_history.sql +++ /dev/null @@ -1,66 +0,0 @@ --- name: InsertOddHistory :one -INSERT INTO odd_history ( - odds_market_id, - market_id, - raw_odd_id, - event_id, - odd_value - ) -VALUES ($1, $2, $3, $4, $5) -RETURNING *; --- name: GetAllOddHistory :many -SELECT * -FROM odd_history -WHERE ( - odds_market_id = sqlc.narg('odd_id') - OR sqlc.narg('odd_id') IS NULL - ) - AND ( - market_id = sqlc.narg('market_id') - OR sqlc.narg('market_id') IS NULL - ) - AND ( - raw_odd_id = sqlc.narg('raw_odd_id') - OR sqlc.narg('raw_odd_id') IS NULL - ) - AND ( - event_id = sqlc.narg('event_id') - OR sqlc.narg('event_id') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetInitialOddPerDay :many -SELECT DISTINCT ON (DATE_TRUNC($1, created_at)) * -FROM odd_history -WHERE ( - odds_market_id = sqlc.narg('odd_id') - OR sqlc.narg('odd_id') IS NULL - ) - AND ( - market_id = sqlc.narg('market_id') - OR sqlc.narg('market_id') IS NULL - ) - AND ( - raw_odd_id = sqlc.narg('raw_odd_id') - OR sqlc.narg('raw_odd_id') IS NULL - ) - AND ( - event_id = sqlc.narg('event_id') - OR sqlc.narg('event_id') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ) -ORDER BY DATE_TRUNC($1, created_at), - created_at ASC; \ No newline at end of file diff --git a/db/query/odds.sql b/db/query/odds.sql deleted file mode 100644 index e865522..0000000 --- a/db/query/odds.sql +++ /dev/null @@ -1,176 +0,0 @@ --- name: InsertOddsMarket :exec -INSERT INTO odds_market ( - event_id, - market_type, - market_name, - market_category, - market_id, - number_of_outcomes, - raw_odds, - fetched_at, - expires_at - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9 - ) ON CONFLICT (event_id, market_id) DO -UPDATE -SET market_type = EXCLUDED.market_type, - market_name = EXCLUDED.market_name, - market_category = EXCLUDED.market_category, - raw_odds = EXCLUDED.raw_odds, - number_of_outcomes = EXCLUDED.number_of_outcomes, - fetched_at = EXCLUDED.fetched_at, - expires_at = EXCLUDED.expires_at; --- name: SaveOddSettings :exec -INSERT INTO company_odd_settings ( - company_id, - odds_market_id, - is_active, - custom_raw_odds - ) -VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - custom_raw_odds = EXCLUDED.custom_raw_odds; --- name: GetAllOdds :many -SELECT * -FROM odds_market_with_event -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetAllOddsWithSettings :many -SELECT o.id, - o.event_id, - o.market_type, - o.market_name, - o.market_category, - o.market_id, - o.number_of_outcomes, - o.default_is_active, - o.fetched_at, - o.expires_at, - cos.company_id, - COALESCE(cos.is_active, o.default_is_active) AS is_active, - COALESCE(cms.is_active, TRUE) AS is_market_active, - COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, - cos.updated_at -FROM odds_market o - LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id - AND cos.company_id = $1 - LEFT JOIN company_odd_market_settings cms ON o.market_id = cms.market_id - AND cms.company_id = $1 -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetOddByID :one -SELECT * -FROM odds_market_with_event -WHERE id = $1; --- name: GetOddsByMarketID :one -SELECT * -FROM odds_market_with_event -WHERE market_id = $1 - AND event_id = $2; --- name: GetOddsWithSettingsByMarketID :one -SELECT o.id, - o.event_id, - o.market_type, - o.market_name, - o.market_category, - o.market_id, - o.number_of_outcomes, - o.default_is_active, - o.fetched_at, - o.expires_at, - cos.company_id, - COALESCE(cos.is_active, o.default_is_active) AS is_active, - COALESCE(cms.is_active, TRUE) AS is_market_active, - COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, - cos.updated_at -FROM odds_market o - LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id - AND cos.company_id = $3 - LEFT JOIN company_odd_market_settings cms ON o.market_id = cms.market_id - AND cms.company_id = $3 -WHERE o.market_id = $1 - AND event_id = $2; --- name: GetOddsWithSettingsByID :one -SELECT o.id, - o.event_id, - o.market_type, - o.market_name, - o.market_category, - o.market_id, - o.number_of_outcomes, - o.default_is_active, - o.fetched_at, - o.expires_at, - cos.company_id, - COALESCE(cos.is_active, o.default_is_active) AS is_active, - COALESCE(cms.is_active, TRUE) AS is_market_active, - COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, - cos.updated_at -FROM odds_market o - LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id - AND cos.company_id = $2 - LEFT JOIN company_odd_market_settings cms ON o.market_id = cms.market_id - AND cms.company_id = $2 -WHERE o.id = $1; --- name: GetOddsByEventID :many -SELECT * -FROM odds_market_with_event -WHERE event_id = $1 - AND ( - is_live = sqlc.narg('is_live') - OR sqlc.narg('is_live') IS NULL - ) - AND ( - status = sqlc.narg('status') - OR sqlc.narg('status') IS NULL - ) - AND ( - source = sqlc.narg('source') - OR sqlc.narg('source') IS NULL - ) -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: GetOddsWithSettingsByEventID :many -SELECT o.id, - o.event_id, - o.market_type, - o.market_name, - o.market_category, - o.market_id, - o.number_of_outcomes, - o.default_is_active, - o.fetched_at, - o.expires_at, - cos.company_id, - COALESCE(cos.is_active, o.default_is_active) AS is_active, - COALESCE(cms.is_active, TRUE) AS is_market_active, - COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, - cos.updated_at -FROM odds_market o - LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id - AND cos.company_id = $2 - LEFT JOIN company_odd_market_settings cms ON o.market_id = cms.market_id - AND cms.company_id = $2 -WHERE event_id = $1 -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: DeleteOddsForEvent :exec -DELETE FROM odds_market -Where event_id = $1; --- name: DeleteAllCompanyOddsSetting :exec -DELETE FROM company_odd_settings -WHERE company_id = $1; --- name: DeleteCompanyOddsSettingByOddMarketID :exec -DELETE FROM company_odd_settings -WHERE company_id = $1 - AND odds_market_id = $2; --- name: UpdateGlobalOddsSetting :exec -UPDATE odds_market -SET default_is_active = COALESCE(sqlc.narg(default_is_active), default_is_active) -WHERE id = $1; \ No newline at end of file diff --git a/db/query/raffle.sql b/db/query/raffle.sql deleted file mode 100644 index 68318a6..0000000 --- a/db/query/raffle.sql +++ /dev/null @@ -1,89 +0,0 @@ --- name: CreateRaffle :one -INSERT INTO raffles (company_id, name, expires_at, ticket_limit, type) -VALUES ($1, $2, $3, $4, $5) -RETURNING *; - --- name: GetRafflesOfCompany :many -SELECT * FROM raffles WHERE company_id = $1; - --- name: DeleteRaffle :one -DELETE FROM raffles -WHERE id = $1 -RETURNING *; - --- name: UpdateRaffleTicketStatus :exec -UPDATE raffle_tickets -SET is_active = $1 -WHERE id = $2; - --- name: CreateRaffleTicket :one -INSERT INTO raffle_tickets (raffle_id, user_id) -VALUES ($1, $2) -RETURNING *; - --- name: GetUserRaffleTickets :many -SELECT - rt.id AS ticket_id, - rt.user_id, - r.name, - r.type, - r.expires_at, - r.status -FROM raffle_tickets rt -JOIN raffles r ON rt.raffle_id = r.id -WHERE rt.user_id = $1; - --- name: GetRaffleStanding :many -SELECT - u.id AS user_id, - rt.raffle_id, - u.first_name, - u.last_name, - u.phone_number, - u.email, - COUNT(*) AS ticket_count -FROM raffle_tickets rt -JOIN users u ON rt.user_id = u.id -WHERE rt.is_active = true - AND rt.raffle_id = $1 -GROUP BY u.id, rt.raffle_id, u.first_name, u.last_name, u.phone_number, u.email -ORDER BY ticket_count DESC -LIMIT $2; - --- name: CreateRaffleWinner :one -INSERT INTO raffle_winners (raffle_id, user_id, rank) -VALUES ($1, $2, $3) -RETURNING *; - --- name: SetRaffleComplete :exec -UPDATE raffles -SET status = 'completed' -WHERE id = $1; - --- name: AddSportRaffleFilter :one -INSERT INTO raffle_sport_filters (raffle_id, sport_id, league_id) -VALUES ($1, $2, $3) -RETURNING *; - --- name: CheckValidSportRaffleFilter :one -SELECT COUNT(*) > 0 AS exists -FROM raffle_sport_filters -WHERE raffle_id = $1 - AND sport_id = $2 - AND league_id = $3; - --- name: CheckSportRaffleHasFilter :one -SELECT EXISTS ( - SELECT 1 FROM raffle_sport_filters WHERE raffle_id = $1 -) AS has_filter; - --- name: GetRaffleTicketLimit :one -SELECT ticket_limit -FROM raffles -WHERE id = $1; - --- name: GetRaffleTicketCount :one -SELECT COUNT(*) -FROM raffle_tickets -WHERE raffle_id = $1 - AND user_id = $2; diff --git a/db/query/referal.sql b/db/query/referal.sql index 3dbe00e..a2712f0 100644 --- a/db/query/referal.sql +++ b/db/query/referal.sql @@ -1,51 +1,51 @@ --- name: CreateReferralCode :one -INSERT INTO referral_codes ( - referral_code, - referrer_id, - company_id, - number_of_referrals, - reward_amount - ) -VALUES ($1, $2, $3, $4, $5) -RETURNING *; --- name: CreateUserReferral :one -INSERT INTO user_referrals (referred_id, referral_code_id) -VALUES ($1, $2) -RETURNING *; --- name: GetReferralCodeByUser :many -SELECt * -FROM referral_codes -WHERE referrer_id = $1; --- name: GetReferralCode :one -SELECT * -FROM referral_codes -WHERE referral_code = $1; --- name: UpdateReferralCode :exec -UPDATE referral_codes -SET is_active = $2, - referral_code = $3, - number_of_referrals = $4, - reward_amount = $5, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1; --- name: GetReferralStats :one -SELECT COUNT(*) AS total_referrals, - COALESCE(SUM(reward_amount), 0)::bigint AS total_reward_earned -FROM user_referrals - JOIN referral_codes ON referral_codes.id = referral_code_id -WHERE referrer_id = $1 - AND company_id = $2; --- name: GetUserReferral :one -SELECT * -FROM user_referrals -WHERE referred_id = $1; --- name: GetUserReferralsByCode :many -SELECT user_referrals.* -FROM user_referrals - JOIN referral_codes ON referral_codes.id = referral_code_id -WHERE referral_code = $1; --- name: GetUserReferralsCount :one -SELECT COUNT(*) -FROM user_referrals - JOIN referral_codes ON referral_codes.id = referral_code_id -WHERE referrer_id = $1; \ No newline at end of file +-- -- name: CreateReferralCode :one +-- INSERT INTO referral_codes ( +-- referral_code, +-- referrer_id, +-- company_id, +-- number_of_referrals, +-- reward_amount +-- ) +-- VALUES ($1, $2, $3, $4, $5) +-- RETURNING *; +-- -- name: CreateUserReferral :one +-- INSERT INTO user_referrals (referred_id, referral_code_id) +-- VALUES ($1, $2) +-- RETURNING *; +-- -- name: GetReferralCodeByUser :many +-- SELECt * +-- FROM referral_codes +-- WHERE referrer_id = $1; +-- -- name: GetReferralCode :one +-- SELECT * +-- FROM referral_codes +-- WHERE referral_code = $1; +-- -- name: UpdateReferralCode :exec +-- UPDATE referral_codes +-- SET is_active = $2, +-- referral_code = $3, +-- number_of_referrals = $4, +-- reward_amount = $5, +-- updated_at = CURRENT_TIMESTAMP +-- WHERE id = $1; +-- -- name: GetReferralStats :one +-- SELECT COUNT(*) AS total_referrals, +-- COALESCE(SUM(reward_amount), 0)::bigint AS total_reward_earned +-- FROM user_referrals +-- JOIN referral_codes ON referral_codes.id = referral_code_id +-- WHERE referrer_id = $1 +-- AND company_id = $2; +-- -- name: GetUserReferral :one +-- SELECT * +-- FROM user_referrals +-- WHERE referred_id = $1; +-- -- name: GetUserReferralsByCode :many +-- SELECT user_referrals.* +-- FROM user_referrals +-- JOIN referral_codes ON referral_codes.id = referral_code_id +-- WHERE referral_code = $1; +-- -- name: GetUserReferralsCount :one +-- SELECT COUNT(*) +-- FROM user_referrals +-- JOIN referral_codes ON referral_codes.id = referral_code_id +-- WHERE referrer_id = $1; \ No newline at end of file diff --git a/db/query/result.sql b/db/query/result.sql deleted file mode 100644 index 9f7a1f6..0000000 --- a/db/query/result.sql +++ /dev/null @@ -1,53 +0,0 @@ --- name: CreateResult :one -INSERT INTO results ( - bet_outcome_id, - event_id, - odd_id, - market_id, - status, - score, - created_at, - updated_at -) VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP -) RETURNING *; - --- name: InsertResult :exec -INSERT INTO results ( - bet_outcome_id, - event_id, - odd_id, - market_id, - status, - score, - full_time_score, - half_time_score, - ss, - created_at, - updated_at -) VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP -); - --- name: GetResultByBetOutcomeID :one -SELECT * FROM results WHERE bet_outcome_id = $1; - --- name: GetPendingBetOutcomes :many -SELECT * FROM bet_outcomes WHERE status = 0 AND expires <= CURRENT_TIMESTAMP; diff --git a/db/query/result_log.sql b/db/query/result_log.sql deleted file mode 100644 index b20d71e..0000000 --- a/db/query/result_log.sql +++ /dev/null @@ -1,28 +0,0 @@ --- name: CreateResultLog :one -INSERT INTO result_log ( - status_not_finished_count, - status_not_finished_bets, - status_to_be_fixed_count, - status_to_be_fixed_bets, - status_postponed_count, - status_postponed_bets, - status_ended_count, - status_ended_bets, - status_removed_count, - status_removed_bets, - removed_count - ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) -RETURNING *; --- name: GetAllResultLog :many -SELECT * -FROM result_log -WHERE ( - created_at < sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at > sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ) -ORDER BY created_at DESC; diff --git a/db/query/settings.sql b/db/query/settings.sql index 44e4691..bcac88e 100644 --- a/db/query/settings.sql +++ b/db/query/settings.sql @@ -1,48 +1,48 @@ --- name: InsertGlobalSetting :exec -INSERT INTO global_settings (key, value) -VALUES ($1, $2) ON CONFLICT (key) DO -UPDATE -SET value = EXCLUDED.value; --- name: GetGlobalSettings :many -SELECT * -FROM global_settings; --- name: GetGlobalSetting :one -SELECT * -FROM global_settings -WHERE key = $1; --- name: UpdateGlobalSetting :exec -UPDATE global_settings -SET value = $2, - updated_at = CURRENT_TIMESTAMP -WHERE key = $1; --- name: InsertCompanySetting :exec -INSERT INTO company_settings (company_id, key, value) -VALUES ($1, $2, $3) ON CONFLICT (company_id, key) DO -UPDATE -SET value = EXCLUDED.value; --- name: GetAllCompanySettings :many -SELECT * -FROM company_settings; --- name: GetCompanySetting :many -SELECT * -FROM company_settings -WHERE company_id = $1; --- name: GetCompanySettingsByKey :many -SELECT * -FROM company_settings -WHERE key = $1; --- name: GetOverrideSettings :many -SELECT gs.key, - gs.created_at, - gs.updated_at, - COALESCE(cs.value, gs.value) AS value -FROM global_settings gs - LEFT JOIN company_settings cs ON cs.key = gs.key - AND cs.company_id = $1; --- name: DeleteCompanySetting :exec -DELETE FROM company_settings -WHERE company_id = $1 - AND key = $2; --- name: DeleteAllCompanySetting :exec -DELETE FROM company_settings -WHERE company_id = $1; \ No newline at end of file +-- -- name: InsertGlobalSetting :exec +-- INSERT INTO global_settings (key, value) +-- VALUES ($1, $2) ON CONFLICT (key) DO +-- UPDATE +-- SET value = EXCLUDED.value; +-- -- name: GetGlobalSettings :many +-- SELECT * +-- FROM global_settings; +-- -- name: GetGlobalSetting :one +-- SELECT * +-- FROM global_settings +-- WHERE key = $1; +-- -- name: UpdateGlobalSetting :exec +-- UPDATE global_settings +-- SET value = $2, +-- updated_at = CURRENT_TIMESTAMP +-- WHERE key = $1; +-- -- name: InsertCompanySetting :exec +-- INSERT INTO company_settings (company_id, key, value) +-- VALUES ($1, $2, $3) ON CONFLICT (company_id, key) DO +-- UPDATE +-- SET value = EXCLUDED.value; +-- -- name: GetAllCompanySettings :many +-- SELECT * +-- FROM company_settings; +-- -- name: GetCompanySetting :many +-- SELECT * +-- FROM company_settings +-- WHERE company_id = $1; +-- -- name: GetCompanySettingsByKey :many +-- SELECT * +-- FROM company_settings +-- WHERE key = $1; +-- -- name: GetOverrideSettings :many +-- SELECT gs.key, +-- gs.created_at, +-- gs.updated_at, +-- COALESCE(cs.value, gs.value) AS value +-- FROM global_settings gs +-- LEFT JOIN company_settings cs ON cs.key = gs.key +-- AND cs.company_id = $1; +-- -- name: DeleteCompanySetting :exec +-- DELETE FROM company_settings +-- WHERE company_id = $1 +-- AND key = $2; +-- -- name: DeleteAllCompanySetting :exec +-- DELETE FROM company_settings +-- WHERE company_id = $1; \ No newline at end of file diff --git a/db/query/shop_transactions.sql b/db/query/shop_transactions.sql index a9f003b..521ec3d 100644 --- a/db/query/shop_transactions.sql +++ b/db/query/shop_transactions.sql @@ -1,178 +1,74 @@ --- name: CreateShopTransaction :one -INSERT INTO shop_transactions ( - amount, - branch_id, - company_id, - user_id, - type, - full_name, - phone_number, - payment_option, - bank_code, - beneficiary_name, - account_name, - account_number, - reference_number - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13 - ) -RETURNING *; --- name: GetAllShopTransactions :many -SELECT * -FROM shop_transaction_detail -wHERE ( - branch_id = sqlc.narg('branch_id') - OR sqlc.narg('branch_id') IS NULL - ) - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ) - AND ( - full_name ILIKE '%' || sqlc.narg('query') || '%' - OR phone_number ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetShopTransactionByID :one -SELECT * -FROM shop_transaction_detail -WHERE id = $1; --- name: GetShopTransactionByBranch :many -SELECT * -FROM shop_transaction_detail -WHERE branch_id = $1; --- name: UpdateShopTransactionVerified :exec -UPDATE shop_transactions -SET verified = $2, - approved_by = $3, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1; --- name: CreateShopBet :one -INSERT INTO shop_bets ( - shop_transaction_id, - cashout_id, - bet_id, - number_of_outcomes - ) -VALUES ($1, $2, $3, $4) -RETURNING *; --- name: GetAllShopBets :many -SELECT * -FROM shop_bet_detail -WHERE ( - full_name ILIKE '%' || sqlc.narg('query') || '%' - OR phone_number ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - branch_id = sqlc.narg('branch_id') - OR sqlc.narg('branch_id') IS NULL - ) - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetShopBetByID :one -SELECT * -FROM shop_bet_detail -WHERE id = $1; --- name: GetShopBetByBetID :one -SELECT * -FROM shop_bet_detail -WHERE bet_id = $1; --- name: GetShopBetByCashoutID :one -SELECT * -FROM shop_bet_detail -WHERE cashout_id = $1; --- name: GetShopBetByShopTransactionID :one -SELECT * -FROM shop_bet_detail -WHERE shop_transaction_id = $1; --- name: UpdateShopBetCashOut :exec -UPDATE shop_bets -SET cashed_out = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1; --- name: UpdateShopBetCashoutID :exec -UPDATE shop_bets -SET cashout_id = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1; --- name: CreateShopDeposit :one -INSERT INTO shop_deposits ( - shop_transaction_id, - customer_id, - branch_wallet_id - ) -VALUES ($1, $2, $3) -RETURNING *; --- name: GetAllShopDeposit :many -SELECT * -FROM shop_deposit_detail -WHERE ( - full_name ILIKE '%' || sqlc.narg('query') || '%' - OR phone_number ILIKE '%' || sqlc.narg('query') || '%' - OR sqlc.narg('query') IS NULL - ) - AND ( - branch_id = sqlc.narg('branch_id') - OR sqlc.narg('branch_id') IS NULL - ) - AND ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ) - AND ( - created_at > sqlc.narg('created_before') - OR sqlc.narg('created_before') IS NULL - ) - AND ( - created_at < sqlc.narg('created_after') - OR sqlc.narg('created_after') IS NULL - ); --- name: GetShopDepositByID :one -SELECT * -FROM shop_deposit_detail -WHERE id = $1; --- name: GetShopDepositByShopTransactionID :one -SELECT * -FROM shop_deposit_detail -WHERE shop_transaction_id = $1; --- name: UpdateShopDepositTransferID :exec -UPDATE shop_deposits -SET wallet_transfer_id = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1; \ No newline at end of file +-- -- name: CreateShopTransaction :one +-- INSERT INTO shop_transactions ( +-- amount, +-- branch_id, +-- company_id, +-- user_id, +-- type, +-- full_name, +-- phone_number, +-- payment_option, +-- bank_code, +-- beneficiary_name, +-- account_name, +-- account_number, +-- reference_number +-- ) +-- VALUES ( +-- $1, +-- $2, +-- $3, +-- $4, +-- $5, +-- $6, +-- $7, +-- $8, +-- $9, +-- $10, +-- $11, +-- $12, +-- $13 +-- ) +-- RETURNING *; +-- -- name: GetAllShopTransactions :many +-- SELECT * +-- FROM shop_transaction_detail +-- wHERE ( +-- branch_id = sqlc.narg('branch_id') +-- OR sqlc.narg('branch_id') IS NULL +-- ) +-- AND ( +-- company_id = sqlc.narg('company_id') +-- OR sqlc.narg('company_id') IS NULL +-- ) +-- AND ( +-- user_id = sqlc.narg('user_id') +-- OR sqlc.narg('user_id') IS NULL +-- ) +-- AND ( +-- full_name ILIKE '%' || sqlc.narg('query') || '%' +-- OR phone_number ILIKE '%' || sqlc.narg('query') || '%' +-- OR sqlc.narg('query') IS NULL +-- ) +-- AND ( +-- created_at > sqlc.narg('created_before') +-- OR sqlc.narg('created_before') IS NULL +-- ) +-- AND ( +-- created_at < sqlc.narg('created_after') +-- OR sqlc.narg('created_after') IS NULL +-- ); +-- -- name: GetShopTransactionByID :one +-- SELECT * +-- FROM shop_transaction_detail +-- WHERE id = $1; +-- -- name: GetShopTransactionByBranch :many +-- SELECT * +-- FROM shop_transaction_detail +-- WHERE branch_id = $1; +-- -- name: UpdateShopTransactionVerified :exec +-- UPDATE shop_transactions +-- SET verified = $2, +-- approved_by = $3, +-- updated_at = CURRENT_TIMESTAMP +-- WHERE id = $1; \ No newline at end of file diff --git a/db/query/ticket.sql b/db/query/ticket.sql deleted file mode 100644 index b93da70..0000000 --- a/db/query/ticket.sql +++ /dev/null @@ -1,70 +0,0 @@ --- name: CreateTicket :one -INSERT INTO tickets (amount, total_odds, ip, company_id) -VALUES ($1, $2, $3, $4) -RETURNING *; --- name: CreateTicketOutcome :copyfrom -INSERT INTO ticket_outcomes ( - ticket_id, - event_id, - odd_id, - home_team_name, - away_team_name, - market_id, - market_name, - odd, - odd_name, - odd_header, - odd_handicap, - expires - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12 - ); --- name: GetAllTickets :many -SELECT * -FROM ticket_with_outcomes -WHERE ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL - ); --- name: GetTicketByID :one -SELECT * -FROM ticket_with_outcomes -WHERE id = $1; --- name: GetTicketOutcome :many -SELECT * -FROM ticket_outcomes -WHERE ticket_id = $1; --- name: CountTicketByIP :one -SELECT count(id) -FROM tickets -WHERE IP = $1; --- name: UpdateTicketOutcomeStatus :exec -UPDATE ticket_outcomes -SET status = $1 -WHERE id = $2; --- name: DeleteTicket :exec -DELETE FROM tickets -WHERE id = $1; --- name: DeleteOldTickets :exec -Delete from tickets -where created_at < now() - interval '1 day'; --- name: DeleteTicketOutcome :exec -Delete from ticket_outcomes -where ticket_id = $1; --- name: GetAllTicketsInRange :one -SELECT COUNT(*) as total_tickets, - COALESCE(SUM(amount), 0) as total_amount -FROM tickets -WHERE created_at BETWEEN $1 AND $2; \ No newline at end of file diff --git a/db/query/transfer.sql b/db/query/transfer.sql deleted file mode 100644 index bd44f37..0000000 --- a/db/query/transfer.sql +++ /dev/null @@ -1,63 +0,0 @@ --- name: CreateTransfer :one -INSERT INTO wallet_transfer ( - amount, - message, - type, - receiver_wallet_id, - sender_wallet_id, - cashier_id, - verified, - reference_number, - ext_reference_number, - session_id, - status, - payment_method - ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) -RETURNING *; --- name: GetAllTransfers :many -SELECT * -FROM wallet_transfer_details; --- name: GetTransfersByWallet :many -SELECT * -FROM wallet_transfer_details -WHERE receiver_wallet_id = $1 - OR sender_wallet_id = $1; --- name: GetTransferByID :one -SELECT * -FROM wallet_transfer_details -WHERE id = $1; --- name: GetTransferByReference :one -SELECT * -FROM wallet_transfer_details -WHERE reference_number = $1; --- name: GetTransferStats :one -SELECT COUNT(*) AS total_transfers, COUNT(*) FILTER ( - WHERE type = 'deposit' - ) AS total_deposits, - COUNT(*) FILTER ( - WHERE type = 'withdraw' - ) AS total_withdraw, - COUNT(*) FILTER ( - WHERE type = 'wallet' - ) AS total_wallet_to_wallet -FROM wallet_transfer -WHERE sender_wallet_id = $1 - OR receiver_wallet_id = $1; --- name: UpdateTransferVerification :exec -UPDATE wallet_transfer -SET verified = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2; --- name: UpdateTransferStatus :exec -UPDATE wallet_transfer -SET status = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2; --- name: GetWalletTransactionsInRange :many -SELECT type, - COUNT(*) as count, - SUM(amount) as total_amount -FROM wallet_transfer -WHERE created_at BETWEEN $1 AND $2 -GROUP BY type; \ No newline at end of file diff --git a/db/query/user.sql b/db/query/user.sql index 549d4fa..6224842 100644 --- a/db/query/user.sql +++ b/db/query/user.sql @@ -2,16 +2,22 @@ INSERT INTO users ( first_name, last_name, + nick_name, email, phone_number, role, password, + age, + education_level, + country, + region, email_verified, phone_verified, - created_at, - updated_at, suspended, - company_id + suspended_at, + organization_id, + created_at, + updated_at ) VALUES ( $1, @@ -25,20 +31,32 @@ VALUES ( $9, $10, $11, - $12 + $12, + $13, + $14, + $15, + $16, + $17, + $18 ) RETURNING id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, - company_id; + suspended_at, + organization_id; -- name: GetUserByID :one SELECT * FROM users @@ -47,23 +65,28 @@ WHERE id = $1; SELECT id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, suspended_at, - company_id + organization_id FROM users wHERE ( role = $1 OR $1 IS NULL ) AND ( - company_id = $2 + organization_id = $2 OR $2 IS NULL ) AND ( @@ -89,27 +112,32 @@ wHERE ( OR $1 IS NULL ) AND ( - company_id = $2 + organization_id = $2 OR $2 IS NULL ); -- name: SearchUserByNameOrPhone :many SELECT id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, suspended_at, - company_id + organization_id FROM users WHERE ( - company_id = sqlc.narg('company_id') - OR sqlc.narg('company_id') IS NULL + organization_id = sqlc.narg('organization_id') + OR sqlc.narg('organization_id') IS NULL ) AND ( first_name ILIKE '%' || $1 || '%' @@ -127,9 +155,9 @@ SET first_name = $1, suspended = $3, updated_at = CURRENT_TIMESTAMP WHERE id = $4; --- name: UpdateUserCompany :exec +-- name: UpdateUserOrganization :exec UPDATE users -SET company_id = $1 +SET organization_id = $1 WHERE id = $2; -- name: SuspendUser :exec UPDATE users @@ -146,60 +174,69 @@ SELECT EXISTS ( FROM users WHERE users.phone_number = $1 AND users.phone_number IS NOT NULL - AND users.company_id = $2 + AND users.organization_id = $2 ) AS phone_exists, EXISTS ( SELECT 1 FROM users WHERE users.email = $3 AND users.email IS NOT NULL - AND users.company_id = $2 + AND users.organization_id = $2 ) AS email_exists; -- name: GetUserByEmail :one SELECT id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, suspended_at, - company_id + organization_id FROM users WHERE email = $1 - AND company_id = $2; + AND organization_id = $2; -- name: GetUserByPhone :one SELECT id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, suspended_at, - company_id + organization_id FROM users WHERE phone_number = $1 - AND company_id = $2; + AND organization_id = $2; -- name: UpdatePassword :exec UPDATE users SET password = $1, updated_at = $4 WHERE ( - email = $2 - OR phone_number = $3 - AND company_id = $5 + (email = $2 OR phone_number = $3) + AND organization_id = $5 ); --- name: GetAdminByCompanyID :one +-- name: GetOwnerByOrganizationID :one SELECT users.* -FROM companies - JOIN users ON companies.admin_id = users.id -where companies.id = $1; \ No newline at end of file +FROM organizations + JOIN users ON organizations.owner_id = users.id +WHERE organizations.id = $1; \ No newline at end of file diff --git a/db/query/virtual_games.sql b/db/query/virtual_games.sql deleted file mode 100644 index 7e4ff4d..0000000 --- a/db/query/virtual_games.sql +++ /dev/null @@ -1,386 +0,0 @@ --- name: CreateVirtualGameProvider :one -INSERT INTO virtual_game_providers ( - provider_id, - provider_name, - logo_dark, - logo_light, - enabled - ) -VALUES ($1, $2, $3, $4, $5) -RETURNING id, - provider_id, - provider_name, - logo_dark, - logo_light, - enabled, - created_at, - updated_at; --- name: DeleteVirtualGameProvider :exec -DELETE FROM virtual_game_providers -WHERE provider_id = $1; --- name: DeleteAllVirtualGameProviders :exec -DELETE FROM virtual_game_providers; --- name: GetVirtualGameProviderByID :one -SELECT id, - provider_id, - provider_name, - logo_dark, - logo_light, - enabled, - created_at, - updated_at -FROM virtual_game_providers -WHERE provider_id = $1; --- name: ListVirtualGameProviders :many -SELECT id, - provider_id, - provider_name, - logo_dark, - logo_light, - enabled, - created_at, - updated_at -FROM virtual_game_providers -ORDER BY created_at DESC -LIMIT $1 OFFSET $2; --- name: CountVirtualGameProviders :one -SELECT COUNT(*) AS total -FROM virtual_game_providers; --- name: UpdateVirtualGameProviderEnabled :one -UPDATE virtual_game_providers -SET enabled = $2, - updated_at = CURRENT_TIMESTAMP -WHERE provider_id = $1 -RETURNING id, - provider_id, - provider_name, - logo_dark, - logo_light, - enabled, - created_at, - updated_at; --- name: CreateVirtualGameSession :one -INSERT INTO virtual_game_sessions ( - user_id, - game_id, - session_token -) -VALUES ($1, $2, $3) -RETURNING - id, - user_id, - game_id, - session_token, - created_at, - updated_at; - --- name: GetVirtualGameSessionByUserID :one -SELECT - id, - user_id, - game_id, - session_token, - created_at, - updated_at -FROM virtual_game_sessions -WHERE user_id = $1; - --- name: GetVirtualGameSessionByToken :one -SELECT id, - user_id, - game_id, - session_token, - created_at, - updated_at -FROM virtual_game_sessions -WHERE session_token = $1; - --- name: CreateVirtualGameTransaction :one -INSERT INTO virtual_game_transactions ( - -- session_id, - user_id, - company_id, - provider, - wallet_id, - transaction_type, - amount, - currency, - external_transaction_id, - status - ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) -RETURNING id, - -- session_id, - user_id, - company_id, - provider, - wallet_id, - transaction_type, - amount, - currency, - external_transaction_id, - status, - created_at, - updated_at; --- name: CreateVirtualGameHistory :one -INSERT INTO virtual_game_histories ( - -- session_id, - user_id, - company_id, - provider, - wallet_id, - game_id, - transaction_type, - amount, - currency, - external_transaction_id, - reference_transaction_id, - status - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11 - -- $12 - ) -RETURNING id, - -- session_id, - user_id, - company_id, - provider, - wallet_id, - game_id, - transaction_type, - amount, - currency, - external_transaction_id, - reference_transaction_id, - status, - created_at, - updated_at; - --- name: GetVirtualGameTransactionByExternalID :one -SELECT id, - -- session_id, - user_id, - wallet_id, - transaction_type, - amount, - currency, - external_transaction_id, - status, - created_at, - updated_at -FROM virtual_game_transactions -WHERE external_transaction_id = $1; - --- name: UpdateVirtualGameTransactionStatus :exec -UPDATE virtual_game_transactions -SET status = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1; --- name: GetVirtualGameSummaryInRange :many -SELECT c.name AS company_name, - vg.name AS game_name, - COUNT(vgt.id) AS number_of_bets, - COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum -FROM virtual_game_transactions vgt - -- JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id - JOIN virtual_games vg ON vgs.game_id = vg.id - JOIN companies c ON vgt.company_id = c.id -WHERE vgt.transaction_type = 'BET' - AND vgt.created_at BETWEEN $1 AND $2 -GROUP BY c.name, - vg.name; - --- name: AddFavoriteGame :exec -INSERT INTO virtual_game_favourites (user_id, game_id, provider_id, created_at) -VALUES ($1, $2, $3, NOW()) -ON CONFLICT (game_id, user_id) DO NOTHING; - --- name: RemoveFavoriteGame :exec -DELETE FROM virtual_game_favourites -WHERE user_id = $1 AND game_id = $2 AND provider_id = $3; - --- name: GetUserFavoriteGamesPaginated :many -SELECT - vg.* -FROM virtual_games vg -JOIN virtual_game_favourites vf - ON vf.game_id = vg.id -WHERE - vf.user_id = $1 - AND ($2::varchar IS NULL OR vf.provider_id = $2) -ORDER BY vf.created_at DESC -LIMIT $3 OFFSET $4; - --- name: CreateVirtualGame :one -INSERT INTO virtual_games ( - game_id, - provider_id, - name, - category, - device_type, - volatility, - rtp, - has_demo, - has_free_bets, - bets, - thumbnail, - status - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12 - ) -RETURNING id, - game_id, - provider_id, - name, - category, - device_type, - volatility, - rtp, - has_demo, - has_free_bets, - bets, - thumbnail, - status, - created_at, - updated_at; --- name: GetAllVirtualGames :many -SELECT vg.id, - vg.game_id, - vg.provider_id, - vp.provider_name, - vg.name, - vg.category, - vg.device_type, - vg.volatility, - vg.rtp, - vg.has_demo, - vg.has_free_bets, - vg.bets, - vg.thumbnail, - vg.status, - vg.created_at, - vg.updated_at -FROM virtual_games vg - JOIN virtual_game_providers vp ON vg.provider_id = vp.provider_id -WHERE ( - vg.category = sqlc.narg('category') - OR sqlc.narg('category') IS NULL - ) - AND ( - name ILIKE '%' || sqlc.narg('name') || '%' - OR sqlc.narg('name') IS NULL - ) - AND ( - vg.provider_id = sqlc.narg('provider_id') - OR sqlc.narg('provider_id') IS NULL - ) -ORDER BY vg.created_at DESC -LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); --- name: DeleteAllVirtualGames :exec -DELETE FROM virtual_games; - - --- name: CreateVirtualGameProviderReport :one -INSERT INTO virtual_game_provider_reports ( - provider_id, - report_date, - total_games_played, - total_bets, - total_payouts, - total_players, - report_type, - created_at, - updated_at -) VALUES ( - $1, $2, $3, $4, $5, $6, COALESCE($7, 'daily'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP -) -ON CONFLICT (provider_id, report_date, report_type) DO UPDATE -SET - total_games_played = EXCLUDED.total_games_played, - total_bets = EXCLUDED.total_bets, - total_payouts = EXCLUDED.total_payouts, - total_players = EXCLUDED.total_players, - updated_at = CURRENT_TIMESTAMP -RETURNING *; - - --- name: CreateVirtualGameReport :one -INSERT INTO virtual_game_reports ( - game_id, - provider_id, - report_date, - total_rounds, - total_bets, - total_payouts, - total_players, - report_type, - created_at, - updated_at -) VALUES ( - $1, $2, $3, $4, $5, $6, $7, COALESCE($8, 'daily'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP -) -ON CONFLICT (game_id, report_date, report_type) DO UPDATE -SET - total_rounds = EXCLUDED.total_rounds, - total_bets = EXCLUDED.total_bets, - total_payouts = EXCLUDED.total_payouts, - total_players = EXCLUDED.total_players, - updated_at = CURRENT_TIMESTAMP -RETURNING *; - --- name: GetVirtualGameProviderReportByProviderAndDate :one -SELECT * -FROM virtual_game_provider_reports -WHERE provider_id = $1 - AND report_date = $2 - AND report_type = $3; - --- name: UpdateVirtualGameProviderReportByDate :exec -UPDATE virtual_game_provider_reports -SET - total_games_played = total_games_played + $4, - total_bets = total_bets + $5, - total_payouts = total_payouts + $6, - total_players = total_players + $7, - updated_at = CURRENT_TIMESTAMP -WHERE - provider_id = $1 - AND report_date = $2 - AND report_type = $3; - --- name: ListVirtualGameProviderReportsByGamesPlayedAsc :many -SELECT * -FROM virtual_game_provider_reports -ORDER BY total_games_played ASC; - --- name: ListVirtualGameProviderReportsByGamesPlayedDesc :many -SELECT * -FROM virtual_game_provider_reports -ORDER BY total_games_played DESC; - - - - diff --git a/db/query/virtual_report.sql b/db/query/virtual_report.sql deleted file mode 100644 index 0bd4868..0000000 --- a/db/query/virtual_report.sql +++ /dev/null @@ -1,139 +0,0 @@ --- name: CreateFinancialReport :one -INSERT INTO virtual_game_financial_reports ( - game_id, provider_id, report_date, report_type, - total_bets, total_wins, created_at -) VALUES ($1, $2, $3, $4, $5, $6, NOW()) -RETURNING *; - --- name: UpsertFinancialReport :one -INSERT INTO virtual_game_financial_reports ( - game_id, provider_id, report_date, report_type, - total_bets, total_wins, created_at, updated_at -) -VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW()) -ON CONFLICT (game_id, provider_id, report_date, report_type) -DO UPDATE SET - total_bets = EXCLUDED.total_bets, - total_wins = EXCLUDED.total_wins, - updated_at = NOW() -RETURNING *; - --- name: GetFinancialReportByID :one -SELECT * FROM virtual_game_financial_reports -WHERE id = $1; - --- name: GetFinancialReportsForGame :many -SELECT * FROM virtual_game_financial_reports -WHERE game_id = $1 - AND provider_id = $2 - AND report_date BETWEEN $3 AND $4 -ORDER BY report_date; - --- name: GetDailyFinancialReports :many -SELECT * FROM virtual_game_financial_reports -WHERE report_date = $1 - AND report_type = 'daily'; - --- name: DeleteFinancialReport :exec -DELETE FROM virtual_game_financial_reports -WHERE id = $1; - --- name: CreateCompanyReport :one -INSERT INTO virtual_game_company_reports ( - company_id, provider_id, - report_date, report_type, - total_bet_amount, total_win_amount, created_at -) -VALUES ($1, $2, $3, $4, $5, $6, NOW()) -RETURNING *; - --- name: UpsertCompanyReport :one -INSERT INTO virtual_game_company_reports ( - company_id, provider_id, - report_date, report_type, - total_bet_amount, total_win_amount, - created_at, updated_at -) -VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW()) -ON CONFLICT (company_id, provider_id, report_date, report_type) -DO UPDATE SET - total_bet_amount = EXCLUDED.total_bet_amount, - total_win_amount = EXCLUDED.total_win_amount, - updated_at = NOW() -RETURNING *; - --- name: GetCompanyReportByID :one -SELECT * FROM virtual_game_company_reports -WHERE id = $1; - --- name: GetCompanyReportsInRange :many -SELECT * FROM virtual_game_company_reports -WHERE company_id = $1 - AND provider_id = $2 - AND report_date BETWEEN $3 AND $4 -ORDER BY report_date; - --- name: GetCompanyProfitTrend :many -SELECT report_date, SUM(net_profit) AS total_profit -FROM virtual_game_company_reports -WHERE company_id = $1 - AND provider_id = $2 - AND report_date BETWEEN $3 AND $4 -GROUP BY report_date -ORDER BY report_date; - --- name: CreatePlayerActivityReport :one -INSERT INTO virtual_game_player_activity_reports ( - user_id, report_date, report_type, - total_deposits, total_withdrawals, - total_bet_amount, total_win_amount, - rounds_played, created_at -) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW()) -RETURNING *; - --- name: UpsertPlayerActivityReport :one -INSERT INTO virtual_game_player_activity_reports ( - user_id, report_date, report_type, - total_deposits, total_withdrawals, - total_bet_amount, total_win_amount, - rounds_played, created_at, updated_at -) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW()) -ON CONFLICT (user_id, report_date, report_type) -DO UPDATE SET - total_deposits = EXCLUDED.total_deposits, - total_withdrawals = EXCLUDED.total_withdrawals, - total_bet_amount = EXCLUDED.total_bet_amount, - total_win_amount = EXCLUDED.total_win_amount, - rounds_played = EXCLUDED.rounds_played, - updated_at = NOW() -RETURNING *; - --- name: GetPlayerActivityByID :one -SELECT * FROM virtual_game_player_activity_reports -WHERE id = $1; - --- name: GetPlayerActivityByDate :one -SELECT * FROM virtual_game_player_activity_reports -WHERE user_id = $1 - AND report_date = $2 - AND report_type = $3; - --- name: GetPlayerActivityRange :many -SELECT * FROM virtual_game_player_activity_reports -WHERE user_id = $1 - AND report_date BETWEEN $2 AND $3 -ORDER BY report_date; - --- name: GetTopPlayersByNetResult :many -SELECT user_id, SUM(net_result) AS total_net -FROM virtual_game_player_activity_reports -WHERE report_date BETWEEN $1 AND $2 -GROUP BY user_id -ORDER BY total_net DESC -LIMIT $3; - --- name: DeletePlayerActivityReport :exec -DELETE FROM virtual_game_player_activity_reports -WHERE id = $1; diff --git a/db/query/wallet.sql b/db/query/wallet.sql deleted file mode 100644 index a6c9998..0000000 --- a/db/query/wallet.sql +++ /dev/null @@ -1,75 +0,0 @@ --- name: CreateWallet :one -INSERT INTO wallets ( - is_withdraw, - is_bettable, - is_transferable, - user_id, - type - ) -VALUES ($1, $2, $3, $4, $5) -RETURNING *; --- name: CreateCustomerWallet :one -INSERT INTO customer_wallets ( - customer_id, - regular_wallet_id, - static_wallet_id - ) -VALUES ($1, $2, $3) -RETURNING *; --- name: GetAllWallets :many -SELECT * -FROM wallets; --- name: GetWalletByID :one -SELECT * -FROM wallets -WHERE id = $1; --- name: GetWalletByUserID :many -SELECT * -FROM wallets -WHERE user_id = $1; --- name: GetAllCustomerWallet :many -SELECT * -FROM customer_wallet_details; --- name: GetCustomerWallet :one -SELECT * -FROM customer_wallet_details -WHERE customer_id = $1; --- name: GetAllBranchWallets :many -SELECT wallets.id, - wallets.balance, - wallets.is_active, - wallets.updated_at, - wallets.created_at, - branches.name, - branches.location, - branches.branch_manager_id, - branches.company_id, - branches.is_self_owned -FROM branches - JOIN wallets ON branches.wallet_id = wallets.id; --- name: UpdateBalance :exec -UPDATE wallets -SET balance = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2; --- name: UpdateWalletActive :exec -UPDATE wallets -SET is_active = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2; --- name: GetCompanyByWalletID :one -SELECT id, name, admin_id, wallet_id -FROM companies -WHERE wallet_id = $1 -LIMIT 1; --- name: GetBranchByWalletID :one -SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at -FROM branches -WHERE wallet_id = $1 -LIMIT 1; - --- -- name: GetCustomerByWalletID :one --- SELECT id, first_name, last_name, email, phone_number,email_verified,phone_verified,company_id,suspended --- FROM users --- WHERE wallet_id = $1 --- LIMIT 1; \ No newline at end of file diff --git a/db/query/wallet_stats.sql b/db/query/wallet_stats.sql deleted file mode 100644 index 361ac45..0000000 --- a/db/query/wallet_stats.sql +++ /dev/null @@ -1,130 +0,0 @@ --- name: UpdateWalletStats :exec -WITH all_transfers AS ( - SELECT sender_wallet_id AS wallet_id, - amount, - type - FROM wallet_transfer - WHERE sender_wallet_id IS NOT NULL - UNION ALL - SELECT receiver_wallet_id AS wallet_id, - amount, - type - FROM wallet_transfer - WHERE receiver_wallet_id IS NOT NULL -), -transfer_stats AS ( - SELECT wallet_id, - COUNT(*) AS number_of_transactions, - COALESCE(SUM(amount), 0) AS total_transactions, - COUNT(*) FILTER ( - WHERE type = 'deposit' - ) AS number_of_deposits, - COALESCE( - SUM( - CASE - WHEN type = 'deposit' THEN amount - ELSE 0 - END - ), - 0 - ) AS total_deposits_amount, - COUNT(*) FILTER ( - WHERE type = 'withdraw' - ) AS number_of_withdraws, - COALESCE( - SUM( - CASE - WHEN type = 'withdraw' THEN amount - ELSE 0 - END - ), - 0 - ) AS total_withdraws_amount, - COUNT(*) FILTER ( - WHERE type = 'wallet' - ) AS number_of_transfers, - COALESCE( - SUM( - CASE - WHEN type = 'wallet' THEN amount - ELSE 0 - END - ), - 0 - ) AS total_transfers_amount - FROM all_transfers - GROUP BY wallet_id -) -INSERT INTO wallet_stats( - wallet_id, - wallet_user_id, - wallet_user_first_name, - wallet_user_last_name, - wallet_type, - interval_start, - number_of_transactions, - total_transactions, - number_of_deposits, - total_deposits_amount, - number_of_withdraws, - total_withdraws_amount, - number_of_transfers, - total_transfers_amount, - updated_at - ) -SELECT w.id AS wallet_id, - w.user_id AS wallet_user_id, - u.first_name AS wallet_user_first_name, - u.last_name AS wallet_user_last_name, - w.type AS wallet_type, - DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start, - COALESCE(ts.number_of_transactions, 0) AS number_of_transactions, - COALESCE(ts.total_transactions, 0) AS total_transactions, - COALESCE(ts.number_of_deposits, 0) AS number_of_deposits, - COALESCE(ts.total_deposits_amount, 0) AS total_deposits_amount, - COALESCE(ts.number_of_withdraws, 0) AS number_of_withdraws, - COALESCE(ts.total_withdraws_amount, 0) AS total_withdraws_amount, - COALESCE(ts.number_of_transfers, 0) AS number_of_transfers, - COALESCE(ts.total_transfers_amount, 0) AS total_transfers_amount, - NOW() AS updated_at -FROM wallets w - LEFT JOIN users u ON u.id = w.user_id - LEFT JOIN transfer_stats ts ON ts.wallet_id = w.id ON CONFLICT (wallet_id, interval_start) DO -UPDATE -SET number_of_transactions = EXCLUDED.number_of_transactions, - total_transactions = EXCLUDED.total_transactions, - number_of_deposits = EXCLUDED.number_of_deposits, - total_deposits_amount = EXCLUDED.total_deposits_amount, - number_of_withdraws = EXCLUDED.number_of_withdraws, - total_withdraws_amount = EXCLUDED.total_withdraws_amount, - number_of_transfers = EXCLUDED.number_of_transfers, - total_transfers_amount = EXCLUDED.total_transfers_amount, - updated_at = EXCLUDED.updated_at; --- name: GetWalletStatsByID :many -SELECT * -FROM wallet_stats -WHERE wallet_id = $1 -ORDER BY interval_start DESC; --- name: GetWalletStats :many -SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start, - wallet_stats.wallet_id, - wallet_stats.wallet_user_id, - wallet_stats.wallet_user_first_name, - wallet_stats.wallet_user_last_name, - wallet_stats.wallet_type, - wallet_stats.number_of_transactions, - wallet_stats.total_transactions, - wallet_stats.number_of_deposits, - wallet_stats.total_deposits_amount, - wallet_stats.number_of_withdraws, - wallet_stats.total_withdraws_amount, - wallet_stats.number_of_transfers, - wallet_stats.total_transfers_amount, - wallet_stats.updated_at -FROM wallet_stats -WHERE ( - wallet_stats.wallet_user_id = sqlc.narg('user_id') - OR sqlc.narg('user_id') IS NULL - ) -GROUP BY interval_start -ORDER BY interval_start DESC; \ No newline at end of file diff --git a/db/scripts/fix_autoincrement_desync.sql b/db/scripts/fix_autoincrement_desync.sql index 835e10e..90518e1 100644 --- a/db/scripts/fix_autoincrement_desync.sql +++ b/db/scripts/fix_autoincrement_desync.sql @@ -1,31 +1,45 @@ --- For each table with an id sequence +-- ====================================================== +-- Reset sequences for LMS tables +-- ====================================================== + SELECT setval( - pg_get_serial_sequence('users', 'id'), - COALESCE(MAX(id), 1) - ) + pg_get_serial_sequence('users', 'id'), + COALESCE(MAX(id), 1) +) FROM users; + SELECT setval( - pg_get_serial_sequence('wallets', 'id'), - COALESCE(MAX(id), 1) - ) -FROM wallets; + pg_get_serial_sequence('refresh_tokens', 'id'), + COALESCE(MAX(id), 1) +) +FROM refresh_tokens; + SELECT setval( - pg_get_serial_sequence('customer_wallets', 'id'), - COALESCE(MAX(id), 1) - ) -FROM customer_wallets; + pg_get_serial_sequence('otps', 'id'), + COALESCE(MAX(id), 1) +) +FROM otps; + SELECT setval( - pg_get_serial_sequence('companies', 'id'), - COALESCE(MAX(id), 1) - ) -FROM companies; + pg_get_serial_sequence('notifications', 'id'), + COALESCE(MAX(id), 1) +) +FROM notifications; + SELECT setval( - pg_get_serial_sequence('branches', 'id'), - COALESCE(MAX(id), 1) - ) -FROM branches; + pg_get_serial_sequence('referral_codes', 'id'), + COALESCE(MAX(id), 1) +) +FROM referral_codes; + SELECT setval( - pg_get_serial_sequence('supported_operations', 'id'), - COALESCE(MAX(id), 1) - ) -FROM supported_operations; \ No newline at end of file + pg_get_serial_sequence('user_referrals', 'id'), + COALESCE(MAX(id), 1) +) +FROM user_referrals; + +SELECT setval( + pg_get_serial_sequence('report_requests', 'id'), + COALESCE(MAX(id), 1) +) +FROM report_requests; diff --git a/docker-compose.yml b/docker-compose.yml index 01d5f6f..0563fda 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: postgres: - container_name: fortunebet-backend-postgres-1 + container_name: yimaru-backend-postgres-1 image: postgres:16-alpine ports: - "5422:5432" @@ -22,7 +22,7 @@ services: - ./exports:/exports mongo: - container_name: fortunebet-mongo + container_name: yimaru-mongo image: mongo:7.0.11 restart: always ports: diff --git a/docs/docs.go b/docs/docs.go index 7a50f0d..1e91cf7 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -24,52 +24,6 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/account": { - "post": { - "description": "Callback endpoint for Atlas game server to fetch player balance", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Atlas Get User Data callback", - "parameters": [ - { - "description": "Get user data input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.AtlasGetUserDataRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.AtlasGetUserDataResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/admin": { "get": { "description": "Get all Admins", @@ -310,595 +264,6 @@ const docTemplate = `{ } } }, - "/api/v1/alea-games/launch": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Generates an authenticated launch URL for Alea Play virtual games", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Alea Virtual Games" - ], - "summary": "Launch an Alea Play virtual game", - "parameters": [ - { - "type": "string", - "description": "Game identifier (e.g., 'aviator', 'plinko')", - "name": "game_id", - "in": "query", - "required": true - }, - { - "enum": [ - "USD", - "EUR", - "GBP" - ], - "type": "string", - "default": "USD", - "description": "Currency code (ISO 4217)", - "name": "currency", - "in": "query" - }, - { - "enum": [ - "real", - "demo" - ], - "type": "string", - "default": "real", - "description": "Game mode", - "name": "mode", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Returns authenticated game launch URL", - "schema": { - "type": "object", - "additionalProperties": { - "allOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "launch_url": { - "type": "string" - } - } - } - ] - } - } - } - } - } - }, - "/api/v1/arifpay/b2c-webhook": { - "post": { - "description": "Handles webhook notifications from Arifpay for B2C transfers and updates transfer + wallet status.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Handle Arifpay B2C Webhook", - "parameters": [ - { - "description": "Arifpay webhook payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.WebhookRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/b2c/transfer": { - "post": { - "description": "Initiates a B2C transfer using Telebirr, CBE, or MPESA depending on the \"type\" query parameter", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Execute B2C Transfer", - "parameters": [ - { - "type": "string", - "description": "Transfer type (telebirr, cbe, mpesa)", - "name": "type", - "in": "query", - "required": true - }, - { - "description": "Transfer request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CheckoutSessionClientRequest" - } - } - ], - "responses": { - "200": { - "description": "message: transfer executed successfully", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "error: invalid request or unsupported transfer type", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: internal server error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/api/v1/arifpay/c2b-webhook": { - "post": { - "description": "Handles webhook notifications from Arifpay for C2B transfers and updates transfer + wallet status.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Handle Arifpay C2B Webhook", - "parameters": [ - { - "description": "Arifpay webhook payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.WebhookRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/checkout": { - "post": { - "description": "Creates a payment session using Arifpay and returns a redirect URL.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Create Arifpay Checkout Session", - "parameters": [ - { - "description": "Checkout session request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CheckoutSessionClientRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/checkout/cancel/{sessionId}": { - "post": { - "description": "Cancels a payment session using Arifpay before completion.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Cancel Arifpay Checkout Session", - "parameters": [ - { - "type": "string", - "description": "Checkout session ID", - "name": "sessionId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/payment-methods": { - "get": { - "description": "Returns all payment method IDs and names for Arifpay", - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "List Arifpay Payment Methods", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ARIFPAYPaymentMethod" - } - } - } - } - } - }, - "/api/v1/arifpay/session-id/verify-transaction/{session_id}": { - "get": { - "description": "Verifies an Arifpay transaction using a session ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Verify Arifpay Transaction by Session ID", - "parameters": [ - { - "type": "string", - "description": "Arifpay Session ID", - "name": "session_id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/transaction-id/verify-transaction": { - "post": { - "description": "Verifies a transaction using transaction ID and payment type", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Verify Arifpay Transaction", - "parameters": [ - { - "description": "Transaction verification payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ArifpayVerifyByTransactionIDRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/atlas/freespin": { - "post": { - "description": "Sends a request to Atlas to create free spins/bets for a given player", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Create free spins for a player", - "parameters": [ - { - "description": "Free spin input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FreeSpinRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.FreeSpinResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/atlas/games": { - "get": { - "description": "Retrieves available Atlas virtual games from the provider", - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "List Atlas virtual games", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AtlasGameEntity" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/atlas/init-game": { - "post": { - "description": "Initializes a game session for the given player using Atlas virtual game provider", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Start an Atlas virtual game session", - "parameters": [ - { - "description": "Start game input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.AtlasGameInitRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.AtlasGameInitResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/auth/logout": { "post": { "description": "Logout customer", @@ -1471,44 +836,6 @@ const docTemplate = `{ } } }, - "/api/v1/branch/{id}/bets": { - "get": { - "description": "Gets bets by its branch id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets bets by its branch id", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/branch/{id}/cashier": { "get": { "description": "Gets branch cashiers", @@ -1780,44 +1107,6 @@ const docTemplate = `{ } } }, - "/api/v1/branchWallet": { - "get": { - "description": "Retrieve all branch wallets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get all branch wallets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.WalletRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/cashier/{id}": { "get": { "description": "Get a single cashier by id", @@ -1868,56 +1157,6 @@ const docTemplate = `{ } } }, - "/api/v1/cashierWallet": { - "get": { - "description": "Get wallet for cashier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Get wallet for cashier", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.UserProfileRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/cashiers": { "get": { "description": "Get all cashiers", @@ -2085,546 +1324,6 @@ const docTemplate = `{ } } }, - "/api/v1/chapa/balances": { - "get": { - "description": "Retrieve Chapa account balance, optionally filtered by currency code (e.g., ETB, USD)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get Chapa account balance", - "parameters": [ - { - "type": "string", - "description": "Currency code (optional)", - "name": "currency_code", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/banks": { - "get": { - "description": "Get list of banks supported by Chapa", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get supported banks", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/payments/deposit": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Starts a new deposit process using Chapa payment gateway", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Initiate a deposit", - "parameters": [ - { - "description": "Deposit request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ChapaDepositRequestPayload" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ChapaDepositResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/payments/receipt/{chapa_ref}": { - "get": { - "description": "Retrieve the Chapa payment receipt URL using the reference ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get Chapa Payment Receipt URL", - "parameters": [ - { - "type": "string", - "description": "Chapa Reference ID", - "name": "chapa_ref", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/payments/webhook/verify": { - "post": { - "description": "Handles payment and transfer notifications from Chapa", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Chapa payment webhook callback (used by Chapa)", - "parameters": [ - { - "description": "Webhook payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ChapaWebhookPayment" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/payments/withdraw": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Initiates a withdrawal request to transfer funds to a bank account via Chapa", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Initiate a withdrawal", - "parameters": [ - { - "description": "Withdrawal request details", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ChapaWithdrawalRequest" - } - } - ], - "responses": { - "200": { - "description": "Chapa withdrawal process initiated successfully", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Invalid request body", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "422": { - "description": "Unprocessable entity", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/swap": { - "post": { - "description": "Convert an amount from one currency to another using Chapa's currency swap API", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Swap currency using Chapa API", - "parameters": [ - { - "description": "Swap request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.SwapRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transaction/cancel/{tx_ref}": { - "put": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Cancels an active Chapa transaction using its transaction reference", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Cancel a Chapa deposit transaction", - "parameters": [ - { - "type": "string", - "description": "Transaction Reference", - "name": "tx_ref", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ChapaCancelResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transaction/events/{ref_id}": { - "get": { - "description": "Retrieve the timeline of events for a specific Chapa transaction", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Fetch transaction events", - "parameters": [ - { - "type": "string", - "description": "Transaction Reference", - "name": "ref_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ChapaTransactionEvent" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transaction/manual/verify/{tx_ref}": { - "get": { - "description": "Manually verify a payment using Chapa's API", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Verify a payment manually", - "parameters": [ - { - "type": "string", - "description": "Transaction Reference", - "name": "tx_ref", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transactions": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Retrieves all transactions from Chapa payment gateway", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get all Chapa transactions", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ChapaTransaction" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transfers": { - "get": { - "description": "Retrieve all transfer records from Chapa", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get all Chapa transfers", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/company": { "get": { "description": "Gets all companies", @@ -3142,1198 +1841,6 @@ const docTemplate = `{ } } }, - "/api/v1/customer/{id}/bets": { - "get": { - "description": "Get customer bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get customer bets", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/customerWallet": { - "get": { - "description": "Retrieve all customer wallets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get all customer wallets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.CustomerWalletRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/direct-deposits": { - "get": { - "description": "Fetches direct deposits filtered by status with pagination", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Get direct deposits by status", - "parameters": [ - { - "type": "string", - "description": "Deposit status (e.g., PENDING, APPROVED, REJECTED)", - "name": "status", - "in": "query", - "required": true - }, - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "pageSize", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.DirectDeposit" - } - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "post": { - "description": "Creates a direct deposit for a customer and notifies both the customer and admins", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Create a new direct deposit", - "parameters": [ - { - "description": "Direct deposit details", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateDirectDeposit" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.DirectDeposit" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/direct-deposits/{depositID}": { - "get": { - "description": "Fetches a single direct deposit by its ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Get a direct deposit by ID", - "parameters": [ - { - "type": "integer", - "description": "Deposit ID", - "name": "depositID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.DirectDeposit" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Deletes a direct deposit by its ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Delete a direct deposit", - "parameters": [ - { - "type": "integer", - "description": "Deposit ID", - "name": "depositID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "string" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/direct-deposits/{depositID}/approve": { - "post": { - "description": "Approves a direct deposit by admin and credits customer wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Approve a direct deposit", - "parameters": [ - { - "type": "integer", - "description": "Deposit ID", - "name": "depositID", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Admin ID performing the approval", - "name": "adminID", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "string" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/direct-deposits/{depositID}/reject": { - "post": { - "description": "Rejects a direct deposit by admin and notifies the customer", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Reject a direct deposit", - "parameters": [ - { - "type": "integer", - "description": "Deposit ID", - "name": "depositID", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Admin ID performing the rejection", - "name": "adminID", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "Reason for rejection", - "name": "reason", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "string" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/betting-offers": { - "get": { - "description": "Fetches all EnetPulse preodds betting offers stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all betting offers", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreoddsBettingOffer" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/fixtures": { - "get": { - "description": "Fetches all fixtures stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all stored fixtures", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseFixture" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/fixtures/preodds": { - "get": { - "description": "Fetches all EnetPulse fixtures along with their associated pre-match odds", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get fixtures with preodds", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseFixtureWithPreodds" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/preodds": { - "get": { - "description": "Fetches all EnetPulse pre-match odds stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all preodds", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreodds" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/preodds-with-offers": { - "get": { - "description": "Fetches all EnetPulse pre-match odds along with their associated betting offers stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all preodds with betting offers", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreodds" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/results": { - "get": { - "description": "Fetches all EnetPulse match results stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all results", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseResult" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/sports": { - "get": { - "description": "Fetches all sports stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all sports", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseSport" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/tournament-stages": { - "get": { - "description": "Fetches all tournament stages stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all tournament stages", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseTournamentStage" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/tournament-templates": { - "get": { - "description": "Fetches all tournament templates stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all tournament templates", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseTournamentTemplate" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/tournaments": { - "get": { - "description": "Fetches all tournaments stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all tournaments", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseTournament" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/events": { - "get": { - "description": "Retrieve all upcoming events from the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all upcoming events", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "League ID Filter", - "name": "league_id", - "in": "query" - }, - { - "type": "string", - "description": "Sport ID Filter", - "name": "sport_id", - "in": "query" - }, - { - "type": "string", - "description": "Country Code Filter", - "name": "cc", - "in": "query" - }, - { - "type": "string", - "description": "Start Time", - "name": "first_start_time", - "in": "query" - }, - { - "type": "string", - "description": "End Time", - "name": "last_start_time", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BaseEvent" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/events/{id}": { - "get": { - "description": "Retrieve an upcoming event by ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve an upcoming by ID", - "parameters": [ - { - "type": "string", - "description": "ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BaseEvent" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Set the event status to removed", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "event" - ], - "summary": "Set the event status to removed", - "parameters": [ - { - "type": "integer", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/events/{id}/bets": { - "get": { - "description": "Retrieve bet outcomes by event id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve bet outcomes by event id", - "parameters": [ - { - "type": "string", - "description": "ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BaseEvent" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/events/{id}/is_monitored": { - "patch": { - "description": "Update the event is_monitored", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "event" - ], - "summary": "update the event is_monitored", - "parameters": [ - { - "type": "integer", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/events/{id}/settings": { - "put": { - "description": "Update the event settings", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "event" - ], - "summary": "update the event settings", - "parameters": [ - { - "type": "integer", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/issues": { "get": { "description": "Admin endpoint to list all reported issues with pagination", @@ -4558,44 +2065,6 @@ const docTemplate = `{ } } }, - "/api/v1/leagues": { - "get": { - "description": "Gets all leagues", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "leagues" - ], - "summary": "Gets all leagues", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BaseLeague" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/logs": { "get": { "description": "Fetches application logs from MongoDB with pagination, level filtering, and search", @@ -4908,203 +2377,6 @@ const docTemplate = `{ } } }, - "/api/v1/market-settings": { - "get": { - "description": "Get all market settings that apply globally", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Retrieve all global market settings", - "parameters": [ - { - "type": "integer", - "description": "Number of results to return (default 10)", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to skip (default 0)", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.MarketSettings" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/odds": { - "get": { - "description": "Retrieve all odds from the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all odds", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.OddMarketFilter" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/odds/upcoming/{upcoming_id}": { - "get": { - "description": "Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve prematch odds by upcoming ID (FI)", - "parameters": [ - { - "type": "string", - "description": "Upcoming Event ID (FI)", - "name": "upcoming_id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Number of results to return (default: 10)", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to skip (default: 0)", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.OddMarketWithEventFilter" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/odds/upcoming/{upcoming_id}/market/{market_id}": { - "get": { - "description": "Retrieve raw odds records using a Market ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve raw odds by Market ID", - "parameters": [ - { - "type": "string", - "description": "Upcoming ID", - "name": "upcoming_id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Market ID", - "name": "market_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.RawOddsByMarketID" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/operation": { "post": { "description": "Creates a operation", @@ -5151,631 +2423,6 @@ const docTemplate = `{ } } }, - "/api/v1/orchestrator/virtual-game/provider-reports/asc": { - "get": { - "description": "Retrieves all virtual game provider reports sorted by total_games_played in ascending order", - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "List virtual game provider reports (ascending)", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.VirtualGameProviderReport" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/orchestrator/virtual-game/provider-reports/desc": { - "get": { - "description": "Retrieves all virtual game provider reports sorted by total_games_played in descending order", - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "List virtual game provider reports (descending)", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.VirtualGameProviderReport" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/orchestrator/virtual-games": { - "get": { - "description": "Returns all virtual games with optional filters (category, search, pagination)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "List all virtual games", - "parameters": [ - { - "type": "string", - "description": "Filter by category", - "name": "category", - "in": "query" - }, - { - "type": "string", - "description": "Search by game name", - "name": "search", - "in": "query" - }, - { - "type": "integer", - "description": "Pagination limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Pagination offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.UnifiedGame" - } - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/report-files/download/{filename}": { - "get": { - "description": "Downloads a generated report CSV file from the server", - "produces": [ - "text/csv" - ], - "tags": [ - "Reports" - ], - "summary": "Download a CSV report file", - "parameters": [ - { - "type": "string", - "description": "Name of the report file to download (e.g., report_daily_2025-06-21.csv)", - "name": "filename", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CSV file will be downloaded", - "schema": { - "type": "file" - } - }, - "400": { - "description": "Missing or invalid filename", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Report file not found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal server error while serving the file", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/report-files/list": { - "get": { - "description": "Returns a paginated list of generated report CSV files with search capability", - "produces": [ - "application/json" - ], - "tags": [ - "Reports" - ], - "summary": "List available report CSV files", - "parameters": [ - { - "type": "string", - "description": "Search term to filter filenames", - "name": "search", - "in": "query" - }, - { - "type": "integer", - "default": 1, - "description": "Page number (default: 1)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "default": 20, - "description": "Items per page (default: 20, max: 100)", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Paginated list of CSV report filenames", - "schema": { - "$ref": "#/definitions/domain.PaginatedFileResponse" - } - }, - "400": { - "description": "Invalid pagination parameters", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Failed to read report directory", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/reports/dashboard": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Returns a comprehensive dashboard report with key metrics", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Reports" - ], - "summary": "Get dashboard report", - "parameters": [ - { - "type": "integer", - "description": "Company ID filter", - "name": "company_id", - "in": "query" - }, - { - "type": "integer", - "description": "Branch ID filter", - "name": "branch_id", - "in": "query" - }, - { - "type": "integer", - "description": "User ID filter", - "name": "user_id", - "in": "query" - }, - { - "type": "string", - "description": "Start time filter (RFC3339 format)", - "name": "start_time", - "in": "query" - }, - { - "type": "string", - "description": "End time filter (RFC3339 format)", - "name": "end_time", - "in": "query" - }, - { - "type": "string", - "description": "Sport ID filter", - "name": "sport_id", - "in": "query" - }, - { - "type": "integer", - "description": "Status filter (0=Pending, 1=Win, 2=Loss, 3=Half, 4=Void, 5=Error)", - "name": "status", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.DashboardSummary" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/result/b365/{id}": { - "get": { - "description": "Get results for an event", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "result" - ], - "summary": "Get results for an event", - "parameters": [ - { - "type": "string", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.ResultRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/santimpay/b2c-withdrawal": { - "post": { - "description": "Initiates a B2C withdrawal request with SantimPay and returns the response.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Process SantimPay B2C Withdrawal", - "parameters": [ - { - "description": "SantimPay B2C withdrawal request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GeneratePaymentURLRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/b2c/partners": { - "get": { - "description": "Fetches a list of available B2C payout partners (e.g., Telebirr, Mpesa, Banks) from SantimPay.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Get SantimPay B2C Partners", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/callback": { - "post": { - "description": "Processes a callback from SantimPay, updates transfer status, and credits user wallet if payment was successful.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Process SantimPay Payment Callback", - "parameters": [ - { - "description": "SantimPay callback payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.SantimPayCallbackPayload" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/direct-payment": { - "post": { - "description": "Initiates a direct payment request with SantimPay and returns the response.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Process SantimPay Direct Payment", - "parameters": [ - { - "description": "SantimPay direct payment request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GeneratePaymentURLRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/payment": { - "post": { - "description": "Generates a payment URL using SantimPay and returns it to the client.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Create SantimPay Payment Session", - "parameters": [ - { - "description": "SantimPay payment request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GeneratePaymentURLRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/transaction-status": { - "post": { - "description": "Retrieves the real-time status of a transaction from SantimPay.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Check SantimPay Transaction Status", - "parameters": [ - { - "description": "Transaction status request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.TransactionStatusRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/search/branch": { "get": { "description": "Search branches by name or location", @@ -5861,581 +2508,6 @@ const docTemplate = `{ } } }, - "/api/v1/shop/bet": { - "get": { - "description": "Gets all the shop bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets all shop bets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ShopBetRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Create bet at branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Create bet at branch", - "parameters": [ - { - "description": "create bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ShopBetReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/bet/{id}": { - "get": { - "description": "Cashout bet at branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Cashout bet at branch", - "parameters": [ - { - "description": "cashout bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CashoutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/bet/{id}/cashout": { - "post": { - "description": "Cashout bet at branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Cashout bet at branch", - "parameters": [ - { - "description": "cashout bet", - "name": "cashoutBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CashoutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/cashout": { - "post": { - "description": "Cashout bet by cashoutID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Cashout bet by cashoutID", - "parameters": [ - { - "description": "cashout bet", - "name": "cashoutBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CashoutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/cashout/{id}": { - "get": { - "description": "Cashout bet at branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Cashout bet at branch", - "parameters": [ - { - "description": "cashout bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CashoutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/deposit": { - "post": { - "description": "Transfers money from branch wallet to customer wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Shop deposit into customer wallet", - "parameters": [ - { - "description": "ShopDepositReq", - "name": "transferToWallet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ShopDepositReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopDepositRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/transaction": { - "get": { - "description": "Gets all the transactions", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Gets all transactions", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/transaction/{id}": { - "get": { - "description": "Gets a single transaction by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Gets transaction by id", - "parameters": [ - { - "type": "integer", - "description": "Transaction ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Updates the verified status of a transaction", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Updates the verified field of a transaction", - "parameters": [ - { - "type": "integer", - "description": "Transaction ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Updates Transaction Verification", - "name": "updateVerified", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateTransactionVerifiedReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/transaction/{id}/bet": { - "get": { - "description": "Gets a single shop bet by transaction id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Gets shop bet by transaction id", - "parameters": [ - { - "type": "integer", - "description": "Transaction ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/sport/bet/{id}": { - "get": { - "description": "Gets a single bet by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets bet by id", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Deletes bet by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Deletes bet by id", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/super-login": { "post": { "description": "Login super-admin", @@ -6464,7 +2536,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handlers.loginAdminRes" + "$ref": "#/definitions/handlers.LoginAdminRes" } }, "400": { @@ -6725,98 +2797,6 @@ const docTemplate = `{ } } }, - "/api/v1/telebirr/callback": { - "post": { - "description": "Processes the Telebirr payment result and updates wallet balance.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Telebirr" - ], - "summary": "Handle Telebirr Payment Callback", - "parameters": [ - { - "description": "Callback payload from Telebirr", - "name": "payload", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.TelebirrPaymentCallbackPayload" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/telebirr/payment": { - "post": { - "description": "Generates a payment URL using Telebirr and returns it to the client.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Telebirr" - ], - "summary": "Create Telebirr Payment Session", - "parameters": [ - { - "description": "Telebirr payment request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GeneratePaymentURLRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/tenant": { "get": { "description": "Check if phone number or email exist", @@ -7018,144 +2998,6 @@ const docTemplate = `{ } } }, - "/api/v1/tenant/{tenant_slug}/customer/{id}/bets": { - "get": { - "description": "Get tenant customer bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get tenant customer bets", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/tenant/{tenant_slug}/events/{id}/bets": { - "get": { - "description": "Retrieve bet outcomes by event id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve bet outcomes by event id", - "parameters": [ - { - "type": "string", - "description": "ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BaseEvent" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/tenant/{tenant_slug}/events/{id}/settings": { - "put": { - "description": "Update the event settings", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "event" - ], - "summary": "update the event settings", - "parameters": [ - { - "type": "integer", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/tenant/{tenant_slug}/referral/stats": { "get": { "security": [ @@ -7196,226 +3038,6 @@ const docTemplate = `{ } } }, - "/api/v1/ticket": { - "get": { - "description": "Retrieve all tickets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Get all tickets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.TicketRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/ticket/{id}": { - "get": { - "description": "Retrieve ticket details by ticket ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Get ticket by ID", - "parameters": [ - { - "type": "integer", - "description": "Ticket ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.TicketRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/transfer/refill/:id": { - "post": { - "description": "Super Admin route to refill a wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transfer" - ], - "summary": "Refill wallet", - "parameters": [ - { - "description": "Create Transfer", - "name": "refillWallet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateTransferReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.TransferWalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/transfer/wallet/:id": { - "post": { - "description": "Create a transfer to wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transfer" - ], - "summary": "Create a transfer to wallet", - "parameters": [ - { - "description": "Create Transfer", - "name": "transferToWallet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateTransferReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.TransferWalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/transfer/wallet/{id}": { - "get": { - "description": "Get transfer by wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transfer" - ], - "summary": "Get transfer by wallet", - "parameters": [ - { - "description": "Create Transfer", - "name": "transferToWallet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateTransferReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.TransferWalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/user/delete/{id}": { "delete": { "description": "Delete a user by their ID", @@ -7694,884 +3316,6 @@ const docTemplate = `{ } } }, - "/api/v1/veli/credit-balances": { - "get": { - "description": "Fetches current credit balances per currency for the specified brand", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get VeliGames credit balances for a brand", - "parameters": [ - { - "type": "string", - "description": "Brand ID", - "name": "brandId", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CreditBalance" - } - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/games-list": { - "post": { - "description": "Retrieves games for the specified provider", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get games by provider", - "parameters": [ - { - "description": "Brand and Provider ID", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GameListRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/gaming-activity": { - "post": { - "description": "Retrieves successfully processed gaming activity transactions (BET, WIN, CANCEL) from Veli Games", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get Veli Gaming Activity", - "parameters": [ - { - "description": "Gaming Activity Request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GamingActivityRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.GamingActivityResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/huge-wins": { - "post": { - "description": "Retrieves huge win transactions based on brand configuration (e.g. win \u003e 10000 USD or 100x bet)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get Veli Huge Wins", - "parameters": [ - { - "description": "Huge Wins Request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.HugeWinsRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.HugeWinsResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/providers": { - "post": { - "description": "Retrieves the list of VeliGames providers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get game providers", - "parameters": [ - { - "description": "Brand ID and paging options", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ProviderRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ProviderResponse" - } - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/start-demo-game": { - "post": { - "description": "Starts a demo session of the specified game (must support demo mode)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Start a demo game session", - "parameters": [ - { - "description": "Start demo game input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.DemoGameRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.GameStartResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/start-game": { - "post": { - "description": "Starts a real VeliGames session with the given player and game info", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Start a real game session", - "parameters": [ - { - "description": "Start game input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GameStartRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.GameStartResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/favorites": { - "get": { - "description": "Lists the games that the user marked as favorite", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Favourites" - ], - "summary": "Get user's favorite games", - "parameters": [ - { - "type": "string", - "description": "Filter by provider ID", - "name": "providerID", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to return", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Results offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.UnifiedGame" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "post": { - "description": "Adds a game to the user's favorite games list", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Favourites" - ], - "summary": "Add game to favorites", - "parameters": [ - { - "description": "Game ID and Provider ID to add", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FavoriteGameRequest" - } - } - ], - "responses": { - "201": { - "description": "created", - "schema": { - "type": "string" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/favorites/{gameID}": { - "delete": { - "description": "Removes a game from the user's favorites", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Favourites" - ], - "summary": "Remove game from favorites", - "parameters": [ - { - "type": "integer", - "description": "Game ID to remove", - "name": "gameID", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Provider ID of the game", - "name": "providerID", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "removed", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/orchestrator/providers/status": { - "patch": { - "description": "Sets the enabled status of a provider", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "Enable/Disable a provider", - "parameters": [ - { - "type": "string", - "description": "Provider ID", - "name": "provider_id", - "in": "path", - "required": true - }, - { - "type": "boolean", - "description": "Enable or Disable", - "name": "enabled", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.VirtualGameProvider" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/providers": { - "get": { - "description": "Lists all providers with pagination", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "List virtual game providers", - "parameters": [ - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": true - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/providers/{provider_id}": { - "get": { - "description": "Fetches a provider by provider_id", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "Get a virtual game provider", - "parameters": [ - { - "type": "string", - "description": "Provider ID", - "name": "provider_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.VirtualGameProvider" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Deletes a provider by provider_id", - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "Remove a virtual game provider", - "parameters": [ - { - "type": "string", - "description": "Provider ID", - "name": "provider_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/wallet": { - "get": { - "description": "Retrieve all wallets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get all wallets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.WalletRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/wallet/{id}": { - "get": { - "description": "Retrieve wallet details by wallet ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get wallet by ID", - "parameters": [ - { - "type": "integer", - "description": "Wallet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.WalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "patch": { - "description": "Can activate and deactivate wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Activate and Deactivate Wallet", - "parameters": [ - { - "type": "integer", - "description": "Wallet ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update Wallet Active", - "name": "updateCashOut", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.UpdateWalletActiveReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/webhooks/alea": { - "post": { - "description": "Handles webhook callbacks from Alea Play virtual games for bet settlement", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Alea Virtual Games" - ], - "summary": "Process Alea Play game callback", - "parameters": [ - { - "description": "Callback payload", - "name": "callback", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.AleaPlayCallback" - } - } - ], - "responses": { - "200": { - "description": "Callback processed successfully", - "schema": { - "type": "object", - "additionalProperties": { - "allOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "status": { - "type": "string" - } - } - } - ] - } - } - } - } - } - }, "/api/v1/{tenant_slug}/admin-login": { "post": { "description": "Login customer", @@ -8600,7 +3344,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handlers.loginAdminRes" + "$ref": "#/definitions/handlers.LoginAdminRes" } }, "400": { @@ -8676,1057 +3420,6 @@ const docTemplate = `{ } } }, - "/api/v1/{tenant_slug}/events": { - "get": { - "description": "Retrieve all upcoming events settings from the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all upcoming events with settings", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "League ID Filter", - "name": "league_id", - "in": "query" - }, - { - "type": "string", - "description": "Sport ID Filter", - "name": "sport_id", - "in": "query" - }, - { - "type": "string", - "description": "Country Code Filter", - "name": "cc", - "in": "query" - }, - { - "type": "string", - "description": "Start Time", - "name": "first_start_time", - "in": "query" - }, - { - "type": "string", - "description": "End Time", - "name": "last_start_time", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BaseEvent" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/leagues": { - "get": { - "description": "Gets all leagues", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "leagues" - ], - "summary": "Gets all leagues", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BaseLeague" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/leagues/{id}/featured": { - "put": { - "description": "Set the league to featured/un-featured", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "leagues" - ], - "summary": "Set the league to featured/un-featured", - "parameters": [ - { - "type": "integer", - "description": "League ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "League Featured Request", - "name": "active", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.SetLeagueAsFeatured" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/leagues/{id}/set-active": { - "put": { - "description": "Set the league to active", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "leagues" - ], - "summary": "Set the league to active", - "parameters": [ - { - "type": "integer", - "description": "League ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "League Active Request", - "name": "active", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.SetLeagueActiveReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/market-settings": { - "get": { - "description": "Get all market settings overridden for a specific tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Retrieve all market settings for a tenant", - "parameters": [ - { - "type": "integer", - "description": "Number of results to return (default 10)", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to skip (default 0)", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CompanyMarketSettings" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Insert new market settings for a specific tenant/company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Insert company-specific market settings", - "parameters": [ - { - "description": "Market Settings", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateCompanyMarketSettings" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Remove all overridden market settings for a specific tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Delete all market settings for a tenant", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/market-settings/{id}": { - "delete": { - "description": "Remove a specific overridden market setting for a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Delete a specific market setting for a tenant", - "parameters": [ - { - "type": "integer", - "description": "Market ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/odds": { - "get": { - "description": "Retrieve all odds from the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all odds", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.OddMarketFilter" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/odds/upcoming/{upcoming_id}": { - "get": { - "description": "Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve prematch odds by upcoming ID (FI)", - "parameters": [ - { - "type": "string", - "description": "Upcoming Event ID (FI)", - "name": "upcoming_id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Number of results to return (default: 10)", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to skip (default: 0)", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.OddMarketFilter" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/odds/upcoming/{upcoming_id}/market/{market_id}": { - "get": { - "description": "Retrieve raw odds records using a Market ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve raw odds by Market ID", - "parameters": [ - { - "type": "string", - "description": "Upcoming ID", - "name": "upcoming_id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Market ID", - "name": "market_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.RawOddsByMarketID" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/bet": { - "get": { - "description": "Gets all the bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets all bets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a bet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Create a bet", - "parameters": [ - { - "description": "Creates bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBetReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/bet/fastcode": { - "post": { - "description": "Creates a bet with fast code", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Create a bet with fast code", - "parameters": [ - { - "description": "Creates bet", - "name": "createBetWithFastCode", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBetWithFastCodeReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/bet/fastcode/{fast_code}": { - "get": { - "description": "Gets a single bet by fast_code", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets bet by fast_code", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "fast_code", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/bet/{id}": { - "get": { - "description": "Gets a single bet by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets bet by id", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Deletes bet by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Deletes bet by id", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "patch": { - "description": "Updates the cashed out field", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Updates the cashed out field", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Updates Cashed Out", - "name": "updateCashOut", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.UpdateCashOutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/random/bet": { - "post": { - "description": "Generate a random bet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Generate a random bet", - "parameters": [ - { - "description": "Create Random bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.RandomBetReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/ticket": { - "get": { - "description": "Retrieve all tickets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Get all tickets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.TicketRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a temporary ticket", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Create a temporary ticket", - "parameters": [ - { - "description": "Creates ticket", - "name": "createTicket", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateTicketReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CreateTicketRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/ticket/{id}": { - "get": { - "description": "Retrieve ticket details by ticket ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Get ticket by ID", - "parameters": [ - { - "type": "integer", - "description": "Ticket ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.TicketRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/top-leagues": { - "get": { - "description": "Retrieve all top leagues", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all top leagues", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.TopLeague" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/{tenant_slug}/user/admin-profile": { "get": { "security": [ @@ -9767,44 +3460,6 @@ const docTemplate = `{ } } }, - "/api/v1/{tenant_slug}/user/bets": { - "get": { - "description": "Gets user bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Gets user bets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/{tenant_slug}/user/checkPhoneEmailExist": { "post": { "description": "Check if phone number or email exist", @@ -10120,765 +3775,9 @@ const docTemplate = `{ } } } - }, - "/api/v1/{tenant_slug}/user/wallet": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "description": "Retrieve customer wallet details", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get customer wallet", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "company_id", - "in": "header", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomerWalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}events/{id}": { - "get": { - "description": "Retrieve an upcoming event by ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve an upcoming by ID", - "parameters": [ - { - "type": "string", - "description": "ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BaseEvent" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/betwin": { - "post": { - "description": "Processes a Bet and Win request from Atlas provider", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Atlas BetWin callback", - "parameters": [ - { - "description": "Atlas BetWin input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.AtlasBetWinRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.AtlasBetWinResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/freespin": { - "post": { - "description": "Handles the result of a free spin/bet from the game server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Free Spin/Bet result callback", - "parameters": [ - { - "description": "Free spin result input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FreeSpinResultRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.FreeSpinResultResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/jackpot": { - "post": { - "description": "Handles the jackpot result from the game server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Jackpot result callback", - "parameters": [ - { - "description": "Jackpot result input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.JackpotRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.JackpotResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/popok/games": { - "get": { - "description": "Retrieves the list of available PopOK slot games", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - PopOK" - ], - "summary": "Get PopOK Games List", - "parameters": [ - { - "type": "string", - "default": "USD", - "description": "Currency (e.g. USD, ETB)", - "name": "currency", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.PopOKGame" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/popok/games/recommend": { - "get": { - "description": "Recommends games based on user history or randomly", - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - PopOK" - ], - "summary": "Recommend virtual games", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "user_id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.GameRecommendation" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/result": { - "post": { - "description": "Processes a round result from Atlas or other providers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Atlas Round Result callback", - "parameters": [ - { - "description": "Round result input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.RoundResultRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.RoundResultResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/rollback": { - "post": { - "description": "Processes a rollback request from Atlas or other providers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Atlas Rollback callback", - "parameters": [ - { - "description": "Rollback request input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.RollbackRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.RollbackResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/virtual-game/callback": { - "post": { - "description": "Processes callbacks from PopOK for game events", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - PopOK" - ], - "summary": "Handle PopOK game callback", - "parameters": [ - { - "description": "Callback data", - "name": "callback", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.PopOKCallback" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/virtual-game/launch": { - "post": { - "security": [ - { - "Bearer": [] - } - ], - "description": "Generates a URL to launch a PopOK game", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - PopOK" - ], - "summary": "Launch a PopOK virtual game", - "parameters": [ - { - "description": "Game launch details", - "name": "launch", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.launchVirtualGameReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.launchVirtualGameRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/win": { - "post": { - "description": "Processes win callbacks from either Veli or PopOK providers by auto-detecting the format", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Wins" - ], - "summary": "Handle win callback (Veli or PopOK)", - "responses": { - "200": { - "description": "Win processing result", - "schema": {} - }, - "400": { - "description": "Invalid request format", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Authentication failed", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "409": { - "description": "Duplicate transaction", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } } }, "definitions": { - "domain.ARIFPAYPaymentMethod": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - } - } - }, - "domain.AleaPlayCallback": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "event_id": { - "type": "string" - }, - "game_id": { - "type": "string" - }, - "is_free_round": { - "type": "boolean" - }, - "multiplier": { - "type": "number" - }, - "operator_id": { - "type": "string" - }, - "round_id": { - "type": "string" - }, - "session_id": { - "type": "string" - }, - "signature": { - "type": "string" - }, - "timestamp": { - "type": "integer" - }, - "transaction_id": { - "type": "string" - }, - "type": { - "description": "BET, WIN, CASHOUT, etc.", - "type": "string" - }, - "user_id": { - "type": "string" - } - } - }, - "domain.ArifpayVerifyByTransactionIDRequest": { - "type": "object", - "properties": { - "paymentType": { - "type": "integer" - }, - "transactionId": { - "type": "string" - } - } - }, - "domain.AtlasBetWinRequest": { - "type": "object", - "properties": { - "betAmount": { - "type": "number" - }, - "casino_id": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "string" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - }, - "transaction_id": { - "type": "string" - }, - "winAmount": { - "type": "number" - } - } - }, - "domain.AtlasBetWinResponse": { - "type": "object", - "properties": { - "balance": { - "type": "number" - }, - "player_id": { - "type": "string" - } - } - }, - "domain.AtlasGameEntity": { - "type": "object", - "properties": { - "demo_url": { - "description": "✅ new field", - "type": "string" - }, - "deviceType": { - "type": "string" - }, - "game_id": { - "type": "string" - }, - "hasFreeBets": { - "type": "boolean" - }, - "has_demo": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "thumbnail_img_url": { - "description": "✅ new field", - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "domain.AtlasGameInitRequest": { - "type": "object", - "properties": { - "currency": { - "type": "string" - }, - "game": { - "type": "string" - }, - "language": { - "type": "string" - }, - "player_id": { - "type": "string" - } - } - }, - "domain.AtlasGameInitResponse": { - "type": "object", - "properties": { - "url": { - "type": "string" - } - } - }, - "domain.AtlasGetUserDataRequest": { - "type": "object", - "properties": { - "casino_id": { - "type": "string" - }, - "game": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "session_id": { - "type": "string" - } - } - }, - "domain.AtlasGetUserDataResponse": { - "type": "object", - "properties": { - "balance": { - "type": "number" - }, - "player_id": { - "type": "string" - } - } - }, "domain.Bank": { "type": "object", "properties": { @@ -10932,264 +3831,6 @@ const docTemplate = `{ } } }, - "domain.BaseEvent": { - "type": "object", - "properties": { - "addedTime": { - "$ref": "#/definitions/domain.ValidInt" - }, - "avgBetAmount": { - "type": "integer" - }, - "awayTeam": { - "type": "string" - }, - "awayTeamID": { - "type": "integer" - }, - "awayTeamImage": { - "type": "string" - }, - "defaultIsActive": { - "type": "boolean" - }, - "defaultIsFeatured": { - "type": "boolean" - }, - "defaultWinningUpperLimit": { - "type": "integer" - }, - "fetchedAt": { - "type": "string" - }, - "homeTeam": { - "type": "string" - }, - "homeTeamID": { - "type": "integer" - }, - "homeTeamImage": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "isLive": { - "type": "boolean" - }, - "isMonitored": { - "type": "boolean" - }, - "leagueCC": { - "$ref": "#/definitions/domain.ValidString" - }, - "leagueID": { - "type": "integer" - }, - "leagueName": { - "type": "string" - }, - "matchMinute": { - "$ref": "#/definitions/domain.ValidInt" - }, - "matchName": { - "type": "string" - }, - "matchPeriod": { - "$ref": "#/definitions/domain.ValidInt" - }, - "numberOfBets": { - "type": "integer" - }, - "score": { - "$ref": "#/definitions/domain.ValidString" - }, - "source": { - "$ref": "#/definitions/domain.EventSource" - }, - "sourceEventID": { - "type": "string" - }, - "sportID": { - "type": "integer" - }, - "startTime": { - "type": "string" - }, - "status": { - "$ref": "#/definitions/domain.EventStatus" - }, - "timerStatus": { - "$ref": "#/definitions/domain.ValidString" - }, - "totalAmount": { - "type": "integer" - }, - "totalOddOutcomes": { - "type": "integer" - }, - "totalPotentialWinnings": { - "type": "integer" - } - } - }, - "domain.BaseLeague": { - "type": "object", - "properties": { - "bet365ID": { - "$ref": "#/definitions/domain.ValidInt32" - }, - "countryCode": { - "$ref": "#/definitions/domain.ValidString" - }, - "defaultIsActive": { - "type": "boolean" - }, - "defaultIsFeatured": { - "type": "boolean" - }, - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "sportID": { - "type": "integer" - } - } - }, - "domain.BetOutcome": { - "type": "object", - "properties": { - "away_team_name": { - "type": "string", - "example": "Liverpool" - }, - "bet_id": { - "type": "integer", - "example": 1 - }, - "event_id": { - "type": "integer", - "example": 1 - }, - "expires": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - }, - "home_team_name": { - "type": "string", - "example": "Manchester" - }, - "id": { - "type": "integer", - "example": 1 - }, - "market_id": { - "type": "integer", - "example": 1 - }, - "market_name": { - "type": "string", - "example": "Fulltime Result" - }, - "odd": { - "type": "number", - "example": 1.5 - }, - "odd_handicap": { - "type": "string", - "example": "1" - }, - "odd_header": { - "type": "string", - "example": "1" - }, - "odd_id": { - "type": "integer", - "example": 1 - }, - "odd_name": { - "type": "string", - "example": "1" - }, - "sport_id": { - "type": "integer", - "example": 1 - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/domain.OutcomeStatus" - } - ], - "example": 1 - } - } - }, - "domain.BetRes": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "cashed_out": { - "type": "boolean", - "example": false - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "company_slug": { - "type": "string", - "example": "fortune" - }, - "created_at": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - }, - "fast_code": { - "type": "string" - }, - "full_name": { - "type": "string", - "example": "John Smith" - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_shop_bet": { - "type": "boolean", - "example": false - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetOutcome" - } - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/domain.OutcomeStatus" - } - ], - "example": 1 - }, - "total_odds": { - "type": "number", - "example": 4.22 - }, - "user_id": { - "type": "integer", - "example": 2 - } - } - }, "domain.BranchDetailRes": { "type": "object", "properties": { @@ -11345,301 +3986,6 @@ const docTemplate = `{ } } }, - "domain.CashoutReq": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "bank_code": { - "type": "string" - }, - "beneficiary_name": { - "type": "string" - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "cashout_id": { - "type": "string", - "example": "1234" - }, - "payment_option": { - "allOf": [ - { - "$ref": "#/definitions/domain.PaymentOption" - } - ], - "example": 1 - }, - "reference_number": { - "type": "string" - } - } - }, - "domain.ChapaCancelResponse": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "message": { - "type": "string" - }, - "status": { - "type": "string" - }, - "tx_ref": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.ChapaCustomer": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "first_name": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_name": { - "type": "string" - }, - "mobile": { - "type": "string" - } - } - }, - "domain.ChapaDepositRequestPayload": { - "type": "object", - "required": [ - "amount" - ], - "properties": { - "amount": { - "type": "number" - } - } - }, - "domain.ChapaDepositResponse": { - "type": "object", - "properties": { - "checkoutURL": { - "type": "string" - }, - "reference": { - "type": "string" - } - } - }, - "domain.ChapaTransaction": { - "type": "object", - "properties": { - "amount": { - "type": "string" - }, - "charge": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "customer": { - "$ref": "#/definitions/domain.ChapaCustomer" - }, - "payment_method": { - "type": "string" - }, - "ref_id": { - "type": "string" - }, - "status": { - "type": "string" - }, - "trans_id": { - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "domain.ChapaTransactionEvent": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "item": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "type": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.ChapaWebhookCustomization": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "logo": { - "type": "string" - }, - "title": { - "type": "string" - } - } - }, - "domain.ChapaWebhookPayment": { - "type": "object", - "properties": { - "amount": { - "type": "string" - }, - "charge": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "customization": { - "$ref": "#/definitions/domain.ChapaWebhookCustomization" - }, - "email": { - "type": "string" - }, - "event": { - "type": "string" - }, - "first_name": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "meta": { - "description": "may vary in structure, so kept flexible" - }, - "mobile": { - "type": "string" - }, - "mode": { - "type": "string" - }, - "payment_method": { - "type": "string" - }, - "reference": { - "type": "string" - }, - "status": { - "type": "string" - }, - "tx_ref": { - "type": "string" - }, - "type": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.ChapaWithdrawalRequest": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "amount": { - "description": "string because Chapa API uses string for monetary values", - "type": "string" - }, - "bank_code": { - "type": "integer" - }, - "currency": { - "type": "string" - }, - "reference": { - "type": "string" - } - } - }, - "domain.CheckoutSessionClientRequest": { - "type": "object", - "required": [ - "amount", - "customerEmail", - "customerPhone" - ], - "properties": { - "amount": { - "type": "number" - }, - "customerEmail": { - "type": "string" - }, - "customerPhone": { - "type": "string" - } - } - }, - "domain.CompanyMarketSettings": { - "type": "object", - "properties": { - "companyID": { - "type": "integer" - }, - "isActive": { - "$ref": "#/definitions/domain.ValidBool" - }, - "marketID": { - "type": "integer" - }, - "marketName": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - } - }, "domain.CompanyRes": { "type": "object", "properties": { @@ -11673,60 +4019,6 @@ const docTemplate = `{ } } }, - "domain.CreateBetOutcomeReq": { - "type": "object", - "properties": { - "event_id": { - "type": "integer", - "example": 1 - }, - "market_id": { - "type": "integer", - "example": 1 - }, - "odd_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CreateBetReq": { - "type": "object", - "required": [ - "amount", - "outcomes" - ], - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CreateBetOutcomeReq" - } - } - } - }, - "domain.CreateBetWithFastCodeReq": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "branch_id": { - "type": "integer" - }, - "fast_code": { - "type": "string" - } - } - }, "domain.CreateBranchOperationReq": { "type": "object", "properties": { @@ -11785,23 +4077,6 @@ const docTemplate = `{ } } }, - "domain.CreateCompanyMarketSettings": { - "type": "object", - "properties": { - "companyID": { - "type": "integer" - }, - "isActive": { - "$ref": "#/definitions/domain.ValidBool" - }, - "marketID": { - "type": "integer" - }, - "marketName": { - "type": "string" - } - } - }, "domain.CreateCompanyReq": { "type": "object", "properties": { @@ -11825,35 +4100,6 @@ const docTemplate = `{ } } }, - "domain.CreateDirectDeposit": { - "type": "object", - "properties": { - "accountHolder": { - "type": "string" - }, - "accountNumber": { - "type": "string" - }, - "amount": { - "type": "number" - }, - "bankName": { - "type": "string" - }, - "customerID": { - "type": "integer" - }, - "referenceNumber": { - "type": "string" - }, - "transferScreenshot": { - "type": "string" - }, - "walletID": { - "type": "integer" - } - } - }, "domain.CreateSupportedOperationReq": { "type": "object", "properties": { @@ -11867,733 +4113,6 @@ const docTemplate = `{ } } }, - "domain.CreateTicketOutcomeReq": { - "type": "object", - "properties": { - "event_id": { - "description": "TicketID int64 ` + "`" + `json:\"ticket_id\" example:\"1\"` + "`" + `", - "type": "integer", - "example": 1 - }, - "market_id": { - "type": "integer", - "example": 1 - }, - "odd_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CreateTicketReq": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CreateTicketOutcomeReq" - } - } - } - }, - "domain.CreateTicketRes": { - "type": "object", - "properties": { - "created_number": { - "type": "integer", - "example": 3 - }, - "fast_code": { - "type": "integer", - "example": 1234 - } - } - }, - "domain.CreditBalance": { - "type": "object", - "properties": { - "balance": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "threshold": { - "type": "number" - } - } - }, - "domain.DashboardSummary": { - "type": "object", - "properties": { - "active_admins": { - "type": "integer" - }, - "active_bets": { - "type": "integer" - }, - "active_branches": { - "type": "integer" - }, - "active_cashiers": { - "type": "integer" - }, - "active_companies": { - "type": "integer" - }, - "active_customers": { - "type": "integer" - }, - "active_games": { - "type": "integer" - }, - "active_managers": { - "type": "integer" - }, - "average_stake": { - "type": "integer" - }, - "branches_count": { - "type": "integer" - }, - "customer_count": { - "type": "integer" - }, - "inactive_admins": { - "type": "integer" - }, - "inactive_branches": { - "type": "integer" - }, - "inactive_cashiers": { - "type": "integer" - }, - "inactive_companies": { - "type": "integer" - }, - "inactive_customers": { - "type": "integer" - }, - "inactive_games": { - "type": "integer" - }, - "inactive_managers": { - "type": "integer" - }, - "profit": { - "type": "integer" - }, - "read_notifications": { - "type": "integer" - }, - "total_admins": { - "type": "integer" - }, - "total_bets": { - "type": "integer" - }, - "total_cashiers": { - "type": "integer" - }, - "total_companies": { - "type": "integer" - }, - "total_deposits": { - "type": "integer" - }, - "total_games": { - "type": "integer" - }, - "total_losses": { - "type": "integer" - }, - "total_managers": { - "type": "integer" - }, - "total_notifications": { - "type": "integer" - }, - "total_stakes": { - "type": "integer" - }, - "total_wallets": { - "type": "integer" - }, - "total_wins": { - "type": "integer" - }, - "total_withdrawals": { - "type": "integer" - }, - "unread_notifications": { - "type": "integer" - }, - "win_balance": { - "type": "integer" - }, - "win_rate": { - "type": "number" - } - } - }, - "domain.DemoGameRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "deviceType": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "language": { - "type": "string" - }, - "providerId": { - "type": "string" - } - } - }, - "domain.DirectDeposit": { - "type": "object", - "properties": { - "accountHolder": { - "type": "string" - }, - "accountNumber": { - "type": "string" - }, - "amount": { - "type": "number" - }, - "approvedAt": { - "type": "string" - }, - "approvedBy": { - "type": "integer" - }, - "bankName": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "customerID": { - "type": "integer" - }, - "id": { - "type": "integer" - }, - "referenceNumber": { - "type": "string" - }, - "rejectionReason": { - "type": "string" - }, - "status": { - "type": "string" - }, - "transferScreenshot": { - "type": "string" - }, - "walletID": { - "type": "integer" - } - } - }, - "domain.EnetpulseFixture": { - "type": "object", - "properties": { - "gender": { - "type": "string" - }, - "id": { - "type": "string" - }, - "n": { - "description": "convert to int", - "type": "string" - }, - "name": { - "type": "string" - }, - "round_typeFK": { - "type": "string" - }, - "sportFK": { - "type": "string" - }, - "sport_name": { - "type": "string" - }, - "startdate": { - "description": "ISO 8601", - "type": "string" - }, - "status_descFK": { - "type": "string" - }, - "status_type": { - "type": "string" - }, - "tournamentFK": { - "type": "string" - }, - "tournament_name": { - "type": "string" - }, - "tournament_stage_name": { - "description": "TournamentStageFK string ` + "`" + `json:\"tournament_stageFK\"` + "`" + `", - "type": "string" - }, - "tournament_templateFK": { - "type": "string" - }, - "tournament_template_name": { - "type": "string" - }, - "ut": { - "description": "parse to time.Time", - "type": "string" - } - } - }, - "domain.EnetpulseFixtureWithPreodds": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "fixtureApiID": { - "type": "string" - }, - "fixtureID": { - "type": "string" - }, - "fixtureName": { - "type": "string" - }, - "lastUpdatedAt": { - "type": "string" - }, - "preodds": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreodds" - } - }, - "roundTypeFk": { - "type": "string" - }, - "sportFk": { - "type": "string" - }, - "startDate": { - "type": "string" - }, - "statusDescFk": { - "type": "string" - }, - "statusType": { - "type": "string" - }, - "tournamentFk": { - "type": "string" - }, - "tournamentStageFk": { - "type": "string" - }, - "tournamentTemplateFk": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updatesCount": { - "type": "integer" - } - } - }, - "domain.EnetpulsePreodds": { - "type": "object", - "properties": { - "bettingOffers": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreoddsBettingOffer" - } - }, - "createdAt": { - "type": "string" - }, - "dparam": { - "type": "string" - }, - "dparam2": { - "type": "string" - }, - "eventFK": { - "type": "integer" - }, - "eventParticipantNumber": { - "type": "integer" - }, - "id": { - "type": "integer" - }, - "iparam": { - "type": "string" - }, - "iparam2": { - "type": "string" - }, - "lastUpdatedAt": { - "type": "string" - }, - "outcomeScopeFK": { - "type": "integer" - }, - "outcomeSubtypeFK": { - "type": "integer" - }, - "outcomeTypeFK": { - "type": "integer" - }, - "preoddsID": { - "type": "string" - }, - "sparam": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updatesCount": { - "type": "integer" - } - } - }, - "domain.EnetpulsePreoddsBettingOffer": { - "type": "object", - "properties": { - "active": { - "type": "string" - }, - "betting_offer_id": { - "type": "string" - }, - "betting_offer_status_fk": { - "type": "integer" - }, - "coupon_key": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_updated_at": { - "type": "string" - }, - "odds": { - "type": "number" - }, - "odds_old": { - "type": "number" - }, - "odds_provider_fk": { - "type": "integer" - }, - "preodds_fk": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "type": "integer" - } - } - }, - "domain.EnetpulseResult": { - "type": "object", - "properties": { - "commentary": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "first_half_ended": { - "type": "string" - }, - "game_ended": { - "type": "string" - }, - "game_started": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_updated_at": { - "type": "string" - }, - "lineup_confirmed": { - "type": "boolean" - }, - "live": { - "type": "string" - }, - "livestats_plus": { - "type": "string" - }, - "livestats_type": { - "type": "string" - }, - "name": { - "type": "string" - }, - "result_id": { - "type": "string" - }, - "round": { - "type": "string" - }, - "round_type_fk": { - "type": "string" - }, - "second_half_ended": { - "type": "string" - }, - "second_half_started": { - "type": "string" - }, - "spectators": { - "type": "integer" - }, - "sport_fk": { - "type": "string" - }, - "sport_name": { - "type": "string" - }, - "start_date": { - "type": "string" - }, - "status_desc_fk": { - "type": "string" - }, - "status_type": { - "type": "string" - }, - "tournament_fk": { - "type": "string" - }, - "tournament_name": { - "type": "string" - }, - "tournament_stage_name": { - "description": "TournamentStageFK string ` + "`" + `json:\"tournament_stage_fk\"` + "`" + `", - "type": "string" - }, - "tournament_template_fk": { - "type": "string" - }, - "tournament_template_name": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "type": "integer" - }, - "venue_name": { - "type": "string" - }, - "verified": { - "type": "boolean" - } - } - }, - "domain.EnetpulseSport": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "description": "DB primary key", - "type": "integer" - }, - "last_updated_at": { - "type": "string" - }, - "name": { - "description": "from API \"name\"", - "type": "string" - }, - "sport_id": { - "description": "from API \"id\"", - "type": "string" - }, - "status": { - "description": "active/inactive", - "type": "integer" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "description": "from API \"n\"", - "type": "integer" - } - } - }, - "domain.EnetpulseTournament": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "id": { - "description": "internal DB PK", - "type": "integer" - }, - "lastUpdatedAt": { - "type": "string" - }, - "name": { - "type": "string" - }, - "status": { - "type": "integer" - }, - "tournamentID": { - "type": "string" - }, - "tournamentTemplateFK": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updatesCount": { - "type": "integer" - } - } - }, - "domain.EnetpulseTournamentStage": { - "type": "object", - "properties": { - "country_fk": { - "description": "country FK from API", - "type": "string" - }, - "country_name": { - "description": "country name from API", - "type": "string" - }, - "created_at": { - "type": "string" - }, - "end_date": { - "description": "end date/time", - "type": "string" - }, - "gender": { - "description": "male/female/mixed/unknown", - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_updated_at": { - "description": "ut from API", - "type": "string" - }, - "name": { - "description": "API name", - "type": "string" - }, - "stage_id": { - "description": "API id", - "type": "string" - }, - "start_date": { - "description": "start date/time", - "type": "string" - }, - "status": { - "description": "active/inactive", - "type": "integer" - }, - "tournament_fk": { - "description": "Foreign key to tournament", - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "description": "n from API", - "type": "integer" - } - } - }, - "domain.EnetpulseTournamentTemplate": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "gender": { - "description": "male, female, mixed, unknown", - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_updated_at": { - "type": "string" - }, - "name": { - "description": "from API \"name\"", - "type": "string" - }, - "sport_fk": { - "description": "related sport id", - "type": "string" - }, - "status": { - "description": "optional", - "type": "integer" - }, - "template_id": { - "description": "from API \"id\"", - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "description": "from API \"n\"", - "type": "integer" - } - } - }, "domain.ErrorResponse": { "type": "object", "properties": { @@ -12605,481 +4124,6 @@ const docTemplate = `{ } } }, - "domain.EventSource": { - "type": "string", - "enum": [ - "b365api", - "bwin", - "bfair", - "1xbet", - "enetpulse" - ], - "x-enum-varnames": [ - "EVENT_SOURCE_BET365", - "EVENT_SOURCE_BWIN", - "EVENT_SOURCE_BETFAIR", - "EVENT_SOURCE_1XBET", - "EVENT_SOURCE_ENET" - ] - }, - "domain.EventStatus": { - "type": "string", - "enum": [ - "upcoming", - "in_play", - "to_be_fixed", - "ended", - "postponed", - "cancelled", - "walkover", - "interrupted", - "abandoned", - "retired", - "suspended", - "decided_by_fa", - "removed" - ], - "x-enum-varnames": [ - "STATUS_PENDING", - "STATUS_IN_PLAY", - "STATUS_TO_BE_FIXED", - "STATUS_ENDED", - "STATUS_POSTPONED", - "STATUS_CANCELLED", - "STATUS_WALKOVER", - "STATUS_INTERRUPTED", - "STATUS_ABANDONED", - "STATUS_RETIRED", - "STATUS_SUSPENDED", - "STATUS_DECIDED_BY_FA", - "STATUS_REMOVED" - ] - }, - "domain.EventWithSettingsRes": { - "type": "object", - "properties": { - "added_time": { - "type": "integer" - }, - "average_bet_amount": { - "type": "number" - }, - "away_team": { - "type": "string" - }, - "away_team_id": { - "type": "integer" - }, - "away_team_image": { - "type": "string" - }, - "default_is_active": { - "type": "boolean" - }, - "default_is_featured": { - "type": "boolean" - }, - "default_winning_upper_limit": { - "type": "integer" - }, - "fetched_at": { - "type": "string" - }, - "home_team": { - "type": "string" - }, - "home_team_id": { - "type": "integer" - }, - "home_team_image": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "is_active": { - "type": "boolean" - }, - "is_featured": { - "type": "boolean" - }, - "is_live": { - "type": "boolean" - }, - "is_monitored": { - "type": "boolean" - }, - "league_cc": { - "type": "string" - }, - "league_id": { - "type": "integer" - }, - "league_name": { - "type": "string" - }, - "match_minute": { - "type": "integer" - }, - "match_name": { - "type": "string" - }, - "match_period": { - "type": "integer" - }, - "number_of_bets": { - "type": "integer" - }, - "score": { - "type": "string" - }, - "source": { - "$ref": "#/definitions/domain.EventSource" - }, - "source_event_id": { - "type": "string" - }, - "sport_id": { - "type": "integer" - }, - "start_time": { - "type": "string" - }, - "status": { - "$ref": "#/definitions/domain.EventStatus" - }, - "timer_status": { - "type": "string" - }, - "total_amount": { - "type": "number" - }, - "total_odd_outcomes": { - "type": "integer" - }, - "total_potential_winnings": { - "type": "number" - }, - "updated_at": { - "type": "string" - }, - "winning_upper_limit": { - "type": "integer" - } - } - }, - "domain.FavoriteGameRequest": { - "type": "object", - "properties": { - "game_id": { - "type": "integer" - }, - "provider_id": { - "type": "string" - } - } - }, - "domain.FreeSpinRequest": { - "type": "object", - "properties": { - "casino_id": { - "type": "string" - }, - "end_date": { - "description": "\"yyyy-mm-ddTHH:MM:SS+00:00\"", - "type": "string" - }, - "freespins_count": { - "description": "count of free spins/bets", - "type": "integer" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - } - } - }, - "domain.FreeSpinResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, - "domain.FreeSpinResultRequest": { - "type": "object", - "properties": { - "amount": { - "description": "win amount", - "type": "number" - }, - "casino_id": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - }, - "transaction_id": { - "type": "string" - } - } - }, - "domain.FreeSpinResultResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, - "domain.GameListRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "page": { - "type": "integer" - }, - "providerId": { - "type": "string" - }, - "size": { - "type": "integer" - } - } - }, - "domain.GameRecommendation": { - "type": "object", - "properties": { - "bets": { - "type": "array", - "items": { - "type": "number" - } - }, - "game_id": { - "type": "integer" - }, - "game_name": { - "type": "string" - }, - "reason": { - "description": "e.g., \"Based on your activity\", \"Popular\", \"Random pick\"", - "type": "string" - }, - "thumbnail": { - "type": "string" - } - } - }, - "domain.GameStartRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "country": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "deviceType": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "language": { - "type": "string" - }, - "playerId": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "sessionId": { - "type": "string" - } - } - }, - "domain.GameStartResponse": { - "type": "object", - "properties": { - "startGameUrl": { - "type": "string" - } - } - }, - "domain.GamingActivityItem": { - "type": "object", - "properties": { - "actionType": { - "type": "string" - }, - "amount": { - "type": "number" - }, - "amountEur": { - "type": "number" - }, - "amountUsd": { - "type": "number" - }, - "brandId": { - "type": "string" - }, - "correlationId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "playerId": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "refActionType": { - "type": "string" - }, - "refRoundType": { - "type": "string" - }, - "refTransactionId": { - "type": "string" - }, - "roundId": { - "type": "string" - }, - "roundType": { - "type": "string" - }, - "sessionId": { - "type": "string" - }, - "transactionId": { - "type": "string" - } - } - }, - "domain.GamingActivityRequest": { - "type": "object", - "properties": { - "currencies": { - "description": "Optional", - "type": "array", - "items": { - "type": "string" - } - }, - "excludeFreeWin": { - "description": "Optional", - "type": "boolean" - }, - "fromDate": { - "description": "YYYY-MM-DD", - "type": "string" - }, - "gameIds": { - "description": "Optional", - "type": "array", - "items": { - "type": "string" - } - }, - "page": { - "description": "Optional, default 1", - "type": "integer" - }, - "playerIds": { - "description": "Optional", - "type": "array", - "items": { - "type": "string" - } - }, - "providerId": { - "description": "Optional", - "type": "string" - }, - "size": { - "description": "Optional, default 100", - "type": "integer" - }, - "toDate": { - "description": "YYYY-MM-DD", - "type": "string" - } - } - }, - "domain.GamingActivityResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.GamingActivityItem" - } - }, - "meta": { - "$ref": "#/definitions/domain.PaginationMeta" - } - } - }, - "domain.GeneratePaymentURLRequest": { - "type": "object", - "properties": { - "amount": { - "type": "integer" - }, - "paymentMethod": { - "type": "string" - }, - "paymentReason": { - "type": "string" - }, - "phoneNumber": { - "type": "string" - } - } - }, "domain.GetCompanyRes": { "type": "object", "properties": { @@ -13175,108 +4219,6 @@ const docTemplate = `{ } } }, - "domain.HugeWinItem": { - "type": "object", - "properties": { - "betAmount": { - "type": "number" - }, - "betAmountUsd": { - "type": "number" - }, - "betTransactionId": { - "type": "string" - }, - "brandId": { - "type": "string" - }, - "correlationId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "operatorId": { - "type": "string" - }, - "playerId": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "roundId": { - "type": "string" - }, - "winAmount": { - "type": "number" - }, - "winAmountUsd": { - "type": "number" - }, - "winTransactionId": { - "type": "string" - } - } - }, - "domain.HugeWinsRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "currencies": { - "type": "array", - "items": { - "type": "string" - } - }, - "fromDate": { - "type": "string" - }, - "gameIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "page": { - "type": "integer" - }, - "providerId": { - "type": "string" - }, - "size": { - "type": "integer" - }, - "toDate": { - "type": "string" - } - } - }, - "domain.HugeWinsResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.HugeWinItem" - } - }, - "meta": { - "$ref": "#/definitions/domain.PaginationMeta" - } - } - }, "domain.InstResponse": { "type": "object", "properties": { @@ -13299,53 +4241,6 @@ const docTemplate = `{ } } }, - "domain.JackpotRequest": { - "type": "object", - "properties": { - "amount": { - "description": "jackpot win", - "type": "number" - }, - "casino_id": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "details": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - }, - "transaction_id": { - "type": "string" - } - } - }, - "domain.JackpotResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, "domain.LogEntry": { "type": "object", "properties": { @@ -13393,99 +4288,6 @@ const docTemplate = `{ } } }, - "domain.MarketSettings": { - "type": "object", - "properties": { - "isActive": { - "type": "boolean" - }, - "marketID": { - "type": "integer" - }, - "marketName": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - } - }, - "domain.OddMarketFilter": { - "type": "object", - "properties": { - "limit": { - "$ref": "#/definitions/domain.ValidInt32" - }, - "offset": { - "$ref": "#/definitions/domain.ValidInt32" - } - } - }, - "domain.OddMarketWithEventFilter": { - "type": "object", - "properties": { - "isLive": { - "$ref": "#/definitions/domain.ValidBool" - }, - "limit": { - "$ref": "#/definitions/domain.ValidInt32" - }, - "offset": { - "$ref": "#/definitions/domain.ValidInt32" - }, - "status": { - "$ref": "#/definitions/domain.ValidString" - } - } - }, - "domain.OutcomeStatus": { - "type": "integer", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5 - ], - "x-enum-comments": { - "OUTCOME_STATUS_ERROR": "Error (Unsettled Bet)", - "OUTCOME_STATUS_HALF": "Half Win and Half Given Back", - "OUTCOME_STATUS_VOID": "Give Back" - }, - "x-enum-varnames": [ - "OUTCOME_STATUS_PENDING", - "OUTCOME_STATUS_WIN", - "OUTCOME_STATUS_LOSS", - "OUTCOME_STATUS_VOID", - "OUTCOME_STATUS_HALF", - "OUTCOME_STATUS_ERROR" - ] - }, - "domain.PaginatedFileResponse": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "string" - } - }, - "message": { - "type": "string" - }, - "metadata": {}, - "pagination": { - "$ref": "#/definitions/domain.Pagination" - }, - "status_code": { - "type": "integer" - }, - "success": { - "type": "boolean" - } - } - }, "domain.Pagination": { "type": "object", "properties": { @@ -13503,178 +4305,6 @@ const docTemplate = `{ } } }, - "domain.PaginationMeta": { - "type": "object", - "properties": { - "currentPage": { - "type": "integer" - }, - "itemCount": { - "type": "integer" - }, - "itemsPerPage": { - "type": "integer" - }, - "totalItems": { - "type": "integer" - }, - "totalPages": { - "type": "integer" - } - } - }, - "domain.PaymentOption": { - "type": "integer", - "enum": [ - 0, - 1, - 2, - 3 - ], - "x-enum-varnames": [ - "CASH_TRANSACTION", - "TELEBIRR_TRANSACTION", - "ARIFPAY_TRANSACTION", - "BANK" - ] - }, - "domain.PopOKCallback": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "session_id": { - "type": "string" - }, - "signature": { - "description": "HMAC-SHA256 signature for verification", - "type": "string" - }, - "timestamp": { - "type": "integer" - }, - "transaction_id": { - "type": "string" - }, - "type": { - "description": "BET, WIN, REFUND, JACKPOT_WIN", - "type": "string" - } - } - }, - "domain.PopOKGame": { - "type": "object", - "properties": { - "bets": { - "type": "array", - "items": { - "type": "number" - } - }, - "gameName": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "status": { - "type": "integer" - }, - "thumbnail": { - "type": "string" - } - } - }, - "domain.ProviderRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "extraData": { - "type": "boolean" - }, - "page": { - "type": "integer" - }, - "size": { - "type": "integer" - } - } - }, - "domain.ProviderResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "logoForDark": { - "type": "string" - }, - "logoForLight": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "providerName": { - "type": "string" - } - } - } - } - } - }, - "domain.RandomBetReq": { - "type": "object", - "required": [ - "branch_id", - "number_of_bets" - ], - "properties": { - "branch_id": { - "type": "integer", - "example": 1 - }, - "number_of_bets": { - "type": "integer", - "example": 1 - } - } - }, - "domain.RawOddsByMarketID": { - "type": "object", - "properties": { - "expires_at": { - "type": "string" - }, - "fetched_at": { - "type": "string" - }, - "handicap": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "market_name": { - "type": "string" - }, - "raw_odds": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true - } - } - } - }, "domain.ReferralStats": { "type": "object", "properties": { @@ -13757,456 +4387,6 @@ const docTemplate = `{ "RoleTransactionApprover" ] }, - "domain.RollbackRequest": { - "type": "object", - "properties": { - "bet_transaction_id": { - "type": "string" - }, - "casino_id": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - } - } - }, - "domain.RollbackResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, - "domain.RoundResultRequest": { - "type": "object", - "properties": { - "amount": { - "description": "win amount", - "type": "number" - }, - "bet_transaction_id": { - "description": "from BET request", - "type": "string" - }, - "casino_id": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - }, - "transaction_id": { - "description": "new transaction id", - "type": "string" - } - } - }, - "domain.RoundResultResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, - "domain.SantimPayCallbackPayload": { - "type": "object", - "properties": { - "accountNumber": { - "type": "string" - }, - "address": { - "type": "string" - }, - "amount": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "failureRedirectUrl": { - "type": "string" - }, - "merId": { - "type": "string" - }, - "merName": { - "type": "string" - }, - "message": { - "type": "string" - }, - "msisdn": { - "type": "string" - }, - "paymentVia": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "receiverWalletID": { - "type": "string" - }, - "refId": { - "type": "string" - }, - "status": { - "type": "string" - }, - "successRedirectUrl": { - "type": "string" - }, - "thirdPartyId": { - "type": "string" - }, - "txnId": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.ShopBetReq": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "amount": { - "type": "number", - "example": 100 - }, - "bank_code": { - "type": "string" - }, - "beneficiary_name": { - "type": "string" - }, - "bet_id": { - "type": "integer", - "example": 1 - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "full_name": { - "type": "string", - "example": "John Smith" - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CreateBetOutcomeReq" - } - }, - "payment_option": { - "allOf": [ - { - "$ref": "#/definitions/domain.PaymentOption" - } - ], - "example": 1 - }, - "phone_number": { - "type": "string", - "example": "0911111111" - }, - "reference_number": { - "type": "string" - } - } - }, - "domain.ShopBetRes": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "bet_id": { - "type": "integer", - "example": 1 - }, - "branch_id": { - "type": "integer", - "example": 2 - }, - "cashed_out": { - "type": "boolean", - "example": false - }, - "cashout_id": { - "type": "string", - "example": "21234" - }, - "company_id": { - "type": "integer", - "example": 2 - }, - "created_at": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - }, - "fast_code": { - "type": "string", - "example": "12SD1" - }, - "full_name": { - "type": "string", - "example": "John" - }, - "id": { - "type": "integer" - }, - "number_of_outcomes": { - "type": "integer", - "example": 1 - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetOutcome" - } - }, - "phone_number": { - "type": "string", - "example": "1234567890" - }, - "shop_transaction_id": { - "type": "integer" - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/domain.OutcomeStatus" - } - ], - "example": 1 - }, - "total_odds": { - "type": "number", - "example": 4.22 - }, - "transaction_verified": { - "type": "boolean", - "example": true - }, - "updated_at": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - } - } - }, - "domain.ShopDepositReq": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "amount": { - "type": "number", - "example": 100 - }, - "bank_code": { - "description": "FullName string ` + "`" + `json:\"full_name\" example:\"John Smith\"` + "`" + `\nPhoneNumber string ` + "`" + `json:\"phone_number\" example:\"0911111111\"` + "`" + `", - "type": "string" - }, - "beneficiary_name": { - "type": "string" - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "customer_id": { - "type": "integer", - "example": 1 - }, - "payment_option": { - "allOf": [ - { - "$ref": "#/definitions/domain.PaymentOption" - } - ], - "example": 1 - }, - "reference_number": { - "type": "string" - } - } - }, - "domain.ShopDepositRes": { - "type": "object", - "properties": { - "customer_id": { - "type": "integer" - }, - "id": { - "type": "integer" - }, - "shop_transaction_id": { - "type": "integer" - }, - "wallet_transfer_id": { - "type": "integer" - } - } - }, - "domain.ShopTransactionRes": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "amount": { - "type": "number", - "example": 100 - }, - "approved_by": { - "type": "integer", - "example": 1 - }, - "approver_first_name": { - "type": "string", - "example": "John" - }, - "approver_last_name": { - "type": "string", - "example": "Smith" - }, - "approver_phone_number": { - "type": "string", - "example": "0911111111" - }, - "bank_code": { - "type": "string" - }, - "beneficiary_name": { - "type": "string" - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "branch_location": { - "type": "string", - "example": "Branch Location" - }, - "branch_name": { - "type": "string", - "example": "Branch Name" - }, - "cashier_name": { - "type": "string", - "example": "John Smith" - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "created_at": { - "type": "string" - }, - "creator_first_name": { - "type": "string", - "example": "John" - }, - "creator_last_name": { - "type": "string", - "example": "Smith" - }, - "creator_phone_number": { - "type": "string", - "example": "0911111111" - }, - "full_name": { - "type": "string", - "example": "John Smith" - }, - "id": { - "type": "integer", - "example": 1 - }, - "payment_option": { - "allOf": [ - { - "$ref": "#/definitions/domain.PaymentOption" - } - ], - "example": 1 - }, - "phone_number": { - "type": "string", - "example": "0911111111" - }, - "reference_number": { - "type": "string" - }, - "type": { - "type": "integer", - "example": 1 - }, - "updated_at": { - "type": "string" - }, - "user_id": { - "type": "integer", - "example": 1 - }, - "verified": { - "type": "boolean", - "example": true - } - } - }, "domain.SupportedOperationRes": { "type": "object", "properties": { @@ -14224,234 +4404,6 @@ const docTemplate = `{ } } }, - "domain.SwapRequest": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "from": { - "type": "string" - }, - "to": { - "type": "string" - } - } - }, - "domain.TelebirrPaymentCallbackPayload": { - "type": "object", - "properties": { - "appid": { - "description": "App ID provided by Telebirr", - "type": "string" - }, - "callback_info": { - "description": "Optional merchant-defined callback data", - "type": "string" - }, - "merch_code": { - "description": "Merchant short code", - "type": "string" - }, - "merch_order_id": { - "description": "Order ID from merchant system", - "type": "string" - }, - "notify_time": { - "description": "Notification timestamp (UTC, in seconds)", - "type": "string" - }, - "notify_url": { - "description": "Optional callback URL", - "type": "string" - }, - "payment_order_id": { - "description": "Order ID from Telebirr system", - "type": "string" - }, - "sign": { - "description": "Signature of the payload", - "type": "string" - }, - "sign_type": { - "description": "Signature type, e.g., SHA256WithRSA", - "type": "string" - }, - "total_amount": { - "description": "Payment amount", - "type": "string" - }, - "trade_status": { - "description": "Payment status (e.g., Completed, Failure)", - "type": "string" - }, - "trans_currency": { - "description": "Currency type (e.g., ETB)", - "type": "string" - }, - "trans_end_time": { - "description": "Transaction end time (UTC seconds)", - "type": "string" - }, - "trans_id": { - "description": "Transaction ID", - "type": "string" - } - } - }, - "domain.TicketOutcome": { - "type": "object", - "properties": { - "away_team_name": { - "type": "string", - "example": "Liverpool" - }, - "event_id": { - "type": "integer", - "example": 1 - }, - "expires": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - }, - "home_team_name": { - "type": "string", - "example": "Manchester" - }, - "id": { - "type": "integer", - "example": 1 - }, - "market_id": { - "type": "integer", - "example": 1 - }, - "market_name": { - "type": "string", - "example": "Fulltime Result" - }, - "odd": { - "type": "number", - "example": 1.5 - }, - "odd_handicap": { - "type": "string", - "example": "1" - }, - "odd_header": { - "type": "string", - "example": "1" - }, - "odd_id": { - "type": "integer", - "example": 1 - }, - "odd_name": { - "type": "string", - "example": "1" - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/domain.OutcomeStatus" - } - ], - "example": 1 - }, - "ticket_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.TicketRes": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "id": { - "type": "integer", - "example": 1 - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.TicketOutcome" - } - }, - "total_odds": { - "type": "number", - "example": 4.22 - } - } - }, - "domain.TransactionStatusRequest": { - "type": "object", - "properties": { - "fullParams": { - "type": "boolean" - }, - "id": { - "type": "string" - } - } - }, - "domain.UnifiedGame": { - "type": "object", - "properties": { - "bets": { - "type": "array", - "items": { - "type": "number" - } - }, - "category": { - "type": "string" - }, - "demoUrl": { - "type": "string" - }, - "deviceType": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "hasDemo": { - "type": "boolean" - }, - "hasFreeBets": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "rtp": { - "type": "number" - }, - "status": { - "type": "integer" - }, - "thumbnail": { - "type": "string" - }, - "volatility": { - "type": "string" - } - } - }, "domain.UpdateCompanyReq": { "type": "object", "properties": { @@ -14476,164 +4428,6 @@ const docTemplate = `{ } } }, - "domain.UpdateTransactionVerifiedReq": { - "type": "object", - "properties": { - "verified": { - "type": "boolean", - "example": true - } - } - }, - "domain.ValidBool": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "boolean" - } - } - }, - "domain.ValidInt": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "integer" - } - } - }, - "domain.ValidInt32": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "integer" - } - } - }, - "domain.ValidString": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "string" - } - } - }, - "domain.VirtualGameProvider": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "logo_dark": { - "type": "string" - }, - "logo_light": { - "type": "string" - }, - "provider_id": { - "description": "ID int64 ` + "`" + `json:\"id\" db:\"id\"` + "`" + `", - "type": "string" - }, - "provider_name": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.VirtualGameProviderReport": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "provider_id": { - "type": "string" - }, - "report_date": { - "type": "string" - }, - "report_type": { - "type": "string" - }, - "total_bets": { - "type": "number" - }, - "total_games_played": { - "type": "integer" - }, - "total_payouts": { - "type": "number" - }, - "total_players": { - "type": "integer" - }, - "total_profit": { - "type": "number" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.WebhookRequest": { - "type": "object", - "properties": { - "nonce": { - "type": "string" - }, - "notificationUrl": { - "type": "string" - }, - "paymentMethod": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "sessionId": { - "type": "string" - }, - "totalAmount": { - "type": "integer" - }, - "transaction": { - "type": "object", - "properties": { - "transactionId": { - "type": "string" - }, - "transactionStatus": { - "type": "string" - } - } - }, - "transactionStatus": { - "type": "string" - }, - "uuid": { - "type": "string" - } - } - }, "handlers.AdminProfileRes": { "type": "object", "properties": { @@ -14866,19 +4660,6 @@ const docTemplate = `{ } } }, - "handlers.CreateTransferReq": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "payment_method": { - "type": "string", - "example": "cash" - } - } - }, "handlers.CustomerProfileRes": { "type": "object", "properties": { @@ -14926,91 +4707,6 @@ const docTemplate = `{ } } }, - "handlers.CustomerWalletRes": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "customer_id": { - "type": "integer", - "example": 1 - }, - "first_name": { - "type": "string", - "example": "John" - }, - "id": { - "type": "integer", - "example": 1 - }, - "last_name": { - "type": "string", - "example": "Smith" - }, - "number_of_deposits": { - "type": "integer" - }, - "number_of_transactions": { - "type": "integer" - }, - "number_of_transfers": { - "type": "integer" - }, - "number_of_withdraws": { - "type": "integer" - }, - "phone_number": { - "type": "string", - "example": "0911111111" - }, - "regular_balance": { - "type": "number", - "example": 100 - }, - "regular_id": { - "type": "integer", - "example": 1 - }, - "regular_is_active": { - "type": "boolean", - "example": true - }, - "regular_updated_at": { - "type": "string" - }, - "static_balance": { - "type": "number", - "example": 100 - }, - "static_id": { - "type": "integer", - "example": 1 - }, - "static_is_active": { - "type": "boolean", - "example": true - }, - "static_updated_at": { - "type": "string" - }, - "total_deposits_amount": { - "type": "number" - }, - "total_transactions": { - "type": "number" - }, - "total_transfers_amount": { - "type": "number" - }, - "total_withdraws_amount": { - "type": "number" - }, - "updated_at": { - "type": "string" - } - } - }, "handlers.CustomersRes": { "type": "object", "properties": { @@ -15117,6 +4813,20 @@ const docTemplate = `{ } } }, + "handlers.LoginAdminRes": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "refresh_token": { + "type": "string" + }, + "role": { + "type": "string" + } + } + }, "handlers.ManagersRes": { "type": "object", "properties": { @@ -15246,17 +4956,6 @@ const docTemplate = `{ } } }, - "handlers.ResultRes": { - "type": "object", - "properties": { - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetOutcome" - } - } - } - }, "handlers.SearchUserByNameOrPhoneReq": { "type": "object", "properties": { @@ -15268,105 +4967,6 @@ const docTemplate = `{ } } }, - "handlers.SetLeagueActiveReq": { - "type": "object", - "properties": { - "is_active": { - "type": "boolean" - } - } - }, - "handlers.SetLeagueAsFeatured": { - "type": "object", - "properties": { - "is_featured": { - "type": "boolean", - "example": true - } - } - }, - "handlers.TopLeague": { - "type": "object", - "properties": { - "events": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EventWithSettingsRes" - } - }, - "league_cc": { - "type": "string" - }, - "league_id": { - "type": "integer" - }, - "league_name": { - "type": "string" - }, - "league_sport_id": { - "type": "integer" - } - } - }, - "handlers.TransferWalletRes": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "created_at": { - "type": "string" - }, - "depositor_first_name": { - "type": "string" - }, - "depositor_id": { - "type": "integer" - }, - "depositor_last_name": { - "type": "string" - }, - "depositor_phone_number": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "payment_method": { - "type": "string" - }, - "receiver_wallet_id": { - "type": "integer" - }, - "reference_number": { - "description": "← Add this", - "type": "string" - }, - "sender_wallet_id": { - "type": "integer" - }, - "type": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "verified": { - "type": "boolean" - } - } - }, - "handlers.UpdateCashOutReq": { - "type": "object", - "properties": { - "cashedOut": { - "type": "boolean" - } - } - }, "handlers.UpdateUserSuspendReq": { "type": "object", "required": [ @@ -15394,18 +4994,6 @@ const docTemplate = `{ } } }, - "handlers.UpdateWalletActiveReq": { - "type": "object", - "required": [ - "is_active" - ], - "properties": { - "is_active": { - "type": "boolean", - "example": true - } - } - }, "handlers.UserProfileRes": { "type": "object", "properties": { @@ -15453,79 +5041,6 @@ const docTemplate = `{ } } }, - "handlers.WalletRes": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "created_at": { - "type": "string" - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": true - }, - "is_bettable": { - "type": "boolean", - "example": true - }, - "is_transferable": { - "type": "boolean", - "example": true - }, - "is_withdraw": { - "type": "boolean", - "example": true - }, - "updated_at": { - "type": "string" - }, - "user_id": { - "type": "integer", - "example": 1 - } - } - }, - "handlers.launchVirtualGameReq": { - "type": "object", - "required": [ - "currency", - "game_id", - "mode" - ], - "properties": { - "currency": { - "type": "string", - "example": "USD" - }, - "game_id": { - "type": "string", - "example": "1" - }, - "mode": { - "type": "string", - "enum": [ - "fun", - "real" - ], - "example": "real" - } - } - }, - "handlers.launchVirtualGameRes": { - "type": "object", - "properties": { - "launch_url": { - "type": "string" - } - } - }, "handlers.loginAdminReq": { "type": "object", "required": [ @@ -15546,20 +5061,6 @@ const docTemplate = `{ } } }, - "handlers.loginAdminRes": { - "type": "object", - "properties": { - "access_token": { - "type": "string" - }, - "refresh_token": { - "type": "string" - }, - "role": { - "type": "string" - } - } - }, "handlers.loginCustomerReq": { "type": "object", "required": [ @@ -15748,8 +5249,8 @@ var SwaggerInfo = &swag.Spec{ Host: "", BasePath: "", Schemes: []string{}, - Title: "FortuneBet API", - Description: "This is server for FortuneBet.", + Title: "Yimaru API", + Description: "This is server for Yimaru.", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, LeftDelim: "{{", diff --git a/docs/swagger.json b/docs/swagger.json index 6d020f8..a4fe6e6 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1,8 +1,8 @@ { "swagger": "2.0", "info": { - "description": "This is server for FortuneBet.", - "title": "FortuneBet API", + "description": "This is server for Yimaru.", + "title": "Yimaru API", "termsOfService": "http://swagger.io/terms/", "contact": { "name": "API Support", @@ -16,52 +16,6 @@ "version": "1.0.1" }, "paths": { - "/account": { - "post": { - "description": "Callback endpoint for Atlas game server to fetch player balance", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Atlas Get User Data callback", - "parameters": [ - { - "description": "Get user data input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.AtlasGetUserDataRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.AtlasGetUserDataResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/admin": { "get": { "description": "Get all Admins", @@ -302,595 +256,6 @@ } } }, - "/api/v1/alea-games/launch": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "description": "Generates an authenticated launch URL for Alea Play virtual games", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Alea Virtual Games" - ], - "summary": "Launch an Alea Play virtual game", - "parameters": [ - { - "type": "string", - "description": "Game identifier (e.g., 'aviator', 'plinko')", - "name": "game_id", - "in": "query", - "required": true - }, - { - "enum": [ - "USD", - "EUR", - "GBP" - ], - "type": "string", - "default": "USD", - "description": "Currency code (ISO 4217)", - "name": "currency", - "in": "query" - }, - { - "enum": [ - "real", - "demo" - ], - "type": "string", - "default": "real", - "description": "Game mode", - "name": "mode", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Returns authenticated game launch URL", - "schema": { - "type": "object", - "additionalProperties": { - "allOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "launch_url": { - "type": "string" - } - } - } - ] - } - } - } - } - } - }, - "/api/v1/arifpay/b2c-webhook": { - "post": { - "description": "Handles webhook notifications from Arifpay for B2C transfers and updates transfer + wallet status.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Handle Arifpay B2C Webhook", - "parameters": [ - { - "description": "Arifpay webhook payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.WebhookRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/b2c/transfer": { - "post": { - "description": "Initiates a B2C transfer using Telebirr, CBE, or MPESA depending on the \"type\" query parameter", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Execute B2C Transfer", - "parameters": [ - { - "type": "string", - "description": "Transfer type (telebirr, cbe, mpesa)", - "name": "type", - "in": "query", - "required": true - }, - { - "description": "Transfer request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CheckoutSessionClientRequest" - } - } - ], - "responses": { - "200": { - "description": "message: transfer executed successfully", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "400": { - "description": "error: invalid request or unsupported transfer type", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "500": { - "description": "error: internal server error", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "/api/v1/arifpay/c2b-webhook": { - "post": { - "description": "Handles webhook notifications from Arifpay for C2B transfers and updates transfer + wallet status.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Handle Arifpay C2B Webhook", - "parameters": [ - { - "description": "Arifpay webhook payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.WebhookRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/checkout": { - "post": { - "description": "Creates a payment session using Arifpay and returns a redirect URL.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Create Arifpay Checkout Session", - "parameters": [ - { - "description": "Checkout session request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CheckoutSessionClientRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/checkout/cancel/{sessionId}": { - "post": { - "description": "Cancels a payment session using Arifpay before completion.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Cancel Arifpay Checkout Session", - "parameters": [ - { - "type": "string", - "description": "Checkout session ID", - "name": "sessionId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/payment-methods": { - "get": { - "description": "Returns all payment method IDs and names for Arifpay", - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "List Arifpay Payment Methods", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ARIFPAYPaymentMethod" - } - } - } - } - } - }, - "/api/v1/arifpay/session-id/verify-transaction/{session_id}": { - "get": { - "description": "Verifies an Arifpay transaction using a session ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Verify Arifpay Transaction by Session ID", - "parameters": [ - { - "type": "string", - "description": "Arifpay Session ID", - "name": "session_id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/arifpay/transaction-id/verify-transaction": { - "post": { - "description": "Verifies a transaction using transaction ID and payment type", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Arifpay" - ], - "summary": "Verify Arifpay Transaction", - "parameters": [ - { - "description": "Transaction verification payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ArifpayVerifyByTransactionIDRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/atlas/freespin": { - "post": { - "description": "Sends a request to Atlas to create free spins/bets for a given player", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Create free spins for a player", - "parameters": [ - { - "description": "Free spin input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FreeSpinRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.FreeSpinResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/atlas/games": { - "get": { - "description": "Retrieves available Atlas virtual games from the provider", - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "List Atlas virtual games", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AtlasGameEntity" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/atlas/init-game": { - "post": { - "description": "Initializes a game session for the given player using Atlas virtual game provider", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Start an Atlas virtual game session", - "parameters": [ - { - "description": "Start game input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.AtlasGameInitRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.AtlasGameInitResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/auth/logout": { "post": { "description": "Logout customer", @@ -1463,44 +828,6 @@ } } }, - "/api/v1/branch/{id}/bets": { - "get": { - "description": "Gets bets by its branch id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets bets by its branch id", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/branch/{id}/cashier": { "get": { "description": "Gets branch cashiers", @@ -1772,44 +1099,6 @@ } } }, - "/api/v1/branchWallet": { - "get": { - "description": "Retrieve all branch wallets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get all branch wallets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.WalletRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/cashier/{id}": { "get": { "description": "Get a single cashier by id", @@ -1860,56 +1149,6 @@ } } }, - "/api/v1/cashierWallet": { - "get": { - "description": "Get wallet for cashier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Get wallet for cashier", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.UserProfileRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/cashiers": { "get": { "description": "Get all cashiers", @@ -2077,546 +1316,6 @@ } } }, - "/api/v1/chapa/balances": { - "get": { - "description": "Retrieve Chapa account balance, optionally filtered by currency code (e.g., ETB, USD)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get Chapa account balance", - "parameters": [ - { - "type": "string", - "description": "Currency code (optional)", - "name": "currency_code", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/banks": { - "get": { - "description": "Get list of banks supported by Chapa", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get supported banks", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/payments/deposit": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Starts a new deposit process using Chapa payment gateway", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Initiate a deposit", - "parameters": [ - { - "description": "Deposit request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ChapaDepositRequestPayload" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ChapaDepositResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/payments/receipt/{chapa_ref}": { - "get": { - "description": "Retrieve the Chapa payment receipt URL using the reference ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get Chapa Payment Receipt URL", - "parameters": [ - { - "type": "string", - "description": "Chapa Reference ID", - "name": "chapa_ref", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/payments/webhook/verify": { - "post": { - "description": "Handles payment and transfer notifications from Chapa", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Chapa payment webhook callback (used by Chapa)", - "parameters": [ - { - "description": "Webhook payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ChapaWebhookPayment" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/payments/withdraw": { - "post": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Initiates a withdrawal request to transfer funds to a bank account via Chapa", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Initiate a withdrawal", - "parameters": [ - { - "description": "Withdrawal request details", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ChapaWithdrawalRequest" - } - } - ], - "responses": { - "200": { - "description": "Chapa withdrawal process initiated successfully", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Invalid request body", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "422": { - "description": "Unprocessable entity", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/swap": { - "post": { - "description": "Convert an amount from one currency to another using Chapa's currency swap API", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Swap currency using Chapa API", - "parameters": [ - { - "description": "Swap request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.SwapRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transaction/cancel/{tx_ref}": { - "put": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Cancels an active Chapa transaction using its transaction reference", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Cancel a Chapa deposit transaction", - "parameters": [ - { - "type": "string", - "description": "Transaction Reference", - "name": "tx_ref", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ChapaCancelResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transaction/events/{ref_id}": { - "get": { - "description": "Retrieve the timeline of events for a specific Chapa transaction", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Fetch transaction events", - "parameters": [ - { - "type": "string", - "description": "Transaction Reference", - "name": "ref_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ChapaTransactionEvent" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transaction/manual/verify/{tx_ref}": { - "get": { - "description": "Manually verify a payment using Chapa's API", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Verify a payment manually", - "parameters": [ - { - "type": "string", - "description": "Transaction Reference", - "name": "tx_ref", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transactions": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Retrieves all transactions from Chapa payment gateway", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get all Chapa transactions", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ChapaTransaction" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/chapa/transfers": { - "get": { - "description": "Retrieve all transfer records from Chapa", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Chapa" - ], - "summary": "Get all Chapa transfers", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/company": { "get": { "description": "Gets all companies", @@ -3134,1198 +1833,6 @@ } } }, - "/api/v1/customer/{id}/bets": { - "get": { - "description": "Get customer bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get customer bets", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/customerWallet": { - "get": { - "description": "Retrieve all customer wallets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get all customer wallets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.CustomerWalletRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/direct-deposits": { - "get": { - "description": "Fetches direct deposits filtered by status with pagination", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Get direct deposits by status", - "parameters": [ - { - "type": "string", - "description": "Deposit status (e.g., PENDING, APPROVED, REJECTED)", - "name": "status", - "in": "query", - "required": true - }, - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "pageSize", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.DirectDeposit" - } - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "post": { - "description": "Creates a direct deposit for a customer and notifies both the customer and admins", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Create a new direct deposit", - "parameters": [ - { - "description": "Direct deposit details", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateDirectDeposit" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.DirectDeposit" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/direct-deposits/{depositID}": { - "get": { - "description": "Fetches a single direct deposit by its ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Get a direct deposit by ID", - "parameters": [ - { - "type": "integer", - "description": "Deposit ID", - "name": "depositID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.DirectDeposit" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Deletes a direct deposit by its ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Delete a direct deposit", - "parameters": [ - { - "type": "integer", - "description": "Deposit ID", - "name": "depositID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "string" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/direct-deposits/{depositID}/approve": { - "post": { - "description": "Approves a direct deposit by admin and credits customer wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Approve a direct deposit", - "parameters": [ - { - "type": "integer", - "description": "Deposit ID", - "name": "depositID", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Admin ID performing the approval", - "name": "adminID", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "string" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/direct-deposits/{depositID}/reject": { - "post": { - "description": "Rejects a direct deposit by admin and notifies the customer", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DirectDeposit" - ], - "summary": "Reject a direct deposit", - "parameters": [ - { - "type": "integer", - "description": "Deposit ID", - "name": "depositID", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Admin ID performing the rejection", - "name": "adminID", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "Reason for rejection", - "name": "reason", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "string" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/betting-offers": { - "get": { - "description": "Fetches all EnetPulse preodds betting offers stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all betting offers", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreoddsBettingOffer" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/fixtures": { - "get": { - "description": "Fetches all fixtures stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all stored fixtures", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseFixture" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/fixtures/preodds": { - "get": { - "description": "Fetches all EnetPulse fixtures along with their associated pre-match odds", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get fixtures with preodds", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseFixtureWithPreodds" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/preodds": { - "get": { - "description": "Fetches all EnetPulse pre-match odds stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all preodds", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreodds" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/preodds-with-offers": { - "get": { - "description": "Fetches all EnetPulse pre-match odds along with their associated betting offers stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all preodds with betting offers", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreodds" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/results": { - "get": { - "description": "Fetches all EnetPulse match results stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all results", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseResult" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/sports": { - "get": { - "description": "Fetches all sports stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all sports", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseSport" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/tournament-stages": { - "get": { - "description": "Fetches all tournament stages stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all tournament stages", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseTournamentStage" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/tournament-templates": { - "get": { - "description": "Fetches all tournament templates stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all tournament templates", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseTournamentTemplate" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/enetpulse/tournaments": { - "get": { - "description": "Fetches all tournaments stored in the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "EnetPulse" - ], - "summary": "Get all tournaments", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulseTournament" - } - } - } - } - ] - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/events": { - "get": { - "description": "Retrieve all upcoming events from the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all upcoming events", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "League ID Filter", - "name": "league_id", - "in": "query" - }, - { - "type": "string", - "description": "Sport ID Filter", - "name": "sport_id", - "in": "query" - }, - { - "type": "string", - "description": "Country Code Filter", - "name": "cc", - "in": "query" - }, - { - "type": "string", - "description": "Start Time", - "name": "first_start_time", - "in": "query" - }, - { - "type": "string", - "description": "End Time", - "name": "last_start_time", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BaseEvent" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/events/{id}": { - "get": { - "description": "Retrieve an upcoming event by ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve an upcoming by ID", - "parameters": [ - { - "type": "string", - "description": "ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BaseEvent" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Set the event status to removed", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "event" - ], - "summary": "Set the event status to removed", - "parameters": [ - { - "type": "integer", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/events/{id}/bets": { - "get": { - "description": "Retrieve bet outcomes by event id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve bet outcomes by event id", - "parameters": [ - { - "type": "string", - "description": "ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BaseEvent" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/events/{id}/is_monitored": { - "patch": { - "description": "Update the event is_monitored", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "event" - ], - "summary": "update the event is_monitored", - "parameters": [ - { - "type": "integer", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/events/{id}/settings": { - "put": { - "description": "Update the event settings", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "event" - ], - "summary": "update the event settings", - "parameters": [ - { - "type": "integer", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/issues": { "get": { "description": "Admin endpoint to list all reported issues with pagination", @@ -4550,44 +2057,6 @@ } } }, - "/api/v1/leagues": { - "get": { - "description": "Gets all leagues", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "leagues" - ], - "summary": "Gets all leagues", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BaseLeague" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/logs": { "get": { "description": "Fetches application logs from MongoDB with pagination, level filtering, and search", @@ -4900,203 +2369,6 @@ } } }, - "/api/v1/market-settings": { - "get": { - "description": "Get all market settings that apply globally", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Retrieve all global market settings", - "parameters": [ - { - "type": "integer", - "description": "Number of results to return (default 10)", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to skip (default 0)", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.MarketSettings" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/odds": { - "get": { - "description": "Retrieve all odds from the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all odds", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.OddMarketFilter" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/odds/upcoming/{upcoming_id}": { - "get": { - "description": "Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve prematch odds by upcoming ID (FI)", - "parameters": [ - { - "type": "string", - "description": "Upcoming Event ID (FI)", - "name": "upcoming_id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Number of results to return (default: 10)", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to skip (default: 0)", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.OddMarketWithEventFilter" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/odds/upcoming/{upcoming_id}/market/{market_id}": { - "get": { - "description": "Retrieve raw odds records using a Market ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve raw odds by Market ID", - "parameters": [ - { - "type": "string", - "description": "Upcoming ID", - "name": "upcoming_id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Market ID", - "name": "market_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.RawOddsByMarketID" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/operation": { "post": { "description": "Creates a operation", @@ -5143,631 +2415,6 @@ } } }, - "/api/v1/orchestrator/virtual-game/provider-reports/asc": { - "get": { - "description": "Retrieves all virtual game provider reports sorted by total_games_played in ascending order", - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "List virtual game provider reports (ascending)", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.VirtualGameProviderReport" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/orchestrator/virtual-game/provider-reports/desc": { - "get": { - "description": "Retrieves all virtual game provider reports sorted by total_games_played in descending order", - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "List virtual game provider reports (descending)", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.VirtualGameProviderReport" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/orchestrator/virtual-games": { - "get": { - "description": "Returns all virtual games with optional filters (category, search, pagination)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "List all virtual games", - "parameters": [ - { - "type": "string", - "description": "Filter by category", - "name": "category", - "in": "query" - }, - { - "type": "string", - "description": "Search by game name", - "name": "search", - "in": "query" - }, - { - "type": "integer", - "description": "Pagination limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Pagination offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.UnifiedGame" - } - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/report-files/download/{filename}": { - "get": { - "description": "Downloads a generated report CSV file from the server", - "produces": [ - "text/csv" - ], - "tags": [ - "Reports" - ], - "summary": "Download a CSV report file", - "parameters": [ - { - "type": "string", - "description": "Name of the report file to download (e.g., report_daily_2025-06-21.csv)", - "name": "filename", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CSV file will be downloaded", - "schema": { - "type": "file" - } - }, - "400": { - "description": "Missing or invalid filename", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Report file not found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal server error while serving the file", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/report-files/list": { - "get": { - "description": "Returns a paginated list of generated report CSV files with search capability", - "produces": [ - "application/json" - ], - "tags": [ - "Reports" - ], - "summary": "List available report CSV files", - "parameters": [ - { - "type": "string", - "description": "Search term to filter filenames", - "name": "search", - "in": "query" - }, - { - "type": "integer", - "default": 1, - "description": "Page number (default: 1)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "default": 20, - "description": "Items per page (default: 20, max: 100)", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Paginated list of CSV report filenames", - "schema": { - "$ref": "#/definitions/domain.PaginatedFileResponse" - } - }, - "400": { - "description": "Invalid pagination parameters", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Failed to read report directory", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/reports/dashboard": { - "get": { - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Returns a comprehensive dashboard report with key metrics", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Reports" - ], - "summary": "Get dashboard report", - "parameters": [ - { - "type": "integer", - "description": "Company ID filter", - "name": "company_id", - "in": "query" - }, - { - "type": "integer", - "description": "Branch ID filter", - "name": "branch_id", - "in": "query" - }, - { - "type": "integer", - "description": "User ID filter", - "name": "user_id", - "in": "query" - }, - { - "type": "string", - "description": "Start time filter (RFC3339 format)", - "name": "start_time", - "in": "query" - }, - { - "type": "string", - "description": "End time filter (RFC3339 format)", - "name": "end_time", - "in": "query" - }, - { - "type": "string", - "description": "Sport ID filter", - "name": "sport_id", - "in": "query" - }, - { - "type": "integer", - "description": "Status filter (0=Pending, 1=Win, 2=Loss, 3=Half, 4=Void, 5=Error)", - "name": "status", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.DashboardSummary" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/result/b365/{id}": { - "get": { - "description": "Get results for an event", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "result" - ], - "summary": "Get results for an event", - "parameters": [ - { - "type": "string", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.ResultRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/santimpay/b2c-withdrawal": { - "post": { - "description": "Initiates a B2C withdrawal request with SantimPay and returns the response.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Process SantimPay B2C Withdrawal", - "parameters": [ - { - "description": "SantimPay B2C withdrawal request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GeneratePaymentURLRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/b2c/partners": { - "get": { - "description": "Fetches a list of available B2C payout partners (e.g., Telebirr, Mpesa, Banks) from SantimPay.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Get SantimPay B2C Partners", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/callback": { - "post": { - "description": "Processes a callback from SantimPay, updates transfer status, and credits user wallet if payment was successful.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Process SantimPay Payment Callback", - "parameters": [ - { - "description": "SantimPay callback payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.SantimPayCallbackPayload" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/direct-payment": { - "post": { - "description": "Initiates a direct payment request with SantimPay and returns the response.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Process SantimPay Direct Payment", - "parameters": [ - { - "description": "SantimPay direct payment request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GeneratePaymentURLRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/payment": { - "post": { - "description": "Generates a payment URL using SantimPay and returns it to the client.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Create SantimPay Payment Session", - "parameters": [ - { - "description": "SantimPay payment request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GeneratePaymentURLRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/santimpay/transaction-status": { - "post": { - "description": "Retrieves the real-time status of a transaction from SantimPay.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "SantimPay" - ], - "summary": "Check SantimPay Transaction Status", - "parameters": [ - { - "description": "Transaction status request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.TransactionStatusRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/search/branch": { "get": { "description": "Search branches by name or location", @@ -5853,581 +2500,6 @@ } } }, - "/api/v1/shop/bet": { - "get": { - "description": "Gets all the shop bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets all shop bets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ShopBetRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Create bet at branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Create bet at branch", - "parameters": [ - { - "description": "create bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ShopBetReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/bet/{id}": { - "get": { - "description": "Cashout bet at branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Cashout bet at branch", - "parameters": [ - { - "description": "cashout bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CashoutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/bet/{id}/cashout": { - "post": { - "description": "Cashout bet at branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Cashout bet at branch", - "parameters": [ - { - "description": "cashout bet", - "name": "cashoutBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CashoutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/cashout": { - "post": { - "description": "Cashout bet by cashoutID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Cashout bet by cashoutID", - "parameters": [ - { - "description": "cashout bet", - "name": "cashoutBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CashoutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/cashout/{id}": { - "get": { - "description": "Cashout bet at branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Cashout bet at branch", - "parameters": [ - { - "description": "cashout bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CashoutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/deposit": { - "post": { - "description": "Transfers money from branch wallet to customer wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Shop deposit into customer wallet", - "parameters": [ - { - "description": "ShopDepositReq", - "name": "transferToWallet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ShopDepositReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopDepositRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/transaction": { - "get": { - "description": "Gets all the transactions", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Gets all transactions", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/transaction/{id}": { - "get": { - "description": "Gets a single transaction by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Gets transaction by id", - "parameters": [ - { - "type": "integer", - "description": "Transaction ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Updates the verified status of a transaction", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Updates the verified field of a transaction", - "parameters": [ - { - "type": "integer", - "description": "Transaction ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Updates Transaction Verification", - "name": "updateVerified", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateTransactionVerifiedReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/shop/transaction/{id}/bet": { - "get": { - "description": "Gets a single shop bet by transaction id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transaction" - ], - "summary": "Gets shop bet by transaction id", - "parameters": [ - { - "type": "integer", - "description": "Transaction ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ShopTransactionRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/sport/bet/{id}": { - "get": { - "description": "Gets a single bet by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets bet by id", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Deletes bet by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Deletes bet by id", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/super-login": { "post": { "description": "Login super-admin", @@ -6456,7 +2528,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handlers.loginAdminRes" + "$ref": "#/definitions/handlers.LoginAdminRes" } }, "400": { @@ -6717,98 +2789,6 @@ } } }, - "/api/v1/telebirr/callback": { - "post": { - "description": "Processes the Telebirr payment result and updates wallet balance.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Telebirr" - ], - "summary": "Handle Telebirr Payment Callback", - "parameters": [ - { - "description": "Callback payload from Telebirr", - "name": "payload", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.TelebirrPaymentCallbackPayload" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/telebirr/payment": { - "post": { - "description": "Generates a payment URL using Telebirr and returns it to the client.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Telebirr" - ], - "summary": "Create Telebirr Payment Session", - "parameters": [ - { - "description": "Telebirr payment request payload", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GeneratePaymentURLRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/tenant": { "get": { "description": "Check if phone number or email exist", @@ -7010,144 +2990,6 @@ } } }, - "/api/v1/tenant/{tenant_slug}/customer/{id}/bets": { - "get": { - "description": "Get tenant customer bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get tenant customer bets", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/tenant/{tenant_slug}/events/{id}/bets": { - "get": { - "description": "Retrieve bet outcomes by event id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve bet outcomes by event id", - "parameters": [ - { - "type": "string", - "description": "ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BaseEvent" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/tenant/{tenant_slug}/events/{id}/settings": { - "put": { - "description": "Update the event settings", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "event" - ], - "summary": "update the event settings", - "parameters": [ - { - "type": "integer", - "description": "Event ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/tenant/{tenant_slug}/referral/stats": { "get": { "security": [ @@ -7188,226 +3030,6 @@ } } }, - "/api/v1/ticket": { - "get": { - "description": "Retrieve all tickets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Get all tickets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.TicketRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/ticket/{id}": { - "get": { - "description": "Retrieve ticket details by ticket ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Get ticket by ID", - "parameters": [ - { - "type": "integer", - "description": "Ticket ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.TicketRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/transfer/refill/:id": { - "post": { - "description": "Super Admin route to refill a wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transfer" - ], - "summary": "Refill wallet", - "parameters": [ - { - "description": "Create Transfer", - "name": "refillWallet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateTransferReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.TransferWalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/transfer/wallet/:id": { - "post": { - "description": "Create a transfer to wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transfer" - ], - "summary": "Create a transfer to wallet", - "parameters": [ - { - "description": "Create Transfer", - "name": "transferToWallet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateTransferReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.TransferWalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/transfer/wallet/{id}": { - "get": { - "description": "Get transfer by wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "transfer" - ], - "summary": "Get transfer by wallet", - "parameters": [ - { - "description": "Create Transfer", - "name": "transferToWallet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateTransferReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.TransferWalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/user/delete/{id}": { "delete": { "description": "Delete a user by their ID", @@ -7686,884 +3308,6 @@ } } }, - "/api/v1/veli/credit-balances": { - "get": { - "description": "Fetches current credit balances per currency for the specified brand", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get VeliGames credit balances for a brand", - "parameters": [ - { - "type": "string", - "description": "Brand ID", - "name": "brandId", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CreditBalance" - } - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/games-list": { - "post": { - "description": "Retrieves games for the specified provider", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get games by provider", - "parameters": [ - { - "description": "Brand and Provider ID", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GameListRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/gaming-activity": { - "post": { - "description": "Retrieves successfully processed gaming activity transactions (BET, WIN, CANCEL) from Veli Games", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get Veli Gaming Activity", - "parameters": [ - { - "description": "Gaming Activity Request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GamingActivityRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.GamingActivityResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/huge-wins": { - "post": { - "description": "Retrieves huge win transactions based on brand configuration (e.g. win \u003e 10000 USD or 100x bet)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get Veli Huge Wins", - "parameters": [ - { - "description": "Huge Wins Request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.HugeWinsRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.HugeWinsResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/providers": { - "post": { - "description": "Retrieves the list of VeliGames providers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Get game providers", - "parameters": [ - { - "description": "Brand ID and paging options", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ProviderRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ProviderResponse" - } - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/start-demo-game": { - "post": { - "description": "Starts a demo session of the specified game (must support demo mode)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Start a demo game session", - "parameters": [ - { - "description": "Start demo game input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.DemoGameRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.GameStartResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/veli/start-game": { - "post": { - "description": "Starts a real VeliGames session with the given player and game info", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - VeliGames" - ], - "summary": "Start a real game session", - "parameters": [ - { - "description": "Start game input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.GameStartRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.GameStartResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/favorites": { - "get": { - "description": "Lists the games that the user marked as favorite", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Favourites" - ], - "summary": "Get user's favorite games", - "parameters": [ - { - "type": "string", - "description": "Filter by provider ID", - "name": "providerID", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to return", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Results offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.UnifiedGame" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "post": { - "description": "Adds a game to the user's favorite games list", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Favourites" - ], - "summary": "Add game to favorites", - "parameters": [ - { - "description": "Game ID and Provider ID to add", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FavoriteGameRequest" - } - } - ], - "responses": { - "201": { - "description": "created", - "schema": { - "type": "string" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/favorites/{gameID}": { - "delete": { - "description": "Removes a game from the user's favorites", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Favourites" - ], - "summary": "Remove game from favorites", - "parameters": [ - { - "type": "integer", - "description": "Game ID to remove", - "name": "gameID", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Provider ID of the game", - "name": "providerID", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "removed", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/orchestrator/providers/status": { - "patch": { - "description": "Sets the enabled status of a provider", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "Enable/Disable a provider", - "parameters": [ - { - "type": "string", - "description": "Provider ID", - "name": "provider_id", - "in": "path", - "required": true - }, - { - "type": "boolean", - "description": "Enable or Disable", - "name": "enabled", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.VirtualGameProvider" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/providers": { - "get": { - "description": "Lists all providers with pagination", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "List virtual game providers", - "parameters": [ - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "additionalProperties": true - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/virtual-game/providers/{provider_id}": { - "get": { - "description": "Fetches a provider by provider_id", - "produces": [ - "application/json" - ], - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "Get a virtual game provider", - "parameters": [ - { - "type": "string", - "description": "Provider ID", - "name": "provider_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.VirtualGameProvider" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Deletes a provider by provider_id", - "tags": [ - "VirtualGames - Orchestration" - ], - "summary": "Remove a virtual game provider", - "parameters": [ - { - "type": "string", - "description": "Provider ID", - "name": "provider_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/wallet": { - "get": { - "description": "Retrieve all wallets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get all wallets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.WalletRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/wallet/{id}": { - "get": { - "description": "Retrieve wallet details by wallet ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get wallet by ID", - "parameters": [ - { - "type": "integer", - "description": "Wallet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.WalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "patch": { - "description": "Can activate and deactivate wallet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Activate and Deactivate Wallet", - "parameters": [ - { - "type": "integer", - "description": "Wallet ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update Wallet Active", - "name": "updateCashOut", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.UpdateWalletActiveReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/webhooks/alea": { - "post": { - "description": "Handles webhook callbacks from Alea Play virtual games for bet settlement", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Alea Virtual Games" - ], - "summary": "Process Alea Play game callback", - "parameters": [ - { - "description": "Callback payload", - "name": "callback", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.AleaPlayCallback" - } - } - ], - "responses": { - "200": { - "description": "Callback processed successfully", - "schema": { - "type": "object", - "additionalProperties": { - "allOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "status": { - "type": "string" - } - } - } - ] - } - } - } - } - } - }, "/api/v1/{tenant_slug}/admin-login": { "post": { "description": "Login customer", @@ -8592,7 +3336,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handlers.loginAdminRes" + "$ref": "#/definitions/handlers.LoginAdminRes" } }, "400": { @@ -8668,1057 +3412,6 @@ } } }, - "/api/v1/{tenant_slug}/events": { - "get": { - "description": "Retrieve all upcoming events settings from the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all upcoming events with settings", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "League ID Filter", - "name": "league_id", - "in": "query" - }, - { - "type": "string", - "description": "Sport ID Filter", - "name": "sport_id", - "in": "query" - }, - { - "type": "string", - "description": "Country Code Filter", - "name": "cc", - "in": "query" - }, - { - "type": "string", - "description": "Start Time", - "name": "first_start_time", - "in": "query" - }, - { - "type": "string", - "description": "End Time", - "name": "last_start_time", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BaseEvent" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/leagues": { - "get": { - "description": "Gets all leagues", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "leagues" - ], - "summary": "Gets all leagues", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BaseLeague" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/leagues/{id}/featured": { - "put": { - "description": "Set the league to featured/un-featured", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "leagues" - ], - "summary": "Set the league to featured/un-featured", - "parameters": [ - { - "type": "integer", - "description": "League ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "League Featured Request", - "name": "active", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.SetLeagueAsFeatured" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/leagues/{id}/set-active": { - "put": { - "description": "Set the league to active", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "leagues" - ], - "summary": "Set the league to active", - "parameters": [ - { - "type": "integer", - "description": "League ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "League Active Request", - "name": "active", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.SetLeagueActiveReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/market-settings": { - "get": { - "description": "Get all market settings overridden for a specific tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Retrieve all market settings for a tenant", - "parameters": [ - { - "type": "integer", - "description": "Number of results to return (default 10)", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to skip (default 0)", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CompanyMarketSettings" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Insert new market settings for a specific tenant/company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Insert company-specific market settings", - "parameters": [ - { - "description": "Market Settings", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateCompanyMarketSettings" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Remove all overridden market settings for a specific tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Delete all market settings for a tenant", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/market-settings/{id}": { - "delete": { - "description": "Remove a specific overridden market setting for a tenant", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "market_settings" - ], - "summary": "Delete a specific market setting for a tenant", - "parameters": [ - { - "type": "integer", - "description": "Market ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/odds": { - "get": { - "description": "Retrieve all odds from the database", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all odds", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.OddMarketFilter" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/odds/upcoming/{upcoming_id}": { - "get": { - "description": "Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve prematch odds by upcoming ID (FI)", - "parameters": [ - { - "type": "string", - "description": "Upcoming Event ID (FI)", - "name": "upcoming_id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Number of results to return (default: 10)", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Number of results to skip (default: 0)", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.OddMarketFilter" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/odds/upcoming/{upcoming_id}/market/{market_id}": { - "get": { - "description": "Retrieve raw odds records using a Market ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve raw odds by Market ID", - "parameters": [ - { - "type": "string", - "description": "Upcoming ID", - "name": "upcoming_id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Market ID", - "name": "market_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.RawOddsByMarketID" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/bet": { - "get": { - "description": "Gets all the bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets all bets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a bet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Create a bet", - "parameters": [ - { - "description": "Creates bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBetReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/bet/fastcode": { - "post": { - "description": "Creates a bet with fast code", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Create a bet with fast code", - "parameters": [ - { - "description": "Creates bet", - "name": "createBetWithFastCode", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBetWithFastCodeReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/bet/fastcode/{fast_code}": { - "get": { - "description": "Gets a single bet by fast_code", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets bet by fast_code", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "fast_code", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/bet/{id}": { - "get": { - "description": "Gets a single bet by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Gets bet by id", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Deletes bet by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Deletes bet by id", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "patch": { - "description": "Updates the cashed out field", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Updates the cashed out field", - "parameters": [ - { - "type": "integer", - "description": "Bet ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Updates Cashed Out", - "name": "updateCashOut", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.UpdateCashOutReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/sport/random/bet": { - "post": { - "description": "Generate a random bet", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "bet" - ], - "summary": "Generate a random bet", - "parameters": [ - { - "description": "Create Random bet", - "name": "createBet", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.RandomBetReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BetRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/ticket": { - "get": { - "description": "Retrieve all tickets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Get all tickets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.TicketRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a temporary ticket", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Create a temporary ticket", - "parameters": [ - { - "description": "Creates ticket", - "name": "createTicket", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateTicketReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CreateTicketRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/ticket/{id}": { - "get": { - "description": "Retrieve ticket details by ticket ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "ticket" - ], - "summary": "Get ticket by ID", - "parameters": [ - { - "type": "integer", - "description": "Ticket ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.TicketRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/top-leagues": { - "get": { - "description": "Retrieve all top leagues", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve all top leagues", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.TopLeague" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/{tenant_slug}/user/admin-profile": { "get": { "security": [ @@ -9759,44 +3452,6 @@ } } }, - "/api/v1/{tenant_slug}/user/bets": { - "get": { - "description": "Gets user bets", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Gets user bets", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/{tenant_slug}/user/checkPhoneEmailExist": { "post": { "description": "Check if phone number or email exist", @@ -10112,765 +3767,9 @@ } } } - }, - "/api/v1/{tenant_slug}/user/wallet": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "description": "Retrieve customer wallet details", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "wallet" - ], - "summary": "Get customer wallet", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "company_id", - "in": "header", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomerWalletRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}events/{id}": { - "get": { - "description": "Retrieve an upcoming event by ID", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "prematch" - ], - "summary": "Retrieve an upcoming by ID", - "parameters": [ - { - "type": "string", - "description": "ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BaseEvent" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/betwin": { - "post": { - "description": "Processes a Bet and Win request from Atlas provider", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Atlas BetWin callback", - "parameters": [ - { - "description": "Atlas BetWin input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.AtlasBetWinRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.AtlasBetWinResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/freespin": { - "post": { - "description": "Handles the result of a free spin/bet from the game server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Free Spin/Bet result callback", - "parameters": [ - { - "description": "Free spin result input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FreeSpinResultRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.FreeSpinResultResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/jackpot": { - "post": { - "description": "Handles the jackpot result from the game server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Jackpot result callback", - "parameters": [ - { - "description": "Jackpot result input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.JackpotRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.JackpotResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/popok/games": { - "get": { - "description": "Retrieves the list of available PopOK slot games", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - PopOK" - ], - "summary": "Get PopOK Games List", - "parameters": [ - { - "type": "string", - "default": "USD", - "description": "Currency (e.g. USD, ETB)", - "name": "currency", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.PopOKGame" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/popok/games/recommend": { - "get": { - "description": "Recommends games based on user history or randomly", - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - PopOK" - ], - "summary": "Recommend virtual games", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "user_id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.GameRecommendation" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/result": { - "post": { - "description": "Processes a round result from Atlas or other providers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Atlas Round Result callback", - "parameters": [ - { - "description": "Round result input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.RoundResultRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.RoundResultResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/rollback": { - "post": { - "description": "Processes a rollback request from Atlas or other providers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - Atlas" - ], - "summary": "Atlas Rollback callback", - "parameters": [ - { - "description": "Rollback request input", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.RollbackRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.RollbackResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "502": { - "description": "Bad Gateway", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/virtual-game/callback": { - "post": { - "description": "Processes callbacks from PopOK for game events", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - PopOK" - ], - "summary": "Handle PopOK game callback", - "parameters": [ - { - "description": "Callback data", - "name": "callback", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.PopOKCallback" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/virtual-game/launch": { - "post": { - "security": [ - { - "Bearer": [] - } - ], - "description": "Generates a URL to launch a PopOK game", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Virtual Games - PopOK" - ], - "summary": "Launch a PopOK virtual game", - "parameters": [ - { - "description": "Game launch details", - "name": "launch", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.launchVirtualGameReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.launchVirtualGameRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/win": { - "post": { - "description": "Processes win callbacks from either Veli or PopOK providers by auto-detecting the format", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Wins" - ], - "summary": "Handle win callback (Veli or PopOK)", - "responses": { - "200": { - "description": "Win processing result", - "schema": {} - }, - "400": { - "description": "Invalid request format", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "401": { - "description": "Authentication failed", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "409": { - "description": "Duplicate transaction", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } } }, "definitions": { - "domain.ARIFPAYPaymentMethod": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - } - } - }, - "domain.AleaPlayCallback": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "event_id": { - "type": "string" - }, - "game_id": { - "type": "string" - }, - "is_free_round": { - "type": "boolean" - }, - "multiplier": { - "type": "number" - }, - "operator_id": { - "type": "string" - }, - "round_id": { - "type": "string" - }, - "session_id": { - "type": "string" - }, - "signature": { - "type": "string" - }, - "timestamp": { - "type": "integer" - }, - "transaction_id": { - "type": "string" - }, - "type": { - "description": "BET, WIN, CASHOUT, etc.", - "type": "string" - }, - "user_id": { - "type": "string" - } - } - }, - "domain.ArifpayVerifyByTransactionIDRequest": { - "type": "object", - "properties": { - "paymentType": { - "type": "integer" - }, - "transactionId": { - "type": "string" - } - } - }, - "domain.AtlasBetWinRequest": { - "type": "object", - "properties": { - "betAmount": { - "type": "number" - }, - "casino_id": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "string" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - }, - "transaction_id": { - "type": "string" - }, - "winAmount": { - "type": "number" - } - } - }, - "domain.AtlasBetWinResponse": { - "type": "object", - "properties": { - "balance": { - "type": "number" - }, - "player_id": { - "type": "string" - } - } - }, - "domain.AtlasGameEntity": { - "type": "object", - "properties": { - "demo_url": { - "description": "✅ new field", - "type": "string" - }, - "deviceType": { - "type": "string" - }, - "game_id": { - "type": "string" - }, - "hasFreeBets": { - "type": "boolean" - }, - "has_demo": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "thumbnail_img_url": { - "description": "✅ new field", - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "domain.AtlasGameInitRequest": { - "type": "object", - "properties": { - "currency": { - "type": "string" - }, - "game": { - "type": "string" - }, - "language": { - "type": "string" - }, - "player_id": { - "type": "string" - } - } - }, - "domain.AtlasGameInitResponse": { - "type": "object", - "properties": { - "url": { - "type": "string" - } - } - }, - "domain.AtlasGetUserDataRequest": { - "type": "object", - "properties": { - "casino_id": { - "type": "string" - }, - "game": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "session_id": { - "type": "string" - } - } - }, - "domain.AtlasGetUserDataResponse": { - "type": "object", - "properties": { - "balance": { - "type": "number" - }, - "player_id": { - "type": "string" - } - } - }, "domain.Bank": { "type": "object", "properties": { @@ -10924,264 +3823,6 @@ } } }, - "domain.BaseEvent": { - "type": "object", - "properties": { - "addedTime": { - "$ref": "#/definitions/domain.ValidInt" - }, - "avgBetAmount": { - "type": "integer" - }, - "awayTeam": { - "type": "string" - }, - "awayTeamID": { - "type": "integer" - }, - "awayTeamImage": { - "type": "string" - }, - "defaultIsActive": { - "type": "boolean" - }, - "defaultIsFeatured": { - "type": "boolean" - }, - "defaultWinningUpperLimit": { - "type": "integer" - }, - "fetchedAt": { - "type": "string" - }, - "homeTeam": { - "type": "string" - }, - "homeTeamID": { - "type": "integer" - }, - "homeTeamImage": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "isLive": { - "type": "boolean" - }, - "isMonitored": { - "type": "boolean" - }, - "leagueCC": { - "$ref": "#/definitions/domain.ValidString" - }, - "leagueID": { - "type": "integer" - }, - "leagueName": { - "type": "string" - }, - "matchMinute": { - "$ref": "#/definitions/domain.ValidInt" - }, - "matchName": { - "type": "string" - }, - "matchPeriod": { - "$ref": "#/definitions/domain.ValidInt" - }, - "numberOfBets": { - "type": "integer" - }, - "score": { - "$ref": "#/definitions/domain.ValidString" - }, - "source": { - "$ref": "#/definitions/domain.EventSource" - }, - "sourceEventID": { - "type": "string" - }, - "sportID": { - "type": "integer" - }, - "startTime": { - "type": "string" - }, - "status": { - "$ref": "#/definitions/domain.EventStatus" - }, - "timerStatus": { - "$ref": "#/definitions/domain.ValidString" - }, - "totalAmount": { - "type": "integer" - }, - "totalOddOutcomes": { - "type": "integer" - }, - "totalPotentialWinnings": { - "type": "integer" - } - } - }, - "domain.BaseLeague": { - "type": "object", - "properties": { - "bet365ID": { - "$ref": "#/definitions/domain.ValidInt32" - }, - "countryCode": { - "$ref": "#/definitions/domain.ValidString" - }, - "defaultIsActive": { - "type": "boolean" - }, - "defaultIsFeatured": { - "type": "boolean" - }, - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "sportID": { - "type": "integer" - } - } - }, - "domain.BetOutcome": { - "type": "object", - "properties": { - "away_team_name": { - "type": "string", - "example": "Liverpool" - }, - "bet_id": { - "type": "integer", - "example": 1 - }, - "event_id": { - "type": "integer", - "example": 1 - }, - "expires": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - }, - "home_team_name": { - "type": "string", - "example": "Manchester" - }, - "id": { - "type": "integer", - "example": 1 - }, - "market_id": { - "type": "integer", - "example": 1 - }, - "market_name": { - "type": "string", - "example": "Fulltime Result" - }, - "odd": { - "type": "number", - "example": 1.5 - }, - "odd_handicap": { - "type": "string", - "example": "1" - }, - "odd_header": { - "type": "string", - "example": "1" - }, - "odd_id": { - "type": "integer", - "example": 1 - }, - "odd_name": { - "type": "string", - "example": "1" - }, - "sport_id": { - "type": "integer", - "example": 1 - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/domain.OutcomeStatus" - } - ], - "example": 1 - } - } - }, - "domain.BetRes": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "cashed_out": { - "type": "boolean", - "example": false - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "company_slug": { - "type": "string", - "example": "fortune" - }, - "created_at": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - }, - "fast_code": { - "type": "string" - }, - "full_name": { - "type": "string", - "example": "John Smith" - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_shop_bet": { - "type": "boolean", - "example": false - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetOutcome" - } - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/domain.OutcomeStatus" - } - ], - "example": 1 - }, - "total_odds": { - "type": "number", - "example": 4.22 - }, - "user_id": { - "type": "integer", - "example": 2 - } - } - }, "domain.BranchDetailRes": { "type": "object", "properties": { @@ -11337,301 +3978,6 @@ } } }, - "domain.CashoutReq": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "bank_code": { - "type": "string" - }, - "beneficiary_name": { - "type": "string" - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "cashout_id": { - "type": "string", - "example": "1234" - }, - "payment_option": { - "allOf": [ - { - "$ref": "#/definitions/domain.PaymentOption" - } - ], - "example": 1 - }, - "reference_number": { - "type": "string" - } - } - }, - "domain.ChapaCancelResponse": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "message": { - "type": "string" - }, - "status": { - "type": "string" - }, - "tx_ref": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.ChapaCustomer": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "first_name": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_name": { - "type": "string" - }, - "mobile": { - "type": "string" - } - } - }, - "domain.ChapaDepositRequestPayload": { - "type": "object", - "required": [ - "amount" - ], - "properties": { - "amount": { - "type": "number" - } - } - }, - "domain.ChapaDepositResponse": { - "type": "object", - "properties": { - "checkoutURL": { - "type": "string" - }, - "reference": { - "type": "string" - } - } - }, - "domain.ChapaTransaction": { - "type": "object", - "properties": { - "amount": { - "type": "string" - }, - "charge": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "customer": { - "$ref": "#/definitions/domain.ChapaCustomer" - }, - "payment_method": { - "type": "string" - }, - "ref_id": { - "type": "string" - }, - "status": { - "type": "string" - }, - "trans_id": { - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "domain.ChapaTransactionEvent": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "item": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "type": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.ChapaWebhookCustomization": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "logo": { - "type": "string" - }, - "title": { - "type": "string" - } - } - }, - "domain.ChapaWebhookPayment": { - "type": "object", - "properties": { - "amount": { - "type": "string" - }, - "charge": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "customization": { - "$ref": "#/definitions/domain.ChapaWebhookCustomization" - }, - "email": { - "type": "string" - }, - "event": { - "type": "string" - }, - "first_name": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "meta": { - "description": "may vary in structure, so kept flexible" - }, - "mobile": { - "type": "string" - }, - "mode": { - "type": "string" - }, - "payment_method": { - "type": "string" - }, - "reference": { - "type": "string" - }, - "status": { - "type": "string" - }, - "tx_ref": { - "type": "string" - }, - "type": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.ChapaWithdrawalRequest": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "amount": { - "description": "string because Chapa API uses string for monetary values", - "type": "string" - }, - "bank_code": { - "type": "integer" - }, - "currency": { - "type": "string" - }, - "reference": { - "type": "string" - } - } - }, - "domain.CheckoutSessionClientRequest": { - "type": "object", - "required": [ - "amount", - "customerEmail", - "customerPhone" - ], - "properties": { - "amount": { - "type": "number" - }, - "customerEmail": { - "type": "string" - }, - "customerPhone": { - "type": "string" - } - } - }, - "domain.CompanyMarketSettings": { - "type": "object", - "properties": { - "companyID": { - "type": "integer" - }, - "isActive": { - "$ref": "#/definitions/domain.ValidBool" - }, - "marketID": { - "type": "integer" - }, - "marketName": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - } - }, "domain.CompanyRes": { "type": "object", "properties": { @@ -11665,60 +4011,6 @@ } } }, - "domain.CreateBetOutcomeReq": { - "type": "object", - "properties": { - "event_id": { - "type": "integer", - "example": 1 - }, - "market_id": { - "type": "integer", - "example": 1 - }, - "odd_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CreateBetReq": { - "type": "object", - "required": [ - "amount", - "outcomes" - ], - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CreateBetOutcomeReq" - } - } - } - }, - "domain.CreateBetWithFastCodeReq": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "branch_id": { - "type": "integer" - }, - "fast_code": { - "type": "string" - } - } - }, "domain.CreateBranchOperationReq": { "type": "object", "properties": { @@ -11777,23 +4069,6 @@ } } }, - "domain.CreateCompanyMarketSettings": { - "type": "object", - "properties": { - "companyID": { - "type": "integer" - }, - "isActive": { - "$ref": "#/definitions/domain.ValidBool" - }, - "marketID": { - "type": "integer" - }, - "marketName": { - "type": "string" - } - } - }, "domain.CreateCompanyReq": { "type": "object", "properties": { @@ -11817,35 +4092,6 @@ } } }, - "domain.CreateDirectDeposit": { - "type": "object", - "properties": { - "accountHolder": { - "type": "string" - }, - "accountNumber": { - "type": "string" - }, - "amount": { - "type": "number" - }, - "bankName": { - "type": "string" - }, - "customerID": { - "type": "integer" - }, - "referenceNumber": { - "type": "string" - }, - "transferScreenshot": { - "type": "string" - }, - "walletID": { - "type": "integer" - } - } - }, "domain.CreateSupportedOperationReq": { "type": "object", "properties": { @@ -11859,733 +4105,6 @@ } } }, - "domain.CreateTicketOutcomeReq": { - "type": "object", - "properties": { - "event_id": { - "description": "TicketID int64 `json:\"ticket_id\" example:\"1\"`", - "type": "integer", - "example": 1 - }, - "market_id": { - "type": "integer", - "example": 1 - }, - "odd_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CreateTicketReq": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CreateTicketOutcomeReq" - } - } - } - }, - "domain.CreateTicketRes": { - "type": "object", - "properties": { - "created_number": { - "type": "integer", - "example": 3 - }, - "fast_code": { - "type": "integer", - "example": 1234 - } - } - }, - "domain.CreditBalance": { - "type": "object", - "properties": { - "balance": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "threshold": { - "type": "number" - } - } - }, - "domain.DashboardSummary": { - "type": "object", - "properties": { - "active_admins": { - "type": "integer" - }, - "active_bets": { - "type": "integer" - }, - "active_branches": { - "type": "integer" - }, - "active_cashiers": { - "type": "integer" - }, - "active_companies": { - "type": "integer" - }, - "active_customers": { - "type": "integer" - }, - "active_games": { - "type": "integer" - }, - "active_managers": { - "type": "integer" - }, - "average_stake": { - "type": "integer" - }, - "branches_count": { - "type": "integer" - }, - "customer_count": { - "type": "integer" - }, - "inactive_admins": { - "type": "integer" - }, - "inactive_branches": { - "type": "integer" - }, - "inactive_cashiers": { - "type": "integer" - }, - "inactive_companies": { - "type": "integer" - }, - "inactive_customers": { - "type": "integer" - }, - "inactive_games": { - "type": "integer" - }, - "inactive_managers": { - "type": "integer" - }, - "profit": { - "type": "integer" - }, - "read_notifications": { - "type": "integer" - }, - "total_admins": { - "type": "integer" - }, - "total_bets": { - "type": "integer" - }, - "total_cashiers": { - "type": "integer" - }, - "total_companies": { - "type": "integer" - }, - "total_deposits": { - "type": "integer" - }, - "total_games": { - "type": "integer" - }, - "total_losses": { - "type": "integer" - }, - "total_managers": { - "type": "integer" - }, - "total_notifications": { - "type": "integer" - }, - "total_stakes": { - "type": "integer" - }, - "total_wallets": { - "type": "integer" - }, - "total_wins": { - "type": "integer" - }, - "total_withdrawals": { - "type": "integer" - }, - "unread_notifications": { - "type": "integer" - }, - "win_balance": { - "type": "integer" - }, - "win_rate": { - "type": "number" - } - } - }, - "domain.DemoGameRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "deviceType": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "language": { - "type": "string" - }, - "providerId": { - "type": "string" - } - } - }, - "domain.DirectDeposit": { - "type": "object", - "properties": { - "accountHolder": { - "type": "string" - }, - "accountNumber": { - "type": "string" - }, - "amount": { - "type": "number" - }, - "approvedAt": { - "type": "string" - }, - "approvedBy": { - "type": "integer" - }, - "bankName": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "customerID": { - "type": "integer" - }, - "id": { - "type": "integer" - }, - "referenceNumber": { - "type": "string" - }, - "rejectionReason": { - "type": "string" - }, - "status": { - "type": "string" - }, - "transferScreenshot": { - "type": "string" - }, - "walletID": { - "type": "integer" - } - } - }, - "domain.EnetpulseFixture": { - "type": "object", - "properties": { - "gender": { - "type": "string" - }, - "id": { - "type": "string" - }, - "n": { - "description": "convert to int", - "type": "string" - }, - "name": { - "type": "string" - }, - "round_typeFK": { - "type": "string" - }, - "sportFK": { - "type": "string" - }, - "sport_name": { - "type": "string" - }, - "startdate": { - "description": "ISO 8601", - "type": "string" - }, - "status_descFK": { - "type": "string" - }, - "status_type": { - "type": "string" - }, - "tournamentFK": { - "type": "string" - }, - "tournament_name": { - "type": "string" - }, - "tournament_stage_name": { - "description": "TournamentStageFK string `json:\"tournament_stageFK\"`", - "type": "string" - }, - "tournament_templateFK": { - "type": "string" - }, - "tournament_template_name": { - "type": "string" - }, - "ut": { - "description": "parse to time.Time", - "type": "string" - } - } - }, - "domain.EnetpulseFixtureWithPreodds": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "fixtureApiID": { - "type": "string" - }, - "fixtureID": { - "type": "string" - }, - "fixtureName": { - "type": "string" - }, - "lastUpdatedAt": { - "type": "string" - }, - "preodds": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreodds" - } - }, - "roundTypeFk": { - "type": "string" - }, - "sportFk": { - "type": "string" - }, - "startDate": { - "type": "string" - }, - "statusDescFk": { - "type": "string" - }, - "statusType": { - "type": "string" - }, - "tournamentFk": { - "type": "string" - }, - "tournamentStageFk": { - "type": "string" - }, - "tournamentTemplateFk": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updatesCount": { - "type": "integer" - } - } - }, - "domain.EnetpulsePreodds": { - "type": "object", - "properties": { - "bettingOffers": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EnetpulsePreoddsBettingOffer" - } - }, - "createdAt": { - "type": "string" - }, - "dparam": { - "type": "string" - }, - "dparam2": { - "type": "string" - }, - "eventFK": { - "type": "integer" - }, - "eventParticipantNumber": { - "type": "integer" - }, - "id": { - "type": "integer" - }, - "iparam": { - "type": "string" - }, - "iparam2": { - "type": "string" - }, - "lastUpdatedAt": { - "type": "string" - }, - "outcomeScopeFK": { - "type": "integer" - }, - "outcomeSubtypeFK": { - "type": "integer" - }, - "outcomeTypeFK": { - "type": "integer" - }, - "preoddsID": { - "type": "string" - }, - "sparam": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updatesCount": { - "type": "integer" - } - } - }, - "domain.EnetpulsePreoddsBettingOffer": { - "type": "object", - "properties": { - "active": { - "type": "string" - }, - "betting_offer_id": { - "type": "string" - }, - "betting_offer_status_fk": { - "type": "integer" - }, - "coupon_key": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_updated_at": { - "type": "string" - }, - "odds": { - "type": "number" - }, - "odds_old": { - "type": "number" - }, - "odds_provider_fk": { - "type": "integer" - }, - "preodds_fk": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "type": "integer" - } - } - }, - "domain.EnetpulseResult": { - "type": "object", - "properties": { - "commentary": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "first_half_ended": { - "type": "string" - }, - "game_ended": { - "type": "string" - }, - "game_started": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_updated_at": { - "type": "string" - }, - "lineup_confirmed": { - "type": "boolean" - }, - "live": { - "type": "string" - }, - "livestats_plus": { - "type": "string" - }, - "livestats_type": { - "type": "string" - }, - "name": { - "type": "string" - }, - "result_id": { - "type": "string" - }, - "round": { - "type": "string" - }, - "round_type_fk": { - "type": "string" - }, - "second_half_ended": { - "type": "string" - }, - "second_half_started": { - "type": "string" - }, - "spectators": { - "type": "integer" - }, - "sport_fk": { - "type": "string" - }, - "sport_name": { - "type": "string" - }, - "start_date": { - "type": "string" - }, - "status_desc_fk": { - "type": "string" - }, - "status_type": { - "type": "string" - }, - "tournament_fk": { - "type": "string" - }, - "tournament_name": { - "type": "string" - }, - "tournament_stage_name": { - "description": "TournamentStageFK string `json:\"tournament_stage_fk\"`", - "type": "string" - }, - "tournament_template_fk": { - "type": "string" - }, - "tournament_template_name": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "type": "integer" - }, - "venue_name": { - "type": "string" - }, - "verified": { - "type": "boolean" - } - } - }, - "domain.EnetpulseSport": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "description": "DB primary key", - "type": "integer" - }, - "last_updated_at": { - "type": "string" - }, - "name": { - "description": "from API \"name\"", - "type": "string" - }, - "sport_id": { - "description": "from API \"id\"", - "type": "string" - }, - "status": { - "description": "active/inactive", - "type": "integer" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "description": "from API \"n\"", - "type": "integer" - } - } - }, - "domain.EnetpulseTournament": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "id": { - "description": "internal DB PK", - "type": "integer" - }, - "lastUpdatedAt": { - "type": "string" - }, - "name": { - "type": "string" - }, - "status": { - "type": "integer" - }, - "tournamentID": { - "type": "string" - }, - "tournamentTemplateFK": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updatesCount": { - "type": "integer" - } - } - }, - "domain.EnetpulseTournamentStage": { - "type": "object", - "properties": { - "country_fk": { - "description": "country FK from API", - "type": "string" - }, - "country_name": { - "description": "country name from API", - "type": "string" - }, - "created_at": { - "type": "string" - }, - "end_date": { - "description": "end date/time", - "type": "string" - }, - "gender": { - "description": "male/female/mixed/unknown", - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_updated_at": { - "description": "ut from API", - "type": "string" - }, - "name": { - "description": "API name", - "type": "string" - }, - "stage_id": { - "description": "API id", - "type": "string" - }, - "start_date": { - "description": "start date/time", - "type": "string" - }, - "status": { - "description": "active/inactive", - "type": "integer" - }, - "tournament_fk": { - "description": "Foreign key to tournament", - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "description": "n from API", - "type": "integer" - } - } - }, - "domain.EnetpulseTournamentTemplate": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "gender": { - "description": "male, female, mixed, unknown", - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_updated_at": { - "type": "string" - }, - "name": { - "description": "from API \"name\"", - "type": "string" - }, - "sport_fk": { - "description": "related sport id", - "type": "string" - }, - "status": { - "description": "optional", - "type": "integer" - }, - "template_id": { - "description": "from API \"id\"", - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "updates_count": { - "description": "from API \"n\"", - "type": "integer" - } - } - }, "domain.ErrorResponse": { "type": "object", "properties": { @@ -12597,481 +4116,6 @@ } } }, - "domain.EventSource": { - "type": "string", - "enum": [ - "b365api", - "bwin", - "bfair", - "1xbet", - "enetpulse" - ], - "x-enum-varnames": [ - "EVENT_SOURCE_BET365", - "EVENT_SOURCE_BWIN", - "EVENT_SOURCE_BETFAIR", - "EVENT_SOURCE_1XBET", - "EVENT_SOURCE_ENET" - ] - }, - "domain.EventStatus": { - "type": "string", - "enum": [ - "upcoming", - "in_play", - "to_be_fixed", - "ended", - "postponed", - "cancelled", - "walkover", - "interrupted", - "abandoned", - "retired", - "suspended", - "decided_by_fa", - "removed" - ], - "x-enum-varnames": [ - "STATUS_PENDING", - "STATUS_IN_PLAY", - "STATUS_TO_BE_FIXED", - "STATUS_ENDED", - "STATUS_POSTPONED", - "STATUS_CANCELLED", - "STATUS_WALKOVER", - "STATUS_INTERRUPTED", - "STATUS_ABANDONED", - "STATUS_RETIRED", - "STATUS_SUSPENDED", - "STATUS_DECIDED_BY_FA", - "STATUS_REMOVED" - ] - }, - "domain.EventWithSettingsRes": { - "type": "object", - "properties": { - "added_time": { - "type": "integer" - }, - "average_bet_amount": { - "type": "number" - }, - "away_team": { - "type": "string" - }, - "away_team_id": { - "type": "integer" - }, - "away_team_image": { - "type": "string" - }, - "default_is_active": { - "type": "boolean" - }, - "default_is_featured": { - "type": "boolean" - }, - "default_winning_upper_limit": { - "type": "integer" - }, - "fetched_at": { - "type": "string" - }, - "home_team": { - "type": "string" - }, - "home_team_id": { - "type": "integer" - }, - "home_team_image": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "is_active": { - "type": "boolean" - }, - "is_featured": { - "type": "boolean" - }, - "is_live": { - "type": "boolean" - }, - "is_monitored": { - "type": "boolean" - }, - "league_cc": { - "type": "string" - }, - "league_id": { - "type": "integer" - }, - "league_name": { - "type": "string" - }, - "match_minute": { - "type": "integer" - }, - "match_name": { - "type": "string" - }, - "match_period": { - "type": "integer" - }, - "number_of_bets": { - "type": "integer" - }, - "score": { - "type": "string" - }, - "source": { - "$ref": "#/definitions/domain.EventSource" - }, - "source_event_id": { - "type": "string" - }, - "sport_id": { - "type": "integer" - }, - "start_time": { - "type": "string" - }, - "status": { - "$ref": "#/definitions/domain.EventStatus" - }, - "timer_status": { - "type": "string" - }, - "total_amount": { - "type": "number" - }, - "total_odd_outcomes": { - "type": "integer" - }, - "total_potential_winnings": { - "type": "number" - }, - "updated_at": { - "type": "string" - }, - "winning_upper_limit": { - "type": "integer" - } - } - }, - "domain.FavoriteGameRequest": { - "type": "object", - "properties": { - "game_id": { - "type": "integer" - }, - "provider_id": { - "type": "string" - } - } - }, - "domain.FreeSpinRequest": { - "type": "object", - "properties": { - "casino_id": { - "type": "string" - }, - "end_date": { - "description": "\"yyyy-mm-ddTHH:MM:SS+00:00\"", - "type": "string" - }, - "freespins_count": { - "description": "count of free spins/bets", - "type": "integer" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - } - } - }, - "domain.FreeSpinResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, - "domain.FreeSpinResultRequest": { - "type": "object", - "properties": { - "amount": { - "description": "win amount", - "type": "number" - }, - "casino_id": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - }, - "transaction_id": { - "type": "string" - } - } - }, - "domain.FreeSpinResultResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, - "domain.GameListRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "page": { - "type": "integer" - }, - "providerId": { - "type": "string" - }, - "size": { - "type": "integer" - } - } - }, - "domain.GameRecommendation": { - "type": "object", - "properties": { - "bets": { - "type": "array", - "items": { - "type": "number" - } - }, - "game_id": { - "type": "integer" - }, - "game_name": { - "type": "string" - }, - "reason": { - "description": "e.g., \"Based on your activity\", \"Popular\", \"Random pick\"", - "type": "string" - }, - "thumbnail": { - "type": "string" - } - } - }, - "domain.GameStartRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "country": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "deviceType": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "language": { - "type": "string" - }, - "playerId": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "sessionId": { - "type": "string" - } - } - }, - "domain.GameStartResponse": { - "type": "object", - "properties": { - "startGameUrl": { - "type": "string" - } - } - }, - "domain.GamingActivityItem": { - "type": "object", - "properties": { - "actionType": { - "type": "string" - }, - "amount": { - "type": "number" - }, - "amountEur": { - "type": "number" - }, - "amountUsd": { - "type": "number" - }, - "brandId": { - "type": "string" - }, - "correlationId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "playerId": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "refActionType": { - "type": "string" - }, - "refRoundType": { - "type": "string" - }, - "refTransactionId": { - "type": "string" - }, - "roundId": { - "type": "string" - }, - "roundType": { - "type": "string" - }, - "sessionId": { - "type": "string" - }, - "transactionId": { - "type": "string" - } - } - }, - "domain.GamingActivityRequest": { - "type": "object", - "properties": { - "currencies": { - "description": "Optional", - "type": "array", - "items": { - "type": "string" - } - }, - "excludeFreeWin": { - "description": "Optional", - "type": "boolean" - }, - "fromDate": { - "description": "YYYY-MM-DD", - "type": "string" - }, - "gameIds": { - "description": "Optional", - "type": "array", - "items": { - "type": "string" - } - }, - "page": { - "description": "Optional, default 1", - "type": "integer" - }, - "playerIds": { - "description": "Optional", - "type": "array", - "items": { - "type": "string" - } - }, - "providerId": { - "description": "Optional", - "type": "string" - }, - "size": { - "description": "Optional, default 100", - "type": "integer" - }, - "toDate": { - "description": "YYYY-MM-DD", - "type": "string" - } - } - }, - "domain.GamingActivityResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.GamingActivityItem" - } - }, - "meta": { - "$ref": "#/definitions/domain.PaginationMeta" - } - } - }, - "domain.GeneratePaymentURLRequest": { - "type": "object", - "properties": { - "amount": { - "type": "integer" - }, - "paymentMethod": { - "type": "string" - }, - "paymentReason": { - "type": "string" - }, - "phoneNumber": { - "type": "string" - } - } - }, "domain.GetCompanyRes": { "type": "object", "properties": { @@ -13167,108 +4211,6 @@ } } }, - "domain.HugeWinItem": { - "type": "object", - "properties": { - "betAmount": { - "type": "number" - }, - "betAmountUsd": { - "type": "number" - }, - "betTransactionId": { - "type": "string" - }, - "brandId": { - "type": "string" - }, - "correlationId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "operatorId": { - "type": "string" - }, - "playerId": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "roundId": { - "type": "string" - }, - "winAmount": { - "type": "number" - }, - "winAmountUsd": { - "type": "number" - }, - "winTransactionId": { - "type": "string" - } - } - }, - "domain.HugeWinsRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "currencies": { - "type": "array", - "items": { - "type": "string" - } - }, - "fromDate": { - "type": "string" - }, - "gameIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "page": { - "type": "integer" - }, - "providerId": { - "type": "string" - }, - "size": { - "type": "integer" - }, - "toDate": { - "type": "string" - } - } - }, - "domain.HugeWinsResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.HugeWinItem" - } - }, - "meta": { - "$ref": "#/definitions/domain.PaginationMeta" - } - } - }, "domain.InstResponse": { "type": "object", "properties": { @@ -13291,53 +4233,6 @@ } } }, - "domain.JackpotRequest": { - "type": "object", - "properties": { - "amount": { - "description": "jackpot win", - "type": "number" - }, - "casino_id": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "details": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - }, - "transaction_id": { - "type": "string" - } - } - }, - "domain.JackpotResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, "domain.LogEntry": { "type": "object", "properties": { @@ -13385,99 +4280,6 @@ } } }, - "domain.MarketSettings": { - "type": "object", - "properties": { - "isActive": { - "type": "boolean" - }, - "marketID": { - "type": "integer" - }, - "marketName": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } - } - }, - "domain.OddMarketFilter": { - "type": "object", - "properties": { - "limit": { - "$ref": "#/definitions/domain.ValidInt32" - }, - "offset": { - "$ref": "#/definitions/domain.ValidInt32" - } - } - }, - "domain.OddMarketWithEventFilter": { - "type": "object", - "properties": { - "isLive": { - "$ref": "#/definitions/domain.ValidBool" - }, - "limit": { - "$ref": "#/definitions/domain.ValidInt32" - }, - "offset": { - "$ref": "#/definitions/domain.ValidInt32" - }, - "status": { - "$ref": "#/definitions/domain.ValidString" - } - } - }, - "domain.OutcomeStatus": { - "type": "integer", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5 - ], - "x-enum-comments": { - "OUTCOME_STATUS_ERROR": "Error (Unsettled Bet)", - "OUTCOME_STATUS_HALF": "Half Win and Half Given Back", - "OUTCOME_STATUS_VOID": "Give Back" - }, - "x-enum-varnames": [ - "OUTCOME_STATUS_PENDING", - "OUTCOME_STATUS_WIN", - "OUTCOME_STATUS_LOSS", - "OUTCOME_STATUS_VOID", - "OUTCOME_STATUS_HALF", - "OUTCOME_STATUS_ERROR" - ] - }, - "domain.PaginatedFileResponse": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "string" - } - }, - "message": { - "type": "string" - }, - "metadata": {}, - "pagination": { - "$ref": "#/definitions/domain.Pagination" - }, - "status_code": { - "type": "integer" - }, - "success": { - "type": "boolean" - } - } - }, "domain.Pagination": { "type": "object", "properties": { @@ -13495,178 +4297,6 @@ } } }, - "domain.PaginationMeta": { - "type": "object", - "properties": { - "currentPage": { - "type": "integer" - }, - "itemCount": { - "type": "integer" - }, - "itemsPerPage": { - "type": "integer" - }, - "totalItems": { - "type": "integer" - }, - "totalPages": { - "type": "integer" - } - } - }, - "domain.PaymentOption": { - "type": "integer", - "enum": [ - 0, - 1, - 2, - 3 - ], - "x-enum-varnames": [ - "CASH_TRANSACTION", - "TELEBIRR_TRANSACTION", - "ARIFPAY_TRANSACTION", - "BANK" - ] - }, - "domain.PopOKCallback": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "session_id": { - "type": "string" - }, - "signature": { - "description": "HMAC-SHA256 signature for verification", - "type": "string" - }, - "timestamp": { - "type": "integer" - }, - "transaction_id": { - "type": "string" - }, - "type": { - "description": "BET, WIN, REFUND, JACKPOT_WIN", - "type": "string" - } - } - }, - "domain.PopOKGame": { - "type": "object", - "properties": { - "bets": { - "type": "array", - "items": { - "type": "number" - } - }, - "gameName": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "status": { - "type": "integer" - }, - "thumbnail": { - "type": "string" - } - } - }, - "domain.ProviderRequest": { - "type": "object", - "properties": { - "brandId": { - "type": "string" - }, - "extraData": { - "type": "boolean" - }, - "page": { - "type": "integer" - }, - "size": { - "type": "integer" - } - } - }, - "domain.ProviderResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "logoForDark": { - "type": "string" - }, - "logoForLight": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "providerName": { - "type": "string" - } - } - } - } - } - }, - "domain.RandomBetReq": { - "type": "object", - "required": [ - "branch_id", - "number_of_bets" - ], - "properties": { - "branch_id": { - "type": "integer", - "example": 1 - }, - "number_of_bets": { - "type": "integer", - "example": 1 - } - } - }, - "domain.RawOddsByMarketID": { - "type": "object", - "properties": { - "expires_at": { - "type": "string" - }, - "fetched_at": { - "type": "string" - }, - "handicap": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "market_name": { - "type": "string" - }, - "raw_odds": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true - } - } - } - }, "domain.ReferralStats": { "type": "object", "properties": { @@ -13749,456 +4379,6 @@ "RoleTransactionApprover" ] }, - "domain.RollbackRequest": { - "type": "object", - "properties": { - "bet_transaction_id": { - "type": "string" - }, - "casino_id": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - } - } - }, - "domain.RollbackResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, - "domain.RoundResultRequest": { - "type": "object", - "properties": { - "amount": { - "description": "win amount", - "type": "number" - }, - "bet_transaction_id": { - "description": "from BET request", - "type": "string" - }, - "casino_id": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "game": { - "type": "string" - }, - "hash": { - "type": "string" - }, - "player_id": { - "type": "string" - }, - "round_id": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "timestamp": { - "type": "string" - }, - "transaction_id": { - "description": "new transaction id", - "type": "string" - } - } - }, - "domain.RoundResultResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - } - } - }, - "domain.SantimPayCallbackPayload": { - "type": "object", - "properties": { - "accountNumber": { - "type": "string" - }, - "address": { - "type": "string" - }, - "amount": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "failureRedirectUrl": { - "type": "string" - }, - "merId": { - "type": "string" - }, - "merName": { - "type": "string" - }, - "message": { - "type": "string" - }, - "msisdn": { - "type": "string" - }, - "paymentVia": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "receiverWalletID": { - "type": "string" - }, - "refId": { - "type": "string" - }, - "status": { - "type": "string" - }, - "successRedirectUrl": { - "type": "string" - }, - "thirdPartyId": { - "type": "string" - }, - "txnId": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.ShopBetReq": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "amount": { - "type": "number", - "example": 100 - }, - "bank_code": { - "type": "string" - }, - "beneficiary_name": { - "type": "string" - }, - "bet_id": { - "type": "integer", - "example": 1 - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "full_name": { - "type": "string", - "example": "John Smith" - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CreateBetOutcomeReq" - } - }, - "payment_option": { - "allOf": [ - { - "$ref": "#/definitions/domain.PaymentOption" - } - ], - "example": 1 - }, - "phone_number": { - "type": "string", - "example": "0911111111" - }, - "reference_number": { - "type": "string" - } - } - }, - "domain.ShopBetRes": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "bet_id": { - "type": "integer", - "example": 1 - }, - "branch_id": { - "type": "integer", - "example": 2 - }, - "cashed_out": { - "type": "boolean", - "example": false - }, - "cashout_id": { - "type": "string", - "example": "21234" - }, - "company_id": { - "type": "integer", - "example": 2 - }, - "created_at": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - }, - "fast_code": { - "type": "string", - "example": "12SD1" - }, - "full_name": { - "type": "string", - "example": "John" - }, - "id": { - "type": "integer" - }, - "number_of_outcomes": { - "type": "integer", - "example": 1 - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetOutcome" - } - }, - "phone_number": { - "type": "string", - "example": "1234567890" - }, - "shop_transaction_id": { - "type": "integer" - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/domain.OutcomeStatus" - } - ], - "example": 1 - }, - "total_odds": { - "type": "number", - "example": 4.22 - }, - "transaction_verified": { - "type": "boolean", - "example": true - }, - "updated_at": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - } - } - }, - "domain.ShopDepositReq": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "amount": { - "type": "number", - "example": 100 - }, - "bank_code": { - "description": "FullName string `json:\"full_name\" example:\"John Smith\"`\nPhoneNumber string `json:\"phone_number\" example:\"0911111111\"`", - "type": "string" - }, - "beneficiary_name": { - "type": "string" - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "customer_id": { - "type": "integer", - "example": 1 - }, - "payment_option": { - "allOf": [ - { - "$ref": "#/definitions/domain.PaymentOption" - } - ], - "example": 1 - }, - "reference_number": { - "type": "string" - } - } - }, - "domain.ShopDepositRes": { - "type": "object", - "properties": { - "customer_id": { - "type": "integer" - }, - "id": { - "type": "integer" - }, - "shop_transaction_id": { - "type": "integer" - }, - "wallet_transfer_id": { - "type": "integer" - } - } - }, - "domain.ShopTransactionRes": { - "type": "object", - "properties": { - "account_name": { - "type": "string" - }, - "account_number": { - "type": "string" - }, - "amount": { - "type": "number", - "example": 100 - }, - "approved_by": { - "type": "integer", - "example": 1 - }, - "approver_first_name": { - "type": "string", - "example": "John" - }, - "approver_last_name": { - "type": "string", - "example": "Smith" - }, - "approver_phone_number": { - "type": "string", - "example": "0911111111" - }, - "bank_code": { - "type": "string" - }, - "beneficiary_name": { - "type": "string" - }, - "branch_id": { - "type": "integer", - "example": 1 - }, - "branch_location": { - "type": "string", - "example": "Branch Location" - }, - "branch_name": { - "type": "string", - "example": "Branch Name" - }, - "cashier_name": { - "type": "string", - "example": "John Smith" - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "created_at": { - "type": "string" - }, - "creator_first_name": { - "type": "string", - "example": "John" - }, - "creator_last_name": { - "type": "string", - "example": "Smith" - }, - "creator_phone_number": { - "type": "string", - "example": "0911111111" - }, - "full_name": { - "type": "string", - "example": "John Smith" - }, - "id": { - "type": "integer", - "example": 1 - }, - "payment_option": { - "allOf": [ - { - "$ref": "#/definitions/domain.PaymentOption" - } - ], - "example": 1 - }, - "phone_number": { - "type": "string", - "example": "0911111111" - }, - "reference_number": { - "type": "string" - }, - "type": { - "type": "integer", - "example": 1 - }, - "updated_at": { - "type": "string" - }, - "user_id": { - "type": "integer", - "example": 1 - }, - "verified": { - "type": "boolean", - "example": true - } - } - }, "domain.SupportedOperationRes": { "type": "object", "properties": { @@ -14216,234 +4396,6 @@ } } }, - "domain.SwapRequest": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "from": { - "type": "string" - }, - "to": { - "type": "string" - } - } - }, - "domain.TelebirrPaymentCallbackPayload": { - "type": "object", - "properties": { - "appid": { - "description": "App ID provided by Telebirr", - "type": "string" - }, - "callback_info": { - "description": "Optional merchant-defined callback data", - "type": "string" - }, - "merch_code": { - "description": "Merchant short code", - "type": "string" - }, - "merch_order_id": { - "description": "Order ID from merchant system", - "type": "string" - }, - "notify_time": { - "description": "Notification timestamp (UTC, in seconds)", - "type": "string" - }, - "notify_url": { - "description": "Optional callback URL", - "type": "string" - }, - "payment_order_id": { - "description": "Order ID from Telebirr system", - "type": "string" - }, - "sign": { - "description": "Signature of the payload", - "type": "string" - }, - "sign_type": { - "description": "Signature type, e.g., SHA256WithRSA", - "type": "string" - }, - "total_amount": { - "description": "Payment amount", - "type": "string" - }, - "trade_status": { - "description": "Payment status (e.g., Completed, Failure)", - "type": "string" - }, - "trans_currency": { - "description": "Currency type (e.g., ETB)", - "type": "string" - }, - "trans_end_time": { - "description": "Transaction end time (UTC seconds)", - "type": "string" - }, - "trans_id": { - "description": "Transaction ID", - "type": "string" - } - } - }, - "domain.TicketOutcome": { - "type": "object", - "properties": { - "away_team_name": { - "type": "string", - "example": "Liverpool" - }, - "event_id": { - "type": "integer", - "example": 1 - }, - "expires": { - "type": "string", - "example": "2025-04-08T12:00:00Z" - }, - "home_team_name": { - "type": "string", - "example": "Manchester" - }, - "id": { - "type": "integer", - "example": 1 - }, - "market_id": { - "type": "integer", - "example": 1 - }, - "market_name": { - "type": "string", - "example": "Fulltime Result" - }, - "odd": { - "type": "number", - "example": 1.5 - }, - "odd_handicap": { - "type": "string", - "example": "1" - }, - "odd_header": { - "type": "string", - "example": "1" - }, - "odd_id": { - "type": "integer", - "example": 1 - }, - "odd_name": { - "type": "string", - "example": "1" - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/domain.OutcomeStatus" - } - ], - "example": 1 - }, - "ticket_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.TicketRes": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "id": { - "type": "integer", - "example": 1 - }, - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.TicketOutcome" - } - }, - "total_odds": { - "type": "number", - "example": 4.22 - } - } - }, - "domain.TransactionStatusRequest": { - "type": "object", - "properties": { - "fullParams": { - "type": "boolean" - }, - "id": { - "type": "string" - } - } - }, - "domain.UnifiedGame": { - "type": "object", - "properties": { - "bets": { - "type": "array", - "items": { - "type": "number" - } - }, - "category": { - "type": "string" - }, - "demoUrl": { - "type": "string" - }, - "deviceType": { - "type": "string" - }, - "gameId": { - "type": "string" - }, - "hasDemo": { - "type": "boolean" - }, - "hasFreeBets": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "providerId": { - "type": "string" - }, - "rtp": { - "type": "number" - }, - "status": { - "type": "integer" - }, - "thumbnail": { - "type": "string" - }, - "volatility": { - "type": "string" - } - } - }, "domain.UpdateCompanyReq": { "type": "object", "properties": { @@ -14468,164 +4420,6 @@ } } }, - "domain.UpdateTransactionVerifiedReq": { - "type": "object", - "properties": { - "verified": { - "type": "boolean", - "example": true - } - } - }, - "domain.ValidBool": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "boolean" - } - } - }, - "domain.ValidInt": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "integer" - } - } - }, - "domain.ValidInt32": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "integer" - } - } - }, - "domain.ValidString": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "string" - } - } - }, - "domain.VirtualGameProvider": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "logo_dark": { - "type": "string" - }, - "logo_light": { - "type": "string" - }, - "provider_id": { - "description": "ID int64 `json:\"id\" db:\"id\"`", - "type": "string" - }, - "provider_name": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.VirtualGameProviderReport": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "provider_id": { - "type": "string" - }, - "report_date": { - "type": "string" - }, - "report_type": { - "type": "string" - }, - "total_bets": { - "type": "number" - }, - "total_games_played": { - "type": "integer" - }, - "total_payouts": { - "type": "number" - }, - "total_players": { - "type": "integer" - }, - "total_profit": { - "type": "number" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.WebhookRequest": { - "type": "object", - "properties": { - "nonce": { - "type": "string" - }, - "notificationUrl": { - "type": "string" - }, - "paymentMethod": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "sessionId": { - "type": "string" - }, - "totalAmount": { - "type": "integer" - }, - "transaction": { - "type": "object", - "properties": { - "transactionId": { - "type": "string" - }, - "transactionStatus": { - "type": "string" - } - } - }, - "transactionStatus": { - "type": "string" - }, - "uuid": { - "type": "string" - } - } - }, "handlers.AdminProfileRes": { "type": "object", "properties": { @@ -14858,19 +4652,6 @@ } } }, - "handlers.CreateTransferReq": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "payment_method": { - "type": "string", - "example": "cash" - } - } - }, "handlers.CustomerProfileRes": { "type": "object", "properties": { @@ -14918,91 +4699,6 @@ } } }, - "handlers.CustomerWalletRes": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "customer_id": { - "type": "integer", - "example": 1 - }, - "first_name": { - "type": "string", - "example": "John" - }, - "id": { - "type": "integer", - "example": 1 - }, - "last_name": { - "type": "string", - "example": "Smith" - }, - "number_of_deposits": { - "type": "integer" - }, - "number_of_transactions": { - "type": "integer" - }, - "number_of_transfers": { - "type": "integer" - }, - "number_of_withdraws": { - "type": "integer" - }, - "phone_number": { - "type": "string", - "example": "0911111111" - }, - "regular_balance": { - "type": "number", - "example": 100 - }, - "regular_id": { - "type": "integer", - "example": 1 - }, - "regular_is_active": { - "type": "boolean", - "example": true - }, - "regular_updated_at": { - "type": "string" - }, - "static_balance": { - "type": "number", - "example": 100 - }, - "static_id": { - "type": "integer", - "example": 1 - }, - "static_is_active": { - "type": "boolean", - "example": true - }, - "static_updated_at": { - "type": "string" - }, - "total_deposits_amount": { - "type": "number" - }, - "total_transactions": { - "type": "number" - }, - "total_transfers_amount": { - "type": "number" - }, - "total_withdraws_amount": { - "type": "number" - }, - "updated_at": { - "type": "string" - } - } - }, "handlers.CustomersRes": { "type": "object", "properties": { @@ -15109,6 +4805,20 @@ } } }, + "handlers.LoginAdminRes": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "refresh_token": { + "type": "string" + }, + "role": { + "type": "string" + } + } + }, "handlers.ManagersRes": { "type": "object", "properties": { @@ -15238,17 +4948,6 @@ } } }, - "handlers.ResultRes": { - "type": "object", - "properties": { - "outcomes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BetOutcome" - } - } - } - }, "handlers.SearchUserByNameOrPhoneReq": { "type": "object", "properties": { @@ -15260,105 +4959,6 @@ } } }, - "handlers.SetLeagueActiveReq": { - "type": "object", - "properties": { - "is_active": { - "type": "boolean" - } - } - }, - "handlers.SetLeagueAsFeatured": { - "type": "object", - "properties": { - "is_featured": { - "type": "boolean", - "example": true - } - } - }, - "handlers.TopLeague": { - "type": "object", - "properties": { - "events": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.EventWithSettingsRes" - } - }, - "league_cc": { - "type": "string" - }, - "league_id": { - "type": "integer" - }, - "league_name": { - "type": "string" - }, - "league_sport_id": { - "type": "integer" - } - } - }, - "handlers.TransferWalletRes": { - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "created_at": { - "type": "string" - }, - "depositor_first_name": { - "type": "string" - }, - "depositor_id": { - "type": "integer" - }, - "depositor_last_name": { - "type": "string" - }, - "depositor_phone_number": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "payment_method": { - "type": "string" - }, - "receiver_wallet_id": { - "type": "integer" - }, - "reference_number": { - "description": "← Add this", - "type": "string" - }, - "sender_wallet_id": { - "type": "integer" - }, - "type": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "verified": { - "type": "boolean" - } - } - }, - "handlers.UpdateCashOutReq": { - "type": "object", - "properties": { - "cashedOut": { - "type": "boolean" - } - } - }, "handlers.UpdateUserSuspendReq": { "type": "object", "required": [ @@ -15386,18 +4986,6 @@ } } }, - "handlers.UpdateWalletActiveReq": { - "type": "object", - "required": [ - "is_active" - ], - "properties": { - "is_active": { - "type": "boolean", - "example": true - } - } - }, "handlers.UserProfileRes": { "type": "object", "properties": { @@ -15445,79 +5033,6 @@ } } }, - "handlers.WalletRes": { - "type": "object", - "properties": { - "amount": { - "type": "number", - "example": 100 - }, - "created_at": { - "type": "string" - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": true - }, - "is_bettable": { - "type": "boolean", - "example": true - }, - "is_transferable": { - "type": "boolean", - "example": true - }, - "is_withdraw": { - "type": "boolean", - "example": true - }, - "updated_at": { - "type": "string" - }, - "user_id": { - "type": "integer", - "example": 1 - } - } - }, - "handlers.launchVirtualGameReq": { - "type": "object", - "required": [ - "currency", - "game_id", - "mode" - ], - "properties": { - "currency": { - "type": "string", - "example": "USD" - }, - "game_id": { - "type": "string", - "example": "1" - }, - "mode": { - "type": "string", - "enum": [ - "fun", - "real" - ], - "example": "real" - } - } - }, - "handlers.launchVirtualGameRes": { - "type": "object", - "properties": { - "launch_url": { - "type": "string" - } - } - }, "handlers.loginAdminReq": { "type": "object", "required": [ @@ -15538,20 +5053,6 @@ } } }, - "handlers.loginAdminRes": { - "type": "object", - "properties": { - "access_token": { - "type": "string" - }, - "refresh_token": { - "type": "string" - }, - "role": { - "type": "string" - } - } - }, "handlers.loginCustomerReq": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 11c3a6c..c279a32 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,139 +1,4 @@ definitions: - domain.ARIFPAYPaymentMethod: - properties: - id: - type: integer - name: - type: string - type: object - domain.AleaPlayCallback: - properties: - amount: - type: number - currency: - type: string - event_id: - type: string - game_id: - type: string - is_free_round: - type: boolean - multiplier: - type: number - operator_id: - type: string - round_id: - type: string - session_id: - type: string - signature: - type: string - timestamp: - type: integer - transaction_id: - type: string - type: - description: BET, WIN, CASHOUT, etc. - type: string - user_id: - type: string - type: object - domain.ArifpayVerifyByTransactionIDRequest: - properties: - paymentType: - type: integer - transactionId: - type: string - type: object - domain.AtlasBetWinRequest: - properties: - betAmount: - type: number - casino_id: - type: string - currency: - type: string - game: - type: string - hash: - type: string - player_id: - type: string - round_id: - type: string - session_id: - type: string - timestamp: - type: string - transaction_id: - type: string - winAmount: - type: number - type: object - domain.AtlasBetWinResponse: - properties: - balance: - type: number - player_id: - type: string - type: object - domain.AtlasGameEntity: - properties: - demo_url: - description: ✅ new field - type: string - deviceType: - type: string - game_id: - type: string - has_demo: - type: boolean - hasFreeBets: - type: boolean - name: - type: string - providerId: - type: string - thumbnail_img_url: - description: ✅ new field - type: string - type: - type: string - type: object - domain.AtlasGameInitRequest: - properties: - currency: - type: string - game: - type: string - language: - type: string - player_id: - type: string - type: object - domain.AtlasGameInitResponse: - properties: - url: - type: string - type: object - domain.AtlasGetUserDataRequest: - properties: - casino_id: - type: string - game: - type: string - player_id: - type: string - session_id: - type: string - type: object - domain.AtlasGetUserDataResponse: - properties: - balance: - type: number - player_id: - type: string - type: object domain.Bank: properties: acct_length: @@ -170,182 +35,6 @@ definitions: updated_at: type: string type: object - domain.BaseEvent: - properties: - addedTime: - $ref: '#/definitions/domain.ValidInt' - avgBetAmount: - type: integer - awayTeam: - type: string - awayTeamID: - type: integer - awayTeamImage: - type: string - defaultIsActive: - type: boolean - defaultIsFeatured: - type: boolean - defaultWinningUpperLimit: - type: integer - fetchedAt: - type: string - homeTeam: - type: string - homeTeamID: - type: integer - homeTeamImage: - type: string - id: - type: integer - isLive: - type: boolean - isMonitored: - type: boolean - leagueCC: - $ref: '#/definitions/domain.ValidString' - leagueID: - type: integer - leagueName: - type: string - matchMinute: - $ref: '#/definitions/domain.ValidInt' - matchName: - type: string - matchPeriod: - $ref: '#/definitions/domain.ValidInt' - numberOfBets: - type: integer - score: - $ref: '#/definitions/domain.ValidString' - source: - $ref: '#/definitions/domain.EventSource' - sourceEventID: - type: string - sportID: - type: integer - startTime: - type: string - status: - $ref: '#/definitions/domain.EventStatus' - timerStatus: - $ref: '#/definitions/domain.ValidString' - totalAmount: - type: integer - totalOddOutcomes: - type: integer - totalPotentialWinnings: - type: integer - type: object - domain.BaseLeague: - properties: - bet365ID: - $ref: '#/definitions/domain.ValidInt32' - countryCode: - $ref: '#/definitions/domain.ValidString' - defaultIsActive: - type: boolean - defaultIsFeatured: - type: boolean - id: - type: integer - name: - type: string - sportID: - type: integer - type: object - domain.BetOutcome: - properties: - away_team_name: - example: Liverpool - type: string - bet_id: - example: 1 - type: integer - event_id: - example: 1 - type: integer - expires: - example: "2025-04-08T12:00:00Z" - type: string - home_team_name: - example: Manchester - type: string - id: - example: 1 - type: integer - market_id: - example: 1 - type: integer - market_name: - example: Fulltime Result - type: string - odd: - example: 1.5 - type: number - odd_handicap: - example: "1" - type: string - odd_header: - example: "1" - type: string - odd_id: - example: 1 - type: integer - odd_name: - example: "1" - type: string - sport_id: - example: 1 - type: integer - status: - allOf: - - $ref: '#/definitions/domain.OutcomeStatus' - example: 1 - type: object - domain.BetRes: - properties: - amount: - example: 100 - type: number - cashed_out: - example: false - type: boolean - company_id: - example: 1 - type: integer - company_slug: - example: fortune - type: string - created_at: - example: "2025-04-08T12:00:00Z" - type: string - fast_code: - type: string - full_name: - example: John Smith - type: string - id: - example: 1 - type: integer - is_shop_bet: - example: false - type: boolean - outcomes: - items: - $ref: '#/definitions/domain.BetOutcome' - type: array - status: - allOf: - - $ref: '#/definitions/domain.OutcomeStatus' - example: 1 - total_odds: - example: 4.22 - type: number - user_id: - example: 2 - type: integer - type: object domain.BranchDetailRes: properties: balance: @@ -457,199 +146,6 @@ definitions: example: 1 type: integer type: object - domain.CashoutReq: - properties: - account_name: - type: string - account_number: - type: string - bank_code: - type: string - beneficiary_name: - type: string - branch_id: - example: 1 - type: integer - cashout_id: - example: "1234" - type: string - payment_option: - allOf: - - $ref: '#/definitions/domain.PaymentOption' - example: 1 - reference_number: - type: string - type: object - domain.ChapaCancelResponse: - properties: - amount: - type: number - created_at: - type: string - currency: - type: string - message: - type: string - status: - type: string - tx_ref: - type: string - updated_at: - type: string - type: object - domain.ChapaCustomer: - properties: - email: - type: string - first_name: - type: string - id: - type: integer - last_name: - type: string - mobile: - type: string - type: object - domain.ChapaDepositRequestPayload: - properties: - amount: - type: number - required: - - amount - type: object - domain.ChapaDepositResponse: - properties: - checkoutURL: - type: string - reference: - type: string - type: object - domain.ChapaTransaction: - properties: - amount: - type: string - charge: - type: string - created_at: - type: string - currency: - type: string - customer: - $ref: '#/definitions/domain.ChapaCustomer' - payment_method: - type: string - ref_id: - type: string - status: - type: string - trans_id: - type: string - type: - type: string - type: object - domain.ChapaTransactionEvent: - properties: - created_at: - type: string - item: - type: integer - message: - type: string - type: - type: string - updated_at: - type: string - type: object - domain.ChapaWebhookCustomization: - properties: - description: - type: string - logo: - type: string - title: - type: string - type: object - domain.ChapaWebhookPayment: - properties: - amount: - type: string - charge: - type: string - created_at: - type: string - currency: - type: string - customization: - $ref: '#/definitions/domain.ChapaWebhookCustomization' - email: - type: string - event: - type: string - first_name: - type: string - last_name: - type: string - meta: - description: may vary in structure, so kept flexible - mobile: - type: string - mode: - type: string - payment_method: - type: string - reference: - type: string - status: - type: string - tx_ref: - type: string - type: - type: string - updated_at: - type: string - type: object - domain.ChapaWithdrawalRequest: - properties: - account_name: - type: string - account_number: - type: string - amount: - description: string because Chapa API uses string for monetary values - type: string - bank_code: - type: integer - currency: - type: string - reference: - type: string - type: object - domain.CheckoutSessionClientRequest: - properties: - amount: - type: number - customerEmail: - type: string - customerPhone: - type: string - required: - - amount - - customerEmail - - customerPhone - type: object - domain.CompanyMarketSettings: - properties: - companyID: - type: integer - isActive: - $ref: '#/definitions/domain.ValidBool' - marketID: - type: integer - marketName: - type: string - updatedAt: - type: string - type: object domain.CompanyRes: properties: admin_id: @@ -674,43 +170,6 @@ definitions: example: 1 type: integer type: object - domain.CreateBetOutcomeReq: - properties: - event_id: - example: 1 - type: integer - market_id: - example: 1 - type: integer - odd_id: - example: 1 - type: integer - type: object - domain.CreateBetReq: - properties: - amount: - example: 100 - type: number - branch_id: - example: 1 - type: integer - outcomes: - items: - $ref: '#/definitions/domain.CreateBetOutcomeReq' - type: array - required: - - amount - - outcomes - type: object - domain.CreateBetWithFastCodeReq: - properties: - amount: - type: number - branch_id: - type: integer - fast_code: - type: string - type: object domain.CreateBranchOperationReq: properties: branch_id: @@ -754,17 +213,6 @@ definitions: - name - operations type: object - domain.CreateCompanyMarketSettings: - properties: - companyID: - type: integer - isActive: - $ref: '#/definitions/domain.ValidBool' - marketID: - type: integer - marketName: - type: string - type: object domain.CreateCompanyReq: properties: admin_id: @@ -781,25 +229,6 @@ definitions: slug: type: string type: object - domain.CreateDirectDeposit: - properties: - accountHolder: - type: string - accountNumber: - type: string - amount: - type: number - bankName: - type: string - customerID: - type: integer - referenceNumber: - type: string - transferScreenshot: - type: string - walletID: - type: integer - type: object domain.CreateSupportedOperationReq: properties: description: @@ -809,497 +238,6 @@ definitions: example: SportsBook type: string type: object - domain.CreateTicketOutcomeReq: - properties: - event_id: - description: TicketID int64 `json:"ticket_id" example:"1"` - example: 1 - type: integer - market_id: - example: 1 - type: integer - odd_id: - example: 1 - type: integer - type: object - domain.CreateTicketReq: - properties: - amount: - example: 100 - type: number - outcomes: - items: - $ref: '#/definitions/domain.CreateTicketOutcomeReq' - type: array - type: object - domain.CreateTicketRes: - properties: - created_number: - example: 3 - type: integer - fast_code: - example: 1234 - type: integer - type: object - domain.CreditBalance: - properties: - balance: - type: number - currency: - type: string - threshold: - type: number - type: object - domain.DashboardSummary: - properties: - active_admins: - type: integer - active_bets: - type: integer - active_branches: - type: integer - active_cashiers: - type: integer - active_companies: - type: integer - active_customers: - type: integer - active_games: - type: integer - active_managers: - type: integer - average_stake: - type: integer - branches_count: - type: integer - customer_count: - type: integer - inactive_admins: - type: integer - inactive_branches: - type: integer - inactive_cashiers: - type: integer - inactive_companies: - type: integer - inactive_customers: - type: integer - inactive_games: - type: integer - inactive_managers: - type: integer - profit: - type: integer - read_notifications: - type: integer - total_admins: - type: integer - total_bets: - type: integer - total_cashiers: - type: integer - total_companies: - type: integer - total_deposits: - type: integer - total_games: - type: integer - total_losses: - type: integer - total_managers: - type: integer - total_notifications: - type: integer - total_stakes: - type: integer - total_wallets: - type: integer - total_wins: - type: integer - total_withdrawals: - type: integer - unread_notifications: - type: integer - win_balance: - type: integer - win_rate: - type: number - type: object - domain.DemoGameRequest: - properties: - brandId: - type: string - deviceType: - type: string - gameId: - type: string - ip: - type: string - language: - type: string - providerId: - type: string - type: object - domain.DirectDeposit: - properties: - accountHolder: - type: string - accountNumber: - type: string - amount: - type: number - approvedAt: - type: string - approvedBy: - type: integer - bankName: - type: string - createdAt: - type: string - customerID: - type: integer - id: - type: integer - referenceNumber: - type: string - rejectionReason: - type: string - status: - type: string - transferScreenshot: - type: string - walletID: - type: integer - type: object - domain.EnetpulseFixture: - properties: - gender: - type: string - id: - type: string - "n": - description: convert to int - type: string - name: - type: string - round_typeFK: - type: string - sport_name: - type: string - sportFK: - type: string - startdate: - description: ISO 8601 - type: string - status_descFK: - type: string - status_type: - type: string - tournament_name: - type: string - tournament_stage_name: - description: TournamentStageFK string `json:"tournament_stageFK"` - type: string - tournament_template_name: - type: string - tournament_templateFK: - type: string - tournamentFK: - type: string - ut: - description: parse to time.Time - type: string - type: object - domain.EnetpulseFixtureWithPreodds: - properties: - createdAt: - type: string - fixtureApiID: - type: string - fixtureID: - type: string - fixtureName: - type: string - lastUpdatedAt: - type: string - preodds: - items: - $ref: '#/definitions/domain.EnetpulsePreodds' - type: array - roundTypeFk: - type: string - sportFk: - type: string - startDate: - type: string - statusDescFk: - type: string - statusType: - type: string - tournamentFk: - type: string - tournamentStageFk: - type: string - tournamentTemplateFk: - type: string - updatedAt: - type: string - updatesCount: - type: integer - type: object - domain.EnetpulsePreodds: - properties: - bettingOffers: - items: - $ref: '#/definitions/domain.EnetpulsePreoddsBettingOffer' - type: array - createdAt: - type: string - dparam: - type: string - dparam2: - type: string - eventFK: - type: integer - eventParticipantNumber: - type: integer - id: - type: integer - iparam: - type: string - iparam2: - type: string - lastUpdatedAt: - type: string - outcomeScopeFK: - type: integer - outcomeSubtypeFK: - type: integer - outcomeTypeFK: - type: integer - preoddsID: - type: string - sparam: - type: string - updatedAt: - type: string - updatesCount: - type: integer - type: object - domain.EnetpulsePreoddsBettingOffer: - properties: - active: - type: string - betting_offer_id: - type: string - betting_offer_status_fk: - type: integer - coupon_key: - type: string - created_at: - type: string - id: - type: integer - last_updated_at: - type: string - odds: - type: number - odds_old: - type: number - odds_provider_fk: - type: integer - preodds_fk: - type: string - updated_at: - type: string - updates_count: - type: integer - type: object - domain.EnetpulseResult: - properties: - commentary: - type: string - created_at: - type: string - first_half_ended: - type: string - game_ended: - type: string - game_started: - type: string - id: - type: integer - last_updated_at: - type: string - lineup_confirmed: - type: boolean - live: - type: string - livestats_plus: - type: string - livestats_type: - type: string - name: - type: string - result_id: - type: string - round: - type: string - round_type_fk: - type: string - second_half_ended: - type: string - second_half_started: - type: string - spectators: - type: integer - sport_fk: - type: string - sport_name: - type: string - start_date: - type: string - status_desc_fk: - type: string - status_type: - type: string - tournament_fk: - type: string - tournament_name: - type: string - tournament_stage_name: - description: TournamentStageFK string `json:"tournament_stage_fk"` - type: string - tournament_template_fk: - type: string - tournament_template_name: - type: string - updated_at: - type: string - updates_count: - type: integer - venue_name: - type: string - verified: - type: boolean - type: object - domain.EnetpulseSport: - properties: - created_at: - type: string - id: - description: DB primary key - type: integer - last_updated_at: - type: string - name: - description: from API "name" - type: string - sport_id: - description: from API "id" - type: string - status: - description: active/inactive - type: integer - updated_at: - type: string - updates_count: - description: from API "n" - type: integer - type: object - domain.EnetpulseTournament: - properties: - createdAt: - type: string - id: - description: internal DB PK - type: integer - lastUpdatedAt: - type: string - name: - type: string - status: - type: integer - tournamentID: - type: string - tournamentTemplateFK: - type: string - updatedAt: - type: string - updatesCount: - type: integer - type: object - domain.EnetpulseTournamentStage: - properties: - country_fk: - description: country FK from API - type: string - country_name: - description: country name from API - type: string - created_at: - type: string - end_date: - description: end date/time - type: string - gender: - description: male/female/mixed/unknown - type: string - id: - type: integer - last_updated_at: - description: ut from API - type: string - name: - description: API name - type: string - stage_id: - description: API id - type: string - start_date: - description: start date/time - type: string - status: - description: active/inactive - type: integer - tournament_fk: - description: Foreign key to tournament - type: string - updated_at: - type: string - updates_count: - description: n from API - type: integer - type: object - domain.EnetpulseTournamentTemplate: - properties: - created_at: - type: string - gender: - description: male, female, mixed, unknown - type: string - id: - type: integer - last_updated_at: - type: string - name: - description: from API "name" - type: string - sport_fk: - description: related sport id - type: string - status: - description: optional - type: integer - template_id: - description: from API "id" - type: string - updated_at: - type: string - updates_count: - description: from API "n" - type: integer - type: object domain.ErrorResponse: properties: error: @@ -1307,333 +245,6 @@ definitions: message: type: string type: object - domain.EventSource: - enum: - - b365api - - bwin - - bfair - - 1xbet - - enetpulse - type: string - x-enum-varnames: - - EVENT_SOURCE_BET365 - - EVENT_SOURCE_BWIN - - EVENT_SOURCE_BETFAIR - - EVENT_SOURCE_1XBET - - EVENT_SOURCE_ENET - domain.EventStatus: - enum: - - upcoming - - in_play - - to_be_fixed - - ended - - postponed - - cancelled - - walkover - - interrupted - - abandoned - - retired - - suspended - - decided_by_fa - - removed - type: string - x-enum-varnames: - - STATUS_PENDING - - STATUS_IN_PLAY - - STATUS_TO_BE_FIXED - - STATUS_ENDED - - STATUS_POSTPONED - - STATUS_CANCELLED - - STATUS_WALKOVER - - STATUS_INTERRUPTED - - STATUS_ABANDONED - - STATUS_RETIRED - - STATUS_SUSPENDED - - STATUS_DECIDED_BY_FA - - STATUS_REMOVED - domain.EventWithSettingsRes: - properties: - added_time: - type: integer - average_bet_amount: - type: number - away_team: - type: string - away_team_id: - type: integer - away_team_image: - type: string - default_is_active: - type: boolean - default_is_featured: - type: boolean - default_winning_upper_limit: - type: integer - fetched_at: - type: string - home_team: - type: string - home_team_id: - type: integer - home_team_image: - type: string - id: - type: integer - is_active: - type: boolean - is_featured: - type: boolean - is_live: - type: boolean - is_monitored: - type: boolean - league_cc: - type: string - league_id: - type: integer - league_name: - type: string - match_minute: - type: integer - match_name: - type: string - match_period: - type: integer - number_of_bets: - type: integer - score: - type: string - source: - $ref: '#/definitions/domain.EventSource' - source_event_id: - type: string - sport_id: - type: integer - start_time: - type: string - status: - $ref: '#/definitions/domain.EventStatus' - timer_status: - type: string - total_amount: - type: number - total_odd_outcomes: - type: integer - total_potential_winnings: - type: number - updated_at: - type: string - winning_upper_limit: - type: integer - type: object - domain.FavoriteGameRequest: - properties: - game_id: - type: integer - provider_id: - type: string - type: object - domain.FreeSpinRequest: - properties: - casino_id: - type: string - end_date: - description: '"yyyy-mm-ddTHH:MM:SS+00:00"' - type: string - freespins_count: - description: count of free spins/bets - type: integer - hash: - type: string - player_id: - type: string - timestamp: - type: string - type: object - domain.FreeSpinResponse: - properties: - success: - type: boolean - type: object - domain.FreeSpinResultRequest: - properties: - amount: - description: win amount - type: number - casino_id: - type: string - currency: - type: string - game: - type: string - hash: - type: string - player_id: - type: string - round_id: - type: integer - session_id: - type: string - timestamp: - type: string - transaction_id: - type: string - type: object - domain.FreeSpinResultResponse: - properties: - success: - type: boolean - type: object - domain.GameListRequest: - properties: - brandId: - type: string - page: - type: integer - providerId: - type: string - size: - type: integer - type: object - domain.GameRecommendation: - properties: - bets: - items: - type: number - type: array - game_id: - type: integer - game_name: - type: string - reason: - description: e.g., "Based on your activity", "Popular", "Random pick" - type: string - thumbnail: - type: string - type: object - domain.GameStartRequest: - properties: - brandId: - type: string - country: - type: string - currency: - type: string - deviceType: - type: string - gameId: - type: string - ip: - type: string - language: - type: string - playerId: - type: string - providerId: - type: string - sessionId: - type: string - type: object - domain.GameStartResponse: - properties: - startGameUrl: - type: string - type: object - domain.GamingActivityItem: - properties: - actionType: - type: string - amount: - type: number - amountEur: - type: number - amountUsd: - type: number - brandId: - type: string - correlationId: - type: string - createdAt: - type: string - currency: - type: string - gameId: - type: string - playerId: - type: string - providerId: - type: string - refActionType: - type: string - refRoundType: - type: string - refTransactionId: - type: string - roundId: - type: string - roundType: - type: string - sessionId: - type: string - transactionId: - type: string - type: object - domain.GamingActivityRequest: - properties: - currencies: - description: Optional - items: - type: string - type: array - excludeFreeWin: - description: Optional - type: boolean - fromDate: - description: YYYY-MM-DD - type: string - gameIds: - description: Optional - items: - type: string - type: array - page: - description: Optional, default 1 - type: integer - playerIds: - description: Optional - items: - type: string - type: array - providerId: - description: Optional - type: string - size: - description: Optional, default 100 - type: integer - toDate: - description: YYYY-MM-DD - type: string - type: object - domain.GamingActivityResponse: - properties: - items: - items: - $ref: '#/definitions/domain.GamingActivityItem' - type: array - meta: - $ref: '#/definitions/domain.PaginationMeta' - type: object - domain.GeneratePaymentURLRequest: - properties: - amount: - type: integer - paymentMethod: - type: string - paymentReason: - type: string - phoneNumber: - type: string - type: object domain.GetCompanyRes: properties: admin_first_name: @@ -1701,73 +312,6 @@ definitions: example: 1 type: integer type: object - domain.HugeWinItem: - properties: - betAmount: - type: number - betAmountUsd: - type: number - betTransactionId: - type: string - brandId: - type: string - correlationId: - type: string - createdAt: - type: string - currency: - type: string - gameId: - type: string - operatorId: - type: string - playerId: - type: string - providerId: - type: string - reason: - type: string - roundId: - type: string - winAmount: - type: number - winAmountUsd: - type: number - winTransactionId: - type: string - type: object - domain.HugeWinsRequest: - properties: - brandId: - type: string - currencies: - items: - type: string - type: array - fromDate: - type: string - gameIds: - items: - type: string - type: array - page: - type: integer - providerId: - type: string - size: - type: integer - toDate: - type: string - type: object - domain.HugeWinsResponse: - properties: - items: - items: - $ref: '#/definitions/domain.HugeWinItem' - type: array - meta: - $ref: '#/definitions/domain.PaginationMeta' - type: object domain.InstResponse: properties: data: @@ -1781,37 +325,6 @@ definitions: status: type: string type: object - domain.JackpotRequest: - properties: - amount: - description: jackpot win - type: number - casino_id: - type: string - currency: - type: string - details: - type: string - game: - type: string - hash: - type: string - player_id: - type: string - round_id: - type: integer - session_id: - type: string - timestamp: - type: string - transaction_id: - type: string - type: object - domain.JackpotResponse: - properties: - success: - type: boolean - type: object domain.LogEntry: properties: caller: @@ -1843,71 +356,6 @@ definitions: pagination: $ref: '#/definitions/domain.Pagination' type: object - domain.MarketSettings: - properties: - isActive: - type: boolean - marketID: - type: integer - marketName: - type: string - updatedAt: - type: string - type: object - domain.OddMarketFilter: - properties: - limit: - $ref: '#/definitions/domain.ValidInt32' - offset: - $ref: '#/definitions/domain.ValidInt32' - type: object - domain.OddMarketWithEventFilter: - properties: - isLive: - $ref: '#/definitions/domain.ValidBool' - limit: - $ref: '#/definitions/domain.ValidInt32' - offset: - $ref: '#/definitions/domain.ValidInt32' - status: - $ref: '#/definitions/domain.ValidString' - type: object - domain.OutcomeStatus: - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - type: integer - x-enum-comments: - OUTCOME_STATUS_ERROR: Error (Unsettled Bet) - OUTCOME_STATUS_HALF: Half Win and Half Given Back - OUTCOME_STATUS_VOID: Give Back - x-enum-varnames: - - OUTCOME_STATUS_PENDING - - OUTCOME_STATUS_WIN - - OUTCOME_STATUS_LOSS - - OUTCOME_STATUS_VOID - - OUTCOME_STATUS_HALF - - OUTCOME_STATUS_ERROR - domain.PaginatedFileResponse: - properties: - data: - items: - type: string - type: array - message: - type: string - metadata: {} - pagination: - $ref: '#/definitions/domain.Pagination' - status_code: - type: integer - success: - type: boolean - type: object domain.Pagination: properties: current_page: @@ -1919,122 +367,6 @@ definitions: total_pages: type: integer type: object - domain.PaginationMeta: - properties: - currentPage: - type: integer - itemCount: - type: integer - itemsPerPage: - type: integer - totalItems: - type: integer - totalPages: - type: integer - type: object - domain.PaymentOption: - enum: - - 0 - - 1 - - 2 - - 3 - type: integer - x-enum-varnames: - - CASH_TRANSACTION - - TELEBIRR_TRANSACTION - - ARIFPAY_TRANSACTION - - BANK - domain.PopOKCallback: - properties: - amount: - type: number - currency: - type: string - session_id: - type: string - signature: - description: HMAC-SHA256 signature for verification - type: string - timestamp: - type: integer - transaction_id: - type: string - type: - description: BET, WIN, REFUND, JACKPOT_WIN - type: string - type: object - domain.PopOKGame: - properties: - bets: - items: - type: number - type: array - gameName: - type: string - id: - type: integer - status: - type: integer - thumbnail: - type: string - type: object - domain.ProviderRequest: - properties: - brandId: - type: string - extraData: - type: boolean - page: - type: integer - size: - type: integer - type: object - domain.ProviderResponse: - properties: - items: - items: - properties: - logoForDark: - type: string - logoForLight: - type: string - providerId: - type: string - providerName: - type: string - type: object - type: array - type: object - domain.RandomBetReq: - properties: - branch_id: - example: 1 - type: integer - number_of_bets: - example: 1 - type: integer - required: - - branch_id - - number_of_bets - type: object - domain.RawOddsByMarketID: - properties: - expires_at: - type: string - fetched_at: - type: string - handicap: - type: string - id: - type: integer - market_name: - type: string - raw_odds: - items: - additionalProperties: true - type: object - type: array - type: object domain.ReferralStats: properties: totalReferrals: @@ -2093,314 +425,6 @@ definitions: - RoleCustomer - RoleCashier - RoleTransactionApprover - domain.RollbackRequest: - properties: - bet_transaction_id: - type: string - casino_id: - type: string - game: - type: string - hash: - type: string - player_id: - type: string - round_id: - type: integer - session_id: - type: string - timestamp: - type: string - type: object - domain.RollbackResponse: - properties: - success: - type: boolean - type: object - domain.RoundResultRequest: - properties: - amount: - description: win amount - type: number - bet_transaction_id: - description: from BET request - type: string - casino_id: - type: string - currency: - type: string - game: - type: string - hash: - type: string - player_id: - type: string - round_id: - type: integer - session_id: - type: string - timestamp: - type: string - transaction_id: - description: new transaction id - type: string - type: object - domain.RoundResultResponse: - properties: - success: - type: boolean - type: object - domain.SantimPayCallbackPayload: - properties: - accountNumber: - type: string - address: - type: string - amount: - type: string - created_at: - type: string - currency: - type: string - failureRedirectUrl: - type: string - merId: - type: string - merName: - type: string - message: - type: string - msisdn: - type: string - paymentVia: - type: string - reason: - type: string - receiverWalletID: - type: string - refId: - type: string - status: - type: string - successRedirectUrl: - type: string - thirdPartyId: - type: string - txnId: - type: string - updated_at: - type: string - type: object - domain.ShopBetReq: - properties: - account_name: - type: string - account_number: - type: string - amount: - example: 100 - type: number - bank_code: - type: string - beneficiary_name: - type: string - bet_id: - example: 1 - type: integer - branch_id: - example: 1 - type: integer - full_name: - example: John Smith - type: string - outcomes: - items: - $ref: '#/definitions/domain.CreateBetOutcomeReq' - type: array - payment_option: - allOf: - - $ref: '#/definitions/domain.PaymentOption' - example: 1 - phone_number: - example: "0911111111" - type: string - reference_number: - type: string - type: object - domain.ShopBetRes: - properties: - amount: - type: number - bet_id: - example: 1 - type: integer - branch_id: - example: 2 - type: integer - cashed_out: - example: false - type: boolean - cashout_id: - example: "21234" - type: string - company_id: - example: 2 - type: integer - created_at: - example: "2025-04-08T12:00:00Z" - type: string - fast_code: - example: 12SD1 - type: string - full_name: - example: John - type: string - id: - type: integer - number_of_outcomes: - example: 1 - type: integer - outcomes: - items: - $ref: '#/definitions/domain.BetOutcome' - type: array - phone_number: - example: "1234567890" - type: string - shop_transaction_id: - type: integer - status: - allOf: - - $ref: '#/definitions/domain.OutcomeStatus' - example: 1 - total_odds: - example: 4.22 - type: number - transaction_verified: - example: true - type: boolean - updated_at: - example: "2025-04-08T12:00:00Z" - type: string - type: object - domain.ShopDepositReq: - properties: - account_name: - type: string - account_number: - type: string - amount: - example: 100 - type: number - bank_code: - description: |- - FullName string `json:"full_name" example:"John Smith"` - PhoneNumber string `json:"phone_number" example:"0911111111"` - type: string - beneficiary_name: - type: string - branch_id: - example: 1 - type: integer - customer_id: - example: 1 - type: integer - payment_option: - allOf: - - $ref: '#/definitions/domain.PaymentOption' - example: 1 - reference_number: - type: string - type: object - domain.ShopDepositRes: - properties: - customer_id: - type: integer - id: - type: integer - shop_transaction_id: - type: integer - wallet_transfer_id: - type: integer - type: object - domain.ShopTransactionRes: - properties: - account_name: - type: string - account_number: - type: string - amount: - example: 100 - type: number - approved_by: - example: 1 - type: integer - approver_first_name: - example: John - type: string - approver_last_name: - example: Smith - type: string - approver_phone_number: - example: "0911111111" - type: string - bank_code: - type: string - beneficiary_name: - type: string - branch_id: - example: 1 - type: integer - branch_location: - example: Branch Location - type: string - branch_name: - example: Branch Name - type: string - cashier_name: - example: John Smith - type: string - company_id: - example: 1 - type: integer - created_at: - type: string - creator_first_name: - example: John - type: string - creator_last_name: - example: Smith - type: string - creator_phone_number: - example: "0911111111" - type: string - full_name: - example: John Smith - type: string - id: - example: 1 - type: integer - payment_option: - allOf: - - $ref: '#/definitions/domain.PaymentOption' - example: 1 - phone_number: - example: "0911111111" - type: string - reference_number: - type: string - type: - example: 1 - type: integer - updated_at: - type: string - user_id: - example: 1 - type: integer - verified: - example: true - type: boolean - type: object domain.SupportedOperationRes: properties: description: @@ -2413,165 +437,6 @@ definitions: example: SportsBook type: string type: object - domain.SwapRequest: - properties: - amount: - type: number - from: - type: string - to: - type: string - type: object - domain.TelebirrPaymentCallbackPayload: - properties: - appid: - description: App ID provided by Telebirr - type: string - callback_info: - description: Optional merchant-defined callback data - type: string - merch_code: - description: Merchant short code - type: string - merch_order_id: - description: Order ID from merchant system - type: string - notify_time: - description: Notification timestamp (UTC, in seconds) - type: string - notify_url: - description: Optional callback URL - type: string - payment_order_id: - description: Order ID from Telebirr system - type: string - sign: - description: Signature of the payload - type: string - sign_type: - description: Signature type, e.g., SHA256WithRSA - type: string - total_amount: - description: Payment amount - type: string - trade_status: - description: Payment status (e.g., Completed, Failure) - type: string - trans_currency: - description: Currency type (e.g., ETB) - type: string - trans_end_time: - description: Transaction end time (UTC seconds) - type: string - trans_id: - description: Transaction ID - type: string - type: object - domain.TicketOutcome: - properties: - away_team_name: - example: Liverpool - type: string - event_id: - example: 1 - type: integer - expires: - example: "2025-04-08T12:00:00Z" - type: string - home_team_name: - example: Manchester - type: string - id: - example: 1 - type: integer - market_id: - example: 1 - type: integer - market_name: - example: Fulltime Result - type: string - odd: - example: 1.5 - type: number - odd_handicap: - example: "1" - type: string - odd_header: - example: "1" - type: string - odd_id: - example: 1 - type: integer - odd_name: - example: "1" - type: string - status: - allOf: - - $ref: '#/definitions/domain.OutcomeStatus' - example: 1 - ticket_id: - example: 1 - type: integer - type: object - domain.TicketRes: - properties: - amount: - example: 100 - type: number - company_id: - example: 1 - type: integer - id: - example: 1 - type: integer - outcomes: - items: - $ref: '#/definitions/domain.TicketOutcome' - type: array - total_odds: - example: 4.22 - type: number - type: object - domain.TransactionStatusRequest: - properties: - fullParams: - type: boolean - id: - type: string - type: object - domain.UnifiedGame: - properties: - bets: - items: - type: number - type: array - category: - type: string - demoUrl: - type: string - deviceType: - type: string - gameId: - type: string - hasDemo: - type: boolean - hasFreeBets: - type: boolean - name: - type: string - provider: - type: string - providerId: - type: string - rtp: - type: number - status: - type: integer - thumbnail: - type: string - volatility: - type: string - type: object domain.UpdateCompanyReq: properties: admin_id: @@ -2589,109 +454,6 @@ definitions: slug: type: string type: object - domain.UpdateTransactionVerifiedReq: - properties: - verified: - example: true - type: boolean - type: object - domain.ValidBool: - properties: - valid: - type: boolean - value: - type: boolean - type: object - domain.ValidInt: - properties: - valid: - type: boolean - value: - type: integer - type: object - domain.ValidInt32: - properties: - valid: - type: boolean - value: - type: integer - type: object - domain.ValidString: - properties: - valid: - type: boolean - value: - type: string - type: object - domain.VirtualGameProvider: - properties: - created_at: - type: string - enabled: - type: boolean - logo_dark: - type: string - logo_light: - type: string - provider_id: - description: ID int64 `json:"id" db:"id"` - type: string - provider_name: - type: string - updated_at: - type: string - type: object - domain.VirtualGameProviderReport: - properties: - created_at: - type: string - id: - type: integer - provider_id: - type: string - report_date: - type: string - report_type: - type: string - total_bets: - type: number - total_games_played: - type: integer - total_payouts: - type: number - total_players: - type: integer - total_profit: - type: number - updated_at: - type: string - type: object - domain.WebhookRequest: - properties: - nonce: - type: string - notificationUrl: - type: string - paymentMethod: - type: string - phone: - type: string - sessionId: - type: string - totalAmount: - type: integer - transaction: - properties: - transactionId: - type: string - transactionStatus: - type: string - type: object - transactionStatus: - type: string - uuid: - type: string - type: object handlers.AdminProfileRes: properties: created_at: @@ -2853,15 +615,6 @@ definitions: example: "1234567890" type: string type: object - handlers.CreateTransferReq: - properties: - amount: - example: 100 - type: number - payment_method: - example: cash - type: string - type: object handlers.CustomerProfileRes: properties: created_at: @@ -2893,66 +646,6 @@ definitions: updated_at: type: string type: object - handlers.CustomerWalletRes: - properties: - created_at: - type: string - customer_id: - example: 1 - type: integer - first_name: - example: John - type: string - id: - example: 1 - type: integer - last_name: - example: Smith - type: string - number_of_deposits: - type: integer - number_of_transactions: - type: integer - number_of_transfers: - type: integer - number_of_withdraws: - type: integer - phone_number: - example: "0911111111" - type: string - regular_balance: - example: 100 - type: number - regular_id: - example: 1 - type: integer - regular_is_active: - example: true - type: boolean - regular_updated_at: - type: string - static_balance: - example: 100 - type: number - static_id: - example: 1 - type: integer - static_is_active: - example: true - type: boolean - static_updated_at: - type: string - total_deposits_amount: - type: number - total_transactions: - type: number - total_transfers_amount: - type: number - total_withdraws_amount: - type: number - updated_at: - type: string - type: object handlers.CustomersRes: properties: company_id: @@ -3023,6 +716,15 @@ definitions: updated_at: type: string type: object + handlers.LoginAdminRes: + properties: + access_token: + type: string + refresh_token: + type: string + role: + type: string + type: object handlers.ManagersRes: properties: created_at: @@ -3113,13 +815,6 @@ definitions: - otp - password type: object - handlers.ResultRes: - properties: - outcomes: - items: - $ref: '#/definitions/domain.BetOutcome' - type: array - type: object handlers.SearchUserByNameOrPhoneReq: properties: query: @@ -3127,71 +822,6 @@ definitions: role: $ref: '#/definitions/domain.Role' type: object - handlers.SetLeagueActiveReq: - properties: - is_active: - type: boolean - type: object - handlers.SetLeagueAsFeatured: - properties: - is_featured: - example: true - type: boolean - type: object - handlers.TopLeague: - properties: - events: - items: - $ref: '#/definitions/domain.EventWithSettingsRes' - type: array - league_cc: - type: string - league_id: - type: integer - league_name: - type: string - league_sport_id: - type: integer - type: object - handlers.TransferWalletRes: - properties: - amount: - type: number - created_at: - type: string - depositor_first_name: - type: string - depositor_id: - type: integer - depositor_last_name: - type: string - depositor_phone_number: - type: string - id: - type: integer - message: - type: string - payment_method: - type: string - receiver_wallet_id: - type: integer - reference_number: - description: ← Add this - type: string - sender_wallet_id: - type: integer - type: - type: string - updated_at: - type: string - verified: - type: boolean - type: object - handlers.UpdateCashOutReq: - properties: - cashedOut: - type: boolean - type: object handlers.UpdateUserSuspendReq: properties: suspended: @@ -3210,14 +840,6 @@ definitions: user_id: type: integer type: object - handlers.UpdateWalletActiveReq: - properties: - is_active: - example: true - type: boolean - required: - - is_active - type: object handlers.UserProfileRes: properties: created_at: @@ -3249,58 +871,6 @@ definitions: updated_at: type: string type: object - handlers.WalletRes: - properties: - amount: - example: 100 - type: number - created_at: - type: string - id: - example: 1 - type: integer - is_active: - example: true - type: boolean - is_bettable: - example: true - type: boolean - is_transferable: - example: true - type: boolean - is_withdraw: - example: true - type: boolean - updated_at: - type: string - user_id: - example: 1 - type: integer - type: object - handlers.launchVirtualGameReq: - properties: - currency: - example: USD - type: string - game_id: - example: "1" - type: string - mode: - enum: - - fun - - real - example: real - type: string - required: - - currency - - game_id - - mode - type: object - handlers.launchVirtualGameRes: - properties: - launch_url: - type: string - type: object handlers.loginAdminReq: properties: email: @@ -3315,15 +885,6 @@ definitions: required: - password type: object - handlers.loginAdminRes: - properties: - access_token: - type: string - refresh_token: - type: string - role: - type: string - type: object handlers.loginCustomerReq: properties: email: @@ -3449,44 +1010,14 @@ info: email: support@swagger.io name: API Support url: http://www.swagger.io/support - description: This is server for FortuneBet. + description: This is server for Yimaru. license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html termsOfService: http://swagger.io/terms/ - title: FortuneBet API + title: Yimaru API version: 1.0.1 paths: - /account: - post: - consumes: - - application/json - description: Callback endpoint for Atlas game server to fetch player balance - parameters: - - description: Get user data input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.AtlasGetUserDataRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.AtlasGetUserDataResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Atlas Get User Data callback - tags: - - Virtual Games - Atlas /api/v1/{tenant_slug}/admin-login: post: consumes: @@ -3505,7 +1036,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handlers.loginAdminRes' + $ref: '#/definitions/handlers.LoginAdminRes' "400": description: Bad Request schema: @@ -3555,700 +1086,6 @@ paths: summary: Login customer tags: - auth - /api/v1/{tenant_slug}/events: - get: - consumes: - - application/json - description: Retrieve all upcoming events settings from the database - parameters: - - description: Page number - in: query - name: page - type: integer - - description: Page size - in: query - name: page_size - type: integer - - description: League ID Filter - in: query - name: league_id - type: string - - description: Sport ID Filter - in: query - name: sport_id - type: string - - description: Country Code Filter - in: query - name: cc - type: string - - description: Start Time - in: query - name: first_start_time - type: string - - description: End Time - in: query - name: last_start_time - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BaseEvent' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve all upcoming events with settings - tags: - - prematch - /api/v1/{tenant_slug}/leagues: - get: - consumes: - - application/json - description: Gets all leagues - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BaseLeague' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all leagues - tags: - - leagues - /api/v1/{tenant_slug}/leagues/{id}/featured: - put: - consumes: - - application/json - description: Set the league to featured/un-featured - parameters: - - description: League ID - in: path - name: id - required: true - type: integer - - description: League Featured Request - in: body - name: active - required: true - schema: - $ref: '#/definitions/handlers.SetLeagueAsFeatured' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Set the league to featured/un-featured - tags: - - leagues - /api/v1/{tenant_slug}/leagues/{id}/set-active: - put: - consumes: - - application/json - description: Set the league to active - parameters: - - description: League ID - in: path - name: id - required: true - type: integer - - description: League Active Request - in: body - name: active - required: true - schema: - $ref: '#/definitions/handlers.SetLeagueActiveReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Set the league to active - tags: - - leagues - /api/v1/{tenant_slug}/market-settings: - delete: - consumes: - - application/json - description: Remove all overridden market settings for a specific tenant - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Delete all market settings for a tenant - tags: - - market_settings - get: - consumes: - - application/json - description: Get all market settings overridden for a specific tenant - parameters: - - description: Number of results to return (default 10) - in: query - name: limit - type: integer - - description: Number of results to skip (default 0) - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.CompanyMarketSettings' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve all market settings for a tenant - tags: - - market_settings - post: - consumes: - - application/json - description: Insert new market settings for a specific tenant/company - parameters: - - description: Market Settings - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.CreateCompanyMarketSettings' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Insert company-specific market settings - tags: - - market_settings - /api/v1/{tenant_slug}/market-settings/{id}: - delete: - consumes: - - application/json - description: Remove a specific overridden market setting for a tenant - parameters: - - description: Market ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Delete a specific market setting for a tenant - tags: - - market_settings - /api/v1/{tenant_slug}/odds: - get: - consumes: - - application/json - description: Retrieve all odds from the database - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.OddMarketFilter' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve all odds - tags: - - prematch - /api/v1/{tenant_slug}/odds/upcoming/{upcoming_id}: - get: - consumes: - - application/json - description: Retrieve prematch odds by upcoming event ID (FI from Bet365) with - optional pagination - parameters: - - description: Upcoming Event ID (FI) - in: path - name: upcoming_id - required: true - type: string - - description: 'Number of results to return (default: 10)' - in: query - name: limit - type: integer - - description: 'Number of results to skip (default: 0)' - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.OddMarketFilter' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve prematch odds by upcoming ID (FI) - tags: - - prematch - /api/v1/{tenant_slug}/odds/upcoming/{upcoming_id}/market/{market_id}: - get: - consumes: - - application/json - description: Retrieve raw odds records using a Market ID - parameters: - - description: Upcoming ID - in: path - name: upcoming_id - required: true - type: string - - description: Market ID - in: path - name: market_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.RawOddsByMarketID' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve raw odds by Market ID - tags: - - prematch - /api/v1/{tenant_slug}/sport/bet: - get: - consumes: - - application/json - description: Gets all the bets - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BetRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all bets - tags: - - bet - post: - consumes: - - application/json - description: Creates a bet - parameters: - - description: Creates bet - in: body - name: createBet - required: true - schema: - $ref: '#/definitions/domain.CreateBetReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BetRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create a bet - tags: - - bet - /api/v1/{tenant_slug}/sport/bet/{id}: - delete: - consumes: - - application/json - description: Deletes bet by id - parameters: - - description: Bet ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Deletes bet by id - tags: - - bet - get: - consumes: - - application/json - description: Gets a single bet by id - parameters: - - description: Bet ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BetRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets bet by id - tags: - - bet - patch: - consumes: - - application/json - description: Updates the cashed out field - parameters: - - description: Bet ID - in: path - name: id - required: true - type: integer - - description: Updates Cashed Out - in: body - name: updateCashOut - required: true - schema: - $ref: '#/definitions/handlers.UpdateCashOutReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Updates the cashed out field - tags: - - bet - /api/v1/{tenant_slug}/sport/bet/fastcode: - post: - consumes: - - application/json - description: Creates a bet with fast code - parameters: - - description: Creates bet - in: body - name: createBetWithFastCode - required: true - schema: - $ref: '#/definitions/domain.CreateBetWithFastCodeReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BetRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create a bet with fast code - tags: - - bet - /api/v1/{tenant_slug}/sport/bet/fastcode/{fast_code}: - get: - consumes: - - application/json - description: Gets a single bet by fast_code - parameters: - - description: Bet ID - in: path - name: fast_code - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BetRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets bet by fast_code - tags: - - bet - /api/v1/{tenant_slug}/sport/random/bet: - post: - consumes: - - application/json - description: Generate a random bet - parameters: - - description: Create Random bet - in: body - name: createBet - required: true - schema: - $ref: '#/definitions/domain.RandomBetReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BetRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Generate a random bet - tags: - - bet - /api/v1/{tenant_slug}/ticket: - get: - consumes: - - application/json - description: Retrieve all tickets - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.TicketRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all tickets - tags: - - ticket - post: - consumes: - - application/json - description: Creates a temporary ticket - parameters: - - description: Creates ticket - in: body - name: createTicket - required: true - schema: - $ref: '#/definitions/domain.CreateTicketReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.CreateTicketRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create a temporary ticket - tags: - - ticket - /api/v1/{tenant_slug}/ticket/{id}: - get: - consumes: - - application/json - description: Retrieve ticket details by ticket ID - parameters: - - description: Ticket ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.TicketRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get ticket by ID - tags: - - ticket - /api/v1/{tenant_slug}/top-leagues: - get: - consumes: - - application/json - description: Retrieve all top leagues - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/handlers.TopLeague' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve all top leagues - tags: - - prematch /api/v1/{tenant_slug}/user/admin-profile: get: consumes: @@ -4274,31 +1111,6 @@ paths: summary: Get user profile tags: - user - /api/v1/{tenant_slug}/user/bets: - get: - consumes: - - application/json - description: Gets user bets - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BetRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets user bets - tags: - - user /api/v1/{tenant_slug}/user/checkPhoneEmailExist: post: consumes: @@ -4504,66 +1316,6 @@ paths: summary: Send reset code tags: - user - /api/v1/{tenant_slug}/user/wallet: - get: - consumes: - - application/json - description: Retrieve customer wallet details - parameters: - - description: Company ID - in: header - name: company_id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.CustomerWalletRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - security: - - Bearer: [] - summary: Get customer wallet - tags: - - wallet - /api/v1/{tenant_slug}events/{id}: - get: - consumes: - - application/json - description: Retrieve an upcoming event by ID - parameters: - - description: ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BaseEvent' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve an upcoming by ID - tags: - - prematch /api/v1/admin: get: consumes: @@ -4722,387 +1474,6 @@ paths: summary: Update Admin tags: - admin - /api/v1/alea-games/launch: - get: - consumes: - - application/json - description: Generates an authenticated launch URL for Alea Play virtual games - parameters: - - description: Game identifier (e.g., 'aviator', 'plinko') - in: query - name: game_id - required: true - type: string - - default: USD - description: Currency code (ISO 4217) - enum: - - USD - - EUR - - GBP - in: query - name: currency - type: string - - default: real - description: Game mode - enum: - - real - - demo - in: query - name: mode - type: string - produces: - - application/json - responses: - "200": - description: Returns authenticated game launch URL - schema: - additionalProperties: - allOf: - - type: string - - properties: - launch_url: - type: string - type: object - type: object - security: - - BearerAuth: [] - summary: Launch an Alea Play virtual game - tags: - - Alea Virtual Games - /api/v1/arifpay/b2c-webhook: - post: - consumes: - - application/json - description: Handles webhook notifications from Arifpay for B2C transfers and - updates transfer + wallet status. - parameters: - - description: Arifpay webhook payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.WebhookRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Handle Arifpay B2C Webhook - tags: - - Arifpay - /api/v1/arifpay/b2c/transfer: - post: - consumes: - - application/json - description: Initiates a B2C transfer using Telebirr, CBE, or MPESA depending - on the "type" query parameter - parameters: - - description: Transfer type (telebirr, cbe, mpesa) - in: query - name: type - required: true - type: string - - description: Transfer request payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.CheckoutSessionClientRequest' - produces: - - application/json - responses: - "200": - description: 'message: transfer executed successfully' - schema: - additionalProperties: - type: string - type: object - "400": - description: 'error: invalid request or unsupported transfer type' - schema: - additionalProperties: - type: string - type: object - "500": - description: 'error: internal server error' - schema: - additionalProperties: - type: string - type: object - summary: Execute B2C Transfer - tags: - - Arifpay - /api/v1/arifpay/c2b-webhook: - post: - consumes: - - application/json - description: Handles webhook notifications from Arifpay for C2B transfers and - updates transfer + wallet status. - parameters: - - description: Arifpay webhook payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.WebhookRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Handle Arifpay C2B Webhook - tags: - - Arifpay - /api/v1/arifpay/checkout: - post: - consumes: - - application/json - description: Creates a payment session using Arifpay and returns a redirect - URL. - parameters: - - description: Checkout session request payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.CheckoutSessionClientRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Create Arifpay Checkout Session - tags: - - Arifpay - /api/v1/arifpay/checkout/cancel/{sessionId}: - post: - consumes: - - application/json - description: Cancels a payment session using Arifpay before completion. - parameters: - - description: Checkout session ID - in: path - name: sessionId - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Cancel Arifpay Checkout Session - tags: - - Arifpay - /api/v1/arifpay/payment-methods: - get: - description: Returns all payment method IDs and names for Arifpay - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.ARIFPAYPaymentMethod' - type: array - summary: List Arifpay Payment Methods - tags: - - Arifpay - /api/v1/arifpay/session-id/verify-transaction/{session_id}: - get: - consumes: - - application/json - description: Verifies an Arifpay transaction using a session ID - parameters: - - description: Arifpay Session ID - in: query - name: session_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Verify Arifpay Transaction by Session ID - tags: - - Arifpay - /api/v1/arifpay/transaction-id/verify-transaction: - post: - consumes: - - application/json - description: Verifies a transaction using transaction ID and payment type - parameters: - - description: Transaction verification payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.ArifpayVerifyByTransactionIDRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Verify Arifpay Transaction - tags: - - Arifpay - /api/v1/atlas/freespin: - post: - consumes: - - application/json - description: Sends a request to Atlas to create free spins/bets for a given - player - parameters: - - description: Free spin input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.FreeSpinRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - $ref: '#/definitions/domain.FreeSpinResponse' - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Create free spins for a player - tags: - - Virtual Games - Atlas - /api/v1/atlas/games: - get: - description: Retrieves available Atlas virtual games from the provider - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.AtlasGameEntity' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: List Atlas virtual games - tags: - - Virtual Games - Atlas - /api/v1/atlas/init-game: - post: - consumes: - - application/json - description: Initializes a game session for the given player using Atlas virtual - game provider - parameters: - - description: Start game input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.AtlasGameInitRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - $ref: '#/definitions/domain.AtlasGameInitResponse' - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Start an Atlas virtual game session - tags: - - Virtual Games - Atlas /api/v1/auth/logout: post: consumes: @@ -5480,31 +1851,6 @@ paths: summary: Updates a branch tags: - branch - /api/v1/branch/{id}/bets: - get: - consumes: - - application/json - description: Gets bets by its branch id - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BetRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets bets by its branch id - tags: - - branch /api/v1/branch/{id}/cashier: get: consumes: @@ -5684,31 +2030,6 @@ paths: summary: Gets all branch locations tags: - branch - /api/v1/branchWallet: - get: - consumes: - - application/json - description: Retrieve all branch wallets - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/handlers.WalletRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all branch wallets - tags: - - wallet /api/v1/cashier/{id}: get: consumes: @@ -5742,39 +2063,6 @@ paths: summary: Get cashier by id tags: - cashier - /api/v1/cashierWallet: - get: - consumes: - - application/json - description: Get wallet for cashier - parameters: - - description: User ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.UserProfileRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get wallet for cashier - tags: - - cashier /api/v1/cashiers: get: consumes: @@ -5885,358 +2173,6 @@ paths: summary: Update cashier tags: - cashier - /api/v1/chapa/balances: - get: - consumes: - - application/json - description: Retrieve Chapa account balance, optionally filtered by currency - code (e.g., ETB, USD) - parameters: - - description: Currency code (optional) - in: query - name: currency_code - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get Chapa account balance - tags: - - Chapa - /api/v1/chapa/banks: - get: - consumes: - - application/json - description: Get list of banks supported by Chapa - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get supported banks - tags: - - Chapa - /api/v1/chapa/payments/deposit: - post: - consumes: - - application/json - description: Starts a new deposit process using Chapa payment gateway - parameters: - - description: Deposit request - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.ChapaDepositRequestPayload' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ChapaDepositResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - security: - - ApiKeyAuth: [] - summary: Initiate a deposit - tags: - - Chapa - /api/v1/chapa/payments/receipt/{chapa_ref}: - get: - consumes: - - application/json - description: Retrieve the Chapa payment receipt URL using the reference ID - parameters: - - description: Chapa Reference ID - in: path - name: chapa_ref - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get Chapa Payment Receipt URL - tags: - - Chapa - /api/v1/chapa/payments/webhook/verify: - post: - consumes: - - application/json - description: Handles payment and transfer notifications from Chapa - parameters: - - description: Webhook payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.ChapaWebhookPayment' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Chapa payment webhook callback (used by Chapa) - tags: - - Chapa - /api/v1/chapa/payments/withdraw: - post: - consumes: - - application/json - description: Initiates a withdrawal request to transfer funds to a bank account - via Chapa - parameters: - - description: Withdrawal request details - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.ChapaWithdrawalRequest' - produces: - - application/json - responses: - "200": - description: Chapa withdrawal process initiated successfully - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Invalid request body - schema: - $ref: '#/definitions/domain.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/domain.ErrorResponse' - "422": - description: Unprocessable entity - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/domain.ErrorResponse' - security: - - ApiKeyAuth: [] - summary: Initiate a withdrawal - tags: - - Chapa - /api/v1/chapa/swap: - post: - consumes: - - application/json - description: Convert an amount from one currency to another using Chapa's currency - swap API - parameters: - - description: Swap request payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.SwapRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Swap currency using Chapa API - tags: - - Chapa - /api/v1/chapa/transaction/cancel/{tx_ref}: - put: - consumes: - - application/json - description: Cancels an active Chapa transaction using its transaction reference - parameters: - - description: Transaction Reference - in: path - name: tx_ref - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ChapaCancelResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "404": - description: Not Found - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - security: - - ApiKeyAuth: [] - summary: Cancel a Chapa deposit transaction - tags: - - Chapa - /api/v1/chapa/transaction/events/{ref_id}: - get: - consumes: - - application/json - description: Retrieve the timeline of events for a specific Chapa transaction - parameters: - - description: Transaction Reference - in: path - name: ref_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.ChapaTransactionEvent' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Fetch transaction events - tags: - - Chapa - /api/v1/chapa/transaction/manual/verify/{tx_ref}: - get: - consumes: - - application/json - description: Manually verify a payment using Chapa's API - parameters: - - description: Transaction Reference - in: path - name: tx_ref - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Verify a payment manually - tags: - - Chapa - /api/v1/chapa/transactions: - get: - consumes: - - application/json - description: Retrieves all transactions from Chapa payment gateway - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.ChapaTransaction' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - security: - - ApiKeyAuth: [] - summary: Get all Chapa transactions - tags: - - Chapa - /api/v1/chapa/transfers: - get: - consumes: - - application/json - description: Retrieve all transfer records from Chapa - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all Chapa transfers - tags: - - Chapa /api/v1/company: get: consumes: @@ -6573,749 +2509,6 @@ paths: summary: Update Customers tags: - customer - /api/v1/customer/{id}/bets: - get: - consumes: - - application/json - description: Get customer bets - parameters: - - description: User ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.CustomersRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get customer bets - tags: - - customer - /api/v1/customerWallet: - get: - consumes: - - application/json - description: Retrieve all customer wallets - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/handlers.CustomerWalletRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all customer wallets - tags: - - wallet - /api/v1/direct-deposits: - get: - consumes: - - application/json - description: Fetches direct deposits filtered by status with pagination - parameters: - - description: Deposit status (e.g., PENDING, APPROVED, REJECTED) - in: query - name: status - required: true - type: string - - description: Page number - in: query - name: page - type: integer - - description: Page size - in: query - name: pageSize - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.DirectDeposit' - type: array - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get direct deposits by status - tags: - - DirectDeposit - post: - consumes: - - application/json - description: Creates a direct deposit for a customer and notifies both the customer - and admins - parameters: - - description: Direct deposit details - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.CreateDirectDeposit' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - $ref: '#/definitions/domain.DirectDeposit' - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Create a new direct deposit - tags: - - DirectDeposit - /api/v1/direct-deposits/{depositID}: - delete: - consumes: - - application/json - description: Deletes a direct deposit by its ID - parameters: - - description: Deposit ID - in: path - name: depositID - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - type: string - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Delete a direct deposit - tags: - - DirectDeposit - get: - consumes: - - application/json - description: Fetches a single direct deposit by its ID - parameters: - - description: Deposit ID - in: path - name: depositID - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - $ref: '#/definitions/domain.DirectDeposit' - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get a direct deposit by ID - tags: - - DirectDeposit - /api/v1/direct-deposits/{depositID}/approve: - post: - consumes: - - application/json - description: Approves a direct deposit by admin and credits customer wallet - parameters: - - description: Deposit ID - in: path - name: depositID - required: true - type: integer - - description: Admin ID performing the approval - in: query - name: adminID - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - type: string - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Approve a direct deposit - tags: - - DirectDeposit - /api/v1/direct-deposits/{depositID}/reject: - post: - consumes: - - application/json - description: Rejects a direct deposit by admin and notifies the customer - parameters: - - description: Deposit ID - in: path - name: depositID - required: true - type: integer - - description: Admin ID performing the rejection - in: query - name: adminID - required: true - type: integer - - description: Reason for rejection - in: query - name: reason - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - type: string - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Reject a direct deposit - tags: - - DirectDeposit - /api/v1/enetpulse/betting-offers: - get: - consumes: - - application/json - description: Fetches all EnetPulse preodds betting offers stored in the database - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulsePreoddsBettingOffer' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all betting offers - tags: - - EnetPulse - /api/v1/enetpulse/fixtures: - get: - consumes: - - application/json - description: Fetches all fixtures stored in the database - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulseFixture' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all stored fixtures - tags: - - EnetPulse - /api/v1/enetpulse/fixtures/preodds: - get: - consumes: - - application/json - description: Fetches all EnetPulse fixtures along with their associated pre-match - odds - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulseFixtureWithPreodds' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get fixtures with preodds - tags: - - EnetPulse - /api/v1/enetpulse/preodds: - get: - consumes: - - application/json - description: Fetches all EnetPulse pre-match odds stored in the database - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulsePreodds' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all preodds - tags: - - EnetPulse - /api/v1/enetpulse/preodds-with-offers: - get: - consumes: - - application/json - description: Fetches all EnetPulse pre-match odds along with their associated - betting offers stored in the database - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulsePreodds' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all preodds with betting offers - tags: - - EnetPulse - /api/v1/enetpulse/results: - get: - consumes: - - application/json - description: Fetches all EnetPulse match results stored in the database - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulseResult' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all results - tags: - - EnetPulse - /api/v1/enetpulse/sports: - get: - consumes: - - application/json - description: Fetches all sports stored in the database - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulseSport' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all sports - tags: - - EnetPulse - /api/v1/enetpulse/tournament-stages: - get: - consumes: - - application/json - description: Fetches all tournament stages stored in the database - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulseTournamentStage' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all tournament stages - tags: - - EnetPulse - /api/v1/enetpulse/tournament-templates: - get: - consumes: - - application/json - description: Fetches all tournament templates stored in the database - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulseTournamentTemplate' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all tournament templates - tags: - - EnetPulse - /api/v1/enetpulse/tournaments: - get: - consumes: - - application/json - description: Fetches all tournaments stored in the database - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.EnetpulseTournament' - type: array - type: object - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all tournaments - tags: - - EnetPulse - /api/v1/events: - get: - consumes: - - application/json - description: Retrieve all upcoming events from the database - parameters: - - description: Page number - in: query - name: page - type: integer - - description: Page size - in: query - name: page_size - type: integer - - description: League ID Filter - in: query - name: league_id - type: string - - description: Sport ID Filter - in: query - name: sport_id - type: string - - description: Country Code Filter - in: query - name: cc - type: string - - description: Start Time - in: query - name: first_start_time - type: string - - description: End Time - in: query - name: last_start_time - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BaseEvent' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve all upcoming events - tags: - - prematch - /api/v1/events/{id}: - delete: - consumes: - - application/json - description: Set the event status to removed - parameters: - - description: Event ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Set the event status to removed - tags: - - event - get: - consumes: - - application/json - description: Retrieve an upcoming event by ID - parameters: - - description: ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BaseEvent' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve an upcoming by ID - tags: - - prematch - /api/v1/events/{id}/bets: - get: - consumes: - - application/json - description: Retrieve bet outcomes by event id - parameters: - - description: ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BaseEvent' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve bet outcomes by event id - tags: - - prematch - /api/v1/events/{id}/is_monitored: - patch: - consumes: - - application/json - description: Update the event is_monitored - parameters: - - description: Event ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: update the event is_monitored - tags: - - event - /api/v1/events/{id}/settings: - put: - consumes: - - application/json - description: Update the event settings - parameters: - - description: Event ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: update the event settings - tags: - - event /api/v1/issues: get: description: Admin endpoint to list all reported issues with pagination @@ -7464,31 +2657,6 @@ paths: summary: Get reported issues by a user tags: - Issues - /api/v1/leagues: - get: - consumes: - - application/json - description: Gets all leagues - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BaseLeague' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all leagues - tags: - - leagues /api/v1/logs: get: description: Fetches application logs from MongoDB with pagination, level filtering, @@ -7697,137 +2865,6 @@ paths: summary: Update Managers tags: - manager - /api/v1/market-settings: - get: - consumes: - - application/json - description: Get all market settings that apply globally - parameters: - - description: Number of results to return (default 10) - in: query - name: limit - type: integer - - description: Number of results to skip (default 0) - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.MarketSettings' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve all global market settings - tags: - - market_settings - /api/v1/odds: - get: - consumes: - - application/json - description: Retrieve all odds from the database - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.OddMarketFilter' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve all odds - tags: - - prematch - /api/v1/odds/upcoming/{upcoming_id}: - get: - consumes: - - application/json - description: Retrieve prematch odds by upcoming event ID (FI from Bet365) with - optional pagination - parameters: - - description: Upcoming Event ID (FI) - in: path - name: upcoming_id - required: true - type: string - - description: 'Number of results to return (default: 10)' - in: query - name: limit - type: integer - - description: 'Number of results to skip (default: 0)' - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.OddMarketWithEventFilter' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve prematch odds by upcoming ID (FI) - tags: - - prematch - /api/v1/odds/upcoming/{upcoming_id}/market/{market_id}: - get: - consumes: - - application/json - description: Retrieve raw odds records using a Market ID - parameters: - - description: Upcoming ID - in: path - name: upcoming_id - required: true - type: string - - description: Market ID - in: path - name: market_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.RawOddsByMarketID' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve raw odds by Market ID - tags: - - prematch /api/v1/operation: post: consumes: @@ -7858,420 +2895,6 @@ paths: summary: Create a operation tags: - branch - /api/v1/orchestrator/virtual-game/provider-reports/asc: - get: - description: Retrieves all virtual game provider reports sorted by total_games_played - in ascending order - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.VirtualGameProviderReport' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: List virtual game provider reports (ascending) - tags: - - VirtualGames - Orchestration - /api/v1/orchestrator/virtual-game/provider-reports/desc: - get: - description: Retrieves all virtual game provider reports sorted by total_games_played - in descending order - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.VirtualGameProviderReport' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: List virtual game provider reports (descending) - tags: - - VirtualGames - Orchestration - /api/v1/orchestrator/virtual-games: - get: - consumes: - - application/json - description: Returns all virtual games with optional filters (category, search, - pagination) - parameters: - - description: Filter by category - in: query - name: category - type: string - - description: Search by game name - in: query - name: search - type: string - - description: Pagination limit - in: query - name: limit - type: integer - - description: Pagination offset - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.UnifiedGame' - type: array - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: List all virtual games - tags: - - VirtualGames - Orchestration - /api/v1/report-files/download/{filename}: - get: - description: Downloads a generated report CSV file from the server - parameters: - - description: Name of the report file to download (e.g., report_daily_2025-06-21.csv) - in: path - name: filename - required: true - type: string - produces: - - text/csv - responses: - "200": - description: CSV file will be downloaded - schema: - type: file - "400": - description: Missing or invalid filename - schema: - $ref: '#/definitions/domain.ErrorResponse' - "404": - description: Report file not found - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal server error while serving the file - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Download a CSV report file - tags: - - Reports - /api/v1/report-files/list: - get: - description: Returns a paginated list of generated report CSV files with search - capability - parameters: - - description: Search term to filter filenames - in: query - name: search - type: string - - default: 1 - description: 'Page number (default: 1)' - in: query - name: page - type: integer - - default: 20 - description: 'Items per page (default: 20, max: 100)' - in: query - name: limit - type: integer - produces: - - application/json - responses: - "200": - description: Paginated list of CSV report filenames - schema: - $ref: '#/definitions/domain.PaginatedFileResponse' - "400": - description: Invalid pagination parameters - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Failed to read report directory - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: List available report CSV files - tags: - - Reports - /api/v1/reports/dashboard: - get: - consumes: - - application/json - description: Returns a comprehensive dashboard report with key metrics - parameters: - - description: Company ID filter - in: query - name: company_id - type: integer - - description: Branch ID filter - in: query - name: branch_id - type: integer - - description: User ID filter - in: query - name: user_id - type: integer - - description: Start time filter (RFC3339 format) - in: query - name: start_time - type: string - - description: End time filter (RFC3339 format) - in: query - name: end_time - type: string - - description: Sport ID filter - in: query - name: sport_id - type: string - - description: Status filter (0=Pending, 1=Win, 2=Loss, 3=Half, 4=Void, 5=Error) - in: query - name: status - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.DashboardSummary' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - security: - - ApiKeyAuth: [] - summary: Get dashboard report - tags: - - Reports - /api/v1/result/b365/{id}: - get: - consumes: - - application/json - description: Get results for an event - parameters: - - description: Event ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/handlers.ResultRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get results for an event - tags: - - result - /api/v1/santimpay/b2c-withdrawal: - post: - consumes: - - application/json - description: Initiates a B2C withdrawal request with SantimPay and returns the - response. - parameters: - - description: SantimPay B2C withdrawal request payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.GeneratePaymentURLRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Process SantimPay B2C Withdrawal - tags: - - SantimPay - /api/v1/santimpay/b2c/partners: - get: - consumes: - - application/json - description: Fetches a list of available B2C payout partners (e.g., Telebirr, - Mpesa, Banks) from SantimPay. - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get SantimPay B2C Partners - tags: - - SantimPay - /api/v1/santimpay/callback: - post: - consumes: - - application/json - description: Processes a callback from SantimPay, updates transfer status, and - credits user wallet if payment was successful. - parameters: - - description: SantimPay callback payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.SantimPayCallbackPayload' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Process SantimPay Payment Callback - tags: - - SantimPay - /api/v1/santimpay/direct-payment: - post: - consumes: - - application/json - description: Initiates a direct payment request with SantimPay and returns the - response. - parameters: - - description: SantimPay direct payment request payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.GeneratePaymentURLRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Process SantimPay Direct Payment - tags: - - SantimPay - /api/v1/santimpay/payment: - post: - consumes: - - application/json - description: Generates a payment URL using SantimPay and returns it to the client. - parameters: - - description: SantimPay payment request payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.GeneratePaymentURLRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Create SantimPay Payment Session - tags: - - SantimPay - /api/v1/santimpay/transaction-status: - post: - consumes: - - application/json - description: Retrieves the real-time status of a transaction from SantimPay. - parameters: - - description: Transaction status request payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.TransactionStatusRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Check SantimPay Transaction Status - tags: - - SantimPay /api/v1/search/branch: get: consumes: @@ -8328,384 +2951,6 @@ paths: summary: Gets all companies tags: - company - /api/v1/shop/bet: - get: - consumes: - - application/json - description: Gets all the shop bets - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.ShopBetRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all shop bets - tags: - - bet - post: - consumes: - - application/json - description: Create bet at branch - parameters: - - description: create bet - in: body - name: createBet - required: true - schema: - $ref: '#/definitions/domain.ShopBetReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ShopTransactionRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create bet at branch - tags: - - transaction - /api/v1/shop/bet/{id}: - get: - consumes: - - application/json - description: Cashout bet at branch - parameters: - - description: cashout bet - in: body - name: createBet - required: true - schema: - $ref: '#/definitions/domain.CashoutReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ShopTransactionRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Cashout bet at branch - tags: - - transaction - /api/v1/shop/bet/{id}/cashout: - post: - consumes: - - application/json - description: Cashout bet at branch - parameters: - - description: cashout bet - in: body - name: cashoutBet - required: true - schema: - $ref: '#/definitions/domain.CashoutReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ShopTransactionRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Cashout bet at branch - tags: - - transaction - /api/v1/shop/cashout: - post: - consumes: - - application/json - description: Cashout bet by cashoutID - parameters: - - description: cashout bet - in: body - name: cashoutBet - required: true - schema: - $ref: '#/definitions/domain.CashoutReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ShopTransactionRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Cashout bet by cashoutID - tags: - - transaction - /api/v1/shop/cashout/{id}: - get: - consumes: - - application/json - description: Cashout bet at branch - parameters: - - description: cashout bet - in: body - name: createBet - required: true - schema: - $ref: '#/definitions/domain.CashoutReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ShopTransactionRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Cashout bet at branch - tags: - - transaction - /api/v1/shop/deposit: - post: - consumes: - - application/json - description: Transfers money from branch wallet to customer wallet - parameters: - - description: ShopDepositReq - in: body - name: transferToWallet - required: true - schema: - $ref: '#/definitions/domain.ShopDepositReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ShopDepositRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Shop deposit into customer wallet - tags: - - transaction - /api/v1/shop/transaction: - get: - consumes: - - application/json - description: Gets all the transactions - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.ShopTransactionRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all transactions - tags: - - transaction - /api/v1/shop/transaction/{id}: - get: - consumes: - - application/json - description: Gets a single transaction by id - parameters: - - description: Transaction ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ShopTransactionRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets transaction by id - tags: - - transaction - put: - consumes: - - application/json - description: Updates the verified status of a transaction - parameters: - - description: Transaction ID - in: path - name: id - required: true - type: integer - - description: Updates Transaction Verification - in: body - name: updateVerified - required: true - schema: - $ref: '#/definitions/domain.UpdateTransactionVerifiedReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Updates the verified field of a transaction - tags: - - transaction - /api/v1/shop/transaction/{id}/bet: - get: - consumes: - - application/json - description: Gets a single shop bet by transaction id - parameters: - - description: Transaction ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ShopTransactionRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets shop bet by transaction id - tags: - - transaction - /api/v1/sport/bet/{id}: - delete: - consumes: - - application/json - description: Deletes bet by id - parameters: - - description: Bet ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Deletes bet by id - tags: - - bet - get: - consumes: - - application/json - description: Gets a single bet by id - parameters: - - description: Bet ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BetRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets bet by id - tags: - - bet /api/v1/super-login: post: consumes: @@ -8724,7 +2969,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handlers.loginAdminRes' + $ref: '#/definitions/handlers.LoginAdminRes' "400": description: Bad Request schema: @@ -8896,66 +3141,6 @@ paths: summary: Update Admin tags: - admin - /api/v1/telebirr/callback: - post: - consumes: - - application/json - description: Processes the Telebirr payment result and updates wallet balance. - parameters: - - description: Callback payload from Telebirr - in: body - name: payload - required: true - schema: - $ref: '#/definitions/domain.TelebirrPaymentCallbackPayload' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Handle Telebirr Payment Callback - tags: - - Telebirr - /api/v1/telebirr/payment: - post: - consumes: - - application/json - description: Generates a payment URL using Telebirr and returns it to the client. - parameters: - - description: Telebirr payment request payload - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.GeneratePaymentURLRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Create Telebirr Payment Session - tags: - - Telebirr /api/v1/tenant: get: consumes: @@ -9088,97 +3273,6 @@ paths: summary: Update Customers tags: - customer - /api/v1/tenant/{tenant_slug}/customer/{id}/bets: - get: - consumes: - - application/json - description: Get tenant customer bets - parameters: - - description: User ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.CustomersRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get tenant customer bets - tags: - - customer - /api/v1/tenant/{tenant_slug}/events/{id}/bets: - get: - consumes: - - application/json - description: Retrieve bet outcomes by event id - parameters: - - description: ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BaseEvent' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Retrieve bet outcomes by event id - tags: - - prematch - /api/v1/tenant/{tenant_slug}/events/{id}/settings: - put: - consumes: - - application/json - description: Update the event settings - parameters: - - description: Event ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: update the event settings - tags: - - event /api/v1/tenant/{tenant_slug}/referral/stats: get: consumes: @@ -9204,150 +3298,6 @@ paths: summary: Get referral statistics tags: - referral - /api/v1/ticket: - get: - consumes: - - application/json - description: Retrieve all tickets - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.TicketRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all tickets - tags: - - ticket - /api/v1/ticket/{id}: - get: - consumes: - - application/json - description: Retrieve ticket details by ticket ID - parameters: - - description: Ticket ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.TicketRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get ticket by ID - tags: - - ticket - /api/v1/transfer/refill/:id: - post: - consumes: - - application/json - description: Super Admin route to refill a wallet - parameters: - - description: Create Transfer - in: body - name: refillWallet - required: true - schema: - $ref: '#/definitions/handlers.CreateTransferReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.TransferWalletRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Refill wallet - tags: - - transfer - /api/v1/transfer/wallet/:id: - post: - consumes: - - application/json - description: Create a transfer to wallet - parameters: - - description: Create Transfer - in: body - name: transferToWallet - required: true - schema: - $ref: '#/definitions/handlers.CreateTransferReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.TransferWalletRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create a transfer to wallet - tags: - - transfer - /api/v1/transfer/wallet/{id}: - get: - consumes: - - application/json - description: Get transfer by wallet - parameters: - - description: Create Transfer - in: body - name: transferToWallet - required: true - schema: - $ref: '#/definitions/handlers.CreateTransferReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.TransferWalletRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get transfer by wallet - tags: - - transfer /api/v1/user/delete/{id}: delete: consumes: @@ -9530,868 +3480,6 @@ paths: summary: Suspend or unsuspend a user tags: - user - /api/v1/veli/credit-balances: - get: - consumes: - - application/json - description: Fetches current credit balances per currency for the specified - brand - parameters: - - description: Brand ID - in: query - name: brandId - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.CreditBalance' - type: array - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get VeliGames credit balances for a brand - tags: - - Virtual Games - VeliGames - /api/v1/veli/games-list: - post: - consumes: - - application/json - description: Retrieves games for the specified provider - parameters: - - description: Brand and Provider ID - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.GameListRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get games by provider - tags: - - Virtual Games - VeliGames - /api/v1/veli/gaming-activity: - post: - consumes: - - application/json - description: Retrieves successfully processed gaming activity transactions (BET, - WIN, CANCEL) from Veli Games - parameters: - - description: Gaming Activity Request - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.GamingActivityRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - $ref: '#/definitions/domain.GamingActivityResponse' - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get Veli Gaming Activity - tags: - - Virtual Games - VeliGames - /api/v1/veli/huge-wins: - post: - consumes: - - application/json - description: Retrieves huge win transactions based on brand configuration (e.g. - win > 10000 USD or 100x bet) - parameters: - - description: Huge Wins Request - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.HugeWinsRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - $ref: '#/definitions/domain.HugeWinsResponse' - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get Veli Huge Wins - tags: - - Virtual Games - VeliGames - /api/v1/veli/providers: - post: - consumes: - - application/json - description: Retrieves the list of VeliGames providers - parameters: - - description: Brand ID and paging options - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.ProviderRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.ProviderResponse' - type: array - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get game providers - tags: - - Virtual Games - VeliGames - /api/v1/veli/start-demo-game: - post: - consumes: - - application/json - description: Starts a demo session of the specified game (must support demo - mode) - parameters: - - description: Start demo game input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.DemoGameRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - $ref: '#/definitions/domain.GameStartResponse' - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Start a demo game session - tags: - - Virtual Games - VeliGames - /api/v1/veli/start-game: - post: - consumes: - - application/json - description: Starts a real VeliGames session with the given player and game - info - parameters: - - description: Start game input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.GameStartRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - $ref: '#/definitions/domain.GameStartResponse' - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Start a real game session - tags: - - Virtual Games - VeliGames - /api/v1/virtual-game/favorites: - get: - description: Lists the games that the user marked as favorite - parameters: - - description: Filter by provider ID - in: query - name: providerID - type: string - - description: Number of results to return - in: query - name: limit - type: integer - - description: Results offset - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.UnifiedGame' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get user's favorite games - tags: - - VirtualGames - Favourites - post: - consumes: - - application/json - description: Adds a game to the user's favorite games list - parameters: - - description: Game ID and Provider ID to add - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.FavoriteGameRequest' - produces: - - application/json - responses: - "201": - description: created - schema: - type: string - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Add game to favorites - tags: - - VirtualGames - Favourites - /api/v1/virtual-game/favorites/{gameID}: - delete: - description: Removes a game from the user's favorites - parameters: - - description: Game ID to remove - in: path - name: gameID - required: true - type: integer - - description: Provider ID of the game - in: query - name: providerID - required: true - type: string - produces: - - application/json - responses: - "200": - description: removed - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Remove game from favorites - tags: - - VirtualGames - Favourites - /api/v1/virtual-game/orchestrator/providers/status: - patch: - description: Sets the enabled status of a provider - parameters: - - description: Provider ID - in: path - name: provider_id - required: true - type: string - - description: Enable or Disable - in: query - name: enabled - required: true - type: boolean - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.VirtualGameProvider' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Enable/Disable a provider - tags: - - VirtualGames - Orchestration - /api/v1/virtual-game/providers: - get: - description: Lists all providers with pagination - parameters: - - description: Limit - in: query - name: limit - type: integer - - description: Offset - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - additionalProperties: true - type: object - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: List virtual game providers - tags: - - VirtualGames - Orchestration - /api/v1/virtual-game/providers/{provider_id}: - delete: - description: Deletes a provider by provider_id - parameters: - - description: Provider ID - in: path - name: provider_id - required: true - type: string - responses: - "200": - description: OK - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Remove a virtual game provider - tags: - - VirtualGames - Orchestration - get: - description: Fetches a provider by provider_id - parameters: - - description: Provider ID - in: path - name: provider_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.VirtualGameProvider' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get a virtual game provider - tags: - - VirtualGames - Orchestration - /api/v1/wallet: - get: - consumes: - - application/json - description: Retrieve all wallets - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/handlers.WalletRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all wallets - tags: - - wallet - /api/v1/wallet/{id}: - get: - consumes: - - application/json - description: Retrieve wallet details by wallet ID - parameters: - - description: Wallet ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.WalletRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get wallet by ID - tags: - - wallet - patch: - consumes: - - application/json - description: Can activate and deactivate wallet - parameters: - - description: Wallet ID - in: path - name: id - required: true - type: integer - - description: Update Wallet Active - in: body - name: updateCashOut - required: true - schema: - $ref: '#/definitions/handlers.UpdateWalletActiveReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Activate and Deactivate Wallet - tags: - - wallet - /api/v1/webhooks/alea: - post: - consumes: - - application/json - description: Handles webhook callbacks from Alea Play virtual games for bet - settlement - parameters: - - description: Callback payload - in: body - name: callback - required: true - schema: - $ref: '#/definitions/domain.AleaPlayCallback' - produces: - - application/json - responses: - "200": - description: Callback processed successfully - schema: - additionalProperties: - allOf: - - type: string - - properties: - status: - type: string - type: object - type: object - summary: Process Alea Play game callback - tags: - - Alea Virtual Games - /betwin: - post: - consumes: - - application/json - description: Processes a Bet and Win request from Atlas provider - parameters: - - description: Atlas BetWin input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.AtlasBetWinRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.AtlasBetWinResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Atlas BetWin callback - tags: - - Virtual Games - Atlas - /freespin: - post: - consumes: - - application/json - description: Handles the result of a free spin/bet from the game server - parameters: - - description: Free spin result input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.FreeSpinResultRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.FreeSpinResultResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Free Spin/Bet result callback - tags: - - Virtual Games - Atlas - /jackpot: - post: - consumes: - - application/json - description: Handles the jackpot result from the game server - parameters: - - description: Jackpot result input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.JackpotRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.JackpotResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Jackpot result callback - tags: - - Virtual Games - Atlas - /popok/games: - get: - consumes: - - application/json - description: Retrieves the list of available PopOK slot games - parameters: - - default: USD - description: Currency (e.g. USD, ETB) - in: query - name: currency - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.PopOKGame' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get PopOK Games List - tags: - - Virtual Games - PopOK - /popok/games/recommend: - get: - description: Recommends games based on user history or randomly - parameters: - - description: User ID - in: query - name: user_id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.GameRecommendation' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Recommend virtual games - tags: - - Virtual Games - PopOK - /result: - post: - consumes: - - application/json - description: Processes a round result from Atlas or other providers - parameters: - - description: Round result input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.RoundResultRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.RoundResultResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Atlas Round Result callback - tags: - - Virtual Games - Atlas - /rollback: - post: - consumes: - - application/json - description: Processes a rollback request from Atlas or other providers - parameters: - - description: Rollback request input - in: body - name: request - required: true - schema: - $ref: '#/definitions/domain.RollbackRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.RollbackResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "502": - description: Bad Gateway - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Atlas Rollback callback - tags: - - Virtual Games - Atlas - /virtual-game/callback: - post: - consumes: - - application/json - description: Processes callbacks from PopOK for game events - parameters: - - description: Callback data - in: body - name: callback - required: true - schema: - $ref: '#/definitions/domain.PopOKCallback' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ErrorResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Handle PopOK game callback - tags: - - Virtual Games - PopOK - /virtual-game/launch: - post: - consumes: - - application/json - description: Generates a URL to launch a PopOK game - parameters: - - description: Game launch details - in: body - name: launch - required: true - schema: - $ref: '#/definitions/handlers.launchVirtualGameReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.launchVirtualGameRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - security: - - Bearer: [] - summary: Launch a PopOK virtual game - tags: - - Virtual Games - PopOK - /win: - post: - consumes: - - application/json - description: Processes win callbacks from either Veli or PopOK providers by - auto-detecting the format - produces: - - application/json - responses: - "200": - description: Win processing result - schema: {} - "400": - description: Invalid request format - schema: - $ref: '#/definitions/domain.ErrorResponse' - "401": - description: Authentication failed - schema: - $ref: '#/definitions/domain.ErrorResponse' - "409": - description: Duplicate transaction - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Handle win callback (Veli or PopOK) - tags: - - Wins securityDefinitions: Bearer: in: header diff --git a/gen/db/auth.sql.go b/gen/db/auth.sql.go index 7d8d59d..d916ed8 100644 --- a/gen/db/auth.sql.go +++ b/gen/db/auth.sql.go @@ -36,7 +36,7 @@ func (q *Queries) CreateRefreshToken(ctx context.Context, arg CreateRefreshToken } const GetRefreshToken = `-- name: GetRefreshToken :one -SELECT id, user_id, token, expires_at, created_at, revoked +SELECT id, user_id, token, expires_at, revoked, created_at FROM refresh_tokens WHERE token = $1 ` @@ -49,14 +49,14 @@ func (q *Queries) GetRefreshToken(ctx context.Context, token string) (RefreshTok &i.UserID, &i.Token, &i.ExpiresAt, - &i.CreatedAt, &i.Revoked, + &i.CreatedAt, ) return i, err } const GetRefreshTokenByUserID = `-- name: GetRefreshTokenByUserID :one -SELECT id, user_id, token, expires_at, created_at, revoked +SELECT id, user_id, token, expires_at, revoked, created_at FROM refresh_tokens WHERE user_id = $1 ` @@ -69,49 +69,54 @@ func (q *Queries) GetRefreshTokenByUserID(ctx context.Context, userID int64) (Re &i.UserID, &i.Token, &i.ExpiresAt, - &i.CreatedAt, &i.Revoked, + &i.CreatedAt, ) return i, err } const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one -SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended +SELECT id, first_name, last_name, nick_name, email, phone_number, role, password, age, education_level, country, region, email_verified, phone_verified, suspended, suspended_at, organization_id, created_at, updated_at FROM users WHERE ( email = $1 OR phone_number = $2 ) AND ( - company_id = $3 + organization_id = $3 OR $3 IS NULL ) ` type GetUserByEmailPhoneParams struct { - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - CompanyID pgtype.Int8 `json:"company_id"` + Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + OrganizationID pgtype.Int8 `json:"organization_id"` } func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPhoneParams) (User, error) { - row := q.db.QueryRow(ctx, GetUserByEmailPhone, arg.Email, arg.PhoneNumber, arg.CompanyID) + row := q.db.QueryRow(ctx, GetUserByEmailPhone, arg.Email, arg.PhoneNumber, arg.OrganizationID) var i User err := row.Scan( &i.ID, &i.FirstName, &i.LastName, + &i.NickName, &i.Email, &i.PhoneNumber, &i.Role, &i.Password, + &i.Age, + &i.EducationLevel, + &i.Country, + &i.Region, &i.EmailVerified, &i.PhoneVerified, + &i.Suspended, + &i.SuspendedAt, + &i.OrganizationID, &i.CreatedAt, &i.UpdatedAt, - &i.CompanyID, - &i.SuspendedAt, - &i.Suspended, ) return i, err } diff --git a/gen/db/bet.sql.go b/gen/db/bet.sql.go deleted file mode 100644 index 9379338..0000000 --- a/gen/db/bet.sql.go +++ /dev/null @@ -1,930 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: bet.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const BulkUpdateBetOutcomeStatusByOddIDs = `-- name: BulkUpdateBetOutcomeStatusByOddIDs :exec -UPDATE bet_outcomes -SET status = $1 -WHERE odd_id = ANY($2::BIGINT []) -` - -type BulkUpdateBetOutcomeStatusByOddIDsParams struct { - Status int32 `json:"status"` - OddIds []int64 `json:"odd_ids"` -} - -func (q *Queries) BulkUpdateBetOutcomeStatusByOddIDs(ctx context.Context, arg BulkUpdateBetOutcomeStatusByOddIDsParams) error { - _, err := q.db.Exec(ctx, BulkUpdateBetOutcomeStatusByOddIDs, arg.Status, arg.OddIds) - return err -} - -const CreateBet = `-- name: CreateBet :one -INSERT INTO bets ( - amount, - total_odds, - status, - user_id, - is_shop_bet, - outcomes_hash, - fast_code, - company_id - ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8) -RETURNING id, company_id, amount, total_odds, potential_win, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at -` - -type CreateBetParams struct { - Amount int64 `json:"amount"` - TotalOdds float32 `json:"total_odds"` - Status int32 `json:"status"` - UserID int64 `json:"user_id"` - IsShopBet bool `json:"is_shop_bet"` - OutcomesHash string `json:"outcomes_hash"` - FastCode string `json:"fast_code"` - CompanyID int64 `json:"company_id"` -} - -func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, error) { - row := q.db.QueryRow(ctx, CreateBet, - arg.Amount, - arg.TotalOdds, - arg.Status, - arg.UserID, - arg.IsShopBet, - arg.OutcomesHash, - arg.FastCode, - arg.CompanyID, - ) - var i Bet - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.Amount, - &i.TotalOdds, - &i.PotentialWin, - &i.Status, - &i.UserID, - &i.IsShopBet, - &i.CashedOut, - &i.OutcomesHash, - &i.FastCode, - &i.Processed, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -type CreateBetOutcomeParams struct { - BetID int64 `json:"bet_id"` - SportID int64 `json:"sport_id"` - EventID int64 `json:"event_id"` - OddID int64 `json:"odd_id"` - HomeTeamName string `json:"home_team_name"` - AwayTeamName string `json:"away_team_name"` - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - Odd float32 `json:"odd"` - OddName string `json:"odd_name"` - OddHeader string `json:"odd_header"` - OddHandicap string `json:"odd_handicap"` - Expires pgtype.Timestamp `json:"expires"` -} - -const DeleteBet = `-- name: DeleteBet :exec -DELETE FROM bets -WHERE id = $1 -` - -func (q *Queries) DeleteBet(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteBet, id) - return err -} - -const DeleteBetOutcome = `-- name: DeleteBetOutcome :exec -DELETE FROM bet_outcomes -WHERE bet_id = $1 -` - -func (q *Queries) DeleteBetOutcome(ctx context.Context, betID int64) error { - _, err := q.db.Exec(ctx, DeleteBetOutcome, betID) - return err -} - -const GetAllBets = `-- name: GetAllBets :many -SELECT id, company_id, amount, total_odds, potential_win, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug -FROM bet_with_outcomes -wHERE ( - user_id = $1 - OR $1 IS NULL - ) - AND ( - is_shop_bet = $2 - OR $2 IS NULL - ) - AND ( - company_id = $3 - OR $3 IS NULL - ) - AND ( - status = $4 - OR $4 IS NULL - ) - AND ( - cashed_out = $5 - OR $5 IS NULL - ) - AND ( - full_name ILIKE '%' || $6 || '%' - OR phone_number ILIKE '%' || $6 || '%' - OR $6 IS NULL - ) - AND ( - created_at > $7 - OR $7 IS NULL - ) - AND ( - created_at < $8 - OR $8 IS NULL - ) -LIMIT $10 OFFSET $9 -` - -type GetAllBetsParams struct { - UserID pgtype.Int8 `json:"user_id"` - IsShopBet pgtype.Bool `json:"is_shop_bet"` - CompanyID pgtype.Int8 `json:"company_id"` - Status pgtype.Int4 `json:"status"` - CashedOut pgtype.Bool `json:"cashed_out"` - Query pgtype.Text `json:"query"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWithOutcome, error) { - rows, err := q.db.Query(ctx, GetAllBets, - arg.UserID, - arg.IsShopBet, - arg.CompanyID, - arg.Status, - arg.CashedOut, - arg.Query, - arg.CreatedBefore, - arg.CreatedAfter, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BetWithOutcome - for rows.Next() { - var i BetWithOutcome - if err := rows.Scan( - &i.ID, - &i.CompanyID, - &i.Amount, - &i.TotalOdds, - &i.PotentialWin, - &i.Status, - &i.UserID, - &i.IsShopBet, - &i.CashedOut, - &i.OutcomesHash, - &i.FastCode, - &i.Processed, - &i.CreatedAt, - &i.UpdatedAt, - &i.FullName, - &i.PhoneNumber, - &i.Outcomes, - &i.CompanySlug, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBetByFastCode = `-- name: GetBetByFastCode :one -SELECT id, company_id, amount, total_odds, potential_win, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug -FROM bet_with_outcomes -WHERE fast_code = $1 -LIMIT 1 -` - -func (q *Queries) GetBetByFastCode(ctx context.Context, fastCode string) (BetWithOutcome, error) { - row := q.db.QueryRow(ctx, GetBetByFastCode, fastCode) - var i BetWithOutcome - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.Amount, - &i.TotalOdds, - &i.PotentialWin, - &i.Status, - &i.UserID, - &i.IsShopBet, - &i.CashedOut, - &i.OutcomesHash, - &i.FastCode, - &i.Processed, - &i.CreatedAt, - &i.UpdatedAt, - &i.FullName, - &i.PhoneNumber, - &i.Outcomes, - &i.CompanySlug, - ) - return i, err -} - -const GetBetByID = `-- name: GetBetByID :one -SELECT id, company_id, amount, total_odds, potential_win, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug -FROM bet_with_outcomes -WHERE id = $1 -` - -func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, error) { - row := q.db.QueryRow(ctx, GetBetByID, id) - var i BetWithOutcome - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.Amount, - &i.TotalOdds, - &i.PotentialWin, - &i.Status, - &i.UserID, - &i.IsShopBet, - &i.CashedOut, - &i.OutcomesHash, - &i.FastCode, - &i.Processed, - &i.CreatedAt, - &i.UpdatedAt, - &i.FullName, - &i.PhoneNumber, - &i.Outcomes, - &i.CompanySlug, - ) - return i, err -} - -const GetBetByUserID = `-- name: GetBetByUserID :many -SELECT id, company_id, amount, total_odds, potential_win, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug -FROM bet_with_outcomes -WHERE user_id = $1 -` - -func (q *Queries) GetBetByUserID(ctx context.Context, userID int64) ([]BetWithOutcome, error) { - rows, err := q.db.Query(ctx, GetBetByUserID, userID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BetWithOutcome - for rows.Next() { - var i BetWithOutcome - if err := rows.Scan( - &i.ID, - &i.CompanyID, - &i.Amount, - &i.TotalOdds, - &i.PotentialWin, - &i.Status, - &i.UserID, - &i.IsShopBet, - &i.CashedOut, - &i.OutcomesHash, - &i.FastCode, - &i.Processed, - &i.CreatedAt, - &i.UpdatedAt, - &i.FullName, - &i.PhoneNumber, - &i.Outcomes, - &i.CompanySlug, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBetCountByOutcomesHash = `-- name: GetBetCountByOutcomesHash :one -SELECT COUNT(*) -FROM bets -WHERE outcomes_hash = $1 -` - -func (q *Queries) GetBetCountByOutcomesHash(ctx context.Context, outcomesHash string) (int64, error) { - row := q.db.QueryRow(ctx, GetBetCountByOutcomesHash, outcomesHash) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetBetCountByUserID = `-- name: GetBetCountByUserID :one -SELECT COUNT(*) -FROM bets -WHERE user_id = $1 - AND outcomes_hash = $2 -` - -type GetBetCountByUserIDParams struct { - UserID int64 `json:"user_id"` - OutcomesHash string `json:"outcomes_hash"` -} - -func (q *Queries) GetBetCountByUserID(ctx context.Context, arg GetBetCountByUserIDParams) (int64, error) { - row := q.db.QueryRow(ctx, GetBetCountByUserID, arg.UserID, arg.OutcomesHash) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetBetOutcomeByBetID = `-- name: GetBetOutcomeByBetID :many -SELECT id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires -FROM bet_outcomes -WHERE bet_id = $1 -` - -func (q *Queries) GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]BetOutcome, error) { - rows, err := q.db.Query(ctx, GetBetOutcomeByBetID, betID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BetOutcome - for rows.Next() { - var i BetOutcome - if err := rows.Scan( - &i.ID, - &i.BetID, - &i.SportID, - &i.EventID, - &i.OddID, - &i.HomeTeamName, - &i.AwayTeamName, - &i.MarketID, - &i.MarketName, - &i.Odd, - &i.OddName, - &i.OddHeader, - &i.OddHandicap, - &i.Status, - &i.Expires, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBetOutcomeByEventID = `-- name: GetBetOutcomeByEventID :many -SELECT id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires -FROM bet_outcomes -WHERE (event_id = $1) - AND ( - status = $2 - OR $2 IS NULL - OR status = $3 - OR $3 IS NULL - ) -` - -type GetBetOutcomeByEventIDParams struct { - EventID int64 `json:"event_id"` - FilterStatus pgtype.Int4 `json:"filter_status"` - FilterStatus2 pgtype.Int4 `json:"filter_status_2"` -} - -func (q *Queries) GetBetOutcomeByEventID(ctx context.Context, arg GetBetOutcomeByEventIDParams) ([]BetOutcome, error) { - rows, err := q.db.Query(ctx, GetBetOutcomeByEventID, arg.EventID, arg.FilterStatus, arg.FilterStatus2) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BetOutcome - for rows.Next() { - var i BetOutcome - if err := rows.Scan( - &i.ID, - &i.BetID, - &i.SportID, - &i.EventID, - &i.OddID, - &i.HomeTeamName, - &i.AwayTeamName, - &i.MarketID, - &i.MarketName, - &i.Odd, - &i.OddName, - &i.OddHeader, - &i.OddHandicap, - &i.Status, - &i.Expires, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBetOutcomeCountByOddID = `-- name: GetBetOutcomeCountByOddID :one -SELECT COUNT(*) -FROM bet_outcomes -WHERE odd_id = $1 -` - -func (q *Queries) GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (int64, error) { - row := q.db.QueryRow(ctx, GetBetOutcomeCountByOddID, oddID) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetBetOutcomeViewByEventID = `-- name: GetBetOutcomeViewByEventID :many -SELECT bet_outcomes.id, bet_outcomes.bet_id, bet_outcomes.sport_id, bet_outcomes.event_id, bet_outcomes.odd_id, bet_outcomes.home_team_name, bet_outcomes.away_team_name, bet_outcomes.market_id, bet_outcomes.market_name, bet_outcomes.odd, bet_outcomes.odd_name, bet_outcomes.odd_header, bet_outcomes.odd_handicap, bet_outcomes.status, bet_outcomes.expires, - users.first_name, - users.last_name, - bets.amount, - bets.total_odds, - companies.name as company_name -FROM bet_outcomes - JOIN bets ON bets.id = bet_outcomes.bet_id - JOIN users ON bets.user_id = users.id - JOIN companies ON bets.company_id = companies.id -WHERE bet_outcomes.event_id = $1 - AND ( - bets.company_id = $2 - OR $2 IS NULL - ) - AND ( - bet_outcomes.status = $3 - OR $3 IS NULL - ) -LIMIT $5 OFFSET $4 -` - -type GetBetOutcomeViewByEventIDParams struct { - EventID int64 `json:"event_id"` - CompanyID pgtype.Int8 `json:"company_id"` - FilterStatus pgtype.Int4 `json:"filter_status"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -type GetBetOutcomeViewByEventIDRow struct { - ID int64 `json:"id"` - BetID int64 `json:"bet_id"` - SportID int64 `json:"sport_id"` - EventID int64 `json:"event_id"` - OddID int64 `json:"odd_id"` - HomeTeamName string `json:"home_team_name"` - AwayTeamName string `json:"away_team_name"` - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - Odd float32 `json:"odd"` - OddName string `json:"odd_name"` - OddHeader string `json:"odd_header"` - OddHandicap string `json:"odd_handicap"` - Status int32 `json:"status"` - Expires pgtype.Timestamp `json:"expires"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Amount int64 `json:"amount"` - TotalOdds float32 `json:"total_odds"` - CompanyName string `json:"company_name"` -} - -func (q *Queries) GetBetOutcomeViewByEventID(ctx context.Context, arg GetBetOutcomeViewByEventIDParams) ([]GetBetOutcomeViewByEventIDRow, error) { - rows, err := q.db.Query(ctx, GetBetOutcomeViewByEventID, - arg.EventID, - arg.CompanyID, - arg.FilterStatus, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetBetOutcomeViewByEventIDRow - for rows.Next() { - var i GetBetOutcomeViewByEventIDRow - if err := rows.Scan( - &i.ID, - &i.BetID, - &i.SportID, - &i.EventID, - &i.OddID, - &i.HomeTeamName, - &i.AwayTeamName, - &i.MarketID, - &i.MarketName, - &i.Odd, - &i.OddName, - &i.OddHeader, - &i.OddHandicap, - &i.Status, - &i.Expires, - &i.FirstName, - &i.LastName, - &i.Amount, - &i.TotalOdds, - &i.CompanyName, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBetsForCashback = `-- name: GetBetsForCashback :many -SELECT id, company_id, amount, total_odds, potential_win, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at, full_name, phone_number, outcomes, company_slug -FROM bet_with_outcomes -WHERE status = 2 - AND processed = false -` - -func (q *Queries) GetBetsForCashback(ctx context.Context) ([]BetWithOutcome, error) { - rows, err := q.db.Query(ctx, GetBetsForCashback) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BetWithOutcome - for rows.Next() { - var i BetWithOutcome - if err := rows.Scan( - &i.ID, - &i.CompanyID, - &i.Amount, - &i.TotalOdds, - &i.PotentialWin, - &i.Status, - &i.UserID, - &i.IsShopBet, - &i.CashedOut, - &i.OutcomesHash, - &i.FastCode, - &i.Processed, - &i.CreatedAt, - &i.UpdatedAt, - &i.FullName, - &i.PhoneNumber, - &i.Outcomes, - &i.CompanySlug, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetTotalBets = `-- name: GetTotalBets :one -SELECT COUNT(*) -FROM bets -wHERE ( - user_id = $1 - OR $1 IS NULL - ) - AND ( - is_shop_bet = $2 - OR $2 IS NULL - ) - AND ( - company_id = $3 - OR $3 IS NULL - ) - AND ( - status = $4 - OR $4 IS NULL - ) - AND ( - cashed_out = $5 - OR $5 IS NULL - ) - AND ( - full_name ILIKE '%' || $6 || '%' - OR phone_number ILIKE '%' || $6 || '%' - OR $6 IS NULL - ) - AND ( - created_at > $7 - OR $7 IS NULL - ) - AND ( - created_at < $8 - OR $8 IS NULL - ) -` - -type GetTotalBetsParams struct { - UserID pgtype.Int8 `json:"user_id"` - IsShopBet pgtype.Bool `json:"is_shop_bet"` - CompanyID pgtype.Int8 `json:"company_id"` - Status pgtype.Int4 `json:"status"` - CashedOut pgtype.Bool `json:"cashed_out"` - Query pgtype.Text `json:"query"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetTotalBets(ctx context.Context, arg GetTotalBetsParams) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalBets, - arg.UserID, - arg.IsShopBet, - arg.CompanyID, - arg.Status, - arg.CashedOut, - arg.Query, - arg.CreatedBefore, - arg.CreatedAfter, - ) - var count int64 - err := row.Scan(&count) - return count, err -} - -const TotalBetOutcomeViewByEventID = `-- name: TotalBetOutcomeViewByEventID :one -SELECT count(*) -FROM bet_outcomes - JOIN bets ON bets.id = bet_outcomes.bet_id -WHERE bet_outcomes.event_id = $1 - AND ( - bets.company_id = $2 - OR $2 IS NULL - ) - AND ( - bet_outcomes.status = $3 - OR $3 IS NULL - ) -` - -type TotalBetOutcomeViewByEventIDParams struct { - EventID int64 `json:"event_id"` - CompanyID pgtype.Int8 `json:"company_id"` - FilterStatus pgtype.Int4 `json:"filter_status"` -} - -func (q *Queries) TotalBetOutcomeViewByEventID(ctx context.Context, arg TotalBetOutcomeViewByEventIDParams) (int64, error) { - row := q.db.QueryRow(ctx, TotalBetOutcomeViewByEventID, arg.EventID, arg.CompanyID, arg.FilterStatus) - var count int64 - err := row.Scan(&count) - return count, err -} - -const UpdateBetOutcomeStatus = `-- name: UpdateBetOutcomeStatus :one -UPDATE bet_outcomes -SET status = $1 -WHERE id = $2 -RETURNING id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires -` - -type UpdateBetOutcomeStatusParams struct { - Status int32 `json:"status"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateBetOutcomeStatus(ctx context.Context, arg UpdateBetOutcomeStatusParams) (BetOutcome, error) { - row := q.db.QueryRow(ctx, UpdateBetOutcomeStatus, arg.Status, arg.ID) - var i BetOutcome - err := row.Scan( - &i.ID, - &i.BetID, - &i.SportID, - &i.EventID, - &i.OddID, - &i.HomeTeamName, - &i.AwayTeamName, - &i.MarketID, - &i.MarketName, - &i.Odd, - &i.OddName, - &i.OddHeader, - &i.OddHandicap, - &i.Status, - &i.Expires, - ) - return i, err -} - -const UpdateBetOutcomeStatusByBetID = `-- name: UpdateBetOutcomeStatusByBetID :one -UPDATE bet_outcomes -SET status = $1 -WHERE bet_id = $2 -RETURNING id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires -` - -type UpdateBetOutcomeStatusByBetIDParams struct { - Status int32 `json:"status"` - BetID int64 `json:"bet_id"` -} - -func (q *Queries) UpdateBetOutcomeStatusByBetID(ctx context.Context, arg UpdateBetOutcomeStatusByBetIDParams) (BetOutcome, error) { - row := q.db.QueryRow(ctx, UpdateBetOutcomeStatusByBetID, arg.Status, arg.BetID) - var i BetOutcome - err := row.Scan( - &i.ID, - &i.BetID, - &i.SportID, - &i.EventID, - &i.OddID, - &i.HomeTeamName, - &i.AwayTeamName, - &i.MarketID, - &i.MarketName, - &i.Odd, - &i.OddName, - &i.OddHeader, - &i.OddHandicap, - &i.Status, - &i.Expires, - ) - return i, err -} - -const UpdateBetOutcomeStatusForEvent = `-- name: UpdateBetOutcomeStatusForEvent :many -UPDATE bet_outcomes -SEt status = $1 -WHERE event_id = $2 -RETURNING id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires -` - -type UpdateBetOutcomeStatusForEventParams struct { - Status int32 `json:"status"` - EventID int64 `json:"event_id"` -} - -func (q *Queries) UpdateBetOutcomeStatusForEvent(ctx context.Context, arg UpdateBetOutcomeStatusForEventParams) ([]BetOutcome, error) { - rows, err := q.db.Query(ctx, UpdateBetOutcomeStatusForEvent, arg.Status, arg.EventID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BetOutcome - for rows.Next() { - var i BetOutcome - if err := rows.Scan( - &i.ID, - &i.BetID, - &i.SportID, - &i.EventID, - &i.OddID, - &i.HomeTeamName, - &i.AwayTeamName, - &i.MarketID, - &i.MarketName, - &i.Odd, - &i.OddName, - &i.OddHeader, - &i.OddHandicap, - &i.Status, - &i.Expires, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateBetOutcomeStatusForOddID = `-- name: UpdateBetOutcomeStatusForOddID :many -UPDATE bet_outcomes -SEt status = $1 -WHERE odd_id = $2 -RETURNING id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires -` - -type UpdateBetOutcomeStatusForOddIDParams struct { - Status int32 `json:"status"` - OddID int64 `json:"odd_id"` -} - -func (q *Queries) UpdateBetOutcomeStatusForOddID(ctx context.Context, arg UpdateBetOutcomeStatusForOddIDParams) ([]BetOutcome, error) { - rows, err := q.db.Query(ctx, UpdateBetOutcomeStatusForOddID, arg.Status, arg.OddID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BetOutcome - for rows.Next() { - var i BetOutcome - if err := rows.Scan( - &i.ID, - &i.BetID, - &i.SportID, - &i.EventID, - &i.OddID, - &i.HomeTeamName, - &i.AwayTeamName, - &i.MarketID, - &i.MarketName, - &i.Odd, - &i.OddName, - &i.OddHeader, - &i.OddHandicap, - &i.Status, - &i.Expires, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateBetWithCashback = `-- name: UpdateBetWithCashback :exec -UPDATE bets -SET processed = $1 -WHERE id = $2 -` - -type UpdateBetWithCashbackParams struct { - Processed bool `json:"processed"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateBetWithCashback(ctx context.Context, arg UpdateBetWithCashbackParams) error { - _, err := q.db.Exec(ctx, UpdateBetWithCashback, arg.Processed, arg.ID) - return err -} - -const UpdateCashOut = `-- name: UpdateCashOut :exec -UPDATE bets -SET cashed_out = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateCashOutParams struct { - ID int64 `json:"id"` - CashedOut bool `json:"cashed_out"` -} - -func (q *Queries) UpdateCashOut(ctx context.Context, arg UpdateCashOutParams) error { - _, err := q.db.Exec(ctx, UpdateCashOut, arg.ID, arg.CashedOut) - return err -} - -const UpdateStatus = `-- name: UpdateStatus :exec -UPDATE bets -SET status = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2 -` - -type UpdateStatusParams struct { - Status int32 `json:"status"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateStatus(ctx context.Context, arg UpdateStatusParams) error { - _, err := q.db.Exec(ctx, UpdateStatus, arg.Status, arg.ID) - return err -} diff --git a/gen/db/bet_stat.sql.go b/gen/db/bet_stat.sql.go deleted file mode 100644 index 95279fa..0000000 --- a/gen/db/bet_stat.sql.go +++ /dev/null @@ -1,429 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: bet_stat.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetBetStats = `-- name: GetBetStats :many -SELECT DATE(created_at) as date, - COUNT(*) as total_bets, - SUM(amount) as total_stakes, - SUM( - CASE - WHEN status = 1 THEN 1 - ELSE 0 - END - ) as total_wins, - SUM( - CASE - WHEN status = 1 THEN amount * total_odds - ELSE 0 - END - ) as total_payouts, - AVG(total_odds) as average_odds -FROM bets -wHERE ( - user_id = $1 - OR $1 IS NULL - ) - AND ( - company_id = $2 - OR $2 IS NULL - ) - AND ( - is_shop_bet = $3 - OR $3 IS NULL - ) - AND ( - cashed_out = $4 - OR $4 IS NULL - ) - AND ( - full_name ILIKE '%' || $5 || '%' - OR phone_number ILIKE '%' || $5 || '%' - OR $5 IS NULL - ) - AND ( - created_at > $6 - OR $6 IS NULL - ) - AND ( - created_at < $7 - OR $7 IS NULL - ) -GROUP BY DATE(created_at) -ORDER BY DATE(created_at) -` - -type GetBetStatsParams struct { - UserID pgtype.Int8 `json:"user_id"` - CompanyID pgtype.Int8 `json:"company_id"` - IsShopBet pgtype.Bool `json:"is_shop_bet"` - CashedOut pgtype.Bool `json:"cashed_out"` - Query pgtype.Text `json:"query"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -type GetBetStatsRow struct { - Date pgtype.Date `json:"date"` - TotalBets int64 `json:"total_bets"` - TotalStakes int64 `json:"total_stakes"` - TotalWins int64 `json:"total_wins"` - TotalPayouts int64 `json:"total_payouts"` - AverageOdds float64 `json:"average_odds"` -} - -func (q *Queries) GetBetStats(ctx context.Context, arg GetBetStatsParams) ([]GetBetStatsRow, error) { - rows, err := q.db.Query(ctx, GetBetStats, - arg.UserID, - arg.CompanyID, - arg.IsShopBet, - arg.CashedOut, - arg.Query, - arg.CreatedBefore, - arg.CreatedAfter, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetBetStatsRow - for rows.Next() { - var i GetBetStatsRow - if err := rows.Scan( - &i.Date, - &i.TotalBets, - &i.TotalStakes, - &i.TotalWins, - &i.TotalPayouts, - &i.AverageOdds, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBetStatsByInterval = `-- name: GetBetStatsByInterval :many -SELECT DATE_TRUNC($1, created_at)::timestamp AS date, - COUNT(*) as total_bets, - SUM(amount) AS total_stake, - SUM( - CASE - WHEN status = 0 THEN 1 - ELSE 0 - END - ) as active_bets, - SUM( - CASE - WHEN status = 1 THEN 1 - ELSE 0 - END - ) as total_wins, - SUM( - CASE - WHEN status = 2 THEN 1 - ELSE 0 - END - ) as total_losses, - SUM( - CASE - WHEN status = 1 THEN amount * total_odds - ELSE 0 - END - ) as win_balance, - COUNT(*) FILTER ( - WHERE status = 5 - ) AS number_of_unsettled, - SUM( - CASE - WHEN status = 5 THEN amount - ELSE 0 - END - ) AS total_unsettled_amount, - COUNT(*) FILTER ( - WHERE is_shop_bet = TRUE - ) AS total_shop_bets -FROM bets -WHERE ( - bets.company_id = $2 - OR $2 IS NULL - ) -` - -type GetBetStatsByIntervalParams struct { - Interval pgtype.Text `json:"interval"` - CompanyID pgtype.Int8 `json:"company_id"` -} - -type GetBetStatsByIntervalRow struct { - Date pgtype.Timestamp `json:"date"` - TotalBets int64 `json:"total_bets"` - TotalStake int64 `json:"total_stake"` - ActiveBets int64 `json:"active_bets"` - TotalWins int64 `json:"total_wins"` - TotalLosses int64 `json:"total_losses"` - WinBalance int64 `json:"win_balance"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount int64 `json:"total_unsettled_amount"` - TotalShopBets int64 `json:"total_shop_bets"` -} - -func (q *Queries) GetBetStatsByInterval(ctx context.Context, arg GetBetStatsByIntervalParams) ([]GetBetStatsByIntervalRow, error) { - rows, err := q.db.Query(ctx, GetBetStatsByInterval, arg.Interval, arg.CompanyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetBetStatsByIntervalRow - for rows.Next() { - var i GetBetStatsByIntervalRow - if err := rows.Scan( - &i.Date, - &i.TotalBets, - &i.TotalStake, - &i.ActiveBets, - &i.TotalWins, - &i.TotalLosses, - &i.WinBalance, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalShopBets, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBetSummary = `-- name: GetBetSummary :one -SELECT SUM(amount) as total_stakes, - COUNT(*) as total_bets, - SUM( - CASE - WHEN status = 0 THEN 1 - ELSE 0 - END - ) as active_bets, - SUM( - CASE - WHEN status = 1 THEN 1 - ELSE 0 - END - ) as total_wins, - SUM( - CASE - WHEN status = 2 THEN 1 - ELSE 0 - END - ) as total_losses, - SUM( - CASE - WHEN status = 1 THEN amount * total_odds - ELSE 0 - END - ) as win_balance -FROM bets -wHERE ( - user_id = $1 - OR $1 IS NULL - ) - AND ( - company_id = $2 - OR $2 IS NULL - ) - AND ( - created_at > $3 - OR $3 IS NULL - ) - AND ( - created_at < $4 - OR $4 IS NULL - ) -` - -type GetBetSummaryParams struct { - UserID pgtype.Int8 `json:"user_id"` - CompanyID pgtype.Int8 `json:"company_id"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -type GetBetSummaryRow struct { - TotalStakes int64 `json:"total_stakes"` - TotalBets int64 `json:"total_bets"` - ActiveBets int64 `json:"active_bets"` - TotalWins int64 `json:"total_wins"` - TotalLosses int64 `json:"total_losses"` - WinBalance int64 `json:"win_balance"` -} - -func (q *Queries) GetBetSummary(ctx context.Context, arg GetBetSummaryParams) (GetBetSummaryRow, error) { - row := q.db.QueryRow(ctx, GetBetSummary, - arg.UserID, - arg.CompanyID, - arg.CreatedBefore, - arg.CreatedAfter, - ) - var i GetBetSummaryRow - err := row.Scan( - &i.TotalStakes, - &i.TotalBets, - &i.ActiveBets, - &i.TotalWins, - &i.TotalLosses, - &i.WinBalance, - ) - return i, err -} - -const GetMarketPopularity = `-- name: GetMarketPopularity :one -WITH market_counts AS ( - SELECT DATE(b.created_at) as date, - bo.market_name, - COUNT(*) as bet_count, - ROW_NUMBER() OVER ( - PARTITION BY DATE(b.created_at) - ORDER BY COUNT(*) DESC - ) as rank - FROM bets b - JOIN bet_outcomes bo ON b.id = bo.bet_id - WHERE bo.market_name IS NOT NULL - AND ( - user_id = $1 - OR $1 IS NULL - ) - AND ( - company_id = $2 - OR $2 IS NULL - ) - AND ( - created_at > $3 - OR $3 IS NULL - ) - AND ( - created_at < $4 - OR $4 IS NULL - ) - GROUP BY DATE(b.created_at), - bo.market_name -) -SELECT date, - market_name -FROM market_counts -WHERE rank = 1 -` - -type GetMarketPopularityParams struct { - UserID pgtype.Int8 `json:"user_id"` - CompanyID pgtype.Int8 `json:"company_id"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -type GetMarketPopularityRow struct { - Date pgtype.Date `json:"date"` - MarketName string `json:"market_name"` -} - -func (q *Queries) GetMarketPopularity(ctx context.Context, arg GetMarketPopularityParams) (GetMarketPopularityRow, error) { - row := q.db.QueryRow(ctx, GetMarketPopularity, - arg.UserID, - arg.CompanyID, - arg.CreatedBefore, - arg.CreatedAfter, - ) - var i GetMarketPopularityRow - err := row.Scan(&i.Date, &i.MarketName) - return i, err -} - -const GetTotalBetsMadeInRange = `-- name: GetTotalBetsMadeInRange :one -SELECT COUNT(*) AS total_bets -FROM bets -WHERE created_at BETWEEN $1 AND $2 -` - -type GetTotalBetsMadeInRangeParams struct { - From pgtype.Timestamp `json:"from"` - To pgtype.Timestamp `json:"to"` -} - -func (q *Queries) GetTotalBetsMadeInRange(ctx context.Context, arg GetTotalBetsMadeInRangeParams) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalBetsMadeInRange, arg.From, arg.To) - var total_bets int64 - err := row.Scan(&total_bets) - return total_bets, err -} - -const GetTotalCashBacksInRange = `-- name: GetTotalCashBacksInRange :one -SELECT COALESCE(SUM(amount), 0) AS total_cash_backs -FROM bets -WHERE created_at BETWEEN $1 AND $2 - AND status = 5 -` - -type GetTotalCashBacksInRangeParams struct { - From pgtype.Timestamp `json:"from"` - To pgtype.Timestamp `json:"to"` -} - -func (q *Queries) GetTotalCashBacksInRange(ctx context.Context, arg GetTotalCashBacksInRangeParams) (interface{}, error) { - row := q.db.QueryRow(ctx, GetTotalCashBacksInRange, arg.From, arg.To) - var total_cash_backs interface{} - err := row.Scan(&total_cash_backs) - return total_cash_backs, err -} - -const GetTotalCashMadeInRange = `-- name: GetTotalCashMadeInRange :one -SELECT COALESCE(SUM(amount), 0) AS total_cash_made -FROM bets -WHERE created_at BETWEEN $1 AND $2 -` - -type GetTotalCashMadeInRangeParams struct { - From pgtype.Timestamp `json:"from"` - To pgtype.Timestamp `json:"to"` -} - -func (q *Queries) GetTotalCashMadeInRange(ctx context.Context, arg GetTotalCashMadeInRangeParams) (interface{}, error) { - row := q.db.QueryRow(ctx, GetTotalCashMadeInRange, arg.From, arg.To) - var total_cash_made interface{} - err := row.Scan(&total_cash_made) - return total_cash_made, err -} - -const GetTotalCashOutInRange = `-- name: GetTotalCashOutInRange :one -SELECT COALESCE(SUM(amount), 0) AS total_cash_out -FROM bets -WHERE created_at BETWEEN $1 AND $2 - AND cashed_out = true -` - -type GetTotalCashOutInRangeParams struct { - From pgtype.Timestamp `json:"from"` - To pgtype.Timestamp `json:"to"` -} - -func (q *Queries) GetTotalCashOutInRange(ctx context.Context, arg GetTotalCashOutInRangeParams) (interface{}, error) { - row := q.db.QueryRow(ctx, GetTotalCashOutInRange, arg.From, arg.To) - var total_cash_out interface{} - err := row.Scan(&total_cash_out) - return total_cash_out, err -} diff --git a/gen/db/bonus.sql.go b/gen/db/bonus.sql.go deleted file mode 100644 index 7c6f168..0000000 --- a/gen/db/bonus.sql.go +++ /dev/null @@ -1,224 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: bonus.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateUserBonus = `-- name: CreateUserBonus :one -INSERT INTO user_bonuses ( - name, - description, - type, - user_id, - reward_amount, - expires_at - ) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING id, name, description, type, user_id, reward_amount, is_claimed, expires_at, claimed_at, created_at, updated_at -` - -type CreateUserBonusParams struct { - Name string `json:"name"` - Description string `json:"description"` - Type string `json:"type"` - UserID int64 `json:"user_id"` - RewardAmount int64 `json:"reward_amount"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` -} - -func (q *Queries) CreateUserBonus(ctx context.Context, arg CreateUserBonusParams) (UserBonuse, error) { - row := q.db.QueryRow(ctx, CreateUserBonus, - arg.Name, - arg.Description, - arg.Type, - arg.UserID, - arg.RewardAmount, - arg.ExpiresAt, - ) - var i UserBonuse - err := row.Scan( - &i.ID, - &i.Name, - &i.Description, - &i.Type, - &i.UserID, - &i.RewardAmount, - &i.IsClaimed, - &i.ExpiresAt, - &i.ClaimedAt, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const DeleteUserBonus = `-- name: DeleteUserBonus :exec -DELETE FROM user_bonuses -WHERE id = $1 -` - -func (q *Queries) DeleteUserBonus(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteUserBonus, id) - return err -} - -const GetAllUserBonuses = `-- name: GetAllUserBonuses :many -SELECT id, name, description, type, user_id, reward_amount, is_claimed, expires_at, claimed_at, created_at, updated_at -FROM user_bonuses -WHERE ( - user_id = $1 - OR $1 IS NULL - ) -LIMIT $3 OFFSET $2 -` - -type GetAllUserBonusesParams struct { - UserID pgtype.Int8 `json:"user_id"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetAllUserBonuses(ctx context.Context, arg GetAllUserBonusesParams) ([]UserBonuse, error) { - rows, err := q.db.Query(ctx, GetAllUserBonuses, arg.UserID, arg.Offset, arg.Limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []UserBonuse - for rows.Next() { - var i UserBonuse - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Description, - &i.Type, - &i.UserID, - &i.RewardAmount, - &i.IsClaimed, - &i.ExpiresAt, - &i.ClaimedAt, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBonusCount = `-- name: GetBonusCount :one -SELECT COUNT(*) -FROM user_bonuses -WHERE ( - user_id = $1 - OR $1 IS NULL - ) -` - -func (q *Queries) GetBonusCount(ctx context.Context, userID pgtype.Int8) (int64, error) { - row := q.db.QueryRow(ctx, GetBonusCount, userID) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetBonusStats = `-- name: GetBonusStats :one -SELECT COUNT(*) AS total_bonuses, - COALESCE(SUM(reward_amount), 0)::bigint AS total_reward_earned, - COUNT( - CASE - WHEN is_claimed = true THEN 1 - END - ) AS claimed_bonuses, - COUNT( - CASE - WHEN expires_at < now() THEN 1 - END - ) AS expired_bonuses -FROM user_bonuses - JOIN users ON users.id = user_bonuses.user_id -WHERE ( - company_id = $1 - OR $1 IS NULL - ) - AND ( - user_id = $2 - OR $2 IS NULL - ) -` - -type GetBonusStatsParams struct { - CompanyID pgtype.Int8 `json:"company_id"` - UserID pgtype.Int8 `json:"user_id"` -} - -type GetBonusStatsRow struct { - TotalBonuses int64 `json:"total_bonuses"` - TotalRewardEarned int64 `json:"total_reward_earned"` - ClaimedBonuses int64 `json:"claimed_bonuses"` - ExpiredBonuses int64 `json:"expired_bonuses"` -} - -func (q *Queries) GetBonusStats(ctx context.Context, arg GetBonusStatsParams) (GetBonusStatsRow, error) { - row := q.db.QueryRow(ctx, GetBonusStats, arg.CompanyID, arg.UserID) - var i GetBonusStatsRow - err := row.Scan( - &i.TotalBonuses, - &i.TotalRewardEarned, - &i.ClaimedBonuses, - &i.ExpiredBonuses, - ) - return i, err -} - -const GetUserBonusByID = `-- name: GetUserBonusByID :one -SELECT id, name, description, type, user_id, reward_amount, is_claimed, expires_at, claimed_at, created_at, updated_at -FROM user_bonuses -WHERE id = $1 -` - -func (q *Queries) GetUserBonusByID(ctx context.Context, id int64) (UserBonuse, error) { - row := q.db.QueryRow(ctx, GetUserBonusByID, id) - var i UserBonuse - err := row.Scan( - &i.ID, - &i.Name, - &i.Description, - &i.Type, - &i.UserID, - &i.RewardAmount, - &i.IsClaimed, - &i.ExpiresAt, - &i.ClaimedAt, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const UpdateUserBonus = `-- name: UpdateUserBonus :exec -UPDATE user_bonuses -SET is_claimed = $2 -WHERE id = $1 -` - -type UpdateUserBonusParams struct { - ID int64 `json:"id"` - IsClaimed bool `json:"is_claimed"` -} - -func (q *Queries) UpdateUserBonus(ctx context.Context, arg UpdateUserBonusParams) error { - _, err := q.db.Exec(ctx, UpdateUserBonus, arg.ID, arg.IsClaimed) - return err -} diff --git a/gen/db/branch.sql.go b/gen/db/branch.sql.go deleted file mode 100644 index 52372a4..0000000 --- a/gen/db/branch.sql.go +++ /dev/null @@ -1,606 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: branch.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateBranch = `-- name: CreateBranch :one -INSERT INTO branches ( - name, - location, - wallet_id, - branch_manager_id, - company_id, - is_self_owned, - profit_percent - ) -VALUES ($1, $2, $3, $4, $5, $6, $7) -RETURNING id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at -` - -type CreateBranchParams struct { - Name string `json:"name"` - Location string `json:"location"` - WalletID int64 `json:"wallet_id"` - BranchManagerID int64 `json:"branch_manager_id"` - CompanyID int64 `json:"company_id"` - IsSelfOwned bool `json:"is_self_owned"` - ProfitPercent float32 `json:"profit_percent"` -} - -func (q *Queries) CreateBranch(ctx context.Context, arg CreateBranchParams) (Branch, error) { - row := q.db.QueryRow(ctx, CreateBranch, - arg.Name, - arg.Location, - arg.WalletID, - arg.BranchManagerID, - arg.CompanyID, - arg.IsSelfOwned, - arg.ProfitPercent, - ) - var i Branch - err := row.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.ProfitPercent, - &i.IsActive, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateBranchCashier = `-- name: CreateBranchCashier :one -INSERT INTO branch_cashiers (user_id, branch_id) -VALUES ($1, $2) -RETURNING id, user_id, branch_id -` - -type CreateBranchCashierParams struct { - UserID int64 `json:"user_id"` - BranchID int64 `json:"branch_id"` -} - -func (q *Queries) CreateBranchCashier(ctx context.Context, arg CreateBranchCashierParams) (BranchCashier, error) { - row := q.db.QueryRow(ctx, CreateBranchCashier, arg.UserID, arg.BranchID) - var i BranchCashier - err := row.Scan(&i.ID, &i.UserID, &i.BranchID) - return i, err -} - -const CreateBranchOperation = `-- name: CreateBranchOperation :one -INSERT INTO branch_operations (operation_id, branch_id) -VALUES ($1, $2) -RETURNING id, operation_id, branch_id, created_at, updated_at -` - -type CreateBranchOperationParams struct { - OperationID int64 `json:"operation_id"` - BranchID int64 `json:"branch_id"` -} - -func (q *Queries) CreateBranchOperation(ctx context.Context, arg CreateBranchOperationParams) (BranchOperation, error) { - row := q.db.QueryRow(ctx, CreateBranchOperation, arg.OperationID, arg.BranchID) - var i BranchOperation - err := row.Scan( - &i.ID, - &i.OperationID, - &i.BranchID, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateSupportedOperation = `-- name: CreateSupportedOperation :one -INSERT INTO supported_operations (name, description) -VALUES ($1, $2) -RETURNING id, name, description -` - -type CreateSupportedOperationParams struct { - Name string `json:"name"` - Description string `json:"description"` -} - -func (q *Queries) CreateSupportedOperation(ctx context.Context, arg CreateSupportedOperationParams) (SupportedOperation, error) { - row := q.db.QueryRow(ctx, CreateSupportedOperation, arg.Name, arg.Description) - var i SupportedOperation - err := row.Scan(&i.ID, &i.Name, &i.Description) - return i, err -} - -const DeleteBranch = `-- name: DeleteBranch :exec -DELETE FROM branches -WHERE id = $1 -` - -func (q *Queries) DeleteBranch(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteBranch, id) - return err -} - -const DeleteBranchCashier = `-- name: DeleteBranchCashier :exec -DELETE FROM branch_cashiers -WHERE user_id = $1 -` - -func (q *Queries) DeleteBranchCashier(ctx context.Context, userID int64) error { - _, err := q.db.Exec(ctx, DeleteBranchCashier, userID) - return err -} - -const DeleteBranchOperation = `-- name: DeleteBranchOperation :exec -DELETE FROM branch_operations -WHERE operation_id = $1 - AND branch_id = $2 -` - -type DeleteBranchOperationParams struct { - OperationID int64 `json:"operation_id"` - BranchID int64 `json:"branch_id"` -} - -func (q *Queries) DeleteBranchOperation(ctx context.Context, arg DeleteBranchOperationParams) error { - _, err := q.db.Exec(ctx, DeleteBranchOperation, arg.OperationID, arg.BranchID) - return err -} - -const GetAllBranches = `-- name: GetAllBranches :many -SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at -FROM branch_details -WHERE ( - company_id = $1 - OR $1 IS NULL - ) - AND ( - is_active = $2 - OR $2 IS NULL - ) - AND ( - branch_manager_id = $3 - OR $3 IS NULL - ) - AND ( - name ILIKE '%' || $4 || '%' - OR location ILIKE '%' || $4 || '%' - OR $4 IS NULL - ) - AND ( - created_at > $5 - OR $5 IS NULL - ) - AND ( - created_at < $6 - OR $6 IS NULL - ) -` - -type GetAllBranchesParams struct { - CompanyID pgtype.Int8 `json:"company_id"` - IsActive pgtype.Bool `json:"is_active"` - BranchManagerID pgtype.Int8 `json:"branch_manager_id"` - Query pgtype.Text `json:"query"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetAllBranches(ctx context.Context, arg GetAllBranchesParams) ([]BranchDetail, error) { - rows, err := q.db.Query(ctx, GetAllBranches, - arg.CompanyID, - arg.IsActive, - arg.BranchManagerID, - arg.Query, - arg.CreatedBefore, - arg.CreatedAfter, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BranchDetail - for rows.Next() { - var i BranchDetail - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.ProfitPercent, - &i.IsActive, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - &i.CreatedAt, - &i.UpdatedAt, - &i.ManagerName, - &i.ManagerPhoneNumber, - &i.Balance, - &i.WalletIsActive, - &i.CompanyName, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalCashiers, - &i.StatsUpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllSupportedOperations = `-- name: GetAllSupportedOperations :many -SELECT id, name, description -FROM supported_operations -` - -func (q *Queries) GetAllSupportedOperations(ctx context.Context) ([]SupportedOperation, error) { - rows, err := q.db.Query(ctx, GetAllSupportedOperations) - if err != nil { - return nil, err - } - defer rows.Close() - var items []SupportedOperation - for rows.Next() { - var i SupportedOperation - if err := rows.Scan(&i.ID, &i.Name, &i.Description); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBranchByCashier = `-- name: GetBranchByCashier :one -SELECT branches.id, branches.name, branches.location, branches.profit_percent, branches.is_active, branches.wallet_id, branches.branch_manager_id, branches.company_id, branches.is_self_owned, branches.created_at, branches.updated_at -FROM branch_cashiers - JOIN branches ON branch_cashiers.branch_id = branches.id -WHERE branch_cashiers.user_id = $1 -` - -func (q *Queries) GetBranchByCashier(ctx context.Context, userID int64) (Branch, error) { - row := q.db.QueryRow(ctx, GetBranchByCashier, userID) - var i Branch - err := row.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.ProfitPercent, - &i.IsActive, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetBranchByCompanyID = `-- name: GetBranchByCompanyID :many -SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at -FROM branch_details -WHERE company_id = $1 -` - -func (q *Queries) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]BranchDetail, error) { - rows, err := q.db.Query(ctx, GetBranchByCompanyID, companyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BranchDetail - for rows.Next() { - var i BranchDetail - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.ProfitPercent, - &i.IsActive, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - &i.CreatedAt, - &i.UpdatedAt, - &i.ManagerName, - &i.ManagerPhoneNumber, - &i.Balance, - &i.WalletIsActive, - &i.CompanyName, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalCashiers, - &i.StatsUpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBranchByID = `-- name: GetBranchByID :one -SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at -FROM branch_details -WHERE id = $1 -` - -func (q *Queries) GetBranchByID(ctx context.Context, id int64) (BranchDetail, error) { - row := q.db.QueryRow(ctx, GetBranchByID, id) - var i BranchDetail - err := row.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.ProfitPercent, - &i.IsActive, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - &i.CreatedAt, - &i.UpdatedAt, - &i.ManagerName, - &i.ManagerPhoneNumber, - &i.Balance, - &i.WalletIsActive, - &i.CompanyName, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalCashiers, - &i.StatsUpdatedAt, - ) - return i, err -} - -const GetBranchByManagerID = `-- name: GetBranchByManagerID :many -SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at -FROM branch_details -WHERE branch_manager_id = $1 -` - -func (q *Queries) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]BranchDetail, error) { - rows, err := q.db.Query(ctx, GetBranchByManagerID, branchManagerID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BranchDetail - for rows.Next() { - var i BranchDetail - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.ProfitPercent, - &i.IsActive, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - &i.CreatedAt, - &i.UpdatedAt, - &i.ManagerName, - &i.ManagerPhoneNumber, - &i.Balance, - &i.WalletIsActive, - &i.CompanyName, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalCashiers, - &i.StatsUpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBranchOperations = `-- name: GetBranchOperations :many -SELECT branch_operations.id, branch_operations.operation_id, branch_operations.branch_id, branch_operations.created_at, branch_operations.updated_at, - supported_operations.name, - supported_operations.description -FROM branch_operations - JOIN supported_operations ON branch_operations.operation_id = supported_operations.id -WHERE branch_operations.branch_id = $1 -` - -type GetBranchOperationsRow struct { - ID int64 `json:"id"` - OperationID int64 `json:"operation_id"` - BranchID int64 `json:"branch_id"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - Name string `json:"name"` - Description string `json:"description"` -} - -func (q *Queries) GetBranchOperations(ctx context.Context, branchID int64) ([]GetBranchOperationsRow, error) { - rows, err := q.db.Query(ctx, GetBranchOperations, branchID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetBranchOperationsRow - for rows.Next() { - var i GetBranchOperationsRow - if err := rows.Scan( - &i.ID, - &i.OperationID, - &i.BranchID, - &i.CreatedAt, - &i.UpdatedAt, - &i.Name, - &i.Description, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const SearchBranchByName = `-- name: SearchBranchByName :many -SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at -FROM branch_details -WHERE name ILIKE '%' || $1 || '%' - AND ( - company_id = $2 - OR $2 IS NULL - ) -` - -type SearchBranchByNameParams struct { - Column1 pgtype.Text `json:"column_1"` - CompanyID pgtype.Int8 `json:"company_id"` -} - -func (q *Queries) SearchBranchByName(ctx context.Context, arg SearchBranchByNameParams) ([]BranchDetail, error) { - rows, err := q.db.Query(ctx, SearchBranchByName, arg.Column1, arg.CompanyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BranchDetail - for rows.Next() { - var i BranchDetail - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.ProfitPercent, - &i.IsActive, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - &i.CreatedAt, - &i.UpdatedAt, - &i.ManagerName, - &i.ManagerPhoneNumber, - &i.Balance, - &i.WalletIsActive, - &i.CompanyName, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalCashiers, - &i.StatsUpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateBranch = `-- name: UpdateBranch :one -UPDATE branches -SET name = COALESCE($2, name), - location = COALESCE($3, location), - branch_manager_id = COALESCE($4, branch_manager_id), - company_id = COALESCE($5, company_id), - is_self_owned = COALESCE($6, is_self_owned), - is_active = COALESCE($7, is_active), - profit_percent = COALESCE($8, profit_percent), - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -RETURNING id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at -` - -type UpdateBranchParams struct { - ID int64 `json:"id"` - Name pgtype.Text `json:"name"` - Location pgtype.Text `json:"location"` - BranchManagerID pgtype.Int8 `json:"branch_manager_id"` - CompanyID pgtype.Int8 `json:"company_id"` - IsSelfOwned pgtype.Bool `json:"is_self_owned"` - IsActive pgtype.Bool `json:"is_active"` - ProfitPercent pgtype.Float4 `json:"profit_percent"` -} - -func (q *Queries) UpdateBranch(ctx context.Context, arg UpdateBranchParams) (Branch, error) { - row := q.db.QueryRow(ctx, UpdateBranch, - arg.ID, - arg.Name, - arg.Location, - arg.BranchManagerID, - arg.CompanyID, - arg.IsSelfOwned, - arg.IsActive, - arg.ProfitPercent, - ) - var i Branch - err := row.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.ProfitPercent, - &i.IsActive, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} diff --git a/gen/db/branch_stats.sql.go b/gen/db/branch_stats.sql.go deleted file mode 100644 index 781478c..0000000 --- a/gen/db/branch_stats.sql.go +++ /dev/null @@ -1,247 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: branch_stats.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetBranchStats = `-- name: GetBranchStats :many -SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start, - branch_stats.branch_id, - branch_stats.branch_name, - branch_stats.company_id, - branch_stats.company_name, - branch_stats.company_slug, - branch_stats.total_bets, - branch_stats.total_stake, - branch_stats.deducted_stake, - branch_stats.total_cash_out, - branch_stats.total_cash_backs, - branch_stats.number_of_unsettled, - branch_stats.total_unsettled_amount, - branch_stats.total_cashiers, - branch_stats.updated_at -FROM branch_stats -WHERE ( - branch_stats.branch_id = $2 - OR $2 IS NULL - ) - AND ( - branch_stats.company_id = $3 - OR $3 IS NULL - ) -GROUP BY interval_start -ORDER BY interval_start DESC -` - -type GetBranchStatsParams struct { - Interval pgtype.Text `json:"interval"` - BranchID pgtype.Int8 `json:"branch_id"` - CompanyID pgtype.Int8 `json:"company_id"` -} - -type GetBranchStatsRow struct { - IntervalStart pgtype.Timestamp `json:"interval_start"` - BranchID int64 `json:"branch_id"` - BranchName string `json:"branch_name"` - CompanyID int64 `json:"company_id"` - CompanyName string `json:"company_name"` - CompanySlug string `json:"company_slug"` - TotalBets int64 `json:"total_bets"` - TotalStake int64 `json:"total_stake"` - DeductedStake int64 `json:"deducted_stake"` - TotalCashOut int64 `json:"total_cash_out"` - TotalCashBacks int64 `json:"total_cash_backs"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount int64 `json:"total_unsettled_amount"` - TotalCashiers int64 `json:"total_cashiers"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetBranchStats(ctx context.Context, arg GetBranchStatsParams) ([]GetBranchStatsRow, error) { - rows, err := q.db.Query(ctx, GetBranchStats, arg.Interval, arg.BranchID, arg.CompanyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetBranchStatsRow - for rows.Next() { - var i GetBranchStatsRow - if err := rows.Scan( - &i.IntervalStart, - &i.BranchID, - &i.BranchName, - &i.CompanyID, - &i.CompanyName, - &i.CompanySlug, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalCashiers, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBranchStatsByID = `-- name: GetBranchStatsByID :many -SELECt branch_id, branch_name, company_id, company_name, company_slug, interval_start, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, updated_at -FROM branch_stats -WHERE branch_id = $1 -ORDER BY interval_start DESC -` - -func (q *Queries) GetBranchStatsByID(ctx context.Context, branchID int64) ([]BranchStat, error) { - rows, err := q.db.Query(ctx, GetBranchStatsByID, branchID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BranchStat - for rows.Next() { - var i BranchStat - if err := rows.Scan( - &i.BranchID, - &i.BranchName, - &i.CompanyID, - &i.CompanyName, - &i.CompanySlug, - &i.IntervalStart, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalCashiers, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateBranchStats = `-- name: UpdateBranchStats :exec -WITH -- Aggregate bet data per branch -bet_stats AS ( - SELECT branch_id, - COUNT(*) AS total_bets, - COALESCE(SUM(amount), 0) AS total_stake, - COALESCE( - SUM(amount) * MAX(profit_percent), - 0 - ) AS deducted_stake, - COALESCE( - SUM( - CASE - WHEN cashed_out THEN amount - ELSE 0 - END - ), - 0 - ) AS total_cash_out, - COALESCE( - SUM( - CASE - WHEN status = 3 THEN amount - ELSE 0 - END - ), - 0 - ) AS total_cash_backs, - COUNT(*) FILTER ( - WHERE status = 5 - ) AS number_of_unsettled, - COALESCE( - SUM( - CASE - WHEN status = 5 THEN amount - ELSE 0 - END - ), - 0 - ) AS total_unsettled_amount - FROM shop_bet_detail - LEFT JOIN branches ON branches.id = shop_bet_detail.branch_id - GROUP BY branch_id -), -cashier_stats AS ( - SELECT branch_id, - COUNT(*) AS total_cashiers - FROM branch_cashiers - GROUP BY branch_id -) -INSERT INTO branch_stats ( - branch_id, - branch_name, - company_id, - company_name, - company_slug, - interval_start, - total_bets, - total_stake, - deducted_stake, - total_cash_out, - total_cash_backs, - number_of_unsettled, - total_unsettled_amount, - total_cashiers, - updated_at - ) -SELECT br.id AS branch_id, - br.name AS branch_name, - c.id AS company_id, - c.name AS company_name, - c.slug AS company_slug, - DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start, - COALESCE(bs.total_bets, 0) AS total_bets, - COALESCE(bs.total_stake, 0) AS total_stake, - COALESCE(bs.deducted_stake, 0) AS deducted_stake, - COALESCE(bs.total_cash_out, 0) AS total_cash_out, - COALESCE(bs.total_cash_backs, 0) AS total_cash_backs, - COALESCE(bs.number_of_unsettled, 0) AS number_of_unsettled, - COALESCE(bs.total_unsettled_amount, 0) AS total_unsettled_amount, - COALESCE(bc.total_cashiers, 0) AS total_cashiers, - NOW() AS updated_at -FROM branches br - LEFT JOIN companies c ON c.id = br.company_id - LEFT JOIN bet_stats bs ON bs.branch_id = br.id - LEFT JOIN cashier_stats bc ON bc.branch_id = br.id ON CONFLICT (branch_id, interval_start) DO -UPDATE -SET total_bets = EXCLUDED.total_bets, - total_stake = EXCLUDED.total_stake, - deducted_stake = EXCLUDED.deducted_stake, - total_cash_out = EXCLUDED.total_cash_out, - total_cash_backs = EXCLUDED.total_cash_backs, - number_of_unsettled = EXCLUDED.number_of_unsettled, - total_unsettled_amount = EXCLUDED.total_unsettled_amount, - total_cashiers = EXCLUDED.total_cashiers, - updated_at = EXCLUDED.updated_at -` - -func (q *Queries) UpdateBranchStats(ctx context.Context) error { - _, err := q.db.Exec(ctx, UpdateBranchStats) - return err -} diff --git a/gen/db/cashier.sql.go b/gen/db/cashier.sql.go deleted file mode 100644 index fc4a7f8..0000000 --- a/gen/db/cashier.sql.go +++ /dev/null @@ -1,205 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: cashier.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetAllCashiers = `-- name: GetAllCashiers :many -SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, - branch_id, - branches.name AS branch_name, - branches.wallet_id AS branch_wallet, - branches.location As branch_location -FROM branch_cashiers - JOIN users ON branch_cashiers.user_id = users.id - JOIN branches ON branches.id = branch_id -WHERE ( - first_name ILIKE '%' || $1 || '%' - OR last_name ILIKE '%' || $1 || '%' - OR phone_number ILIKE '%' || $1 || '%' - OR $1 IS NULL - ) - AND ( - users.created_at > $2 - OR $2 IS NULL - ) - AND ( - users.created_at < $3 - OR $3 IS NULL - ) -` - -type GetAllCashiersParams struct { - Query pgtype.Text `json:"query"` - CreatedBefore pgtype.Timestamptz `json:"created_before"` - CreatedAfter pgtype.Timestamptz `json:"created_after"` -} - -type GetAllCashiersRow struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - Role string `json:"role"` - Password []byte `json:"password"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - CompanyID pgtype.Int8 `json:"company_id"` - SuspendedAt pgtype.Timestamptz `json:"suspended_at"` - Suspended bool `json:"suspended"` - BranchID int64 `json:"branch_id"` - BranchName string `json:"branch_name"` - BranchWallet int64 `json:"branch_wallet"` - BranchLocation string `json:"branch_location"` -} - -func (q *Queries) GetAllCashiers(ctx context.Context, arg GetAllCashiersParams) ([]GetAllCashiersRow, error) { - rows, err := q.db.Query(ctx, GetAllCashiers, arg.Query, arg.CreatedBefore, arg.CreatedAfter) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetAllCashiersRow - for rows.Next() { - var i GetAllCashiersRow - if err := rows.Scan( - &i.ID, - &i.FirstName, - &i.LastName, - &i.Email, - &i.PhoneNumber, - &i.Role, - &i.Password, - &i.EmailVerified, - &i.PhoneVerified, - &i.CreatedAt, - &i.UpdatedAt, - &i.CompanyID, - &i.SuspendedAt, - &i.Suspended, - &i.BranchID, - &i.BranchName, - &i.BranchWallet, - &i.BranchLocation, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetCashierByID = `-- name: GetCashierByID :one -SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, - branch_id, - branches.name AS branch_name, - branches.wallet_id AS branch_wallet, - branches.location As branch_location -FROM branch_cashiers - JOIN users ON branch_cashiers.user_id = users.id - JOIN branches ON branches.id = branch_id -WHERE users.id = $1 -` - -type GetCashierByIDRow struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - Role string `json:"role"` - Password []byte `json:"password"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - CompanyID pgtype.Int8 `json:"company_id"` - SuspendedAt pgtype.Timestamptz `json:"suspended_at"` - Suspended bool `json:"suspended"` - BranchID int64 `json:"branch_id"` - BranchName string `json:"branch_name"` - BranchWallet int64 `json:"branch_wallet"` - BranchLocation string `json:"branch_location"` -} - -func (q *Queries) GetCashierByID(ctx context.Context, id int64) (GetCashierByIDRow, error) { - row := q.db.QueryRow(ctx, GetCashierByID, id) - var i GetCashierByIDRow - err := row.Scan( - &i.ID, - &i.FirstName, - &i.LastName, - &i.Email, - &i.PhoneNumber, - &i.Role, - &i.Password, - &i.EmailVerified, - &i.PhoneVerified, - &i.CreatedAt, - &i.UpdatedAt, - &i.CompanyID, - &i.SuspendedAt, - &i.Suspended, - &i.BranchID, - &i.BranchName, - &i.BranchWallet, - &i.BranchLocation, - ) - return i, err -} - -const GetCashiersByBranch = `-- name: GetCashiersByBranch :many -SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended -FROM branch_cashiers - JOIN users ON branch_cashiers.user_id = users.id - JOIN branches ON branches.id = branch_id -WHERE branch_cashiers.branch_id = $1 -` - -func (q *Queries) GetCashiersByBranch(ctx context.Context, branchID int64) ([]User, error) { - rows, err := q.db.Query(ctx, GetCashiersByBranch, branchID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []User - for rows.Next() { - var i User - if err := rows.Scan( - &i.ID, - &i.FirstName, - &i.LastName, - &i.Email, - &i.PhoneNumber, - &i.Role, - &i.Password, - &i.EmailVerified, - &i.PhoneVerified, - &i.CreatedAt, - &i.UpdatedAt, - &i.CompanyID, - &i.SuspendedAt, - &i.Suspended, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/gen/db/company.sql.go b/gen/db/company.sql.go deleted file mode 100644 index d7a62a8..0000000 --- a/gen/db/company.sql.go +++ /dev/null @@ -1,298 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: company.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateCompany = `-- name: CreateCompany :one -INSERT INTO companies ( - name, - slug, - admin_id, - wallet_id, - deducted_percentage, - is_active - ) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at -` - -type CreateCompanyParams struct { - Name string `json:"name"` - Slug string `json:"slug"` - AdminID int64 `json:"admin_id"` - WalletID int64 `json:"wallet_id"` - DeductedPercentage float32 `json:"deducted_percentage"` - IsActive bool `json:"is_active"` -} - -func (q *Queries) CreateCompany(ctx context.Context, arg CreateCompanyParams) (Company, error) { - row := q.db.QueryRow(ctx, CreateCompany, - arg.Name, - arg.Slug, - arg.AdminID, - arg.WalletID, - arg.DeductedPercentage, - arg.IsActive, - ) - var i Company - err := row.Scan( - &i.ID, - &i.Name, - &i.Slug, - &i.AdminID, - &i.WalletID, - &i.DeductedPercentage, - &i.IsActive, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const DeleteCompany = `-- name: DeleteCompany :exec -DELETE FROM companies -WHERE id = $1 -` - -func (q *Queries) DeleteCompany(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteCompany, id) - return err -} - -const GetAllCompanies = `-- name: GetAllCompanies :many -SELECT id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at, balance, wallet_is_active, admin_first_name, admin_last_name, admin_phone_number, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_admins, total_managers, total_cashiers, total_customers, total_approvers, total_branches, stats_updated_at -FROM companies_details -WHERE ( - name ILIKE '%' || $1 || '%' - OR admin_first_name ILIKE '%' || $1 || '%' - OR admin_last_name ILIKE '%' || $1 || '%' - OR admin_phone_number ILIKE '%' || $1 || '%' - OR $1 IS NULL - ) - AND ( - created_at > $2 - OR $2 IS NULL - ) - AND ( - created_at < $3 - OR $3 IS NULL - ) -` - -type GetAllCompaniesParams struct { - Query pgtype.Text `json:"query"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetAllCompanies(ctx context.Context, arg GetAllCompaniesParams) ([]CompaniesDetail, error) { - rows, err := q.db.Query(ctx, GetAllCompanies, arg.Query, arg.CreatedBefore, arg.CreatedAfter) - if err != nil { - return nil, err - } - defer rows.Close() - var items []CompaniesDetail - for rows.Next() { - var i CompaniesDetail - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Slug, - &i.AdminID, - &i.WalletID, - &i.DeductedPercentage, - &i.IsActive, - &i.CreatedAt, - &i.UpdatedAt, - &i.Balance, - &i.WalletIsActive, - &i.AdminFirstName, - &i.AdminLastName, - &i.AdminPhoneNumber, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalAdmins, - &i.TotalManagers, - &i.TotalCashiers, - &i.TotalCustomers, - &i.TotalApprovers, - &i.TotalBranches, - &i.StatsUpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetCompanyByID = `-- name: GetCompanyByID :one -SELECT id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at, balance, wallet_is_active, admin_first_name, admin_last_name, admin_phone_number, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_admins, total_managers, total_cashiers, total_customers, total_approvers, total_branches, stats_updated_at -FROM companies_details -WHERE id = $1 -` - -func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (CompaniesDetail, error) { - row := q.db.QueryRow(ctx, GetCompanyByID, id) - var i CompaniesDetail - err := row.Scan( - &i.ID, - &i.Name, - &i.Slug, - &i.AdminID, - &i.WalletID, - &i.DeductedPercentage, - &i.IsActive, - &i.CreatedAt, - &i.UpdatedAt, - &i.Balance, - &i.WalletIsActive, - &i.AdminFirstName, - &i.AdminLastName, - &i.AdminPhoneNumber, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalAdmins, - &i.TotalManagers, - &i.TotalCashiers, - &i.TotalCustomers, - &i.TotalApprovers, - &i.TotalBranches, - &i.StatsUpdatedAt, - ) - return i, err -} - -const GetCompanyUsingSlug = `-- name: GetCompanyUsingSlug :one -SELECT id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at -FROM companies -WHERE slug = $1 -` - -func (q *Queries) GetCompanyUsingSlug(ctx context.Context, slug string) (Company, error) { - row := q.db.QueryRow(ctx, GetCompanyUsingSlug, slug) - var i Company - err := row.Scan( - &i.ID, - &i.Name, - &i.Slug, - &i.AdminID, - &i.WalletID, - &i.DeductedPercentage, - &i.IsActive, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const SearchCompanyByName = `-- name: SearchCompanyByName :many -SELECT id, name, slug, admin_id, wallet_id, deducted_percentage, is_active, created_at, updated_at, balance, wallet_is_active, admin_first_name, admin_last_name, admin_phone_number, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_admins, total_managers, total_cashiers, total_customers, total_approvers, total_branches, stats_updated_at -FROM companies_details -WHERE name ILIKE '%' || $1 || '%' -` - -func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text) ([]CompaniesDetail, error) { - rows, err := q.db.Query(ctx, SearchCompanyByName, dollar_1) - if err != nil { - return nil, err - } - defer rows.Close() - var items []CompaniesDetail - for rows.Next() { - var i CompaniesDetail - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Slug, - &i.AdminID, - &i.WalletID, - &i.DeductedPercentage, - &i.IsActive, - &i.CreatedAt, - &i.UpdatedAt, - &i.Balance, - &i.WalletIsActive, - &i.AdminFirstName, - &i.AdminLastName, - &i.AdminPhoneNumber, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalAdmins, - &i.TotalManagers, - &i.TotalCashiers, - &i.TotalCustomers, - &i.TotalApprovers, - &i.TotalBranches, - &i.StatsUpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateCompany = `-- name: UpdateCompany :exec -UPDATE companies -SET name = COALESCE($2, name), - admin_id = COALESCE($3, admin_id), - is_active = COALESCE($4, is_active), - deducted_percentage = COALESCE( - $5, - deducted_percentage - ), - slug = COALESCE($6, slug), - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateCompanyParams struct { - ID int64 `json:"id"` - Name pgtype.Text `json:"name"` - AdminID pgtype.Int8 `json:"admin_id"` - IsActive pgtype.Bool `json:"is_active"` - DeductedPercentage pgtype.Float4 `json:"deducted_percentage"` - Slug pgtype.Text `json:"slug"` -} - -func (q *Queries) UpdateCompany(ctx context.Context, arg UpdateCompanyParams) error { - _, err := q.db.Exec(ctx, UpdateCompany, - arg.ID, - arg.Name, - arg.AdminID, - arg.IsActive, - arg.DeductedPercentage, - arg.Slug, - ) - return err -} diff --git a/gen/db/company_stats.sql.go b/gen/db/company_stats.sql.go deleted file mode 100644 index 1c4b6f8..0000000 --- a/gen/db/company_stats.sql.go +++ /dev/null @@ -1,287 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: company_stats.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetCompanyStats = `-- name: GetCompanyStats :many -SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start, - company_stats.company_id, - company_stats.company_name, - company_stats.company_slug, - company_stats.total_bets, - company_stats.total_stake, - company_stats.deducted_stake, - company_stats.total_cash_out, - company_stats.total_cash_backs, - company_stats.number_of_unsettled, - company_stats.total_unsettled_amount, - company_stats.total_admins, - company_stats.total_managers, - company_stats.total_cashiers, - company_stats.total_customers, - company_stats.total_approvers, - company_stats.total_branches, - company_stats.updated_at -FROM company_stats -WHERE ( - company_stats.company_id = $2 - OR $2 IS NULL - ) -GROUP BY interval_start -ORDER BY interval_start DESC -` - -type GetCompanyStatsParams struct { - Interval pgtype.Text `json:"interval"` - CompanyID pgtype.Int8 `json:"company_id"` -} - -type GetCompanyStatsRow struct { - IntervalStart pgtype.Timestamp `json:"interval_start"` - CompanyID int64 `json:"company_id"` - CompanyName string `json:"company_name"` - CompanySlug string `json:"company_slug"` - TotalBets int64 `json:"total_bets"` - TotalStake int64 `json:"total_stake"` - DeductedStake int64 `json:"deducted_stake"` - TotalCashOut int64 `json:"total_cash_out"` - TotalCashBacks int64 `json:"total_cash_backs"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount int64 `json:"total_unsettled_amount"` - TotalAdmins int64 `json:"total_admins"` - TotalManagers int64 `json:"total_managers"` - TotalCashiers int64 `json:"total_cashiers"` - TotalCustomers int64 `json:"total_customers"` - TotalApprovers int64 `json:"total_approvers"` - TotalBranches int64 `json:"total_branches"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetCompanyStats(ctx context.Context, arg GetCompanyStatsParams) ([]GetCompanyStatsRow, error) { - rows, err := q.db.Query(ctx, GetCompanyStats, arg.Interval, arg.CompanyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetCompanyStatsRow - for rows.Next() { - var i GetCompanyStatsRow - if err := rows.Scan( - &i.IntervalStart, - &i.CompanyID, - &i.CompanyName, - &i.CompanySlug, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalAdmins, - &i.TotalManagers, - &i.TotalCashiers, - &i.TotalCustomers, - &i.TotalApprovers, - &i.TotalBranches, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetCompanyStatsByID = `-- name: GetCompanyStatsByID :many -SELECT company_id, company_name, company_slug, interval_start, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_admins, total_managers, total_cashiers, total_customers, total_approvers, total_branches, updated_at -FROM company_stats -WHERE company_id = $1 -ORDER BY interval_start DESC -` - -func (q *Queries) GetCompanyStatsByID(ctx context.Context, companyID int64) ([]CompanyStat, error) { - rows, err := q.db.Query(ctx, GetCompanyStatsByID, companyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []CompanyStat - for rows.Next() { - var i CompanyStat - if err := rows.Scan( - &i.CompanyID, - &i.CompanyName, - &i.CompanySlug, - &i.IntervalStart, - &i.TotalBets, - &i.TotalStake, - &i.DeductedStake, - &i.TotalCashOut, - &i.TotalCashBacks, - &i.NumberOfUnsettled, - &i.TotalUnsettledAmount, - &i.TotalAdmins, - &i.TotalManagers, - &i.TotalCashiers, - &i.TotalCustomers, - &i.TotalApprovers, - &i.TotalBranches, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateCompanyStats = `-- name: UpdateCompanyStats :exec -WITH -- Aggregate bet data per company -bet_stats AS ( - SELECT company_id, - COUNT(*) AS total_bets, - COALESCE(SUM(amount), 0) AS total_stake, - COALESCE( - SUM(amount) * MAX(companies.deducted_percentage), - 0 - ) AS deducted_stake, - COALESCE( - SUM( - CASE - WHEN cashed_out THEN amount - ELSE 0 - END - ), - 0 - ) AS total_cash_out, - COALESCE( - SUM( - CASE - WHEN status = 3 THEN amount - ELSE 0 - END - ), - 0 - ) AS total_cash_backs, - COUNT(*) FILTER ( - WHERE status = 5 - ) AS number_of_unsettled, - COALESCE( - SUM( - CASE - WHEN status = 5 THEN amount - ELSE 0 - END - ), - 0 - ) AS total_unsettled_amount - FROM shop_bet_detail - LEFT JOIN companies ON companies.id = shop_bet_detail.company_id - GROUP BY company_id -), -user_stats AS ( - SELECT company_id, - COUNT(*) FILTER ( - WHERE role = 'admin' - ) AS total_admins, - COUNT(*) FILTER ( - WHERE role = 'branch_manager' - ) AS total_managers, - COUNT(*) FILTER ( - WHERE role = 'cashier' - ) AS total_cashiers, - COUNT(*) FILTER ( - WHERE role = 'customer' - ) AS total_customers, - COUNT(*) FILTER ( - WHERE role = 'transaction_approver' - ) AS total_approvers - FROM users - GROUP BY company_id -), -branch_stats AS ( - SELECT company_id, - COUNT(*) AS total_branches - FROM branches - GROUP BY company_id -) -- Final combined aggregation -INSERT INTO company_stats ( - company_id, - company_name, - company_slug, - interval_start, - total_bets, - total_stake, - deducted_stake, - total_cash_out, - total_cash_backs, - number_of_unsettled, - total_unsettled_amount, - total_admins, - total_managers, - total_cashiers, - total_customers, - total_approvers, - total_branches, - updated_at - ) -SELECT c.id AS company_id, - c.name AS company_name, - c.slug AS company_slug, - DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start, - COALESCE(b.total_bets, 0) AS total_bets, - COALESCE(b.total_stake, 0) AS total_stake, - COALESCE(b.deducted_stake, 0) AS deducted_stake, - COALESCE(b.total_cash_out, 0) AS total_cash_out, - COALESCE(b.total_cash_backs, 0) AS total_cash_backs, - COALESCE(b.number_of_unsettled, 0) AS number_of_unsettled, - COALESCE(b.total_unsettled_amount, 0) AS total_unsettled_amount, - COALESCE(u.total_admins, 0) AS total_admins, - COALESCE(u.total_managers, 0) AS total_managers, - COALESCE(u.total_cashiers, 0) AS total_cashiers, - COALESCE(u.total_customers, 0) AS total_customers, - COALESCE(u.total_approvers, 0) AS total_approvers, - COALESCE(br.total_branches, 0) AS total_branches, - NOW() AS updated_at -FROM companies c - LEFT JOIN bet_stats b ON b.company_id = c.id - LEFT JOIN user_stats u ON u.company_id = c.id - LEFT JOIN branch_stats br ON br.company_id = c.id ON CONFLICT (company_id, interval_start) DO -UPDATE -SET total_bets = EXCLUDED.total_bets, - total_stake = EXCLUDED.total_stake, - deducted_stake = EXCLUDED.deducted_stake, - total_cash_out = EXCLUDED.total_cash_out, - total_cash_backs = EXCLUDED.total_cash_backs, - number_of_unsettled = EXCLUDED.number_of_unsettled, - total_unsettled_amount = EXCLUDED.total_unsettled_amount, - total_admins = EXCLUDED.total_admins, - total_managers = EXCLUDED.total_managers, - total_cashiers = EXCLUDED.total_cashiers, - total_customers = EXCLUDED.total_customers, - total_approvers = EXCLUDED.total_approvers, - total_branches = EXCLUDED.total_branches, - updated_at = EXCLUDED.updated_at -` - -// Aggregate user counts per company -// Aggregate branch counts per company -func (q *Queries) UpdateCompanyStats(ctx context.Context) error { - _, err := q.db.Exec(ctx, UpdateCompanyStats) - return err -} diff --git a/gen/db/copyfrom.go b/gen/db/copyfrom.go deleted file mode 100644 index 1212253..0000000 --- a/gen/db/copyfrom.go +++ /dev/null @@ -1,97 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: copyfrom.go - -package dbgen - -import ( - "context" -) - -// iteratorForCreateBetOutcome implements pgx.CopyFromSource. -type iteratorForCreateBetOutcome struct { - rows []CreateBetOutcomeParams - skippedFirstNextCall bool -} - -func (r *iteratorForCreateBetOutcome) Next() bool { - if len(r.rows) == 0 { - return false - } - if !r.skippedFirstNextCall { - r.skippedFirstNextCall = true - return true - } - r.rows = r.rows[1:] - return len(r.rows) > 0 -} - -func (r iteratorForCreateBetOutcome) Values() ([]interface{}, error) { - return []interface{}{ - r.rows[0].BetID, - r.rows[0].SportID, - r.rows[0].EventID, - r.rows[0].OddID, - r.rows[0].HomeTeamName, - r.rows[0].AwayTeamName, - r.rows[0].MarketID, - r.rows[0].MarketName, - r.rows[0].Odd, - r.rows[0].OddName, - r.rows[0].OddHeader, - r.rows[0].OddHandicap, - r.rows[0].Expires, - }, nil -} - -func (r iteratorForCreateBetOutcome) Err() error { - return nil -} - -func (q *Queries) CreateBetOutcome(ctx context.Context, arg []CreateBetOutcomeParams) (int64, error) { - return q.db.CopyFrom(ctx, []string{"bet_outcomes"}, []string{"bet_id", "sport_id", "event_id", "odd_id", "home_team_name", "away_team_name", "market_id", "market_name", "odd", "odd_name", "odd_header", "odd_handicap", "expires"}, &iteratorForCreateBetOutcome{rows: arg}) -} - -// iteratorForCreateTicketOutcome implements pgx.CopyFromSource. -type iteratorForCreateTicketOutcome struct { - rows []CreateTicketOutcomeParams - skippedFirstNextCall bool -} - -func (r *iteratorForCreateTicketOutcome) Next() bool { - if len(r.rows) == 0 { - return false - } - if !r.skippedFirstNextCall { - r.skippedFirstNextCall = true - return true - } - r.rows = r.rows[1:] - return len(r.rows) > 0 -} - -func (r iteratorForCreateTicketOutcome) Values() ([]interface{}, error) { - return []interface{}{ - r.rows[0].TicketID, - r.rows[0].EventID, - r.rows[0].OddID, - r.rows[0].HomeTeamName, - r.rows[0].AwayTeamName, - r.rows[0].MarketID, - r.rows[0].MarketName, - r.rows[0].Odd, - r.rows[0].OddName, - r.rows[0].OddHeader, - r.rows[0].OddHandicap, - r.rows[0].Expires, - }, nil -} - -func (r iteratorForCreateTicketOutcome) Err() error { - return nil -} - -func (q *Queries) CreateTicketOutcome(ctx context.Context, arg []CreateTicketOutcomeParams) (int64, error) { - return q.db.CopyFrom(ctx, []string{"ticket_outcomes"}, []string{"ticket_id", "event_id", "odd_id", "home_team_name", "away_team_name", "market_id", "market_name", "odd", "odd_name", "odd_header", "odd_handicap", "expires"}, &iteratorForCreateTicketOutcome{rows: arg}) -} diff --git a/gen/db/db.go b/gen/db/db.go index 84de07c..9c7d00d 100644 --- a/gen/db/db.go +++ b/gen/db/db.go @@ -15,7 +15,6 @@ type DBTX interface { Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) Query(context.Context, string, ...interface{}) (pgx.Rows, error) QueryRow(context.Context, string, ...interface{}) pgx.Row - CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNames []string, rowSrc pgx.CopyFromSource) (int64, error) } func New(db DBTX) *Queries { diff --git a/gen/db/direct_deposit.sql.go b/gen/db/direct_deposit.sql.go deleted file mode 100644 index aab1d6b..0000000 --- a/gen/db/direct_deposit.sql.go +++ /dev/null @@ -1,221 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: direct_deposit.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const ApproveDirectDeposit = `-- name: ApproveDirectDeposit :exec -UPDATE direct_deposits -SET - status = 'APPROVED', - approved_by = $2, - approved_at = NOW() -WHERE - id = $1 - AND status = 'PENDING' -` - -type ApproveDirectDepositParams struct { - ID int64 `json:"id"` - ApprovedBy pgtype.Int8 `json:"approved_by"` -} - -func (q *Queries) ApproveDirectDeposit(ctx context.Context, arg ApproveDirectDepositParams) error { - _, err := q.db.Exec(ctx, ApproveDirectDeposit, arg.ID, arg.ApprovedBy) - return err -} - -const CountDirectDepositsByStatus = `-- name: CountDirectDepositsByStatus :one -SELECT COUNT(*) -FROM direct_deposits -WHERE status = $1 -` - -func (q *Queries) CountDirectDepositsByStatus(ctx context.Context, status pgtype.Text) (int64, error) { - row := q.db.QueryRow(ctx, CountDirectDepositsByStatus, status) - var count int64 - err := row.Scan(&count) - return count, err -} - -const CreateDirectDeposit = `-- name: CreateDirectDeposit :one -INSERT INTO direct_deposits ( - customer_id, wallet_id, bank_name, account_number, - account_holder, amount, reference_number, - transfer_screenshot, status -) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'PENDING') -RETURNING id, customer_id, wallet_id, bank_name, account_number, account_holder, amount, reference_number, transfer_screenshot, status, created_at, approved_by, approved_at, rejection_reason -` - -type CreateDirectDepositParams struct { - CustomerID pgtype.Int8 `json:"customer_id"` - WalletID pgtype.Int8 `json:"wallet_id"` - BankName pgtype.Text `json:"bank_name"` - AccountNumber pgtype.Text `json:"account_number"` - AccountHolder pgtype.Text `json:"account_holder"` - Amount pgtype.Numeric `json:"amount"` - ReferenceNumber pgtype.Text `json:"reference_number"` - TransferScreenshot pgtype.Text `json:"transfer_screenshot"` -} - -func (q *Queries) CreateDirectDeposit(ctx context.Context, arg CreateDirectDepositParams) (DirectDeposit, error) { - row := q.db.QueryRow(ctx, CreateDirectDeposit, - arg.CustomerID, - arg.WalletID, - arg.BankName, - arg.AccountNumber, - arg.AccountHolder, - arg.Amount, - arg.ReferenceNumber, - arg.TransferScreenshot, - ) - var i DirectDeposit - err := row.Scan( - &i.ID, - &i.CustomerID, - &i.WalletID, - &i.BankName, - &i.AccountNumber, - &i.AccountHolder, - &i.Amount, - &i.ReferenceNumber, - &i.TransferScreenshot, - &i.Status, - &i.CreatedAt, - &i.ApprovedBy, - &i.ApprovedAt, - &i.RejectionReason, - ) - return i, err -} - -const DeleteDirectDeposit = `-- name: DeleteDirectDeposit :exec -DELETE FROM direct_deposits -WHERE id = $1 -` - -func (q *Queries) DeleteDirectDeposit(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteDirectDeposit, id) - return err -} - -const GetDirectDepositByID = `-- name: GetDirectDepositByID :one -SELECT id, customer_id, wallet_id, bank_name, account_number, account_holder, amount, reference_number, transfer_screenshot, status, created_at, approved_by, approved_at, rejection_reason -FROM direct_deposits -WHERE id = $1 -` - -func (q *Queries) GetDirectDepositByID(ctx context.Context, id int64) (DirectDeposit, error) { - row := q.db.QueryRow(ctx, GetDirectDepositByID, id) - var i DirectDeposit - err := row.Scan( - &i.ID, - &i.CustomerID, - &i.WalletID, - &i.BankName, - &i.AccountNumber, - &i.AccountHolder, - &i.Amount, - &i.ReferenceNumber, - &i.TransferScreenshot, - &i.Status, - &i.CreatedAt, - &i.ApprovedBy, - &i.ApprovedAt, - &i.RejectionReason, - ) - return i, err -} - -const GetDirectDepositsByStatus = `-- name: GetDirectDepositsByStatus :many -SELECT - id, - customer_id, - wallet_id, - bank_name, - account_number, - account_holder, - amount, - reference_number, - transfer_screenshot, - status, - created_at, - approved_by, - approved_at, - rejection_reason -FROM direct_deposits -WHERE status = $1 -ORDER BY created_at DESC -LIMIT $2 OFFSET $3 -` - -type GetDirectDepositsByStatusParams struct { - Status pgtype.Text `json:"status"` - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -func (q *Queries) GetDirectDepositsByStatus(ctx context.Context, arg GetDirectDepositsByStatusParams) ([]DirectDeposit, error) { - rows, err := q.db.Query(ctx, GetDirectDepositsByStatus, arg.Status, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []DirectDeposit - for rows.Next() { - var i DirectDeposit - if err := rows.Scan( - &i.ID, - &i.CustomerID, - &i.WalletID, - &i.BankName, - &i.AccountNumber, - &i.AccountHolder, - &i.Amount, - &i.ReferenceNumber, - &i.TransferScreenshot, - &i.Status, - &i.CreatedAt, - &i.ApprovedBy, - &i.ApprovedAt, - &i.RejectionReason, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const RejectDirectDeposit = `-- name: RejectDirectDeposit :exec -UPDATE direct_deposits -SET - status = 'REJECTED', - approved_by = $2, -- still track the admin who took final action - approved_at = NOW(), - rejection_reason = $3 -WHERE - id = $1 - AND status = 'PENDING' -` - -type RejectDirectDepositParams struct { - ID int64 `json:"id"` - ApprovedBy pgtype.Int8 `json:"approved_by"` - RejectionReason pgtype.Text `json:"rejection_reason"` -} - -func (q *Queries) RejectDirectDeposit(ctx context.Context, arg RejectDirectDepositParams) error { - _, err := q.db.Exec(ctx, RejectDirectDeposit, arg.ID, arg.ApprovedBy, arg.RejectionReason) - return err -} diff --git a/gen/db/enet_pulse.sql.go b/gen/db/enet_pulse.sql.go deleted file mode 100644 index f06cd69..0000000 --- a/gen/db/enet_pulse.sql.go +++ /dev/null @@ -1,1678 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: enet_pulse.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateEnetpulseFixture = `-- name: CreateEnetpulseFixture :one -INSERT INTO enetpulse_fixtures ( - fixture_id, - name, - sport_fk, - tournament_fk, - tournament_template_fk, - tournament_name, - tournament_template_name, - sport_name, - gender, - start_date, - status_type, - status_desc_fk, - round_type_fk, - updates_count, - last_updated_at, - created_at, - updated_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP -) -ON CONFLICT (fixture_id) DO UPDATE -SET - name = EXCLUDED.name, - sport_fk = EXCLUDED.sport_fk, - tournament_fk = EXCLUDED.tournament_fk, - tournament_template_fk = EXCLUDED.tournament_template_fk, - tournament_name = EXCLUDED.tournament_name, - tournament_template_name = EXCLUDED.tournament_template_name, - sport_name = EXCLUDED.sport_name, - gender = EXCLUDED.gender, - start_date = EXCLUDED.start_date, - status_type = EXCLUDED.status_type, - status_desc_fk = EXCLUDED.status_desc_fk, - round_type_fk = EXCLUDED.round_type_fk, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - updated_at = CURRENT_TIMESTAMP -RETURNING id, fixture_id, name, sport_fk, tournament_fk, tournament_template_fk, tournament_name, tournament_template_name, sport_name, gender, start_date, status_type, status_desc_fk, round_type_fk, updates_count, last_updated_at, created_at, updated_at -` - -type CreateEnetpulseFixtureParams struct { - FixtureID string `json:"fixture_id"` - Name string `json:"name"` - SportFk string `json:"sport_fk"` - TournamentFk pgtype.Text `json:"tournament_fk"` - TournamentTemplateFk pgtype.Text `json:"tournament_template_fk"` - TournamentName pgtype.Text `json:"tournament_name"` - TournamentTemplateName pgtype.Text `json:"tournament_template_name"` - SportName pgtype.Text `json:"sport_name"` - Gender pgtype.Text `json:"gender"` - StartDate pgtype.Timestamptz `json:"start_date"` - StatusType pgtype.Text `json:"status_type"` - StatusDescFk pgtype.Text `json:"status_desc_fk"` - RoundTypeFk pgtype.Text `json:"round_type_fk"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` -} - -func (q *Queries) CreateEnetpulseFixture(ctx context.Context, arg CreateEnetpulseFixtureParams) (EnetpulseFixture, error) { - row := q.db.QueryRow(ctx, CreateEnetpulseFixture, - arg.FixtureID, - arg.Name, - arg.SportFk, - arg.TournamentFk, - arg.TournamentTemplateFk, - arg.TournamentName, - arg.TournamentTemplateName, - arg.SportName, - arg.Gender, - arg.StartDate, - arg.StatusType, - arg.StatusDescFk, - arg.RoundTypeFk, - arg.UpdatesCount, - arg.LastUpdatedAt, - ) - var i EnetpulseFixture - err := row.Scan( - &i.ID, - &i.FixtureID, - &i.Name, - &i.SportFk, - &i.TournamentFk, - &i.TournamentTemplateFk, - &i.TournamentName, - &i.TournamentTemplateName, - &i.SportName, - &i.Gender, - &i.StartDate, - &i.StatusType, - &i.StatusDescFk, - &i.RoundTypeFk, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateEnetpulseOutcomeType = `-- name: CreateEnetpulseOutcomeType :one -INSERT INTO enetpulse_outcome_types ( - outcome_type_id, - name, - description, - updates_count, - last_updated_at, - created_at, - updated_at -) VALUES ( - $1, $2, $3, $4, $5, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP -) -ON CONFLICT (outcome_type_id) DO UPDATE -SET - name = EXCLUDED.name, - description = EXCLUDED.description, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - updated_at = CURRENT_TIMESTAMP -RETURNING id, outcome_type_id, name, description, updates_count, last_updated_at, created_at, updated_at -` - -type CreateEnetpulseOutcomeTypeParams struct { - OutcomeTypeID string `json:"outcome_type_id"` - Name string `json:"name"` - Description pgtype.Text `json:"description"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` -} - -func (q *Queries) CreateEnetpulseOutcomeType(ctx context.Context, arg CreateEnetpulseOutcomeTypeParams) (EnetpulseOutcomeType, error) { - row := q.db.QueryRow(ctx, CreateEnetpulseOutcomeType, - arg.OutcomeTypeID, - arg.Name, - arg.Description, - arg.UpdatesCount, - arg.LastUpdatedAt, - ) - var i EnetpulseOutcomeType - err := row.Scan( - &i.ID, - &i.OutcomeTypeID, - &i.Name, - &i.Description, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateEnetpulsePreodds = `-- name: CreateEnetpulsePreodds :one -INSERT INTO enetpulse_preodds ( - preodds_id, - event_fk, - outcome_type_fk, - outcome_scope_fk, - outcome_subtype_fk, - event_participant_number, - iparam, - iparam2, - dparam, - dparam2, - sparam, - updates_count, - last_updated_at, - created_at, - updated_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP -) -ON CONFLICT (preodds_id) DO UPDATE -SET - event_fk = EXCLUDED.event_fk, - outcome_type_fk = EXCLUDED.outcome_type_fk, - outcome_scope_fk = EXCLUDED.outcome_scope_fk, - outcome_subtype_fk = EXCLUDED.outcome_subtype_fk, - event_participant_number = EXCLUDED.event_participant_number, - iparam = EXCLUDED.iparam, - iparam2 = EXCLUDED.iparam2, - dparam = EXCLUDED.dparam, - dparam2 = EXCLUDED.dparam2, - sparam = EXCLUDED.sparam, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - updated_at = CURRENT_TIMESTAMP -RETURNING id, preodds_id, event_fk, outcome_type_fk, outcome_scope_fk, outcome_subtype_fk, event_participant_number, iparam, iparam2, dparam, dparam2, sparam, updates_count, last_updated_at, created_at, updated_at -` - -type CreateEnetpulsePreoddsParams struct { - PreoddsID string `json:"preodds_id"` - EventFk int64 `json:"event_fk"` - OutcomeTypeFk pgtype.Int4 `json:"outcome_type_fk"` - OutcomeScopeFk pgtype.Int4 `json:"outcome_scope_fk"` - OutcomeSubtypeFk pgtype.Int4 `json:"outcome_subtype_fk"` - EventParticipantNumber pgtype.Int4 `json:"event_participant_number"` - Iparam pgtype.Text `json:"iparam"` - Iparam2 pgtype.Text `json:"iparam2"` - Dparam pgtype.Text `json:"dparam"` - Dparam2 pgtype.Text `json:"dparam2"` - Sparam pgtype.Text `json:"sparam"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` -} - -func (q *Queries) CreateEnetpulsePreodds(ctx context.Context, arg CreateEnetpulsePreoddsParams) (EnetpulsePreodd, error) { - row := q.db.QueryRow(ctx, CreateEnetpulsePreodds, - arg.PreoddsID, - arg.EventFk, - arg.OutcomeTypeFk, - arg.OutcomeScopeFk, - arg.OutcomeSubtypeFk, - arg.EventParticipantNumber, - arg.Iparam, - arg.Iparam2, - arg.Dparam, - arg.Dparam2, - arg.Sparam, - arg.UpdatesCount, - arg.LastUpdatedAt, - ) - var i EnetpulsePreodd - err := row.Scan( - &i.ID, - &i.PreoddsID, - &i.EventFk, - &i.OutcomeTypeFk, - &i.OutcomeScopeFk, - &i.OutcomeSubtypeFk, - &i.EventParticipantNumber, - &i.Iparam, - &i.Iparam2, - &i.Dparam, - &i.Dparam2, - &i.Sparam, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateEnetpulsePreoddsBettingOffer = `-- name: CreateEnetpulsePreoddsBettingOffer :one -INSERT INTO enetpulse_preodds_bettingoffers ( - bettingoffer_id, - preodds_fk, - bettingoffer_status_fk, - odds_provider_fk, - odds, - odds_old, - active, - coupon_key, - updates_count, - last_updated_at, - created_at, - updated_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP -) -ON CONFLICT (bettingoffer_id) DO UPDATE -SET - preodds_fk = EXCLUDED.preodds_fk, - bettingoffer_status_fk = EXCLUDED.bettingoffer_status_fk, - odds_provider_fk = EXCLUDED.odds_provider_fk, - odds = EXCLUDED.odds, - odds_old = EXCLUDED.odds_old, - active = EXCLUDED.active, - coupon_key = EXCLUDED.coupon_key, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - updated_at = CURRENT_TIMESTAMP -RETURNING id, bettingoffer_id, preodds_fk, bettingoffer_status_fk, odds_provider_fk, odds, odds_old, active, coupon_key, updates_count, last_updated_at, created_at, updated_at -` - -type CreateEnetpulsePreoddsBettingOfferParams struct { - BettingofferID string `json:"bettingoffer_id"` - PreoddsFk string `json:"preodds_fk"` - BettingofferStatusFk pgtype.Int4 `json:"bettingoffer_status_fk"` - OddsProviderFk pgtype.Int4 `json:"odds_provider_fk"` - Odds pgtype.Numeric `json:"odds"` - OddsOld pgtype.Numeric `json:"odds_old"` - Active pgtype.Bool `json:"active"` - CouponKey pgtype.Text `json:"coupon_key"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` -} - -func (q *Queries) CreateEnetpulsePreoddsBettingOffer(ctx context.Context, arg CreateEnetpulsePreoddsBettingOfferParams) (EnetpulsePreoddsBettingoffer, error) { - row := q.db.QueryRow(ctx, CreateEnetpulsePreoddsBettingOffer, - arg.BettingofferID, - arg.PreoddsFk, - arg.BettingofferStatusFk, - arg.OddsProviderFk, - arg.Odds, - arg.OddsOld, - arg.Active, - arg.CouponKey, - arg.UpdatesCount, - arg.LastUpdatedAt, - ) - var i EnetpulsePreoddsBettingoffer - err := row.Scan( - &i.ID, - &i.BettingofferID, - &i.PreoddsFk, - &i.BettingofferStatusFk, - &i.OddsProviderFk, - &i.Odds, - &i.OddsOld, - &i.Active, - &i.CouponKey, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateEnetpulseResult = `-- name: CreateEnetpulseResult :one -INSERT INTO enetpulse_results ( - result_id, - name, - sport_fk, - tournament_fk, - tournament_template_fk, - tournament_name, - tournament_template_name, - sport_name, - start_date, - status_type, - status_desc_fk, - round_type_fk, - updates_count, - last_updated_at, - round, - live, - venue_name, - livestats_plus, - livestats_type, - commentary, - lineup_confirmed, - verified, - spectators, - game_started, - first_half_ended, - second_half_started, - second_half_ended, - game_ended, - created_at, - updated_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10, - $11,$12,$13,$14,$15,$16,$17,$18, - $19,$20,$21,$22,$23,$24,$25,$26, - $27,$28,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP -) -ON CONFLICT (result_id) DO UPDATE -SET - name = EXCLUDED.name, - sport_fk = EXCLUDED.sport_fk, - tournament_fk = EXCLUDED.tournament_fk, - tournament_template_fk = EXCLUDED.tournament_template_fk, - tournament_name = EXCLUDED.tournament_name, - tournament_template_name = EXCLUDED.tournament_template_name, - sport_name = EXCLUDED.sport_name, - start_date = EXCLUDED.start_date, - status_type = EXCLUDED.status_type, - status_desc_fk = EXCLUDED.status_desc_fk, - round_type_fk = EXCLUDED.round_type_fk, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - round = EXCLUDED.round, - live = EXCLUDED.live, - venue_name = EXCLUDED.venue_name, - livestats_plus = EXCLUDED.livestats_plus, - livestats_type = EXCLUDED.livestats_type, - commentary = EXCLUDED.commentary, - lineup_confirmed = EXCLUDED.lineup_confirmed, - verified = EXCLUDED.verified, - spectators = EXCLUDED.spectators, - game_started = EXCLUDED.game_started, - first_half_ended = EXCLUDED.first_half_ended, - second_half_started = EXCLUDED.second_half_started, - second_half_ended = EXCLUDED.second_half_ended, - game_ended = EXCLUDED.game_ended, - updated_at = CURRENT_TIMESTAMP -RETURNING id, result_id, name, sport_fk, tournament_fk, tournament_template_fk, tournament_name, tournament_template_name, sport_name, start_date, status_type, status_desc_fk, round_type_fk, updates_count, last_updated_at, round, live, venue_name, livestats_plus, livestats_type, commentary, lineup_confirmed, verified, spectators, game_started, first_half_ended, second_half_started, second_half_ended, game_ended, created_at, updated_at -` - -type CreateEnetpulseResultParams struct { - ResultID string `json:"result_id"` - Name string `json:"name"` - SportFk string `json:"sport_fk"` - TournamentFk pgtype.Text `json:"tournament_fk"` - TournamentTemplateFk pgtype.Text `json:"tournament_template_fk"` - TournamentName pgtype.Text `json:"tournament_name"` - TournamentTemplateName pgtype.Text `json:"tournament_template_name"` - SportName pgtype.Text `json:"sport_name"` - StartDate pgtype.Timestamptz `json:"start_date"` - StatusType pgtype.Text `json:"status_type"` - StatusDescFk pgtype.Text `json:"status_desc_fk"` - RoundTypeFk pgtype.Text `json:"round_type_fk"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Round pgtype.Text `json:"round"` - Live pgtype.Text `json:"live"` - VenueName pgtype.Text `json:"venue_name"` - LivestatsPlus pgtype.Text `json:"livestats_plus"` - LivestatsType pgtype.Text `json:"livestats_type"` - Commentary pgtype.Text `json:"commentary"` - LineupConfirmed pgtype.Bool `json:"lineup_confirmed"` - Verified pgtype.Bool `json:"verified"` - Spectators pgtype.Int4 `json:"spectators"` - GameStarted pgtype.Timestamptz `json:"game_started"` - FirstHalfEnded pgtype.Timestamptz `json:"first_half_ended"` - SecondHalfStarted pgtype.Timestamptz `json:"second_half_started"` - SecondHalfEnded pgtype.Timestamptz `json:"second_half_ended"` - GameEnded pgtype.Timestamptz `json:"game_ended"` -} - -func (q *Queries) CreateEnetpulseResult(ctx context.Context, arg CreateEnetpulseResultParams) (EnetpulseResult, error) { - row := q.db.QueryRow(ctx, CreateEnetpulseResult, - arg.ResultID, - arg.Name, - arg.SportFk, - arg.TournamentFk, - arg.TournamentTemplateFk, - arg.TournamentName, - arg.TournamentTemplateName, - arg.SportName, - arg.StartDate, - arg.StatusType, - arg.StatusDescFk, - arg.RoundTypeFk, - arg.UpdatesCount, - arg.LastUpdatedAt, - arg.Round, - arg.Live, - arg.VenueName, - arg.LivestatsPlus, - arg.LivestatsType, - arg.Commentary, - arg.LineupConfirmed, - arg.Verified, - arg.Spectators, - arg.GameStarted, - arg.FirstHalfEnded, - arg.SecondHalfStarted, - arg.SecondHalfEnded, - arg.GameEnded, - ) - var i EnetpulseResult - err := row.Scan( - &i.ID, - &i.ResultID, - &i.Name, - &i.SportFk, - &i.TournamentFk, - &i.TournamentTemplateFk, - &i.TournamentName, - &i.TournamentTemplateName, - &i.SportName, - &i.StartDate, - &i.StatusType, - &i.StatusDescFk, - &i.RoundTypeFk, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Round, - &i.Live, - &i.VenueName, - &i.LivestatsPlus, - &i.LivestatsType, - &i.Commentary, - &i.LineupConfirmed, - &i.Verified, - &i.Spectators, - &i.GameStarted, - &i.FirstHalfEnded, - &i.SecondHalfStarted, - &i.SecondHalfEnded, - &i.GameEnded, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateEnetpulseResultParticipant = `-- name: CreateEnetpulseResultParticipant :one -INSERT INTO enetpulse_result_participants ( - participant_map_id, - result_fk, - participant_fk, - number, - name, - gender, - type, - country_fk, - country_name, - ordinary_time, - running_score, - halftime, - final_result, - last_updated_at, - created_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,CURRENT_TIMESTAMP -) -ON CONFLICT (participant_map_id) DO UPDATE -SET - result_fk = EXCLUDED.result_fk, - participant_fk = EXCLUDED.participant_fk, - number = EXCLUDED.number, - name = EXCLUDED.name, - gender = EXCLUDED.gender, - type = EXCLUDED.type, - country_fk = EXCLUDED.country_fk, - country_name = EXCLUDED.country_name, - ordinary_time = EXCLUDED.ordinary_time, - running_score = EXCLUDED.running_score, - halftime = EXCLUDED.halftime, - final_result = EXCLUDED.final_result, - last_updated_at = EXCLUDED.last_updated_at -RETURNING id, participant_map_id, result_fk, participant_fk, number, name, gender, type, country_fk, country_name, ordinary_time, running_score, halftime, final_result, last_updated_at, created_at -` - -type CreateEnetpulseResultParticipantParams struct { - ParticipantMapID string `json:"participant_map_id"` - ResultFk string `json:"result_fk"` - ParticipantFk string `json:"participant_fk"` - Number pgtype.Int4 `json:"number"` - Name pgtype.Text `json:"name"` - Gender pgtype.Text `json:"gender"` - Type pgtype.Text `json:"type"` - CountryFk pgtype.Text `json:"country_fk"` - CountryName pgtype.Text `json:"country_name"` - OrdinaryTime pgtype.Text `json:"ordinary_time"` - RunningScore pgtype.Text `json:"running_score"` - Halftime pgtype.Text `json:"halftime"` - FinalResult pgtype.Text `json:"final_result"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` -} - -func (q *Queries) CreateEnetpulseResultParticipant(ctx context.Context, arg CreateEnetpulseResultParticipantParams) (EnetpulseResultParticipant, error) { - row := q.db.QueryRow(ctx, CreateEnetpulseResultParticipant, - arg.ParticipantMapID, - arg.ResultFk, - arg.ParticipantFk, - arg.Number, - arg.Name, - arg.Gender, - arg.Type, - arg.CountryFk, - arg.CountryName, - arg.OrdinaryTime, - arg.RunningScore, - arg.Halftime, - arg.FinalResult, - arg.LastUpdatedAt, - ) - var i EnetpulseResultParticipant - err := row.Scan( - &i.ID, - &i.ParticipantMapID, - &i.ResultFk, - &i.ParticipantFk, - &i.Number, - &i.Name, - &i.Gender, - &i.Type, - &i.CountryFk, - &i.CountryName, - &i.OrdinaryTime, - &i.RunningScore, - &i.Halftime, - &i.FinalResult, - &i.LastUpdatedAt, - &i.CreatedAt, - ) - return i, err -} - -const CreateEnetpulseResultReferee = `-- name: CreateEnetpulseResultReferee :one -INSERT INTO enetpulse_result_referees ( - result_fk, - referee_fk, - assistant1_referee_fk, - assistant2_referee_fk, - fourth_referee_fk, - var1_referee_fk, - var2_referee_fk, - last_updated_at, - created_at -) VALUES ( - $1,$2,$3,$4,$5,$6,$7,$8,CURRENT_TIMESTAMP -) -ON CONFLICT (result_fk) DO UPDATE -SET - referee_fk = EXCLUDED.referee_fk, - assistant1_referee_fk = EXCLUDED.assistant1_referee_fk, - assistant2_referee_fk = EXCLUDED.assistant2_referee_fk, - fourth_referee_fk = EXCLUDED.fourth_referee_fk, - var1_referee_fk = EXCLUDED.var1_referee_fk, - var2_referee_fk = EXCLUDED.var2_referee_fk, - last_updated_at = EXCLUDED.last_updated_at -RETURNING id, result_fk, referee_fk, assistant1_referee_fk, assistant2_referee_fk, fourth_referee_fk, var1_referee_fk, var2_referee_fk, last_updated_at, created_at -` - -type CreateEnetpulseResultRefereeParams struct { - ResultFk string `json:"result_fk"` - RefereeFk pgtype.Text `json:"referee_fk"` - Assistant1RefereeFk pgtype.Text `json:"assistant1_referee_fk"` - Assistant2RefereeFk pgtype.Text `json:"assistant2_referee_fk"` - FourthRefereeFk pgtype.Text `json:"fourth_referee_fk"` - Var1RefereeFk pgtype.Text `json:"var1_referee_fk"` - Var2RefereeFk pgtype.Text `json:"var2_referee_fk"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` -} - -func (q *Queries) CreateEnetpulseResultReferee(ctx context.Context, arg CreateEnetpulseResultRefereeParams) (EnetpulseResultReferee, error) { - row := q.db.QueryRow(ctx, CreateEnetpulseResultReferee, - arg.ResultFk, - arg.RefereeFk, - arg.Assistant1RefereeFk, - arg.Assistant2RefereeFk, - arg.FourthRefereeFk, - arg.Var1RefereeFk, - arg.Var2RefereeFk, - arg.LastUpdatedAt, - ) - var i EnetpulseResultReferee - err := row.Scan( - &i.ID, - &i.ResultFk, - &i.RefereeFk, - &i.Assistant1RefereeFk, - &i.Assistant2RefereeFk, - &i.FourthRefereeFk, - &i.Var1RefereeFk, - &i.Var2RefereeFk, - &i.LastUpdatedAt, - &i.CreatedAt, - ) - return i, err -} - -const CreateEnetpulseSport = `-- name: CreateEnetpulseSport :one -INSERT INTO enetpulse_sports ( - sport_id, - name, - updates_count, - last_updated_at, - status, - updated_at -) VALUES ( - $1, $2, $3, $4, $5, NOW() -) -ON CONFLICT (sport_id) DO UPDATE -SET - name = EXCLUDED.name, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - status = EXCLUDED.status, - updated_at = NOW() -RETURNING id, sport_id, name, updates_count, last_updated_at, status, created_at, updated_at -` - -type CreateEnetpulseSportParams struct { - SportID string `json:"sport_id"` - Name string `json:"name"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Status pgtype.Int4 `json:"status"` -} - -func (q *Queries) CreateEnetpulseSport(ctx context.Context, arg CreateEnetpulseSportParams) (EnetpulseSport, error) { - row := q.db.QueryRow(ctx, CreateEnetpulseSport, - arg.SportID, - arg.Name, - arg.UpdatesCount, - arg.LastUpdatedAt, - arg.Status, - ) - var i EnetpulseSport - err := row.Scan( - &i.ID, - &i.SportID, - &i.Name, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateEnetpulseTournament = `-- name: CreateEnetpulseTournament :one - -INSERT INTO enetpulse_tournaments ( - tournament_id, - name, - tournament_template_fk, - updates_count, - last_updated_at, - status -) VALUES ($1, $2, $3, $4, $5, $6) -ON CONFLICT (tournament_id) DO UPDATE -SET - name = EXCLUDED.name, - tournament_template_fk = EXCLUDED.tournament_template_fk, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - status = EXCLUDED.status -RETURNING id, tournament_id, name, tournament_template_fk, updates_count, last_updated_at, status, created_at, updated_at -` - -type CreateEnetpulseTournamentParams struct { - TournamentID string `json:"tournament_id"` - Name string `json:"name"` - TournamentTemplateFk string `json:"tournament_template_fk"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Status pgtype.Int4 `json:"status"` -} - -// -- name: DeleteEnetpulseTournamentTemplateByID :exec -// DELETE FROM enetpulse_tournament_templates WHERE template_id = $1; -func (q *Queries) CreateEnetpulseTournament(ctx context.Context, arg CreateEnetpulseTournamentParams) (EnetpulseTournament, error) { - row := q.db.QueryRow(ctx, CreateEnetpulseTournament, - arg.TournamentID, - arg.Name, - arg.TournamentTemplateFk, - arg.UpdatesCount, - arg.LastUpdatedAt, - arg.Status, - ) - var i EnetpulseTournament - err := row.Scan( - &i.ID, - &i.TournamentID, - &i.Name, - &i.TournamentTemplateFk, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateEnetpulseTournamentStage = `-- name: CreateEnetpulseTournamentStage :one -INSERT INTO enetpulse_tournament_stages ( - stage_id, - name, - tournament_fk, - gender, - country_fk, - country_name, - start_date, - end_date, - updates_count, - last_updated_at, - status -) -VALUES ( - $1, -- stage_id - $2, -- name - $3, -- tournament_fk - $4, -- gender - $5, -- country_fk - $6, -- country_name - $7, -- start_date - $8, -- end_date - $9, -- updates_count - $10, -- last_updated_at - $11 -- status -) -ON CONFLICT (stage_id) DO UPDATE -SET - name = EXCLUDED.name, - tournament_fk = EXCLUDED.tournament_fk, - gender = EXCLUDED.gender, - country_fk = EXCLUDED.country_fk, - country_name = EXCLUDED.country_name, - start_date = EXCLUDED.start_date, - end_date = EXCLUDED.end_date, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - status = EXCLUDED.status, - updated_at = NOW() -RETURNING id, stage_id, name, tournament_fk, gender, country_fk, country_name, start_date, end_date, updates_count, last_updated_at, status, created_at, updated_at -` - -type CreateEnetpulseTournamentStageParams struct { - StageID string `json:"stage_id"` - Name string `json:"name"` - TournamentFk string `json:"tournament_fk"` - Gender pgtype.Text `json:"gender"` - CountryFk pgtype.Text `json:"country_fk"` - CountryName pgtype.Text `json:"country_name"` - StartDate pgtype.Timestamptz `json:"start_date"` - EndDate pgtype.Timestamptz `json:"end_date"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Status pgtype.Int4 `json:"status"` -} - -func (q *Queries) CreateEnetpulseTournamentStage(ctx context.Context, arg CreateEnetpulseTournamentStageParams) (EnetpulseTournamentStage, error) { - row := q.db.QueryRow(ctx, CreateEnetpulseTournamentStage, - arg.StageID, - arg.Name, - arg.TournamentFk, - arg.Gender, - arg.CountryFk, - arg.CountryName, - arg.StartDate, - arg.EndDate, - arg.UpdatesCount, - arg.LastUpdatedAt, - arg.Status, - ) - var i EnetpulseTournamentStage - err := row.Scan( - &i.ID, - &i.StageID, - &i.Name, - &i.TournamentFk, - &i.Gender, - &i.CountryFk, - &i.CountryName, - &i.StartDate, - &i.EndDate, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateEnetpulseTournamentTemplate = `-- name: CreateEnetpulseTournamentTemplate :one -INSERT INTO enetpulse_tournament_templates ( - template_id, - name, - sport_fk, - gender, - updates_count, - last_updated_at, - status, - updated_at -) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW()) -ON CONFLICT (template_id) DO UPDATE -SET - name = EXCLUDED.name, - sport_fk = EXCLUDED.sport_fk, - gender = EXCLUDED.gender, - updates_count = EXCLUDED.updates_count, - last_updated_at = EXCLUDED.last_updated_at, - status = EXCLUDED.status, - updated_at = NOW() -RETURNING id, template_id, name, sport_fk, gender, updates_count, last_updated_at, status, created_at, updated_at -` - -type CreateEnetpulseTournamentTemplateParams struct { - TemplateID string `json:"template_id"` - Name string `json:"name"` - SportFk string `json:"sport_fk"` - Gender pgtype.Text `json:"gender"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Status pgtype.Int4 `json:"status"` -} - -func (q *Queries) CreateEnetpulseTournamentTemplate(ctx context.Context, arg CreateEnetpulseTournamentTemplateParams) (EnetpulseTournamentTemplate, error) { - row := q.db.QueryRow(ctx, CreateEnetpulseTournamentTemplate, - arg.TemplateID, - arg.Name, - arg.SportFk, - arg.Gender, - arg.UpdatesCount, - arg.LastUpdatedAt, - arg.Status, - ) - var i EnetpulseTournamentTemplate - err := row.Scan( - &i.ID, - &i.TemplateID, - &i.Name, - &i.SportFk, - &i.Gender, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetAllEnetpulseFixtures = `-- name: GetAllEnetpulseFixtures :many -SELECT id, fixture_id, name, sport_fk, tournament_fk, tournament_template_fk, tournament_name, tournament_template_name, sport_name, gender, start_date, status_type, status_desc_fk, round_type_fk, updates_count, last_updated_at, created_at, updated_at -FROM enetpulse_fixtures -ORDER BY created_at DESC -` - -func (q *Queries) GetAllEnetpulseFixtures(ctx context.Context) ([]EnetpulseFixture, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulseFixtures) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseFixture - for rows.Next() { - var i EnetpulseFixture - if err := rows.Scan( - &i.ID, - &i.FixtureID, - &i.Name, - &i.SportFk, - &i.TournamentFk, - &i.TournamentTemplateFk, - &i.TournamentName, - &i.TournamentTemplateName, - &i.SportName, - &i.Gender, - &i.StartDate, - &i.StatusType, - &i.StatusDescFk, - &i.RoundTypeFk, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllEnetpulseOutcomeTypes = `-- name: GetAllEnetpulseOutcomeTypes :many -SELECT id, outcome_type_id, name, description, updates_count, last_updated_at, created_at, updated_at -FROM enetpulse_outcome_types -ORDER BY created_at DESC -` - -func (q *Queries) GetAllEnetpulseOutcomeTypes(ctx context.Context) ([]EnetpulseOutcomeType, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulseOutcomeTypes) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseOutcomeType - for rows.Next() { - var i EnetpulseOutcomeType - if err := rows.Scan( - &i.ID, - &i.OutcomeTypeID, - &i.Name, - &i.Description, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllEnetpulsePreodds = `-- name: GetAllEnetpulsePreodds :many -SELECT id, preodds_id, event_fk, outcome_type_fk, outcome_scope_fk, outcome_subtype_fk, event_participant_number, iparam, iparam2, dparam, dparam2, sparam, updates_count, last_updated_at, created_at, updated_at -FROM enetpulse_preodds -ORDER BY created_at DESC -` - -func (q *Queries) GetAllEnetpulsePreodds(ctx context.Context) ([]EnetpulsePreodd, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulsePreodds) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulsePreodd - for rows.Next() { - var i EnetpulsePreodd - if err := rows.Scan( - &i.ID, - &i.PreoddsID, - &i.EventFk, - &i.OutcomeTypeFk, - &i.OutcomeScopeFk, - &i.OutcomeSubtypeFk, - &i.EventParticipantNumber, - &i.Iparam, - &i.Iparam2, - &i.Dparam, - &i.Dparam2, - &i.Sparam, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllEnetpulsePreoddsBettingOffers = `-- name: GetAllEnetpulsePreoddsBettingOffers :many -SELECT id, bettingoffer_id, preodds_fk, bettingoffer_status_fk, odds_provider_fk, odds, odds_old, active, coupon_key, updates_count, last_updated_at, created_at, updated_at -FROM enetpulse_preodds_bettingoffers -ORDER BY created_at DESC -` - -func (q *Queries) GetAllEnetpulsePreoddsBettingOffers(ctx context.Context) ([]EnetpulsePreoddsBettingoffer, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulsePreoddsBettingOffers) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulsePreoddsBettingoffer - for rows.Next() { - var i EnetpulsePreoddsBettingoffer - if err := rows.Scan( - &i.ID, - &i.BettingofferID, - &i.PreoddsFk, - &i.BettingofferStatusFk, - &i.OddsProviderFk, - &i.Odds, - &i.OddsOld, - &i.Active, - &i.CouponKey, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllEnetpulsePreoddsWithBettingOffers = `-- name: GetAllEnetpulsePreoddsWithBettingOffers :many -SELECT - p.id AS preodds_db_id, - p.preodds_id, - p.event_fk, - p.outcome_type_fk, - p.outcome_scope_fk, - p.outcome_subtype_fk, - p.event_participant_number, - p.iparam, - p.iparam2, - p.dparam, - p.dparam2, - p.sparam, - p.updates_count AS preodds_updates_count, - p.last_updated_at AS preodds_last_updated_at, - p.created_at AS preodds_created_at, - p.updated_at AS preodds_updated_at, - - -- Betting offer fields - bo.id AS bettingoffer_db_id, - bo.bettingoffer_id, - bo.preodds_fk, -- ✅ ensure alias matches struct field - bo.bettingoffer_status_fk, - bo.odds_provider_fk, - bo.odds, - bo.odds_old, - bo.active, - bo.coupon_key, - bo.updates_count AS bettingoffer_updates_count, - bo.last_updated_at AS bettingoffer_last_updated_at, - bo.created_at AS bettingoffer_created_at, - bo.updated_at AS bettingoffer_updated_at - -FROM enetpulse_preodds p -LEFT JOIN enetpulse_preodds_bettingoffers bo - ON bo.preodds_fk = p.preodds_id -ORDER BY p.created_at DESC, bo.created_at DESC -` - -type GetAllEnetpulsePreoddsWithBettingOffersRow struct { - PreoddsDbID int64 `json:"preodds_db_id"` - PreoddsID string `json:"preodds_id"` - EventFk int64 `json:"event_fk"` - OutcomeTypeFk pgtype.Int4 `json:"outcome_type_fk"` - OutcomeScopeFk pgtype.Int4 `json:"outcome_scope_fk"` - OutcomeSubtypeFk pgtype.Int4 `json:"outcome_subtype_fk"` - EventParticipantNumber pgtype.Int4 `json:"event_participant_number"` - Iparam pgtype.Text `json:"iparam"` - Iparam2 pgtype.Text `json:"iparam2"` - Dparam pgtype.Text `json:"dparam"` - Dparam2 pgtype.Text `json:"dparam2"` - Sparam pgtype.Text `json:"sparam"` - PreoddsUpdatesCount pgtype.Int4 `json:"preodds_updates_count"` - PreoddsLastUpdatedAt pgtype.Timestamptz `json:"preodds_last_updated_at"` - PreoddsCreatedAt pgtype.Timestamptz `json:"preodds_created_at"` - PreoddsUpdatedAt pgtype.Timestamptz `json:"preodds_updated_at"` - BettingofferDbID pgtype.Int8 `json:"bettingoffer_db_id"` - BettingofferID pgtype.Text `json:"bettingoffer_id"` - PreoddsFk pgtype.Text `json:"preodds_fk"` - BettingofferStatusFk pgtype.Int4 `json:"bettingoffer_status_fk"` - OddsProviderFk pgtype.Int4 `json:"odds_provider_fk"` - Odds pgtype.Numeric `json:"odds"` - OddsOld pgtype.Numeric `json:"odds_old"` - Active pgtype.Bool `json:"active"` - CouponKey pgtype.Text `json:"coupon_key"` - BettingofferUpdatesCount pgtype.Int4 `json:"bettingoffer_updates_count"` - BettingofferLastUpdatedAt pgtype.Timestamptz `json:"bettingoffer_last_updated_at"` - BettingofferCreatedAt pgtype.Timestamptz `json:"bettingoffer_created_at"` - BettingofferUpdatedAt pgtype.Timestamptz `json:"bettingoffer_updated_at"` -} - -func (q *Queries) GetAllEnetpulsePreoddsWithBettingOffers(ctx context.Context) ([]GetAllEnetpulsePreoddsWithBettingOffersRow, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulsePreoddsWithBettingOffers) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetAllEnetpulsePreoddsWithBettingOffersRow - for rows.Next() { - var i GetAllEnetpulsePreoddsWithBettingOffersRow - if err := rows.Scan( - &i.PreoddsDbID, - &i.PreoddsID, - &i.EventFk, - &i.OutcomeTypeFk, - &i.OutcomeScopeFk, - &i.OutcomeSubtypeFk, - &i.EventParticipantNumber, - &i.Iparam, - &i.Iparam2, - &i.Dparam, - &i.Dparam2, - &i.Sparam, - &i.PreoddsUpdatesCount, - &i.PreoddsLastUpdatedAt, - &i.PreoddsCreatedAt, - &i.PreoddsUpdatedAt, - &i.BettingofferDbID, - &i.BettingofferID, - &i.PreoddsFk, - &i.BettingofferStatusFk, - &i.OddsProviderFk, - &i.Odds, - &i.OddsOld, - &i.Active, - &i.CouponKey, - &i.BettingofferUpdatesCount, - &i.BettingofferLastUpdatedAt, - &i.BettingofferCreatedAt, - &i.BettingofferUpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllEnetpulseResults = `-- name: GetAllEnetpulseResults :many -SELECT id, result_id, name, sport_fk, tournament_fk, tournament_template_fk, tournament_name, tournament_template_name, sport_name, start_date, status_type, status_desc_fk, round_type_fk, updates_count, last_updated_at, round, live, venue_name, livestats_plus, livestats_type, commentary, lineup_confirmed, verified, spectators, game_started, first_half_ended, second_half_started, second_half_ended, game_ended, created_at, updated_at -FROM enetpulse_results -ORDER BY created_at DESC -` - -func (q *Queries) GetAllEnetpulseResults(ctx context.Context) ([]EnetpulseResult, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulseResults) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseResult - for rows.Next() { - var i EnetpulseResult - if err := rows.Scan( - &i.ID, - &i.ResultID, - &i.Name, - &i.SportFk, - &i.TournamentFk, - &i.TournamentTemplateFk, - &i.TournamentName, - &i.TournamentTemplateName, - &i.SportName, - &i.StartDate, - &i.StatusType, - &i.StatusDescFk, - &i.RoundTypeFk, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Round, - &i.Live, - &i.VenueName, - &i.LivestatsPlus, - &i.LivestatsType, - &i.Commentary, - &i.LineupConfirmed, - &i.Verified, - &i.Spectators, - &i.GameStarted, - &i.FirstHalfEnded, - &i.SecondHalfStarted, - &i.SecondHalfEnded, - &i.GameEnded, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllEnetpulseSports = `-- name: GetAllEnetpulseSports :many -SELECT - id, - sport_id, - name, - updates_count, - last_updated_at, - status, - created_at, - updated_at -FROM enetpulse_sports -ORDER BY name -` - -func (q *Queries) GetAllEnetpulseSports(ctx context.Context) ([]EnetpulseSport, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulseSports) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseSport - for rows.Next() { - var i EnetpulseSport - if err := rows.Scan( - &i.ID, - &i.SportID, - &i.Name, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllEnetpulseTournamentStages = `-- name: GetAllEnetpulseTournamentStages :many -SELECT id, stage_id, name, tournament_fk, gender, country_fk, country_name, start_date, end_date, updates_count, last_updated_at, status, created_at, updated_at -FROM enetpulse_tournament_stages -ORDER BY created_at DESC -` - -func (q *Queries) GetAllEnetpulseTournamentStages(ctx context.Context) ([]EnetpulseTournamentStage, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulseTournamentStages) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseTournamentStage - for rows.Next() { - var i EnetpulseTournamentStage - if err := rows.Scan( - &i.ID, - &i.StageID, - &i.Name, - &i.TournamentFk, - &i.Gender, - &i.CountryFk, - &i.CountryName, - &i.StartDate, - &i.EndDate, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllEnetpulseTournamentTemplates = `-- name: GetAllEnetpulseTournamentTemplates :many -SELECT - id, - template_id, - name, - sport_fk, - gender, - updates_count, - last_updated_at, - status, - created_at, - updated_at -FROM enetpulse_tournament_templates -ORDER BY name -` - -func (q *Queries) GetAllEnetpulseTournamentTemplates(ctx context.Context) ([]EnetpulseTournamentTemplate, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulseTournamentTemplates) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseTournamentTemplate - for rows.Next() { - var i EnetpulseTournamentTemplate - if err := rows.Scan( - &i.ID, - &i.TemplateID, - &i.Name, - &i.SportFk, - &i.Gender, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllEnetpulseTournaments = `-- name: GetAllEnetpulseTournaments :many -SELECT id, tournament_id, name, tournament_template_fk, updates_count, last_updated_at, status, created_at, updated_at -FROM enetpulse_tournaments -ORDER BY created_at DESC -` - -func (q *Queries) GetAllEnetpulseTournaments(ctx context.Context) ([]EnetpulseTournament, error) { - rows, err := q.db.Query(ctx, GetAllEnetpulseTournaments) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseTournament - for rows.Next() { - var i EnetpulseTournament - if err := rows.Scan( - &i.ID, - &i.TournamentID, - &i.Name, - &i.TournamentTemplateFk, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetEnetpulseResultParticipantsByResultFK = `-- name: GetEnetpulseResultParticipantsByResultFK :many -SELECT id, participant_map_id, result_fk, participant_fk, number, name, gender, type, country_fk, country_name, ordinary_time, running_score, halftime, final_result, last_updated_at, created_at -FROM enetpulse_result_participants -WHERE result_fk = $1 -ORDER BY created_at DESC -` - -func (q *Queries) GetEnetpulseResultParticipantsByResultFK(ctx context.Context, resultFk string) ([]EnetpulseResultParticipant, error) { - rows, err := q.db.Query(ctx, GetEnetpulseResultParticipantsByResultFK, resultFk) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseResultParticipant - for rows.Next() { - var i EnetpulseResultParticipant - if err := rows.Scan( - &i.ID, - &i.ParticipantMapID, - &i.ResultFk, - &i.ParticipantFk, - &i.Number, - &i.Name, - &i.Gender, - &i.Type, - &i.CountryFk, - &i.CountryName, - &i.OrdinaryTime, - &i.RunningScore, - &i.Halftime, - &i.FinalResult, - &i.LastUpdatedAt, - &i.CreatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetEnetpulseResultRefereesByResultFK = `-- name: GetEnetpulseResultRefereesByResultFK :many -SELECT id, result_fk, referee_fk, assistant1_referee_fk, assistant2_referee_fk, fourth_referee_fk, var1_referee_fk, var2_referee_fk, last_updated_at, created_at -FROM enetpulse_result_referees -WHERE result_fk = $1 -ORDER BY created_at DESC -` - -func (q *Queries) GetEnetpulseResultRefereesByResultFK(ctx context.Context, resultFk string) ([]EnetpulseResultReferee, error) { - rows, err := q.db.Query(ctx, GetEnetpulseResultRefereesByResultFK, resultFk) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseResultReferee - for rows.Next() { - var i EnetpulseResultReferee - if err := rows.Scan( - &i.ID, - &i.ResultFk, - &i.RefereeFk, - &i.Assistant1RefereeFk, - &i.Assistant2RefereeFk, - &i.FourthRefereeFk, - &i.Var1RefereeFk, - &i.Var2RefereeFk, - &i.LastUpdatedAt, - &i.CreatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetFixturesWithPreodds = `-- name: GetFixturesWithPreodds :many -SELECT - f.fixture_id AS id, - f.fixture_id AS fixture_id, - f.name AS fixture_name, - f.sport_fk, - f.tournament_fk, - f.tournament_template_fk, - f.start_date, - f.status_type, - f.status_desc_fk, - f.round_type_fk, - f.updates_count AS fixture_updates_count, - f.last_updated_at AS fixture_last_updated_at, - f.created_at AS fixture_created_at, - f.updated_at AS fixture_updated_at, - - -- Preodds fields - p.id AS preodds_db_id, - p.preodds_id, - p.event_fk, - p.outcome_type_fk, - p.outcome_scope_fk, - p.outcome_subtype_fk, - p.event_participant_number, - p.iparam, - p.iparam2, - p.dparam, - p.dparam2, - p.sparam, - p.updates_count AS preodds_updates_count, - p.last_updated_at AS preodds_last_updated_at, - p.created_at AS preodds_created_at, - p.updated_at AS preodds_updated_at - -FROM enetpulse_fixtures f -LEFT JOIN enetpulse_preodds p - ON p.event_fk = f.id -ORDER BY f.start_date DESC -` - -type GetFixturesWithPreoddsRow struct { - ID string `json:"id"` - FixtureID string `json:"fixture_id"` - FixtureName string `json:"fixture_name"` - SportFk string `json:"sport_fk"` - TournamentFk pgtype.Text `json:"tournament_fk"` - TournamentTemplateFk pgtype.Text `json:"tournament_template_fk"` - StartDate pgtype.Timestamptz `json:"start_date"` - StatusType pgtype.Text `json:"status_type"` - StatusDescFk pgtype.Text `json:"status_desc_fk"` - RoundTypeFk pgtype.Text `json:"round_type_fk"` - FixtureUpdatesCount pgtype.Int4 `json:"fixture_updates_count"` - FixtureLastUpdatedAt pgtype.Timestamptz `json:"fixture_last_updated_at"` - FixtureCreatedAt pgtype.Timestamptz `json:"fixture_created_at"` - FixtureUpdatedAt pgtype.Timestamptz `json:"fixture_updated_at"` - PreoddsDbID pgtype.Int8 `json:"preodds_db_id"` - PreoddsID pgtype.Text `json:"preodds_id"` - EventFk pgtype.Int8 `json:"event_fk"` - OutcomeTypeFk pgtype.Int4 `json:"outcome_type_fk"` - OutcomeScopeFk pgtype.Int4 `json:"outcome_scope_fk"` - OutcomeSubtypeFk pgtype.Int4 `json:"outcome_subtype_fk"` - EventParticipantNumber pgtype.Int4 `json:"event_participant_number"` - Iparam pgtype.Text `json:"iparam"` - Iparam2 pgtype.Text `json:"iparam2"` - Dparam pgtype.Text `json:"dparam"` - Dparam2 pgtype.Text `json:"dparam2"` - Sparam pgtype.Text `json:"sparam"` - PreoddsUpdatesCount pgtype.Int4 `json:"preodds_updates_count"` - PreoddsLastUpdatedAt pgtype.Timestamptz `json:"preodds_last_updated_at"` - PreoddsCreatedAt pgtype.Timestamptz `json:"preodds_created_at"` - PreoddsUpdatedAt pgtype.Timestamptz `json:"preodds_updated_at"` -} - -func (q *Queries) GetFixturesWithPreodds(ctx context.Context) ([]GetFixturesWithPreoddsRow, error) { - rows, err := q.db.Query(ctx, GetFixturesWithPreodds) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetFixturesWithPreoddsRow - for rows.Next() { - var i GetFixturesWithPreoddsRow - if err := rows.Scan( - &i.ID, - &i.FixtureID, - &i.FixtureName, - &i.SportFk, - &i.TournamentFk, - &i.TournamentTemplateFk, - &i.StartDate, - &i.StatusType, - &i.StatusDescFk, - &i.RoundTypeFk, - &i.FixtureUpdatesCount, - &i.FixtureLastUpdatedAt, - &i.FixtureCreatedAt, - &i.FixtureUpdatedAt, - &i.PreoddsDbID, - &i.PreoddsID, - &i.EventFk, - &i.OutcomeTypeFk, - &i.OutcomeScopeFk, - &i.OutcomeSubtypeFk, - &i.EventParticipantNumber, - &i.Iparam, - &i.Iparam2, - &i.Dparam, - &i.Dparam2, - &i.Sparam, - &i.PreoddsUpdatesCount, - &i.PreoddsLastUpdatedAt, - &i.PreoddsCreatedAt, - &i.PreoddsUpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetTournamentStagesByTournamentFK = `-- name: GetTournamentStagesByTournamentFK :many -SELECT id, stage_id, name, tournament_fk, gender, country_fk, country_name, start_date, end_date, updates_count, last_updated_at, status, created_at, updated_at -FROM enetpulse_tournament_stages -WHERE tournament_fk = $1 -ORDER BY created_at DESC -` - -func (q *Queries) GetTournamentStagesByTournamentFK(ctx context.Context, tournamentFk string) ([]EnetpulseTournamentStage, error) { - rows, err := q.db.Query(ctx, GetTournamentStagesByTournamentFK, tournamentFk) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EnetpulseTournamentStage - for rows.Next() { - var i EnetpulseTournamentStage - if err := rows.Scan( - &i.ID, - &i.StageID, - &i.Name, - &i.TournamentFk, - &i.Gender, - &i.CountryFk, - &i.CountryName, - &i.StartDate, - &i.EndDate, - &i.UpdatesCount, - &i.LastUpdatedAt, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/gen/db/event_history.sql.go b/gen/db/event_history.sql.go deleted file mode 100644 index a4f1c2e..0000000 --- a/gen/db/event_history.sql.go +++ /dev/null @@ -1,133 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: event_history.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetAllEventHistory = `-- name: GetAllEventHistory :many -SELECT id, event_id, status, created_at -FROM event_history -WHERE ( - event_id = $1 - OR $1 IS NULL - ) - AND ( - created_at > $2 - OR $2 IS NULL - ) - AND ( - created_at < $3 - OR $3 IS NULL - ) -` - -type GetAllEventHistoryParams struct { - EventID pgtype.Int8 `json:"event_id"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetAllEventHistory(ctx context.Context, arg GetAllEventHistoryParams) ([]EventHistory, error) { - rows, err := q.db.Query(ctx, GetAllEventHistory, arg.EventID, arg.CreatedBefore, arg.CreatedAfter) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EventHistory - for rows.Next() { - var i EventHistory - if err := rows.Scan( - &i.ID, - &i.EventID, - &i.Status, - &i.CreatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetInitialEventPerDay = `-- name: GetInitialEventPerDay :many -SELECT DISTINCT ON (DATE_TRUNC('day', created_at)) id, event_id, status, created_at -FROM event_history -WHERE ( - event_id = $1 - OR $1 IS NULL - ) - AND ( - created_at > $2 - OR $2 IS NULL - ) - AND ( - created_at < $3 - OR $3 IS NULL - ) -ORDER BY DATE_TRUNC('day', created_at), - created_at ASC -` - -type GetInitialEventPerDayParams struct { - EventID pgtype.Int8 `json:"event_id"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetInitialEventPerDay(ctx context.Context, arg GetInitialEventPerDayParams) ([]EventHistory, error) { - rows, err := q.db.Query(ctx, GetInitialEventPerDay, arg.EventID, arg.CreatedBefore, arg.CreatedAfter) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EventHistory - for rows.Next() { - var i EventHistory - if err := rows.Scan( - &i.ID, - &i.EventID, - &i.Status, - &i.CreatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const InsertEventHistory = `-- name: InsertEventHistory :one -INSERT INTO event_history (event_id, status) -VALUES ($1, $2) -RETURNING id, event_id, status, created_at -` - -type InsertEventHistoryParams struct { - EventID int64 `json:"event_id"` - Status string `json:"status"` -} - -func (q *Queries) InsertEventHistory(ctx context.Context, arg InsertEventHistoryParams) (EventHistory, error) { - row := q.db.QueryRow(ctx, InsertEventHistory, arg.EventID, arg.Status) - var i EventHistory - err := row.Scan( - &i.ID, - &i.EventID, - &i.Status, - &i.CreatedAt, - ) - return i, err -} diff --git a/gen/db/events.sql.go b/gen/db/events.sql.go deleted file mode 100644 index 0621dd5..0000000 --- a/gen/db/events.sql.go +++ /dev/null @@ -1,545 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: events.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const DeleteEvent = `-- name: DeleteEvent :exec -DELETE FROM events -WHERE id = $1 -` - -func (q *Queries) DeleteEvent(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteEvent, id) - return err -} - -const GetAllEvents = `-- name: GetAllEvents :many -SELECT id, source_event_id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, updated_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc, total_outcomes, number_of_bets, total_amount, avg_bet_amount, total_potential_winnings -FROM event_detailed -WHERE ( - is_live = $1 - OR $1 IS NULL - ) - AND ( - status = $2 - OR $2 IS NULL - ) - AND ( - league_id = $3 - OR $3 IS NULL - ) - AND ( - sport_id = $4 - OR $4 IS NULL - ) - AND ( - match_name ILIKE '%' || $5 || '%' - OR league_name ILIKE '%' || $5 || '%' - OR $5 IS NULL - ) - AND ( - start_time < $6 - OR $6 IS NULL - ) - AND ( - start_time > $7 - OR $7 IS NULL - ) - AND ( - league_cc = $8 - OR $8 IS NULL - ) - AND ( - source = $9 - OR $9 IS NULL - ) -ORDER BY start_time ASC -LIMIT $11 OFFSET $10 -` - -type GetAllEventsParams struct { - IsLive pgtype.Bool `json:"is_live"` - Status pgtype.Text `json:"status"` - LeagueID pgtype.Int8 `json:"league_id"` - SportID pgtype.Int4 `json:"sport_id"` - Query pgtype.Text `json:"query"` - LastStartTime pgtype.Timestamp `json:"last_start_time"` - FirstStartTime pgtype.Timestamp `json:"first_start_time"` - CountryCode pgtype.Text `json:"country_code"` - Source pgtype.Text `json:"source"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -// TODO: Figure out how to make this code reusable. SQLC prohibits CTEs within multiple queries -func (q *Queries) GetAllEvents(ctx context.Context, arg GetAllEventsParams) ([]EventDetailed, error) { - rows, err := q.db.Query(ctx, GetAllEvents, - arg.IsLive, - arg.Status, - arg.LeagueID, - arg.SportID, - arg.Query, - arg.LastStartTime, - arg.FirstStartTime, - arg.CountryCode, - arg.Source, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EventDetailed - for rows.Next() { - var i EventDetailed - if err := rows.Scan( - &i.ID, - &i.SourceEventID, - &i.SportID, - &i.MatchName, - &i.HomeTeam, - &i.AwayTeam, - &i.HomeTeamID, - &i.AwayTeamID, - &i.HomeKitImage, - &i.AwayKitImage, - &i.LeagueID, - &i.LeagueName, - &i.StartTime, - &i.Score, - &i.MatchMinute, - &i.TimerStatus, - &i.AddedTime, - &i.MatchPeriod, - &i.IsLive, - &i.Status, - &i.FetchedAt, - &i.UpdatedAt, - &i.Source, - &i.DefaultIsActive, - &i.DefaultIsFeatured, - &i.DefaultWinningUpperLimit, - &i.IsMonitored, - &i.LeagueCc, - &i.TotalOutcomes, - &i.NumberOfBets, - &i.TotalAmount, - &i.AvgBetAmount, - &i.TotalPotentialWinnings, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetEventByID = `-- name: GetEventByID :one -SELECT id, source_event_id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, updated_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc, total_outcomes, number_of_bets, total_amount, avg_bet_amount, total_potential_winnings -FROM event_detailed -WHERE id = $1 -LIMIT 1 -` - -func (q *Queries) GetEventByID(ctx context.Context, id int64) (EventDetailed, error) { - row := q.db.QueryRow(ctx, GetEventByID, id) - var i EventDetailed - err := row.Scan( - &i.ID, - &i.SourceEventID, - &i.SportID, - &i.MatchName, - &i.HomeTeam, - &i.AwayTeam, - &i.HomeTeamID, - &i.AwayTeamID, - &i.HomeKitImage, - &i.AwayKitImage, - &i.LeagueID, - &i.LeagueName, - &i.StartTime, - &i.Score, - &i.MatchMinute, - &i.TimerStatus, - &i.AddedTime, - &i.MatchPeriod, - &i.IsLive, - &i.Status, - &i.FetchedAt, - &i.UpdatedAt, - &i.Source, - &i.DefaultIsActive, - &i.DefaultIsFeatured, - &i.DefaultWinningUpperLimit, - &i.IsMonitored, - &i.LeagueCc, - &i.TotalOutcomes, - &i.NumberOfBets, - &i.TotalAmount, - &i.AvgBetAmount, - &i.TotalPotentialWinnings, - ) - return i, err -} - -const GetEventBySourceID = `-- name: GetEventBySourceID :one -SELECT id, source_event_id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, updated_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc, total_outcomes, number_of_bets, total_amount, avg_bet_amount, total_potential_winnings -FROM event_detailed -WHERE source_event_id = $1 - AND source = $2 -` - -type GetEventBySourceIDParams struct { - SourceEventID string `json:"source_event_id"` - Source string `json:"source"` -} - -func (q *Queries) GetEventBySourceID(ctx context.Context, arg GetEventBySourceIDParams) (EventDetailed, error) { - row := q.db.QueryRow(ctx, GetEventBySourceID, arg.SourceEventID, arg.Source) - var i EventDetailed - err := row.Scan( - &i.ID, - &i.SourceEventID, - &i.SportID, - &i.MatchName, - &i.HomeTeam, - &i.AwayTeam, - &i.HomeTeamID, - &i.AwayTeamID, - &i.HomeKitImage, - &i.AwayKitImage, - &i.LeagueID, - &i.LeagueName, - &i.StartTime, - &i.Score, - &i.MatchMinute, - &i.TimerStatus, - &i.AddedTime, - &i.MatchPeriod, - &i.IsLive, - &i.Status, - &i.FetchedAt, - &i.UpdatedAt, - &i.Source, - &i.DefaultIsActive, - &i.DefaultIsFeatured, - &i.DefaultWinningUpperLimit, - &i.IsMonitored, - &i.LeagueCc, - &i.TotalOutcomes, - &i.NumberOfBets, - &i.TotalAmount, - &i.AvgBetAmount, - &i.TotalPotentialWinnings, - ) - return i, err -} - -const GetSportAndLeagueIDs = `-- name: GetSportAndLeagueIDs :one -SELECT sport_id, - league_id -FROM events -WHERE id = $1 -` - -type GetSportAndLeagueIDsRow struct { - SportID int32 `json:"sport_id"` - LeagueID int64 `json:"league_id"` -} - -func (q *Queries) GetSportAndLeagueIDs(ctx context.Context, id int64) (GetSportAndLeagueIDsRow, error) { - row := q.db.QueryRow(ctx, GetSportAndLeagueIDs, id) - var i GetSportAndLeagueIDsRow - err := row.Scan(&i.SportID, &i.LeagueID) - return i, err -} - -const GetTotalEvents = `-- name: GetTotalEvents :one -SELECT COUNT(*) -FROM event_detailed -WHERE ( - is_live = $1 - OR $1 IS NULL - ) - AND ( - status = $2 - OR $2 IS NULL - ) - AND ( - league_id = $3 - OR $3 IS NULL - ) - AND ( - sport_id = $4 - OR $4 IS NULL - ) - AND ( - match_name ILIKE '%' || $5 || '%' - OR league_name ILIKE '%' || $5 || '%' - OR $5 IS NULL - ) - AND ( - start_time < $6 - OR $6 IS NULL - ) - AND ( - start_time > $7 - OR $7 IS NULL - ) - AND ( - league_cc = $8 - OR $8 IS NULL - ) - AND ( - source = $9 - OR $9 IS NULL - ) -` - -type GetTotalEventsParams struct { - IsLive pgtype.Bool `json:"is_live"` - Status pgtype.Text `json:"status"` - LeagueID pgtype.Int8 `json:"league_id"` - SportID pgtype.Int4 `json:"sport_id"` - Query pgtype.Text `json:"query"` - LastStartTime pgtype.Timestamp `json:"last_start_time"` - FirstStartTime pgtype.Timestamp `json:"first_start_time"` - CountryCode pgtype.Text `json:"country_code"` - Source pgtype.Text `json:"source"` -} - -func (q *Queries) GetTotalEvents(ctx context.Context, arg GetTotalEventsParams) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalEvents, - arg.IsLive, - arg.Status, - arg.LeagueID, - arg.SportID, - arg.Query, - arg.LastStartTime, - arg.FirstStartTime, - arg.CountryCode, - arg.Source, - ) - var count int64 - err := row.Scan(&count) - return count, err -} - -const InsertEvent = `-- name: InsertEvent :exec -INSERT INTO events ( - source_event_id, - sport_id, - match_name, - home_team, - away_team, - home_team_id, - away_team_id, - home_kit_image, - away_kit_image, - league_id, - league_name, - start_time, - is_live, - status, - source, - default_winning_upper_limit - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15, - $16 - ) ON CONFLICT (source_event_id, source) DO -UPDATE -SET sport_id = EXCLUDED.sport_id, - match_name = EXCLUDED.match_name, - home_team = EXCLUDED.home_team, - away_team = EXCLUDED.away_team, - home_team_id = EXCLUDED.home_team_id, - away_team_id = EXCLUDED.away_team_id, - home_kit_image = EXCLUDED.home_kit_image, - away_kit_image = EXCLUDED.away_kit_image, - league_id = EXCLUDED.league_id, - league_name = EXCLUDED.league_name, - start_time = EXCLUDED.start_time, - score = EXCLUDED.score, - match_minute = EXCLUDED.match_minute, - timer_status = EXCLUDED.timer_status, - added_time = EXCLUDED.added_time, - match_period = EXCLUDED.match_period, - is_live = EXCLUDED.is_live, - source = EXCLUDED.source, - default_winning_upper_limit = EXCLUDED.default_winning_upper_limit, - fetched_at = now() -` - -type InsertEventParams struct { - SourceEventID string `json:"source_event_id"` - SportID int32 `json:"sport_id"` - MatchName string `json:"match_name"` - HomeTeam string `json:"home_team"` - AwayTeam string `json:"away_team"` - HomeTeamID int64 `json:"home_team_id"` - AwayTeamID int64 `json:"away_team_id"` - HomeKitImage string `json:"home_kit_image"` - AwayKitImage string `json:"away_kit_image"` - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - StartTime pgtype.Timestamp `json:"start_time"` - IsLive bool `json:"is_live"` - Status string `json:"status"` - Source string `json:"source"` - DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` -} - -func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error { - _, err := q.db.Exec(ctx, InsertEvent, - arg.SourceEventID, - arg.SportID, - arg.MatchName, - arg.HomeTeam, - arg.AwayTeam, - arg.HomeTeamID, - arg.AwayTeamID, - arg.HomeKitImage, - arg.AwayKitImage, - arg.LeagueID, - arg.LeagueName, - arg.StartTime, - arg.IsLive, - arg.Status, - arg.Source, - arg.DefaultWinningUpperLimit, - ) - return err -} - -const IsEventMonitored = `-- name: IsEventMonitored :one -SELECT is_monitored -FROM events -WHERE id = $1 -` - -func (q *Queries) IsEventMonitored(ctx context.Context, id int64) (bool, error) { - row := q.db.QueryRow(ctx, IsEventMonitored, id) - var is_monitored bool - err := row.Scan(&is_monitored) - return is_monitored, err -} - -const ListLiveEvents = `-- name: ListLiveEvents :many -SELECT id -FROM event_detailed -WHERE is_live = true -` - -func (q *Queries) ListLiveEvents(ctx context.Context) ([]int64, error) { - rows, err := q.db.Query(ctx, ListLiveEvents) - if err != nil { - return nil, err - } - defer rows.Close() - var items []int64 - for rows.Next() { - var id int64 - if err := rows.Scan(&id); err != nil { - return nil, err - } - items = append(items, id) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateEventMonitored = `-- name: UpdateEventMonitored :exec -UPDATE events -SET is_monitored = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2 -` - -type UpdateEventMonitoredParams struct { - IsMonitored bool `json:"is_monitored"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateEventMonitored(ctx context.Context, arg UpdateEventMonitoredParams) error { - _, err := q.db.Exec(ctx, UpdateEventMonitored, arg.IsMonitored, arg.ID) - return err -} - -const UpdateGlobalEventSettings = `-- name: UpdateGlobalEventSettings :exec -UPDATE events -SET default_is_active = COALESCE($2, default_is_active), - default_is_featured = COALESCE( - $3, - default_is_featured - ), - default_winning_upper_limit = COALESCE( - $4, - default_winning_upper_limit - ), - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateGlobalEventSettingsParams struct { - ID int64 `json:"id"` - DefaultIsActive pgtype.Bool `json:"default_is_active"` - DefaultIsFeatured pgtype.Bool `json:"default_is_featured"` - DefaultWinningUpperLimit pgtype.Int8 `json:"default_winning_upper_limit"` -} - -func (q *Queries) UpdateGlobalEventSettings(ctx context.Context, arg UpdateGlobalEventSettingsParams) error { - _, err := q.db.Exec(ctx, UpdateGlobalEventSettings, - arg.ID, - arg.DefaultIsActive, - arg.DefaultIsFeatured, - arg.DefaultWinningUpperLimit, - ) - return err -} - -const UpdateMatchResult = `-- name: UpdateMatchResult :exec -UPDATE events -SET score = $1, - status = $2 -WHERE id = $3 -` - -type UpdateMatchResultParams struct { - Score pgtype.Text `json:"score"` - Status string `json:"status"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateMatchResult(ctx context.Context, arg UpdateMatchResultParams) error { - _, err := q.db.Exec(ctx, UpdateMatchResult, arg.Score, arg.Status, arg.ID) - return err -} diff --git a/gen/db/events_bet_stats.sql.go b/gen/db/events_bet_stats.sql.go deleted file mode 100644 index 10f44c2..0000000 --- a/gen/db/events_bet_stats.sql.go +++ /dev/null @@ -1,42 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: events_bet_stats.sql - -package dbgen - -import ( - "context" -) - -const UpdateEventBetStats = `-- name: UpdateEventBetStats :exec -INSERT INTO event_bet_stats ( - event_id, - number_of_bets, - total_amount, - avg_bet_amount, - total_potential_winnings, - updated_at - ) -SELECT bo.event_id, - COUNT(DISTINCT b.id) AS number_of_bets, - COALESCE(SUM(b.amount), 0) AS total_amount, - COALESCE(AVG(b.amount), 0) AS avg_bet_amount, - COALESCE(SUM(b.potential_win), 0) AS total_potential_winnings, - NOW() AS updated_at -FROM bet_outcomes bo - JOIN bets b ON bo.bet_id = b.id -GROUP BY bo.event_id ON CONFLICT (event_id) DO -UPDATE -SET number_of_bets = EXCLUDED.number_of_bets, - total_amount = EXCLUDED.total_amount, - avg_bet_amount = EXCLUDED.avg_bet_amount, - total_potential_winnings = EXCLUDED.total_potential_winnings, - updated_at = EXCLUDED.updated_at -` - -// Aggregate bet stats per event -func (q *Queries) UpdateEventBetStats(ctx context.Context) error { - _, err := q.db.Exec(ctx, UpdateEventBetStats) - return err -} diff --git a/gen/db/events_stat.sql.go b/gen/db/events_stat.sql.go deleted file mode 100644 index 816f493..0000000 --- a/gen/db/events_stat.sql.go +++ /dev/null @@ -1,259 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: events_stat.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetTotalEventStats = `-- name: GetTotalEventStats :one -SELECT COUNT(*) AS event_count, - COUNT(*) FILTER ( - WHERE events.default_is_active = TRUE - ) AS total_active_events, - COUNT(*) FILTER ( - WHERE events.default_is_active = FALSE - ) AS total_inactive_events, - COUNT(*) FILTER ( - WHERE events.default_is_featured = TRUE - ) AS total_featured_events, - COUNT(DISTINCT league_id) as total_leagues, - COUNT(*) FILTER ( - WHERE events.status = 'upcoming' - ) AS pending, - COUNT(*) FILTER ( - WHERE events.status = 'in_play' - ) AS in_play, - COUNT(*) FILTER ( - WHERE events.status = 'to_be_fixed' - ) AS to_be_fixed, - COUNT(*) FILTER ( - WHERE events.status = 'ended' - ) AS ended, - COUNT(*) FILTER ( - WHERE events.status = 'postponed' - ) AS postponed, - COUNT(*) FILTER ( - WHERE events.status = 'cancelled' - ) AS cancelled, - COUNT(*) FILTER ( - WHERE events.status = 'walkover' - ) AS walkover, - COUNT(*) FILTER ( - WHERE events.status = 'interrupted' - ) AS interrupted, - COUNT(*) FILTER ( - WHERE events.status = 'abandoned' - ) AS abandoned, - COUNT(*) FILTER ( - WHERE events.status = 'retired' - ) AS retired, - COUNT(*) FILTER ( - WHERE events.status = 'suspended' - ) AS suspended, - COUNT(*) FILTER ( - WHERE events.status = 'decided_by_fa' - ) AS decided_by_fa, - COUNT(*) FILTER ( - WHERE events.status = 'removed' - ) AS removed -FROM events -WHERE ( - events.league_id = $1 - OR $1 IS NULL - ) - AND ( - events.sport_id = $2 - OR $2 IS NULL - ) -` - -type GetTotalEventStatsParams struct { - LeagueID pgtype.Int8 `json:"league_id"` - SportID pgtype.Int4 `json:"sport_id"` -} - -type GetTotalEventStatsRow struct { - EventCount int64 `json:"event_count"` - TotalActiveEvents int64 `json:"total_active_events"` - TotalInactiveEvents int64 `json:"total_inactive_events"` - TotalFeaturedEvents int64 `json:"total_featured_events"` - TotalLeagues int64 `json:"total_leagues"` - Pending int64 `json:"pending"` - InPlay int64 `json:"in_play"` - ToBeFixed int64 `json:"to_be_fixed"` - Ended int64 `json:"ended"` - Postponed int64 `json:"postponed"` - Cancelled int64 `json:"cancelled"` - Walkover int64 `json:"walkover"` - Interrupted int64 `json:"interrupted"` - Abandoned int64 `json:"abandoned"` - Retired int64 `json:"retired"` - Suspended int64 `json:"suspended"` - DecidedByFa int64 `json:"decided_by_fa"` - Removed int64 `json:"removed"` -} - -func (q *Queries) GetTotalEventStats(ctx context.Context, arg GetTotalEventStatsParams) (GetTotalEventStatsRow, error) { - row := q.db.QueryRow(ctx, GetTotalEventStats, arg.LeagueID, arg.SportID) - var i GetTotalEventStatsRow - err := row.Scan( - &i.EventCount, - &i.TotalActiveEvents, - &i.TotalInactiveEvents, - &i.TotalFeaturedEvents, - &i.TotalLeagues, - &i.Pending, - &i.InPlay, - &i.ToBeFixed, - &i.Ended, - &i.Postponed, - &i.Cancelled, - &i.Walkover, - &i.Interrupted, - &i.Abandoned, - &i.Retired, - &i.Suspended, - &i.DecidedByFa, - &i.Removed, - ) - return i, err -} - -const GetTotalEventStatsByInterval = `-- name: GetTotalEventStatsByInterval :many -SELECT DATE_TRUNC($1, start_time)::timestamp AS date, - COUNT(*) AS event_count, - COUNT(*) FILTER ( - WHERE events.default_is_active = TRUE - ) AS total_active_events, - COUNT(*) FILTER ( - WHERE events.default_is_active = FALSE - ) AS total_inactive_events, - COUNT(*) FILTER ( - WHERE events.default_is_featured = TRUE - ) AS total_featured_events, - COUNT(DISTINCT league_id) as total_leagues, - COUNT(*) FILTER ( - WHERE events.status = 'upcoming' - ) AS pending, - COUNT(*) FILTER ( - WHERE events.status = 'in_play' - ) AS in_play, - COUNT(*) FILTER ( - WHERE events.status = 'to_be_fixed' - ) AS to_be_fixed, - COUNT(*) FILTER ( - WHERE events.status = 'ended' - ) AS ended, - COUNT(*) FILTER ( - WHERE events.status = 'postponed' - ) AS postponed, - COUNT(*) FILTER ( - WHERE events.status = 'cancelled' - ) AS cancelled, - COUNT(*) FILTER ( - WHERE events.status = 'walkover' - ) AS walkover, - COUNT(*) FILTER ( - WHERE events.status = 'interrupted' - ) AS interrupted, - COUNT(*) FILTER ( - WHERE events.status = 'abandoned' - ) AS abandoned, - COUNT(*) FILTER ( - WHERE events.status = 'retired' - ) AS retired, - COUNT(*) FILTER ( - WHERE events.status = 'suspended' - ) AS suspended, - COUNT(*) FILTER ( - WHERE events.status = 'decided_by_fa' - ) AS decided_by_fa, - COUNT(*) FILTER ( - WHERE events.status = 'removed' - ) AS removed -FROM events -WHERE ( - events.league_id = $2 - OR $2 IS NULL - ) - AND ( - events.sport_id = $3 - OR $3 IS NULL - ) -GROUP BY date -ORDER BY date -` - -type GetTotalEventStatsByIntervalParams struct { - Interval pgtype.Text `json:"interval"` - LeagueID pgtype.Int8 `json:"league_id"` - SportID pgtype.Int4 `json:"sport_id"` -} - -type GetTotalEventStatsByIntervalRow struct { - Date pgtype.Timestamp `json:"date"` - EventCount int64 `json:"event_count"` - TotalActiveEvents int64 `json:"total_active_events"` - TotalInactiveEvents int64 `json:"total_inactive_events"` - TotalFeaturedEvents int64 `json:"total_featured_events"` - TotalLeagues int64 `json:"total_leagues"` - Pending int64 `json:"pending"` - InPlay int64 `json:"in_play"` - ToBeFixed int64 `json:"to_be_fixed"` - Ended int64 `json:"ended"` - Postponed int64 `json:"postponed"` - Cancelled int64 `json:"cancelled"` - Walkover int64 `json:"walkover"` - Interrupted int64 `json:"interrupted"` - Abandoned int64 `json:"abandoned"` - Retired int64 `json:"retired"` - Suspended int64 `json:"suspended"` - DecidedByFa int64 `json:"decided_by_fa"` - Removed int64 `json:"removed"` -} - -func (q *Queries) GetTotalEventStatsByInterval(ctx context.Context, arg GetTotalEventStatsByIntervalParams) ([]GetTotalEventStatsByIntervalRow, error) { - rows, err := q.db.Query(ctx, GetTotalEventStatsByInterval, arg.Interval, arg.LeagueID, arg.SportID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetTotalEventStatsByIntervalRow - for rows.Next() { - var i GetTotalEventStatsByIntervalRow - if err := rows.Scan( - &i.Date, - &i.EventCount, - &i.TotalActiveEvents, - &i.TotalInactiveEvents, - &i.TotalFeaturedEvents, - &i.TotalLeagues, - &i.Pending, - &i.InPlay, - &i.ToBeFixed, - &i.Ended, - &i.Postponed, - &i.Cancelled, - &i.Walkover, - &i.Interrupted, - &i.Abandoned, - &i.Retired, - &i.Suspended, - &i.DecidedByFa, - &i.Removed, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/gen/db/events_with_settings.sql.go b/gen/db/events_with_settings.sql.go deleted file mode 100644 index c207058..0000000 --- a/gen/db/events_with_settings.sql.go +++ /dev/null @@ -1,469 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: events_with_settings.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetEventWithSettingByID = `-- name: GetEventWithSettingByID :one -SELECT e.id, e.source_event_id, e.sport_id, e.match_name, e.home_team, e.away_team, e.home_team_id, e.away_team_id, e.home_kit_image, e.away_kit_image, e.league_id, e.league_name, e.start_time, e.score, e.match_minute, e.timer_status, e.added_time, e.match_period, e.is_live, e.status, e.fetched_at, e.updated_at, e.source, e.default_is_active, e.default_is_featured, e.default_winning_upper_limit, e.is_monitored, - ces.company_id, - COALESCE(ces.is_active, e.default_is_active) AS is_active, - COALESCE(ces.is_featured, e.default_is_featured) AS is_featured, - COALESCE( - ces.winning_upper_limit, - e.default_winning_upper_limit - ) AS winning_upper_limit, - ces.updated_at, - l.country_code as league_cc, - COALESCE(om.total_outcomes, 0) AS total_outcomes, - COALESCE(ebs.number_of_bets, 0) AS number_of_bets, - COALESCE(ebs.total_amount, 0) AS total_amount, - COALESCE(ebs.avg_bet_amount, 0) AS avg_bet_amount, - COALESCE(ebs.total_potential_winnings, 0) AS total_potential_winnings -FROM events e - LEFT JOIN company_event_settings ces ON e.id = ces.event_id - AND ces.company_id = $2 - LEFT JOIN event_bet_stats ebs ON ebs.event_id = e.id - JOIN leagues l ON l.id = e.league_id - LEFT JOIN ( - SELECT event_id, - SUM(number_of_outcomes) AS total_outcomes - FROM odds_market - GROUP BY event_id - ) om ON om.event_id = e.id -WHERE e.id = $1 -LIMIT 1 -` - -type GetEventWithSettingByIDParams struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` -} - -type GetEventWithSettingByIDRow struct { - ID int64 `json:"id"` - SourceEventID string `json:"source_event_id"` - SportID int32 `json:"sport_id"` - MatchName string `json:"match_name"` - HomeTeam string `json:"home_team"` - AwayTeam string `json:"away_team"` - HomeTeamID int64 `json:"home_team_id"` - AwayTeamID int64 `json:"away_team_id"` - HomeKitImage string `json:"home_kit_image"` - AwayKitImage string `json:"away_kit_image"` - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - StartTime pgtype.Timestamp `json:"start_time"` - Score pgtype.Text `json:"score"` - MatchMinute pgtype.Int4 `json:"match_minute"` - TimerStatus pgtype.Text `json:"timer_status"` - AddedTime pgtype.Int4 `json:"added_time"` - MatchPeriod pgtype.Int4 `json:"match_period"` - IsLive bool `json:"is_live"` - Status string `json:"status"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - Source string `json:"source"` - DefaultIsActive bool `json:"default_is_active"` - DefaultIsFeatured bool `json:"default_is_featured"` - DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` - IsMonitored bool `json:"is_monitored"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsFeatured bool `json:"is_featured"` - WinningUpperLimit int64 `json:"winning_upper_limit"` - UpdatedAt_2 pgtype.Timestamp `json:"updated_at_2"` - LeagueCc pgtype.Text `json:"league_cc"` - TotalOutcomes int64 `json:"total_outcomes"` - NumberOfBets int64 `json:"number_of_bets"` - TotalAmount int64 `json:"total_amount"` - AvgBetAmount float64 `json:"avg_bet_amount"` - TotalPotentialWinnings int64 `json:"total_potential_winnings"` -} - -func (q *Queries) GetEventWithSettingByID(ctx context.Context, arg GetEventWithSettingByIDParams) (GetEventWithSettingByIDRow, error) { - row := q.db.QueryRow(ctx, GetEventWithSettingByID, arg.ID, arg.CompanyID) - var i GetEventWithSettingByIDRow - err := row.Scan( - &i.ID, - &i.SourceEventID, - &i.SportID, - &i.MatchName, - &i.HomeTeam, - &i.AwayTeam, - &i.HomeTeamID, - &i.AwayTeamID, - &i.HomeKitImage, - &i.AwayKitImage, - &i.LeagueID, - &i.LeagueName, - &i.StartTime, - &i.Score, - &i.MatchMinute, - &i.TimerStatus, - &i.AddedTime, - &i.MatchPeriod, - &i.IsLive, - &i.Status, - &i.FetchedAt, - &i.UpdatedAt, - &i.Source, - &i.DefaultIsActive, - &i.DefaultIsFeatured, - &i.DefaultWinningUpperLimit, - &i.IsMonitored, - &i.CompanyID, - &i.IsActive, - &i.IsFeatured, - &i.WinningUpperLimit, - &i.UpdatedAt_2, - &i.LeagueCc, - &i.TotalOutcomes, - &i.NumberOfBets, - &i.TotalAmount, - &i.AvgBetAmount, - &i.TotalPotentialWinnings, - ) - return i, err -} - -const GetEventsWithSettings = `-- name: GetEventsWithSettings :many -SELECT e.id, e.source_event_id, e.sport_id, e.match_name, e.home_team, e.away_team, e.home_team_id, e.away_team_id, e.home_kit_image, e.away_kit_image, e.league_id, e.league_name, e.start_time, e.score, e.match_minute, e.timer_status, e.added_time, e.match_period, e.is_live, e.status, e.fetched_at, e.updated_at, e.source, e.default_is_active, e.default_is_featured, e.default_winning_upper_limit, e.is_monitored, - ces.company_id, - COALESCE(ces.is_active, e.default_is_active) AS is_active, - COALESCE(ces.is_featured, e.default_is_featured) AS is_featured, - COALESCE( - ces.winning_upper_limit, - e.default_winning_upper_limit - ) AS winning_upper_limit, - ces.updated_at, - l.country_code as league_cc, - COALESCE(om.total_outcomes, 0) AS total_outcomes, - COALESCE(ebs.number_of_bets, 0) AS number_of_bets, - COALESCE(ebs.total_amount, 0) AS total_amount, - COALESCE(ebs.avg_bet_amount, 0) AS avg_bet_amount, - COALESCE(ebs.total_potential_winnings, 0) AS total_potential_winnings -FROM events e - LEFT JOIN company_event_settings ces ON e.id = ces.event_id - AND ces.company_id = $1 - LEFT JOIN event_bet_stats ebs ON ebs.event_id = e.id - JOIN leagues l ON l.id = e.league_id - LEFT JOIN ( - SELECT event_id, - SUM(number_of_outcomes) AS total_outcomes - FROM odds_market - GROUP BY event_id - ) om ON om.event_id = e.id -WHERE ( - is_live = $2 - OR $2 IS NULL - ) - AND ( - status = $3 - OR $3 IS NULL - ) - AND( - league_id = $4 - OR $4 IS NULL - ) - AND ( - e.sport_id = $5 - OR $5 IS NULL - ) - AND ( - match_name ILIKE '%' || $6 || '%' - OR league_name ILIKE '%' || $6 || '%' - OR $6 IS NULL - ) - AND ( - start_time < $7 - OR $7 IS NULL - ) - AND ( - start_time > $8 - OR $8 IS NULL - ) - AND ( - l.country_code = $9 - OR $9 IS NULL - ) - AND ( - ces.is_featured = $10 - OR e.default_is_featured = $10 - OR $10 IS NULL - ) - AND ( - ces.is_active = $11 - OR e.default_is_active = $11 - OR $11 IS NULL - ) - AND ( - source = $12 - OR $12 IS NULL - ) -ORDER BY start_time ASC -LIMIT $14 OFFSET $13 -` - -type GetEventsWithSettingsParams struct { - CompanyID int64 `json:"company_id"` - IsLive pgtype.Bool `json:"is_live"` - Status pgtype.Text `json:"status"` - LeagueID pgtype.Int8 `json:"league_id"` - SportID pgtype.Int4 `json:"sport_id"` - Query pgtype.Text `json:"query"` - LastStartTime pgtype.Timestamp `json:"last_start_time"` - FirstStartTime pgtype.Timestamp `json:"first_start_time"` - CountryCode pgtype.Text `json:"country_code"` - IsFeatured pgtype.Bool `json:"is_featured"` - IsActive pgtype.Bool `json:"is_active"` - Source pgtype.Text `json:"source"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -type GetEventsWithSettingsRow struct { - ID int64 `json:"id"` - SourceEventID string `json:"source_event_id"` - SportID int32 `json:"sport_id"` - MatchName string `json:"match_name"` - HomeTeam string `json:"home_team"` - AwayTeam string `json:"away_team"` - HomeTeamID int64 `json:"home_team_id"` - AwayTeamID int64 `json:"away_team_id"` - HomeKitImage string `json:"home_kit_image"` - AwayKitImage string `json:"away_kit_image"` - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - StartTime pgtype.Timestamp `json:"start_time"` - Score pgtype.Text `json:"score"` - MatchMinute pgtype.Int4 `json:"match_minute"` - TimerStatus pgtype.Text `json:"timer_status"` - AddedTime pgtype.Int4 `json:"added_time"` - MatchPeriod pgtype.Int4 `json:"match_period"` - IsLive bool `json:"is_live"` - Status string `json:"status"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - Source string `json:"source"` - DefaultIsActive bool `json:"default_is_active"` - DefaultIsFeatured bool `json:"default_is_featured"` - DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` - IsMonitored bool `json:"is_monitored"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsFeatured bool `json:"is_featured"` - WinningUpperLimit int64 `json:"winning_upper_limit"` - UpdatedAt_2 pgtype.Timestamp `json:"updated_at_2"` - LeagueCc pgtype.Text `json:"league_cc"` - TotalOutcomes int64 `json:"total_outcomes"` - NumberOfBets int64 `json:"number_of_bets"` - TotalAmount int64 `json:"total_amount"` - AvgBetAmount float64 `json:"avg_bet_amount"` - TotalPotentialWinnings int64 `json:"total_potential_winnings"` -} - -func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSettingsParams) ([]GetEventsWithSettingsRow, error) { - rows, err := q.db.Query(ctx, GetEventsWithSettings, - arg.CompanyID, - arg.IsLive, - arg.Status, - arg.LeagueID, - arg.SportID, - arg.Query, - arg.LastStartTime, - arg.FirstStartTime, - arg.CountryCode, - arg.IsFeatured, - arg.IsActive, - arg.Source, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetEventsWithSettingsRow - for rows.Next() { - var i GetEventsWithSettingsRow - if err := rows.Scan( - &i.ID, - &i.SourceEventID, - &i.SportID, - &i.MatchName, - &i.HomeTeam, - &i.AwayTeam, - &i.HomeTeamID, - &i.AwayTeamID, - &i.HomeKitImage, - &i.AwayKitImage, - &i.LeagueID, - &i.LeagueName, - &i.StartTime, - &i.Score, - &i.MatchMinute, - &i.TimerStatus, - &i.AddedTime, - &i.MatchPeriod, - &i.IsLive, - &i.Status, - &i.FetchedAt, - &i.UpdatedAt, - &i.Source, - &i.DefaultIsActive, - &i.DefaultIsFeatured, - &i.DefaultWinningUpperLimit, - &i.IsMonitored, - &i.CompanyID, - &i.IsActive, - &i.IsFeatured, - &i.WinningUpperLimit, - &i.UpdatedAt_2, - &i.LeagueCc, - &i.TotalOutcomes, - &i.NumberOfBets, - &i.TotalAmount, - &i.AvgBetAmount, - &i.TotalPotentialWinnings, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetTotalCompanyEvents = `-- name: GetTotalCompanyEvents :one -SELECT COUNT(*) -FROM events e - LEFT JOIN company_event_settings ces ON e.id = ces.event_id - AND ces.company_id = $1 - JOIN leagues l ON l.id = e.league_id -WHERE ( - is_live = $2 - OR $2 IS NULL - ) - AND ( - status = $3 - OR $3 IS NULL - ) - AND( - league_id = $4 - OR $4 IS NULL - ) - AND ( - e.sport_id = $5 - OR $5 IS NULL - ) - AND ( - match_name ILIKE '%' || $6 || '%' - OR league_name ILIKE '%' || $6 || '%' - OR $6 IS NULL - ) - AND ( - start_time < $7 - OR $7 IS NULL - ) - AND ( - start_time > $8 - OR $8 IS NULL - ) - AND ( - l.country_code = $9 - OR $9 IS NULL - ) - AND ( - ces.is_featured = $10 - OR e.default_is_featured = $10 - OR $10 IS NULL - ) - AND ( - ces.is_active = $11 - OR e.default_is_active = $11 - OR $11 IS NULL - ) - AND ( - source = $12 - OR $12 IS NULL - ) -` - -type GetTotalCompanyEventsParams struct { - CompanyID int64 `json:"company_id"` - IsLive pgtype.Bool `json:"is_live"` - Status pgtype.Text `json:"status"` - LeagueID pgtype.Int8 `json:"league_id"` - SportID pgtype.Int4 `json:"sport_id"` - Query pgtype.Text `json:"query"` - LastStartTime pgtype.Timestamp `json:"last_start_time"` - FirstStartTime pgtype.Timestamp `json:"first_start_time"` - CountryCode pgtype.Text `json:"country_code"` - IsFeatured pgtype.Bool `json:"is_featured"` - IsActive pgtype.Bool `json:"is_active"` - Source pgtype.Text `json:"source"` -} - -func (q *Queries) GetTotalCompanyEvents(ctx context.Context, arg GetTotalCompanyEventsParams) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalCompanyEvents, - arg.CompanyID, - arg.IsLive, - arg.Status, - arg.LeagueID, - arg.SportID, - arg.Query, - arg.LastStartTime, - arg.FirstStartTime, - arg.CountryCode, - arg.IsFeatured, - arg.IsActive, - arg.Source, - ) - var count int64 - err := row.Scan(&count) - return count, err -} - -const SaveTenantEventSettings = `-- name: SaveTenantEventSettings :exec -INSERT INTO company_event_settings ( - company_id, - event_id, - is_active, - is_featured, - winning_upper_limit - ) -VALUES ($1, $2, $3, $4, $5) ON CONFLICT(company_id, event_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - is_featured = EXCLUDED.is_featured, - winning_upper_limit = EXCLUDED.winning_upper_limit -` - -type SaveTenantEventSettingsParams struct { - CompanyID int64 `json:"company_id"` - EventID int64 `json:"event_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` - WinningUpperLimit pgtype.Int8 `json:"winning_upper_limit"` -} - -func (q *Queries) SaveTenantEventSettings(ctx context.Context, arg SaveTenantEventSettingsParams) error { - _, err := q.db.Exec(ctx, SaveTenantEventSettings, - arg.CompanyID, - arg.EventID, - arg.IsActive, - arg.IsFeatured, - arg.WinningUpperLimit, - ) - return err -} diff --git a/gen/db/flags.sql.go b/gen/db/flags.sql.go deleted file mode 100644 index 653543f..0000000 --- a/gen/db/flags.sql.go +++ /dev/null @@ -1,42 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: flags.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateFlag = `-- name: CreateFlag :one -INSERT INTO flags ( - bet_id, - odds_market_id, - reason -) VALUES ( - $1, $2, $3 -) RETURNING id, bet_id, odds_market_id, reason, flagged_at, resolved -` - -type CreateFlagParams struct { - BetID pgtype.Int8 `json:"bet_id"` - OddsMarketID pgtype.Int8 `json:"odds_market_id"` - Reason pgtype.Text `json:"reason"` -} - -func (q *Queries) CreateFlag(ctx context.Context, arg CreateFlagParams) (Flag, error) { - row := q.db.QueryRow(ctx, CreateFlag, arg.BetID, arg.OddsMarketID, arg.Reason) - var i Flag - err := row.Scan( - &i.ID, - &i.BetID, - &i.OddsMarketID, - &i.Reason, - &i.FlaggedAt, - &i.Resolved, - ) - return i, err -} diff --git a/gen/db/institutions.sql.go b/gen/db/institutions.sql.go deleted file mode 100644 index 324ac3e..0000000 --- a/gen/db/institutions.sql.go +++ /dev/null @@ -1,301 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: institutions.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CountBanks = `-- name: CountBanks :one -SELECT COUNT(*) -FROM banks -WHERE ( - country_id = $1 - OR $1 IS NULL - ) - AND ( - is_active = $2 - OR $2 IS NULL - ) - AND ( - name ILIKE '%' || $3 || '%' - OR code ILIKE '%' || $3 || '%' - OR $3 IS NULL - ) -` - -type CountBanksParams struct { - CountryID int32 `json:"country_id"` - IsActive int32 `json:"is_active"` - Column3 pgtype.Text `json:"column_3"` -} - -func (q *Queries) CountBanks(ctx context.Context, arg CountBanksParams) (int64, error) { - row := q.db.QueryRow(ctx, CountBanks, arg.CountryID, arg.IsActive, arg.Column3) - var count int64 - err := row.Scan(&count) - return count, err -} - -const CreateBank = `-- name: CreateBank :one -INSERT INTO banks ( - slug, - swift, - name, - acct_length, - country_id, - is_mobilemoney, - is_active, - is_rtgs, - active, - is_24hrs, - created_at, - updated_at, - currency, - bank_logo -) -VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, $11, $12 -) -RETURNING id, slug, swift, name, acct_length, country_id, is_mobilemoney, is_active, is_rtgs, active, is_24hrs, created_at, updated_at, currency, bank_logo -` - -type CreateBankParams struct { - Slug string `json:"slug"` - Swift string `json:"swift"` - Name string `json:"name"` - AcctLength int32 `json:"acct_length"` - CountryID int32 `json:"country_id"` - IsMobilemoney pgtype.Int4 `json:"is_mobilemoney"` - IsActive int32 `json:"is_active"` - IsRtgs int32 `json:"is_rtgs"` - Active int32 `json:"active"` - Is24hrs pgtype.Int4 `json:"is_24hrs"` - Currency string `json:"currency"` - BankLogo pgtype.Text `json:"bank_logo"` -} - -func (q *Queries) CreateBank(ctx context.Context, arg CreateBankParams) (Bank, error) { - row := q.db.QueryRow(ctx, CreateBank, - arg.Slug, - arg.Swift, - arg.Name, - arg.AcctLength, - arg.CountryID, - arg.IsMobilemoney, - arg.IsActive, - arg.IsRtgs, - arg.Active, - arg.Is24hrs, - arg.Currency, - arg.BankLogo, - ) - var i Bank - err := row.Scan( - &i.ID, - &i.Slug, - &i.Swift, - &i.Name, - &i.AcctLength, - &i.CountryID, - &i.IsMobilemoney, - &i.IsActive, - &i.IsRtgs, - &i.Active, - &i.Is24hrs, - &i.CreatedAt, - &i.UpdatedAt, - &i.Currency, - &i.BankLogo, - ) - return i, err -} - -const DeleteBank = `-- name: DeleteBank :exec -DELETE FROM banks -WHERE id = $1 -` - -func (q *Queries) DeleteBank(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteBank, id) - return err -} - -const GetAllBanks = `-- name: GetAllBanks :many -SELECT id, slug, swift, name, acct_length, country_id, is_mobilemoney, is_active, is_rtgs, active, is_24hrs, created_at, updated_at, currency, bank_logo -FROM banks -WHERE ( - country_id = $1 - OR $1 IS NULL - ) - AND ( - is_active = $2 - OR $2 IS NULL - ) - AND ( - name ILIKE '%' || $3 || '%' - OR $3 IS NULL - ) - AND ( - code ILIKE '%' || $3 || '%' - OR $3 IS NULL - ) -ORDER BY name ASC -LIMIT $5 OFFSET $4 -` - -type GetAllBanksParams struct { - CountryID pgtype.Int4 `json:"country_id"` - IsActive pgtype.Int4 `json:"is_active"` - SearchTerm pgtype.Text `json:"search_term"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetAllBanks(ctx context.Context, arg GetAllBanksParams) ([]Bank, error) { - rows, err := q.db.Query(ctx, GetAllBanks, - arg.CountryID, - arg.IsActive, - arg.SearchTerm, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Bank - for rows.Next() { - var i Bank - if err := rows.Scan( - &i.ID, - &i.Slug, - &i.Swift, - &i.Name, - &i.AcctLength, - &i.CountryID, - &i.IsMobilemoney, - &i.IsActive, - &i.IsRtgs, - &i.Active, - &i.Is24hrs, - &i.CreatedAt, - &i.UpdatedAt, - &i.Currency, - &i.BankLogo, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBankByID = `-- name: GetBankByID :one -SELECT id, slug, swift, name, acct_length, country_id, is_mobilemoney, is_active, is_rtgs, active, is_24hrs, created_at, updated_at, currency, bank_logo -FROM banks -WHERE id = $1 -` - -func (q *Queries) GetBankByID(ctx context.Context, id int64) (Bank, error) { - row := q.db.QueryRow(ctx, GetBankByID, id) - var i Bank - err := row.Scan( - &i.ID, - &i.Slug, - &i.Swift, - &i.Name, - &i.AcctLength, - &i.CountryID, - &i.IsMobilemoney, - &i.IsActive, - &i.IsRtgs, - &i.Active, - &i.Is24hrs, - &i.CreatedAt, - &i.UpdatedAt, - &i.Currency, - &i.BankLogo, - ) - return i, err -} - -const UpdateBank = `-- name: UpdateBank :one -UPDATE banks -SET slug = COALESCE($2, slug), - swift = COALESCE($3, swift), - name = COALESCE($4, name), - acct_length = COALESCE($5, acct_length), - country_id = COALESCE($6, country_id), - is_mobilemoney = COALESCE($7, is_mobilemoney), - is_active = COALESCE($8, is_active), - is_rtgs = COALESCE($9, is_rtgs), - active = COALESCE($10, active), - is_24hrs = COALESCE($11, is_24hrs), - updated_at = CURRENT_TIMESTAMP, - currency = COALESCE($12, currency), - bank_logo = COALESCE($13, bank_logo) -WHERE id = $1 -RETURNING id, slug, swift, name, acct_length, country_id, is_mobilemoney, is_active, is_rtgs, active, is_24hrs, created_at, updated_at, currency, bank_logo -` - -type UpdateBankParams struct { - ID int64 `json:"id"` - Slug pgtype.Text `json:"slug"` - Swift pgtype.Text `json:"swift"` - Name pgtype.Text `json:"name"` - AcctLength pgtype.Int4 `json:"acct_length"` - CountryID pgtype.Int4 `json:"country_id"` - IsMobilemoney pgtype.Int4 `json:"is_mobilemoney"` - IsActive pgtype.Int4 `json:"is_active"` - IsRtgs pgtype.Int4 `json:"is_rtgs"` - Active pgtype.Int4 `json:"active"` - Is24hrs pgtype.Int4 `json:"is_24hrs"` - Currency pgtype.Text `json:"currency"` - BankLogo pgtype.Text `json:"bank_logo"` -} - -func (q *Queries) UpdateBank(ctx context.Context, arg UpdateBankParams) (Bank, error) { - row := q.db.QueryRow(ctx, UpdateBank, - arg.ID, - arg.Slug, - arg.Swift, - arg.Name, - arg.AcctLength, - arg.CountryID, - arg.IsMobilemoney, - arg.IsActive, - arg.IsRtgs, - arg.Active, - arg.Is24hrs, - arg.Currency, - arg.BankLogo, - ) - var i Bank - err := row.Scan( - &i.ID, - &i.Slug, - &i.Swift, - &i.Name, - &i.AcctLength, - &i.CountryID, - &i.IsMobilemoney, - &i.IsActive, - &i.IsRtgs, - &i.Active, - &i.Is24hrs, - &i.CreatedAt, - &i.UpdatedAt, - &i.Currency, - &i.BankLogo, - ) - return i, err -} diff --git a/gen/db/issue_reporting.sql.go b/gen/db/issue_reporting.sql.go deleted file mode 100644 index 7fcb4af..0000000 --- a/gen/db/issue_reporting.sql.go +++ /dev/null @@ -1,197 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: issue_reporting.sql - -package dbgen - -import ( - "context" -) - -const CountReportedIssues = `-- name: CountReportedIssues :one -SELECT COUNT(*) -FROM reported_issues -` - -func (q *Queries) CountReportedIssues(ctx context.Context) (int64, error) { - row := q.db.QueryRow(ctx, CountReportedIssues) - var count int64 - err := row.Scan(&count) - return count, err -} - -const CountReportedIssuesByUser = `-- name: CountReportedIssuesByUser :one -SELECT COUNT(*) -FROM reported_issues -WHERE user_id = $1 -` - -func (q *Queries) CountReportedIssuesByUser(ctx context.Context, userID int64) (int64, error) { - row := q.db.QueryRow(ctx, CountReportedIssuesByUser, userID) - var count int64 - err := row.Scan(&count) - return count, err -} - -const CreateReportedIssue = `-- name: CreateReportedIssue :one -INSERT INTO reported_issues ( - user_id, - user_role, - subject, - description, - issue_type, - metadata - ) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING id, user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at -` - -type CreateReportedIssueParams struct { - UserID int64 `json:"user_id"` - UserRole string `json:"user_role"` - Subject string `json:"subject"` - Description string `json:"description"` - IssueType string `json:"issue_type"` - Metadata []byte `json:"metadata"` -} - -func (q *Queries) CreateReportedIssue(ctx context.Context, arg CreateReportedIssueParams) (ReportedIssue, error) { - row := q.db.QueryRow(ctx, CreateReportedIssue, - arg.UserID, - arg.UserRole, - arg.Subject, - arg.Description, - arg.IssueType, - arg.Metadata, - ) - var i ReportedIssue - err := row.Scan( - &i.ID, - &i.UserID, - &i.UserRole, - &i.Subject, - &i.Description, - &i.IssueType, - &i.Status, - &i.Metadata, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const DeleteReportedIssue = `-- name: DeleteReportedIssue :exec -DELETE FROM reported_issues -WHERE id = $1 -` - -func (q *Queries) DeleteReportedIssue(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteReportedIssue, id) - return err -} - -const ListReportedIssues = `-- name: ListReportedIssues :many -SELECT id, user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at -FROM reported_issues -ORDER BY created_at DESC -LIMIT $1 OFFSET $2 -` - -type ListReportedIssuesParams struct { - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -func (q *Queries) ListReportedIssues(ctx context.Context, arg ListReportedIssuesParams) ([]ReportedIssue, error) { - rows, err := q.db.Query(ctx, ListReportedIssues, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ReportedIssue - for rows.Next() { - var i ReportedIssue - if err := rows.Scan( - &i.ID, - &i.UserID, - &i.UserRole, - &i.Subject, - &i.Description, - &i.IssueType, - &i.Status, - &i.Metadata, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const ListReportedIssuesByUser = `-- name: ListReportedIssuesByUser :many -SELECT id, user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at -FROM reported_issues -WHERE user_id = $1 -ORDER BY created_at DESC -LIMIT $2 OFFSET $3 -` - -type ListReportedIssuesByUserParams struct { - UserID int64 `json:"user_id"` - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -func (q *Queries) ListReportedIssuesByUser(ctx context.Context, arg ListReportedIssuesByUserParams) ([]ReportedIssue, error) { - rows, err := q.db.Query(ctx, ListReportedIssuesByUser, arg.UserID, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ReportedIssue - for rows.Next() { - var i ReportedIssue - if err := rows.Scan( - &i.ID, - &i.UserID, - &i.UserRole, - &i.Subject, - &i.Description, - &i.IssueType, - &i.Status, - &i.Metadata, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateReportedIssueStatus = `-- name: UpdateReportedIssueStatus :exec -UPDATE reported_issues -SET status = $2, - updated_at = NOW() -WHERE id = $1 -` - -type UpdateReportedIssueStatusParams struct { - ID int64 `json:"id"` - Status string `json:"status"` -} - -func (q *Queries) UpdateReportedIssueStatus(ctx context.Context, arg UpdateReportedIssueStatusParams) error { - _, err := q.db.Exec(ctx, UpdateReportedIssueStatus, arg.ID, arg.Status) - return err -} diff --git a/gen/db/league_stats.sql.go b/gen/db/league_stats.sql.go deleted file mode 100644 index b1ad816..0000000 --- a/gen/db/league_stats.sql.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: league_stats.sql - -package dbgen - -import ( - "context" -) - -const GetLeagueEventStat = `-- name: GetLeagueEventStat :many -SELECT leagues.id, - leagues.name, - COUNT(*) AS total_events, - COUNT(*) FILTER ( - WHERE events.status = 'upcoming' - ) AS pending, - COUNT(*) FILTER ( - WHERE events.status = 'in_play' - ) AS in_play, - COUNT(*) FILTER ( - WHERE events.status = 'to_be_fixed' - ) AS to_be_fixed, - COUNT(*) FILTER ( - WHERE events.status = 'ended' - ) AS ended, - COUNT(*) FILTER ( - WHERE events.status = 'postponed' - ) AS postponed, - COUNT(*) FILTER ( - WHERE events.status = 'cancelled' - ) AS cancelled, - COUNT(*) FILTER ( - WHERE events.status = 'walkover' - ) AS walkover, - COUNT(*) FILTER ( - WHERE events.status = 'interrupted' - ) AS interrupted, - COUNT(*) FILTER ( - WHERE events.status = 'abandoned' - ) AS abandoned, - COUNT(*) FILTER ( - WHERE events.status = 'retired' - ) AS retired, - COUNT(*) FILTER ( - WHERE events.status = 'suspended' - ) AS suspended, - COUNT(*) FILTER ( - WHERE events.status = 'decided_by_fa' - ) AS decided_by_fa, - COUNT(*) FILTER ( - WHERE events.status = 'removed' - ) AS removed -FROM leagues - JOIN events ON leagues.id = events.league_id -GROUP BY leagues.id, - leagues.name -` - -type GetLeagueEventStatRow struct { - ID int64 `json:"id"` - Name string `json:"name"` - TotalEvents int64 `json:"total_events"` - Pending int64 `json:"pending"` - InPlay int64 `json:"in_play"` - ToBeFixed int64 `json:"to_be_fixed"` - Ended int64 `json:"ended"` - Postponed int64 `json:"postponed"` - Cancelled int64 `json:"cancelled"` - Walkover int64 `json:"walkover"` - Interrupted int64 `json:"interrupted"` - Abandoned int64 `json:"abandoned"` - Retired int64 `json:"retired"` - Suspended int64 `json:"suspended"` - DecidedByFa int64 `json:"decided_by_fa"` - Removed int64 `json:"removed"` -} - -func (q *Queries) GetLeagueEventStat(ctx context.Context) ([]GetLeagueEventStatRow, error) { - rows, err := q.db.Query(ctx, GetLeagueEventStat) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetLeagueEventStatRow - for rows.Next() { - var i GetLeagueEventStatRow - if err := rows.Scan( - &i.ID, - &i.Name, - &i.TotalEvents, - &i.Pending, - &i.InPlay, - &i.ToBeFixed, - &i.Ended, - &i.Postponed, - &i.Cancelled, - &i.Walkover, - &i.Interrupted, - &i.Abandoned, - &i.Retired, - &i.Suspended, - &i.DecidedByFa, - &i.Removed, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/gen/db/leagues.sql.go b/gen/db/leagues.sql.go deleted file mode 100644 index 1413699..0000000 --- a/gen/db/leagues.sql.go +++ /dev/null @@ -1,443 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: leagues.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CheckLeagueSupport = `-- name: CheckLeagueSupport :one -SELECT EXISTS( - SELECT 1 - FROM company_league_settings - WHERE league_id = $1 - AND company_id = $2 - AND is_active = true - ) -` - -type CheckLeagueSupportParams struct { - LeagueID int64 `json:"league_id"` - CompanyID int64 `json:"company_id"` -} - -func (q *Queries) CheckLeagueSupport(ctx context.Context, arg CheckLeagueSupportParams) (bool, error) { - row := q.db.QueryRow(ctx, CheckLeagueSupport, arg.LeagueID, arg.CompanyID) - var exists bool - err := row.Scan(&exists) - return exists, err -} - -const GetAllLeagues = `-- name: GetAllLeagues :many -SELECT id, name, img_url, country_code, bet365_id, sport_id, default_is_active, default_is_featured -FROM leagues -WHERE ( - country_code = $1 - OR $1 IS NULL - ) - AND ( - sport_id = $2 - OR $2 IS NULL - ) - AND ( - name ILIKE '%' || $3 || '%' - OR $3 IS NULL - ) - AND ( - default_is_active = $4 - OR $4 IS NULL - ) -ORDER BY name ASC -LIMIT $6 OFFSET $5 -` - -type GetAllLeaguesParams struct { - CountryCode pgtype.Text `json:"country_code"` - SportID pgtype.Int4 `json:"sport_id"` - Query pgtype.Text `json:"query"` - IsActive pgtype.Bool `json:"is_active"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetAllLeagues(ctx context.Context, arg GetAllLeaguesParams) ([]League, error) { - rows, err := q.db.Query(ctx, GetAllLeagues, - arg.CountryCode, - arg.SportID, - arg.Query, - arg.IsActive, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []League - for rows.Next() { - var i League - if err := rows.Scan( - &i.ID, - &i.Name, - &i.ImgUrl, - &i.CountryCode, - &i.Bet365ID, - &i.SportID, - &i.DefaultIsActive, - &i.DefaultIsFeatured, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllLeaguesWithSettings = `-- name: GetAllLeaguesWithSettings :many -SELECT l.id, l.name, l.img_url, l.country_code, l.bet365_id, l.sport_id, l.default_is_active, l.default_is_featured, - cls.company_id, - COALESCE(cls.is_active, l.default_is_active) AS is_active, - COALESCE(cls.is_featured, l.default_is_featured) AS is_featured, - cls.updated_at -FROM leagues l - LEFT JOIN company_league_settings cls ON l.id = cls.league_id - AND company_id = $1 -WHERE ( - country_code = $2 - OR $2 IS NULL - ) - AND ( - sport_id = $3 - OR $3 IS NULL - ) - AND ( - is_active = $4 - OR default_is_active = $4 - OR $4 IS NULL - ) - AND ( - is_featured = $5 - OR default_is_featured = $5 - OR $5 IS NULL - ) - AND ( - name ILIKE '%' || $6 || '%' - OR $6 IS NULL - ) -ORDER BY is_featured DESC, - name ASC -LIMIT $8 OFFSET $7 -` - -type GetAllLeaguesWithSettingsParams struct { - CompanyID int64 `json:"company_id"` - CountryCode pgtype.Text `json:"country_code"` - SportID pgtype.Int4 `json:"sport_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` - Query pgtype.Text `json:"query"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -type GetAllLeaguesWithSettingsRow struct { - ID int64 `json:"id"` - Name string `json:"name"` - ImgUrl pgtype.Text `json:"img_url"` - CountryCode pgtype.Text `json:"country_code"` - Bet365ID pgtype.Int4 `json:"bet365_id"` - SportID int32 `json:"sport_id"` - DefaultIsActive bool `json:"default_is_active"` - DefaultIsFeatured bool `json:"default_is_featured"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsFeatured bool `json:"is_featured"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetAllLeaguesWithSettings(ctx context.Context, arg GetAllLeaguesWithSettingsParams) ([]GetAllLeaguesWithSettingsRow, error) { - rows, err := q.db.Query(ctx, GetAllLeaguesWithSettings, - arg.CompanyID, - arg.CountryCode, - arg.SportID, - arg.IsActive, - arg.IsFeatured, - arg.Query, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetAllLeaguesWithSettingsRow - for rows.Next() { - var i GetAllLeaguesWithSettingsRow - if err := rows.Scan( - &i.ID, - &i.Name, - &i.ImgUrl, - &i.CountryCode, - &i.Bet365ID, - &i.SportID, - &i.DefaultIsActive, - &i.DefaultIsFeatured, - &i.CompanyID, - &i.IsActive, - &i.IsFeatured, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetTotalLeagues = `-- name: GetTotalLeagues :one -SELECT COUNT(*) -FROM leagues -WHERE ( - country_code = $1 - OR $1 IS NULL - ) - AND ( - sport_id = $2 - OR $2 IS NULL - ) - AND ( - name ILIKE '%' || $3 || '%' - OR $3 IS NULL - ) - AND ( - default_is_active = $4 - OR $4 IS NULL - ) -` - -type GetTotalLeaguesParams struct { - CountryCode pgtype.Text `json:"country_code"` - SportID pgtype.Int4 `json:"sport_id"` - Query pgtype.Text `json:"query"` - IsActive pgtype.Bool `json:"is_active"` -} - -func (q *Queries) GetTotalLeagues(ctx context.Context, arg GetTotalLeaguesParams) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalLeagues, - arg.CountryCode, - arg.SportID, - arg.Query, - arg.IsActive, - ) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetTotalLeaguesWithSettings = `-- name: GetTotalLeaguesWithSettings :one -SELECT COUNT(*) -FROM leagues l - LEFT JOIN company_league_settings cls ON l.id = cls.league_id - AND company_id = $1 -WHERE ( - country_code = $2 - OR $2 IS NULL - ) - AND ( - sport_id = $3 - OR $3 IS NULL - ) - AND ( - is_active = $4 - OR default_is_active = $4 - OR $4 IS NULL - ) - AND ( - is_featured = $5 - OR default_is_featured = $5 - OR $5 IS NULL - ) - AND ( - name ILIKE '%' || $6 || '%' - OR $6 IS NULL - ) -` - -type GetTotalLeaguesWithSettingsParams struct { - CompanyID int64 `json:"company_id"` - CountryCode pgtype.Text `json:"country_code"` - SportID pgtype.Int4 `json:"sport_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` - Query pgtype.Text `json:"query"` -} - -func (q *Queries) GetTotalLeaguesWithSettings(ctx context.Context, arg GetTotalLeaguesWithSettingsParams) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalLeaguesWithSettings, - arg.CompanyID, - arg.CountryCode, - arg.SportID, - arg.IsActive, - arg.IsFeatured, - arg.Query, - ) - var count int64 - err := row.Scan(&count) - return count, err -} - -const InsertLeague = `-- name: InsertLeague :exec -INSERT INTO leagues ( - id, - name, - country_code, - bet365_id, - sport_id, - default_is_active, - default_is_featured - ) -VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO -UPDATE -SET name = EXCLUDED.name, - country_code = EXCLUDED.country_code, - bet365_id = EXCLUDED.bet365_id, - sport_id = EXCLUDED.sport_id -` - -type InsertLeagueParams struct { - ID int64 `json:"id"` - Name string `json:"name"` - CountryCode pgtype.Text `json:"country_code"` - Bet365ID pgtype.Int4 `json:"bet365_id"` - SportID int32 `json:"sport_id"` - DefaultIsActive bool `json:"default_is_active"` - DefaultIsFeatured bool `json:"default_is_featured"` -} - -func (q *Queries) InsertLeague(ctx context.Context, arg InsertLeagueParams) error { - _, err := q.db.Exec(ctx, InsertLeague, - arg.ID, - arg.Name, - arg.CountryCode, - arg.Bet365ID, - arg.SportID, - arg.DefaultIsActive, - arg.DefaultIsFeatured, - ) - return err -} - -const SaveLeagueSettings = `-- name: SaveLeagueSettings :exec -INSERT INTO company_league_settings ( - company_id, - league_id, - is_active, - is_featured - ) -VALUES ($1, $2, $3, $4) ON CONFLICT(company_id, league_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - is_featured = EXCLUDED.is_featured -` - -type SaveLeagueSettingsParams struct { - CompanyID int64 `json:"company_id"` - LeagueID int64 `json:"league_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` -} - -func (q *Queries) SaveLeagueSettings(ctx context.Context, arg SaveLeagueSettingsParams) error { - _, err := q.db.Exec(ctx, SaveLeagueSettings, - arg.CompanyID, - arg.LeagueID, - arg.IsActive, - arg.IsFeatured, - ) - return err -} - -const UpdateCompanyLeagueSettings = `-- name: UpdateCompanyLeagueSettings :exec -UPDATE company_league_settings -SET is_active = COALESCE($3, is_active), - is_featured = COALESCE( - $4, - is_featured - ) -WHERE league_id = $1 - AND company_id = $2 -` - -type UpdateCompanyLeagueSettingsParams struct { - LeagueID int64 `json:"league_id"` - CompanyID int64 `json:"company_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` -} - -func (q *Queries) UpdateCompanyLeagueSettings(ctx context.Context, arg UpdateCompanyLeagueSettingsParams) error { - _, err := q.db.Exec(ctx, UpdateCompanyLeagueSettings, - arg.LeagueID, - arg.CompanyID, - arg.IsActive, - arg.IsFeatured, - ) - return err -} - -const UpdateGlobalLeagueSettings = `-- name: UpdateGlobalLeagueSettings :exec -UPDATE leagues -SET default_is_active = COALESCE($2, default_is_active), - default_is_featured = COALESCE($3, default_is_featured) -WHERE id = $1 -` - -type UpdateGlobalLeagueSettingsParams struct { - ID int64 `json:"id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` -} - -func (q *Queries) UpdateGlobalLeagueSettings(ctx context.Context, arg UpdateGlobalLeagueSettingsParams) error { - _, err := q.db.Exec(ctx, UpdateGlobalLeagueSettings, arg.ID, arg.IsActive, arg.IsFeatured) - return err -} - -const UpdateLeague = `-- name: UpdateLeague :exec -UPDATE leagues -SET name = COALESCE($2, name), - country_code = COALESCE($3, country_code), - bet365_id = COALESCE($4, bet365_id), - sport_id = COALESCE($5, sport_id) -WHERE id = $1 -` - -type UpdateLeagueParams struct { - ID int64 `json:"id"` - Name pgtype.Text `json:"name"` - CountryCode pgtype.Text `json:"country_code"` - Bet365ID pgtype.Int4 `json:"bet365_id"` - SportID pgtype.Int4 `json:"sport_id"` -} - -func (q *Queries) UpdateLeague(ctx context.Context, arg UpdateLeagueParams) error { - _, err := q.db.Exec(ctx, UpdateLeague, - arg.ID, - arg.Name, - arg.CountryCode, - arg.Bet365ID, - arg.SportID, - ) - return err -} diff --git a/gen/db/location.sql.go b/gen/db/location.sql.go deleted file mode 100644 index 008aa61..0000000 --- a/gen/db/location.sql.go +++ /dev/null @@ -1,41 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: location.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetAllBranchLocations = `-- name: GetAllBranchLocations :many -SELECT key, value -FROM branch_locations -WHERE ( - value ILIKE '%' || $1 || '%' - OR $1 IS NULL - ) -` - -func (q *Queries) GetAllBranchLocations(ctx context.Context, query pgtype.Text) ([]BranchLocation, error) { - rows, err := q.db.Query(ctx, GetAllBranchLocations, query) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BranchLocation - for rows.Next() { - var i BranchLocation - if err := rows.Scan(&i.Key, &i.Value); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/gen/db/market_settings.sql.go b/gen/db/market_settings.sql.go deleted file mode 100644 index d8a58d4..0000000 --- a/gen/db/market_settings.sql.go +++ /dev/null @@ -1,281 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: market_settings.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const DeleteAllMarketSettingsForCompany = `-- name: DeleteAllMarketSettingsForCompany :exec -DELETE FROM company_odd_market_settings -WHERE company_id = $1 -` - -func (q *Queries) DeleteAllMarketSettingsForCompany(ctx context.Context, companyID int64) error { - _, err := q.db.Exec(ctx, DeleteAllMarketSettingsForCompany, companyID) - return err -} - -const DeleteCompanyMarketSettings = `-- name: DeleteCompanyMarketSettings :exec -DELETE FROM company_odd_market_settings -WHERE market_id = $1 - AND company_id = $2 -` - -type DeleteCompanyMarketSettingsParams struct { - MarketID int64 `json:"market_id"` - CompanyID int64 `json:"company_id"` -} - -func (q *Queries) DeleteCompanyMarketSettings(ctx context.Context, arg DeleteCompanyMarketSettingsParams) error { - _, err := q.db.Exec(ctx, DeleteCompanyMarketSettings, arg.MarketID, arg.CompanyID) - return err -} - -const GetAllCompanyMarketSettings = `-- name: GetAllCompanyMarketSettings :many -SELECT market_id, market_name, company_id, is_active, updated_at -FROM company_odd_market_settings -WHERE ( - company_id = $1 - OR $1 IS NULL - ) -LIMIT $3 OFFSET $2 -` - -type GetAllCompanyMarketSettingsParams struct { - CompanyID pgtype.Int8 `json:"company_id"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetAllCompanyMarketSettings(ctx context.Context, arg GetAllCompanyMarketSettingsParams) ([]CompanyOddMarketSetting, error) { - rows, err := q.db.Query(ctx, GetAllCompanyMarketSettings, arg.CompanyID, arg.Offset, arg.Limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []CompanyOddMarketSetting - for rows.Next() { - var i CompanyOddMarketSetting - if err := rows.Scan( - &i.MarketID, - &i.MarketName, - &i.CompanyID, - &i.IsActive, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllGlobalMarketSettings = `-- name: GetAllGlobalMarketSettings :many -SELECT market_id, market_name, is_active, updated_at -FROM global_odd_market_settings -LIMIT $2 OFFSET $1 -` - -type GetAllGlobalMarketSettingsParams struct { - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetAllGlobalMarketSettings(ctx context.Context, arg GetAllGlobalMarketSettingsParams) ([]GlobalOddMarketSetting, error) { - rows, err := q.db.Query(ctx, GetAllGlobalMarketSettings, arg.Offset, arg.Limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GlobalOddMarketSetting - for rows.Next() { - var i GlobalOddMarketSetting - if err := rows.Scan( - &i.MarketID, - &i.MarketName, - &i.IsActive, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllOverrideMarketSettings = `-- name: GetAllOverrideMarketSettings :many -SELECT gdm.market_id, - gdm.market_name, - COALESCE(cdm.is_active, gdm.is_active) AS is_active, - COALESCE(cdm.updated_at, gdm.updated_at) AS updated_at -FROM global_odd_market_settings gdm - LEFT JOIN company_odd_market_settings cdm ON cdm.market_id = gdm.market_id - AND company_id = $1 -LIMIT $3 OFFSET $2 -` - -type GetAllOverrideMarketSettingsParams struct { - CompanyID int64 `json:"company_id"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -type GetAllOverrideMarketSettingsRow struct { - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - IsActive bool `json:"is_active"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetAllOverrideMarketSettings(ctx context.Context, arg GetAllOverrideMarketSettingsParams) ([]GetAllOverrideMarketSettingsRow, error) { - rows, err := q.db.Query(ctx, GetAllOverrideMarketSettings, arg.CompanyID, arg.Offset, arg.Limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetAllOverrideMarketSettingsRow - for rows.Next() { - var i GetAllOverrideMarketSettingsRow - if err := rows.Scan( - &i.MarketID, - &i.MarketName, - &i.IsActive, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetCompanyMarketSettingsByID = `-- name: GetCompanyMarketSettingsByID :one -SELECT market_id, market_name, company_id, is_active, updated_at -FROM company_odd_market_settings -WHERE market_id = $1 -` - -func (q *Queries) GetCompanyMarketSettingsByID(ctx context.Context, marketID int64) (CompanyOddMarketSetting, error) { - row := q.db.QueryRow(ctx, GetCompanyMarketSettingsByID, marketID) - var i CompanyOddMarketSetting - err := row.Scan( - &i.MarketID, - &i.MarketName, - &i.CompanyID, - &i.IsActive, - &i.UpdatedAt, - ) - return i, err -} - -const GetGlobalMarketSettingsByID = `-- name: GetGlobalMarketSettingsByID :one -SELECT market_id, market_name, is_active, updated_at -FROM global_odd_market_settings -WHERE market_id = $1 -` - -func (q *Queries) GetGlobalMarketSettingsByID(ctx context.Context, marketID int64) (GlobalOddMarketSetting, error) { - row := q.db.QueryRow(ctx, GetGlobalMarketSettingsByID, marketID) - var i GlobalOddMarketSetting - err := row.Scan( - &i.MarketID, - &i.MarketName, - &i.IsActive, - &i.UpdatedAt, - ) - return i, err -} - -const GetOverrideMarketSettingByID = `-- name: GetOverrideMarketSettingByID :one -SELECT gdm.market_id, - gdm.market_name, - COALESCE(cdm.is_active, gdm.is_active) AS is_active, - COALESCE(cdm.updated_at, gdm.updated_at) AS updated_at -FROM global_odd_market_settings gdm - LEFT JOIN company_odd_market_settings cdm ON cdm.market_id = gdm.market_id - AND company_id = $1 -WHERE gdm.market_id = $2 -` - -type GetOverrideMarketSettingByIDParams struct { - CompanyID int64 `json:"company_id"` - MarketID int64 `json:"market_id"` -} - -type GetOverrideMarketSettingByIDRow struct { - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - IsActive bool `json:"is_active"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetOverrideMarketSettingByID(ctx context.Context, arg GetOverrideMarketSettingByIDParams) (GetOverrideMarketSettingByIDRow, error) { - row := q.db.QueryRow(ctx, GetOverrideMarketSettingByID, arg.CompanyID, arg.MarketID) - var i GetOverrideMarketSettingByIDRow - err := row.Scan( - &i.MarketID, - &i.MarketName, - &i.IsActive, - &i.UpdatedAt, - ) - return i, err -} - -const InsertCompanyMarketSettings = `-- name: InsertCompanyMarketSettings :exec -INSERT INTO company_odd_market_settings (company_id, market_id, market_name, is_active) -VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, market_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - updated_at = CURRENT_TIMESTAMP -` - -type InsertCompanyMarketSettingsParams struct { - CompanyID int64 `json:"company_id"` - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - IsActive pgtype.Bool `json:"is_active"` -} - -func (q *Queries) InsertCompanyMarketSettings(ctx context.Context, arg InsertCompanyMarketSettingsParams) error { - _, err := q.db.Exec(ctx, InsertCompanyMarketSettings, - arg.CompanyID, - arg.MarketID, - arg.MarketName, - arg.IsActive, - ) - return err -} - -const InsertGlobalMarketSettings = `-- name: InsertGlobalMarketSettings :exec -INSERT INTO global_odd_market_settings (market_id, market_name, is_active) -VALUES ($1, $2, $3) ON CONFLICT (market_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - updated_at = CURRENT_TIMESTAMP -` - -type InsertGlobalMarketSettingsParams struct { - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - IsActive bool `json:"is_active"` -} - -func (q *Queries) InsertGlobalMarketSettings(ctx context.Context, arg InsertGlobalMarketSettingsParams) error { - _, err := q.db.Exec(ctx, InsertGlobalMarketSettings, arg.MarketID, arg.MarketName, arg.IsActive) - return err -} diff --git a/gen/db/models.go b/gen/db/models.go index 60d03dd..924d777 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -8,777 +8,111 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -type Bank struct { - ID int64 `json:"id"` - Slug string `json:"slug"` - Swift string `json:"swift"` - Name string `json:"name"` - AcctLength int32 `json:"acct_length"` - CountryID int32 `json:"country_id"` - IsMobilemoney pgtype.Int4 `json:"is_mobilemoney"` - IsActive int32 `json:"is_active"` - IsRtgs int32 `json:"is_rtgs"` - Active int32 `json:"active"` - Is24hrs pgtype.Int4 `json:"is_24hrs"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - Currency string `json:"currency"` - BankLogo pgtype.Text `json:"bank_logo"` +type Assessment struct { + ID int64 `json:"id"` + CourseID int64 `json:"course_id"` + Title string `json:"title"` + Type string `json:"type"` + TotalScore int32 `json:"total_score"` + DueDate pgtype.Timestamptz `json:"due_date"` + CreatedAt pgtype.Timestamptz `json:"created_at"` } -type Bet struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - Amount int64 `json:"amount"` - TotalOdds float32 `json:"total_odds"` - PotentialWin pgtype.Int8 `json:"potential_win"` - Status int32 `json:"status"` - UserID int64 `json:"user_id"` - IsShopBet bool `json:"is_shop_bet"` - CashedOut bool `json:"cashed_out"` - OutcomesHash string `json:"outcomes_hash"` - FastCode string `json:"fast_code"` - Processed bool `json:"processed"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` +type AssessmentSubmission struct { + ID int64 `json:"id"` + AssessmentID int64 `json:"assessment_id"` + StudentID int64 `json:"student_id"` + Score pgtype.Int4 `json:"score"` + Feedback pgtype.Text `json:"feedback"` + SubmittedAt pgtype.Timestamptz `json:"submitted_at"` + GradedAt pgtype.Timestamptz `json:"graded_at"` } -type BetOutcome struct { - ID int64 `json:"id"` - BetID int64 `json:"bet_id"` - SportID int64 `json:"sport_id"` - EventID int64 `json:"event_id"` - OddID int64 `json:"odd_id"` - HomeTeamName string `json:"home_team_name"` - AwayTeamName string `json:"away_team_name"` - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - Odd float32 `json:"odd"` - OddName string `json:"odd_name"` - OddHeader string `json:"odd_header"` - OddHandicap string `json:"odd_handicap"` - Status int32 `json:"status"` - Expires pgtype.Timestamp `json:"expires"` +type Course struct { + ID int64 `json:"id"` + OrganizationID int64 `json:"organization_id"` + InstructorID int64 `json:"instructor_id"` + Title string `json:"title"` + Description pgtype.Text `json:"description"` + Level pgtype.Text `json:"level"` + Language pgtype.Text `json:"language"` + IsPublished bool `json:"is_published"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } -type BetWithOutcome struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - Amount int64 `json:"amount"` - TotalOdds float32 `json:"total_odds"` - PotentialWin pgtype.Int8 `json:"potential_win"` - Status int32 `json:"status"` - UserID int64 `json:"user_id"` - IsShopBet bool `json:"is_shop_bet"` - CashedOut bool `json:"cashed_out"` - OutcomesHash string `json:"outcomes_hash"` - FastCode string `json:"fast_code"` - Processed bool `json:"processed"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - FullName interface{} `json:"full_name"` - PhoneNumber pgtype.Text `json:"phone_number"` - Outcomes []BetOutcome `json:"outcomes"` - CompanySlug string `json:"company_slug"` +type CourseModule struct { + ID int64 `json:"id"` + CourseID int64 `json:"course_id"` + Title string `json:"title"` + Position int32 `json:"position"` + CreatedAt pgtype.Timestamptz `json:"created_at"` } -type Branch struct { - ID int64 `json:"id"` - Name string `json:"name"` - Location string `json:"location"` - ProfitPercent float32 `json:"profit_percent"` - IsActive bool `json:"is_active"` - WalletID int64 `json:"wallet_id"` - BranchManagerID int64 `json:"branch_manager_id"` - CompanyID int64 `json:"company_id"` - IsSelfOwned bool `json:"is_self_owned"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type BranchCashier struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - BranchID int64 `json:"branch_id"` -} - -type BranchDetail struct { - ID int64 `json:"id"` - Name string `json:"name"` - Location string `json:"location"` - ProfitPercent float32 `json:"profit_percent"` - IsActive bool `json:"is_active"` - WalletID int64 `json:"wallet_id"` - BranchManagerID int64 `json:"branch_manager_id"` - CompanyID int64 `json:"company_id"` - IsSelfOwned bool `json:"is_self_owned"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - ManagerName interface{} `json:"manager_name"` - ManagerPhoneNumber pgtype.Text `json:"manager_phone_number"` - Balance pgtype.Int8 `json:"balance"` - WalletIsActive pgtype.Bool `json:"wallet_is_active"` - CompanyName string `json:"company_name"` - TotalBets int64 `json:"total_bets"` - TotalStake int64 `json:"total_stake"` - DeductedStake int64 `json:"deducted_stake"` - TotalCashOut int64 `json:"total_cash_out"` - TotalCashBacks int64 `json:"total_cash_backs"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount int64 `json:"total_unsettled_amount"` - TotalCashiers int64 `json:"total_cashiers"` - StatsUpdatedAt pgtype.Timestamp `json:"stats_updated_at"` -} - -type BranchLocation struct { - Key string `json:"key"` - Value string `json:"value"` -} - -type BranchOperation struct { - ID int64 `json:"id"` - OperationID int64 `json:"operation_id"` - BranchID int64 `json:"branch_id"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type BranchStat struct { - BranchID int64 `json:"branch_id"` - BranchName string `json:"branch_name"` - CompanyID int64 `json:"company_id"` - CompanyName string `json:"company_name"` - CompanySlug string `json:"company_slug"` - IntervalStart pgtype.Timestamp `json:"interval_start"` - TotalBets int64 `json:"total_bets"` - TotalStake int64 `json:"total_stake"` - DeductedStake int64 `json:"deducted_stake"` - TotalCashOut int64 `json:"total_cash_out"` - TotalCashBacks int64 `json:"total_cash_backs"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount int64 `json:"total_unsettled_amount"` - TotalCashiers int64 `json:"total_cashiers"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type CompaniesDetail struct { - ID int64 `json:"id"` - Name string `json:"name"` - Slug string `json:"slug"` - AdminID int64 `json:"admin_id"` - WalletID int64 `json:"wallet_id"` - DeductedPercentage float32 `json:"deducted_percentage"` - IsActive bool `json:"is_active"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - Balance int64 `json:"balance"` - WalletIsActive bool `json:"wallet_is_active"` - AdminFirstName string `json:"admin_first_name"` - AdminLastName string `json:"admin_last_name"` - AdminPhoneNumber pgtype.Text `json:"admin_phone_number"` - TotalBets int64 `json:"total_bets"` - TotalStake int64 `json:"total_stake"` - DeductedStake int64 `json:"deducted_stake"` - TotalCashOut int64 `json:"total_cash_out"` - TotalCashBacks int64 `json:"total_cash_backs"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount int64 `json:"total_unsettled_amount"` - TotalAdmins int64 `json:"total_admins"` - TotalManagers int64 `json:"total_managers"` - TotalCashiers int64 `json:"total_cashiers"` - TotalCustomers int64 `json:"total_customers"` - TotalApprovers int64 `json:"total_approvers"` - TotalBranches int64 `json:"total_branches"` - StatsUpdatedAt pgtype.Timestamp `json:"stats_updated_at"` -} - -type Company struct { - ID int64 `json:"id"` - Name string `json:"name"` - Slug string `json:"slug"` - AdminID int64 `json:"admin_id"` - WalletID int64 `json:"wallet_id"` - DeductedPercentage float32 `json:"deducted_percentage"` - IsActive bool `json:"is_active"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type CompanyAccumulator struct { - ID int32 `json:"id"` - CompanyID int64 `json:"company_id"` - OutcomeCount int64 `json:"outcome_count"` - Multiplier float32 `json:"multiplier"` -} - -type CompanyEventSetting struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - EventID int64 `json:"event_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` - WinningUpperLimit pgtype.Int8 `json:"winning_upper_limit"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type CompanyLeagueSetting struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - LeagueID int64 `json:"league_id"` - IsActive pgtype.Bool `json:"is_active"` - IsFeatured pgtype.Bool `json:"is_featured"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type CompanyOddMarketSetting struct { - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - CompanyID int64 `json:"company_id"` - IsActive pgtype.Bool `json:"is_active"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type CompanyOddSetting struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - OddsMarketID int64 `json:"odds_market_id"` - IsActive pgtype.Bool `json:"is_active"` - CustomRawOdds []byte `json:"custom_raw_odds"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type CompanySetting struct { - CompanyID int64 `json:"company_id"` - Key string `json:"key"` - Value string `json:"value"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type CompanyStat struct { - CompanyID int64 `json:"company_id"` - CompanyName string `json:"company_name"` - CompanySlug string `json:"company_slug"` - IntervalStart pgtype.Timestamp `json:"interval_start"` - TotalBets int64 `json:"total_bets"` - TotalStake int64 `json:"total_stake"` - DeductedStake int64 `json:"deducted_stake"` - TotalCashOut int64 `json:"total_cash_out"` - TotalCashBacks int64 `json:"total_cash_backs"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount int64 `json:"total_unsettled_amount"` - TotalAdmins int64 `json:"total_admins"` - TotalManagers int64 `json:"total_managers"` - TotalCashiers int64 `json:"total_cashiers"` - TotalCustomers int64 `json:"total_customers"` - TotalApprovers int64 `json:"total_approvers"` - TotalBranches int64 `json:"total_branches"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type CustomerWallet struct { - ID int64 `json:"id"` - CustomerID int64 `json:"customer_id"` - RegularWalletID int64 `json:"regular_wallet_id"` - StaticWalletID int64 `json:"static_wallet_id"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type CustomerWalletDetail struct { - ID int64 `json:"id"` - CustomerID int64 `json:"customer_id"` - RegularID int64 `json:"regular_id"` - RegularBalance int64 `json:"regular_balance"` - StaticID int64 `json:"static_id"` - StaticBalance int64 `json:"static_balance"` - RegularIsActive bool `json:"regular_is_active"` - StaticIsActive bool `json:"static_is_active"` - RegularUpdatedAt pgtype.Timestamp `json:"regular_updated_at"` - StaticUpdatedAt pgtype.Timestamp `json:"static_updated_at"` - CreatedAt pgtype.Timestamp `json:"created_at"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - PhoneNumber pgtype.Text `json:"phone_number"` - NumberOfTransactions int64 `json:"number_of_transactions"` - TotalTransactions int64 `json:"total_transactions"` - NumberOfDeposits int64 `json:"number_of_deposits"` - TotalDepositsAmount int64 `json:"total_deposits_amount"` - NumberOfWithdraws int64 `json:"number_of_withdraws"` - TotalWithdrawsAmount int64 `json:"total_withdraws_amount"` - NumberOfTransfers int64 `json:"number_of_transfers"` - TotalTransfersAmount int64 `json:"total_transfers_amount"` - StatsUpdatedAt pgtype.Timestamp `json:"stats_updated_at"` -} - -type DirectDeposit struct { - ID int64 `json:"id"` - CustomerID pgtype.Int8 `json:"customer_id"` - WalletID pgtype.Int8 `json:"wallet_id"` - BankName pgtype.Text `json:"bank_name"` - AccountNumber pgtype.Text `json:"account_number"` - AccountHolder pgtype.Text `json:"account_holder"` - Amount pgtype.Numeric `json:"amount"` - ReferenceNumber pgtype.Text `json:"reference_number"` - TransferScreenshot pgtype.Text `json:"transfer_screenshot"` - Status pgtype.Text `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - ApprovedBy pgtype.Int8 `json:"approved_by"` - ApprovedAt pgtype.Timestamptz `json:"approved_at"` - RejectionReason pgtype.Text `json:"rejection_reason"` -} - -type EnetpulseFixture struct { - ID int64 `json:"id"` - FixtureID string `json:"fixture_id"` - Name string `json:"name"` - SportFk string `json:"sport_fk"` - TournamentFk pgtype.Text `json:"tournament_fk"` - TournamentTemplateFk pgtype.Text `json:"tournament_template_fk"` - TournamentName pgtype.Text `json:"tournament_name"` - TournamentTemplateName pgtype.Text `json:"tournament_template_name"` - SportName pgtype.Text `json:"sport_name"` - Gender pgtype.Text `json:"gender"` - StartDate pgtype.Timestamptz `json:"start_date"` - StatusType pgtype.Text `json:"status_type"` - StatusDescFk pgtype.Text `json:"status_desc_fk"` - RoundTypeFk pgtype.Text `json:"round_type_fk"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type EnetpulseOutcomeType struct { - ID int64 `json:"id"` - OutcomeTypeID string `json:"outcome_type_id"` - Name string `json:"name"` - Description pgtype.Text `json:"description"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type EnetpulsePreodd struct { - ID int64 `json:"id"` - PreoddsID string `json:"preodds_id"` - EventFk int64 `json:"event_fk"` - OutcomeTypeFk pgtype.Int4 `json:"outcome_type_fk"` - OutcomeScopeFk pgtype.Int4 `json:"outcome_scope_fk"` - OutcomeSubtypeFk pgtype.Int4 `json:"outcome_subtype_fk"` - EventParticipantNumber pgtype.Int4 `json:"event_participant_number"` - Iparam pgtype.Text `json:"iparam"` - Iparam2 pgtype.Text `json:"iparam2"` - Dparam pgtype.Text `json:"dparam"` - Dparam2 pgtype.Text `json:"dparam2"` - Sparam pgtype.Text `json:"sparam"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type EnetpulsePreoddsBettingoffer struct { - ID int64 `json:"id"` - BettingofferID string `json:"bettingoffer_id"` - PreoddsFk string `json:"preodds_fk"` - BettingofferStatusFk pgtype.Int4 `json:"bettingoffer_status_fk"` - OddsProviderFk pgtype.Int4 `json:"odds_provider_fk"` - Odds pgtype.Numeric `json:"odds"` - OddsOld pgtype.Numeric `json:"odds_old"` - Active pgtype.Bool `json:"active"` - CouponKey pgtype.Text `json:"coupon_key"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type EnetpulseResult struct { - ID int64 `json:"id"` - ResultID string `json:"result_id"` - Name string `json:"name"` - SportFk string `json:"sport_fk"` - TournamentFk pgtype.Text `json:"tournament_fk"` - TournamentTemplateFk pgtype.Text `json:"tournament_template_fk"` - TournamentName pgtype.Text `json:"tournament_name"` - TournamentTemplateName pgtype.Text `json:"tournament_template_name"` - SportName pgtype.Text `json:"sport_name"` - StartDate pgtype.Timestamptz `json:"start_date"` - StatusType pgtype.Text `json:"status_type"` - StatusDescFk pgtype.Text `json:"status_desc_fk"` - RoundTypeFk pgtype.Text `json:"round_type_fk"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Round pgtype.Text `json:"round"` - Live pgtype.Text `json:"live"` - VenueName pgtype.Text `json:"venue_name"` - LivestatsPlus pgtype.Text `json:"livestats_plus"` - LivestatsType pgtype.Text `json:"livestats_type"` - Commentary pgtype.Text `json:"commentary"` - LineupConfirmed pgtype.Bool `json:"lineup_confirmed"` - Verified pgtype.Bool `json:"verified"` - Spectators pgtype.Int4 `json:"spectators"` - GameStarted pgtype.Timestamptz `json:"game_started"` - FirstHalfEnded pgtype.Timestamptz `json:"first_half_ended"` - SecondHalfStarted pgtype.Timestamptz `json:"second_half_started"` - SecondHalfEnded pgtype.Timestamptz `json:"second_half_ended"` - GameEnded pgtype.Timestamptz `json:"game_ended"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type EnetpulseResultParticipant struct { - ID int64 `json:"id"` - ParticipantMapID string `json:"participant_map_id"` - ResultFk string `json:"result_fk"` - ParticipantFk string `json:"participant_fk"` - Number pgtype.Int4 `json:"number"` - Name pgtype.Text `json:"name"` - Gender pgtype.Text `json:"gender"` - Type pgtype.Text `json:"type"` - CountryFk pgtype.Text `json:"country_fk"` - CountryName pgtype.Text `json:"country_name"` - OrdinaryTime pgtype.Text `json:"ordinary_time"` - RunningScore pgtype.Text `json:"running_score"` - Halftime pgtype.Text `json:"halftime"` - FinalResult pgtype.Text `json:"final_result"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - CreatedAt pgtype.Timestamptz `json:"created_at"` -} - -type EnetpulseResultReferee struct { - ID int64 `json:"id"` - ResultFk string `json:"result_fk"` - RefereeFk pgtype.Text `json:"referee_fk"` - Assistant1RefereeFk pgtype.Text `json:"assistant1_referee_fk"` - Assistant2RefereeFk pgtype.Text `json:"assistant2_referee_fk"` - FourthRefereeFk pgtype.Text `json:"fourth_referee_fk"` - Var1RefereeFk pgtype.Text `json:"var1_referee_fk"` - Var2RefereeFk pgtype.Text `json:"var2_referee_fk"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - CreatedAt pgtype.Timestamptz `json:"created_at"` -} - -type EnetpulseSport struct { - ID int64 `json:"id"` - SportID string `json:"sport_id"` - Name string `json:"name"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Status pgtype.Int4 `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type EnetpulseTournament struct { - ID int64 `json:"id"` - TournamentID string `json:"tournament_id"` - Name string `json:"name"` - TournamentTemplateFk string `json:"tournament_template_fk"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Status pgtype.Int4 `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type EnetpulseTournamentStage struct { - ID int64 `json:"id"` - StageID string `json:"stage_id"` - Name string `json:"name"` - TournamentFk string `json:"tournament_fk"` - Gender pgtype.Text `json:"gender"` - CountryFk pgtype.Text `json:"country_fk"` - CountryName pgtype.Text `json:"country_name"` - StartDate pgtype.Timestamptz `json:"start_date"` - EndDate pgtype.Timestamptz `json:"end_date"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Status pgtype.Int4 `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type EnetpulseTournamentTemplate struct { - ID int64 `json:"id"` - TemplateID string `json:"template_id"` - Name string `json:"name"` - SportFk string `json:"sport_fk"` - Gender pgtype.Text `json:"gender"` - UpdatesCount pgtype.Int4 `json:"updates_count"` - LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` - Status pgtype.Int4 `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type Event struct { - ID int64 `json:"id"` - SourceEventID string `json:"source_event_id"` - SportID int32 `json:"sport_id"` - MatchName string `json:"match_name"` - HomeTeam string `json:"home_team"` - AwayTeam string `json:"away_team"` - HomeTeamID int64 `json:"home_team_id"` - AwayTeamID int64 `json:"away_team_id"` - HomeKitImage string `json:"home_kit_image"` - AwayKitImage string `json:"away_kit_image"` - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - StartTime pgtype.Timestamp `json:"start_time"` - Score pgtype.Text `json:"score"` - MatchMinute pgtype.Int4 `json:"match_minute"` - TimerStatus pgtype.Text `json:"timer_status"` - AddedTime pgtype.Int4 `json:"added_time"` - MatchPeriod pgtype.Int4 `json:"match_period"` - IsLive bool `json:"is_live"` - Status string `json:"status"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - Source string `json:"source"` - DefaultIsActive bool `json:"default_is_active"` - DefaultIsFeatured bool `json:"default_is_featured"` - DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` - IsMonitored bool `json:"is_monitored"` -} - -type EventBetStat struct { - EventID int64 `json:"event_id"` - NumberOfBets pgtype.Int8 `json:"number_of_bets"` - TotalAmount pgtype.Int8 `json:"total_amount"` - AvgBetAmount pgtype.Float8 `json:"avg_bet_amount"` - TotalPotentialWinnings pgtype.Int8 `json:"total_potential_winnings"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type EventDetailed struct { - ID int64 `json:"id"` - SourceEventID string `json:"source_event_id"` - SportID int32 `json:"sport_id"` - MatchName string `json:"match_name"` - HomeTeam string `json:"home_team"` - AwayTeam string `json:"away_team"` - HomeTeamID int64 `json:"home_team_id"` - AwayTeamID int64 `json:"away_team_id"` - HomeKitImage string `json:"home_kit_image"` - AwayKitImage string `json:"away_kit_image"` - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - StartTime pgtype.Timestamp `json:"start_time"` - Score pgtype.Text `json:"score"` - MatchMinute pgtype.Int4 `json:"match_minute"` - TimerStatus pgtype.Text `json:"timer_status"` - AddedTime pgtype.Int4 `json:"added_time"` - MatchPeriod pgtype.Int4 `json:"match_period"` - IsLive bool `json:"is_live"` - Status string `json:"status"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - Source string `json:"source"` - DefaultIsActive bool `json:"default_is_active"` - DefaultIsFeatured bool `json:"default_is_featured"` - DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` - IsMonitored bool `json:"is_monitored"` - LeagueCc pgtype.Text `json:"league_cc"` - TotalOutcomes int64 `json:"total_outcomes"` - NumberOfBets int64 `json:"number_of_bets"` - TotalAmount int64 `json:"total_amount"` - AvgBetAmount float64 `json:"avg_bet_amount"` - TotalPotentialWinnings int64 `json:"total_potential_winnings"` -} - -type EventHistory struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - Status string `json:"status"` - CreatedAt pgtype.Timestamp `json:"created_at"` -} - -type EventWithSetting struct { - ID int64 `json:"id"` - SourceEventID string `json:"source_event_id"` - SportID int32 `json:"sport_id"` - MatchName string `json:"match_name"` - HomeTeam string `json:"home_team"` - AwayTeam string `json:"away_team"` - HomeTeamID int64 `json:"home_team_id"` - AwayTeamID int64 `json:"away_team_id"` - HomeKitImage string `json:"home_kit_image"` - AwayKitImage string `json:"away_kit_image"` - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - StartTime pgtype.Timestamp `json:"start_time"` - Score pgtype.Text `json:"score"` - MatchMinute pgtype.Int4 `json:"match_minute"` - TimerStatus pgtype.Text `json:"timer_status"` - AddedTime pgtype.Int4 `json:"added_time"` - MatchPeriod pgtype.Int4 `json:"match_period"` - IsLive bool `json:"is_live"` - Status string `json:"status"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - Source string `json:"source"` - DefaultIsActive bool `json:"default_is_active"` - DefaultIsFeatured bool `json:"default_is_featured"` - DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` - IsMonitored bool `json:"is_monitored"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsFeatured bool `json:"is_featured"` - WinningUpperLimit int64 `json:"winning_upper_limit"` - CompanyUpdatedAt pgtype.Timestamp `json:"company_updated_at"` - LeagueCc pgtype.Text `json:"league_cc"` - TotalOutcomes int64 `json:"total_outcomes"` - NumberOfBets int64 `json:"number_of_bets"` - TotalAmount int64 `json:"total_amount"` - AvgBetAmount float64 `json:"avg_bet_amount"` - TotalPotentialWinnings int64 `json:"total_potential_winnings"` -} - -type ExchangeRate struct { - ID int32 `json:"id"` - FromCurrency string `json:"from_currency"` - ToCurrency string `json:"to_currency"` - Rate pgtype.Numeric `json:"rate"` - ValidUntil pgtype.Timestamp `json:"valid_until"` - CreatedAt pgtype.Timestamp `json:"created_at"` -} - -type FavoriteGame struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - GameID int64 `json:"game_id"` - CreatedAt pgtype.Timestamp `json:"created_at"` -} - -type Flag struct { - ID int64 `json:"id"` - BetID pgtype.Int8 `json:"bet_id"` - OddsMarketID pgtype.Int8 `json:"odds_market_id"` - Reason pgtype.Text `json:"reason"` - FlaggedAt pgtype.Timestamp `json:"flagged_at"` - Resolved pgtype.Bool `json:"resolved"` -} - -type GlobalOddMarketSetting struct { - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - IsActive bool `json:"is_active"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` +type Enrollment struct { + ID int64 `json:"id"` + CourseID int64 `json:"course_id"` + StudentID int64 `json:"student_id"` + EnrolledAt pgtype.Timestamptz `json:"enrolled_at"` + CompletedAt pgtype.Timestamptz `json:"completed_at"` } type GlobalSetting struct { - Key string `json:"key"` - Value string `json:"value"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` + Key string `json:"key"` + Value string `json:"value"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } -type League struct { - ID int64 `json:"id"` - Name string `json:"name"` - ImgUrl pgtype.Text `json:"img_url"` - CountryCode pgtype.Text `json:"country_code"` - Bet365ID pgtype.Int4 `json:"bet365_id"` - SportID int32 `json:"sport_id"` - DefaultIsActive bool `json:"default_is_active"` - DefaultIsFeatured bool `json:"default_is_featured"` +type Lesson struct { + ID int64 `json:"id"` + ModuleID int64 `json:"module_id"` + Title string `json:"title"` + ContentType string `json:"content_type"` + ContentUrl pgtype.Text `json:"content_url"` + DurationMinutes pgtype.Int4 `json:"duration_minutes"` + Position int32 `json:"position"` + CreatedAt pgtype.Timestamptz `json:"created_at"` } -type LeagueWithSetting struct { - ID int64 `json:"id"` - Name string `json:"name"` - ImgUrl pgtype.Text `json:"img_url"` - CountryCode pgtype.Text `json:"country_code"` - Bet365ID pgtype.Int4 `json:"bet365_id"` - SportID int32 `json:"sport_id"` - DefaultIsActive bool `json:"default_is_active"` - DefaultIsFeatured bool `json:"default_is_featured"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsFeatured bool `json:"is_featured"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` +type LessonProgress struct { + ID int64 `json:"id"` + LessonID int64 `json:"lesson_id"` + StudentID int64 `json:"student_id"` + Completed bool `json:"completed"` + CompletedAt pgtype.Timestamptz `json:"completed_at"` } type Notification struct { - ID string `json:"id"` - RecipientID int64 `json:"recipient_id"` - Type string `json:"type"` - Level string `json:"level"` - ErrorSeverity pgtype.Text `json:"error_severity"` - Reciever string `json:"reciever"` - IsRead bool `json:"is_read"` - DeliveryStatus string `json:"delivery_status"` - DeliveryChannel pgtype.Text `json:"delivery_channel"` - Payload []byte `json:"payload"` - Priority pgtype.Int4 `json:"priority"` - Version int32 `json:"version"` - Timestamp pgtype.Timestamptz `json:"timestamp"` - Img pgtype.Text `json:"img"` - Expires pgtype.Timestamptz `json:"expires"` - Metadata []byte `json:"metadata"` + ID int64 `json:"id"` + UserID int64 `json:"user_id"` + Type string `json:"type"` + Level string `json:"level"` + Channel pgtype.Text `json:"channel"` + Title string `json:"title"` + Message string `json:"message"` + Payload []byte `json:"payload"` + IsRead bool `json:"is_read"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + ReadAt pgtype.Timestamptz `json:"read_at"` } -type OddHistory struct { - ID int64 `json:"id"` - OddsMarketID int64 `json:"odds_market_id"` - RawOddID int64 `json:"raw_odd_id"` - MarketID int64 `json:"market_id"` - EventID int64 `json:"event_id"` - OddValue float64 `json:"odd_value"` - CreatedAt pgtype.Timestamp `json:"created_at"` +type Organization struct { + ID int64 `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + OwnerID int64 `json:"owner_id"` + IsActive bool `json:"is_active"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } -type OddsMarket struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - RawOdds []byte `json:"raw_odds"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - DefaultIsActive bool `json:"default_is_active"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` -} - -type OddsMarketWithEvent struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - RawOdds []byte `json:"raw_odds"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - DefaultIsActive bool `json:"default_is_active"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - IsMonitored bool `json:"is_monitored"` - IsLive bool `json:"is_live"` - Status string `json:"status"` - Source string `json:"source"` -} - -type OddsMarketWithSetting struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - DefaultIsActive bool `json:"default_is_active"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsMarketActive bool `json:"is_market_active"` - RawOdds []byte `json:"raw_odds"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` +type OrganizationSetting struct { + OrganizationID int64 `json:"organization_id"` + Key string `json:"key"` + Value string `json:"value"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } type Otp struct { @@ -789,59 +123,21 @@ type Otp struct { Otp string `json:"otp"` Used bool `json:"used"` UsedAt pgtype.Timestamptz `json:"used_at"` - CreatedAt pgtype.Timestamptz `json:"created_at"` ExpiresAt pgtype.Timestamptz `json:"expires_at"` -} - -type Raffle struct { - ID int32 `json:"id"` - CompanyID int32 `json:"company_id"` - Name string `json:"name"` - CreatedAt pgtype.Timestamp `json:"created_at"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - TicketLimit int32 `json:"ticket_limit"` - Type string `json:"type"` - Status string `json:"status"` -} - -type RaffleGameFilter struct { - ID int32 `json:"id"` - RaffleID int32 `json:"raffle_id"` - GameID string `json:"game_id"` -} - -type RaffleSportFilter struct { - ID int32 `json:"id"` - RaffleID int32 `json:"raffle_id"` - SportID int64 `json:"sport_id"` - LeagueID int64 `json:"league_id"` -} - -type RaffleTicket struct { - ID int32 `json:"id"` - RaffleID int32 `json:"raffle_id"` - UserID int32 `json:"user_id"` - IsActive pgtype.Bool `json:"is_active"` -} - -type RaffleWinner struct { - ID int32 `json:"id"` - RaffleID int32 `json:"raffle_id"` - UserID int32 `json:"user_id"` - Rank int32 `json:"rank"` - CreatedAt pgtype.Timestamp `json:"created_at"` + CreatedAt pgtype.Timestamptz `json:"created_at"` } type ReferralCode struct { - ID int64 `json:"id"` - ReferralCode string `json:"referral_code"` - ReferrerID int64 `json:"referrer_id"` - CompanyID int64 `json:"company_id"` - IsActive bool `json:"is_active"` - NumberOfReferrals int64 `json:"number_of_referrals"` - RewardAmount int64 `json:"reward_amount"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` + ID int64 `json:"id"` + Code string `json:"code"` + ReferrerID int64 `json:"referrer_id"` + IsActive bool `json:"is_active"` + MaxUses pgtype.Int4 `json:"max_uses"` + CurrentUses int32 `json:"current_uses"` + IncentiveType string `json:"incentive_type"` + IncentiveValue pgtype.Text `json:"incentive_value"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } type RefreshToken struct { @@ -849,39 +145,8 @@ type RefreshToken struct { UserID int64 `json:"user_id"` Token string `json:"token"` ExpiresAt pgtype.Timestamptz `json:"expires_at"` - CreatedAt pgtype.Timestamptz `json:"created_at"` Revoked bool `json:"revoked"` -} - -type ReportRequest struct { - ID int64 `json:"id"` - CompanyID pgtype.Int8 `json:"company_id"` - RequestedBy pgtype.Int8 `json:"requested_by"` - FilePath pgtype.Text `json:"file_path"` - Type string `json:"type"` - Status string `json:"status"` - Metadata []byte `json:"metadata"` - RejectReason pgtype.Text `json:"reject_reason"` - CreatedAt pgtype.Timestamp `json:"created_at"` - CompletedAt pgtype.Timestamp `json:"completed_at"` -} - -type ReportRequestDetail struct { - ID int64 `json:"id"` - CompanyID pgtype.Int8 `json:"company_id"` - RequestedBy pgtype.Int8 `json:"requested_by"` - FilePath pgtype.Text `json:"file_path"` - Type string `json:"type"` - Status string `json:"status"` - Metadata []byte `json:"metadata"` - RejectReason pgtype.Text `json:"reject_reason"` - CreatedAt pgtype.Timestamp `json:"created_at"` - CompletedAt pgtype.Timestamp `json:"completed_at"` - CompanyName pgtype.Text `json:"company_name"` - CompanySlug pgtype.Text `json:"company_slug"` - RequesterFirstName pgtype.Text `json:"requester_first_name"` - RequesterLastName pgtype.Text `json:"requester_last_name"` - RequesterRole pgtype.Text `json:"requester_role"` + CreatedAt pgtype.Timestamptz `json:"created_at"` } type ReportedIssue struct { @@ -897,474 +162,32 @@ type ReportedIssue struct { UpdatedAt pgtype.Timestamp `json:"updated_at"` } -type Result struct { - ID int64 `json:"id"` - BetOutcomeID int64 `json:"bet_outcome_id"` - EventID int64 `json:"event_id"` - OddID int64 `json:"odd_id"` - MarketID int64 `json:"market_id"` - Status int32 `json:"status"` - Score pgtype.Text `json:"score"` - FullTimeScore pgtype.Text `json:"full_time_score"` - HalfTimeScore pgtype.Text `json:"half_time_score"` - Ss pgtype.Text `json:"ss"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type ResultLog struct { - ID int64 `json:"id"` - StatusNotFinishedCount int32 `json:"status_not_finished_count"` - StatusNotFinishedBets int32 `json:"status_not_finished_bets"` - StatusToBeFixedCount int32 `json:"status_to_be_fixed_count"` - StatusToBeFixedBets int32 `json:"status_to_be_fixed_bets"` - StatusPostponedCount int32 `json:"status_postponed_count"` - StatusPostponedBets int32 `json:"status_postponed_bets"` - StatusEndedCount int32 `json:"status_ended_count"` - StatusEndedBets int32 `json:"status_ended_bets"` - StatusRemovedCount int32 `json:"status_removed_count"` - StatusRemovedBets int32 `json:"status_removed_bets"` - RemovedCount int32 `json:"removed_count"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type ShopBet struct { - ID int64 `json:"id"` - ShopTransactionID int64 `json:"shop_transaction_id"` - CashoutID string `json:"cashout_id"` - CashedOutBy pgtype.Int8 `json:"cashed_out_by"` - BetID int64 `json:"bet_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - CashedOut bool `json:"cashed_out"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type ShopBetDetail struct { - ID int64 `json:"id"` - ShopTransactionID int64 `json:"shop_transaction_id"` - CashoutID string `json:"cashout_id"` - CashedOutBy pgtype.Int8 `json:"cashed_out_by"` - BetID int64 `json:"bet_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - CashedOut bool `json:"cashed_out"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - CustomerFullName string `json:"customer_full_name"` - CustomerPhoneNumber string `json:"customer_phone_number"` - BranchID int64 `json:"branch_id"` - CompanyID int64 `json:"company_id"` - Amount int64 `json:"amount"` - TransactionVerified bool `json:"transaction_verified"` - Status int32 `json:"status"` - TotalOdds float32 `json:"total_odds"` - FastCode string `json:"fast_code"` - Outcomes []BetOutcome `json:"outcomes"` -} - -type ShopDeposit struct { - ID int64 `json:"id"` - ShopTransactionID int64 `json:"shop_transaction_id"` - CustomerID int64 `json:"customer_id"` - WalletTransferID pgtype.Int8 `json:"wallet_transfer_id"` - BranchWalletID int64 `json:"branch_wallet_id"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type ShopDepositDetail struct { - ID int64 `json:"id"` - ShopTransactionID int64 `json:"shop_transaction_id"` - CustomerID int64 `json:"customer_id"` - WalletTransferID pgtype.Int8 `json:"wallet_transfer_id"` - BranchWalletID int64 `json:"branch_wallet_id"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - FullName string `json:"full_name"` - PhoneNumber string `json:"phone_number"` - BranchID int64 `json:"branch_id"` - CompanyID int64 `json:"company_id"` - Amount int64 `json:"amount"` - TransactionVerified bool `json:"transaction_verified"` -} - -type ShopTransaction struct { - ID int64 `json:"id"` - Amount int64 `json:"amount"` - BranchID int64 `json:"branch_id"` - CompanyID int64 `json:"company_id"` - UserID int64 `json:"user_id"` - Type int64 `json:"type"` - FullName string `json:"full_name"` - PhoneNumber string `json:"phone_number"` - PaymentOption int64 `json:"payment_option"` - BankCode pgtype.Text `json:"bank_code"` - BeneficiaryName pgtype.Text `json:"beneficiary_name"` - AccountName pgtype.Text `json:"account_name"` - AccountNumber pgtype.Text `json:"account_number"` - ReferenceNumber pgtype.Text `json:"reference_number"` - ApprovedBy pgtype.Int8 `json:"approved_by"` - Verified bool `json:"verified"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type ShopTransactionDetail struct { - ID int64 `json:"id"` - Amount int64 `json:"amount"` - BranchID int64 `json:"branch_id"` - CompanyID int64 `json:"company_id"` - UserID int64 `json:"user_id"` - Type int64 `json:"type"` - FullName string `json:"full_name"` - PhoneNumber string `json:"phone_number"` - PaymentOption int64 `json:"payment_option"` - BankCode pgtype.Text `json:"bank_code"` - BeneficiaryName pgtype.Text `json:"beneficiary_name"` - AccountName pgtype.Text `json:"account_name"` - AccountNumber pgtype.Text `json:"account_number"` - ReferenceNumber pgtype.Text `json:"reference_number"` - ApprovedBy pgtype.Int8 `json:"approved_by"` - Verified bool `json:"verified"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - CreatorFirstName pgtype.Text `json:"creator_first_name"` - CreatorLastName pgtype.Text `json:"creator_last_name"` - CreatorPhoneNumber pgtype.Text `json:"creator_phone_number"` - ApproverFirstName pgtype.Text `json:"approver_first_name"` - ApproverLastName pgtype.Text `json:"approver_last_name"` - ApproverPhoneNumber pgtype.Text `json:"approver_phone_number"` - BranchName pgtype.Text `json:"branch_name"` - BranchLocation pgtype.Text `json:"branch_location"` -} - -type SupportedOperation struct { - ID int64 `json:"id"` - Name string `json:"name"` - Description string `json:"description"` -} - -type Team struct { - ID int64 `json:"id"` - TeamName string `json:"team_name"` - CountryCode string `json:"country_code"` - Bet365ID pgtype.Int8 `json:"bet365_id"` - ImgUrl pgtype.Text `json:"img_url"` -} - -type Ticket struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - Amount int64 `json:"amount"` - TotalOdds float32 `json:"total_odds"` - Ip string `json:"ip"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type TicketOutcome struct { - ID int64 `json:"id"` - TicketID int64 `json:"ticket_id"` - EventID int64 `json:"event_id"` - OddID int64 `json:"odd_id"` - HomeTeamName string `json:"home_team_name"` - AwayTeamName string `json:"away_team_name"` - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - Odd float32 `json:"odd"` - OddName string `json:"odd_name"` - OddHeader string `json:"odd_header"` - OddHandicap string `json:"odd_handicap"` - Status int32 `json:"status"` - Expires pgtype.Timestamp `json:"expires"` -} - -type TicketWithOutcome struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - Amount int64 `json:"amount"` - TotalOdds float32 `json:"total_odds"` - Ip string `json:"ip"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - Outcomes []TicketOutcome `json:"outcomes"` -} - type User struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - Role string `json:"role"` - Password []byte `json:"password"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - CompanyID pgtype.Int8 `json:"company_id"` - SuspendedAt pgtype.Timestamptz `json:"suspended_at"` - Suspended bool `json:"suspended"` -} - -type UserBonuse struct { - ID int64 `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Type string `json:"type"` - UserID int64 `json:"user_id"` - RewardAmount int64 `json:"reward_amount"` - IsClaimed bool `json:"is_claimed"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - ClaimedAt pgtype.Timestamp `json:"claimed_at"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type UserGameInteraction struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - GameID int64 `json:"game_id"` - InteractionType string `json:"interaction_type"` - Amount pgtype.Numeric `json:"amount"` - DurationSeconds pgtype.Int4 `json:"duration_seconds"` - CreatedAt pgtype.Timestamptz `json:"created_at"` -} - -type UserReferral struct { ID int64 `json:"id"` - ReferredID int64 `json:"referred_id"` - ReferralCodeID int64 `json:"referral_code_id"` - CreatedAt pgtype.Timestamptz `json:"created_at"` -} - -type VirtualGame struct { - ID int64 `json:"id"` - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - Name string `json:"name"` - Category pgtype.Text `json:"category"` - DeviceType pgtype.Text `json:"device_type"` - Volatility pgtype.Text `json:"volatility"` - Rtp pgtype.Numeric `json:"rtp"` - HasDemo pgtype.Bool `json:"has_demo"` - HasFreeBets pgtype.Bool `json:"has_free_bets"` - Bets []pgtype.Numeric `json:"bets"` - Thumbnail pgtype.Text `json:"thumbnail"` - Status pgtype.Int4 `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type VirtualGameCompanyReport struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` - TotalBetAmount pgtype.Numeric `json:"total_bet_amount"` - TotalWinAmount pgtype.Numeric `json:"total_win_amount"` - NetProfit pgtype.Numeric `json:"net_profit"` - ProfitMargin pgtype.Numeric `json:"profit_margin"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + NickName pgtype.Text `json:"nick_name"` + Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + Role string `json:"role"` + Password []byte `json:"password"` + Age pgtype.Int4 `json:"age"` + EducationLevel pgtype.Text `json:"education_level"` + Country pgtype.Text `json:"country"` + Region pgtype.Text `json:"region"` + EmailVerified bool `json:"email_verified"` + PhoneVerified bool `json:"phone_verified"` + Suspended bool `json:"suspended"` + SuspendedAt pgtype.Timestamptz `json:"suspended_at"` + OrganizationID pgtype.Int8 `json:"organization_id"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` } -type VirtualGameFavourite struct { - ID int64 `json:"id"` - GameID int64 `json:"game_id"` - UserID int64 `json:"user_id"` - ProviderID string `json:"provider_id"` - CreatedAt pgtype.Timestamptz `json:"created_at"` -} - -type VirtualGameFinancialReport struct { - ID int64 `json:"id"` - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` - TotalBets pgtype.Numeric `json:"total_bets"` - TotalWins pgtype.Numeric `json:"total_wins"` - Ggr pgtype.Numeric `json:"ggr"` - Rtp pgtype.Numeric `json:"rtp"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type VirtualGameHistory struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - CompanyID pgtype.Int8 `json:"company_id"` - Provider pgtype.Text `json:"provider"` - WalletID pgtype.Int8 `json:"wallet_id"` - GameID pgtype.Int8 `json:"game_id"` - TransactionType string `json:"transaction_type"` - Amount int64 `json:"amount"` - Currency string `json:"currency"` - ExternalTransactionID string `json:"external_transaction_id"` - ReferenceTransactionID pgtype.Text `json:"reference_transaction_id"` - Status string `json:"status"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type VirtualGamePlayerActivityReport struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` - TotalDeposits pgtype.Numeric `json:"total_deposits"` - TotalWithdrawals pgtype.Numeric `json:"total_withdrawals"` - NetContribution pgtype.Numeric `json:"net_contribution"` - TotalBetAmount pgtype.Numeric `json:"total_bet_amount"` - TotalWinAmount pgtype.Numeric `json:"total_win_amount"` - NetResult pgtype.Numeric `json:"net_result"` - RoundsPlayed pgtype.Int8 `json:"rounds_played"` - AvgBetSize pgtype.Numeric `json:"avg_bet_size"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type VirtualGameProvider struct { - ID int64 `json:"id"` - ProviderID string `json:"provider_id"` - ProviderName string `json:"provider_name"` - LogoDark pgtype.Text `json:"logo_dark"` - LogoLight pgtype.Text `json:"logo_light"` - Enabled bool `json:"enabled"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type VirtualGameProviderReport struct { - ID int64 `json:"id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - TotalGamesPlayed pgtype.Int8 `json:"total_games_played"` - TotalBets pgtype.Numeric `json:"total_bets"` - TotalPayouts pgtype.Numeric `json:"total_payouts"` - TotalProfit pgtype.Numeric `json:"total_profit"` - TotalPlayers pgtype.Int8 `json:"total_players"` - ReportType pgtype.Text `json:"report_type"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type VirtualGameReport struct { - ID int64 `json:"id"` - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - TotalRounds pgtype.Int8 `json:"total_rounds"` - TotalBets pgtype.Numeric `json:"total_bets"` - TotalPayouts pgtype.Numeric `json:"total_payouts"` - TotalProfit pgtype.Numeric `json:"total_profit"` - TotalPlayers pgtype.Int8 `json:"total_players"` - ReportType pgtype.Text `json:"report_type"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type VirtualGameSession struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - GameID string `json:"game_id"` - SessionToken string `json:"session_token"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type VirtualGameTransaction struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - CompanyID pgtype.Int8 `json:"company_id"` - Provider pgtype.Text `json:"provider"` - GameID pgtype.Text `json:"game_id"` - WalletID int64 `json:"wallet_id"` - TransactionType string `json:"transaction_type"` - Amount int64 `json:"amount"` - Currency string `json:"currency"` - ExternalTransactionID string `json:"external_transaction_id"` - Status string `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -type Wallet struct { - ID int64 `json:"id"` - Balance int64 `json:"balance"` - Currency string `json:"currency"` - IsWithdraw bool `json:"is_withdraw"` - IsBettable bool `json:"is_bettable"` - IsTransferable bool `json:"is_transferable"` - UserID int64 `json:"user_id"` - Type string `json:"type"` - IsActive bool `json:"is_active"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type WalletStat struct { - WalletID int64 `json:"wallet_id"` - WalletUserID int64 `json:"wallet_user_id"` - WalletUserFirstName string `json:"wallet_user_first_name"` - WalletUserLastName string `json:"wallet_user_last_name"` - WalletType string `json:"wallet_type"` - IntervalStart pgtype.Timestamp `json:"interval_start"` - NumberOfTransactions int64 `json:"number_of_transactions"` - TotalTransactions int64 `json:"total_transactions"` - NumberOfDeposits int64 `json:"number_of_deposits"` - TotalDepositsAmount int64 `json:"total_deposits_amount"` - NumberOfWithdraws int64 `json:"number_of_withdraws"` - TotalWithdrawsAmount int64 `json:"total_withdraws_amount"` - NumberOfTransfers int64 `json:"number_of_transfers"` - TotalTransfersAmount int64 `json:"total_transfers_amount"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type WalletThresholdNotification struct { - CompanyID int64 `json:"company_id"` - Threshold float64 `json:"threshold"` - NotifiedAt pgtype.Timestamptz `json:"notified_at"` -} - -type WalletTransfer struct { - ID int64 `json:"id"` - Amount pgtype.Int8 `json:"amount"` - Message string `json:"message"` - Type pgtype.Text `json:"type"` - ReceiverWalletID pgtype.Int8 `json:"receiver_wallet_id"` - SenderWalletID pgtype.Int8 `json:"sender_wallet_id"` - CashierID pgtype.Int8 `json:"cashier_id"` - Verified pgtype.Bool `json:"verified"` - ReferenceNumber string `json:"reference_number"` - ExtReferenceNumber pgtype.Text `json:"ext_reference_number"` - SessionID pgtype.Text `json:"session_id"` - Status pgtype.Text `json:"status"` - PaymentMethod pgtype.Text `json:"payment_method"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -type WalletTransferDetail struct { - ID int64 `json:"id"` - Amount pgtype.Int8 `json:"amount"` - Message string `json:"message"` - Type pgtype.Text `json:"type"` - ReceiverWalletID pgtype.Int8 `json:"receiver_wallet_id"` - SenderWalletID pgtype.Int8 `json:"sender_wallet_id"` - CashierID pgtype.Int8 `json:"cashier_id"` - Verified pgtype.Bool `json:"verified"` - ReferenceNumber string `json:"reference_number"` - ExtReferenceNumber pgtype.Text `json:"ext_reference_number"` - SessionID pgtype.Text `json:"session_id"` - Status pgtype.Text `json:"status"` - PaymentMethod pgtype.Text `json:"payment_method"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - FirstName pgtype.Text `json:"first_name"` - LastName pgtype.Text `json:"last_name"` - PhoneNumber pgtype.Text `json:"phone_number"` +type UserReferral struct { + ID int64 `json:"id"` + ReferrerID int64 `json:"referrer_id"` + ReferredUserID int64 `json:"referred_user_id"` + ReferralCodeID int64 `json:"referral_code_id"` + CreatedAt pgtype.Timestamptz `json:"created_at"` } diff --git a/gen/db/monitor.sql.go b/gen/db/monitor.sql.go deleted file mode 100644 index a9a7ecb..0000000 --- a/gen/db/monitor.sql.go +++ /dev/null @@ -1,131 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: monitor.sql - -package dbgen - -import ( - "context" -) - -const CountThresholdNotifications = `-- name: CountThresholdNotifications :one -SELECT COUNT(*) -FROM wallet_threshold_notifications -WHERE company_id = $1 AND threshold = $2 -` - -type CountThresholdNotificationsParams struct { - CompanyID int64 `json:"company_id"` - Threshold float64 `json:"threshold"` -} - -func (q *Queries) CountThresholdNotifications(ctx context.Context, arg CountThresholdNotificationsParams) (int64, error) { - row := q.db.QueryRow(ctx, CountThresholdNotifications, arg.CompanyID, arg.Threshold) - var count int64 - err := row.Scan(&count) - return count, err -} - -const CreateThresholdNotification = `-- name: CreateThresholdNotification :exec -INSERT INTO wallet_threshold_notifications (company_id, threshold) -VALUES ($1, $2) -` - -type CreateThresholdNotificationParams struct { - CompanyID int64 `json:"company_id"` - Threshold float64 `json:"threshold"` -} - -func (q *Queries) CreateThresholdNotification(ctx context.Context, arg CreateThresholdNotificationParams) error { - _, err := q.db.Exec(ctx, CreateThresholdNotification, arg.CompanyID, arg.Threshold) - return err -} - -const GetAllCompaniesBranch = `-- name: GetAllCompaniesBranch :many -SELECT id, name, wallet_id, admin_id -FROM companies -` - -type GetAllCompaniesBranchRow struct { - ID int64 `json:"id"` - Name string `json:"name"` - WalletID int64 `json:"wallet_id"` - AdminID int64 `json:"admin_id"` -} - -func (q *Queries) GetAllCompaniesBranch(ctx context.Context) ([]GetAllCompaniesBranchRow, error) { - rows, err := q.db.Query(ctx, GetAllCompaniesBranch) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetAllCompaniesBranchRow - for rows.Next() { - var i GetAllCompaniesBranchRow - if err := rows.Scan( - &i.ID, - &i.Name, - &i.WalletID, - &i.AdminID, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBranchesByCompanyID = `-- name: GetBranchesByCompanyID :many -SELECT - id, - name, - location, - wallet_id, - branch_manager_id, - company_id, - is_self_owned -FROM branches -WHERE company_id = $1 -` - -type GetBranchesByCompanyIDRow struct { - ID int64 `json:"id"` - Name string `json:"name"` - Location string `json:"location"` - WalletID int64 `json:"wallet_id"` - BranchManagerID int64 `json:"branch_manager_id"` - CompanyID int64 `json:"company_id"` - IsSelfOwned bool `json:"is_self_owned"` -} - -func (q *Queries) GetBranchesByCompanyID(ctx context.Context, companyID int64) ([]GetBranchesByCompanyIDRow, error) { - rows, err := q.db.Query(ctx, GetBranchesByCompanyID, companyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetBranchesByCompanyIDRow - for rows.Next() { - var i GetBranchesByCompanyIDRow - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/gen/db/notification.sql.go b/gen/db/notification.sql.go deleted file mode 100644 index 3c029fa..0000000 --- a/gen/db/notification.sql.go +++ /dev/null @@ -1,446 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: notification.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CountUnreadNotifications = `-- name: CountUnreadNotifications :one -SELECT count(id) -FROM notifications -WHERE recipient_id = $1 - AND is_read = false -` - -func (q *Queries) CountUnreadNotifications(ctx context.Context, recipientID int64) (int64, error) { - row := q.db.QueryRow(ctx, CountUnreadNotifications, recipientID) - var count int64 - err := row.Scan(&count) - return count, err -} - -const CreateNotification = `-- name: CreateNotification :one -INSERT INTO notifications ( - id, - recipient_id, - type, - level, - error_severity, - reciever, - is_read, - delivery_status, - delivery_channel, - payload, - priority, - timestamp, - expires, - img, - metadata - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15 - ) -RETURNING id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata -` - -type CreateNotificationParams struct { - ID string `json:"id"` - RecipientID int64 `json:"recipient_id"` - Type string `json:"type"` - Level string `json:"level"` - ErrorSeverity pgtype.Text `json:"error_severity"` - Reciever string `json:"reciever"` - IsRead bool `json:"is_read"` - DeliveryStatus string `json:"delivery_status"` - DeliveryChannel pgtype.Text `json:"delivery_channel"` - Payload []byte `json:"payload"` - Priority pgtype.Int4 `json:"priority"` - Timestamp pgtype.Timestamptz `json:"timestamp"` - Expires pgtype.Timestamptz `json:"expires"` - Img pgtype.Text `json:"img"` - Metadata []byte `json:"metadata"` -} - -func (q *Queries) CreateNotification(ctx context.Context, arg CreateNotificationParams) (Notification, error) { - row := q.db.QueryRow(ctx, CreateNotification, - arg.ID, - arg.RecipientID, - arg.Type, - arg.Level, - arg.ErrorSeverity, - arg.Reciever, - arg.IsRead, - arg.DeliveryStatus, - arg.DeliveryChannel, - arg.Payload, - arg.Priority, - arg.Timestamp, - arg.Expires, - arg.Img, - arg.Metadata, - ) - var i Notification - err := row.Scan( - &i.ID, - &i.RecipientID, - &i.Type, - &i.Level, - &i.ErrorSeverity, - &i.Reciever, - &i.IsRead, - &i.DeliveryStatus, - &i.DeliveryChannel, - &i.Payload, - &i.Priority, - &i.Version, - &i.Timestamp, - &i.Img, - &i.Expires, - &i.Metadata, - ) - return i, err -} - -const DeleteOldNotifications = `-- name: DeleteOldNotifications :exec -DELETE FROM notifications -WHERE expires < now() -` - -func (q *Queries) DeleteOldNotifications(ctx context.Context) error { - _, err := q.db.Exec(ctx, DeleteOldNotifications) - return err -} - -const GetAllNotifications = `-- name: GetAllNotifications :many -SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata -FROM notifications -ORDER BY timestamp DESC -LIMIT $1 OFFSET $2 -` - -type GetAllNotificationsParams struct { - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -func (q *Queries) GetAllNotifications(ctx context.Context, arg GetAllNotificationsParams) ([]Notification, error) { - rows, err := q.db.Query(ctx, GetAllNotifications, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Notification - for rows.Next() { - var i Notification - if err := rows.Scan( - &i.ID, - &i.RecipientID, - &i.Type, - &i.Level, - &i.ErrorSeverity, - &i.Reciever, - &i.IsRead, - &i.DeliveryStatus, - &i.DeliveryChannel, - &i.Payload, - &i.Priority, - &i.Version, - &i.Timestamp, - &i.Img, - &i.Expires, - &i.Metadata, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetNotification = `-- name: GetNotification :one -SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata -FROM notifications -WHERE id = $1 -LIMIT 1 -` - -func (q *Queries) GetNotification(ctx context.Context, id string) (Notification, error) { - row := q.db.QueryRow(ctx, GetNotification, id) - var i Notification - err := row.Scan( - &i.ID, - &i.RecipientID, - &i.Type, - &i.Level, - &i.ErrorSeverity, - &i.Reciever, - &i.IsRead, - &i.DeliveryStatus, - &i.DeliveryChannel, - &i.Payload, - &i.Priority, - &i.Version, - &i.Timestamp, - &i.Img, - &i.Expires, - &i.Metadata, - ) - return i, err -} - -const GetNotificationCounts = `-- name: GetNotificationCounts :many -SELECT COUNT(*) as total, - COUNT( - CASE - WHEN is_read = true THEN 1 - END - ) as read, - COUNT( - CASE - WHEN is_read = false THEN 1 - END - ) as unread -FROM notifications -` - -type GetNotificationCountsRow struct { - Total int64 `json:"total"` - Read int64 `json:"read"` - Unread int64 `json:"unread"` -} - -func (q *Queries) GetNotificationCounts(ctx context.Context) ([]GetNotificationCountsRow, error) { - rows, err := q.db.Query(ctx, GetNotificationCounts) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetNotificationCountsRow - for rows.Next() { - var i GetNotificationCountsRow - if err := rows.Scan(&i.Total, &i.Read, &i.Unread); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetTotalNotificationCount = `-- name: GetTotalNotificationCount :one -SELECT COUNT(*) -FROM notifications -` - -func (q *Queries) GetTotalNotificationCount(ctx context.Context) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalNotificationCount) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetUserNotificationCount = `-- name: GetUserNotificationCount :one -SELECT COUNT(*) -FROM notifications -WHERE recipient_id = $1 -` - -func (q *Queries) GetUserNotificationCount(ctx context.Context, recipientID int64) (int64, error) { - row := q.db.QueryRow(ctx, GetUserNotificationCount, recipientID) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetUserNotifications = `-- name: GetUserNotifications :many -SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata -FROM notifications -WHERE recipient_id = $1 -ORDER BY timestamp DESC -LIMIT $2 OFFSET $3 -` - -type GetUserNotificationsParams struct { - RecipientID int64 `json:"recipient_id"` - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -func (q *Queries) GetUserNotifications(ctx context.Context, arg GetUserNotificationsParams) ([]Notification, error) { - rows, err := q.db.Query(ctx, GetUserNotifications, arg.RecipientID, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Notification - for rows.Next() { - var i Notification - if err := rows.Scan( - &i.ID, - &i.RecipientID, - &i.Type, - &i.Level, - &i.ErrorSeverity, - &i.Reciever, - &i.IsRead, - &i.DeliveryStatus, - &i.DeliveryChannel, - &i.Payload, - &i.Priority, - &i.Version, - &i.Timestamp, - &i.Img, - &i.Expires, - &i.Metadata, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const ListFailedNotifications = `-- name: ListFailedNotifications :many -SELECT id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata -FROM notifications -WHERE delivery_status = 'failed' - AND timestamp < NOW() - INTERVAL '1 hour' -ORDER BY timestamp ASC -LIMIT $1 -` - -func (q *Queries) ListFailedNotifications(ctx context.Context, limit int32) ([]Notification, error) { - rows, err := q.db.Query(ctx, ListFailedNotifications, limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Notification - for rows.Next() { - var i Notification - if err := rows.Scan( - &i.ID, - &i.RecipientID, - &i.Type, - &i.Level, - &i.ErrorSeverity, - &i.Reciever, - &i.IsRead, - &i.DeliveryStatus, - &i.DeliveryChannel, - &i.Payload, - &i.Priority, - &i.Version, - &i.Timestamp, - &i.Img, - &i.Expires, - &i.Metadata, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const ListRecipientIDsByReceiver = `-- name: ListRecipientIDsByReceiver :many -SELECT recipient_id -FROM notifications -WHERE reciever = $1 -` - -func (q *Queries) ListRecipientIDsByReceiver(ctx context.Context, reciever string) ([]int64, error) { - rows, err := q.db.Query(ctx, ListRecipientIDsByReceiver, reciever) - if err != nil { - return nil, err - } - defer rows.Close() - var items []int64 - for rows.Next() { - var recipient_id int64 - if err := rows.Scan(&recipient_id); err != nil { - return nil, err - } - items = append(items, recipient_id) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateNotificationStatus = `-- name: UpdateNotificationStatus :one -UPDATE notifications -SET delivery_status = $2, - is_read = $3, - metadata = $4 -WHERE id = $1 -RETURNING id, recipient_id, type, level, error_severity, reciever, is_read, delivery_status, delivery_channel, payload, priority, version, timestamp, img, expires, metadata -` - -type UpdateNotificationStatusParams struct { - ID string `json:"id"` - DeliveryStatus string `json:"delivery_status"` - IsRead bool `json:"is_read"` - Metadata []byte `json:"metadata"` -} - -func (q *Queries) UpdateNotificationStatus(ctx context.Context, arg UpdateNotificationStatusParams) (Notification, error) { - row := q.db.QueryRow(ctx, UpdateNotificationStatus, - arg.ID, - arg.DeliveryStatus, - arg.IsRead, - arg.Metadata, - ) - var i Notification - err := row.Scan( - &i.ID, - &i.RecipientID, - &i.Type, - &i.Level, - &i.ErrorSeverity, - &i.Reciever, - &i.IsRead, - &i.DeliveryStatus, - &i.DeliveryChannel, - &i.Payload, - &i.Priority, - &i.Version, - &i.Timestamp, - &i.Img, - &i.Expires, - &i.Metadata, - ) - return i, err -} diff --git a/gen/db/odd_history.sql.go b/gen/db/odd_history.sql.go deleted file mode 100644 index 3fe7dd9..0000000 --- a/gen/db/odd_history.sql.go +++ /dev/null @@ -1,203 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: odd_history.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetAllOddHistory = `-- name: GetAllOddHistory :many -SELECT id, odds_market_id, raw_odd_id, market_id, event_id, odd_value, created_at -FROM odd_history -WHERE ( - odds_market_id = $1 - OR $1 IS NULL - ) - AND ( - market_id = $2 - OR $2 IS NULL - ) - AND ( - raw_odd_id = $3 - OR $3 IS NULL - ) - AND ( - event_id = $4 - OR $4 IS NULL - ) - AND ( - created_at > $5 - OR $5 IS NULL - ) - AND ( - created_at < $6 - OR $6 IS NULL - ) -` - -type GetAllOddHistoryParams struct { - OddID pgtype.Int8 `json:"odd_id"` - MarketID pgtype.Int8 `json:"market_id"` - RawOddID pgtype.Int8 `json:"raw_odd_id"` - EventID pgtype.Int8 `json:"event_id"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetAllOddHistory(ctx context.Context, arg GetAllOddHistoryParams) ([]OddHistory, error) { - rows, err := q.db.Query(ctx, GetAllOddHistory, - arg.OddID, - arg.MarketID, - arg.RawOddID, - arg.EventID, - arg.CreatedBefore, - arg.CreatedAfter, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []OddHistory - for rows.Next() { - var i OddHistory - if err := rows.Scan( - &i.ID, - &i.OddsMarketID, - &i.RawOddID, - &i.MarketID, - &i.EventID, - &i.OddValue, - &i.CreatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetInitialOddPerDay = `-- name: GetInitialOddPerDay :many -SELECT DISTINCT ON (DATE_TRUNC($1, created_at)) id, odds_market_id, raw_odd_id, market_id, event_id, odd_value, created_at -FROM odd_history -WHERE ( - odds_market_id = $2 - OR $2 IS NULL - ) - AND ( - market_id = $3 - OR $3 IS NULL - ) - AND ( - raw_odd_id = $4 - OR $4 IS NULL - ) - AND ( - event_id = $5 - OR $5 IS NULL - ) - AND ( - created_at > $6 - OR $6 IS NULL - ) - AND ( - created_at < $7 - OR $7 IS NULL - ) -ORDER BY DATE_TRUNC($1, created_at), - created_at ASC -` - -type GetInitialOddPerDayParams struct { - DateTrunc string `json:"date_trunc"` - OddID pgtype.Int8 `json:"odd_id"` - MarketID pgtype.Int8 `json:"market_id"` - RawOddID pgtype.Int8 `json:"raw_odd_id"` - EventID pgtype.Int8 `json:"event_id"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetInitialOddPerDay(ctx context.Context, arg GetInitialOddPerDayParams) ([]OddHistory, error) { - rows, err := q.db.Query(ctx, GetInitialOddPerDay, - arg.DateTrunc, - arg.OddID, - arg.MarketID, - arg.RawOddID, - arg.EventID, - arg.CreatedBefore, - arg.CreatedAfter, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []OddHistory - for rows.Next() { - var i OddHistory - if err := rows.Scan( - &i.ID, - &i.OddsMarketID, - &i.RawOddID, - &i.MarketID, - &i.EventID, - &i.OddValue, - &i.CreatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const InsertOddHistory = `-- name: InsertOddHistory :one -INSERT INTO odd_history ( - odds_market_id, - market_id, - raw_odd_id, - event_id, - odd_value - ) -VALUES ($1, $2, $3, $4, $5) -RETURNING id, odds_market_id, raw_odd_id, market_id, event_id, odd_value, created_at -` - -type InsertOddHistoryParams struct { - OddsMarketID int64 `json:"odds_market_id"` - MarketID int64 `json:"market_id"` - RawOddID int64 `json:"raw_odd_id"` - EventID int64 `json:"event_id"` - OddValue float64 `json:"odd_value"` -} - -func (q *Queries) InsertOddHistory(ctx context.Context, arg InsertOddHistoryParams) (OddHistory, error) { - row := q.db.QueryRow(ctx, InsertOddHistory, - arg.OddsMarketID, - arg.MarketID, - arg.RawOddID, - arg.EventID, - arg.OddValue, - ) - var i OddHistory - err := row.Scan( - &i.ID, - &i.OddsMarketID, - &i.RawOddID, - &i.MarketID, - &i.EventID, - &i.OddValue, - &i.CreatedAt, - ) - return i, err -} diff --git a/gen/db/odds.sql.go b/gen/db/odds.sql.go deleted file mode 100644 index d394b56..0000000 --- a/gen/db/odds.sql.go +++ /dev/null @@ -1,653 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: odds.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const DeleteAllCompanyOddsSetting = `-- name: DeleteAllCompanyOddsSetting :exec -DELETE FROM company_odd_settings -WHERE company_id = $1 -` - -func (q *Queries) DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error { - _, err := q.db.Exec(ctx, DeleteAllCompanyOddsSetting, companyID) - return err -} - -const DeleteCompanyOddsSettingByOddMarketID = `-- name: DeleteCompanyOddsSettingByOddMarketID :exec -DELETE FROM company_odd_settings -WHERE company_id = $1 - AND odds_market_id = $2 -` - -type DeleteCompanyOddsSettingByOddMarketIDParams struct { - CompanyID int64 `json:"company_id"` - OddsMarketID int64 `json:"odds_market_id"` -} - -func (q *Queries) DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, arg DeleteCompanyOddsSettingByOddMarketIDParams) error { - _, err := q.db.Exec(ctx, DeleteCompanyOddsSettingByOddMarketID, arg.CompanyID, arg.OddsMarketID) - return err -} - -const DeleteOddsForEvent = `-- name: DeleteOddsForEvent :exec -DELETE FROM odds_market -Where event_id = $1 -` - -func (q *Queries) DeleteOddsForEvent(ctx context.Context, eventID int64) error { - _, err := q.db.Exec(ctx, DeleteOddsForEvent, eventID) - return err -} - -const GetAllOdds = `-- name: GetAllOdds :many -SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, number_of_outcomes, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source -FROM odds_market_with_event -LIMIT $2 OFFSET $1 -` - -type GetAllOddsParams struct { - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetAllOdds(ctx context.Context, arg GetAllOddsParams) ([]OddsMarketWithEvent, error) { - rows, err := q.db.Query(ctx, GetAllOdds, arg.Offset, arg.Limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []OddsMarketWithEvent - for rows.Next() { - var i OddsMarketWithEvent - if err := rows.Scan( - &i.ID, - &i.EventID, - &i.MarketType, - &i.MarketName, - &i.MarketCategory, - &i.MarketID, - &i.RawOdds, - &i.NumberOfOutcomes, - &i.DefaultIsActive, - &i.FetchedAt, - &i.ExpiresAt, - &i.IsMonitored, - &i.IsLive, - &i.Status, - &i.Source, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllOddsWithSettings = `-- name: GetAllOddsWithSettings :many -SELECT o.id, - o.event_id, - o.market_type, - o.market_name, - o.market_category, - o.market_id, - o.number_of_outcomes, - o.default_is_active, - o.fetched_at, - o.expires_at, - cos.company_id, - COALESCE(cos.is_active, o.default_is_active) AS is_active, - COALESCE(cms.is_active, TRUE) AS is_market_active, - COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, - cos.updated_at -FROM odds_market o - LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id - AND cos.company_id = $1 - LEFT JOIN company_odd_market_settings cms ON o.market_id = cms.market_id - AND cms.company_id = $1 -LIMIT $3 OFFSET $2 -` - -type GetAllOddsWithSettingsParams struct { - CompanyID int64 `json:"company_id"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -type GetAllOddsWithSettingsRow struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - DefaultIsActive bool `json:"default_is_active"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsMarketActive bool `json:"is_market_active"` - RawOdds []byte `json:"raw_odds"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWithSettingsParams) ([]GetAllOddsWithSettingsRow, error) { - rows, err := q.db.Query(ctx, GetAllOddsWithSettings, arg.CompanyID, arg.Offset, arg.Limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetAllOddsWithSettingsRow - for rows.Next() { - var i GetAllOddsWithSettingsRow - if err := rows.Scan( - &i.ID, - &i.EventID, - &i.MarketType, - &i.MarketName, - &i.MarketCategory, - &i.MarketID, - &i.NumberOfOutcomes, - &i.DefaultIsActive, - &i.FetchedAt, - &i.ExpiresAt, - &i.CompanyID, - &i.IsActive, - &i.IsMarketActive, - &i.RawOdds, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetOddByID = `-- name: GetOddByID :one -SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, number_of_outcomes, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source -FROM odds_market_with_event -WHERE id = $1 -` - -func (q *Queries) GetOddByID(ctx context.Context, id int64) (OddsMarketWithEvent, error) { - row := q.db.QueryRow(ctx, GetOddByID, id) - var i OddsMarketWithEvent - err := row.Scan( - &i.ID, - &i.EventID, - &i.MarketType, - &i.MarketName, - &i.MarketCategory, - &i.MarketID, - &i.RawOdds, - &i.NumberOfOutcomes, - &i.DefaultIsActive, - &i.FetchedAt, - &i.ExpiresAt, - &i.IsMonitored, - &i.IsLive, - &i.Status, - &i.Source, - ) - return i, err -} - -const GetOddsByEventID = `-- name: GetOddsByEventID :many -SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, number_of_outcomes, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source -FROM odds_market_with_event -WHERE event_id = $1 - AND ( - is_live = $2 - OR $2 IS NULL - ) - AND ( - status = $3 - OR $3 IS NULL - ) - AND ( - source = $4 - OR $4 IS NULL - ) -LIMIT $6 OFFSET $5 -` - -type GetOddsByEventIDParams struct { - EventID int64 `json:"event_id"` - IsLive pgtype.Bool `json:"is_live"` - Status pgtype.Text `json:"status"` - Source pgtype.Text `json:"source"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetOddsByEventID(ctx context.Context, arg GetOddsByEventIDParams) ([]OddsMarketWithEvent, error) { - rows, err := q.db.Query(ctx, GetOddsByEventID, - arg.EventID, - arg.IsLive, - arg.Status, - arg.Source, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []OddsMarketWithEvent - for rows.Next() { - var i OddsMarketWithEvent - if err := rows.Scan( - &i.ID, - &i.EventID, - &i.MarketType, - &i.MarketName, - &i.MarketCategory, - &i.MarketID, - &i.RawOdds, - &i.NumberOfOutcomes, - &i.DefaultIsActive, - &i.FetchedAt, - &i.ExpiresAt, - &i.IsMonitored, - &i.IsLive, - &i.Status, - &i.Source, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetOddsByMarketID = `-- name: GetOddsByMarketID :one -SELECT id, event_id, market_type, market_name, market_category, market_id, raw_odds, number_of_outcomes, default_is_active, fetched_at, expires_at, is_monitored, is_live, status, source -FROM odds_market_with_event -WHERE market_id = $1 - AND event_id = $2 -` - -type GetOddsByMarketIDParams struct { - MarketID int64 `json:"market_id"` - EventID int64 `json:"event_id"` -} - -func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDParams) (OddsMarketWithEvent, error) { - row := q.db.QueryRow(ctx, GetOddsByMarketID, arg.MarketID, arg.EventID) - var i OddsMarketWithEvent - err := row.Scan( - &i.ID, - &i.EventID, - &i.MarketType, - &i.MarketName, - &i.MarketCategory, - &i.MarketID, - &i.RawOdds, - &i.NumberOfOutcomes, - &i.DefaultIsActive, - &i.FetchedAt, - &i.ExpiresAt, - &i.IsMonitored, - &i.IsLive, - &i.Status, - &i.Source, - ) - return i, err -} - -const GetOddsWithSettingsByEventID = `-- name: GetOddsWithSettingsByEventID :many -SELECT o.id, - o.event_id, - o.market_type, - o.market_name, - o.market_category, - o.market_id, - o.number_of_outcomes, - o.default_is_active, - o.fetched_at, - o.expires_at, - cos.company_id, - COALESCE(cos.is_active, o.default_is_active) AS is_active, - COALESCE(cms.is_active, TRUE) AS is_market_active, - COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, - cos.updated_at -FROM odds_market o - LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id - AND cos.company_id = $2 - LEFT JOIN company_odd_market_settings cms ON o.market_id = cms.market_id - AND cms.company_id = $2 -WHERE event_id = $1 -LIMIT $4 OFFSET $3 -` - -type GetOddsWithSettingsByEventIDParams struct { - EventID int64 `json:"event_id"` - CompanyID int64 `json:"company_id"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -type GetOddsWithSettingsByEventIDRow struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - DefaultIsActive bool `json:"default_is_active"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsMarketActive bool `json:"is_market_active"` - RawOdds []byte `json:"raw_odds"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsWithSettingsByEventIDParams) ([]GetOddsWithSettingsByEventIDRow, error) { - rows, err := q.db.Query(ctx, GetOddsWithSettingsByEventID, - arg.EventID, - arg.CompanyID, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetOddsWithSettingsByEventIDRow - for rows.Next() { - var i GetOddsWithSettingsByEventIDRow - if err := rows.Scan( - &i.ID, - &i.EventID, - &i.MarketType, - &i.MarketName, - &i.MarketCategory, - &i.MarketID, - &i.NumberOfOutcomes, - &i.DefaultIsActive, - &i.FetchedAt, - &i.ExpiresAt, - &i.CompanyID, - &i.IsActive, - &i.IsMarketActive, - &i.RawOdds, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetOddsWithSettingsByID = `-- name: GetOddsWithSettingsByID :one -SELECT o.id, - o.event_id, - o.market_type, - o.market_name, - o.market_category, - o.market_id, - o.number_of_outcomes, - o.default_is_active, - o.fetched_at, - o.expires_at, - cos.company_id, - COALESCE(cos.is_active, o.default_is_active) AS is_active, - COALESCE(cms.is_active, TRUE) AS is_market_active, - COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, - cos.updated_at -FROM odds_market o - LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id - AND cos.company_id = $2 - LEFT JOIN company_odd_market_settings cms ON o.market_id = cms.market_id - AND cms.company_id = $2 -WHERE o.id = $1 -` - -type GetOddsWithSettingsByIDParams struct { - ID int64 `json:"id"` - CompanyID int64 `json:"company_id"` -} - -type GetOddsWithSettingsByIDRow struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - DefaultIsActive bool `json:"default_is_active"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsMarketActive bool `json:"is_market_active"` - RawOdds []byte `json:"raw_odds"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetOddsWithSettingsByID(ctx context.Context, arg GetOddsWithSettingsByIDParams) (GetOddsWithSettingsByIDRow, error) { - row := q.db.QueryRow(ctx, GetOddsWithSettingsByID, arg.ID, arg.CompanyID) - var i GetOddsWithSettingsByIDRow - err := row.Scan( - &i.ID, - &i.EventID, - &i.MarketType, - &i.MarketName, - &i.MarketCategory, - &i.MarketID, - &i.NumberOfOutcomes, - &i.DefaultIsActive, - &i.FetchedAt, - &i.ExpiresAt, - &i.CompanyID, - &i.IsActive, - &i.IsMarketActive, - &i.RawOdds, - &i.UpdatedAt, - ) - return i, err -} - -const GetOddsWithSettingsByMarketID = `-- name: GetOddsWithSettingsByMarketID :one -SELECT o.id, - o.event_id, - o.market_type, - o.market_name, - o.market_category, - o.market_id, - o.number_of_outcomes, - o.default_is_active, - o.fetched_at, - o.expires_at, - cos.company_id, - COALESCE(cos.is_active, o.default_is_active) AS is_active, - COALESCE(cms.is_active, TRUE) AS is_market_active, - COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, - cos.updated_at -FROM odds_market o - LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id - AND cos.company_id = $3 - LEFT JOIN company_odd_market_settings cms ON o.market_id = cms.market_id - AND cms.company_id = $3 -WHERE o.market_id = $1 - AND event_id = $2 -` - -type GetOddsWithSettingsByMarketIDParams struct { - MarketID int64 `json:"market_id"` - EventID int64 `json:"event_id"` - CompanyID int64 `json:"company_id"` -} - -type GetOddsWithSettingsByMarketIDRow struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - DefaultIsActive bool `json:"default_is_active"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - CompanyID pgtype.Int8 `json:"company_id"` - IsActive bool `json:"is_active"` - IsMarketActive bool `json:"is_market_active"` - RawOdds []byte `json:"raw_odds"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOddsWithSettingsByMarketIDParams) (GetOddsWithSettingsByMarketIDRow, error) { - row := q.db.QueryRow(ctx, GetOddsWithSettingsByMarketID, arg.MarketID, arg.EventID, arg.CompanyID) - var i GetOddsWithSettingsByMarketIDRow - err := row.Scan( - &i.ID, - &i.EventID, - &i.MarketType, - &i.MarketName, - &i.MarketCategory, - &i.MarketID, - &i.NumberOfOutcomes, - &i.DefaultIsActive, - &i.FetchedAt, - &i.ExpiresAt, - &i.CompanyID, - &i.IsActive, - &i.IsMarketActive, - &i.RawOdds, - &i.UpdatedAt, - ) - return i, err -} - -const InsertOddsMarket = `-- name: InsertOddsMarket :exec -INSERT INTO odds_market ( - event_id, - market_type, - market_name, - market_category, - market_id, - number_of_outcomes, - raw_odds, - fetched_at, - expires_at - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9 - ) ON CONFLICT (event_id, market_id) DO -UPDATE -SET market_type = EXCLUDED.market_type, - market_name = EXCLUDED.market_name, - market_category = EXCLUDED.market_category, - raw_odds = EXCLUDED.raw_odds, - number_of_outcomes = EXCLUDED.number_of_outcomes, - fetched_at = EXCLUDED.fetched_at, - expires_at = EXCLUDED.expires_at -` - -type InsertOddsMarketParams struct { - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - RawOdds []byte `json:"raw_odds"` - FetchedAt pgtype.Timestamp `json:"fetched_at"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` -} - -func (q *Queries) InsertOddsMarket(ctx context.Context, arg InsertOddsMarketParams) error { - _, err := q.db.Exec(ctx, InsertOddsMarket, - arg.EventID, - arg.MarketType, - arg.MarketName, - arg.MarketCategory, - arg.MarketID, - arg.NumberOfOutcomes, - arg.RawOdds, - arg.FetchedAt, - arg.ExpiresAt, - ) - return err -} - -const SaveOddSettings = `-- name: SaveOddSettings :exec -INSERT INTO company_odd_settings ( - company_id, - odds_market_id, - is_active, - custom_raw_odds - ) -VALUES ($1, $2, $3, $4) ON CONFLICT (company_id, odds_market_id) DO -UPDATE -SET is_active = EXCLUDED.is_active, - custom_raw_odds = EXCLUDED.custom_raw_odds -` - -type SaveOddSettingsParams struct { - CompanyID int64 `json:"company_id"` - OddsMarketID int64 `json:"odds_market_id"` - IsActive pgtype.Bool `json:"is_active"` - CustomRawOdds []byte `json:"custom_raw_odds"` -} - -func (q *Queries) SaveOddSettings(ctx context.Context, arg SaveOddSettingsParams) error { - _, err := q.db.Exec(ctx, SaveOddSettings, - arg.CompanyID, - arg.OddsMarketID, - arg.IsActive, - arg.CustomRawOdds, - ) - return err -} - -const UpdateGlobalOddsSetting = `-- name: UpdateGlobalOddsSetting :exec -UPDATE odds_market -SET default_is_active = COALESCE($2, default_is_active) -WHERE id = $1 -` - -type UpdateGlobalOddsSettingParams struct { - ID int64 `json:"id"` - DefaultIsActive pgtype.Bool `json:"default_is_active"` -} - -func (q *Queries) UpdateGlobalOddsSetting(ctx context.Context, arg UpdateGlobalOddsSettingParams) error { - _, err := q.db.Exec(ctx, UpdateGlobalOddsSetting, arg.ID, arg.DefaultIsActive) - return err -} diff --git a/gen/db/otp.sql.go b/gen/db/otp.sql.go index 7dba175..8f7a863 100644 --- a/gen/db/otp.sql.go +++ b/gen/db/otp.sql.go @@ -50,9 +50,21 @@ type GetOtpParams struct { Medium string `json:"medium"` } -func (q *Queries) GetOtp(ctx context.Context, arg GetOtpParams) (Otp, error) { +type GetOtpRow struct { + ID int64 `json:"id"` + SentTo string `json:"sent_to"` + Medium string `json:"medium"` + OtpFor string `json:"otp_for"` + Otp string `json:"otp"` + Used bool `json:"used"` + UsedAt pgtype.Timestamptz `json:"used_at"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + ExpiresAt pgtype.Timestamptz `json:"expires_at"` +} + +func (q *Queries) GetOtp(ctx context.Context, arg GetOtpParams) (GetOtpRow, error) { row := q.db.QueryRow(ctx, GetOtp, arg.SentTo, arg.OtpFor, arg.Medium) - var i Otp + var i GetOtpRow err := row.Scan( &i.ID, &i.SentTo, diff --git a/gen/db/raffle.sql.go b/gen/db/raffle.sql.go deleted file mode 100644 index d4b85d3..0000000 --- a/gen/db/raffle.sql.go +++ /dev/null @@ -1,378 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: raffle.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const AddSportRaffleFilter = `-- name: AddSportRaffleFilter :one -INSERT INTO raffle_sport_filters (raffle_id, sport_id, league_id) -VALUES ($1, $2, $3) -RETURNING id, raffle_id, sport_id, league_id -` - -type AddSportRaffleFilterParams struct { - RaffleID int32 `json:"raffle_id"` - SportID int64 `json:"sport_id"` - LeagueID int64 `json:"league_id"` -} - -func (q *Queries) AddSportRaffleFilter(ctx context.Context, arg AddSportRaffleFilterParams) (RaffleSportFilter, error) { - row := q.db.QueryRow(ctx, AddSportRaffleFilter, arg.RaffleID, arg.SportID, arg.LeagueID) - var i RaffleSportFilter - err := row.Scan( - &i.ID, - &i.RaffleID, - &i.SportID, - &i.LeagueID, - ) - return i, err -} - -const CheckSportRaffleHasFilter = `-- name: CheckSportRaffleHasFilter :one -SELECT EXISTS ( - SELECT 1 FROM raffle_sport_filters WHERE raffle_id = $1 -) AS has_filter -` - -func (q *Queries) CheckSportRaffleHasFilter(ctx context.Context, raffleID int32) (bool, error) { - row := q.db.QueryRow(ctx, CheckSportRaffleHasFilter, raffleID) - var has_filter bool - err := row.Scan(&has_filter) - return has_filter, err -} - -const CheckValidSportRaffleFilter = `-- name: CheckValidSportRaffleFilter :one -SELECT COUNT(*) > 0 AS exists -FROM raffle_sport_filters -WHERE raffle_id = $1 - AND sport_id = $2 - AND league_id = $3 -` - -type CheckValidSportRaffleFilterParams struct { - RaffleID int32 `json:"raffle_id"` - SportID int64 `json:"sport_id"` - LeagueID int64 `json:"league_id"` -} - -func (q *Queries) CheckValidSportRaffleFilter(ctx context.Context, arg CheckValidSportRaffleFilterParams) (bool, error) { - row := q.db.QueryRow(ctx, CheckValidSportRaffleFilter, arg.RaffleID, arg.SportID, arg.LeagueID) - var exists bool - err := row.Scan(&exists) - return exists, err -} - -const CreateRaffle = `-- name: CreateRaffle :one -INSERT INTO raffles (company_id, name, expires_at, ticket_limit, type) -VALUES ($1, $2, $3, $4, $5) -RETURNING id, company_id, name, created_at, expires_at, ticket_limit, type, status -` - -type CreateRaffleParams struct { - CompanyID int32 `json:"company_id"` - Name string `json:"name"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - TicketLimit int32 `json:"ticket_limit"` - Type string `json:"type"` -} - -func (q *Queries) CreateRaffle(ctx context.Context, arg CreateRaffleParams) (Raffle, error) { - row := q.db.QueryRow(ctx, CreateRaffle, - arg.CompanyID, - arg.Name, - arg.ExpiresAt, - arg.TicketLimit, - arg.Type, - ) - var i Raffle - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.Name, - &i.CreatedAt, - &i.ExpiresAt, - &i.TicketLimit, - &i.Type, - &i.Status, - ) - return i, err -} - -const CreateRaffleTicket = `-- name: CreateRaffleTicket :one -INSERT INTO raffle_tickets (raffle_id, user_id) -VALUES ($1, $2) -RETURNING id, raffle_id, user_id, is_active -` - -type CreateRaffleTicketParams struct { - RaffleID int32 `json:"raffle_id"` - UserID int32 `json:"user_id"` -} - -func (q *Queries) CreateRaffleTicket(ctx context.Context, arg CreateRaffleTicketParams) (RaffleTicket, error) { - row := q.db.QueryRow(ctx, CreateRaffleTicket, arg.RaffleID, arg.UserID) - var i RaffleTicket - err := row.Scan( - &i.ID, - &i.RaffleID, - &i.UserID, - &i.IsActive, - ) - return i, err -} - -const CreateRaffleWinner = `-- name: CreateRaffleWinner :one -INSERT INTO raffle_winners (raffle_id, user_id, rank) -VALUES ($1, $2, $3) -RETURNING id, raffle_id, user_id, rank, created_at -` - -type CreateRaffleWinnerParams struct { - RaffleID int32 `json:"raffle_id"` - UserID int32 `json:"user_id"` - Rank int32 `json:"rank"` -} - -func (q *Queries) CreateRaffleWinner(ctx context.Context, arg CreateRaffleWinnerParams) (RaffleWinner, error) { - row := q.db.QueryRow(ctx, CreateRaffleWinner, arg.RaffleID, arg.UserID, arg.Rank) - var i RaffleWinner - err := row.Scan( - &i.ID, - &i.RaffleID, - &i.UserID, - &i.Rank, - &i.CreatedAt, - ) - return i, err -} - -const DeleteRaffle = `-- name: DeleteRaffle :one -DELETE FROM raffles -WHERE id = $1 -RETURNING id, company_id, name, created_at, expires_at, ticket_limit, type, status -` - -func (q *Queries) DeleteRaffle(ctx context.Context, id int32) (Raffle, error) { - row := q.db.QueryRow(ctx, DeleteRaffle, id) - var i Raffle - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.Name, - &i.CreatedAt, - &i.ExpiresAt, - &i.TicketLimit, - &i.Type, - &i.Status, - ) - return i, err -} - -const GetRaffleStanding = `-- name: GetRaffleStanding :many -SELECT - u.id AS user_id, - rt.raffle_id, - u.first_name, - u.last_name, - u.phone_number, - u.email, - COUNT(*) AS ticket_count -FROM raffle_tickets rt -JOIN users u ON rt.user_id = u.id -WHERE rt.is_active = true - AND rt.raffle_id = $1 -GROUP BY u.id, rt.raffle_id, u.first_name, u.last_name, u.phone_number, u.email -ORDER BY ticket_count DESC -LIMIT $2 -` - -type GetRaffleStandingParams struct { - RaffleID int32 `json:"raffle_id"` - Limit int32 `json:"limit"` -} - -type GetRaffleStandingRow struct { - UserID int64 `json:"user_id"` - RaffleID int32 `json:"raffle_id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - PhoneNumber pgtype.Text `json:"phone_number"` - Email pgtype.Text `json:"email"` - TicketCount int64 `json:"ticket_count"` -} - -func (q *Queries) GetRaffleStanding(ctx context.Context, arg GetRaffleStandingParams) ([]GetRaffleStandingRow, error) { - rows, err := q.db.Query(ctx, GetRaffleStanding, arg.RaffleID, arg.Limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetRaffleStandingRow - for rows.Next() { - var i GetRaffleStandingRow - if err := rows.Scan( - &i.UserID, - &i.RaffleID, - &i.FirstName, - &i.LastName, - &i.PhoneNumber, - &i.Email, - &i.TicketCount, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetRaffleTicketCount = `-- name: GetRaffleTicketCount :one -SELECT COUNT(*) -FROM raffle_tickets -WHERE raffle_id = $1 - AND user_id = $2 -` - -type GetRaffleTicketCountParams struct { - RaffleID int32 `json:"raffle_id"` - UserID int32 `json:"user_id"` -} - -func (q *Queries) GetRaffleTicketCount(ctx context.Context, arg GetRaffleTicketCountParams) (int64, error) { - row := q.db.QueryRow(ctx, GetRaffleTicketCount, arg.RaffleID, arg.UserID) - var count int64 - err := row.Scan(&count) - return count, err -} - -const GetRaffleTicketLimit = `-- name: GetRaffleTicketLimit :one -SELECT ticket_limit -FROM raffles -WHERE id = $1 -` - -func (q *Queries) GetRaffleTicketLimit(ctx context.Context, id int32) (int32, error) { - row := q.db.QueryRow(ctx, GetRaffleTicketLimit, id) - var ticket_limit int32 - err := row.Scan(&ticket_limit) - return ticket_limit, err -} - -const GetRafflesOfCompany = `-- name: GetRafflesOfCompany :many -SELECT id, company_id, name, created_at, expires_at, ticket_limit, type, status FROM raffles WHERE company_id = $1 -` - -func (q *Queries) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]Raffle, error) { - rows, err := q.db.Query(ctx, GetRafflesOfCompany, companyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Raffle - for rows.Next() { - var i Raffle - if err := rows.Scan( - &i.ID, - &i.CompanyID, - &i.Name, - &i.CreatedAt, - &i.ExpiresAt, - &i.TicketLimit, - &i.Type, - &i.Status, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetUserRaffleTickets = `-- name: GetUserRaffleTickets :many -SELECT - rt.id AS ticket_id, - rt.user_id, - r.name, - r.type, - r.expires_at, - r.status -FROM raffle_tickets rt -JOIN raffles r ON rt.raffle_id = r.id -WHERE rt.user_id = $1 -` - -type GetUserRaffleTicketsRow struct { - TicketID int32 `json:"ticket_id"` - UserID int32 `json:"user_id"` - Name string `json:"name"` - Type string `json:"type"` - ExpiresAt pgtype.Timestamp `json:"expires_at"` - Status string `json:"status"` -} - -func (q *Queries) GetUserRaffleTickets(ctx context.Context, userID int32) ([]GetUserRaffleTicketsRow, error) { - rows, err := q.db.Query(ctx, GetUserRaffleTickets, userID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetUserRaffleTicketsRow - for rows.Next() { - var i GetUserRaffleTicketsRow - if err := rows.Scan( - &i.TicketID, - &i.UserID, - &i.Name, - &i.Type, - &i.ExpiresAt, - &i.Status, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const SetRaffleComplete = `-- name: SetRaffleComplete :exec -UPDATE raffles -SET status = 'completed' -WHERE id = $1 -` - -func (q *Queries) SetRaffleComplete(ctx context.Context, id int32) error { - _, err := q.db.Exec(ctx, SetRaffleComplete, id) - return err -} - -const UpdateRaffleTicketStatus = `-- name: UpdateRaffleTicketStatus :exec -UPDATE raffle_tickets -SET is_active = $1 -WHERE id = $2 -` - -type UpdateRaffleTicketStatusParams struct { - IsActive pgtype.Bool `json:"is_active"` - ID int32 `json:"id"` -} - -func (q *Queries) UpdateRaffleTicketStatus(ctx context.Context, arg UpdateRaffleTicketStatusParams) error { - _, err := q.db.Exec(ctx, UpdateRaffleTicketStatus, arg.IsActive, arg.ID) - return err -} diff --git a/gen/db/referal.sql.go b/gen/db/referal.sql.go deleted file mode 100644 index caaa01a..0000000 --- a/gen/db/referal.sql.go +++ /dev/null @@ -1,254 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: referal.sql - -package dbgen - -import ( - "context" -) - -const CreateReferralCode = `-- name: CreateReferralCode :one -INSERT INTO referral_codes ( - referral_code, - referrer_id, - company_id, - number_of_referrals, - reward_amount - ) -VALUES ($1, $2, $3, $4, $5) -RETURNING id, referral_code, referrer_id, company_id, is_active, number_of_referrals, reward_amount, created_at, updated_at -` - -type CreateReferralCodeParams struct { - ReferralCode string `json:"referral_code"` - ReferrerID int64 `json:"referrer_id"` - CompanyID int64 `json:"company_id"` - NumberOfReferrals int64 `json:"number_of_referrals"` - RewardAmount int64 `json:"reward_amount"` -} - -func (q *Queries) CreateReferralCode(ctx context.Context, arg CreateReferralCodeParams) (ReferralCode, error) { - row := q.db.QueryRow(ctx, CreateReferralCode, - arg.ReferralCode, - arg.ReferrerID, - arg.CompanyID, - arg.NumberOfReferrals, - arg.RewardAmount, - ) - var i ReferralCode - err := row.Scan( - &i.ID, - &i.ReferralCode, - &i.ReferrerID, - &i.CompanyID, - &i.IsActive, - &i.NumberOfReferrals, - &i.RewardAmount, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateUserReferral = `-- name: CreateUserReferral :one -INSERT INTO user_referrals (referred_id, referral_code_id) -VALUES ($1, $2) -RETURNING id, referred_id, referral_code_id, created_at -` - -type CreateUserReferralParams struct { - ReferredID int64 `json:"referred_id"` - ReferralCodeID int64 `json:"referral_code_id"` -} - -func (q *Queries) CreateUserReferral(ctx context.Context, arg CreateUserReferralParams) (UserReferral, error) { - row := q.db.QueryRow(ctx, CreateUserReferral, arg.ReferredID, arg.ReferralCodeID) - var i UserReferral - err := row.Scan( - &i.ID, - &i.ReferredID, - &i.ReferralCodeID, - &i.CreatedAt, - ) - return i, err -} - -const GetReferralCode = `-- name: GetReferralCode :one -SELECT id, referral_code, referrer_id, company_id, is_active, number_of_referrals, reward_amount, created_at, updated_at -FROM referral_codes -WHERE referral_code = $1 -` - -func (q *Queries) GetReferralCode(ctx context.Context, referralCode string) (ReferralCode, error) { - row := q.db.QueryRow(ctx, GetReferralCode, referralCode) - var i ReferralCode - err := row.Scan( - &i.ID, - &i.ReferralCode, - &i.ReferrerID, - &i.CompanyID, - &i.IsActive, - &i.NumberOfReferrals, - &i.RewardAmount, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetReferralCodeByUser = `-- name: GetReferralCodeByUser :many -SELECt id, referral_code, referrer_id, company_id, is_active, number_of_referrals, reward_amount, created_at, updated_at -FROM referral_codes -WHERE referrer_id = $1 -` - -func (q *Queries) GetReferralCodeByUser(ctx context.Context, referrerID int64) ([]ReferralCode, error) { - rows, err := q.db.Query(ctx, GetReferralCodeByUser, referrerID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ReferralCode - for rows.Next() { - var i ReferralCode - if err := rows.Scan( - &i.ID, - &i.ReferralCode, - &i.ReferrerID, - &i.CompanyID, - &i.IsActive, - &i.NumberOfReferrals, - &i.RewardAmount, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetReferralStats = `-- name: GetReferralStats :one -SELECT COUNT(*) AS total_referrals, - COALESCE(SUM(reward_amount), 0)::bigint AS total_reward_earned -FROM user_referrals - JOIN referral_codes ON referral_codes.id = referral_code_id -WHERE referrer_id = $1 - AND company_id = $2 -` - -type GetReferralStatsParams struct { - ReferrerID int64 `json:"referrer_id"` - CompanyID int64 `json:"company_id"` -} - -type GetReferralStatsRow struct { - TotalReferrals int64 `json:"total_referrals"` - TotalRewardEarned int64 `json:"total_reward_earned"` -} - -func (q *Queries) GetReferralStats(ctx context.Context, arg GetReferralStatsParams) (GetReferralStatsRow, error) { - row := q.db.QueryRow(ctx, GetReferralStats, arg.ReferrerID, arg.CompanyID) - var i GetReferralStatsRow - err := row.Scan(&i.TotalReferrals, &i.TotalRewardEarned) - return i, err -} - -const GetUserReferral = `-- name: GetUserReferral :one -SELECT id, referred_id, referral_code_id, created_at -FROM user_referrals -WHERE referred_id = $1 -` - -func (q *Queries) GetUserReferral(ctx context.Context, referredID int64) (UserReferral, error) { - row := q.db.QueryRow(ctx, GetUserReferral, referredID) - var i UserReferral - err := row.Scan( - &i.ID, - &i.ReferredID, - &i.ReferralCodeID, - &i.CreatedAt, - ) - return i, err -} - -const GetUserReferralsByCode = `-- name: GetUserReferralsByCode :many -SELECT user_referrals.id, user_referrals.referred_id, user_referrals.referral_code_id, user_referrals.created_at -FROM user_referrals - JOIN referral_codes ON referral_codes.id = referral_code_id -WHERE referral_code = $1 -` - -func (q *Queries) GetUserReferralsByCode(ctx context.Context, referralCode string) ([]UserReferral, error) { - rows, err := q.db.Query(ctx, GetUserReferralsByCode, referralCode) - if err != nil { - return nil, err - } - defer rows.Close() - var items []UserReferral - for rows.Next() { - var i UserReferral - if err := rows.Scan( - &i.ID, - &i.ReferredID, - &i.ReferralCodeID, - &i.CreatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetUserReferralsCount = `-- name: GetUserReferralsCount :one -SELECT COUNT(*) -FROM user_referrals - JOIN referral_codes ON referral_codes.id = referral_code_id -WHERE referrer_id = $1 -` - -func (q *Queries) GetUserReferralsCount(ctx context.Context, referrerID int64) (int64, error) { - row := q.db.QueryRow(ctx, GetUserReferralsCount, referrerID) - var count int64 - err := row.Scan(&count) - return count, err -} - -const UpdateReferralCode = `-- name: UpdateReferralCode :exec -UPDATE referral_codes -SET is_active = $2, - referral_code = $3, - number_of_referrals = $4, - reward_amount = $5, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateReferralCodeParams struct { - ID int64 `json:"id"` - IsActive bool `json:"is_active"` - ReferralCode string `json:"referral_code"` - NumberOfReferrals int64 `json:"number_of_referrals"` - RewardAmount int64 `json:"reward_amount"` -} - -func (q *Queries) UpdateReferralCode(ctx context.Context, arg UpdateReferralCodeParams) error { - _, err := q.db.Exec(ctx, UpdateReferralCode, - arg.ID, - arg.IsActive, - arg.ReferralCode, - arg.NumberOfReferrals, - arg.RewardAmount, - ) - return err -} diff --git a/gen/db/report.sql.go b/gen/db/report.sql.go deleted file mode 100644 index 075b4f1..0000000 --- a/gen/db/report.sql.go +++ /dev/null @@ -1,289 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: report.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateReportRequest = `-- name: CreateReportRequest :one -INSERT INTO report_requests ( - company_id, - requested_by, - type, - metadata - ) -VALUES ($1, $2, $3, $4) -RETURNING id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at -` - -type CreateReportRequestParams struct { - CompanyID pgtype.Int8 `json:"company_id"` - RequestedBy pgtype.Int8 `json:"requested_by"` - Type string `json:"type"` - Metadata []byte `json:"metadata"` -} - -func (q *Queries) CreateReportRequest(ctx context.Context, arg CreateReportRequestParams) (ReportRequest, error) { - row := q.db.QueryRow(ctx, CreateReportRequest, - arg.CompanyID, - arg.RequestedBy, - arg.Type, - arg.Metadata, - ) - var i ReportRequest - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.RequestedBy, - &i.FilePath, - &i.Type, - &i.Status, - &i.Metadata, - &i.RejectReason, - &i.CreatedAt, - &i.CompletedAt, - ) - return i, err -} - -const GetAllReportRequests = `-- name: GetAllReportRequests :many -SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name, requester_role -FROM report_request_detail -WHERE ( - company_id = $1 - OR $1 IS NULL - ) - AND ( - type = $2 - OR $2 IS NULL - ) - AND ( - status = $3 - OR $3 IS NULL - ) - AND ( - requested_by = $4 - OR $4 IS NULL - ) -ORDER BY id DESC -LIMIT $6 OFFSET $5 -` - -type GetAllReportRequestsParams struct { - CompanyID pgtype.Int8 `json:"company_id"` - Type pgtype.Text `json:"type"` - Status pgtype.Text `json:"status"` - RequestedBy pgtype.Int8 `json:"requested_by"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetAllReportRequests(ctx context.Context, arg GetAllReportRequestsParams) ([]ReportRequestDetail, error) { - rows, err := q.db.Query(ctx, GetAllReportRequests, - arg.CompanyID, - arg.Type, - arg.Status, - arg.RequestedBy, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ReportRequestDetail - for rows.Next() { - var i ReportRequestDetail - if err := rows.Scan( - &i.ID, - &i.CompanyID, - &i.RequestedBy, - &i.FilePath, - &i.Type, - &i.Status, - &i.Metadata, - &i.RejectReason, - &i.CreatedAt, - &i.CompletedAt, - &i.CompanyName, - &i.CompanySlug, - &i.RequesterFirstName, - &i.RequesterLastName, - &i.RequesterRole, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetReportRequestByID = `-- name: GetReportRequestByID :one -SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name, requester_role -FROM report_request_detail -WHERE id = $1 -` - -func (q *Queries) GetReportRequestByID(ctx context.Context, id int64) (ReportRequestDetail, error) { - row := q.db.QueryRow(ctx, GetReportRequestByID, id) - var i ReportRequestDetail - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.RequestedBy, - &i.FilePath, - &i.Type, - &i.Status, - &i.Metadata, - &i.RejectReason, - &i.CreatedAt, - &i.CompletedAt, - &i.CompanyName, - &i.CompanySlug, - &i.RequesterFirstName, - &i.RequesterLastName, - &i.RequesterRole, - ) - return i, err -} - -const GetReportRequestByRequestedByID = `-- name: GetReportRequestByRequestedByID :many -SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name, requester_role -FROM report_request_detail -WHERE requested_by = $1 - AND ( - type = $2 - OR $2 IS NULL - ) - AND ( - status = $3 - OR $3 IS NULL - ) -ORDER BY id DESC -LIMIT $5 OFFSET $4 -` - -type GetReportRequestByRequestedByIDParams struct { - RequestedBy pgtype.Int8 `json:"requested_by"` - Type pgtype.Text `json:"type"` - Status pgtype.Text `json:"status"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetReportRequestByRequestedByID(ctx context.Context, arg GetReportRequestByRequestedByIDParams) ([]ReportRequestDetail, error) { - rows, err := q.db.Query(ctx, GetReportRequestByRequestedByID, - arg.RequestedBy, - arg.Type, - arg.Status, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ReportRequestDetail - for rows.Next() { - var i ReportRequestDetail - if err := rows.Scan( - &i.ID, - &i.CompanyID, - &i.RequestedBy, - &i.FilePath, - &i.Type, - &i.Status, - &i.Metadata, - &i.RejectReason, - &i.CreatedAt, - &i.CompletedAt, - &i.CompanyName, - &i.CompanySlug, - &i.RequesterFirstName, - &i.RequesterLastName, - &i.RequesterRole, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetTotalReportRequests = `-- name: GetTotalReportRequests :one -SELECT COUNT(id) -FROM report_request_detail -WHERE ( - company_id = $1 - OR $1 IS NULL - ) - AND ( - type = $2 - OR $2 IS NULL - ) - AND ( - status = $3 - OR $3 IS NULL - ) - AND ( - requested_by = $4 - OR $4 IS NULL - ) -` - -type GetTotalReportRequestsParams struct { - CompanyID pgtype.Int8 `json:"company_id"` - Type pgtype.Text `json:"type"` - Status pgtype.Text `json:"status"` - RequestedBy pgtype.Int8 `json:"requested_by"` -} - -func (q *Queries) GetTotalReportRequests(ctx context.Context, arg GetTotalReportRequestsParams) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalReportRequests, - arg.CompanyID, - arg.Type, - arg.Status, - arg.RequestedBy, - ) - var count int64 - err := row.Scan(&count) - return count, err -} - -const UpdateReportRequest = `-- name: UpdateReportRequest :exec -UPDATE report_requests -SET file_path = COALESCE($2, file_path), - reject_reason = COALESCE($3, reject_reason), - status = COALESCE($4, status), - completed_at = now() -WHERE id = $1 -` - -type UpdateReportRequestParams struct { - ID int64 `json:"id"` - FilePath pgtype.Text `json:"file_path"` - RejectReason pgtype.Text `json:"reject_reason"` - Status pgtype.Text `json:"status"` -} - -func (q *Queries) UpdateReportRequest(ctx context.Context, arg UpdateReportRequestParams) error { - _, err := q.db.Exec(ctx, UpdateReportRequest, - arg.ID, - arg.FilePath, - arg.RejectReason, - arg.Status, - ) - return err -} diff --git a/gen/db/result.sql.go b/gen/db/result.sql.go deleted file mode 100644 index bff7b1e..0000000 --- a/gen/db/result.sql.go +++ /dev/null @@ -1,189 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: result.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateResult = `-- name: CreateResult :one -INSERT INTO results ( - bet_outcome_id, - event_id, - odd_id, - market_id, - status, - score, - created_at, - updated_at -) VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP -) RETURNING id, bet_outcome_id, event_id, odd_id, market_id, status, score, full_time_score, half_time_score, ss, created_at, updated_at -` - -type CreateResultParams struct { - BetOutcomeID int64 `json:"bet_outcome_id"` - EventID int64 `json:"event_id"` - OddID int64 `json:"odd_id"` - MarketID int64 `json:"market_id"` - Status int32 `json:"status"` - Score pgtype.Text `json:"score"` -} - -func (q *Queries) CreateResult(ctx context.Context, arg CreateResultParams) (Result, error) { - row := q.db.QueryRow(ctx, CreateResult, - arg.BetOutcomeID, - arg.EventID, - arg.OddID, - arg.MarketID, - arg.Status, - arg.Score, - ) - var i Result - err := row.Scan( - &i.ID, - &i.BetOutcomeID, - &i.EventID, - &i.OddID, - &i.MarketID, - &i.Status, - &i.Score, - &i.FullTimeScore, - &i.HalfTimeScore, - &i.Ss, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetPendingBetOutcomes = `-- name: GetPendingBetOutcomes :many -SELECT id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires FROM bet_outcomes WHERE status = 0 AND expires <= CURRENT_TIMESTAMP -` - -func (q *Queries) GetPendingBetOutcomes(ctx context.Context) ([]BetOutcome, error) { - rows, err := q.db.Query(ctx, GetPendingBetOutcomes) - if err != nil { - return nil, err - } - defer rows.Close() - var items []BetOutcome - for rows.Next() { - var i BetOutcome - if err := rows.Scan( - &i.ID, - &i.BetID, - &i.SportID, - &i.EventID, - &i.OddID, - &i.HomeTeamName, - &i.AwayTeamName, - &i.MarketID, - &i.MarketName, - &i.Odd, - &i.OddName, - &i.OddHeader, - &i.OddHandicap, - &i.Status, - &i.Expires, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetResultByBetOutcomeID = `-- name: GetResultByBetOutcomeID :one -SELECT id, bet_outcome_id, event_id, odd_id, market_id, status, score, full_time_score, half_time_score, ss, created_at, updated_at FROM results WHERE bet_outcome_id = $1 -` - -func (q *Queries) GetResultByBetOutcomeID(ctx context.Context, betOutcomeID int64) (Result, error) { - row := q.db.QueryRow(ctx, GetResultByBetOutcomeID, betOutcomeID) - var i Result - err := row.Scan( - &i.ID, - &i.BetOutcomeID, - &i.EventID, - &i.OddID, - &i.MarketID, - &i.Status, - &i.Score, - &i.FullTimeScore, - &i.HalfTimeScore, - &i.Ss, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const InsertResult = `-- name: InsertResult :exec -INSERT INTO results ( - bet_outcome_id, - event_id, - odd_id, - market_id, - status, - score, - full_time_score, - half_time_score, - ss, - created_at, - updated_at -) VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - CURRENT_TIMESTAMP, - CURRENT_TIMESTAMP -) -` - -type InsertResultParams struct { - BetOutcomeID int64 `json:"bet_outcome_id"` - EventID int64 `json:"event_id"` - OddID int64 `json:"odd_id"` - MarketID int64 `json:"market_id"` - Status int32 `json:"status"` - Score pgtype.Text `json:"score"` - FullTimeScore pgtype.Text `json:"full_time_score"` - HalfTimeScore pgtype.Text `json:"half_time_score"` - Ss pgtype.Text `json:"ss"` -} - -func (q *Queries) InsertResult(ctx context.Context, arg InsertResultParams) error { - _, err := q.db.Exec(ctx, InsertResult, - arg.BetOutcomeID, - arg.EventID, - arg.OddID, - arg.MarketID, - arg.Status, - arg.Score, - arg.FullTimeScore, - arg.HalfTimeScore, - arg.Ss, - ) - return err -} diff --git a/gen/db/result_log.sql.go b/gen/db/result_log.sql.go deleted file mode 100644 index 468795e..0000000 --- a/gen/db/result_log.sql.go +++ /dev/null @@ -1,132 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: result_log.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateResultLog = `-- name: CreateResultLog :one -INSERT INTO result_log ( - status_not_finished_count, - status_not_finished_bets, - status_to_be_fixed_count, - status_to_be_fixed_bets, - status_postponed_count, - status_postponed_bets, - status_ended_count, - status_ended_bets, - status_removed_count, - status_removed_bets, - removed_count - ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) -RETURNING id, status_not_finished_count, status_not_finished_bets, status_to_be_fixed_count, status_to_be_fixed_bets, status_postponed_count, status_postponed_bets, status_ended_count, status_ended_bets, status_removed_count, status_removed_bets, removed_count, created_at, updated_at -` - -type CreateResultLogParams struct { - StatusNotFinishedCount int32 `json:"status_not_finished_count"` - StatusNotFinishedBets int32 `json:"status_not_finished_bets"` - StatusToBeFixedCount int32 `json:"status_to_be_fixed_count"` - StatusToBeFixedBets int32 `json:"status_to_be_fixed_bets"` - StatusPostponedCount int32 `json:"status_postponed_count"` - StatusPostponedBets int32 `json:"status_postponed_bets"` - StatusEndedCount int32 `json:"status_ended_count"` - StatusEndedBets int32 `json:"status_ended_bets"` - StatusRemovedCount int32 `json:"status_removed_count"` - StatusRemovedBets int32 `json:"status_removed_bets"` - RemovedCount int32 `json:"removed_count"` -} - -func (q *Queries) CreateResultLog(ctx context.Context, arg CreateResultLogParams) (ResultLog, error) { - row := q.db.QueryRow(ctx, CreateResultLog, - arg.StatusNotFinishedCount, - arg.StatusNotFinishedBets, - arg.StatusToBeFixedCount, - arg.StatusToBeFixedBets, - arg.StatusPostponedCount, - arg.StatusPostponedBets, - arg.StatusEndedCount, - arg.StatusEndedBets, - arg.StatusRemovedCount, - arg.StatusRemovedBets, - arg.RemovedCount, - ) - var i ResultLog - err := row.Scan( - &i.ID, - &i.StatusNotFinishedCount, - &i.StatusNotFinishedBets, - &i.StatusToBeFixedCount, - &i.StatusToBeFixedBets, - &i.StatusPostponedCount, - &i.StatusPostponedBets, - &i.StatusEndedCount, - &i.StatusEndedBets, - &i.StatusRemovedCount, - &i.StatusRemovedBets, - &i.RemovedCount, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetAllResultLog = `-- name: GetAllResultLog :many -SELECT id, status_not_finished_count, status_not_finished_bets, status_to_be_fixed_count, status_to_be_fixed_bets, status_postponed_count, status_postponed_bets, status_ended_count, status_ended_bets, status_removed_count, status_removed_bets, removed_count, created_at, updated_at -FROM result_log -WHERE ( - created_at < $1 - OR $1 IS NULL - ) - AND ( - created_at > $2 - OR $2 IS NULL - ) -ORDER BY created_at DESC -` - -type GetAllResultLogParams struct { - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetAllResultLog(ctx context.Context, arg GetAllResultLogParams) ([]ResultLog, error) { - rows, err := q.db.Query(ctx, GetAllResultLog, arg.CreatedBefore, arg.CreatedAfter) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ResultLog - for rows.Next() { - var i ResultLog - if err := rows.Scan( - &i.ID, - &i.StatusNotFinishedCount, - &i.StatusNotFinishedBets, - &i.StatusToBeFixedCount, - &i.StatusToBeFixedBets, - &i.StatusPostponedCount, - &i.StatusPostponedBets, - &i.StatusEndedCount, - &i.StatusEndedBets, - &i.StatusRemovedCount, - &i.StatusRemovedBets, - &i.RemovedCount, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/gen/db/settings.sql.go b/gen/db/settings.sql.go deleted file mode 100644 index f4261fb..0000000 --- a/gen/db/settings.sql.go +++ /dev/null @@ -1,275 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: settings.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const DeleteAllCompanySetting = `-- name: DeleteAllCompanySetting :exec -DELETE FROM company_settings -WHERE company_id = $1 -` - -func (q *Queries) DeleteAllCompanySetting(ctx context.Context, companyID int64) error { - _, err := q.db.Exec(ctx, DeleteAllCompanySetting, companyID) - return err -} - -const DeleteCompanySetting = `-- name: DeleteCompanySetting :exec -DELETE FROM company_settings -WHERE company_id = $1 - AND key = $2 -` - -type DeleteCompanySettingParams struct { - CompanyID int64 `json:"company_id"` - Key string `json:"key"` -} - -func (q *Queries) DeleteCompanySetting(ctx context.Context, arg DeleteCompanySettingParams) error { - _, err := q.db.Exec(ctx, DeleteCompanySetting, arg.CompanyID, arg.Key) - return err -} - -const GetAllCompanySettings = `-- name: GetAllCompanySettings :many -SELECT company_id, key, value, created_at, updated_at -FROM company_settings -` - -func (q *Queries) GetAllCompanySettings(ctx context.Context) ([]CompanySetting, error) { - rows, err := q.db.Query(ctx, GetAllCompanySettings) - if err != nil { - return nil, err - } - defer rows.Close() - var items []CompanySetting - for rows.Next() { - var i CompanySetting - if err := rows.Scan( - &i.CompanyID, - &i.Key, - &i.Value, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetCompanySetting = `-- name: GetCompanySetting :many -SELECT company_id, key, value, created_at, updated_at -FROM company_settings -WHERE company_id = $1 -` - -func (q *Queries) GetCompanySetting(ctx context.Context, companyID int64) ([]CompanySetting, error) { - rows, err := q.db.Query(ctx, GetCompanySetting, companyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []CompanySetting - for rows.Next() { - var i CompanySetting - if err := rows.Scan( - &i.CompanyID, - &i.Key, - &i.Value, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetCompanySettingsByKey = `-- name: GetCompanySettingsByKey :many -SELECT company_id, key, value, created_at, updated_at -FROM company_settings -WHERE key = $1 -` - -func (q *Queries) GetCompanySettingsByKey(ctx context.Context, key string) ([]CompanySetting, error) { - rows, err := q.db.Query(ctx, GetCompanySettingsByKey, key) - if err != nil { - return nil, err - } - defer rows.Close() - var items []CompanySetting - for rows.Next() { - var i CompanySetting - if err := rows.Scan( - &i.CompanyID, - &i.Key, - &i.Value, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetGlobalSetting = `-- name: GetGlobalSetting :one -SELECT key, value, created_at, updated_at -FROM global_settings -WHERE key = $1 -` - -func (q *Queries) GetGlobalSetting(ctx context.Context, key string) (GlobalSetting, error) { - row := q.db.QueryRow(ctx, GetGlobalSetting, key) - var i GlobalSetting - err := row.Scan( - &i.Key, - &i.Value, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetGlobalSettings = `-- name: GetGlobalSettings :many -SELECT key, value, created_at, updated_at -FROM global_settings -` - -func (q *Queries) GetGlobalSettings(ctx context.Context) ([]GlobalSetting, error) { - rows, err := q.db.Query(ctx, GetGlobalSettings) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GlobalSetting - for rows.Next() { - var i GlobalSetting - if err := rows.Scan( - &i.Key, - &i.Value, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetOverrideSettings = `-- name: GetOverrideSettings :many -SELECT gs.key, - gs.created_at, - gs.updated_at, - COALESCE(cs.value, gs.value) AS value -FROM global_settings gs - LEFT JOIN company_settings cs ON cs.key = gs.key - AND cs.company_id = $1 -` - -type GetOverrideSettingsRow struct { - Key string `json:"key"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - Value string `json:"value"` -} - -func (q *Queries) GetOverrideSettings(ctx context.Context, companyID int64) ([]GetOverrideSettingsRow, error) { - rows, err := q.db.Query(ctx, GetOverrideSettings, companyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetOverrideSettingsRow - for rows.Next() { - var i GetOverrideSettingsRow - if err := rows.Scan( - &i.Key, - &i.CreatedAt, - &i.UpdatedAt, - &i.Value, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const InsertCompanySetting = `-- name: InsertCompanySetting :exec -INSERT INTO company_settings (company_id, key, value) -VALUES ($1, $2, $3) ON CONFLICT (company_id, key) DO -UPDATE -SET value = EXCLUDED.value -` - -type InsertCompanySettingParams struct { - CompanyID int64 `json:"company_id"` - Key string `json:"key"` - Value string `json:"value"` -} - -func (q *Queries) InsertCompanySetting(ctx context.Context, arg InsertCompanySettingParams) error { - _, err := q.db.Exec(ctx, InsertCompanySetting, arg.CompanyID, arg.Key, arg.Value) - return err -} - -const InsertGlobalSetting = `-- name: InsertGlobalSetting :exec -INSERT INTO global_settings (key, value) -VALUES ($1, $2) ON CONFLICT (key) DO -UPDATE -SET value = EXCLUDED.value -` - -type InsertGlobalSettingParams struct { - Key string `json:"key"` - Value string `json:"value"` -} - -func (q *Queries) InsertGlobalSetting(ctx context.Context, arg InsertGlobalSettingParams) error { - _, err := q.db.Exec(ctx, InsertGlobalSetting, arg.Key, arg.Value) - return err -} - -const UpdateGlobalSetting = `-- name: UpdateGlobalSetting :exec -UPDATE global_settings -SET value = $2, - updated_at = CURRENT_TIMESTAMP -WHERE key = $1 -` - -type UpdateGlobalSettingParams struct { - Key string `json:"key"` - Value string `json:"value"` -} - -func (q *Queries) UpdateGlobalSetting(ctx context.Context, arg UpdateGlobalSettingParams) error { - _, err := q.db.Exec(ctx, UpdateGlobalSetting, arg.Key, arg.Value) - return err -} diff --git a/gen/db/shop_transactions.sql.go b/gen/db/shop_transactions.sql.go deleted file mode 100644 index 5c4b52e..0000000 --- a/gen/db/shop_transactions.sql.go +++ /dev/null @@ -1,769 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: shop_transactions.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateShopBet = `-- name: CreateShopBet :one -INSERT INTO shop_bets ( - shop_transaction_id, - cashout_id, - bet_id, - number_of_outcomes - ) -VALUES ($1, $2, $3, $4) -RETURNING id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at -` - -type CreateShopBetParams struct { - ShopTransactionID int64 `json:"shop_transaction_id"` - CashoutID string `json:"cashout_id"` - BetID int64 `json:"bet_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` -} - -func (q *Queries) CreateShopBet(ctx context.Context, arg CreateShopBetParams) (ShopBet, error) { - row := q.db.QueryRow(ctx, CreateShopBet, - arg.ShopTransactionID, - arg.CashoutID, - arg.BetID, - arg.NumberOfOutcomes, - ) - var i ShopBet - err := row.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CashoutID, - &i.CashedOutBy, - &i.BetID, - &i.NumberOfOutcomes, - &i.CashedOut, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateShopDeposit = `-- name: CreateShopDeposit :one -INSERT INTO shop_deposits ( - shop_transaction_id, - customer_id, - branch_wallet_id - ) -VALUES ($1, $2, $3) -RETURNING id, shop_transaction_id, customer_id, wallet_transfer_id, branch_wallet_id, created_at, updated_at -` - -type CreateShopDepositParams struct { - ShopTransactionID int64 `json:"shop_transaction_id"` - CustomerID int64 `json:"customer_id"` - BranchWalletID int64 `json:"branch_wallet_id"` -} - -func (q *Queries) CreateShopDeposit(ctx context.Context, arg CreateShopDepositParams) (ShopDeposit, error) { - row := q.db.QueryRow(ctx, CreateShopDeposit, arg.ShopTransactionID, arg.CustomerID, arg.BranchWalletID) - var i ShopDeposit - err := row.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CustomerID, - &i.WalletTransferID, - &i.BranchWalletID, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateShopTransaction = `-- name: CreateShopTransaction :one -INSERT INTO shop_transactions ( - amount, - branch_id, - company_id, - user_id, - type, - full_name, - phone_number, - payment_option, - bank_code, - beneficiary_name, - account_name, - account_number, - reference_number - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13 - ) -RETURNING id, amount, branch_id, company_id, user_id, type, full_name, phone_number, payment_option, bank_code, beneficiary_name, account_name, account_number, reference_number, approved_by, verified, created_at, updated_at -` - -type CreateShopTransactionParams struct { - Amount int64 `json:"amount"` - BranchID int64 `json:"branch_id"` - CompanyID int64 `json:"company_id"` - UserID int64 `json:"user_id"` - Type int64 `json:"type"` - FullName string `json:"full_name"` - PhoneNumber string `json:"phone_number"` - PaymentOption int64 `json:"payment_option"` - BankCode pgtype.Text `json:"bank_code"` - BeneficiaryName pgtype.Text `json:"beneficiary_name"` - AccountName pgtype.Text `json:"account_name"` - AccountNumber pgtype.Text `json:"account_number"` - ReferenceNumber pgtype.Text `json:"reference_number"` -} - -func (q *Queries) CreateShopTransaction(ctx context.Context, arg CreateShopTransactionParams) (ShopTransaction, error) { - row := q.db.QueryRow(ctx, CreateShopTransaction, - arg.Amount, - arg.BranchID, - arg.CompanyID, - arg.UserID, - arg.Type, - arg.FullName, - arg.PhoneNumber, - arg.PaymentOption, - arg.BankCode, - arg.BeneficiaryName, - arg.AccountName, - arg.AccountNumber, - arg.ReferenceNumber, - ) - var i ShopTransaction - err := row.Scan( - &i.ID, - &i.Amount, - &i.BranchID, - &i.CompanyID, - &i.UserID, - &i.Type, - &i.FullName, - &i.PhoneNumber, - &i.PaymentOption, - &i.BankCode, - &i.BeneficiaryName, - &i.AccountName, - &i.AccountNumber, - &i.ReferenceNumber, - &i.ApprovedBy, - &i.Verified, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetAllShopBets = `-- name: GetAllShopBets :many -SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes -FROM shop_bet_detail -WHERE ( - full_name ILIKE '%' || $1 || '%' - OR phone_number ILIKE '%' || $1 || '%' - OR $1 IS NULL - ) - AND ( - branch_id = $2 - OR $2 IS NULL - ) - AND ( - company_id = $3 - OR $3 IS NULL - ) - AND ( - created_at > $4 - OR $4 IS NULL - ) - AND ( - created_at < $5 - OR $5 IS NULL - ) -` - -type GetAllShopBetsParams struct { - Query pgtype.Text `json:"query"` - BranchID pgtype.Int8 `json:"branch_id"` - CompanyID pgtype.Int8 `json:"company_id"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetAllShopBets(ctx context.Context, arg GetAllShopBetsParams) ([]ShopBetDetail, error) { - rows, err := q.db.Query(ctx, GetAllShopBets, - arg.Query, - arg.BranchID, - arg.CompanyID, - arg.CreatedBefore, - arg.CreatedAfter, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ShopBetDetail - for rows.Next() { - var i ShopBetDetail - if err := rows.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CashoutID, - &i.CashedOutBy, - &i.BetID, - &i.NumberOfOutcomes, - &i.CashedOut, - &i.CreatedAt, - &i.UpdatedAt, - &i.CustomerFullName, - &i.CustomerPhoneNumber, - &i.BranchID, - &i.CompanyID, - &i.Amount, - &i.TransactionVerified, - &i.Status, - &i.TotalOdds, - &i.FastCode, - &i.Outcomes, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllShopDeposit = `-- name: GetAllShopDeposit :many -SELECT id, shop_transaction_id, customer_id, wallet_transfer_id, branch_wallet_id, created_at, updated_at, full_name, phone_number, branch_id, company_id, amount, transaction_verified -FROM shop_deposit_detail -WHERE ( - full_name ILIKE '%' || $1 || '%' - OR phone_number ILIKE '%' || $1 || '%' - OR $1 IS NULL - ) - AND ( - branch_id = $2 - OR $2 IS NULL - ) - AND ( - company_id = $3 - OR $3 IS NULL - ) - AND ( - created_at > $4 - OR $4 IS NULL - ) - AND ( - created_at < $5 - OR $5 IS NULL - ) -` - -type GetAllShopDepositParams struct { - Query pgtype.Text `json:"query"` - BranchID pgtype.Int8 `json:"branch_id"` - CompanyID pgtype.Int8 `json:"company_id"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetAllShopDeposit(ctx context.Context, arg GetAllShopDepositParams) ([]ShopDepositDetail, error) { - rows, err := q.db.Query(ctx, GetAllShopDeposit, - arg.Query, - arg.BranchID, - arg.CompanyID, - arg.CreatedBefore, - arg.CreatedAfter, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ShopDepositDetail - for rows.Next() { - var i ShopDepositDetail - if err := rows.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CustomerID, - &i.WalletTransferID, - &i.BranchWalletID, - &i.CreatedAt, - &i.UpdatedAt, - &i.FullName, - &i.PhoneNumber, - &i.BranchID, - &i.CompanyID, - &i.Amount, - &i.TransactionVerified, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllShopTransactions = `-- name: GetAllShopTransactions :many -SELECT id, amount, branch_id, company_id, user_id, type, full_name, phone_number, payment_option, bank_code, beneficiary_name, account_name, account_number, reference_number, approved_by, verified, created_at, updated_at, creator_first_name, creator_last_name, creator_phone_number, approver_first_name, approver_last_name, approver_phone_number, branch_name, branch_location -FROM shop_transaction_detail -wHERE ( - branch_id = $1 - OR $1 IS NULL - ) - AND ( - company_id = $2 - OR $2 IS NULL - ) - AND ( - user_id = $3 - OR $3 IS NULL - ) - AND ( - full_name ILIKE '%' || $4 || '%' - OR phone_number ILIKE '%' || $4 || '%' - OR $4 IS NULL - ) - AND ( - created_at > $5 - OR $5 IS NULL - ) - AND ( - created_at < $6 - OR $6 IS NULL - ) -` - -type GetAllShopTransactionsParams struct { - BranchID pgtype.Int8 `json:"branch_id"` - CompanyID pgtype.Int8 `json:"company_id"` - UserID pgtype.Int8 `json:"user_id"` - Query pgtype.Text `json:"query"` - CreatedBefore pgtype.Timestamp `json:"created_before"` - CreatedAfter pgtype.Timestamp `json:"created_after"` -} - -func (q *Queries) GetAllShopTransactions(ctx context.Context, arg GetAllShopTransactionsParams) ([]ShopTransactionDetail, error) { - rows, err := q.db.Query(ctx, GetAllShopTransactions, - arg.BranchID, - arg.CompanyID, - arg.UserID, - arg.Query, - arg.CreatedBefore, - arg.CreatedAfter, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ShopTransactionDetail - for rows.Next() { - var i ShopTransactionDetail - if err := rows.Scan( - &i.ID, - &i.Amount, - &i.BranchID, - &i.CompanyID, - &i.UserID, - &i.Type, - &i.FullName, - &i.PhoneNumber, - &i.PaymentOption, - &i.BankCode, - &i.BeneficiaryName, - &i.AccountName, - &i.AccountNumber, - &i.ReferenceNumber, - &i.ApprovedBy, - &i.Verified, - &i.CreatedAt, - &i.UpdatedAt, - &i.CreatorFirstName, - &i.CreatorLastName, - &i.CreatorPhoneNumber, - &i.ApproverFirstName, - &i.ApproverLastName, - &i.ApproverPhoneNumber, - &i.BranchName, - &i.BranchLocation, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetShopBetByBetID = `-- name: GetShopBetByBetID :one -SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes -FROM shop_bet_detail -WHERE bet_id = $1 -` - -func (q *Queries) GetShopBetByBetID(ctx context.Context, betID int64) (ShopBetDetail, error) { - row := q.db.QueryRow(ctx, GetShopBetByBetID, betID) - var i ShopBetDetail - err := row.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CashoutID, - &i.CashedOutBy, - &i.BetID, - &i.NumberOfOutcomes, - &i.CashedOut, - &i.CreatedAt, - &i.UpdatedAt, - &i.CustomerFullName, - &i.CustomerPhoneNumber, - &i.BranchID, - &i.CompanyID, - &i.Amount, - &i.TransactionVerified, - &i.Status, - &i.TotalOdds, - &i.FastCode, - &i.Outcomes, - ) - return i, err -} - -const GetShopBetByCashoutID = `-- name: GetShopBetByCashoutID :one -SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes -FROM shop_bet_detail -WHERE cashout_id = $1 -` - -func (q *Queries) GetShopBetByCashoutID(ctx context.Context, cashoutID string) (ShopBetDetail, error) { - row := q.db.QueryRow(ctx, GetShopBetByCashoutID, cashoutID) - var i ShopBetDetail - err := row.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CashoutID, - &i.CashedOutBy, - &i.BetID, - &i.NumberOfOutcomes, - &i.CashedOut, - &i.CreatedAt, - &i.UpdatedAt, - &i.CustomerFullName, - &i.CustomerPhoneNumber, - &i.BranchID, - &i.CompanyID, - &i.Amount, - &i.TransactionVerified, - &i.Status, - &i.TotalOdds, - &i.FastCode, - &i.Outcomes, - ) - return i, err -} - -const GetShopBetByID = `-- name: GetShopBetByID :one -SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes -FROM shop_bet_detail -WHERE id = $1 -` - -func (q *Queries) GetShopBetByID(ctx context.Context, id int64) (ShopBetDetail, error) { - row := q.db.QueryRow(ctx, GetShopBetByID, id) - var i ShopBetDetail - err := row.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CashoutID, - &i.CashedOutBy, - &i.BetID, - &i.NumberOfOutcomes, - &i.CashedOut, - &i.CreatedAt, - &i.UpdatedAt, - &i.CustomerFullName, - &i.CustomerPhoneNumber, - &i.BranchID, - &i.CompanyID, - &i.Amount, - &i.TransactionVerified, - &i.Status, - &i.TotalOdds, - &i.FastCode, - &i.Outcomes, - ) - return i, err -} - -const GetShopBetByShopTransactionID = `-- name: GetShopBetByShopTransactionID :one -SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, fast_code, outcomes -FROM shop_bet_detail -WHERE shop_transaction_id = $1 -` - -func (q *Queries) GetShopBetByShopTransactionID(ctx context.Context, shopTransactionID int64) (ShopBetDetail, error) { - row := q.db.QueryRow(ctx, GetShopBetByShopTransactionID, shopTransactionID) - var i ShopBetDetail - err := row.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CashoutID, - &i.CashedOutBy, - &i.BetID, - &i.NumberOfOutcomes, - &i.CashedOut, - &i.CreatedAt, - &i.UpdatedAt, - &i.CustomerFullName, - &i.CustomerPhoneNumber, - &i.BranchID, - &i.CompanyID, - &i.Amount, - &i.TransactionVerified, - &i.Status, - &i.TotalOdds, - &i.FastCode, - &i.Outcomes, - ) - return i, err -} - -const GetShopDepositByID = `-- name: GetShopDepositByID :one -SELECT id, shop_transaction_id, customer_id, wallet_transfer_id, branch_wallet_id, created_at, updated_at, full_name, phone_number, branch_id, company_id, amount, transaction_verified -FROM shop_deposit_detail -WHERE id = $1 -` - -func (q *Queries) GetShopDepositByID(ctx context.Context, id int64) (ShopDepositDetail, error) { - row := q.db.QueryRow(ctx, GetShopDepositByID, id) - var i ShopDepositDetail - err := row.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CustomerID, - &i.WalletTransferID, - &i.BranchWalletID, - &i.CreatedAt, - &i.UpdatedAt, - &i.FullName, - &i.PhoneNumber, - &i.BranchID, - &i.CompanyID, - &i.Amount, - &i.TransactionVerified, - ) - return i, err -} - -const GetShopDepositByShopTransactionID = `-- name: GetShopDepositByShopTransactionID :one -SELECT id, shop_transaction_id, customer_id, wallet_transfer_id, branch_wallet_id, created_at, updated_at, full_name, phone_number, branch_id, company_id, amount, transaction_verified -FROM shop_deposit_detail -WHERE shop_transaction_id = $1 -` - -func (q *Queries) GetShopDepositByShopTransactionID(ctx context.Context, shopTransactionID int64) (ShopDepositDetail, error) { - row := q.db.QueryRow(ctx, GetShopDepositByShopTransactionID, shopTransactionID) - var i ShopDepositDetail - err := row.Scan( - &i.ID, - &i.ShopTransactionID, - &i.CustomerID, - &i.WalletTransferID, - &i.BranchWalletID, - &i.CreatedAt, - &i.UpdatedAt, - &i.FullName, - &i.PhoneNumber, - &i.BranchID, - &i.CompanyID, - &i.Amount, - &i.TransactionVerified, - ) - return i, err -} - -const GetShopTransactionByBranch = `-- name: GetShopTransactionByBranch :many -SELECT id, amount, branch_id, company_id, user_id, type, full_name, phone_number, payment_option, bank_code, beneficiary_name, account_name, account_number, reference_number, approved_by, verified, created_at, updated_at, creator_first_name, creator_last_name, creator_phone_number, approver_first_name, approver_last_name, approver_phone_number, branch_name, branch_location -FROM shop_transaction_detail -WHERE branch_id = $1 -` - -func (q *Queries) GetShopTransactionByBranch(ctx context.Context, branchID int64) ([]ShopTransactionDetail, error) { - rows, err := q.db.Query(ctx, GetShopTransactionByBranch, branchID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ShopTransactionDetail - for rows.Next() { - var i ShopTransactionDetail - if err := rows.Scan( - &i.ID, - &i.Amount, - &i.BranchID, - &i.CompanyID, - &i.UserID, - &i.Type, - &i.FullName, - &i.PhoneNumber, - &i.PaymentOption, - &i.BankCode, - &i.BeneficiaryName, - &i.AccountName, - &i.AccountNumber, - &i.ReferenceNumber, - &i.ApprovedBy, - &i.Verified, - &i.CreatedAt, - &i.UpdatedAt, - &i.CreatorFirstName, - &i.CreatorLastName, - &i.CreatorPhoneNumber, - &i.ApproverFirstName, - &i.ApproverLastName, - &i.ApproverPhoneNumber, - &i.BranchName, - &i.BranchLocation, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetShopTransactionByID = `-- name: GetShopTransactionByID :one -SELECT id, amount, branch_id, company_id, user_id, type, full_name, phone_number, payment_option, bank_code, beneficiary_name, account_name, account_number, reference_number, approved_by, verified, created_at, updated_at, creator_first_name, creator_last_name, creator_phone_number, approver_first_name, approver_last_name, approver_phone_number, branch_name, branch_location -FROM shop_transaction_detail -WHERE id = $1 -` - -func (q *Queries) GetShopTransactionByID(ctx context.Context, id int64) (ShopTransactionDetail, error) { - row := q.db.QueryRow(ctx, GetShopTransactionByID, id) - var i ShopTransactionDetail - err := row.Scan( - &i.ID, - &i.Amount, - &i.BranchID, - &i.CompanyID, - &i.UserID, - &i.Type, - &i.FullName, - &i.PhoneNumber, - &i.PaymentOption, - &i.BankCode, - &i.BeneficiaryName, - &i.AccountName, - &i.AccountNumber, - &i.ReferenceNumber, - &i.ApprovedBy, - &i.Verified, - &i.CreatedAt, - &i.UpdatedAt, - &i.CreatorFirstName, - &i.CreatorLastName, - &i.CreatorPhoneNumber, - &i.ApproverFirstName, - &i.ApproverLastName, - &i.ApproverPhoneNumber, - &i.BranchName, - &i.BranchLocation, - ) - return i, err -} - -const UpdateShopBetCashOut = `-- name: UpdateShopBetCashOut :exec -UPDATE shop_bets -SET cashed_out = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateShopBetCashOutParams struct { - ID int64 `json:"id"` - CashedOut bool `json:"cashed_out"` -} - -func (q *Queries) UpdateShopBetCashOut(ctx context.Context, arg UpdateShopBetCashOutParams) error { - _, err := q.db.Exec(ctx, UpdateShopBetCashOut, arg.ID, arg.CashedOut) - return err -} - -const UpdateShopBetCashoutID = `-- name: UpdateShopBetCashoutID :exec -UPDATE shop_bets -SET cashout_id = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateShopBetCashoutIDParams struct { - ID int64 `json:"id"` - CashoutID string `json:"cashout_id"` -} - -func (q *Queries) UpdateShopBetCashoutID(ctx context.Context, arg UpdateShopBetCashoutIDParams) error { - _, err := q.db.Exec(ctx, UpdateShopBetCashoutID, arg.ID, arg.CashoutID) - return err -} - -const UpdateShopDepositTransferID = `-- name: UpdateShopDepositTransferID :exec -UPDATE shop_deposits -SET wallet_transfer_id = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateShopDepositTransferIDParams struct { - ID int64 `json:"id"` - WalletTransferID pgtype.Int8 `json:"wallet_transfer_id"` -} - -func (q *Queries) UpdateShopDepositTransferID(ctx context.Context, arg UpdateShopDepositTransferIDParams) error { - _, err := q.db.Exec(ctx, UpdateShopDepositTransferID, arg.ID, arg.WalletTransferID) - return err -} - -const UpdateShopTransactionVerified = `-- name: UpdateShopTransactionVerified :exec -UPDATE shop_transactions -SET verified = $2, - approved_by = $3, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateShopTransactionVerifiedParams struct { - ID int64 `json:"id"` - Verified bool `json:"verified"` - ApprovedBy pgtype.Int8 `json:"approved_by"` -} - -func (q *Queries) UpdateShopTransactionVerified(ctx context.Context, arg UpdateShopTransactionVerifiedParams) error { - _, err := q.db.Exec(ctx, UpdateShopTransactionVerified, arg.ID, arg.Verified, arg.ApprovedBy) - return err -} diff --git a/gen/db/ticket.sql.go b/gen/db/ticket.sql.go deleted file mode 100644 index bc9bb5f..0000000 --- a/gen/db/ticket.sql.go +++ /dev/null @@ -1,244 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: ticket.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CountTicketByIP = `-- name: CountTicketByIP :one -SELECT count(id) -FROM tickets -WHERE IP = $1 -` - -func (q *Queries) CountTicketByIP(ctx context.Context, ip string) (int64, error) { - row := q.db.QueryRow(ctx, CountTicketByIP, ip) - var count int64 - err := row.Scan(&count) - return count, err -} - -const CreateTicket = `-- name: CreateTicket :one -INSERT INTO tickets (amount, total_odds, ip, company_id) -VALUES ($1, $2, $3, $4) -RETURNING id, company_id, amount, total_odds, ip, created_at, updated_at -` - -type CreateTicketParams struct { - Amount int64 `json:"amount"` - TotalOdds float32 `json:"total_odds"` - Ip string `json:"ip"` - CompanyID int64 `json:"company_id"` -} - -func (q *Queries) CreateTicket(ctx context.Context, arg CreateTicketParams) (Ticket, error) { - row := q.db.QueryRow(ctx, CreateTicket, - arg.Amount, - arg.TotalOdds, - arg.Ip, - arg.CompanyID, - ) - var i Ticket - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.Amount, - &i.TotalOdds, - &i.Ip, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -type CreateTicketOutcomeParams struct { - TicketID int64 `json:"ticket_id"` - EventID int64 `json:"event_id"` - OddID int64 `json:"odd_id"` - HomeTeamName string `json:"home_team_name"` - AwayTeamName string `json:"away_team_name"` - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - Odd float32 `json:"odd"` - OddName string `json:"odd_name"` - OddHeader string `json:"odd_header"` - OddHandicap string `json:"odd_handicap"` - Expires pgtype.Timestamp `json:"expires"` -} - -const DeleteOldTickets = `-- name: DeleteOldTickets :exec -Delete from tickets -where created_at < now() - interval '1 day' -` - -func (q *Queries) DeleteOldTickets(ctx context.Context) error { - _, err := q.db.Exec(ctx, DeleteOldTickets) - return err -} - -const DeleteTicket = `-- name: DeleteTicket :exec -DELETE FROM tickets -WHERE id = $1 -` - -func (q *Queries) DeleteTicket(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteTicket, id) - return err -} - -const DeleteTicketOutcome = `-- name: DeleteTicketOutcome :exec -Delete from ticket_outcomes -where ticket_id = $1 -` - -func (q *Queries) DeleteTicketOutcome(ctx context.Context, ticketID int64) error { - _, err := q.db.Exec(ctx, DeleteTicketOutcome, ticketID) - return err -} - -const GetAllTickets = `-- name: GetAllTickets :many -SELECT id, company_id, amount, total_odds, ip, created_at, updated_at, outcomes -FROM ticket_with_outcomes -WHERE ( - company_id = $1 - OR $1 IS NULL - ) -` - -func (q *Queries) GetAllTickets(ctx context.Context, companyID pgtype.Int8) ([]TicketWithOutcome, error) { - rows, err := q.db.Query(ctx, GetAllTickets, companyID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []TicketWithOutcome - for rows.Next() { - var i TicketWithOutcome - if err := rows.Scan( - &i.ID, - &i.CompanyID, - &i.Amount, - &i.TotalOdds, - &i.Ip, - &i.CreatedAt, - &i.UpdatedAt, - &i.Outcomes, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllTicketsInRange = `-- name: GetAllTicketsInRange :one -SELECT COUNT(*) as total_tickets, - COALESCE(SUM(amount), 0) as total_amount -FROM tickets -WHERE created_at BETWEEN $1 AND $2 -` - -type GetAllTicketsInRangeParams struct { - CreatedAt pgtype.Timestamp `json:"created_at"` - CreatedAt_2 pgtype.Timestamp `json:"created_at_2"` -} - -type GetAllTicketsInRangeRow struct { - TotalTickets int64 `json:"total_tickets"` - TotalAmount interface{} `json:"total_amount"` -} - -func (q *Queries) GetAllTicketsInRange(ctx context.Context, arg GetAllTicketsInRangeParams) (GetAllTicketsInRangeRow, error) { - row := q.db.QueryRow(ctx, GetAllTicketsInRange, arg.CreatedAt, arg.CreatedAt_2) - var i GetAllTicketsInRangeRow - err := row.Scan(&i.TotalTickets, &i.TotalAmount) - return i, err -} - -const GetTicketByID = `-- name: GetTicketByID :one -SELECT id, company_id, amount, total_odds, ip, created_at, updated_at, outcomes -FROM ticket_with_outcomes -WHERE id = $1 -` - -func (q *Queries) GetTicketByID(ctx context.Context, id int64) (TicketWithOutcome, error) { - row := q.db.QueryRow(ctx, GetTicketByID, id) - var i TicketWithOutcome - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.Amount, - &i.TotalOdds, - &i.Ip, - &i.CreatedAt, - &i.UpdatedAt, - &i.Outcomes, - ) - return i, err -} - -const GetTicketOutcome = `-- name: GetTicketOutcome :many -SELECT id, ticket_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires -FROM ticket_outcomes -WHERE ticket_id = $1 -` - -func (q *Queries) GetTicketOutcome(ctx context.Context, ticketID int64) ([]TicketOutcome, error) { - rows, err := q.db.Query(ctx, GetTicketOutcome, ticketID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []TicketOutcome - for rows.Next() { - var i TicketOutcome - if err := rows.Scan( - &i.ID, - &i.TicketID, - &i.EventID, - &i.OddID, - &i.HomeTeamName, - &i.AwayTeamName, - &i.MarketID, - &i.MarketName, - &i.Odd, - &i.OddName, - &i.OddHeader, - &i.OddHandicap, - &i.Status, - &i.Expires, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateTicketOutcomeStatus = `-- name: UpdateTicketOutcomeStatus :exec -UPDATE ticket_outcomes -SET status = $1 -WHERE id = $2 -` - -type UpdateTicketOutcomeStatusParams struct { - Status int32 `json:"status"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateTicketOutcomeStatus(ctx context.Context, arg UpdateTicketOutcomeStatusParams) error { - _, err := q.db.Exec(ctx, UpdateTicketOutcomeStatus, arg.Status, arg.ID) - return err -} diff --git a/gen/db/transfer.sql.go b/gen/db/transfer.sql.go deleted file mode 100644 index 185225b..0000000 --- a/gen/db/transfer.sql.go +++ /dev/null @@ -1,344 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: transfer.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateTransfer = `-- name: CreateTransfer :one -INSERT INTO wallet_transfer ( - amount, - message, - type, - receiver_wallet_id, - sender_wallet_id, - cashier_id, - verified, - reference_number, - ext_reference_number, - session_id, - status, - payment_method - ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) -RETURNING id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, ext_reference_number, session_id, status, payment_method, created_at, updated_at -` - -type CreateTransferParams struct { - Amount pgtype.Int8 `json:"amount"` - Message string `json:"message"` - Type pgtype.Text `json:"type"` - ReceiverWalletID pgtype.Int8 `json:"receiver_wallet_id"` - SenderWalletID pgtype.Int8 `json:"sender_wallet_id"` - CashierID pgtype.Int8 `json:"cashier_id"` - Verified pgtype.Bool `json:"verified"` - ReferenceNumber string `json:"reference_number"` - ExtReferenceNumber pgtype.Text `json:"ext_reference_number"` - SessionID pgtype.Text `json:"session_id"` - Status pgtype.Text `json:"status"` - PaymentMethod pgtype.Text `json:"payment_method"` -} - -func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams) (WalletTransfer, error) { - row := q.db.QueryRow(ctx, CreateTransfer, - arg.Amount, - arg.Message, - arg.Type, - arg.ReceiverWalletID, - arg.SenderWalletID, - arg.CashierID, - arg.Verified, - arg.ReferenceNumber, - arg.ExtReferenceNumber, - arg.SessionID, - arg.Status, - arg.PaymentMethod, - ) - var i WalletTransfer - err := row.Scan( - &i.ID, - &i.Amount, - &i.Message, - &i.Type, - &i.ReceiverWalletID, - &i.SenderWalletID, - &i.CashierID, - &i.Verified, - &i.ReferenceNumber, - &i.ExtReferenceNumber, - &i.SessionID, - &i.Status, - &i.PaymentMethod, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetAllTransfers = `-- name: GetAllTransfers :many -SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, ext_reference_number, session_id, status, payment_method, created_at, updated_at, first_name, last_name, phone_number -FROM wallet_transfer_details -` - -func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransferDetail, error) { - rows, err := q.db.Query(ctx, GetAllTransfers) - if err != nil { - return nil, err - } - defer rows.Close() - var items []WalletTransferDetail - for rows.Next() { - var i WalletTransferDetail - if err := rows.Scan( - &i.ID, - &i.Amount, - &i.Message, - &i.Type, - &i.ReceiverWalletID, - &i.SenderWalletID, - &i.CashierID, - &i.Verified, - &i.ReferenceNumber, - &i.ExtReferenceNumber, - &i.SessionID, - &i.Status, - &i.PaymentMethod, - &i.CreatedAt, - &i.UpdatedAt, - &i.FirstName, - &i.LastName, - &i.PhoneNumber, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetTransferByID = `-- name: GetTransferByID :one -SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, ext_reference_number, session_id, status, payment_method, created_at, updated_at, first_name, last_name, phone_number -FROM wallet_transfer_details -WHERE id = $1 -` - -func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransferDetail, error) { - row := q.db.QueryRow(ctx, GetTransferByID, id) - var i WalletTransferDetail - err := row.Scan( - &i.ID, - &i.Amount, - &i.Message, - &i.Type, - &i.ReceiverWalletID, - &i.SenderWalletID, - &i.CashierID, - &i.Verified, - &i.ReferenceNumber, - &i.ExtReferenceNumber, - &i.SessionID, - &i.Status, - &i.PaymentMethod, - &i.CreatedAt, - &i.UpdatedAt, - &i.FirstName, - &i.LastName, - &i.PhoneNumber, - ) - return i, err -} - -const GetTransferByReference = `-- name: GetTransferByReference :one -SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, ext_reference_number, session_id, status, payment_method, created_at, updated_at, first_name, last_name, phone_number -FROM wallet_transfer_details -WHERE reference_number = $1 -` - -func (q *Queries) GetTransferByReference(ctx context.Context, referenceNumber string) (WalletTransferDetail, error) { - row := q.db.QueryRow(ctx, GetTransferByReference, referenceNumber) - var i WalletTransferDetail - err := row.Scan( - &i.ID, - &i.Amount, - &i.Message, - &i.Type, - &i.ReceiverWalletID, - &i.SenderWalletID, - &i.CashierID, - &i.Verified, - &i.ReferenceNumber, - &i.ExtReferenceNumber, - &i.SessionID, - &i.Status, - &i.PaymentMethod, - &i.CreatedAt, - &i.UpdatedAt, - &i.FirstName, - &i.LastName, - &i.PhoneNumber, - ) - return i, err -} - -const GetTransferStats = `-- name: GetTransferStats :one -SELECT COUNT(*) AS total_transfers, COUNT(*) FILTER ( - WHERE type = 'deposit' - ) AS total_deposits, - COUNT(*) FILTER ( - WHERE type = 'withdraw' - ) AS total_withdraw, - COUNT(*) FILTER ( - WHERE type = 'wallet' - ) AS total_wallet_to_wallet -FROM wallet_transfer -WHERE sender_wallet_id = $1 - OR receiver_wallet_id = $1 -` - -type GetTransferStatsRow struct { - TotalTransfers int64 `json:"total_transfers"` - TotalDeposits int64 `json:"total_deposits"` - TotalWithdraw int64 `json:"total_withdraw"` - TotalWalletToWallet int64 `json:"total_wallet_to_wallet"` -} - -func (q *Queries) GetTransferStats(ctx context.Context, senderWalletID pgtype.Int8) (GetTransferStatsRow, error) { - row := q.db.QueryRow(ctx, GetTransferStats, senderWalletID) - var i GetTransferStatsRow - err := row.Scan( - &i.TotalTransfers, - &i.TotalDeposits, - &i.TotalWithdraw, - &i.TotalWalletToWallet, - ) - return i, err -} - -const GetTransfersByWallet = `-- name: GetTransfersByWallet :many -SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, ext_reference_number, session_id, status, payment_method, created_at, updated_at, first_name, last_name, phone_number -FROM wallet_transfer_details -WHERE receiver_wallet_id = $1 - OR sender_wallet_id = $1 -` - -func (q *Queries) GetTransfersByWallet(ctx context.Context, receiverWalletID pgtype.Int8) ([]WalletTransferDetail, error) { - rows, err := q.db.Query(ctx, GetTransfersByWallet, receiverWalletID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []WalletTransferDetail - for rows.Next() { - var i WalletTransferDetail - if err := rows.Scan( - &i.ID, - &i.Amount, - &i.Message, - &i.Type, - &i.ReceiverWalletID, - &i.SenderWalletID, - &i.CashierID, - &i.Verified, - &i.ReferenceNumber, - &i.ExtReferenceNumber, - &i.SessionID, - &i.Status, - &i.PaymentMethod, - &i.CreatedAt, - &i.UpdatedAt, - &i.FirstName, - &i.LastName, - &i.PhoneNumber, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetWalletTransactionsInRange = `-- name: GetWalletTransactionsInRange :many -SELECT type, - COUNT(*) as count, - SUM(amount) as total_amount -FROM wallet_transfer -WHERE created_at BETWEEN $1 AND $2 -GROUP BY type -` - -type GetWalletTransactionsInRangeParams struct { - CreatedAt pgtype.Timestamp `json:"created_at"` - CreatedAt_2 pgtype.Timestamp `json:"created_at_2"` -} - -type GetWalletTransactionsInRangeRow struct { - Type pgtype.Text `json:"type"` - Count int64 `json:"count"` - TotalAmount int64 `json:"total_amount"` -} - -func (q *Queries) GetWalletTransactionsInRange(ctx context.Context, arg GetWalletTransactionsInRangeParams) ([]GetWalletTransactionsInRangeRow, error) { - rows, err := q.db.Query(ctx, GetWalletTransactionsInRange, arg.CreatedAt, arg.CreatedAt_2) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetWalletTransactionsInRangeRow - for rows.Next() { - var i GetWalletTransactionsInRangeRow - if err := rows.Scan(&i.Type, &i.Count, &i.TotalAmount); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateTransferStatus = `-- name: UpdateTransferStatus :exec -UPDATE wallet_transfer -SET status = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2 -` - -type UpdateTransferStatusParams struct { - Status pgtype.Text `json:"status"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateTransferStatus(ctx context.Context, arg UpdateTransferStatusParams) error { - _, err := q.db.Exec(ctx, UpdateTransferStatus, arg.Status, arg.ID) - return err -} - -const UpdateTransferVerification = `-- name: UpdateTransferVerification :exec -UPDATE wallet_transfer -SET verified = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2 -` - -type UpdateTransferVerificationParams struct { - Verified pgtype.Bool `json:"verified"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateTransferVerification(ctx context.Context, arg UpdateTransferVerificationParams) error { - _, err := q.db.Exec(ctx, UpdateTransferVerification, arg.Verified, arg.ID) - return err -} diff --git a/gen/db/user.sql.go b/gen/db/user.sql.go index f2f9fff..0c1f9c2 100644 --- a/gen/db/user.sql.go +++ b/gen/db/user.sql.go @@ -17,21 +17,21 @@ SELECT EXISTS ( FROM users WHERE users.phone_number = $1 AND users.phone_number IS NOT NULL - AND users.company_id = $2 + AND users.organization_id = $2 ) AS phone_exists, EXISTS ( SELECT 1 FROM users WHERE users.email = $3 AND users.email IS NOT NULL - AND users.company_id = $2 + AND users.organization_id = $2 ) AS email_exists ` type CheckPhoneEmailExistParams struct { - PhoneNumber pgtype.Text `json:"phone_number"` - CompanyID pgtype.Int8 `json:"company_id"` - Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + OrganizationID pgtype.Int8 `json:"organization_id"` + Email pgtype.Text `json:"email"` } type CheckPhoneEmailExistRow struct { @@ -40,7 +40,7 @@ type CheckPhoneEmailExistRow struct { } func (q *Queries) CheckPhoneEmailExist(ctx context.Context, arg CheckPhoneEmailExistParams) (CheckPhoneEmailExistRow, error) { - row := q.db.QueryRow(ctx, CheckPhoneEmailExist, arg.PhoneNumber, arg.CompanyID, arg.Email) + row := q.db.QueryRow(ctx, CheckPhoneEmailExist, arg.PhoneNumber, arg.OrganizationID, arg.Email) var i CheckPhoneEmailExistRow err := row.Scan(&i.PhoneExists, &i.EmailExists) return i, err @@ -50,16 +50,22 @@ const CreateUser = `-- name: CreateUser :one INSERT INTO users ( first_name, last_name, + nick_name, email, phone_number, role, password, + age, + education_level, + country, + region, email_verified, phone_verified, - created_at, - updated_at, suspended, - company_id + suspended_at, + organization_id, + created_at, + updated_at ) VALUES ( $1, @@ -73,81 +79,117 @@ VALUES ( $9, $10, $11, - $12 + $12, + $13, + $14, + $15, + $16, + $17, + $18 ) RETURNING id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, - company_id + suspended_at, + organization_id ` type CreateUserParams struct { - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - Role string `json:"role"` - Password []byte `json:"password"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - Suspended bool `json:"suspended"` - CompanyID pgtype.Int8 `json:"company_id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + NickName pgtype.Text `json:"nick_name"` + Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + Role string `json:"role"` + Password []byte `json:"password"` + Age pgtype.Int4 `json:"age"` + EducationLevel pgtype.Text `json:"education_level"` + Country pgtype.Text `json:"country"` + Region pgtype.Text `json:"region"` + EmailVerified bool `json:"email_verified"` + PhoneVerified bool `json:"phone_verified"` + Suspended bool `json:"suspended"` + SuspendedAt pgtype.Timestamptz `json:"suspended_at"` + OrganizationID pgtype.Int8 `json:"organization_id"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` } type CreateUserRow struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - Role string `json:"role"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - Suspended bool `json:"suspended"` - CompanyID pgtype.Int8 `json:"company_id"` + ID int64 `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + NickName pgtype.Text `json:"nick_name"` + Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + Role string `json:"role"` + Age pgtype.Int4 `json:"age"` + EducationLevel pgtype.Text `json:"education_level"` + Country pgtype.Text `json:"country"` + Region pgtype.Text `json:"region"` + EmailVerified bool `json:"email_verified"` + PhoneVerified bool `json:"phone_verified"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` + Suspended bool `json:"suspended"` + SuspendedAt pgtype.Timestamptz `json:"suspended_at"` + OrganizationID pgtype.Int8 `json:"organization_id"` } func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) { row := q.db.QueryRow(ctx, CreateUser, arg.FirstName, arg.LastName, + arg.NickName, arg.Email, arg.PhoneNumber, arg.Role, arg.Password, + arg.Age, + arg.EducationLevel, + arg.Country, + arg.Region, arg.EmailVerified, arg.PhoneVerified, + arg.Suspended, + arg.SuspendedAt, + arg.OrganizationID, arg.CreatedAt, arg.UpdatedAt, - arg.Suspended, - arg.CompanyID, ) var i CreateUserRow err := row.Scan( &i.ID, &i.FirstName, &i.LastName, + &i.NickName, &i.Email, &i.PhoneNumber, &i.Role, + &i.Age, + &i.EducationLevel, + &i.Country, + &i.Region, &i.EmailVerified, &i.PhoneVerified, &i.CreatedAt, &i.UpdatedAt, &i.Suspended, - &i.CompanyID, + &i.SuspendedAt, + &i.OrganizationID, ) return i, err } @@ -162,56 +204,32 @@ func (q *Queries) DeleteUser(ctx context.Context, id int64) error { return err } -const GetAdminByCompanyID = `-- name: GetAdminByCompanyID :one -SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended -FROM companies - JOIN users ON companies.admin_id = users.id -where companies.id = $1 -` - -func (q *Queries) GetAdminByCompanyID(ctx context.Context, id int64) (User, error) { - row := q.db.QueryRow(ctx, GetAdminByCompanyID, id) - var i User - err := row.Scan( - &i.ID, - &i.FirstName, - &i.LastName, - &i.Email, - &i.PhoneNumber, - &i.Role, - &i.Password, - &i.EmailVerified, - &i.PhoneVerified, - &i.CreatedAt, - &i.UpdatedAt, - &i.CompanyID, - &i.SuspendedAt, - &i.Suspended, - ) - return i, err -} - const GetAllUsers = `-- name: GetAllUsers :many SELECT id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, suspended_at, - company_id + organization_id FROM users wHERE ( role = $1 OR $1 IS NULL ) AND ( - company_id = $2 + organization_id = $2 OR $2 IS NULL ) AND ( @@ -232,35 +250,40 @@ LIMIT $7 OFFSET $6 ` type GetAllUsersParams struct { - Role string `json:"role"` - CompanyID pgtype.Int8 `json:"company_id"` - Query pgtype.Text `json:"query"` - CreatedBefore pgtype.Timestamptz `json:"created_before"` - CreatedAfter pgtype.Timestamptz `json:"created_after"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` + Role string `json:"role"` + OrganizationID pgtype.Int8 `json:"organization_id"` + Query pgtype.Text `json:"query"` + CreatedBefore pgtype.Timestamptz `json:"created_before"` + CreatedAfter pgtype.Timestamptz `json:"created_after"` + Offset pgtype.Int4 `json:"offset"` + Limit pgtype.Int4 `json:"limit"` } type GetAllUsersRow struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - Role string `json:"role"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - Suspended bool `json:"suspended"` - SuspendedAt pgtype.Timestamptz `json:"suspended_at"` - CompanyID pgtype.Int8 `json:"company_id"` + ID int64 `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + NickName pgtype.Text `json:"nick_name"` + Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + Role string `json:"role"` + Age pgtype.Int4 `json:"age"` + EducationLevel pgtype.Text `json:"education_level"` + Country pgtype.Text `json:"country"` + Region pgtype.Text `json:"region"` + EmailVerified bool `json:"email_verified"` + PhoneVerified bool `json:"phone_verified"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` + Suspended bool `json:"suspended"` + SuspendedAt pgtype.Timestamptz `json:"suspended_at"` + OrganizationID pgtype.Int8 `json:"organization_id"` } func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]GetAllUsersRow, error) { rows, err := q.db.Query(ctx, GetAllUsers, arg.Role, - arg.CompanyID, + arg.OrganizationID, arg.Query, arg.CreatedBefore, arg.CreatedAfter, @@ -278,16 +301,21 @@ func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]Get &i.ID, &i.FirstName, &i.LastName, + &i.NickName, &i.Email, &i.PhoneNumber, &i.Role, + &i.Age, + &i.EducationLevel, + &i.Country, + &i.Region, &i.EmailVerified, &i.PhoneVerified, &i.CreatedAt, &i.UpdatedAt, &i.Suspended, &i.SuspendedAt, - &i.CompanyID, + &i.OrganizationID, ); err != nil { return nil, err } @@ -299,6 +327,40 @@ func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]Get return items, nil } +const GetOwnerByOrganizationID = `-- name: GetOwnerByOrganizationID :one +SELECT users.id, users.first_name, users.last_name, users.nick_name, users.email, users.phone_number, users.role, users.password, users.age, users.education_level, users.country, users.region, users.email_verified, users.phone_verified, users.suspended, users.suspended_at, users.organization_id, users.created_at, users.updated_at +FROM organizations + JOIN users ON organizations.owner_id = users.id +WHERE organizations.id = $1 +` + +func (q *Queries) GetOwnerByOrganizationID(ctx context.Context, id int64) (User, error) { + row := q.db.QueryRow(ctx, GetOwnerByOrganizationID, id) + var i User + err := row.Scan( + &i.ID, + &i.FirstName, + &i.LastName, + &i.NickName, + &i.Email, + &i.PhoneNumber, + &i.Role, + &i.Password, + &i.Age, + &i.EducationLevel, + &i.Country, + &i.Region, + &i.EmailVerified, + &i.PhoneVerified, + &i.Suspended, + &i.SuspendedAt, + &i.OrganizationID, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + const GetTotalUsers = `-- name: GetTotalUsers :one SELECT COUNT(*) FROM users @@ -307,18 +369,18 @@ wHERE ( OR $1 IS NULL ) AND ( - company_id = $2 + organization_id = $2 OR $2 IS NULL ) ` type GetTotalUsersParams struct { - Role string `json:"role"` - CompanyID pgtype.Int8 `json:"company_id"` + Role string `json:"role"` + OrganizationID pgtype.Int8 `json:"organization_id"` } func (q *Queries) GetTotalUsers(ctx context.Context, arg GetTotalUsersParams) (int64, error) { - row := q.db.QueryRow(ctx, GetTotalUsers, arg.Role, arg.CompanyID) + row := q.db.QueryRow(ctx, GetTotalUsers, arg.Role, arg.OrganizationID) var count int64 err := row.Scan(&count) return count, err @@ -328,65 +390,80 @@ const GetUserByEmail = `-- name: GetUserByEmail :one SELECT id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, suspended_at, - company_id + organization_id FROM users WHERE email = $1 - AND company_id = $2 + AND organization_id = $2 ` type GetUserByEmailParams struct { - Email pgtype.Text `json:"email"` - CompanyID pgtype.Int8 `json:"company_id"` + Email pgtype.Text `json:"email"` + OrganizationID pgtype.Int8 `json:"organization_id"` } type GetUserByEmailRow struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - Role string `json:"role"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - Suspended bool `json:"suspended"` - SuspendedAt pgtype.Timestamptz `json:"suspended_at"` - CompanyID pgtype.Int8 `json:"company_id"` + ID int64 `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + NickName pgtype.Text `json:"nick_name"` + Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + Role string `json:"role"` + Age pgtype.Int4 `json:"age"` + EducationLevel pgtype.Text `json:"education_level"` + Country pgtype.Text `json:"country"` + Region pgtype.Text `json:"region"` + EmailVerified bool `json:"email_verified"` + PhoneVerified bool `json:"phone_verified"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` + Suspended bool `json:"suspended"` + SuspendedAt pgtype.Timestamptz `json:"suspended_at"` + OrganizationID pgtype.Int8 `json:"organization_id"` } func (q *Queries) GetUserByEmail(ctx context.Context, arg GetUserByEmailParams) (GetUserByEmailRow, error) { - row := q.db.QueryRow(ctx, GetUserByEmail, arg.Email, arg.CompanyID) + row := q.db.QueryRow(ctx, GetUserByEmail, arg.Email, arg.OrganizationID) var i GetUserByEmailRow err := row.Scan( &i.ID, &i.FirstName, &i.LastName, + &i.NickName, &i.Email, &i.PhoneNumber, &i.Role, + &i.Age, + &i.EducationLevel, + &i.Country, + &i.Region, &i.EmailVerified, &i.PhoneVerified, &i.CreatedAt, &i.UpdatedAt, &i.Suspended, &i.SuspendedAt, - &i.CompanyID, + &i.OrganizationID, ) return i, err } const GetUserByID = `-- name: GetUserByID :one -SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended +SELECT id, first_name, last_name, nick_name, email, phone_number, role, password, age, education_level, country, region, email_verified, phone_verified, suspended, suspended_at, organization_id, created_at, updated_at FROM users WHERE id = $1 ` @@ -398,17 +475,22 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) { &i.ID, &i.FirstName, &i.LastName, + &i.NickName, &i.Email, &i.PhoneNumber, &i.Role, &i.Password, + &i.Age, + &i.EducationLevel, + &i.Country, + &i.Region, &i.EmailVerified, &i.PhoneVerified, + &i.Suspended, + &i.SuspendedAt, + &i.OrganizationID, &i.CreatedAt, &i.UpdatedAt, - &i.CompanyID, - &i.SuspendedAt, - &i.Suspended, ) return i, err } @@ -417,59 +499,74 @@ const GetUserByPhone = `-- name: GetUserByPhone :one SELECT id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, suspended_at, - company_id + organization_id FROM users WHERE phone_number = $1 - AND company_id = $2 + AND organization_id = $2 ` type GetUserByPhoneParams struct { - PhoneNumber pgtype.Text `json:"phone_number"` - CompanyID pgtype.Int8 `json:"company_id"` + PhoneNumber pgtype.Text `json:"phone_number"` + OrganizationID pgtype.Int8 `json:"organization_id"` } type GetUserByPhoneRow struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - Role string `json:"role"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - Suspended bool `json:"suspended"` - SuspendedAt pgtype.Timestamptz `json:"suspended_at"` - CompanyID pgtype.Int8 `json:"company_id"` + ID int64 `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + NickName pgtype.Text `json:"nick_name"` + Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + Role string `json:"role"` + Age pgtype.Int4 `json:"age"` + EducationLevel pgtype.Text `json:"education_level"` + Country pgtype.Text `json:"country"` + Region pgtype.Text `json:"region"` + EmailVerified bool `json:"email_verified"` + PhoneVerified bool `json:"phone_verified"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` + Suspended bool `json:"suspended"` + SuspendedAt pgtype.Timestamptz `json:"suspended_at"` + OrganizationID pgtype.Int8 `json:"organization_id"` } func (q *Queries) GetUserByPhone(ctx context.Context, arg GetUserByPhoneParams) (GetUserByPhoneRow, error) { - row := q.db.QueryRow(ctx, GetUserByPhone, arg.PhoneNumber, arg.CompanyID) + row := q.db.QueryRow(ctx, GetUserByPhone, arg.PhoneNumber, arg.OrganizationID) var i GetUserByPhoneRow err := row.Scan( &i.ID, &i.FirstName, &i.LastName, + &i.NickName, &i.Email, &i.PhoneNumber, &i.Role, + &i.Age, + &i.EducationLevel, + &i.Country, + &i.Region, &i.EmailVerified, &i.PhoneVerified, &i.CreatedAt, &i.UpdatedAt, &i.Suspended, &i.SuspendedAt, - &i.CompanyID, + &i.OrganizationID, ) return i, err } @@ -478,19 +575,24 @@ const SearchUserByNameOrPhone = `-- name: SearchUserByNameOrPhone :many SELECT id, first_name, last_name, + nick_name, email, phone_number, role, + age, + education_level, + country, + region, email_verified, phone_verified, created_at, updated_at, suspended, suspended_at, - company_id + organization_id FROM users WHERE ( - company_id = $2 + organization_id = $2 OR $2 IS NULL ) AND ( @@ -505,29 +607,34 @@ WHERE ( ` type SearchUserByNameOrPhoneParams struct { - Column1 pgtype.Text `json:"column_1"` - CompanyID pgtype.Int8 `json:"company_id"` - Role pgtype.Text `json:"role"` + Column1 pgtype.Text `json:"column_1"` + OrganizationID pgtype.Int8 `json:"organization_id"` + Role pgtype.Text `json:"role"` } type SearchUserByNameOrPhoneRow struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - Role string `json:"role"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - Suspended bool `json:"suspended"` - SuspendedAt pgtype.Timestamptz `json:"suspended_at"` - CompanyID pgtype.Int8 `json:"company_id"` + ID int64 `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + NickName pgtype.Text `json:"nick_name"` + Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + Role string `json:"role"` + Age pgtype.Int4 `json:"age"` + EducationLevel pgtype.Text `json:"education_level"` + Country pgtype.Text `json:"country"` + Region pgtype.Text `json:"region"` + EmailVerified bool `json:"email_verified"` + PhoneVerified bool `json:"phone_verified"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` + Suspended bool `json:"suspended"` + SuspendedAt pgtype.Timestamptz `json:"suspended_at"` + OrganizationID pgtype.Int8 `json:"organization_id"` } func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByNameOrPhoneParams) ([]SearchUserByNameOrPhoneRow, error) { - rows, err := q.db.Query(ctx, SearchUserByNameOrPhone, arg.Column1, arg.CompanyID, arg.Role) + rows, err := q.db.Query(ctx, SearchUserByNameOrPhone, arg.Column1, arg.OrganizationID, arg.Role) if err != nil { return nil, err } @@ -539,16 +646,21 @@ func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByN &i.ID, &i.FirstName, &i.LastName, + &i.NickName, &i.Email, &i.PhoneNumber, &i.Role, + &i.Age, + &i.EducationLevel, + &i.Country, + &i.Region, &i.EmailVerified, &i.PhoneVerified, &i.CreatedAt, &i.UpdatedAt, &i.Suspended, &i.SuspendedAt, - &i.CompanyID, + &i.OrganizationID, ); err != nil { return nil, err } @@ -584,18 +696,17 @@ UPDATE users SET password = $1, updated_at = $4 WHERE ( - email = $2 - OR phone_number = $3 - AND company_id = $5 + (email = $2 OR phone_number = $3) + AND organization_id = $5 ) ` type UpdatePasswordParams struct { - Password []byte `json:"password"` - Email pgtype.Text `json:"email"` - PhoneNumber pgtype.Text `json:"phone_number"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` - CompanyID pgtype.Int8 `json:"company_id"` + Password []byte `json:"password"` + Email pgtype.Text `json:"email"` + PhoneNumber pgtype.Text `json:"phone_number"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` + OrganizationID pgtype.Int8 `json:"organization_id"` } func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error { @@ -604,7 +715,7 @@ func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) arg.Email, arg.PhoneNumber, arg.UpdatedAt, - arg.CompanyID, + arg.OrganizationID, ) return err } @@ -635,18 +746,18 @@ func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error { return err } -const UpdateUserCompany = `-- name: UpdateUserCompany :exec +const UpdateUserOrganization = `-- name: UpdateUserOrganization :exec UPDATE users -SET company_id = $1 +SET organization_id = $1 WHERE id = $2 ` -type UpdateUserCompanyParams struct { - CompanyID pgtype.Int8 `json:"company_id"` - ID int64 `json:"id"` +type UpdateUserOrganizationParams struct { + OrganizationID pgtype.Int8 `json:"organization_id"` + ID int64 `json:"id"` } -func (q *Queries) UpdateUserCompany(ctx context.Context, arg UpdateUserCompanyParams) error { - _, err := q.db.Exec(ctx, UpdateUserCompany, arg.CompanyID, arg.ID) +func (q *Queries) UpdateUserOrganization(ctx context.Context, arg UpdateUserOrganizationParams) error { + _, err := q.db.Exec(ctx, UpdateUserOrganization, arg.OrganizationID, arg.ID) return err } diff --git a/gen/db/virtual_games.sql.go b/gen/db/virtual_games.sql.go deleted file mode 100644 index e9a0454..0000000 --- a/gen/db/virtual_games.sql.go +++ /dev/null @@ -1,1165 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: virtual_games.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const AddFavoriteGame = `-- name: AddFavoriteGame :exec -INSERT INTO virtual_game_favourites (user_id, game_id, provider_id, created_at) -VALUES ($1, $2, $3, NOW()) -ON CONFLICT (game_id, user_id) DO NOTHING -` - -type AddFavoriteGameParams struct { - UserID int64 `json:"user_id"` - GameID int64 `json:"game_id"` - ProviderID string `json:"provider_id"` -} - -func (q *Queries) AddFavoriteGame(ctx context.Context, arg AddFavoriteGameParams) error { - _, err := q.db.Exec(ctx, AddFavoriteGame, arg.UserID, arg.GameID, arg.ProviderID) - return err -} - -const CountVirtualGameProviders = `-- name: CountVirtualGameProviders :one -SELECT COUNT(*) AS total -FROM virtual_game_providers -` - -func (q *Queries) CountVirtualGameProviders(ctx context.Context) (int64, error) { - row := q.db.QueryRow(ctx, CountVirtualGameProviders) - var total int64 - err := row.Scan(&total) - return total, err -} - -const CreateVirtualGame = `-- name: CreateVirtualGame :one -INSERT INTO virtual_games ( - game_id, - provider_id, - name, - category, - device_type, - volatility, - rtp, - has_demo, - has_free_bets, - bets, - thumbnail, - status - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12 - ) -RETURNING id, - game_id, - provider_id, - name, - category, - device_type, - volatility, - rtp, - has_demo, - has_free_bets, - bets, - thumbnail, - status, - created_at, - updated_at -` - -type CreateVirtualGameParams struct { - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - Name string `json:"name"` - Category pgtype.Text `json:"category"` - DeviceType pgtype.Text `json:"device_type"` - Volatility pgtype.Text `json:"volatility"` - Rtp pgtype.Numeric `json:"rtp"` - HasDemo pgtype.Bool `json:"has_demo"` - HasFreeBets pgtype.Bool `json:"has_free_bets"` - Bets []pgtype.Numeric `json:"bets"` - Thumbnail pgtype.Text `json:"thumbnail"` - Status pgtype.Int4 `json:"status"` -} - -func (q *Queries) CreateVirtualGame(ctx context.Context, arg CreateVirtualGameParams) (VirtualGame, error) { - row := q.db.QueryRow(ctx, CreateVirtualGame, - arg.GameID, - arg.ProviderID, - arg.Name, - arg.Category, - arg.DeviceType, - arg.Volatility, - arg.Rtp, - arg.HasDemo, - arg.HasFreeBets, - arg.Bets, - arg.Thumbnail, - arg.Status, - ) - var i VirtualGame - err := row.Scan( - &i.ID, - &i.GameID, - &i.ProviderID, - &i.Name, - &i.Category, - &i.DeviceType, - &i.Volatility, - &i.Rtp, - &i.HasDemo, - &i.HasFreeBets, - &i.Bets, - &i.Thumbnail, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateVirtualGameHistory = `-- name: CreateVirtualGameHistory :one -INSERT INTO virtual_game_histories ( - -- session_id, - user_id, - company_id, - provider, - wallet_id, - game_id, - transaction_type, - amount, - currency, - external_transaction_id, - reference_transaction_id, - status - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11 - -- $12 - ) -RETURNING id, - -- session_id, - user_id, - company_id, - provider, - wallet_id, - game_id, - transaction_type, - amount, - currency, - external_transaction_id, - reference_transaction_id, - status, - created_at, - updated_at -` - -type CreateVirtualGameHistoryParams struct { - UserID int64 `json:"user_id"` - CompanyID pgtype.Int8 `json:"company_id"` - Provider pgtype.Text `json:"provider"` - WalletID pgtype.Int8 `json:"wallet_id"` - GameID pgtype.Int8 `json:"game_id"` - TransactionType string `json:"transaction_type"` - Amount int64 `json:"amount"` - Currency string `json:"currency"` - ExternalTransactionID string `json:"external_transaction_id"` - ReferenceTransactionID pgtype.Text `json:"reference_transaction_id"` - Status string `json:"status"` -} - -func (q *Queries) CreateVirtualGameHistory(ctx context.Context, arg CreateVirtualGameHistoryParams) (VirtualGameHistory, error) { - row := q.db.QueryRow(ctx, CreateVirtualGameHistory, - arg.UserID, - arg.CompanyID, - arg.Provider, - arg.WalletID, - arg.GameID, - arg.TransactionType, - arg.Amount, - arg.Currency, - arg.ExternalTransactionID, - arg.ReferenceTransactionID, - arg.Status, - ) - var i VirtualGameHistory - err := row.Scan( - &i.ID, - &i.UserID, - &i.CompanyID, - &i.Provider, - &i.WalletID, - &i.GameID, - &i.TransactionType, - &i.Amount, - &i.Currency, - &i.ExternalTransactionID, - &i.ReferenceTransactionID, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateVirtualGameProvider = `-- name: CreateVirtualGameProvider :one -INSERT INTO virtual_game_providers ( - provider_id, - provider_name, - logo_dark, - logo_light, - enabled - ) -VALUES ($1, $2, $3, $4, $5) -RETURNING id, - provider_id, - provider_name, - logo_dark, - logo_light, - enabled, - created_at, - updated_at -` - -type CreateVirtualGameProviderParams struct { - ProviderID string `json:"provider_id"` - ProviderName string `json:"provider_name"` - LogoDark pgtype.Text `json:"logo_dark"` - LogoLight pgtype.Text `json:"logo_light"` - Enabled bool `json:"enabled"` -} - -func (q *Queries) CreateVirtualGameProvider(ctx context.Context, arg CreateVirtualGameProviderParams) (VirtualGameProvider, error) { - row := q.db.QueryRow(ctx, CreateVirtualGameProvider, - arg.ProviderID, - arg.ProviderName, - arg.LogoDark, - arg.LogoLight, - arg.Enabled, - ) - var i VirtualGameProvider - err := row.Scan( - &i.ID, - &i.ProviderID, - &i.ProviderName, - &i.LogoDark, - &i.LogoLight, - &i.Enabled, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateVirtualGameProviderReport = `-- name: CreateVirtualGameProviderReport :one -INSERT INTO virtual_game_provider_reports ( - provider_id, - report_date, - total_games_played, - total_bets, - total_payouts, - total_players, - report_type, - created_at, - updated_at -) VALUES ( - $1, $2, $3, $4, $5, $6, COALESCE($7, 'daily'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP -) -ON CONFLICT (provider_id, report_date, report_type) DO UPDATE -SET - total_games_played = EXCLUDED.total_games_played, - total_bets = EXCLUDED.total_bets, - total_payouts = EXCLUDED.total_payouts, - total_players = EXCLUDED.total_players, - updated_at = CURRENT_TIMESTAMP -RETURNING id, provider_id, report_date, total_games_played, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at -` - -type CreateVirtualGameProviderReportParams struct { - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - TotalGamesPlayed pgtype.Int8 `json:"total_games_played"` - TotalBets pgtype.Numeric `json:"total_bets"` - TotalPayouts pgtype.Numeric `json:"total_payouts"` - TotalPlayers pgtype.Int8 `json:"total_players"` - Column7 interface{} `json:"column_7"` -} - -func (q *Queries) CreateVirtualGameProviderReport(ctx context.Context, arg CreateVirtualGameProviderReportParams) (VirtualGameProviderReport, error) { - row := q.db.QueryRow(ctx, CreateVirtualGameProviderReport, - arg.ProviderID, - arg.ReportDate, - arg.TotalGamesPlayed, - arg.TotalBets, - arg.TotalPayouts, - arg.TotalPlayers, - arg.Column7, - ) - var i VirtualGameProviderReport - err := row.Scan( - &i.ID, - &i.ProviderID, - &i.ReportDate, - &i.TotalGamesPlayed, - &i.TotalBets, - &i.TotalPayouts, - &i.TotalProfit, - &i.TotalPlayers, - &i.ReportType, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateVirtualGameReport = `-- name: CreateVirtualGameReport :one -INSERT INTO virtual_game_reports ( - game_id, - provider_id, - report_date, - total_rounds, - total_bets, - total_payouts, - total_players, - report_type, - created_at, - updated_at -) VALUES ( - $1, $2, $3, $4, $5, $6, $7, COALESCE($8, 'daily'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP -) -ON CONFLICT (game_id, report_date, report_type) DO UPDATE -SET - total_rounds = EXCLUDED.total_rounds, - total_bets = EXCLUDED.total_bets, - total_payouts = EXCLUDED.total_payouts, - total_players = EXCLUDED.total_players, - updated_at = CURRENT_TIMESTAMP -RETURNING id, game_id, provider_id, report_date, total_rounds, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at -` - -type CreateVirtualGameReportParams struct { - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - TotalRounds pgtype.Int8 `json:"total_rounds"` - TotalBets pgtype.Numeric `json:"total_bets"` - TotalPayouts pgtype.Numeric `json:"total_payouts"` - TotalPlayers pgtype.Int8 `json:"total_players"` - Column8 interface{} `json:"column_8"` -} - -func (q *Queries) CreateVirtualGameReport(ctx context.Context, arg CreateVirtualGameReportParams) (VirtualGameReport, error) { - row := q.db.QueryRow(ctx, CreateVirtualGameReport, - arg.GameID, - arg.ProviderID, - arg.ReportDate, - arg.TotalRounds, - arg.TotalBets, - arg.TotalPayouts, - arg.TotalPlayers, - arg.Column8, - ) - var i VirtualGameReport - err := row.Scan( - &i.ID, - &i.GameID, - &i.ProviderID, - &i.ReportDate, - &i.TotalRounds, - &i.TotalBets, - &i.TotalPayouts, - &i.TotalProfit, - &i.TotalPlayers, - &i.ReportType, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateVirtualGameSession = `-- name: CreateVirtualGameSession :one -INSERT INTO virtual_game_sessions ( - user_id, - game_id, - session_token -) -VALUES ($1, $2, $3) -RETURNING - id, - user_id, - game_id, - session_token, - created_at, - updated_at -` - -type CreateVirtualGameSessionParams struct { - UserID int64 `json:"user_id"` - GameID string `json:"game_id"` - SessionToken string `json:"session_token"` -} - -func (q *Queries) CreateVirtualGameSession(ctx context.Context, arg CreateVirtualGameSessionParams) (VirtualGameSession, error) { - row := q.db.QueryRow(ctx, CreateVirtualGameSession, arg.UserID, arg.GameID, arg.SessionToken) - var i VirtualGameSession - err := row.Scan( - &i.ID, - &i.UserID, - &i.GameID, - &i.SessionToken, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateVirtualGameTransaction = `-- name: CreateVirtualGameTransaction :one -INSERT INTO virtual_game_transactions ( - -- session_id, - user_id, - company_id, - provider, - wallet_id, - transaction_type, - amount, - currency, - external_transaction_id, - status - ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) -RETURNING id, - -- session_id, - user_id, - company_id, - provider, - wallet_id, - transaction_type, - amount, - currency, - external_transaction_id, - status, - created_at, - updated_at -` - -type CreateVirtualGameTransactionParams struct { - UserID int64 `json:"user_id"` - CompanyID pgtype.Int8 `json:"company_id"` - Provider pgtype.Text `json:"provider"` - WalletID int64 `json:"wallet_id"` - TransactionType string `json:"transaction_type"` - Amount int64 `json:"amount"` - Currency string `json:"currency"` - ExternalTransactionID string `json:"external_transaction_id"` - Status string `json:"status"` -} - -type CreateVirtualGameTransactionRow struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - CompanyID pgtype.Int8 `json:"company_id"` - Provider pgtype.Text `json:"provider"` - WalletID int64 `json:"wallet_id"` - TransactionType string `json:"transaction_type"` - Amount int64 `json:"amount"` - Currency string `json:"currency"` - ExternalTransactionID string `json:"external_transaction_id"` - Status string `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -func (q *Queries) CreateVirtualGameTransaction(ctx context.Context, arg CreateVirtualGameTransactionParams) (CreateVirtualGameTransactionRow, error) { - row := q.db.QueryRow(ctx, CreateVirtualGameTransaction, - arg.UserID, - arg.CompanyID, - arg.Provider, - arg.WalletID, - arg.TransactionType, - arg.Amount, - arg.Currency, - arg.ExternalTransactionID, - arg.Status, - ) - var i CreateVirtualGameTransactionRow - err := row.Scan( - &i.ID, - &i.UserID, - &i.CompanyID, - &i.Provider, - &i.WalletID, - &i.TransactionType, - &i.Amount, - &i.Currency, - &i.ExternalTransactionID, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const DeleteAllVirtualGameProviders = `-- name: DeleteAllVirtualGameProviders :exec -DELETE FROM virtual_game_providers -` - -func (q *Queries) DeleteAllVirtualGameProviders(ctx context.Context) error { - _, err := q.db.Exec(ctx, DeleteAllVirtualGameProviders) - return err -} - -const DeleteAllVirtualGames = `-- name: DeleteAllVirtualGames :exec -DELETE FROM virtual_games -` - -func (q *Queries) DeleteAllVirtualGames(ctx context.Context) error { - _, err := q.db.Exec(ctx, DeleteAllVirtualGames) - return err -} - -const DeleteVirtualGameProvider = `-- name: DeleteVirtualGameProvider :exec -DELETE FROM virtual_game_providers -WHERE provider_id = $1 -` - -func (q *Queries) DeleteVirtualGameProvider(ctx context.Context, providerID string) error { - _, err := q.db.Exec(ctx, DeleteVirtualGameProvider, providerID) - return err -} - -const GetAllVirtualGames = `-- name: GetAllVirtualGames :many -SELECT vg.id, - vg.game_id, - vg.provider_id, - vp.provider_name, - vg.name, - vg.category, - vg.device_type, - vg.volatility, - vg.rtp, - vg.has_demo, - vg.has_free_bets, - vg.bets, - vg.thumbnail, - vg.status, - vg.created_at, - vg.updated_at -FROM virtual_games vg - JOIN virtual_game_providers vp ON vg.provider_id = vp.provider_id -WHERE ( - vg.category = $1 - OR $1 IS NULL - ) - AND ( - name ILIKE '%' || $2 || '%' - OR $2 IS NULL - ) - AND ( - vg.provider_id = $3 - OR $3 IS NULL - ) -ORDER BY vg.created_at DESC -LIMIT $5 OFFSET $4 -` - -type GetAllVirtualGamesParams struct { - Category pgtype.Text `json:"category"` - Name pgtype.Text `json:"name"` - ProviderID pgtype.Text `json:"provider_id"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -type GetAllVirtualGamesRow struct { - ID int64 `json:"id"` - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - ProviderName string `json:"provider_name"` - Name string `json:"name"` - Category pgtype.Text `json:"category"` - DeviceType pgtype.Text `json:"device_type"` - Volatility pgtype.Text `json:"volatility"` - Rtp pgtype.Numeric `json:"rtp"` - HasDemo pgtype.Bool `json:"has_demo"` - HasFreeBets pgtype.Bool `json:"has_free_bets"` - Bets []pgtype.Numeric `json:"bets"` - Thumbnail pgtype.Text `json:"thumbnail"` - Status pgtype.Int4 `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -func (q *Queries) GetAllVirtualGames(ctx context.Context, arg GetAllVirtualGamesParams) ([]GetAllVirtualGamesRow, error) { - rows, err := q.db.Query(ctx, GetAllVirtualGames, - arg.Category, - arg.Name, - arg.ProviderID, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetAllVirtualGamesRow - for rows.Next() { - var i GetAllVirtualGamesRow - if err := rows.Scan( - &i.ID, - &i.GameID, - &i.ProviderID, - &i.ProviderName, - &i.Name, - &i.Category, - &i.DeviceType, - &i.Volatility, - &i.Rtp, - &i.HasDemo, - &i.HasFreeBets, - &i.Bets, - &i.Thumbnail, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetUserFavoriteGamesPaginated = `-- name: GetUserFavoriteGamesPaginated :many -SELECT - vg.id, vg.game_id, vg.provider_id, vg.name, vg.category, vg.device_type, vg.volatility, vg.rtp, vg.has_demo, vg.has_free_bets, vg.bets, vg.thumbnail, vg.status, vg.created_at, vg.updated_at -FROM virtual_games vg -JOIN virtual_game_favourites vf - ON vf.game_id = vg.id -WHERE - vf.user_id = $1 - AND ($2::varchar IS NULL OR vf.provider_id = $2) -ORDER BY vf.created_at DESC -LIMIT $3 OFFSET $4 -` - -type GetUserFavoriteGamesPaginatedParams struct { - UserID int64 `json:"user_id"` - Column2 string `json:"column_2"` - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -func (q *Queries) GetUserFavoriteGamesPaginated(ctx context.Context, arg GetUserFavoriteGamesPaginatedParams) ([]VirtualGame, error) { - rows, err := q.db.Query(ctx, GetUserFavoriteGamesPaginated, - arg.UserID, - arg.Column2, - arg.Limit, - arg.Offset, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []VirtualGame - for rows.Next() { - var i VirtualGame - if err := rows.Scan( - &i.ID, - &i.GameID, - &i.ProviderID, - &i.Name, - &i.Category, - &i.DeviceType, - &i.Volatility, - &i.Rtp, - &i.HasDemo, - &i.HasFreeBets, - &i.Bets, - &i.Thumbnail, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetVirtualGameProviderByID = `-- name: GetVirtualGameProviderByID :one -SELECT id, - provider_id, - provider_name, - logo_dark, - logo_light, - enabled, - created_at, - updated_at -FROM virtual_game_providers -WHERE provider_id = $1 -` - -func (q *Queries) GetVirtualGameProviderByID(ctx context.Context, providerID string) (VirtualGameProvider, error) { - row := q.db.QueryRow(ctx, GetVirtualGameProviderByID, providerID) - var i VirtualGameProvider - err := row.Scan( - &i.ID, - &i.ProviderID, - &i.ProviderName, - &i.LogoDark, - &i.LogoLight, - &i.Enabled, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetVirtualGameProviderReportByProviderAndDate = `-- name: GetVirtualGameProviderReportByProviderAndDate :one -SELECT id, provider_id, report_date, total_games_played, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at -FROM virtual_game_provider_reports -WHERE provider_id = $1 - AND report_date = $2 - AND report_type = $3 -` - -type GetVirtualGameProviderReportByProviderAndDateParams struct { - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType pgtype.Text `json:"report_type"` -} - -func (q *Queries) GetVirtualGameProviderReportByProviderAndDate(ctx context.Context, arg GetVirtualGameProviderReportByProviderAndDateParams) (VirtualGameProviderReport, error) { - row := q.db.QueryRow(ctx, GetVirtualGameProviderReportByProviderAndDate, arg.ProviderID, arg.ReportDate, arg.ReportType) - var i VirtualGameProviderReport - err := row.Scan( - &i.ID, - &i.ProviderID, - &i.ReportDate, - &i.TotalGamesPlayed, - &i.TotalBets, - &i.TotalPayouts, - &i.TotalProfit, - &i.TotalPlayers, - &i.ReportType, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetVirtualGameSessionByToken = `-- name: GetVirtualGameSessionByToken :one -SELECT id, - user_id, - game_id, - session_token, - created_at, - updated_at -FROM virtual_game_sessions -WHERE session_token = $1 -` - -func (q *Queries) GetVirtualGameSessionByToken(ctx context.Context, sessionToken string) (VirtualGameSession, error) { - row := q.db.QueryRow(ctx, GetVirtualGameSessionByToken, sessionToken) - var i VirtualGameSession - err := row.Scan( - &i.ID, - &i.UserID, - &i.GameID, - &i.SessionToken, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetVirtualGameSessionByUserID = `-- name: GetVirtualGameSessionByUserID :one -SELECT - id, - user_id, - game_id, - session_token, - created_at, - updated_at -FROM virtual_game_sessions -WHERE user_id = $1 -` - -func (q *Queries) GetVirtualGameSessionByUserID(ctx context.Context, userID int64) (VirtualGameSession, error) { - row := q.db.QueryRow(ctx, GetVirtualGameSessionByUserID, userID) - var i VirtualGameSession - err := row.Scan( - &i.ID, - &i.UserID, - &i.GameID, - &i.SessionToken, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetVirtualGameSummaryInRange = `-- name: GetVirtualGameSummaryInRange :many -SELECT c.name AS company_name, - vg.name AS game_name, - COUNT(vgt.id) AS number_of_bets, - COALESCE(SUM(vgt.amount), 0) AS total_transaction_sum -FROM virtual_game_transactions vgt - -- JOIN virtual_game_sessions vgs ON vgt.session_id = vgs.id - JOIN virtual_games vg ON vgs.game_id = vg.id - JOIN companies c ON vgt.company_id = c.id -WHERE vgt.transaction_type = 'BET' - AND vgt.created_at BETWEEN $1 AND $2 -GROUP BY c.name, - vg.name -` - -type GetVirtualGameSummaryInRangeParams struct { - CreatedAt pgtype.Timestamptz `json:"created_at"` - CreatedAt_2 pgtype.Timestamptz `json:"created_at_2"` -} - -type GetVirtualGameSummaryInRangeRow struct { - CompanyName string `json:"company_name"` - GameName string `json:"game_name"` - NumberOfBets int64 `json:"number_of_bets"` - TotalTransactionSum interface{} `json:"total_transaction_sum"` -} - -func (q *Queries) GetVirtualGameSummaryInRange(ctx context.Context, arg GetVirtualGameSummaryInRangeParams) ([]GetVirtualGameSummaryInRangeRow, error) { - rows, err := q.db.Query(ctx, GetVirtualGameSummaryInRange, arg.CreatedAt, arg.CreatedAt_2) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetVirtualGameSummaryInRangeRow - for rows.Next() { - var i GetVirtualGameSummaryInRangeRow - if err := rows.Scan( - &i.CompanyName, - &i.GameName, - &i.NumberOfBets, - &i.TotalTransactionSum, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetVirtualGameTransactionByExternalID = `-- name: GetVirtualGameTransactionByExternalID :one -SELECT id, - -- session_id, - user_id, - wallet_id, - transaction_type, - amount, - currency, - external_transaction_id, - status, - created_at, - updated_at -FROM virtual_game_transactions -WHERE external_transaction_id = $1 -` - -type GetVirtualGameTransactionByExternalIDRow struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - WalletID int64 `json:"wallet_id"` - TransactionType string `json:"transaction_type"` - Amount int64 `json:"amount"` - Currency string `json:"currency"` - ExternalTransactionID string `json:"external_transaction_id"` - Status string `json:"status"` - CreatedAt pgtype.Timestamptz `json:"created_at"` - UpdatedAt pgtype.Timestamptz `json:"updated_at"` -} - -func (q *Queries) GetVirtualGameTransactionByExternalID(ctx context.Context, externalTransactionID string) (GetVirtualGameTransactionByExternalIDRow, error) { - row := q.db.QueryRow(ctx, GetVirtualGameTransactionByExternalID, externalTransactionID) - var i GetVirtualGameTransactionByExternalIDRow - err := row.Scan( - &i.ID, - &i.UserID, - &i.WalletID, - &i.TransactionType, - &i.Amount, - &i.Currency, - &i.ExternalTransactionID, - &i.Status, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const ListVirtualGameProviderReportsByGamesPlayedAsc = `-- name: ListVirtualGameProviderReportsByGamesPlayedAsc :many -SELECT id, provider_id, report_date, total_games_played, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at -FROM virtual_game_provider_reports -ORDER BY total_games_played ASC -` - -func (q *Queries) ListVirtualGameProviderReportsByGamesPlayedAsc(ctx context.Context) ([]VirtualGameProviderReport, error) { - rows, err := q.db.Query(ctx, ListVirtualGameProviderReportsByGamesPlayedAsc) - if err != nil { - return nil, err - } - defer rows.Close() - var items []VirtualGameProviderReport - for rows.Next() { - var i VirtualGameProviderReport - if err := rows.Scan( - &i.ID, - &i.ProviderID, - &i.ReportDate, - &i.TotalGamesPlayed, - &i.TotalBets, - &i.TotalPayouts, - &i.TotalProfit, - &i.TotalPlayers, - &i.ReportType, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const ListVirtualGameProviderReportsByGamesPlayedDesc = `-- name: ListVirtualGameProviderReportsByGamesPlayedDesc :many -SELECT id, provider_id, report_date, total_games_played, total_bets, total_payouts, total_profit, total_players, report_type, created_at, updated_at -FROM virtual_game_provider_reports -ORDER BY total_games_played DESC -` - -func (q *Queries) ListVirtualGameProviderReportsByGamesPlayedDesc(ctx context.Context) ([]VirtualGameProviderReport, error) { - rows, err := q.db.Query(ctx, ListVirtualGameProviderReportsByGamesPlayedDesc) - if err != nil { - return nil, err - } - defer rows.Close() - var items []VirtualGameProviderReport - for rows.Next() { - var i VirtualGameProviderReport - if err := rows.Scan( - &i.ID, - &i.ProviderID, - &i.ReportDate, - &i.TotalGamesPlayed, - &i.TotalBets, - &i.TotalPayouts, - &i.TotalProfit, - &i.TotalPlayers, - &i.ReportType, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const ListVirtualGameProviders = `-- name: ListVirtualGameProviders :many -SELECT id, - provider_id, - provider_name, - logo_dark, - logo_light, - enabled, - created_at, - updated_at -FROM virtual_game_providers -ORDER BY created_at DESC -LIMIT $1 OFFSET $2 -` - -type ListVirtualGameProvidersParams struct { - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -func (q *Queries) ListVirtualGameProviders(ctx context.Context, arg ListVirtualGameProvidersParams) ([]VirtualGameProvider, error) { - rows, err := q.db.Query(ctx, ListVirtualGameProviders, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []VirtualGameProvider - for rows.Next() { - var i VirtualGameProvider - if err := rows.Scan( - &i.ID, - &i.ProviderID, - &i.ProviderName, - &i.LogoDark, - &i.LogoLight, - &i.Enabled, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const RemoveFavoriteGame = `-- name: RemoveFavoriteGame :exec -DELETE FROM virtual_game_favourites -WHERE user_id = $1 AND game_id = $2 AND provider_id = $3 -` - -type RemoveFavoriteGameParams struct { - UserID int64 `json:"user_id"` - GameID int64 `json:"game_id"` - ProviderID string `json:"provider_id"` -} - -func (q *Queries) RemoveFavoriteGame(ctx context.Context, arg RemoveFavoriteGameParams) error { - _, err := q.db.Exec(ctx, RemoveFavoriteGame, arg.UserID, arg.GameID, arg.ProviderID) - return err -} - -const UpdateVirtualGameProviderEnabled = `-- name: UpdateVirtualGameProviderEnabled :one -UPDATE virtual_game_providers -SET enabled = $2, - updated_at = CURRENT_TIMESTAMP -WHERE provider_id = $1 -RETURNING id, - provider_id, - provider_name, - logo_dark, - logo_light, - enabled, - created_at, - updated_at -` - -type UpdateVirtualGameProviderEnabledParams struct { - ProviderID string `json:"provider_id"` - Enabled bool `json:"enabled"` -} - -func (q *Queries) UpdateVirtualGameProviderEnabled(ctx context.Context, arg UpdateVirtualGameProviderEnabledParams) (VirtualGameProvider, error) { - row := q.db.QueryRow(ctx, UpdateVirtualGameProviderEnabled, arg.ProviderID, arg.Enabled) - var i VirtualGameProvider - err := row.Scan( - &i.ID, - &i.ProviderID, - &i.ProviderName, - &i.LogoDark, - &i.LogoLight, - &i.Enabled, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const UpdateVirtualGameProviderReportByDate = `-- name: UpdateVirtualGameProviderReportByDate :exec -UPDATE virtual_game_provider_reports -SET - total_games_played = total_games_played + $4, - total_bets = total_bets + $5, - total_payouts = total_payouts + $6, - total_players = total_players + $7, - updated_at = CURRENT_TIMESTAMP -WHERE - provider_id = $1 - AND report_date = $2 - AND report_type = $3 -` - -type UpdateVirtualGameProviderReportByDateParams struct { - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType pgtype.Text `json:"report_type"` - TotalGamesPlayed pgtype.Int8 `json:"total_games_played"` - TotalBets pgtype.Numeric `json:"total_bets"` - TotalPayouts pgtype.Numeric `json:"total_payouts"` - TotalPlayers pgtype.Int8 `json:"total_players"` -} - -func (q *Queries) UpdateVirtualGameProviderReportByDate(ctx context.Context, arg UpdateVirtualGameProviderReportByDateParams) error { - _, err := q.db.Exec(ctx, UpdateVirtualGameProviderReportByDate, - arg.ProviderID, - arg.ReportDate, - arg.ReportType, - arg.TotalGamesPlayed, - arg.TotalBets, - arg.TotalPayouts, - arg.TotalPlayers, - ) - return err -} - -const UpdateVirtualGameTransactionStatus = `-- name: UpdateVirtualGameTransactionStatus :exec -UPDATE virtual_game_transactions -SET status = $2, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateVirtualGameTransactionStatusParams struct { - ID int64 `json:"id"` - Status string `json:"status"` -} - -func (q *Queries) UpdateVirtualGameTransactionStatus(ctx context.Context, arg UpdateVirtualGameTransactionStatusParams) error { - _, err := q.db.Exec(ctx, UpdateVirtualGameTransactionStatus, arg.ID, arg.Status) - return err -} diff --git a/gen/db/virtual_report.sql.go b/gen/db/virtual_report.sql.go deleted file mode 100644 index 104b4c5..0000000 --- a/gen/db/virtual_report.sql.go +++ /dev/null @@ -1,721 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: virtual_report.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateCompanyReport = `-- name: CreateCompanyReport :one -INSERT INTO virtual_game_company_reports ( - company_id, provider_id, - report_date, report_type, - total_bet_amount, total_win_amount, created_at -) -VALUES ($1, $2, $3, $4, $5, $6, NOW()) -RETURNING id, company_id, provider_id, report_date, report_type, total_bet_amount, total_win_amount, net_profit, profit_margin, created_at, updated_at -` - -type CreateCompanyReportParams struct { - CompanyID int64 `json:"company_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` - TotalBetAmount pgtype.Numeric `json:"total_bet_amount"` - TotalWinAmount pgtype.Numeric `json:"total_win_amount"` -} - -func (q *Queries) CreateCompanyReport(ctx context.Context, arg CreateCompanyReportParams) (VirtualGameCompanyReport, error) { - row := q.db.QueryRow(ctx, CreateCompanyReport, - arg.CompanyID, - arg.ProviderID, - arg.ReportDate, - arg.ReportType, - arg.TotalBetAmount, - arg.TotalWinAmount, - ) - var i VirtualGameCompanyReport - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.ProviderID, - &i.ReportDate, - &i.ReportType, - &i.TotalBetAmount, - &i.TotalWinAmount, - &i.NetProfit, - &i.ProfitMargin, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateFinancialReport = `-- name: CreateFinancialReport :one -INSERT INTO virtual_game_financial_reports ( - game_id, provider_id, report_date, report_type, - total_bets, total_wins, created_at -) VALUES ($1, $2, $3, $4, $5, $6, NOW()) -RETURNING id, game_id, provider_id, report_date, report_type, total_bets, total_wins, ggr, rtp, created_at, updated_at -` - -type CreateFinancialReportParams struct { - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` - TotalBets pgtype.Numeric `json:"total_bets"` - TotalWins pgtype.Numeric `json:"total_wins"` -} - -func (q *Queries) CreateFinancialReport(ctx context.Context, arg CreateFinancialReportParams) (VirtualGameFinancialReport, error) { - row := q.db.QueryRow(ctx, CreateFinancialReport, - arg.GameID, - arg.ProviderID, - arg.ReportDate, - arg.ReportType, - arg.TotalBets, - arg.TotalWins, - ) - var i VirtualGameFinancialReport - err := row.Scan( - &i.ID, - &i.GameID, - &i.ProviderID, - &i.ReportDate, - &i.ReportType, - &i.TotalBets, - &i.TotalWins, - &i.Ggr, - &i.Rtp, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreatePlayerActivityReport = `-- name: CreatePlayerActivityReport :one -INSERT INTO virtual_game_player_activity_reports ( - user_id, report_date, report_type, - total_deposits, total_withdrawals, - total_bet_amount, total_win_amount, - rounds_played, created_at -) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW()) -RETURNING id, user_id, report_date, report_type, total_deposits, total_withdrawals, net_contribution, total_bet_amount, total_win_amount, net_result, rounds_played, avg_bet_size, created_at, updated_at -` - -type CreatePlayerActivityReportParams struct { - UserID int64 `json:"user_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` - TotalDeposits pgtype.Numeric `json:"total_deposits"` - TotalWithdrawals pgtype.Numeric `json:"total_withdrawals"` - TotalBetAmount pgtype.Numeric `json:"total_bet_amount"` - TotalWinAmount pgtype.Numeric `json:"total_win_amount"` - RoundsPlayed pgtype.Int8 `json:"rounds_played"` -} - -func (q *Queries) CreatePlayerActivityReport(ctx context.Context, arg CreatePlayerActivityReportParams) (VirtualGamePlayerActivityReport, error) { - row := q.db.QueryRow(ctx, CreatePlayerActivityReport, - arg.UserID, - arg.ReportDate, - arg.ReportType, - arg.TotalDeposits, - arg.TotalWithdrawals, - arg.TotalBetAmount, - arg.TotalWinAmount, - arg.RoundsPlayed, - ) - var i VirtualGamePlayerActivityReport - err := row.Scan( - &i.ID, - &i.UserID, - &i.ReportDate, - &i.ReportType, - &i.TotalDeposits, - &i.TotalWithdrawals, - &i.NetContribution, - &i.TotalBetAmount, - &i.TotalWinAmount, - &i.NetResult, - &i.RoundsPlayed, - &i.AvgBetSize, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const DeleteFinancialReport = `-- name: DeleteFinancialReport :exec -DELETE FROM virtual_game_financial_reports -WHERE id = $1 -` - -func (q *Queries) DeleteFinancialReport(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeleteFinancialReport, id) - return err -} - -const DeletePlayerActivityReport = `-- name: DeletePlayerActivityReport :exec -DELETE FROM virtual_game_player_activity_reports -WHERE id = $1 -` - -func (q *Queries) DeletePlayerActivityReport(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, DeletePlayerActivityReport, id) - return err -} - -const GetCompanyProfitTrend = `-- name: GetCompanyProfitTrend :many -SELECT report_date, SUM(net_profit) AS total_profit -FROM virtual_game_company_reports -WHERE company_id = $1 - AND provider_id = $2 - AND report_date BETWEEN $3 AND $4 -GROUP BY report_date -ORDER BY report_date -` - -type GetCompanyProfitTrendParams struct { - CompanyID int64 `json:"company_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportDate_2 pgtype.Date `json:"report_date_2"` -} - -type GetCompanyProfitTrendRow struct { - ReportDate pgtype.Date `json:"report_date"` - TotalProfit int64 `json:"total_profit"` -} - -func (q *Queries) GetCompanyProfitTrend(ctx context.Context, arg GetCompanyProfitTrendParams) ([]GetCompanyProfitTrendRow, error) { - rows, err := q.db.Query(ctx, GetCompanyProfitTrend, - arg.CompanyID, - arg.ProviderID, - arg.ReportDate, - arg.ReportDate_2, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetCompanyProfitTrendRow - for rows.Next() { - var i GetCompanyProfitTrendRow - if err := rows.Scan(&i.ReportDate, &i.TotalProfit); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetCompanyReportByID = `-- name: GetCompanyReportByID :one -SELECT id, company_id, provider_id, report_date, report_type, total_bet_amount, total_win_amount, net_profit, profit_margin, created_at, updated_at FROM virtual_game_company_reports -WHERE id = $1 -` - -func (q *Queries) GetCompanyReportByID(ctx context.Context, id int64) (VirtualGameCompanyReport, error) { - row := q.db.QueryRow(ctx, GetCompanyReportByID, id) - var i VirtualGameCompanyReport - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.ProviderID, - &i.ReportDate, - &i.ReportType, - &i.TotalBetAmount, - &i.TotalWinAmount, - &i.NetProfit, - &i.ProfitMargin, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetCompanyReportsInRange = `-- name: GetCompanyReportsInRange :many -SELECT id, company_id, provider_id, report_date, report_type, total_bet_amount, total_win_amount, net_profit, profit_margin, created_at, updated_at FROM virtual_game_company_reports -WHERE company_id = $1 - AND provider_id = $2 - AND report_date BETWEEN $3 AND $4 -ORDER BY report_date -` - -type GetCompanyReportsInRangeParams struct { - CompanyID int64 `json:"company_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportDate_2 pgtype.Date `json:"report_date_2"` -} - -func (q *Queries) GetCompanyReportsInRange(ctx context.Context, arg GetCompanyReportsInRangeParams) ([]VirtualGameCompanyReport, error) { - rows, err := q.db.Query(ctx, GetCompanyReportsInRange, - arg.CompanyID, - arg.ProviderID, - arg.ReportDate, - arg.ReportDate_2, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []VirtualGameCompanyReport - for rows.Next() { - var i VirtualGameCompanyReport - if err := rows.Scan( - &i.ID, - &i.CompanyID, - &i.ProviderID, - &i.ReportDate, - &i.ReportType, - &i.TotalBetAmount, - &i.TotalWinAmount, - &i.NetProfit, - &i.ProfitMargin, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetDailyFinancialReports = `-- name: GetDailyFinancialReports :many -SELECT id, game_id, provider_id, report_date, report_type, total_bets, total_wins, ggr, rtp, created_at, updated_at FROM virtual_game_financial_reports -WHERE report_date = $1 - AND report_type = 'daily' -` - -func (q *Queries) GetDailyFinancialReports(ctx context.Context, reportDate pgtype.Date) ([]VirtualGameFinancialReport, error) { - rows, err := q.db.Query(ctx, GetDailyFinancialReports, reportDate) - if err != nil { - return nil, err - } - defer rows.Close() - var items []VirtualGameFinancialReport - for rows.Next() { - var i VirtualGameFinancialReport - if err := rows.Scan( - &i.ID, - &i.GameID, - &i.ProviderID, - &i.ReportDate, - &i.ReportType, - &i.TotalBets, - &i.TotalWins, - &i.Ggr, - &i.Rtp, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetFinancialReportByID = `-- name: GetFinancialReportByID :one -SELECT id, game_id, provider_id, report_date, report_type, total_bets, total_wins, ggr, rtp, created_at, updated_at FROM virtual_game_financial_reports -WHERE id = $1 -` - -func (q *Queries) GetFinancialReportByID(ctx context.Context, id int64) (VirtualGameFinancialReport, error) { - row := q.db.QueryRow(ctx, GetFinancialReportByID, id) - var i VirtualGameFinancialReport - err := row.Scan( - &i.ID, - &i.GameID, - &i.ProviderID, - &i.ReportDate, - &i.ReportType, - &i.TotalBets, - &i.TotalWins, - &i.Ggr, - &i.Rtp, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetFinancialReportsForGame = `-- name: GetFinancialReportsForGame :many -SELECT id, game_id, provider_id, report_date, report_type, total_bets, total_wins, ggr, rtp, created_at, updated_at FROM virtual_game_financial_reports -WHERE game_id = $1 - AND provider_id = $2 - AND report_date BETWEEN $3 AND $4 -ORDER BY report_date -` - -type GetFinancialReportsForGameParams struct { - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportDate_2 pgtype.Date `json:"report_date_2"` -} - -func (q *Queries) GetFinancialReportsForGame(ctx context.Context, arg GetFinancialReportsForGameParams) ([]VirtualGameFinancialReport, error) { - rows, err := q.db.Query(ctx, GetFinancialReportsForGame, - arg.GameID, - arg.ProviderID, - arg.ReportDate, - arg.ReportDate_2, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []VirtualGameFinancialReport - for rows.Next() { - var i VirtualGameFinancialReport - if err := rows.Scan( - &i.ID, - &i.GameID, - &i.ProviderID, - &i.ReportDate, - &i.ReportType, - &i.TotalBets, - &i.TotalWins, - &i.Ggr, - &i.Rtp, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetPlayerActivityByDate = `-- name: GetPlayerActivityByDate :one -SELECT id, user_id, report_date, report_type, total_deposits, total_withdrawals, net_contribution, total_bet_amount, total_win_amount, net_result, rounds_played, avg_bet_size, created_at, updated_at FROM virtual_game_player_activity_reports -WHERE user_id = $1 - AND report_date = $2 - AND report_type = $3 -` - -type GetPlayerActivityByDateParams struct { - UserID int64 `json:"user_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` -} - -func (q *Queries) GetPlayerActivityByDate(ctx context.Context, arg GetPlayerActivityByDateParams) (VirtualGamePlayerActivityReport, error) { - row := q.db.QueryRow(ctx, GetPlayerActivityByDate, arg.UserID, arg.ReportDate, arg.ReportType) - var i VirtualGamePlayerActivityReport - err := row.Scan( - &i.ID, - &i.UserID, - &i.ReportDate, - &i.ReportType, - &i.TotalDeposits, - &i.TotalWithdrawals, - &i.NetContribution, - &i.TotalBetAmount, - &i.TotalWinAmount, - &i.NetResult, - &i.RoundsPlayed, - &i.AvgBetSize, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetPlayerActivityByID = `-- name: GetPlayerActivityByID :one -SELECT id, user_id, report_date, report_type, total_deposits, total_withdrawals, net_contribution, total_bet_amount, total_win_amount, net_result, rounds_played, avg_bet_size, created_at, updated_at FROM virtual_game_player_activity_reports -WHERE id = $1 -` - -func (q *Queries) GetPlayerActivityByID(ctx context.Context, id int64) (VirtualGamePlayerActivityReport, error) { - row := q.db.QueryRow(ctx, GetPlayerActivityByID, id) - var i VirtualGamePlayerActivityReport - err := row.Scan( - &i.ID, - &i.UserID, - &i.ReportDate, - &i.ReportType, - &i.TotalDeposits, - &i.TotalWithdrawals, - &i.NetContribution, - &i.TotalBetAmount, - &i.TotalWinAmount, - &i.NetResult, - &i.RoundsPlayed, - &i.AvgBetSize, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetPlayerActivityRange = `-- name: GetPlayerActivityRange :many -SELECT id, user_id, report_date, report_type, total_deposits, total_withdrawals, net_contribution, total_bet_amount, total_win_amount, net_result, rounds_played, avg_bet_size, created_at, updated_at FROM virtual_game_player_activity_reports -WHERE user_id = $1 - AND report_date BETWEEN $2 AND $3 -ORDER BY report_date -` - -type GetPlayerActivityRangeParams struct { - UserID int64 `json:"user_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportDate_2 pgtype.Date `json:"report_date_2"` -} - -func (q *Queries) GetPlayerActivityRange(ctx context.Context, arg GetPlayerActivityRangeParams) ([]VirtualGamePlayerActivityReport, error) { - rows, err := q.db.Query(ctx, GetPlayerActivityRange, arg.UserID, arg.ReportDate, arg.ReportDate_2) - if err != nil { - return nil, err - } - defer rows.Close() - var items []VirtualGamePlayerActivityReport - for rows.Next() { - var i VirtualGamePlayerActivityReport - if err := rows.Scan( - &i.ID, - &i.UserID, - &i.ReportDate, - &i.ReportType, - &i.TotalDeposits, - &i.TotalWithdrawals, - &i.NetContribution, - &i.TotalBetAmount, - &i.TotalWinAmount, - &i.NetResult, - &i.RoundsPlayed, - &i.AvgBetSize, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetTopPlayersByNetResult = `-- name: GetTopPlayersByNetResult :many -SELECT user_id, SUM(net_result) AS total_net -FROM virtual_game_player_activity_reports -WHERE report_date BETWEEN $1 AND $2 -GROUP BY user_id -ORDER BY total_net DESC -LIMIT $3 -` - -type GetTopPlayersByNetResultParams struct { - ReportDate pgtype.Date `json:"report_date"` - ReportDate_2 pgtype.Date `json:"report_date_2"` - Limit int32 `json:"limit"` -} - -type GetTopPlayersByNetResultRow struct { - UserID int64 `json:"user_id"` - TotalNet int64 `json:"total_net"` -} - -func (q *Queries) GetTopPlayersByNetResult(ctx context.Context, arg GetTopPlayersByNetResultParams) ([]GetTopPlayersByNetResultRow, error) { - rows, err := q.db.Query(ctx, GetTopPlayersByNetResult, arg.ReportDate, arg.ReportDate_2, arg.Limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetTopPlayersByNetResultRow - for rows.Next() { - var i GetTopPlayersByNetResultRow - if err := rows.Scan(&i.UserID, &i.TotalNet); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpsertCompanyReport = `-- name: UpsertCompanyReport :one -INSERT INTO virtual_game_company_reports ( - company_id, provider_id, - report_date, report_type, - total_bet_amount, total_win_amount, - created_at, updated_at -) -VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW()) -ON CONFLICT (company_id, provider_id, report_date, report_type) -DO UPDATE SET - total_bet_amount = EXCLUDED.total_bet_amount, - total_win_amount = EXCLUDED.total_win_amount, - updated_at = NOW() -RETURNING id, company_id, provider_id, report_date, report_type, total_bet_amount, total_win_amount, net_profit, profit_margin, created_at, updated_at -` - -type UpsertCompanyReportParams struct { - CompanyID int64 `json:"company_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` - TotalBetAmount pgtype.Numeric `json:"total_bet_amount"` - TotalWinAmount pgtype.Numeric `json:"total_win_amount"` -} - -func (q *Queries) UpsertCompanyReport(ctx context.Context, arg UpsertCompanyReportParams) (VirtualGameCompanyReport, error) { - row := q.db.QueryRow(ctx, UpsertCompanyReport, - arg.CompanyID, - arg.ProviderID, - arg.ReportDate, - arg.ReportType, - arg.TotalBetAmount, - arg.TotalWinAmount, - ) - var i VirtualGameCompanyReport - err := row.Scan( - &i.ID, - &i.CompanyID, - &i.ProviderID, - &i.ReportDate, - &i.ReportType, - &i.TotalBetAmount, - &i.TotalWinAmount, - &i.NetProfit, - &i.ProfitMargin, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const UpsertFinancialReport = `-- name: UpsertFinancialReport :one -INSERT INTO virtual_game_financial_reports ( - game_id, provider_id, report_date, report_type, - total_bets, total_wins, created_at, updated_at -) -VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW()) -ON CONFLICT (game_id, provider_id, report_date, report_type) -DO UPDATE SET - total_bets = EXCLUDED.total_bets, - total_wins = EXCLUDED.total_wins, - updated_at = NOW() -RETURNING id, game_id, provider_id, report_date, report_type, total_bets, total_wins, ggr, rtp, created_at, updated_at -` - -type UpsertFinancialReportParams struct { - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` - TotalBets pgtype.Numeric `json:"total_bets"` - TotalWins pgtype.Numeric `json:"total_wins"` -} - -func (q *Queries) UpsertFinancialReport(ctx context.Context, arg UpsertFinancialReportParams) (VirtualGameFinancialReport, error) { - row := q.db.QueryRow(ctx, UpsertFinancialReport, - arg.GameID, - arg.ProviderID, - arg.ReportDate, - arg.ReportType, - arg.TotalBets, - arg.TotalWins, - ) - var i VirtualGameFinancialReport - err := row.Scan( - &i.ID, - &i.GameID, - &i.ProviderID, - &i.ReportDate, - &i.ReportType, - &i.TotalBets, - &i.TotalWins, - &i.Ggr, - &i.Rtp, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const UpsertPlayerActivityReport = `-- name: UpsertPlayerActivityReport :one -INSERT INTO virtual_game_player_activity_reports ( - user_id, report_date, report_type, - total_deposits, total_withdrawals, - total_bet_amount, total_win_amount, - rounds_played, created_at, updated_at -) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW()) -ON CONFLICT (user_id, report_date, report_type) -DO UPDATE SET - total_deposits = EXCLUDED.total_deposits, - total_withdrawals = EXCLUDED.total_withdrawals, - total_bet_amount = EXCLUDED.total_bet_amount, - total_win_amount = EXCLUDED.total_win_amount, - rounds_played = EXCLUDED.rounds_played, - updated_at = NOW() -RETURNING id, user_id, report_date, report_type, total_deposits, total_withdrawals, net_contribution, total_bet_amount, total_win_amount, net_result, rounds_played, avg_bet_size, created_at, updated_at -` - -type UpsertPlayerActivityReportParams struct { - UserID int64 `json:"user_id"` - ReportDate pgtype.Date `json:"report_date"` - ReportType string `json:"report_type"` - TotalDeposits pgtype.Numeric `json:"total_deposits"` - TotalWithdrawals pgtype.Numeric `json:"total_withdrawals"` - TotalBetAmount pgtype.Numeric `json:"total_bet_amount"` - TotalWinAmount pgtype.Numeric `json:"total_win_amount"` - RoundsPlayed pgtype.Int8 `json:"rounds_played"` -} - -func (q *Queries) UpsertPlayerActivityReport(ctx context.Context, arg UpsertPlayerActivityReportParams) (VirtualGamePlayerActivityReport, error) { - row := q.db.QueryRow(ctx, UpsertPlayerActivityReport, - arg.UserID, - arg.ReportDate, - arg.ReportType, - arg.TotalDeposits, - arg.TotalWithdrawals, - arg.TotalBetAmount, - arg.TotalWinAmount, - arg.RoundsPlayed, - ) - var i VirtualGamePlayerActivityReport - err := row.Scan( - &i.ID, - &i.UserID, - &i.ReportDate, - &i.ReportType, - &i.TotalDeposits, - &i.TotalWithdrawals, - &i.NetContribution, - &i.TotalBetAmount, - &i.TotalWinAmount, - &i.NetResult, - &i.RoundsPlayed, - &i.AvgBetSize, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} diff --git a/gen/db/wallet.sql.go b/gen/db/wallet.sql.go deleted file mode 100644 index 384bb63..0000000 --- a/gen/db/wallet.sql.go +++ /dev/null @@ -1,430 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: wallet.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateCustomerWallet = `-- name: CreateCustomerWallet :one -INSERT INTO customer_wallets ( - customer_id, - regular_wallet_id, - static_wallet_id - ) -VALUES ($1, $2, $3) -RETURNING id, customer_id, regular_wallet_id, static_wallet_id, created_at, updated_at -` - -type CreateCustomerWalletParams struct { - CustomerID int64 `json:"customer_id"` - RegularWalletID int64 `json:"regular_wallet_id"` - StaticWalletID int64 `json:"static_wallet_id"` -} - -func (q *Queries) CreateCustomerWallet(ctx context.Context, arg CreateCustomerWalletParams) (CustomerWallet, error) { - row := q.db.QueryRow(ctx, CreateCustomerWallet, arg.CustomerID, arg.RegularWalletID, arg.StaticWalletID) - var i CustomerWallet - err := row.Scan( - &i.ID, - &i.CustomerID, - &i.RegularWalletID, - &i.StaticWalletID, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const CreateWallet = `-- name: CreateWallet :one -INSERT INTO wallets ( - is_withdraw, - is_bettable, - is_transferable, - user_id, - type - ) -VALUES ($1, $2, $3, $4, $5) -RETURNING id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at -` - -type CreateWalletParams struct { - IsWithdraw bool `json:"is_withdraw"` - IsBettable bool `json:"is_bettable"` - IsTransferable bool `json:"is_transferable"` - UserID int64 `json:"user_id"` - Type string `json:"type"` -} - -func (q *Queries) CreateWallet(ctx context.Context, arg CreateWalletParams) (Wallet, error) { - row := q.db.QueryRow(ctx, CreateWallet, - arg.IsWithdraw, - arg.IsBettable, - arg.IsTransferable, - arg.UserID, - arg.Type, - ) - var i Wallet - err := row.Scan( - &i.ID, - &i.Balance, - &i.Currency, - &i.IsWithdraw, - &i.IsBettable, - &i.IsTransferable, - &i.UserID, - &i.Type, - &i.IsActive, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetAllBranchWallets = `-- name: GetAllBranchWallets :many -SELECT wallets.id, - wallets.balance, - wallets.is_active, - wallets.updated_at, - wallets.created_at, - branches.name, - branches.location, - branches.branch_manager_id, - branches.company_id, - branches.is_self_owned -FROM branches - JOIN wallets ON branches.wallet_id = wallets.id -` - -type GetAllBranchWalletsRow struct { - ID int64 `json:"id"` - Balance int64 `json:"balance"` - IsActive bool `json:"is_active"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` - CreatedAt pgtype.Timestamp `json:"created_at"` - Name string `json:"name"` - Location string `json:"location"` - BranchManagerID int64 `json:"branch_manager_id"` - CompanyID int64 `json:"company_id"` - IsSelfOwned bool `json:"is_self_owned"` -} - -func (q *Queries) GetAllBranchWallets(ctx context.Context) ([]GetAllBranchWalletsRow, error) { - rows, err := q.db.Query(ctx, GetAllBranchWallets) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetAllBranchWalletsRow - for rows.Next() { - var i GetAllBranchWalletsRow - if err := rows.Scan( - &i.ID, - &i.Balance, - &i.IsActive, - &i.UpdatedAt, - &i.CreatedAt, - &i.Name, - &i.Location, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllCustomerWallet = `-- name: GetAllCustomerWallet :many -SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number, number_of_transactions, total_transactions, number_of_deposits, total_deposits_amount, number_of_withdraws, total_withdraws_amount, number_of_transfers, total_transfers_amount, stats_updated_at -FROM customer_wallet_details -` - -func (q *Queries) GetAllCustomerWallet(ctx context.Context) ([]CustomerWalletDetail, error) { - rows, err := q.db.Query(ctx, GetAllCustomerWallet) - if err != nil { - return nil, err - } - defer rows.Close() - var items []CustomerWalletDetail - for rows.Next() { - var i CustomerWalletDetail - if err := rows.Scan( - &i.ID, - &i.CustomerID, - &i.RegularID, - &i.RegularBalance, - &i.StaticID, - &i.StaticBalance, - &i.RegularIsActive, - &i.StaticIsActive, - &i.RegularUpdatedAt, - &i.StaticUpdatedAt, - &i.CreatedAt, - &i.FirstName, - &i.LastName, - &i.PhoneNumber, - &i.NumberOfTransactions, - &i.TotalTransactions, - &i.NumberOfDeposits, - &i.TotalDepositsAmount, - &i.NumberOfWithdraws, - &i.TotalWithdrawsAmount, - &i.NumberOfTransfers, - &i.TotalTransfersAmount, - &i.StatsUpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetAllWallets = `-- name: GetAllWallets :many -SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at -FROM wallets -` - -func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) { - rows, err := q.db.Query(ctx, GetAllWallets) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Wallet - for rows.Next() { - var i Wallet - if err := rows.Scan( - &i.ID, - &i.Balance, - &i.Currency, - &i.IsWithdraw, - &i.IsBettable, - &i.IsTransferable, - &i.UserID, - &i.Type, - &i.IsActive, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetBranchByWalletID = `-- name: GetBranchByWalletID :one -SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at -FROM branches -WHERE wallet_id = $1 -LIMIT 1 -` - -type GetBranchByWalletIDRow struct { - ID int64 `json:"id"` - Name string `json:"name"` - Location string `json:"location"` - IsActive bool `json:"is_active"` - WalletID int64 `json:"wallet_id"` - BranchManagerID int64 `json:"branch_manager_id"` - CompanyID int64 `json:"company_id"` - IsSelfOwned bool `json:"is_self_owned"` - CreatedAt pgtype.Timestamp `json:"created_at"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetBranchByWalletID(ctx context.Context, walletID int64) (GetBranchByWalletIDRow, error) { - row := q.db.QueryRow(ctx, GetBranchByWalletID, walletID) - var i GetBranchByWalletIDRow - err := row.Scan( - &i.ID, - &i.Name, - &i.Location, - &i.IsActive, - &i.WalletID, - &i.BranchManagerID, - &i.CompanyID, - &i.IsSelfOwned, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetCompanyByWalletID = `-- name: GetCompanyByWalletID :one -SELECT id, name, admin_id, wallet_id -FROM companies -WHERE wallet_id = $1 -LIMIT 1 -` - -type GetCompanyByWalletIDRow struct { - ID int64 `json:"id"` - Name string `json:"name"` - AdminID int64 `json:"admin_id"` - WalletID int64 `json:"wallet_id"` -} - -func (q *Queries) GetCompanyByWalletID(ctx context.Context, walletID int64) (GetCompanyByWalletIDRow, error) { - row := q.db.QueryRow(ctx, GetCompanyByWalletID, walletID) - var i GetCompanyByWalletIDRow - err := row.Scan( - &i.ID, - &i.Name, - &i.AdminID, - &i.WalletID, - ) - return i, err -} - -const GetCustomerWallet = `-- name: GetCustomerWallet :one -SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number, number_of_transactions, total_transactions, number_of_deposits, total_deposits_amount, number_of_withdraws, total_withdraws_amount, number_of_transfers, total_transfers_amount, stats_updated_at -FROM customer_wallet_details -WHERE customer_id = $1 -` - -func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (CustomerWalletDetail, error) { - row := q.db.QueryRow(ctx, GetCustomerWallet, customerID) - var i CustomerWalletDetail - err := row.Scan( - &i.ID, - &i.CustomerID, - &i.RegularID, - &i.RegularBalance, - &i.StaticID, - &i.StaticBalance, - &i.RegularIsActive, - &i.StaticIsActive, - &i.RegularUpdatedAt, - &i.StaticUpdatedAt, - &i.CreatedAt, - &i.FirstName, - &i.LastName, - &i.PhoneNumber, - &i.NumberOfTransactions, - &i.TotalTransactions, - &i.NumberOfDeposits, - &i.TotalDepositsAmount, - &i.NumberOfWithdraws, - &i.TotalWithdrawsAmount, - &i.NumberOfTransfers, - &i.TotalTransfersAmount, - &i.StatsUpdatedAt, - ) - return i, err -} - -const GetWalletByID = `-- name: GetWalletByID :one -SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at -FROM wallets -WHERE id = $1 -` - -func (q *Queries) GetWalletByID(ctx context.Context, id int64) (Wallet, error) { - row := q.db.QueryRow(ctx, GetWalletByID, id) - var i Wallet - err := row.Scan( - &i.ID, - &i.Balance, - &i.Currency, - &i.IsWithdraw, - &i.IsBettable, - &i.IsTransferable, - &i.UserID, - &i.Type, - &i.IsActive, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetWalletByUserID = `-- name: GetWalletByUserID :many -SELECT id, balance, currency, is_withdraw, is_bettable, is_transferable, user_id, type, is_active, created_at, updated_at -FROM wallets -WHERE user_id = $1 -` - -func (q *Queries) GetWalletByUserID(ctx context.Context, userID int64) ([]Wallet, error) { - rows, err := q.db.Query(ctx, GetWalletByUserID, userID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Wallet - for rows.Next() { - var i Wallet - if err := rows.Scan( - &i.ID, - &i.Balance, - &i.Currency, - &i.IsWithdraw, - &i.IsBettable, - &i.IsTransferable, - &i.UserID, - &i.Type, - &i.IsActive, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateBalance = `-- name: UpdateBalance :exec -UPDATE wallets -SET balance = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2 -` - -type UpdateBalanceParams struct { - Balance int64 `json:"balance"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateBalance(ctx context.Context, arg UpdateBalanceParams) error { - _, err := q.db.Exec(ctx, UpdateBalance, arg.Balance, arg.ID) - return err -} - -const UpdateWalletActive = `-- name: UpdateWalletActive :exec -UPDATE wallets -SET is_active = $1, - updated_at = CURRENT_TIMESTAMP -WHERE id = $2 -` - -type UpdateWalletActiveParams struct { - IsActive bool `json:"is_active"` - ID int64 `json:"id"` -} - -func (q *Queries) UpdateWalletActive(ctx context.Context, arg UpdateWalletActiveParams) error { - _, err := q.db.Exec(ctx, UpdateWalletActive, arg.IsActive, arg.ID) - return err -} diff --git a/gen/db/wallet_stats.sql.go b/gen/db/wallet_stats.sql.go deleted file mode 100644 index 8911d42..0000000 --- a/gen/db/wallet_stats.sql.go +++ /dev/null @@ -1,248 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: wallet_stats.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const GetWalletStats = `-- name: GetWalletStats :many -SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start, - wallet_stats.wallet_id, - wallet_stats.wallet_user_id, - wallet_stats.wallet_user_first_name, - wallet_stats.wallet_user_last_name, - wallet_stats.wallet_type, - wallet_stats.number_of_transactions, - wallet_stats.total_transactions, - wallet_stats.number_of_deposits, - wallet_stats.total_deposits_amount, - wallet_stats.number_of_withdraws, - wallet_stats.total_withdraws_amount, - wallet_stats.number_of_transfers, - wallet_stats.total_transfers_amount, - wallet_stats.updated_at -FROM wallet_stats -WHERE ( - wallet_stats.wallet_user_id = $2 - OR $2 IS NULL - ) -GROUP BY interval_start -ORDER BY interval_start DESC -` - -type GetWalletStatsParams struct { - Interval pgtype.Text `json:"interval"` - UserID pgtype.Int8 `json:"user_id"` -} - -type GetWalletStatsRow struct { - IntervalStart pgtype.Timestamp `json:"interval_start"` - WalletID int64 `json:"wallet_id"` - WalletUserID int64 `json:"wallet_user_id"` - WalletUserFirstName string `json:"wallet_user_first_name"` - WalletUserLastName string `json:"wallet_user_last_name"` - WalletType string `json:"wallet_type"` - NumberOfTransactions int64 `json:"number_of_transactions"` - TotalTransactions int64 `json:"total_transactions"` - NumberOfDeposits int64 `json:"number_of_deposits"` - TotalDepositsAmount int64 `json:"total_deposits_amount"` - NumberOfWithdraws int64 `json:"number_of_withdraws"` - TotalWithdrawsAmount int64 `json:"total_withdraws_amount"` - NumberOfTransfers int64 `json:"number_of_transfers"` - TotalTransfersAmount int64 `json:"total_transfers_amount"` - UpdatedAt pgtype.Timestamp `json:"updated_at"` -} - -func (q *Queries) GetWalletStats(ctx context.Context, arg GetWalletStatsParams) ([]GetWalletStatsRow, error) { - rows, err := q.db.Query(ctx, GetWalletStats, arg.Interval, arg.UserID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetWalletStatsRow - for rows.Next() { - var i GetWalletStatsRow - if err := rows.Scan( - &i.IntervalStart, - &i.WalletID, - &i.WalletUserID, - &i.WalletUserFirstName, - &i.WalletUserLastName, - &i.WalletType, - &i.NumberOfTransactions, - &i.TotalTransactions, - &i.NumberOfDeposits, - &i.TotalDepositsAmount, - &i.NumberOfWithdraws, - &i.TotalWithdrawsAmount, - &i.NumberOfTransfers, - &i.TotalTransfersAmount, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetWalletStatsByID = `-- name: GetWalletStatsByID :many -SELECT wallet_id, wallet_user_id, wallet_user_first_name, wallet_user_last_name, wallet_type, interval_start, number_of_transactions, total_transactions, number_of_deposits, total_deposits_amount, number_of_withdraws, total_withdraws_amount, number_of_transfers, total_transfers_amount, updated_at -FROM wallet_stats -WHERE wallet_id = $1 -ORDER BY interval_start DESC -` - -func (q *Queries) GetWalletStatsByID(ctx context.Context, walletID int64) ([]WalletStat, error) { - rows, err := q.db.Query(ctx, GetWalletStatsByID, walletID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []WalletStat - for rows.Next() { - var i WalletStat - if err := rows.Scan( - &i.WalletID, - &i.WalletUserID, - &i.WalletUserFirstName, - &i.WalletUserLastName, - &i.WalletType, - &i.IntervalStart, - &i.NumberOfTransactions, - &i.TotalTransactions, - &i.NumberOfDeposits, - &i.TotalDepositsAmount, - &i.NumberOfWithdraws, - &i.TotalWithdrawsAmount, - &i.NumberOfTransfers, - &i.TotalTransfersAmount, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const UpdateWalletStats = `-- name: UpdateWalletStats :exec -WITH all_transfers AS ( - SELECT sender_wallet_id AS wallet_id, - amount, - type - FROM wallet_transfer - WHERE sender_wallet_id IS NOT NULL - UNION ALL - SELECT receiver_wallet_id AS wallet_id, - amount, - type - FROM wallet_transfer - WHERE receiver_wallet_id IS NOT NULL -), -transfer_stats AS ( - SELECT wallet_id, - COUNT(*) AS number_of_transactions, - COALESCE(SUM(amount), 0) AS total_transactions, - COUNT(*) FILTER ( - WHERE type = 'deposit' - ) AS number_of_deposits, - COALESCE( - SUM( - CASE - WHEN type = 'deposit' THEN amount - ELSE 0 - END - ), - 0 - ) AS total_deposits_amount, - COUNT(*) FILTER ( - WHERE type = 'withdraw' - ) AS number_of_withdraws, - COALESCE( - SUM( - CASE - WHEN type = 'withdraw' THEN amount - ELSE 0 - END - ), - 0 - ) AS total_withdraws_amount, - COUNT(*) FILTER ( - WHERE type = 'wallet' - ) AS number_of_transfers, - COALESCE( - SUM( - CASE - WHEN type = 'wallet' THEN amount - ELSE 0 - END - ), - 0 - ) AS total_transfers_amount - FROM all_transfers - GROUP BY wallet_id -) -INSERT INTO wallet_stats( - wallet_id, - wallet_user_id, - wallet_user_first_name, - wallet_user_last_name, - wallet_type, - interval_start, - number_of_transactions, - total_transactions, - number_of_deposits, - total_deposits_amount, - number_of_withdraws, - total_withdraws_amount, - number_of_transfers, - total_transfers_amount, - updated_at - ) -SELECT w.id AS wallet_id, - w.user_id AS wallet_user_id, - u.first_name AS wallet_user_first_name, - u.last_name AS wallet_user_last_name, - w.type AS wallet_type, - DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start, - COALESCE(ts.number_of_transactions, 0) AS number_of_transactions, - COALESCE(ts.total_transactions, 0) AS total_transactions, - COALESCE(ts.number_of_deposits, 0) AS number_of_deposits, - COALESCE(ts.total_deposits_amount, 0) AS total_deposits_amount, - COALESCE(ts.number_of_withdraws, 0) AS number_of_withdraws, - COALESCE(ts.total_withdraws_amount, 0) AS total_withdraws_amount, - COALESCE(ts.number_of_transfers, 0) AS number_of_transfers, - COALESCE(ts.total_transfers_amount, 0) AS total_transfers_amount, - NOW() AS updated_at -FROM wallets w - LEFT JOIN users u ON u.id = w.user_id - LEFT JOIN transfer_stats ts ON ts.wallet_id = w.id ON CONFLICT (wallet_id, interval_start) DO -UPDATE -SET number_of_transactions = EXCLUDED.number_of_transactions, - total_transactions = EXCLUDED.total_transactions, - number_of_deposits = EXCLUDED.number_of_deposits, - total_deposits_amount = EXCLUDED.total_deposits_amount, - number_of_withdraws = EXCLUDED.number_of_withdraws, - total_withdraws_amount = EXCLUDED.total_withdraws_amount, - number_of_transfers = EXCLUDED.number_of_transfers, - total_transfers_amount = EXCLUDED.total_transfers_amount, - updated_at = EXCLUDED.updated_at -` - -func (q *Queries) UpdateWalletStats(ctx context.Context) error { - _, err := q.db.Exec(ctx, UpdateWalletStats) - return err -} diff --git a/go.mod b/go.mod index 207087d..9f374c4 100644 --- a/go.mod +++ b/go.mod @@ -1,91 +1,83 @@ -module github.com/SamuelTariku/FortuneBet-Backend +module Yimaru-Backend -go 1.24.1 +go 1.24.0 + +toolchain go1.24.11 require ( github.com/amanuelabay/afrosms-go v1.0.6 - github.com/bytedance/sonic v1.13.2 - github.com/go-playground/validator/v10 v10.26.0 - github.com/gofiber/fiber/v2 v2.52.6 - github.com/golang-jwt/jwt/v5 v5.2.2 - github.com/google/uuid v1.6.0 - github.com/gorilla/websocket v1.5.3 - github.com/jackc/pgx/v5 v5.7.4 + github.com/go-co-op/gocron v1.37.0 + github.com/go-playground/validator/v10 v10.29.0 github.com/joho/godotenv v1.5.1 - github.com/robfig/cron/v3 v3.0.1 - // github.com/stretchr/testify v1.10.0 + github.com/resend/resend-go/v2 v2.28.0 github.com/swaggo/fiber-swagger v1.3.0 - github.com/swaggo/swag v1.16.4 - github.com/valyala/fasthttp v1.63.0 - golang.org/x/crypto v0.41.0 + github.com/swaggo/swag v1.16.6 + github.com/twilio/twilio-go v1.28.8 + golang.org/x/crypto v0.45.0 ) require ( - // github.com/davecgh/go-spew v1.1.1 // indirect - // github.com/pmezard/go-difflib v1.0.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect - github.com/andybalholm/brotli v1.2.0 // indirect - // github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 - github.com/bytedance/sonic/loader v0.2.4 // indirect - github.com/cloudwego/base64x v0.1.5 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect - github.com/go-openapi/jsonpointer v0.21.1 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/swag v0.23.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic/loader v0.4.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/gabriel-vasile/mimetype v1.4.11 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.18.0 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/mailru/easyjson v0.9.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - go.mongodb.org/mongo-driver v1.17.3 - golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.28.0 // indirect - golang.org/x/tools v0.36.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) - -require ( - github.com/golang/snappy v0.0.4 // indirect - github.com/montanaflynn/stats v0.7.1 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect - go.uber.org/zap v1.27.0 -) - -require ( - github.com/go-co-op/gocron v1.37.0 - github.com/resend/resend-go/v2 v2.20.0 // direct - go.uber.org/multierr v1.10.0 // indirect -) - -// github.com/go-resty/resty/v2 v2.16.5 -require github.com/twilio/twilio-go v1.26.3 - -require ( - github.com/golang/mock v1.6.0 // indirect - github.com/pkg/errors v0.9.1 // indirect go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/tools v0.38.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) -// require github.com/AnaniyaBelew/ArifpayGoPlugin v0.0.0-20231127130208-54b9bc51118f - -// require github.com/AnaniyaBelew/ArifpayGoPlugin v0.0.0-20231127130208-54b9bc51118f // direct +require ( + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/bytedance/sonic v1.14.2 + github.com/gofiber/fiber/v2 v2.52.10 + github.com/golang-jwt/jwt/v5 v5.3.0 + github.com/golang/mock v1.6.0 // indirect + github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.3 + github.com/jackc/pgtype v1.14.4 + github.com/jackc/pgx/v5 v5.7.6 + github.com/klauspost/compress v1.17.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.51.0 + github.com/valyala/tcplisten v1.0.0 // indirect + go.mongodb.org/mongo-driver v1.17.6 + go.uber.org/zap v1.27.1 + golang.org/x/sys v0.38.0 // indirect +) diff --git a/go.sum b/go.sum index d639aba..ce3fe7d 100644 --- a/go.sum +++ b/go.sum @@ -1,78 +1,128 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/amanuelabay/afrosms-go v1.0.6 h1:/B9upckMqzr5/de7dbXPqIfmJm4utbQq0QUQePxmXtk= github.com/amanuelabay/afrosms-go v1.0.6/go.mod h1:5mzzZtWSCDdvQsA0OyYf5CtbdGpl9lXyrcpl8/DyBj0= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= -github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= -github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= -github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= -github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= -github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= +github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= +github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= +github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik= +github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= -github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= -github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= -github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-playground/validator/v10 v10.29.0 h1:lQlF5VNJWNlRbRZNeOIkWElR+1LL/OuHcc0Kp14w1xk= +github.com/go-playground/validator/v10 v10.29.0/go.mod h1:D6QxqeMlgIPuT02L66f2ccrZ7AGgHkzKmmTMZhk/Kc4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofiber/fiber/v2 v2.32.0/go.mod h1:CMy5ZLiXkn6qwthrl03YMyW1NLfj0rhxz2LKl4t7ZTY= -github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= -github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY= +github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg= -github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= +github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= +github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -80,32 +130,43 @@ github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q= github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= @@ -119,35 +180,50 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6 github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/resend/resend-go/v2 v2.20.0 h1:MrIrgV0aHhwRgmcRPw33Nexn6aGJvCvG2XwfFpAMBGM= -github.com/resend/resend-go/v2 v2.20.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ= +github.com/resend/resend-go/v2 v2.28.0 h1:ttM1/VZR4fApBv3xI1TneSKi1pbfFsVrq7fXFlHKtj4= +github.com/resend/resend-go/v2 v2.28.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/swaggo/fiber-swagger v1.3.0 h1:RMjIVDleQodNVdKuu7GRs25Eq8RVXK7MwY9f5jbobNg= @@ -155,10 +231,10 @@ github.com/swaggo/fiber-swagger v1.3.0/go.mod h1:18MuDqBkYEiUmeM/cAAB8CI28Bi62d/ github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc= github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ= -github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= -github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= -github.com/twilio/twilio-go v1.26.3 h1:K2mYBzbhPVyWF+Jq5Sw53edBFvkgWo4sKTvgaO7461I= -github.com/twilio/twilio-go v1.26.3/go.mod h1:FpgNWMoD8CFnmukpKq9RNpUSGXC0BwnbeKZj2YHlIkw= +github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= +github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= +github.com/twilio/twilio-go v1.28.8 h1:wbFz7Wt4S5mCEaes6FcM/ddcJGIhdjwp/9CHb9e+4fk= +github.com/twilio/twilio-go v1.28.8/go.mod h1:FpgNWMoD8CFnmukpKq9RNpUSGXC0BwnbeKZj2YHlIkw= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= @@ -166,8 +242,9 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.35.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/fasthttp v1.36.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= -github.com/valyala/fasthttp v1.63.0 h1:DisIL8OjB7ul2d7cBaMRcKTQDYnrGy56R4FCiuDP0Ns= -github.com/valyala/fasthttp v1.63.0/go.mod h1:REc4IeW+cAEyLrRPa5A81MIjvz0QE1laoTX2EaPHKJM= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -175,38 +252,62 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= -go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= +go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= @@ -214,15 +315,27 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -234,26 +347,49 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -263,11 +399,13 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/internal/config/config.go b/internal/config/config.go index 329cf75..fdf4bb2 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,16 +1,15 @@ package config import ( + "Yimaru-Backend/internal/domain" + customlogger "Yimaru-Backend/internal/logger" "errors" - "fmt" "log/slog" "os" "strconv" "strings" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - customlogger "github.com/SamuelTariku/FortuneBet-Backend/internal/logger" "github.com/joho/godotenv" ) @@ -25,19 +24,19 @@ var ( ErrInvalidEnv = errors.New("env not set or invalid") ErrInvalidReportExportPath = errors.New("report export path is invalid") ErrInvalidSMSAPIKey = errors.New("SMS API key is invalid") - ErrMissingBetToken = errors.New("missing BET365_TOKEN in .env") - ErrInvalidPopOKClientID = errors.New("PopOK client ID is invalid") - ErrInvalidPopOKSecretKey = errors.New("PopOK secret key is invalid") - ErrInvalidPopOKBaseURL = errors.New("PopOK base URL is invalid") - ErrInvalidPopOKCallbackURL = errors.New("PopOK callback URL is invalid") - ErrInvalidVeliAPIURL = errors.New("Veli API URL is invalid") - ErrInvalidVeliOperatorKey = errors.New("Veli operator key is invalid") - ErrInvalidVeliSecretKey = errors.New("Veli secret key is invalid") - ErrInvalidAtlasBaseUrl = errors.New("Atlas Base URL is invalid") - ErrInvalidAtlasOperatorID = errors.New("Atlas operator ID is invalid") - ErrInvalidAtlasSecretKey = errors.New("Atlas secret key is invalid") - ErrInvalidAtlasBrandID = errors.New("Atlas brand ID is invalid") - ErrInvalidAtlasPartnerID = errors.New("Atlas Partner ID is invalid") + // ErrMissingBetToken = errors.New("missing BET365_TOKEN in .env") + // ErrInvalidPopOKClientID = errors.New("PopOK client ID is invalid") + // ErrInvalidPopOKSecretKey = errors.New("PopOK secret key is invalid") + // ErrInvalidPopOKBaseURL = errors.New("PopOK base URL is invalid") + // ErrInvalidPopOKCallbackURL = errors.New("PopOK callback URL is invalid") + // ErrInvalidVeliAPIURL = errors.New("Veli API URL is invalid") + // ErrInvalidVeliOperatorKey = errors.New("Veli operator key is invalid") + // ErrInvalidVeliSecretKey = errors.New("Veli secret key is invalid") + // ErrInvalidAtlasBaseUrl = errors.New("Atlas Base URL is invalid") + // ErrInvalidAtlasOperatorID = errors.New("Atlas operator ID is invalid") + // ErrInvalidAtlasSecretKey = errors.New("Atlas secret key is invalid") + // ErrInvalidAtlasBrandID = errors.New("Atlas brand ID is invalid") + // ErrInvalidAtlasPartnerID = errors.New("Atlas Partner ID is invalid") ErrMissingResendApiKey = errors.New("missing Resend Api key") ErrMissingResendSenderEmail = errors.New("missing Resend sender name") @@ -46,40 +45,40 @@ var ( ErrMissingTwilioSenderPhoneNumber = errors.New("missing twilio sender phone number") ) -type EnetPulseConfig struct { - UserName string `mapstructure:"username"` - Token string `mapstructure:"token"` - ProviderID string `mapstructure:"provider_id"` -} +// type EnetPulseConfig struct { +// UserName string `mapstructure:"username"` +// Token string `mapstructure:"token"` +// ProviderID string `mapstructure:"provider_id"` +// } -type AleaPlayConfig struct { - Enabled bool `mapstructure:"enabled"` - BaseURL string `mapstructure:"base_url"` // "https://api.aleaplay.com" - OperatorID string `mapstructure:"operator_id"` // Your operator ID with Alea - SecretKey string `mapstructure:"secret_key"` // API secret for signatures - GameListURL string `mapstructure:"game_list_url"` // Endpoint to fetch available games - DefaultCurrency string `mapstructure:"default_currency"` // "USD", "EUR", etc. - SessionTimeout int `mapstructure:"session_timeout"` // In hours -} +// type AleaPlayConfig struct { +// Enabled bool `mapstructure:"enabled"` +// BaseURL string `mapstructure:"base_url"` // "https://api.aleaplay.com" +// OperatorID string `mapstructure:"operator_id"` // Your operator ID with Alea +// SecretKey string `mapstructure:"secret_key"` // API secret for signatures +// GameListURL string `mapstructure:"game_list_url"` // Endpoint to fetch available games +// DefaultCurrency string `mapstructure:"default_currency"` // "USD", "EUR", etc. +// SessionTimeout int `mapstructure:"session_timeout"` // In hours +// } -type VeliConfig struct { - APIKey string `mapstructure:"VELI_API_KEY"` - BaseURL string `mapstructure:"VELI_BASE_URL"` - SecretKey string `mapstructure:"VELI_SECRET_KEY"` - OperatorID string `mapstructure:"VELI_OPERATOR_ID"` - BrandID string `mapstructure:"VELI_BRAND_ID"` - Currency string `mapstructure:"VELI_DEFAULT_CURRENCY"` - WebhookURL string `mapstructure:"VELI_WEBHOOK_URL"` - Enabled bool `mapstructure:"Enabled"` -} +// type VeliConfig struct { +// APIKey string `mapstructure:"VELI_API_KEY"` +// BaseURL string `mapstructure:"VELI_BASE_URL"` +// SecretKey string `mapstructure:"VELI_SECRET_KEY"` +// OperatorID string `mapstructure:"VELI_OPERATOR_ID"` +// BrandID string `mapstructure:"VELI_BRAND_ID"` +// Currency string `mapstructure:"VELI_DEFAULT_CURRENCY"` +// WebhookURL string `mapstructure:"VELI_WEBHOOK_URL"` +// Enabled bool `mapstructure:"Enabled"` +// } -type AtlasConfig struct { - BaseURL string `mapstructure:"ATLAS_BASE_URL"` - SecretKey string `mapstructure:"ATLAS_SECRET_KEY"` - OperatorID string `mapstructure:"ATLAS_OPERATOR_ID"` - CasinoID string `mapstructure:"ATLAS_BRAND_ID"` - PartnerID string `mapstructure:"ATLAS_PARTNER_ID"` -} +// type AtlasConfig struct { +// BaseURL string `mapstructure:"ATLAS_BASE_URL"` +// SecretKey string `mapstructure:"ATLAS_SECRET_KEY"` +// OperatorID string `mapstructure:"ATLAS_OPERATOR_ID"` +// CasinoID string `mapstructure:"ATLAS_BRAND_ID"` +// PartnerID string `mapstructure:"ATLAS_PARTNER_ID"` +// } type ARIFPAYConfig struct { APIKey string `mapstructure:"ARIFPAY_API_KEY"` @@ -121,6 +120,7 @@ type TELEBIRRConfig struct { } type Config struct { + APP_VERSION string FIXER_API_KEY string FIXER_BASE_URL string BASE_CURRENCY domain.IntCurrency @@ -147,12 +147,6 @@ type Config struct { CHAPA_CALLBACK_URL string CHAPA_RETURN_URL string CHAPA_RECEIPT_URL string - Bet365Token string - EnetPulseConfig EnetPulseConfig - PopOK domain.PopOKConfig - AleaPlay AleaPlayConfig `mapstructure:"alea_play"` - Atlas AtlasConfig `mapstructure:"atlas"` - VeliGames VeliConfig `mapstructure:"veli_games"` ARIFPAY ARIFPAYConfig `mapstructure:"arifpay_config"` SANTIMPAY SANTIMPAYConfig `mapstructure:"santimpay_config"` TELEBIRR TELEBIRRConfig `mapstructure:"telebirr_config"` @@ -185,6 +179,8 @@ func (c *Config) loadEnv() error { } c.Env = env + c.APP_VERSION = os.Getenv("APP_VERSION") + c.ReportExportPath = os.Getenv("REPORT_EXPORT_PATH") if c.ReportExportPath == "" { @@ -193,10 +189,6 @@ func (c *Config) loadEnv() error { c.RedisAddr = os.Getenv("REDIS_ADDR") c.KafkaBrokers = strings.Split(os.Getenv("KAFKA_BROKERS"), ",") - c.EnetPulseConfig.Token = os.Getenv("ENETPULSE_TOKEN") - c.EnetPulseConfig.UserName = os.Getenv("ENETPULSE_USERNAME") - c.EnetPulseConfig.ProviderID = os.Getenv("ENETPULSE_PROVIDER_ID") - c.CHAPA_TRANSFER_TYPE = os.Getenv("CHAPA_TRANSFER_TYPE") c.CHAPA_PAYMENT_TYPE = os.Getenv("CHAPA_PAYMENT_TYPE") @@ -298,86 +290,86 @@ func (c *Config) loadEnv() error { c.SANTIMPAY.NotifyURL = os.Getenv("SANTIMPAY_NOTIFY_URL") c.SANTIMPAY.CancelUrl = os.Getenv("SANTIMPAY_CANCEL_URL") - //Alea Play - aleaEnabled := os.Getenv("ALEA_ENABLED") - if aleaEnabled == "" { - aleaEnabled = "false" // Default disabled - } + // //Alea Play + // aleaEnabled := os.Getenv("ALEA_ENABLED") + // if aleaEnabled == "" { + // aleaEnabled = "false" // Default disabled + // } - if enabled, err := strconv.ParseBool(aleaEnabled); err != nil { - return fmt.Errorf("invalid ALEA_ENABLED value: %w", err) - } else { - c.AleaPlay.Enabled = enabled - } + // if enabled, err := strconv.ParseBool(aleaEnabled); err != nil { + // return fmt.Errorf("invalid ALEA_ENABLED value: %w", err) + // } else { + // c.AleaPlay.Enabled = enabled + // } - c.AleaPlay.BaseURL = os.Getenv("ALEA_BASE_URL") - if c.AleaPlay.BaseURL == "" && c.AleaPlay.Enabled { - return errors.New("ALEA_BASE_URL is required when Alea is enabled") - } + // c.AleaPlay.BaseURL = os.Getenv("ALEA_BASE_URL") + // if c.AleaPlay.BaseURL == "" && c.AleaPlay.Enabled { + // return errors.New("ALEA_BASE_URL is required when Alea is enabled") + // } - c.AleaPlay.OperatorID = os.Getenv("ALEA_OPERATOR_ID") - if c.AleaPlay.OperatorID == "" && c.AleaPlay.Enabled { - return errors.New("ALEA_OPERATOR_ID is required when Alea is enabled") - } + // c.AleaPlay.OperatorID = os.Getenv("ALEA_OPERATOR_ID") + // if c.AleaPlay.OperatorID == "" && c.AleaPlay.Enabled { + // return errors.New("ALEA_OPERATOR_ID is required when Alea is enabled") + // } - c.AleaPlay.SecretKey = os.Getenv("ALEA_SECRET_KEY") - if c.AleaPlay.SecretKey == "" && c.AleaPlay.Enabled { - return errors.New("ALEA_SECRET_KEY is required when Alea is enabled") - } + // c.AleaPlay.SecretKey = os.Getenv("ALEA_SECRET_KEY") + // if c.AleaPlay.SecretKey == "" && c.AleaPlay.Enabled { + // return errors.New("ALEA_SECRET_KEY is required when Alea is enabled") + // } - c.AleaPlay.GameListURL = os.Getenv("ALEA_GAME_LIST_URL") - c.AleaPlay.DefaultCurrency = os.Getenv("ALEA_DEFAULT_CURRENCY") - if c.AleaPlay.DefaultCurrency == "" { - c.AleaPlay.DefaultCurrency = "USD" - } + // c.AleaPlay.GameListURL = os.Getenv("ALEA_GAME_LIST_URL") + // c.AleaPlay.DefaultCurrency = os.Getenv("ALEA_DEFAULT_CURRENCY") + // if c.AleaPlay.DefaultCurrency == "" { + // c.AleaPlay.DefaultCurrency = "USD" + // } - sessionTimeoutStr := os.Getenv("ALEA_SESSION_TIMEOUT") - if sessionTimeoutStr != "" { - timeout, err := strconv.Atoi(sessionTimeoutStr) - if err == nil { - c.AleaPlay.SessionTimeout = timeout - } - } + // sessionTimeoutStr := os.Getenv("ALEA_SESSION_TIMEOUT") + // if sessionTimeoutStr != "" { + // timeout, err := strconv.Atoi(sessionTimeoutStr) + // if err == nil { + // c.AleaPlay.SessionTimeout = timeout + // } + // } - //Veli Games - veliEnabled := os.Getenv("VELI_ENABLED") - if veliEnabled == "" { - veliEnabled = "true" // Default to disabled if not specified - } + // //Veli Games + // veliEnabled := os.Getenv("VELI_ENABLED") + // if veliEnabled == "" { + // veliEnabled = "true" // Default to disabled if not specified + // } // veliOperatorID := os.Getenv("VELI_OPERATOR_ID") // veliBrandID := os.Getenv("VELI_BRAND_ID") - c.VeliGames.OperatorID = os.Getenv("VELI_OPERATOR_ID") - c.VeliGames.BrandID = os.Getenv("VELI_BRAND_ID") + // c.VeliGames.OperatorID = os.Getenv("VELI_OPERATOR_ID") + // c.VeliGames.BrandID = os.Getenv("VELI_BRAND_ID") - if enabled, err := strconv.ParseBool(veliEnabled); err != nil { - return fmt.Errorf("invalid VELI_ENABLED value: %w", err) - } else { - c.VeliGames.Enabled = enabled - } - - // apiURL := os.Getenv("VELI_BASE_URL") - // if apiURL == "" { - // apiURL = "https://api.velitech.games" // Default production URL + // if enabled, err := strconv.ParseBool(veliEnabled); err != nil { + // return fmt.Errorf("invalid VELI_ENABLED value: %w", err) + // } else { + // c.VeliGames.Enabled = enabled // } - c.VeliGames.BaseURL = os.Getenv("VELI_BASE_URL") - operatorKey := os.Getenv("VELI_OPERATOR_KEY") - if operatorKey == "" && c.VeliGames.Enabled { - return ErrInvalidVeliOperatorKey - } + // // apiURL := os.Getenv("VELI_BASE_URL") + // // if apiURL == "" { + // // apiURL = "https://api.velitech.games" // Default production URL + // // } + // c.VeliGames.BaseURL = os.Getenv("VELI_BASE_URL") + + // operatorKey := os.Getenv("VELI_OPERATOR_KEY") + // if operatorKey == "" && c.VeliGames.Enabled { + // return ErrInvalidVeliOperatorKey + // } // c.VeliGames.OperatorKey = operatorKey - secretKey := os.Getenv("VELI_SECRET_KEY") - if secretKey == "" && c.VeliGames.Enabled { - return ErrInvalidVeliSecretKey - } - c.VeliGames.SecretKey = secretKey - // c.VeliGames.GameIDs.Aviator = os.Getenv("VELI_GAME_ID_AVIATOR") + // secretKey := os.Getenv("VELI_SECRET_KEY") + // if secretKey == "" && c.VeliGames.Enabled { + // return ErrInvalidVeliSecretKey + // } + // c.VeliGames.SecretKey = secretKey + // // c.VeliGames.GameIDs.Aviator = os.Getenv("VELI_GAME_ID_AVIATOR") - defaultCurrency := os.Getenv("VELI_DEFAULT_CURRENCY") - if defaultCurrency == "" { - defaultCurrency = "USD" // Default currency - } + // defaultCurrency := os.Getenv("VELI_DEFAULT_CURRENCY") + // if defaultCurrency == "" { + // defaultCurrency = "USD" // Default currency + // } // c.VeliGames.DefaultCurrency = defaultCurrency c.LogLevel = lvl @@ -400,42 +392,42 @@ func (c *Config) loadEnv() error { } //Atlas - c.Atlas.BaseURL = os.Getenv("ATLAS_BASE_URL") - c.Atlas.CasinoID = os.Getenv("ATLAS_CASINO_ID") - c.Atlas.OperatorID = os.Getenv("ATLAS_OPERATOR_ID") - c.Atlas.PartnerID = os.Getenv("ATLAS_PARTNER_ID") - c.Atlas.SecretKey = os.Getenv("ATLAS_SECRET_KEY") + // c.Atlas.BaseURL = os.Getenv("ATLAS_BASE_URL") + // c.Atlas.CasinoID = os.Getenv("ATLAS_CASINO_ID") + // c.Atlas.OperatorID = os.Getenv("ATLAS_OPERATOR_ID") + // c.Atlas.PartnerID = os.Getenv("ATLAS_PARTNER_ID") + // c.Atlas.SecretKey = os.Getenv("ATLAS_SECRET_KEY") - popOKClientID := os.Getenv("POPOK_CLIENT_ID") + // popOKClientID := os.Getenv("POPOK_CLIENT_ID") - popOKPlatform := os.Getenv("POPOK_PLATFORM") + // // popOKPlatform := os.Getenv("POPOK_PLATFORM") - if popOKClientID == "" { - return ErrInvalidPopOKClientID - } + // if popOKClientID == "" { + // return ErrInvalidPopOKClientID + // } - popOKSecretKey := os.Getenv("POPOK_SECRET_KEY") - if popOKSecretKey == "" { - return ErrInvalidPopOKSecretKey - } + // popOKSecretKey := os.Getenv("POPOK_SECRET_KEY") + // if popOKSecretKey == "" { + // return ErrInvalidPopOKSecretKey + // } - popOKBaseURL := os.Getenv("POPOK_BASE_URL") - if popOKBaseURL == "" { - return ErrInvalidPopOKBaseURL - } + // popOKBaseURL := os.Getenv("POPOK_BASE_URL") + // if popOKBaseURL == "" { + // return ErrInvalidPopOKBaseURL + // } - popOKCallbackURL := os.Getenv("POPOK_CALLBACK_URL") - if popOKCallbackURL == "" { - return ErrInvalidPopOKCallbackURL - } + // popOKCallbackURL := os.Getenv("POPOK_CALLBACK_URL") + // if popOKCallbackURL == "" { + // return ErrInvalidPopOKCallbackURL + // } - c.PopOK = domain.PopOKConfig{ - ClientID: popOKClientID, - SecretKey: popOKSecretKey, - BaseURL: popOKBaseURL, - CallbackURL: popOKCallbackURL, - Platform: popOKPlatform, - } + // c.PopOK = domain.PopOKConfig{ + // ClientID: popOKClientID, + // SecretKey: popOKSecretKey, + // BaseURL: popOKBaseURL, + // CallbackURL: popOKCallbackURL, + // Platform: popOKPlatform, + // } // AtlasBaseUrl := os.Getenv("ATLAS_BASE_URL") // if AtlasBaseUrl == "" { @@ -466,11 +458,11 @@ func (c *Config) loadEnv() error { // OperatorID: AtlasOperatorID, // } - betToken := os.Getenv("BET365_TOKEN") - if betToken == "" { - return ErrMissingBetToken - } - c.Bet365Token = betToken + // betToken := os.Getenv("BET365_TOKEN") + // if betToken == "" { + // return ErrMissingBetToken + // } + // c.Bet365Token = betToken resendApiKey := os.Getenv("RESEND_API_KEY") if resendApiKey == "" { diff --git a/internal/domain/atlas.go b/internal/domain/atlas.go deleted file mode 100644 index bf3fa47..0000000 --- a/internal/domain/atlas.go +++ /dev/null @@ -1,151 +0,0 @@ -package domain - -import "errors" - -type AtlasGameInitRequest struct { - Game string `json:"game"` - PlayerID string `json:"player_id"` - Language string `json:"language"` - Currency string `json:"currency"` -} - -type AtlasGameInitResponse struct { - URL string `json:"url"` -} - -type AtlasGetUserDataRequest struct { - Game string `json:"game"` - CasinoID string `json:"casino_id"` - PlayerID string `json:"player_id"` - SessionID string `json:"session_id"` - // timestamp and hash will also be included in the incoming JSON -} - -type AtlasGetUserDataResponse struct { - PlayerID string `json:"player_id"` - Balance float64 `json:"balance"` -} - -type AtlasBetRequest struct { - Game string `json:"game"` - CasinoID string `json:"casino_id"` - PlayerID string `json:"player_id"` - SessionID string `json:"session_id"` - Amount float64 `json:"amount"` - Currency string `json:"currency"` - RoundID int64 `json:"round_id"` - TransactionID string `json:"transaction_id"` - Details string `json:"details"` -} - -type AtlasBetResponse struct { - PlayerID string `json:"player_id"` - Balance float64 `json:"balance"` -} - -type AtlasBetWinRequest struct { - Game string `json:"game"` - CasinoID string `json:"casino_id"` - RoundID string `json:"round_id"` - PlayerID string `json:"player_id"` - SessionID string `json:"session_id"` - BetAmount float64 `json:"betAmount"` - WinAmount float64 `json:"winAmount"` - Currency string `json:"currency"` - TransactionID string `json:"transaction_id"` - Timestamp string `json:"timestamp"` - Hash string `json:"hash"` -} - -type AtlasBetWinResponse struct { - PlayerID string `json:"player_id"` - Balance float64 `json:"balance"` -} - -type RoundResultRequest struct { - Game string `json:"game"` - RoundID int64 `json:"round_id"` - CasinoID string `json:"casino_id"` - PlayerID string `json:"player_id"` - Amount float64 `json:"amount"` // win amount - Currency string `json:"currency"` - TransactionID string `json:"transaction_id"` // new transaction id - BetTransactionID string `json:"bet_transaction_id"` // from BET request - SessionID string `json:"session_id"` - Timestamp string `json:"timestamp"` - Hash string `json:"hash"` -} - -type RoundResultResponse struct { - Success bool `json:"success"` -} - -type RollbackRequest struct { - Game string `json:"game"` - RoundID int64 `json:"round_id"` - CasinoID string `json:"casino_id"` - PlayerID string `json:"player_id"` - BetTransactionID string `json:"bet_transaction_id"` - SessionID string `json:"session_id"` - Timestamp string `json:"timestamp"` - Hash string `json:"hash"` -} - -type RollbackResponse struct { - Success bool `json:"success"` -} - -type FreeSpinRequest struct { - CasinoID string `json:"casino_id"` - PlayerID string `json:"player_id"` - EndDate string `json:"end_date"` // "yyyy-mm-ddTHH:MM:SS+00:00" - FreeSpinsCount int `json:"freespins_count"` // count of free spins/bets - Timestamp string `json:"timestamp"` - Hash string `json:"hash"` -} - -type FreeSpinResponse struct { - Success bool `json:"success"` -} - -type FreeSpinResultRequest struct { - Game string `json:"game"` - RoundID int64 `json:"round_id"` - CasinoID string `json:"casino_id"` - PlayerID string `json:"player_id"` - Amount float64 `json:"amount"` // win amount - Currency string `json:"currency"` - TransactionID string `json:"transaction_id"` - SessionID string `json:"session_id"` - Timestamp string `json:"timestamp"` - Hash string `json:"hash"` -} - -type FreeSpinResultResponse struct { - Success bool `json:"success"` -} - -type JackpotRequest struct { - Game string `json:"game"` - CasinoID string `json:"casino_id"` - PlayerID string `json:"player_id"` - SessionID string `json:"session_id"` - Amount float64 `json:"amount"` // jackpot win - Currency string `json:"currency"` - RoundID int64 `json:"round_id"` - TransactionID string `json:"transaction_id"` - Details string `json:"details,omitempty"` - Timestamp string `json:"timestamp"` - Hash string `json:"hash"` -} - -type JackpotResponse struct { - Success bool `json:"success"` -} - -var ( - ErrPlayerNotFound = errors.New("PLAYER_NOT_FOUND") - ErrSessionExpired = errors.New("SESSION_EXPIRED") - ErrWalletsNotFound = errors.New("WALLETS_NOT_FOUND") - ErrDuplicateTransaction = errors.New("DUPLICATE_TRANSACTION") -) diff --git a/internal/domain/bet.go b/internal/domain/bet.go deleted file mode 100644 index fcad545..0000000 --- a/internal/domain/bet.go +++ /dev/null @@ -1,357 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/jackc/pgx/v5/pgtype" -) - -// The Odd ID here is not the odd id from our database -// but the raw_odd_id from the betapi.from within the raw_odds json -// This can be refactor later -type BetOutcome struct { - ID int64 `json:"id" example:"1"` - BetID int64 `json:"bet_id" example:"1"` - EventID int64 `json:"event_id" example:"1"` - OddID int64 `json:"odd_id" example:"1"` - SportID int64 `json:"sport_id" example:"1"` - HomeTeamName string `json:"home_team_name" example:"Manchester"` - AwayTeamName string `json:"away_team_name" example:"Liverpool"` - MarketID int64 `json:"market_id" example:"1"` - MarketName string `json:"market_name" example:"Fulltime Result"` - Odd float32 `json:"odd" example:"1.5"` - OddName string `json:"odd_name" example:"1"` - OddHeader string `json:"odd_header" example:"1"` - OddHandicap string `json:"odd_handicap" example:"1"` - Status OutcomeStatus `json:"status" example:"1"` - Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"` -} - -type CreateBetOutcome struct { - BetID int64 `json:"bet_id" example:"1"` - EventID int64 `json:"event_id" example:"1"` - OddID int64 `json:"odd_id" example:"1"` - SportID int64 `json:"sport_id" example:"1"` - HomeTeamName string `json:"home_team_name" example:"Manchester"` - AwayTeamName string `json:"away_team_name" example:"Liverpool"` - MarketID int64 `json:"market_id" example:"1"` - MarketName string `json:"market_name" example:"Fulltime Result"` - Odd float32 `json:"odd" example:"1.5"` - OddName string `json:"odd_name" example:"1"` - OddHeader string `json:"odd_header" example:"1"` - OddHandicap string `json:"odd_handicap" example:"1"` - Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"` -} - -// If it is a ShopBet then UserID and Fullname will be the cashier -// If it is a DigitalBet then UserID and Fullname will be the user -type Bet struct { - ID int64 - Amount Currency - TotalOdds float32 - Status OutcomeStatus - UserID int64 - CompanyID int64 - IsShopBet bool - CashedOut bool - FastCode string - CreatedAt time.Time -} - -type BetFilter struct { - Status ValidOutcomeStatus - UserID ValidInt64 - CompanyID ValidInt64 - CashedOut ValidBool - IsShopBet ValidBool - Query ValidString - CreatedBefore ValidTime - CreatedAfter ValidTime - Limit ValidInt32 - Offset ValidInt32 -} - -type Flag struct { - ID int64 - BetID int64 - OddID int64 - Reason string - FlaggedAt time.Time - Resolved bool -} - -type GetBet struct { - ID int64 - Amount Currency - TotalOdds float32 - Status OutcomeStatus - FullName string - PhoneNumber string - UserID int64 - CompanyID int64 - CompanySlug string - IsShopBet bool - CashedOut bool - Outcomes []BetOutcome - FastCode string - CreatedAt time.Time -} - -type CreateBet struct { - Amount Currency - TotalOdds float32 - Status OutcomeStatus - UserID int64 - CompanyID int64 - IsShopBet bool - OutcomesHash string - FastCode string -} - -type CreateBetOutcomeReq struct { - EventID int64 `json:"event_id" example:"1"` - OddID int64 `json:"odd_id" example:"1"` - MarketID int64 `json:"market_id" example:"1"` -} - -type CreateBetReq struct { - Outcomes []CreateBetOutcomeReq `json:"outcomes" validate:"required"` - Amount float32 `json:"amount" validate:"required,gt=0" example:"100.0"` - BranchID *int64 `json:"branch_id,omitempty" example:"1"` -} - -type CreateBetWithFastCodeReq struct { - FastCode string `json:"fast_code"` - Amount float32 `json:"amount"` - BranchID *int64 `json:"branch_id"` -} - -type CreateFlagReq struct { - BetID int64 - OddID int64 - Reason string -} - -type RandomBetReq struct { - BranchID int64 `json:"branch_id" validate:"required" example:"1"` - NumberOfBets int64 `json:"number_of_bets" validate:"required" example:"1"` -} - -type CreateBetRes struct { - ID int64 `json:"id" example:"1"` - Amount float32 `json:"amount" example:"100.0"` - TotalOdds float32 `json:"total_odds" example:"4.22"` - Status OutcomeStatus `json:"status" example:"1"` - UserID int64 `json:"user_id" example:"2"` - CompanyID int64 `json:"company_id" example:"1"` - IsShopBet bool `json:"is_shop_bet" example:"false"` - CreatedNumber int64 `json:"created_number" example:"2"` - FastCode string `json:"fast_code"` -} -type BetRes struct { - ID int64 `json:"id" example:"1"` - Outcomes []BetOutcome `json:"outcomes"` - Amount float32 `json:"amount" example:"100.0"` - TotalOdds float32 `json:"total_odds" example:"4.22"` - Status OutcomeStatus `json:"status" example:"1"` - Fullname string `json:"full_name" example:"John Smith"` - UserID int64 `json:"user_id" example:"2"` - CompanyID int64 `json:"company_id" example:"1"` - CompanySlug string `json:"company_slug" example:"fortune"` - IsShopBet bool `json:"is_shop_bet" example:"false"` - CashedOut bool `json:"cashed_out" example:"false"` - CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"` - FastCode string `json:"fast_code"` -} - -type BetOutcomeViewRes struct { - ID int64 `json:"id"` - BetID int64 `json:"bet_id"` - CompanyName string `json:"company_name"` - SportID int64 `json:"sport_id"` - EventID int64 `json:"event_id"` - OddID int64 `json:"odd_id"` - HomeTeamName string `json:"home_team_name"` - AwayTeamName string `json:"away_team_name"` - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - Odd float32 `json:"odd"` - OddName string `json:"odd_name"` - OddHeader string `json:"odd_header"` - OddHandicap string `json:"odd_handicap"` - Status OutcomeStatus `json:"status"` - Expires time.Time `json:"expires"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Amount int64 `json:"amount"` - TotalOdds float32 `json:"total_odds"` -} - -type BetOutcomeViewFilter struct { - OutcomeStatus ValidOutcomeStatus - CompanyID ValidInt64 - Limit ValidInt32 - Offset ValidInt32 -} - -func ConvertCreateBetRes(bet Bet, createdNumber int64) CreateBetRes { - return CreateBetRes{ - ID: bet.ID, - Amount: bet.Amount.Float32(), - TotalOdds: bet.TotalOdds, - Status: bet.Status, - UserID: bet.UserID, - CompanyID: bet.CompanyID, - CreatedNumber: createdNumber, - IsShopBet: bet.IsShopBet, - FastCode: bet.FastCode, - } -} - -func ConvertBet(bet GetBet) BetRes { - return BetRes{ - ID: bet.ID, - Amount: bet.Amount.Float32(), - TotalOdds: bet.TotalOdds, - Status: bet.Status, - Fullname: bet.FullName, - UserID: bet.UserID, - CompanyID: bet.CompanyID, - CompanySlug: bet.CompanySlug, - Outcomes: bet.Outcomes, - IsShopBet: bet.IsShopBet, - CashedOut: bet.CashedOut, - CreatedAt: bet.CreatedAt, - FastCode: bet.FastCode, - } -} - -func ConvertDBBet(bet dbgen.Bet) Bet { - return Bet{ - ID: bet.ID, - Amount: Currency(bet.Amount), - TotalOdds: bet.TotalOdds, - Status: OutcomeStatus(bet.Status), - UserID: bet.UserID, - CompanyID: bet.CompanyID, - IsShopBet: bet.IsShopBet, - CashedOut: bet.CashedOut, - FastCode: bet.FastCode, - CreatedAt: bet.CreatedAt.Time, - } -} - -func ConvertDBBetOutcomes(outcome dbgen.BetOutcome) BetOutcome { - return BetOutcome{ - ID: outcome.ID, - BetID: outcome.BetID, - SportID: outcome.SportID, - 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, - } -} -func ConvertDBBetOutcomesView(outcome dbgen.GetBetOutcomeViewByEventIDRow) BetOutcomeViewRes { - return BetOutcomeViewRes{ - ID: outcome.ID, - BetID: outcome.BetID, - CompanyName: outcome.CompanyName, - SportID: outcome.SportID, - 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, - FirstName: outcome.FirstName, - LastName: outcome.LastName, - Amount: outcome.Amount, - TotalOdds: outcome.TotalOdds, - } -} - -func ConvertDBBetWithOutcomes(bet dbgen.BetWithOutcome) GetBet { - var outcomes []BetOutcome = make([]BetOutcome, 0, len(bet.Outcomes)) - - for _, outcome := range bet.Outcomes { - outcomes = append(outcomes, ConvertDBBetOutcomes(outcome)) - } - - return GetBet{ - ID: bet.ID, - CompanyID: bet.CompanyID, - CompanySlug: bet.CompanySlug, - Amount: Currency(bet.Amount), - TotalOdds: bet.TotalOdds, - Status: OutcomeStatus(bet.Status), - FullName: bet.FullName.(string), - PhoneNumber: bet.PhoneNumber.String, - UserID: bet.UserID, - IsShopBet: bet.IsShopBet, - CashedOut: bet.CashedOut, - Outcomes: outcomes, - FastCode: bet.FastCode, - CreatedAt: bet.CreatedAt.Time, - } -} - -func ConvertDBFlag(flag dbgen.Flag) Flag { - return Flag{ - ID: flag.ID, - BetID: flag.BetID.Int64, - OddID: flag.OddsMarketID.Int64, - Reason: flag.Reason.String, - FlaggedAt: flag.FlaggedAt.Time, - Resolved: flag.Resolved.Bool, - } -} - -func ConvertDBCreateBetOutcome(betOutcome CreateBetOutcome) dbgen.CreateBetOutcomeParams { - return dbgen.CreateBetOutcomeParams{ - BetID: betOutcome.BetID, - EventID: betOutcome.EventID, - SportID: betOutcome.SportID, - OddID: betOutcome.OddID, - HomeTeamName: betOutcome.HomeTeamName, - AwayTeamName: betOutcome.AwayTeamName, - MarketID: betOutcome.MarketID, - MarketName: betOutcome.MarketName, - Odd: betOutcome.Odd, - OddName: betOutcome.OddName, - OddHeader: betOutcome.OddHeader, - OddHandicap: betOutcome.OddHandicap, - Expires: pgtype.Timestamp{ - Time: betOutcome.Expires, - Valid: true, - }, - } -} - -func ConvertCreateBet(bet CreateBet) dbgen.CreateBetParams { - return dbgen.CreateBetParams{ - Amount: int64(bet.Amount), - TotalOdds: bet.TotalOdds, - Status: int32(bet.Status), - UserID: bet.UserID, - CompanyID: bet.CompanyID, - IsShopBet: bet.IsShopBet, - OutcomesHash: bet.OutcomesHash, - FastCode: bet.FastCode, - } -} diff --git a/internal/domain/bet_stats.go b/internal/domain/bet_stats.go deleted file mode 100644 index 5ddce5c..0000000 --- a/internal/domain/bet_stats.go +++ /dev/null @@ -1,49 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type BetStatsByInterval struct { - Date time.Time `json:"date"` - TotalBets int64 `json:"total_bets"` - TotalStake int64 `json:"total_stake"` - ActiveBets int64 `json:"active_bets"` - TotalWins int64 `json:"total_wins"` - TotalLosses int64 `json:"total_losses"` - WinBalance int64 `json:"win_balance"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount int64 `json:"total_unsettled_amount"` - TotalShopBets int64 `json:"total_shop_bets"` -} - -type BetStatsByIntervalFilter struct { - Interval ValidDateInterval - CompanyID ValidInt64 - -} - -func ConvertDBBetStatsByInterval(stats dbgen.GetBetStatsByIntervalRow) BetStatsByInterval { - return BetStatsByInterval{ - Date: stats.Date.Time, - TotalBets: stats.TotalBets, - TotalStake: stats.TotalStake, - ActiveBets: stats.ActiveBets, - TotalWins: stats.TotalWins, - TotalLosses: stats.TotalLosses, - WinBalance: stats.WinBalance, - NumberOfUnsettled: stats.NumberOfUnsettled, - TotalUnsettledAmount: stats.TotalUnsettledAmount, - TotalShopBets: stats.TotalShopBets, - } -} - -func ConvertDBBetStatsByIntervalList(stats []dbgen.GetBetStatsByIntervalRow) []BetStatsByInterval { - result := make([]BetStatsByInterval, len(stats)) - for i, e := range stats { - result[i] = ConvertDBBetStatsByInterval(e) - } - return result -} diff --git a/internal/domain/bonus.go b/internal/domain/bonus.go deleted file mode 100644 index f436381..0000000 --- a/internal/domain/bonus.go +++ /dev/null @@ -1,172 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/jackc/pgx/v5/pgtype" -) - -type BonusType string - -var ( - WelcomeBonus BonusType = "welcome_bonus" - DepositBonus BonusType = "deposit_bonus" -) - -type UserBonus struct { - ID int64 - Name string - Description string - UserID int64 - Type BonusType - RewardAmount Currency - IsClaimed bool - ExpiresAt time.Time - CreatedAt time.Time - UpdatedAt time.Time -} - -type UserBonusRes struct { - ID int64 `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - UserID int64 `json:"user_id"` - Type BonusType `json:"type"` - RewardAmount float32 `json:"reward_amount"` - IsClaimed bool `json:"is_claimed"` - ExpiresAt time.Time `json:"expires_at"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -func ConvertToBonusRes(bonus UserBonus) UserBonusRes { - return UserBonusRes{ - ID: bonus.ID, - Name: bonus.Name, - Description: bonus.Description, - Type: bonus.Type, - UserID: bonus.UserID, - RewardAmount: bonus.RewardAmount.Float32(), - IsClaimed: bonus.IsClaimed, - ExpiresAt: bonus.ExpiresAt, - CreatedAt: bonus.CreatedAt, - UpdatedAt: bonus.UpdatedAt, - } -} - -func ConvertToBonusResList(bonuses []UserBonus) []UserBonusRes { - result := make([]UserBonusRes, len(bonuses)) - - for i, bonus := range bonuses { - result[i] = ConvertToBonusRes(bonus) - } - - return result -} - -type CreateBonus struct { - Name string - Description string - Type BonusType - UserID int64 - RewardAmount Currency - ExpiresAt time.Time -} - -// type CreateBonusReq struct { -// Name string `json:"name"` -// Description string `json:"description"` -// Type BonusType `json:"type"` -// UserID int64 `json:"user_id"` -// RewardAmount float32 `json:"reward_amount"` -// ExpiresAt time.Time `json:"expires_at"` -// } - -// func ConvertCreateBonusReq(bonus CreateBonusReq, companyID int64) CreateBonus { -// return CreateBonus{ -// Name: bonus.Name, -// Description: bonus.Description, -// Type: bonus.Type, -// UserID: bonus.UserID, -// RewardAmount: ToCurrency(bonus.RewardAmount), -// ExpiresAt: bonus.ExpiresAt, -// } -// } - -func ConvertCreateBonus(bonus CreateBonus) dbgen.CreateUserBonusParams { - return dbgen.CreateUserBonusParams{ - Name: bonus.Name, - Description: bonus.Description, - Type: string(bonus.Type), - UserID: bonus.UserID, - RewardAmount: int64(bonus.RewardAmount), - ExpiresAt: pgtype.Timestamp{ - Time: bonus.ExpiresAt, - Valid: true, - }, - } -} - -func ConvertDBBonus(bonus dbgen.UserBonuse) UserBonus { - return UserBonus{ - ID: bonus.ID, - Name: bonus.Name, - Description: bonus.Description, - Type: BonusType(bonus.Type), - UserID: bonus.UserID, - - RewardAmount: Currency(bonus.RewardAmount), - IsClaimed: bonus.IsClaimed, - ExpiresAt: bonus.ExpiresAt.Time, - CreatedAt: bonus.CreatedAt.Time, - UpdatedAt: bonus.UpdatedAt.Time, - } -} - -func ConvertDBBonuses(bonuses []dbgen.UserBonuse) []UserBonus { - result := make([]UserBonus, len(bonuses)) - for i, bonus := range bonuses { - result[i] = ConvertDBBonus(bonus) - } - return result -} - -type BonusFilter struct { - UserID ValidInt64 - CompanyID ValidInt64 - Limit ValidInt - Offset ValidInt -} - -type BonusStats struct { - TotalBonus int64 - TotalRewardAmount Currency - ClaimedBonuses int64 - ExpiredBonuses int64 -} - -type BonusStatsRes struct { - TotalBonus int64 `json:"total_bonus"` - TotalRewardAmount float32 `json:"total_reward_amount"` - ClaimedBonuses int64 `json:"claimed_bonuses"` - ExpiredBonuses int64 `json:"expired_bonuses"` -} - -func ConvertToBonusStatsRes(bonus BonusStats) BonusStatsRes { - return BonusStatsRes{ - TotalBonus: bonus.TotalBonus, - TotalRewardAmount: bonus.TotalRewardAmount.Float32(), - ClaimedBonuses: bonus.ClaimedBonuses, - ExpiredBonuses: bonus.ExpiredBonuses, - } -} - -func ConvertDBBonusStats(stats dbgen.GetBonusStatsRow) BonusStats { - return BonusStats{ - TotalBonus: stats.TotalBonuses, - TotalRewardAmount: Currency(stats.TotalRewardEarned), - ClaimedBonuses: stats.ClaimedBonuses, - ExpiredBonuses: stats.ExpiredBonuses, - } -} diff --git a/internal/domain/branch.go b/internal/domain/branch.go deleted file mode 100644 index 0b4b7bb..0000000 --- a/internal/domain/branch.go +++ /dev/null @@ -1,330 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/jackc/pgx/v5/pgtype" -) - -type Branch struct { - ID int64 - Name string - Location string - WalletID int64 - BranchManagerID int64 - CompanyID int64 - IsActive bool - IsSelfOwned bool - ProfitPercentage float32 -} - -type BranchLocation struct { - Key string `json:"key" example:"addis_ababa" ` - Name string `json:"name" example:"Addis Ababa"` -} - -type BranchFilter struct { - CompanyID ValidInt64 - IsActive ValidBool - BranchManagerID ValidInt64 - Query ValidString - CreatedBefore ValidTime - CreatedAfter ValidTime -} - -type BranchDetail struct { - ID int64 - Name string - Location string - WalletID int64 - Balance Currency - BranchManagerID int64 - CompanyID int64 - IsActive bool - IsSelfOwned bool - ManagerName string - ManagerPhoneNumber string - WalletIsActive bool - ProfitPercentage float32 - CompanyName string - TotalBets int64 - TotalStake Currency - DeductedStake Currency - TotalCashOut Currency - TotalCashBacks Currency - NumberOfUnsettled int64 - TotalUnsettledAmount Currency - TotalCashiers int64 - StatsUpdatedAt time.Time -} - -type SupportedOperation struct { - ID int64 - Name string - Description string -} - -type BranchOperation struct { - ID int64 - OperationName string - OperationDescription string -} - -type CreateBranch struct { - Name string - Location string - WalletID int64 - BranchManagerID int64 - CompanyID int64 - IsSelfOwned bool - ProfitPercentage float32 -} - -type UpdateBranch struct { - ID int64 - Name *string - Location *string - BranchManagerID *int64 - CompanyID *int64 - IsSelfOwned *bool - IsActive *bool - ProfitPercentage *float32 -} - -type CreateSupportedOperation struct { - Name string - Description string -} -type CreateBranchOperation struct { - BranchID int64 - OperationID int64 -} - -type CreateBranchReq struct { - Name string `json:"name" validate:"required,min=3,max=100" example:"4-kilo Branch"` - Location string `json:"location" validate:"required,min=3,max=100" example:"Addis Ababa"` - BranchManagerID int64 `json:"branch_manager_id" validate:"required,gt=0" example:"1"` - ProfitPercentage float32 `json:"profit_percentage" example:"0.1" validate:"lt=1" ` - CompanyID *int64 `json:"company_id,omitempty" example:"1"` - IsSelfOwned *bool `json:"is_self_owned,omitempty" example:"false"` - Operations []int64 `json:"operations" validate:"required,dive,gt=0"` -} - -type UpdateBranchReq struct { - Name *string `json:"name,omitempty" example:"4-kilo Branch"` - Location *string `json:"location,omitempty" example:"Addis Ababa"` - BranchManagerID *int64 `json:"branch_manager_id,omitempty" example:"1"` - CompanyID *int64 `json:"company_id,omitempty" example:"1"` - IsSelfOwned *bool `json:"is_self_owned,omitempty" example:"false"` - IsActive *bool `json:"is_active,omitempty" example:"false"` - ProfitPercentage *float32 `json:"profit_percentage,omitempty" example:"0.1" validate:"lt=1" ` -} - -type CreateSupportedOperationReq struct { - Name string `json:"name" example:"SportsBook"` - Description string `json:"description" example:"Betting on sport events"` -} - -type SupportedOperationRes struct { - ID int64 `json:"id" example:"1"` - Name string `json:"name" example:"SportsBook"` - Description string `json:"description" example:"Betting on sport events"` -} - -type CreateBranchOperationReq struct { - BranchID int64 `json:"branch_id" example:"1"` - OperationID int64 `json:"operation_id" example:"1"` -} - -type BranchOperationRes struct { - Name string `json:"name" example:"SportsBook"` - Description string `json:"description" example:"Betting on sport events"` -} - -type BranchRes struct { - ID int64 `json:"id" example:"1"` - Name string `json:"name" example:"4-kilo Branch"` - Location string `json:"location" example:"Addis Ababa"` - WalletID int64 `json:"wallet_id" example:"1"` - BranchManagerID int64 `json:"branch_manager_id" example:"1"` - CompanyID int64 `json:"company_id" example:"1"` - IsSelfOwned bool `json:"is_self_owned" example:"false"` - IsActive bool `json:"is_active" example:"false"` - ProfitPercentage float32 `json:"profit_percentage" example:"0.1"` -} - -type BranchDetailRes struct { - ID int64 `json:"id" example:"1"` - Name string `json:"name" example:"4-kilo Branch"` - Location string `json:"location" example:"Addis Ababa"` - WalletID int64 `json:"wallet_id" example:"1"` - BranchManagerID int64 `json:"branch_manager_id" example:"1"` - CompanyID int64 `json:"company_id" example:"1"` - IsSelfOwned bool `json:"is_self_owned" example:"false"` - ManagerName string `json:"manager_name" example:"John Smith"` - ManagerPhoneNumber string `json:"manager_phone_number" example:"0911111111"` - Balance float32 `json:"balance" example:"100.5"` - IsActive bool `json:"is_active" example:"false"` - WalletIsActive bool `json:"is_wallet_active" example:"false"` - ProfitPercentage float32 `json:"profit_percentage" example:"0.1"` - CompanyName string `json:"company_name" example:"fortune"` - TotalBets int64 `json:"total_bets"` - TotalStake float32 `json:"total_stake"` - DeductedStake float32 `json:"deducted_stake"` - TotalCashOut float32 `json:"total_cash_out"` - TotalCashBacks float32 `json:"total_cash_backs"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount float32 `json:"total_unsettled_amount"` - TotalCashiers int64 `json:"total_cashiers"` - StatsUpdatedAt time.Time `json:"stats_updated_at"` -} - -func ConvertBranch(branch Branch) BranchRes { - return BranchRes{ - ID: branch.ID, - Name: branch.Name, - Location: branch.Location, - WalletID: branch.WalletID, - BranchManagerID: branch.BranchManagerID, - CompanyID: branch.CompanyID, - IsSelfOwned: branch.IsSelfOwned, - IsActive: branch.IsActive, - ProfitPercentage: branch.ProfitPercentage, - } -} - -func ConvertBranchDetail(branch BranchDetail) BranchDetailRes { - return BranchDetailRes{ - ID: branch.ID, - Name: branch.Name, - Location: branch.Location, - WalletID: branch.WalletID, - BranchManagerID: branch.BranchManagerID, - CompanyID: branch.CompanyID, - IsSelfOwned: branch.IsSelfOwned, - ManagerName: branch.ManagerName, - ManagerPhoneNumber: branch.ManagerPhoneNumber, - Balance: branch.Balance.Float32(), - IsActive: branch.IsActive, - WalletIsActive: branch.WalletIsActive, - ProfitPercentage: branch.ProfitPercentage, - CompanyName: branch.CompanyName, - TotalBets: branch.TotalBets, - TotalStake: branch.TotalStake.Float32(), - DeductedStake: branch.DeductedStake.Float32(), - TotalCashOut: branch.TotalCashOut.Float32(), - TotalCashBacks: branch.TotalCashBacks.Float32(), - NumberOfUnsettled: branch.NumberOfUnsettled, - TotalUnsettledAmount: branch.TotalUnsettledAmount.Float32(), - TotalCashiers: branch.TotalCashiers, - StatsUpdatedAt: branch.StatsUpdatedAt, - } -} - -func ConvertCreateBranch(branch CreateBranch) dbgen.CreateBranchParams { - return dbgen.CreateBranchParams{ - Name: branch.Name, - Location: branch.Location, - WalletID: branch.WalletID, - BranchManagerID: branch.BranchManagerID, - CompanyID: branch.CompanyID, - IsSelfOwned: branch.IsSelfOwned, - ProfitPercent: branch.ProfitPercentage, - } -} - -func ConvertDBBranchDetail(dbBranch dbgen.BranchDetail) BranchDetail { - return BranchDetail{ - ID: dbBranch.ID, - Name: dbBranch.Name, - Location: dbBranch.Location, - WalletID: dbBranch.WalletID, - BranchManagerID: dbBranch.BranchManagerID, - CompanyID: dbBranch.CompanyID, - IsSelfOwned: dbBranch.IsSelfOwned, - ManagerName: dbBranch.ManagerName.(string), - ManagerPhoneNumber: dbBranch.ManagerPhoneNumber.String, - Balance: Currency(dbBranch.Balance.Int64), - IsActive: dbBranch.IsActive, - WalletIsActive: dbBranch.WalletIsActive.Bool, - ProfitPercentage: dbBranch.ProfitPercent, - CompanyName: dbBranch.CompanyName, - TotalBets: dbBranch.TotalBets, - TotalStake: Currency(dbBranch.TotalStake), - DeductedStake: Currency(dbBranch.DeductedStake), - TotalCashOut: Currency(dbBranch.TotalCashOut), - TotalCashBacks: Currency(dbBranch.TotalCashBacks), - NumberOfUnsettled: dbBranch.NumberOfUnsettled, - TotalUnsettledAmount: Currency(dbBranch.TotalUnsettledAmount), - TotalCashiers: dbBranch.TotalCashiers, - StatsUpdatedAt: dbBranch.StatsUpdatedAt.Time, - } -} - -func ConvertDBBranch(dbBranch dbgen.Branch) Branch { - return Branch{ - ID: dbBranch.ID, - Name: dbBranch.Name, - Location: dbBranch.Location, - WalletID: dbBranch.WalletID, - BranchManagerID: dbBranch.BranchManagerID, - CompanyID: dbBranch.CompanyID, - IsSelfOwned: dbBranch.IsSelfOwned, - IsActive: dbBranch.IsActive, - ProfitPercentage: dbBranch.ProfitPercent, - } -} - -func ConvertUpdateBranch(updateBranch UpdateBranch) dbgen.UpdateBranchParams { - - var newUpdateBranch dbgen.UpdateBranchParams - - newUpdateBranch.ID = updateBranch.ID - - if updateBranch.Name != nil { - newUpdateBranch.Name = pgtype.Text{ - String: *updateBranch.Name, - Valid: true, - } - } - if updateBranch.Location != nil { - newUpdateBranch.Location = pgtype.Text{ - String: *updateBranch.Location, - Valid: true, - } - } - if updateBranch.BranchManagerID != nil { - newUpdateBranch.BranchManagerID = pgtype.Int8{ - Int64: *updateBranch.BranchManagerID, - Valid: true, - } - } - if updateBranch.CompanyID != nil { - newUpdateBranch.CompanyID = pgtype.Int8{ - Int64: *updateBranch.CompanyID, - Valid: true, - } - } - if updateBranch.IsSelfOwned != nil { - newUpdateBranch.IsSelfOwned = pgtype.Bool{ - Bool: *updateBranch.IsSelfOwned, - Valid: true, - } - } - if updateBranch.IsActive != nil { - newUpdateBranch.IsActive = pgtype.Bool{ - Bool: *updateBranch.IsActive, - Valid: true, - } - } - - if updateBranch.ProfitPercentage != nil { - newUpdateBranch.ProfitPercent = pgtype.Float4{ - Float32: *updateBranch.ProfitPercentage, - Valid: true, - } - } - - return newUpdateBranch -} diff --git a/internal/domain/branch_stats.go b/internal/domain/branch_stats.go deleted file mode 100644 index 52f3302..0000000 --- a/internal/domain/branch_stats.go +++ /dev/null @@ -1,88 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -// Branch-level aggregated report -type BranchStat struct { - IntervalStart time.Time - BranchID int64 - BranchName string - CompanyID int64 - CompanyName string - CompanySlug string - TotalBets int64 - TotalStake Currency - DeductedStake Currency - TotalCashOut Currency - TotalCashBacks Currency - NumberOfUnsettled int64 - TotalUnsettledAmount Currency - TotalCashiers int64 - UpdatedAt time.Time -} - -type BranchStatFilter struct { - Interval ValidDateInterval - BranchID ValidInt64 - CompanyID ValidInt64 -} - -func ConvertDBBranchStats(branch dbgen.BranchStat) BranchStat { - return BranchStat{ - IntervalStart: branch.IntervalStart.Time, - BranchID: branch.BranchID, - BranchName: branch.BranchName, - CompanyID: branch.CompanyID, - CompanyName: branch.CompanyName, - CompanySlug: branch.CompanySlug, - TotalBets: branch.TotalBets, - TotalStake: Currency(branch.TotalStake), - DeductedStake: Currency(branch.DeductedStake), - TotalCashOut: Currency(branch.TotalCashOut), - TotalCashBacks: Currency(branch.TotalCashBacks), - NumberOfUnsettled: branch.NumberOfUnsettled, - TotalUnsettledAmount: Currency(branch.TotalUnsettledAmount), - TotalCashiers: branch.TotalCashiers, - UpdatedAt: branch.UpdatedAt.Time, - } -} - -func ConvertDBBranchStatsList(stats []dbgen.BranchStat) []BranchStat { - result := make([]BranchStat, len(stats)) - for i, stat := range stats { - result[i] = ConvertDBBranchStats(stat) - } - return result -} - -func ConvertDBBranchStatsByInterval(branch dbgen.GetBranchStatsRow) BranchStat { - return BranchStat{ - IntervalStart: branch.IntervalStart.Time, - BranchID: branch.BranchID, - BranchName: branch.BranchName, - CompanyID: branch.CompanyID, - CompanyName: branch.CompanyName, - CompanySlug: branch.CompanySlug, - TotalBets: branch.TotalBets, - TotalStake: Currency(branch.TotalStake), - DeductedStake: Currency(branch.DeductedStake), - TotalCashOut: Currency(branch.TotalCashOut), - TotalCashBacks: Currency(branch.TotalCashBacks), - NumberOfUnsettled: branch.NumberOfUnsettled, - TotalUnsettledAmount: Currency(branch.TotalUnsettledAmount), - TotalCashiers: branch.TotalCashiers, - UpdatedAt: branch.UpdatedAt.Time, - } -} - -func ConvertDBBranchStatsByIntervalList(stats []dbgen.GetBranchStatsRow) []BranchStat { - result := make([]BranchStat, len(stats)) - for i, stat := range stats { - result[i] = ConvertDBBranchStatsByInterval(stat) - } - return result -} diff --git a/internal/domain/company.go b/internal/domain/company.go deleted file mode 100644 index 506da64..0000000 --- a/internal/domain/company.go +++ /dev/null @@ -1,290 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/jackc/pgx/v5/pgtype" -) - -// Company represents the client that we will contract the services with -// they are the ones that manage the branches and branch managers -// they will have their own wallet that they will use to distribute to the branch wallets -type Company struct { - ID int64 - Name string - Slug string - AdminID int64 - WalletID int64 - DeductedPercentage float32 - IsActive bool -} - -type CompanyFilter struct { - IsActive ValidBool - Query ValidString - CreatedBefore ValidTime - CreatedAfter ValidTime -} - -type GetCompany struct { - ID int64 - Name string - Slug string - AdminID int64 - AdminFirstName string - AdminLastName string - AdminPhoneNumber string - WalletID int64 - WalletBalance Currency - IsWalletActive bool - DeductedPercentage float32 - IsActive bool - TotalBets int64 - TotalStake Currency - DeductedStake Currency - TotalCashOut Currency - TotalCashBacks Currency - NumberOfUnsettled int64 - TotalUnsettledAmount Currency - TotalAdmins int64 - TotalManagers int64 - TotalCashiers int64 - TotalCustomers int64 - TotalApprovers int64 - TotalBranches int64 - StatsUpdatedAt time.Time -} - -type CreateCompany struct { - Name string - AdminID int64 - WalletID int64 - DeductedPercentage float32 - Slug string - IsActive bool -} - -type UpdateCompany struct { - ID int64 - Name ValidString - AdminID ValidInt64 - IsActive ValidBool - DeductedPercentage ValidFloat32 - Slug ValidString -} - -type CreateCompanyReq struct { - Name string `json:"name" example:"CompanyName"` - AdminID int64 `json:"admin_id" example:"1"` - DeductedPercentage float32 `json:"deducted_percentage" example:"0.1" validate:"lt=1"` - Slug string `json:"slug"` - IsActive bool `json:"is_active"` -} -type UpdateCompanyReq struct { - Name *string `json:"name,omitempty" example:"CompanyName"` - AdminID *int64 `json:"admin_id,omitempty" example:"1"` - IsActive *bool `json:"is_active,omitempty" example:"true"` - DeductedPercentage *float32 `json:"deducted_percentage,omitempty" example:"0.1" validate:"lt=1"` - Slug *string `json:"slug"` -} - -type CompanyRes struct { - ID int64 `json:"id" example:"1"` - Name string `json:"name" example:"CompanyName"` - Slug string `json:"slug" example:"slug"` - AdminID int64 `json:"admin_id" example:"1"` - WalletID int64 `json:"wallet_id" example:"1"` - DeductedPercentage float32 `json:"deducted_percentage" example:"0.1"` - IsActive bool `json:"is_active" example:"true"` -} - -type GetCompanyRes struct { - ID int64 `json:"id" example:"1"` - Name string `json:"name" example:"CompanyName"` - Slug string `json:"slug" example:"slug"` - AdminID int64 `json:"admin_id" example:"1"` - WalletID int64 `json:"wallet_id" example:"1"` - WalletBalance float32 `json:"balance" example:"1"` - WalletIsActive bool `json:"is_wallet_active" example:"false"` - IsActive bool `json:"is_active" example:"false"` - DeductedPercentage float32 `json:"deducted_percentage" example:"0.1"` - AdminFirstName string `json:"admin_first_name" example:"John"` - AdminLastName string `json:"admin_last_name" example:"Doe"` - AdminPhoneNumber string `json:"admin_phone_number" example:"1234567890"` - TotalBets int64 `json:"total_bets"` - TotalStake float32 `json:"total_stake"` - DeductedStake float32 `json:"deducted_stake"` - TotalCashOut float32 `json:"total_cash_out"` - TotalCashBacks float32 `json:"total_cash_backs"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount float32 `json:"total_unsettled_amount"` - TotalAdmins int64 `json:"total_admins"` - TotalManagers int64 `json:"total_managers"` - TotalCashiers int64 `json:"total_cashiers"` - TotalCustomers int64 `json:"total_customers"` - TotalApprovers int64 `json:"total_approvers"` - TotalBranches int64 `json:"total_branches"` - StatsUpdatedAt time.Time `json:"stats_updated_at"` -} - -func ConvertCompany(company Company) CompanyRes { - return CompanyRes(company) -} - -func ConvertGetCompany(company GetCompany) GetCompanyRes { - return GetCompanyRes{ - ID: company.ID, - Name: company.Name, - Slug: company.Slug, - AdminID: company.AdminID, - WalletID: company.WalletID, - WalletBalance: company.WalletBalance.Float32(), - IsActive: company.IsActive, - WalletIsActive: company.IsWalletActive, - DeductedPercentage: company.DeductedPercentage, - AdminFirstName: company.AdminFirstName, - AdminLastName: company.AdminLastName, - AdminPhoneNumber: company.AdminPhoneNumber, - TotalBets: company.TotalBets, - TotalStake: company.TotalStake.Float32(), - DeductedStake: company.DeductedStake.Float32(), - TotalCashOut: company.TotalCashOut.Float32(), - TotalCashBacks: company.TotalCashBacks.Float32(), - NumberOfUnsettled: company.NumberOfUnsettled, - TotalUnsettledAmount: company.TotalUnsettledAmount.Float32(), - TotalAdmins: company.TotalAdmins, - TotalManagers: company.TotalManagers, - TotalCashiers: company.TotalCashiers, - TotalCustomers: company.TotalCustomers, - TotalApprovers: company.TotalApprovers, - TotalBranches: company.TotalBranches, - StatsUpdatedAt: company.StatsUpdatedAt, - } -} - -func ConvertCreateCompany(company CreateCompany) dbgen.CreateCompanyParams { - return dbgen.CreateCompanyParams{ - Name: company.Name, - Slug: company.Slug, - AdminID: company.AdminID, - WalletID: company.WalletID, - DeductedPercentage: company.DeductedPercentage, - IsActive: company.IsActive, - } -} - -func ConvertDBCompany(dbCompany dbgen.Company) Company { - return Company{ - ID: dbCompany.ID, - Name: dbCompany.Name, - Slug: dbCompany.Slug, - AdminID: dbCompany.AdminID, - WalletID: dbCompany.WalletID, - DeductedPercentage: dbCompany.DeductedPercentage, - IsActive: dbCompany.IsActive, - } -} - -func ConvertDBCompanyDetails(dbCompany dbgen.CompaniesDetail) GetCompany { - return GetCompany{ - ID: dbCompany.ID, - Name: dbCompany.Name, - Slug: dbCompany.Slug, - AdminID: dbCompany.AdminID, - WalletID: dbCompany.WalletID, - WalletBalance: Currency(dbCompany.Balance), - IsWalletActive: dbCompany.WalletIsActive, - AdminFirstName: dbCompany.AdminFirstName, - AdminLastName: dbCompany.AdminLastName, - AdminPhoneNumber: dbCompany.AdminPhoneNumber.String, - DeductedPercentage: dbCompany.DeductedPercentage, - IsActive: dbCompany.IsActive, - TotalBets: dbCompany.TotalBets, - TotalStake: Currency(dbCompany.TotalStake), - DeductedStake: Currency(dbCompany.DeductedStake), - TotalCashOut: Currency(dbCompany.TotalCashOut), - TotalCashBacks: Currency(dbCompany.TotalCashBacks), - NumberOfUnsettled: dbCompany.NumberOfUnsettled, - TotalUnsettledAmount: Currency(dbCompany.TotalUnsettledAmount), - TotalAdmins: dbCompany.TotalAdmins, - TotalManagers: dbCompany.TotalManagers, - TotalCashiers: dbCompany.TotalCashiers, - TotalCustomers: dbCompany.TotalCustomers, - TotalApprovers: dbCompany.TotalApprovers, - TotalBranches: dbCompany.TotalBranches, - StatsUpdatedAt: dbCompany.StatsUpdatedAt.Time, - } -} - -func ConvertUpdateCompany(updateCompany UpdateCompany) dbgen.UpdateCompanyParams { - newUpdateCompany := dbgen.UpdateCompanyParams{ - ID: updateCompany.ID, - Name: updateCompany.Name.ToPG(), - AdminID: updateCompany.AdminID.ToPG(), - IsActive: updateCompany.IsActive.ToPG(), - DeductedPercentage: updateCompany.DeductedPercentage.ToPG(), - Slug: updateCompany.Slug.ToPG(), - } - - return newUpdateCompany -} - -func ConvertUpdateCompanyReq(req UpdateCompanyReq, companyID int64) UpdateCompany { - var updateCompany UpdateCompany - - updateCompany.ID = companyID - - if req.Name != nil { - updateCompany.Name = ValidString{ - Value: *req.Name, - Valid: true, - } - } - - if req.AdminID != nil { - updateCompany.AdminID = ValidInt64{ - Value: *req.AdminID, - Valid: true, - } - } - - if req.IsActive != nil { - updateCompany.IsActive = ValidBool{ - Value: *req.IsActive, - Valid: true, - } - } - - if req.DeductedPercentage != nil { - updateCompany.DeductedPercentage = ValidFloat32{ - Value: *req.DeductedPercentage, - Valid: true, - } - } - if req.Slug != nil { - updateCompany.Slug = ValidString{ - Value: *req.Slug, - Valid: true, - } - } - - return updateCompany -} - -func ConvertGetAllCompaniesParams(filter CompanyFilter) dbgen.GetAllCompaniesParams { - return dbgen.GetAllCompaniesParams{ - 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, - }, - } -} diff --git a/internal/domain/company_stats.go b/internal/domain/company_stats.go deleted file mode 100644 index 6d63f5e..0000000 --- a/internal/domain/company_stats.go +++ /dev/null @@ -1,114 +0,0 @@ -package domain - -import ( - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "time" -) - -type CompanyStat struct { - IntervalStart time.Time - CompanyID int64 - CompanyName string - CompanySlug string - TotalBets int64 - TotalStake Currency - DeductedStake Currency - TotalCashOut Currency - TotalCashBacks Currency - NumberOfUnsettled int64 - TotalUnsettledAmount Currency - TotalAdmins int64 - TotalManagers int64 - TotalCashiers int64 - TotalCustomers int64 - TotalApprovers int64 - TotalBranches int64 - UpdatedAt time.Time -} -type CompanyStatRes struct { - IntervalStart time.Time `json:"interval_start"` - CompanyID int64 `json:"company_id"` - CompanyName string `json:"company_name"` - CompanySlug string `json:"company_slug"` - TotalBets int64 `json:"total_bets"` - TotalStake float32 `json:"total_stake"` - DeductedStake float32 `json:"deducted_stake"` - TotalCashOut float32 `json:"total_cash_out"` - TotalCashBacks float32 `json:"total_cash_backs"` - NumberOfUnsettled int64 `json:"number_of_unsettled"` - TotalUnsettledAmount float32 `json:"total_unsettled_amount"` - TotalAdmins int64 `json:"total_admins"` - TotalManagers int64 `json:"total_managers"` - TotalCashiers int64 `json:"total_cashiers"` - TotalCustomers int64 `json:"total_customers"` - TotalApprovers int64 `json:"total_approvers"` - TotalBranches int64 `json:"total_branches"` - UpdatedAt time.Time `json:"updated_at"` -} - -type CompanyStatFilter struct { - Interval ValidDateInterval - CompanyID ValidInt64 -} - -func ConvertDBCompanyStats(company dbgen.CompanyStat) CompanyStat { - return CompanyStat{ - IntervalStart: company.IntervalStart.Time, - CompanyID: company.CompanyID, - CompanyName: company.CompanyName, - CompanySlug: company.CompanySlug, - TotalBets: company.TotalBets, - TotalStake: Currency(company.TotalStake), - DeductedStake: Currency(company.DeductedStake), - TotalCashOut: Currency(company.TotalCashOut), - TotalCashBacks: Currency(company.TotalCashBacks), - NumberOfUnsettled: company.NumberOfUnsettled, - TotalUnsettledAmount: Currency(company.TotalUnsettledAmount), - TotalAdmins: company.TotalAdmins, - TotalManagers: company.TotalManagers, - TotalCashiers: company.TotalCashiers, - TotalCustomers: company.TotalCustomers, - TotalApprovers: company.TotalApprovers, - TotalBranches: company.TotalBranches, - UpdatedAt: company.UpdatedAt.Time, - } -} - -func ConvertDBCompanyStatsList(stats []dbgen.CompanyStat) []CompanyStat { - result := make([]CompanyStat, len(stats)) - for i, stat := range stats { - result[i] = ConvertDBCompanyStats(stat) - } - return result -} - -func ConvertDBCompanyStatsByInterval(company dbgen.GetCompanyStatsRow) CompanyStat { - return CompanyStat{ - IntervalStart: company.IntervalStart.Time, - CompanyID: company.CompanyID, - CompanyName: company.CompanyName, - CompanySlug: company.CompanySlug, - TotalBets: company.TotalBets, - TotalStake: Currency(company.TotalStake), - DeductedStake: Currency(company.DeductedStake), - TotalCashOut: Currency(company.TotalCashOut), - TotalCashBacks: Currency(company.TotalCashBacks), - NumberOfUnsettled: company.NumberOfUnsettled, - TotalUnsettledAmount: Currency(company.TotalUnsettledAmount), - TotalAdmins: company.TotalAdmins, - TotalManagers: company.TotalManagers, - TotalCashiers: company.TotalCashiers, - TotalCustomers: company.TotalCustomers, - TotalApprovers: company.TotalApprovers, - TotalBranches: company.TotalBranches, - UpdatedAt: company.UpdatedAt.Time, - } -} - -func ConvertDBCompanyStatsByIntervalList(stats []dbgen.GetCompanyStatsRow) []CompanyStat { - result := make([]CompanyStat, len(stats)) - for i, stat := range stats { - result[i] = ConvertDBCompanyStatsByInterval(stat) - } - return result -} diff --git a/internal/domain/custom_odds.go b/internal/domain/custom_odds.go deleted file mode 100644 index 6163c70..0000000 --- a/internal/domain/custom_odds.go +++ /dev/null @@ -1,69 +0,0 @@ -package domain - -// import ( -// "encoding/json" -// "time" - -// dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -// ) - -// type CustomOdd struct { -// ID int64 -// OddID int64 -// CompanyID int64 -// EventID string -// RawOdds []json.RawMessage -// CreatedAt time.Time -// } - -// type CreateCustomOdd struct { -// OddID int64 -// CompanyID int64 -// EventID string -// RawOdds []json.RawMessage -// } - -// type CustomOddFilter struct { -// CompanyID ValidInt64 -// } - -// func ConvertCreateCustomOdd(odd CreateCustomOdd) (dbgen.InsertCustomOddParams, error) { -// rawOddsBytes, err := json.Marshal(odd.RawOdds) - -// if err != nil { -// return dbgen.InsertCustomOddParams{}, err -// } -// return dbgen.InsertCustomOddParams{ -// OddID: odd.OddID, -// CompanyID: odd.CompanyID, -// EventID: odd.EventID, -// RawOdds: rawOddsBytes, -// }, nil -// } - -// func ConvertDBCustomOdd(dbCustomOdd dbgen.CustomOdd) (CustomOdd, error) { -// var rawOdds []json.RawMessage -// if err := json.Unmarshal(dbCustomOdd.RawOdds, &rawOdds); err != nil { -// return CustomOdd{}, err -// } -// return CustomOdd{ -// ID: dbCustomOdd.ID, -// OddID: dbCustomOdd.OddID, -// CompanyID: dbCustomOdd.CompanyID, -// EventID: dbCustomOdd.EventID, -// RawOdds: rawOdds, -// CreatedAt: dbCustomOdd.CreatedAt.Time, -// }, nil -// } - -// func ConvertDbCustomOdds(list []dbgen.CustomOdd) ([]CustomOdd, error) { -// result := make([]CustomOdd, 0, len(list)) -// for _, item := range list { -// convertedItem, err := ConvertDBCustomOdd(item) -// if err != nil { -// return nil, err -// } -// result = append(result, convertedItem) -// } -// return result, nil -// } diff --git a/internal/domain/enet_pulse.go b/internal/domain/enet_pulse.go deleted file mode 100644 index 00725d9..0000000 --- a/internal/domain/enet_pulse.go +++ /dev/null @@ -1,769 +0,0 @@ -package domain - -import "time" - -type EnetPulseSport struct { - ID string `json:"id"` - Name string `json:"name"` - N string `json:"n"` - UT string `json:"ut"` -} - -type SportsResponse struct { - Sports map[string]EnetPulseSport `json:"sports"` -} - -type TournamentTemplatesResponse struct { - TournamentTemplates map[string]TournamentTemplate `json:"tournament_templates"` -} - -type TournamentTemplate struct { - ID string `json:"id"` - Name string `json:"name"` - SportFK string `json:"sportFK"` - Gender string `json:"gender"` - N string `json:"n"` - UT string `json:"ut"` -} - -type TournamentTemplateParticipantsResponse struct { - // Map of participant entries or whatever eAPI returns - Participants map[string]TournamentParticipant `json:"participants"` -} - -type TournamentTemplateParticipant struct { - ID string `json:"id"` - DateFrom string `json:"date_from"` - DateTo string `json:"date_to"` - Active string `json:"active"` - N string `json:"n"` - UT string `json:"ut"` - // plus nested participant info - Participant interface{} `json:"participant"` -} - -// For optional parameters -type ParticipantsOptions struct { - IncludeProperties bool - IncludeParticipantProperties bool - IncludeParticipantSports bool - IncludeCountries bool - IncludeCountryCodes bool - ParticipantType string -} - -type Tournament struct { - ID string `json:"id"` - Name string `json:"name"` - TournamentTemplateFK string `json:"tournament_templateFK"` - N string `json:"n"` - UT string `json:"ut"` -} - -type TournamentsResponse struct { - Tournaments map[string]Tournament `json:"tournaments"` -} - -type TournamentParticipant struct { - ID string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Gender string `json:"gender"` - CountryFK string `json:"countryFK"` - Country string `json:"country_name"` -} - -type TournamentWithParticipants struct { - ID string `json:"id"` - Name string `json:"name"` - Participants map[string]map[string]interface{} `json:"participants"` // or a typed struct -} - -type TournamentParticipantsResponse struct { - Tournaments map[string]TournamentWithParticipants `json:"tournaments"` -} - -type TournamentStage struct { - ID string `json:"id"` - Name string `json:"name"` - TournamentFK string `json:"tournamentFK"` - Gender string `json:"gender"` - CountryFK string `json:"countryFK"` - StartDate string `json:"startdate"` - EndDate string `json:"enddate"` - N string `json:"n"` - UT string `json:"ut"` - CountryName string `json:"country_name"` -} - -type TournamentStagesResponse struct { - TournamentStages map[string]TournamentStage `json:"tournament_stages"` -} - -type TournamentStageParticipant struct { - ID string `json:"id"` - Name string `json:"name"` - Gender string `json:"gender"` - Type string `json:"type"` - CountryFK string `json:"countryFK"` - Country string `json:"country_name"` -} - -type TournamentStageWithParticipants struct { - ID string `json:"id"` - Name string `json:"name"` - TournamentFK string `json:"tournamentFK"` - Participants map[string]map[string]interface{} `json:"participants"` // can refine later -} - -type TournamentStageParticipantsResponse struct { - TournamentStages map[string]TournamentStageWithParticipants `json:"tournament_stages"` -} - -type DailyEventsRequest struct { - SportFK int // one of these three required - TournamentTemplateFK int - // TournamentStageFK int - Date string // YYYY-MM-DD optional - Live string // yes/no optional - IncludeVenue string // yes/no optional - StatusType string // e.g. inprogress, finished... - IncludeEventProperties string // yes/no optional - IncludeCountryCodes string // yes/no optional - IncludeFirstLastName string // yes/no optional - IncludeDeleted string // yes/no optional -} - -type DailyEventsResponse struct { - Events map[string]struct { - ID string `json:"id"` - Name string `json:"name"` - StartDate string `json:"startdate"` - Status string `json:"status"` - // add other fields you care about from API - } `json:"events"` -} - -type FixturesRequest struct { - SportFK int - TournamentTemplateFK int - // TournamentStageFK int - LanguageTypeFK int - Date string // YYYY-MM-DD - Live string // "yes" | "no" - IncludeVenue bool - IncludeEventProperties bool - IncludeCountryCodes bool - IncludeFirstLastName bool -} - -// Adjust according to the real API response JSON -type FixturesResponse struct { - Events []FixtureEvent `json:"events"` -} - -type FixtureEvent struct { - ID string `json:"id"` - Name string `json:"name"` - StartDate string `json:"startdate"` - // ... add more fields as per API -} - -type ResultsRequest struct { - SportFK int - TournamentTemplateFK int - // TournamentStageFK int - LanguageTypeFK int - Date string // YYYY-MM-DD - Live string // "yes" | "no" - IncludeVenue bool - IncludeEventProperties bool - IncludeCountryCodes bool - IncludeFirstLastName bool - Limit int - Offset int -} - -// Adjust fields to match API JSON exactly -type ResultsResponse struct { - Events []ResultEvent `json:"events"` -} - -type ResultEvent struct { - ID string `json:"id"` - Name string `json:"name"` - StartDate string `json:"startdate"` - Status string `json:"status"` - // ... add more fields based on actual API -} - -type EventDetailsRequest struct { - ID int - IncludeLineups bool - IncludeIncidents bool - IncludeExtendedResults bool - IncludeProperties bool - IncludeLivestats bool - IncludeVenue bool - IncludeCountryCodes bool - IncludeFirstLastName bool - IncludeDeleted bool - IncludeReference bool - IncludeObjectParticipants bool - IncludeEventIncidentRelation bool - IncludeTeamProperties bool - IncludeObjectRound bool -} - -// Adjust fields to match the actual JSON from Enetpulse -type EventDetailsResponse struct { - EventID string `json:"id"` - EventName string `json:"name"` - StartDate string `json:"startdate"` - // Add additional nested structs/fields as needed based on API response -} - -type EventListRequest struct { - TournamentFK int // optional - // TournamentStageFK int // optional - IncludeEventProperties bool // default true - StatusType string // e.g. "finished", "inprogress" - IncludeVenue bool - IncludeDeleted bool - Limit int - Offset int -} - -// Adjust fields to match the actual JSON structure you get from Enetpulse -type EventListResponse struct { - Events map[string]struct { - ID string `json:"id"` - Name string `json:"name"` - StartDate string `json:"startdate"` - // add more fields as per response - } `json:"events"` -} - -type ParticipantFixturesRequest struct { - ParticipantFK int // required - - SportFK int - TournamentFK int - TournamentTemplateFK int - // TournamentStageFK int - Date string - Live string - Limit int - Offset int - - IncludeVenue bool - IncludeCountryCodes bool - IncludeFirstLastName bool - IncludeEventProperties bool - LanguageTypeFK int -} - -// Adjust this to match the actual API response structure -type ParticipantFixturesResponse struct { - Events []struct { - ID int `json:"id"` - Name string `json:"name"` - // ... other fields from the API - } `json:"events"` -} - -type ParticipantResultsRequest struct { - ParticipantFK int64 `json:"participantFK"` - Limit int `json:"limit,omitempty"` - Offset int `json:"offset,omitempty"` - IncludeVenue bool `json:"includeVenue,omitempty"` - IncludeDeleted bool `json:"includeDeleted,omitempty"` -} - -type ParticipantResultsResponse struct { - // Adjust to match Enetpulse’s JSON structure - Results []struct { - EventFK int64 `json:"eventFK"` - ParticipantFK int64 `json:"participantFK"` - Score string `json:"score"` - Status string `json:"status"` - // add more fields as needed - } `json:"results"` -} - -type TeamLogoResponse struct { - ContentType string `json:"contentType"` // e.g. "image/png" - Data []byte `json:"-"` // raw image bytes - URL string `json:"url,omitempty"` // optional original URL -} - -type TeamShirtsResponse struct { - ContentType string `json:"contentType"` // could be "application/json" or "image/png" - RawData []byte `json:"-"` // raw response - URL string `json:"url,omitempty"` -} - -type ImageResponse struct { - ContentType string `json:"contentType"` // e.g., "image/png" - RawData []byte `json:"-"` // raw image bytes - URL string `json:"url,omitempty"` -} - -type PreMatchOddsRequest struct { - ObjectFK int64 `json:"objectFK"` - OddsProviderFK []int64 `json:"oddsProviderFK,omitempty"` - OutcomeTypeFK int64 `json:"outcomeTypeFK,omitempty"` - OutcomeScopeFK int64 `json:"outcomeScopeFK,omitempty"` - OutcomeSubtypeFK int64 `json:"outcomeSubtypeFK,omitempty"` - Limit int `json:"limit,omitempty"` - Offset int `json:"offset,omitempty"` - LanguageTypeFK int64 `json:"languageTypeFK,omitempty"` -} - -type PreMatchOddsResponse struct { - // Define fields according to the Enetpulse preodds response structure - // Example: - EventID int64 `json:"eventFK"` - Odds []PreMatchOutcome `json:"odds"` -} - -type PreMatchOutcome struct { - OutcomeFK int64 `json:"outcomeFK"` - OutcomeValue string `json:"outcomeValue"` - OddsValue float64 `json:"oddsValue"` - ProviderFK int64 `json:"oddsProviderFK"` - OutcomeTypeFK int64 `json:"outcomeTypeFK"` -} - -type TournamentStageOddsRequest struct { - ObjectFK int64 `json:"objectFK"` // Tournament stage ID - OddsProviderFK int64 `json:"oddsProviderFK,omitempty"` - OutcomeTypeFK int64 `json:"outcomeTypeFK"` // Mandatory - OutcomeScopeFK int64 `json:"outcomeScopeFK,omitempty"` - OutcomeSubtypeFK int64 `json:"outcomeSubtypeFK,omitempty"` - Limit int `json:"limit,omitempty"` - Offset int `json:"offset,omitempty"` - LanguageTypeFK int64 `json:"languageTypeFK,omitempty"` -} - -type TournamentStageOddsResponse struct { - // Define fields according to Enetpulse response - StageID int64 `json:"objectFK"` - Odds []PreMatchOutcome `json:"odds"` // reuse PreMatchOutcome from pre-match odds -} - -type TournamentOddsRequest struct { - ObjectFK int64 `json:"objectFK"` // Tournament ID - OddsProviderFK int64 `json:"oddsProviderFK,omitempty"` - OutcomeTypeFK int64 `json:"outcomeTypeFK"` // Mandatory - OutcomeScopeFK int64 `json:"outcomeScopeFK,omitempty"` - OutcomeSubtypeFK int64 `json:"outcomeSubtypeFK,omitempty"` - Limit int `json:"limit,omitempty"` - Offset int `json:"offset,omitempty"` - LanguageTypeFK int64 `json:"languageTypeFK,omitempty"` -} - -type TournamentOddsResponse struct { - TournamentID int64 `json:"objectFK"` - Odds []PreMatchOutcome `json:"odds"` // reuse PreMatchOutcome struct from pre-match odds -} - -type CreateEnetpulseSport struct { - SportID string `json:"sport_id"` // from API "id" - Name string `json:"name"` // from API "name" - UpdatesCount int `json:"updates_count,omitempty"` // from API "n" - LastUpdatedAt time.Time `json:"last_updated_at"` // from API "ut" - Status int `json:"status,omitempty"` // optional, default 1 -} - -type EnetpulseSport struct { - ID int64 `json:"id"` // DB primary key - SportID string `json:"sport_id"` // from API "id" - Name string `json:"name"` // from API "name" - UpdatesCount int `json:"updates_count"` // from API "n" - LastUpdatedAt time.Time `json:"last_updated_at"` - Status int `json:"status"` // active/inactive - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -type EnetpulseTournamentTemplate struct { - ID int64 `json:"id"` - TemplateID string `json:"template_id"` // from API "id" - Name string `json:"name"` // from API "name" - SportFK string `json:"sport_fk"` // related sport id - Gender string `json:"gender"` // male, female, mixed, unknown - UpdatesCount int `json:"updates_count"` // from API "n" - LastUpdatedAt time.Time `json:"last_updated_at"` - Status int `json:"status"` // optional - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -type CreateEnetpulseTournamentTemplate struct { - TemplateID string `json:"templateId"` // from API "id" - Name string `json:"name"` // from API "name" - SportFK int64 `json:"sportFK"` // foreign key to sport - Gender string `json:"gender"` // male, female, mixed, unknown - UpdatesCount int `json:"updatesCount"` // from API "n" - LastUpdatedAt time.Time `json:"lastUpdatedAt"` // from API "ut" - Status int `json:"status"` // optional, e.g., active/inactive -} - -type CreateEnetpulseTournament struct { - TournamentID string // API "id" - Name string // API "name" - TournamentTemplateFK string // API "tournament_templateFK" (links to template_id) - UpdatesCount int // API "n" - LastUpdatedAt time.Time // API "ut" - Status int // optional, default 1 -} - -type EnetpulseTournament struct { - ID int64 // internal DB PK - TournamentID string - Name string - TournamentTemplateFK string - UpdatesCount int - LastUpdatedAt time.Time - Status int - CreatedAt time.Time - UpdatedAt *time.Time -} - -type EnetpulseTournamentStage struct { - ID int64 `json:"id"` - StageID string `json:"stage_id"` // API id - Name string `json:"name"` // API name - TournamentFK string `json:"tournament_fk"` // Foreign key to tournament - Gender string `json:"gender"` // male/female/mixed/unknown - CountryFK string `json:"country_fk"` // country FK from API - StartDate time.Time `json:"start_date"` // start date/time - EndDate time.Time `json:"end_date"` // end date/time - UpdatesCount int `json:"updates_count"` // n from API - LastUpdatedAt time.Time `json:"last_updated_at"` // ut from API - CountryName string `json:"country_name"` // country name from API - Status int `json:"status"` // active/inactive - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -// ✅ Struct for creating new tournament stage rows -type CreateEnetpulseTournamentStage struct { - StageID string `json:"stage_id"` // API id - Name string `json:"name"` // API name - TournamentFK string `json:"tournament_fk"` // DB foreign key to tournaments - Gender string `json:"gender"` // male/female/mixed/unknown - CountryFK string `json:"country_fk"` // country FK from API - StartDate time.Time `json:"start_date"` // start date/time - EndDate time.Time `json:"end_date"` // end date/time - UpdatesCount int `json:"updates_count"` // n from API - LastUpdatedAt time.Time `json:"last_updated_at"` // ut from API - CountryName string `json:"country_name"` // country name from API - Status int `json:"status"` // active/inactive -} - -// For insertion -type CreateEnetpulseFixture struct { - FixtureID string - Name string - SportFK string - TournamentFK string - TournamentTemplateFK string - TournamentStageFK string - TournamentStageName string - TournamentName string - TournamentTemplateName string - SportName string - Gender string - StartDate time.Time - StatusType string - StatusDescFK string - RoundTypeFK string - UpdatesCount int - LastUpdatedAt time.Time -} - -// Full domain model -type EnetpulseFixture struct { - FixtureID string `json:"id"` - Name string `json:"name"` - SportFK string `json:"sportFK"` - TournamentFK string `json:"tournamentFK"` - TournamentTemplateFK string `json:"tournament_templateFK"` - // TournamentStageFK string `json:"tournament_stageFK"` - TournamentStageName string `json:"tournament_stage_name"` - TournamentName string `json:"tournament_name"` - TournamentTemplateName string `json:"tournament_template_name"` - SportName string `json:"sport_name"` - Gender string `json:"gender"` - StartDate string `json:"startdate"` // ISO 8601 - StatusType string `json:"status_type"` - StatusDescFK string `json:"status_descFK"` - RoundTypeFK string `json:"round_typeFK"` - UpdatesCount string `json:"n"` // convert to int - LastUpdatedAt string `json:"ut"` // parse to time.Time -} - -type CreateEnetpulseResult struct { - ResultID string `json:"result_id"` - Name string `json:"name"` - SportFK string `json:"sport_fk"` - TournamentFK string `json:"tournament_fk"` - TournamentTemplateFK string `json:"tournament_template_fk"` - // TournamentStageFK string `json:"tournament_stage_fk"` - TournamentStageName string `json:"tournament_stage_name"` - TournamentName string `json:"tournament_name"` - TournamentTemplateName string `json:"tournament_template_name"` - SportName string `json:"sport_name"` - StartDate time.Time `json:"start_date"` - StatusType string `json:"status_type"` - StatusDescFK string `json:"status_desc_fk"` - RoundTypeFK string `json:"round_type_fk"` - UpdatesCount int32 `json:"updates_count"` - LastUpdatedAt time.Time `json:"last_updated_at"` - - // Optional metadata - Round string `json:"round"` - Live string `json:"live"` - VenueName string `json:"venue_name"` - LivestatsPlus string `json:"livestats_plus"` - LivestatsType string `json:"livestats_type"` - Commentary string `json:"commentary"` - LineupConfirmed bool `json:"lineup_confirmed"` - Verified bool `json:"verified"` - Spectators int32 `json:"spectators"` - - // Time-related metadata - GameStarted *time.Time `json:"game_started"` - FirstHalfEnded *time.Time `json:"first_half_ended"` - SecondHalfStarted *time.Time `json:"second_half_started"` - SecondHalfEnded *time.Time `json:"second_half_ended"` - GameEnded *time.Time `json:"game_ended"` -} - -// ✅ Used for reading result records -type EnetpulseResult struct { - ID int64 `json:"id"` - ResultID string `json:"result_id"` - Name string `json:"name"` - SportFK string `json:"sport_fk"` - TournamentFK string `json:"tournament_fk"` - TournamentTemplateFK string `json:"tournament_template_fk"` - // TournamentStageFK string `json:"tournament_stage_fk"` - TournamentStageName string `json:"tournament_stage_name"` - TournamentName string `json:"tournament_name"` - TournamentTemplateName string `json:"tournament_template_name"` - SportName string `json:"sport_name"` - StartDate time.Time `json:"start_date"` - StatusType string `json:"status_type"` - StatusDescFK string `json:"status_desc_fk"` - RoundTypeFK string `json:"round_type_fk"` - UpdatesCount int32 `json:"updates_count"` - LastUpdatedAt *time.Time `json:"last_updated_at"` - - Round string `json:"round"` - Live string `json:"live"` - VenueName string `json:"venue_name"` - LivestatsPlus string `json:"livestats_plus"` - LivestatsType string `json:"livestats_type"` - Commentary string `json:"commentary"` - LineupConfirmed bool `json:"lineup_confirmed"` - Verified bool `json:"verified"` - Spectators int32 `json:"spectators"` - - GameStarted *time.Time `json:"game_started"` - FirstHalfEnded *time.Time `json:"first_half_ended"` - SecondHalfStarted *time.Time `json:"second_half_started"` - SecondHalfEnded *time.Time `json:"second_half_ended"` - GameEnded *time.Time `json:"game_ended"` - - CreatedAt time.Time `json:"created_at"` - UpdatedAt *time.Time `json:"updated_at"` -} - -type EnetpulseOutcomeType struct { - ID int64 `json:"id"` - OutcomeTypeID string `json:"outcome_type_id"` // changed from int64 → string - Name string `json:"name"` - Description string `json:"description"` - UpdatesCount int32 `json:"updates_count"` - LastUpdatedAt time.Time `json:"last_updated_at"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -// CreateEnetpulseOutcomeType represents the payload to create or update an outcome type. -type CreateEnetpulseOutcomeType struct { - OutcomeTypeID string `json:"outcome_type_id"` // changed from int64 → string - Name string `json:"name"` - Description string `json:"description"` - UpdatesCount int32 `json:"updates_count"` - LastUpdatedAt time.Time `json:"last_updated_at"` -} - -type EnetpulsePreoddsTemp struct { - PreoddsID string `json:"preodds_id"` - EventFK string `json:"event_fk"` - OutcomeTypeFK string `json:"outcome_type_fk"` - OutcomeScopeFK string `json:"outcome_scope_fk"` - OutcomeSubtypeFK string `json:"outcome_subtype_fk"` - EventParticipantNumber int `json:"event_participant_number"` - IParam string `json:"iparam"` - IParam2 string `json:"iparam2"` - DParam string `json:"dparam"` - DParam2 string `json:"dparam2"` - SParam string `json:"sparam"` - UpdatesCount int `json:"updates_count"` - LastUpdatedAt time.Time `json:"last_updated_at"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -// CreateEnetpulsePreodds is used when inserting a new preodds record -type CreateEnetpulsePreodds struct { - PreoddsID string `json:"preodds_id"` - EventFK string `json:"event_fk"` - OutcomeTypeFK string `json:"outcome_type_fk"` - OutcomeScopeFK string `json:"outcome_scope_fk"` - OutcomeSubtypeFK string `json:"outcome_subtype_fk"` - EventParticipantNumber int `json:"event_participant_number"` - IParam string `json:"iparam"` - IParam2 string `json:"iparam2"` - DParam string `json:"dparam"` - DParam2 string `json:"dparam2"` - SParam string `json:"sparam"` - UpdatesCount int `json:"updates_count"` - LastUpdatedAt time.Time `json:"last_updated_at"` -} - -type CreateEnetpulsePreoddsBettingOffer struct { - BettingOfferID string - PreoddsFK string - BettingOfferStatusFK int32 - OddsProviderFK int32 - Odds float64 - OddsOld float64 - Active string - CouponKey string - UpdatesCount int - LastUpdatedAt time.Time -} - -// EnetpulsePreoddsBettingOffer represents the DB record of a betting offer -type EnetpulsePreoddsBettingOffer struct { - ID int64 `json:"id"` - BettingOfferID string `json:"betting_offer_id"` - PreoddsFK string `json:"preodds_fk"` - BettingOfferStatusFK int32 `json:"betting_offer_status_fk"` - OddsProviderFK int32 `json:"odds_provider_fk"` - Odds float64 `json:"odds"` - OddsOld float64 `json:"odds_old"` - Active string `json:"active"` - CouponKey string `json:"coupon_key"` - UpdatesCount int `json:"updates_count"` - LastUpdatedAt time.Time `json:"last_updated_at"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -type EnetpulseFixtureWithPreodds struct { - FixtureID string - FixtureApiID string - FixtureName string - SportFk string - TournamentFk string - TournamentTemplateFk string - TournamentStageFk string - StartDate time.Time - StatusType string - StatusDescFk string - RoundTypeFk string - UpdatesCount int32 - LastUpdatedAt time.Time - CreatedAt time.Time - UpdatedAt time.Time - Preodds []EnetpulsePreodds -} - -type EnetpulsePreodds struct { - ID int64 - PreoddsID string - EventFK int64 - OutcomeTypeFK int32 - OutcomeScopeFK int32 - OutcomeSubtypeFK int32 - EventParticipantNumber int32 - IParam string - IParam2 string - DParam string - DParam2 string - SParam string - UpdatesCount int32 - LastUpdatedAt time.Time - CreatedAt time.Time - UpdatedAt time.Time - BettingOffers []EnetpulsePreoddsBettingOffer -} - -type EnetpulseResultParticipant struct { - ID int64 `json:"id"` - ParticipantMapID string `json:"participant_map_id"` - ResultFk string `json:"result_fk"` - ParticipantFk string `json:"participant_fk"` - Number int32 `json:"number"` - Name string `json:"name"` - Gender string `json:"gender"` - Type string `json:"type"` - CountryFk string `json:"country_fk"` - CountryName string `json:"country_name"` - OrdinaryTime string `json:"ordinary_time"` - RunningScore string `json:"running_score"` - Halftime string `json:"halftime"` - FinalResult string `json:"final_result"` - LastUpdatedAt time.Time `json:"last_updated_at"` - CreatedAt time.Time `json:"created_at"` -} - -// CreateEnetpulseResultParticipant is the payload for inserting or updating a participant record. -type CreateEnetpulseResultParticipant struct { - ParticipantMapID string `json:"participant_map_id"` - ResultFk string `json:"result_fk"` - ParticipantFk string `json:"participant_fk"` - Number int32 `json:"number"` - Name string `json:"name"` - Gender string `json:"gender"` - Type string `json:"type"` - CountryFk string `json:"country_fk"` - CountryName string `json:"country_name"` - OrdinaryTime string `json:"ordinary_time"` - RunningScore string `json:"running_score"` - Halftime string `json:"halftime"` - FinalResult string `json:"final_result"` - LastUpdatedAt time.Time `json:"last_updated_at"` -} - -type EnetpulseResultReferee struct { - ID int64 `json:"id"` - ResultFk string `json:"result_fk"` - RefereeFk string `json:"referee_fk"` - Assistant1RefereeFk string `json:"assistant1_referee_fk"` - Assistant2RefereeFk string `json:"assistant2_referee_fk"` - FourthRefereeFk string `json:"fourth_referee_fk"` - Var1RefereeFk string `json:"var1_referee_fk"` - Var2RefereeFk string `json:"var2_referee_fk"` - LastUpdatedAt time.Time `json:"last_updated_at"` - CreatedAt time.Time `json:"created_at"` -} - -// CreateEnetpulseResultReferee is the payload for inserting or updating referee assignments. -type CreateEnetpulseResultReferee struct { - ResultFk string `json:"result_fk"` - RefereeFk string `json:"referee_fk"` - Assistant1RefereeFk string `json:"assistant1_referee_fk"` - Assistant2RefereeFk string `json:"assistant2_referee_fk"` - FourthRefereeFk string `json:"fourth_referee_fk"` - Var1RefereeFk string `json:"var1_referee_fk"` - Var2RefereeFk string `json:"var2_referee_fk"` - LastUpdatedAt time.Time `json:"last_updated_at"` -} diff --git a/internal/domain/event.go b/internal/domain/event.go deleted file mode 100644 index 40aa66d..0000000 --- a/internal/domain/event.go +++ /dev/null @@ -1,241 +0,0 @@ -package domain - -import ( - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/jackc/pgx/v5/pgtype" - "time" -) - -type BaseEvent struct { - ID int64 - SourceEventID string - SportID int32 - MatchName string - HomeTeam string - AwayTeam string - HomeTeamID int64 - AwayTeamID int64 - HomeTeamImage string - AwayTeamImage string - LeagueID int64 - LeagueName string - LeagueCC ValidString - StartTime time.Time - Source EventSource - Status EventStatus - IsMonitored bool - DefaultIsFeatured bool - DefaultIsActive bool - DefaultWinningUpperLimit int64 - Score ValidString - MatchMinute ValidInt - TimerStatus ValidString - AddedTime ValidInt - MatchPeriod ValidInt - IsLive bool - FetchedAt time.Time - TotalOddOutcomes int64 - NumberOfBets int64 - TotalAmount Currency - AvgBetAmount Currency - TotalPotentialWinnings Currency -} - -type BaseEventRes struct { - ID int64 `json:"id"` - SourceEventID string `json:"source_event_id"` - SportID int32 `json:"sport_id"` - MatchName string `json:"match_name"` - HomeTeam string `json:"home_team"` - AwayTeam string `json:"away_team"` - HomeTeamID int64 `json:"home_team_id"` - AwayTeamID int64 `json:"away_team_id"` - HomeTeamImage string `json:"home_team_image"` - AwayTeamImage string `json:"away_team_image"` - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - LeagueCC string `json:"league_cc"` - StartTime time.Time `json:"start_time"` - Source EventSource `json:"source"` - Status EventStatus `json:"status"` - IsMonitored bool `json:"is_monitored"` - DefaultIsFeatured bool `json:"default_is_featured"` - DefaultIsActive bool `json:"default_is_active"` - DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` - Score string `json:"score"` - MatchMinute int `json:"match_minute"` - TimerStatus string `json:"timer_status"` - AddedTime int `json:"added_time"` - MatchPeriod int `json:"match_period"` - IsLive bool `json:"is_live"` - FetchedAt time.Time `json:"fetched_at"` - TotalOddOutcomes int64 `json:"total_odd_outcomes"` - NumberOfBets int64 `json:"number_of_bets"` - TotalAmount float32 `json:"total_amount"` - AvgBetAmount float32 `json:"average_bet_amount"` - TotalPotentialWinnings float32 `json:"total_potential_winnings"` -} - -type CreateEvent struct { - SourceEventID string - SportID int32 - MatchName string - HomeTeam string - AwayTeam string - HomeTeamID int64 - AwayTeamID int64 - HomeTeamImage string - AwayTeamImage string - LeagueID int64 - LeagueName string - StartTime time.Time - IsLive bool - Status EventStatus - Source EventSource - DefaultWinningUpperLimit int64 -} -type EventFilter struct { - Query ValidString - SportID ValidInt32 - LeagueID ValidInt64 - CountryCode ValidString - FirstStartTime ValidTime - LastStartTime ValidTime - Limit ValidInt32 - Offset ValidInt32 - Featured ValidBool - Active ValidBool - IsLive ValidBool - Status ValidEventStatus - Source ValidEventSource -} - -func ConvertDBEvent(event dbgen.EventDetailed) BaseEvent { - return BaseEvent{ - ID: event.ID, - SourceEventID: event.SourceEventID, - SportID: event.SportID, - MatchName: event.MatchName, - HomeTeam: event.HomeTeam, - AwayTeam: event.AwayTeam, - HomeTeamID: event.HomeTeamID, - AwayTeamID: event.AwayTeamID, - HomeTeamImage: event.HomeKitImage, - AwayTeamImage: event.AwayKitImage, - LeagueID: event.LeagueID, - LeagueName: event.LeagueName, - LeagueCC: ValidString{ - Value: event.LeagueCc.String, - Valid: event.LeagueCc.Valid, - }, - StartTime: event.StartTime.Time.UTC(), - Source: EventSource(event.Source), - Status: EventStatus(event.Status), - DefaultIsFeatured: event.DefaultIsFeatured, - IsMonitored: event.IsMonitored, - DefaultIsActive: event.DefaultIsActive, - DefaultWinningUpperLimit: event.DefaultWinningUpperLimit, - Score: ValidString{ - Value: event.Score.String, - Valid: event.Score.Valid, - }, - MatchMinute: ValidInt{ - Value: int(event.MatchMinute.Int32), - Valid: event.MatchMinute.Valid, - }, - TimerStatus: ValidString{ - Value: event.TimerStatus.String, - Valid: event.TimerStatus.Valid, - }, - AddedTime: ValidInt{ - Value: int(event.AddedTime.Int32), - Valid: event.AddedTime.Valid, - }, - MatchPeriod: ValidInt{ - Value: int(event.MatchPeriod.Int32), - Valid: event.MatchPeriod.Valid, - }, - IsLive: event.IsLive, - FetchedAt: event.FetchedAt.Time, - TotalOddOutcomes: event.TotalOutcomes, - NumberOfBets: event.NumberOfBets, - TotalAmount: Currency(event.TotalAmount), - AvgBetAmount: Currency(event.AvgBetAmount), - TotalPotentialWinnings: Currency(event.TotalPotentialWinnings), - } -} - -func ConvertDBEvents(events []dbgen.EventDetailed) []BaseEvent { - result := make([]BaseEvent, len(events)) - for i, e := range events { - result[i] = ConvertDBEvent(e) - } - return result -} - -func ConvertCreateEvent(e CreateEvent) dbgen.InsertEventParams { - return dbgen.InsertEventParams{ - SourceEventID: e.SourceEventID, - SportID: e.SportID, - MatchName: e.MatchName, - HomeTeam: e.HomeTeam, - AwayTeam: e.AwayTeam, - HomeTeamID: e.HomeTeamID, - AwayTeamID: e.AwayTeamID, - HomeKitImage: e.HomeTeamImage, - AwayKitImage: e.AwayTeamImage, - LeagueID: e.LeagueID, - LeagueName: e.LeagueName, - StartTime: pgtype.Timestamp{Time: e.StartTime, Valid: true}, - IsLive: e.IsLive, - Status: string(e.Status), - Source: string(e.Source), - DefaultWinningUpperLimit: e.DefaultWinningUpperLimit, - } -} - - -func ConvertEventRes(event BaseEvent) BaseEventRes { - return BaseEventRes{ - ID: event.ID, - SourceEventID: event.SourceEventID, - SportID: event.SportID, - MatchName: event.MatchName, - HomeTeam: event.HomeTeam, - AwayTeam: event.AwayTeam, - HomeTeamID: event.HomeTeamID, - AwayTeamID: event.AwayTeamID, - HomeTeamImage: event.HomeTeamImage, - AwayTeamImage: event.AwayTeamImage, - LeagueID: event.LeagueID, - LeagueName: event.LeagueName, - LeagueCC: event.LeagueCC.Value, - StartTime: event.StartTime.UTC(), - Source: EventSource(event.Source), - Status: EventStatus(event.Status), - DefaultIsFeatured: event.DefaultIsFeatured, - IsMonitored: event.IsMonitored, - DefaultIsActive: event.DefaultIsActive, - DefaultWinningUpperLimit: event.DefaultWinningUpperLimit, - Score: event.Score.Value, - MatchMinute: event.MatchMinute.Value, - TimerStatus: event.TimerStatus.Value, - AddedTime: event.AddedTime.Value, - MatchPeriod: event.MatchPeriod.Value, - IsLive: event.IsLive, - FetchedAt: event.FetchedAt.UTC(), - TotalOddOutcomes: event.TotalOddOutcomes, - NumberOfBets: event.NumberOfBets, - TotalAmount: event.TotalAmount.Float32(), - AvgBetAmount: event.AvgBetAmount.Float32(), - TotalPotentialWinnings: event.TotalPotentialWinnings.Float32(), - } -} -func ConvertEventResList(events []BaseEvent) []BaseEventRes { - result := make([]BaseEventRes, 0, len(events)) - for _, event := range events { - result = append(result, ConvertEventRes(event)) - } - return result -} - diff --git a/internal/domain/event_history.go b/internal/domain/event_history.go deleted file mode 100644 index 4574014..0000000 --- a/internal/domain/event_history.go +++ /dev/null @@ -1,41 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type EventHistory struct { - ID int64 - EventID int64 - Status string - CreatedAt time.Time -} - -type CreateEventHistory struct { - EventID int64 - Status string -} - -type EventHistoryFilter struct { - EventID ValidInt64 - CreatedBefore ValidTime - CreatedAfter ValidTime -} - -func ConvertCreateEventHistory(eventHistory CreateEventHistory) dbgen.InsertEventHistoryParams { - return dbgen.InsertEventHistoryParams{ - EventID: eventHistory.EventID, - Status: eventHistory.Status, - } -} - -func ConvertDBEventHistory(eventHistory dbgen.EventHistory) EventHistory { - return EventHistory{ - ID: eventHistory.ID, - EventID: eventHistory.EventID, - Status: eventHistory.Status, - CreatedAt: eventHistory.CreatedAt.Time, - } -} diff --git a/internal/domain/event_source.go b/internal/domain/event_source.go deleted file mode 100644 index c13fbda..0000000 --- a/internal/domain/event_source.go +++ /dev/null @@ -1,51 +0,0 @@ -package domain - -import ( - "fmt" - - "github.com/jackc/pgx/v5/pgtype" -) - -type EventSource string - -const ( - EVENT_SOURCE_BET365 EventSource = "b365api" - EVENT_SOURCE_BWIN EventSource = "bwin" - EVENT_SOURCE_BETFAIR EventSource = "bfair" - EVENT_SOURCE_1XBET EventSource = "1xbet" - EVENT_SOURCE_ENET EventSource = "enetpulse" -) - -// --- EventSource Validation --- -func (s EventSource) IsValid() bool { - switch s { - case EVENT_SOURCE_BET365, - EVENT_SOURCE_BWIN, - EVENT_SOURCE_BETFAIR, - EVENT_SOURCE_1XBET, - EVENT_SOURCE_ENET: - return true - default: - return false - } -} - -func ParseEventSource(val string) (EventSource, error) { - s := EventSource(val) - if !s.IsValid() { - return "", fmt.Errorf("invalid EventSource: %q", val) - } - return s, nil -} - -type ValidEventSource struct { - Value EventSource - Valid bool -} - -func (v ValidEventSource) ToPG() pgtype.Text { - return pgtype.Text{ - String: string(v.Value), - Valid: v.Valid, - } -} diff --git a/internal/domain/event_stats.go b/internal/domain/event_stats.go deleted file mode 100644 index bf7aba2..0000000 --- a/internal/domain/event_stats.go +++ /dev/null @@ -1,116 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type EventStats struct { - EventCount int64 `json:"event_count"` - TotalActiveEvents int64 `json:"total_active_events"` - TotalInActiveEvents int64 `json:"total_inactive_events"` - TotalFeaturedEvents int64 `json:"total_featured_events"` - TotalLeagues int64 `json:"total_leagues"` - Pending int64 `json:"pending"` - InPlay int64 `json:"in_play"` - ToBeFixed int64 `json:"to_be_fixed"` - Ended int64 `json:"ended"` - Postponed int64 `json:"postponed"` - Cancelled int64 `json:"cancelled"` - Walkover int64 `json:"walkover"` - Interrupted int64 `json:"interrupted"` - Abandoned int64 `json:"abandoned"` - Retired int64 `json:"retired"` - Suspended int64 `json:"suspended"` - DecidedByFa int64 `json:"decided_by_fa"` - Removed int64 `json:"removed"` -} - -type EventStatsFilter struct { - Interval ValidDateInterval - LeagueID ValidInt64 - SportID ValidInt32 -} -type EventStatsByIntervalFilter struct { - Interval ValidDateInterval - LeagueID ValidInt64 - SportID ValidInt32 -} - -type EventStatsByInterval struct { - Date time.Time `json:"date"` - EventCount int64 `json:"event_count"` - TotalActiveEvents int64 `json:"total_active_events"` - TotalInActiveEvents int64 `json:"total_inactive_events"` - TotalFeaturedEvents int64 `json:"total_featured_events"` - TotalLeagues int64 `json:"total_leagues"` - Pending int64 `json:"pending"` - InPlay int64 `json:"in_play"` - ToBeFixed int64 `json:"to_be_fixed"` - Ended int64 `json:"ended"` - Postponed int64 `json:"postponed"` - Cancelled int64 `json:"cancelled"` - Walkover int64 `json:"walkover"` - Interrupted int64 `json:"interrupted"` - Abandoned int64 `json:"abandoned"` - Retired int64 `json:"retired"` - Suspended int64 `json:"suspended"` - DecidedByFa int64 `json:"decided_by_fa"` - Removed int64 `json:"removed"` -} - -func ConvertDBEventStats(stats dbgen.GetTotalEventStatsRow) EventStats { - return EventStats{ - EventCount: stats.EventCount, - TotalActiveEvents: stats.TotalActiveEvents, - TotalInActiveEvents: stats.TotalInactiveEvents, - TotalFeaturedEvents: stats.TotalFeaturedEvents, - TotalLeagues: stats.TotalLeagues, - Pending: stats.Pending, - InPlay: stats.InPlay, - ToBeFixed: stats.ToBeFixed, - Ended: stats.Ended, - Postponed: stats.Postponed, - Cancelled: stats.Cancelled, - Walkover: stats.Walkover, - Interrupted: stats.Interrupted, - Abandoned: stats.Abandoned, - Retired: stats.Retired, - Suspended: stats.Suspended, - DecidedByFa: stats.DecidedByFa, - Removed: stats.Removed, - } -} - -func ConvertDBEventStatsByInterval(stats dbgen.GetTotalEventStatsByIntervalRow) EventStatsByInterval { - return EventStatsByInterval{ - Date: stats.Date.Time, - EventCount: stats.EventCount, - TotalActiveEvents: stats.TotalActiveEvents, - TotalInActiveEvents: stats.TotalInactiveEvents, - TotalFeaturedEvents: stats.TotalFeaturedEvents, - TotalLeagues: stats.TotalLeagues, - Pending: stats.Pending, - InPlay: stats.InPlay, - ToBeFixed: stats.ToBeFixed, - Ended: stats.Ended, - Postponed: stats.Postponed, - Cancelled: stats.Cancelled, - Walkover: stats.Walkover, - Interrupted: stats.Interrupted, - Abandoned: stats.Abandoned, - Retired: stats.Retired, - Suspended: stats.Suspended, - DecidedByFa: stats.DecidedByFa, - Removed: stats.Removed, - } -} - -func ConvertDBEventStatsByIntervalList(stats []dbgen.GetTotalEventStatsByIntervalRow) []EventStatsByInterval { - result := make([]EventStatsByInterval, len(stats)) - for i, e := range stats { - result[i] = ConvertDBEventStatsByInterval(e) - } - return result -} diff --git a/internal/domain/event_status.go b/internal/domain/event_status.go deleted file mode 100644 index 38d74da..0000000 --- a/internal/domain/event_status.go +++ /dev/null @@ -1,69 +0,0 @@ -package domain - -import ( - "fmt" - - "github.com/jackc/pgx/v5/pgtype" -) - -type EventStatus string - -const ( - STATUS_PENDING EventStatus = "upcoming" - STATUS_IN_PLAY EventStatus = "in_play" - STATUS_TO_BE_FIXED EventStatus = "to_be_fixed" - STATUS_ENDED EventStatus = "ended" - STATUS_POSTPONED EventStatus = "postponed" - STATUS_CANCELLED EventStatus = "cancelled" - STATUS_WALKOVER EventStatus = "walkover" - STATUS_INTERRUPTED EventStatus = "interrupted" - STATUS_ABANDONED EventStatus = "abandoned" - STATUS_RETIRED EventStatus = "retired" - STATUS_SUSPENDED EventStatus = "suspended" - STATUS_DECIDED_BY_FA EventStatus = "decided_by_fa" - STATUS_REMOVED EventStatus = "removed" -) - -func (s EventStatus) IsValid() bool { - switch s { - case STATUS_PENDING, - STATUS_IN_PLAY, - STATUS_TO_BE_FIXED, - STATUS_ENDED, - STATUS_POSTPONED, - STATUS_CANCELLED, - STATUS_WALKOVER, - STATUS_INTERRUPTED, - STATUS_ABANDONED, - STATUS_RETIRED, - STATUS_SUSPENDED, - STATUS_DECIDED_BY_FA, - STATUS_REMOVED: - return true - default: - return false - } -} - -func ParseEventStatus(val string) (EventStatus, error) { - s := EventStatus(val) - if !s.IsValid() { - return "", fmt.Errorf("invalid EventStatus: %q", val) - } - return s, nil -} - - - -type ValidEventStatus struct { - Value EventStatus - Valid bool -} - -func (v ValidEventStatus) ToPG() pgtype.Text { - return pgtype.Text{ - String: string(v.Value), - Valid: v.Valid, - } -} - diff --git a/internal/domain/event_with_settings.go b/internal/domain/event_with_settings.go deleted file mode 100644 index a441856..0000000 --- a/internal/domain/event_with_settings.go +++ /dev/null @@ -1,252 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type EventWithSettings struct { - ID int64 - SourceEventID string - SportID int32 - MatchName string - HomeTeam string - AwayTeam string - HomeTeamID int64 - AwayTeamID int64 - HomeTeamImage string - AwayTeamImage string - LeagueID int64 - LeagueName string - LeagueCC ValidString - StartTime time.Time - Source EventSource - Status EventStatus - IsMonitored bool - IsFeatured bool - IsActive bool - WinningUpperLimit int64 - DefaultIsFeatured bool - DefaultIsActive bool - DefaultWinningUpperLimit int64 - Score ValidString - MatchMinute ValidInt - TimerStatus ValidString - AddedTime ValidInt - MatchPeriod ValidInt - IsLive bool - UpdatedAt time.Time - FetchedAt time.Time - TotalOddOutcomes int64 - NumberOfBets int64 - TotalAmount Currency - AvgBetAmount Currency - TotalPotentialWinnings Currency -} - -type EventWithSettingsRes struct { - ID int64 `json:"id"` - SourceEventID string `json:"source_event_id"` - SportID int32 `json:"sport_id"` - MatchName string `json:"match_name"` - HomeTeam string `json:"home_team"` - AwayTeam string `json:"away_team"` - HomeTeamID int64 `json:"home_team_id"` - AwayTeamID int64 `json:"away_team_id"` - HomeTeamImage string `json:"home_team_image"` - AwayTeamImage string `json:"away_team_image"` - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - LeagueCC string `json:"league_cc"` - StartTime time.Time `json:"start_time"` - Source EventSource `json:"source"` - Status EventStatus `json:"status"` - IsMonitored bool `json:"is_monitored"` - IsFeatured bool `json:"is_featured"` - IsActive bool `json:"is_active"` - WinningUpperLimit int64 `json:"winning_upper_limit"` - DefaultIsFeatured bool `json:"default_is_featured"` - DefaultIsActive bool `json:"default_is_active"` - DefaultWinningUpperLimit int64 `json:"default_winning_upper_limit"` - Score string `json:"score,omitempty"` - MatchMinute int `json:"match_minute,omitempty"` - TimerStatus string `json:"timer_status,omitempty"` - AddedTime int `json:"added_time,omitempty"` - MatchPeriod int `json:"match_period,omitempty"` - IsLive bool `json:"is_live"` - UpdatedAt time.Time `json:"updated_at"` - FetchedAt time.Time `json:"fetched_at"` - TotalOddOutcomes int64 `json:"total_odd_outcomes"` - NumberOfBets int64 `json:"number_of_bets"` - TotalAmount float32 `json:"total_amount"` - AvgBetAmount float32 `json:"average_bet_amount"` - TotalPotentialWinnings float32 `json:"total_potential_winnings"` -} - -type EventSettings struct { - CompanyID int64 - EventID int64 - IsActive ValidBool - IsFeatured ValidBool - WinningUpperLimit ValidInt - UpdatedAt time.Time -} - -type UpdateTenantEventSettings struct { - CompanyID int64 - EventID int64 - IsActive ValidBool - IsFeatured ValidBool - WinningUpperLimit ValidInt64 -} -type UpdateGlobalEventSettings struct { - EventID int64 - IsActive ValidBool - IsFeatured ValidBool - WinningUpperLimit ValidInt64 -} - -func ConvertCreateEventSettings(eventSettings UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams { - return dbgen.SaveTenantEventSettingsParams{ - CompanyID: eventSettings.CompanyID, - EventID: eventSettings.EventID, - IsActive: eventSettings.IsActive.ToPG(), - IsFeatured: eventSettings.IsFeatured.ToPG(), - WinningUpperLimit: eventSettings.WinningUpperLimit.ToPG(), - } -} - -func ConvertDBEventWithSetting(event dbgen.EventWithSetting) EventWithSettings { - return EventWithSettings{ - ID: event.ID, - SourceEventID: event.SourceEventID, - WinningUpperLimit: event.WinningUpperLimit, - SportID: event.SportID, - MatchName: event.MatchName, - HomeTeam: event.HomeTeam, - AwayTeam: event.AwayTeam, - HomeTeamID: event.HomeTeamID, - AwayTeamID: event.AwayTeamID, - HomeTeamImage: event.HomeKitImage, - AwayTeamImage: event.AwayKitImage, - LeagueID: event.LeagueID, - LeagueName: event.LeagueName, - LeagueCC: ValidString{ - Value: event.LeagueCc.String, - Valid: event.LeagueCc.Valid, - }, - StartTime: event.StartTime.Time.UTC(), - Source: EventSource(event.Source), - Status: EventStatus(event.Status), - IsFeatured: event.IsFeatured, - IsMonitored: event.IsMonitored, - IsActive: event.IsActive, - DefaultIsFeatured: event.DefaultIsFeatured, - DefaultIsActive: event.DefaultIsActive, - DefaultWinningUpperLimit: event.DefaultWinningUpperLimit, - Score: ValidString{ - Value: event.Score.String, - Valid: event.Score.Valid, - }, - MatchMinute: ValidInt{ - Value: int(event.MatchMinute.Int32), - Valid: event.MatchMinute.Valid, - }, - TimerStatus: ValidString{ - Value: event.TimerStatus.String, - Valid: event.TimerStatus.Valid, - }, - AddedTime: ValidInt{ - Value: int(event.AddedTime.Int32), - Valid: event.AddedTime.Valid, - }, - MatchPeriod: ValidInt{ - Value: int(event.MatchPeriod.Int32), - Valid: event.MatchPeriod.Valid, - }, - IsLive: event.IsLive, - UpdatedAt: event.UpdatedAt.Time, - FetchedAt: event.FetchedAt.Time, - TotalOddOutcomes: event.TotalOutcomes, - NumberOfBets: event.NumberOfBets, - TotalAmount: Currency(event.TotalAmount), - AvgBetAmount: Currency(event.AvgBetAmount), - TotalPotentialWinnings: Currency(event.TotalPotentialWinnings), - } -} - -func ConvertDBEventWithSettings(events []dbgen.EventWithSetting) []EventWithSettings { - result := make([]EventWithSettings, len(events)) - for i, e := range events { - result[i] = ConvertDBEventWithSetting(e) - } - return result -} - -func ConvertUpdateTenantEventSettings(event UpdateTenantEventSettings) dbgen.SaveTenantEventSettingsParams { - return dbgen.SaveTenantEventSettingsParams{ - EventID: event.EventID, - CompanyID: event.CompanyID, - IsActive: event.IsActive.ToPG(), - IsFeatured: event.IsFeatured.ToPG(), - WinningUpperLimit: event.WinningUpperLimit.ToPG(), - } -} -func ConvertUpdateGlobalEventSettings(event UpdateGlobalEventSettings) dbgen.UpdateGlobalEventSettingsParams { - return dbgen.UpdateGlobalEventSettingsParams{ - ID: event.EventID, - DefaultIsActive: event.IsActive.ToPG(), - DefaultIsFeatured: event.IsFeatured.ToPG(), - DefaultWinningUpperLimit: event.WinningUpperLimit.ToPG(), - } -} - -func ConvertEventWitSettingRes(event EventWithSettings) EventWithSettingsRes { - return EventWithSettingsRes{ - ID: event.ID, - SourceEventID: event.SourceEventID, - SportID: event.SportID, - MatchName: event.MatchName, - HomeTeam: event.HomeTeam, - AwayTeam: event.AwayTeam, - HomeTeamID: event.HomeTeamID, - AwayTeamID: event.AwayTeamID, - HomeTeamImage: event.HomeTeamImage, - AwayTeamImage: event.AwayTeamImage, - LeagueID: event.LeagueID, - LeagueName: event.LeagueName, - LeagueCC: event.LeagueCC.Value, - StartTime: event.StartTime.UTC(), - Source: EventSource(event.Source), - Status: EventStatus(event.Status), - IsFeatured: event.IsFeatured, - IsMonitored: event.IsMonitored, - IsActive: event.IsActive, - DefaultIsFeatured: event.DefaultIsFeatured, - DefaultIsActive: event.DefaultIsActive, - DefaultWinningUpperLimit: event.DefaultWinningUpperLimit, - WinningUpperLimit: event.WinningUpperLimit, - Score: event.Score.Value, - MatchMinute: event.MatchMinute.Value, - TimerStatus: event.TimerStatus.Value, - AddedTime: event.AddedTime.Value, - MatchPeriod: event.MatchPeriod.Value, - IsLive: event.IsLive, - FetchedAt: event.FetchedAt.UTC(), - UpdatedAt: event.UpdatedAt, - TotalOddOutcomes: event.TotalOddOutcomes, - NumberOfBets: event.NumberOfBets, - TotalAmount: event.TotalAmount.Float32(), - AvgBetAmount: event.AvgBetAmount.Float32(), - TotalPotentialWinnings: event.TotalPotentialWinnings.Float32(), - } -} - -func ConvertEventWithSettingResList(events []EventWithSettings) []EventWithSettingsRes { - result := make([]EventWithSettingsRes, 0, len(events)) - for _, event := range events { - result = append(result, ConvertEventWitSettingRes(event)) - } - return result -} diff --git a/internal/domain/eventres.go b/internal/domain/eventres.go deleted file mode 100644 index 89365a9..0000000 --- a/internal/domain/eventres.go +++ /dev/null @@ -1,35 +0,0 @@ -package domain - -type MatchResult struct { - EventID string - FullScore string - HalfScore string - Status string - Scores map[string]map[string]string -} - -type B365UpcomingRes struct { - Success int `json:"success"` - Pager struct { - Page int `json:"page"` - PerPage int `json:"per_page"` - Total int `json:"total"` - } - Results []struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - League struct { - ID string `json:"id"` - Name string `json:"name"` - } `json:"league"` - Home struct { - ID string `json:"id"` - Name string `json:"name"` - } `json:"home"` - Away *struct { - ID string `json:"id"` - Name string `json:"name"` - } `json:"away"` - } `json:"results"` -} \ No newline at end of file diff --git a/internal/domain/featured_leagues.go b/internal/domain/featured_leagues.go deleted file mode 100644 index 8afdce1..0000000 --- a/internal/domain/featured_leagues.go +++ /dev/null @@ -1,63 +0,0 @@ -package domain - -// These leagues are automatically featured when the league is created -var FeaturedLeagues = []int64{ - // Football - 10044469, // Ethiopian Premier League - 10041282, //Premier League - 10083364, //La Liga - 10041095, //German Bundesliga - 10041100, //Ligue 1 - 10041809, //UEFA Champions League - 10041957, //UEFA Europa League - 10079560, //UEFA Conference League - 10050282, //UEFA Nations League - 10044685, //FIFA Club World Cup - 10050346, //UEFA Super Cup - 10081269, //CONCACAF Champions Cup - 10070189, //CONCACAF Gold Cup - 10076185, //UEFA Regions Cup - - 10067913, //Europe - World Cup Qualifying - 10040162, //Asia - World Cup Qualifying - 10067624, //South America - World Cup Qualifying - 10073057, //North & Central America - World Cup Qualifying - - 10037075, //International Match - 10077480, //Women’s International - 10037109, //Europe Friendlies - 10068837, //Euro U21 - - 10041315, //Italian Serie A - 10036538, //Spain Segunda - 10047168, // US MLS - - 10043156, //England FA Cup - 10042103, //France Cup - 10041088, //Premier League 2 - 10084250, //Turkiye Super League - 10041187, //Kenya Super League - 10041391, //Netherlands Eredivisie - - // Basketball - 10041830, //NBA - 10049984, //WNBA - 10037165, //German Bundesliga - 10036608, //Italian Lega 1 - 10040795, //EuroLeague - 10041534, //Basketball Africa League - - // Ice Hockey - 10037477, //NHL - 10037447, //AHL - 10069385, //IIHF World Championship - - // AMERICAN FOOTBALL - 10037219, //NFL - - // BASEBALL - 10037485, // MLB - - // VOLLEYBALL - 10069666, //FIVB Nations League -} diff --git a/internal/domain/league.go b/internal/domain/league.go deleted file mode 100644 index c2de5d1..0000000 --- a/internal/domain/league.go +++ /dev/null @@ -1,252 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -// The ID and the Bet365 IDs we have here are both gotten from the betapi -type LeagueWithSettings struct { - ID int64 - Name string - CompanyID int64 - CountryCode ValidString - Bet365ID ValidInt32 - SportID int32 - IsActive bool - IsFeatured bool - DefaultIsActive bool - DefaultIsFeatured bool - UpdatedAt time.Time -} -type LeagueWithSettingsRes struct { - ID int64 `json:"id" example:"1"` - Name string `json:"name" example:"BPL"` - CompanyID int64 `json:"company_id" example:"1"` - CountryCode string `json:"cc" example:"uk"` - Bet365ID int32 `json:"bet365_id" example:"1121"` - IsActive bool `json:"is_active" example:"false"` - SportID int32 `json:"sport_id" example:"1"` - IsFeatured bool `json:"is_featured" example:"false"` - DefaultIsActive bool `json:"default_is_active" example:"false"` - DefaultIsFeatured bool `json:"default_is_featured" example:"false"` - UpdatedAt time.Time `json:"updated_at"` -} -type BaseLeague struct { - ID int64 - Name string - CountryCode ValidString - Bet365ID ValidInt32 - SportID int32 - DefaultIsActive bool - DefaultIsFeatured bool -} - -type BaseLeagueRes struct { - ID int64 `json:"id" example:"1"` - Name string `json:"name" example:"BPL"` - CountryCode string `json:"cc" example:"uk"` - Bet365ID int32 `json:"bet365_id" example:"1121"` - SportID int32 `json:"sport_id" example:"1"` - DefaultIsActive bool `json:"default_is_active" example:"false"` - DefaultIsFeatured bool `json:"default_is_featured" example:"false"` -} - -type CreateLeague struct { - ID int64 - Name string - CountryCode ValidString - Bet365ID ValidInt32 - SportID int32 - DefaultIsActive bool - DefaultIsFeatured bool -} - -type LeagueSettings struct { - CompanyID int64 - LeagueID int64 - IsActive ValidBool - IsFeatured ValidBool - UpdatedAt time.Time -} -type CreateLeagueSettings struct { - CompanyID int64 - LeagueID int64 - IsActive ValidBool - IsFeatured ValidBool -} - -type UpdateLeague struct { - ID int64 `json:"id" example:"1"` - Name ValidString `json:"name" example:"BPL"` - CountryCode ValidString `json:"cc" example:"uk"` - Bet365ID ValidInt32 `json:"bet365_id" example:"1121"` - IsActive ValidBool `json:"is_active" example:"false"` - IsFeatured ValidBool `json:"is_featured" example:"false"` - SportID ValidInt32 `json:"sport_id" example:"1"` -} - -type UpdateLeagueSettingsReq struct { - IsFeatured *bool `json:"is_featured" example:"true"` - IsActive *bool `json:"is_active" example:"true"` -} - -type UpdateGlobalLeagueSettings struct { - ID int64 - DefaultIsActive ValidBool - DefaultIsFeatured ValidBool -} - -type LeagueFilter struct { - Query ValidString - CountryCode ValidString - SportID ValidInt32 - IsActive ValidBool - IsFeatured ValidBool - Limit ValidInt64 - Offset ValidInt64 -} - -func ConvertCreateLeague(league CreateLeague) dbgen.InsertLeagueParams { - return dbgen.InsertLeagueParams{ - ID: league.ID, - Name: league.Name, - CountryCode: league.CountryCode.ToPG(), - Bet365ID: league.Bet365ID.ToPG(), - SportID: league.SportID, - DefaultIsActive: league.DefaultIsActive, - DefaultIsFeatured: league.DefaultIsFeatured, - } -} - -func ConvertCreateLeagueSettings(leagueSetting CreateLeagueSettings) dbgen.SaveLeagueSettingsParams { - return dbgen.SaveLeagueSettingsParams{ - CompanyID: leagueSetting.CompanyID, - LeagueID: leagueSetting.LeagueID, - IsActive: leagueSetting.IsActive.ToPG(), - IsFeatured: leagueSetting.IsFeatured.ToPG(), - } -} - -func ConvertDBBaseLeague(league dbgen.League) BaseLeague { - return BaseLeague{ - ID: league.ID, - Name: league.Name, - CountryCode: ValidString{ - Value: league.CountryCode.String, - Valid: league.CountryCode.Valid, - }, - Bet365ID: ValidInt32{ - Value: league.Bet365ID.Int32, - Valid: league.Bet365ID.Valid, - }, - SportID: league.SportID, - DefaultIsActive: league.DefaultIsActive, - DefaultIsFeatured: league.DefaultIsFeatured, - } -} - -func ConvertDBBaseLeagues(leagues []dbgen.League) []BaseLeague { - result := make([]BaseLeague, len(leagues)) - for i, league := range leagues { - result[i] = ConvertDBBaseLeague(league) - } - return result -} - -func ConvertDBLeagueWithSetting(lws dbgen.GetAllLeaguesWithSettingsRow) LeagueWithSettings { - return LeagueWithSettings{ - ID: lws.ID, - Name: lws.Name, - CompanyID: lws.CompanyID.Int64, - CountryCode: ValidString{ - Value: lws.CountryCode.String, - Valid: lws.CountryCode.Valid, - }, - Bet365ID: ValidInt32{ - Value: lws.Bet365ID.Int32, - Valid: lws.Bet365ID.Valid, - }, - IsActive: lws.IsActive, - SportID: lws.SportID, - IsFeatured: lws.IsFeatured, - UpdatedAt: lws.UpdatedAt.Time, - - DefaultIsActive: lws.DefaultIsActive, - DefaultIsFeatured: lws.DefaultIsFeatured, - } -} - -func ConvertDBLeagueWithSettings(lws []dbgen.GetAllLeaguesWithSettingsRow) []LeagueWithSettings { - result := make([]LeagueWithSettings, len(lws)) - for i, league := range lws { - result[i] = ConvertDBLeagueWithSetting(league) - } - return result -} - -func ConvertUpdateLeague(updateLeague UpdateLeague) dbgen.UpdateLeagueParams { - return dbgen.UpdateLeagueParams{ - ID: updateLeague.ID, - Name: updateLeague.Name.ToPG(), - CountryCode: updateLeague.CountryCode.ToPG(), - Bet365ID: updateLeague.Bet365ID.ToPG(), - SportID: updateLeague.SportID.ToPG(), - } -} - -func ConvertLeagueWithSettingRes(lws LeagueWithSettings) LeagueWithSettingsRes { - return LeagueWithSettingsRes{ - ID: lws.ID, - Name: lws.Name, - CompanyID: lws.CompanyID, - CountryCode: lws.CountryCode.Value, - Bet365ID: lws.Bet365ID.Value, - IsActive: lws.IsActive, - SportID: lws.SportID, - IsFeatured: lws.IsFeatured, - UpdatedAt: lws.UpdatedAt, - DefaultIsActive: lws.DefaultIsActive, - DefaultIsFeatured: lws.DefaultIsFeatured, - } -} - -func ConvertLeagueWithSettingResList(leagues []LeagueWithSettings) []LeagueWithSettingsRes { - result := make([]LeagueWithSettingsRes, len(leagues)) - - for i, lws := range leagues { - result[i] = ConvertLeagueWithSettingRes(lws) - } - - return result -} - -func ConvertBaseLeagueRes(league BaseLeague) BaseLeagueRes { - return BaseLeagueRes{ - ID: league.ID, - Name: league.Name, - CountryCode: league.CountryCode.Value, - Bet365ID: league.Bet365ID.Value, - SportID: league.SportID, - DefaultIsActive: league.DefaultIsActive, - DefaultIsFeatured: league.DefaultIsFeatured, - } -} - -func ConvertBaseLeagueResList(leagues []BaseLeague) []BaseLeagueRes { - result := make([]BaseLeagueRes, len(leagues)) - for i, league := range leagues { - result[i] = ConvertBaseLeagueRes(league) - } - - return result -} - -func ConvertUpdateGlobalLeagueSetting(league UpdateGlobalLeagueSettings) dbgen.UpdateGlobalLeagueSettingsParams { - return dbgen.UpdateGlobalLeagueSettingsParams{ - ID: league.ID, - IsActive: league.DefaultIsActive.ToPG(), - IsFeatured: league.DefaultIsFeatured.ToPG(), - } -} diff --git a/internal/domain/market_settings.go b/internal/domain/market_settings.go deleted file mode 100644 index 62f8064..0000000 --- a/internal/domain/market_settings.go +++ /dev/null @@ -1,169 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type MarketSettings struct { - MarketID int64 - MarketName string - IsActive bool - UpdatedAt time.Time -} -type MarketSettingsRes struct { - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - IsActive bool `json:"is_active"` - UpdatedAt time.Time `json:"updated_at"` -} - -type CreateGlobalMarketSettings struct { - MarketID int64 - MarketName string - IsActive bool -} - -type CreateCompanyMarketSettings struct { - CompanyID int64 - MarketID int64 - MarketName string - IsActive ValidBool -} -type CreateCompanyMarketSettingsReq struct { - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - IsActive *bool `json:"is_active,omitempty"` -} - -type CompanyMarketSettings struct { - CompanyID int64 - MarketID int64 - MarketName string - IsActive ValidBool - UpdatedAt time.Time -} - -type CompanyMarketSettingsRes struct { - CompanyID int64 `json:"company_id"` - MarketID int64 `json:"market_id"` - MarketName string `json:"market_name"` - IsActive bool `json:"is_active"` - UpdatedAt time.Time `json:"updated_at"` -} - -type MarketSettingFilter struct { - Limit ValidInt32 - Offset ValidInt32 -} -type CompanyMarketSettingFilter struct { - Limit ValidInt32 - Offset ValidInt32 - CompanyID ValidInt64 -} - -func ConvertMarketSettingsRes(setting MarketSettings) MarketSettingsRes { - return MarketSettingsRes{ - MarketID: setting.MarketID, - MarketName: setting.MarketName, - IsActive: setting.IsActive, - UpdatedAt: setting.UpdatedAt, - } -} - -func ConvertMarketSettingsResList(settings []MarketSettings) []MarketSettingsRes { - result := make([]MarketSettingsRes, len(settings)) - for i, setting := range settings { - result[i] = ConvertMarketSettingsRes(setting) - } - return result -} - -func ConvertCreateCompanyMarketSettingsReq(setting CreateCompanyMarketSettingsReq, companyId int64) CreateCompanyMarketSettings { - return CreateCompanyMarketSettings{ - CompanyID: companyId, - MarketID: setting.MarketID, - MarketName: setting.MarketName, - IsActive: ConvertBoolPtr(setting.IsActive), - } -} - -func ConvertCreateGlobalMarketSettings(setting CreateGlobalMarketSettings) dbgen.InsertGlobalMarketSettingsParams { - return dbgen.InsertGlobalMarketSettingsParams{ - MarketID: setting.MarketID, - MarketName: setting.MarketName, - IsActive: setting.IsActive, - } -} - -func ConvertCreateCompanyMarketSettings(setting CreateCompanyMarketSettings) dbgen.InsertCompanyMarketSettingsParams { - return dbgen.InsertCompanyMarketSettingsParams{ - CompanyID: setting.CompanyID, - MarketID: setting.MarketID, - MarketName: setting.MarketName, - IsActive: setting.IsActive.ToPG(), - } -} - -func ConvertDBGlobalMarketSettings(setting dbgen.GlobalOddMarketSetting) MarketSettings { - return MarketSettings{ - MarketID: setting.MarketID, - MarketName: setting.MarketName, - IsActive: setting.IsActive, - UpdatedAt: setting.UpdatedAt.Time, - } -} - -func ConvertDBCompanyMarketSettingsList(settings []dbgen.CompanyOddMarketSetting) []CompanyMarketSettings { - result := make([]CompanyMarketSettings, len(settings)) - for i, setting := range settings { - result[i] = ConvertDBCompanyMarketSettings(setting) - } - return result -} -func ConvertDBCompanyMarketSettings(setting dbgen.CompanyOddMarketSetting) CompanyMarketSettings { - return CompanyMarketSettings{ - MarketID: setting.MarketID, - MarketName: setting.MarketName, - IsActive: ValidBool{ - Value: setting.IsActive.Bool, - Valid: setting.IsActive.Valid, - }, - UpdatedAt: setting.UpdatedAt.Time, - } -} - -func ConvertDBGlobalMarketSettingsList(settings []dbgen.GlobalOddMarketSetting) []MarketSettings { - result := make([]MarketSettings, len(settings)) - for i, setting := range settings { - result[i] = ConvertDBGlobalMarketSettings(setting) - } - return result -} - -func ConvertDBGetAllOverrideMarketSettings(setting dbgen.GetAllOverrideMarketSettingsRow) MarketSettings { - return MarketSettings{ - MarketID: setting.MarketID, - MarketName: setting.MarketName, - IsActive: setting.IsActive, - UpdatedAt: setting.UpdatedAt.Time, - } -} - -func ConvertDBGetOverrideMarketSettingsByID(setting dbgen.GetOverrideMarketSettingByIDRow) MarketSettings { - return MarketSettings{ - MarketID: setting.MarketID, - MarketName: setting.MarketName, - IsActive: setting.IsActive, - UpdatedAt: setting.UpdatedAt.Time, - } -} - -func ConvertDBGetAllOverrideMarketSettingsList(settings []dbgen.GetAllOverrideMarketSettingsRow) []MarketSettings { - result := make([]MarketSettings, len(settings)) - for i, setting := range settings { - result[i] = ConvertDBGetAllOverrideMarketSettings(setting) - } - return result -} diff --git a/internal/domain/market_string.go b/internal/domain/market_string.go deleted file mode 100644 index 2fa9d4d..0000000 --- a/internal/domain/market_string.go +++ /dev/null @@ -1,407 +0,0 @@ -// Code generated by "stringer -type=Market"; DO NOT EDIT. - -package domain - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[FOOTBALL_FULL_TIME_RESULT-40] - _ = x[FOOTBALL_DOUBLE_CHANCE-10114] - _ = x[FOOTBALL_GOALS_OVER_UNDER-981] - _ = x[FOOTBALL_CORRECT_SCORE-43] - _ = x[FOOTBALL_ASIAN_HANDICAP-938] - _ = x[FOOTBALL_GOAL_LINE-10143] - _ = x[FOOTBALL_FULL_TIME_RESULT_ENHANCED-4001] - _ = x[FOOTBALL_BOTH_TEAMS_TO_SCORE-10150] - _ = x[FOOTBALL_RESULT_BOTH_TEAMS_TO_SCORE-50404] - _ = x[FOOTBALL_MATCH_GOAL_RANGE-177816] - _ = x[FOOTBALL_TEAM_GOAL_RANGE-177817] - _ = x[FOOTBALL_BOTH_TEAMS_TO_RECEIVE_CARDS-50942] - _ = x[FOOTBALL_FIRST_HALF_GOAL_RANGE-177819] - _ = x[FOOTBALL_SECOND_HALF_GOAL_RANGE-177820] - _ = x[FOOTBALL_RESULT_GOAL_RANGE-177821] - _ = x[FOOTBALL_DOUBLE_CHANCE_GOAL_RANGE-177822] - _ = x[FOOTBALL_HALF_TIME_RESULT-1579] - _ = x[FOOTBALL_FIRST_HALF_ASIAN_HANDICAP-50137] - _ = x[FOOTBALL_FIRST_HALF_GOAL_LINE-50136] - _ = x[FOOTBALL_FIRST_TEAM_TO_SCORE-1178] - _ = x[FOOTBALL_GOALS_ODD_EVEN-10111] - _ = x[FOOTBALL_DRAW_NO_BET-10544] - _ = x[FOOTBALL_HALF_TIME_DOUBLE_CHANCE-10257] - _ = x[FOOTBALL_HALF_TIME_RESULT_BOTH_TEAMS_TO_SCORE-50425] - _ = x[FOOTBALL_ALTERNATE_FIRST_HALF_ASIAN_HANDICAP-50265] - _ = x[FOOTBALL_ALTERNATE_FIRST_HALF_GOAL_LINE-50266] - _ = x[FOOTBALL_FIRST_HALF_HANDICAP-50264] - _ = x[FOOTBALL_ALTERNATE_FIRST_HALF_HANDICAP-10207] - _ = x[FOOTBALL_FIRST_HALF_GOAL-10538] - _ = x[FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN-10206] - _ = x[FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN-50433] - _ = x[FOOTBALL_HALF_TIME_CORRECT_SCORE-10540] - _ = x[FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF-50424] - _ = x[FOOTBALL_BOTH_TEAMS_TO_SCORE_SECOND_HALF-50432] - _ = x[FOOTBALL_TO_SCORE_IN_HALF-50419] - _ = x[FOOTBALL_HALF_WITH_MOST_GOALS-10537] - _ = x[FOOTBALL_HOME_TEAM_WITH_HIGHEST_SCORING_HALF-50417] - _ = x[FOOTBALL_AWAY_TEAM_WITH_HIGHEST_SCORING_HALF-50418] - _ = x[FOOTBALL_SECOND_HALF_RESULT-10208] - _ = x[FOOTBALL_SECOND_HALF_GOALS-10209] - _ = x[FOOTBALL_TEN_MINUTE_RESULT-10244] - _ = x[FOOTBALL_FIRST_TEN_MINUTE-10245] - _ = x[FOOTBALL_TEAM_PERFORMANCE-10110] - _ = x[FOOTBALL_TEAM_TOTAL_GOALS-10127] - _ = x[FOOTBALL_ASIAN_TOTAL_CARDS-10166] - _ = x[FOOTBALL_EXACT_TOTAL_GOALS-10203] - _ = x[FOOTBALL_ALTERNATIVE_HANDICAP_RESULT-10204] - _ = x[FOOTBALL_EXACT_FIRST_HALF_GOALS-10205] - _ = x[FOOTBALL_CLEAN_SHEET-10210] - _ = x[FOOTBALL_TEAMS_TO_SCORE-10211] - _ = x[FOOTBALL_TIME_OF_FIRST_TEAM_GOAL-10214] - _ = x[FOOTBALL_FIRST_GOAL_METHOD-10216] - _ = x[FOOTBALL_MULTI_SCORERS-10217] - _ = x[FOOTBALL_OWN_GOAL-10223] - _ = x[FOOTBALL_TO_SCORE_PENALTY-10229] - _ = x[FOOTBALL_TO_MISS_PENALTY-10230] - _ = x[FOOTBALL_ASIAN_HANDICAP_CARDS-10239] - _ = x[FOOTBALL_CARD_HANDICAP-10240] - _ = x[FOOTBALL_ALTERNATIVE_CARD_HANDICAP-10241] - _ = x[FOOTBALL_TEAM_CARDS-10242] - _ = x[FOOTBALL_EXACT_SECOND_HALF_GOALS-10252] - _ = x[FOOTBALL_EARLY_GOAL-10258] - _ = x[FOOTBALL_LATE_GOAL-10259] - _ = x[FOOTBALL_FIRST_MATCH_CORNER-10519] - _ = x[FOOTBALL_LAST_MATCH_CORNER-10520] - _ = x[FOOTBALL_LAST_TEAM_TO_SCORE-10534] - _ = x[FOOTBALL_CORNER_HANDICAP-10535] - _ = x[FOOTBALL_NUMBER_OF_GOALS_IN_MATCH-10536] - _ = x[FOOTBALL_TIME_OF_FIRST_GOAL_BRACKETS-10541] - _ = x[FOOTBALL_CORNER_MATCH_BET-1175] - _ = x[FOOTBALL_MULTI_CORNERS-1181] - _ = x[FOOTBALL_TIME_OF_FIRST_CARD-1183] - _ = x[FOOTBALL_HANDICAP_RESULT-171] - _ = x[FOOTBALL_TOTAL_GOAL_MINUTES-1776] - _ = x[FOOTBALL_PLAYER_TO_SCORE_ASSIST-177704] - _ = x[FOOTBALL_TEAM_TO_GET_MOST-177790] - _ = x[FOOTBALL_GOALSCORER-45] - _ = x[FOOTBALL_FIRST_CARD_RECEIVED-476] - _ = x[FOOTBALL_PLAYER_CARD-50135] - _ = x[FOOTBALL_ALTERNATIVE_ASIAN_HANDICAP-50138] - _ = x[FOOTBALL_ALTERNATIVE_GOAL_LINE-50139] - _ = x[FOOTBALL_HOME_TEAM_ODD_EVEN_GOALS-50406] - _ = x[FOOTBALL_AWAY_TEAM_ODD_EVEN_GOALS-50407] - _ = x[FOOTBALL_HOME_TEAM_EXACT_GOALS-50415] - _ = x[FOOTBALL_AWAY_TEAM_EXACT_GOALS-50416] - _ = x[FOOTBALL_HALF_TIME_RESULT_TOTAL_GOALS-50426] - _ = x[FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF_SECOND_HALF-50435] - _ = x[FOOTBALL_MATCH_SHOTS_ON_TARGET-50527] - _ = x[FOOTBALL_MATCH_SHOTS-50528] - _ = x[FOOTBALL_TEAM_SHOTS_ON_TARGET-50530] - _ = x[FOOTBALL_TEAM_SHOTS-50532] - _ = x[FOOTBALL_GOAL_METHOD-50962] - _ = x[FOOTBALL_WINNING_MARGIN-56] - _ = x[FOOTBALL_TIME_OF_FIRST_CORNER-761] - _ = x[FOOTBALL_TEAM_GOALSCORER-10151] - _ = x[FOOTBALL_PLAYER_SHOTS_ON_TARGET-50920] - _ = x[FOOTBALL_PLAYER_SHOTS-50921] - _ = x[FOOTBALL_SPECIALS-10224] - _ = x[FOOTBALL_CORNERS-760] - _ = x[FOOTBALL_CORNERS_TWO_WAY-10235] - _ = x[FOOTBALL_FIRST_HALF_CORNERS-10539] - _ = x[FOOTBALL_ASIAN_TOTAL_CORNERS-10164] - _ = x[FOOTBALL_FIRST_HALF_ASIAN_CORNERS-10233] - _ = x[FOOTBALL_ASIAN_HANDICAP_CORNERS-10165] - _ = x[FOOTBALL_ALTERNATIVE_CORNER-10234] - _ = x[FOOTBALL_CORNER_RACE-10238] - _ = x[FOOTBALL_NUMBER_OF_CARDS_IN_MATCH-10542] - _ = x[BASKETBALL_GAME_LINES-1453] - _ = x[BASKETBALL_FIRST_HALF-928] - _ = x[BASKETBALL_FIRST_QUARTER-941] - _ = x[BASKETBALL_RESULT_AND_BOTH_TEAMS_TO_SCORE_X_POINTS-181273] - _ = x[BASKETBALL_DOUBLE_RESULT-1517] - _ = x[BASKETBALL_MATCH_RESULT_AND_TOTAL-181125] - _ = x[BASKETBALL_MATCH_HANDICAP_AND_TOTAL-181126] - _ = x[BASKETBALL_RACE_TO_20_POINTS-1503] - _ = x[BASKETBALL_TIED_AT_END_OF_REGULATION-181127] - _ = x[BASKETBALL_QUARTER_CORRECT_SCORE-181276] - _ = x[BASKETBALL_FIRST_HALF_TEAM_TOTALS-181159] - _ = x[BASKETBALL_FIRST_HALF_WINNING_MARGIN-181185] - _ = x[BASKETBALL_FIRST_HALF_RESULT_AND_TOTAL-181181] - _ = x[BASKETBALL_FIRST_HALF_HANDICAP_AND_TOTAL-181182] - _ = x[BASKETBALL_FIRST_HALF_RACE_TO_POINTS-181186] - _ = x[BASKETBALL_FIRST_HALF_BOTH_TEAMS_TO_SCORE_X_POINTS-181195] - _ = x[BASKETBALL_FIRST_HALF_TEAM_TO_SCORE_X_POINTS-181198] - _ = x[BASKETBALL_FIRST_HALF_MONEY_LINE_3_WAY-181183] - _ = x[BASKETBALL_GAME_TOTAL_ODD_EVEN-180013] - _ = x[BASKETBALL_FIRST_QUARTER_TOTAL_ODD_EVEN-180170] - _ = x[BASKETBALL_FIRST_QUARTER_MARGIN_OF_VICTORY-180180] - _ = x[BASKETBALL_HIGHEST_SCORING_HALF-181131] - _ = x[BASKETBALL_HIGHEST_SCORING_QUARTER-181132] - _ = x[BASKETBALL_FIRST_HALF_DOUBLE_CHANCE-181184] - _ = x[BASKETBALL_FIRST_HALF_TOTAL_ODD_EVEN-181204] - _ = x[BASKETBALL_FIRST_QUARTER_3_WAY_LINES-181212] - _ = x[BASKETBALL_FIRST_QUARTER_RESULT_AND_TOTAL-181242] - _ = x[BASKETBALL_FIRST_QUARTER_HANDICAP_AND_TOTAL-181243] - _ = x[BASKETBALL_FIRST_QUARTER_DOUBLE_CHANCE-181245] - _ = x[BASKETBALL_FIRST_QUARTER_RACE_TO_POINTS-181248] - _ = x[BASKETBALL_FIRST_QUARTER_BOTH_TEAMS_TO_SCORE_X_POINTS-181252] - _ = x[BASKETBALL_FIRST_QUARTER_TEAM_TO_SCORE_X_POINTS-181255] - _ = x[BASKETBALL_FIRST_QUARTER_TEAM_TOTALS-181220] - _ = x[BASKETBALL_FIRST_QUARTER_WINNING_MARGIN-181247] - _ = x[BASKETBALL_TEAM_WITH_HIGHEST_SCORING_QUARTER-181377] - _ = x[BASKETBALL_TEAM_TOTALS-181335] - _ = x[BASKETBALL_TEAM_TOTAL_ODD_EVEN-1731] - _ = x[ICE_HOCKEY_GAME_LINES-972] - _ = x[ICE_HOCKEY_FIRST_PERIOD-1531] - _ = x[ICE_HOCKEY_THREE_WAY-170008] - _ = x[ICE_HOCKEY_DRAW_NO_BET-170447] - _ = x[ICE_HOCKEY_DOUBLE_CHANCE-170038] - _ = x[ICE_HOCKEY_WINNING_MARGIN-1556] - _ = x[ICE_HOCKEY_HIGHEST_SCORING_PERIOD-1557] - _ = x[ICE_HOCKEY_TIED_AFTER_REGULATION-170479] - _ = x[ICE_HOCKEY_WHEN_WILL_MATCH_END-170481] - _ = x[ICE_HOCKEY_GAME_TOTAL_ODD_EVEN-170013] - _ = x[ICE_HOCKEY_ALTERNATIVE_PUCK_LINE_TWO_WAY-170226] - _ = x[ICE_HOCKEY_ALTERNATIVE_TOTAL_TWO_WAY-170240] - _ = x[CRICKET_TO_WIN_THE_MATCH-1246] - _ = x[CRICKET_TEAM_TOP_BATTER-1241] - _ = x[CRICKET_TEAM_TOP_BOWLE-1242] - _ = x[CRICKET_PLAYER_OF_THE_MATCH-346] - _ = x[CRICKET_FIRST_WICKET_METHOD-30205] - _ = x[CRICKET_FIRST_OVER_TOTAL_RUNS-300336] - _ = x[CRICKET_FIRST_OVER_TOTAL_RUNS_Odd_Even-300118] - _ = x[CRICKET_FIRST_INNINIGS_SCORE-300338] - _ = x[CRICKET_INNINGS_OF_MATCH_BOWLED_OUT-300108] - _ = x[CRICKET_TOP_MATCH_BATTER-30245] - _ = x[CRICKET_TOP_MATCH_BOWLER-30246] - _ = x[VOLLEYBALL_GAME_LINES-910000] - _ = x[VOLLEYBALL_CORRECT_SET_SCORE-910201] - _ = x[VOLLEYBALL_MATCH_TOTAL_ODD_EVEN-910217] - _ = x[VOLLEYBALL_SET_ONE_LINES-910204] - _ = x[VOLLEYBALL_SET_ONE_TO_GO_TO_EXTRA_POINTS-910209] - _ = x[VOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN-910218] - _ = x[DARTS_MATCH_WINNER-703] - _ = x[DARTS_MATCH_DOUBLE-150228] - _ = x[DARTS_MATCH_TREBLE-150230] - _ = x[DARTS_CORRECT_LEG_SCORE-150015] - _ = x[DARTS_TOTAL_LEGS-150117] - _ = x[DARTS_MOST_HUNDERED_EIGHTYS-150030] - _ = x[DARTS_TOTAL_HUNDERED_EIGHTYS-150012] - _ = x[DARTS_MOST_HUNDERED_EIGHTYS_HANDICAP-150227] - _ = x[DARTS_PLAYER_HUNDERED_EIGHTYS-150121] - _ = x[DARTS_FIRST_DART-150125] - _ = x[FUTSAL_GAME_LINES-830001] - _ = x[FUTSAL_MONEY_LINE-830130] - _ = x[FUTSAL_DOUBLE_RESULT_9_WAY-830124] - _ = x[FUTSAL_TEAM_TO_SCORE_FIRST-830141] - _ = x[FUTSAL_RACE_TO_GOALS-830142] - _ = x[AMERICAN_FOOTBALL_GAME_LINES-1441] - _ = x[RUGBY_L_GAME_BETTING_2_WAY-190006] - _ = x[RUGBY_U_GAME_BETTING_2_WAY-80007] - _ = x[BASEBALL_GAME_LINES-1096] -} - -const _Market_name = "FOOTBALL_FULL_TIME_RESULTFOOTBALL_CORRECT_SCOREFOOTBALL_GOALSCORERFOOTBALL_WINNING_MARGINFOOTBALL_HANDICAP_RESULTCRICKET_PLAYER_OF_THE_MATCHFOOTBALL_FIRST_CARD_RECEIVEDDARTS_MATCH_WINNERFOOTBALL_CORNERSFOOTBALL_TIME_OF_FIRST_CORNERBASKETBALL_FIRST_HALFFOOTBALL_ASIAN_HANDICAPBASKETBALL_FIRST_QUARTERICE_HOCKEY_GAME_LINESFOOTBALL_GOALS_OVER_UNDERBASEBALL_GAME_LINESFOOTBALL_CORNER_MATCH_BETFOOTBALL_FIRST_TEAM_TO_SCOREFOOTBALL_MULTI_CORNERSFOOTBALL_TIME_OF_FIRST_CARDCRICKET_TEAM_TOP_BATTERCRICKET_TEAM_TOP_BOWLECRICKET_TO_WIN_THE_MATCHAMERICAN_FOOTBALL_GAME_LINESBASKETBALL_GAME_LINESBASKETBALL_RACE_TO_20_POINTSBASKETBALL_DOUBLE_RESULTICE_HOCKEY_FIRST_PERIODICE_HOCKEY_WINNING_MARGINICE_HOCKEY_HIGHEST_SCORING_PERIODFOOTBALL_HALF_TIME_RESULTBASKETBALL_TEAM_TOTAL_ODD_EVENFOOTBALL_TOTAL_GOAL_MINUTESFOOTBALL_FULL_TIME_RESULT_ENHANCEDFOOTBALL_TEAM_PERFORMANCEFOOTBALL_GOALS_ODD_EVENFOOTBALL_DOUBLE_CHANCEFOOTBALL_TEAM_TOTAL_GOALSFOOTBALL_GOAL_LINEFOOTBALL_BOTH_TEAMS_TO_SCOREFOOTBALL_TEAM_GOALSCORERFOOTBALL_ASIAN_TOTAL_CORNERSFOOTBALL_ASIAN_HANDICAP_CORNERSFOOTBALL_ASIAN_TOTAL_CARDSFOOTBALL_EXACT_TOTAL_GOALSFOOTBALL_ALTERNATIVE_HANDICAP_RESULTFOOTBALL_EXACT_FIRST_HALF_GOALSFOOTBALL_FIRST_HALF_GOALS_ODD_EVENFOOTBALL_ALTERNATE_FIRST_HALF_HANDICAPFOOTBALL_SECOND_HALF_RESULTFOOTBALL_SECOND_HALF_GOALSFOOTBALL_CLEAN_SHEETFOOTBALL_TEAMS_TO_SCOREFOOTBALL_TIME_OF_FIRST_TEAM_GOALFOOTBALL_FIRST_GOAL_METHODFOOTBALL_MULTI_SCORERSFOOTBALL_OWN_GOALFOOTBALL_SPECIALSFOOTBALL_TO_SCORE_PENALTYFOOTBALL_TO_MISS_PENALTYFOOTBALL_FIRST_HALF_ASIAN_CORNERSFOOTBALL_ALTERNATIVE_CORNERFOOTBALL_CORNERS_TWO_WAYFOOTBALL_CORNER_RACEFOOTBALL_ASIAN_HANDICAP_CARDSFOOTBALL_CARD_HANDICAPFOOTBALL_ALTERNATIVE_CARD_HANDICAPFOOTBALL_TEAM_CARDSFOOTBALL_TEN_MINUTE_RESULTFOOTBALL_FIRST_TEN_MINUTEFOOTBALL_EXACT_SECOND_HALF_GOALSFOOTBALL_HALF_TIME_DOUBLE_CHANCEFOOTBALL_EARLY_GOALFOOTBALL_LATE_GOALFOOTBALL_FIRST_MATCH_CORNERFOOTBALL_LAST_MATCH_CORNERFOOTBALL_LAST_TEAM_TO_SCOREFOOTBALL_CORNER_HANDICAPFOOTBALL_NUMBER_OF_GOALS_IN_MATCHFOOTBALL_HALF_WITH_MOST_GOALSFOOTBALL_FIRST_HALF_GOALFOOTBALL_FIRST_HALF_CORNERSFOOTBALL_HALF_TIME_CORRECT_SCOREFOOTBALL_TIME_OF_FIRST_GOAL_BRACKETSFOOTBALL_NUMBER_OF_CARDS_IN_MATCHFOOTBALL_DRAW_NO_BETCRICKET_FIRST_WICKET_METHODCRICKET_TOP_MATCH_BATTERCRICKET_TOP_MATCH_BOWLERFOOTBALL_PLAYER_CARDFOOTBALL_FIRST_HALF_GOAL_LINEFOOTBALL_FIRST_HALF_ASIAN_HANDICAPFOOTBALL_ALTERNATIVE_ASIAN_HANDICAPFOOTBALL_ALTERNATIVE_GOAL_LINEFOOTBALL_FIRST_HALF_HANDICAPFOOTBALL_ALTERNATE_FIRST_HALF_ASIAN_HANDICAPFOOTBALL_ALTERNATE_FIRST_HALF_GOAL_LINEFOOTBALL_RESULT_BOTH_TEAMS_TO_SCOREFOOTBALL_HOME_TEAM_ODD_EVEN_GOALSFOOTBALL_AWAY_TEAM_ODD_EVEN_GOALSFOOTBALL_HOME_TEAM_EXACT_GOALSFOOTBALL_AWAY_TEAM_EXACT_GOALSFOOTBALL_HOME_TEAM_WITH_HIGHEST_SCORING_HALFFOOTBALL_AWAY_TEAM_WITH_HIGHEST_SCORING_HALFFOOTBALL_TO_SCORE_IN_HALFFOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALFFOOTBALL_HALF_TIME_RESULT_BOTH_TEAMS_TO_SCOREFOOTBALL_HALF_TIME_RESULT_TOTAL_GOALSFOOTBALL_BOTH_TEAMS_TO_SCORE_SECOND_HALFFOOTBALL_SECOND_HALF_GOALS_ODD_EVENFOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF_SECOND_HALFFOOTBALL_MATCH_SHOTS_ON_TARGETFOOTBALL_MATCH_SHOTSFOOTBALL_TEAM_SHOTS_ON_TARGETFOOTBALL_TEAM_SHOTSFOOTBALL_PLAYER_SHOTS_ON_TARGETFOOTBALL_PLAYER_SHOTSFOOTBALL_BOTH_TEAMS_TO_RECEIVE_CARDSFOOTBALL_GOAL_METHODRUGBY_U_GAME_BETTING_2_WAYDARTS_TOTAL_HUNDERED_EIGHTYSDARTS_CORRECT_LEG_SCOREDARTS_MOST_HUNDERED_EIGHTYSDARTS_TOTAL_LEGSDARTS_PLAYER_HUNDERED_EIGHTYSDARTS_FIRST_DARTDARTS_MOST_HUNDERED_EIGHTYS_HANDICAPDARTS_MATCH_DOUBLEDARTS_MATCH_TREBLEICE_HOCKEY_THREE_WAYICE_HOCKEY_GAME_TOTAL_ODD_EVENICE_HOCKEY_DOUBLE_CHANCEICE_HOCKEY_ALTERNATIVE_PUCK_LINE_TWO_WAYICE_HOCKEY_ALTERNATIVE_TOTAL_TWO_WAYICE_HOCKEY_DRAW_NO_BETICE_HOCKEY_TIED_AFTER_REGULATIONICE_HOCKEY_WHEN_WILL_MATCH_ENDFOOTBALL_PLAYER_TO_SCORE_ASSISTFOOTBALL_TEAM_TO_GET_MOSTFOOTBALL_MATCH_GOAL_RANGEFOOTBALL_TEAM_GOAL_RANGEFOOTBALL_FIRST_HALF_GOAL_RANGEFOOTBALL_SECOND_HALF_GOAL_RANGEFOOTBALL_RESULT_GOAL_RANGEFOOTBALL_DOUBLE_CHANCE_GOAL_RANGEBASKETBALL_GAME_TOTAL_ODD_EVENBASKETBALL_FIRST_QUARTER_TOTAL_ODD_EVENBASKETBALL_FIRST_QUARTER_MARGIN_OF_VICTORYBASKETBALL_MATCH_RESULT_AND_TOTALBASKETBALL_MATCH_HANDICAP_AND_TOTALBASKETBALL_TIED_AT_END_OF_REGULATIONBASKETBALL_HIGHEST_SCORING_HALFBASKETBALL_HIGHEST_SCORING_QUARTERBASKETBALL_FIRST_HALF_TEAM_TOTALSBASKETBALL_FIRST_HALF_RESULT_AND_TOTALBASKETBALL_FIRST_HALF_HANDICAP_AND_TOTALBASKETBALL_FIRST_HALF_MONEY_LINE_3_WAYBASKETBALL_FIRST_HALF_DOUBLE_CHANCEBASKETBALL_FIRST_HALF_WINNING_MARGINBASKETBALL_FIRST_HALF_RACE_TO_POINTSBASKETBALL_FIRST_HALF_BOTH_TEAMS_TO_SCORE_X_POINTSBASKETBALL_FIRST_HALF_TEAM_TO_SCORE_X_POINTSBASKETBALL_FIRST_HALF_TOTAL_ODD_EVENBASKETBALL_FIRST_QUARTER_3_WAY_LINESBASKETBALL_FIRST_QUARTER_TEAM_TOTALSBASKETBALL_FIRST_QUARTER_RESULT_AND_TOTALBASKETBALL_FIRST_QUARTER_HANDICAP_AND_TOTALBASKETBALL_FIRST_QUARTER_DOUBLE_CHANCEBASKETBALL_FIRST_QUARTER_WINNING_MARGINBASKETBALL_FIRST_QUARTER_RACE_TO_POINTSBASKETBALL_FIRST_QUARTER_BOTH_TEAMS_TO_SCORE_X_POINTSBASKETBALL_FIRST_QUARTER_TEAM_TO_SCORE_X_POINTSBASKETBALL_RESULT_AND_BOTH_TEAMS_TO_SCORE_X_POINTSBASKETBALL_QUARTER_CORRECT_SCOREBASKETBALL_TEAM_TOTALSBASKETBALL_TEAM_WITH_HIGHEST_SCORING_QUARTERRUGBY_L_GAME_BETTING_2_WAYCRICKET_INNINGS_OF_MATCH_BOWLED_OUTCRICKET_FIRST_OVER_TOTAL_RUNS_Odd_EvenCRICKET_FIRST_OVER_TOTAL_RUNSCRICKET_FIRST_INNINIGS_SCOREFUTSAL_GAME_LINESFUTSAL_DOUBLE_RESULT_9_WAYFUTSAL_MONEY_LINEFUTSAL_TEAM_TO_SCORE_FIRSTFUTSAL_RACE_TO_GOALSVOLLEYBALL_GAME_LINESVOLLEYBALL_CORRECT_SET_SCOREVOLLEYBALL_SET_ONE_LINESVOLLEYBALL_SET_ONE_TO_GO_TO_EXTRA_POINTSVOLLEYBALL_MATCH_TOTAL_ODD_EVENVOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN" - -var _Market_map = map[Market]string{ - 40: _Market_name[0:25], - 43: _Market_name[25:47], - 45: _Market_name[47:66], - 56: _Market_name[66:89], - 171: _Market_name[89:113], - 346: _Market_name[113:140], - 476: _Market_name[140:168], - 703: _Market_name[168:186], - 760: _Market_name[186:202], - 761: _Market_name[202:231], - 928: _Market_name[231:252], - 938: _Market_name[252:275], - 941: _Market_name[275:299], - 972: _Market_name[299:320], - 981: _Market_name[320:345], - 1096: _Market_name[345:364], - 1175: _Market_name[364:389], - 1178: _Market_name[389:417], - 1181: _Market_name[417:439], - 1183: _Market_name[439:466], - 1241: _Market_name[466:489], - 1242: _Market_name[489:511], - 1246: _Market_name[511:535], - 1441: _Market_name[535:563], - 1453: _Market_name[563:584], - 1503: _Market_name[584:612], - 1517: _Market_name[612:636], - 1531: _Market_name[636:659], - 1556: _Market_name[659:684], - 1557: _Market_name[684:717], - 1579: _Market_name[717:742], - 1731: _Market_name[742:772], - 1776: _Market_name[772:799], - 4001: _Market_name[799:833], - 10110: _Market_name[833:858], - 10111: _Market_name[858:881], - 10114: _Market_name[881:903], - 10127: _Market_name[903:928], - 10143: _Market_name[928:946], - 10150: _Market_name[946:974], - 10151: _Market_name[974:998], - 10164: _Market_name[998:1026], - 10165: _Market_name[1026:1057], - 10166: _Market_name[1057:1083], - 10203: _Market_name[1083:1109], - 10204: _Market_name[1109:1145], - 10205: _Market_name[1145:1176], - 10206: _Market_name[1176:1210], - 10207: _Market_name[1210:1248], - 10208: _Market_name[1248:1275], - 10209: _Market_name[1275:1301], - 10210: _Market_name[1301:1321], - 10211: _Market_name[1321:1344], - 10214: _Market_name[1344:1376], - 10216: _Market_name[1376:1402], - 10217: _Market_name[1402:1424], - 10223: _Market_name[1424:1441], - 10224: _Market_name[1441:1458], - 10229: _Market_name[1458:1483], - 10230: _Market_name[1483:1507], - 10233: _Market_name[1507:1540], - 10234: _Market_name[1540:1567], - 10235: _Market_name[1567:1591], - 10238: _Market_name[1591:1611], - 10239: _Market_name[1611:1640], - 10240: _Market_name[1640:1662], - 10241: _Market_name[1662:1696], - 10242: _Market_name[1696:1715], - 10244: _Market_name[1715:1741], - 10245: _Market_name[1741:1766], - 10252: _Market_name[1766:1798], - 10257: _Market_name[1798:1830], - 10258: _Market_name[1830:1849], - 10259: _Market_name[1849:1867], - 10519: _Market_name[1867:1894], - 10520: _Market_name[1894:1920], - 10534: _Market_name[1920:1947], - 10535: _Market_name[1947:1971], - 10536: _Market_name[1971:2004], - 10537: _Market_name[2004:2033], - 10538: _Market_name[2033:2057], - 10539: _Market_name[2057:2084], - 10540: _Market_name[2084:2116], - 10541: _Market_name[2116:2152], - 10542: _Market_name[2152:2185], - 10544: _Market_name[2185:2205], - 30205: _Market_name[2205:2232], - 30245: _Market_name[2232:2256], - 30246: _Market_name[2256:2280], - 50135: _Market_name[2280:2300], - 50136: _Market_name[2300:2329], - 50137: _Market_name[2329:2363], - 50138: _Market_name[2363:2398], - 50139: _Market_name[2398:2428], - 50264: _Market_name[2428:2456], - 50265: _Market_name[2456:2500], - 50266: _Market_name[2500:2539], - 50404: _Market_name[2539:2574], - 50406: _Market_name[2574:2607], - 50407: _Market_name[2607:2640], - 50415: _Market_name[2640:2670], - 50416: _Market_name[2670:2700], - 50417: _Market_name[2700:2744], - 50418: _Market_name[2744:2788], - 50419: _Market_name[2788:2813], - 50424: _Market_name[2813:2852], - 50425: _Market_name[2852:2897], - 50426: _Market_name[2897:2934], - 50432: _Market_name[2934:2974], - 50433: _Market_name[2974:3009], - 50435: _Market_name[3009:3060], - 50527: _Market_name[3060:3090], - 50528: _Market_name[3090:3110], - 50530: _Market_name[3110:3139], - 50532: _Market_name[3139:3158], - 50920: _Market_name[3158:3189], - 50921: _Market_name[3189:3210], - 50942: _Market_name[3210:3246], - 50962: _Market_name[3246:3266], - 80007: _Market_name[3266:3292], - 150012: _Market_name[3292:3320], - 150015: _Market_name[3320:3343], - 150030: _Market_name[3343:3370], - 150117: _Market_name[3370:3386], - 150121: _Market_name[3386:3415], - 150125: _Market_name[3415:3431], - 150227: _Market_name[3431:3467], - 150228: _Market_name[3467:3485], - 150230: _Market_name[3485:3503], - 170008: _Market_name[3503:3523], - 170013: _Market_name[3523:3553], - 170038: _Market_name[3553:3577], - 170226: _Market_name[3577:3617], - 170240: _Market_name[3617:3653], - 170447: _Market_name[3653:3675], - 170479: _Market_name[3675:3707], - 170481: _Market_name[3707:3737], - 177704: _Market_name[3737:3768], - 177790: _Market_name[3768:3793], - 177816: _Market_name[3793:3818], - 177817: _Market_name[3818:3842], - 177819: _Market_name[3842:3872], - 177820: _Market_name[3872:3903], - 177821: _Market_name[3903:3929], - 177822: _Market_name[3929:3962], - 180013: _Market_name[3962:3992], - 180170: _Market_name[3992:4031], - 180180: _Market_name[4031:4073], - 181125: _Market_name[4073:4106], - 181126: _Market_name[4106:4141], - 181127: _Market_name[4141:4177], - 181131: _Market_name[4177:4208], - 181132: _Market_name[4208:4242], - 181159: _Market_name[4242:4275], - 181181: _Market_name[4275:4313], - 181182: _Market_name[4313:4353], - 181183: _Market_name[4353:4391], - 181184: _Market_name[4391:4426], - 181185: _Market_name[4426:4462], - 181186: _Market_name[4462:4498], - 181195: _Market_name[4498:4548], - 181198: _Market_name[4548:4592], - 181204: _Market_name[4592:4628], - 181212: _Market_name[4628:4664], - 181220: _Market_name[4664:4700], - 181242: _Market_name[4700:4741], - 181243: _Market_name[4741:4784], - 181245: _Market_name[4784:4822], - 181247: _Market_name[4822:4861], - 181248: _Market_name[4861:4900], - 181252: _Market_name[4900:4953], - 181255: _Market_name[4953:5000], - 181273: _Market_name[5000:5050], - 181276: _Market_name[5050:5082], - 181335: _Market_name[5082:5104], - 181377: _Market_name[5104:5148], - 190006: _Market_name[5148:5174], - 300108: _Market_name[5174:5209], - 300118: _Market_name[5209:5247], - 300336: _Market_name[5247:5276], - 300338: _Market_name[5276:5304], - 830001: _Market_name[5304:5321], - 830124: _Market_name[5321:5347], - 830130: _Market_name[5347:5364], - 830141: _Market_name[5364:5390], - 830142: _Market_name[5390:5410], - 910000: _Market_name[5410:5431], - 910201: _Market_name[5431:5459], - 910204: _Market_name[5459:5483], - 910209: _Market_name[5483:5523], - 910217: _Market_name[5523:5554], - 910218: _Market_name[5554:5587], -} - -func (i Market) String() string { - if str, ok := _Market_map[i]; ok { - return str - } - return "Market(" + strconv.FormatInt(int64(i), 10) + ")" -} diff --git a/internal/domain/oddres.go b/internal/domain/oddres.go deleted file mode 100644 index 71dfae5..0000000 --- a/internal/domain/oddres.go +++ /dev/null @@ -1,143 +0,0 @@ -package domain - -import "encoding/json" - -type BaseNonLiveOddResponse struct { - Success int `json:"success"` - Results []json.RawMessage `json:"results"` -} - -type OddsSection struct { - UpdatedAt string `json:"updated_at"` - Sp map[string]OddsMarket `json:"sp"` -} - -type ParseOddSectionsRes struct { - Sections map[string]OddsSection - OtherRes []OddsSection - EventFI string -} -type RawOdd struct { - ID string `json:"id"` - Name string `json:"name"` - Header string `json:"header,omitempty"` - Handicap string `json:"handicap,omitempty"` - Odds string `json:"odds"` -} - -// The Market ID for the json data can be either string / int which is causing problems when UnMarshalling -type OddsMarket struct { - ID StringOrNumber `json:"id"` - Name string `json:"name"` - Odds []json.RawMessage `json:"odds"` - Header string `json:"header,omitempty"` - Handicap string `json:"handicap,omitempty"` - Open int64 `json:"open,omitempty"` -} - -type FootballOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - AsianLines OddsSection `json:"asian_lines"` - Cards OddsSection `json:"cards"` - Corners OddsSection `json:"corners"` - Goals OddsSection `json:"goals"` - Half OddsSection `json:"half"` - Main OddsSection `json:"main"` - Minutes OddsSection `json:"minutes"` - Others []OddsSection `json:"others"` - Player OddsSection `json:"player"` - Specials OddsSection `json:"specials"` -} - -type BasketballOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - Main OddsSection `json:"main"` - HalfProps OddsSection `json:"half_props"` - QuarterProps OddsSection `json:"quarter_props"` - TeamProps OddsSection `json:"team_props"` - Others []OddsSection `json:"others"` -} - -type IceHockeyOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - Main OddsSection `json:"main"` - Main2 OddsSection `json:"main_2"` - FirstPeriod OddsSection `json:"1st_period"` - Others []OddsSection `json:"others"` -} -type CricketOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - First_Over OddsSection `json:"1st_over"` - First_Innings OddsSection `json:"innings_1"` - Main OddsSection `json:"main"` - Match OddsSection `json:"match"` - Others []OddsSection `json:"others"` - Player OddsSection `json:"player"` - Team OddsSection `json:"team"` -} -type VolleyballOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - Main OddsSection `json:"main"` - Others []OddsSection `json:"others"` -} -type DartsOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - OneEightys OddsSection `json:"180s"` - Extra OddsSection `json:"extra"` - Leg OddsSection `json:"leg"` - Main OddsSection `json:"main"` - Others []OddsSection `json:"others"` -} - -type FutsalOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - Main OddsSection `json:"main"` - Score OddsSection `json:"score"` - Others []OddsSection `json:"others"` -} - -type AmericanFootballOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - HalfProps OddsSection `json:"half_props"` - Main OddsSection `json:"main"` - QuarterProps OddsSection `json:"quarter_props"` - Others []OddsSection `json:"others"` -} - -type RugbyLeagueOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - TenMinute OddsSection `json:"10minute"` - Half OddsSection `json:"half"` - Main OddsSection `json:"main"` - Main2 OddsSection `json:"main_2"` - Others []OddsSection `json:"others"` - Player OddsSection `json:"player"` - Score OddsSection `json:"score"` - Team OddsSection `json:"team"` -} -type RugbyUnionOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - Half OddsSection `json:"half"` - Main OddsSection `json:"main"` - Main2 OddsSection `json:"main_2"` - Others []OddsSection `json:"others"` - Player OddsSection `json:"player"` - Score OddsSection `json:"score"` - Team OddsSection `json:"team"` -} -type BaseballOddsResponse struct { - EventID string `json:"event_id"` - FI string `json:"FI"` - Main OddsSection `json:"main"` - MainProps OddsSection `json:"main_props"` -} diff --git a/internal/domain/odds.go b/internal/domain/odds.go deleted file mode 100644 index f082fd7..0000000 --- a/internal/domain/odds.go +++ /dev/null @@ -1,215 +0,0 @@ -package domain - -import ( - "encoding/json" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/jackc/pgx/v5/pgtype" -) - -type CreateOddMarket struct { - EventID int64 - MarketCategory string - MarketType string - MarketName string - MarketID int64 - NumberOfOutcomes int64 - UpdatedAt time.Time - Odds []map[string]interface{} -} - -type OddMarket struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - RawOdds []json.RawMessage `json:"raw_odds"` - FetchedAt time.Time `json:"fetched_at"` - ExpiresAt time.Time `json:"expires_at"` - DefaultIsActive bool `json:"is_active"` - IsMonitored bool `json:"is_monitored"` - IsLive bool `json:"is_live"` - Status EventStatus `json:"status"` - Source EventSource `json:"source"` -} -type OddMarketWithSettings struct { - ID int64 `json:"id"` - EventID int64 `json:"event_id"` - MarketType string `json:"market_type"` - MarketName string `json:"market_name"` - MarketCategory string `json:"market_category"` - MarketID int64 `json:"market_id"` - NumberOfOutcomes int64 `json:"number_of_outcomes"` - RawOdds []json.RawMessage `json:"raw_odds"` - FetchedAt time.Time `json:"fetched_at"` - ExpiresAt time.Time `json:"expires_at"` - IsActive bool `json:"is_active"` -} - -type OddMarketSettings struct { - CompanyID int64 - OddMarketID int64 - IsActive ValidBool - CustomRawOdds string - UpdatedAt time.Time -} -type CreateOddMarketSettings struct { - CompanyID int64 - OddMarketID int64 - IsActive ValidBool - CustomRawOdds []map[string]interface{} -} - -type UpdateGlobalOddMarketSettings struct { - OddMarketID int64 - IsActive ValidBool -} - -type CustomOdd struct { - OddID int64 `json:"odd_id"` - OddValue float32 `json:"odd_value"` -} - -type CreateOddMarketSettingsReq struct { - OddMarketID int64 `json:"odd_market_id"` - IsActive *bool `json:"is_active,omitempty"` - CustomOdd []CustomOdd `json:"custom_odd,omitempty"` -} - -type UpdateGlobalOddMarketSettingsReq struct { - OddMarketID int64 `json:"odd_market_id"` - IsActive *bool `json:"is_active,omitempty"` -} - -type RawOddsByMarketID struct { - ID int64 `json:"id"` - MarketName string `json:"market_name"` - Handicap string `json:"handicap"` - RawOdds []map[string]interface{} `json:"raw_odds"` - FetchedAt time.Time `json:"fetched_at"` - ExpiresAt time.Time `json:"expires_at"` -} - -type OddMarketFilter struct { - Limit ValidInt32 - Offset ValidInt32 -} -type OddMarketWithEventFilter struct { - Status ValidString - IsLive ValidBool - Limit ValidInt32 - Offset ValidInt32 -} - -func ConvertDBOddMarket(oddMarket dbgen.OddsMarketWithEvent) (OddMarket, error) { - var rawOdds []json.RawMessage - if len(oddMarket.RawOdds) > 0 { - if err := json.Unmarshal(oddMarket.RawOdds, &rawOdds); err != nil { - return OddMarket{}, err - } - } else { - rawOdds = []json.RawMessage{} // explicit empty slice - } - return OddMarket{ - ID: oddMarket.ID, - EventID: oddMarket.EventID, - MarketType: oddMarket.MarketType, - MarketName: oddMarket.MarketName, - MarketCategory: oddMarket.MarketCategory, - MarketID: oddMarket.MarketID, - NumberOfOutcomes: oddMarket.NumberOfOutcomes, - RawOdds: rawOdds, - FetchedAt: oddMarket.FetchedAt.Time, - ExpiresAt: oddMarket.ExpiresAt.Time, - DefaultIsActive: oddMarket.DefaultIsActive, - IsMonitored: oddMarket.IsMonitored, - IsLive: oddMarket.IsLive, - Status: EventStatus(oddMarket.Status), - Source: EventSource(oddMarket.Source), - }, nil -} - -func ConvertDBOddMarkets(oddMarkets []dbgen.OddsMarketWithEvent) ([]OddMarket, error) { - result := make([]OddMarket, len(oddMarkets)) - for i, om := range oddMarkets { - convertedMarket, err := ConvertDBOddMarket(om) - if err != nil { - return nil, err - } - result[i] = convertedMarket - } - return result, nil -} - -func ConvertCreateOddMarket(oddMarket CreateOddMarket) (dbgen.InsertOddsMarketParams, error) { - rawOddsBytes, err := json.Marshal(oddMarket.Odds) - if err != nil { - return dbgen.InsertOddsMarketParams{}, err - } - - return dbgen.InsertOddsMarketParams{ - EventID: oddMarket.EventID, - MarketType: oddMarket.MarketType, - MarketName: oddMarket.MarketName, - MarketCategory: oddMarket.MarketCategory, - MarketID: oddMarket.MarketID, - NumberOfOutcomes: oddMarket.NumberOfOutcomes, - RawOdds: rawOddsBytes, - FetchedAt: pgtype.Timestamp{Time: time.Now(), Valid: true}, - ExpiresAt: pgtype.Timestamp{Time: (time.Now()).Add(time.Hour), Valid: true}, - }, nil -} - -func ConvertCreateOddMarketSetting(oms CreateOddMarketSettings) (dbgen.SaveOddSettingsParams, error) { - rawOddsBytes, err := json.Marshal(oms.CustomRawOdds) - if err != nil { - return dbgen.SaveOddSettingsParams{}, err - } - return dbgen.SaveOddSettingsParams{ - CompanyID: oms.CompanyID, - OddsMarketID: oms.OddMarketID, - IsActive: oms.IsActive.ToPG(), - CustomRawOdds: rawOddsBytes, - }, nil -} - -func ConvertDBOddMarketWithSetting(oms dbgen.OddsMarketWithSetting) (OddMarketWithSettings, error) { - var rawOdds []json.RawMessage - if len(oms.RawOdds) > 0 { - if err := json.Unmarshal(oms.RawOdds, &rawOdds); err != nil { - return OddMarketWithSettings{}, err - } - } else { - rawOdds = []json.RawMessage{} // explicit empty slice - } - return OddMarketWithSettings{ - ID: oms.ID, - EventID: oms.EventID, - MarketType: oms.MarketType, - MarketName: oms.MarketName, - MarketCategory: oms.MarketCategory, - MarketID: oms.MarketID, - NumberOfOutcomes: oms.NumberOfOutcomes, - RawOdds: rawOdds, - FetchedAt: oms.FetchedAt.Time, - ExpiresAt: oms.ExpiresAt.Time, - IsActive: oms.IsActive && oms.IsMarketActive, - }, nil -} - -func ConvertDBOddMarketWithSettings(oms []dbgen.OddsMarketWithSetting) ([]OddMarketWithSettings, error) { - result := make([]OddMarketWithSettings, len(oms)) - for i, o := range oms { - converted, err := ConvertDBOddMarketWithSetting(o) - if err != nil { - return nil, err - } - result[i] = converted - } - - return result, nil -} diff --git a/internal/domain/odds_history.go b/internal/domain/odds_history.go deleted file mode 100644 index 021ea5a..0000000 --- a/internal/domain/odds_history.go +++ /dev/null @@ -1,64 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type OddHistory struct { - ID int64 - OddID int64 - MarketID int64 - RawOddID int64 - EventID int64 - OddValue float64 - CreatedAt time.Time -} - -type CreateOddHistory struct { - OddID int64 - MarketID int64 - RawOddID int64 - EventID int64 - OddValue float64 -} - -type OddHistoryFilter struct { - OddID ValidInt64 - MarketID ValidInt64 - RawOddID ValidInt64 - EventID ValidInt64 - CreatedBefore ValidTime - CreatedAfter ValidTime -} - -func ConvertCreateOddHistory(odd CreateOddHistory) dbgen.InsertOddHistoryParams { - return dbgen.InsertOddHistoryParams{ - OddsMarketID: odd.OddID, - MarketID: odd.MarketID, - RawOddID: odd.RawOddID, - EventID: odd.EventID, - OddValue: odd.OddValue, - } -} - -func ConvertDBOddHistory(dbOddHistory dbgen.OddHistory) OddHistory { - return OddHistory{ - ID: dbOddHistory.ID, - OddID: dbOddHistory.OddsMarketID, - MarketID: dbOddHistory.MarketID, - RawOddID: dbOddHistory.RawOddID, - EventID: dbOddHistory.EventID, - OddValue: dbOddHistory.OddValue, - CreatedAt: dbOddHistory.CreatedAt.Time, - } -} - -func ConvertOddHistories(list []dbgen.OddHistory) []OddHistory { - result := make([]OddHistory, 0, len(list)) - for _, item := range list { - result = append(result, ConvertDBOddHistory(item)) - } - return result -} diff --git a/internal/domain/raffle.go b/internal/domain/raffle.go deleted file mode 100644 index 17ec892..0000000 --- a/internal/domain/raffle.go +++ /dev/null @@ -1,149 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/jackc/pgx/v5/pgtype" -) - -type Raffle struct { - ID int32 - CompanyID int32 - Name string - CreatedAt time.Time - ExpiresAt time.Time - TicketLimit int32 - Type string - Status string -} - -type RaffleFilter struct { - // requireds will depend on type of raffle (sport or game) - Type string `json:"type" validate:"required,oneof=sport game"` - RaffleID int32 `json:"raffle_id" validate:"required"` - SportID int32 `json:"sport_id" validate:"required_if=Type sport"` - LeagueID int32 `json:"league_id" validate:"required_if=Type sport"` - GameID string `json:"game_id" validate:"required_if=Type game"` -} - -type RaffleStanding struct { - UserID int64 - RaffleID int32 - FirstName string - LastName string - PhoneNumber string - Email string - TicketCount int64 -} - -type RaffleStandingRes struct { - UserID int64 `json:"user_id"` - RaffleID int32 `json:"raffle_id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - PhoneNumber string `json:"phone_number"` - Email string `json:"email"` - TicketCount int64 `json:"ticket_count"` -} - -type RaffleWinnerParams struct { - RaffleID int32 - UserID int32 - Rank int32 -} - -type RaffleTicket struct { - ID int32 - RaffleID int32 - UserID int32 - IsActive bool -} - -type RaffleTicketRes struct { - TicketID int32 - UserID int32 - Name string - Type string - ExpiresAt time.Time - Status string -} - -type CreateRaffle struct { - CompanyID int32 `json:"company_id" validate:"required"` - Name string `json:"name" validate:"required"` - ExpiresAt *time.Time `json:"expires_at" validate:"required"` - TicketLimit int32 `json:"ticket_limit" validate:"required"` - Type string `json:"type" validate:"required"` -} - -type CreateRaffleTicket struct { - RaffleID int32 `json:"raffle_id" validate:"required"` - UserID int32 `json:"user_id" validate:"required"` -} - -// aside from ID, atleast one of the fields should be required -type UpdateRaffleParams struct { - ID int32 `json:"id" validate:"required"` - 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/recommendation.go b/internal/domain/recommendation.go index 53a7e9a..f1bce66 100644 --- a/internal/domain/recommendation.go +++ b/internal/domain/recommendation.go @@ -2,7 +2,7 @@ package domain type RecommendationSuccessfulResponse struct { Message string `json:"message"` - RecommendedGames []VirtualGame `json:"recommended_games"` + // RecommendedGames []VirtualGame `json:"recommended_games"` } type RecommendationErrorResponse struct { diff --git a/internal/domain/referal.go b/internal/domain/referal.go index bb9e1bb..e4ed32d 100644 --- a/internal/domain/referal.go +++ b/internal/domain/referal.go @@ -1,186 +1,186 @@ package domain -import ( - "time" +// import ( +// "time" - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) +// dbgen "Yimaru-Backend/gen/db" +// ) -type ReferralCode struct { - ID int64 - ReferrerID int64 - ReferralCode string - CompanyID int64 - NumberOfReferrals int64 - RewardAmount Currency - CreatedAt time.Time - UpdatedAt time.Time -} - -type ReferralCodeRes struct { - ID int64 `json:"id"` - ReferrerID int64 `json:"referrer_id"` - ReferralCode string `json:"referral_code"` - CompanyID int64 `json:"company_id"` - NumberOfReferrals int64 `json:"number_of_referrals"` - RewardAmount float32 `json:"reward_amount"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -type CreateReferralCode struct { - ReferrerID int64 - ReferralCode string - CompanyID int64 - NumberOfReferrals int64 - RewardAmount Currency -} - -type UserReferral struct { - ReferredID int64 - ReferralCodeID int64 -} - -type CreateUserReferrals struct { - ReferredID int64 - ReferralCodeID int64 -} - -type UpdateReferralCode struct { - ID int64 - IsActive bool - ReferralCode string - RewardAmount Currency - NumberOfReferrals int64 -} - -type ReferralStats struct { - TotalReferrals int64 - TotalRewardEarned Currency -} - -type ReferralStatsRes struct { - TotalReferrals int64 `json:"total_referrals"` - TotalRewardEarned float32 `json:"total_reward_earned"` -} - -// type ReferralSettings struct { -// ID int64 -// ReferralRewardAmount float64 -// CashbackPercentage float64 -// BetReferralBonusPercentage float64 -// MaxReferrals int32 -// ExpiresAfterDays int32 -// UpdatedBy string -// CreatedAt time.Time -// UpdatedAt time.Time -// Version int32 +// type ReferralCode struct { +// ID int64 +// ReferrerID int64 +// ReferralCode string +// CompanyID int64 +// NumberOfReferrals int64 +// RewardAmount Currency +// CreatedAt time.Time +// UpdatedAt time.Time // } -// type ReferralSettingsReq struct { -// ReferralRewardAmount float64 `json:"referral_reward_amount" validate:"required"` -// CashbackPercentage float64 `json:"cashback_percentage" validate:"required"` -// MaxReferrals int32 `json:"max_referrals" validate:"required"` -// UpdatedBy string `json:"updated_by" validate:"required"` +// type ReferralCodeRes struct { +// ID int64 `json:"id"` +// ReferrerID int64 `json:"referrer_id"` +// ReferralCode string `json:"referral_code"` +// CompanyID int64 `json:"company_id"` +// NumberOfReferrals int64 `json:"number_of_referrals"` +// RewardAmount float32 `json:"reward_amount"` +// CreatedAt time.Time `json:"created_at"` +// UpdatedAt time.Time `json:"updated_at"` // } -func ConvertCreateReferralCode(code CreateReferralCode) dbgen.CreateReferralCodeParams { - return dbgen.CreateReferralCodeParams{ - ReferralCode: code.ReferralCode, - ReferrerID: code.ReferrerID, - CompanyID: code.CompanyID, - NumberOfReferrals: code.NumberOfReferrals, - RewardAmount: int64(code.RewardAmount), - } -} +// type CreateReferralCode struct { +// ReferrerID int64 +// ReferralCode string +// CompanyID int64 +// NumberOfReferrals int64 +// RewardAmount Currency +// } - func ConvertDBReferralCode(code dbgen.ReferralCode) ReferralCode { - return ReferralCode{ - ID: code.ID, - ReferrerID: code.ReferrerID, - ReferralCode: code.ReferralCode, - NumberOfReferrals: code.NumberOfReferrals, - RewardAmount: Currency(code.RewardAmount), - CompanyID: code.CompanyID, - CreatedAt: code.CreatedAt.Time, - UpdatedAt: code.UpdatedAt.Time, - } -} +// type UserReferral struct { +// ReferredID int64 +// ReferralCodeID int64 +// } -func ConvertDBReferralCodes(codes []dbgen.ReferralCode) []ReferralCode { - result := make([]ReferralCode, len(codes)) - for i, code := range codes { - result[i] = ConvertDBReferralCode(code) - } - return result -} +// type CreateUserReferrals struct { +// ReferredID int64 +// ReferralCodeID int64 +// } -func ConvertCreateUserReferral(referral CreateUserReferrals) dbgen.CreateUserReferralParams { - return dbgen.CreateUserReferralParams{ - ReferredID: referral.ReferredID, - ReferralCodeID: referral.ReferralCodeID, - } -} +// type UpdateReferralCode struct { +// ID int64 +// IsActive bool +// ReferralCode string +// RewardAmount Currency +// NumberOfReferrals int64 +// } -func ConvertDBUserReferral(referral dbgen.UserReferral) UserReferral { - return UserReferral{ - ReferredID: referral.ReferredID, - ReferralCodeID: referral.ReferralCodeID, - } -} +// type ReferralStats struct { +// TotalReferrals int64 +// TotalRewardEarned Currency +// } -func ConvertDBUserReferrals(referrals []dbgen.UserReferral) []UserReferral { - result := make([]UserReferral, len(referrals)) - for i, referral := range referrals { - result[i] = ConvertDBUserReferral(referral) - } +// type ReferralStatsRes struct { +// TotalReferrals int64 `json:"total_referrals"` +// TotalRewardEarned float32 `json:"total_reward_earned"` +// } - return result -} +// // type ReferralSettings struct { +// // ID int64 +// // ReferralRewardAmount float64 +// // CashbackPercentage float64 +// // BetReferralBonusPercentage float64 +// // MaxReferrals int32 +// // ExpiresAfterDays int32 +// // UpdatedBy string +// // CreatedAt time.Time +// // UpdatedAt time.Time +// // Version int32 +// // } -func ConvertUpdateReferralCode(referralCode UpdateReferralCode) dbgen.UpdateReferralCodeParams { - return dbgen.UpdateReferralCodeParams{ - ID: referralCode.ID, - IsActive: referralCode.IsActive, - ReferralCode: referralCode.ReferralCode, - NumberOfReferrals: referralCode.NumberOfReferrals, - RewardAmount: int64(referralCode.RewardAmount), - } -} +// // type ReferralSettingsReq struct { +// // ReferralRewardAmount float64 `json:"referral_reward_amount" validate:"required"` +// // CashbackPercentage float64 `json:"cashback_percentage" validate:"required"` +// // MaxReferrals int32 `json:"max_referrals" validate:"required"` +// // UpdatedBy string `json:"updated_by" validate:"required"` +// // } -func ConvertDBReferralStats(stats dbgen.GetReferralStatsRow) ReferralStats { - return ReferralStats{ - TotalReferrals: stats.TotalReferrals, - TotalRewardEarned: Currency(stats.TotalRewardEarned), - } -} +// func ConvertCreateReferralCode(code CreateReferralCode) dbgen.CreateReferralCodeParams { +// return dbgen.CreateReferralCodeParams{ +// ReferralCode: code.ReferralCode, +// ReferrerID: code.ReferrerID, +// CompanyID: code.CompanyID, +// NumberOfReferrals: code.NumberOfReferrals, +// RewardAmount: int64(code.RewardAmount), +// } +// } -func ConvertReferralCodeRes(referral ReferralCode) ReferralCodeRes { - return ReferralCodeRes{ - ID: referral.ID, - ReferrerID: referral.ReferrerID, - ReferralCode: referral.ReferralCode, - CompanyID: referral.CompanyID, - NumberOfReferrals: referral.NumberOfReferrals, - RewardAmount: referral.RewardAmount.Float32(), - CreatedAt: referral.CreatedAt, - UpdatedAt: referral.UpdatedAt, - } -} +// func ConvertDBReferralCode(code dbgen.ReferralCode) ReferralCode { +// return ReferralCode{ +// ID: code.ID, +// ReferrerID: code.ReferrerID, +// ReferralCode: code.ReferralCode, +// NumberOfReferrals: code.NumberOfReferrals, +// RewardAmount: Currency(code.RewardAmount), +// CompanyID: code.CompanyID, +// CreatedAt: code.CreatedAt.Time, +// UpdatedAt: code.UpdatedAt.Time, +// } +// } -func ConvertReferralCodeResList(referrals []ReferralCode) []ReferralCodeRes { - result := make([]ReferralCodeRes, len(referrals)) +// func ConvertDBReferralCodes(codes []dbgen.ReferralCode) []ReferralCode { +// result := make([]ReferralCode, len(codes)) +// for i, code := range codes { +// result[i] = ConvertDBReferralCode(code) +// } +// return result +// } - for i, referral := range referrals { - result[i] = ConvertReferralCodeRes(referral) - } +// func ConvertCreateUserReferral(referral CreateUserReferrals) dbgen.CreateUserReferralParams { +// return dbgen.CreateUserReferralParams{ +// ReferredID: referral.ReferredID, +// ReferralCodeID: referral.ReferralCodeID, +// } +// } - return result -} +// func ConvertDBUserReferral(referral dbgen.UserReferral) UserReferral { +// return UserReferral{ +// ReferredID: referral.ReferredID, +// ReferralCodeID: referral.ReferralCodeID, +// } +// } -func ConvertReferralStatsRes(stats ReferralStats) ReferralStatsRes { - return ReferralStatsRes{ - TotalReferrals: stats.TotalReferrals, - TotalRewardEarned: stats.TotalRewardEarned.Float32(), - } -} +// func ConvertDBUserReferrals(referrals []dbgen.UserReferral) []UserReferral { +// result := make([]UserReferral, len(referrals)) +// for i, referral := range referrals { +// result[i] = ConvertDBUserReferral(referral) +// } + +// return result +// } + +// func ConvertUpdateReferralCode(referralCode UpdateReferralCode) dbgen.UpdateReferralCodeParams { +// return dbgen.UpdateReferralCodeParams{ +// ID: referralCode.ID, +// IsActive: referralCode.IsActive, +// ReferralCode: referralCode.ReferralCode, +// NumberOfReferrals: referralCode.NumberOfReferrals, +// RewardAmount: int64(referralCode.RewardAmount), +// } +// } + +// func ConvertDBReferralStats(stats dbgen.GetReferralStatsRow) ReferralStats { +// return ReferralStats{ +// TotalReferrals: stats.TotalReferrals, +// TotalRewardEarned: Currency(stats.TotalRewardEarned), +// } +// } + +// func ConvertReferralCodeRes(referral ReferralCode) ReferralCodeRes { +// return ReferralCodeRes{ +// ID: referral.ID, +// ReferrerID: referral.ReferrerID, +// ReferralCode: referral.ReferralCode, +// CompanyID: referral.CompanyID, +// NumberOfReferrals: referral.NumberOfReferrals, +// RewardAmount: referral.RewardAmount.Float32(), +// CreatedAt: referral.CreatedAt, +// UpdatedAt: referral.UpdatedAt, +// } +// } + +// func ConvertReferralCodeResList(referrals []ReferralCode) []ReferralCodeRes { +// result := make([]ReferralCodeRes, len(referrals)) + +// for i, referral := range referrals { +// result[i] = ConvertReferralCodeRes(referral) +// } + +// return result +// } + +// func ConvertReferralStatsRes(stats ReferralStats) ReferralStatsRes { +// return ReferralStatsRes{ +// TotalReferrals: stats.TotalReferrals, +// TotalRewardEarned: stats.TotalRewardEarned.Float32(), +// } +// } diff --git a/internal/domain/report_data.go b/internal/domain/report_data.go index 47413e3..3167a50 100644 --- a/internal/domain/report_data.go +++ b/internal/domain/report_data.go @@ -190,10 +190,10 @@ func ConvertDashboardSummaryToRes(summary DashboardSummary) DashboardSummaryRes ActiveCashiers: summary.ActiveCashiers, InactiveCashiers: summary.InactiveCashiers, - TotalWallets: summary.TotalWallets, - TotalGames: summary.TotalGames, - ActiveGames: summary.ActiveGames, - InactiveGames: summary.InactiveGames, + // TotalWallets: summary.TotalWallets, + // TotalGames: summary.TotalGames, + // ActiveGames: summary.ActiveGames, + // InactiveGames: summary.InactiveGames, TotalManagers: summary.TotalManagers, ActiveManagers: summary.ActiveManagers, @@ -253,45 +253,45 @@ type BranchPerformance struct { PerformanceScore float64 `json:"performance_score"` } -type SportPerformance struct { - SportID string `json:"sport_id"` - SportName string `json:"sport_name"` - TotalBets int64 `json:"total_bets"` - TotalStakes Currency `json:"total_stakes"` - TotalWins int64 `json:"total_wins"` - TotalPayouts Currency `json:"total_payouts"` - Profit Currency `json:"profit"` - PopularityRank int `json:"popularity_rank"` - WinRate float64 `json:"win_rate"` - AverageStake Currency `json:"average_stake"` - AverageOdds float64 `json:"average_odds"` - MostPopularMarket string `json:"most_popular_market"` -} +// type SportPerformance struct { +// SportID string `json:"sport_id"` +// SportName string `json:"sport_name"` +// TotalBets int64 `json:"total_bets"` +// TotalStakes Currency `json:"total_stakes"` +// TotalWins int64 `json:"total_wins"` +// TotalPayouts Currency `json:"total_payouts"` +// Profit Currency `json:"profit"` +// PopularityRank int `json:"popularity_rank"` +// WinRate float64 `json:"win_rate"` +// AverageStake Currency `json:"average_stake"` +// AverageOdds float64 `json:"average_odds"` +// MostPopularMarket string `json:"most_popular_market"` +// } -type BetAnalysis struct { - Date time.Time `json:"date"` - TotalBets int64 `json:"total_bets"` - TotalStakes Currency `json:"total_stakes"` - TotalWins int64 `json:"total_wins"` - TotalPayouts Currency `json:"total_payouts"` - Profit Currency `json:"profit"` - MostPopularSport string `json:"most_popular_sport"` - MostPopularMarket string `json:"most_popular_market"` - HighestStake Currency `json:"highest_stake"` - HighestPayout Currency `json:"highest_payout"` - AverageOdds float64 `json:"average_odds"` -} +// type BetAnalysis struct { +// Date time.Time `json:"date"` +// TotalBets int64 `json:"total_bets"` +// TotalStakes Currency `json:"total_stakes"` +// TotalWins int64 `json:"total_wins"` +// TotalPayouts Currency `json:"total_payouts"` +// Profit Currency `json:"profit"` +// MostPopularSport string `json:"most_popular_sport"` +// MostPopularMarket string `json:"most_popular_market"` +// HighestStake Currency `json:"highest_stake"` +// HighestPayout Currency `json:"highest_payout"` +// AverageOdds float64 `json:"average_odds"` +// } // ReportFilter contains filters for report generation type ReportFilter struct { - StartTime ValidTime `json:"start_time"` - EndTime ValidTime `json:"end_time"` - CompanyID ValidInt64 `json:"company_id"` - BranchID ValidInt64 `json:"branch_id"` - RecipientID ValidInt64 `json:"recipient_id"` - UserID ValidInt64 `json:"user_id"` - SportID ValidString `json:"sport_id"` - Status ValidOutcomeStatus `json:"status"` + StartTime ValidTime `json:"start_time"` + EndTime ValidTime `json:"end_time"` + CompanyID ValidInt64 `json:"company_id"` + BranchID ValidInt64 `json:"branch_id"` + RecipientID ValidInt64 `json:"recipient_id"` + UserID ValidInt64 `json:"user_id"` + SportID ValidString `json:"sport_id"` + Status string `json:"status"` } // BetStat represents aggregated bet statistics @@ -464,25 +464,24 @@ type CashierPerformance struct { ActiveDays int `json:"active_days"` } -type CompanyWalletBalance struct { - CompanyID int64 `json:"company_id"` - CompanyName string `json:"company_name"` - Balance float64 `json:"balance"` -} +// type CompanyWalletBalance struct { +// CompanyID int64 `json:"company_id"` +// CompanyName string `json:"company_name"` +// Balance float64 `json:"balance"` +// } -type BranchWalletBalance struct { - BranchID int64 `json:"branch_id"` - BranchName string `json:"branch_name"` - CompanyID int64 `json:"company_id"` - Balance float64 `json:"balance"` -} - -type LiveWalletMetrics struct { - Timestamp time.Time `json:"timestamp"` - CompanyBalances []CompanyWalletBalance `json:"company_balances"` - BranchBalances []BranchWalletBalance `json:"branch_balances"` -} +// type BranchWalletBalance struct { +// BranchID int64 `json:"branch_id"` +// BranchName string `json:"branch_name"` +// CompanyID int64 `json:"company_id"` +// Balance float64 `json:"balance"` +// } +// type LiveWalletMetrics struct { +// Timestamp time.Time `json:"timestamp"` +// CompanyBalances []CompanyWalletBalance `json:"company_balances"` +// BranchBalances []BranchWalletBalance `json:"branch_balances"` +// } // type CompanyReport struct { // CompanyID int64 diff --git a/internal/domain/report_request.go b/internal/domain/report_request.go deleted file mode 100644 index a563626..0000000 --- a/internal/domain/report_request.go +++ /dev/null @@ -1,305 +0,0 @@ -package domain - -import ( - "fmt" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type ReportRequest struct { - ID int64 - CompanyID ValidInt64 - RequestedBy ValidInt64 - FilePath ValidString - Type ReportRequestType - Status ReportRequestStatus - Metadata ReportMetadataJSON - RejectReason ValidString - CreatedAt time.Time - CompletedAt ValidTime -} - -type ReportRequestDetail struct { - ID int64 - CompanyID ValidInt64 - CompanyName ValidString - CompanySlug ValidString - RequestedBy ValidInt64 - RequesterFirstName ValidString - RequesterLastName ValidString - RequesterRole ValidRole - FilePath ValidString - Type ReportRequestType - Status ReportRequestStatus - Metadata ReportMetadataJSON - RejectReason ValidString - CreatedAt time.Time - CompletedAt ValidTime -} - -type CreateReportRequest struct { - CompanyID ValidInt64 - RequestedBy ValidInt64 - Type ReportRequestType - Metadata ReportMetadataJSON -} - -type CreateReportRequestReq struct { - Type string `json:"type"` - Metadata ReportMetadataJSON `json:"metadata"` -} - -type ReportRequestRes struct { - ID int64 `json:"id"` - RequestedBy *int64 `json:"requested_by,omitempty"` - CompanyID *int64 `json:"company_id,omitempty"` - FilePath *string `json:"file_path,omitempty"` - Type ReportRequestType `json:"type"` - Status string `json:"status"` - RejectReason *string `json:"reject_reason,omitempty"` - Metadata ReportMetadataJSON `json:"metadata"` - CreatedAt time.Time `json:"created_at"` - CompletedAt *string `json:"completed_at,omitempty"` -} -type ReportRequestDetailRes struct { - ID int64 `json:"id"` - CompanyID *int64 `json:"company_id,omitempty"` - CompanyName *string `json:"company_name,omitempty"` - CompanySlug *string `json:"company_slug,omitempty"` - RequestedBy *int64 `json:"requested_by,omitempty"` - RequesterFirstName *string `json:"requester_first_name,omitempty"` - RequesterLastName *string `json:"requester_last_name,omitempty"` - RequesterRole *string `json:"requester_role,omitempty"` - FilePath *string `json:"file_path,omitempty"` - Type ReportRequestType `json:"type"` - Status string `json:"status"` - RejectReason *string `json:"reject_reason,omitempty"` - Metadata ReportMetadataJSON `json:"metadata"` - CreatedAt time.Time `json:"created_at"` - CompletedAt *string `json:"completed_at,omitempty"` -} - -type ReportRequestFilter struct { - CompanyID ValidInt64 - Type ValidReportRequestType - Status ValidReportRequestStatus - Limit ValidInt32 - Offset ValidInt32 - RequestedBy ValidInt64 -} - -type UpdateRequestRequest struct { - ID int64 - FilePath ValidString - RejectReason ValidString - Status ValidReportRequestStatus -} - -func ConvertDBReportRequest(report dbgen.ReportRequest) (ReportRequest, error) { - parsedMetadataJSON, err := ParseReportMetadataJSON(report.Metadata) - if err != nil { - return ReportRequest{}, fmt.Errorf("failed to parse report metadata: %w", err) - } - - return ReportRequest{ - ID: report.ID, - CompanyID: ValidInt64{ - Value: report.CompanyID.Int64, - Valid: report.CompanyID.Valid, - }, - RequestedBy: ValidInt64{ - Value: report.RequestedBy.Int64, - Valid: report.RequestedBy.Valid, - }, - FilePath: ValidString{ - Value: report.FilePath.String, - Valid: report.FilePath.Valid, - }, - Type: ReportRequestType(report.Type), - Status: ReportRequestStatus(report.Status), - Metadata: parsedMetadataJSON, - RejectReason: ValidString{ - Value: report.RejectReason.String, - Valid: report.RejectReason.Valid, - }, - CreatedAt: report.CreatedAt.Time, - CompletedAt: ValidTime{ - Value: report.CompletedAt.Time, - Valid: report.CompletedAt.Valid, - }, - }, nil -} - -func ConvertDBReportRequestList(reports []dbgen.ReportRequest) ([]ReportRequest, error) { - result := make([]ReportRequest, len(reports)) - var err error - for i, request := range reports { - result[i], err = ConvertDBReportRequest(request) - if err != nil { - return nil, err - } - } - return result, nil -} - -func ConvertDBReportRequestDetail(report dbgen.ReportRequestDetail) (ReportRequestDetail, error) { - parsedMetadataJSON, err := ParseReportMetadataJSON(report.Metadata) - if err != nil { - return ReportRequestDetail{}, fmt.Errorf("failed to parse report metadata: %w", err) - } - - return ReportRequestDetail{ - ID: report.ID, - CompanyID: ValidInt64{ - Value: report.CompanyID.Int64, - Valid: report.CompanyID.Valid, - }, - CompanyName: ValidString{ - Value: report.CompanyName.String, - Valid: report.CompanyName.Valid, - }, - CompanySlug: ValidString{ - Value: report.CompanySlug.String, - Valid: report.CompanySlug.Valid, - }, - RequestedBy: ValidInt64{ - Value: report.RequestedBy.Int64, - Valid: report.RequestedBy.Valid, - }, - RequesterFirstName: ValidString{ - Value: report.RequesterFirstName.String, - Valid: report.RequesterFirstName.Valid, - }, - RequesterLastName: ValidString{ - Value: report.RequesterLastName.String, - Valid: report.RequesterLastName.Valid, - }, - RequesterRole: ValidRole{ - Value: Role(report.RequesterRole.String), - Valid: report.RequesterRole.Valid, - }, - FilePath: ValidString{ - Value: report.FilePath.String, - Valid: report.FilePath.Valid, - }, - Type: ReportRequestType(report.Type), - Status: ReportRequestStatus(report.Status), - Metadata: parsedMetadataJSON, - RejectReason: ValidString{ - Value: report.RejectReason.String, - Valid: report.RejectReason.Valid, - }, - CreatedAt: report.CreatedAt.Time, - CompletedAt: ValidTime{ - Value: report.CompletedAt.Time, - Valid: report.CompletedAt.Valid, - }, - }, nil -} - -func ConvertDBReportRequestDetailList(reports []dbgen.ReportRequestDetail) ([]ReportRequestDetail, error) { - result := make([]ReportRequestDetail, len(reports)) - var err error - for i, request := range reports { - result[i], err = ConvertDBReportRequestDetail(request) - if err != nil { - return nil, err - } - } - return result, nil -} - -func ConvertReportRequest(request ReportRequest) ReportRequestRes { - var res ReportRequestRes - - if request.RequestedBy.Valid { - res.RequestedBy = &request.RequestedBy.Value - } - - if request.CompanyID.Valid { - res.CompanyID = &request.CompanyID.Value - } - - if request.FilePath.Valid { - res.FilePath = &request.FilePath.Value - } - - if request.RejectReason.Valid { - res.RejectReason = &request.RejectReason.Value - } - - if request.CompletedAt.Valid { - str := request.CompletedAt.Value.Format(time.RFC3339) - res.CompletedAt = &str - } - - res.ID = request.ID - res.Type = request.Type - res.Status = string(request.Status) - res.Metadata = request.Metadata - res.CreatedAt = request.CreatedAt - - return res -} - -func ConvertReportRequestList(requests []ReportRequest) []ReportRequestRes { - result := make([]ReportRequestRes, len(requests)) - for i, request := range requests { - result[i] = ConvertReportRequest(request) - } - return result -} - -func ConvertReportRequestDetail(request ReportRequestDetail) ReportRequestDetailRes { - var res ReportRequestDetailRes - - if request.RequestedBy.Valid { - res.RequestedBy = &request.RequestedBy.Value - } - if request.RequesterFirstName.Valid { - res.RequesterFirstName = &request.RequesterFirstName.Value - } - if request.RequesterLastName.Valid { - res.RequesterLastName = &request.RequesterLastName.Value - } - if request.RequesterRole.Valid { - res.RequesterRole = (*string)(&request.RequesterRole.Value) - } - if request.CompanyID.Valid { - res.CompanyID = &request.CompanyID.Value - } - if request.CompanyName.Valid { - res.CompanyName = &request.CompanyName.Value - } - if request.CompanySlug.Valid { - res.CompanySlug = &request.CompanySlug.Value - } - if request.FilePath.Valid { - res.FilePath = &request.FilePath.Value - } - if request.RejectReason.Valid { - res.RejectReason = &request.RejectReason.Value - } - - if request.CompletedAt.Valid { - str := request.CompletedAt.Value.Format(time.RFC3339) - res.CompletedAt = &str - } - - res.ID = request.ID - res.Type = request.Type - res.Status = string(request.Status) - res.Metadata = request.Metadata - res.CreatedAt = request.CreatedAt - - return res -} - -func ConvertReportRequestDetailList(requests []ReportRequestDetail) []ReportRequestDetailRes { - result := make([]ReportRequestDetailRes, len(requests)) - for i, request := range requests { - result[i] = ConvertReportRequestDetail(request) - } - return result -} diff --git a/internal/domain/responses.go b/internal/domain/responses.go index 4836dc0..8672e58 100644 --- a/internal/domain/responses.go +++ b/internal/domain/responses.go @@ -3,7 +3,7 @@ package domain import ( "errors" - "github.com/gofiber/fiber/v2" + fiber "github.com/gofiber/fiber/v2" ) func UnProcessableEntityResponse(c *fiber.Ctx) error { diff --git a/internal/domain/result.go b/internal/domain/result.go deleted file mode 100644 index b14fcaa..0000000 --- a/internal/domain/result.go +++ /dev/null @@ -1,121 +0,0 @@ -package domain - -import ( - "fmt" - "time" - - "github.com/jackc/pgx/v5/pgtype" -) - -type MarketConfig struct { - Sport string - MarketCategories map[string]bool - MarketTypes map[int64]bool -} - -type Result struct { - ID int64 - BetOutcomeID int64 - EventID int64 - OddID int64 - MarketID int64 - Status OutcomeStatus - Score string - FullTimeScore string - HalfTimeScore string - SS string - Scores map[string]ScoreResultResponse - CreatedAt time.Time - UpdatedAt time.Time -} - -type CreateResult struct { - BetOutcomeID int64 - EventID int64 - OddID int64 - MarketID int64 - Status OutcomeStatus - Score string -} - -type OutcomeStatus int32 - -const ( - OUTCOME_STATUS_PENDING OutcomeStatus = 0 - OUTCOME_STATUS_WIN OutcomeStatus = 1 - OUTCOME_STATUS_LOSS OutcomeStatus = 2 - OUTCOME_STATUS_VOID OutcomeStatus = 3 //Give Back - OUTCOME_STATUS_HALF OutcomeStatus = 4 //Half Win and Half Given Back - OUTCOME_STATUS_ERROR OutcomeStatus = 5 //Error (Unsettled Bet) -) - -func (o OutcomeStatus) IsValid() bool { - switch o { - case OUTCOME_STATUS_PENDING, - OUTCOME_STATUS_WIN, - OUTCOME_STATUS_LOSS, - OUTCOME_STATUS_VOID, - OUTCOME_STATUS_HALF, - OUTCOME_STATUS_ERROR: - return true - default: - return false - } -} - -func ParseOutcomeStatus(val int) (OutcomeStatus, error) { - o := OutcomeStatus(val) - if !o.IsValid() { - return 0, fmt.Errorf("invalid OutcomeStatus: %d", val) - } - return o, nil -} - -func (o *OutcomeStatus) String() string { - switch *o { - case OUTCOME_STATUS_PENDING: - return "PENDING" - case OUTCOME_STATUS_WIN: - return "WIN" - case OUTCOME_STATUS_LOSS: - return "LOSS" - case OUTCOME_STATUS_VOID: - return "VOID" - case OUTCOME_STATUS_HALF: - return "HALF" - case OUTCOME_STATUS_ERROR: - return "ERROR" - default: - return "UNKNOWN" - } -} - -type ValidOutcomeStatus struct { - Value OutcomeStatus - Valid bool -} - -func (v ValidOutcomeStatus) ToPG() pgtype.Int4 { - return pgtype.Int4{ - Int32: int32(v.Value), - Valid: v.Valid, - } -} - -type TimeStatus int32 - -const ( - TIME_STATUS_NOT_STARTED TimeStatus = 0 - TIME_STATUS_IN_PLAY TimeStatus = 1 - TIME_STATUS_TO_BE_FIXED TimeStatus = 2 - TIME_STATUS_ENDED TimeStatus = 3 - TIME_STATUS_POSTPONED TimeStatus = 4 - TIME_STATUS_CANCELLED TimeStatus = 5 - TIME_STATUS_WALKOVER TimeStatus = 6 - TIME_STATUS_INTERRUPTED TimeStatus = 7 - TIME_STATUS_ABANDONED TimeStatus = 8 - TIME_STATUS_RETIRED TimeStatus = 9 - TIME_STATUS_SUSPENDED TimeStatus = 10 - TIME_STATUS_DECIDED_BY_FA TimeStatus = 11 - TIME_STATUS_REMOVED TimeStatus = 99 -) diff --git a/internal/domain/result_log.go b/internal/domain/result_log.go deleted file mode 100644 index 5e70829..0000000 --- a/internal/domain/result_log.go +++ /dev/null @@ -1,84 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - - -type ResultLog struct { - ID int64 `json:"id"` - StatusNotFinishedCount int `json:"status_not_finished_count"` - StatusNotFinishedBets int `json:"status_not_finished_bets"` - StatusToBeFixedCount int `json:"status_to_be_fixed_count"` - StatusToBeFixedBets int `json:"status_to_be_fixed_bets"` - StatusPostponedCount int `json:"status_postponed_count"` - StatusPostponedBets int `json:"status_postponed_bets"` - StatusEndedCount int `json:"status_ended_count"` - StatusEndedBets int `json:"status_ended_bets"` - StatusRemovedCount int `json:"status_removed_count"` - StatusRemovedBets int `json:"status_removed_bets"` - RemovedCount int `json:"removed"` - CreatedAt time.Time `json:"created_at"` -} - -type CreateResultLog struct { - StatusNotFinishedCount int `json:"status_not_finished_count"` - StatusNotFinishedBets int `json:"status_not_finished_bets"` - StatusToBeFixedCount int `json:"status_to_be_fixed_count"` - StatusToBeFixedBets int `json:"status_to_be_fixed_bets"` - StatusPostponedCount int `json:"status_postponed_count"` - StatusPostponedBets int `json:"status_postponed_bets"` - StatusEndedCount int `json:"status_ended_count"` - StatusEndedBets int `json:"status_ended_bets"` - StatusRemovedCount int `json:"status_removed_count"` - StatusRemovedBets int `json:"status_removed_bets"` - RemovedCount int `json:"removed"` -} - -type ResultLogFilter struct { - CreatedBefore ValidTime - CreatedAfter ValidTime -} -type ResultStatusBets struct { - StatusNotFinished []int64 `json:"status_not_finished"` - StatusToBeFixed []int64 `json:"status_to_be_fixed"` - StatusPostponed []int64 `json:"status_postponed"` - StatusEnded []int64 `json:"status_ended"` - StatusRemoved []int64 `json:"status_removed"` -} - -func ConvertDBResultLog(result dbgen.ResultLog) ResultLog { - return ResultLog{ - ID: result.ID, - StatusNotFinishedCount: int(result.StatusNotFinishedCount), - StatusNotFinishedBets: int(result.StatusNotFinishedBets), - StatusToBeFixedCount: int(result.StatusToBeFixedCount), - StatusToBeFixedBets: int(result.StatusToBeFixedBets), - StatusPostponedCount: int(result.StatusPostponedCount), - StatusPostponedBets: int(result.StatusPostponedBets), - StatusEndedCount: int(result.StatusEndedCount), - StatusEndedBets: int(result.StatusEndedBets), - StatusRemovedCount: int(result.StatusRemovedCount), - StatusRemovedBets: int(result.StatusRemovedBets), - RemovedCount: int(result.RemovedCount), - CreatedAt: result.CreatedAt.Time, - } -} - -func ConvertCreateResultLog(result CreateResultLog) dbgen.CreateResultLogParams { - return dbgen.CreateResultLogParams{ - StatusNotFinishedCount: int32(result.StatusNotFinishedCount), - StatusNotFinishedBets: int32(result.StatusNotFinishedBets), - StatusToBeFixedCount: int32(result.StatusToBeFixedCount), - StatusToBeFixedBets: int32(result.StatusToBeFixedBets), - StatusPostponedCount: int32(result.StatusPostponedCount), - StatusPostponedBets: int32(result.StatusPostponedBets), - StatusEndedCount: int32(result.StatusEndedCount), - StatusEndedBets: int32(result.StatusEndedBets), - StatusRemovedCount: int32(result.StatusRemovedCount), - StatusRemovedBets: int32(result.StatusRemovedBets), - RemovedCount: int32(result.RemovedCount), - } -} diff --git a/internal/domain/resultres.go b/internal/domain/resultres.go deleted file mode 100644 index 064ab61..0000000 --- a/internal/domain/resultres.go +++ /dev/null @@ -1,468 +0,0 @@ -package domain - -import ( - "encoding/json" -) - -type BaseResultResponse struct { - Success int `json:"success"` - Results []json.RawMessage `json:"results"` -} - -type LeagueResultResponse struct { - ID string `json:"id"` - Name string `json:"name"` - CC string `json:"cc"` -} - -type TeamResultResponse struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID ValidInt64 `json:"image_id"` - CC string `json:"cc"` -} - -type ScoreResultResponse struct { - Home string `json:"home"` - Away string `json:"away"` -} - -type CommonResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus ValidInt64 `json:"time_status"` - League LeagueResultResponse `json:"league"` - Home TeamResultResponse `json:"home"` - Away TeamResultResponse `json:"away"` - SS string `json:"ss"` -} - -type FootballResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League LeagueResultResponse `json:"league"` - Home TeamResultResponse `json:"home"` - Away TeamResultResponse `json:"away"` - SS string `json:"ss"` - Scores struct { - FirstHalf ScoreResultResponse `json:"1"` - SecondHalf ScoreResultResponse `json:"2"` - } `json:"scores"` - Stats struct { - Attacks []string `json:"attacks"` - Corners []string `json:"corners"` - HalfTimeCorners []string `json:"corner_h"` - DangerousAttacks []string `json:"dangerous_attacks"` - Goals []string `json:"goals"` - OffTarget []string `json:"off_target"` - OnTarget []string `json:"on_target"` - Penalties []string `json:"penalties"` - PossessionRT []string `json:"possession_rt"` - RedCards []string `json:"redcards"` - Substitutions []string `json:"substitutions"` - YellowCards []string `json:"yellowcards"` - } `json:"stats"` - Extra struct { - HomePos string `json:"home_pos"` - AwayPos string `json:"away_pos"` - StadiumData map[string]string `json:"stadium_data"` - Round string `json:"round"` - } `json:"extra"` - Events []map[string]string `json:"events"` - HasLineup int `json:"has_lineup"` - ConfirmedAt string `json:"confirmed_at"` - Bet365ID string `json:"bet365_id"` -} - -type BasketballResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League LeagueResultResponse `json:"league"` - Home TeamResultResponse `json:"home"` - Away TeamResultResponse `json:"away"` - SS string `json:"ss"` - Scores struct { - FirstQuarter ScoreResultResponse `json:"1"` - SecondQuarter ScoreResultResponse `json:"2"` - FirstHalf ScoreResultResponse `json:"3"` - ThirdQuarter ScoreResultResponse `json:"4"` - FourthQuarter ScoreResultResponse `json:"5"` - TotalScore ScoreResultResponse `json:"7"` - } `json:"scores"` - Stats struct { - TwoPoints []string `json:"2points"` - ThreePoints []string `json:"3points"` - BiggestLead []string `json:"biggest_lead"` - Fouls []string `json:"fouls"` - FreeThrows []string `json:"free_throws"` - FreeThrowRate []string `json:"free_throws_rate"` - LeadChanges []string `json:"lead_changes"` - MaxpointsInarow []string `json:"maxpoints_inarow"` - Possession []string `json:"possession"` - SuccessAttempts []string `json:"success_attempts"` - TimeSpendInLead []string `json:"timespent_inlead"` - TimeOuts []string `json:"time_outs"` - } `json:"stats"` - Extra struct { - HomePos string `json:"home_pos"` - AwayPos string `json:"away_pos"` - AwayManager map[string]string `json:"away_manager"` - HomeManager map[string]string `json:"home_manager"` - NumberOfPeriods string `json:"numberofperiods"` - PeriodLength string `json:"periodlength"` - StadiumData map[string]string `json:"stadium_data"` - Length int `json:"length"` - Round string `json:"round"` - } `json:"extra"` - Events []map[string]string `json:"events"` - HasLineup int `json:"has_lineup"` - ConfirmedAt string `json:"confirmed_at"` - Bet365ID string `json:"bet365_id"` -} -type IceHockeyResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League LeagueResultResponse `json:"league"` - Home TeamResultResponse `json:"home"` - Away TeamResultResponse `json:"away"` - SS string `json:"ss"` - Scores struct { - FirstPeriod ScoreResultResponse `json:"1"` - SecondPeriod ScoreResultResponse `json:"2"` - ThirdPeriod ScoreResultResponse `json:"3"` - TotalScore ScoreResultResponse `json:"5"` - } `json:"scores"` - - Stats struct { - Shots []string `json:"shots"` - Penalties []string `json:"penalties"` - GoalsOnPowerPlay []string `json:"goals_on_power_play"` - SSeven []string `json:"s7"` - } `json:"stats"` - Extra struct { - HomePos string `json:"home_pos"` - AwayPos string `json:"away_pos"` - AwayManager map[string]string `json:"away_manager"` - HomeManager map[string]string `json:"home_manager"` - NumberOfPeriods string `json:"numberofperiods"` - PeriodLength string `json:"periodlength"` - StadiumData map[string]string `json:"stadium_data"` - Length int `json:"length"` - Round string `json:"round"` - } `json:"extra"` - Events []map[string]string `json:"events"` - HasLineup int `json:"has_lineup"` - ConfirmedAt string `json:"confirmed_at"` - Bet365ID string `json:"bet365_id"` -} - -type CricketResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League struct { - ID string `json:"id"` - Name string `json:"name"` - CC string `json:"cc"` - } `json:"league"` - Home struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"home"` - Away struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"away"` - SS string `json:"ss"` - Extra struct { - HomePos string `json:"home_pos"` - AwayPos string `json:"away_pos"` - NumberOfPeriods string `json:"numberofperiods"` - PeriodLength string `json:"periodlength"` - StadiumData map[string]string `json:"stadium_data"` - } `json:"extra"` - HasLineup int `json:"has_lineup"` - ConfirmedAt string `json:"confirmed_at"` - Bet365ID string `json:"bet365_id"` -} - -type VolleyballResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League struct { - ID string `json:"id"` - Name string `json:"name"` - CC string `json:"cc"` - } `json:"league"` - Home struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"home"` - Away struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"away"` - SS string `json:"ss"` - Scores struct { - FirstSet ScoreResultResponse `json:"1"` - SecondSet ScoreResultResponse `json:"2"` - ThirdSet ScoreResultResponse `json:"3"` - FourthSet ScoreResultResponse `json:"4"` - FivethSet ScoreResultResponse `json:"5"` - } `json:"scores"` - Stats struct { - PointsWonOnServe []string `json:"points_won_on_serve"` - LongestStreak []string `json:"longest_streak"` - } `json:"stats"` - InplayCreatedAt string `json:"inplay_created_at"` - InplayUpdatedAt string `json:"inplay_updated_at"` - Bet365ID string `json:"bet365_id"` -} - -type DartsResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League struct { - ID string `json:"id"` - Name string `json:"name"` - CC string `json:"cc"` - } `json:"league"` - Home struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"home"` - Away struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"away"` - SS string `json:"ss"` - InplayCreatedAt string `json:"inplay_created_at"` - InplayUpdatedAt string `json:"inplay_updated_at"` - ConfirmedAt string `json:"confirmed_at"` - Bet365ID string `json:"bet365_id"` -} - -type FutsalResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League struct { - ID string `json:"id"` - Name string `json:"name"` - CC string `json:"cc"` - } `json:"league"` - Home struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"home"` - Away struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"away"` - SS string `json:"ss"` - Scores struct { - FirstPeriod ScoreResultResponse `json:"1"` - SecondPeriod ScoreResultResponse `json:"2"` - ThirdPeriod ScoreResultResponse `json:"3"` - TotalScore ScoreResultResponse `json:"4"` - } `json:"scores"` - Events []map[string]string `json:"events"` - InplayCreatedAt string `json:"inplay_created_at"` - InplayUpdatedAt string `json:"inplay_updated_at"` - ConfirmedAt string `json:"confirmed_at"` - Bet365ID string `json:"bet365_id"` -} - -// NFLResultResponse represents the structure for NFL game results -type NFLResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League struct { - ID string `json:"id"` - Name string `json:"name"` - CC string `json:"cc"` - } `json:"league"` - Home struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"home"` - Away struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"away"` - SS string `json:"ss"` - Scores struct { - FirstQuarter ScoreResultResponse `json:"1"` - SecondQuarter ScoreResultResponse `json:"2"` - ThirdQuarter ScoreResultResponse `json:"3"` - FourthQuarter ScoreResultResponse `json:"4"` - Overtime ScoreResultResponse `json:"5"` - TotalScore ScoreResultResponse `json:"7"` - } `json:"scores"` - Stats struct { - FirstDowns []string `json:"first_downs"` - TotalYards []string `json:"total_yards"` - PassingYards []string `json:"passing_yards"` - RushingYards []string `json:"rushing_yards"` - Turnovers []string `json:"turnovers"` - TimeOfPossession []string `json:"time_of_possession"` - ThirdDownEfficiency []string `json:"third_down_efficiency"` - FourthDownEfficiency []string `json:"fourth_down_efficiency"` - } `json:"stats"` - Extra struct { - HomePos string `json:"home_pos"` - AwayPos string `json:"away_pos"` - StadiumData map[string]string `json:"stadium_data"` - Round string `json:"round"` - } `json:"extra"` - Events []map[string]string `json:"events"` - HasLineup int `json:"has_lineup"` - ConfirmedAt string `json:"confirmed_at"` - Bet365ID string `json:"bet365_id"` -} - -// RugbyResultResponse represents the structure for Rugby game results -type RugbyResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League struct { - ID string `json:"id"` - Name string `json:"name"` - CC string `json:"cc"` - } `json:"league"` - Home struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"home"` - Away struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"away"` - SS string `json:"ss"` - Scores struct { - FirstHalf ScoreResultResponse `json:"1"` - SecondHalf ScoreResultResponse `json:"2"` - TotalScore ScoreResultResponse `json:"7"` - } `json:"scores"` - Stats struct { - Tries []string `json:"tries"` - Conversions []string `json:"conversions"` - Penalties []string `json:"penalties"` - DropGoals []string `json:"drop_goals"` - Possession []string `json:"possession"` - Territory []string `json:"territory"` - Lineouts []string `json:"lineouts"` - Scrums []string `json:"scrums"` - PenaltiesConceded []string `json:"penalties_conceded"` - } `json:"stats"` - Extra struct { - HomePos string `json:"home_pos"` - AwayPos string `json:"away_pos"` - StadiumData map[string]string `json:"stadium_data"` - Round string `json:"round"` - } `json:"extra"` - Events []map[string]string `json:"events"` - HasLineup int `json:"has_lineup"` - ConfirmedAt string `json:"confirmed_at"` - Bet365ID string `json:"bet365_id"` -} - -// BaseballResultResponse represents the structure for Baseball game results -type BaseballResultResponse struct { - ID string `json:"id"` - SportID string `json:"sport_id"` - Time string `json:"time"` - TimeStatus string `json:"time_status"` - League struct { - ID string `json:"id"` - Name string `json:"name"` - CC string `json:"cc"` - } `json:"league"` - Home struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"home"` - Away struct { - ID string `json:"id"` - Name string `json:"name"` - ImageID string `json:"image_id"` - CC string `json:"cc"` - } `json:"away"` - SS string `json:"ss"` - Scores struct { - FirstInning ScoreResultResponse `json:"1"` - SecondInning ScoreResultResponse `json:"2"` - ThirdInning ScoreResultResponse `json:"3"` - FourthInning ScoreResultResponse `json:"4"` - FifthInning ScoreResultResponse `json:"5"` - SixthInning ScoreResultResponse `json:"6"` - SeventhInning ScoreResultResponse `json:"7"` - EighthInning ScoreResultResponse `json:"8"` - NinthInning ScoreResultResponse `json:"9"` - ExtraInnings ScoreResultResponse `json:"10"` - TotalScore ScoreResultResponse `json:"11"` - } `json:"scores"` - Stats struct { - Hits []string `json:"hits"` - Errors []string `json:"errors"` - LeftOnBase []string `json:"left_on_base"` - Strikeouts []string `json:"strikeouts"` - Walks []string `json:"walks"` - HomeRuns []string `json:"home_runs"` - TotalBases []string `json:"total_bases"` - BattingAverage []string `json:"batting_average"` - } `json:"stats"` - Extra struct { - HomePos string `json:"home_pos"` - AwayPos string `json:"away_pos"` - StadiumData map[string]string `json:"stadium_data"` - Round string `json:"round"` - } `json:"extra"` - Events []map[string]string `json:"events"` - HasLineup int `json:"has_lineup"` - ConfirmedAt string `json:"confirmed_at"` - Bet365ID string `json:"bet365_id"` -} diff --git a/internal/domain/setting_list.go b/internal/domain/setting_list.go index b8b48f8..5011660 100644 --- a/internal/domain/setting_list.go +++ b/internal/domain/setting_list.go @@ -1,691 +1,691 @@ package domain -import ( - "errors" - "fmt" - "strconv" - "strings" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "go.uber.org/zap" -) - -var ( - ErrSettingNotFound = errors.New("cannot find setting in list") -) - -type SettingList struct { - SMSProvider SMSProvider `json:"sms_provider"` - MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"` - MaxUnsettledBets int64 `json:"max_unsettled_bets"` - BetAmountLimit Currency `json:"bet_amount_limit"` - DailyTicketPerIP int64 `json:"daily_ticket_limit"` - TotalWinningLimit Currency `json:"total_winning_limit"` - TotalWinningNotify Currency `json:"total_winning_notify"` - AmountForBetReferral Currency `json:"amount_for_bet_referral"` - CashbackAmountCap Currency `json:"cashback_amount_cap"` - DefaultWinningLimit int64 `json:"default_winning_limit"` - ReferralRewardAmount Currency `json:"referral_reward_amount"` - CashbackPercentage float32 `json:"cashback_percentage"` - DefaultMaxReferrals int64 `json:"default_max_referrals"` - MinimumBetAmount Currency `json:"minimum_bet_amount"` - BetDuplicateLimit int64 `json:"bet_duplicate_limit"` - SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"` - SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"` - WelcomeBonusActive bool `json:"welcome_bonus_active"` - WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"` - WelcomeBonusCap Currency `json:"welcome_bonus_cap"` - WelcomeBonusCount int64 `json:"welcome_bonus_count"` - WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"` -} - -// Default Global Settings added to the database if the setting isn't found -// This is already configured in the seed_data.sql, but just added it here too -// in case of database to code drift -func NewDefaultSettingList() SettingList { - return SettingList{ - SMSProvider: AfroMessage, //Provider that will be used for sending sms - MaxNumberOfOutcomes: 30, //Maximum number of outcomes for a single bet - MaxUnsettledBets: 100, //Maximum number of unsettled bets before system disabled bet service - BetAmountLimit: 10000000, //Maximum amount that can be bet (100,000.00 Birr Limit) - DailyTicketPerIP: 50, //Daily Number of Tickets That can be cut - TotalWinningLimit: 100000000, //Winning Limit (1,000,000.00 Birr) - TotalWinningNotify: 10000000, //Notify if user wins (100,000.00+ Birr) - AmountForBetReferral: 1000000, //Reward for bet referral (only for betting) (10,000.00 Birr) - CashbackAmountCap: 10.00, //Cashback amount limit (10 Birr) - DefaultWinningLimit: 5000000, //Birr Limit on single event (50,000.00) - ReferralRewardAmount: 10000, //Reward for user referral (100.00 Birr) - CashbackPercentage: 0.2, //Cashback Percent (20%) - DefaultMaxReferrals: 15, //Max number of user referrals (15) - MinimumBetAmount: 100, //Minimum Bet Amount (1 Birr) - BetDuplicateLimit: 5, //Maximum number of duplicate bets (5) - SendEmailOnBetFinish: true, //Whether to send email to user when he wins bet - SendSMSOnBetFinish: false, //Whether to send sms to user when he wins bet - WelcomeBonusActive: false, //Is Welcome Bonus in effect - WelcomeBonusMultiplier: 1.5, //Welcome Bonus Multiplier - WelcomeBonusCap: 100000, //Welcome Bonus Limit - WelcomeBonusCount: 3, //Maximum number of welcome bonuses given - WelcomeBonusExpire: 10, //Welcome Bonus Expiry - } -} - -func (s SettingList) ToSettingArray() []Setting { - return []Setting{ - {"sms_provider", string(s.SMSProvider), time.Now()}, - {"max_number_of_outcomes", strconv.FormatInt(s.MaxNumberOfOutcomes, 10), time.Now()}, - {"max_unsettled_bets", strconv.FormatInt(s.MaxUnsettledBets, 10), time.Now()}, - {"bet_amount_limit", strconv.FormatInt(int64(s.BetAmountLimit), 10), time.Now()}, - {"daily_ticket_limit", strconv.FormatInt(s.DailyTicketPerIP, 10), time.Now()}, - {"total_winnings_limit", strconv.FormatInt(int64(s.TotalWinningLimit), 10), time.Now()}, - {"total_winnings_notify", strconv.FormatInt(int64(s.TotalWinningNotify), 10), time.Now()}, - {"amount_for_bet_referral", strconv.FormatInt(int64(s.AmountForBetReferral), 10), time.Now()}, - {"cashback_amount_cap", strconv.FormatInt(int64(s.CashbackAmountCap), 10), time.Now()}, - {"default_winning_limit", strconv.FormatInt(s.DefaultWinningLimit, 10), time.Now()}, - {"referral_reward_amount", strconv.FormatInt(int64(s.ReferralRewardAmount), 10), time.Now()}, - {"cashback_percentage", strconv.FormatFloat(float64(s.CashbackPercentage), 'f', -1, 32), time.Now()}, - {"default_max_referrals", strconv.FormatInt(s.DefaultMaxReferrals, 10), time.Now()}, - {"minimum_bet_amount", strconv.FormatInt(int64(s.MinimumBetAmount), 10), time.Now()}, - {"bet_duplicate_limit", strconv.FormatInt(s.BetDuplicateLimit, 10), time.Now()}, - {"send_email_on_bet_finish", strconv.FormatBool(s.SendEmailOnBetFinish), time.Now()}, - {"send_sms_on_bet_finish", strconv.FormatBool(s.SendSMSOnBetFinish), time.Now()}, - {"welcome_bonus_active", strconv.FormatBool(s.WelcomeBonusActive), time.Now()}, - {"welcome_bonus_multiplier", strconv.FormatFloat(float64(s.WelcomeBonusMultiplier), 'f', -1, 32), time.Now()}, - {"welcome_bonus_cap", strconv.FormatInt(int64(s.WelcomeBonusCap), 10), time.Now()}, - {"welcome_bonus_count", strconv.FormatInt(s.WelcomeBonusCount, 10), time.Now()}, - {"welcome_bonus_expiry", strconv.FormatInt(s.WelcomeBonusExpire, 10), time.Now()}, - } -} - -type SettingListRes struct { - SMSProvider SMSProvider `json:"sms_provider"` - MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"` - MaxUnsettledBets int64 `json:"max_unsettled_bets"` - BetAmountLimit float32 `json:"bet_amount_limit"` - DailyTicketPerIP int64 `json:"daily_ticket_limit"` - TotalWinningLimit float32 `json:"total_winning_limit"` - TotalWinningNotify float32 `json:"total_winning_notify"` - AmountForBetReferral float32 `json:"amount_for_bet_referral"` - CashbackAmountCap float32 `json:"cashback_amount_cap"` - DefaultWinningLimit int64 `json:"default_winning_limit"` - ReferralRewardAmount float32 `json:"referral_reward_amount"` - CashbackPercentage float32 `json:"cashback_percentage"` - DefaultMaxReferrals int64 `json:"default_max_referrals"` - MinimumBetAmount float32 `json:"minimum_bet_amount"` - BetDuplicateLimit int64 `json:"bet_duplicate_limit"` - SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"` - SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"` - WelcomeBonusActive bool `json:"welcome_bonus_active"` - WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"` - WelcomeBonusCap float32 `json:"welcome_bonus_cap"` - WelcomeBonusCount int64 `json:"welcome_bonus_count"` - WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"` -} - -func ConvertSettingListRes(settings SettingList) SettingListRes { - return SettingListRes{ - SMSProvider: settings.SMSProvider, - MaxNumberOfOutcomes: settings.MaxNumberOfOutcomes, - MaxUnsettledBets: settings.MaxUnsettledBets, - BetAmountLimit: settings.BetAmountLimit.Float32(), - DailyTicketPerIP: settings.DailyTicketPerIP, - TotalWinningLimit: settings.TotalWinningLimit.Float32(), - TotalWinningNotify: settings.TotalWinningNotify.Float32(), - AmountForBetReferral: settings.AmountForBetReferral.Float32(), - CashbackAmountCap: settings.CashbackAmountCap.Float32(), - DefaultWinningLimit: settings.DefaultWinningLimit, - ReferralRewardAmount: settings.ReferralRewardAmount.Float32(), - CashbackPercentage: settings.CashbackPercentage, - DefaultMaxReferrals: settings.DefaultMaxReferrals, - MinimumBetAmount: settings.MinimumBetAmount.Float32(), - BetDuplicateLimit: settings.BetDuplicateLimit, - SendEmailOnBetFinish: settings.SendEmailOnBetFinish, - SendSMSOnBetFinish: settings.SendSMSOnBetFinish, - WelcomeBonusActive: settings.WelcomeBonusActive, - WelcomeBonusMultiplier: settings.WelcomeBonusMultiplier, - WelcomeBonusCap: settings.WelcomeBonusCap.Float32(), - WelcomeBonusCount: settings.WelcomeBonusCount, - WelcomeBonusExpire: settings.WelcomeBonusExpire, - } -} - -type SaveSettingListReq struct { - SMSProvider *string `json:"sms_provider,omitempty"` - MaxNumberOfOutcomes *int64 `json:"max_number_of_outcomes,omitempty"` - MaxUnsettledBets *int64 `json:"max_unsettled_bets,omitempty"` - BetAmountLimit *float32 `json:"bet_amount_limit,omitempty"` - DailyTicketPerIP *int64 `json:"daily_ticket_limit,omitempty"` - TotalWinningLimit *float32 `json:"total_winning_limit,omitempty"` - TotalWinningNotify *float32 `json:"total_winning_notify,omitempty"` - AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"` - CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"` - DefaultWinningLimit *int64 `json:"default_winning_limit,omitempty"` - ReferralRewardAmount *float32 `json:"referral_reward_amount,omitempty"` - CashbackPercentage *float32 `json:"cashback_percentage,omitempty"` - DefaultMaxReferrals *int64 `json:"default_max_referrals,omitempty"` - MinimumBetAmount *float32 `json:"minimum_bet_amount,omitempty"` - BetDuplicateLimit *int64 `json:"bet_duplicate_limit,omitempty"` - SendEmailOnBetFinish *bool `json:"send_email_on_bet_finish,omitempty"` - SendSMSOnBetFinish *bool `json:"send_sms_on_bet_finish,omitempty"` - WelcomeBonusActive *bool `json:"welcome_bonus_active,omitempty"` - WelcomeBonusMultiplier *float32 `json:"welcome_bonus_multiplier,omitempty"` - WelcomeBonusCap *float32 `json:"welcome_bonus_cap,omitempty"` - WelcomeBonusCount *int64 `json:"welcome_bonus_count,omitempty"` - WelcomeBonusExpire *int64 `json:"welcome_bonus_expiry,omitempty"` -} - -type ValidSettingList struct { - SMSProvider ValidString - MaxNumberOfOutcomes ValidInt64 - MaxUnsettledBets ValidInt64 - BetAmountLimit ValidCurrency - DailyTicketPerIP ValidInt64 - TotalWinningLimit ValidCurrency - TotalWinningNotify ValidCurrency - AmountForBetReferral ValidCurrency - CashbackAmountCap ValidCurrency - DefaultWinningLimit ValidInt64 - ReferralRewardAmount ValidCurrency - CashbackPercentage ValidFloat32 - DefaultMaxReferrals ValidInt64 - MinimumBetAmount ValidCurrency - BetDuplicateLimit ValidInt64 - SendEmailOnBetFinish ValidBool - SendSMSOnBetFinish ValidBool - WelcomeBonusActive ValidBool - WelcomeBonusMultiplier ValidFloat32 - WelcomeBonusCap ValidCurrency - WelcomeBonusCount ValidInt64 - WelcomeBonusExpire ValidInt64 -} - -func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList { - return ValidSettingList{ - SMSProvider: ConvertStringPtr(settings.SMSProvider), - MaxNumberOfOutcomes: ConvertInt64Ptr(settings.MaxNumberOfOutcomes), - MaxUnsettledBets: ConvertInt64Ptr(settings.MaxUnsettledBets), - BetAmountLimit: ConvertFloat32PtrToCurrency(settings.BetAmountLimit), - DailyTicketPerIP: ConvertInt64Ptr(settings.DailyTicketPerIP), - TotalWinningLimit: ConvertFloat32PtrToCurrency(settings.TotalWinningLimit), - TotalWinningNotify: ConvertFloat32PtrToCurrency(settings.TotalWinningNotify), - AmountForBetReferral: ConvertFloat32PtrToCurrency(settings.AmountForBetReferral), - CashbackAmountCap: ConvertFloat32PtrToCurrency(settings.CashbackAmountCap), - DefaultWinningLimit: ConvertInt64Ptr(settings.DefaultWinningLimit), - ReferralRewardAmount: ConvertFloat32PtrToCurrency(settings.ReferralRewardAmount), - CashbackPercentage: ConvertFloat32Ptr(settings.CashbackPercentage), - DefaultMaxReferrals: ConvertInt64Ptr(settings.DefaultMaxReferrals), - MinimumBetAmount: ConvertFloat32PtrToCurrency(settings.MinimumBetAmount), - BetDuplicateLimit: ConvertInt64Ptr(settings.BetDuplicateLimit), - SendEmailOnBetFinish: ConvertBoolPtr(settings.SendEmailOnBetFinish), - SendSMSOnBetFinish: ConvertBoolPtr(settings.SendSMSOnBetFinish), - WelcomeBonusActive: ConvertBoolPtr(settings.WelcomeBonusActive), - WelcomeBonusMultiplier: ConvertFloat32Ptr(settings.WelcomeBonusMultiplier), - WelcomeBonusCap: ConvertFloat32PtrToCurrency(settings.WelcomeBonusCap), - WelcomeBonusCount: ConvertInt64Ptr(settings.WelcomeBonusCount), - WelcomeBonusExpire: ConvertInt64Ptr(settings.WelcomeBonusExpire), - } -} - -// Always make sure to run the validation before converting this -func (vsl *ValidSettingList) ToSettingList() SettingList { - return SettingList{ - SMSProvider: SMSProvider(vsl.SMSProvider.Value), - MaxNumberOfOutcomes: vsl.MaxNumberOfOutcomes.Value, - MaxUnsettledBets: vsl.MaxUnsettledBets.Value, - BetAmountLimit: vsl.BetAmountLimit.Value, - DailyTicketPerIP: vsl.DailyTicketPerIP.Value, - TotalWinningLimit: vsl.TotalWinningLimit.Value, - TotalWinningNotify: vsl.TotalWinningNotify.Value, - AmountForBetReferral: vsl.AmountForBetReferral.Value, - CashbackAmountCap: vsl.CashbackAmountCap.Value, - DefaultWinningLimit: vsl.DefaultWinningLimit.Value, - ReferralRewardAmount: vsl.ReferralRewardAmount.Value, - CashbackPercentage: vsl.CashbackPercentage.Value, - DefaultMaxReferrals: vsl.DefaultMaxReferrals.Value, - MinimumBetAmount: vsl.MinimumBetAmount.Value, - BetDuplicateLimit: vsl.BetDuplicateLimit.Value, - SendEmailOnBetFinish: vsl.SendEmailOnBetFinish.Value, - SendSMSOnBetFinish: vsl.SendSMSOnBetFinish.Value, - WelcomeBonusActive: vsl.WelcomeBonusActive.Value, - WelcomeBonusMultiplier: vsl.WelcomeBonusMultiplier.Value, - WelcomeBonusCap: vsl.WelcomeBonusCap.Value, - WelcomeBonusCount: vsl.WelcomeBonusCount.Value, - WelcomeBonusExpire: vsl.WelcomeBonusExpire.Value, - } -} - -// Custom Validations for non-generic types -func (vsl *ValidSettingList) CustomValidationSettings() error { - if !SMSProvider(vsl.SMSProvider.Value).IsValid() { - return fmt.Errorf("sms provider invalid") - } - return nil -} - -func (vsl *ValidSettingList) GetInt64SettingsMap() map[string]*ValidInt64 { - return map[string]*ValidInt64{ - "max_number_of_outcomes": &vsl.MaxNumberOfOutcomes, - "max_unsettled_bets": &vsl.MaxUnsettledBets, - "daily_ticket_limit": &vsl.DailyTicketPerIP, - "default_winning_limit": &vsl.DefaultWinningLimit, - "default_max_referrals": &vsl.DefaultMaxReferrals, - "bet_duplicate_limit": &vsl.BetDuplicateLimit, - "welcome_bonus_count": &vsl.WelcomeBonusCount, - "welcome_bonus_expiry": &vsl.WelcomeBonusExpire, - } -} - -func (vsl *ValidSettingList) GetCurrencySettingsMap() map[string]*ValidCurrency { - return map[string]*ValidCurrency{ - "bet_amount_limit": &vsl.BetAmountLimit, - "total_winnings_limit": &vsl.TotalWinningLimit, - "total_winnings_notify": &vsl.TotalWinningNotify, - "amount_for_bet_referral": &vsl.AmountForBetReferral, - "cashback_amount_cap": &vsl.CashbackAmountCap, - "referral_reward_amount": &vsl.ReferralRewardAmount, - "minimum_bet_amount": &vsl.MinimumBetAmount, - "welcome_bonus_cap": &vsl.WelcomeBonusCap, - } -} - -func (vsl *ValidSettingList) GetStringSettingsMap() map[string]*ValidString { - return map[string]*ValidString{ - "sms_provider": &vsl.SMSProvider, - } -} - -func (vsl *ValidSettingList) GetBoolSettingsMap() map[string]*ValidBool { - return map[string]*ValidBool{ - "send_email_on_bet_finish": &vsl.SendEmailOnBetFinish, - "send_sms_on_bet_finish": &vsl.SendSMSOnBetFinish, - "welcome_bonus_active": &vsl.WelcomeBonusActive, - } -} - -func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 { - return map[string]*ValidFloat32{ - "cashback_percentage": &vsl.CashbackPercentage, - "welcome_bonus_multiplier": &vsl.WelcomeBonusMultiplier, - } -} - -func (vsl *ValidSettingList) GetTimeSettingsMap() map[string]*ValidTime { - return map[string]*ValidTime{} -} - -// Setting Functions - -func (vsl *ValidSettingList) GetTotalSettings() int { - return len(vsl.GetInt64SettingsMap()) + - len(vsl.GetCurrencySettingsMap()) + - len(vsl.GetStringSettingsMap()) + - len(vsl.GetBoolSettingsMap()) + - len(vsl.GetFloat32SettingsMap()) + - len(vsl.GetTimeSettingsMap()) -} - -func (vsl *ValidSettingList) GetAllValid() map[string]*bool { - - settingValid := make(map[string]*bool) - - for key, setting := range vsl.GetInt64SettingsMap() { - settingValid[key] = &(*setting).Valid - } - for key, setting := range vsl.GetCurrencySettingsMap() { - settingValid[key] = &(*setting).Valid - } - for key, setting := range vsl.GetStringSettingsMap() { - settingValid[key] = &(*setting).Valid - } - for key, setting := range vsl.GetBoolSettingsMap() { - settingValid[key] = &(*setting).Valid - } - for key, setting := range vsl.GetFloat32SettingsMap() { - settingValid[key] = &(*setting).Valid - } - for key, setting := range vsl.GetTimeSettingsMap() { - settingValid[key] = &(*setting).Valid - } - - return settingValid -} - -// func setValidSetting[T any](settings map[string]*T, searchKey string, searchVal string, setVal func(string) (T, error)) error { -// for key, setting := range settings { - -// if key == searchKey { -// s, err := setVal(searchVal) -// if err != nil { -// return err -// } -// *setting = s -// } -// return nil -// } -// return ErrSettingNotFound -// } -func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error { - for key, setting := range vsl.GetInt64SettingsMap() { - if key == searchKey { - value, err := strconv.ParseInt(searchVal, 10, 64) - if err != nil { - return err - } - *setting = ValidInt64{Value: value, Valid: true} - return nil - } - } - return ErrSettingNotFound -} - -func (vsl *ValidSettingList) SetCurrencySetting(searchKey string, searchVal string) error { - for key, setting := range vsl.GetCurrencySettingsMap() { - if key == searchKey { - value, err := strconv.ParseInt(searchVal, 10, 64) - if err != nil { - return err - } - *setting = ValidCurrency{Value: Currency(value), Valid: true} - return nil - } - } - - return ErrSettingNotFound -} - -func (vsl *ValidSettingList) SetStringSetting(searchKey string, searchVal string) error { - for key, setting := range vsl.GetStringSettingsMap() { - if key == searchKey { - *setting = ValidString{Value: searchVal, Valid: true} - return nil - } - } - - return ErrSettingNotFound -} - -func (vsl *ValidSettingList) SetBoolSetting(searchKey string, searchVal string) error { - for key, setting := range vsl.GetBoolSettingsMap() { - - if key == searchKey { - value, err := strconv.ParseBool(searchVal) - if err != nil { - return err - } - - *setting = ValidBool{Value: value, Valid: true} - return nil - } - - } - - return ErrSettingNotFound -} - -func (vsl *ValidSettingList) SetFloat32Setting(searchKey string, searchVal string) error { - for key, setting := range vsl.GetFloat32SettingsMap() { - if key == searchKey { - value, err := strconv.ParseFloat(searchVal, 32) - if err != nil { - return err - } - *setting = ValidFloat32{Value: float32(value), Valid: true} - return nil - } - } - - return ErrSettingNotFound -} - -func (vsl *ValidSettingList) SetTimeSetting(searchKey string, searchVal string) error { - for key, setting := range vsl.GetTimeSettingsMap() { - if key == searchKey { - value, err := time.Parse(time.RFC3339, searchVal) - if err != nil { - return err - } - *setting = ValidTime{Value: value, Valid: true} - return nil - } - } - return ErrSettingNotFound -} - -func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) error { - setters := []func(string, string) error{ - vsl.SetInt64Setting, - vsl.SetCurrencySetting, - vsl.SetStringSetting, - vsl.SetBoolSetting, - vsl.SetFloat32Setting, - vsl.SetTimeSetting, - } - - for _, setter := range setters { - if err := setter(searchKey, searchVal); err != nil { - if err == ErrSettingNotFound { - // fmt.Printf("setting is not found %v \n", searchKey) - continue // not this setter, try the next - } - return fmt.Errorf("error while processing setting %q: %w \n", searchKey, err) - } - return nil // successfully set - } - - // If we get here, none of the setters matched - return ErrSettingNotFound -} - -func convertValidSettings[T any]( - settings map[string]*T, - isValid func(*T) bool, - toString func(*T) string, -) []Setting { - result := make([]Setting, 0, len(settings)) - for key, s := range settings { - if isValid(s) { - result = append(result, Setting{ - Key: key, - Value: toString(s), - }) - } - } - return result -} - -func (vsl *ValidSettingList) ConvertInt64Settings() []Setting { - return convertValidSettings( - vsl.GetInt64SettingsMap(), - func(s *ValidInt64) bool { return s.Valid }, - func(s *ValidInt64) string { return strconv.FormatInt(s.Value, 10) }, - ) -} - -func (vsl *ValidSettingList) ConvertCurrencySettings() []Setting { - return convertValidSettings( - vsl.GetCurrencySettingsMap(), - func(s *ValidCurrency) bool { return s.Valid }, - func(s *ValidCurrency) string { return strconv.FormatInt(int64(s.Value), 10) }, - ) -} - -func (vsl *ValidSettingList) ConvertStringSettings() []Setting { - return convertValidSettings( - vsl.GetStringSettingsMap(), - func(s *ValidString) bool { return s.Valid }, - func(s *ValidString) string { return s.Value }, - ) -} - -func (vsl *ValidSettingList) ConvertBoolSettings() []Setting { - return convertValidSettings( - vsl.GetBoolSettingsMap(), - func(s *ValidBool) bool { return s.Valid }, - func(s *ValidBool) string { return strconv.FormatBool(s.Value) }, - ) -} - -func (vsl *ValidSettingList) ConvertFloat32Settings() []Setting { - return convertValidSettings( - vsl.GetFloat32SettingsMap(), - func(s *ValidFloat32) bool { return s.Valid }, - func(s *ValidFloat32) string { return strconv.FormatFloat(float64(s.Value), 'f', -1, 32) }, - ) -} - -func (vsl *ValidSettingList) ConvertTimeSettings() []Setting { - return convertValidSettings( - vsl.GetTimeSettingsMap(), - func(s *ValidTime) bool { return s.Valid }, - func(s *ValidTime) string { return s.Value.Format(time.RFC3339) }, - ) -} - -func validateSettings[T any]( - settings map[string]*T, - customValidator func(*T) bool, -) error { - var errs []string - for key, s := range settings { - if !customValidator(s) { - - errs = append(errs, fmt.Sprintf("%v is invalid", key)) - } - } - if len(errs) > 0 { - return errors.New(strings.Join(errs, "; ")) - } - - return nil -} - -func (vsl *ValidSettingList) ValidateInt64Settings() error { - return validateSettings(vsl.GetInt64SettingsMap(), - func(s *ValidInt64) bool { - return s.Valid - }, - ) -} - -func (vsl *ValidSettingList) ValidateCurrencySettings() error { - return validateSettings(vsl.GetCurrencySettingsMap(), - func(s *ValidCurrency) bool { - return s.Valid - }, - ) -} - -func (vsl *ValidSettingList) ValidateStringSettings() error { - return validateSettings(vsl.GetStringSettingsMap(), - func(s *ValidString) bool { - return s.Valid - }, - ) -} - -func (vsl *ValidSettingList) ValidateBoolSettings() error { - return validateSettings(vsl.GetBoolSettingsMap(), - func(s *ValidBool) bool { - return s.Valid - }, - ) -} - -func (vsl *ValidSettingList) ValidateFloat32Settings() error { - return validateSettings(vsl.GetFloat32SettingsMap(), - func(s *ValidFloat32) bool { - return s.Valid - }, - ) -} - -func (vsl *ValidSettingList) ValidateTimeSettings() error { - return validateSettings(vsl.GetTimeSettingsMap(), - func(s *ValidTime) bool { - return s.Valid - }, - ) -} - -func (vsl *ValidSettingList) ValidateAllSettings() error { - var errs []string - validators := []func() error{ - vsl.ValidateInt64Settings, - vsl.ValidateCurrencySettings, - vsl.ValidateStringSettings, - vsl.ValidateBoolSettings, - vsl.ValidateFloat32Settings, - vsl.ValidateTimeSettings, - vsl.CustomValidationSettings, - } - - for _, validator := range validators { - if err := validator(); err != nil { - - errs = append(errs, err.Error()) - } - } - if len(errs) > 0 { - return errors.New(strings.Join(errs, "; ")) - } - - return nil -} - -func (vsl *ValidSettingList) ConvertAllSettings() []Setting { - totalCap := vsl.GetTotalSettings() - all := make([]Setting, 0, totalCap) - - all = append(all, vsl.ConvertInt64Settings()...) - all = append(all, vsl.ConvertCurrencySettings()...) - all = append(all, vsl.ConvertStringSettings()...) - all = append(all, vsl.ConvertBoolSettings()...) - all = append(all, vsl.ConvertFloat32Settings()...) - all = append(all, vsl.ConvertTimeSettings()...) - - return all -} - -func ConvertDBGlobalSettingList(settings []dbgen.GlobalSetting) (SettingList, error) { - var dbSettingList ValidSettingList - - for _, setting := range settings { - if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil { - if err == ErrSettingNotFound { - MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key)) - continue - } - MongoDBLogger.Error("unknown error while fetching settings", zap.Error(err)) - } - } - - if err := dbSettingList.ValidateAllSettings(); err != nil { - MongoDBLogger.Warn("setting validation error", zap.Error(err), zap.Any("db_setting_list", dbSettingList)) - return SettingList{}, err - } - - settingList := dbSettingList.ToSettingList() - - return settingList, nil -} - -func ConvertDBOverrideSettingList(settings []dbgen.GetOverrideSettingsRow) (SettingList, error) { - var dbSettingList ValidSettingList - - for _, setting := range settings { - if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil { - if err == ErrSettingNotFound { - MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key)) - } - } - } - - if err := dbSettingList.ValidateAllSettings(); err != nil { - MongoDBLogger.Warn("setting validation error", zap.Error(err)) - return SettingList{}, err - } - - settingList := dbSettingList.ToSettingList() - - return settingList, nil -} +// import ( +// "errors" +// "fmt" +// "strconv" +// "strings" +// "time" + +// dbgen "Yimaru-Backend/gen/db" +// "go.uber.org/zap" +// ) + +// var ( +// ErrSettingNotFound = errors.New("cannot find setting in list") +// ) + +// type SettingList struct { +// SMSProvider SMSProvider `json:"sms_provider"` +// MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"` +// MaxUnsettledBets int64 `json:"max_unsettled_bets"` +// BetAmountLimit Currency `json:"bet_amount_limit"` +// DailyTicketPerIP int64 `json:"daily_ticket_limit"` +// TotalWinningLimit Currency `json:"total_winning_limit"` +// TotalWinningNotify Currency `json:"total_winning_notify"` +// AmountForBetReferral Currency `json:"amount_for_bet_referral"` +// CashbackAmountCap Currency `json:"cashback_amount_cap"` +// DefaultWinningLimit int64 `json:"default_winning_limit"` +// ReferralRewardAmount Currency `json:"referral_reward_amount"` +// CashbackPercentage float32 `json:"cashback_percentage"` +// DefaultMaxReferrals int64 `json:"default_max_referrals"` +// MinimumBetAmount Currency `json:"minimum_bet_amount"` +// BetDuplicateLimit int64 `json:"bet_duplicate_limit"` +// SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"` +// SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"` +// WelcomeBonusActive bool `json:"welcome_bonus_active"` +// WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"` +// WelcomeBonusCap Currency `json:"welcome_bonus_cap"` +// WelcomeBonusCount int64 `json:"welcome_bonus_count"` +// WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"` +// } + +// // Default Global Settings added to the database if the setting isn't found +// // This is already configured in the seed_data.sql, but just added it here too +// // in case of database to code drift +// func NewDefaultSettingList() SettingList { +// return SettingList{ +// SMSProvider: AfroMessage, //Provider that will be used for sending sms +// MaxNumberOfOutcomes: 30, //Maximum number of outcomes for a single bet +// MaxUnsettledBets: 100, //Maximum number of unsettled bets before system disabled bet service +// BetAmountLimit: 10000000, //Maximum amount that can be bet (100,000.00 Birr Limit) +// DailyTicketPerIP: 50, //Daily Number of Tickets That can be cut +// TotalWinningLimit: 100000000, //Winning Limit (1,000,000.00 Birr) +// TotalWinningNotify: 10000000, //Notify if user wins (100,000.00+ Birr) +// AmountForBetReferral: 1000000, //Reward for bet referral (only for betting) (10,000.00 Birr) +// CashbackAmountCap: 10.00, //Cashback amount limit (10 Birr) +// DefaultWinningLimit: 5000000, //Birr Limit on single event (50,000.00) +// ReferralRewardAmount: 10000, //Reward for user referral (100.00 Birr) +// CashbackPercentage: 0.2, //Cashback Percent (20%) +// DefaultMaxReferrals: 15, //Max number of user referrals (15) +// MinimumBetAmount: 100, //Minimum Bet Amount (1 Birr) +// BetDuplicateLimit: 5, //Maximum number of duplicate bets (5) +// SendEmailOnBetFinish: true, //Whether to send email to user when he wins bet +// SendSMSOnBetFinish: false, //Whether to send sms to user when he wins bet +// WelcomeBonusActive: false, //Is Welcome Bonus in effect +// WelcomeBonusMultiplier: 1.5, //Welcome Bonus Multiplier +// WelcomeBonusCap: 100000, //Welcome Bonus Limit +// WelcomeBonusCount: 3, //Maximum number of welcome bonuses given +// WelcomeBonusExpire: 10, //Welcome Bonus Expiry +// } +// } + +// func (s SettingList) ToSettingArray() []Setting { +// return []Setting{ +// {"sms_provider", string(s.SMSProvider), time.Now()}, +// {"max_number_of_outcomes", strconv.FormatInt(s.MaxNumberOfOutcomes, 10), time.Now()}, +// {"max_unsettled_bets", strconv.FormatInt(s.MaxUnsettledBets, 10), time.Now()}, +// {"bet_amount_limit", strconv.FormatInt(int64(s.BetAmountLimit), 10), time.Now()}, +// {"daily_ticket_limit", strconv.FormatInt(s.DailyTicketPerIP, 10), time.Now()}, +// {"total_winnings_limit", strconv.FormatInt(int64(s.TotalWinningLimit), 10), time.Now()}, +// {"total_winnings_notify", strconv.FormatInt(int64(s.TotalWinningNotify), 10), time.Now()}, +// {"amount_for_bet_referral", strconv.FormatInt(int64(s.AmountForBetReferral), 10), time.Now()}, +// {"cashback_amount_cap", strconv.FormatInt(int64(s.CashbackAmountCap), 10), time.Now()}, +// {"default_winning_limit", strconv.FormatInt(s.DefaultWinningLimit, 10), time.Now()}, +// {"referral_reward_amount", strconv.FormatInt(int64(s.ReferralRewardAmount), 10), time.Now()}, +// {"cashback_percentage", strconv.FormatFloat(float64(s.CashbackPercentage), 'f', -1, 32), time.Now()}, +// {"default_max_referrals", strconv.FormatInt(s.DefaultMaxReferrals, 10), time.Now()}, +// {"minimum_bet_amount", strconv.FormatInt(int64(s.MinimumBetAmount), 10), time.Now()}, +// {"bet_duplicate_limit", strconv.FormatInt(s.BetDuplicateLimit, 10), time.Now()}, +// {"send_email_on_bet_finish", strconv.FormatBool(s.SendEmailOnBetFinish), time.Now()}, +// {"send_sms_on_bet_finish", strconv.FormatBool(s.SendSMSOnBetFinish), time.Now()}, +// {"welcome_bonus_active", strconv.FormatBool(s.WelcomeBonusActive), time.Now()}, +// {"welcome_bonus_multiplier", strconv.FormatFloat(float64(s.WelcomeBonusMultiplier), 'f', -1, 32), time.Now()}, +// {"welcome_bonus_cap", strconv.FormatInt(int64(s.WelcomeBonusCap), 10), time.Now()}, +// {"welcome_bonus_count", strconv.FormatInt(s.WelcomeBonusCount, 10), time.Now()}, +// {"welcome_bonus_expiry", strconv.FormatInt(s.WelcomeBonusExpire, 10), time.Now()}, +// } +// } + +// type SettingListRes struct { +// SMSProvider SMSProvider `json:"sms_provider"` +// MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"` +// MaxUnsettledBets int64 `json:"max_unsettled_bets"` +// BetAmountLimit float32 `json:"bet_amount_limit"` +// DailyTicketPerIP int64 `json:"daily_ticket_limit"` +// TotalWinningLimit float32 `json:"total_winning_limit"` +// TotalWinningNotify float32 `json:"total_winning_notify"` +// AmountForBetReferral float32 `json:"amount_for_bet_referral"` +// CashbackAmountCap float32 `json:"cashback_amount_cap"` +// DefaultWinningLimit int64 `json:"default_winning_limit"` +// ReferralRewardAmount float32 `json:"referral_reward_amount"` +// CashbackPercentage float32 `json:"cashback_percentage"` +// DefaultMaxReferrals int64 `json:"default_max_referrals"` +// MinimumBetAmount float32 `json:"minimum_bet_amount"` +// BetDuplicateLimit int64 `json:"bet_duplicate_limit"` +// SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"` +// SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"` +// WelcomeBonusActive bool `json:"welcome_bonus_active"` +// WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"` +// WelcomeBonusCap float32 `json:"welcome_bonus_cap"` +// WelcomeBonusCount int64 `json:"welcome_bonus_count"` +// WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"` +// } + +// func ConvertSettingListRes(settings SettingList) SettingListRes { +// return SettingListRes{ +// SMSProvider: settings.SMSProvider, +// MaxNumberOfOutcomes: settings.MaxNumberOfOutcomes, +// MaxUnsettledBets: settings.MaxUnsettledBets, +// BetAmountLimit: settings.BetAmountLimit.Float32(), +// DailyTicketPerIP: settings.DailyTicketPerIP, +// TotalWinningLimit: settings.TotalWinningLimit.Float32(), +// TotalWinningNotify: settings.TotalWinningNotify.Float32(), +// AmountForBetReferral: settings.AmountForBetReferral.Float32(), +// CashbackAmountCap: settings.CashbackAmountCap.Float32(), +// DefaultWinningLimit: settings.DefaultWinningLimit, +// ReferralRewardAmount: settings.ReferralRewardAmount.Float32(), +// CashbackPercentage: settings.CashbackPercentage, +// DefaultMaxReferrals: settings.DefaultMaxReferrals, +// MinimumBetAmount: settings.MinimumBetAmount.Float32(), +// BetDuplicateLimit: settings.BetDuplicateLimit, +// SendEmailOnBetFinish: settings.SendEmailOnBetFinish, +// SendSMSOnBetFinish: settings.SendSMSOnBetFinish, +// WelcomeBonusActive: settings.WelcomeBonusActive, +// WelcomeBonusMultiplier: settings.WelcomeBonusMultiplier, +// WelcomeBonusCap: settings.WelcomeBonusCap.Float32(), +// WelcomeBonusCount: settings.WelcomeBonusCount, +// WelcomeBonusExpire: settings.WelcomeBonusExpire, +// } +// } + +// type SaveSettingListReq struct { +// SMSProvider *string `json:"sms_provider,omitempty"` +// MaxNumberOfOutcomes *int64 `json:"max_number_of_outcomes,omitempty"` +// MaxUnsettledBets *int64 `json:"max_unsettled_bets,omitempty"` +// BetAmountLimit *float32 `json:"bet_amount_limit,omitempty"` +// DailyTicketPerIP *int64 `json:"daily_ticket_limit,omitempty"` +// TotalWinningLimit *float32 `json:"total_winning_limit,omitempty"` +// TotalWinningNotify *float32 `json:"total_winning_notify,omitempty"` +// AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"` +// CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"` +// DefaultWinningLimit *int64 `json:"default_winning_limit,omitempty"` +// ReferralRewardAmount *float32 `json:"referral_reward_amount,omitempty"` +// CashbackPercentage *float32 `json:"cashback_percentage,omitempty"` +// DefaultMaxReferrals *int64 `json:"default_max_referrals,omitempty"` +// MinimumBetAmount *float32 `json:"minimum_bet_amount,omitempty"` +// BetDuplicateLimit *int64 `json:"bet_duplicate_limit,omitempty"` +// SendEmailOnBetFinish *bool `json:"send_email_on_bet_finish,omitempty"` +// SendSMSOnBetFinish *bool `json:"send_sms_on_bet_finish,omitempty"` +// WelcomeBonusActive *bool `json:"welcome_bonus_active,omitempty"` +// WelcomeBonusMultiplier *float32 `json:"welcome_bonus_multiplier,omitempty"` +// WelcomeBonusCap *float32 `json:"welcome_bonus_cap,omitempty"` +// WelcomeBonusCount *int64 `json:"welcome_bonus_count,omitempty"` +// WelcomeBonusExpire *int64 `json:"welcome_bonus_expiry,omitempty"` +// } + +// type ValidSettingList struct { +// SMSProvider ValidString +// MaxNumberOfOutcomes ValidInt64 +// MaxUnsettledBets ValidInt64 +// BetAmountLimit ValidCurrency +// DailyTicketPerIP ValidInt64 +// TotalWinningLimit ValidCurrency +// TotalWinningNotify ValidCurrency +// AmountForBetReferral ValidCurrency +// CashbackAmountCap ValidCurrency +// DefaultWinningLimit ValidInt64 +// ReferralRewardAmount ValidCurrency +// CashbackPercentage ValidFloat32 +// DefaultMaxReferrals ValidInt64 +// MinimumBetAmount ValidCurrency +// BetDuplicateLimit ValidInt64 +// SendEmailOnBetFinish ValidBool +// SendSMSOnBetFinish ValidBool +// WelcomeBonusActive ValidBool +// WelcomeBonusMultiplier ValidFloat32 +// WelcomeBonusCap ValidCurrency +// WelcomeBonusCount ValidInt64 +// WelcomeBonusExpire ValidInt64 +// } + +// func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList { +// return ValidSettingList{ +// SMSProvider: ConvertStringPtr(settings.SMSProvider), +// MaxNumberOfOutcomes: ConvertInt64Ptr(settings.MaxNumberOfOutcomes), +// MaxUnsettledBets: ConvertInt64Ptr(settings.MaxUnsettledBets), +// BetAmountLimit: ConvertFloat32PtrToCurrency(settings.BetAmountLimit), +// DailyTicketPerIP: ConvertInt64Ptr(settings.DailyTicketPerIP), +// TotalWinningLimit: ConvertFloat32PtrToCurrency(settings.TotalWinningLimit), +// TotalWinningNotify: ConvertFloat32PtrToCurrency(settings.TotalWinningNotify), +// AmountForBetReferral: ConvertFloat32PtrToCurrency(settings.AmountForBetReferral), +// CashbackAmountCap: ConvertFloat32PtrToCurrency(settings.CashbackAmountCap), +// DefaultWinningLimit: ConvertInt64Ptr(settings.DefaultWinningLimit), +// ReferralRewardAmount: ConvertFloat32PtrToCurrency(settings.ReferralRewardAmount), +// CashbackPercentage: ConvertFloat32Ptr(settings.CashbackPercentage), +// DefaultMaxReferrals: ConvertInt64Ptr(settings.DefaultMaxReferrals), +// MinimumBetAmount: ConvertFloat32PtrToCurrency(settings.MinimumBetAmount), +// BetDuplicateLimit: ConvertInt64Ptr(settings.BetDuplicateLimit), +// SendEmailOnBetFinish: ConvertBoolPtr(settings.SendEmailOnBetFinish), +// SendSMSOnBetFinish: ConvertBoolPtr(settings.SendSMSOnBetFinish), +// WelcomeBonusActive: ConvertBoolPtr(settings.WelcomeBonusActive), +// WelcomeBonusMultiplier: ConvertFloat32Ptr(settings.WelcomeBonusMultiplier), +// WelcomeBonusCap: ConvertFloat32PtrToCurrency(settings.WelcomeBonusCap), +// WelcomeBonusCount: ConvertInt64Ptr(settings.WelcomeBonusCount), +// WelcomeBonusExpire: ConvertInt64Ptr(settings.WelcomeBonusExpire), +// } +// } + +// // Always make sure to run the validation before converting this +// func (vsl *ValidSettingList) ToSettingList() SettingList { +// return SettingList{ +// SMSProvider: SMSProvider(vsl.SMSProvider.Value), +// MaxNumberOfOutcomes: vsl.MaxNumberOfOutcomes.Value, +// MaxUnsettledBets: vsl.MaxUnsettledBets.Value, +// BetAmountLimit: vsl.BetAmountLimit.Value, +// DailyTicketPerIP: vsl.DailyTicketPerIP.Value, +// TotalWinningLimit: vsl.TotalWinningLimit.Value, +// TotalWinningNotify: vsl.TotalWinningNotify.Value, +// AmountForBetReferral: vsl.AmountForBetReferral.Value, +// CashbackAmountCap: vsl.CashbackAmountCap.Value, +// DefaultWinningLimit: vsl.DefaultWinningLimit.Value, +// ReferralRewardAmount: vsl.ReferralRewardAmount.Value, +// CashbackPercentage: vsl.CashbackPercentage.Value, +// DefaultMaxReferrals: vsl.DefaultMaxReferrals.Value, +// MinimumBetAmount: vsl.MinimumBetAmount.Value, +// BetDuplicateLimit: vsl.BetDuplicateLimit.Value, +// SendEmailOnBetFinish: vsl.SendEmailOnBetFinish.Value, +// SendSMSOnBetFinish: vsl.SendSMSOnBetFinish.Value, +// WelcomeBonusActive: vsl.WelcomeBonusActive.Value, +// WelcomeBonusMultiplier: vsl.WelcomeBonusMultiplier.Value, +// WelcomeBonusCap: vsl.WelcomeBonusCap.Value, +// WelcomeBonusCount: vsl.WelcomeBonusCount.Value, +// WelcomeBonusExpire: vsl.WelcomeBonusExpire.Value, +// } +// } + +// // Custom Validations for non-generic types +// func (vsl *ValidSettingList) CustomValidationSettings() error { +// if !SMSProvider(vsl.SMSProvider.Value).IsValid() { +// return fmt.Errorf("sms provider invalid") +// } +// return nil +// } + +// func (vsl *ValidSettingList) GetInt64SettingsMap() map[string]*ValidInt64 { +// return map[string]*ValidInt64{ +// "max_number_of_outcomes": &vsl.MaxNumberOfOutcomes, +// "max_unsettled_bets": &vsl.MaxUnsettledBets, +// "daily_ticket_limit": &vsl.DailyTicketPerIP, +// "default_winning_limit": &vsl.DefaultWinningLimit, +// "default_max_referrals": &vsl.DefaultMaxReferrals, +// "bet_duplicate_limit": &vsl.BetDuplicateLimit, +// "welcome_bonus_count": &vsl.WelcomeBonusCount, +// "welcome_bonus_expiry": &vsl.WelcomeBonusExpire, +// } +// } + +// func (vsl *ValidSettingList) GetCurrencySettingsMap() map[string]*ValidCurrency { +// return map[string]*ValidCurrency{ +// "bet_amount_limit": &vsl.BetAmountLimit, +// "total_winnings_limit": &vsl.TotalWinningLimit, +// "total_winnings_notify": &vsl.TotalWinningNotify, +// "amount_for_bet_referral": &vsl.AmountForBetReferral, +// "cashback_amount_cap": &vsl.CashbackAmountCap, +// "referral_reward_amount": &vsl.ReferralRewardAmount, +// "minimum_bet_amount": &vsl.MinimumBetAmount, +// "welcome_bonus_cap": &vsl.WelcomeBonusCap, +// } +// } + +// func (vsl *ValidSettingList) GetStringSettingsMap() map[string]*ValidString { +// return map[string]*ValidString{ +// "sms_provider": &vsl.SMSProvider, +// } +// } + +// func (vsl *ValidSettingList) GetBoolSettingsMap() map[string]*ValidBool { +// return map[string]*ValidBool{ +// "send_email_on_bet_finish": &vsl.SendEmailOnBetFinish, +// "send_sms_on_bet_finish": &vsl.SendSMSOnBetFinish, +// "welcome_bonus_active": &vsl.WelcomeBonusActive, +// } +// } + +// func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 { +// return map[string]*ValidFloat32{ +// "cashback_percentage": &vsl.CashbackPercentage, +// "welcome_bonus_multiplier": &vsl.WelcomeBonusMultiplier, +// } +// } + +// func (vsl *ValidSettingList) GetTimeSettingsMap() map[string]*ValidTime { +// return map[string]*ValidTime{} +// } + +// // Setting Functions + +// func (vsl *ValidSettingList) GetTotalSettings() int { +// return len(vsl.GetInt64SettingsMap()) + +// len(vsl.GetCurrencySettingsMap()) + +// len(vsl.GetStringSettingsMap()) + +// len(vsl.GetBoolSettingsMap()) + +// len(vsl.GetFloat32SettingsMap()) + +// len(vsl.GetTimeSettingsMap()) +// } + +// func (vsl *ValidSettingList) GetAllValid() map[string]*bool { + +// settingValid := make(map[string]*bool) + +// for key, setting := range vsl.GetInt64SettingsMap() { +// settingValid[key] = &(*setting).Valid +// } +// for key, setting := range vsl.GetCurrencySettingsMap() { +// settingValid[key] = &(*setting).Valid +// } +// for key, setting := range vsl.GetStringSettingsMap() { +// settingValid[key] = &(*setting).Valid +// } +// for key, setting := range vsl.GetBoolSettingsMap() { +// settingValid[key] = &(*setting).Valid +// } +// for key, setting := range vsl.GetFloat32SettingsMap() { +// settingValid[key] = &(*setting).Valid +// } +// for key, setting := range vsl.GetTimeSettingsMap() { +// settingValid[key] = &(*setting).Valid +// } + +// return settingValid +// } + +// // func setValidSetting[T any](settings map[string]*T, searchKey string, searchVal string, setVal func(string) (T, error)) error { +// // for key, setting := range settings { + +// // if key == searchKey { +// // s, err := setVal(searchVal) +// // if err != nil { +// // return err +// // } +// // *setting = s +// // } +// // return nil +// // } +// // return ErrSettingNotFound +// // } +// func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error { +// for key, setting := range vsl.GetInt64SettingsMap() { +// if key == searchKey { +// value, err := strconv.ParseInt(searchVal, 10, 64) +// if err != nil { +// return err +// } +// *setting = ValidInt64{Value: value, Valid: true} +// return nil +// } +// } +// return ErrSettingNotFound +// } + +// func (vsl *ValidSettingList) SetCurrencySetting(searchKey string, searchVal string) error { +// for key, setting := range vsl.GetCurrencySettingsMap() { +// if key == searchKey { +// value, err := strconv.ParseInt(searchVal, 10, 64) +// if err != nil { +// return err +// } +// *setting = ValidCurrency{Value: Currency(value), Valid: true} +// return nil +// } +// } + +// return ErrSettingNotFound +// } + +// func (vsl *ValidSettingList) SetStringSetting(searchKey string, searchVal string) error { +// for key, setting := range vsl.GetStringSettingsMap() { +// if key == searchKey { +// *setting = ValidString{Value: searchVal, Valid: true} +// return nil +// } +// } + +// return ErrSettingNotFound +// } + +// func (vsl *ValidSettingList) SetBoolSetting(searchKey string, searchVal string) error { +// for key, setting := range vsl.GetBoolSettingsMap() { + +// if key == searchKey { +// value, err := strconv.ParseBool(searchVal) +// if err != nil { +// return err +// } + +// *setting = ValidBool{Value: value, Valid: true} +// return nil +// } + +// } + +// return ErrSettingNotFound +// } + +// func (vsl *ValidSettingList) SetFloat32Setting(searchKey string, searchVal string) error { +// for key, setting := range vsl.GetFloat32SettingsMap() { +// if key == searchKey { +// value, err := strconv.ParseFloat(searchVal, 32) +// if err != nil { +// return err +// } +// *setting = ValidFloat32{Value: float32(value), Valid: true} +// return nil +// } +// } + +// return ErrSettingNotFound +// } + +// func (vsl *ValidSettingList) SetTimeSetting(searchKey string, searchVal string) error { +// for key, setting := range vsl.GetTimeSettingsMap() { +// if key == searchKey { +// value, err := time.Parse(time.RFC3339, searchVal) +// if err != nil { +// return err +// } +// *setting = ValidTime{Value: value, Valid: true} +// return nil +// } +// } +// return ErrSettingNotFound +// } + +// func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) error { +// setters := []func(string, string) error{ +// vsl.SetInt64Setting, +// vsl.SetCurrencySetting, +// vsl.SetStringSetting, +// vsl.SetBoolSetting, +// vsl.SetFloat32Setting, +// vsl.SetTimeSetting, +// } + +// for _, setter := range setters { +// if err := setter(searchKey, searchVal); err != nil { +// if err == ErrSettingNotFound { +// // fmt.Printf("setting is not found %v \n", searchKey) +// continue // not this setter, try the next +// } +// return fmt.Errorf("error while processing setting %q: %w \n", searchKey, err) +// } +// return nil // successfully set +// } + +// // If we get here, none of the setters matched +// return ErrSettingNotFound +// } + +// func convertValidSettings[T any]( +// settings map[string]*T, +// isValid func(*T) bool, +// toString func(*T) string, +// ) []Setting { +// result := make([]Setting, 0, len(settings)) +// for key, s := range settings { +// if isValid(s) { +// result = append(result, Setting{ +// Key: key, +// Value: toString(s), +// }) +// } +// } +// return result +// } + +// func (vsl *ValidSettingList) ConvertInt64Settings() []Setting { +// return convertValidSettings( +// vsl.GetInt64SettingsMap(), +// func(s *ValidInt64) bool { return s.Valid }, +// func(s *ValidInt64) string { return strconv.FormatInt(s.Value, 10) }, +// ) +// } + +// func (vsl *ValidSettingList) ConvertCurrencySettings() []Setting { +// return convertValidSettings( +// vsl.GetCurrencySettingsMap(), +// func(s *ValidCurrency) bool { return s.Valid }, +// func(s *ValidCurrency) string { return strconv.FormatInt(int64(s.Value), 10) }, +// ) +// } + +// func (vsl *ValidSettingList) ConvertStringSettings() []Setting { +// return convertValidSettings( +// vsl.GetStringSettingsMap(), +// func(s *ValidString) bool { return s.Valid }, +// func(s *ValidString) string { return s.Value }, +// ) +// } + +// func (vsl *ValidSettingList) ConvertBoolSettings() []Setting { +// return convertValidSettings( +// vsl.GetBoolSettingsMap(), +// func(s *ValidBool) bool { return s.Valid }, +// func(s *ValidBool) string { return strconv.FormatBool(s.Value) }, +// ) +// } + +// func (vsl *ValidSettingList) ConvertFloat32Settings() []Setting { +// return convertValidSettings( +// vsl.GetFloat32SettingsMap(), +// func(s *ValidFloat32) bool { return s.Valid }, +// func(s *ValidFloat32) string { return strconv.FormatFloat(float64(s.Value), 'f', -1, 32) }, +// ) +// } + +// func (vsl *ValidSettingList) ConvertTimeSettings() []Setting { +// return convertValidSettings( +// vsl.GetTimeSettingsMap(), +// func(s *ValidTime) bool { return s.Valid }, +// func(s *ValidTime) string { return s.Value.Format(time.RFC3339) }, +// ) +// } + +// func validateSettings[T any]( +// settings map[string]*T, +// customValidator func(*T) bool, +// ) error { +// var errs []string +// for key, s := range settings { +// if !customValidator(s) { + +// errs = append(errs, fmt.Sprintf("%v is invalid", key)) +// } +// } +// if len(errs) > 0 { +// return errors.New(strings.Join(errs, "; ")) +// } + +// return nil +// } + +// func (vsl *ValidSettingList) ValidateInt64Settings() error { +// return validateSettings(vsl.GetInt64SettingsMap(), +// func(s *ValidInt64) bool { +// return s.Valid +// }, +// ) +// } + +// func (vsl *ValidSettingList) ValidateCurrencySettings() error { +// return validateSettings(vsl.GetCurrencySettingsMap(), +// func(s *ValidCurrency) bool { +// return s.Valid +// }, +// ) +// } + +// func (vsl *ValidSettingList) ValidateStringSettings() error { +// return validateSettings(vsl.GetStringSettingsMap(), +// func(s *ValidString) bool { +// return s.Valid +// }, +// ) +// } + +// func (vsl *ValidSettingList) ValidateBoolSettings() error { +// return validateSettings(vsl.GetBoolSettingsMap(), +// func(s *ValidBool) bool { +// return s.Valid +// }, +// ) +// } + +// func (vsl *ValidSettingList) ValidateFloat32Settings() error { +// return validateSettings(vsl.GetFloat32SettingsMap(), +// func(s *ValidFloat32) bool { +// return s.Valid +// }, +// ) +// } + +// func (vsl *ValidSettingList) ValidateTimeSettings() error { +// return validateSettings(vsl.GetTimeSettingsMap(), +// func(s *ValidTime) bool { +// return s.Valid +// }, +// ) +// } + +// func (vsl *ValidSettingList) ValidateAllSettings() error { +// var errs []string +// validators := []func() error{ +// vsl.ValidateInt64Settings, +// vsl.ValidateCurrencySettings, +// vsl.ValidateStringSettings, +// vsl.ValidateBoolSettings, +// vsl.ValidateFloat32Settings, +// vsl.ValidateTimeSettings, +// vsl.CustomValidationSettings, +// } + +// for _, validator := range validators { +// if err := validator(); err != nil { + +// errs = append(errs, err.Error()) +// } +// } +// if len(errs) > 0 { +// return errors.New(strings.Join(errs, "; ")) +// } + +// return nil +// } + +// func (vsl *ValidSettingList) ConvertAllSettings() []Setting { +// totalCap := vsl.GetTotalSettings() +// all := make([]Setting, 0, totalCap) + +// all = append(all, vsl.ConvertInt64Settings()...) +// all = append(all, vsl.ConvertCurrencySettings()...) +// all = append(all, vsl.ConvertStringSettings()...) +// all = append(all, vsl.ConvertBoolSettings()...) +// all = append(all, vsl.ConvertFloat32Settings()...) +// all = append(all, vsl.ConvertTimeSettings()...) + +// return all +// } + +// func ConvertDBGlobalSettingList(settings []dbgen.GlobalSetting) (SettingList, error) { +// var dbSettingList ValidSettingList + +// for _, setting := range settings { +// if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil { +// if err == ErrSettingNotFound { +// MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key)) +// continue +// } +// MongoDBLogger.Error("unknown error while fetching settings", zap.Error(err)) +// } +// } + +// if err := dbSettingList.ValidateAllSettings(); err != nil { +// MongoDBLogger.Warn("setting validation error", zap.Error(err), zap.Any("db_setting_list", dbSettingList)) +// return SettingList{}, err +// } + +// settingList := dbSettingList.ToSettingList() + +// return settingList, nil +// } + +// func ConvertDBOverrideSettingList(settings []dbgen.GetOverrideSettingsRow) (SettingList, error) { +// var dbSettingList ValidSettingList + +// for _, setting := range settings { +// if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil { +// if err == ErrSettingNotFound { +// MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key)) +// } +// } +// } + +// if err := dbSettingList.ValidateAllSettings(); err != nil { +// MongoDBLogger.Warn("setting validation error", zap.Error(err)) +// return SettingList{}, err +// } + +// settingList := dbSettingList.ToSettingList() + +// return settingList, nil +// } diff --git a/internal/domain/settings.go b/internal/domain/settings.go index f84a608..c30fd91 100644 --- a/internal/domain/settings.go +++ b/internal/domain/settings.go @@ -1,61 +1,61 @@ package domain -import ( - "time" +// import ( +// "time" - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) +// dbgen "Yimaru-Backend/gen/db" +// ) -type Setting struct { - Key string - Value string - UpdatedAt time.Time -} +// type Setting struct { +// Key string +// Value string +// UpdatedAt time.Time +// } -type CreateSetting struct { - Key string - Value string -} +// type CreateSetting struct { +// Key string +// Value string +// } -type SettingRes struct { - Key string `json:"key"` - Value string `json:"value"` - UpdatedAt time.Time `json:"updated_at"` -} -type CompanySetting struct { - Key string - Value string - CompanyID int64 - UpdatedAt time.Time - CreatedAt time.Time -} +// type SettingRes struct { +// Key string `json:"key"` +// Value string `json:"value"` +// UpdatedAt time.Time `json:"updated_at"` +// } +// type CompanySetting struct { +// Key string +// Value string +// CompanyID int64 +// UpdatedAt time.Time +// CreatedAt time.Time +// } -type CompanySettingRes struct { - Key string `json:"key"` - Value string `json:"value"` - CompanyID int64 `json:"company_id"` - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` -} +// type CompanySettingRes struct { +// Key string `json:"key"` +// Value string `json:"value"` +// CompanyID int64 `json:"company_id"` +// UpdatedAt time.Time `json:"updated_at"` +// CreatedAt time.Time `json:"created_at"` +// } -func ConvertSetting(setting Setting) SettingRes { - return SettingRes(setting) -} +// func ConvertSetting(setting Setting) SettingRes { +// return SettingRes(setting) +// } -func ConvertCompanySetting(companySetting dbgen.CompanySetting) CompanySetting { - return CompanySetting{ - Key: companySetting.Key, - Value: companySetting.Value, - CompanyID: companySetting.CompanyID, - UpdatedAt: companySetting.UpdatedAt.Time, - CreatedAt: companySetting.CreatedAt.Time, - } -} +// func ConvertCompanySetting(companySetting dbgen.CompanySetting) CompanySetting { +// return CompanySetting{ +// Key: companySetting.Key, +// Value: companySetting.Value, +// CompanyID: companySetting.CompanyID, +// UpdatedAt: companySetting.UpdatedAt.Time, +// CreatedAt: companySetting.CreatedAt.Time, +// } +// } -func ConvertCompanySettings(settings []dbgen.CompanySetting) []CompanySetting { - result := make([]CompanySetting, 0, len(settings)) - for _, setting := range settings { - result = append(result, ConvertCompanySetting(setting)) - } - return result -} +// func ConvertCompanySettings(settings []dbgen.CompanySetting) []CompanySetting { +// result := make([]CompanySetting, 0, len(settings)) +// for _, setting := range settings { +// result = append(result, ConvertCompanySetting(setting)) +// } +// return result +// } diff --git a/internal/domain/shop_bet.go b/internal/domain/shop_bet.go deleted file mode 100644 index c321a02..0000000 --- a/internal/domain/shop_bet.go +++ /dev/null @@ -1,128 +0,0 @@ -package domain - -import "time" - -type ShopBet struct { - ID int64 - ShopTransactionID int64 - CashoutID string - CashedOut bool - BetID int64 - NumberOfOutcomes int64 -} - -type ShopBetFilter struct { - CompanyID ValidInt64 - BranchID ValidInt64 - Query ValidString - CreatedBefore ValidTime - CreatedAfter ValidTime -} - -type CreateShopBet struct { - ShopTransactionID int64 - CashoutID string - BetID int64 - NumberOfOutcomes int64 -} - -type ShopBetDetail struct { - ID int64 - ShopTransactionID int64 - TotalOdds float32 - BranchID int64 - CompanyID int64 - FullName string - PhoneNumber string - FastCode string - CashoutID string - CashedOut bool - BetID int64 - NumberOfOutcomes int64 - Status OutcomeStatus - Amount Currency - Outcomes []BetOutcome - TransactionVerified bool - UpdatedAt time.Time - CreatedAt time.Time -} - -type ShopBetReq struct { - Outcomes []CreateBetOutcomeReq `json:"outcomes"` - Amount float32 `json:"amount" example:"100.0"` - BetID int64 `json:"bet_id" example:"1"` - PaymentOption PaymentOption `json:"payment_option" example:"1"` - FullName string `json:"full_name" example:"John Smith"` - PhoneNumber string `json:"phone_number" example:"0911111111"` - BankCode string `json:"bank_code"` - BeneficiaryName string `json:"beneficiary_name"` - AccountName string `json:"account_name"` - AccountNumber string `json:"account_number"` - ReferenceNumber string `json:"reference_number"` - BranchID *int64 `json:"branch_id,omitempty" example:"1"` -} - -type CashoutReq struct { - CashoutID string `json:"cashout_id" example:"1234"` - PaymentOption PaymentOption `json:"payment_option" example:"1"` - BankCode string `json:"bank_code"` - BeneficiaryName string `json:"beneficiary_name"` - AccountName string `json:"account_name"` - AccountNumber string `json:"account_number"` - ReferenceNumber string `json:"reference_number"` - BranchID *int64 `json:"branch_id,omitempty" example:"1"` -} - -type ShopBetRes struct { - ID int64 `json:"id"` - ShopTransactionID int64 `json:"shop_transaction_id"` - TotalOdds float32 `json:"total_odds" example:"4.22"` - BranchID int64 `json:"branch_id" example:"2"` - CompanyID int64 `json:"company_id" example:"2"` - FullName string `json:"full_name" example:"John"` - PhoneNumber string `json:"phone_number" example:"1234567890"` - FastCode string `json:"fast_code" example:"12SD1"` - CashoutID string `json:"cashout_id" example:"21234"` - CashedOut bool `json:"cashed_out" example:"false"` - BetID int64 `json:"bet_id" example:"1"` - NumberOfOutcomes int64 `json:"number_of_outcomes" example:"1"` - Status OutcomeStatus `json:"status" example:"1"` - Amount float32 `json:"amount"` - Outcomes []BetOutcome `json:"outcomes"` - TransactionVerified bool `json:"transaction_verified" example:"true"` - UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:00:00Z"` - CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"` -} - -func ConvertShopBet(shopBet ShopBet) ShopBetRes { - return ShopBetRes{ - ID: shopBet.ID, - ShopTransactionID: shopBet.ShopTransactionID, - CashoutID: shopBet.CashoutID, - CashedOut: shopBet.CashedOut, - BetID: shopBet.BetID, - NumberOfOutcomes: shopBet.NumberOfOutcomes, - } -} -func ConvertShopBetDetail(shopBet ShopBetDetail) ShopBetRes { - return ShopBetRes{ - ID: shopBet.ID, - ShopTransactionID: shopBet.ShopTransactionID, - TotalOdds: shopBet.TotalOdds, - BranchID: shopBet.BranchID, - CompanyID: shopBet.CompanyID, - FullName: shopBet.FullName, - PhoneNumber: shopBet.PhoneNumber, - FastCode: shopBet.FastCode, - CashoutID: shopBet.CashoutID, - CashedOut: shopBet.CashedOut, - BetID: shopBet.BetID, - NumberOfOutcomes: shopBet.NumberOfOutcomes, - Status: shopBet.Status, - Amount: shopBet.Amount.Float32(), - Outcomes: shopBet.Outcomes, - TransactionVerified: shopBet.TransactionVerified, - UpdatedAt: shopBet.UpdatedAt, - CreatedAt: shopBet.UpdatedAt, - } -} diff --git a/internal/domain/shop_deposit.go b/internal/domain/shop_deposit.go deleted file mode 100644 index 9b13438..0000000 --- a/internal/domain/shop_deposit.go +++ /dev/null @@ -1,78 +0,0 @@ -package domain - -import ( - "time" -) - -type ShopDeposit struct { - ID int64 - ShopTransactionID int64 - CustomerID int64 - WalletTransferID ValidInt64 - BranchWalletID int64 -} -type CreateShopDeposit struct { - ShopTransactionID int64 - CustomerID int64 - BranchWalletID int64 -} - -type ShopDepositFilter struct { - CompanyID ValidInt64 - BranchID ValidInt64 - Query ValidString - CreatedBefore ValidTime - CreatedAfter ValidTime -} - -type ShopDepositDetail struct { - ID int64 - ShopTransactionID int64 - CustomerID int64 - BranchWalletID int64 - WalletTransferID ValidInt64 - FullName string - PhoneNumber string - Amount Currency - BranchID int64 - CompanyID int64 - TransactionVerified bool - UpdatedAt time.Time - CreatedAt time.Time -} - -type ShopDepositReq struct { - CustomerID int64 `json:"customer_id" example:"1"` - Amount float32 `json:"amount" example:"100.0"` - PaymentOption PaymentOption `json:"payment_option" example:"1"` - // FullName string `json:"full_name" example:"John Smith"` - // PhoneNumber string `json:"phone_number" example:"0911111111"` - BankCode string `json:"bank_code"` - BeneficiaryName string `json:"beneficiary_name"` - AccountName string `json:"account_name"` - AccountNumber string `json:"account_number"` - ReferenceNumber string `json:"reference_number"` - BranchID *int64 `json:"branch_id,omitempty" example:"1"` -} - -type ShopDepositRes struct { - ID int64 `json:"id"` - ShopTransactionID int64 `json:"shop_transaction_id"` - CustomerID int64 `json:"customer_id"` - WalletTransferID *int64 `json:"wallet_transfer_id,omitempty"` -} - -func ConvertShopDeposit(shopDeposit ShopDeposit) ShopDepositRes { - - res := ShopDepositRes{ - ID: shopDeposit.ID, - ShopTransactionID: shopDeposit.ShopTransactionID, - CustomerID: shopDeposit.CustomerID, - } - - if shopDeposit.WalletTransferID.Valid { - res.WalletTransferID = &shopDeposit.WalletTransferID.Value - } - - return res -} diff --git a/internal/domain/shop_transaction.go b/internal/domain/shop_transaction.go deleted file mode 100644 index 5574866..0000000 --- a/internal/domain/shop_transaction.go +++ /dev/null @@ -1,315 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type ShopTransactionType int - -const ( - TRANSACTION_CASHOUT ShopTransactionType = iota - TRANSACTION_DEPOSIT - TRANSACTION_BET -) - -type PaymentOption int64 - -const ( - CASH_TRANSACTION PaymentOption = iota - TELEBIRR_TRANSACTION - ARIFPAY_TRANSACTION - BANK -) - -// ShopTransaction only represents branch transactions -// This is only used for statistic data -type ShopTransaction struct { - ID int64 - Amount Currency - BranchID int64 - CompanyID int64 - UserID int64 - Type ShopTransactionType - PaymentOption PaymentOption - FullName string - PhoneNumber string - // Payment Details for bank - BankCode ValidString - BeneficiaryName ValidString - AccountName ValidString - AccountNumber ValidString - ReferenceNumber ValidString - Verified bool - ApprovedBy ValidInt64 - UpdatedAt time.Time - CreatedAt time.Time -} - -type ShopTransactionRes struct { - ID int64 `json:"id" example:"1"` - Amount float32 `json:"amount" example:"100.0"` - BranchID int64 `json:"branch_id" example:"1"` - BranchName string `json:"branch_name" example:"Branch Name"` - BranchLocation string `json:"branch_location" example:"Branch Location"` - CompanyID int64 `json:"company_id" example:"1"` - UserID int64 `json:"user_id" example:"1"` - CreatorFirstName string `json:"creator_first_name" example:"John"` - CreatorLastName string `json:"creator_last_name" example:"Smith"` - CreatorPhoneNumber string `json:"creator_phone_number" example:"0911111111"` - CashierName string `json:"cashier_name" example:"John Smith"` - Type int64 `json:"type" example:"1"` - PaymentOption PaymentOption `json:"payment_option" example:"1"` - FullName string `json:"full_name" example:"John Smith"` - PhoneNumber string `json:"phone_number" example:"0911111111"` - BankCode string `json:"bank_code"` - BeneficiaryName string `json:"beneficiary_name"` - AccountName string `json:"account_name"` - AccountNumber string `json:"account_number"` - ReferenceNumber string `json:"reference_number"` - Verified bool `json:"verified" example:"true"` - ApprovedBy int64 `json:"approved_by" example:"1"` - ApproverFirstName string `json:"approver_first_name" example:"John"` - ApproverLastName string `json:"approver_last_name" example:"Smith"` - ApproverPhoneNumber string `json:"approver_phone_number" example:"0911111111"` - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` -} - -type ShopTransactionDetail struct { - ID int64 - Amount Currency - BranchID int64 - BranchName string - BranchLocation string - CompanyID int64 - UserID int64 - CreatorFirstName string - CreatorLastName string - CreatorPhoneNumber string - Type ShopTransactionType - PaymentOption PaymentOption - FullName string - PhoneNumber string - // Payment Details for bank - BankCode ValidString - BeneficiaryName ValidString - AccountName ValidString - AccountNumber ValidString - ReferenceNumber ValidString - Verified bool - ApprovedBy ValidInt64 - ApproverFirstName ValidString - ApproverLastName ValidString - ApproverPhoneNumber ValidString - UpdatedAt time.Time - CreatedAt time.Time -} - -type ShopTransactionFilter struct { - CompanyID ValidInt64 - BranchID ValidInt64 - UserID ValidInt64 - Query ValidString - CreatedBefore ValidTime - CreatedAfter ValidTime -} -type CreateShopTransaction struct { - Amount Currency - BranchID int64 - CompanyID int64 - UserID int64 - Type ShopTransactionType - PaymentOption PaymentOption - FullName string - PhoneNumber string - // Payment Details for bank - BankCode ValidString - BeneficiaryName ValidString - AccountName ValidString - AccountNumber ValidString - ReferenceNumber ValidString - Verified bool - ApprovedBy ValidInt64 -} - -func ConvertShopTransaction(transaction ShopTransaction) ShopTransactionRes { - newTransaction := ShopTransactionRes{ - ID: transaction.ID, - Amount: transaction.Amount.Float32(), - BranchID: transaction.BranchID, - CompanyID: transaction.CompanyID, - UserID: transaction.UserID, - Type: int64(transaction.Type), - PaymentOption: transaction.PaymentOption, - FullName: transaction.FullName, - PhoneNumber: transaction.PhoneNumber, - BankCode: transaction.BankCode.Value, - BeneficiaryName: transaction.BeneficiaryName.Value, - AccountName: transaction.AccountName.Value, - AccountNumber: transaction.AccountNumber.Value, - ReferenceNumber: transaction.ReferenceNumber.Value, - Verified: transaction.Verified, - ApprovedBy: transaction.ApprovedBy.Value, - CreatedAt: transaction.CreatedAt, - UpdatedAt: transaction.UpdatedAt, - } - - return newTransaction -} - -func ConvertShopTransactionDetail(transaction ShopTransactionDetail) ShopTransactionRes { - newTransaction := ShopTransactionRes{ - ID: transaction.ID, - Amount: transaction.Amount.Float32(), - BranchID: transaction.BranchID, - BranchName: transaction.BranchName, - BranchLocation: transaction.BranchLocation, - CompanyID: transaction.CompanyID, - UserID: transaction.UserID, - CreatorFirstName: transaction.CreatorFirstName, - CreatorLastName: transaction.CreatorLastName, - CreatorPhoneNumber: transaction.CreatorPhoneNumber, - Type: int64(transaction.Type), - PaymentOption: transaction.PaymentOption, - FullName: transaction.FullName, - PhoneNumber: transaction.PhoneNumber, - BankCode: transaction.BankCode.Value, - BeneficiaryName: transaction.BeneficiaryName.Value, - AccountName: transaction.AccountName.Value, - AccountNumber: transaction.AccountNumber.Value, - ReferenceNumber: transaction.ReferenceNumber.Value, - Verified: transaction.Verified, - ApprovedBy: transaction.ApprovedBy.Value, - ApproverFirstName: transaction.ApproverFirstName.Value, - ApproverLastName: transaction.ApproverLastName.Value, - ApproverPhoneNumber: transaction.ApproverPhoneNumber.Value, - CreatedAt: transaction.CreatedAt, - UpdatedAt: transaction.UpdatedAt, - } - - return newTransaction -} - -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/sport.go b/internal/domain/sport.go deleted file mode 100644 index 01e01aa..0000000 --- a/internal/domain/sport.go +++ /dev/null @@ -1,112 +0,0 @@ -package domain - -type Sport int64 - -const ( - FOOTBALL = 1 - BASKETBALL = 18 - VOLLEYBALL = 91 - HANDBALL = 78 - BASEBALL = 16 - HORSE_RACING = 2 - GREYHOUNDS = 4 - ICE_HOCKEY = 17 - SNOOKER = 14 - AMERICAN_FOOTBALL = 12 - CRICKET = 3 - FUTSAL = 83 - DARTS = 15 - TABLE_TENNIS = 92 - BADMINTON = 94 - RUGBY_UNION = 8 - RUGBY_LEAGUE = 19 - AUSTRALIAN_RULES = 36 - BOWLS = 66 - BOXING = 9 - GAELIC_SPORTS = 75 - FLOORBALL = 90 - BEACH_VOLLEYBALL = 95 - WATER_POLO = 110 - SQUASH = 107 - E_SPORTS = 151 - MMA = 162 - SURFING = 148 -) - -func (s Sport) String() string { - switch s { - case FOOTBALL: - return "FOOTBALL" - case BASKETBALL: - return "BASKETBALL" - case VOLLEYBALL: - return "VOLLEYBALL" - case HANDBALL: - return "HANDBALL" - case BASEBALL: - return "BASEBALL" - case HORSE_RACING: - return "HORSE_RACING" - case GREYHOUNDS: - return "GREYHOUNDS" - case ICE_HOCKEY: - return "ICE_HOCKEY" - case SNOOKER: - return "SNOOKER" - case AMERICAN_FOOTBALL: - return "AMERICAN_FOOTBALL" - case CRICKET: - return "CRICKET" - case FUTSAL: - return "FUTSAL" - case DARTS: - return "DARTS" - case TABLE_TENNIS: - return "TABLE_TENNIS" - case BADMINTON: - return "BADMINTON" - case RUGBY_UNION: - return "RUGBY_UNION" - case RUGBY_LEAGUE: - return "RUGBY_LEAGUE" - case AUSTRALIAN_RULES: - return "AUSTRALIAN_RULES" - case BOWLS: - return "BOWLS" - case BOXING: - return "BOXING" - case GAELIC_SPORTS: - return "GAELIC_SPORTS" - case FLOORBALL: - return "FLOORBALL" - case BEACH_VOLLEYBALL: - return "BEACH_VOLLEYBALL" - case WATER_POLO: - return "WATER_POLO" - case SQUASH: - return "SQUASH" - case E_SPORTS: - return "E_SPORTS" - case MMA: - return "MMA" - case SURFING: - return "SURFING" - default: - return "Unknown" - } -} - -func IsValidSport(s Sport) bool { - switch s { - case - FOOTBALL, BASKETBALL, VOLLEYBALL, HANDBALL, BASEBALL, - HORSE_RACING, GREYHOUNDS, ICE_HOCKEY, SNOOKER, AMERICAN_FOOTBALL, - CRICKET, FUTSAL, DARTS, TABLE_TENNIS, BADMINTON, - RUGBY_UNION, RUGBY_LEAGUE, AUSTRALIAN_RULES, BOWLS, BOXING, - GAELIC_SPORTS, FLOORBALL, BEACH_VOLLEYBALL, WATER_POLO, - SQUASH, E_SPORTS, MMA, SURFING: - return true - default: - return false - } -} diff --git a/internal/domain/sportmarket.go b/internal/domain/sportmarket.go deleted file mode 100644 index 6e071ba..0000000 --- a/internal/domain/sportmarket.go +++ /dev/null @@ -1,525 +0,0 @@ -//go:generate stringer -type=Market -package domain - -import ( - "fmt" - "strings" -) - -func GetMarketName(id int64) (string, error) { - name := Market(id).String() - - if strings.HasPrefix(name, "Market(") { - return "", fmt.Errorf("prefix_incorrect_%d_name_%v", id, name) - } - - name = strings.ToLower(name) - name = strings.ReplaceAll(name, "_", " ") - - return name, nil -} - -type Market int64 - -const ( - - // Main - FOOTBALL_FULL_TIME_RESULT Market = 40 //"full_time_result" - FOOTBALL_DOUBLE_CHANCE Market = 10114 //"double_chance" - FOOTBALL_GOALS_OVER_UNDER Market = 981 //"goals_over_under" - FOOTBALL_CORRECT_SCORE Market = 43 //"correct_score" - FOOTBALL_ASIAN_HANDICAP Market = 938 //"asian_handicap" - FOOTBALL_GOAL_LINE Market = 10143 //"goal_line" - - // Main New - FOOTBALL_FULL_TIME_RESULT_ENHANCED Market = 4001 //"full_time_result_–_enhanced_prices" - FOOTBALL_BOTH_TEAMS_TO_SCORE Market = 10150 //"both_teams_to_score" - FOOTBALL_RESULT_BOTH_TEAMS_TO_SCORE Market = 50404 //"result_both_teams_to_score" - FOOTBALL_MATCH_GOAL_RANGE Market = 177816 //"match_goals_range" - FOOTBALL_TEAM_GOAL_RANGE Market = 177817 //"team_goals_range" - FOOTBALL_BOTH_TEAMS_TO_RECEIVE_CARDS Market = 50942 //"both_teams_to_receive_cards" - FOOTBALL_FIRST_HALF_GOAL_RANGE Market = 177819 //"1st_half_goals_range" - FOOTBALL_SECOND_HALF_GOAL_RANGE Market = 177820 //"2nd_half_goals_range" - FOOTBALL_RESULT_GOAL_RANGE Market = 177821 //"results_goals_range" - FOOTBALL_DOUBLE_CHANCE_GOAL_RANGE Market = 177822 //"double_chance_goals_range" - - // Half - FOOTBALL_HALF_TIME_RESULT Market = 1579 //"half_time_result" - FOOTBALL_FIRST_HALF_ASIAN_HANDICAP Market = 50137 //"1st_half_asian_handicap" - FOOTBALL_FIRST_HALF_GOAL_LINE Market = 50136 //"1st_half_goal_line" - FOOTBALL_FIRST_TEAM_TO_SCORE Market = 1178 //"first_team_to_score" - FOOTBALL_GOALS_ODD_EVEN Market = 10111 //"goals_odd_even" - FOOTBALL_DRAW_NO_BET Market = 10544 //"draw_no_bet" - FOOTBALL_HALF_TIME_DOUBLE_CHANCE Market = 10257 //"half_time_double_chance" - FOOTBALL_HALF_TIME_RESULT_BOTH_TEAMS_TO_SCORE Market = 50425 //"half_time_result_both_teams_to_score" - FOOTBALL_ALTERNATE_FIRST_HALF_ASIAN_HANDICAP Market = 50265 //"alternative_1st_half_asian_handicap" - FOOTBALL_ALTERNATE_FIRST_HALF_GOAL_LINE Market = 50266 //"alternative_1st_half_goal_line" - FOOTBALL_FIRST_HALF_HANDICAP Market = 50264 //"1st_half_handicap" - FOOTBALL_ALTERNATE_FIRST_HALF_HANDICAP Market = 10207 //"alternative_1st_half_handicap_result" - FOOTBALL_FIRST_HALF_GOAL Market = 10538 //"first_half_goals" - FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN Market = 10206 //"1st_half_goals_odd_even" - FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN Market = 50433 //"2nd_half_goals_odd_even" - FOOTBALL_HALF_TIME_CORRECT_SCORE Market = 10540 //"half_time_correct_score" - FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF Market = 50424 //"both_teams_to_score_in_1st_half" - FOOTBALL_BOTH_TEAMS_TO_SCORE_SECOND_HALF Market = 50432 //"both_teams_to_score_in_2nd_half" - FOOTBALL_TO_SCORE_IN_HALF Market = 50419 //"to_score_in_half" - FOOTBALL_HALF_WITH_MOST_GOALS Market = 10537 //"half_with_most_goals" - FOOTBALL_HOME_TEAM_WITH_HIGHEST_SCORING_HALF Market = 50417 //"home_team_highest_scoring_half" - FOOTBALL_AWAY_TEAM_WITH_HIGHEST_SCORING_HALF Market = 50418 //"away_team_highest_scoring_half" - FOOTBALL_SECOND_HALF_RESULT Market = 10208 //"2nd_half_result" - FOOTBALL_SECOND_HALF_GOALS Market = 10209 //"2nd_half_goals" - - // Minutes - FOOTBALL_TEN_MINUTE_RESULT Market = 10244 //"10_minute_result" - FOOTBALL_FIRST_TEN_MINUTE Market = 10245 //"first_10_minutes_(00:00_09:59)" - - // Others - FOOTBALL_TEAM_PERFORMANCE Market = 10110 //"team_performances" - FOOTBALL_TEAM_TOTAL_GOALS Market = 10127 //"team_total_goals" - FOOTBALL_ASIAN_TOTAL_CARDS Market = 10166 //"asian_total_cards" - FOOTBALL_EXACT_TOTAL_GOALS Market = 10203 //"asian_total_cards" - FOOTBALL_ALTERNATIVE_HANDICAP_RESULT Market = 10204 //"alternative_handicap_result" - FOOTBALL_EXACT_FIRST_HALF_GOALS Market = 10205 //"exact_1st_half_goals" - FOOTBALL_CLEAN_SHEET Market = 10210 //"clean_sheet" - FOOTBALL_TEAMS_TO_SCORE Market = 10211 //"teams_to_score" - FOOTBALL_TIME_OF_FIRST_TEAM_GOAL Market = 10214 //"time_of_1st_team_goal" - FOOTBALL_FIRST_GOAL_METHOD Market = 10216 //"first_goal_method" - FOOTBALL_MULTI_SCORERS Market = 10217 //"multi_scorers" - FOOTBALL_OWN_GOAL Market = 10223 //"own_goal" - FOOTBALL_TO_SCORE_PENALTY Market = 10229 //"to_score_a_penalty" - FOOTBALL_TO_MISS_PENALTY Market = 10230 //"to_miss_a_penalty" - FOOTBALL_ASIAN_HANDICAP_CARDS Market = 10239 //"asian_handicap_cards" - FOOTBALL_CARD_HANDICAP Market = 10240 //"card_handicap" - FOOTBALL_ALTERNATIVE_CARD_HANDICAP Market = 10241 //"alternative_card_handicap" - FOOTBALL_TEAM_CARDS Market = 10242 //"team_cards" - FOOTBALL_EXACT_SECOND_HALF_GOALS Market = 10252 //"exact_2nd_half_goals" - FOOTBALL_EARLY_GOAL Market = 10258 //"early_goal" - FOOTBALL_LATE_GOAL Market = 10259 //"late_goal" - FOOTBALL_FIRST_MATCH_CORNER Market = 10519 //"first_match_corner" - FOOTBALL_LAST_MATCH_CORNER Market = 10520 //"last_match_corner" - FOOTBALL_LAST_TEAM_TO_SCORE Market = 10534 //"last_team_to_score" - FOOTBALL_CORNER_HANDICAP Market = 10535 //"corner_handicap" - FOOTBALL_NUMBER_OF_GOALS_IN_MATCH Market = 10536 //"number_of_goals_in_match" - FOOTBALL_TIME_OF_FIRST_GOAL_BRACKETS Market = 10541 //"time_of_first_goal_brackets" - FOOTBALL_CORNER_MATCH_BET Market = 1175 //"corner_match_bet" - FOOTBALL_MULTI_CORNERS Market = 1181 //"Multicorners" - FOOTBALL_TIME_OF_FIRST_CARD Market = 1183 //"time_of_first_card" - FOOTBALL_HANDICAP_RESULT Market = 171 //"handicap_result" - FOOTBALL_TOTAL_GOAL_MINUTES Market = 1776 //"total_goal_minutes" - FOOTBALL_PLAYER_TO_SCORE_ASSIST Market = 177704 //"player_to_score_or_assist" - FOOTBALL_TEAM_TO_GET_MOST Market = 177790 //"team_to_get_most" - FOOTBALL_GOALSCORER Market = 45 //"goalscorers" - FOOTBALL_FIRST_CARD_RECEIVED Market = 476 //"first_card_received" - FOOTBALL_PLAYER_CARD Market = 50135 //"player_cards" - FOOTBALL_ALTERNATIVE_ASIAN_HANDICAP Market = 50138 //"alternative_asian_handicap" - FOOTBALL_ALTERNATIVE_GOAL_LINE Market = 50139 //"alternative_goal_line" - FOOTBALL_HOME_TEAM_ODD_EVEN_GOALS Market = 50406 //"home_team_odd_even_goals" - FOOTBALL_AWAY_TEAM_ODD_EVEN_GOALS Market = 50407 //"away_team_odd_even_goals" - FOOTBALL_HOME_TEAM_EXACT_GOALS Market = 50415 //"home_team_exact_goals" - FOOTBALL_AWAY_TEAM_EXACT_GOALS Market = 50416 //"away_team_exact_goals" - FOOTBALL_HALF_TIME_RESULT_TOTAL_GOALS Market = 50426 //"half_time_result_total_goals" - FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF_SECOND_HALF Market = 50435 //"both_teams_to_score_1st_half_2nd_half" - FOOTBALL_MATCH_SHOTS_ON_TARGET Market = 50527 //"match_shots_on_target" - FOOTBALL_MATCH_SHOTS Market = 50528 //"match_shots" - FOOTBALL_TEAM_SHOTS_ON_TARGET Market = 50530 //"team_shots_on_target" - FOOTBALL_TEAM_SHOTS Market = 50532 //"team_shots" - FOOTBALL_GOAL_METHOD Market = 50962 //"goal_method" - FOOTBALL_WINNING_MARGIN Market = 56 //"winning_margin" - FOOTBALL_TIME_OF_FIRST_CORNER Market = 761 //"time_of_first_corner" - - // Player - FOOTBALL_TEAM_GOALSCORER Market = 10151 //"team_goalscorer" - FOOTBALL_PLAYER_SHOTS_ON_TARGET Market = 50920 //"player_shots_on_target" - FOOTBALL_PLAYER_SHOTS Market = 50921 //"player_shots" - - // Specials - FOOTBALL_SPECIALS Market = 10224 //"specials - - // Corner - FOOTBALL_CORNERS Market = 760 //"corners" - FOOTBALL_CORNERS_TWO_WAY Market = 10235 //"corners_2_way" - FOOTBALL_FIRST_HALF_CORNERS Market = 10539 //"first_half_corners" - FOOTBALL_ASIAN_TOTAL_CORNERS Market = 10164 //"asian_total_corners" - FOOTBALL_FIRST_HALF_ASIAN_CORNERS Market = 10233 //"1st_half_asian_corners" - FOOTBALL_ASIAN_HANDICAP_CORNERS Market = 10165 //"asian_handicap_corners" - FOOTBALL_ALTERNATIVE_CORNER Market = 10234 //"alternative_corners" - FOOTBALL_CORNER_RACE Market = 10238 //"corners_race" - - // Cards - FOOTBALL_NUMBER_OF_CARDS_IN_MATCH Market = 10542 //"number_of_cards_in_match" -) - -// Basketball Markets -const ( - // Main - BASKETBALL_GAME_LINES Market = 1453 //"game_lines" - BASKETBALL_FIRST_HALF Market = 928 //"1st_half" - BASKETBALL_FIRST_QUARTER Market = 941 //"1st_quarter" - - // Main Props - BASKETBALL_RESULT_AND_BOTH_TEAMS_TO_SCORE_X_POINTS Market = 181273 //"result_and_both_teams_to_score_'x'_points" - BASKETBALL_DOUBLE_RESULT Market = 1517 //"double_result" - BASKETBALL_MATCH_RESULT_AND_TOTAL Market = 181125 //"match_result_and_total" - BASKETBALL_MATCH_HANDICAP_AND_TOTAL Market = 181126 //"match_handicap_and_total" - BASKETBALL_RACE_TO_20_POINTS Market = 1503 //"race_to_20_points" - BASKETBALL_TIED_AT_END_OF_REGULATION Market = 181127 //"tied_at_end_of_regulation" - BASKETBALL_QUARTER_CORRECT_SCORE Market = 181276 //"quarter_correct_score" - - // Half Props - BASKETBALL_FIRST_HALF_TEAM_TOTALS Market = 181159 //"1st_half_team_totals" - BASKETBALL_FIRST_HALF_WINNING_MARGIN Market = 181185 //"1st_half_winning_margin" - BASKETBALL_FIRST_HALF_RESULT_AND_TOTAL Market = 181181 //"1st_half_result_and_total" - BASKETBALL_FIRST_HALF_HANDICAP_AND_TOTAL Market = 181182 //"1st_half_handicap_and_total" - BASKETBALL_FIRST_HALF_RACE_TO_POINTS Market = 181186 //"1st_half_race_to_(points)" - BASKETBALL_FIRST_HALF_BOTH_TEAMS_TO_SCORE_X_POINTS Market = 181195 //"1st_half_both_teams_to_score_x_points" - BASKETBALL_FIRST_HALF_TEAM_TO_SCORE_X_POINTS Market = 181198 //"1st_half_team_to_score_x_points" - BASKETBALL_FIRST_HALF_MONEY_LINE_3_WAY Market = 181183 //"1st_half_money_line_3_way" - - // Others - BASKETBALL_GAME_TOTAL_ODD_EVEN Market = 180013 //"game_total_odd_even" - BASKETBALL_FIRST_QUARTER_TOTAL_ODD_EVEN Market = 180170 //"1st_quarter_total_odd_even" - BASKETBALL_FIRST_QUARTER_MARGIN_OF_VICTORY Market = 180180 //"1st_quarter_margin_of_victory" - BASKETBALL_HIGHEST_SCORING_HALF Market = 181131 //"highest_scoring_half" - BASKETBALL_HIGHEST_SCORING_QUARTER Market = 181132 //"highest_scoring_quarter" - BASKETBALL_FIRST_HALF_DOUBLE_CHANCE Market = 181184 //"1st_half_double_chance" - BASKETBALL_FIRST_HALF_TOTAL_ODD_EVEN Market = 181204 //"1st_half_total_odd_even" - BASKETBALL_FIRST_QUARTER_3_WAY_LINES Market = 181212 //"1st_quarter_3_way_lines" - BASKETBALL_FIRST_QUARTER_RESULT_AND_TOTAL Market = 181242 //"1st_quarter_result_and_total" - BASKETBALL_FIRST_QUARTER_HANDICAP_AND_TOTAL Market = 181243 //"1st_quarter_handicap_and_total" - BASKETBALL_FIRST_QUARTER_DOUBLE_CHANCE Market = 181245 //"1st_quarter_double_chance" - BASKETBALL_FIRST_QUARTER_RACE_TO_POINTS Market = 181248 //"1st_quarter_race_to_(points)" - BASKETBALL_FIRST_QUARTER_BOTH_TEAMS_TO_SCORE_X_POINTS Market = 181252 //"1st_quarter_both_teams_to_score_x_points" - BASKETBALL_FIRST_QUARTER_TEAM_TO_SCORE_X_POINTS Market = 181255 //"1st_quarter_team_to_score_x_points" - - // Quarter Props - BASKETBALL_FIRST_QUARTER_TEAM_TOTALS Market = 181220 //"1st_quarter_team_totals" - BASKETBALL_FIRST_QUARTER_WINNING_MARGIN Market = 181247 //"1st_quarter_winning_margin" - - // Team Props - BASKETBALL_TEAM_WITH_HIGHEST_SCORING_QUARTER Market = 181377 //"team_with_highest_scoring_quarter" - BASKETBALL_TEAM_TOTALS Market = 181335 //"team_totals" - - BASKETBALL_TEAM_TOTAL_ODD_EVEN Market = 1731 //"team_total_odd_even" -) - -const ( - // Main - ICE_HOCKEY_GAME_LINES Market = 972 - - ICE_HOCKEY_FIRST_PERIOD Market = 1531 - ICE_HOCKEY_THREE_WAY Market = 170008 - ICE_HOCKEY_DRAW_NO_BET Market = 170447 - ICE_HOCKEY_DOUBLE_CHANCE Market = 170038 - ICE_HOCKEY_WINNING_MARGIN Market = 1556 - ICE_HOCKEY_HIGHEST_SCORING_PERIOD Market = 1557 - ICE_HOCKEY_TIED_AFTER_REGULATION Market = 170479 - ICE_HOCKEY_WHEN_WILL_MATCH_END Market = 170481 - ICE_HOCKEY_GAME_TOTAL_ODD_EVEN Market = 170013 - - ICE_HOCKEY_ALTERNATIVE_PUCK_LINE_TWO_WAY Market = 170226 - ICE_HOCKEY_ALTERNATIVE_TOTAL_TWO_WAY Market = 170240 -) - -const ( - // Main - CRICKET_TO_WIN_THE_MATCH Market = 1246 - CRICKET_TEAM_TOP_BATTER Market = 1241 - CRICKET_TEAM_TOP_BOWLE Market = 1242 - CRICKET_PLAYER_OF_THE_MATCH Market = 346 - CRICKET_FIRST_WICKET_METHOD Market = 30205 - - // First Over - CRICKET_FIRST_OVER_TOTAL_RUNS Market = 300336 - CRICKET_FIRST_OVER_TOTAL_RUNS_Odd_Even Market = 300118 - - // Inninigs 1 - CRICKET_FIRST_INNINIGS_SCORE Market = 300338 - CRICKET_INNINGS_OF_MATCH_BOWLED_OUT Market = 300108 - - // Match - CRICKET_TOP_MATCH_BATTER Market = 30245 - CRICKET_TOP_MATCH_BOWLER Market = 30246 -) - -const ( - VOLLEYBALL_GAME_LINES Market = 910000 - VOLLEYBALL_CORRECT_SET_SCORE Market = 910201 - VOLLEYBALL_MATCH_TOTAL_ODD_EVEN Market = 910217 - VOLLEYBALL_SET_ONE_LINES Market = 910204 - VOLLEYBALL_SET_ONE_TO_GO_TO_EXTRA_POINTS Market = 910209 - VOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN Market = 910218 -) - -const ( - // Main - DARTS_MATCH_WINNER Market = 703 // match_winner - DARTS_MATCH_DOUBLE Market = 150228 // match_double - DARTS_MATCH_TREBLE Market = 150230 // match_treble - DARTS_CORRECT_LEG_SCORE Market = 150015 // correct_leg_score - DARTS_TOTAL_LEGS Market = 150117 // total_legs - - DARTS_MOST_HUNDERED_EIGHTYS Market = 150030 // "most_180s" - DARTS_TOTAL_HUNDERED_EIGHTYS Market = 150012 // total_180s - DARTS_MOST_HUNDERED_EIGHTYS_HANDICAP Market = 150227 // most_180s_handicap - DARTS_PLAYER_HUNDERED_EIGHTYS Market = 150121 // player_180s - DARTS_FIRST_DART Market = 150125 // first_dart -) - -const ( - // Main - FUTSAL_GAME_LINES Market = 830001 - FUTSAL_MONEY_LINE Market = 830130 - - // Others - FUTSAL_DOUBLE_RESULT_9_WAY Market = 830124 - - // Score - FUTSAL_TEAM_TO_SCORE_FIRST Market = 830141 - FUTSAL_RACE_TO_GOALS Market = 830142 -) - -const ( - // Main - AMERICAN_FOOTBALL_GAME_LINES Market = 1441 -) - -const ( - // Main - RUGBY_L_GAME_BETTING_2_WAY Market = 190006 -) - -const ( - // Main - RUGBY_U_GAME_BETTING_2_WAY Market = 80007 -) - -const ( - // Main - BASEBALL_GAME_LINES Market = 1096 -) - -// TODO: Move this into the database so that it can be modified dynamically - -var SupportedMarkets = map[int64]bool{ - - // Football Markets - int64(FOOTBALL_FULL_TIME_RESULT): true, //"full_time_result" - int64(FOOTBALL_DOUBLE_CHANCE): true, //"double_chance" - int64(FOOTBALL_GOALS_OVER_UNDER): true, //"goals_over_under" - int64(FOOTBALL_CORRECT_SCORE): true, //"correct_score" - int64(FOOTBALL_ASIAN_HANDICAP): true, //"asian_handicap" - int64(FOOTBALL_GOAL_LINE): true, //"goal_line" - int64(FOOTBALL_HALF_TIME_RESULT): true, //"half_time_result" - int64(FOOTBALL_FIRST_HALF_ASIAN_HANDICAP): true, //"1st_half_asian_handicap" - int64(FOOTBALL_FIRST_HALF_GOAL_LINE): true, //"1st_half_goal_line" - int64(FOOTBALL_FIRST_TEAM_TO_SCORE): true, //"first_team_to_score" - int64(FOOTBALL_GOALS_ODD_EVEN): true, //"goals_odd_even" - int64(FOOTBALL_DRAW_NO_BET): true, //"draw_no_bet" - int64(FOOTBALL_CORNERS): true, - int64(FOOTBALL_CORNERS_TWO_WAY): true, - int64(FOOTBALL_FIRST_HALF_CORNERS): true, - int64(FOOTBALL_ASIAN_TOTAL_CORNERS): true, - int64(FOOTBALL_FIRST_HALF_ASIAN_CORNERS): true, - int64(FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN): true, - int64(FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN): true, - - int64(FOOTBALL_FULL_TIME_RESULT_ENHANCED): true, - int64(FOOTBALL_ALTERNATIVE_ASIAN_HANDICAP): true, - int64(FOOTBALL_ALTERNATIVE_GOAL_LINE): true, - int64(FOOTBALL_ALTERNATE_FIRST_HALF_ASIAN_HANDICAP): true, - int64(FOOTBALL_ALTERNATE_FIRST_HALF_GOAL_LINE): true, - int64(FOOTBALL_ALTERNATIVE_CORNER): true, - int64(FOOTBALL_BOTH_TEAMS_TO_SCORE): true, - int64(FOOTBALL_RESULT_BOTH_TEAMS_TO_SCORE): true, - int64(FOOTBALL_HALF_TIME_CORRECT_SCORE): true, - int64(FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF): true, - int64(FOOTBALL_BOTH_TEAMS_TO_SCORE_SECOND_HALF): true, - int64(FOOTBALL_SECOND_HALF_RESULT): true, - int64(FOOTBALL_CLEAN_SHEET): true, - int64(FOOTBALL_LAST_TEAM_TO_SCORE): true, - int64(FOOTBALL_WINNING_MARGIN): true, - int64(FOOTBALL_BOTH_TEAMS_TO_RECEIVE_CARDS): true, - int64(FOOTBALL_HALF_TIME_DOUBLE_CHANCE): true, - int64(FOOTBALL_HALF_TIME_RESULT_BOTH_TEAMS_TO_SCORE): true, - int64(FOOTBALL_HALF_WITH_MOST_GOALS): true, - int64(FOOTBALL_HOME_TEAM_WITH_HIGHEST_SCORING_HALF): true, - int64(FOOTBALL_AWAY_TEAM_WITH_HIGHEST_SCORING_HALF): true, - int64(FOOTBALL_SECOND_HALF_GOALS): true, - int64(FOOTBALL_TEAM_TOTAL_GOALS): true, - int64(FOOTBALL_EXACT_TOTAL_GOALS): true, - int64(FOOTBALL_EXACT_FIRST_HALF_GOALS): true, - int64(FOOTBALL_TEAMS_TO_SCORE): true, - int64(FOOTBALL_EXACT_SECOND_HALF_GOALS): true, - int64(FOOTBALL_FIRST_MATCH_CORNER): true, - int64(FOOTBALL_LAST_MATCH_CORNER): true, - int64(FOOTBALL_CORNER_MATCH_BET): true, - int64(FOOTBALL_MULTI_CORNERS): true, - int64(FOOTBALL_MATCH_SHOTS_ON_TARGET): true, - int64(FOOTBALL_TEAM_SHOTS_ON_TARGET): true, - int64(FOOTBALL_SPECIALS): true, - int64(FOOTBALL_ASIAN_HANDICAP_CORNERS): true, - int64(FOOTBALL_CORNER_HANDICAP): true, - int64(FOOTBALL_ASIAN_TOTAL_CARDS): true, - int64(FOOTBALL_NUMBER_OF_CARDS_IN_MATCH): true, - int64(FOOTBALL_TIME_OF_FIRST_GOAL_BRACKETS): true, - int64(FOOTBALL_EARLY_GOAL): true, - int64(FOOTBALL_LATE_GOAL): true, - - // Basketball Markets - int64(BASKETBALL_GAME_LINES): true, - int64(BASKETBALL_RESULT_AND_BOTH_TEAMS_TO_SCORE_X_POINTS): true, - int64(BASKETBALL_DOUBLE_RESULT): true, - int64(BASKETBALL_MATCH_RESULT_AND_TOTAL): true, - int64(BASKETBALL_MATCH_HANDICAP_AND_TOTAL): true, - int64(BASKETBALL_GAME_TOTAL_ODD_EVEN): true, - int64(BASKETBALL_TEAM_TOTALS): true, - int64(BASKETBALL_TEAM_TOTAL_ODD_EVEN): true, - int64(BASKETBALL_RACE_TO_20_POINTS): false, - int64(BASKETBALL_TIED_AT_END_OF_REGULATION): false, - - int64(BASKETBALL_FIRST_HALF): true, - int64(BASKETBALL_FIRST_HALF_TEAM_TOTALS): true, - int64(BASKETBALL_FIRST_HALF_WINNING_MARGIN): false, - int64(BASKETBALL_FIRST_HALF_HANDICAP_AND_TOTAL): true, - int64(BASKETBALL_FIRST_HALF_BOTH_TEAMS_TO_SCORE_X_POINTS): true, - int64(BASKETBALL_FIRST_HALF_MONEY_LINE_3_WAY): true, - int64(BASKETBALL_FIRST_HALF_DOUBLE_CHANCE): true, - int64(BASKETBALL_FIRST_HALF_TOTAL_ODD_EVEN): true, - int64(BASKETBALL_HIGHEST_SCORING_HALF): true, - int64(BASKETBALL_FIRST_HALF_RESULT_AND_TOTAL): false, - int64(BASKETBALL_FIRST_HALF_RACE_TO_POINTS): false, - int64(BASKETBALL_FIRST_HALF_TEAM_TO_SCORE_X_POINTS): false, - - int64(BASKETBALL_FIRST_QUARTER): true, - int64(BASKETBALL_FIRST_QUARTER_HANDICAP_AND_TOTAL): true, - int64(BASKETBALL_FIRST_QUARTER_DOUBLE_CHANCE): true, - int64(BASKETBALL_FIRST_QUARTER_TEAM_TOTALS): true, - int64(BASKETBALL_FIRST_QUARTER_WINNING_MARGIN): false, - int64(BASKETBALL_FIRST_QUARTER_TOTAL_ODD_EVEN): true, - int64(BASKETBALL_HIGHEST_SCORING_QUARTER): true, - int64(BASKETBALL_TEAM_WITH_HIGHEST_SCORING_QUARTER): true, - int64(BASKETBALL_QUARTER_CORRECT_SCORE): false, - int64(BASKETBALL_FIRST_QUARTER_3_WAY_LINES): false, - int64(BASKETBALL_FIRST_QUARTER_RESULT_AND_TOTAL): true, - int64(BASKETBALL_FIRST_QUARTER_RACE_TO_POINTS): false, - int64(BASKETBALL_FIRST_QUARTER_BOTH_TEAMS_TO_SCORE_X_POINTS): false, - int64(BASKETBALL_FIRST_QUARTER_TEAM_TO_SCORE_X_POINTS): false, - int64(BASKETBALL_FIRST_QUARTER_MARGIN_OF_VICTORY): false, - - // Ice Hockey Markets - int64(ICE_HOCKEY_GAME_LINES): true, - int64(ICE_HOCKEY_FIRST_PERIOD): true, - int64(ICE_HOCKEY_THREE_WAY): true, - int64(ICE_HOCKEY_DRAW_NO_BET): true, - int64(ICE_HOCKEY_DOUBLE_CHANCE): true, - int64(ICE_HOCKEY_WINNING_MARGIN): true, - int64(ICE_HOCKEY_HIGHEST_SCORING_PERIOD): true, - int64(ICE_HOCKEY_TIED_AFTER_REGULATION): true, - int64(ICE_HOCKEY_WHEN_WILL_MATCH_END): false, - int64(ICE_HOCKEY_GAME_TOTAL_ODD_EVEN): true, - - int64(ICE_HOCKEY_ALTERNATIVE_PUCK_LINE_TWO_WAY): false, - int64(ICE_HOCKEY_ALTERNATIVE_TOTAL_TWO_WAY): false, - - // Cricket Markets - int64(CRICKET_TO_WIN_THE_MATCH): true, - - int64(CRICKET_FIRST_OVER_TOTAL_RUNS_Odd_Even): false, - int64(CRICKET_FIRST_INNINIGS_SCORE): false, - int64(CRICKET_INNINGS_OF_MATCH_BOWLED_OUT): false, - int64(CRICKET_FIRST_OVER_TOTAL_RUNS): false, - int64(CRICKET_TEAM_TOP_BATTER): false, - int64(CRICKET_TEAM_TOP_BOWLE): false, - int64(CRICKET_PLAYER_OF_THE_MATCH): false, - int64(CRICKET_FIRST_WICKET_METHOD): false, - int64(CRICKET_TOP_MATCH_BATTER): false, - int64(CRICKET_TOP_MATCH_BOWLER): false, - - // Volleyball Markets - int64(VOLLEYBALL_GAME_LINES): true, - int64(VOLLEYBALL_CORRECT_SET_SCORE): true, - int64(VOLLEYBALL_MATCH_TOTAL_ODD_EVEN): true, - - int64(VOLLEYBALL_SET_ONE_LINES): false, - int64(VOLLEYBALL_SET_ONE_TO_GO_TO_EXTRA_POINTS): false, - int64(VOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN): false, - - // Darts Markets - int64(DARTS_MATCH_WINNER): true, - int64(DARTS_TOTAL_LEGS): true, - int64(DARTS_CORRECT_LEG_SCORE): false, - int64(DARTS_MATCH_DOUBLE): false, - int64(DARTS_MATCH_TREBLE): false, - - int64(DARTS_MOST_HUNDERED_EIGHTYS): false, - int64(DARTS_TOTAL_HUNDERED_EIGHTYS): false, - int64(DARTS_MOST_HUNDERED_EIGHTYS_HANDICAP): false, - int64(DARTS_PLAYER_HUNDERED_EIGHTYS): false, - int64(DARTS_FIRST_DART): false, - - // Futsal Markets - int64(FUTSAL_MONEY_LINE): true, - int64(FUTSAL_GAME_LINES): true, - int64(FUTSAL_TEAM_TO_SCORE_FIRST): true, - - int64(FUTSAL_DOUBLE_RESULT_9_WAY): false, - int64(FUTSAL_RACE_TO_GOALS): false, - - // American Football Markets - int64(AMERICAN_FOOTBALL_GAME_LINES): true, - - // Rugby League Markets - int64(RUGBY_L_GAME_BETTING_2_WAY): true, - - // Ruby Union Markets - int64(RUGBY_U_GAME_BETTING_2_WAY): true, - - // Baseball Markets - int64(BASEBALL_GAME_LINES): true, -} - - -// These are temporarily disabled markets that will be disabled for all companies except for fortune -var DisabledMarkets = map[int64]bool{ - int64(FOOTBALL_FULL_TIME_RESULT_ENHANCED): true, - int64(FOOTBALL_ALTERNATIVE_ASIAN_HANDICAP): true, - int64(FOOTBALL_ALTERNATIVE_GOAL_LINE): true, - int64(FOOTBALL_ALTERNATE_FIRST_HALF_ASIAN_HANDICAP): true, - int64(FOOTBALL_ALTERNATE_FIRST_HALF_GOAL_LINE): true, - int64(FOOTBALL_ALTERNATIVE_CORNER): true, - int64(FOOTBALL_BOTH_TEAMS_TO_SCORE): true, - int64(FOOTBALL_RESULT_BOTH_TEAMS_TO_SCORE): true, - int64(FOOTBALL_HALF_TIME_CORRECT_SCORE): true, - int64(FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF): true, - int64(FOOTBALL_BOTH_TEAMS_TO_SCORE_SECOND_HALF): true, - int64(FOOTBALL_SECOND_HALF_RESULT): true, - int64(FOOTBALL_CLEAN_SHEET): true, - int64(FOOTBALL_LAST_TEAM_TO_SCORE): true, - int64(FOOTBALL_WINNING_MARGIN): true, - int64(FOOTBALL_BOTH_TEAMS_TO_RECEIVE_CARDS): true, - int64(FOOTBALL_HALF_TIME_DOUBLE_CHANCE): true, - int64(FOOTBALL_HALF_TIME_RESULT_BOTH_TEAMS_TO_SCORE): true, - int64(FOOTBALL_HALF_WITH_MOST_GOALS): true, - int64(FOOTBALL_HOME_TEAM_WITH_HIGHEST_SCORING_HALF): true, - int64(FOOTBALL_AWAY_TEAM_WITH_HIGHEST_SCORING_HALF): true, - int64(FOOTBALL_SECOND_HALF_GOALS): true, - int64(FOOTBALL_TEAM_TOTAL_GOALS): true, - int64(FOOTBALL_EXACT_TOTAL_GOALS): true, - int64(FOOTBALL_EXACT_FIRST_HALF_GOALS): true, - int64(FOOTBALL_TEAMS_TO_SCORE): true, - int64(FOOTBALL_EXACT_SECOND_HALF_GOALS): true, - int64(FOOTBALL_FIRST_MATCH_CORNER): true, - int64(FOOTBALL_LAST_MATCH_CORNER): true, - int64(FOOTBALL_CORNER_MATCH_BET): true, - int64(FOOTBALL_MULTI_CORNERS): true, - int64(FOOTBALL_MATCH_SHOTS_ON_TARGET): true, - int64(FOOTBALL_TEAM_SHOTS_ON_TARGET): true, - int64(FOOTBALL_SPECIALS): true, - int64(FOOTBALL_ASIAN_HANDICAP_CORNERS): true, - int64(FOOTBALL_CORNER_HANDICAP): true, - int64(FOOTBALL_ASIAN_TOTAL_CARDS): true, - int64(FOOTBALL_NUMBER_OF_CARDS_IN_MATCH): true, - int64(FOOTBALL_TIME_OF_FIRST_GOAL_BRACKETS): true, - int64(FOOTBALL_EARLY_GOAL): true, - int64(FOOTBALL_LATE_GOAL): true, -} diff --git a/internal/domain/sports_result.go b/internal/domain/sports_result.go deleted file mode 100644 index c29a402..0000000 --- a/internal/domain/sports_result.go +++ /dev/null @@ -1,125 +0,0 @@ -package domain - -// import ( -// "encoding/json" -// "strconv" -// "strings" -// ) - -// // ParseNFLResult parses NFL result from raw JSON data -// func ParseNFLResult(data json.RawMessage) (*NFLResultResponse, error) { -// var result NFLResultResponse -// if err := json.Unmarshal(data, &result); err != nil { -// return nil, err -// } -// return &result, nil -// } - -// // ParseRugbyResult parses Rugby result from raw JSON data -// func ParseRugbyResult(data json.RawMessage) (*RugbyResultResponse, error) { -// var result RugbyResultResponse -// if err := json.Unmarshal(data, &result); err != nil { -// return nil, err -// } -// return &result, nil -// } - -// // ParseRugbyUnionResult parses Rugby Union result from raw JSON data -// func ParseRugbyUnionResult(data json.RawMessage) (*RugbyResultResponse, error) { -// return ParseRugbyResult(data) -// } - -// // ParseRugbyLeagueResult parses Rugby League result from raw JSON data -// func ParseRugbyLeagueResult(data json.RawMessage) (*RugbyResultResponse, error) { -// return ParseRugbyResult(data) -// } - -// // ParseBaseballResult parses Baseball result from raw JSON data -// func ParseBaseballResult(data json.RawMessage) (*BaseballResultResponse, error) { -// var result BaseballResultResponse -// if err := json.Unmarshal(data, &result); err != nil { -// return nil, err -// } -// return &result, nil -// } - -// // GetNFLWinner determines the winner of an NFL game -// func GetNFLWinner(result *NFLResultResponse) (string, error) { -// homeScore, err := strconv.Atoi(result.Scores.TotalScore.Home) -// if err != nil { -// return "", err -// } -// awayScore, err := strconv.Atoi(result.Scores.TotalScore.Away) -// if err != nil { -// return "", err -// } - -// if homeScore > awayScore { -// return result.Home.Name, nil -// } else if awayScore > homeScore { -// return result.Away.Name, nil -// } -// return "Draw", nil -// } - -// // GetRugbyWinner determines the winner of a Rugby game -// func GetRugbyWinner(result *RugbyResultResponse) (string, error) { -// homeScore, err := strconv.Atoi(result.Scores.TotalScore.Home) -// if err != nil { -// return "", err -// } -// awayScore, err := strconv.Atoi(result.Scores.TotalScore.Away) -// if err != nil { -// return "", err -// } - -// if homeScore > awayScore { -// return result.Home.Name, nil -// } else if awayScore > homeScore { -// return result.Away.Name, nil -// } -// return "Draw", nil -// } - -// // GetBaseballWinner determines the winner of a Baseball game -// func GetBaseballWinner(result *BaseballResultResponse) (string, error) { -// homeScore, err := strconv.Atoi(result.Scores.TotalScore.Home) -// if err != nil { -// return "", err -// } -// awayScore, err := strconv.Atoi(result.Scores.TotalScore.Away) -// if err != nil { -// return "", err -// } - -// if homeScore > awayScore { -// return result.Home.Name, nil -// } else if awayScore > homeScore { -// return result.Away.Name, nil -// } -// return "Draw", nil -// } - -// // FormatNFLScore formats the NFL score in a readable format -// func FormatNFLScore(result *NFLResultResponse) string { -// return strings.Join([]string{ -// result.Home.Name + " " + result.Scores.TotalScore.Home, -// result.Away.Name + " " + result.Scores.TotalScore.Away, -// }, " - ") -// } - -// // FormatRugbyScore formats the Rugby score in a readable format -// func FormatRugbyScore(result *RugbyResultResponse) string { -// return strings.Join([]string{ -// result.Home.Name + " " + result.Scores.TotalScore.Home, -// result.Away.Name + " " + result.Scores.TotalScore.Away, -// }, " - ") -// } - -// // FormatBaseballScore formats the Baseball score in a readable format -// func FormatBaseballScore(result *BaseballResultResponse) string { -// return strings.Join([]string{ -// result.Home.Name + " " + result.Scores.TotalScore.Home, -// result.Away.Name + " " + result.Scores.TotalScore.Away, -// }, " - ") -// } diff --git a/internal/domain/telebirr.go b/internal/domain/telebirr.go deleted file mode 100644 index 02fc02d..0000000 --- a/internal/domain/telebirr.go +++ /dev/null @@ -1,60 +0,0 @@ -package domain - -type TelebirrFabricTokenResponse struct { - Token string `json:"token"` - EffectiveDate string `json:"effectiveDate"` - ExpirationDate string `json:"expirationDate"` -} - -type TelebirrBizContent struct { - NotifyURL string `json:"notify_url"` - AppID string `json:"appid"` - MerchCode string `json:"merch_code"` - MerchOrderID string `json:"merch_order_id"` - TradeType string `json:"trade_type"` - Title string `json:"title"` - TotalAmount string `json:"total_amount"` - TransCurrency string `json:"trans_currency"` - TimeoutExpress string `json:"timeout_express"` - BusinessType string `json:"business_type"` - PayeeIdentifier string `json:"payee_identifier"` - PayeeIdentifierType string `json:"payee_identifier_type"` - PayeeType string `json:"payee_type"` - RedirectURL string `json:"redirect_url"` - CallbackInfo string `json:"callback_info"` -} - -type TelebirrPreOrderRequestPayload struct { - Timestamp string `json:"timestamp"` - NonceStr string `json:"nonce_str"` - Method string `json:"method"` - Version string `json:"version"` - BizContent TelebirrBizContent `json:"biz_content"` - SignType string `json:"sign_type"` - Sign string `json:"sign"` -} - -type TelebirrCheckoutParams struct { - AppID string `json:"appid"` - MerchCode string `json:"merch_code"` - NonceStr string `json:"nonce_str"` - PrepayID string `json:"prepay_id"` - Timestamp string `json:"timestamp"` -} - -type TelebirrPaymentCallbackPayload struct { - NotifyURL string `json:"notify_url"` // Optional callback URL - AppID string `json:"appid"` // App ID provided by Telebirr - NotifyTime string `json:"notify_time"` // Notification timestamp (UTC, in seconds) - MerchCode string `json:"merch_code"` // Merchant short code - MerchOrderID string `json:"merch_order_id"` // Order ID from merchant system - PaymentOrderID string `json:"payment_order_id"` // Order ID from Telebirr system - TotalAmount string `json:"total_amount"` // Payment amount - TransID string `json:"trans_id"` // Transaction ID - TransCurrency string `json:"trans_currency"` // Currency type (e.g., ETB) - TradeStatus string `json:"trade_status"` // Payment status (e.g., Completed, Failure) - TransEndTime string `json:"trans_end_time"` // Transaction end time (UTC seconds) - CallbackInfo string `json:"callback_info"` // Optional merchant-defined callback data - Sign string `json:"sign"` // Signature of the payload - SignType string `json:"sign_type"` // Signature type, e.g., SHA256WithRSA -} diff --git a/internal/domain/ticket.go b/internal/domain/ticket.go deleted file mode 100644 index b1ba771..0000000 --- a/internal/domain/ticket.go +++ /dev/null @@ -1,165 +0,0 @@ -package domain - -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"` - TicketID int64 `json:"ticket_id" example:"1"` - EventID int64 `json:"event_id" example:"1"` - HomeTeamName string `json:"home_team_name" example:"Manchester"` - AwayTeamName string `json:"away_team_name" example:"Liverpool"` - MarketID int64 `json:"market_id" example:"1"` - MarketName string `json:"market_name" example:"Fulltime Result"` - OddID int64 `json:"odd_id" example:"1"` - Odd float32 `json:"odd" example:"1.5"` - OddName string `json:"odd_name" example:"1"` - OddHeader string `json:"odd_header" example:"1"` - OddHandicap string `json:"odd_handicap" example:"1"` - Status OutcomeStatus `json:"status" example:"1"` - Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"` -} - -type CreateTicketOutcome struct { - TicketID int64 `json:"ticket_id" example:"1"` - EventID int64 `json:"event_id" example:"1"` - OddID int64 `json:"odd_id" example:"1"` - HomeTeamName string `json:"home_team_name" example:"Manchester"` - AwayTeamName string `json:"away_team_name" example:"Liverpool"` - MarketID int64 `json:"market_id" example:"1"` - MarketName string `json:"market_name" example:"Fulltime Result"` - Odd float32 `json:"odd" example:"1.5"` - OddName string `json:"odd_name" example:"1"` - OddHeader string `json:"odd_header" example:"1"` - OddHandicap string `json:"odd_handicap" example:"1"` - Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"` -} - -// ID will serve as the fast code since this doesn't need to be secure -type Ticket struct { - ID int64 - Amount Currency - TotalOdds float32 - CompanyID int64 -} - -type GetTicket struct { - ID int64 - Amount Currency - TotalOdds float32 - Outcomes []TicketOutcome - CompanyID int64 -} - -type CreateTicket struct { - Amount Currency - TotalOdds float32 - IP string - CompanyID int64 -} - -type CreateTicketOutcomeReq struct { - // TicketID int64 `json:"ticket_id" example:"1"` - EventID int64 `json:"event_id" example:"1"` - OddID int64 `json:"odd_id" example:"1"` - MarketID int64 `json:"market_id" example:"1"` - // HomeTeamName string `json:"home_team_name" example:"Manchester"` - // AwayTeamName string `json:"away_team_name" example:"Liverpool"` - // MarketName string `json:"market_name" example:"Fulltime Result"` - // Odd float32 `json:"odd" example:"1.5"` - // OddName string `json:"odd_name" example:"1"` - // Expires time.Time `json:"expires" example:"2025-04-08T12:00:00Z"` -} - -type CreateTicketReq struct { - Outcomes []CreateTicketOutcomeReq `json:"outcomes"` - Amount float32 `json:"amount" example:"100.0"` -} -type CreateTicketRes struct { - FastCode int64 `json:"fast_code" example:"1234"` - CreatedNumber int64 `json:"created_number" example:"3"` -} -type TicketRes struct { - ID int64 `json:"id" example:"1"` - Outcomes []TicketOutcome `json:"outcomes"` - Amount float32 `json:"amount" example:"100.0"` - TotalOdds float32 `json:"total_odds" example:"4.22"` - CompanyID int64 `json:"company_id" example:"1"` -} - -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 deleted file mode 100644 index 19ff813..0000000 --- a/internal/domain/transfer.go +++ /dev/null @@ -1,198 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/jackc/pgx/v5/pgtype" -) - -type TransferType string - -const ( - DEPOSIT TransferType = "deposit" - WITHDRAW TransferType = "withdraw" - WALLET TransferType = "wallet" -) - -type PaymentMethod string - -// Info on why the wallet was modified -// If its internal system modification then, its always direct -const ( - TRANSFER_DIRECT PaymentMethod = "direct" - TRANSFER_CASH PaymentMethod = "cash" - TRANSFER_BANK PaymentMethod = "bank" - TRANSFER_CHAPA PaymentMethod = "chapa" - TRANSFER_ARIFPAY PaymentMethod = "arifpay" - TRANSFER_SANTIMPAY PaymentMethod = "santimpay" - TRANSFER_ADDISPAY PaymentMethod = "addispay" - TRANSFER_OTHER PaymentMethod = "other" -) - -type TransactionApproval struct { - ID int64 - TransferID int64 - ApprovedBy int64 // User ID of approver - Status string // "pending", "approved", "rejected" - Comments string - CreatedAt time.Time - UpdatedAt time.Time -} - -type ApprovalAction string - -const ( - ApprovalActionApprove ApprovalAction = "approve" - ApprovalActionReject ApprovalAction = "reject" -) - -type ApprovalRequest struct { - TransferID int64 - Action ApprovalAction - Comments string - ApproverID int64 -} - -// Info for the payment providers -type PaymentDetails struct { - ReferenceNumber ValidString - BankNumber ValidString -} - -// A Transfer is logged for every modification of ALL wallets and wallet types -type Transfer struct { - ID int64 `json:"id"` - Amount Currency `json:"amount"` - Verified bool `json:"verified"` - Message string `json:"message"` - Type TransferType `json:"type"` - PaymentMethod PaymentMethod `json:"payment_method"` - ReceiverWalletID ValidInt64 `json:"receiver_wallet_id"` - SenderWalletID ValidInt64 `json:"sender_wallet_id"` - ReferenceNumber string `json:"reference_number"` // <-- needed - SessionID string `json:"session_id"` - Status string `json:"status"` - DepositorID ValidInt64 `json:"depositor_id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} -type TransferDetail struct { - ID int64 `json:"id"` - Amount Currency `json:"amount"` - Verified bool `json:"verified"` - Message string `json:"message"` - Type TransferType `json:"type"` - PaymentMethod PaymentMethod `json:"payment_method"` - ReceiverWalletID ValidInt64 `json:"receiver_wallet_id"` - SenderWalletID ValidInt64 `json:"sender_wallet_id"` - ReferenceNumber string `json:"reference_number"` // <-- needed - SessionID string `json:"session_id"` - Status string `json:"status"` - DepositorID ValidInt64 `json:"depositor_id"` - DepositorFirstName string `json:"depositor_first_name"` - DepositorLastName string `json:"depositor_last_name"` - DepositorPhoneNumber string `json:"depositor_phone_number"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -type CreateTransfer struct { - Amount Currency `json:"amount"` - Verified bool `json:"verified"` - Message string `json:"message"` - Type TransferType `json:"type"` - PaymentMethod PaymentMethod `json:"payment_method"` - ReceiverWalletID ValidInt64 `json:"receiver_wallet_id"` - SenderWalletID ValidInt64 `json:"sender_wallet_id"` - ReferenceNumber string `json:"reference_number"` // <-- needed - SessionID string `json:"session_id"` - Status string `json:"status"` - CashierID ValidInt64 `json:"cashier_id"` -} - -type TransferStats struct { - TotalTransfer int64 - TotalDeposits int64 - 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/user.go b/internal/domain/user.go index 73920a5..0a9f173 100644 --- a/internal/domain/user.go +++ b/internal/domain/user.go @@ -10,25 +10,30 @@ var ( ) type User struct { - ID int64 - FirstName string - LastName string - Email string `json:"email"` - PhoneNumber string `json:"phone_number"` - Password []byte - Role Role - EmailVerified bool - PhoneVerified bool - CreatedAt time.Time - UpdatedAt time.Time - SuspendedAt time.Time - Suspended bool - CompanyID ValidInt64 + ID int64 + FirstName string + LastName string + NickName string + Email string `json:"email"` + PhoneNumber string `json:"phone_number"` + Password []byte + Role Role + Age int + EducationLevel string + Country string + Region string + EmailVerified bool + PhoneVerified bool + Suspended bool + SuspendedAt time.Time + OrganizationID ValidInt64 + CreatedAt time.Time + UpdatedAt time.Time } type UserFilter struct { Role string - CompanyID ValidInt64 + OrganizationID ValidInt64 Page ValidInt PageSize ValidInt Query ValidString @@ -37,64 +42,92 @@ type UserFilter struct { } type RegisterUserReq struct { - FirstName string - LastName string - Email string - PhoneNumber string - Password string - Role string - Otp string - ReferralCode string `json:"referral_code"` - OtpMedium OtpMedium - CompanyID ValidInt64 + FirstName string + LastName string + NickName string + Email string + PhoneNumber string + Password string + Role string + Otp string + ReferralCode string `json:"referral_code"` + OtpMedium OtpMedium + OrganizationID ValidInt64 + Age int + EducationLevel string + Country string + Region string } + type CreateUserReq struct { - FirstName string - LastName string - Email string - PhoneNumber string - Password string - Role string - Suspended bool - CompanyID ValidInt64 + FirstName string + LastName string + NickName string + Email string + PhoneNumber string + Password string + Role string + Suspended bool + OrganizationID ValidInt64 + Age int + EducationLevel string + Country string + Region string } + type ResetPasswordReq struct { - Email string - PhoneNumber string - Password string - Otp string - OtpMedium OtpMedium - CompanyID int64 + Email string + PhoneNumber string + Password string + Otp string + OtpMedium OtpMedium + OrganizationID int64 } + type UpdateUserReq struct { - UserId int64 - FirstName ValidString - LastName ValidString - Suspended ValidBool - - CompanyID ValidInt64 + UserID int64 + FirstName ValidString + LastName ValidString + NickName ValidString + Suspended ValidBool + OrganizationID ValidInt64 + Age ValidInt + EducationLevel ValidString + Country ValidString + Region ValidString } -type UpdateUserReferalCode struct { +type UpdateUserReferralCode struct { UserID int64 Code string } -type GetCashier struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email string `json:"email"` - PhoneNumber string `json:"phone_number"` - Role Role `json:"role"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - SuspendedAt time.Time `json:"suspended_at"` - Suspended bool `json:"suspended"` - BranchID int64 `json:"branch_id"` - BranchName string `json:"branch_name"` - BranchWallet int64 `json:"branch_wallet"` - BranchLocation string `json:"branch_location"` -} +// ValidInt64 wraps int64 for optional values +// type ValidInt64 struct { +// Value int64 +// Valid bool +// } + +// // ValidInt wraps int for optional values +// type ValidInt struct { +// Value int +// Valid bool +// } + +// // ValidString wraps string for optional values +// type ValidString struct { +// Value string +// Valid bool +// } + +// // ValidBool wraps bool for optional values +// type ValidBool struct { +// Value bool +// Valid bool +// } + +// // ValidTime wraps time.Time for optional values +// type ValidTime struct { +// Value time.Time +// Valid bool +// } diff --git a/internal/domain/veli_games.go b/internal/domain/veli_games.go deleted file mode 100644 index 33857d5..0000000 --- a/internal/domain/veli_games.go +++ /dev/null @@ -1,284 +0,0 @@ -package domain - -type VeliCallbackErrorResponse struct { - ErrorStatus int `json:"errorStatus"` - ErrorData struct { - Error string `json:"error"` - Details *string `json:"details"` - } `json:"errorData"` -} - -type ProviderRequest struct { - BrandID string `json:"brandId"` - ExtraData bool `json:"extraData"` - Size int `json:"size,omitempty"` - Page int `json:"page,omitempty"` -} - -type ProviderResponse struct { - Items []struct { - ProviderID string `json:"providerId"` - ProviderName string `json:"providerName"` - LogoForDark string `json:"logoForDark"` - LogoForLight string `json:"logoForLight"` - } `json:"items"` -} - -type GameListRequest struct { - BrandID string `json:"brandId"` - ProviderID string `json:"providerId"` - Size int `json:"size,omitempty"` - Page int `json:"page,omitempty"` -} - -type GameEntity struct { - GameID string `json:"gameId"` - ProviderID string `json:"providerId"` - Name string `json:"name"` - DeviceType string `json:"deviceType"` - Category string `json:"category"` - HasDemoMode bool `json:"hasDemoMode"` - HasFreeBets bool `json:"hasFreeBets"` - // Thumbnail string `json:"thumbnail"` // ✅ new field - // DemoURL string `json:"demoUrl"` // ✅ new field -} - -type AtlasGameEntity struct { - GameID string `json:"game_id"` - ProviderID string `json:"providerId"` - Name string `json:"name"` - DeviceType string `json:"deviceType"` - Category string `json:"type"` - HasDemoMode bool `json:"has_demo"` - HasFreeBets bool `json:"hasFreeBets"` - Thumbnail string `json:"thumbnail_img_url"` // ✅ new field - DemoURL string `json:"demo_url"` // ✅ new field -} - -type GameStartRequest struct { - SessionID string `json:"sessionId"` - ProviderID string `json:"providerId"` - GameID string `json:"gameId"` - Language string `json:"language"` - PlayerID string `json:"playerId,omitempty"` - Currency string `json:"currency"` - DeviceType string `json:"deviceType"` - Country string `json:"country"` - IP string `json:"ip"` - BrandID string `json:"brandId"` - // UserAgent string `json:"userAgent,omitempty"` - // LobbyURL string `json:"lobbyUrl,omitempty"` - // CashierURL string `json:"cashierUrl,omitempty"` - // PlayerName string `json:"playerName,omitempty"` -} - -type DemoGameRequest struct { - ProviderID string `json:"providerId"` - GameID string `json:"gameId"` - Language string `json:"language"` - DeviceType string `json:"deviceType"` - IP string `json:"ip"` - BrandID string `json:"brandId"` - // PlayerID string `json:"playerId,omitempty"` - // Country string `json:"country,omitempty"` -} - -type GameStartResponse struct { - StartGameURL string `json:"startGameUrl"` -} - -type BalanceRequest struct { - SessionID string `json:"sessionId"` - ProviderID string `json:"providerId"` - PlayerID string `json:"playerId"` - BrandID string `json:"brandId"` - GameID string `json:"gameId,omitempty"` - Currency string `json:"currency"` -} - -type BalanceResponse struct { - Real struct { - Currency string `json:"currency"` - Amount float64 `json:"amount"` - } `json:"real"` - Bonus *struct { - Currency string `json:"currency"` - Amount float64 `json:"amount"` - } `json:"bonus,omitempty"` -} - -type BetRequest struct { - SessionID string `json:"sessionId"` - BetType string `json:"betType"` - TransactionID string `json:"transactionId"` - GameID string `json:"gameId"` - GameType string `json:"gameType"` - PlayerID string `json:"playerId"` - RoundID string `json:"roundId"` - ProviderID string `json:"providerId"` - CorrelationID string `json:"correlationId"` - BrandID string `json:"brandId"` - JackpotID string `json:"jackpotId,omitempty"` - JackpotContribution float64 `json:"jackpotContribution,omitempty"` - IsAdjustment bool `json:"isAdjustment,omitempty"` - Amount struct { - Amount float64 `json:"amount"` - Currency string `json:"currency"` - } `json:"amount"` -} - -type WinRequest struct { - SessionID string `json:"sessionId"` - WinType string `json:"winType"` // WIN_ORDINARY, WIN_FREE, WIN_JACKPOT - TransactionID string `json:"transactionId"` - GameID string `json:"gameId"` - GameType string `json:"gameType"` // CRASH or OTHER - PlayerID string `json:"playerId"` - RoundID string `json:"roundId"` - CorrelationID string `json:"correlationId"` - ProviderID string `json:"providerId"` - BrandID string `json:"brandId"` - RewardID string `json:"rewardId,omitempty"` - IsCashOut bool `json:"isCashOut,omitempty"` - Amount struct { - Amount float64 `json:"amount"` - Currency string `json:"currency"` - } `json:"amount"` -} - -type CancelRequest struct { - SessionID string `json:"sessionId"` - CancelType string `json:"cancelType"` // CANCEL_BET, CANCEL_ROUND, CANCEL_TRANSACTION - TransactionID string `json:"transactionId"` - RefTransactionID string `json:"refTransactionId,omitempty"` - GameID string `json:"gameId"` - PlayerID string `json:"playerId"` - GameType string `json:"gameType"` - RoundID string `json:"roundId"` - CorrelationID string `json:"correlationId,omitempty"` - ProviderID string `json:"providerId"` - BrandID string `json:"brandId"` - IsAdjustment bool `json:"isAdjustment,omitempty"` - AdjustmentRefund *struct { - Amount float64 `json:"amount"` - Currency string `json:"currency"` - } `json:"adjustmentRefund,omitempty"` -} - -type WinResponse struct { - WalletTransactionID string `json:"walletTransactionId"` - Real BalanceDetail `json:"real"` - Bonus *BalanceDetail `json:"bonus,omitempty"` - UsedRealAmount float64 `json:"usedRealAmount,omitempty"` - UsedBonusAmount float64 `json:"usedBonusAmount,omitempty"` -} - -type BetResponse struct { - WalletTransactionID string `json:"walletTransactionId"` - Real BalanceDetail `json:"real"` - Bonus *BalanceDetail `json:"bonus,omitempty"` - UsedRealAmount float64 `json:"usedRealAmount,omitempty"` - UsedBonusAmount float64 `json:"usedBonusAmount,omitempty"` -} - -type CancelResponse struct { - WalletTransactionID string `json:"walletTransactionId"` - Real BalanceDetail `json:"real"` - Bonus *BalanceDetail `json:"bonus,omitempty"` - UsedRealAmount float64 `json:"usedRealAmount,omitempty"` - UsedBonusAmount float64 `json:"usedBonusAmount,omitempty"` -} - -type BalanceDetail struct { - Currency string `json:"currency"` - Amount float64 `json:"amount"` -} - -// Request -type GamingActivityRequest struct { - FromDate string `json:"fromDate"` // YYYY-MM-DD - ToDate string `json:"toDate"` // YYYY-MM-DD - ProviderID string `json:"providerId,omitempty"` // Optional - Currencies []string `json:"currencies,omitempty"` // Optional - GameIDs []string `json:"gameIds,omitempty"` // Optional - ExcludeFreeWin *bool `json:"excludeFreeWin,omitempty"` // Optional - Page int `json:"page,omitempty"` // Optional, default 1 - Size int `json:"size,omitempty"` // Optional, default 100 - PlayerIDs []string `json:"playerIds,omitempty"` // Optional -} - -// Response -type GamingActivityResponse struct { - Items []GamingActivityItem `json:"items"` - Meta PaginationMeta `json:"meta"` -} - -type GamingActivityItem struct { - TransactionID string `json:"transactionId"` - SessionID string `json:"sessionId"` - RoundID string `json:"roundId"` - CorrelationID string `json:"correlationId"` - RoundType string `json:"roundType"` - ActionType string `json:"actionType"` - ProviderID string `json:"providerId"` - BrandID string `json:"brandId"` - PlayerID string `json:"playerId"` - GameID string `json:"gameId"` - Amount float64 `json:"amount"` - Currency string `json:"currency"` - CreatedAt string `json:"createdAt"` - AmountEur float64 `json:"amountEur"` - AmountUsd float64 `json:"amountUsd"` - RefTransactionID string `json:"refTransactionId,omitempty"` - RefRoundType string `json:"refRoundType,omitempty"` - RefActionType string `json:"refActionType,omitempty"` -} - -type PaginationMeta struct { - TotalItems int `json:"totalItems"` - ItemCount int `json:"itemCount"` - ItemsPerPage int `json:"itemsPerPage"` - TotalPages int `json:"totalPages"` - CurrentPage int `json:"currentPage"` -} - -type HugeWinsRequest struct { - FromDate string `json:"fromDate"` - ToDate string `json:"toDate"` - ProviderID string `json:"providerId,omitempty"` - Currencies []string `json:"currencies,omitempty"` - BrandID string `json:"brandId"` - GameIDs []string `json:"gameIds,omitempty"` - Page int `json:"page,omitempty"` - Size int `json:"size,omitempty"` -} - -type HugeWinsResponse struct { - Items []HugeWinItem `json:"items"` - Meta PaginationMeta `json:"meta"` -} - -type HugeWinItem struct { - BetTransactionID string `json:"betTransactionId"` - WinTransactionID string `json:"winTransactionId"` - Currency string `json:"currency"` - GameID string `json:"gameId"` - BetAmount float64 `json:"betAmount"` - WinAmount float64 `json:"winAmount"` - BetAmountUsd float64 `json:"betAmountUsd"` - WinAmountUsd float64 `json:"winAmountUsd"` - ProviderID string `json:"providerId"` - PlayerID string `json:"playerId"` - RoundID string `json:"roundId"` - CorrelationID string `json:"correlationId"` - BrandID string `json:"brandId"` - OperatorID string `json:"operatorId"` - CreatedAt string `json:"createdAt"` - Reason string `json:"reason"` -} - -type CreditBalance struct { - Currency string `json:"currency"` - Balance float64 `json:"balance"` - Threshold float64 `json:"threshold"` -} diff --git a/internal/domain/virtual_game.go b/internal/domain/virtual_game.go deleted file mode 100644 index d5eacf6..0000000 --- a/internal/domain/virtual_game.go +++ /dev/null @@ -1,377 +0,0 @@ -package domain - -import ( - "time" -) - -type Provider string - -const ( - PROVIDER_POPOK Provider = "PopOk" - PROVIDER_ALEA_PLAY Provider = "AleaPlay" - PROVIDER_VELI_GAMES Provider = "VeliGames" -) - -type FavoriteGame struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - GameID int64 `json:"game_id"` - CreatedAt time.Time `json:"created_at"` -} - -type FavoriteGameRequest struct { - GameID int64 `json:"game_id"` - ProviderID string `json:"provider_id"` -} - -type FavoriteGameResponse struct { - GameID int64 `json:"game_id"` - GameName string `json:"game_name"` -} - -type VirtualGame struct { - ID int64 `json:"id"` - Name string `json:"name"` - Provider string `json:"provider"` - Category string `json:"category"` - MinBet float64 `json:"min_bet"` - MaxBet float64 `json:"max_bet"` - Volatility string `json:"volatility"` - IsActive bool `json:"is_active"` - RTP float64 `json:"rtp"` - IsFeatured bool `json:"is_featured"` - PopularityScore int `json:"popularity_score"` - ThumbnailURL string `json:"thumbnail_url"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` -} - -type VirtualGameSession struct { - ID int64 `json:"id"` - UserID int64 `json:"user_id"` - GameID string `json:"game_id"` - SessionToken string `json:"session_token"` - Currency string `json:"currency"` - Status string `json:"status"` // ACTIVE, COMPLETED, FAILED - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - ExpiresAt time.Time `json:"expires_at"` - - // Alea Play specific fields - ExternalSessionID string `json:"external_session_id"` // Alea's session reference - OperatorID string `json:"operator_id"` // Your operator ID with Alea - GameMode string `json:"game_mode"` // real, demo, tournament -} - -type VirtualGameHistory struct { - ID int64 `json:"id"` - SessionID string `json:"session_id,omitempty"` // Optional, if session tracking is used - UserID int64 `json:"user_id"` - CompanyID int64 `json:"company_id"` - Provider string `json:"provider"` - WalletID *int64 `json:"wallet_id,omitempty"` // Optional if wallet detail is needed - GameID *int64 `json:"game_id,omitempty"` // Optional for game-level analysis - TransactionType string `json:"transaction_type"` // BET, WIN, CANCEL, etc. - Amount int64 `json:"amount"` // Stored in minor units (e.g. cents) - Currency string `json:"currency"` // e.g., ETB, USD - ExternalTransactionID string `json:"external_transaction_id"` // Provider transaction ID - ReferenceTransactionID string `json:"reference_transaction_id,omitempty"` // For CANCELs pointing to BETs - Status string `json:"status"` // COMPLETED, CANCELLED, FAILED, etc. - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -type VirtualGameTransaction struct { - ID int64 `json:"id"` - SessionID int64 `json:"session_id"` - UserID int64 `json:"user_id"` - CompanyID int64 `json:"company_id"` - Provider string `json:"provider"` - GameID string `json:"game_id"` - WalletID int64 `json:"wallet_id"` - TransactionType string `json:"transaction_type"` // BET, WIN, REFUND, CASHOUT, etc. - Amount int64 `json:"amount"` // Always in cents - Currency string `json:"currency"` - ExternalTransactionID string `json:"external_transaction_id"` - ReferenceTransactionID string `json:"reference_transaction_id"` - Status string `json:"status"` // PENDING, COMPLETED, FAILED - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - - // Alea Play specific fields - GameRoundID string `json:"game_round_id"` // Round identifier - Multiplier float64 `json:"multiplier"` // For games like Aviator - IsFreeRound bool `json:"is_free_round"` // For bonus play - OperatorID string `json:"operator_id"` // Your operator ID - - // Veli specific fields - GameSpecificData GameSpecificData `json:"game_specific_data"` -} - -type CreateVirtualGameSession struct { - UserID int64 - GameID string - Currency string - Mode string // REAL, DEMO -} - -type PopOKConfig struct { - ClientID string - SecretKey string - BaseURL string - CallbackURL string - Platform string -} - -type PopOKCallback struct { - TransactionID string `json:"transaction_id"` - SessionID string `json:"session_id"` - Type string `json:"type"` // BET, WIN, REFUND, JACKPOT_WIN - Amount float64 `json:"amount"` - Currency string `json:"currency"` - Timestamp int64 `json:"timestamp"` - Signature string `json:"signature"` // HMAC-SHA256 signature for verification -} - -type PopOKPlayerInfoRequest struct { - ExternalToken string `json:"externalToken"` -} - -type PopOKPlayerInfoResponse struct { - Country string `json:"country"` - Currency string `json:"currency"` - Balance float64 `json:"balance"` - PlayerID string `json:"playerId"` -} - -type PopOKBetRequest struct { - ExternalToken string `json:"externalToken"` - PlayerID string `json:"playerId"` - GameID string `json:"gameId"` - TransactionID string `json:"transactionId"` - Amount float64 `json:"amount"` - Currency string `json:"currency"` -} - -type PopOKBetResponse struct { - TransactionID string `json:"transactionId"` - ExternalTrxID string `json:"externalTrxId"` - Balance float64 `json:"balance"` -} - -// domain/popok.go -type PopOKWinRequest struct { - ExternalToken string `json:"externalToken"` - PlayerID string `json:"playerId"` - GameID string `json:"gameId"` - TransactionID string `json:"transactionId"` - Amount float64 `json:"amount"` - Currency string `json:"currency"` -} - -type PopOKWinResponse struct { - TransactionID string `json:"transactionId"` - ExternalTrxID string `json:"externalTrxId"` - Balance float64 `json:"balance"` -} - -type PopOKGenerateTokenRequest struct { - GameID string `json:"newGameId"` - Token string `json:"token"` -} - -type PopOKCancelRequest struct { - ExternalToken string `json:"externalToken"` - PlayerID string `json:"playerId"` - GameID string `json:"gameId"` - TransactionID string `json:"transactionId"` -} - -type PopOKCancelResponse struct { - TransactionID string `json:"transactionId"` - ExternalTrxID string `json:"externalTrxId"` - Balance float64 `json:"balance"` -} - -type PopOKGenerateTokenResponse struct { - NewToken string `json:"newToken"` -} - -type AleaPlayCallback struct { - EventID string `json:"event_id"` - TransactionID string `json:"transaction_id"` - SessionID string `json:"session_id"` - UserID string `json:"user_id"` - GameID string `json:"game_id"` - Type string `json:"type"` // BET, WIN, CASHOUT, etc. - Amount float64 `json:"amount"` - Currency string `json:"currency"` - RoundID string `json:"round_id"` - Multiplier float64 `json:"multiplier"` - IsFreeRound bool `json:"is_free_round"` - OperatorID string `json:"operator_id"` - Timestamp int64 `json:"timestamp"` - Signature string `json:"signature"` -} - -type VeliCallback struct { - EventType string `json:"event_type"` // "bet_placed", "game_result", etc. - RoundID string `json:"round_id"` // Unique round identifier (replaces transaction_id) - SessionID string `json:"session_id"` // Matches VirtualGameSession.SessionToken - UserID string `json:"user_id"` // Veli's user identifier - GameID string `json:"game_id"` // e.g., "veli_aviator_v1" - Amount float64 `json:"amount"` // Transaction amount - Multiplier float64 `json:"multiplier"` // For games with multipliers (Aviator/Plinko) - Currency string `json:"currency"` // e.g., "USD" - Timestamp int64 `json:"timestamp"` // Unix timestamp - Signature string `json:"signature"` // HMAC-SHA256 -} - -type GameSpecificData struct { - Multiplier float64 `json:"multiplier,omitempty"` - RiskLevel string `json:"risk_level,omitempty"` // For Mines - BucketIndex int `json:"bucket_index,omitempty"` // For Plinko -} - -type PopOKGame struct { - ID int `json:"id"` - GameName string `json:"gameName"` - Bets []float64 `json:"bets"` - Thumbnail string `json:"thumbnail"` - Status int `json:"status"` -} - -type PopOKGameListResponse struct { - Code int `json:"code"` - Message string `json:"message"` - Data struct { - Slots []PopOKGame `json:"slots"` - } `json:"data"` -} - -type GameRecommendation struct { - GameID int `json:"game_id"` - GameName string `json:"game_name"` - Thumbnail string `json:"thumbnail"` - Bets []float64 `json:"bets"` - Reason string `json:"reason"` // e.g., "Based on your activity", "Popular", "Random pick" -} - -type PopokLaunchRequest struct { - Action string `json:"action"` - Platform int `json:"platform"` - PartnerID int `json:"partnerId"` - Time string `json:"time"` - Hash string `json:"hash"` - Data PopokLaunchRequestData `json:"data"` -} - -type PopokLaunchRequestData struct { - GameMode string `json:"gameMode"` - GameID string `json:"gameId"` - Lang string `json:"lang"` - Token string `json:"token"` - ExitURL string `json:"exitURL"` -} - -type PopokLaunchResponse struct { - Code int `json:"code"` - Message string `json:"message"` - Data struct { - LauncherURL string `json:"launcherURL"` - } `json:"data"` -} - -type VirtualGameProvider struct { - // ID int64 `json:"id" db:"id"` - ProviderID string `json:"provider_id" db:"provider_id"` - ProviderName string `json:"provider_name" db:"provider_name"` - LogoDark *string `json:"logo_dark,omitempty" db:"logo_dark"` - LogoLight *string `json:"logo_light,omitempty" db:"logo_light"` - Enabled bool `json:"enabled" db:"enabled"` - CreatedAt time.Time `json:"created_at" db:"created_at"` - UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"` -} - -// VirtualGameProviderPagination is used when returning paginated results -type VirtualGameProviderPagination struct { - Providers []VirtualGameProvider `json:"providers"` - TotalCount int64 `json:"total_count"` - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` -} - -type UnifiedGame struct { - GameID string `json:"gameId"` - ProviderID string `json:"providerId"` - Provider string `json:"provider"` - Name string `json:"name"` - Category string `json:"category,omitempty"` - DeviceType string `json:"deviceType,omitempty"` - Volatility string `json:"volatility,omitempty"` - RTP *float64 `json:"rtp,omitempty"` - HasDemo bool `json:"hasDemo"` - HasFreeBets bool `json:"hasFreeBets"` - Bets []float64 `json:"bets,omitempty"` - Thumbnail string `json:"thumbnail,omitempty"` - Status int `json:"status,omitempty"` - DemoURL string `json:"demoUrl"` -} - -type CreateVirtualGameProviderReport struct { - ProviderID string `json:"provider_id"` - ReportDate time.Time `json:"report_date"` - TotalGamesPlayed int64 `json:"total_games_played"` - TotalBets float64 `json:"total_bets"` - TotalPayouts float64 `json:"total_payouts"` - TotalPlayers int64 `json:"total_players"` - ReportType string `json:"report_type"` // e.g., "daily", "weekly" -} - -type VirtualGameProviderReport struct { - ID int64 `json:"id"` - ProviderID string `json:"provider_id"` - ReportDate time.Time `json:"report_date"` - TotalGamesPlayed int64 `json:"total_games_played"` - TotalBets float64 `json:"total_bets"` - TotalPayouts float64 `json:"total_payouts"` - TotalProfit float64 `json:"total_profit"` - TotalPlayers int64 `json:"total_players"` - ReportType string `json:"report_type"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -type CreateVirtualGameReport struct { - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - ReportDate time.Time `json:"report_date"` - TotalRounds int64 `json:"total_rounds"` - TotalBets float64 `json:"total_bets"` - TotalPayouts float64 `json:"total_payouts"` - TotalPlayers int64 `json:"total_players"` - ReportType string `json:"report_type"` // e.g., "daily", "weekly" -} - -type VirtualGameReport struct { - ID int64 `json:"id"` - GameID string `json:"game_id"` - ProviderID string `json:"provider_id"` - ReportDate time.Time `json:"report_date"` - TotalRounds int64 `json:"total_rounds"` - TotalBets float64 `json:"total_bets"` - TotalPayouts float64 `json:"total_payouts"` - TotalProfit float64 `json:"total_profit"` - TotalPlayers int64 `json:"total_players"` - ReportType string `json:"report_type"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -type CreateVirtualGameProviderReportsRequest struct { - Reports []CreateVirtualGameProviderReport -} - -type CreateVirtualGameReportsRequest struct { - Reports []CreateVirtualGameReport -} diff --git a/internal/domain/virtual_report.go b/internal/domain/virtual_report.go deleted file mode 100644 index 4b8778a..0000000 --- a/internal/domain/virtual_report.go +++ /dev/null @@ -1,149 +0,0 @@ -package domain - -import ( - "math/big" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type FinancialReport struct { - ID int64 `json:"id"` - GameID string `json:"gameId"` - ProviderID string `json:"providerId"` - ReportDate string `json:"reportDate"` // YYYY-MM-DD - ReportType string `json:"reportType"` - TotalBets float64 `json:"totalBets"` - TotalWins float64 `json:"totalWins"` - GGR float64 `json:"ggr"` - RTP float64 `json:"rtp"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt *time.Time `json:"updatedAt,omitempty"` -} - -func ConvertDBFinancialReport(dbReport dbgen.VirtualGameFinancialReport) FinancialReport { - return FinancialReport{ - ID: dbReport.ID, - GameID: dbReport.GameID, - ProviderID: dbReport.ProviderID, - ReportDate: dbReport.ReportDate.Time.Format("2006-01-02"), - ReportType: dbReport.ReportType, - TotalBets: float64(dbReport.TotalBets.Exp), - TotalWins: float64(dbReport.TotalWins.Exp), - GGR: float64(dbReport.Ggr.Exp), - RTP: float64(dbReport.Rtp.Exp), - CreatedAt: dbReport.CreatedAt.Time, - UpdatedAt: &dbReport.UpdatedAt.Time, - } -} - -type CompanyReport struct { - ID int64 `json:"id"` - CompanyID int64 `json:"companyId"` - ProviderID string `json:"providerId"` - ReportDate string `json:"reportDate"` // YYYY-MM-DD - ReportType string `json:"reportType"` - TotalBetAmount float64 `json:"totalBetAmount"` - TotalWinAmount float64 `json:"totalWinAmount"` - NetProfit float64 `json:"netProfit"` - ProfitMargin float64 `json:"profitMargin"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt *time.Time `json:"updatedAt,omitempty"` -} - -// ConvertDBCompanyReport converts the SQLC generated CompanyReport struct to domain.CompanyReport -func ConvertDBCompanyReport(dbReport dbgen.VirtualGameCompanyReport) CompanyReport { - var updatedAt *time.Time - if !dbReport.UpdatedAt.Time.IsZero() { - updatedAt = &dbReport.UpdatedAt.Time - } - - // convert big.Int-backed numeric fields to float64 safely - var totalBetAmount float64 - if dbReport.TotalBetAmount.Int != nil { - if f, _ := new(big.Float).SetInt(dbReport.TotalBetAmount.Int).Float64(); true { - totalBetAmount = f - } - } - - var totalWinAmount float64 - if dbReport.TotalWinAmount.Int != nil { - if f, _ := new(big.Float).SetInt(dbReport.TotalWinAmount.Int).Float64(); true { - totalWinAmount = f - } - } - - var netProfit float64 - if dbReport.NetProfit.Int != nil { - if f, _ := new(big.Float).SetInt(dbReport.NetProfit.Int).Float64(); true { - netProfit = f - } - } - - var profitMargin float64 - if dbReport.ProfitMargin.Int != nil { - if f, _ := new(big.Float).SetInt(dbReport.ProfitMargin.Int).Float64(); true { - profitMargin = f - } - } - - return CompanyReport{ - ID: dbReport.ID, - CompanyID: dbReport.CompanyID, - ProviderID: dbReport.ProviderID, - ReportDate: dbReport.ReportDate.Time.Format("2006-01-02"), - ReportType: dbReport.ReportType, - TotalBetAmount: totalBetAmount, - TotalWinAmount: totalWinAmount, - NetProfit: netProfit, - ProfitMargin: profitMargin, - CreatedAt: dbReport.CreatedAt.Time, - UpdatedAt: updatedAt, - } -} - -type CompanyProfitTrend struct { - ReportDate string `json:"reportDate"` - TotalProfit float64 `json:"totalProfit"` -} - -type PlayerActivityReport struct { - ID int64 `json:"id"` - UserID int64 `json:"userId"` - ReportDate string `json:"reportDate"` // YYYY-MM-DD - ReportType string `json:"reportType"` - TotalDeposits float64 `json:"totalDeposits"` - TotalWithdrawals float64 `json:"totalWithdrawals"` - NetContribution float64 `json:"netContribution"` - TotalBetAmount float64 `json:"totalBetAmount"` - TotalWinAmount float64 `json:"totalWinAmount"` - NetResult float64 `json:"netResult"` - RoundsPlayed int64 `json:"roundsPlayed"` - AvgBetSize float64 `json:"avgBetSize"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt *time.Time `json:"updatedAt,omitempty"` -} - -func ConvertDBPlayerActivityReport(dbReport dbgen.VirtualGamePlayerActivityReport) PlayerActivityReport { - return PlayerActivityReport{ - ID: dbReport.ID, - UserID: dbReport.UserID, - ReportDate: dbReport.ReportDate.Time.Format("2006-01-02"), - ReportType: dbReport.ReportType, - TotalDeposits: float64(dbReport.TotalDeposits.Exp), - TotalWithdrawals: float64(dbReport.TotalWithdrawals.Exp), - NetContribution: float64(dbReport.NetContribution.Exp), - TotalBetAmount: float64(dbReport.TotalBetAmount.Exp), - TotalWinAmount: float64(dbReport.TotalWinAmount.Exp), - NetResult: float64(dbReport.NetResult.Exp), - RoundsPlayed: dbReport.RoundsPlayed.Int64, - AvgBetSize: float64(dbReport.AvgBetSize.Exp), - CreatedAt: dbReport.CreatedAt.Time, - UpdatedAt: &dbReport.UpdatedAt.Time, - } -} - -type TopPlayerNetResult struct { - UserID int64 `json:"userId"` - TotalNet float64 `json:"totalNet"` -} diff --git a/internal/domain/wallet.go b/internal/domain/wallet.go deleted file mode 100644 index ca5407e..0000000 --- a/internal/domain/wallet.go +++ /dev/null @@ -1,170 +0,0 @@ -package domain - -import ( - "errors" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -var ( - ErrWalletIDDuplicate = errors.New("there already exists user id with wallet_type") -) - -type Wallet struct { - ID int64 - Balance Currency - Currency IntCurrency - IsWithdraw bool - IsBettable bool - IsTransferable bool - IsActive bool - UserID int64 - Type WalletType - UpdatedAt time.Time - CreatedAt time.Time -} - -type WalletFilter struct { - IsActive ValidBool - Query ValidString - CreatedBefore ValidTime - CreatedAfter ValidTime -} - -type CustomerWallet struct { - ID int64 - RegularID int64 - StaticID int64 - CustomerID int64 -} -type GetCustomerWallet struct { - ID int64 - RegularID int64 - RegularBalance Currency - StaticID int64 - StaticBalance Currency - CustomerID int64 - RegularIsActive bool - StaticIsActive bool - RegularUpdatedAt time.Time - StaticUpdatedAt time.Time - CreatedAt time.Time - FirstName string - LastName string - PhoneNumber string - NumberOfTransactions int64 - TotalTransactions Currency - NumberOfDeposits int64 - TotalDepositsAmount Currency - NumberOfWithdraws int64 - TotalWithdrawsAmount Currency - NumberOfTransfers int64 - TotalTransfersAmount Currency - UpdatedAt time.Time -} - -type BranchWallet struct { - ID int64 - Balance Currency - IsActive bool - Name string - Location string - BranchManagerID int64 - CompanyID int64 - IsSelfOwned bool - UpdatedAt time.Time - CreatedAt time.Time -} - -type CreateWallet struct { - IsWithdraw bool - IsBettable bool - IsTransferable bool - UserID int64 - Type WalletType -} - -type CreateCustomerWallet struct { - CustomerID int64 - RegularWalletID int64 - StaticWalletID int64 -} - -type WalletType string - -const ( - RegularWalletType WalletType = "regular_wallet" - StaticWalletType WalletType = "static_wallet" - BranchWalletType WalletType = "branch_wallet" - CompanyWalletType WalletType = "company_wallet" -) - -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, - } -} - -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), - } -} - -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, - } -} - -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, - NumberOfTransactions: customerWallet.NumberOfTransactions, - TotalTransactions: Currency(customerWallet.TotalTransactions), - NumberOfDeposits: customerWallet.NumberOfDeposits, - TotalDepositsAmount: Currency(customerWallet.TotalDepositsAmount), - NumberOfWithdraws: customerWallet.NumberOfWithdraws, - TotalWithdrawsAmount: Currency(customerWallet.TotalWithdrawsAmount), - NumberOfTransfers: customerWallet.NumberOfTransfers, - TotalTransfersAmount: Currency(customerWallet.TotalTransfersAmount), - UpdatedAt: customerWallet.StatsUpdatedAt.Time, - } -} diff --git a/internal/domain/wallet_stats.go b/internal/domain/wallet_stats.go deleted file mode 100644 index 247b89a..0000000 --- a/internal/domain/wallet_stats.go +++ /dev/null @@ -1,108 +0,0 @@ -package domain - -import ( - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type WalletStat struct { - IntervalStart time.Time - WalletID int64 - WalletUserID int64 - WalletUserFirstName string - WalletUserLastName string - WalletType string - NumberOfTransactions int64 - TotalTransactions Currency - NumberOfDeposits int64 - TotalDepositsAmount Currency - NumberOfWithdraws int64 - TotalWithdrawsAmount Currency - NumberOfTransfers int64 - TotalTransfersAmount Currency - UpdatedAt time.Time -} - -type WalletStatRes struct { - IntervalStart time.Time `json:"interval_start"` - WalletID int64 `json:"wallet_id"` - WalletUserID int64 `json:"wallet_user_id"` - WalletUserFirstName string `json:"wallet_user_first_name"` - WalletUserLastName string `json:"wallet_user_last_name"` - WalletType string `json:"wallet_type"` - NumberOfTransactions int64 `json:"number_of_transactions"` - TotalTransactions float32 `json:"total_transactions"` - NumberOfDeposits int64 `json:"number_of_deposits"` - TotalDepositsAmount float32 `json:"total_deposits_amount"` - NumberOfWithdraws int64 `json:"number_of_withdraws"` - TotalWithdrawsAmount float32 `json:"total_withdraws_amount"` - NumberOfTransfers int64 `json:"number_of_transfers"` - TotalTransfersAmount float32 `json:"total_transfers_amount"` - UpdatedAt time.Time `json:"updated_at"` -} - -type WalletStatFilter struct { - Interval ValidDateInterval - UserID ValidInt64 -} - -func ConvertDBWalletStats(stats dbgen.WalletStat) WalletStat { - return WalletStat{ - IntervalStart: stats.IntervalStart.Time, - WalletID: stats.WalletID, - WalletUserID: stats.WalletUserID, - WalletUserFirstName: stats.WalletUserFirstName, - WalletUserLastName: stats.WalletUserLastName, - WalletType: stats.WalletType, - NumberOfTransactions: stats.NumberOfTransactions, - TotalTransactions: Currency(stats.TotalTransactions), - NumberOfDeposits: stats.NumberOfDeposits, - TotalDepositsAmount: Currency(stats.TotalDepositsAmount), - NumberOfWithdraws: stats.NumberOfWithdraws, - TotalWithdrawsAmount: Currency(stats.TotalWithdrawsAmount), - NumberOfTransfers: stats.NumberOfTransfers, - TotalTransfersAmount: Currency(stats.TotalTransfersAmount), - UpdatedAt: stats.UpdatedAt.Time, - } -} - - -func ConvertDBWalletStatsList(stats []dbgen.WalletStat) []WalletStat { - result := make([]WalletStat, len(stats)) - for i, stat := range stats { - result[i] = ConvertDBWalletStats(stat) - } - return result -} - -func ConvertDBWalletStatsByInterval(stats dbgen.GetWalletStatsRow) WalletStat { - return WalletStat{ - IntervalStart: stats.IntervalStart.Time, - WalletID: stats.WalletID, - WalletUserID: stats.WalletUserID, - WalletUserFirstName: stats.WalletUserFirstName, - WalletUserLastName: stats.WalletUserLastName, - WalletType: stats.WalletType, - NumberOfTransactions: stats.NumberOfTransactions, - TotalTransactions: Currency(stats.TotalTransactions), - NumberOfDeposits: stats.NumberOfDeposits, - TotalDepositsAmount: Currency(stats.TotalDepositsAmount), - NumberOfWithdraws: stats.NumberOfWithdraws, - TotalWithdrawsAmount: Currency(stats.TotalWithdrawsAmount), - NumberOfTransfers: stats.NumberOfTransfers, - TotalTransfersAmount: Currency(stats.TotalTransfersAmount), - UpdatedAt: stats.UpdatedAt.Time, - } -} - - -func ConvertDBWalletStatsByIntervalList(stats []dbgen.GetWalletStatsRow) []WalletStat { - result := make([]WalletStat, len(stats)) - for i, stat := range stats { - result[i] = ConvertDBWalletStatsByInterval(stat) - } - return result -} - - diff --git a/internal/event/wallet_event.go b/internal/event/wallet_event.go deleted file mode 100644 index 54fc158..0000000 --- a/internal/event/wallet_event.go +++ /dev/null @@ -1,19 +0,0 @@ -package event - -import "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - -type WalletEventType string - -const ( - WalletBalanceUpdated WalletEventType = "wallet.balance.updated" - WalletLowAlert WalletEventType = "wallet.alert.low_balance" -) - -type WalletEvent struct { - EventType WalletEventType `json:"event_type"` - WalletID int64 `json:"wallet_id"` - UserID int64 `json:"user_id"` - Balance domain.Currency `json:"balance"` - WalletType domain.WalletType `json:"wallet_type"` - Trigger string `json:"trigger"` // e.g. "AddToWallet", "DeductFromWallet" -} diff --git a/internal/infrastructure/csv_exporter.go b/internal/infrastructure/csv_exporter.go index ac6a07f..f2263fa 100644 --- a/internal/infrastructure/csv_exporter.go +++ b/internal/infrastructure/csv_exporter.go @@ -2,12 +2,11 @@ package infrastructure import ( + "Yimaru-Backend/internal/domain" "encoding/csv" "os" "strconv" "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) type CSVExporter struct { diff --git a/internal/logger/mongoLogger/init.go b/internal/logger/mongoLogger/init.go index 6784c04..f62b2c4 100644 --- a/internal/logger/mongoLogger/init.go +++ b/internal/logger/mongoLogger/init.go @@ -1,10 +1,10 @@ package mongoLogger import ( + "Yimaru-Backend/internal/config" "fmt" "os" - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) diff --git a/internal/logger/mongoLogger/logger.go b/internal/logger/mongoLogger/logger.go index 55197df..a1b1c83 100644 --- a/internal/logger/mongoLogger/logger.go +++ b/internal/logger/mongoLogger/logger.go @@ -1,13 +1,13 @@ package mongoLogger import ( + "Yimaru-Backend/internal/config" "context" "fmt" "time" "maps" - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" diff --git a/internal/middleware/alea.go b/internal/middleware/alea.go deleted file mode 100644 index 36e3f1b..0000000 --- a/internal/middleware/alea.go +++ /dev/null @@ -1,34 +0,0 @@ -package middleware - -import ( - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - - "github.com/gofiber/fiber/v2" -) - -func AleaWebhookMiddleware(secretKey string) fiber.Handler { - return func(c *fiber.Ctx) error { - // Verify IP comes from Alea's allowed IPs - // OR verify a signature header - - // Example signature verification: - receivedSig := c.Get("X-Alea-Signature") - body := c.Body() - - h := hmac.New(sha256.New, []byte(secretKey)) - h.Write(body) - expectedSig := hex.EncodeToString(h.Sum(nil)) - - if receivedSig != expectedSig { - return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ - "error": "invalid signature", - }) - } - - return c.Next() - } -} - -// Then update your route: diff --git a/internal/ports/auth.go b/internal/ports/auth.go index 57fdb5f..f27f650 100644 --- a/internal/ports/auth.go +++ b/internal/ports/auth.go @@ -1,9 +1,8 @@ package ports import ( + "Yimaru-Backend/internal/domain" "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) type TokenStore interface { diff --git a/internal/ports/bet.go b/internal/ports/bet.go deleted file mode 100644 index 75e862d..0000000 --- a/internal/ports/bet.go +++ /dev/null @@ -1,56 +0,0 @@ -package ports - -import ( - "context" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "time" -) - -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 -} - -type BetStatStore interface { - GetBetStatsByInterval(ctx context.Context, filter domain.BetStatsByIntervalFilter) ([]domain.BetStatsByInterval, error) -} diff --git a/internal/ports/bonus.go b/internal/ports/bonus.go deleted file mode 100644 index a48c06f..0000000 --- a/internal/ports/bonus.go +++ /dev/null @@ -1,18 +0,0 @@ -package ports - - -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/ports/branch.go b/internal/ports/branch.go deleted file mode 100644 index c8f9666..0000000 --- a/internal/ports/branch.go +++ /dev/null @@ -1,40 +0,0 @@ -package ports - -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) -} - -type BranchStatStore interface { - UpdateBranchStats(ctx context.Context) error - GetBranchStatByID(ctx context.Context, branchID int64) ([]domain.BranchStat, error) - GetBranchStatsByInterval(ctx context.Context, filter domain.BranchStatFilter) ([]domain.BranchStat, error) -} diff --git a/internal/ports/company.go b/internal/ports/company.go deleted file mode 100644 index d9342cc..0000000 --- a/internal/ports/company.go +++ /dev/null @@ -1,26 +0,0 @@ -package ports - -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) -} - -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/ports/event.go b/internal/ports/event.go deleted file mode 100644 index fca037b..0000000 --- a/internal/ports/event.go +++ /dev/null @@ -1,40 +0,0 @@ -package ports - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type EventStore interface { - 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) - 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 -} - -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 -} diff --git a/internal/ports/league.go b/internal/ports/league.go deleted file mode 100644 index 8adbc79..0000000 --- a/internal/ports/league.go +++ /dev/null @@ -1,17 +0,0 @@ -package ports - -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/ports/market_settings.go b/internal/ports/market_settings.go deleted file mode 100644 index 5905ef3..0000000 --- a/internal/ports/market_settings.go +++ /dev/null @@ -1,21 +0,0 @@ -package ports - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type MarketSettingStore interface { - InsertGlobalMarketSettings(ctx context.Context, setting domain.CreateGlobalMarketSettings) error - InsertCompanyMarketSettings(ctx context.Context, setting domain.CreateCompanyMarketSettings) error - GetAllGlobalMarketSettings(ctx context.Context, filter domain.MarketSettingFilter) ([]domain.MarketSettings, error) - GetGlobalMarketSettingsByID(ctx context.Context, Id int64) (domain.MarketSettings, error) - GetAllCompanyMarketSettings(ctx context.Context, filter domain.CompanyMarketSettingFilter) ([]domain.CompanyMarketSettings, error) - GetCompanyMarketSettings(ctx context.Context, ID int64) (domain.CompanyMarketSettings, error) - GetAllOverrideMarketSettings(ctx context.Context, companyID int64, filter domain.MarketSettingFilter) ([]domain.MarketSettings, error) - GetOverrideMarketSettingByID(ctx context.Context, companyID int64, marketID int64) (domain.MarketSettings, error) - DeleteAllCompanyMarketSettings(ctx context.Context, companyID int64) error - DeleteCompanyMarketSettings(ctx context.Context, companyID int64, marketID int64) error - EnsureAllMarketSettingsExist(ctx context.Context) error -} diff --git a/internal/ports/notification.go b/internal/ports/notification.go index adcd3b5..fa5c1d0 100644 --- a/internal/ports/notification.go +++ b/internal/ports/notification.go @@ -3,7 +3,7 @@ package ports import ( "context" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "Yimaru-Backend/internal/domain" ) type NotificationStore interface { diff --git a/internal/ports/odds.go b/internal/ports/odds.go deleted file mode 100644 index d85ddfc..0000000 --- a/internal/ports/odds.go +++ /dev/null @@ -1,31 +0,0 @@ -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/ports/raffle.go b/internal/ports/raffle.go deleted file mode 100644 index 9ef6ea7..0000000 --- a/internal/ports/raffle.go +++ /dev/null @@ -1,27 +0,0 @@ -package ports - -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/ports/referral.go b/internal/ports/referral.go index 2e78c14..7faf79a 100644 --- a/internal/ports/referral.go +++ b/internal/ports/referral.go @@ -1,19 +1,13 @@ 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) + // 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 deleted file mode 100644 index 88fb18a..0000000 --- a/internal/ports/report.go +++ /dev/null @@ -1,44 +0,0 @@ -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/ports/result.go b/internal/ports/result.go deleted file mode 100644 index b2e32c3..0000000 --- a/internal/ports/result.go +++ /dev/null @@ -1,12 +0,0 @@ -package ports - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -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/ports/settings.go b/internal/ports/settings.go index bd16c0b..6e97586 100644 --- a/internal/ports/settings.go +++ b/internal/ports/settings.go @@ -2,24 +2,22 @@ package ports 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 + // 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 + // 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 EnsureAllSettingsExist(ctx context.Context) error } diff --git a/internal/ports/ticket.go b/internal/ports/ticket.go deleted file mode 100644 index 453c68a..0000000 --- a/internal/ports/ticket.go +++ /dev/null @@ -1,18 +0,0 @@ -package ports - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type TicketStore interface { - CreateTicket(ctx context.Context, ticket domain.CreateTicket) (domain.Ticket, error) - CreateTicketOutcome(ctx context.Context, outcomes []domain.CreateTicketOutcome) (int64, error) - GetTicketByID(ctx context.Context, id int64) (domain.GetTicket, error) - GetAllTickets(ctx context.Context, filter domain.TicketFilter) ([]domain.GetTicket, error) - CountTicketByIP(ctx context.Context, IP string) (int64, error) - UpdateTicketOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error - DeleteOldTickets(ctx context.Context) error - DeleteTicket(ctx context.Context, id int64) error -} diff --git a/internal/ports/transaction.go b/internal/ports/transaction.go index 2c377e3..2c072e1 100644 --- a/internal/ports/transaction.go +++ b/internal/ports/transaction.go @@ -1,31 +1,20 @@ package ports -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) + // 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 + // 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 + // 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/ports/user.go b/internal/ports/user.go index 0a8b138..2450de3 100644 --- a/internal/ports/user.go +++ b/internal/ports/user.go @@ -3,7 +3,7 @@ package ports import ( "context" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "Yimaru-Backend/internal/domain" ) type UserStore interface { @@ -11,8 +11,8 @@ type UserStore interface { 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) + // 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 diff --git a/internal/ports/virtual_report.go b/internal/ports/virtual_report.go deleted file mode 100644 index ddc6078..0000000 --- a/internal/ports/virtual_report.go +++ /dev/null @@ -1,27 +0,0 @@ -package ports - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type VirtualGameReportStore interface { - CreateFinancialReport(ctx context.Context, report domain.FinancialReport) (domain.FinancialReport, error) - UpsertFinancialReport(ctx context.Context, report domain.FinancialReport) (domain.FinancialReport, error) - GetFinancialReportByID(ctx context.Context, id int64) (domain.FinancialReport, error) - GetFinancialReportsForGame(ctx context.Context, gameID, providerID string, from, to string) ([]domain.FinancialReport, error) - GetDailyFinancialReports(ctx context.Context, reportDate string) ([]domain.FinancialReport, error) - DeleteFinancialReport(ctx context.Context, id int64) error - CreateCompanyReport(ctx context.Context, report domain.CompanyReport) (domain.CompanyReport, error) - UpsertCompanyReport(ctx context.Context, report domain.CompanyReport) (domain.CompanyReport, error) - GetCompanyReportByID(ctx context.Context, id int64) (domain.CompanyReport, error) - GetCompanyReportsInRange(ctx context.Context, companyID int64, providerID string, startDate, endDate string) ([]domain.CompanyReport, error) - GetCompanyProfitTrend(ctx context.Context, companyID int64, providerID string, startDate, endDate string) ([]domain.CompanyProfitTrend, error) - CreatePlayerActivityReport(ctx context.Context, report domain.PlayerActivityReport) (domain.PlayerActivityReport, error) - GetPlayerActivityByID(ctx context.Context, id int64) (domain.PlayerActivityReport, error) - GetPlayerActivityByDate(ctx context.Context, userID int64, reportDate, reportType string) (domain.PlayerActivityReport, error) - GetPlayerActivityRange(ctx context.Context, userID int64, startDate, endDate string) ([]domain.PlayerActivityReport, error) - GetTopPlayersByNetResult(ctx context.Context, startDate, endDate string, limit int) ([]domain.TopPlayerNetResult, error) - DeletePlayerActivityReport(ctx context.Context, id int64) error -} diff --git a/internal/ports/wallet.go b/internal/ports/wallet.go deleted file mode 100644 index c8eb97d..0000000 --- a/internal/ports/wallet.go +++ /dev/null @@ -1,62 +0,0 @@ -package ports - -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) -// } - -type WalletStatStore interface { - UpdateWalletStats(ctx context.Context) error - GetWalletStatByID(ctx context.Context, walletID int64) ([]domain.WalletStat, error) - GetWalletStatsByInterval(ctx context.Context, filter domain.WalletStatFilter) ([]domain.WalletStat, error) -} diff --git a/internal/repository/auth.go b/internal/repository/auth.go index 25ca516..7a92857 100644 --- a/internal/repository/auth.go +++ b/internal/repository/auth.go @@ -5,32 +5,31 @@ import ( "database/sql" "errors" - 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" + dbgen "Yimaru-Backend/gen/db" + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/ports" + "Yimaru-Backend/internal/services/authentication" + "github.com/jackc/pgx/v5/pgtype" ) -// Interface for creating new token store -func NewTokenStore(s *Store) ports.TokenStore { return s } +// NewTokenStore returns a TokenStore implementation +func NewTokenStore(s *Store) ports.TokenStore { + return s +} +// CreateRefreshToken inserts a new refresh token into the database func (s *Store) CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error { return s.queries.CreateRefreshToken(ctx, dbgen.CreateRefreshTokenParams{ - UserID: rt.UserID, - Token: rt.Token, - CreatedAt: pgtype.Timestamptz{ - Time: rt.CreatedAt, - Valid: true, - }, - ExpiresAt: pgtype.Timestamptz{ - Time: rt.ExpiresAt, - Valid: true, - }, - Revoked: rt.Revoked, + UserID: rt.UserID, + Token: rt.Token, + ExpiresAt: pgtype.Timestamptz{Time: rt.ExpiresAt}, + CreatedAt: pgtype.Timestamptz{Time: rt.CreatedAt}, + Revoked: rt.Revoked, }) - } + +// GetRefreshToken retrieves a refresh token by its token string func (s *Store) GetRefreshToken(ctx context.Context, token string) (domain.RefreshToken, error) { rf, err := s.queries.GetRefreshToken(ctx, token) if err != nil { @@ -39,6 +38,7 @@ func (s *Store) GetRefreshToken(ctx context.Context, token string) (domain.Refre } return domain.RefreshToken{}, err } + return domain.RefreshToken{ Token: rf.Token, UserID: rf.UserID, @@ -48,6 +48,7 @@ func (s *Store) GetRefreshToken(ctx context.Context, token string) (domain.Refre }, nil } +// GetRefreshTokenByUserID retrieves a refresh token for a specific user func (s *Store) GetRefreshTokenByUserID(ctx context.Context, id int64) (domain.RefreshToken, error) { rf, err := s.queries.GetRefreshTokenByUserID(ctx, id) if err != nil { @@ -66,6 +67,47 @@ func (s *Store) GetRefreshTokenByUserID(ctx context.Context, id int64) (domain.R }, nil } +// RevokeRefreshToken marks a refresh token as revoked func (s *Store) RevokeRefreshToken(ctx context.Context, token string) error { return s.queries.RevokeRefreshToken(ctx, token) } + +// GetUserByEmailOrPhone retrieves a user by email or phone number and optional organization ID +func (s *Store) GetUserByEmailOrPhone(ctx context.Context, email, phone string, organizationID *int64) (domain.User, error) { + // prepare organizationID param for the query + // var orgParam pgtype.Int8 + // if organizationID != nil { + // orgParam = pgtype.Int8{Int64: *organizationID} + // } else { + // orgParam = pgtype.Int8{Status: pgtype.Null} + // } + + u, err := s.queries.GetUserByEmailPhone(ctx, dbgen.GetUserByEmailPhoneParams{ + Email: pgtype.Text{String: email, Valid: email != ""}, + PhoneNumber: pgtype.Text{String: phone, Valid: phone != ""}, + OrganizationID: pgtype.Int8{Int64: *organizationID}, + }) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return domain.User{}, authentication.ErrUserNotFound + } + return domain.User{}, err + } + + return domain.User{ + ID: u.ID, + FirstName: u.FirstName, + LastName: u.LastName, + Email: u.Email.String, + PhoneNumber: u.PhoneNumber.String, + Role: domain.Role(u.Role), + Password: u.Password, + EmailVerified: u.EmailVerified, + PhoneVerified: u.PhoneVerified, + Suspended: u.Suspended, + SuspendedAt: u.SuspendedAt.Time, + OrganizationID: domain.ValidInt64{Value: u.OrganizationID.Int64, Valid: u.OrganizationID.Valid}, + CreatedAt: u.CreatedAt.Time, + UpdatedAt: u.UpdatedAt.Time, + }, nil +} diff --git a/internal/repository/bet.go b/internal/repository/bet.go deleted file mode 100644 index cec0a20..0000000 --- a/internal/repository/bet.go +++ /dev/null @@ -1,1384 +0,0 @@ -package repository - -import ( - "context" - "fmt" - "time" - - 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" -) - -// 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 { - return domain.Bet{}, err - } - return domain.ConvertDBBet(newBet), err - -} - -func (s *Store) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBetOutcome) (int64, error) { - var dbParams []dbgen.CreateBetOutcomeParams = make([]dbgen.CreateBetOutcomeParams, 0, len(outcomes)) - - for _, outcome := range outcomes { - dbParams = append(dbParams, domain.ConvertDBCreateBetOutcome(outcome)) - } - - rows, err := s.queries.CreateBetOutcome(ctx, dbParams) - if err != nil { - domain.MongoDBLogger.Error("failed to create bet outcomes in DB", - zap.Int("outcome_count", len(outcomes)), - zap.Any("bet_id", outcomes[0].BetID), // assumes all outcomes have same BetID - zap.Error(err), - ) - return rows, err - } - - return rows, nil -} - -func (s *Store) CreateFlag(ctx context.Context, flag domain.CreateFlagReq) (domain.Flag, error) { - createFlag := dbgen.CreateFlagParams{ - BetID: pgtype.Int8{ - Int64: flag.BetID, - Valid: flag.BetID != 0, - }, - OddsMarketID: pgtype.Int8{ - Int64: flag.OddID, - Valid: flag.OddID != 0, - }, - Reason: pgtype.Text{ - String: flag.Reason, - Valid: true, - }, - } - - f, err := s.queries.CreateFlag(ctx, createFlag) - if err != nil { - domain.MongoDBLogger.Error("failed to create flag", - zap.String("flag", f.Reason.String), - zap.Any("flag_id", f.ID), - zap.Error(err), - ) - return domain.Flag{}, err - } - - return domain.ConvertDBFlag(f), nil -} - -func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) { - bet, err := s.queries.GetBetByID(ctx, id) - if err != nil { - domain.MongoDBLogger.Error("failed to get bet by ID", - zap.Int64("bet_id", id), - zap.Error(err), - ) - return domain.GetBet{}, err - } - - return domain.ConvertDBBetWithOutcomes(bet), nil -} - -func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, int64, error) { - bets, err := s.queries.GetAllBets(ctx, dbgen.GetAllBetsParams{ - UserID: filter.UserID.ToPG(), - CompanyID: filter.CompanyID.ToPG(), - Status: filter.Status.ToPG(), - CashedOut: filter.CashedOut.ToPG(), - IsShopBet: filter.IsShopBet.ToPG(), - Query: filter.Query.ToPG(), - CreatedBefore: filter.CreatedBefore.ToPG(), - CreatedAfter: filter.CreatedAfter.ToPG(), - Offset: pgtype.Int4{ - Int32: int32(filter.Offset.Value * filter.Limit.Value), - Valid: filter.Offset.Valid, - }, - Limit: filter.Limit.ToPG(), - }) - if err != nil { - domain.MongoDBLogger.Error("failed to get all bets", - zap.Any("filter", filter), - zap.Error(err), - ) - return nil, 0, err - } - - total, err := s.queries.GetTotalBets(ctx, dbgen.GetTotalBetsParams{ - UserID: filter.UserID.ToPG(), - CompanyID: filter.CompanyID.ToPG(), - Status: filter.Status.ToPG(), - CashedOut: filter.CashedOut.ToPG(), - IsShopBet: filter.IsShopBet.ToPG(), - Query: filter.Query.ToPG(), - CreatedBefore: filter.CreatedBefore.ToPG(), - CreatedAfter: filter.CreatedAfter.ToPG(), - }) - - if err != nil { - // domain.MongoDBLogger.Error("failed to get all bets", - // zap.Any("filter", filter), - // zap.Error(err), - // ) - return nil, 0, err - } - - var result []domain.GetBet = make([]domain.GetBet, 0, len(bets)) - for _, bet := range bets { - result = append(result, domain.ConvertDBBetWithOutcomes(bet)) - } - - return result, total, nil -} - -func (s *Store) GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error) { - bets, err := s.queries.GetBetByUserID(ctx, UserID) - - if err != nil { - return nil, err - } - - var result []domain.GetBet = make([]domain.GetBet, 0, len(bets)) - for _, bet := range bets { - result = append(result, domain.ConvertDBBetWithOutcomes(bet)) - } - - return result, nil -} - -func (s *Store) GetBetByFastCode(ctx context.Context, fastcode string) (domain.GetBet, error) { - bet, err := s.queries.GetBetByFastCode(ctx, fastcode) - - if err != nil { - return domain.GetBet{}, err - } - - return domain.ConvertDBBetWithOutcomes(bet), nil -} - -func (s *Store) GetBetsForCashback(ctx context.Context) ([]domain.GetBet, error) { - bets, err := s.queries.GetBetsForCashback(ctx) - var res []domain.GetBet - - if err != nil { - return nil, err - } - - for _, bet := range bets { - cashbackBet := domain.ConvertDBBetWithOutcomes(bet) - res = append(res, cashbackBet) - } - - return res, nil -} - -func (s *Store) GetBetCountByUserID(ctx context.Context, UserID int64, outcomesHash string) (int64, error) { - count, err := s.queries.GetBetCountByUserID(ctx, dbgen.GetBetCountByUserIDParams{ - UserID: UserID, - OutcomesHash: outcomesHash, - }) - - if err != nil { - return 0, err - } - - return count, nil -} - -func (s *Store) GetBetCountByOutcomesHash(ctx context.Context, outcomesHash string) (int64, error) { - count, err := s.queries.GetBetCountByOutcomesHash(ctx, outcomesHash) - if err != nil { - return 0, err - } - - return count, nil -} - -func (s *Store) GetBetOutcomeCountByOddID(ctx context.Context, oddID int64) (int64, error) { - count, err := s.queries.GetBetOutcomeCountByOddID(ctx, oddID) - if err != nil { - return 0, err - } - - return count, nil -} - -func (s *Store) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error { - err := s.queries.UpdateCashOut(ctx, dbgen.UpdateCashOutParams{ - ID: id, - CashedOut: cashedOut, - }) - if err != nil { - domain.MongoDBLogger.Error("failed to update cashout", - zap.Int64("id", id), - zap.Bool("cashed_out", cashedOut), - zap.Error(err), - ) - } - return err -} - -func (s *Store) UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error { - err := s.queries.UpdateStatus(ctx, dbgen.UpdateStatusParams{ - ID: id, - Status: int32(status), - }) - if err != nil { - domain.MongoDBLogger.Error("failed to update status", - zap.Int64("id", id), - zap.Int32("status", int32(status)), - zap.Error(err), - ) - } - return err -} - -func (s *Store) SettleWinningBet(ctx context.Context, betID int64, userID int64, amount domain.Currency, status domain.OutcomeStatus) error { - tx, err := s.conn.BeginTx(ctx, pgx.TxOptions{}) - if err != nil { - return err - } - qtx := s.queries.WithTx(tx) - - wallet, err := qtx.GetCustomerWallet(ctx, userID) - if err != nil { - tx.Rollback(ctx) - return err - } - - // 1. Update wallet - newAmount := wallet.RegularBalance + int64(amount) - if err := qtx.UpdateBalance(ctx, dbgen.UpdateBalanceParams{ - Balance: newAmount, - ID: wallet.RegularID, - }); err != nil { - tx.Rollback(ctx) - return err - } - - // 2. Update bet - if err := qtx.UpdateStatus(ctx, dbgen.UpdateStatusParams{ - Status: int32(status), - ID: betID, - }); err != nil { - tx.Rollback(ctx) - return err - } - - // 3. Commit both together - if err := tx.Commit(ctx); err != nil { - return err - } - - return nil -} - -func (s *Store) GetBetOutcomeViewByEventID(ctx context.Context, eventID int64, filter domain.BetOutcomeViewFilter) ([]domain.BetOutcomeViewRes, int64, error) { - - outcomes, err := s.queries.GetBetOutcomeViewByEventID(ctx, dbgen.GetBetOutcomeViewByEventIDParams{ - EventID: eventID, - FilterStatus: filter.OutcomeStatus.ToPG(), - CompanyID: filter.CompanyID.ToPG(), - Offset: filter.Offset.ToPG(), - Limit: filter.Limit.ToPG(), - }) - - if err != nil { - domain.MongoDBLogger.Error("failed to get bet outcomes by event ID", - zap.Int64("event_id", eventID), - zap.Error(err), - ) - return nil, 0, err - } - - total, err := s.queries.TotalBetOutcomeViewByEventID(ctx, dbgen.TotalBetOutcomeViewByEventIDParams{ - EventID: eventID, - FilterStatus: filter.OutcomeStatus.ToPG(), - CompanyID: filter.CompanyID.ToPG(), - }) - - var result []domain.BetOutcomeViewRes = make([]domain.BetOutcomeViewRes, 0, len(outcomes)) - for _, outcome := range outcomes { - result = append(result, domain.ConvertDBBetOutcomesView(outcome)) - } - return result, total, nil -} -func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) { - - outcomes, err := s.queries.GetBetOutcomeByEventID(ctx, dbgen.GetBetOutcomeByEventIDParams{ - EventID: eventID, - FilterStatus: pgtype.Int4{ - Int32: int32(domain.OUTCOME_STATUS_PENDING), - Valid: is_filtered, - }, - FilterStatus2: pgtype.Int4{ - Int32: int32(domain.OUTCOME_STATUS_ERROR), - Valid: is_filtered, - }, - }) - if err != nil { - domain.MongoDBLogger.Error("failed to get bet outcomes by event ID", - zap.Int64("event_id", eventID), - zap.Error(err), - ) - return nil, err - } - - var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes)) - for _, outcome := range outcomes { - result = append(result, domain.ConvertDBBetOutcomes(outcome)) - } - return result, nil -} - -func (s *Store) GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error) { - outcomes, err := s.queries.GetBetOutcomeByBetID(ctx, betID) - if err != nil { - domain.MongoDBLogger.Error("failed to get bet outcomes by bet ID", - zap.Int64("bet_id", betID), - zap.Error(err), - ) - return nil, err - } - - var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes)) - for _, outcome := range outcomes { - result = append(result, domain.ConvertDBBetOutcomes(outcome)) - } - return result, nil -} - -func (s *Store) UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error) { - update, err := s.queries.UpdateBetOutcomeStatus(ctx, dbgen.UpdateBetOutcomeStatusParams{ - Status: int32(status), - ID: id, - }) - if err != nil { - domain.MongoDBLogger.Error("failed to update bet outcome status", - zap.Int64("id", id), - zap.Int32("status", int32(status)), - zap.Error(err), - ) - return domain.BetOutcome{}, err - } - - res := domain.ConvertDBBetOutcomes(update) - return res, nil -} - -func (s *Store) UpdateBetOutcomeStatusByBetID(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error) { - update, err := s.queries.UpdateBetOutcomeStatusByBetID(ctx, dbgen.UpdateBetOutcomeStatusByBetIDParams{ - Status: int32(status), - BetID: id, - }) - if err != nil { - domain.MongoDBLogger.Error("failed to update bet outcome status", - zap.Int64("id", id), - zap.Int32("status", int32(status)), - zap.Error(err), - ) - return domain.BetOutcome{}, err - } - - res := domain.ConvertDBBetOutcomes(update) - return res, nil -} - -func (s *Store) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) { - outcomes, err := s.queries.UpdateBetOutcomeStatusForEvent(ctx, dbgen.UpdateBetOutcomeStatusForEventParams{ - EventID: eventID, - Status: int32(status), - }) - - if err != nil { - domain.MongoDBLogger.Error("failed to update bet outcome status for event", - zap.Int64("eventID", eventID), - zap.Int32("status", int32(status)), - zap.Error(err), - ) - return nil, err - } - - var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes)) - for _, outcome := range outcomes { - result = append(result, domain.ConvertDBBetOutcomes(outcome)) - } - return result, nil -} -func (s *Store) UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) { - outcomes, err := s.queries.UpdateBetOutcomeStatusForOddID(ctx, dbgen.UpdateBetOutcomeStatusForOddIDParams{ - OddID: oddID, - Status: int32(status), - }) - - if err != nil { - domain.MongoDBLogger.Error("failed to update bet outcome status for oddID", - zap.Int64("oddId", oddID), - zap.Int32("status", int32(status)), - zap.Error(err), - ) - return nil, err - } - - var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes)) - for _, outcome := range outcomes { - result = append(result, domain.ConvertDBBetOutcomes(outcome)) - } - return result, nil -} - -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, - }) - - if err != nil { - domain.MongoDBLogger.Error("failed to update bet outcome status for oddIDs", - zap.Int64s("oddIds", oddID), - zap.Int32("status", int32(status)), - zap.Error(err), - ) - return err - } - - return nil -} - -func (s *Store) UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error { - err := s.queries.UpdateBetWithCashback(ctx, dbgen.UpdateBetWithCashbackParams{ - ID: betID, - Processed: cashbackStatus, - }) - - if err != nil { - domain.MongoDBLogger.Error("failed to update bet outcome status for event", - zap.Int64("betID", betID), - zap.Bool("status", cashbackStatus), - zap.Error(err), - ) - return err - } - - return nil -} - -// GetBetSummary returns aggregated bet statistics -func (s *Store) GetBetSummary(ctx context.Context, filter domain.ReportFilter) ( - totalStakes domain.Currency, - totalBets int64, - activeBets int64, - totalWins int64, - totalLosses int64, - winBalance domain.Currency, - err error, -) { - query := `SELECT - COALESCE(SUM(amount), 0) as total_stakes, - COALESCE(COUNT(*), 0) as total_bets, - COALESCE(SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END), 0) as active_bets, - COALESCE(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END), 0) as total_wins, - COALESCE(SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END), 0) as total_losses, - COALESCE(SUM(CASE WHEN status = 1 THEN amount * total_odds ELSE 0 END), 0) as win_balance - FROM bets` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - // if filter.CompanyID.Valid { - // query += fmt.Sprintf(" WHERE company_id = $%d", argPos) - // args = append(args, filter.CompanyID.Value) - // argPos++ - // } - // if filter.BranchID.Valid { - // query += fmt.Sprintf(" AND %sbranch_id = $%d", func() string { - // if len(args) == 0 { - // return " WHERE " - // } - // return " AND " - // }(), argPos) - // args = append(args, filter.BranchID.Value) - // argPos++ - // } - if filter.UserID.Valid { - query += fmt.Sprintf(" %suser_id = $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %screated_at >= $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - if filter.Status.Valid { - query += fmt.Sprintf(" AND %sstatus = $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.Status.Value) - argPos++ - } - - row := s.conn.QueryRow(ctx, query, args...) - err = row.Scan(&totalStakes, &totalBets, &activeBets, &totalWins, &totalLosses, &winBalance) - if err != nil { - domain.MongoDBLogger.Error("failed to get bet summary", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return 0, 0, 0, 0, 0, 0, fmt.Errorf("failed to get bet summary: %w", err) - } - - // domain.MongoDBLogger.Info("GetBetSummary executed successfully", - // zap.String("query", query), - // zap.Any("args", args), - // zap.Float64("totalStakes", float64(totalStakes)), // convert if needed - // zap.Int64("totalBets", totalBets), - // zap.Int64("activeBets", activeBets), - // zap.Int64("totalWins", totalWins), - // zap.Int64("totalLosses", totalLosses), - // zap.Float64("winBalance", float64(winBalance)), // convert if needed - // ) - return totalStakes, totalBets, activeBets, totalWins, totalLosses, winBalance, nil -} - -// GetBetStats returns bet statistics grouped by date -func (s *Store) GetBetStats(ctx context.Context, filter domain.ReportFilter) ([]domain.BetStat, error) { - query := `SELECT - DATE(created_at) as date, - COUNT(*) as total_bets, - COALESCE(SUM(amount), 0) as total_stakes, - SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as total_wins, - COALESCE(SUM(CASE WHEN status = 1 THEN amount * total_odds ELSE 0 END), 0) as total_payouts, - AVG(total_odds) as average_odds - FROM bets` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - // if filter.CompanyID.Valid { - // query += fmt.Sprintf(" WHERE company_id = $%d", argPos) - // args = append(args, filter.CompanyID.Value) - // argPos++ - // } - // if filter.BranchID.Valid { - // query += fmt.Sprintf(" AND %sbranch_id = $%d", func() string { - // if len(args) == 0 { - // return " WHERE " - // } - // return " AND " - // }(), argPos) - // args = append(args, filter.BranchID.Value) - // argPos++ - // } - if filter.UserID.Valid { - query += fmt.Sprintf(" AND %suser_id = $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %screated_at >= $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - if filter.Status.Valid { - query += fmt.Sprintf(" AND %sstatus = $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.Status.Value) - argPos++ - } - - query += " GROUP BY DATE(created_at) ORDER BY DATE(created_at)" - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - domain.MongoDBLogger.Error("failed to query bet stats", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to query bet stats: %w", err) - } - defer rows.Close() - - var stats []domain.BetStat - for rows.Next() { - var stat domain.BetStat - if err := rows.Scan( - &stat.Date, - &stat.TotalBets, - &stat.TotalStakes, - &stat.TotalWins, - &stat.TotalPayouts, - &stat.AverageOdds, - ); err != nil { - domain.MongoDBLogger.Error("failed to scan bet stat", - zap.Error(err), - ) - return nil, fmt.Errorf("failed to scan bet stat: %w", err) - } - stats = append(stats, stat) - } - - if err = rows.Err(); err != nil { - domain.MongoDBLogger.Error("rows error after iteration", - zap.Error(err), - ) - return nil, fmt.Errorf("rows error: %w", err) - } - - domain.MongoDBLogger.Info("GetBetStats executed successfully", - zap.Int("result_count", len(stats)), - zap.String("query", query), - zap.Any("args", args), - ) - return stats, nil -} - -// GetSportPopularity returns the most popular sport by date -func (s *Store) GetSportPopularity(ctx context.Context, filter domain.ReportFilter) (map[time.Time]string, error) { - query := `WITH sport_counts AS ( - SELECT - DATE(b.created_at) as date, - bo.sport_id, - COUNT(*) as bet_count, - ROW_NUMBER() OVER (PARTITION BY DATE(b.created_at) ORDER BY COUNT(*) DESC) as rank - FROM bets b - JOIN bet_outcomes bo ON b.id = bo.bet_id - WHERE bo.sport_id IS NOT NULL` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - // if filter.CompanyID.Valid { - // query += fmt.Sprintf(" AND b.company_id = $%d", argPos) - // args = append(args, filter.CompanyID.Value) - // argPos++ - // } - // if filter.BranchID.Valid { - // query += fmt.Sprintf(" AND b.branch_id = $%d", argPos) - // args = append(args, filter.BranchID.Value) - // argPos++ - // } - if filter.UserID.Valid { - query += fmt.Sprintf(" AND b.user_id = $%d", argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND b.created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND b.created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - if filter.Status.Valid { - query += fmt.Sprintf(" AND b.status = $%d", argPos) - args = append(args, filter.Status.Value) - argPos++ - } - - query += ` GROUP BY DATE(b.created_at), bo.sport_id - ) - SELECT date, sport_id FROM sport_counts WHERE rank = 1` - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - domain.MongoDBLogger.Error("failed to query sport popularity", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to query sport popularity: %w", err) - } - defer rows.Close() - - popularity := make(map[time.Time]string) - for rows.Next() { - var date time.Time - var sportID string - if err := rows.Scan(&date, &sportID); err != nil { - domain.MongoDBLogger.Error("failed to scan sport popularity", - zap.Error(err), - ) - return nil, fmt.Errorf("failed to scan sport popularity: %w", err) - } - popularity[date] = sportID - } - - if err = rows.Err(); err != nil { - domain.MongoDBLogger.Error("rows error after iteration", - zap.Error(err), - ) - return nil, fmt.Errorf("rows error: %w", err) - } - - domain.MongoDBLogger.Info("GetSportPopularity executed successfully", - zap.Int("result_count", len(popularity)), - zap.String("query", query), - zap.Any("args", args), - ) - return popularity, nil -} - -// GetMarketPopularity returns the most popular market by date -func (s *Store) GetMarketPopularity(ctx context.Context, filter domain.ReportFilter) (map[time.Time]string, error) { - query := `WITH market_counts AS ( - SELECT - DATE(b.created_at) as date, - bo.market_name, - COUNT(*) as bet_count, - ROW_NUMBER() OVER (PARTITION BY DATE(b.created_at) ORDER BY COUNT(*) DESC) as rank - FROM bets b - JOIN bet_outcomes bo ON b.id = bo.bet_id - WHERE bo.market_name IS NOT NULL` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.UserID.Valid { - query += fmt.Sprintf(" AND b.user_id = $%d", argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND b.created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND b.created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - if filter.Status.Valid { - query += fmt.Sprintf(" AND b.status = $%d", argPos) - args = append(args, filter.Status.Value) - argPos++ - } - - query += ` GROUP BY DATE(b.created_at), bo.market_name - ) - SELECT date, market_name FROM market_counts WHERE rank = 1` - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - domain.MongoDBLogger.Error("failed to query market popularity", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to query market popularity: %w", err) - } - defer rows.Close() - - popularity := make(map[time.Time]string) - for rows.Next() { - var date time.Time - var marketName string - if err := rows.Scan(&date, &marketName); err != nil { - domain.MongoDBLogger.Error("failed to scan market popularity", - zap.Error(err), - ) - return nil, fmt.Errorf("failed to scan market popularity: %w", err) - } - popularity[date] = marketName - } - - if err = rows.Err(); err != nil { - domain.MongoDBLogger.Error("rows error after iteration", - zap.Error(err), - ) - return nil, fmt.Errorf("rows error: %w", err) - } - - domain.MongoDBLogger.Info("GetMarketPopularity executed successfully", - zap.Int("result_count", len(popularity)), - zap.String("query", query), - zap.Any("args", args), - ) - return popularity, nil -} - -// GetExtremeValues returns the highest stake and payout by date -func (s *Store) GetExtremeValues(ctx context.Context, filter domain.ReportFilter) (map[time.Time]domain.ExtremeValues, error) { - query := `SELECT - DATE(created_at) as date, - MAX(amount) as highest_stake, - MAX(CASE WHEN status = 1 THEN amount * total_odds ELSE 0 END) as highest_payout - FROM bets` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - // if filter.CompanyID.Valid { - // query += fmt.Sprintf(" WHERE company_id = $%d", argPos) - // args = append(args, filter.CompanyID.Value) - // argPos++ - // } - // if filter.BranchID.Valid { - // query += fmt.Sprintf(" AND %sbranch_id = $%d", func() string { - // if len(args) == 0 { - // return " WHERE " - // } - // return " AND " - // }(), argPos) - // args = append(args, filter.BranchID.Value) - // argPos++ - // } - if filter.UserID.Valid { - query += fmt.Sprintf(" AND %suser_id = $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %screated_at >= $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - if filter.Status.Valid { - query += fmt.Sprintf(" AND %sstatus = $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.Status.Value) - argPos++ - } - - query += " GROUP BY DATE(created_at)" - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - domain.MongoDBLogger.Error("failed to query extreme values", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to query extreme values: %w", err) - } - defer rows.Close() - - extremes := make(map[time.Time]domain.ExtremeValues) - for rows.Next() { - var date time.Time - var extreme domain.ExtremeValues - if err := rows.Scan(&date, &extreme.HighestStake, &extreme.HighestPayout); err != nil { - domain.MongoDBLogger.Error("failed to scan extreme values", - zap.Error(err), - ) - return nil, fmt.Errorf("failed to scan extreme values: %w", err) - } - extremes[date] = extreme - } - - if err = rows.Err(); err != nil { - domain.MongoDBLogger.Error("rows error after iteration", - zap.Error(err), - ) - return nil, fmt.Errorf("rows error: %w", err) - } - - domain.MongoDBLogger.Info("GetExtremeValues executed successfully", - zap.Int("result_count", len(extremes)), - zap.String("query", query), - zap.Any("args", args), - ) - return extremes, nil -} - -// GetCustomerBetActivity returns bet activity by customer -func (s *Store) GetCustomerBetActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.CustomerBetActivity, error) { - query := `SELECT - user_id as customer_id, - COUNT(*) as total_bets, - COALESCE(SUM(amount), 0) as total_stakes, - SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as total_wins, - COALESCE(SUM(CASE WHEN status = 1 THEN amount * total_odds ELSE 0 END), 0) as total_payouts, - MIN(created_at) as first_bet_date, - MAX(created_at) as last_bet_date, - AVG(total_odds) as average_odds - FROM bets - WHERE user_id IS NOT NULL` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - // if filter.CompanyID.Valid { - // query += fmt.Sprintf(" AND company_id = $%d", argPos) - // args = append(args, filter.CompanyID.Value) - // argPos++ - // } - // if filter.BranchID.Valid { - // query += fmt.Sprintf(" AND branch_id = $%d", argPos) - // args = append(args, filter.BranchID.Value) - // argPos++ - // } - if filter.UserID.Valid { - query += fmt.Sprintf(" AND user_id = $%d", argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - if filter.Status.Valid { - query += fmt.Sprintf(" AND status = $%d", argPos) - args = append(args, filter.Status.Value) - argPos++ - } - - query += " GROUP BY user_id" - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - domain.MongoDBLogger.Error("failed to query customer bet activity", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to query customer bet activity: %w", err) - } - defer rows.Close() - - var activities []domain.CustomerBetActivity - for rows.Next() { - var activity domain.CustomerBetActivity - if err := rows.Scan( - &activity.CustomerID, - &activity.TotalBets, - &activity.TotalStakes, - &activity.TotalWins, - &activity.TotalPayouts, - &activity.FirstBetDate, - &activity.LastBetDate, - &activity.AverageOdds, - ); err != nil { - domain.MongoDBLogger.Error("failed to scan customer bet activity", - zap.Error(err), - ) - return nil, fmt.Errorf("failed to scan customer bet activity: %w", err) - } - activities = append(activities, activity) - } - - if err = rows.Err(); err != nil { - domain.MongoDBLogger.Error("rows error after iteration", - zap.Error(err), - ) - return nil, fmt.Errorf("rows error: %w", err) - } - - domain.MongoDBLogger.Info("GetCustomerBetActivity executed successfully", - zap.Int("result_count", len(activities)), - zap.String("query", query), - zap.Any("args", args), - ) - return activities, nil -} - -// GetBranchBetActivity returns bet activity by branch -func (s *Store) GetBranchBetActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.BranchBetActivity, error) { - query := `SELECT - branch_id, - COUNT(*) as total_bets, - COALESCE(SUM(amount), 0) as total_stakes, - SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as total_wins, - COALESCE(SUM(CASE WHEN status = 1 THEN amount * total_odds ELSE 0 END), 0) as total_payouts - FROM bets - WHERE branch_id IS NOT NULL` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - // if filter.CompanyID.Valid { - // query += fmt.Sprintf(" AND company_id = $%d", argPos) - // args = append(args, filter.CompanyID.Value) - // argPos++ - // } - // if filter.BranchID.Valid { - // query += fmt.Sprintf(" AND branch_id = $%d", argPos) - // args = append(args, filter.BranchID.Value) - // argPos++ - // } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - if filter.Status.Valid { - query += fmt.Sprintf(" AND status = $%d", argPos) - args = append(args, filter.Status.Value) - argPos++ - } - - query += " GROUP BY branch_id" - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - domain.MongoDBLogger.Error("failed to query branch bet activity", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to query branch bet activity: %w", err) - } - defer rows.Close() - - var activities []domain.BranchBetActivity - for rows.Next() { - var activity domain.BranchBetActivity - if err := rows.Scan( - &activity.BranchID, - &activity.TotalBets, - &activity.TotalStakes, - &activity.TotalWins, - &activity.TotalPayouts, - ); err != nil { - domain.MongoDBLogger.Error("failed to scan branch bet activity", zap.Error(err)) - return nil, fmt.Errorf("failed to scan branch bet activity: %w", err) - } - activities = append(activities, activity) - } - - if err = rows.Err(); err != nil { - domain.MongoDBLogger.Error("rows error after iteration", zap.Error(err)) - return nil, fmt.Errorf("rows error: %w", err) - } - - domain.MongoDBLogger.Info("GetBranchBetActivity executed successfully", - zap.Int("result_count", len(activities)), - zap.String("query", query), - zap.Any("args", args), - ) - return activities, nil -} - -// GetSportBetActivity returns bet activity by sport -func (s *Store) GetSportBetActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.SportBetActivity, error) { - query := `SELECT - bo.sport_id, - COUNT(*) as total_bets, - COALESCE(SUM(b.amount), 0) as total_stakes, - SUM(CASE WHEN b.status = 1 THEN 1 ELSE 0 END) as total_wins, - COALESCE(SUM(CASE WHEN b.status = 1 THEN b.amount * b.total_odds ELSE 0 END), 0) as total_payouts, - AVG(b.total_odds) as average_odds - FROM bets b - JOIN bet_outcomes bo ON b.id = bo.bet_id - WHERE bo.sport_id IS NOT NULL` - - args := []interface{}{} - argPos := 1 - - // if filter.CompanyID.Valid { - // query += fmt.Sprintf(" AND b.company_id = $%d", argPos) - // args = append(args, filter.CompanyID.Value) - // argPos++ - // } - // if filter.BranchID.Valid { - // query += fmt.Sprintf(" AND b.branch_id = $%d", argPos) - // args = append(args, filter.BranchID.Value) - // argPos++ - // } - if filter.UserID.Valid { - query += fmt.Sprintf(" AND b.user_id = $%d", argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND b.created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND b.created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - if filter.Status.Valid { - query += fmt.Sprintf(" AND b.status = $%d", argPos) - args = append(args, filter.Status.Value) - argPos++ - } - - query += " GROUP BY bo.sport_id" - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - domain.MongoDBLogger.Error("failed to query sport bet activity", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to query sport bet activity: %w", err) - } - defer rows.Close() - - var activities []domain.SportBetActivity - for rows.Next() { - var activity domain.SportBetActivity - if err := rows.Scan( - &activity.SportID, - &activity.TotalBets, - &activity.TotalStakes, - &activity.TotalWins, - &activity.TotalPayouts, - &activity.AverageOdds, - ); err != nil { - domain.MongoDBLogger.Error("failed to scan sport bet activity", zap.Error(err)) - return nil, fmt.Errorf("failed to scan sport bet activity: %w", err) - } - activities = append(activities, activity) - } - - if err = rows.Err(); err != nil { - domain.MongoDBLogger.Error("rows error after iteration", zap.Error(err)) - return nil, fmt.Errorf("rows error: %w", err) - } - - domain.MongoDBLogger.Info("GetSportBetActivity executed successfully", - zap.Int("result_count", len(activities)), - zap.String("query", query), - zap.Any("args", args), - ) - return activities, nil -} - -// GetSportDetails returns sport names by ID -func (s *Store) GetSportDetails(ctx context.Context, filter domain.ReportFilter) (map[string]string, error) { - query := `SELECT DISTINCT bo.sport_id, e.match_name - FROM bet_outcomes bo - JOIN events e ON bo.event_id = e.id::bigint - JOIN bets b ON b.id = bo.bet_id - WHERE bo.sport_id IS NOT NULL` - - args := []interface{}{} - argPos := 1 - - // if filter.CompanyID.Valid { - // query += fmt.Sprintf(" AND b.company_id = $%d", argPos) - // args = append(args, filter.CompanyID.Value) - // argPos++ - // } - // if filter.BranchID.Valid { - // query += fmt.Sprintf(" AND b.branch_id = $%d", argPos) - // args = append(args, filter.BranchID.Value) - // argPos++ - // } - if filter.UserID.Valid { - query += fmt.Sprintf(" AND b.user_id = $%d", argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND bo.created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND bo.created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - domain.MongoDBLogger.Error("failed to query sport details", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to query sport details: %w", err) - } - defer rows.Close() - - details := make(map[string]string) - for rows.Next() { - var sportID, matchName string - if err := rows.Scan(&sportID, &matchName); err != nil { - domain.MongoDBLogger.Error("failed to scan sport detail", zap.Error(err)) - return nil, fmt.Errorf("failed to scan sport detail: %w", err) - } - details[sportID] = matchName - } - - if err = rows.Err(); err != nil { - domain.MongoDBLogger.Error("rows error after iteration", zap.Error(err)) - return nil, fmt.Errorf("rows error: %w", err) - } - - domain.MongoDBLogger.Info("GetSportDetails executed successfully", - zap.Int("result_count", len(details)), - zap.String("query", query), - zap.Any("args", args), - ) - - return details, nil -} - -// GetSportMarketPopularity returns most popular market by sport -func (s *Store) GetSportMarketPopularity(ctx context.Context, filter domain.ReportFilter) (map[string]string, error) { - query := `WITH market_counts AS ( - SELECT - bo.sport_id, - bo.market_name, - COUNT(*) AS bet_count, - ROW_NUMBER() OVER (PARTITION BY bo.sport_id ORDER BY COUNT(*) DESC) as rank - FROM bets b - JOIN bet_outcomes bo ON b.id = bo.bet_id - WHERE bo.sport_id IS NOT NULL AND bo.market_name IS NOT NULL` - - args := []interface{}{} - argPos := 1 - - // if filter.CompanyID.Valid { - // query += fmt.Sprintf(" AND b.company_id = $%d", argPos) - // args = append(args, filter.CompanyID.Value) - // argPos++ - // } - // if filter.BranchID.Valid { - // query += fmt.Sprintf(" AND b.branch_id = $%d", argPos) - // args = append(args, filter.BranchID.Value) - // argPos++ - // } - if filter.UserID.Valid { - query += fmt.Sprintf(" AND b.user_id = $%d", argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND b.created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND b.created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - if filter.Status.Valid { - query += fmt.Sprintf(" AND b.status = $%d", argPos) - args = append(args, filter.Status.Value) - argPos++ - } - - query += ` GROUP BY bo.sport_id, bo.market_name - ) - SELECT sport_id, market_name FROM market_counts WHERE rank = 1` - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - domain.MongoDBLogger.Error("failed to query sport market popularity", - zap.String("query", query), - zap.Any("args", args), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to query sport market popularity: %w", err) - } - defer rows.Close() - - popularity := make(map[string]string) - for rows.Next() { - var sportID, marketName string - if err := rows.Scan(&sportID, &marketName); err != nil { - domain.MongoDBLogger.Error("failed to scan sport market popularity", zap.Error(err)) - return nil, fmt.Errorf("failed to scan sport market popularity: %w", err) - } - popularity[sportID] = marketName - } - - if err = rows.Err(); err != nil { - domain.MongoDBLogger.Error("rows error after iteration", zap.Error(err)) - return nil, fmt.Errorf("rows error: %w", err) - } - - domain.MongoDBLogger.Info("GetSportMarketPopularity executed successfully", - zap.Int("result_count", len(popularity)), - zap.String("query", query), - zap.Any("args", args), - ) - - return popularity, nil -} diff --git a/internal/repository/bet_stats.go b/internal/repository/bet_stats.go deleted file mode 100644 index bb8daf5..0000000 --- a/internal/repository/bet_stats.go +++ /dev/null @@ -1,23 +0,0 @@ -package repository - -import ( - "context" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" -) - -func NewBetStatStore(s *Store) ports.BetStatStore { return s } - -func (s *Store) GetBetStatsByInterval(ctx context.Context, filter domain.BetStatsByIntervalFilter) ([]domain.BetStatsByInterval, error) { - stats, err := s.queries.GetBetStatsByInterval(ctx, dbgen.GetBetStatsByIntervalParams{ - Interval: filter.Interval.ToPG(), - CompanyID: filter.CompanyID.ToPG(), - }) - if err != nil { - return nil, err - } - - return domain.ConvertDBBetStatsByIntervalList(stats), nil -} diff --git a/internal/repository/bonus.go b/internal/repository/bonus.go deleted file mode 100644 index a9b717c..0000000 --- a/internal/repository/bonus.go +++ /dev/null @@ -1,87 +0,0 @@ -package repository - -import ( - "context" - - 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)) - - if err != nil { - return domain.UserBonus{}, err - } - - return domain.ConvertDBBonus(newBonus), nil -} - -func (s *Store) GetAllUserBonuses(ctx context.Context, filter domain.BonusFilter) ([]domain.UserBonus, error) { - bonuses, err := s.queries.GetAllUserBonuses(ctx, dbgen.GetAllUserBonusesParams{ - UserID: filter.UserID.ToPG(), - Offset: filter.Offset.ToPG(), - Limit: filter.Limit.ToPG(), - }) - - if err != nil { - return nil, err - } - - return domain.ConvertDBBonuses(bonuses), nil -} - -func (s *Store) GetBonusCount(ctx context.Context, filter domain.BonusFilter) (int64, error) { - count, err := s.queries.GetBonusCount(ctx, filter.UserID.ToPG()) - if err != nil { - return 0, err - } - return count, nil -} - -func (s *Store) GetBonusByID(ctx context.Context, bonusID int64) (domain.UserBonus, error) { - bonus, err := s.queries.GetUserBonusByID(ctx, bonusID) - if err != nil { - return domain.UserBonus{}, err - } - return domain.ConvertDBBonus(bonus), nil -} - - - - -func (s *Store) GetBonusStats(ctx context.Context, filter domain.BonusFilter) (domain.BonusStats, error) { - bonus, err := s.queries.GetBonusStats(ctx, dbgen.GetBonusStatsParams{ - CompanyID: filter.CompanyID.ToPG(), - UserID: filter.UserID.ToPG(), - }) - if err != nil { - return domain.BonusStats{}, err - } - return domain.ConvertDBBonusStats(bonus), nil -} - -func (s *Store) UpdateUserBonus(ctx context.Context, bonusID int64, IsClaimed bool) (error) { - err := s.queries.UpdateUserBonus(ctx, dbgen.UpdateUserBonusParams{ - ID: bonusID, - IsClaimed: IsClaimed, - }) - - if err != nil { - return err - } - return nil -} - - -func (s *Store) DeleteUserBonus(ctx context.Context, bonusID int64) (error) { - err := s.queries.DeleteUserBonus(ctx, bonusID) - if err != nil { - return err - } - return nil -} diff --git a/internal/repository/branch.go b/internal/repository/branch.go deleted file mode 100644 index b347d9a..0000000 --- a/internal/repository/branch.go +++ /dev/null @@ -1,351 +0,0 @@ -package repository - -import ( - "context" - "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/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)) - - if err != nil { - return domain.Branch{}, err - } - return domain.ConvertDBBranch(dbBranch), nil -} - -func (s *Store) GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error) { - dbBranch, err := s.queries.GetBranchByID(ctx, id) - if err != nil { - return domain.BranchDetail{}, err - } - return domain.ConvertDBBranchDetail(dbBranch), nil -} - -func (s *Store) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error) { - dbBranches, err := s.queries.GetBranchByManagerID(ctx, branchManagerID) - if err != nil { - return nil, err - } - var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches)) - for _, dbBranch := range dbBranches { - branches = append(branches, domain.ConvertDBBranchDetail(dbBranch)) - } - return branches, nil -} -func (s *Store) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error) { - dbBranches, err := s.queries.GetBranchByCompanyID(ctx, companyID) - if err != nil { - return nil, err - } - var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches)) - for _, dbBranch := range dbBranches { - branches = append(branches, domain.ConvertDBBranchDetail(dbBranch)) - } - return branches, nil -} - -func (s *Store) GetAllBranches(ctx context.Context, filter domain.BranchFilter) ([]domain.BranchDetail, error) { - dbBranches, err := s.queries.GetAllBranches(ctx, dbgen.GetAllBranchesParams{ - CompanyID: filter.CompanyID.ToPG(), - BranchManagerID: filter.BranchManagerID.ToPG(), - Query: filter.Query.ToPG(), - CreatedBefore: filter.CreatedBefore.ToPG(), - CreatedAfter: filter.CreatedAfter.ToPG(), - }) - if err != nil { - return nil, err - } - var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches)) - for _, dbBranch := range dbBranches { - branches = append(branches, domain.ConvertDBBranchDetail(dbBranch)) - } - return branches, nil -} - -func (s *Store) SearchBranchByName(ctx context.Context, name string, companyID domain.ValidInt64) ([]domain.BranchDetail, error) { - dbBranches, err := s.queries.SearchBranchByName(ctx, dbgen.SearchBranchByNameParams{ - Column1: pgtype.Text{String: name, Valid: true}, - CompanyID: companyID.ToPG(), - }) - if err != nil { - return nil, err - } - - var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches)) - for _, dbBranch := range dbBranches { - branches = append(branches, domain.ConvertDBBranchDetail(dbBranch)) - } - return branches, nil -} - -func (s *Store) UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error) { - - dbBranch, err := s.queries.UpdateBranch(ctx, domain.ConvertUpdateBranch(branch)) - if err != nil { - return domain.Branch{}, err - } - return domain.ConvertDBBranch(dbBranch), nil -} - -func (s *Store) DeleteBranch(ctx context.Context, id int64) error { - return s.queries.DeleteBranch(ctx, id) -} - -// Branch Operations - -func (s *Store) CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error { - _, err := s.queries.CreateBranchOperation(ctx, dbgen.CreateBranchOperationParams{ - BranchID: branchOperation.BranchID, - OperationID: branchOperation.OperationID, - }) - return err -} - -func (s *Store) CreateSupportedOperation(ctx context.Context, supportedOperation domain.CreateSupportedOperation) (domain.SupportedOperation, error) { - dbSupportedOperation, err := s.queries.CreateSupportedOperation(ctx, dbgen.CreateSupportedOperationParams{ - Name: supportedOperation.Name, - Description: supportedOperation.Description, - }) - if err != nil { - return domain.SupportedOperation{}, err - } - return domain.SupportedOperation{ - ID: dbSupportedOperation.ID, - Name: dbSupportedOperation.Name, - Description: dbSupportedOperation.Description, - }, nil -} - -func (s *Store) CreateBranchCashier(ctx context.Context, branchID int64, userID int64) error { - _, err := s.queries.CreateBranchCashier(ctx, dbgen.CreateBranchCashierParams{ - UserID: userID, - BranchID: branchID, - }) - - if err != nil { - return err - } - return nil -} - -func (s *Store) GetAllSupportedOperations(ctx context.Context) ([]domain.SupportedOperation, error) { - dbOperations, err := s.queries.GetAllSupportedOperations(ctx) - if err != nil { - return nil, err - } - - var operations []domain.SupportedOperation = make([]domain.SupportedOperation, 0, len(dbOperations)) - for _, dbOperation := range dbOperations { - operations = append(operations, domain.SupportedOperation{ - ID: dbOperation.ID, - Name: dbOperation.Name, - Description: dbOperation.Description, - }) - } - return operations, nil - -} - -func (s *Store) GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error) { - dbBranchOperations, err := s.queries.GetBranchOperations(ctx, branchID) - if err != nil { - return nil, err - } - var branchOperations []domain.BranchOperation = make([]domain.BranchOperation, 0, len(dbBranchOperations)) - for _, dbBranchOperation := range dbBranchOperations { - branchOperations = append(branchOperations, domain.BranchOperation{ - ID: dbBranchOperation.ID, - OperationName: dbBranchOperation.Name, - OperationDescription: dbBranchOperation.Description, - }) - } - return branchOperations, nil -} - -func (s *Store) GetBranchByCashier(ctx context.Context, userID int64) (domain.Branch, error) { - branch, err := s.queries.GetBranchByCashier(ctx, userID) - if err != nil { - return domain.Branch{}, err - } - - return domain.ConvertDBBranch(branch), err -} - -func (s *Store) DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error { - err := s.queries.DeleteBranchOperation(ctx, dbgen.DeleteBranchOperationParams{ - BranchID: branchID, - OperationID: operationID, - }) - return err -} - -func (s *Store) DeleteBranchCashier(ctx context.Context, userID int64) error { - return s.queries.DeleteBranchCashier(ctx, userID) - -} - -// GetBranchCounts returns total and active branch counts -func (s *Store) GetBranchCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) { - query := `SELECT - COUNT(*) as total, - COUNT(CASE WHEN is_active = true THEN 1 END) as active, - COUNT(CASE WHEN is_active = false THEN 1 END) as inactive - FROM branches` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.CompanyID.Valid { - query += fmt.Sprintf(" WHERE company_id = $%d", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %screated_at >= $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - row := s.conn.QueryRow(ctx, query, args...) - err = row.Scan(&total, &active, &inactive) - if err != nil { - return 0, 0, 0, fmt.Errorf("failed to get branch counts: %w", err) - } - - return total, active, inactive, nil -} - -// GetBranchDetails returns branch details map -func (s *Store) GetBranchDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.BranchDetail, error) { - query := `SELECT - b.id, - b.name, - b.location, - CONCAT(u.first_name, ' ', u.last_name) as manager_name - FROM branches b - LEFT JOIN users u ON b.branch_manager_id = u.id` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.CompanyID.Valid { - query += fmt.Sprintf(" WHERE b.company_id = $%d", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ - } - if filter.BranchID.Valid { - query += fmt.Sprintf(" AND %sb.id = $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.BranchID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %sb.created_at >= $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND b.created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - return nil, fmt.Errorf("failed to query branch details: %w", err) - } - defer rows.Close() - - details := make(map[int64]domain.BranchDetail) - for rows.Next() { - var id int64 - var detail domain.BranchDetail - if err := rows.Scan(&id, &detail.Name, &detail.Location, &detail.ManagerName); err != nil { - return nil, fmt.Errorf("failed to scan branch detail: %w", err) - } - details[id] = detail - } - - if err = rows.Err(); err != nil { - return nil, fmt.Errorf("rows error: %w", err) - } - - return details, nil -} - -// In internal/repository/branch.go -func (s *Store) GetAllCompaniesBranch(ctx context.Context) ([]domain.Company, error) { - dbCompanies, err := s.queries.GetAllCompanies(ctx, dbgen.GetAllCompaniesParams{}) - if err != nil { - return nil, fmt.Errorf("failed to get all companies: %w", err) - } - - companies := make([]domain.Company, 0, len(dbCompanies)) - for _, dbCompany := range dbCompanies { - companies = append(companies, domain.Company{ - ID: dbCompany.ID, - Name: dbCompany.Name, - WalletID: dbCompany.WalletID, - AdminID: dbCompany.AdminID, - }) - } - - return companies, nil -} - -// In internal/repository/branch.go -func (s *Store) GetBranchesByCompany(ctx context.Context, companyID int64) ([]domain.Branch, error) { - dbBranches, err := s.queries.GetBranchByCompanyID(ctx, companyID) - if err != nil { - return nil, fmt.Errorf("failed to get branches for company %d: %w", companyID, err) - } - - branches := make([]domain.Branch, 0, len(dbBranches)) - for _, dbBranch := range dbBranches { - branch := domain.Branch{ - ID: dbBranch.ID, - Name: dbBranch.Name, - Location: dbBranch.Location, - WalletID: dbBranch.WalletID, - CompanyID: dbBranch.CompanyID, - IsSelfOwned: dbBranch.IsSelfOwned, - } - - branch.BranchManagerID = dbBranch.BranchManagerID - - branches = append(branches, branch) - } - - return branches, nil -} diff --git a/internal/repository/branch_stats.go b/internal/repository/branch_stats.go deleted file mode 100644 index d10883e..0000000 --- a/internal/repository/branch_stats.go +++ /dev/null @@ -1,38 +0,0 @@ -package repository - -import ( - "context" - - 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 branch stats store -func NewBranchStatStore(s *Store) ports.BranchStatStore { return s } - -func (s *Store) UpdateBranchStats(ctx context.Context) error { - return s.queries.UpdateBranchStats(ctx) -} - -func (s *Store) GetBranchStatByID(ctx context.Context, branchID int64) ([]domain.BranchStat, error) { - stats, err := s.queries.GetBranchStatsByID(ctx, branchID) - - if err != nil { - return nil, err - } - return domain.ConvertDBBranchStatsList(stats), nil -} - -func (s *Store) GetBranchStatsByInterval(ctx context.Context, filter domain.BranchStatFilter) ([]domain.BranchStat, error) { - stats, err := s.queries.GetBranchStats(ctx, dbgen.GetBranchStatsParams{ - Interval: filter.Interval.ToPG(), - BranchID: filter.BranchID.ToPG(), - }) - - if err != nil { - return nil, err - } - - return domain.ConvertDBBranchStatsByIntervalList(stats), nil -} diff --git a/internal/repository/company.go b/internal/repository/company.go deleted file mode 100644 index a9625d8..0000000 --- a/internal/repository/company.go +++ /dev/null @@ -1,123 +0,0 @@ -package repository - -import ( - "context" - "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)) - - if err != nil { - return domain.Company{}, err - } - return domain.ConvertDBCompany(dbCompany), nil -} - -func (s *Store) GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error) { - dbCompanies, err := s.queries.GetAllCompanies(ctx, domain.ConvertGetAllCompaniesParams(filter)) - if err != nil { - return nil, err - } - - var companies []domain.GetCompany = make([]domain.GetCompany, 0, len(dbCompanies)) - for _, dbCompany := range dbCompanies { - companies = append(companies, domain.ConvertDBCompanyDetails(dbCompany)) - } - - return companies, nil -} - -func (s *Store) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) { - dbCompanies, err := s.queries.SearchCompanyByName(ctx, pgtype.Text{ - String: name, - Valid: true, - }) - - if err != nil { - return nil, err - } - var companies []domain.GetCompany = make([]domain.GetCompany, 0, len(dbCompanies)) - - for _, dbCompany := range dbCompanies { - companies = append(companies, domain.ConvertDBCompanyDetails(dbCompany)) - } - return companies, nil -} - -func (s *Store) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) { - dbCompany, err := s.queries.GetCompanyByID(ctx, id) - - if err != nil { - return domain.GetCompany{}, err - } - return domain.ConvertDBCompanyDetails(dbCompany), nil -} - -func (s *Store) GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) { - dbCompany, err := s.queries.GetCompanyUsingSlug(ctx, slug) - - if err != nil { - return domain.Company{}, err - } - return domain.ConvertDBCompany(dbCompany), nil -} - -func (s *Store) UpdateCompany(ctx context.Context, company domain.UpdateCompany) error { - fmt.Printf("company %v\n", company) - err := s.queries.UpdateCompany(ctx, domain.ConvertUpdateCompany(company)) - - if err != nil { - return err - } - - return nil -} - -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, - COUNT(CASE WHEN w.is_active = true THEN 1 END) as active, - COUNT(CASE WHEN w.is_active = false THEN 1 END) as inactive - FROM companies c - JOIN wallets w ON c.wallet_id = w.id` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.StartTime.Valid { - query += fmt.Sprintf(" WHERE %screated_at >= $%d", func() string { - if len(args) == 0 { - return "" - } - return " AND " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - row := s.conn.QueryRow(ctx, query, args...) - err = row.Scan(&total, &active, &inactive) - if err != nil { - return 0, 0, 0, fmt.Errorf("failed to get company counts: %w", err) - } - - return total, active, inactive, nil -} diff --git a/internal/repository/company_stats.go b/internal/repository/company_stats.go deleted file mode 100644 index 03cbdd6..0000000 --- a/internal/repository/company_stats.go +++ /dev/null @@ -1,38 +0,0 @@ -package repository - -import ( - "context" - - 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) -} - -func (s *Store) GetCompanyStatByID(ctx context.Context, companyID int64) ([]domain.CompanyStat, error) { - stats, err := s.queries.GetCompanyStatsByID(ctx, companyID) - - if err != nil { - return nil, err - } - return domain.ConvertDBCompanyStatsList(stats), nil -} - -func (s *Store) GetCompanyStatsByInterval(ctx context.Context, filter domain.CompanyStatFilter) ([]domain.CompanyStat, error) { - stats, err := s.queries.GetCompanyStats(ctx, dbgen.GetCompanyStatsParams{ - Interval: filter.Interval.ToPG(), - CompanyID: filter.CompanyID.ToPG(), - }) - - if err != nil { - return nil, err - } - - return domain.ConvertDBCompanyStatsByIntervalList(stats), nil -} diff --git a/internal/repository/currency.go b/internal/repository/currency.go index 4aea3eb..391ba45 100644 --- a/internal/repository/currency.go +++ b/internal/repository/currency.go @@ -5,7 +5,7 @@ import ( "database/sql" "fmt" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "Yimaru-Backend/internal/domain" ) type CurrencyRepository interface { diff --git a/internal/repository/custom_odds.go b/internal/repository/custom_odds.go deleted file mode 100644 index 3400878..0000000 --- a/internal/repository/custom_odds.go +++ /dev/null @@ -1,105 +0,0 @@ -package repository - -// import ( -// "context" - -// dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -// ) - -// func (s *Store) InsertCustomOdds(ctx context.Context, odd domain.CreateCustomOdd) (domain.CustomOdd, error) { -// convertedCustomOdd, err := domain.ConvertCreateCustomOdd(odd) -// if err != nil { -// return domain.CustomOdd{}, err -// } -// dbCustomOdd, err := s.queries.InsertCustomOdd(ctx, convertedCustomOdd) - -// if err != nil { -// return domain.CustomOdd{}, err -// } - -// convertDbCustomOdd, err := domain.ConvertDBCustomOdd(dbCustomOdd) - -// if err != nil { -// return domain.CustomOdd{}, err -// } - -// return convertDbCustomOdd, nil -// } - -// func (s *Store) GetAllCustomOdds(ctx context.Context, filter domain.CustomOddFilter) ([]domain.CustomOdd, error) { -// dbCustomOdds, err := s.queries.GetAllCustomOdds(ctx, filter.CompanyID.ToPG()) -// if err != nil { -// return nil, err -// } - -// convertDbCustomOdds, err := domain.ConvertDbCustomOdds(dbCustomOdds) - -// if err != nil { -// return nil, err -// } - -// return convertDbCustomOdds, nil -// } - -// func (s *Store) GetCustomOddByID(ctx context.Context, id int64) (domain.CustomOdd, error) { -// dbCustomOdd, err := s.queries.GetCustomOddByID(ctx, id) -// if err != nil { -// return domain.CustomOdd{}, nil -// } - -// convertedDBCustomOdd, err := domain.ConvertDBCustomOdd(dbCustomOdd) -// if err != nil { -// return domain.CustomOdd{}, nil -// } - -// return convertedDBCustomOdd, nil -// } - -// func (s *Store) GetCustomOddByOddID(ctx context.Context, oddId int64, companyID int64) (domain.CustomOdd, error) { -// dbCustomOdd, err := s.queries.GetCustomOddByOddID(ctx, dbgen.GetCustomOddByOddIDParams{ -// OddID: oddId, -// CompanyID: companyID, -// }) - -// if err != nil { -// return domain.CustomOdd{}, nil -// } - -// convertedDBCustomOdd, err := domain.ConvertDBCustomOdd(dbCustomOdd) -// if err != nil { -// return domain.CustomOdd{}, nil -// } - -// return convertedDBCustomOdd, nil -// } - -// func (s *Store) DeleteCustomOddByID(ctx context.Context, id int64) error { -// err := s.queries.DeleteCustomOddsByID(ctx, id) -// if err != nil { -// return err -// } -// return nil -// } - -// func (s *Store) DeleteCustomOddsByOddID(ctx context.Context, oddId int64, companyID int64) error { -// err := s.queries.DeleteCustomOddsByOddID(ctx, dbgen.DeleteCustomOddsByOddIDParams{ -// OddID: oddId, -// CompanyID: companyID, -// }) -// if err != nil { -// return err -// } - -// return nil -// } - -// func (s *Store) DeleteCustomOddByEventID(ctx context.Context, eventID string) error { -// err := s.queries.DeleteCustomOddByEventID(ctx, eventID) - -// if err != nil { -// return err -// } - -// return nil -// } diff --git a/internal/repository/direct_deposit.go b/internal/repository/direct_deposit.go deleted file mode 100644 index f9f160c..0000000 --- a/internal/repository/direct_deposit.go +++ /dev/null @@ -1,196 +0,0 @@ -package repository - -import ( - "context" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" -) - -type DirectDepositRepository interface { - CreateDirectDeposit(ctx context.Context, deposit *domain.DirectDeposit) error - GetDirectDepositsByStatus(ctx context.Context, status string, page int, pageSize int) ([]domain.DirectDeposit, int64, error) - ApproveDirectDeposit(ctx context.Context, depositID int, adminID int) error - RejectDirectDeposit(ctx context.Context, depositID int, adminID int, reason string) error - DeleteDirectDeposit(ctx context.Context, id int) error - GetDirectDepositByID(ctx context.Context, id int) (*domain.DirectDeposit, error) -} - -type DirectDepositRepo struct { - store *Store -} - -func NewDirectDepositRepository(store *Store) DirectDepositRepository { - return &DirectDepositRepo{store: store} -} - -func (r *DirectDepositRepo) CreateDirectDeposit(ctx context.Context, deposit *domain.DirectDeposit) error { - params := dbgen.CreateDirectDepositParams{ - CustomerID: pgtype.Int8{Int64: int64(deposit.CustomerID)}, - WalletID: pgtype.Int8{Int64: int64(deposit.WalletID)}, - BankName: pgtype.Text{String: deposit.BankName}, - AccountNumber: pgtype.Text{String: deposit.AccountNumber}, - AccountHolder: pgtype.Text{String: deposit.AccountHolder}, - Amount: pgtype.Numeric{Exp: int32(deposit.Amount)}, - ReferenceNumber: pgtype.Text{String: deposit.ReferenceNumber}, - TransferScreenshot: pgtype.Text{String: deposit.TransferScreenshot}, - } - - dbDeposit, err := r.store.queries.CreateDirectDeposit(ctx, params) - if err != nil { - return err - } - - // Map back to domain struct - deposit.ID = int(dbDeposit.ID) - deposit.Status = dbDeposit.Status.String - deposit.CreatedAt = dbDeposit.CreatedAt.Time - - return nil -} - -func (r *DirectDepositRepo) GetDirectDepositsByStatus( - ctx context.Context, - status string, - page int, - pageSize int, -) ([]domain.DirectDeposit, int64, error) { - - // Default pagination rules - if page < 1 { - page = 1 - } - if pageSize < 1 || pageSize > 100 { - pageSize = 50 - } - offset := (page - 1) * pageSize - - params := dbgen.GetDirectDepositsByStatusParams{ - Status: pgtype.Text{String: status}, - Limit: int32(pageSize), - Offset: int32(offset), - } - - dbItems, err := r.store.queries.GetDirectDepositsByStatus(ctx, params) - if err != nil { - return nil, 0, err - } - - total, err := r.store.queries.CountDirectDepositsByStatus(ctx, pgtype.Text{String: status}) - if err != nil { - return nil, 0, err - } - - deposits := make([]domain.DirectDeposit, len(dbItems)) - for i, d := range dbItems { - deposits[i] = *mapDBDirectDepositToDomain(&d) - } - - return deposits, total, nil -} - -func (r *DirectDepositRepo) ApproveDirectDeposit( - ctx context.Context, - depositID int, - adminID int, -) error { - - params := dbgen.ApproveDirectDepositParams{ - ID: int64(depositID), - ApprovedBy: pgtype.Int8{Int64: int64(adminID)}, - } - - err := r.store.queries.ApproveDirectDeposit(ctx, params) - if err != nil { - return err - } - - return nil -} - -func (r *DirectDepositRepo) GetDirectDepositByID( - ctx context.Context, - id int, -) (*domain.DirectDeposit, error) { - - dbDeposit, err := r.store.queries.GetDirectDepositByID(ctx, int64(id)) - if err != nil { - return nil, err - } - - deposit := mapDBDirectDepositToDomain(&dbDeposit) - return deposit, nil -} - -func (r *DirectDepositRepo) DeleteDirectDeposit( - ctx context.Context, - id int, -) error { - - err := r.store.queries.DeleteDirectDeposit(ctx, int64(id)) - if err != nil { - return err - } - - return nil -} - -func (r *DirectDepositRepo) RejectDirectDeposit( - ctx context.Context, - depositID int, - adminID int, - reason string, -) error { - - params := dbgen.RejectDirectDepositParams{ - ID: int64(depositID), - ApprovedBy: pgtype.Int8{Int64: int64(adminID)}, - RejectionReason: pgtype.Text{String: reason}, - } - - err := r.store.queries.RejectDirectDeposit(ctx, params) - if err != nil { - return err - } - - return nil -} - -func mapDBDirectDepositToDomain(d *dbgen.DirectDeposit) *domain.DirectDeposit { - var approvedBy *int - if d.ApprovedBy.Valid { - v := int(d.ApprovedBy.Int64) - approvedBy = &v - } - - var approvedAt *time.Time - if d.ApprovedAt.Valid { - t := d.ApprovedAt.Time - approvedAt = &t - } - - var rejectionReason *string - if d.RejectionReason.Valid { - r := d.RejectionReason.String - rejectionReason = &r - } - - return &domain.DirectDeposit{ - ID: int(d.ID), - CustomerID: int(d.CustomerID.Int64), - WalletID: int(d.WalletID.Int64), - BankName: d.BankName.String, - AccountNumber: d.AccountNumber.String, - AccountHolder: d.AccountHolder.String, - Amount: float64(d.Amount.Exp), - ReferenceNumber: d.ReferenceNumber.String, - TransferScreenshot: d.TransferScreenshot.String, - Status: d.Status.String, - CreatedAt: d.CreatedAt.Time, - ApprovedBy: approvedBy, - ApprovedAt: approvedAt, - RejectionReason: rejectionReason, - } -} diff --git a/internal/repository/enet_pulse.go b/internal/repository/enet_pulse.go deleted file mode 100644 index 4d3b2df..0000000 --- a/internal/repository/enet_pulse.go +++ /dev/null @@ -1,1014 +0,0 @@ -package repository - -import ( - "context" - "fmt" - "math" - "math/big" - "strconv" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" -) - -func (s *Store) CreateEnetpulseSport(ctx context.Context, sport domain.CreateEnetpulseSport) (domain.EnetpulseSport, error) { - // Convert domain model to DB model if needed - dbSport, err := s.queries.CreateEnetpulseSport(ctx, ConvertCreateEnetpulseSport(sport)) - if err != nil { - return domain.EnetpulseSport{}, err - } - return ConvertDBEnetpulseSport(dbSport), nil -} - -// Fetch all sports -func (s *Store) GetAllEnetpulseSports(ctx context.Context) ([]domain.EnetpulseSport, error) { - dbSports, err := s.queries.GetAllEnetpulseSports(ctx) - if err != nil { - return nil, err - } - - var sports []domain.EnetpulseSport - for _, dbSport := range dbSports { - sports = append(sports, ConvertDBEnetpulseSport(dbSport)) - } - - return sports, nil -} - -func (s *Store) CreateEnetpulseTournamentTemplate( - ctx context.Context, - template domain.CreateEnetpulseTournamentTemplate, -) (domain.EnetpulseTournamentTemplate, error) { - // Convert domain model to DB model if needed - dbTemplate, err := s.queries.CreateEnetpulseTournamentTemplate( - ctx, - ConvertCreateEnetpulseTournamentTemplate(template), - ) - if err != nil { - return domain.EnetpulseTournamentTemplate{}, err - } - return ConvertDBEnetpulseTournamentTemplate(dbTemplate), nil -} - -// Fetch all tournament templates -func (s *Store) GetAllEnetpulseTournamentTemplates(ctx context.Context) ([]domain.EnetpulseTournamentTemplate, error) { - dbTemplates, err := s.queries.GetAllEnetpulseTournamentTemplates(ctx) - if err != nil { - return nil, err - } - - var templates []domain.EnetpulseTournamentTemplate - for _, dbTemplate := range dbTemplates { - templates = append(templates, ConvertDBEnetpulseTournamentTemplate(dbTemplate)) - } - - return templates, nil -} - -// Store.go -func (s *Store) CreateEnetpulseTournament( - ctx context.Context, - tournament domain.CreateEnetpulseTournament, -) (domain.EnetpulseTournament, error) { - // Convert domain model to DB model if needed - dbTournament, err := s.queries.CreateEnetpulseTournament( - ctx, - ConvertCreateEnetpulseTournament(tournament), - ) - if err != nil { - return domain.EnetpulseTournament{}, err - } - return ConvertDBEnetpulseTournament(dbTournament), nil -} - -// store.go -func (s *Store) GetAllEnetpulseTournaments(ctx context.Context) ([]domain.EnetpulseTournament, error) { - dbTournaments, err := s.queries.GetAllEnetpulseTournaments(ctx) - if err != nil { - return nil, err - } - - var tournaments []domain.EnetpulseTournament - for _, dbT := range dbTournaments { - tournaments = append(tournaments, ConvertDBEnetpulseTournament(dbT)) - } - - return tournaments, nil -} - -func (s *Store) CreateEnetpulseTournamentStage( - ctx context.Context, - stage domain.CreateEnetpulseTournamentStage, -) (domain.EnetpulseTournamentStage, error) { - // Convert domain model to DB model if needed - dbStage, err := s.queries.CreateEnetpulseTournamentStage( - ctx, - ConvertCreateEnetpulseTournamentStage(stage), - ) - if err != nil { - return domain.EnetpulseTournamentStage{}, err - } - return ConvertDBEnetpulseTournamentStage(dbStage), nil -} - -// Fetch all tournament stages -func (s *Store) GetAllEnetpulseTournamentStages(ctx context.Context) ([]domain.EnetpulseTournamentStage, error) { - dbStages, err := s.queries.GetAllEnetpulseTournamentStages(ctx) - if err != nil { - return nil, err - } - - var stages []domain.EnetpulseTournamentStage - for _, dbStage := range dbStages { - stages = append(stages, ConvertDBEnetpulseTournamentStage(dbStage)) - } - - return stages, nil -} - -// Optional: Fetch stages by TournamentFK -func (s *Store) GetTournamentStagesByTournamentFK(ctx context.Context, tournamentFK string) ([]domain.EnetpulseTournamentStage, error) { - dbStages, err := s.queries.GetTournamentStagesByTournamentFK(ctx, tournamentFK) - if err != nil { - return nil, err - } - - var stages []domain.EnetpulseTournamentStage - for _, dbStage := range dbStages { - stages = append(stages, ConvertDBEnetpulseTournamentStage(dbStage)) - } - - return stages, nil -} - -// Create a new fixture -func (s *Store) CreateEnetpulseFixture( - ctx context.Context, - fixture domain.CreateEnetpulseFixture, -) (domain.EnetpulseFixture, error) { - // Convert domain model to DB params (sqlc-generated struct or parameters) - dbFixture, err := s.queries.CreateEnetpulseFixture( - ctx, - ConvertCreateEnetpulseFixture(fixture), // your converter - ) - if err != nil { - return domain.EnetpulseFixture{}, err - } - return ConvertDBEnetpulseFixture(dbFixture), nil // convert DB row to domain -} - -// Fetch all fixtures -func (s *Store) GetAllEnetpulseFixtures(ctx context.Context) ([]domain.EnetpulseFixture, error) { - dbFixtures, err := s.queries.GetAllEnetpulseFixtures(ctx) - if err != nil { - return nil, err - } - - var fixtures []domain.EnetpulseFixture - for _, dbFixture := range dbFixtures { - fixtures = append(fixtures, ConvertDBEnetpulseFixture(dbFixture)) - } - - return fixtures, nil -} - -func (s *Store) CreateEnetpulseResult( - ctx context.Context, - result domain.CreateEnetpulseResult, -) (domain.EnetpulseResult, error) { - dbResult, err := s.queries.CreateEnetpulseResult( - ctx, - ConvertCreateEnetpulseResult(result), - ) - if err != nil { - return domain.EnetpulseResult{}, err - } - - return ConvertDBEnetpulseResult(dbResult), nil -} - -// GetAllEnetpulseResults retrieves all Enetpulse results. -func (s *Store) GetAllEnetpulseResults(ctx context.Context) ([]domain.EnetpulseResult, error) { - dbResults, err := s.queries.GetAllEnetpulseResults(ctx) - if err != nil { - return nil, err - } - - results := make([]domain.EnetpulseResult, 0, len(dbResults)) - for _, dbR := range dbResults { - results = append(results, ConvertDBEnetpulseResult(dbR)) - } - - return results, nil -} - -// CreateEnetpulseOutcomeType inserts or updates an EnetPulse outcome type record. -func (s *Store) CreateEnetpulseOutcomeType( - ctx context.Context, - outcomeType domain.CreateEnetpulseOutcomeType, -) (domain.EnetpulseOutcomeType, error) { - dbOutcome, err := s.queries.CreateEnetpulseOutcomeType( - ctx, - ConvertCreateEnetpulseOutcomeType(outcomeType), - ) - if err != nil { - return domain.EnetpulseOutcomeType{}, err - } - - return ConvertDBEnetpulseOutcomeType(dbOutcome), nil -} - -// GetAllEnetpulseOutcomeTypes retrieves all outcome types. -func (s *Store) GetAllEnetpulseOutcomeTypes(ctx context.Context) ([]domain.EnetpulseOutcomeType, error) { - dbOutcomes, err := s.queries.GetAllEnetpulseOutcomeTypes(ctx) - if err != nil { - return nil, err - } - - outcomes := make([]domain.EnetpulseOutcomeType, 0, len(dbOutcomes)) - for _, dbO := range dbOutcomes { - outcomes = append(outcomes, ConvertDBEnetpulseOutcomeType(dbO)) - } - - return outcomes, nil -} - -// CreateEnetpulsePreodds inserts or updates a preodds record. -func (s *Store) CreateEnetpulsePreodds( - ctx context.Context, - preodds domain.CreateEnetpulsePreodds, -) (domain.EnetpulsePreodds, error) { - - // Convert domain to DB params - params, err := ConvertCreateEnetpulsePreodds(preodds) - if err != nil { - return domain.EnetpulsePreodds{}, err - } - - // Insert into DB - dbPreodds, err := s.queries.CreateEnetpulsePreodds(ctx, params) - if err != nil { - return domain.EnetpulsePreodds{}, err - } - - return ConvertDBEnetpulsePreodds(dbPreodds), nil -} - -// GetAllEnetpulsePreodds retrieves all preodds records. -func (s *Store) GetAllEnetpulsePreodds(ctx context.Context) ([]domain.EnetpulsePreodds, error) { - dbPreodds, err := s.queries.GetAllEnetpulsePreodds(ctx) - if err != nil { - return nil, err - } - - preodds := make([]domain.EnetpulsePreodds, 0, len(dbPreodds)) - for _, dbP := range dbPreodds { - preodds = append(preodds, ConvertDBEnetpulsePreodds(dbP)) - } - - return preodds, nil -} - -// CreateEnetpulsePreoddsBettingOffer inserts or updates a betting offer -func (s *Store) CreateEnetpulsePreoddsBettingOffer( - ctx context.Context, - bettingOffer domain.CreateEnetpulsePreoddsBettingOffer, -) (domain.EnetpulsePreoddsBettingOffer, error) { - - params := ConvertCreateEnetpulsePreoddsBettingOffer(bettingOffer) - - dbOffer, err := s.queries.CreateEnetpulsePreoddsBettingOffer(ctx, params) - if err != nil { - return domain.EnetpulsePreoddsBettingOffer{}, err - } - - return ConvertDBEnetpulsePreoddsBettingOffer(dbOffer), nil -} - -// GetAllEnetpulsePreoddsBettingOffers retrieves all betting offers -func (s *Store) GetAllEnetpulsePreoddsBettingOffers(ctx context.Context) ([]domain.EnetpulsePreoddsBettingOffer, error) { - dbOffers, err := s.queries.GetAllEnetpulsePreoddsBettingOffers(ctx) - if err != nil { - return nil, err - } - - offers := make([]domain.EnetpulsePreoddsBettingOffer, 0, len(dbOffers)) - for _, dbO := range dbOffers { - offers = append(offers, ConvertDBEnetpulsePreoddsBettingOffer(dbO)) - } - - return offers, nil -} - -func (s *Store) GetAllEnetpulsePreoddsWithBettingOffers(ctx context.Context) ([]domain.EnetpulsePreodds, error) { - rows, err := s.queries.GetAllEnetpulsePreoddsWithBettingOffers(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch preodds with betting offers: %w", err) - } - - // Map for grouping betting offers under each Preodd - preoddsMap := make(map[string]*domain.EnetpulsePreodds) - - for _, row := range rows { - pid := row.PreoddsID - preodd, exists := preoddsMap[pid] - if !exists { - // Create the base Preodd entry - preodd = &domain.EnetpulsePreodds{ - ID: row.PreoddsDbID, - PreoddsID: row.PreoddsID, - EventFK: row.EventFk, - OutcomeTypeFK: row.OutcomeTypeFk.Int32, - OutcomeScopeFK: row.OutcomeScopeFk.Int32, - OutcomeSubtypeFK: row.OutcomeSubtypeFk.Int32, - EventParticipantNumber: row.EventParticipantNumber.Int32, - IParam: row.Iparam.String, - IParam2: row.Iparam2.String, - DParam: row.Dparam.String, - DParam2: row.Dparam2.String, - SParam: row.Sparam.String, - UpdatesCount: row.PreoddsUpdatesCount.Int32, - LastUpdatedAt: row.PreoddsLastUpdatedAt.Time, - CreatedAt: row.PreoddsCreatedAt.Time, - UpdatedAt: row.PreoddsUpdatedAt.Time, - BettingOffers: []domain.EnetpulsePreoddsBettingOffer{}, - } - preoddsMap[pid] = preodd - } - - // Append BettingOffer only if exists - if row.BettingofferID.Valid && row.BettingofferID.String != "" { - offer := domain.EnetpulsePreoddsBettingOffer{ - ID: row.BettingofferDbID.Int64, - BettingOfferID: row.BettingofferID.String, - BettingOfferStatusFK: row.BettingofferStatusFk.Int32, - OddsProviderFK: row.OddsProviderFk.Int32, - Odds: float64(row.Odds.Exp), - OddsOld: float64(row.OddsOld.Exp), - Active: fmt.Sprintf("%v", row.Active), - CouponKey: row.CouponKey.String, - UpdatesCount: int(row.BettingofferUpdatesCount.Int32), - LastUpdatedAt: row.BettingofferLastUpdatedAt.Time, - CreatedAt: row.BettingofferCreatedAt.Time, - UpdatedAt: row.BettingofferUpdatedAt.Time, - } - preodd.BettingOffers = append(preodd.BettingOffers, offer) - } - } - - // Convert map to slice - result := make([]domain.EnetpulsePreodds, 0, len(preoddsMap)) - for _, p := range preoddsMap { - result = append(result, *p) - } - - return result, nil -} - -func (s *Store) GetFixturesWithPreodds(ctx context.Context) ([]domain.EnetpulseFixtureWithPreodds, error) { - dbRows, err := s.queries.GetFixturesWithPreodds(ctx) - if err != nil { - return nil, err - } - - // Use a map to group preodds by fixture - fixtureMap := make(map[string]*domain.EnetpulseFixtureWithPreodds) - - for _, row := range dbRows { - // If fixture not yet in map, add it - if _, exists := fixtureMap[row.FixtureID]; !exists { - fixtureMap[row.FixtureID] = &domain.EnetpulseFixtureWithPreodds{ - FixtureID: row.FixtureID, - FixtureApiID: row.FixtureID, // same alias used in query - FixtureName: row.FixtureName, - SportFk: row.SportFk, - TournamentFk: row.TournamentFk.String, - TournamentTemplateFk: row.TournamentTemplateFk.String, - // TournamentStageFk: row.TournamentStageFk.String, - StartDate: row.StartDate.Time, - StatusType: row.StatusType.String, - StatusDescFk: row.StatusDescFk.String, - RoundTypeFk: row.RoundTypeFk.String, - UpdatesCount: row.FixtureUpdatesCount.Int32, - LastUpdatedAt: row.FixtureLastUpdatedAt.Time, - CreatedAt: row.FixtureCreatedAt.Time, - UpdatedAt: row.FixtureUpdatedAt.Time, - Preodds: []domain.EnetpulsePreodds{}, // initialize slice - } - } - - // Add preodds only if it exists (avoid NULL rows) - if row.PreoddsDbID.Valid { - preodds := domain.EnetpulsePreodds{ - ID: row.PreoddsDbID.Int64, - PreoddsID: row.PreoddsID.String, - EventFK: row.EventFk.Int64, - OutcomeTypeFK: row.OutcomeTypeFk.Int32, - OutcomeScopeFK: row.OutcomeScopeFk.Int32, - OutcomeSubtypeFK: row.OutcomeSubtypeFk.Int32, - EventParticipantNumber: row.EventParticipantNumber.Int32, - IParam: row.Iparam.String, - IParam2: row.Iparam2.String, - DParam: row.Dparam.String, - DParam2: row.Dparam2.String, - SParam: row.Sparam.String, - UpdatesCount: row.PreoddsUpdatesCount.Int32, - LastUpdatedAt: row.PreoddsLastUpdatedAt.Time, - CreatedAt: row.PreoddsCreatedAt.Time, - UpdatedAt: row.PreoddsUpdatedAt.Time, - } - fixtureMap[row.FixtureID].Preodds = append(fixtureMap[row.FixtureID].Preodds, preodds) - } - } - - // Flatten the map into a slice - result := make([]domain.EnetpulseFixtureWithPreodds, 0, len(fixtureMap)) - for _, f := range fixtureMap { - result = append(result, *f) - } - - return result, nil -} - -// func ConvertCreateEnetpulseTournamentStage(stage domain.CreateEnetpulseTournamentStage) dbgen.EnetpulseTournamentStage { -// return dbgen.EnetpulseTournamentStage{ -// StageID: stage.StageID, -// Name: stage.Name, -// TournamentFK: stage.TournamentFK, -// Gender: stage.Gender, -// CountryFK: stage.CountryFK, -// StartDate: stage.StartDate, -// EndDate: stage.EndDate, -// UpdatesCount: int32(stage.UpdatesCount), -// LastUpdatedAt: stage.LastUpdatedAt, -// CountryName: stage.CountryName, -// Status: int32(stage.Status), -// UpdatedAt: time.Now(), -// } -// } - -// ConvertCreateEnetpulseFixture converts the domain model to the SQLC params struct. -func ConvertCreateEnetpulseFixture(f domain.CreateEnetpulseFixture) dbgen.CreateEnetpulseFixtureParams { - return dbgen.CreateEnetpulseFixtureParams{ - FixtureID: f.FixtureID, - Name: f.Name, - SportFk: f.SportFK, - TournamentFk: pgtype.Text{String: f.TournamentFK, Valid: f.TournamentFK != ""}, - TournamentTemplateFk: pgtype.Text{String: f.TournamentTemplateFK, Valid: f.TournamentTemplateFK != ""}, - // TournamentStageFk: pgtype.Text{String: f.TournamentStageFK, Valid: f.TournamentStageFK != ""}, - // TournamentStageName: pgtype.Text{String: f.TournamentStageName, Valid: f.TournamentStageName != ""}, - TournamentName: pgtype.Text{String: f.TournamentName, Valid: f.TournamentName != ""}, - TournamentTemplateName: pgtype.Text{String: f.TournamentTemplateName, Valid: f.TournamentTemplateName != ""}, - SportName: pgtype.Text{String: f.SportName, Valid: f.SportName != ""}, - Gender: pgtype.Text{String: f.Gender, Valid: f.Gender != ""}, - StartDate: pgtype.Timestamptz{Time: f.StartDate, Valid: !f.StartDate.IsZero()}, - StatusType: pgtype.Text{String: f.StatusType, Valid: f.StatusType != ""}, - StatusDescFk: pgtype.Text{String: f.StatusDescFK, Valid: f.StatusDescFK != ""}, - RoundTypeFk: pgtype.Text{String: f.RoundTypeFK, Valid: f.RoundTypeFK != ""}, - UpdatesCount: pgtype.Int4{Int32: int32(f.UpdatesCount), Valid: true}, - LastUpdatedAt: pgtype.Timestamptz{Time: f.LastUpdatedAt, Valid: !f.LastUpdatedAt.IsZero()}, - } -} - -// ConvertDBEnetpulseFixture converts the DB row to the domain model. -func ConvertDBEnetpulseFixture(dbF dbgen.EnetpulseFixture) domain.EnetpulseFixture { - return domain.EnetpulseFixture{ - FixtureID: dbF.FixtureID, - Name: dbF.Name, - SportFK: dbF.SportFk, - TournamentFK: dbF.TournamentFk.String, - TournamentTemplateFK: dbF.TournamentTemplateFk.String, - // TournamentStageFK: dbF.TournamentStageFk.String, - // TournamentStageName: dbF.TournamentStageName.String, - TournamentName: dbF.TournamentName.String, - TournamentTemplateName: dbF.TournamentTemplateName.String, - SportName: dbF.SportName.String, - Gender: dbF.Gender.String, - StartDate: dbF.StartDate.Time.String(), - StatusType: dbF.StatusType.String, - StatusDescFK: dbF.StatusDescFk.String, - RoundTypeFK: dbF.RoundTypeFk.String, - UpdatesCount: fmt.Sprintf("%v", dbF.UpdatesCount), - LastUpdatedAt: dbF.LastUpdatedAt.Time.String(), - // CreatedAt: dbF.CreatedAt.Time, - // UpdatedAt: dbF.UpdatedAt.Time, - } -} - -func ConvertCreateEnetpulseTournamentStage(stage domain.CreateEnetpulseTournamentStage) dbgen.CreateEnetpulseTournamentStageParams { - return dbgen.CreateEnetpulseTournamentStageParams{ - StageID: stage.StageID, - Name: stage.Name, - TournamentFk: stage.TournamentFK, - Gender: pgtype.Text{String: stage.Gender, Valid: stage.Gender != ""}, - CountryFk: pgtype.Text{String: stage.CountryFK, Valid: stage.CountryFK != ""}, - StartDate: pgtype.Timestamptz{Time: stage.StartDate, Valid: !stage.StartDate.IsZero()}, - EndDate: pgtype.Timestamptz{Time: stage.EndDate, Valid: !stage.EndDate.IsZero()}, - UpdatesCount: pgtype.Int4{Int32: int32(stage.UpdatesCount), Valid: true}, - LastUpdatedAt: pgtype.Timestamptz{Time: stage.LastUpdatedAt, Valid: !stage.LastUpdatedAt.IsZero()}, - CountryName: pgtype.Text{String: stage.CountryName, Valid: stage.CountryFK != ""}, - Status: pgtype.Int4{Int32: int32(stage.Status), Valid: true}, - // Las: pgtype.Timestamptz{Time: time.Now(), Valid: true}, - } -} - -func ConvertDBEnetpulseTournamentStage(db dbgen.EnetpulseTournamentStage) domain.EnetpulseTournamentStage { - return domain.EnetpulseTournamentStage{ - ID: db.ID, - StageID: db.StageID, - Name: db.Name, - TournamentFK: db.TournamentFk, - Gender: db.Gender.String, - CountryFK: db.CountryFk.String, - StartDate: db.StartDate.Time, - EndDate: db.EndDate.Time, - UpdatesCount: func() int { - if db.UpdatesCount.Valid { - return int(db.UpdatesCount.Int32) - } - return 0 - }(), - LastUpdatedAt: db.LastUpdatedAt.Time, - CountryName: db.CountryName.String, - Status: func() int { - if db.Status.Valid { - return int(db.Status.Int32) - } - return 0 - }(), - CreatedAt: db.CreatedAt.Time, - UpdatedAt: db.UpdatedAt.Time, - } -} - -func ConvertCreateEnetpulseSport(s domain.CreateEnetpulseSport) dbgen.CreateEnetpulseSportParams { - return dbgen.CreateEnetpulseSportParams{ - SportID: s.SportID, - Name: s.Name, - UpdatesCount: pgtype.Int4{Int32: int32(s.UpdatesCount), Valid: true}, // SQLC might use int32 - LastUpdatedAt: pgtype.Timestamptz{Time: s.LastUpdatedAt, Valid: true}, - Status: pgtype.Int4{Int32: int32(s.Status), Valid: true}, - // UpdatedAt: nil, // SQLC will default NOW() if nil - } -} - -func ConvertDBEnetpulseSport(db dbgen.EnetpulseSport) domain.EnetpulseSport { - return domain.EnetpulseSport{ - ID: db.ID, - SportID: db.SportID, - Name: db.Name, - UpdatesCount: func() int { - if db.UpdatesCount.Valid { - return int(db.UpdatesCount.Int32) - } - return 0 // or another default value if needed - }(), // cast from int32 - LastUpdatedAt: db.LastUpdatedAt.Time, - Status: func() int { - if db.Status.Valid { - return int(db.Status.Int32) - } - return 0 // or another default value if needed - }(), // cast from int32 - CreatedAt: db.CreatedAt.Time, - UpdatedAt: db.UpdatedAt.Time, - } -} - -func ConvertDBEnetpulseTournamentTemplate(db dbgen.EnetpulseTournamentTemplate) domain.EnetpulseTournamentTemplate { - return domain.EnetpulseTournamentTemplate{ - ID: db.ID, - TemplateID: db.TemplateID, - Name: db.Name, - SportFK: db.SportFk, - Gender: func() string { - if db.Gender.Valid { - return db.Gender.String - } - return "" - }(), - UpdatesCount: func() int { - if db.UpdatesCount.Valid { - return int(db.UpdatesCount.Int32) - } - return 0 - }(), - LastUpdatedAt: db.LastUpdatedAt.Time, - Status: func() int { - if db.Status.Valid { - return int(db.Status.Int32) - } - return 0 - }(), - CreatedAt: db.CreatedAt.Time, - UpdatedAt: db.UpdatedAt.Time, - } -} - -func ConvertCreateEnetpulseTournamentTemplate( - t domain.CreateEnetpulseTournamentTemplate, -) dbgen.CreateEnetpulseTournamentTemplateParams { - return dbgen.CreateEnetpulseTournamentTemplateParams{ - TemplateID: t.TemplateID, - SportFk: fmt.Sprintf("%d", t.SportFK), - Gender: pgtype.Text{String: t.Gender, Valid: t.Gender != ""}, - UpdatesCount: pgtype.Int4{Int32: int32(t.UpdatesCount), Valid: true}, - LastUpdatedAt: pgtype.Timestamptz{Time: t.LastUpdatedAt, Valid: true}, - Status: pgtype.Int4{Int32: int32(t.Status), Valid: true}, - } -} - -// Convert domain to DB insert struct for sqlc -func ConvertCreateEnetpulseTournament(t domain.CreateEnetpulseTournament) dbgen.CreateEnetpulseTournamentParams { - return dbgen.CreateEnetpulseTournamentParams{ - TournamentID: t.TournamentID, - Name: t.Name, - TournamentTemplateFk: t.TournamentTemplateFK, - UpdatesCount: pgtype.Int4{Int32: int32(t.UpdatesCount), Valid: true}, - LastUpdatedAt: pgtype.Timestamptz{Time: t.LastUpdatedAt, Valid: !t.LastUpdatedAt.IsZero()}, - Status: pgtype.Int4{Int32: int32(t.Status), Valid: true}, - } -} - -// Convert DB row to domain model -func ConvertDBEnetpulseTournament(dbT dbgen.EnetpulseTournament) domain.EnetpulseTournament { - return domain.EnetpulseTournament{ - ID: dbT.ID, - TournamentID: dbT.TournamentID, - Name: dbT.Name, - TournamentTemplateFK: dbT.TournamentTemplateFk, - UpdatesCount: func() int { - if dbT.UpdatesCount.Valid { - return int(dbT.UpdatesCount.Int32) - } - return 0 - }(), - LastUpdatedAt: dbT.LastUpdatedAt.Time, - Status: func() int { - if dbT.Status.Valid { - return int(dbT.Status.Int32) - } - return 0 - }(), - CreatedAt: dbT.CreatedAt.Time, - UpdatedAt: func() *time.Time { - if dbT.UpdatedAt.Valid { - return &dbT.UpdatedAt.Time - } - return nil - }(), - } -} - -func ConvertCreateEnetpulseResult(input domain.CreateEnetpulseResult) dbgen.CreateEnetpulseResultParams { - return dbgen.CreateEnetpulseResultParams{ - ResultID: input.ResultID, - Name: input.Name, - SportFk: input.SportFK, - TournamentFk: pgtype.Text{String: input.TournamentFK, Valid: input.TournamentFK != ""}, - TournamentTemplateFk: pgtype.Text{String: input.TournamentTemplateFK, Valid: input.TournamentTemplateFK != ""}, - // TournamentStageFk: pgtype.Text{String: input.TournamentStageFK, Valid: input.TournamentStageFK != ""}, - // TournamentStageName: pgtype.Text{String: input.TournamentStageName, Valid: input.TournamentStageName != ""}, - TournamentName: pgtype.Text{String: input.TournamentName, Valid: input.TournamentName != ""}, - TournamentTemplateName: pgtype.Text{String: input.TournamentTemplateName, Valid: input.TournamentTemplateName != ""}, - SportName: pgtype.Text{String: input.SportName, Valid: input.SportName != ""}, - StartDate: pgtype.Timestamptz{Time: input.StartDate, Valid: !input.StartDate.IsZero()}, - StatusType: pgtype.Text{String: input.StatusType, Valid: input.StatusType != ""}, - StatusDescFk: pgtype.Text{String: input.StatusDescFK, Valid: input.StatusDescFK != ""}, - RoundTypeFk: pgtype.Text{String: input.RoundTypeFK, Valid: input.RoundTypeFK != ""}, - UpdatesCount: pgtype.Int4{Int32: int32(input.UpdatesCount), Valid: true}, - LastUpdatedAt: pgtype.Timestamptz{Time: input.LastUpdatedAt, Valid: !input.LastUpdatedAt.IsZero()}, - Round: pgtype.Text{String: input.Round, Valid: input.Round != ""}, - Live: pgtype.Text{String: input.Live, Valid: input.Live != ""}, - VenueName: pgtype.Text{String: input.VenueName, Valid: input.VenueName != ""}, - LivestatsPlus: pgtype.Text{String: input.LivestatsPlus, Valid: input.LivestatsPlus != ""}, - LivestatsType: pgtype.Text{String: input.LivestatsType, Valid: input.LivestatsType != ""}, - Commentary: pgtype.Text{String: input.Commentary, Valid: input.Commentary != ""}, - LineupConfirmed: pgtype.Bool{Bool: input.LineupConfirmed, Valid: true}, - Verified: pgtype.Bool{Bool: input.Verified, Valid: true}, - Spectators: pgtype.Int4{Int32: int32(input.Spectators), Valid: true}, - GameStarted: pgtype.Timestamptz{Time: *input.GameStarted, Valid: !input.GameStarted.IsZero()}, - FirstHalfEnded: pgtype.Timestamptz{Time: *input.FirstHalfEnded, Valid: !input.FirstHalfEnded.IsZero()}, - SecondHalfStarted: pgtype.Timestamptz{Time: *input.SecondHalfStarted, Valid: !input.SecondHalfStarted.IsZero()}, - SecondHalfEnded: pgtype.Timestamptz{Time: *input.SecondHalfEnded, Valid: !input.SecondHalfEnded.IsZero()}, - GameEnded: pgtype.Timestamptz{Time: *input.GameEnded, Valid: !input.GameEnded.IsZero()}, - } -} - -// ConvertDBEnetpulseResult maps SQLC result → domain model -func ConvertDBEnetpulseResult(db dbgen.EnetpulseResult) domain.EnetpulseResult { - return domain.EnetpulseResult{ - ID: db.ID, - ResultID: db.ResultID, - Name: db.Name, - SportFK: db.SportFk, - TournamentFK: db.TournamentFk.String, - TournamentTemplateFK: db.TournamentTemplateFk.String, - // TournamentStageFK: db.TournamentStageFk.String, - // TournamentStageName: db.TournamentStageName.String, - TournamentName: db.TournamentName.String, - TournamentTemplateName: db.TournamentTemplateName.String, - SportName: db.SportName.String, - StartDate: db.StartDate.Time, - StatusType: db.StatusType.String, - StatusDescFK: db.StatusDescFk.String, - RoundTypeFK: db.RoundTypeFk.String, - UpdatesCount: db.UpdatesCount.Int32, - LastUpdatedAt: &db.LastUpdatedAt.Time, - Round: db.Round.String, - Live: db.Live.String, - VenueName: db.VenueName.String, - LivestatsPlus: db.LivestatsPlus.String, - LivestatsType: db.LivestatsType.String, - Commentary: db.Commentary.String, - LineupConfirmed: db.LineupConfirmed.Bool, - Verified: db.Verified.Bool, - Spectators: db.Spectators.Int32, - GameStarted: &db.GameStarted.Time, - FirstHalfEnded: &db.FirstHalfEnded.Time, - SecondHalfStarted: &db.SecondHalfStarted.Time, - SecondHalfEnded: &db.SecondHalfEnded.Time, - GameEnded: &db.GameEnded.Time, - CreatedAt: db.CreatedAt.Time, - UpdatedAt: &db.UpdatedAt.Time, - } -} - -// ConvertCreateEnetpulseOutcomeType converts the domain struct to SQLC params. -func ConvertCreateEnetpulseOutcomeType(o domain.CreateEnetpulseOutcomeType) dbgen.CreateEnetpulseOutcomeTypeParams { - return dbgen.CreateEnetpulseOutcomeTypeParams{ - OutcomeTypeID: o.OutcomeTypeID, - Name: o.Name, - Description: pgtype.Text{String: o.Description, Valid: o.Description != ""}, // TODO: thiso.Description, - UpdatesCount: pgtype.Int4{Int32: int32(o.UpdatesCount), Valid: true}, - LastUpdatedAt: pgtype.Timestamptz{Time: o.LastUpdatedAt, Valid: !o.LastUpdatedAt.IsZero()}, - } -} - -// ConvertDBEnetpulseOutcomeType converts SQLC DB model to domain model. -func ConvertDBEnetpulseOutcomeType(dbO dbgen.EnetpulseOutcomeType) domain.EnetpulseOutcomeType { - return domain.EnetpulseOutcomeType{ - ID: dbO.ID, - OutcomeTypeID: dbO.OutcomeTypeID, - Name: dbO.Name, - Description: dbO.Description.String, - UpdatesCount: dbO.UpdatesCount.Int32, - LastUpdatedAt: dbO.LastUpdatedAt.Time, - CreatedAt: dbO.CreatedAt.Time, - UpdatedAt: dbO.UpdatedAt.Time, - } -} - -func ConvertCreateEnetpulsePreodds(p domain.CreateEnetpulsePreodds) (dbgen.CreateEnetpulsePreoddsParams, error) { - eventFK, err := strconv.ParseInt(p.EventFK, 10, 64) - if err != nil { - return dbgen.CreateEnetpulsePreoddsParams{}, fmt.Errorf("invalid EventFK: %w", err) - } - - outcomeTypeFK, err := strconv.ParseInt(p.OutcomeTypeFK, 10, 32) - if err != nil { - return dbgen.CreateEnetpulsePreoddsParams{}, fmt.Errorf("invalid OutcomeTypeFK: %w", err) - } - - outcomeScopeFK, err := strconv.ParseInt(p.OutcomeScopeFK, 10, 32) - if err != nil { - return dbgen.CreateEnetpulsePreoddsParams{}, fmt.Errorf("invalid OutcomeScopeFK: %w", err) - } - - outcomeSubtypeFK, err := strconv.ParseInt(p.OutcomeSubtypeFK, 10, 32) - if err != nil { - return dbgen.CreateEnetpulsePreoddsParams{}, fmt.Errorf("invalid OutcomeSubtypeFK: %w", err) - } - - return dbgen.CreateEnetpulsePreoddsParams{ - PreoddsID: p.PreoddsID, - EventFk: eventFK, - OutcomeTypeFk: pgtype.Int4{Int32: int32(outcomeTypeFK), Valid: true}, - OutcomeScopeFk: pgtype.Int4{Int32: int32(outcomeScopeFK), Valid: true}, - OutcomeSubtypeFk: pgtype.Int4{Int32: int32(outcomeSubtypeFK), Valid: true}, - EventParticipantNumber: pgtype.Int4{Int32: int32(p.EventParticipantNumber), Valid: true}, - Iparam: pgtype.Text{String: p.IParam, Valid: p.IParam != ""}, - Iparam2: pgtype.Text{String: p.IParam2, Valid: p.IParam2 != ""}, - Dparam: pgtype.Text{String: p.DParam, Valid: p.DParam != ""}, - Dparam2: pgtype.Text{String: p.DParam2, Valid: p.DParam2 != ""}, - Sparam: pgtype.Text{String: p.SParam, Valid: p.SParam != ""}, - UpdatesCount: pgtype.Int4{Int32: int32(p.UpdatesCount), Valid: true}, - LastUpdatedAt: pgtype.Timestamptz{Time: p.LastUpdatedAt, Valid: !p.LastUpdatedAt.IsZero()}, - }, nil -} - -func ConvertDBEnetpulsePreodds(dbP dbgen.EnetpulsePreodd) domain.EnetpulsePreodds { - return domain.EnetpulsePreodds{ - PreoddsID: dbP.PreoddsID, - EventFK: dbP.EventFk, - OutcomeTypeFK: dbP.OutcomeTypeFk.Int32, - OutcomeScopeFK: dbP.OutcomeScopeFk.Int32, - OutcomeSubtypeFK: dbP.OutcomeSubtypeFk.Int32, - EventParticipantNumber: dbP.EventParticipantNumber.Int32, - IParam: dbP.Iparam.String, - IParam2: dbP.Iparam2.String, - DParam: dbP.Dparam.String, - DParam2: dbP.Dparam2.String, - SParam: dbP.Sparam.String, - UpdatesCount: dbP.UpdatesCount.Int32, - LastUpdatedAt: dbP.LastUpdatedAt.Time, - CreatedAt: dbP.CreatedAt.Time, - UpdatedAt: dbP.UpdatedAt.Time, - } -} - -func ConvertCreateEnetpulsePreoddsBettingOffer(o domain.CreateEnetpulsePreoddsBettingOffer) dbgen.CreateEnetpulsePreoddsBettingOfferParams { - // Convert float64 to int64 with scale 2 - oddsInt := big.NewInt(int64(math.Round(o.Odds * 100))) - oddsOldInt := big.NewInt(int64(math.Round(o.OddsOld * 100))) - - return dbgen.CreateEnetpulsePreoddsBettingOfferParams{ - BettingofferID: o.BettingOfferID, - PreoddsFk: o.PreoddsFK, - BettingofferStatusFk: pgtype.Int4{Int32: o.BettingOfferStatusFK, Valid: true}, - OddsProviderFk: pgtype.Int4{Int32: o.OddsProviderFK, Valid: true}, - Odds: pgtype.Numeric{ - Int: oddsInt, - Exp: -2, // scale 2 decimal places - Valid: true, - }, - OddsOld: pgtype.Numeric{ - Int: oddsOldInt, - Exp: -2, - Valid: true, - }, - Active: pgtype.Bool{Bool: o.Active == "yes", Valid: true}, - CouponKey: pgtype.Text{ - String: o.CouponKey, - Valid: o.CouponKey != "", - }, - UpdatesCount: pgtype.Int4{Int32: int32(o.UpdatesCount), Valid: true}, - LastUpdatedAt: pgtype.Timestamptz{Time: o.LastUpdatedAt, Valid: !o.LastUpdatedAt.IsZero()}, - } -} - -// Convert DB result to domain struct -func ConvertDBEnetpulsePreoddsBettingOffer(o dbgen.EnetpulsePreoddsBettingoffer) domain.EnetpulsePreoddsBettingOffer { - var odds, oddsOld float64 - if o.Odds.Valid { - odds, _ = o.Odds.Int.Float64() // Convert pgtype.Numeric to float64 - } - if o.OddsOld.Valid { - oddsOld, _ = o.OddsOld.Int.Float64() - } - - active := "no" - if o.Active.Valid && o.Active.Bool { - active = "yes" - } - - return domain.EnetpulsePreoddsBettingOffer{ - ID: o.ID, - BettingOfferID: o.BettingofferID, - PreoddsFK: o.PreoddsFk, - BettingOfferStatusFK: o.BettingofferStatusFk.Int32, - OddsProviderFK: o.OddsProviderFk.Int32, - Odds: odds, - OddsOld: oddsOld, - Active: active, - CouponKey: o.CouponKey.String, - UpdatesCount: int(o.UpdatesCount.Int32), - LastUpdatedAt: o.LastUpdatedAt.Time, - CreatedAt: o.CreatedAt.Time, - UpdatedAt: o.UpdatedAt.Time, - } -} - -func (s *Store) CreateEnetpulseResultParticipant( - ctx context.Context, - participant domain.CreateEnetpulseResultParticipant, -) (domain.EnetpulseResultParticipant, error) { - dbParticipant, err := s.queries.CreateEnetpulseResultParticipant( - ctx, - ConvertCreateEnetpulseResultParticipant(participant), - ) - if err != nil { - return domain.EnetpulseResultParticipant{}, err - } - - return ConvertDBEnetpulseResultParticipant(dbParticipant), nil -} - -func (s *Store) GetEnetpulseResultParticipantsByResultFK( - ctx context.Context, - resultFK string, -) ([]domain.EnetpulseResultParticipant, error) { - dbParticipants, err := s.queries.GetEnetpulseResultParticipantsByResultFK(ctx, resultFK) - if err != nil { - return nil, err - } - - participants := make([]domain.EnetpulseResultParticipant, 0, len(dbParticipants)) - for _, dbp := range dbParticipants { - participants = append(participants, ConvertDBEnetpulseResultParticipant(dbp)) - } - - return participants, nil -} - -func (s *Store) CreateEnetpulseResultReferee( - ctx context.Context, - referee domain.CreateEnetpulseResultReferee, -) (domain.EnetpulseResultReferee, error) { - dbReferee, err := s.queries.CreateEnetpulseResultReferee( - ctx, - ConvertCreateEnetpulseResultReferee(referee), - ) - if err != nil { - return domain.EnetpulseResultReferee{}, err - } - - return ConvertDBEnetpulseResultReferee(dbReferee), nil -} - -func (s *Store) GetEnetpulseResultRefereesByResultFK( - ctx context.Context, - resultFK string, -) ([]domain.EnetpulseResultReferee, error) { - dbReferees, err := s.queries.GetEnetpulseResultRefereesByResultFK(ctx, resultFK) - if err != nil { - return nil, err - } - - referees := make([]domain.EnetpulseResultReferee, 0, len(dbReferees)) - for _, dbr := range dbReferees { - referees = append(referees, ConvertDBEnetpulseResultReferee(dbr)) - } - - return referees, nil -} - -func ConvertCreateEnetpulseResultParticipant(p domain.CreateEnetpulseResultParticipant) dbgen.CreateEnetpulseResultParticipantParams { - return dbgen.CreateEnetpulseResultParticipantParams{ - ParticipantMapID: p.ParticipantMapID, - ResultFk: p.ResultFk, - ParticipantFk: p.ParticipantFk, - Number: pgtype.Int4{Int32: p.Number}, - Name: pgtype.Text{String: p.Name}, - Gender: pgtype.Text{String: p.Gender}, - Type: pgtype.Text{String: p.Type}, - CountryFk: pgtype.Text{String: p.CountryFk}, - CountryName: pgtype.Text{String: p.CountryName}, - OrdinaryTime: pgtype.Text{String: p.OrdinaryTime}, - RunningScore: pgtype.Text{String: p.RunningScore}, - Halftime: pgtype.Text{String: p.Halftime}, - FinalResult: pgtype.Text{String: p.FinalResult}, - LastUpdatedAt: pgtype.Timestamptz{Time: p.LastUpdatedAt, Valid: !p.LastUpdatedAt.IsZero()}, - } -} - -func ConvertDBEnetpulseResultParticipant(p dbgen.EnetpulseResultParticipant) domain.EnetpulseResultParticipant { - return domain.EnetpulseResultParticipant{ - ID: p.ID, - ParticipantMapID: p.ParticipantMapID, - ResultFk: p.ResultFk, - ParticipantFk: p.ParticipantFk, - Number: p.Number.Int32, - Name: p.Name.String, - Gender: p.Gender.String, - Type: p.Type.String, - CountryFk: p.CountryFk.String, - CountryName: p.CountryName.String, - OrdinaryTime: p.OrdinaryTime.String, - RunningScore: p.RunningScore.String, - Halftime: p.Halftime.String, - FinalResult: p.FinalResult.String, - LastUpdatedAt: p.LastUpdatedAt.Time, - CreatedAt: p.CreatedAt.Time, - } -} - -func ConvertCreateEnetpulseResultReferee(r domain.CreateEnetpulseResultReferee) dbgen.CreateEnetpulseResultRefereeParams { - return dbgen.CreateEnetpulseResultRefereeParams{ - ResultFk: r.ResultFk, - RefereeFk: pgtype.Text{String: r.RefereeFk}, - Assistant1RefereeFk: pgtype.Text{String: r.Assistant1RefereeFk}, - Assistant2RefereeFk: pgtype.Text{String: r.Assistant2RefereeFk}, - FourthRefereeFk: pgtype.Text{String: r.FourthRefereeFk}, - Var1RefereeFk: pgtype.Text{String: r.Var1RefereeFk}, - Var2RefereeFk: pgtype.Text{String: r.Var2RefereeFk}, - LastUpdatedAt: pgtype.Timestamptz{Time: r.LastUpdatedAt}, - } -} - -func ConvertDBEnetpulseResultReferee(r dbgen.EnetpulseResultReferee) domain.EnetpulseResultReferee { - return domain.EnetpulseResultReferee{ - ID: r.ID, - ResultFk: r.ResultFk, - RefereeFk: r.RefereeFk.String, - Assistant1RefereeFk: r.Assistant1RefereeFk.String, - Assistant2RefereeFk: r.Assistant2RefereeFk.String, - FourthRefereeFk: r.FourthRefereeFk.String, - Var1RefereeFk: r.Var1RefereeFk.String, - Var2RefereeFk: r.Var2RefereeFk.String, - LastUpdatedAt: r.LastUpdatedAt.Time, - CreatedAt: r.CreatedAt.Time, - } -} diff --git a/internal/repository/event.go b/internal/repository/event.go deleted file mode 100644 index 8840e96..0000000 --- a/internal/repository/event.go +++ /dev/null @@ -1,149 +0,0 @@ -package repository - -import ( - "context" - "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/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)) -} - -func (s *Store) GetLiveEventIDs(ctx context.Context) ([]int64, error) { - return s.queries.ListLiveEvents(ctx) -} - -func (s *Store) GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) { - - events, err := s.queries.GetAllEvents(ctx, dbgen.GetAllEventsParams{ - LeagueID: filter.LeagueID.ToPG(), - SportID: filter.SportID.ToPG(), - Query: filter.Query.ToPG(), - Limit: filter.Limit.ToPG(), - Offset: pgtype.Int4{ - Int32: int32(filter.Offset.Value * filter.Limit.Value), - Valid: filter.Offset.Valid, - }, - - FirstStartTime: filter.FirstStartTime.ToPG(), - LastStartTime: filter.LastStartTime.ToPG(), - CountryCode: filter.CountryCode.ToPG(), - IsLive: filter.IsLive.ToPG(), - Status: filter.Status.ToPG(), - Source: filter.Source.ToPG(), - }) - - if err != nil { - return nil, 0, err - } - - totalCount, err := s.queries.GetTotalEvents(ctx, dbgen.GetTotalEventsParams{ - LeagueID: filter.LeagueID.ToPG(), - SportID: filter.SportID.ToPG(), - Query: filter.Query.ToPG(), - FirstStartTime: filter.FirstStartTime.ToPG(), - LastStartTime: filter.LastStartTime.ToPG(), - CountryCode: filter.CountryCode.ToPG(), - IsLive: filter.IsLive.ToPG(), - Status: filter.Status.ToPG(), - Source: filter.Source.ToPG(), - }) - if err != nil { - return nil, 0, err - } - - return domain.ConvertDBEvents(events), totalCount, nil -} - -func (s *Store) GetEventByID(ctx context.Context, ID int64) (domain.BaseEvent, error) { - event, err := s.queries.GetEventByID(ctx, ID) - if err != nil { - return domain.BaseEvent{}, err - } - - return domain.ConvertDBEvent(event), nil -} -func (s *Store) GetEventBySourceID(ctx context.Context, id string, source domain.EventSource) (domain.BaseEvent, error) { - event, err := s.queries.GetEventBySourceID(ctx, dbgen.GetEventBySourceIDParams{ - SourceEventID: id, - Source: string(source), - }) - if err != nil { - return domain.BaseEvent{}, err - } - - return domain.ConvertDBEvent(event), nil -} -func (s *Store) UpdateFinalScore(ctx context.Context, eventID int64, fullScore string, status domain.EventStatus) error { - params := dbgen.UpdateMatchResultParams{ - Score: pgtype.Text{String: fullScore, Valid: true}, - Status: string(status), - ID: eventID, - } - - err := s.queries.UpdateMatchResult(ctx, params) - if err != nil { - return fmt.Errorf("failed to update final score for event %v: %w", eventID, err) - } - - return nil -} - -func (s *Store) UpdateEventStatus(ctx context.Context, eventID int64, status domain.EventStatus) error { - params := dbgen.UpdateMatchResultParams{ - Status: string(status), - ID: eventID, - } - - err := s.queries.UpdateMatchResult(ctx, params) - - if err != nil { - return err - } - return nil - -} - -func (s *Store) IsEventMonitored(ctx context.Context, eventID int64) (bool, error) { - isMonitored, err := s.queries.IsEventMonitored(ctx, eventID) - - if err != nil { - return false, err - } - return isMonitored, err -} -func (s *Store) UpdateEventMonitored(ctx context.Context, eventID int64, IsMonitored bool) error { - return s.queries.UpdateEventMonitored(ctx, dbgen.UpdateEventMonitoredParams{ - ID: eventID, - IsMonitored: IsMonitored, - }) -} - - -func (s *Store) DeleteEvent(ctx context.Context, eventID int64) error { - err := s.queries.DeleteEvent(ctx, eventID) - if err != nil { - return err - } - return nil -} - -func (s *Store) GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error) { - sportAndLeagueIDs, err := s.queries.GetSportAndLeagueIDs(ctx, eventID) - if err != nil { - return nil, err - } - - res := []int64{int64(sportAndLeagueIDs.SportID), sportAndLeagueIDs.LeagueID} - return res, err -} diff --git a/internal/repository/event_history.go b/internal/repository/event_history.go deleted file mode 100644 index cdff4b0..0000000 --- a/internal/repository/event_history.go +++ /dev/null @@ -1,59 +0,0 @@ -package repository - -import ( - "context" - "fmt" - - 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)) - - if err != nil { - return domain.EventHistory{}, fmt.Errorf("InsertEventHistory failed: %w", err) - } - - return domain.ConvertDBEventHistory(dbEventHistory), nil -} - -func convertEventHistory(list []dbgen.EventHistory) []domain.EventHistory { - result := make([]domain.EventHistory, 0, len(list)) - for _, item := range list { - result = append(result, domain.ConvertDBEventHistory(item)) - } - return result -} - -func (s *Store) GetAllEventHistory(ctx context.Context, filter domain.EventHistoryFilter) ([]domain.EventHistory, error) { - dbEventHistories, err := s.queries.GetAllEventHistory(ctx, dbgen.GetAllEventHistoryParams{ - EventID: filter.EventID.ToPG(), - CreatedAfter: filter.CreatedAfter.ToPG(), - CreatedBefore: filter.CreatedBefore.ToPG(), - }) - - if err != nil { - return nil, fmt.Errorf("GetAllEventHistory failed: %w", err) - } - - return convertEventHistory(dbEventHistories), nil -} - -func (s *Store) GetInitialEventPerDay(ctx context.Context, filter domain.EventHistoryFilter) ([]domain.EventHistory, error) { - dbEventHistories, err := s.queries.GetInitialEventPerDay(ctx, dbgen.GetInitialEventPerDayParams{ - EventID: filter.EventID.ToPG(), - CreatedAfter: filter.CreatedAfter.ToPG(), - CreatedBefore: filter.CreatedBefore.ToPG(), - }) - - if err != nil { - return nil, fmt.Errorf("GetInitialEventPerDay failed: %w", err) - } - - return convertEventHistory(dbEventHistories), nil -} diff --git a/internal/repository/event_stats.go b/internal/repository/event_stats.go deleted file mode 100644 index 380c887..0000000 --- a/internal/repository/event_stats.go +++ /dev/null @@ -1,42 +0,0 @@ -package repository - -import ( - "context" - - 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(), - SportID: filter.SportID.ToPG(), - }) - if err != nil { - return domain.EventStats{}, err - } - - return domain.ConvertDBEventStats(stats), nil -} - -func (s *Store) GetTotalEventStatsByInterval(ctx context.Context, filter domain.EventStatsByIntervalFilter) ([]domain.EventStatsByInterval, error) { - stats, err := s.queries.GetTotalEventStatsByInterval(ctx, dbgen.GetTotalEventStatsByIntervalParams{ - Interval: filter.Interval.ToPG(), - LeagueID: filter.LeagueID.ToPG(), - SportID: filter.SportID.ToPG(), - }) - - if err != nil { - return nil, err - } - - return domain.ConvertDBEventStatsByIntervalList(stats), nil -} - -func (s *Store) UpdateEventBetStats(ctx context.Context) error { - return s.queries.UpdateEventBetStats(ctx) -} diff --git a/internal/repository/event_with_settings.go b/internal/repository/event_with_settings.go deleted file mode 100644 index 98611ab..0000000 --- a/internal/repository/event_with_settings.go +++ /dev/null @@ -1,183 +0,0 @@ -package repository -import ( - "context" - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" -) - - -func (s *Store) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) { - events, err := s.queries.GetEventsWithSettings(ctx, dbgen.GetEventsWithSettingsParams{ - CompanyID: companyID, - LeagueID: filter.LeagueID.ToPG(), - SportID: filter.SportID.ToPG(), - Query: filter.Query.ToPG(), - Limit: filter.Limit.ToPG(), - Offset: pgtype.Int4{ - Int32: int32(filter.Offset.Value * filter.Limit.Value), - Valid: filter.Offset.Valid, - }, - FirstStartTime: filter.FirstStartTime.ToPG(), - LastStartTime: filter.LastStartTime.ToPG(), - CountryCode: filter.CountryCode.ToPG(), - IsFeatured: filter.Featured.ToPG(), - IsActive: filter.Active.ToPG(), - IsLive: filter.IsLive.ToPG(), - Status: filter.Status.ToPG(), - Source: filter.Source.ToPG(), - }) - - if err != nil { - return nil, 0, err - } - - totalCount, err := s.queries.GetTotalCompanyEvents(ctx, dbgen.GetTotalCompanyEventsParams{ - CompanyID: companyID, - LeagueID: filter.LeagueID.ToPG(), - SportID: filter.SportID.ToPG(), - Query: filter.Query.ToPG(), - FirstStartTime: filter.FirstStartTime.ToPG(), - LastStartTime: filter.LastStartTime.ToPG(), - CountryCode: filter.CountryCode.ToPG(), - IsFeatured: filter.Featured.ToPG(), - IsActive: filter.Active.ToPG(), - IsLive: filter.IsLive.ToPG(), - Status: filter.Status.ToPG(), - Source: filter.Source.ToPG(), - }) - if err != nil { - return nil, 0, err - } - - result := make([]domain.EventWithSettings, len(events)) - - for i, event := range events { - result[i] = domain.EventWithSettings{ - ID: event.ID, - SportID: event.SportID, - MatchName: event.MatchName, - HomeTeam: event.HomeTeam, - AwayTeam: event.AwayTeam, - HomeTeamID: event.HomeTeamID, - AwayTeamID: event.AwayTeamID, - HomeTeamImage: event.HomeKitImage, - AwayTeamImage: event.AwayKitImage, - LeagueID: event.LeagueID, - LeagueName: event.LeagueName, - LeagueCC: domain.ValidString{ - Value: event.LeagueCc.String, - Valid: event.LeagueCc.Valid, - }, - StartTime: event.StartTime.Time.UTC(), - Source: domain.EventSource(event.Source), - Status: domain.EventStatus(event.Status), - TotalOddOutcomes: event.TotalOutcomes, - SourceEventID: event.SourceEventID, - WinningUpperLimit: event.WinningUpperLimit, - IsFeatured: event.IsFeatured, - IsMonitored: event.IsMonitored, - IsActive: event.IsActive, - DefaultIsFeatured: event.DefaultIsFeatured, - DefaultIsActive: event.DefaultIsActive, - DefaultWinningUpperLimit: event.DefaultWinningUpperLimit, - Score: domain.ValidString{ - Value: event.Score.String, - Valid: event.Score.Valid, - }, - MatchMinute: domain.ValidInt{ - Value: int(event.MatchMinute.Int32), - Valid: event.MatchMinute.Valid, - }, - TimerStatus: domain.ValidString{ - Value: event.TimerStatus.String, - Valid: event.TimerStatus.Valid, - }, - AddedTime: domain.ValidInt{ - Value: int(event.AddedTime.Int32), - Valid: event.AddedTime.Valid, - }, - MatchPeriod: domain.ValidInt{ - Value: int(event.MatchPeriod.Int32), - Valid: event.MatchPeriod.Valid, - }, - IsLive: event.IsLive, - UpdatedAt: event.UpdatedAt.Time, - FetchedAt: event.FetchedAt.Time, - } - } - - return result, totalCount, nil -} - -func (s *Store) GetEventWithSettingByID(ctx context.Context, ID int64, companyID int64) (domain.EventWithSettings, error) { - event, err := s.queries.GetEventWithSettingByID(ctx, dbgen.GetEventWithSettingByIDParams{ - ID: ID, - CompanyID: companyID, - }) - if err != nil { - return domain.EventWithSettings{}, err - } - - res := domain.EventWithSettings{ - ID: event.ID, - SportID: event.SportID, - MatchName: event.MatchName, - HomeTeam: event.HomeTeam, - AwayTeam: event.AwayTeam, - HomeTeamID: event.HomeTeamID, - AwayTeamID: event.AwayTeamID, - HomeTeamImage: event.HomeKitImage, - AwayTeamImage: event.AwayKitImage, - LeagueID: event.LeagueID, - LeagueName: event.LeagueName, - LeagueCC: domain.ValidString{ - Value: event.LeagueCc.String, - Valid: event.LeagueCc.Valid, - }, - StartTime: event.StartTime.Time.UTC(), - Source: domain.EventSource(event.Source), - Status: domain.EventStatus(event.Status), - TotalOddOutcomes: event.TotalOutcomes, - SourceEventID: event.SourceEventID, - WinningUpperLimit: event.WinningUpperLimit, - IsFeatured: event.IsFeatured, - IsMonitored: event.IsMonitored, - IsActive: event.IsActive, - DefaultIsFeatured: event.DefaultIsFeatured, - DefaultIsActive: event.DefaultIsActive, - DefaultWinningUpperLimit: event.DefaultWinningUpperLimit, - Score: domain.ValidString{ - Value: event.Score.String, - Valid: event.Score.Valid, - }, - MatchMinute: domain.ValidInt{ - Value: int(event.MatchMinute.Int32), - Valid: event.MatchMinute.Valid, - }, - TimerStatus: domain.ValidString{ - Value: event.TimerStatus.String, - Valid: event.TimerStatus.Valid, - }, - AddedTime: domain.ValidInt{ - Value: int(event.AddedTime.Int32), - Valid: event.AddedTime.Valid, - }, - MatchPeriod: domain.ValidInt{ - Value: int(event.MatchPeriod.Int32), - Valid: event.MatchPeriod.Valid, - }, - IsLive: event.IsLive, - UpdatedAt: event.UpdatedAt.Time, - FetchedAt: event.FetchedAt.Time, - } - return res, nil -} - -func (s *Store) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error { - return s.queries.SaveTenantEventSettings(ctx, domain.ConvertUpdateTenantEventSettings(event)) -} - -func (s *Store) UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error { - return s.queries.UpdateGlobalEventSettings(ctx, domain.ConvertUpdateGlobalEventSettings(event)) -} \ No newline at end of file diff --git a/internal/repository/institutions.go b/internal/repository/institutions.go deleted file mode 100644 index f901957..0000000 --- a/internal/repository/institutions.go +++ /dev/null @@ -1,190 +0,0 @@ -package repository - -import ( - "context" - "database/sql" - "errors" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" -) - -type BankRepository interface { - CreateBank(ctx context.Context, bank *domain.Bank) error - GetBankByID(ctx context.Context, id int) (*domain.Bank, error) - GetAllBanks( - ctx context.Context, - countryID *int, - isActive *bool, - searchTerm *string, - page int, - pageSize int, - ) ([]domain.Bank, int64, error) - UpdateBank(ctx context.Context, bank *domain.Bank) error - DeleteBank(ctx context.Context, id int) error -} - -type BankRepo struct { - store *Store -} - -func NewBankRepository(store *Store) BankRepository { - return &BankRepo{store: store} -} - -func (r *BankRepo) CreateBank(ctx context.Context, bank *domain.Bank) error { - params := dbgen.CreateBankParams{ - Slug: bank.Slug, - Swift: bank.Swift, - Name: bank.Name, - AcctLength: int32(bank.AcctLength), - CountryID: int32(bank.CountryID), - IsMobilemoney: pgtype.Int4{Int32: int32(bank.IsMobileMoney), Valid: true}, - IsActive: int32(bank.IsActive), - IsRtgs: int32(bank.IsRTGS), - Active: int32(bank.Active), - Is24hrs: pgtype.Int4{Int32: int32(bank.Is24Hrs), Valid: true}, - Currency: bank.Currency, - BankLogo: pgtype.Text{String: bank.BankLogo, Valid: true}, - } - createdBank, err := r.store.queries.CreateBank(ctx, params) - if err != nil { - return err - } - // Update the ID and timestamps on the passed struct - bank.ID = int(createdBank.ID) - bank.CreatedAt = createdBank.CreatedAt.Time - bank.UpdatedAt = createdBank.UpdatedAt.Time - return nil -} - -func (r *BankRepo) GetBankByID(ctx context.Context, id int) (*domain.Bank, error) { - dbBank, err := r.store.queries.GetBankByID(ctx, int64(id)) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } - return nil, err - } - return mapDBBankToDomain(&dbBank), nil -} - -func (r *BankRepo) GetAllBanks( - ctx context.Context, - countryID *int, - isActive *bool, - searchTerm *string, - page int, - pageSize int, -) ([]domain.Bank, int64, error) { - // Set default pagination values if not provided - if page < 1 { - page = 1 - } - if pageSize < 1 || pageSize > 100 { - pageSize = 50 - } - offset := (page - 1) * pageSize - - params := dbgen.GetAllBanksParams{ - CountryID: pgtype.Int4{}, - IsActive: pgtype.Int4{}, - SearchTerm: pgtype.Text{}, - Limit: pgtype.Int4{Int32: int32(pageSize), Valid: true}, - Offset: pgtype.Int4{Int32: int32(offset), Valid: true}, - } - - if countryID != nil { - params.CountryID = pgtype.Int4{Int32: int32(*countryID), Valid: true} - } - if isActive != nil { - var activeInt int32 - if *isActive { - activeInt = 1 - } else { - activeInt = 0 - } - params.IsActive = pgtype.Int4{Int32: activeInt, Valid: true} - } - if searchTerm != nil { - params.SearchTerm = pgtype.Text{String: *searchTerm, Valid: true} - } - - // Get paginated results - dbBanks, err := r.store.queries.GetAllBanks(ctx, params) - if err != nil { - return nil, 0, err - } - - // Get total count for pagination - var countCountryID int32 - if params.CountryID.Valid { - countCountryID = params.CountryID.Int32 - } - total, err := r.store.queries.CountBanks(ctx, dbgen.CountBanksParams{ - CountryID: countCountryID, - IsActive: params.IsActive.Int32, - }) - if err != nil { - return nil, 0, err - } - - banks := make([]domain.Bank, len(dbBanks)) - for i, b := range dbBanks { - banks[i] = *mapDBBankToDomain(&b) - } - - return banks, total, nil -} - -func (r *BankRepo) UpdateBank(ctx context.Context, bank *domain.Bank) error { - params := dbgen.UpdateBankParams{ - ID: int64(bank.ID), - Slug: pgtype.Text{String: bank.Slug, Valid: true}, - Swift: pgtype.Text{String: bank.Swift, Valid: true}, - Name: pgtype.Text{String: bank.Name, Valid: true}, - AcctLength: pgtype.Int4{Int32: int32(bank.AcctLength), Valid: true}, - CountryID: pgtype.Int4{Int32: int32(bank.CountryID), Valid: true}, - IsMobilemoney: pgtype.Int4{Int32: int32(bank.IsMobileMoney), Valid: true}, - IsActive: pgtype.Int4{Int32: int32(bank.IsActive), Valid: true}, - IsRtgs: pgtype.Int4{Int32: int32(bank.IsRTGS), Valid: true}, - Active: pgtype.Int4{Int32: int32(bank.Active), Valid: true}, - Is24hrs: pgtype.Int4{Int32: int32(bank.Is24Hrs), Valid: true}, - Currency: pgtype.Text{String: bank.Currency, Valid: true}, - BankLogo: pgtype.Text{String: bank.BankLogo, Valid: true}, - } - updatedBank, err := r.store.queries.UpdateBank(ctx, params) - if err != nil { - return err - } - - // update timestamps in domain struct - bank.UpdatedAt = updatedBank.UpdatedAt.Time - return nil -} - -func (r *BankRepo) DeleteBank(ctx context.Context, id int) error { - return r.store.queries.DeleteBank(ctx, int64(id)) -} - -// Helper to map DB struct to domain -func mapDBBankToDomain(dbBank *dbgen.Bank) *domain.Bank { - return &domain.Bank{ - ID: int(dbBank.ID), - Slug: dbBank.Slug, - Swift: dbBank.Swift, - Name: dbBank.Name, - AcctLength: int(dbBank.AcctLength), - CountryID: int(dbBank.CountryID), - IsMobileMoney: int(dbBank.IsMobilemoney.Int32), - IsActive: int(dbBank.IsActive), - IsRTGS: int(dbBank.IsRtgs), - Active: int(dbBank.Active), - Is24Hrs: int(dbBank.Is24hrs.Int32), - CreatedAt: dbBank.CreatedAt.Time, - UpdatedAt: dbBank.UpdatedAt.Time, - Currency: dbBank.Currency, - BankLogo: dbBank.BankLogo.String, - } -} diff --git a/internal/repository/issue_reporting.go b/internal/repository/issue_reporting.go deleted file mode 100644 index 5c6054c..0000000 --- a/internal/repository/issue_reporting.go +++ /dev/null @@ -1,65 +0,0 @@ -package repository - -import ( - "context" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" -) - -type ReportedIssueRepository interface { - CreateReportedIssue(ctx context.Context, arg dbgen.CreateReportedIssueParams) (dbgen.ReportedIssue, error) - ListReportedIssues(ctx context.Context, limit, offset int32) ([]dbgen.ReportedIssue, error) - ListReportedIssuesByUser(ctx context.Context, userID int64, limit, offset int32) ([]dbgen.ReportedIssue, error) - CountReportedIssues(ctx context.Context) (int64, error) - CountReportedIssuesByUser(ctx context.Context, userID int64) (int64, error) - UpdateReportedIssueStatus(ctx context.Context, id int64, status string) error - DeleteReportedIssue(ctx context.Context, id int64) error -} - -type ReportedIssueRepo struct { - store *Store -} - -func NewReportedIssueRepository(store *Store) ReportedIssueRepository { - return &ReportedIssueRepo{store: store} -} - -func (s *ReportedIssueRepo) CreateReportedIssue(ctx context.Context, arg dbgen.CreateReportedIssueParams) (dbgen.ReportedIssue, error) { - return s.store.queries.CreateReportedIssue(ctx, arg) -} - -func (s *ReportedIssueRepo) ListReportedIssues(ctx context.Context, limit, offset int32) ([]dbgen.ReportedIssue, error) { - params := dbgen.ListReportedIssuesParams{ - Limit: limit, - Offset: offset, - } - return s.store.queries.ListReportedIssues(ctx, params) -} - -func (s *ReportedIssueRepo) ListReportedIssuesByUser(ctx context.Context, userID int64, limit, offset int32) ([]dbgen.ReportedIssue, error) { - params := dbgen.ListReportedIssuesByUserParams{ - UserID: userID, - Limit: limit, - Offset: offset, - } - return s.store.queries.ListReportedIssuesByUser(ctx, params) -} - -func (s *ReportedIssueRepo) CountReportedIssues(ctx context.Context) (int64, error) { - return s.store.queries.CountReportedIssues(ctx) -} - -func (s *ReportedIssueRepo) CountReportedIssuesByUser(ctx context.Context, userID int64) (int64, error) { - return s.store.queries.CountReportedIssuesByUser(ctx, userID) -} - -func (s *ReportedIssueRepo) UpdateReportedIssueStatus(ctx context.Context, id int64, status string) error { - return s.store.queries.UpdateReportedIssueStatus(ctx, dbgen.UpdateReportedIssueStatusParams{ - ID: id, - Status: status, - }) -} - -func (s *ReportedIssueRepo) DeleteReportedIssue(ctx context.Context, id int64) error { - return s.store.queries.DeleteReportedIssue(ctx, id) -} diff --git a/internal/repository/league.go b/internal/repository/league.go deleted file mode 100644 index b21c9d0..0000000 --- a/internal/repository/league.go +++ /dev/null @@ -1,102 +0,0 @@ -package repository - -import ( - "context" - - 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)) -} - -func (s *Store) SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error { - return s.queries.SaveLeagueSettings(ctx, domain.ConvertCreateLeagueSettings(leagueSettings)) -} - -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(), - SportID: filter.SportID.ToPG(), - Limit: pgtype.Int4{ - Int32: int32(filter.Limit.Value), - Valid: filter.Limit.Valid, - }, - Offset: pgtype.Int4{ - Int32: int32(filter.Offset.Value * filter.Limit.Value), - Valid: filter.Offset.Valid, - }, - }) - if err != nil { - return nil, 0, err - } - - total, err := s.queries.GetTotalLeagues(ctx, dbgen.GetTotalLeaguesParams{ - Query: filter.Query.ToPG(), - CountryCode: filter.CountryCode.ToPG(), - SportID: filter.SportID.ToPG(), - IsActive: filter.IsActive.ToPG(), - }) - - return domain.ConvertDBBaseLeagues(l), total, nil -} - -func (s *Store) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error) { - - l, err := s.queries.GetAllLeaguesWithSettings(ctx, dbgen.GetAllLeaguesWithSettingsParams{ - Query: filter.Query.ToPG(), - CompanyID: companyID, - CountryCode: filter.CountryCode.ToPG(), - SportID: filter.SportID.ToPG(), - Limit: pgtype.Int4{ - Int32: int32(filter.Limit.Value), - Valid: filter.Limit.Valid, - }, - Offset: pgtype.Int4{ - Int32: int32(filter.Offset.Value * filter.Limit.Value), - Valid: filter.Offset.Valid, - }, - IsFeatured: filter.IsFeatured.ToPG(), - IsActive: filter.IsActive.ToPG(), - }) - - if err != nil { - return nil, 0, err - } - - total, err := s.queries.GetTotalLeaguesWithSettings(ctx, dbgen.GetTotalLeaguesWithSettingsParams{ - Query: filter.Query.ToPG(), - CompanyID: companyID, - CountryCode: filter.CountryCode.ToPG(), - SportID: filter.SportID.ToPG(), - IsFeatured: filter.IsFeatured.ToPG(), - IsActive: filter.IsActive.ToPG(), - }) - - if err != nil { - return nil, 0, err - } - return domain.ConvertDBLeagueWithSettings(l), total, nil -} - -func (s *Store) CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error) { - return s.queries.CheckLeagueSupport(ctx, dbgen.CheckLeagueSupportParams{ - LeagueID: leagueID, - CompanyID: companyID, - }) -} - -func (s *Store) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error { - return s.queries.UpdateLeague(ctx, domain.ConvertUpdateLeague(league)) -} - -func (s *Store) UpdateGlobalLeagueSettings(ctx context.Context, league domain.UpdateGlobalLeagueSettings) error { - return s.queries.UpdateGlobalLeagueSettings(ctx, domain.ConvertUpdateGlobalLeagueSetting(league)) -} diff --git a/internal/repository/location.go b/internal/repository/location.go deleted file mode 100644 index d2c958b..0000000 --- a/internal/repository/location.go +++ /dev/null @@ -1,30 +0,0 @@ -package repository - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" -) - - -func (s *Store) GetAllBranchLocations (ctx context.Context, query domain.ValidString) ([]domain.BranchLocation, error) { - locations, err := s.queries.GetAllBranchLocations(ctx, pgtype.Text{ - String: query.Value, - Valid: query.Valid, - }) - - if err != nil { - return nil, err - } - - var result []domain.BranchLocation = make([]domain.BranchLocation, 0, len(locations)) - - for _, location := range locations { - result = append(result, domain.BranchLocation{ - Key: location.Key, - Name: location.Value, - }) - } - return result, nil -} \ No newline at end of file diff --git a/internal/repository/market_settings.go b/internal/repository/market_settings.go deleted file mode 100644 index 06ad9ed..0000000 --- a/internal/repository/market_settings.go +++ /dev/null @@ -1,187 +0,0 @@ -package repository - -import ( - "context" - "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/pgtype" -) - - - -func NewMarketSettingStore(s *Store) ports.MarketSettingStore { return s } - -func (s *Store) InsertGlobalMarketSettings(ctx context.Context, setting domain.CreateGlobalMarketSettings) error { - err := s.queries.InsertGlobalMarketSettings(ctx, domain.ConvertCreateGlobalMarketSettings(setting)) - if err != nil { - return err - } - return nil -} - -func (s *Store) InsertCompanyMarketSettings(ctx context.Context, setting domain.CreateCompanyMarketSettings) error { - err := s.queries.InsertCompanyMarketSettings(ctx, domain.ConvertCreateCompanyMarketSettings(setting)) - if err != nil { - return err - } - return nil -} - -func (s *Store) GetAllGlobalMarketSettings(ctx context.Context, filter domain.MarketSettingFilter) ([]domain.MarketSettings, error) { - settings, err := s.queries.GetAllGlobalMarketSettings(ctx, dbgen.GetAllGlobalMarketSettingsParams{ - Offset: pgtype.Int4{ - Int32: int32(filter.Offset.Value * filter.Limit.Value), - Valid: filter.Offset.Valid, - }, - Limit: filter.Limit.ToPG(), - }) - if err != nil { - return nil, err - } - - return domain.ConvertDBGlobalMarketSettingsList(settings), nil -} - -func (s *Store) GetGlobalMarketSettingsByID(ctx context.Context, Id int64) (domain.MarketSettings, error) { - setting, err := s.queries.GetGlobalMarketSettingsByID(ctx, Id) - if err != nil { - return domain.MarketSettings{}, err - } - - return domain.ConvertDBGlobalMarketSettings(setting), nil -} - -func (s *Store) GetAllCompanyMarketSettings(ctx context.Context, filter domain.CompanyMarketSettingFilter) ([]domain.CompanyMarketSettings, error) { - settings, err := s.queries.GetAllCompanyMarketSettings(ctx, dbgen.GetAllCompanyMarketSettingsParams{ - Offset: pgtype.Int4{ - Int32: int32(filter.Offset.Value * filter.Limit.Value), - Valid: filter.Offset.Valid, - }, - Limit: filter.Limit.ToPG(), - CompanyID: filter.CompanyID.ToPG(), - }) - if err != nil { - return nil, err - } - - return domain.ConvertDBCompanyMarketSettingsList(settings), nil -} - -func (s *Store) GetCompanyMarketSettings(ctx context.Context, ID int64) (domain.CompanyMarketSettings, error) { - setting, err := s.queries.GetCompanyMarketSettingsByID(ctx, ID) - if err != nil { - return domain.CompanyMarketSettings{}, err - } - - return domain.ConvertDBCompanyMarketSettings(setting), nil -} - -func (s *Store) GetAllOverrideMarketSettings(ctx context.Context, companyID int64, filter domain.MarketSettingFilter) ([]domain.MarketSettings, error) { - settings, err := s.queries.GetAllOverrideMarketSettings(ctx, dbgen.GetAllOverrideMarketSettingsParams{ - CompanyID: companyID, - Offset: pgtype.Int4{ - Int32: int32(filter.Offset.Value * filter.Limit.Value), - Valid: filter.Offset.Valid, - }, - Limit: filter.Limit.ToPG(), - }) - if err != nil { - return nil, err - } - - return domain.ConvertDBGetAllOverrideMarketSettingsList(settings), nil -} - -func (s *Store) GetOverrideMarketSettingByID(ctx context.Context, companyID int64, marketID int64) (domain.MarketSettings, error) { - setting, err := s.queries.GetOverrideMarketSettingByID(ctx, dbgen.GetOverrideMarketSettingByIDParams{ - CompanyID: companyID, - MarketID: marketID, - }) - - if err != nil { - return domain.MarketSettings{}, nil - } - - return domain.ConvertDBGetOverrideMarketSettingsByID(setting), nil -} - -func (s *Store) DeleteAllCompanyMarketSettings(ctx context.Context, companyID int64) error { - err := s.queries.DeleteAllMarketSettingsForCompany(ctx, companyID) - if err != nil { - return err - } - return nil -} - -func (s *Store) DeleteCompanyMarketSettings(ctx context.Context, companyID int64, marketID int64) error { - err := s.queries.DeleteCompanyMarketSettings(ctx, dbgen.DeleteCompanyMarketSettingsParams{ - MarketID: marketID, - CompanyID: companyID, - }) - if err != nil { - return err - } - return nil -} - -func (s *Store) EnsureAllMarketSettingsExist(ctx context.Context) error { - dbMarketSettings, err := s.GetAllGlobalMarketSettings(ctx, domain.MarketSettingFilter{}) - if err != nil { - return fmt.Errorf("failed to fetch global market settings: %w", err) - } - - dbCompanies, err := s.GetAllCompanies(ctx, domain.CompanyFilter{}) - if err != nil { - return fmt.Errorf("failed to fetch companies: %w", err) - } - existing := map[int64]struct{}{} - for _, s := range dbMarketSettings { - existing[s.MarketID] = struct{}{} - } - - for id, defaultIsActive := range domain.SupportedMarkets { - if _, found := existing[id]; !found { - name, err := domain.GetMarketName(id) - if err != nil { - return err - } - err = s.InsertGlobalMarketSettings(ctx, domain.CreateGlobalMarketSettings{ - MarketID: id, - MarketName: name, - IsActive: defaultIsActive, - }) - if err != nil{ - return fmt.Errorf("failed to insert market %d (%s): %w", id, name, err) - } - } - - // This is to auto disabled markets that haven't been tested yet - for _, company := range dbCompanies { - if company.Slug == "fortunebets" { - continue - } - - if _, found := domain.DisabledMarkets[id]; !found { - continue - } - name, err := domain.GetMarketName(id) - err = s.InsertCompanyMarketSettings(ctx, domain.CreateCompanyMarketSettings{ - CompanyID: company.ID, - MarketID: id, - MarketName: name, - IsActive: domain.ValidBool{ - Value: false, - Valid: true, - }, - }) - if err != nil { - return fmt.Errorf("failed to insert company market %d (%s): %w", id, name, err) - } - } - } - return nil - -} \ No newline at end of file diff --git a/internal/repository/notification.go b/internal/repository/notification.go index 62ceb3c..78ebf0d 100644 --- a/internal/repository/notification.go +++ b/internal/repository/notification.go @@ -1,308 +1,109 @@ package repository -import ( - "context" - "encoding/json" - "fmt" +// import ( +// "context" +// "encoding/json" +// "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/pgtype" -) +// dbgen "Yimaru-Backend/gen/db" +// "Yimaru-Backend/internal/domain" +// "Yimaru-Backend/internal/ports" +// "github.com/jackc/pgx/v5/pgtype" +// ) -func NewNotificationStore(s *Store) ports.NotificationStore { return s } +// func NewNotificationStore(s *Store) ports.NotificationStore { return s } -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) - errorSeverity.Valid = true - } +// 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) +// errorSeverity.Valid = true +// } - var deliveryChannel pgtype.Text - if notification.DeliveryChannel != "" { - deliveryChannel.String = string(notification.DeliveryChannel) - deliveryChannel.Valid = true - } +// var deliveryChannel pgtype.Text +// if notification.DeliveryChannel != "" { +// deliveryChannel.String = string(notification.DeliveryChannel) +// deliveryChannel.Valid = true +// } - var priority pgtype.Int4 - if notification.Priority != 0 { - priority.Int32 = int32(notification.Priority) - priority.Valid = true - } +// var priority pgtype.Int4 +// if notification.Priority != 0 { +// priority.Int32 = int32(notification.Priority) +// priority.Valid = true +// } - params := dbgen.CreateNotificationParams{ - ID: notification.ID, - RecipientID: notification.RecipientID, - Type: string(notification.Type), - Level: string(notification.Level), - ErrorSeverity: errorSeverity, - Reciever: string(notification.Reciever), - IsRead: notification.IsRead, - DeliveryStatus: string(notification.DeliveryStatus), - DeliveryChannel: deliveryChannel, - Payload: marshalPayload(notification.Payload), - Priority: priority, - Timestamp: pgtype.Timestamptz{Time: notification.Timestamp, Valid: true}, - Expires: pgtype.Timestamptz{Time: notification.Expires, Valid: true}, - Img: pgtype.Text{String: notification.Image, Valid: notification.Image != ""}, - Metadata: notification.Metadata, - } +// params := dbgen.CreateNotificationParams{ +// ID: notification.ID, +// RecipientID: notification.RecipientID, +// Type: string(notification.Type), +// Level: string(notification.Level), +// ErrorSeverity: errorSeverity, +// Reciever: string(notification.Reciever), +// IsRead: notification.IsRead, +// DeliveryStatus: string(notification.DeliveryStatus), +// DeliveryChannel: deliveryChannel, +// Payload: marshalPayload(notification.Payload), +// Priority: priority, +// Timestamp: pgtype.Timestamptz{Time: notification.Timestamp, Valid: true}, +// Expires: pgtype.Timestamptz{Time: notification.Expires, Valid: true}, +// Img: pgtype.Text{String: notification.Image, Valid: notification.Image != ""}, +// Metadata: notification.Metadata, +// } - dbNotification, err := r.queries.CreateNotification(ctx, params) - if err != nil { - return nil, err - } +// dbNotification, err := r.queries.CreateNotification(ctx, params) +// if err != nil { +// return nil, err +// } - return r.mapDBToDomain(&dbNotification), nil -} +// return r.mapDBToDomain(&dbNotification), nil +// } -func (r *Store) UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error) { - params := dbgen.UpdateNotificationStatusParams{ - ID: id, - DeliveryStatus: status, - IsRead: isRead, - Metadata: metadata, - } +// func (r *Store) UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error) { +// params := dbgen.UpdateNotificationStatusParams{ +// ID: id, +// DeliveryStatus: status, +// IsRead: isRead, +// Metadata: metadata, +// } - dbNotification, err := r.queries.UpdateNotificationStatus(ctx, params) - if err != nil { - return nil, err - } +// dbNotification, err := r.queries.UpdateNotificationStatus(ctx, params) +// if err != nil { +// return nil, err +// } - return r.mapDBToDomain(&dbNotification), nil -} +// return r.mapDBToDomain(&dbNotification), nil +// } -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), - } +// 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.queries.GetUserNotifications(ctx, params) - if err != nil { - return nil, 0, err - } +// dbNotifications, err := r.queries.GetUserNotifications(ctx, params) +// if err != nil { +// return nil, 0, err +// } - total, err := r.queries.GetUserNotificationCount(ctx, recipientID) +// total, err := r.queries.GetUserNotificationCount(ctx, recipientID) - if err != nil { - return nil, 0, err - } +// if err != nil { +// return nil, 0, err +// } - var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications)) - for _, dbNotif := range dbNotifications { - domainNotif := r.mapDBToDomain(&dbNotif) - result = append(result, *domainNotif) - } +// var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications)) +// for _, dbNotif := range dbNotifications { +// domainNotif := r.mapDBToDomain(&dbNotif) +// result = append(result, *domainNotif) +// } - return result, total, nil -} +// return result, total, nil +// } -func (r *Store) 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.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{ - Limit: int32(limit), - Offset: int32(offset), - }) - if err != nil { - return nil, err - } - - var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications)) - for _, dbNotif := range dbNotifications { - domainNotif := r.mapDBToDomain(&dbNotif) - result = append(result, *domainNotif) - } - return result, nil -} - -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 - } - - var result []domain.Notification - for _, dbNotif := range dbNotifications { - domainNotif := r.mapDBToDomain(&dbNotif) - result = append(result, *domainNotif) - } - - return result, nil -} - -func (r *Store) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) { - return r.queries.ListRecipientIDsByReceiver(ctx, string(receiver)) -} - -func (s *Store) DeleteOldNotifications(ctx context.Context) error { - return s.queries.DeleteOldNotifications(ctx) -} - -func (r *Store) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notification { - var errorSeverity domain.NotificationErrorSeverity - if dbNotif.ErrorSeverity.Valid { - errorSeverity = domain.NotificationErrorSeverity(dbNotif.ErrorSeverity.String) - - } else { - errorSeverity = "" - } - - var deliveryChannel domain.DeliveryChannel - if dbNotif.DeliveryChannel.Valid { - deliveryChannel = domain.DeliveryChannel(dbNotif.DeliveryChannel.String) - } else { - deliveryChannel = "" - } - - var priority int - if dbNotif.Priority.Valid { - priority = int(dbNotif.Priority.Int32) - } - - payload, err := unmarshalPayload(dbNotif.Payload) - if err != nil { - payload = domain.NotificationPayload{} - } - - return &domain.Notification{ - ID: dbNotif.ID, - RecipientID: dbNotif.RecipientID, - Type: domain.NotificationType(dbNotif.Type), - Level: domain.NotificationLevel(dbNotif.Level), - ErrorSeverity: errorSeverity, - Reciever: domain.NotificationRecieverSide(dbNotif.Reciever), - IsRead: dbNotif.IsRead, - DeliveryStatus: domain.NotificationDeliveryStatus(dbNotif.DeliveryStatus), - DeliveryChannel: deliveryChannel, - Payload: payload, - Priority: priority, - Timestamp: dbNotif.Timestamp.Time, - Expires: dbNotif.Expires.Time, - Image: dbNotif.Img.String, - Metadata: dbNotif.Metadata, - } -} - -func marshalPayload(payload domain.NotificationPayload) []byte { - data, _ := json.Marshal(payload) - return data -} - -func unmarshalPayload(data []byte) (domain.NotificationPayload, error) { - var payload domain.NotificationPayload - if err := json.Unmarshal(data, &payload); err != nil { - return domain.NotificationPayload{}, err - } - return payload, nil -} - -func (r *Store) CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) { - return r.queries.CountUnreadNotifications(ctx, recipient_id) -} - -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) - } - - // var total, read, unread int64 - for _, row := range rows { - total += row.Total - read += row.Read - unread += row.Unread - } - - return total, read, unread, nil -} - -func (s *Store) GetMostActiveNotificationRecipients(ctx context.Context, filter domain.ReportFilter, limit int) ([]domain.ActiveNotificationRecipient, error) { - query := `SELECT - n.recipient_id, - u.first_name || ' ' || u.last_name as recipient_name, - COUNT(*) as notification_count, - MAX(n.timestamp) as last_notification_time - FROM notifications n - JOIN users u ON n.recipient_id = u.id - WHERE n.timestamp BETWEEN $1 AND $2 - GROUP BY n.recipient_id, u.first_name, u.last_name - ORDER BY notification_count DESC - LIMIT $3` - - var recipients []domain.ActiveNotificationRecipient - rows, err := s.conn.Query(ctx, query, filter.StartTime.Value, filter.EndTime.Value, limit) - if err != nil { - return nil, fmt.Errorf("failed to get active notification recipients: %w", err) - } - defer rows.Close() - - for rows.Next() { - var r domain.ActiveNotificationRecipient - if err := rows.Scan(&r.RecipientID, &r.RecipientName, &r.NotificationCount, &r.LastNotificationTime); err != nil { - return nil, err - } - recipients = append(recipients, r) - } - - return recipients, nil -} - -// GetNotificationDeliveryStats -func (s *Store) GetNotificationDeliveryStats(ctx context.Context, filter domain.ReportFilter) (domain.NotificationDeliveryStats, error) { - query := `SELECT - COUNT(*) as total_sent, - COUNT(CASE WHEN delivery_status = 'failed' THEN 1 END) as failed_deliveries, - (COUNT(CASE WHEN delivery_status = 'sent' THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0)) as success_rate, - MODE() WITHIN GROUP (ORDER BY delivery_channel) as most_used_channel - FROM notifications - WHERE timestamp BETWEEN $1 AND $2` - - var stats domain.NotificationDeliveryStats - row := s.conn.QueryRow(ctx, query, filter.StartTime.Value, filter.EndTime.Value) - err := row.Scan(&stats.TotalSent, &stats.FailedDeliveries, &stats.SuccessRate, &stats.MostUsedChannel) - if err != nil { - return domain.NotificationDeliveryStats{}, fmt.Errorf("failed to get notification delivery stats: %w", err) - } - - return stats, nil -} - -// GetNotificationCountsByType -func (s *Store) GetNotificationCountsByType(ctx context.Context, filter domain.ReportFilter) (map[string]domain.NotificationTypeCount, error) { - query := `SELECT - type, - COUNT(*) as total, - COUNT(CASE WHEN is_read = true THEN 1 END) as read, - COUNT(CASE WHEN is_read = false THEN 1 END) as unread - FROM notifications - WHERE timestamp BETWEEN $1 AND $2 - GROUP BY type` - - counts := make(map[string]domain.NotificationTypeCount) - rows, err := s.conn.Query(ctx, query, filter.StartTime.Value, filter.EndTime.Value) - if err != nil { - return nil, fmt.Errorf("failed to get notification counts by type: %w", err) - } - defer rows.Close() - - for rows.Next() { - var nt domain.NotificationTypeCount - var typ string - if err := rows.Scan(&typ, &nt.Total, &nt.Read, &nt.Unread); err != nil { - return nil, err - } - counts[typ] = nt - } - - return counts, nil -} - -// func (s *Store) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) { -// dbNotifications, err := s.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{ +// dbNotifications, err := r.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{ // Limit: int32(limit), // Offset: int32(offset), // }) @@ -310,47 +111,246 @@ func (s *Store) GetNotificationCountsByType(ctx context.Context, filter domain.R // return nil, err // } -// result := make([]domain.Notification, 0, len(dbNotifications)) +// var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications)) // for _, dbNotif := range dbNotifications { -// // You may want to move this mapping logic to a shared function if not already present -// var errorSeverity *domain.NotificationErrorSeverity -// if dbNotif.ErrorSeverity.Valid { -// s := domain.NotificationErrorSeverity(dbNotif.ErrorSeverity.String) -// errorSeverity = &s -// } - -// var deliveryChannel domain.DeliveryChannel -// if dbNotif.DeliveryChannel.Valid { -// deliveryChannel = domain.DeliveryChannel(dbNotif.DeliveryChannel.String) -// } else { -// deliveryChannel = "" -// } - -// var priority int -// if dbNotif.Priority.Valid { -// priority = int(dbNotif.Priority.Int32) -// } - -// payload, err := unmarshalPayload(dbNotif.Payload) -// if err != nil { -// payload = domain.NotificationPayload{} -// } - -// result = append(result, domain.Notification{ -// ID: dbNotif.ID, -// RecipientID: dbNotif.RecipientID, -// Type: domain.NotificationType(dbNotif.Type), -// Level: domain.NotificationLevel(dbNotif.Level), -// ErrorSeverity: errorSeverity, -// Reciever: domain.NotificationRecieverSide(dbNotif.Reciever), -// IsRead: dbNotif.IsRead, -// DeliveryStatus: domain.NotificationDeliveryStatus(dbNotif.DeliveryStatus), -// DeliveryChannel: deliveryChannel, -// Payload: payload, -// Priority: priority, -// Timestamp: dbNotif.Timestamp.Time, -// Metadata: dbNotif.Metadata, -// }) +// domainNotif := r.mapDBToDomain(&dbNotif) +// result = append(result, *domainNotif) // } // return result, nil // } + +// 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 +// } + +// var result []domain.Notification +// for _, dbNotif := range dbNotifications { +// domainNotif := r.mapDBToDomain(&dbNotif) +// result = append(result, *domainNotif) +// } + +// return result, nil +// } + +// func (r *Store) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) { +// return r.queries.ListRecipientIDsByReceiver(ctx, string(receiver)) +// } + +// func (s *Store) DeleteOldNotifications(ctx context.Context) error { +// return s.queries.DeleteOldNotifications(ctx) +// } + +// func (r *Store) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notification { +// var errorSeverity domain.NotificationErrorSeverity +// if dbNotif.ErrorSeverity.Valid { +// errorSeverity = domain.NotificationErrorSeverity(dbNotif.ErrorSeverity.String) + +// } else { +// errorSeverity = "" +// } + +// var deliveryChannel domain.DeliveryChannel +// if dbNotif.DeliveryChannel.Valid { +// deliveryChannel = domain.DeliveryChannel(dbNotif.DeliveryChannel.String) +// } else { +// deliveryChannel = "" +// } + +// var priority int +// if dbNotif.Priority.Valid { +// priority = int(dbNotif.Priority.Int32) +// } + +// payload, err := unmarshalPayload(dbNotif.Payload) +// if err != nil { +// payload = domain.NotificationPayload{} +// } + +// return &domain.Notification{ +// ID: dbNotif.ID, +// RecipientID: dbNotif.RecipientID, +// Type: domain.NotificationType(dbNotif.Type), +// Level: domain.NotificationLevel(dbNotif.Level), +// ErrorSeverity: errorSeverity, +// Reciever: domain.NotificationRecieverSide(dbNotif.Reciever), +// IsRead: dbNotif.IsRead, +// DeliveryStatus: domain.NotificationDeliveryStatus(dbNotif.DeliveryStatus), +// DeliveryChannel: deliveryChannel, +// Payload: payload, +// Priority: priority, +// Timestamp: dbNotif.Timestamp.Time, +// Expires: dbNotif.Expires.Time, +// Image: dbNotif.Img.String, +// Metadata: dbNotif.Metadata, +// } +// } + +// func marshalPayload(payload domain.NotificationPayload) []byte { +// data, _ := json.Marshal(payload) +// return data +// } + +// func unmarshalPayload(data []byte) (domain.NotificationPayload, error) { +// var payload domain.NotificationPayload +// if err := json.Unmarshal(data, &payload); err != nil { +// return domain.NotificationPayload{}, err +// } +// return payload, nil +// } + +// func (r *Store) CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) { +// return r.queries.CountUnreadNotifications(ctx, recipient_id) +// } + +// 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) +// } + +// // var total, read, unread int64 +// for _, row := range rows { +// total += row.Total +// read += row.Read +// unread += row.Unread +// } + +// return total, read, unread, nil +// } + +// func (s *Store) GetMostActiveNotificationRecipients(ctx context.Context, filter domain.ReportFilter, limit int) ([]domain.ActiveNotificationRecipient, error) { +// query := `SELECT +// n.recipient_id, +// u.first_name || ' ' || u.last_name as recipient_name, +// COUNT(*) as notification_count, +// MAX(n.timestamp) as last_notification_time +// FROM notifications n +// JOIN users u ON n.recipient_id = u.id +// WHERE n.timestamp BETWEEN $1 AND $2 +// GROUP BY n.recipient_id, u.first_name, u.last_name +// ORDER BY notification_count DESC +// LIMIT $3` + +// var recipients []domain.ActiveNotificationRecipient +// rows, err := s.conn.Query(ctx, query, filter.StartTime.Value, filter.EndTime.Value, limit) +// if err != nil { +// return nil, fmt.Errorf("failed to get active notification recipients: %w", err) +// } +// defer rows.Close() + +// for rows.Next() { +// var r domain.ActiveNotificationRecipient +// if err := rows.Scan(&r.RecipientID, &r.RecipientName, &r.NotificationCount, &r.LastNotificationTime); err != nil { +// return nil, err +// } +// recipients = append(recipients, r) +// } + +// return recipients, nil +// } + +// // GetNotificationDeliveryStats +// func (s *Store) GetNotificationDeliveryStats(ctx context.Context, filter domain.ReportFilter) (domain.NotificationDeliveryStats, error) { +// query := `SELECT +// COUNT(*) as total_sent, +// COUNT(CASE WHEN delivery_status = 'failed' THEN 1 END) as failed_deliveries, +// (COUNT(CASE WHEN delivery_status = 'sent' THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0)) as success_rate, +// MODE() WITHIN GROUP (ORDER BY delivery_channel) as most_used_channel +// FROM notifications +// WHERE timestamp BETWEEN $1 AND $2` + +// var stats domain.NotificationDeliveryStats +// row := s.conn.QueryRow(ctx, query, filter.StartTime.Value, filter.EndTime.Value) +// err := row.Scan(&stats.TotalSent, &stats.FailedDeliveries, &stats.SuccessRate, &stats.MostUsedChannel) +// if err != nil { +// return domain.NotificationDeliveryStats{}, fmt.Errorf("failed to get notification delivery stats: %w", err) +// } + +// return stats, nil +// } + +// // GetNotificationCountsByType +// func (s *Store) GetNotificationCountsByType(ctx context.Context, filter domain.ReportFilter) (map[string]domain.NotificationTypeCount, error) { +// query := `SELECT +// type, +// COUNT(*) as total, +// COUNT(CASE WHEN is_read = true THEN 1 END) as read, +// COUNT(CASE WHEN is_read = false THEN 1 END) as unread +// FROM notifications +// WHERE timestamp BETWEEN $1 AND $2 +// GROUP BY type` + +// counts := make(map[string]domain.NotificationTypeCount) +// rows, err := s.conn.Query(ctx, query, filter.StartTime.Value, filter.EndTime.Value) +// if err != nil { +// return nil, fmt.Errorf("failed to get notification counts by type: %w", err) +// } +// defer rows.Close() + +// for rows.Next() { +// var nt domain.NotificationTypeCount +// var typ string +// if err := rows.Scan(&typ, &nt.Total, &nt.Read, &nt.Unread); err != nil { +// return nil, err +// } +// counts[typ] = nt +// } + +// return counts, 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), +// // Offset: int32(offset), +// // }) +// // if err != nil { +// // return nil, err +// // } + +// // result := make([]domain.Notification, 0, len(dbNotifications)) +// // for _, dbNotif := range dbNotifications { +// // // You may want to move this mapping logic to a shared function if not already present +// // var errorSeverity *domain.NotificationErrorSeverity +// // if dbNotif.ErrorSeverity.Valid { +// // s := domain.NotificationErrorSeverity(dbNotif.ErrorSeverity.String) +// // errorSeverity = &s +// // } + +// // var deliveryChannel domain.DeliveryChannel +// // if dbNotif.DeliveryChannel.Valid { +// // deliveryChannel = domain.DeliveryChannel(dbNotif.DeliveryChannel.String) +// // } else { +// // deliveryChannel = "" +// // } + +// // var priority int +// // if dbNotif.Priority.Valid { +// // priority = int(dbNotif.Priority.Int32) +// // } + +// // payload, err := unmarshalPayload(dbNotif.Payload) +// // if err != nil { +// // payload = domain.NotificationPayload{} +// // } + +// // result = append(result, domain.Notification{ +// // ID: dbNotif.ID, +// // RecipientID: dbNotif.RecipientID, +// // Type: domain.NotificationType(dbNotif.Type), +// // Level: domain.NotificationLevel(dbNotif.Level), +// // ErrorSeverity: errorSeverity, +// // Reciever: domain.NotificationRecieverSide(dbNotif.Reciever), +// // IsRead: dbNotif.IsRead, +// // DeliveryStatus: domain.NotificationDeliveryStatus(dbNotif.DeliveryStatus), +// // DeliveryChannel: deliveryChannel, +// // Payload: payload, +// // Priority: priority, +// // Timestamp: dbNotif.Timestamp.Time, +// // Metadata: dbNotif.Metadata, +// // }) +// // } +// // return result, nil +// // } diff --git a/internal/repository/odd_history.go b/internal/repository/odd_history.go deleted file mode 100644 index 115c572..0000000 --- a/internal/repository/odd_history.go +++ /dev/null @@ -1,53 +0,0 @@ -package repository - -import ( - "context" - "fmt" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Store) InsertOddHistory(ctx context.Context, odd domain.CreateOddHistory) (domain.OddHistory, error) { - dbOddHistory, err := s.queries.InsertOddHistory(ctx, domain.ConvertCreateOddHistory(odd)) - - if err != nil { - return domain.OddHistory{}, fmt.Errorf("InsertOddHistory failed: %w", err) - } - - return domain.ConvertDBOddHistory(dbOddHistory), nil -} - -func (s *Store) GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) { - dbOddHistories, err := s.queries.GetAllOddHistory(ctx, dbgen.GetAllOddHistoryParams{ - OddID: filter.OddID.ToPG(), - MarketID: filter.MarketID.ToPG(), - RawOddID: filter.RawOddID.ToPG(), - EventID: filter.EventID.ToPG(), - CreatedAfter: filter.CreatedAfter.ToPG(), - CreatedBefore: filter.CreatedBefore.ToPG(), - }) - - if err != nil { - return nil, fmt.Errorf("GetAllOddHistory failed: %w", err) - } - - return domain.ConvertOddHistories(dbOddHistories), nil -} - -func (s *Store) GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) { - dbOddHistories, err := s.queries.GetInitialOddPerDay(ctx, dbgen.GetInitialOddPerDayParams{ - OddID: filter.OddID.ToPG(), - MarketID: filter.MarketID.ToPG(), - RawOddID: filter.RawOddID.ToPG(), - EventID: filter.EventID.ToPG(), - CreatedAfter: filter.CreatedAfter.ToPG(), - CreatedBefore: filter.CreatedBefore.ToPG(), - DateTrunc: "day", - }) - - if err != nil { - return nil, fmt.Errorf("GetInitialOddPerDay failed: %w", err) - } - return domain.ConvertOddHistories(dbOddHistories), nil -} diff --git a/internal/repository/odds.go b/internal/repository/odds.go deleted file mode 100644 index ec5e13c..0000000 --- a/internal/repository/odds.go +++ /dev/null @@ -1,343 +0,0 @@ -package repository - -import ( - "context" - "encoding/json" - - "os" - "time" - - 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 - } - - params, err := domain.ConvertCreateOddMarket(m) - - if err != nil { - return err - } - - err = s.queries.InsertOddsMarket(ctx, params) - if err != nil { - _ = writeFailedMarketLog(m, err) - return err - } - return nil -} - -func writeFailedMarketLog(m domain.CreateOddMarket, err error) error { - logDir := "logs" - logFile := logDir + "/failed_markets.log" - - if mkErr := os.MkdirAll(logDir, 0755); mkErr != nil { - return mkErr - } - - f, fileErr := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if fileErr != nil { - return fileErr - } - defer f.Close() - - entry := struct { - Time string `json:"time"` - Error string `json:"error"` - Record domain.CreateOddMarket `json:"record"` - }{ - Time: time.Now().Format(time.RFC3339), - Error: err.Error(), - Record: m, - } - - jsonData, _ := json.MarshalIndent(entry, "", " ") - _, writeErr := f.WriteString(string(jsonData) + "\n\n") - return writeErr -} - -func (s *Store) GetAllOdds(ctx context.Context, filter domain.OddMarketFilter) ([]domain.OddMarket, error) { - rows, err := s.queries.GetAllOdds(ctx, dbgen.GetAllOddsParams{ - Offset: filter.Offset.ToPG(), - Limit: filter.Limit.ToPG(), - }) - if err != nil { - return nil, err - } - - domainOdds, err := domain.ConvertDBOddMarkets(rows) - - if err != nil { - return nil, err - } - - return domainOdds, nil -} - -func (s *Store) GetAllOddsWithSettings(ctx context.Context, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) { - odds, err := s.queries.GetAllOddsWithSettings(ctx, dbgen.GetAllOddsWithSettingsParams{ - CompanyID: companyID, - Offset: filter.Offset.ToPG(), - Limit: filter.Limit.ToPG(), - }) - if err != nil { - return nil, err - } - - // domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds) - // if err != nil { - // return nil, err - // } - - result := make([]domain.OddMarketWithSettings, len(odds)) - for i, o := range odds { - var rawOdds []json.RawMessage - if len(o.RawOdds) > 0 { - if err := json.Unmarshal(o.RawOdds, &rawOdds); err != nil { - return nil, err - } - } else { - rawOdds = []json.RawMessage{} // explicit empty slice - } - - result[i] = domain.OddMarketWithSettings{ - ID: o.ID, - EventID: o.EventID, - MarketType: o.MarketType, - MarketName: o.MarketName, - MarketCategory: o.MarketCategory, - MarketID: o.MarketID, - RawOdds: rawOdds, - FetchedAt: o.FetchedAt.Time, - ExpiresAt: o.ExpiresAt.Time, - IsActive: o.IsActive && o.IsMarketActive, - } - } - - return result, nil -} - -func (s *Store) GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error) { - odd, err := s.queries.GetOddByID(ctx, id) - if err != nil { - return domain.OddMarket{}, err - } - - convertedOdd, err := domain.ConvertDBOddMarket(odd) - - if err != nil { - return domain.OddMarket{}, err - } - return convertedOdd, nil -} - -func (s *Store) GetOddsByMarketID(ctx context.Context, marketID int64, eventID int64) (domain.OddMarket, error) { - - odds, err := s.queries.GetOddsByMarketID(ctx, dbgen.GetOddsByMarketIDParams{ - MarketID: marketID, - EventID: eventID, - }) - if err != nil { - return domain.OddMarket{}, err - } - - convertedOdd, err := domain.ConvertDBOddMarket(odds) - - if err != nil { - return domain.OddMarket{}, err - } - return convertedOdd, nil -} - -func (s *Store) GetOddsWithSettingsByMarketID(ctx context.Context, marketID int64, eventID int64, companyID int64) (domain.OddMarketWithSettings, error) { - - odds, err := s.queries.GetOddsWithSettingsByMarketID(ctx, dbgen.GetOddsWithSettingsByMarketIDParams{ - MarketID: marketID, - EventID: eventID, - CompanyID: companyID, - }) - if err != nil { - return domain.OddMarketWithSettings{}, err - } - - // convertedOdd, err := domain.ConvertDBOddMarketWithSetting(odds) - - // if err != nil { - // return domain.OddMarketWithSettings{}, err - // } - - var rawOdds []json.RawMessage - if len(odds.RawOdds) > 0 { - if err := json.Unmarshal(odds.RawOdds, &rawOdds); err != nil { - return domain.OddMarketWithSettings{}, err - } - } else { - rawOdds = []json.RawMessage{} // explicit empty slice - } - - converted := domain.OddMarketWithSettings{ - ID: odds.ID, - EventID: odds.EventID, - MarketType: odds.MarketType, - MarketName: odds.MarketName, - MarketCategory: odds.MarketCategory, - MarketID: odds.MarketID, - NumberOfOutcomes: odds.NumberOfOutcomes, - RawOdds: rawOdds, - FetchedAt: odds.FetchedAt.Time, - ExpiresAt: odds.ExpiresAt.Time, - IsActive: odds.IsActive && odds.IsMarketActive, - } - return converted, nil -} - -func (s *Store) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error) { - - odds, err := s.queries.GetOddsWithSettingsByID(ctx, dbgen.GetOddsWithSettingsByIDParams{ - ID: ID, - CompanyID: companyID, - }) - - if err != nil { - return domain.OddMarketWithSettings{}, err - } - - // convertedOdd, err := domain.ConvertDBOddMarketWithSetting(odds) - - // if err != nil { - // return domain.OddMarketWithSettings{}, err - // } - - var rawOdds []json.RawMessage - if len(odds.RawOdds) > 0 { - if err := json.Unmarshal(odds.RawOdds, &rawOdds); err != nil { - return domain.OddMarketWithSettings{}, err - } - } else { - rawOdds = []json.RawMessage{} // explicit empty slice - } - - converted := domain.OddMarketWithSettings{ - ID: odds.ID, - EventID: odds.EventID, - MarketType: odds.MarketType, - MarketName: odds.MarketName, - MarketCategory: odds.MarketCategory, - MarketID: odds.MarketID, - NumberOfOutcomes: odds.NumberOfOutcomes, - RawOdds: rawOdds, - FetchedAt: odds.FetchedAt.Time, - ExpiresAt: odds.ExpiresAt.Time, - IsActive: odds.IsActive && odds.IsMarketActive, - } - - return converted, nil -} - -func (s *Store) GetOddsByEventID(ctx context.Context, eventID int64, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) { - odds, err := s.queries.GetOddsByEventID(ctx, dbgen.GetOddsByEventIDParams{ - EventID: eventID, - Status: filter.Status.ToPG(), - IsLive: filter.IsLive.ToPG(), - Limit: filter.Limit.ToPG(), - Offset: filter.Offset.ToPG(), - Source: pgtype.Text{}, - }) - if err != nil { - return nil, err - } - // Map the results to domain.Odd - domainOdds, err := domain.ConvertDBOddMarkets(odds) - if err != nil { - return nil, err - } - - return domainOdds, nil -} - -func (s *Store) GetOddsWithSettingsByEventID(ctx context.Context, eventID int64, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) { - - odds, err := s.queries.GetOddsWithSettingsByEventID(ctx, dbgen.GetOddsWithSettingsByEventIDParams{ - EventID: eventID, - CompanyID: companyID, - Offset: filter.Offset.ToPG(), - Limit: filter.Limit.ToPG(), - }) - if err != nil { - return nil, err - } - - // Map the results to domain.Odd - // domainOdds, err := domain.ConvertDBOddMarketWithSettings(odds) - // if err != nil { - // return nil, err - // } - - result := make([]domain.OddMarketWithSettings, len(odds)) - for i, o := range odds { - var rawOdds []json.RawMessage - if len(o.RawOdds) > 0 { - if err := json.Unmarshal(o.RawOdds, &rawOdds); err != nil { - return nil, err - } - } else { - rawOdds = []json.RawMessage{} // explicit empty slice - } - - result[i] = domain.OddMarketWithSettings{ - ID: o.ID, - EventID: o.EventID, - MarketType: o.MarketType, - MarketName: o.MarketName, - MarketCategory: o.MarketCategory, - MarketID: o.MarketID, - NumberOfOutcomes: o.NumberOfOutcomes, - RawOdds: rawOdds, - FetchedAt: o.FetchedAt.Time, - ExpiresAt: o.ExpiresAt.Time, - IsActive: o.IsActive && o.IsMarketActive, - } - } - - return result, nil -} - -func (s *Store) DeleteOddsForEvent(ctx context.Context, eventID int64) error { - return s.queries.DeleteOddsForEvent(ctx, eventID) -} - -func (s *Store) SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error { - - res, err := domain.ConvertCreateOddMarketSetting(odd) - - if err != nil { - return nil - } - return s.queries.SaveOddSettings(ctx, res) -} - -func (s *Store) UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) error { - return s.queries.UpdateGlobalOddsSetting(ctx, dbgen.UpdateGlobalOddsSettingParams{ - ID: odd.OddMarketID, - DefaultIsActive: odd.IsActive.ToPG(), - }) -} - -func (s *Store) DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error { - return s.queries.DeleteAllCompanyOddsSetting(ctx, companyID) -} - -func (s *Store) DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, companyID int64, oddMarketID int64) error { - return s.queries.DeleteCompanyOddsSettingByOddMarketID(ctx, dbgen.DeleteCompanyOddsSettingByOddMarketIDParams{ - CompanyID: companyID, - OddsMarketID: oddMarketID, - }) -} diff --git a/internal/repository/old_report.go b/internal/repository/old_report.go deleted file mode 100644 index 1fb7af4..0000000 --- a/internal/repository/old_report.go +++ /dev/null @@ -1,240 +0,0 @@ -package repository - -import ( - "context" - "fmt" - "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" -) - - -// 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) - - // 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) -// } - -// 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 -} - -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 (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 (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) -} - -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) -} - -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) -} - -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) -} - -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) -} - -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 (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 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 (s *Store) GetCompanyWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetCompanyWiseReportRow, error) { -// params := dbgen.GetCompanyWiseReportParams{ -// From: ToPgTimestamp(from), -// To: ToPgTimestamp(to), -// } -// return s.queries.GetCompanyWiseReport(ctx, params) -// } - -// func (s *Store) GetBranchWiseReport(ctx context.Context, from, to time.Time) ([]dbgen.GetBranchWiseReportRow, error) { -// params := dbgen.GetBranchWiseReportParams{ -// From: ToPgTimestamp(from), -// To: ToPgTimestamp(to), -// } -// return s.queries.GetBranchWiseReport(ctx, params) -// } diff --git a/internal/repository/otp.go b/internal/repository/otp.go index 93ceefa..9f8cb42 100644 --- a/internal/repository/otp.go +++ b/internal/repository/otp.go @@ -5,9 +5,9 @@ import ( "database/sql" "fmt" - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" + dbgen "Yimaru-Backend/gen/db" + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/ports" "github.com/jackc/pgx/v5/pgtype" ) diff --git a/internal/repository/raffle.go b/internal/repository/raffle.go deleted file mode 100644 index 01e495e..0000000 --- a/internal/repository/raffle.go +++ /dev/null @@ -1,156 +0,0 @@ -package repository - -import ( - "context" - - 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 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, domain.ConvertCreateRaffle(raffle)) - if err != nil { - return domain.Raffle{}, err - } - - return domain.ConvertRaffleOutcome(raffleRes), nil -} - -func (s *Store) DeleteRaffle(ctx context.Context, raffleID int32) (domain.Raffle, error) { - raffleRes, err := s.queries.DeleteRaffle(ctx, raffleID) - if err != nil { - return domain.Raffle{}, err - } - - return domain.ConvertRaffleOutcome(raffleRes), nil -} - -func (s *Store) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]dbgen.Raffle, error) { - raffles, err := s.queries.GetRafflesOfCompany(ctx, companyID) - if err != nil { - return nil, err - } - - return raffles, nil -} - -func (s *Store) CreateRaffleTicket(ctx context.Context, raffleTicketParams domain.CreateRaffleTicket) (domain.RaffleTicket, error) { - raffleTicket, err := s.queries.CreateRaffleTicket(ctx, dbgen.CreateRaffleTicketParams{ - RaffleID: raffleTicketParams.RaffleID, - UserID: raffleTicketParams.UserID, - }) - if err != nil { - return domain.RaffleTicket{}, err - } - - return domain.ConvertRaffleTicketOutcome(raffleTicket), nil -} - -func (s *Store) GetUserRaffleTickets(ctx context.Context, userID int32) ([]domain.RaffleTicketRes, error) { - raffleTickets, err := s.queries.GetUserRaffleTickets(ctx, userID) - if err != nil { - return nil, err - } - - res := []domain.RaffleTicketRes{} - for _, raffle := range raffleTickets { - res = append(res, domain.ConvertJoinedRaffleTicketOutcome(raffle)) - } - - return res, nil -} - -func (s *Store) SuspendRaffleTicket(ctx context.Context, raffleTicketID int32) error { - return s.queries.UpdateRaffleTicketStatus(ctx, dbgen.UpdateRaffleTicketStatusParams{ - ID: raffleTicketID, - IsActive: pgtype.Bool{ - Bool: false, - Valid: true, - }, - }) -} - -func (s *Store) UnSuspendRaffleTicket(ctx context.Context, raffleID int32) error { - return s.queries.UpdateRaffleTicketStatus(ctx, dbgen.UpdateRaffleTicketStatusParams{ - ID: raffleID, - IsActive: pgtype.Bool{ - Bool: true, - Valid: true, - }, - }) -} - -// TODO: could also add -> suspend a specific user's raffle tickets - -func (s *Store) GetRaffleStanding(ctx context.Context, raffleID, limit int32) ([]domain.RaffleStanding, error) { - raffleStanding, err := s.queries.GetRaffleStanding(ctx, dbgen.GetRaffleStandingParams{ - RaffleID: raffleID, - Limit: limit, - }) - if err != nil { - return nil, err - } - - res := []domain.RaffleStanding{} - for _, standing := range raffleStanding { - res = append(res, domain.ConvertRaffleStanding(standing)) - } - - return res, nil -} - -func (s *Store) CreateRaffleWinner(ctx context.Context, raffleWinnerParams domain.RaffleWinnerParams) error { - _, err := s.queries.CreateRaffleWinner(ctx, dbgen.CreateRaffleWinnerParams{ - RaffleID: raffleWinnerParams.RaffleID, - UserID: raffleWinnerParams.UserID, - Rank: raffleWinnerParams.Rank, - }) - - return err -} - -func (s *Store) SetRaffleComplete(ctx context.Context, raffleID int32) error { - return s.queries.SetRaffleComplete(ctx, raffleID) -} - -func (s *Store) AddSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) error { - _, err := s.queries.AddSportRaffleFilter(ctx, dbgen.AddSportRaffleFilterParams{ - RaffleID: raffleID, - SportID: sportID, - LeagueID: leagueID, - }) - return err -} - -func (s *Store) CheckValidSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) (bool, error) { - res, err := s.queries.CheckValidSportRaffleFilter(ctx, dbgen.CheckValidSportRaffleFilterParams{ - RaffleID: raffleID, - SportID: sportID, - LeagueID: leagueID, - }) - if err != nil { - return false, err - } - - return res, nil -} - -func (s *Store) GetRaffleTicketLimit(ctx context.Context, raffleID int32) (int32, error) { - return s.queries.GetRaffleTicketLimit(ctx, raffleID) -} - -func (s *Store) GetRaffleTicketCount(ctx context.Context, raffleID, userID int32) (int64, error) { - return s.queries.GetRaffleTicketCount(ctx, dbgen.GetRaffleTicketCountParams{ - RaffleID: raffleID, - UserID: userID, - }) -} - -func (s *Store) CheckSportRaffleHasFilter(ctx context.Context, raffleID int32) (bool, error) { - return s.queries.CheckSportRaffleHasFilter(ctx, raffleID) -} diff --git a/internal/repository/referal.go b/internal/repository/referal.go deleted file mode 100644 index 7fd7a56..0000000 --- a/internal/repository/referal.go +++ /dev/null @@ -1,99 +0,0 @@ -package repository - -import ( - "context" - - 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 referral store -func NewReferralStore(s *Store) ports.ReferralStore { return s } - -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 - } - return domain.ConvertDBReferralCode(newReferralCode), nil -} - -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 - } - - return domain.ConvertDBUserReferral(newReferral), nil -} - -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 - } - - return domain.ConvertDBReferralCodes(codes), nil -} - -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 - } - - return domain.ConvertDBReferralCode(referralCode), nil - -} -func (s *Store) UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error { - err := s.queries.UpdateReferralCode(ctx, domain.ConvertUpdateReferralCode(referral)) - - if err != nil { - return err - } - - return nil -} - -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, - }) - if err != nil { - return domain.ReferralStats{}, err - } - - return domain.ConvertDBReferralStats(stats), nil -} - -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 (s *Store) GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) { - dbReferrals, err := s.queries.GetUserReferralsByCode(ctx, code) - - if err != nil { - return nil, err - } - - return domain.ConvertDBUserReferrals(dbReferrals), nil -} - -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 -} diff --git a/internal/repository/result.go b/internal/repository/result.go deleted file mode 100644 index b11ea6a..0000000 --- a/internal/repository/result.go +++ /dev/null @@ -1,43 +0,0 @@ -package repository - -import ( - "context" - - 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 { - return domain.ResultLog{}, err - } - return domain.ConvertDBResultLog(dbResult), nil -} - -func (s *Store) GetAllResultLog(ctx context.Context, filter domain.ResultLogFilter) ([]domain.ResultLog, error) { - dbResultLogs, err := s.queries.GetAllResultLog(ctx, dbgen.GetAllResultLogParams{ - CreatedBefore: pgtype.Timestamp{ - Time: filter.CreatedBefore.Value, - Valid: filter.CreatedBefore.Valid, - }, - CreatedAfter: pgtype.Timestamp{ - Time: filter.CreatedAfter.Value, - Valid: filter.CreatedAfter.Valid, - }, - }) - if err != nil { - return nil, err - } - result := make([]domain.ResultLog, 0, len(dbResultLogs)) - for _, dbResultLog := range dbResultLogs { - result = append(result, domain.ConvertDBResultLog(dbResultLog)) - } - return result, nil -} diff --git a/internal/repository/settings.go b/internal/repository/settings.go deleted file mode 100644 index cfda489..0000000 --- a/internal/repository/settings.go +++ /dev/null @@ -1,216 +0,0 @@ -package repository - -import ( - "context" - "fmt" - - 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) InsertGlobalSetting(ctx context.Context, setting domain.CreateSetting) error { - err := s.queries.InsertGlobalSetting(ctx, dbgen.InsertGlobalSettingParams{ - Key: setting.Key, - Value: setting.Value, - }) - if err != nil { - return err - } - return nil -} - -func (s *Store) GetGlobalSettingList(ctx context.Context) (domain.SettingList, error) { - settings, err := s.queries.GetGlobalSettings(ctx) - if err != nil { - domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err)) - return domain.SettingList{}, err - } - - return domain.ConvertDBGlobalSettingList(settings) -} - -func (s *Store) GetGlobalSettings(ctx context.Context) ([]domain.Setting, error) { - settings, err := s.queries.GetGlobalSettings(ctx) - - if err != nil { - domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err)) - } - - var result []domain.Setting = make([]domain.Setting, 0, len(settings)) - for _, setting := range settings { - result = append(result, domain.Setting{ - Key: setting.Key, - Value: setting.Value, - UpdatedAt: setting.UpdatedAt.Time, - }) - } - - return result, nil -} - -func (s *Store) GetGlobalSetting(ctx context.Context, key string) (domain.Setting, error) { - dbSetting, err := s.queries.GetGlobalSetting(ctx, key) - - if err != nil { - domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err)) - } - - result := domain.Setting{ - Key: dbSetting.Key, - Value: dbSetting.Value, - UpdatedAt: dbSetting.UpdatedAt.Time, - } - - return result, nil -} - -func (s *Store) UpdateGlobalSetting(ctx context.Context, key, value string) error { - err := s.queries.UpdateGlobalSetting(ctx, dbgen.UpdateGlobalSettingParams{ - Key: key, - Value: value, - }) - - if err != nil { - domain.MongoDBLogger.Error("failed to update setting", - zap.String("key", key), - zap.String("value", value), - zap.Error(err), - ) - - return err - } - - return err - -} - -func (s *Store) UpdateGlobalSettingList(ctx context.Context, settingList domain.ValidSettingList) error { - convertedSettings := settingList.ConvertAllSettings() - - for _, setting := range convertedSettings { - err := s.UpdateGlobalSetting(ctx, setting.Key, setting.Value) - if err != nil { - domain.MongoDBLogger.Warn("failed to update setting list", zap.String("key", setting.Key), zap.Error(err)) - return err - } - } - return nil -} - -func (s *Store) InsertCompanySetting(ctx context.Context, key, value string, companyID int64) error { - err := s.queries.InsertCompanySetting(ctx, dbgen.InsertCompanySettingParams{ - CompanyID: companyID, - Key: key, - Value: value, - }) - - if err != nil { - return err - } - - return nil -} - -func (s *Store) InsertCompanySettingList(ctx context.Context, settingList domain.ValidSettingList, companyID int64) error { - convertedSettings := settingList.ConvertAllSettings() - - for _, setting := range convertedSettings { - err := s.InsertCompanySetting(ctx, setting.Key, setting.Value, companyID) - if err != nil { - domain.MongoDBLogger.Warn("failed to update setting list", zap.String("key", setting.Key), zap.Error(err)) - return err - } - } - return nil -} - -func (s *Store) GetAllCompanySettings(ctx context.Context) ([]domain.CompanySetting, error) { - settings, err := s.queries.GetAllCompanySettings(ctx) - - if err != nil { - return nil, err - } - - return domain.ConvertCompanySettings(settings), nil -} - -func (s *Store) GetCompanySettingsByKey(ctx context.Context, key string) ([]domain.CompanySetting, error) { - settings, err := s.queries.GetCompanySettingsByKey(ctx, key) - - if err != nil { - return nil, err - } - - return domain.ConvertCompanySettings(settings), nil -} - -func (s *Store) GetOverrideSettings(ctx context.Context, companyID int64) ([]domain.Setting, error) { - settings, err := s.queries.GetOverrideSettings(ctx, companyID) - - if err != nil { - return nil, err - } - - result := make([]domain.Setting, 0, len(settings)) - for _, setting := range settings { - result = append(result, domain.Setting{ - Key: setting.Key, - Value: setting.Value, - UpdatedAt: setting.UpdatedAt.Time, - }) - } - - return result, nil -} - -func (s *Store) GetOverrideSettingsList(ctx context.Context, companyID int64) (domain.SettingList, error) { - settings, err := s.queries.GetOverrideSettings(ctx, companyID) - - if err != nil { - return domain.SettingList{}, err - } - - return domain.ConvertDBOverrideSettingList(settings) -} - -func (s *Store) DeleteCompanySetting(ctx context.Context, companyID int64, key string) error { - return s.queries.DeleteCompanySetting(ctx, dbgen.DeleteCompanySettingParams{ - CompanyID: companyID, - Key: key, - }) -} -func (s *Store) DeleteAllCompanySetting(ctx context.Context, companyID int64) error { - return s.queries.DeleteAllCompanySetting(ctx, companyID) -} - -func (s *Store) EnsureAllSettingsExist(ctx context.Context) error { - defaultSettings := domain.NewDefaultSettingList().ToSettingArray() // returns []domain.Setting from your typed struct - - dbSettings, err := s.GetGlobalSettings(ctx) - if err != nil { - return fmt.Errorf("failed to fetch settings: %w", err) - } - - existing := map[string]struct{}{} - for _, s := range dbSettings { - existing[s.Key] = struct{}{} - } - - for _, setting := range defaultSettings { - if _, found := existing[setting.Key]; !found { - if err := s.InsertGlobalSetting(ctx, domain.CreateSetting{ - Key: setting.Key, - Value: setting.Value, - }); err != nil { - return fmt.Errorf("failed to create missing setting %q: %w", setting.Key, err) - } - } - } - - return nil -} diff --git a/internal/repository/shop_bet.go b/internal/repository/shop_bet.go deleted file mode 100644 index 3a5bd5a..0000000 --- a/internal/repository/shop_bet.go +++ /dev/null @@ -1,167 +0,0 @@ -package repository - -import ( - "context" - "fmt" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" - "go.uber.org/zap" -) - -func convertDBShopBet(bet dbgen.ShopBet) domain.ShopBet { - return domain.ShopBet{ - ID: bet.ID, - ShopTransactionID: bet.ShopTransactionID, - CashoutID: bet.CashoutID, - CashedOut: bet.CashedOut, - BetID: bet.BetID, - NumberOfOutcomes: bet.NumberOfOutcomes, - } -} - -func convertDBShopBetDetail(bet dbgen.ShopBetDetail) domain.ShopBetDetail { - var outcomes []domain.BetOutcome = make([]domain.BetOutcome, 0, len(bet.Outcomes)) - - for _, outcome := range bet.Outcomes { - outcomes = append(outcomes, domain.ConvertDBBetOutcomes(outcome)) - } - return domain.ShopBetDetail{ - ID: bet.ID, - ShopTransactionID: bet.ShopTransactionID, - TotalOdds: bet.TotalOdds, - BranchID: bet.BranchID, - CompanyID: bet.CompanyID, - FullName: bet.CustomerFullName, - PhoneNumber: bet.CustomerPhoneNumber, - FastCode: bet.FastCode, - CashoutID: bet.CashoutID, - CashedOut: bet.CashedOut, - BetID: bet.BetID, - NumberOfOutcomes: bet.NumberOfOutcomes, - Status: domain.OutcomeStatus(bet.Status), - Amount: domain.Currency(bet.Amount), - Outcomes: outcomes, - TransactionVerified: bet.TransactionVerified, - UpdatedAt: bet.UpdatedAt.Time, - CreatedAt: bet.CreatedAt.Time, - } -} - -func convertCreateShopBet(bet domain.CreateShopBet) dbgen.CreateShopBetParams { - return dbgen.CreateShopBetParams{ - ShopTransactionID: bet.ShopTransactionID, - CashoutID: bet.CashoutID, - BetID: bet.BetID, - NumberOfOutcomes: bet.NumberOfOutcomes, - } -} - -func (s *Store) CreateShopBet(ctx context.Context, bet domain.CreateShopBet) (domain.ShopBet, error) { - newShopBet, err := s.queries.CreateShopBet(ctx, convertCreateShopBet(bet)) - - if err != nil { - return domain.ShopBet{}, err - } - - return convertDBShopBet(newShopBet), err -} - -func (s *Store) GetAllShopBet(ctx context.Context, filter domain.ShopBetFilter) ([]domain.ShopBetDetail, error) { - bets, err := s.queries.GetAllShopBets(ctx, dbgen.GetAllShopBetsParams{ - BranchID: pgtype.Int8{ - Int64: filter.BranchID.Value, - Valid: filter.BranchID.Valid, - }, - CompanyID: pgtype.Int8{ - Int64: filter.CompanyID.Value, - Valid: filter.CompanyID.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, - }, - }) - if err != nil { - return nil, err - } - - var result []domain.ShopBetDetail = make([]domain.ShopBetDetail, 0, len(bets)) - for _, bet := range bets { - result = append(result, convertDBShopBetDetail(bet)) - } - - return result, nil -} - -func (s *Store) GetShopBetByID(ctx context.Context, id int64) (domain.ShopBetDetail, error) { - bet, err := s.queries.GetShopBetByID(ctx, id) - if err != nil { - fmt.Printf("GetShopBetByID Repo BetID %d err %v \n", id, err.Error()) - return domain.ShopBetDetail{}, err - } - - return convertDBShopBetDetail(bet), nil -} - -func (s *Store) GetShopBetByBetID(ctx context.Context, betID int64) (domain.ShopBetDetail, error) { - bet, err := s.queries.GetShopBetByBetID(ctx, betID) - if err != nil { - return domain.ShopBetDetail{}, err - } - return convertDBShopBetDetail(bet), nil -} - -func (s *Store) GetShopBetByCashoutID(ctx context.Context, cashoutID string) (domain.ShopBetDetail, error) { - bet, err := s.queries.GetShopBetByCashoutID(ctx, cashoutID) - if err != nil { - return domain.ShopBetDetail{}, err - } - return convertDBShopBetDetail(bet), nil -} -func (s *Store) GetShopBetByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopBetDetail, error) { - bet, err := s.queries.GetShopBetByShopTransactionID(ctx, shopTransactionID) - if err != nil { - return domain.ShopBetDetail{}, err - } - return convertDBShopBetDetail(bet), nil -} - -func (s *Store) UpdateShopBetCashOut(ctx context.Context, id int64, cashedOut bool) error { - err := s.queries.UpdateShopBetCashOut(ctx, dbgen.UpdateShopBetCashOutParams{ - ID: id, - CashedOut: cashedOut, - }) - if err != nil { - domain.MongoDBLogger.Error("failed to update cashout", - zap.Int64("id", id), - zap.Bool("cashed_out", cashedOut), - zap.Error(err), - ) - } - return err -} - -func (s *Store) UpdateShopBetCashoutID(ctx context.Context, id int64, cashoutID string) error { - err := s.queries.UpdateShopBetCashoutID(ctx, dbgen.UpdateShopBetCashoutIDParams{ - ID: id, - CashoutID: cashoutID, - }) - if err != nil { - domain.MongoDBLogger.Error("failed to update cashout_id", - zap.Int64("id", id), - zap.String("cashout_id", cashoutID), - zap.Error(err), - ) - } - return err -} diff --git a/internal/repository/shop_deposit.go b/internal/repository/shop_deposit.go deleted file mode 100644 index 1f73ea7..0000000 --- a/internal/repository/shop_deposit.go +++ /dev/null @@ -1,122 +0,0 @@ -package repository - -import ( - "context" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" -) - -func convertShopDeposit(deposit dbgen.ShopDeposit) domain.ShopDeposit { - return domain.ShopDeposit{ - ID: deposit.ID, - ShopTransactionID: deposit.ShopTransactionID, - CustomerID: deposit.CustomerID, - BranchWalletID: deposit.BranchWalletID, - WalletTransferID: domain.ValidInt64{ - Value: deposit.WalletTransferID.Int64, - Valid: deposit.WalletTransferID.Valid, - }, - } -} - -func convertShopDepositDetail(deposit dbgen.ShopDepositDetail) domain.ShopDepositDetail { - return domain.ShopDepositDetail{ - ID: deposit.ID, - ShopTransactionID: deposit.ShopTransactionID, - CustomerID: deposit.CustomerID, - BranchWalletID: deposit.BranchWalletID, - WalletTransferID: domain.ValidInt64{ - Value: deposit.WalletTransferID.Int64, - Valid: deposit.WalletTransferID.Valid, - }, - FullName: deposit.FullName, - PhoneNumber: deposit.PhoneNumber, - Amount: domain.Currency(deposit.Amount), - BranchID: deposit.BranchID, - CompanyID: deposit.CompanyID, - TransactionVerified: deposit.TransactionVerified, - UpdatedAt: deposit.UpdatedAt.Time, - CreatedAt: deposit.CreatedAt.Time, - } -} -func convertCreateShopDeposit(deposit domain.CreateShopDeposit) dbgen.CreateShopDepositParams { - return dbgen.CreateShopDepositParams{ - ShopTransactionID: deposit.ShopTransactionID, - CustomerID: deposit.CustomerID, - BranchWalletID: deposit.BranchWalletID, - } -} - -func (s *Store) CreateShopDeposit(ctx context.Context, deposit domain.CreateShopDeposit) (domain.ShopDeposit, error) { - newShopDeposit, err := s.queries.CreateShopDeposit(ctx, convertCreateShopDeposit(deposit)) - - if err != nil { - return domain.ShopDeposit{}, err - } - return convertShopDeposit(newShopDeposit), nil -} - -func (s *Store) GetAllShopDeposit(ctx context.Context, filter domain.ShopDepositFilter) ([]domain.ShopDepositDetail, error) { - deposits, err := s.queries.GetAllShopDeposit(ctx, dbgen.GetAllShopDepositParams{ - BranchID: pgtype.Int8{ - Int64: filter.BranchID.Value, - Valid: filter.BranchID.Valid, - }, - CompanyID: pgtype.Int8{ - Int64: filter.CompanyID.Value, - Valid: filter.CompanyID.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, - }, - }) - if err != nil { - return nil, err - } - - var result []domain.ShopDepositDetail = make([]domain.ShopDepositDetail, 0, len(deposits)) - - for _, deposit := range deposits { - result = append(result, convertShopDepositDetail(deposit)) - } - return result, nil -} - -func (s *Store) GetShopDepositByID(ctx context.Context, id int64) (domain.ShopDepositDetail, error) { - deposit, err := s.queries.GetShopDepositByID(ctx, id) - if err != nil { - return domain.ShopDepositDetail{}, err - } - - return convertShopDepositDetail(deposit), err -} - -func (s *Store) GetShopDepositByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopDepositDetail, error) { - deposit, err := s.queries.GetShopDepositByShopTransactionID(ctx, shopTransactionID) - if err != nil { - return domain.ShopDepositDetail{}, err - } - - return convertShopDepositDetail(deposit), err -} - -func (s *Store) UpdateShopDepositTransferID(ctx context.Context, id int64, transferID domain.ValidInt64) error { - return s.queries.UpdateShopDepositTransferID(ctx, dbgen.UpdateShopDepositTransferIDParams{ - WalletTransferID: pgtype.Int8{ - Int64: transferID.Value, - Valid: transferID.Valid, - }, - ID: id, - }) -} diff --git a/internal/repository/shop_transaction.go b/internal/repository/shop_transaction.go index af22fd1..fa7bad7 100644 --- a/internal/repository/shop_transaction.go +++ b/internal/repository/shop_transaction.go @@ -1,181 +1,6 @@ package repository -import ( - "context" - "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/pgtype" -) +import "Yimaru-Backend/internal/ports" // 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, domain.ConvertCreateShopTransaction(transaction)) - if err != nil { - return domain.ShopTransaction{}, err - } - return domain.ConvertDBShopTransaction(newTransaction), err -} - -func (s *Store) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransactionDetail, error) { - transaction, err := s.queries.GetShopTransactionByID(ctx, id) - if err != nil { - return domain.ShopTransactionDetail{}, err - } - - 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: 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 { - return nil, err - } - - var result []domain.ShopTransactionDetail = make([]domain.ShopTransactionDetail, 0, len(transaction)) - for _, tAction := range transaction { - result = append(result, domain.ConvertDBShopTransactionDetail(tAction)) - } - return result, nil -} -func (s *Store) GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransactionDetail, error) { - transaction, err := s.queries.GetShopTransactionByBranch(ctx, id) - - if err != nil { - return nil, err - } - - var result []domain.ShopTransactionDetail = make([]domain.ShopTransactionDetail, 0, len(transaction)) - for _, ticket := range transaction { - result = append(result, domain.ConvertDBShopTransactionDetail(ticket)) - } - return result, nil -} - -func (s *Store) UpdateShopTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64) error { - err := s.queries.UpdateShopTransactionVerified(ctx, dbgen.UpdateShopTransactionVerifiedParams{ - ID: id, - ApprovedBy: pgtype.Int8{ - Int64: approvedBy, - Valid: true, - }, - Verified: verified, - }) - 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 - COALESCE(SUM(CASE WHEN type = 1 THEN amount ELSE 0 END), 0) as deposits, - COALESCE(SUM(CASE WHEN type = 0 THEN amount ELSE 0 END), 0) as withdrawals - FROM shop_transactions` - - args := []interface{}{} - argPos := 1 - - if filter.CompanyID.Valid { - query += fmt.Sprintf(" WHERE company_id = $%d", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ - } else if filter.BranchID.Valid { - query += fmt.Sprintf(" WHERE branch_id = $%d", argPos) - args = append(args, filter.BranchID.Value) - argPos++ - } else if filter.UserID.Valid { - query += fmt.Sprintf(" WHERE user_id = $%d", argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %screated_at >= $%d", func() string { - if len(args) == 0 { - return "" - } - return " " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - row := s.conn.QueryRow(ctx, query, args...) - err = row.Scan(&deposits, &withdrawals) - if err != nil { - return 0, 0, fmt.Errorf("failed to get transaction totals: %w", err) - } - return deposits, withdrawals, nil -} - -// GetBranchTransactionTotals returns transaction totals by branch -func (s *Store) GetBranchTransactionTotals(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.BranchTransactions, error) { - query := `SELECT - branch_id, - COALESCE(SUM(CASE WHEN type = 1 THEN amount ELSE 0 END), 0) as deposits, - COALESCE(SUM(CASE WHEN type = 0 THEN amount ELSE 0 END), 0) as withdrawals - FROM shop_transactions` - - args := []interface{}{} - argPos := 1 - - if filter.CompanyID.Valid { - query += fmt.Sprintf(" WHERE company_id = $%d", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %screated_at >= $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - query += " GROUP BY branch_id" - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - return nil, fmt.Errorf("failed to query branch transaction totals: %w", err) - } - defer rows.Close() - - totals := make(map[int64]domain.BranchTransactions) - for rows.Next() { - var branchID int64 - var transactions domain.BranchTransactions - if err := rows.Scan(&branchID, &transactions.Deposits, &transactions.Withdrawals); err != nil { - return nil, fmt.Errorf("failed to scan branch transaction totals: %w", err) - } - totals[branchID] = transactions - } - if err := rows.Err(); err != nil { - return nil, fmt.Errorf("rows error: %w", err) - } - return totals, nil -} diff --git a/internal/repository/store.go b/internal/repository/store.go index c60aa2b..1c1cef4 100644 --- a/internal/repository/store.go +++ b/internal/repository/store.go @@ -4,7 +4,8 @@ import ( "context" "time" - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + dbgen "Yimaru-Backend/gen/db" + "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" // "golang.org/x/net/websocket" diff --git a/internal/repository/ticket.go b/internal/repository/ticket.go deleted file mode 100644 index 24afe8c..0000000 --- a/internal/repository/ticket.go +++ /dev/null @@ -1,88 +0,0 @@ -package repository - -import ( - "context" - - 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 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, domain.ConvertCreateTicket(ticket)) - if err != nil { - return domain.Ticket{}, err - } - return domain.ConvertDBTicket(newTicket), err - -} - -func (s *Store) CreateTicketOutcome(ctx context.Context, outcomes []domain.CreateTicketOutcome) (int64, error) { - - var dbParams []dbgen.CreateTicketOutcomeParams = make([]dbgen.CreateTicketOutcomeParams, 0, len(outcomes)) - for _, outcome := range outcomes { - dbParams = append(dbParams, domain.ConvertDBCreateTicketOutcome(outcome)) - } - - rows, err := s.queries.CreateTicketOutcome(ctx, dbParams) - - if err != nil { - return rows, err - } - - return rows, nil -} - -func (s *Store) GetTicketByID(ctx context.Context, id int64) (domain.GetTicket, error) { - ticket, err := s.queries.GetTicketByID(ctx, id) - - if err != nil { - return domain.GetTicket{}, err - } - - return domain.ConvertDBTicketOutcomes(ticket), nil -} - -func (s *Store) GetAllTickets(ctx context.Context, filter domain.TicketFilter) ([]domain.GetTicket, error) { - tickets, err := s.queries.GetAllTickets(ctx, filter.CompanyID.ToPG()) - if err != nil { - return nil, err - } - - var result []domain.GetTicket = make([]domain.GetTicket, 0, len(tickets)) - for _, ticket := range tickets { - result = append(result, domain.ConvertDBTicketOutcomes(ticket)) - } - - return result, nil -} - -func (s *Store) CountTicketByIP(ctx context.Context, IP string) (int64, error) { - count, err := s.queries.CountTicketByIP(ctx, IP) - - if err != nil { - return 0, err - } - - return count, err -} - -func (s *Store) UpdateTicketOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error { - err := s.queries.UpdateTicketOutcomeStatus(ctx, dbgen.UpdateTicketOutcomeStatusParams{ - Status: int32(status), - ID: id, - }) - return err -} - -func (s *Store) DeleteOldTickets(ctx context.Context) error { - return s.queries.DeleteOldTickets(ctx) -} - -func (s *Store) DeleteTicket(ctx context.Context, id int64) error { - return s.queries.DeleteTicket(ctx, id) -} diff --git a/internal/repository/transfer.go b/internal/repository/transfer.go deleted file mode 100644 index be23e52..0000000 --- a/internal/repository/transfer.go +++ /dev/null @@ -1,104 +0,0 @@ -package repository - -import ( - "context" - - 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 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, domain.ConvertCreateTransfer(transfer)) - if err != nil { - return domain.Transfer{}, err - } - return domain.ConvertDBTransfer(newTransfer), nil -} - -func (s *Store) GetAllTransfers(ctx context.Context) ([]domain.TransferDetail, error) { - transfers, err := s.queries.GetAllTransfers(ctx) - if err != nil { - return nil, err - } - var result []domain.TransferDetail = make([]domain.TransferDetail, 0, len(transfers)) - - for _, transfer := range transfers { - result = append(result, domain.ConvertDBTransferDetail(transfer)) - } - return result, nil -} - -func (s *Store) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.TransferDetail, error) { - transfers, err := s.queries.GetTransfersByWallet(ctx, pgtype.Int8{Int64: walletID, Valid: true}) - if err != nil { - return nil, err - } - - var result []domain.TransferDetail = make([]domain.TransferDetail, 0, len(transfers)) - - for _, transfer := range transfers { - result = append(result, domain.ConvertDBTransferDetail(transfer)) - } - return result, nil -} - -func (s *Store) GetTransferByReference(ctx context.Context, reference string) (domain.TransferDetail, error) { - transfer, err := s.queries.GetTransferByReference(ctx, reference) - if err != nil { - return domain.TransferDetail{}, nil - } - return domain.ConvertDBTransferDetail(transfer), nil -} - -func (s *Store) GetTransferByID(ctx context.Context, id int64) (domain.TransferDetail, error) { - transfer, err := s.queries.GetTransferByID(ctx, id) - if err != nil { - return domain.TransferDetail{}, nil - } - return domain.ConvertDBTransferDetail(transfer), nil -} - -func (s *Store) GetTransferStats(ctx context.Context, walletID int64) (domain.TransferStats, error) { - stats, err := s.queries.GetTransferStats(ctx, pgtype.Int8{ - Int64: walletID, - Valid: true, - }) - - if err != nil { - return domain.TransferStats{}, err - } - - return domain.TransferStats{ - TotalTransfer: stats.TotalTransfers, - TotalDeposits: stats.TotalDeposits, - TotalWithdraws: stats.TotalWithdraw, - TotalWalletToWallet: stats.TotalWalletToWallet, - }, nil -} - -func (s *Store) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error { - err := s.queries.UpdateTransferVerification(ctx, dbgen.UpdateTransferVerificationParams{ - ID: id, - Verified: pgtype.Bool{Bool: verified, Valid: true}, - }) - - return err -} - -func (s *Store) UpdateTransferStatus(ctx context.Context, id int64, status string) error { - err := s.queries.UpdateTransferStatus(ctx, dbgen.UpdateTransferStatusParams{ - ID: id, - Status: pgtype.Text{String: status, Valid: true}, - }) - - return err -} - -func ApproveTransfer(ctx context.Context, approval domain.ApprovalRequest) error { - return nil -} diff --git a/internal/repository/user.go b/internal/repository/user.go index e3ee3a1..51bbe22 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -1,864 +1,361 @@ package repository import ( + dbgen "Yimaru-Backend/gen/db" + "Yimaru-Backend/internal/domain" "context" - "database/sql" "errors" - "fmt" "time" - 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 } +// type Store struct { +// db *pgxpool.Pool +// queries *dbgen.Queries +// } -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, - UsedAt: pgtype.Timestamptz{ - Time: time.Now(), - Valid: true, - }, - }) - if err != nil { - return domain.User{}, err +// func NewStore(db *pgxpool.Pool) *Store { +// return &Store{ +// db: db, +// queries: dbgen.New(db), +// } +// } + +// CreateUser inserts a new user into the database +func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int64) (domain.User, error) { + // Optional: mark OTP as used + if usedOtpId > 0 { + err := s.queries.MarkOtpAsUsed(ctx, dbgen.MarkOtpAsUsedParams{ + ID: usedOtpId, + UsedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true}, + }) + if err != nil { + return domain.User{}, err + } } + userRes, err := s.queries.CreateUser(ctx, dbgen.CreateUserParams{ - FirstName: user.FirstName, - LastName: user.LastName, - Email: pgtype.Text{ - String: user.Email, - Valid: user.Email != "", - }, - PhoneNumber: pgtype.Text{ - String: user.PhoneNumber, - Valid: user.PhoneNumber != "", - }, - Password: user.Password, - Role: string(user.Role), - EmailVerified: user.EmailVerified, - PhoneVerified: user.PhoneVerified, - CreatedAt: pgtype.Timestamptz{ - Time: time.Now(), - Valid: true, - }, - UpdatedAt: pgtype.Timestamptz{ - Time: time.Now(), - Valid: true, - }, - CompanyID: user.CompanyID.ToPG(), - Suspended: user.Suspended, + FirstName: user.FirstName, + LastName: user.LastName, + NickName: pgtype.Text{String: user.NickName}, + Email: pgtype.Text{String: user.Email, Valid: user.Email != ""}, + PhoneNumber: pgtype.Text{String: user.PhoneNumber, Valid: user.PhoneNumber != ""}, + Role: string(user.Role), + Password: user.Password, + Age: pgtype.Int4{Int32: int32(user.Age), Valid: user.Age > 0}, + EducationLevel: pgtype.Text{String: user.EducationLevel, Valid: user.EducationLevel != ""}, + Country: pgtype.Text{String: user.Country, Valid: user.Country != ""}, + Region: pgtype.Text{String: user.Region, Valid: user.Region != ""}, + EmailVerified: user.EmailVerified, + PhoneVerified: user.PhoneVerified, + Suspended: user.Suspended, + SuspendedAt: pgtype.Timestamptz{Time: user.SuspendedAt, Valid: !user.SuspendedAt.IsZero()}, + OrganizationID: pgtype.Int8{Int64: user.OrganizationID.Value, Valid: user.OrganizationID.Valid}, + CreatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true}, + UpdatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true}, }) if err != nil { return domain.User{}, err } + return domain.User{ - ID: userRes.ID, - FirstName: userRes.FirstName, - LastName: userRes.LastName, - Email: userRes.Email.String, - PhoneNumber: userRes.PhoneNumber.String, - Role: domain.Role(userRes.Role), - CompanyID: domain.ValidInt64{ - Value: userRes.CompanyID.Int64, - Valid: userRes.CompanyID.Valid, - }, - EmailVerified: userRes.EmailVerified, - PhoneVerified: userRes.PhoneVerified, - CreatedAt: userRes.CreatedAt.Time, - UpdatedAt: userRes.UpdatedAt.Time, - Suspended: userRes.Suspended, + ID: userRes.ID, + FirstName: userRes.FirstName, + LastName: userRes.LastName, + NickName: userRes.NickName.String, + Email: userRes.Email.String, + PhoneNumber: userRes.PhoneNumber.String, + Role: domain.Role(userRes.Role), + Age: int(userRes.Age.Int32), + EducationLevel: userRes.EducationLevel.String, + Country: userRes.Country.String, + Region: userRes.Region.String, + EmailVerified: userRes.EmailVerified, + PhoneVerified: userRes.PhoneVerified, + Suspended: userRes.Suspended, + SuspendedAt: userRes.SuspendedAt.Time, + OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid}, + CreatedAt: userRes.CreatedAt.Time, + UpdatedAt: userRes.UpdatedAt.Time, }, nil + } + +// GetUserByID retrieves a user by ID func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error) { - user, err := s.queries.GetUserByID(ctx, id) + userRes, err := s.queries.GetUserByID(ctx, id) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return domain.User{}, domain.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, - Role: domain.Role(user.Role), - EmailVerified: user.EmailVerified, - Password: user.Password, - 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, - }, + ID: userRes.ID, + FirstName: userRes.FirstName, + LastName: userRes.LastName, + NickName: userRes.NickName.String, + Email: userRes.Email.String, + PhoneNumber: userRes.PhoneNumber.String, + Role: domain.Role(userRes.Role), + Age: int(userRes.Age.Int32), + EducationLevel: userRes.EducationLevel.String, + Country: userRes.Country.String, + Region: userRes.Region.String, + EmailVerified: userRes.EmailVerified, + PhoneVerified: userRes.PhoneVerified, + Suspended: userRes.Suspended, + SuspendedAt: userRes.SuspendedAt.Time, + OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid}, + CreatedAt: userRes.CreatedAt.Time, + UpdatedAt: userRes.UpdatedAt.Time, }, nil + } -func (s *Store) GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error) { - users, err := s.queries.GetAllUsers(ctx, dbgen.GetAllUsersParams{ - Role: filter.Role, - CompanyID: pgtype.Int8{ - Int64: filter.CompanyID.Value, - Valid: filter.CompanyID.Valid, - }, - Limit: pgtype.Int4{ - Int32: int32(filter.PageSize.Value), - Valid: filter.PageSize.Valid, - }, - Offset: pgtype.Int4{ - Int32: int32(filter.Page.Value * filter.PageSize.Value), - Valid: filter.Page.Valid, - }, - Query: pgtype.Text{ - String: filter.Query.Value, - Valid: filter.Query.Valid, - }, - CreatedBefore: pgtype.Timestamptz{ - Time: filter.CreatedBefore.Value, - Valid: filter.CreatedBefore.Valid, - }, - CreatedAfter: pgtype.Timestamptz{ - Time: filter.CreatedAfter.Value, - Valid: filter.CreatedAfter.Valid, - }, + +// GetAllUsers retrieves users with optional filters +func (s *Store) GetAllUsers(ctx context.Context, role *string, organizationID *int64, query *string, createdBefore, createdAfter *time.Time, limit, offset int32) ([]domain.User, error) { + rows, err := s.queries.GetAllUsers(ctx, dbgen.GetAllUsersParams{ + Role: *role, + OrganizationID: pgtype.Int8{Int64: *organizationID}, + Query: pgtype.Text{String: *query}, + CreatedBefore: pgtype.Timestamptz{Time: *createdBefore}, + CreatedAfter: pgtype.Timestamptz{Time: *createdAfter}, + Limit: pgtype.Int4{Int32: limit}, + Offset: pgtype.Int4{Int32: offset}, }) - if err != nil { - return nil, 0, err - } - userList := make([]domain.User, len(users)) - for i, user := range users { - userList[i] = domain.User{ - ID: user.ID, - FirstName: user.FirstName, - LastName: user.LastName, - Email: user.Email.String, - EmailVerified: user.EmailVerified, - PhoneNumber: user.PhoneNumber.String, - Role: domain.Role(user.Role), - 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, - }, - } - } - totalCount, _ := s.queries.GetTotalUsers(ctx, dbgen.GetTotalUsersParams{ - Role: filter.Role, - CompanyID: pgtype.Int8{ - Int64: filter.CompanyID.Value, - Valid: filter.CompanyID.Valid, - }, - }) - return userList, totalCount, nil -} - -func (s *Store) GetAllCashiers(ctx context.Context, filter domain.UserFilter) ([]domain.GetCashier, int64, error) { - users, err := s.queries.GetAllCashiers(ctx, dbgen.GetAllCashiersParams{ - Query: pgtype.Text{ - String: filter.Query.Value, - Valid: filter.Query.Valid, - }, - CreatedBefore: pgtype.Timestamptz{ - Time: filter.CreatedBefore.Value, - Valid: filter.CreatedBefore.Valid, - }, - CreatedAfter: pgtype.Timestamptz{ - Time: filter.CreatedAfter.Value, - Valid: filter.CreatedAfter.Valid, - }, - }) - if err != nil { - return nil, 0, err - } - userList := make([]domain.GetCashier, len(users)) - for i, user := range users { - userList[i] = domain.GetCashier{ - ID: user.ID, - FirstName: user.FirstName, - LastName: user.LastName, - Email: user.Email.String, - PhoneNumber: user.PhoneNumber.String, - 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, - BranchID: user.BranchID, - BranchName: user.BranchName, - BranchWallet: user.BranchWallet, - BranchLocation: user.BranchLocation, - } - } - totalCount, _ := s.queries.GetTotalUsers(ctx, dbgen.GetTotalUsersParams{ - Role: string(domain.RoleCashier), - }) - return userList, totalCount, nil -} - -func (s *Store) GetCashierByID(ctx context.Context, cashierID int64) (domain.GetCashier, error) { - user, err := s.queries.GetCashierByID(ctx, cashierID) - if err != nil { - return domain.GetCashier{}, err - } - return domain.GetCashier{ - ID: user.ID, - FirstName: user.FirstName, - LastName: user.LastName, - Email: user.Email.String, - PhoneNumber: user.PhoneNumber.String, - 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, - BranchID: user.BranchID, - BranchName: user.BranchName, - BranchWallet: user.BranchWallet, - BranchLocation: user.BranchLocation, - }, nil -} - -func (s *Store) GetCashiersByBranch(ctx context.Context, branchID int64) ([]domain.User, error) { - users, err := s.queries.GetCashiersByBranch(ctx, branchID) - if err != nil { - return nil, err - } - userList := make([]domain.User, len(users)) - for i, user := range users { - userList[i] = domain.User{ - ID: user.ID, - FirstName: user.FirstName, - LastName: user.LastName, - Email: user.Email.String, - PhoneNumber: user.PhoneNumber.String, - 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, - } - } - return userList, nil -} - -func (s *Store) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) { - - query := dbgen.SearchUserByNameOrPhoneParams{ - CompanyID: companyID.ToPG(), - Column1: pgtype.Text{ - String: searchString, - Valid: true, - }, - } - - if role != nil { - query.Role = pgtype.Text{ - String: string(*role), - Valid: true, - } - } - - users, err := s.queries.SearchUserByNameOrPhone(ctx, query) if err != nil { return nil, err } - userList := make([]domain.User, 0, len(users)) - for _, user := range users { - userList = append(userList, domain.User{ - ID: user.ID, - FirstName: user.FirstName, - LastName: user.LastName, - Email: user.Email.String, - PhoneNumber: user.PhoneNumber.String, - Role: domain.Role(user.Role), - EmailVerified: user.EmailVerified, - PhoneVerified: user.PhoneVerified, - CreatedAt: user.CreatedAt.Time, - UpdatedAt: user.UpdatedAt.Time, - Suspended: user.Suspended, - SuspendedAt: user.SuspendedAt.Time, - }) + users := make([]domain.User, len(rows)) + for i, u := range rows { + users[i] = domain.User{ + ID: u.ID, + FirstName: u.FirstName, + LastName: u.LastName, + NickName: u.NickName.String, + Email: u.Email.String, + PhoneNumber: u.PhoneNumber.String, + Role: domain.Role(u.Role), + Age: int(u.Age.Int32), + EducationLevel: u.EducationLevel.String, + Country: u.Country.String, + Region: u.Region.String, + EmailVerified: u.EmailVerified, + PhoneVerified: u.PhoneVerified, + Suspended: u.Suspended, + SuspendedAt: u.SuspendedAt.Time, + OrganizationID: domain.ValidInt64{Value: u.OrganizationID.Int64, Valid: u.OrganizationID.Valid}, + CreatedAt: u.CreatedAt.Time, + UpdatedAt: u.UpdatedAt.Time, + } } - return userList, nil + + return users, nil } -func (s *Store) UpdateUser(ctx context.Context, user domain.UpdateUserReq) error { - err := s.queries.UpdateUser(ctx, dbgen.UpdateUserParams{ - ID: user.UserId, - FirstName: user.FirstName.Value, - LastName: user.LastName.Value, - Suspended: user.Suspended.Value, - }) - fmt.Printf("Updating User %v with values %v", user.UserId, user) - if err != nil { - return err - } - return nil -} - -func (s *Store) UpdateUserCompany(ctx context.Context, id int64, companyID int64) error { - err := s.queries.UpdateUserCompany(ctx, dbgen.UpdateUserCompanyParams{ - CompanyID: pgtype.Int8{ - Int64: companyID, - Valid: true, - }, - ID: id, +// GetTotalUsers counts users with optional filters +func (s *Store) GetTotalUsers(ctx context.Context, role *string, organizationID *int64) (int64, error) { + count, err := s.queries.GetTotalUsers(ctx, dbgen.GetTotalUsersParams{ + Role: *role, + OrganizationID: pgtype.Int8{Int64: *organizationID}, }) if err != nil { - return err + return 0, err } - return nil + return count, nil } -func (s *Store) UpdateUserSuspend(ctx context.Context, id int64, status bool) error { - err := s.queries.SuspendUser(ctx, dbgen.SuspendUserParams{ - ID: id, - Suspended: status, - SuspendedAt: pgtype.Timestamptz{ - Time: time.Now(), - Valid: true, - }, +// SearchUserByNameOrPhone searches users by name or phone +func (s *Store) SearchUserByNameOrPhone(ctx context.Context, search string, organizationID *int64, role *string) ([]domain.User, error) { + rows, err := s.queries.SearchUserByNameOrPhone(ctx, dbgen.SearchUserByNameOrPhoneParams{ + Column1: pgtype.Text{String: search}, + OrganizationID: pgtype.Int8{Int64: *organizationID}, + Role: pgtype.Text{String: *role}, }) if err != nil { - return err + return nil, err } - return nil + + users := make([]domain.User, len(rows)) + for i, u := range rows { + users[i] = domain.User{ + ID: u.ID, + FirstName: u.FirstName, + LastName: u.LastName, + NickName: u.NickName.String, + Email: u.Email.String, + PhoneNumber: u.PhoneNumber.String, + Role: domain.Role(u.Role), + Age: int(u.Age.Int32), + EducationLevel: u.EducationLevel.String, + Country: u.Country.String, + Region: u.Region.String, + EmailVerified: u.EmailVerified, + PhoneVerified: u.PhoneVerified, + Suspended: u.Suspended, + SuspendedAt: u.SuspendedAt.Time, + OrganizationID: domain.ValidInt64{Value: u.OrganizationID.Int64, Valid: u.OrganizationID.Valid}, + CreatedAt: u.CreatedAt.Time, + UpdatedAt: u.UpdatedAt.Time, + } + } + + return users, nil } -func (s *Store) DeleteUser(ctx context.Context, id int64) error { - err := s.queries.DeleteUser(ctx, id) - if err != nil { - return err - } - return nil -} -func (s *Store) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error) { - - row, err := s.queries.CheckPhoneEmailExist(ctx, dbgen.CheckPhoneEmailExistParams{ - PhoneNumber: pgtype.Text{ - String: phoneNum, - Valid: phoneNum != "", - }, - Email: pgtype.Text{ - String: email, - - Valid: email != "", - }, - CompanyID: companyID.ToPG(), +// UpdateUser updates basic user info +func (s *Store) UpdateUser(ctx context.Context, user domain.User) error { + return s.queries.UpdateUser(ctx, dbgen.UpdateUserParams{ + FirstName: user.FirstName, + LastName: user.LastName, + Suspended: user.Suspended, + ID: user.ID, }) +} +// UpdateUserOrganization updates a user's organization +func (s *Store) UpdateUserOrganization(ctx context.Context, userID, organizationID int64) error { + return s.queries.UpdateUserOrganization(ctx, dbgen.UpdateUserOrganizationParams{ + OrganizationID: pgtype.Int8{Int64: organizationID, Valid: true}, + ID: userID, + }) +} + +// SuspendUser suspends a user +func (s *Store) SuspendUser(ctx context.Context, userID int64, suspended bool, suspendedAt time.Time) error { + return s.queries.SuspendUser(ctx, dbgen.SuspendUserParams{ + Suspended: suspended, + SuspendedAt: pgtype.Timestamptz{Time: suspendedAt, Valid: true}, + ID: userID, + }) +} + +// DeleteUser removes a user +func (s *Store) DeleteUser(ctx context.Context, userID int64) error { + return s.queries.DeleteUser(ctx, userID) +} + +// CheckPhoneEmailExist checks if phone or email exists in an organization +func (s *Store) CheckPhoneEmailExist(ctx context.Context, phone, email string, organizationID int64) (phoneExists, emailExists bool, err error) { + res, err := s.queries.CheckPhoneEmailExist(ctx, dbgen.CheckPhoneEmailExistParams{ + PhoneNumber: pgtype.Text{String: phone}, + Email: pgtype.Text{String: email}, + OrganizationID: pgtype.Int8{Int64: organizationID}, + }) if err != nil { return false, false, err } - return row.EmailExists, row.PhoneExists, nil + + return res.PhoneExists, res.EmailExists, nil } -func (s *Store) GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (domain.User, error) { - user, err := s.queries.GetUserByEmail(ctx, dbgen.GetUserByEmailParams{ - Email: pgtype.Text{ - String: email, - Valid: true, - }, - CompanyID: companyID.ToPG(), - }) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return domain.User{}, domain.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, - Role: domain.Role(user.Role), - EmailVerified: user.EmailVerified, - PhoneVerified: user.PhoneVerified, - CreatedAt: user.CreatedAt.Time, - UpdatedAt: user.UpdatedAt.Time, - Suspended: user.Suspended, - SuspendedAt: user.SuspendedAt.Time, - }, nil -} -func (s *Store) GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error) { - user, err := s.queries.GetUserByPhone(ctx, dbgen.GetUserByPhoneParams{ - PhoneNumber: pgtype.Text{ - String: phoneNum, - Valid: true, - }, - CompanyID: companyID.ToPG(), - }) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return domain.User{}, domain.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, - Role: domain.Role(user.Role), - EmailVerified: user.EmailVerified, - PhoneVerified: user.PhoneVerified, - CreatedAt: user.CreatedAt.Time, - UpdatedAt: user.UpdatedAt.Time, - Suspended: user.Suspended, - SuspendedAt: user.SuspendedAt.Time, - }, nil -} - -func (s *Store) UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64, companyId int64) error { - err := s.queries.MarkOtpAsUsed(ctx, dbgen.MarkOtpAsUsedParams{ - ID: usedOtpId, - UsedAt: pgtype.Timestamptz{ - Time: time.Now(), - Valid: true, - }, - }) - if err != nil { - return err - } - err = s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{ - Password: password, - Email: pgtype.Text{ - String: identifier, - Valid: true, - }, - PhoneNumber: pgtype.Text{ - String: identifier, - Valid: true, - }, - CompanyID: pgtype.Int8{ - Int64: companyId, - Valid: true, - }, - }) - if err != nil { - return err - } - return nil -} -func (s *Store) CreateUserWithoutOtp(ctx context.Context, user domain.User, is_company bool) (domain.User, error) { - userRes, err := s.queries.CreateUser(ctx, dbgen.CreateUserParams{ - FirstName: user.FirstName, - LastName: user.LastName, - Email: pgtype.Text{ - String: user.Email, - Valid: user.Email != "", - }, - PhoneNumber: pgtype.Text{ - String: user.PhoneNumber, - Valid: user.PhoneNumber != "", - }, - Password: user.Password, - Role: string(user.Role), - EmailVerified: user.EmailVerified, - PhoneVerified: user.PhoneVerified, - CreatedAt: pgtype.Timestamptz{ - Time: time.Now(), - Valid: true, - }, - UpdatedAt: pgtype.Timestamptz{ - Time: time.Now(), - Valid: true, - }, - Suspended: user.Suspended, - CompanyID: pgtype.Int8{ - Int64: user.CompanyID.Value, - Valid: user.CompanyID.Valid, - }, +// GetUserByEmail retrieves a user by email and organization +func (s *Store) GetUserByEmail(ctx context.Context, email string, organizationID int64) (domain.User, error) { + userRes, err := s.queries.GetUserByEmail(ctx, dbgen.GetUserByEmailParams{ + Email: pgtype.Text{String: email}, + OrganizationID: pgtype.Int8{Int64: organizationID}, }) if err != nil { return domain.User{}, err } return domain.User{ - ID: userRes.ID, - FirstName: userRes.FirstName, - LastName: userRes.LastName, - Email: userRes.Email.String, - PhoneNumber: userRes.PhoneNumber.String, - Role: domain.Role(userRes.Role), - EmailVerified: userRes.EmailVerified, - PhoneVerified: userRes.PhoneVerified, - CreatedAt: userRes.CreatedAt.Time, - UpdatedAt: userRes.UpdatedAt.Time, - Suspended: userRes.Suspended, + ID: userRes.ID, + FirstName: userRes.FirstName, + LastName: userRes.LastName, + NickName: userRes.NickName.String, + Email: userRes.Email.String, + PhoneNumber: userRes.PhoneNumber.String, + Role: domain.Role(userRes.Role), + Age: int(userRes.Age.Int32), + EducationLevel: userRes.EducationLevel.String, + Country: userRes.Country.String, + Region: userRes.Region.String, + EmailVerified: userRes.EmailVerified, + PhoneVerified: userRes.PhoneVerified, + Suspended: userRes.Suspended, + SuspendedAt: userRes.SuspendedAt.Time, + OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid}, + CreatedAt: userRes.CreatedAt.Time, + UpdatedAt: userRes.UpdatedAt.Time, }, nil } -func (s *Store) GetAdminByCompanyID(ctx context.Context, companyID int64) (domain.User, error) { - userRes, err := s.queries.GetAdminByCompanyID(ctx, companyID) - - if err != nil { - return domain.User{}, err - } - return domain.User{ - ID: userRes.ID, - FirstName: userRes.FirstName, - LastName: userRes.LastName, - Email: userRes.Email.String, - PhoneNumber: userRes.PhoneNumber.String, - Role: domain.Role(userRes.Role), - EmailVerified: userRes.EmailVerified, - PhoneVerified: userRes.PhoneVerified, - CreatedAt: userRes.CreatedAt.Time, - UpdatedAt: userRes.UpdatedAt.Time, - Suspended: userRes.Suspended, - }, 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(), +// GetUserByPhone retrieves a user by phone and organization +func (s *Store) GetUserByPhone(ctx context.Context, phone string, organizationID int64) (domain.User, error) { + userRes, err := s.queries.GetUserByPhone(ctx, dbgen.GetUserByPhoneParams{ + PhoneNumber: pgtype.Text{String: phone}, + OrganizationID: pgtype.Int8{Int64: organizationID}, }) 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, - }, + ID: userRes.ID, + FirstName: userRes.FirstName, + LastName: userRes.LastName, + NickName: userRes.NickName.String, + Email: userRes.Email.String, + PhoneNumber: userRes.PhoneNumber.String, + Role: domain.Role(userRes.Role), + Age: int(userRes.Age.Int32), + EducationLevel: userRes.EducationLevel.String, + Country: userRes.Country.String, + Region: userRes.Region.String, + EmailVerified: userRes.EmailVerified, + PhoneVerified: userRes.PhoneVerified, + Suspended: userRes.Suspended, + SuspendedAt: userRes.SuspendedAt.Time, + OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid}, + CreatedAt: userRes.CreatedAt.Time, + UpdatedAt: userRes.UpdatedAt.Time, }, 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 - COUNT(*) as total, - SUM(CASE WHEN suspended = false THEN 1 ELSE 0 END) as active, - SUM(CASE WHEN suspended = true THEN 1 ELSE 0 END) as inactive - FROM users WHERE role = 'customer'` +// UpdatePassword updates a user's password +func (s *Store) UpdatePassword(ctx context.Context, password, email, phone string, organizationID int64, updatedAt time.Time) error { + return s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{ + Password: []byte(password), + Email: pgtype.Text{String: email}, + PhoneNumber: pgtype.Text{String: phone}, + UpdatedAt: pgtype.Timestamptz{Time: updatedAt}, + OrganizationID: pgtype.Int8{Int64: organizationID}, + }) +} - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.CompanyID.Valid { - query += fmt.Sprintf(" AND company_id = $%d", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ - } - if filter.BranchID.Valid { - query += fmt.Sprintf(" AND id IN (SELECT user_id FROM branch_cashiers WHERE branch_id = $%d)", argPos) - args = append(args, filter.BranchID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - row := s.conn.QueryRow(ctx, query, args...) - err = row.Scan(&total, &active, &inactive) +// GetOwnerByOrganizationID retrieves the owner user of an organization +func (s *Store) GetOwnerByOrganizationID(ctx context.Context, organizationID int64) (domain.User, error) { + userRes, err := s.queries.GetOwnerByOrganizationID(ctx, organizationID) if err != nil { - return 0, 0, 0, fmt.Errorf("failed to get customer counts: %w", err) + return domain.User{}, err } - - return total, active, inactive, nil + return mapUser(userRes), nil } -// GetCustomerDetails returns customer details map -func (s *Store) GetCustomerDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.CustomerDetail, error) { - query := `SELECT id, first_name, last_name - FROM users WHERE role = 'customer'` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.CompanyID.Valid { - query += fmt.Sprintf(" AND company_id = $%d", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ +// mapUser converts dbgen.User to domain.User +func mapUser(u dbgen.User) domain.User { + return domain.User{ + ID: u.ID, + FirstName: u.FirstName, + LastName: u.LastName, + NickName: u.NickName.String, + Email: u.Email.String, + PhoneNumber: u.PhoneNumber.String, + Role: domain.Role(u.Role), + Age: int(u.Age.Int32), + EducationLevel: u.EducationLevel.String, + Country: u.Country.String, + Region: u.Region.String, + EmailVerified: u.EmailVerified, + PhoneVerified: u.PhoneVerified, + Suspended: u.Suspended, + SuspendedAt: u.SuspendedAt.Time, + OrganizationID: domain.ValidInt64{Value: u.OrganizationID.Int64, Valid: u.OrganizationID.Valid}, + CreatedAt: u.CreatedAt.Time, + UpdatedAt: u.UpdatedAt.Time, } - if filter.BranchID.Valid { - query += fmt.Sprintf(" AND id IN (SELECT user_id FROM branch_cashiers WHERE branch_id = $%d)", argPos) - args = append(args, filter.BranchID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - return nil, fmt.Errorf("failed to query customer details: %w", err) - } - defer rows.Close() - - details := make(map[int64]domain.CustomerDetail) - for rows.Next() { - var id int64 - var firstName, lastName string - if err := rows.Scan(&id, &firstName, &lastName); err != nil { - return nil, fmt.Errorf("failed to scan customer detail: %w", err) - } - details[id] = domain.CustomerDetail{ - Name: fmt.Sprintf("%s %s", firstName, lastName), - } - } - - if err = rows.Err(); err != nil { - return nil, fmt.Errorf("rows error: %w", err) - } - - return details, nil -} - -// GetBranchCustomerCounts returns customer counts per branch -func (s *Store) GetBranchCustomerCounts(ctx context.Context, filter domain.ReportFilter) (map[int64]int64, error) { - query := `SELECT branch_id, COUNT(DISTINCT user_id) - FROM branch_cashiers - JOIN users ON branch_cashiers.user_id = users.id - WHERE users.role = 'customer'` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.CompanyID.Valid { - query += fmt.Sprintf(" AND branch_id IN (SELECT id FROM branches WHERE company_id = $%d)", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ - } - if filter.BranchID.Valid { - query += fmt.Sprintf(" AND branch_id = $%d", argPos) - args = append(args, filter.BranchID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND users.created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND users.created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - query += " GROUP BY branch_id" - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - return nil, fmt.Errorf("failed to query branch customer counts: %w", err) - } - defer rows.Close() - - counts := make(map[int64]int64) - for rows.Next() { - var branchID int64 - var count int64 - if err := rows.Scan(&branchID, &count); err != nil { - return nil, fmt.Errorf("failed to scan branch customer count: %w", err) - } - counts[branchID] = count - } - - if err = rows.Err(); err != nil { - return nil, fmt.Errorf("rows error: %w", err) - } - - return counts, nil -} - -func (s *Store) GetCustomerPreferences(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.CustomerPreferences, error) { - query := `WITH customer_sports AS ( - SELECT - b.user_id, - bo.sport_id, - COUNT(*) as bet_count, - ROW_NUMBER() OVER (PARTITION BY b.user_id ORDER BY COUNT(*) DESC) as sport_rank - FROM bets b - JOIN bet_outcomes bo ON b.id = bo.bet_id - WHERE b.user_id IS NOT NULL AND bo.sport_id IS NOT NULL - ), - customer_markets AS ( - SELECT - b.user_id, - bo.market_name, - COUNT(*) as bet_count, - ROW_NUMBER() OVER (PARTITION BY b.user_id ORDER BY COUNT(*) DESC) as market_rank - FROM bets b - JOIN bet_outcomes bo ON b.id = bo.bet_id - WHERE b.user_id IS NOT NULL AND bo.market_name IS NOT NULL - ` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.CompanyID.Valid { - query += fmt.Sprintf(" AND b.company_id = $%d", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ - } - if filter.BranchID.Valid { - query += fmt.Sprintf(" AND b.branch_id = $%d", argPos) - args = append(args, filter.BranchID.Value) - argPos++ - } - if filter.UserID.Valid { - query += fmt.Sprintf(" AND b.user_id = $%d", argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND b.created_at >= $%d", argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND b.created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - query += ` GROUP BY b.user_id, bo.sport_id - ), - favorite_sports AS ( - SELECT user_id, sport_id - FROM customer_sports - WHERE sport_rank = 1 - ), - favorite_markets AS ( - SELECT user_id, market_name - FROM customer_markets - WHERE market_rank = 1 - ) - SELECT - fs.user_id, - fs.sport_id as favorite_sport, - fm.market_name as favorite_market - FROM favorite_sports fs - LEFT JOIN favorite_markets fm ON fs.user_id = fm.user_id` - - rows, err := s.conn.Query(ctx, query, args...) - if err != nil { - return nil, fmt.Errorf("failed to query customer preferences: %w", err) - } - defer rows.Close() - - preferences := make(map[int64]domain.CustomerPreferences) - for rows.Next() { - var userID int64 - var pref domain.CustomerPreferences - if err := rows.Scan(&userID, &pref.FavoriteSport, &pref.FavoriteMarket); err != nil { - return nil, fmt.Errorf("failed to scan customer preference: %w", err) - } - preferences[userID] = pref - } - - if err = rows.Err(); err != nil { - return nil, fmt.Errorf("rows error: %w", err) - } - - return preferences, nil -} - -func (s *Store) GetRoleCounts(ctx context.Context, role string, filter domain.ReportFilter) (total, active, inactive int64, err error) { - query := `SELECT - COUNT(*) as total, - COUNT(CASE WHEN suspended = false THEN 1 END) as active, - COUNT(CASE WHEN suspended = true THEN 1 END) as inactive - FROM users WHERE role = $1` - - args := []interface{}{role} - argPos := 2 - - // Add filters if provided - if filter.CompanyID.Valid { - query += fmt.Sprintf(" AND company_id = $%d", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ - } - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %screated_at >= $%d", func() string { - if len(args) == 1 { // Only role parameter so far - return " " - } - return " AND " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - row := s.conn.QueryRow(ctx, query, args...) - err = row.Scan(&total, &active, &inactive) - if err != nil { - return 0, 0, 0, fmt.Errorf("failed to get %s counts: %w", role, err) - } - - return total, active, inactive, nil } diff --git a/internal/repository/virtual_game.go b/internal/repository/virtual_game.go deleted file mode 100644 index 651192a..0000000 --- a/internal/repository/virtual_game.go +++ /dev/null @@ -1,550 +0,0 @@ -package repository - -import ( - "context" - "database/sql" - "errors" - "fmt" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/jackc/pgx/v5/pgtype" -) - -type VirtualGameRepository interface { - CountVirtualGameProviders(ctx context.Context) (int64, error) - CreateVirtualGameProvider(ctx context.Context, arg dbgen.CreateVirtualGameProviderParams) (dbgen.VirtualGameProvider, error) - DeleteVirtualGameProvider(ctx context.Context, providerID string) error - DeleteAllVirtualGameProviders(ctx context.Context) error - GetVirtualGameProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) - ListVirtualGameProviders(ctx context.Context, limit, offset int32) ([]dbgen.VirtualGameProvider, error) - UpdateVirtualGameProviderEnabled(ctx context.Context, providerID string, enabled bool) (dbgen.VirtualGameProvider, error) - CreateVirtualGameSession(ctx context.Context, session *domain.VirtualGameSession) error - GetVirtualGameSessionByUserID(ctx context.Context, userID int64) (*domain.VirtualGameSession, error) - GetVirtualGameSessionByToken(ctx context.Context, token string) (*domain.VirtualGameSession, error) - // UpdateVirtualGameSessionStatus(ctx context.Context, id int64, status string) error - CreateVirtualGameTransaction(ctx context.Context, tx *domain.VirtualGameTransaction) error - GetVirtualGameTransactionByExternalID(ctx context.Context, externalID string) (*domain.VirtualGameTransaction, error) - UpdateVirtualGameTransactionStatus(ctx context.Context, id int64, status string) error - // WithTransaction(ctx context.Context, fn func(ctx context.Context) error) error - AddFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error - RemoveFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error - ListFavoriteGames( - ctx context.Context, - userID int64, - providerID *string, - limit, offset int32, - ) ([]dbgen.VirtualGame, error) - // GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) - GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error) - CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error - - CreateVirtualGame(ctx context.Context, arg dbgen.CreateVirtualGameParams) (dbgen.VirtualGame, error) - ListAllVirtualGames(ctx context.Context, arg dbgen.GetAllVirtualGamesParams) ([]dbgen.GetAllVirtualGamesRow, error) - RemoveAllVirtualGames(ctx context.Context) error - CreateVirtualGameProviderReport(ctx context.Context, report domain.CreateVirtualGameProviderReport) (domain.VirtualGameProviderReport, error) - CreateVirtualGameReport(ctx context.Context, report domain.CreateVirtualGameReport) (domain.VirtualGameReport, error) - GetVirtualGameProviderReportByProviderAndDate(ctx context.Context, providerID string, createdAt time.Time, reportType string) (domain.VirtualGameProviderReport, error) - UpdateVirtualGameProviderReportByDate(ctx context.Context, providerID string, reportDate time.Time, reportType string, totalGamesPlayed int64, totalBets float64, totalPayouts float64, totalPlayers int64) error - ListVirtualGameProviderReportsByGamesPlayedAsc(ctx context.Context) ([]domain.VirtualGameProviderReport, error) - ListVirtualGameProviderReportsByGamesPlayedDesc(ctx context.Context) ([]domain.VirtualGameProviderReport, error) -} - -type VirtualGameRepo struct { - store *Store -} - -// GetGameCounts implements VirtualGameRepository. -// func (r *VirtualGameRepo) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total int64, active int64, inactive int64, err error) { -// panic("unimplemented") -// } - -// func convertDBVirtualGameProvider(p dbgen.VirtualGameProvider) domain.VirtualGameProvider { -// var logoDark *string -// if p.LogoDark.Valid { -// logoDark = &p.LogoDark.String -// } -// var logoLight *string -// if p.LogoLight.Valid { -// logoLight = &p.LogoLight.String -// } -// return domain.VirtualGameProvider{ -// // ID: p.ID, -// ProviderID: p.ProviderID, -// ProviderName: p.ProviderName, -// LogoDark: logoDark, -// LogoLight: logoLight, -// Enabled: p.Enabled, -// CreatedAt: p.CreatedAt.Time, -// UpdatedAt: &p.UpdatedAt.Time, -// } -// } - -func ConvertCreateVirtualGameProvider(p domain.VirtualGameProvider) dbgen.CreateVirtualGameProviderParams { - return dbgen.CreateVirtualGameProviderParams{ - ProviderID: p.ProviderID, - ProviderName: p.ProviderName, - LogoDark: pgtype.Text{String: func() string { - if p.LogoDark != nil { - return *p.LogoDark - } - return "" - }(), Valid: p.LogoDark != nil}, - LogoLight: pgtype.Text{String: func() string { - if p.LogoLight != nil { - return *p.LogoLight - } - return "" - }(), Valid: p.LogoLight != nil}, - Enabled: p.Enabled, - // CreatedAt: time.Now(), - } -} - -func NewVirtualGameRepository(store *Store) VirtualGameRepository { - return &VirtualGameRepo{store: store} -} - -func (r *VirtualGameRepo) CreateVirtualGameProvider(ctx context.Context, arg dbgen.CreateVirtualGameProviderParams) (dbgen.VirtualGameProvider, error) { - return r.store.queries.CreateVirtualGameProvider(ctx, arg) -} - -func (r *VirtualGameRepo) RemoveVirtualGameProvider(ctx context.Context, providerID string) error { - return r.store.queries.DeleteVirtualGameProvider(ctx, providerID) -} - -func (r *VirtualGameRepo) DeleteAllVirtualGameProviders(ctx context.Context) error { - return r.store.queries.DeleteAllVirtualGameProviders(ctx) -} - -func (r *VirtualGameRepo) DeleteVirtualGameProvider(ctx context.Context, providerID string) error { - return r.store.queries.DeleteVirtualGameProvider(ctx, providerID) -} - -func (r *VirtualGameRepo) GetVirtualGameProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) { - return r.store.queries.GetVirtualGameProviderByID(ctx, providerID) -} - -func (r *VirtualGameRepo) ListVirtualGameProviders(ctx context.Context, limit, offset int32) ([]dbgen.VirtualGameProvider, error) { - args := dbgen.ListVirtualGameProvidersParams{ - Limit: limit, - Offset: offset, - } - return r.store.queries.ListVirtualGameProviders(ctx, args) -} - -func (r *VirtualGameRepo) UpdateVirtualGameProviderEnabled(ctx context.Context, providerID string, enabled bool) (dbgen.VirtualGameProvider, error) { - params := dbgen.UpdateVirtualGameProviderEnabledParams{ - ProviderID: providerID, - Enabled: enabled, - } - return r.store.queries.UpdateVirtualGameProviderEnabled(ctx, params) -} - -func (r *VirtualGameRepo) AddFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error { - params := dbgen.AddFavoriteGameParams{ - UserID: userID, - GameID: gameID, - ProviderID: providerID, - } - - return r.store.queries.AddFavoriteGame(ctx, params) -} - -func (r *VirtualGameRepo) RemoveFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error { - params := dbgen.RemoveFavoriteGameParams{ - UserID: userID, - GameID: gameID, - ProviderID: providerID, - } - - return r.store.queries.RemoveFavoriteGame(ctx, params) -} - -func (r *VirtualGameRepo) ListFavoriteGames( - ctx context.Context, - userID int64, - providerID *string, - limit, offset int32, -) ([]dbgen.VirtualGame, error) { - - params := dbgen.GetUserFavoriteGamesPaginatedParams{ - UserID: userID, - Limit: limit, - Offset: offset, - } - - if providerID != nil { - params.Column2 = *providerID - } else { - params.Column2 = "" - } - - return r.store.queries.GetUserFavoriteGamesPaginated(ctx, params) -} - -func (r *VirtualGameRepo) CountVirtualGameProviders(ctx context.Context) (int64, error) { - return r.store.queries.CountVirtualGameProviders(ctx) -} - -func (r *VirtualGameRepo) CreateVirtualGameSession(ctx context.Context, session *domain.VirtualGameSession) error { - params := dbgen.CreateVirtualGameSessionParams{ - UserID: session.UserID, - GameID: session.GameID, - SessionToken: session.SessionToken, - // Currency: session.Currency, - // Status: session.Status, - // ExpiresAt: pgtype.Timestamptz{Time: session.ExpiresAt, Valid: true}, - } - _, err := r.store.queries.CreateVirtualGameSession(ctx, params) - return err -} - -func (r *VirtualGameRepo) GetVirtualGameSessionByUserID(ctx context.Context, userID int64) (*domain.VirtualGameSession, error) { - dbSession, err := r.store.queries.GetVirtualGameSessionByUserID(ctx, userID) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } - return nil, err - } - - return &domain.VirtualGameSession{ - ID: dbSession.ID, - UserID: dbSession.UserID, - GameID: dbSession.GameID, - SessionToken: dbSession.SessionToken, - CreatedAt: dbSession.CreatedAt.Time, - UpdatedAt: dbSession.UpdatedAt.Time, - }, nil -} - -func (r *VirtualGameRepo) GetVirtualGameSessionByToken(ctx context.Context, token string) (*domain.VirtualGameSession, error) { - dbSession, err := r.store.queries.GetVirtualGameSessionByToken(ctx, token) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } - return nil, err - } - return &domain.VirtualGameSession{ - ID: dbSession.ID, - UserID: dbSession.UserID, - GameID: dbSession.GameID, - SessionToken: dbSession.SessionToken, - // Currency: dbSession.Currency, - // Status: dbSession.Status, - CreatedAt: dbSession.CreatedAt.Time, - UpdatedAt: dbSession.UpdatedAt.Time, - // ExpiresAt: dbSession.ExpiresAt.Time, - }, nil -} - -// func (r *VirtualGameRepo) UpdateVirtualGameSessionStatus(ctx context.Context, id int64, status string) error { -// return r.store.queries.UpdateVirtualGameSessionStatus(ctx, dbgen.UpdateVirtualGameSessionStatusParams{ -// ID: id, -// Status: status, -// }) -// } - -func (r *VirtualGameRepo) CreateVirtualGameTransaction(ctx context.Context, tx *domain.VirtualGameTransaction) error { - params := dbgen.CreateVirtualGameTransactionParams{ - // SessionID: tx.SessionID, - UserID: tx.UserID, - WalletID: tx.WalletID, - TransactionType: tx.TransactionType, - Amount: tx.Amount, - Currency: tx.Currency, - ExternalTransactionID: tx.ExternalTransactionID, - Status: tx.Status, - } - _, err := r.store.queries.CreateVirtualGameTransaction(ctx, params) - return err -} - -func (r *VirtualGameRepo) CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error { - params := dbgen.CreateVirtualGameHistoryParams{ - // SessionID: pgtype.Text{String: his.SessionID, Valid: true}, - UserID: his.UserID, - // WalletID: pgtype.Int8{Int64: *his.WalletID, Valid: true}, - TransactionType: his.TransactionType, - Amount: his.Amount, - Currency: his.Currency, - ExternalTransactionID: his.ExternalTransactionID, - Status: his.Status, - } - _, err := r.store.queries.CreateVirtualGameHistory(ctx, params) - return err -} - -func (r *VirtualGameRepo) GetVirtualGameTransactionByExternalID(ctx context.Context, externalID string) (*domain.VirtualGameTransaction, error) { - dbTx, err := r.store.queries.GetVirtualGameTransactionByExternalID(ctx, externalID) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } - return nil, err - } - return &domain.VirtualGameTransaction{ - ID: dbTx.ID, - // SessionID: dbTx.SessionID, - UserID: dbTx.UserID, - WalletID: dbTx.WalletID, - TransactionType: dbTx.TransactionType, - Amount: dbTx.Amount, - Currency: dbTx.Currency, - ExternalTransactionID: dbTx.ExternalTransactionID, - Status: dbTx.Status, - CreatedAt: dbTx.CreatedAt.Time, - UpdatedAt: dbTx.UpdatedAt.Time, - }, nil -} - -func (r *VirtualGameRepo) UpdateVirtualGameTransactionStatus(ctx context.Context, id int64, status string) error { - return r.store.queries.UpdateVirtualGameTransactionStatus(ctx, dbgen.UpdateVirtualGameTransactionStatusParams{ - ID: id, - Status: status, - }) -} - -// func (r *VirtualGameRepo) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) { -// query := `SELECT -// COUNT(*) as total, -// COUNT(CASE WHEN is_active = true THEN 1 END) as active, -// COUNT(CASE WHEN is_active = false THEN 1 END) as inactive -// FROM virtual_games` - -// args := []interface{}{} -// argPos := 1 - -// // Add filters if provided -// if filter.StartTime.Valid { -// query += fmt.Sprintf(" WHERE created_at >= $%d", argPos) -// args = append(args, filter.StartTime.Value) -// argPos++ -// } -// if filter.EndTime.Valid { -// query += fmt.Sprintf(" AND created_at <= $%d", argPos) -// args = append(args, filter.EndTime.Value) -// argPos++ -// } - -// row := r.store.conn.QueryRow(ctx, query, args...) -// err = row.Scan(&total, &active, &inactive) -// if err != nil { -// return 0, 0, 0, fmt.Errorf("failed to get game counts: %w", err) -// } - -// return total, active, inactive, nil -// } - -func (r *VirtualGameRepo) GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error) { - query := `SELECT game_id FROM virtual_game_histories WHERE user_id = $1 AND transaction_type = 'BET' ORDER BY created_at DESC LIMIT 100` - rows, err := r.store.conn.Query(ctx, query, userID) - if err != nil { - return nil, err - } - defer rows.Close() - - var history []domain.VirtualGameHistory - for rows.Next() { - var tx domain.VirtualGameHistory - if err := rows.Scan(&tx.GameID); err == nil { - history = append(history, tx) - } - } - return history, nil -} - -func (r *VirtualGameRepo) CreateVirtualGame(ctx context.Context, arg dbgen.CreateVirtualGameParams) (dbgen.VirtualGame, error) { - return r.store.queries.CreateVirtualGame(ctx, arg) -} - -func (r *VirtualGameRepo) ListAllVirtualGames(ctx context.Context, arg dbgen.GetAllVirtualGamesParams) ([]dbgen.GetAllVirtualGamesRow, error) { - return r.store.queries.GetAllVirtualGames(ctx, arg) -} - -func (r *VirtualGameRepo) RemoveAllVirtualGames(ctx context.Context) error { - return r.store.queries.DeleteAllVirtualGames(ctx) -} - -func (r *VirtualGameRepo) CreateVirtualGameProviderReport( - ctx context.Context, - report domain.CreateVirtualGameProviderReport, -) (domain.VirtualGameProviderReport, error) { - dbReport, err := r.store.queries.CreateVirtualGameProviderReport( - ctx, - ConvertCreateVirtualGameProviderReport(report), - ) - if err != nil { - return domain.VirtualGameProviderReport{}, err - } - - return ConvertDBVirtualGameProviderReport(dbReport), nil -} - -func (r *VirtualGameRepo) CreateVirtualGameReport( - ctx context.Context, - report domain.CreateVirtualGameReport, -) (domain.VirtualGameReport, error) { - dbReport, err := r.store.queries.CreateVirtualGameReport( - ctx, - ConvertCreateVirtualGameReport(report), - ) - if err != nil { - return domain.VirtualGameReport{}, err - } - - return ConvertDBVirtualGameReport(dbReport), nil -} - -func (r *VirtualGameRepo) GetVirtualGameProviderReportByProviderAndDate( - ctx context.Context, - providerID string, - reportDate time.Time, - reportType string, -) (domain.VirtualGameProviderReport, error) { - arg := dbgen.GetVirtualGameProviderReportByProviderAndDateParams{ - ProviderID: providerID, - ReportDate: pgtype.Date{Time: reportDate, Valid: true}, - ReportType: pgtype.Text{String: reportType, Valid: true}, - } - - dbReport, err := r.store.queries.GetVirtualGameProviderReportByProviderAndDate(ctx, arg) - if err != nil { - return domain.VirtualGameProviderReport{}, err - } - - return ConvertDBVirtualGameProviderReport(dbReport), nil -} - -func (r *VirtualGameRepo) UpdateVirtualGameProviderReportByDate( - ctx context.Context, - providerID string, - reportDate time.Time, - reportType string, - totalGamesPlayed int64, - totalBets float64, - totalPayouts float64, - totalPlayers int64, -) error { - arg := dbgen.UpdateVirtualGameProviderReportByDateParams{ - ProviderID: providerID, - ReportDate: pgtype.Date{Time: reportDate, Valid: true}, - ReportType: pgtype.Text{String: reportType, Valid: true}, - TotalGamesPlayed: pgtype.Int8{Int64: totalGamesPlayed, Valid: true}, - TotalBets: pgtype.Numeric{}, - TotalPayouts: pgtype.Numeric{}, - TotalPlayers: pgtype.Int8{Int64: totalPlayers, Valid: true}, - } - - // Safely convert float64 → pgtype.Numeric - if err := arg.TotalBets.Scan(totalBets); err != nil { - return fmt.Errorf("failed to set total_bets: %w", err) - } - if err := arg.TotalPayouts.Scan(totalPayouts); err != nil { - return fmt.Errorf("failed to set total_payouts: %w", err) - } - - if err := r.store.queries.UpdateVirtualGameProviderReportByDate(ctx, arg); err != nil { - return fmt.Errorf("failed to update provider report for %s: %w", providerID, err) - } - - return nil -} - -func (r *VirtualGameRepo) ListVirtualGameProviderReportsByGamesPlayedAsc( - ctx context.Context, -) ([]domain.VirtualGameProviderReport, error) { - dbReports, err := r.store.queries.ListVirtualGameProviderReportsByGamesPlayedAsc(ctx) - if err != nil { - return nil, err - } - - reports := make([]domain.VirtualGameProviderReport, len(dbReports)) - for i, r := range dbReports { - reports[i] = ConvertDBVirtualGameProviderReport(r) - } - - return reports, nil -} - -func (r *VirtualGameRepo) ListVirtualGameProviderReportsByGamesPlayedDesc( - ctx context.Context, -) ([]domain.VirtualGameProviderReport, error) { - dbReports, err := r.store.queries.ListVirtualGameProviderReportsByGamesPlayedDesc(ctx) - if err != nil { - return nil, err - } - - reports := make([]domain.VirtualGameProviderReport, len(dbReports)) - for i, r := range dbReports { - reports[i] = ConvertDBVirtualGameProviderReport(r) - } - - return reports, nil -} - -func ConvertCreateVirtualGameProviderReport(r domain.CreateVirtualGameProviderReport) dbgen.CreateVirtualGameProviderReportParams { - // var totalBets, totalPayouts pgtype.Numeric - - // _ = r.TotalBets. - // _ = totalPayouts.Set(r.TotalPayouts) - - return dbgen.CreateVirtualGameProviderReportParams{ - ProviderID: r.ProviderID, - ReportDate: pgtype.Date{Time: r.ReportDate, Valid: true}, - TotalGamesPlayed: pgtype.Int8{Int64: r.TotalGamesPlayed, Valid: true}, - TotalBets: pgtype.Numeric{Exp: int32(r.TotalBets)}, - TotalPayouts: pgtype.Numeric{Exp: int32(r.TotalPayouts)}, - TotalPlayers: pgtype.Int8{Int64: r.TotalPlayers, Valid: true}, - Column7: pgtype.Text{String: r.ReportType, Valid: true}, - } -} - -func ConvertDBVirtualGameProviderReport(db dbgen.VirtualGameProviderReport) domain.VirtualGameProviderReport { - return domain.VirtualGameProviderReport{ - ID: db.ID, - ProviderID: db.ProviderID, - ReportDate: db.ReportDate.Time, - TotalGamesPlayed: db.TotalGamesPlayed.Int64, - TotalBets: float64(db.TotalBets.Exp), - TotalPayouts: float64(db.TotalPayouts.Exp), - TotalProfit: float64(db.TotalProfit.Exp), - TotalPlayers: db.TotalPlayers.Int64, - ReportType: db.ReportType.String, - CreatedAt: db.CreatedAt.Time, - UpdatedAt: db.UpdatedAt.Time, - } -} - -func ConvertCreateVirtualGameReport(r domain.CreateVirtualGameReport) dbgen.CreateVirtualGameReportParams { - return dbgen.CreateVirtualGameReportParams{ - GameID: r.GameID, - ProviderID: r.ProviderID, - ReportDate: pgtype.Date{Time: r.ReportDate}, - TotalRounds: pgtype.Int8{Int64: r.TotalRounds}, - TotalBets: pgtype.Numeric{Exp: int32(r.TotalBets)}, - TotalPayouts: pgtype.Numeric{Exp: int32(r.TotalPayouts)}, - TotalPlayers: pgtype.Int8{Int64: r.TotalPlayers}, - Column8: r.ReportType, - } -} - -func ConvertDBVirtualGameReport(db dbgen.VirtualGameReport) domain.VirtualGameReport { - return domain.VirtualGameReport{ - ID: db.ID, - GameID: db.GameID, - ProviderID: db.ProviderID, - ReportDate: db.ReportDate.Time, - TotalRounds: db.TotalRounds.Int64, - TotalBets: float64(db.TotalBets.Exp), - TotalPayouts: float64(db.TotalPayouts.Exp), - TotalProfit: float64(db.TotalProfit.Exp), - TotalPlayers: db.TotalPlayers.Int64, - ReportType: db.ReportType.String, - CreatedAt: db.CreatedAt.Time, - UpdatedAt: db.UpdatedAt.Time, - } -} diff --git a/internal/repository/virtual_report.go b/internal/repository/virtual_report.go deleted file mode 100644 index a384220..0000000 --- a/internal/repository/virtual_report.go +++ /dev/null @@ -1,450 +0,0 @@ -package repository - -import ( - "context" - "fmt" - "time" - - 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" -) - -// NewVirtualGameReportStore returns a new VirtualGameReportStore interface -func NewVirtualGameReportStore(s *Store) ports.VirtualGameReportStore { - return s -} - -// ------------------- Financial Reports ------------------- -func (s *Store) CreateFinancialReport(ctx context.Context, report domain.FinancialReport) (domain.FinancialReport, error) { - // pgtype.Numeric no longer exposes a Set method in this pgx version; - // use zero-value pgtype.Numeric here (or replace with a proper conversion helper). - totalBets := pgtype.Numeric{} - totalWins := pgtype.Numeric{} - - // parse report.ReportDate (try RFC3339 then YYYY-MM-DD) - t, err := time.Parse(time.RFC3339, report.ReportDate) - if err != nil { - t, err = time.Parse("2006-01-02", report.ReportDate) - if err != nil { - return domain.FinancialReport{}, fmt.Errorf("invalid report date: %w", err) - } - } - - dbReport, err := s.queries.CreateFinancialReport(ctx, dbgen.CreateFinancialReportParams{ - GameID: report.GameID, - ProviderID: report.ProviderID, - ReportDate: pgtype.Date{Time: t}, - ReportType: report.ReportType, - TotalBets: totalBets, - TotalWins: totalWins, - }) - if err != nil { - return domain.FinancialReport{}, fmt.Errorf("failed to create financial report: %w", err) - } - return domain.ConvertDBFinancialReport(dbReport), nil -} - -func (s *Store) UpsertFinancialReport(ctx context.Context, report domain.FinancialReport) (domain.FinancialReport, error) { - totalBets := pgtype.Numeric{} - totalWins := pgtype.Numeric{} - - // parse report.ReportDate - t, err := time.Parse(time.RFC3339, report.ReportDate) - if err != nil { - t, err = time.Parse("2006-01-02", report.ReportDate) - if err != nil { - return domain.FinancialReport{}, fmt.Errorf("invalid report date: %w", err) - } - } - - dbReport, err := s.queries.UpsertFinancialReport(ctx, dbgen.UpsertFinancialReportParams{ - GameID: report.GameID, - ProviderID: report.ProviderID, - ReportDate: pgtype.Date{Time: t}, - ReportType: report.ReportType, - TotalBets: totalBets, - TotalWins: totalWins, - }) - if err != nil { - return domain.FinancialReport{}, fmt.Errorf("failed to upsert financial report: %w", err) - } - - return domain.ConvertDBFinancialReport(dbReport), nil -} - -// GetFinancialReportByID fetches a single report by its ID -func (s *Store) GetFinancialReportByID(ctx context.Context, id int64) (domain.FinancialReport, error) { - dbReport, err := s.queries.GetFinancialReportByID(ctx, id) - if err != nil { - return domain.FinancialReport{}, fmt.Errorf("failed to get financial report by ID: %w", err) - } - return domain.ConvertDBFinancialReport(dbReport), nil -} - -// GetFinancialReportsForGame fetches all reports for a specific game + provider in a date range -func (s *Store) GetFinancialReportsForGame(ctx context.Context, gameID, providerID string, from, to string) ([]domain.FinancialReport, error) { - fromDate, err := time.Parse("2006-01-02", from) - if err != nil { - return nil, fmt.Errorf("invalid from date: %w", err) - } - toDate, err := time.Parse("2006-01-02", to) - if err != nil { - return nil, fmt.Errorf("invalid to date: %w", err) - } - - dbReports, err := s.queries.GetFinancialReportsForGame(ctx, dbgen.GetFinancialReportsForGameParams{ - GameID: gameID, - ProviderID: providerID, - ReportDate: pgtype.Date{Time: fromDate}, - ReportDate_2: pgtype.Date{Time: toDate}, - }) - if err != nil { - return nil, fmt.Errorf("failed to get financial reports for game: %w", err) - } - - var result []domain.FinancialReport - for _, r := range dbReports { - result = append(result, domain.ConvertDBFinancialReport(r)) - } - - return result, nil -} - -func (s *Store) GetDailyFinancialReports(ctx context.Context, reportDate string) ([]domain.FinancialReport, error) { - t, err := time.Parse("2006-01-02", reportDate) - if err != nil { - return nil, fmt.Errorf("invalid report date: %w", err) - } - - dbReports, err := s.queries.GetDailyFinancialReports(ctx, pgtype.Date{Time: t}) - if err != nil { - return nil, fmt.Errorf("failed to get daily financial reports: %w", err) - } - - var result []domain.FinancialReport - for _, r := range dbReports { - result = append(result, domain.ConvertDBFinancialReport(r)) - } - - return result, nil -} - -// DeleteFinancialReport deletes a financial report by ID -func (s *Store) DeleteFinancialReport(ctx context.Context, id int64) error { - err := s.queries.DeleteFinancialReport(ctx, id) - if err != nil { - return fmt.Errorf("failed to delete financial report: %w", err) - } - return nil -} - -// CreateCompanyReport inserts a new company-level financial report -func (s *Store) CreateCompanyReport(ctx context.Context, report domain.CompanyReport) (domain.CompanyReport, error) { - // parse report date - t, err := time.Parse("2006-01-02", report.ReportDate) - if err != nil { - return domain.CompanyReport{}, fmt.Errorf("invalid report date: %w", err) - } - - dbReport, err := s.queries.CreateCompanyReport(ctx, dbgen.CreateCompanyReportParams{ - CompanyID: report.CompanyID, - ProviderID: report.ProviderID, - ReportDate: pgtype.Date{Time: t}, - ReportType: report.ReportType, - TotalBetAmount: pgtype.Numeric{}, // zero value; set actual value if required - TotalWinAmount: pgtype.Numeric{}, - }) - if err != nil { - return domain.CompanyReport{}, fmt.Errorf("failed to create company report: %w", err) - } - - return domain.ConvertDBCompanyReport(dbReport), nil -} - -func (s *Store) UpsertCompanyReport(ctx context.Context, report domain.CompanyReport) (domain.CompanyReport, error) { - // Convert report date - t, err := time.Parse("2006-01-02", report.ReportDate) - if err != nil { - t, err = time.Parse(time.RFC3339, report.ReportDate) - if err != nil { - return domain.CompanyReport{}, fmt.Errorf("invalid report date: %w", err) - } - } - - dbReport, err := s.queries.UpsertCompanyReport(ctx, dbgen.UpsertCompanyReportParams{ - CompanyID: report.CompanyID, - ProviderID: report.ProviderID, - ReportDate: pgtype.Date{Time: t}, - ReportType: report.ReportType, - TotalBetAmount: func() pgtype.Numeric { - var n pgtype.Numeric - _ = n.Scan(report.TotalBetAmount) - return n - }(), - TotalWinAmount: func() pgtype.Numeric { - var n pgtype.Numeric - _ = n.Scan(report.TotalWinAmount) - return n - }(), - }) - if err != nil { - return domain.CompanyReport{}, fmt.Errorf("failed to upsert company report: %w", err) - } - - return domain.ConvertDBCompanyReport(dbReport), nil -} - -func (s *Store) GetCompanyReportByID(ctx context.Context, id int64) (domain.CompanyReport, error) { - dbReport, err := s.queries.GetCompanyReportByID(ctx, id) - if err != nil { - return domain.CompanyReport{}, fmt.Errorf("failed to get company report by ID: %w", err) - } - - return domain.ConvertDBCompanyReport(dbReport), nil -} - -func (s *Store) GetCompanyReportsInRange(ctx context.Context, companyID int64, providerID string, startDate, endDate string) ([]domain.CompanyReport, error) { - start, err := time.Parse("2006-01-02", startDate) - if err != nil { - start, err = time.Parse(time.RFC3339, startDate) - if err != nil { - return nil, fmt.Errorf("invalid start date: %w", err) - } - } - - end, err := time.Parse("2006-01-02", endDate) - if err != nil { - end, err = time.Parse(time.RFC3339, endDate) - if err != nil { - return nil, fmt.Errorf("invalid end date: %w", err) - } - } - - dbReports, err := s.queries.GetCompanyReportsInRange(ctx, dbgen.GetCompanyReportsInRangeParams{ - CompanyID: companyID, - ProviderID: providerID, - ReportDate: pgtype.Date{Time: start}, - ReportDate_2: pgtype.Date{Time: end}, - }) - if err != nil { - return nil, fmt.Errorf("failed to get company reports in range: %w", err) - } - - reports := make([]domain.CompanyReport, 0, len(dbReports)) - for _, r := range dbReports { - reports = append(reports, domain.ConvertDBCompanyReport(r)) - } - - return reports, nil -} - -func (s *Store) GetCompanyProfitTrend(ctx context.Context, companyID int64, providerID string, startDate, endDate string) ([]domain.CompanyProfitTrend, error) { - start, err := time.Parse("2006-01-02", startDate) - if err != nil { - start, err = time.Parse(time.RFC3339, startDate) - if err != nil { - return nil, fmt.Errorf("invalid start date: %w", err) - } - } - - end, err := time.Parse("2006-01-02", endDate) - if err != nil { - end, err = time.Parse(time.RFC3339, endDate) - if err != nil { - return nil, fmt.Errorf("invalid end date: %w", err) - } - } - - rows, err := s.queries.GetCompanyProfitTrend(ctx, dbgen.GetCompanyProfitTrendParams{ - CompanyID: companyID, - ProviderID: providerID, - ReportDate: pgtype.Date{Time: start}, - ReportDate_2: pgtype.Date{Time: end}, - }) - if err != nil { - return nil, fmt.Errorf("failed to get company profit trend: %w", err) - } - - trends := make([]domain.CompanyProfitTrend, 0, len(rows)) - for _, r := range rows { - trends = append(trends, domain.CompanyProfitTrend{ - ReportDate: r.ReportDate.Time.Format("2006-01-02"), - TotalProfit: float64(r.TotalProfit), - }) - } - return trends, nil -} - -// CreatePlayerActivityReport inserts a new player activity report -func (s *Store) CreatePlayerActivityReport(ctx context.Context, report domain.PlayerActivityReport) (domain.PlayerActivityReport, error) { - t, err := time.Parse("2006-01-02", report.ReportDate) - if err != nil { - t, err = time.Parse(time.RFC3339, report.ReportDate) - if err != nil { - return domain.PlayerActivityReport{}, fmt.Errorf("invalid report date: %w", err) - } - } - - createParams := dbgen.CreatePlayerActivityReportParams{ - UserID: report.UserID, - ReportDate: pgtype.Date{Time: t}, - ReportType: report.ReportType, - TotalDeposits: func() pgtype.Numeric { - var n pgtype.Numeric - _ = n.Scan(report.TotalDeposits) - return n - }(), - TotalWithdrawals: func() pgtype.Numeric { - var n pgtype.Numeric - _ = n.Scan(report.TotalWithdrawals) - return n - }(), - TotalBetAmount: func() pgtype.Numeric { - var n pgtype.Numeric - _ = n.Scan(report.TotalBetAmount) - return n - }(), - TotalWinAmount: func() pgtype.Numeric { - var n pgtype.Numeric - _ = n.Scan(report.TotalWinAmount) - return n - }(), - RoundsPlayed: pgtype.Int8{Int64: int64(report.RoundsPlayed)}, - } - - var dbReport dbgen.VirtualGamePlayerActivityReport - dbReport, err = s.queries.CreatePlayerActivityReport(ctx, createParams) - if err != nil { - // try upsert as a fallback - upsertParams := dbgen.UpsertPlayerActivityReportParams{ - UserID: report.UserID, - ReportDate: pgtype.Date{Time: t}, - ReportType: report.ReportType, - TotalDeposits: pgtype.Numeric{Exp: int32(report.TotalDeposits)}, - TotalWithdrawals: pgtype.Numeric{Exp: int32(report.TotalWithdrawals)}, - TotalBetAmount: pgtype.Numeric{Exp: int32(report.TotalBetAmount)}, - TotalWinAmount: pgtype.Numeric{Exp: int32(report.TotalWinAmount)}, - RoundsPlayed: pgtype.Int8{Int64: int64(report.RoundsPlayed)}, - } - dbReport, err = s.queries.UpsertPlayerActivityReport(ctx, upsertParams) - if err != nil { - return domain.PlayerActivityReport{}, fmt.Errorf("failed to create or upsert player activity report: %w", err) - } - } - - return domain.ConvertDBPlayerActivityReport(dbReport), nil -} - -func (s *Store) GetPlayerActivityByID(ctx context.Context, id int64) (domain.PlayerActivityReport, error) { - dbReport, err := s.queries.GetPlayerActivityByID(ctx, id) - if err != nil { - return domain.PlayerActivityReport{}, err - } - return domain.ConvertDBPlayerActivityReport(dbReport), nil -} - -// GetPlayerActivityByDate fetches a player activity report for a specific user and date -func (s *Store) GetPlayerActivityByDate(ctx context.Context, userID int64, reportDate, reportType string) (domain.PlayerActivityReport, error) { - t, err := time.Parse("2006-01-02", reportDate) - if err != nil { - t, err = time.Parse(time.RFC3339, reportDate) - if err != nil { - return domain.PlayerActivityReport{}, fmt.Errorf("invalid report date: %w", err) - } - } - - dbReport, err := s.queries.GetPlayerActivityByDate(ctx, dbgen.GetPlayerActivityByDateParams{ - UserID: userID, - ReportDate: pgtype.Date{Time: t}, - ReportType: reportType, - }) - if err != nil { - return domain.PlayerActivityReport{}, err - } - - return domain.ConvertDBPlayerActivityReport(dbReport), nil -} - -// GetPlayerActivityRange fetches all activity reports for a user in a date range -func (s *Store) GetPlayerActivityRange(ctx context.Context, userID int64, startDate, endDate string) ([]domain.PlayerActivityReport, error) { - start, err := time.Parse("2006-01-02", startDate) - if err != nil { - start, err = time.Parse(time.RFC3339, startDate) - if err != nil { - return nil, fmt.Errorf("invalid start date: %w", err) - } - } - - end, err := time.Parse("2006-01-02", endDate) - if err != nil { - end, err = time.Parse(time.RFC3339, endDate) - if err != nil { - return nil, fmt.Errorf("invalid end date: %w", err) - } - } - - dbReports, err := s.queries.GetPlayerActivityRange(ctx, dbgen.GetPlayerActivityRangeParams{ - UserID: userID, - ReportDate: pgtype.Date{Time: start}, - ReportDate_2: pgtype.Date{Time: end}, - }) - if err != nil { - return nil, err - } - - activities := make([]domain.PlayerActivityReport, 0, len(dbReports)) - for _, r := range dbReports { - activities = append(activities, domain.ConvertDBPlayerActivityReport(r)) - } - return activities, nil -} - -// GetTopPlayersByNetResult returns the top N players by net result in a date range -func (s *Store) GetTopPlayersByNetResult(ctx context.Context, startDate, endDate string, limit int) ([]domain.TopPlayerNetResult, error) { - start, err := time.Parse("2006-01-02", startDate) - if err != nil { - start, err = time.Parse(time.RFC3339, startDate) - if err != nil { - return nil, fmt.Errorf("invalid start date: %w", err) - } - } - - end, err := time.Parse("2006-01-02", endDate) - if err != nil { - end, err = time.Parse(time.RFC3339, endDate) - if err != nil { - return nil, fmt.Errorf("invalid end date: %w", err) - } - } - - rows, err := s.conn.Query(ctx, `SELECT user_id, SUM(net_result) AS total_net - FROM virtual_game_player_activity_reports - WHERE report_date BETWEEN $1 AND $2 - GROUP BY user_id - ORDER BY total_net DESC - LIMIT $3`, start, end, limit) - if err != nil { - return nil, fmt.Errorf("failed to fetch top players: %w", err) - } - defer rows.Close() - - var topPlayers []domain.TopPlayerNetResult - for rows.Next() { - var tp domain.TopPlayerNetResult - var totalNet pgtype.Numeric - if err := rows.Scan(&tp.UserID, &totalNet); err != nil { - return nil, err - } - tp.TotalNet = float64(totalNet.Exp) - topPlayers = append(topPlayers, tp) - } - return topPlayers, nil -} - -// DeletePlayerActivityReport deletes a player activity report by its ID -func (s *Store) DeletePlayerActivityReport(ctx context.Context, id int64) error { - err := s.queries.DeletePlayerActivityReport(ctx, id) - return err -} diff --git a/internal/repository/wallet.go b/internal/repository/wallet.go deleted file mode 100644 index 8e113e5..0000000 --- a/internal/repository/wallet.go +++ /dev/null @@ -1,261 +0,0 @@ -package repository - -import ( - "context" - "fmt" - - 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 wallet store -func NewWalletStore(s *Store) ports.WalletStore { return s } - - -func (s *Store) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) { - 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 domain.ConvertDBWallet(newWallet), nil -} - -func (s *Store) CreateCustomerWallet(ctx context.Context, customerWallet domain.CreateCustomerWallet) (domain.CustomerWallet, error) { - - newCustomerWallet, err := s.queries.CreateCustomerWallet(ctx, domain.ConvertCreateCustomerWallet(customerWallet)) - if err != nil { - return domain.CustomerWallet{}, err - } - return domain.ConvertDBCustomerWallet(newCustomerWallet), nil -} - -func (s *Store) GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error) { - wallet, err := s.queries.GetWalletByID(ctx, id) - - if err != nil { - return domain.Wallet{}, err - } - return domain.ConvertDBWallet(wallet), nil -} - -func (s *Store) GetAllWallets(ctx context.Context) ([]domain.Wallet, error) { - wallets, err := s.queries.GetAllWallets(ctx) - if err != nil { - return nil, err - } - - var result []domain.Wallet = make([]domain.Wallet, 0, len(wallets)) - - for _, wallet := range wallets { - result = append(result, domain.ConvertDBWallet(wallet)) - } - return result, nil -} - -func (s *Store) GetWalletsByUser(ctx context.Context, userID int64) ([]domain.Wallet, error) { - wallets, err := s.queries.GetWalletByUserID(ctx, userID) - if err != nil { - return nil, err - } - - var result []domain.Wallet = make([]domain.Wallet, 0, len(wallets)) - - for _, wallet := range wallets { - result = append(result, domain.ConvertDBWallet(wallet)) - } - return result, nil -} - -func (s *Store) GetAllCustomerWallets(ctx context.Context) ([]domain.GetCustomerWallet, error) { - customerWallets, err := s.queries.GetAllCustomerWallet(ctx) - if err != nil { - return nil, err - } - - var result []domain.GetCustomerWallet = make([]domain.GetCustomerWallet, 0, len(customerWallets)) - for _, wallet := range customerWallets { - result = append(result, domain.ConvertDBGetCustomerWallet(wallet)) - } - return result, nil -} - -func (s *Store) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) { - customerWallet, err := s.queries.GetCustomerWallet(ctx, customerID) - - if err != nil { - return domain.GetCustomerWallet{}, err - } - return domain.ConvertDBGetCustomerWallet(customerWallet), nil -} - -func (s *Store) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error) { - wallets, err := s.queries.GetAllBranchWallets(ctx) - if err != nil { - return nil, err - } - - var result []domain.BranchWallet = make([]domain.BranchWallet, 0, len(wallets)) - - for _, wallet := range wallets { - result = append(result, domain.BranchWallet{ - ID: wallet.ID, - Balance: domain.Currency(wallet.Balance), - IsActive: wallet.IsActive, - UpdatedAt: wallet.UpdatedAt.Time, - CreatedAt: wallet.CreatedAt.Time, - Name: wallet.Name, - Location: wallet.Location, - BranchManagerID: wallet.BranchManagerID, - CompanyID: wallet.CompanyID, - IsSelfOwned: wallet.IsSelfOwned, - }) - } - return result, nil -} - -func (s *Store) UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error { - err := s.queries.UpdateBalance(ctx, dbgen.UpdateBalanceParams{ - ID: id, - Balance: int64(balance), - }) - return err -} - -func (s *Store) UpdateWalletActive(ctx context.Context, id int64, isActive bool) error { - err := s.queries.UpdateWalletActive(ctx, dbgen.UpdateWalletActiveParams{ - ID: id, - IsActive: isActive, - }) - return err -} - -func (s *Store) GetCompanyByWalletID(ctx context.Context, walletID int64) (domain.Company, error) { - dbCompany, err := s.queries.GetCompanyByWalletID(ctx, walletID) - if err != nil { - return domain.Company{}, err - } - - return domain.Company{ - ID: dbCompany.ID, - Name: dbCompany.Name, - AdminID: dbCompany.AdminID, - WalletID: dbCompany.WalletID, - }, nil -} - -func (s *Store) GetBranchByWalletID(ctx context.Context, walletID int64) (domain.Branch, error) { - dbBranch, err := s.queries.GetBranchByWalletID(ctx, walletID) - if err != nil { - return domain.Branch{}, err - } - - return domain.Branch{ - ID: dbBranch.ID, - Name: dbBranch.Name, - Location: dbBranch.Location, - IsActive: dbBranch.IsActive, - WalletID: dbBranch.WalletID, - BranchManagerID: dbBranch.BranchManagerID, - CompanyID: dbBranch.CompanyID, - IsSelfOwned: dbBranch.IsSelfOwned, - // Creat: dbBranch.CreatedAt.Time, - // UpdatedAt: dbBranch.UpdatedAt.Time, - }, nil -} - -// GetBalanceSummary returns wallet balance summary -func (s *Store) GetBalanceSummary(ctx context.Context, filter domain.ReportFilter) (domain.BalanceSummary, error) { - var summary domain.BalanceSummary - - query := `SELECT - COALESCE(SUM(balance), 0) as total_balance, - COALESCE(SUM(CASE WHEN is_active = true THEN balance ELSE 0 END), 0) as active_balance, - COALESCE(SUM(CASE WHEN is_active = false THEN balance ELSE 0 END), 0) as inactive_balance, - COALESCE(SUM(CASE WHEN is_bettable = true THEN balance ELSE 0 END), 0) as bettable_balance, - COALESCE(SUM(CASE WHEN is_bettable = false THEN balance ELSE 0 END), 0) as non_bettable_balance - FROM wallets` - - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.CompanyID.Valid { - query += fmt.Sprintf(" WHERE user_id IN (SELECT id FROM users WHERE company_id = $%d)", argPos) - args = append(args, filter.CompanyID.Value) - argPos++ - } else if filter.BranchID.Valid { - query += fmt.Sprintf(" WHERE user_id IN (SELECT user_id FROM branch_cashiers WHERE branch_id = $%d)", argPos) - args = append(args, filter.BranchID.Value) - argPos++ - } else if filter.UserID.Valid { - query += fmt.Sprintf(" WHERE user_id = $%d", argPos) - args = append(args, filter.UserID.Value) - argPos++ - } - - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %screated_at >= $%d", func() string { - if len(args) == 0 { - return "" - } - return " " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - row := s.conn.QueryRow(ctx, query, args...) - err := row.Scan( - &summary.TotalBalance, - &summary.ActiveBalance, - &summary.InactiveBalance, - &summary.BettableBalance, - &summary.NonBettableBalance, - ) - if err != nil { - return domain.BalanceSummary{}, fmt.Errorf("failed to get balance summary: %w", err) - } - - return summary, nil -} - -func (s *Store) GetTotalWallets(ctx context.Context, filter domain.ReportFilter) (int64, error) { - query := `SELECT COUNT(*) FROM wallets WHERE is_active = true` - args := []interface{}{} - argPos := 1 - - // Add filters if provided - if filter.StartTime.Valid { - query += fmt.Sprintf(" AND %screated_at >= $%d", func() string { - if len(args) == 0 { - return " WHERE " - } - return " AND " - }(), argPos) - args = append(args, filter.StartTime.Value) - argPos++ - } - if filter.EndTime.Valid { - query += fmt.Sprintf(" AND created_at <= $%d", argPos) - args = append(args, filter.EndTime.Value) - argPos++ - } - - var total int64 - row := s.conn.QueryRow(ctx, query, args...) - err := row.Scan(&total) - if err != nil { - return 0, fmt.Errorf("failed to get wallet counts: %w", err) - } - - return total, nil -} diff --git a/internal/repository/wallet_stats.go b/internal/repository/wallet_stats.go deleted file mode 100644 index feaa94d..0000000 --- a/internal/repository/wallet_stats.go +++ /dev/null @@ -1,37 +0,0 @@ -package repository - -import ( - "context" - - 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 wallet stats store -func NewWalletStatStore(s *Store) ports.WalletStatStore { return s } - -func (s *Store) UpdateWalletStats(ctx context.Context) error { - return s.queries.UpdateWalletStats(ctx) -} - -func (s *Store) GetWalletStatByID(ctx context.Context, walletID int64) ([]domain.WalletStat, error) { - stats, err := s.queries.GetWalletStatsByID(ctx, walletID) - if err != nil { - return nil, err - } - - return domain.ConvertDBWalletStatsList(stats), nil -} - -func (s *Store) GetWalletStatsByInterval(ctx context.Context, filter domain.WalletStatFilter) ([]domain.WalletStat, error) { - stats, err := s.queries.GetWalletStats(ctx, dbgen.GetWalletStatsParams{ - Interval: filter.Interval.ToPG(), - UserID: filter.UserID.ToPG(), - }) - if err != nil { - return nil, err - } - - return domain.ConvertDBWalletStatsByIntervalList(stats), nil -} diff --git a/internal/services/arifpay/service.go b/internal/services/arifpay/service.go index dc42eea..394f6f0 100644 --- a/internal/services/arifpay/service.go +++ b/internal/services/arifpay/service.go @@ -4,32 +4,29 @@ import ( "bytes" "context" "encoding/json" - "errors" "fmt" "io" "net/http" "strings" - "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" + "Yimaru-Backend/internal/config" + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/services/transaction" + "github.com/google/uuid" ) type ArifpayService struct { - cfg *config.Config - transferStore ports.TransferStore - walletSvc *wallet.Service - httpClient *http.Client + cfg *config.Config + transactionSvc transaction.Service + httpClient *http.Client } -func NewArifpayService(cfg *config.Config, transferStore ports.TransferStore, walletSvc *wallet.Service, httpClient *http.Client) *ArifpayService { +func NewArifpayService(cfg *config.Config, transactionSvc transaction.Service, httpClient *http.Client) *ArifpayService { return &ArifpayService{ - cfg: cfg, - transferStore: transferStore, - walletSvc: walletSvc, - httpClient: httpClient, + cfg: cfg, + transactionSvc: transactionSvc, + httpClient: httpClient, } } @@ -124,22 +121,22 @@ func (s *ArifpayService) CreateCheckoutSession(req domain.CheckoutSessionClientR // paymentURL := data["paymentUrl"].(string) // Store transfer in DB - transfer := domain.CreateTransfer{ - Amount: domain.Currency(req.Amount), - Verified: false, - Type: domain.DEPOSIT, - ReferenceNumber: nonce, - SessionID: fmt.Sprintf("%v", data["sessionId"]), - Status: string(domain.PaymentStatusPending), - CashierID: domain.ValidInt64{ - Value: userId, - Valid: true, - }, - } + // transfer := domain.CreateTransfer{ + // Amount: domain.Currency(req.Amount), + // Verified: false, + // Type: domain.DEPOSIT, + // ReferenceNumber: nonce, + // SessionID: fmt.Sprintf("%v", data["sessionId"]), + // Status: string(domain.PaymentStatusPending), + // CashierID: domain.ValidInt64{ + // Value: userId, + // Valid: true, + // }, + // } - if _, err := s.transferStore.CreateTransfer(context.Background(), transfer); err != nil { - return nil, err - } + // if _, err := s.transactionSvc(context.Background(), transfer); err != nil { + // return nil, err + // } return data, nil } @@ -187,21 +184,21 @@ func (s *ArifpayService) CancelCheckoutSession(ctx context.Context, sessionID st func (s *ArifpayService) ProcessWebhook(ctx context.Context, req domain.WebhookRequest, isDeposit bool) error { // 1. Get transfer by SessionID - transfer, err := s.transferStore.GetTransferByReference(ctx, req.Nonce) - if err != nil { - return err - } + // transfer, err := s.transactionSvc.GetTransferByReference(ctx, req.Nonce) + // if err != nil { + // return err + // } - userId := transfer.DepositorID.Value + // userId := transfer.DepositorID.Value - wallet, err := s.walletSvc.GetCustomerWallet(ctx, userId) - if err != nil { - return err - } + // wallet, err := s.walletSvc.GetCustomerWallet(ctx, userId) + // if err != nil { + // return err + // } - if transfer.Verified { - return errors.New("transfer already verified") - } + // if transfer.Verified { + // return errors.New("transfer already verified") + // } // 2. Update transfer status newStatus := strings.ToLower(req.Transaction.TransactionStatus) @@ -209,60 +206,61 @@ func (s *ArifpayService) ProcessWebhook(ctx context.Context, req domain.WebhookR // newStatus = req.Transaction.TransactionStatus // } - err = s.transferStore.UpdateTransferStatus(ctx, transfer.ID, newStatus) - if err != nil { - return err - } + // err = s.transferStore.UpdateTransferStatus(ctx, transfer.ID, newStatus) + // if err != nil { + // return err + // } - err = s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true) - if err != nil { - return err - } + // err = s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true) + // if err != nil { + // return err + // } // 3. If SUCCESS -> update customer wallet balance if (newStatus == "success" && isDeposit) || (newStatus == "failed" && !isDeposit) { - _, err = s.walletSvc.AddToWallet(ctx, wallet.RegularID, domain.Currency(req.TotalAmount), domain.ValidInt64{}, transfer.PaymentMethod, domain.PaymentDetails{ - ReferenceNumber: domain.ValidString{ - Value: req.Nonce, - Valid: true, - }, - BankNumber: domain.ValidString{ - Value: "", - Valid: false, - }, - }, "") - if err != nil { - return err - } - } + // _, err = s.walletSvc.AddToWallet(ctx, wallet.RegularID, domain.Currency(req.TotalAmount), domain.ValidInt64{}, transfer.PaymentMethod, domain.PaymentDetails{ + // ReferenceNumber: domain.ValidString{ + // Value: req.Nonce, + // Valid: true, + // }, + // BankNumber: domain.ValidString{ + // Value: "", + // Valid: false, + // }, + // }, "") + // if err != nil { + // return err + // } + // } + } return nil } func (s *ArifpayService) ExecuteTelebirrB2CTransfer(ctx context.Context, req domain.CheckoutSessionClientRequest, userId int64) error { // Step 1: Create Session - userWallet, err := s.walletSvc.GetCustomerWallet(ctx, userId) - if err != nil { - return fmt.Errorf("failed to get user wallets: %w", err) - } + // userWallet, err := s.walletSvc.GetCustomerWallet(ctx, userId) + // if err != nil { + // return fmt.Errorf("failed to get user wallets: %w", err) + // } // if len(userWallets) == 0 { // return fmt.Errorf("no wallet found for user %d", userId) // } - _, err = s.walletSvc.DeductFromWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - "", - ) - if err != nil { - return fmt.Errorf("failed to deduct from wallet: %w", err) - } + // _, err = s.walletSvc.DeductFromWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // "", + // ) + // if err != nil { + // return fmt.Errorf("failed to deduct from wallet: %w", err) + // } - referenceNum := uuid.NewString() + // referenceNum := uuid.NewString() sessionReq := domain.CheckoutSessionClientRequest{ Amount: req.Amount, @@ -272,18 +270,18 @@ func (s *ArifpayService) ExecuteTelebirrB2CTransfer(ctx context.Context, req dom sessionResp, err := s.CreateCheckoutSession(sessionReq, false, userId) if err != nil { - _, err = s.walletSvc.AddToWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - domain.PaymentDetails{}, - "", - ) - if err != nil { - return fmt.Errorf("failed to deduct from wallet: %w", err) - } + // _, err = s.walletSvc.AddToWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // domain.PaymentDetails{}, + // "", + // ) + // if err != nil { + // return fmt.Errorf("failed to deduct from wallet: %w", err) + // } return fmt.Errorf("failed to create session: %w", err) } @@ -298,35 +296,35 @@ func (s *ArifpayService) ExecuteTelebirrB2CTransfer(ctx context.Context, req dom payload, err := json.Marshal(reqBody) if err != nil { - _, err = s.walletSvc.AddToWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - domain.PaymentDetails{}, - "", - ) - if err != nil { - return fmt.Errorf("failed to deduct from wallet: %w", err) - } + // _, err = s.walletSvc.AddToWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // domain.PaymentDetails{}, + // "", + // ) + // if err != nil { + // return fmt.Errorf("failed to deduct from wallet: %w", err) + // } return fmt.Errorf("failed to marshal transfer request: %w", err) } transferReq, err := http.NewRequestWithContext(ctx, http.MethodPost, transferURL, bytes.NewBuffer(payload)) if err != nil { - _, err = s.walletSvc.AddToWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - domain.PaymentDetails{}, - "", - ) - if err != nil { - return fmt.Errorf("failed to deduct from wallet: %w", err) - } + // _, err = s.walletSvc.AddToWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // domain.PaymentDetails{}, + // "", + // ) + // if err != nil { + // return fmt.Errorf("failed to deduct from wallet: %w", err) + // } return fmt.Errorf("failed to build transfer request: %w", err) } transferReq.Header.Set("Content-Type", "application/json") @@ -334,56 +332,56 @@ func (s *ArifpayService) ExecuteTelebirrB2CTransfer(ctx context.Context, req dom transferResp, err := s.httpClient.Do(transferReq) if err != nil { - _, err = s.walletSvc.AddToWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - domain.PaymentDetails{}, - "", - ) - if err != nil { - return fmt.Errorf("failed to deduct from wallet: %w", err) - } + // _, err = s.walletSvc.AddToWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // domain.PaymentDetails{}, + // "", + // ) + // if err != nil { + // return fmt.Errorf("failed to deduct from wallet: %w", err) + // } return fmt.Errorf("failed to execute transfer request: %w", err) } defer transferResp.Body.Close() if transferResp.StatusCode != http.StatusOK { - _, err = s.walletSvc.AddToWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - domain.PaymentDetails{}, - "", - ) - if err != nil { - return fmt.Errorf("failed to deduct from wallet: %w", err) - } + // _, err = s.walletSvc.AddToWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // domain.PaymentDetails{}, + // "", + // ) + // if err != nil { + // return fmt.Errorf("failed to deduct from wallet: %w", err) + // } body, _ := io.ReadAll(transferResp.Body) return fmt.Errorf("transfer failed with status %d: %s", transferResp.StatusCode, string(body)) } // Step 3: Store transfer in DB - transfer := domain.CreateTransfer{ - Amount: domain.Currency(req.Amount), - Verified: false, - Type: domain.WITHDRAW, // B2C = payout - ReferenceNumber: referenceNum, - SessionID: fmt.Sprintf("%v", sessionRespData["sessionId"]), - Status: string(domain.PaymentStatusPending), - PaymentMethod: domain.TRANSFER_ARIFPAY, - CashierID: domain.ValidInt64{ - Value: userId, - Valid: true, - }, - } - if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil { - return fmt.Errorf("failed to store transfer: %w", err) - } + // transfer := domain.CreateTransfer{ + // Amount: domain.Currency(req.Amount), + // Verified: false, + // Type: domain.WITHDRAW, // B2C = payout + // ReferenceNumber: referenceNum, + // SessionID: fmt.Sprintf("%v", sessionRespData["sessionId"]), + // Status: string(domain.PaymentStatusPending), + // PaymentMethod: domain.TRANSFER_ARIFPAY, + // CashierID: domain.ValidInt64{ + // Value: userId, + // Valid: true, + // }, + // } + // if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil { + // return fmt.Errorf("failed to store transfer: %w", err) + // } // Step 4: Deduct from wallet @@ -392,24 +390,24 @@ func (s *ArifpayService) ExecuteTelebirrB2CTransfer(ctx context.Context, req dom func (s *ArifpayService) ExecuteCBEB2CTransfer(ctx context.Context, req domain.CheckoutSessionClientRequest, userId int64) error { // Step 1: Deduct from user wallet first - userWallet, err := s.walletSvc.GetCustomerWallet(ctx, userId) - if err != nil { - return fmt.Errorf("cbebirr: failed to get user wallet: %w", err) - } + // userWallet, err := s.walletSvc.GetCustomerWallet(ctx, userId) + // if err != nil { + // return fmt.Errorf("cbebirr: failed to get user wallet: %w", err) + // } - _, err = s.walletSvc.DeductFromWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - "", - ) - if err != nil { - return fmt.Errorf("cbebirr: failed to deduct from wallet: %w", err) - } + // _, err = s.walletSvc.DeductFromWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // "", + // ) + // if err != nil { + // return fmt.Errorf("cbebirr: failed to deduct from wallet: %w", err) + // } - referenceNum := uuid.NewString() + // referenceNum := uuid.NewString() // Step 2: Create Session sessionReq := domain.CheckoutSessionClientRequest{ @@ -421,18 +419,18 @@ func (s *ArifpayService) ExecuteCBEB2CTransfer(ctx context.Context, req domain.C sessionResp, err := s.CreateCheckoutSession(sessionReq, false, userId) if err != nil { // refund wallet if session creation fails - _, refundErr := s.walletSvc.AddToWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - domain.PaymentDetails{}, - "", - ) - if refundErr != nil { - return fmt.Errorf("cbebirr: refund failed after session creation error: %v", refundErr) - } + // _, refundErr := s.walletSvc.AddToWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // domain.PaymentDetails{}, + // "", + // ) + // if refundErr != nil { + // return fmt.Errorf("cbebirr: refund failed after session creation error: %v", refundErr) + // } return fmt.Errorf("cbebirr: failed to create session: %w", err) } @@ -445,13 +443,13 @@ func (s *ArifpayService) ExecuteCBEB2CTransfer(ctx context.Context, req domain.C payload, err := json.Marshal(reqBody) if err != nil { - s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") + // s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") return fmt.Errorf("cbebirr: failed to marshal transfer request: %w", err) } transferReq, err := http.NewRequestWithContext(ctx, http.MethodPost, transferURL, bytes.NewBuffer(payload)) if err != nil { - s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") + // s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") return fmt.Errorf("cbebirr: failed to build transfer request: %w", err) } transferReq.Header.Set("Content-Type", "application/json") @@ -459,59 +457,59 @@ func (s *ArifpayService) ExecuteCBEB2CTransfer(ctx context.Context, req domain.C transferResp, err := s.httpClient.Do(transferReq) if err != nil { - s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") + // s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") return fmt.Errorf("cbebirr: failed to execute transfer request: %w", err) } defer transferResp.Body.Close() if transferResp.StatusCode != http.StatusOK { body, _ := io.ReadAll(transferResp.Body) - s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") + // s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") return fmt.Errorf("cbebirr: transfer failed with status %d: %s", transferResp.StatusCode, string(body)) } // Step 4: Store transfer in DB - transfer := domain.CreateTransfer{ - Amount: domain.Currency(req.Amount), - Verified: false, - Type: domain.WITHDRAW, // B2C = payout - ReferenceNumber: referenceNum, - SessionID: fmt.Sprintf("%v", sessionResp["sessionId"]), - Status: string(domain.PaymentStatusPending), - PaymentMethod: domain.TRANSFER_ARIFPAY, - CashierID: domain.ValidInt64{ - Value: userId, - Valid: true, - }, - } + // transfer := domain.CreateTransfer{ + // Amount: domain.Currency(req.Amount), + // Verified: false, + // Type: domain.WITHDRAW, // B2C = payout + // ReferenceNumber: referenceNum, + // SessionID: fmt.Sprintf("%v", sessionResp["sessionId"]), + // Status: string(domain.PaymentStatusPending), + // PaymentMethod: domain.TRANSFER_ARIFPAY, + // CashierID: domain.ValidInt64{ + // Value: userId, + // Valid: true, + // }, + // } - if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil { - return fmt.Errorf("cbebirr: failed to store transfer: %w", err) - } + // if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil { + // return fmt.Errorf("cbebirr: failed to store transfer: %w", err) + // } return nil } func (s *ArifpayService) ExecuteMPesaB2CTransfer(ctx context.Context, req domain.CheckoutSessionClientRequest, userId int64) error { // Step 1: Deduct from user wallet first - userWallet, err := s.walletSvc.GetCustomerWallet(ctx, userId) - if err != nil { - return fmt.Errorf("mpesa: failed to get user wallet: %w", err) - } + // userWallet, err := s.walletSvc.GetCustomerWallet(ctx, userId) + // if err != nil { + // return fmt.Errorf("mpesa: failed to get user wallet: %w", err) + // } - _, err = s.walletSvc.DeductFromWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - "", - ) - if err != nil { - return fmt.Errorf("mpesa: failed to deduct from wallet: %w", err) - } + // _, err = s.walletSvc.DeductFromWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // "", + // ) + // if err != nil { + // return fmt.Errorf("mpesa: failed to deduct from wallet: %w", err) + // } - referenceNum := uuid.NewString() + // referenceNum := uuid.NewString() // Step 2: Create Session sessionReq := domain.CheckoutSessionClientRequest{ @@ -523,18 +521,18 @@ func (s *ArifpayService) ExecuteMPesaB2CTransfer(ctx context.Context, req domain sessionResp, err := s.CreateCheckoutSession(sessionReq, false, userId) if err != nil { // Refund wallet if session creation fails - _, refundErr := s.walletSvc.AddToWallet( - ctx, - userWallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_ARIFPAY, - domain.PaymentDetails{}, - "", - ) - if refundErr != nil { - return fmt.Errorf("mpesa: refund failed after session creation error: %v", refundErr) - } + // _, refundErr := s.walletSvc.AddToWallet( + // ctx, + // userWallet.RegularID, + // domain.Currency(req.Amount), + // domain.ValidInt64{}, + // domain.TRANSFER_ARIFPAY, + // domain.PaymentDetails{}, + // "", + // ) + // if refundErr != nil { + // return fmt.Errorf("mpesa: refund failed after session creation error: %v", refundErr) + // } return fmt.Errorf("mpesa: failed to create session: %w", err) } @@ -547,13 +545,13 @@ func (s *ArifpayService) ExecuteMPesaB2CTransfer(ctx context.Context, req domain payload, err := json.Marshal(reqBody) if err != nil { - s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") + // s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") return fmt.Errorf("mpesa: failed to marshal transfer request: %w", err) } transferReq, err := http.NewRequestWithContext(ctx, http.MethodPost, transferURL, bytes.NewBuffer(payload)) if err != nil { - s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") + // s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") return fmt.Errorf("mpesa: failed to build transfer request: %w", err) } transferReq.Header.Set("Content-Type", "application/json") @@ -561,35 +559,35 @@ func (s *ArifpayService) ExecuteMPesaB2CTransfer(ctx context.Context, req domain transferResp, err := s.httpClient.Do(transferReq) if err != nil { - s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") + // s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") return fmt.Errorf("mpesa: failed to execute transfer request: %w", err) } defer transferResp.Body.Close() if transferResp.StatusCode != http.StatusOK { body, _ := io.ReadAll(transferResp.Body) - s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") + // s.walletSvc.AddToWallet(ctx, userWallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.TRANSFER_ARIFPAY, domain.PaymentDetails{}, "") return fmt.Errorf("mpesa: transfer failed with status %d: %s", transferResp.StatusCode, string(body)) } // Step 4: Store transfer in DB - transfer := domain.CreateTransfer{ - Amount: domain.Currency(req.Amount), - Verified: false, - Type: domain.WITHDRAW, // B2C = payout - ReferenceNumber: referenceNum, - SessionID: fmt.Sprintf("%v", sessionResp["sessionId"]), - Status: string(domain.PaymentStatusPending), - PaymentMethod: domain.TRANSFER_ARIFPAY, - CashierID: domain.ValidInt64{ - Value: userId, - Valid: true, - }, - } + // transfer := domain.CreateTransfer{ + // Amount: domain.Currency(req.Amount), + // Verified: false, + // Type: domain.WITHDRAW, // B2C = payout + // ReferenceNumber: referenceNum, + // SessionID: fmt.Sprintf("%v", sessionResp["sessionId"]), + // Status: string(domain.PaymentStatusPending), + // PaymentMethod: domain.TRANSFER_ARIFPAY, + // CashierID: domain.ValidInt64{ + // Value: userId, + // Valid: true, + // }, + // } - if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil { - return fmt.Errorf("mpesa: failed to store transfer: %w", err) - } + // if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil { + // return fmt.Errorf("mpesa: failed to store transfer: %w", err) + // } return nil } diff --git a/internal/services/authentication/impl.go b/internal/services/authentication/impl.go index 8bebc32..b4eebfc 100644 --- a/internal/services/authentication/impl.go +++ b/internal/services/authentication/impl.go @@ -7,7 +7,7 @@ import ( "errors" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "Yimaru-Backend/internal/domain" "golang.org/x/crypto/bcrypt" ) @@ -71,7 +71,7 @@ func (s *Service) Login(ctx context.Context, email, phone string, password strin UserId: user.ID, Role: user.Role, RfToken: refreshToken, - CompanyID: user.CompanyID, + CompanyID: user.OrganizationID, }, nil } diff --git a/internal/services/authentication/interface.go b/internal/services/authentication/interface.go index d2cf202..ffffa83 100644 --- a/internal/services/authentication/interface.go +++ b/internal/services/authentication/interface.go @@ -3,7 +3,6 @@ package authentication // import ( // "context" -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" // ) // type UserStore interface { diff --git a/internal/services/authentication/service.go b/internal/services/authentication/service.go index 37b78e2..ad428b8 100644 --- a/internal/services/authentication/service.go +++ b/internal/services/authentication/service.go @@ -1,6 +1,6 @@ package authentication -import "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" +import "Yimaru-Backend/internal/ports" // type EmailPhone struct { // Email ValidString diff --git a/internal/services/bet/interface.go b/internal/services/bet/interface.go deleted file mode 100644 index 19a8d78..0000000 --- a/internal/services/bet/interface.go +++ /dev/null @@ -1,53 +0,0 @@ -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/notification.go b/internal/services/bet/notification.go deleted file mode 100644 index edeb70c..0000000 --- a/internal/services/bet/notification.go +++ /dev/null @@ -1,316 +0,0 @@ -package bet - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" -) - -func newBetResultNotification(userID int64, level domain.NotificationLevel, channel domain.DeliveryChannel, headline, message string, metadata any) *domain.Notification { - raw, _ := json.Marshal(metadata) - return &domain.Notification{ - RecipientID: userID, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Type: domain.NOTIFICATION_TYPE_BET_RESULT, - Level: level, - Reciever: domain.NotificationRecieverSideCustomer, - DeliveryChannel: channel, - Payload: domain.NotificationPayload{ - Headline: headline, - Message: message, - }, - Priority: 2, - Metadata: raw, - } -} - -type SendResultNotificationParam struct { - BetID int64 - Status domain.OutcomeStatus - UserID int64 - WinningAmount domain.Currency - Extra string - SendEmail bool - SendSMS bool -} - -func (p SendResultNotificationParam) Validate() error { - if p.BetID == 0 { - return errors.New("BetID is required") - } - if p.UserID == 0 { - return errors.New("UserID is required") - } - return nil -} - -func shouldSend(channel domain.DeliveryChannel, sendEmail, sendSMS bool) bool { - switch { - case channel == domain.DeliveryChannelEmail && sendEmail: - return true - case channel == domain.DeliveryChannelSMS && sendSMS: - return true - case channel == domain.DeliveryChannelInApp: - return true - default: - return false - } -} - -func (s *Service) SendWinningStatusNotification(ctx context.Context, param SendResultNotificationParam) error { - if err := param.Validate(); err != nil { - return err - } - - var headline string - var message string - - switch param.Status { - case domain.OUTCOME_STATUS_WIN: - headline = fmt.Sprintf("Bet #%v Won!", param.BetID) - message = fmt.Sprintf( - "Congratulations! Your bet #%v has won. %.2f has been credited to your wallet.", - param.BetID, - param.WinningAmount.Float32(), - ) - case domain.OUTCOME_STATUS_HALF: - headline = fmt.Sprintf("Bet #%v Half-Win", param.BetID) - message = fmt.Sprintf( - "Your bet #%v resulted in a half-win. %.2f has been credited to your wallet.", - param.BetID, - param.WinningAmount.Float32(), - ) - case domain.OUTCOME_STATUS_VOID: - headline = fmt.Sprintf("Bet #%v Refunded", param.BetID) - message = fmt.Sprintf( - "Your bet #%v has been voided. %.2f has been refunded to your wallet.", - param.BetID, - param.WinningAmount.Float32(), - ) - - default: - return fmt.Errorf("unsupported status: %v", param.Status) - } - - for _, channel := range []domain.DeliveryChannel{ - domain.DeliveryChannelInApp, - domain.DeliveryChannelEmail, - domain.DeliveryChannelSMS, - } { - if !shouldSend(channel, param.SendEmail, param.SendSMS) { - continue - } - n := newBetResultNotification(param.UserID, domain.NotificationLevelSuccess, channel, headline, message, map[string]any{ - "winning_amount": param.WinningAmount.Float32(), - "status": param.Status, - "more": param.Extra, - }) - if err := s.notificationSvc.SendNotification(ctx, n); err != nil { - return err - } - } - - return nil -} - -func (s *Service) SendLosingStatusNotification(ctx context.Context, param SendResultNotificationParam) error { - if err := param.Validate(); err != nil { - return err - } - - var headline string - var message string - - switch param.Status { - case domain.OUTCOME_STATUS_LOSS: - headline = fmt.Sprintf("Bet #%v Lost", param.BetID) - message = "Unfortunately, your bet did not win this time. Better luck next time!" - default: - return fmt.Errorf("unsupported status: %v", param.Status) - } - - for _, channel := range []domain.DeliveryChannel{ - domain.DeliveryChannelInApp, - domain.DeliveryChannelEmail, - domain.DeliveryChannelSMS, - } { - if !shouldSend(channel, param.SendEmail, param.SendSMS) { - continue - } - n := newBetResultNotification(param.UserID, domain.NotificationLevelWarning, channel, headline, message, map[string]any{ - "status": param.Status, - "more": param.Extra, - }) - if err := s.notificationSvc.SendNotification(ctx, n); err != nil { - return err - } - } - - return nil -} - -func (s *Service) SendErrorStatusNotification(ctx context.Context, betID int64, status domain.OutcomeStatus, userID int64, extra string) error { - - var headline string - var message string - - switch status { - case domain.OUTCOME_STATUS_ERROR, domain.OUTCOME_STATUS_PENDING: - headline = fmt.Sprintf("Bet #%v Processing Issue", betID) - message = "We encountered a problem while processing your bet. Our team is working to resolve it as soon as possible." - - default: - return fmt.Errorf("unsupported status: %v", status) - } - - for _, channel := range []domain.DeliveryChannel{ - domain.DeliveryChannelInApp, - domain.DeliveryChannelEmail, - } { - n := newBetResultNotification(userID, domain.NotificationLevelError, channel, headline, message, map[string]any{ - "status": status, - "more": extra, - }) - if err := s.notificationSvc.SendNotification(ctx, n); err != nil { - return err - } - } - return nil -} - -func (s *Service) SendAdminErrorNotification(ctx context.Context, betID int64, status domain.OutcomeStatus, extra string, companyID int64) error { - - var headline string - var message string - - switch status { - case domain.OUTCOME_STATUS_ERROR, domain.OUTCOME_STATUS_PENDING: - headline = fmt.Sprintf("Processing Error for Bet #%v", betID) - message = "A processing error occurred with this bet. Please review and take corrective action." - - default: - return fmt.Errorf("unsupported status: %v", status) - } - - super_admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{ - Role: string(domain.RoleSuperAdmin), - }) - - if err != nil { - s.mongoLogger.Error("failed to get super_admin recipients", - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return err - } - - admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{ - Role: string(domain.RoleAdmin), - CompanyID: domain.ValidInt64{ - Value: companyID, - Valid: true, - }, - }) - - if err != nil { - s.mongoLogger.Error("failed to get admin recipients", - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return err - } - - users := append(super_admin_users, admin_users...) - - for _, user := range users { - for _, channel := range []domain.DeliveryChannel{ - domain.DeliveryChannelInApp, - // domain.DeliveryChannelEmail, - } { - n := newBetResultNotification(user.ID, domain.NotificationLevelError, channel, headline, message, map[string]any{ - "status": status, - "more": extra, - }) - if err := s.notificationSvc.SendNotification(ctx, n); err != nil { - return err - } - } - } - - return nil -} -func (s *Service) SendAdminLargeBetNotification(ctx context.Context, betID int64, totalWinnings float32, extra string, companyID int64) error { - - headline := "SYSTEM WARNING: High Risk Bet" - message := fmt.Sprintf("Bet #%d has been created with %v payout", betID, totalWinnings) - - super_admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{ - Role: string(domain.RoleSuperAdmin), - }) - - if err != nil { - s.mongoLogger.Error("failed to get super_admin recipients", - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return err - } - - admin_users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{ - Role: string(domain.RoleAdmin), - CompanyID: domain.ValidInt64{ - Value: companyID, - Valid: true, - }, - }) - - if err != nil { - s.mongoLogger.Error("failed to get admin recipients", - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return err - } - - users := append(super_admin_users, admin_users...) - - for _, user := range users { - for _, channel := range []domain.DeliveryChannel{ - domain.DeliveryChannelInApp, - // domain.DeliveryChannelEmail, - } { - raw, _ := json.Marshal(map[string]any{ - "winnings": totalWinnings, - "more": extra, - }) - - n := &domain.Notification{ - RecipientID: user.ID, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT, - Level: domain.NotificationLevelWarning, - Reciever: domain.NotificationRecieverSideAdmin, - DeliveryChannel: channel, - Payload: domain.NotificationPayload{ - Headline: headline, - Message: message, - }, - Priority: 2, - Metadata: raw, - } - // n := newBetResultNotification(user.ID, domain.NotificationLevelWarning, channel, headline, message) - if err := s.notificationSvc.SendNotification(ctx, n); err != nil { - return err - } - } - } - - return nil -} diff --git a/internal/services/bet/service.go b/internal/services/bet/service.go deleted file mode 100644 index 3e14fdc..0000000 --- a/internal/services/bet/service.go +++ /dev/null @@ -1,1436 +0,0 @@ -package bet - -import ( - "context" - "crypto/rand" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "log/slog" - "math" - "math/big" - random "math/rand" - "sort" - "strconv" - "strings" - "time" - - "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" - 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" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" - "go.uber.org/zap" -) - -var ( - ErrNoEventsAvailable = errors.New("not enough events available with the given filters") - ErrGenerateRandomOutcome = errors.New("failed to generate any random outcome for events") - ErrOutcomesNotCompleted = errors.New("some bet outcomes are still pending") - ErrEventHasBeenRemoved = errors.New("event has been removed") - ErrEventHasBeenDisabled = errors.New("event has been disabled") - - ErrEventHasNotEnded = errors.New("event has not ended yet") - ErrOddHasBeenDisabled = errors.New("odd has been disabled") - ErrRawOddInvalid = errors.New("prematch Raw Odd is Invalid") - ErrBranchIDRequired = errors.New("branch ID required for this role") - ErrOutcomeLimit = errors.New("too many outcomes on a single bet") - ErrTotalBalanceNotEnough = errors.New("total Wallet balance is insufficient to create bet") - - ErrTooManyUnsettled = errors.New("too many unsettled bets") - ErrInvalidAmount = errors.New("invalid amount") - ErrBetAmountTooHigh = errors.New("cannot create a bet with an amount above limit") - ErrBetWinningTooHigh = errors.New("total Winnings over set limit") - - ErrCompanyDeductedPercentInvalid = errors.New("invalid company deducted percentage") -) - -type Service struct { - betStore ports.BetStore - eventSvc *event.Service - prematchSvc odds.ServiceImpl - walletSvc wallet.Service - branchSvc branch.Service - companySvc company.Service - settingSvc settings.Service - userSvc user.Service - notificationSvc *notificationservice.Service - logger *slog.Logger - mongoLogger *zap.Logger -} - -func NewService( - betStore ports.BetStore, - eventSvc *event.Service, - prematchSvc odds.ServiceImpl, - walletSvc wallet.Service, - branchSvc branch.Service, - companySvc company.Service, - settingSvc settings.Service, - userSvc user.Service, - notificationSvc *notificationservice.Service, - logger *slog.Logger, - mongoLogger *zap.Logger, -) *Service { - return &Service{ - betStore: betStore, - eventSvc: eventSvc, - prematchSvc: prematchSvc, - walletSvc: walletSvc, - branchSvc: branchSvc, - companySvc: companySvc, - settingSvc: settingSvc, - notificationSvc: notificationSvc, - logger: logger, - mongoLogger: mongoLogger, - } -} - -func (s *Service) GenerateCashoutID() (string, error) { - const chars = "abcdefghijklmnopqrstuvwxyz0123456789" - const length int = 13 - charLen := big.NewInt(int64(len(chars))) - result := make([]byte, length) - - for i := 0; i < length; i++ { - index, err := rand.Int(rand.Reader, charLen) - if err != nil { - return "", err - } - result[i] = chars[index.Int64()] - } - - return string(result), nil -} - -func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketID int64, oddID int64, companyID int64) (domain.CreateBetOutcome, error) { - oddIDStr := strconv.FormatInt(oddID, 10) - - event, err := s.eventSvc.GetEventWithSettingByID(ctx, eventID, companyID) - if err != nil { - s.mongoLogger.Error("failed to fetch upcoming event by ID", - zap.Int64("event_id", eventID), - zap.Error(err), - ) - return domain.CreateBetOutcome{}, ErrEventHasBeenRemoved - } - - if !event.IsActive { - s.mongoLogger.Warn("attempting to create bet with disabled event", - zap.Int64("event_id", eventID), - zap.Error(err), - ) - return domain.CreateBetOutcome{}, ErrEventHasBeenDisabled - } - - currentTime := time.Now() - if event.StartTime.Before(currentTime) { - s.mongoLogger.Error("event has already started", - zap.Int64("event_id", eventID), - zap.Time("event_start_time", event.StartTime), - zap.Time("current_time", currentTime), - ) - return domain.CreateBetOutcome{}, ErrEventHasNotEnded - } - - - setting, err := s.prematchSvc.GetOverrideMarketSettingByID(ctx, companyID, marketID) - if err != nil { - s.mongoLogger.Error("failed to get override market settings by id", - zap.Int64("company_id", companyID), - zap.Int64("event_id", eventID), - zap.Int64("market_id", marketID), - zap.Error(err), - ) - return domain.CreateBetOutcome{}, err - } - - if !setting.IsActive { - s.mongoLogger.Error("Attempted to create bet on disabled market", - zap.Int64("event_id", eventID), - zap.Int64("market_id", marketID), - zap.Int64("company_id", companyID), - zap.Error(err), - ) - return domain.CreateBetOutcome{}, ErrOddHasBeenDisabled - } - - odds, err := s.prematchSvc.GetOddsWithSettingsByMarketID(ctx, marketID, eventID, companyID) - if err != nil { - s.mongoLogger.Error("failed to get raw odds by market ID", - zap.Int64("event_id", eventID), - zap.Int64("market_id", marketID), - zap.Error(err), - ) - return domain.CreateBetOutcome{}, err - } - - if !odds.IsActive { - s.mongoLogger.Info("Attempted to created bet on disabled odd", - zap.Int64("event_id", eventID), - zap.Int64("market_id", marketID), - zap.Error(err), - ) - return domain.CreateBetOutcome{}, ErrOddHasBeenDisabled - } - - - type rawOddType struct { - ID string - Name string - Odds string - Header string - Handicap string - } - - var selectedOdd rawOddType - var isOddFound bool - - for _, raw := range odds.RawOdds { - var rawOdd rawOddType - rawBytes, err := json.Marshal(raw) - if err != nil { - s.mongoLogger.Error("failed to marshal raw odd", - zap.Any("raw", raw), - zap.Error(err), - ) - continue - } - err = json.Unmarshal(rawBytes, &rawOdd) - if err != nil { - s.mongoLogger.Error("failed to unmarshal raw odd", - zap.ByteString("raw_bytes", rawBytes), - zap.Error(err), - ) - continue - } - if rawOdd.ID == oddIDStr { - selectedOdd = rawOdd - isOddFound = true - break - } - } - if !isOddFound { - s.mongoLogger.Error("odd ID not found in raw odds", - zap.Int64("odd_id", oddID), - zap.Int64("market_id", marketID), - zap.Int64("event_id", eventID), - ) - return domain.CreateBetOutcome{}, ErrRawOddInvalid - } - - parsedOdd, err := strconv.ParseFloat(selectedOdd.Odds, 32) - if err != nil { - s.mongoLogger.Error("failed to parse selected odd value", - zap.String("odd", selectedOdd.Odds), - zap.Int64("odd_id", oddID), - zap.Error(err), - ) - return domain.CreateBetOutcome{}, err - } - - newOutcome := domain.CreateBetOutcome{ - EventID: eventID, - OddID: oddID, - MarketID: marketID, - SportID: int64(event.SportID), - HomeTeamName: event.HomeTeam, - AwayTeamName: event.AwayTeam, - MarketName: odds.MarketName, - Odd: float32(parsedOdd), - OddName: selectedOdd.Name, - OddHeader: selectedOdd.Header, - OddHandicap: selectedOdd.Handicap, - Expires: event.StartTime, - } - - return newOutcome, nil -} - -func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID int64, role domain.Role, companyID int64) (domain.CreateBetRes, error) { - settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID) - - if err != nil { - return domain.CreateBetRes{}, err - } - - _, totalUnsettledBets, err := s.GetAllBets(ctx, domain.BetFilter{ - Status: domain.ValidOutcomeStatus{ - Value: domain.OUTCOME_STATUS_ERROR, - Valid: true, - }, - }) - - if err != nil { - return domain.CreateBetRes{}, err - } - - if totalUnsettledBets > settingsList.MaxUnsettledBets { - s.mongoLogger.Error("System block bet creation until unsettled bets fixed", - zap.Int64("total_unsettled_bets", totalUnsettledBets), - zap.Int64("user_id", userID), - ) - return domain.CreateBetRes{}, ErrTooManyUnsettled - } - if req.Amount < settingsList.MinimumBetAmount.Float32() { - return domain.CreateBetRes{}, ErrInvalidAmount - } - - if req.Amount > settingsList.BetAmountLimit.Float32() { - return domain.CreateBetRes{}, ErrBetAmountTooHigh - } - - if len(req.Outcomes) > int(settingsList.MaxNumberOfOutcomes) { - s.mongoLogger.Info("too many outcomes", - zap.Int("count", len(req.Outcomes)), - zap.Int64("user_id", userID), - ) - return domain.CreateBetRes{}, ErrOutcomeLimit - } - - var outcomes []domain.CreateBetOutcome = make([]domain.CreateBetOutcome, 0, len(req.Outcomes)) - var totalOdds float32 = 1 - - for _, outcomeReq := range req.Outcomes { - newOutcome, err := s.GenerateBetOutcome(ctx, outcomeReq.EventID, outcomeReq.MarketID, outcomeReq.OddID, companyID) - if err != nil { - s.mongoLogger.Error("failed to generate outcome", - zap.Int64("event_id", outcomeReq.EventID), - zap.Int64("market_id", outcomeReq.MarketID), - zap.Int64("odd_id", outcomeReq.OddID), - zap.Int64("user_id", userID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - totalOdds *= float32(newOutcome.Odd) - outcomes = append(outcomes, newOutcome) - } - - totalWinnings := req.Amount * totalOdds - - if totalWinnings > settingsList.TotalWinningLimit.Float32() { - s.mongoLogger.Info("Total Winnings over limit", - zap.Float32("Total Odds", totalOdds), - zap.Float32("amount", req.Amount), - zap.Float32("limit", settingsList.TotalWinningLimit.Float32())) - return domain.CreateBetRes{}, ErrBetWinningTooHigh - } - - outcomesHash, err := generateOutcomeHash(outcomes) - if err != nil { - s.mongoLogger.Error("failed to generate outcome hash", - zap.Int64("user_id", userID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - count, err := s.GetBetCountByUserID(ctx, userID, outcomesHash) - if err != nil { - s.mongoLogger.Error("failed to generate cashout ID", - zap.Int64("user_id", userID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - if role == domain.RoleCustomer && count >= settingsList.BetDuplicateLimit { - return domain.CreateBetRes{}, fmt.Errorf("max user limit for duplicate bet") - } - - fastCode := helpers.GenerateFastCode() - // accumulator := calculateAccumulator(len(outcomes)) - // amount := req.Amount + (req.Amount * accumulator) - amount := req.Amount - - newBet := domain.CreateBet{ - Amount: domain.ToCurrency(amount), - TotalOdds: totalOdds, - Status: domain.OUTCOME_STATUS_PENDING, - OutcomesHash: outcomesHash, - FastCode: fastCode, - UserID: userID, - CompanyID: companyID, - } - - switch role { - case domain.RoleCashier: - newBet.IsShopBet = true - branch, err := s.branchSvc.GetBranchByCashier(ctx, userID) - if err != nil { - s.mongoLogger.Error("failed to get branch by cashier", - zap.Int64("user_id", userID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - err = s.DeductBetFromBranchWallet(ctx, req.Amount, branch.WalletID, branch.CompanyID, userID) - if err != nil { - s.mongoLogger.Error("wallet deduction for bet failed", - zap.String("role", string(role)), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - // For - case domain.RoleBranchManager, domain.RoleAdmin, domain.RoleSuperAdmin: - newBet.IsShopBet = true - // Branch Manager, Admin and Super Admin are required to pass a branch id if they want to create a bet - if req.BranchID == nil { - s.mongoLogger.Warn("branch ID required for admin/manager", - zap.Int64("user_id", userID), - ) - return domain.CreateBetRes{}, ErrBranchIDRequired - } - - branch, err := s.branchSvc.GetBranchByID(ctx, *req.BranchID) - if err != nil { - s.mongoLogger.Error("failed to get branch by ID", - zap.Int64("branch_id", *req.BranchID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - if role == domain.RoleBranchManager { - if branch.BranchManagerID != userID { - s.mongoLogger.Warn("unauthorized branch for branch manager", - zap.Int64("branch_id", *req.BranchID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - } - if branch.CompanyID != companyID { - s.mongoLogger.Warn("unauthorized company", - zap.Int64("branch_id", *req.BranchID), - zap.Int64("branch_company_id", branch.CompanyID), - zap.Int64("company_id", companyID), - zap.Error(err), - ) - } - - err = s.DeductBetFromBranchWallet(ctx, req.Amount, branch.WalletID, branch.CompanyID, userID) - if err != nil { - s.mongoLogger.Error("wallet deduction for bet failed", - zap.String("role", string(role)), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - case domain.RoleCustomer: - // Only the customer is able to create a online bet - newBet.IsShopBet = false - err = s.DeductBetFromCustomerWallet(ctx, req.Amount, userID) - if err != nil { - s.mongoLogger.Warn("customer wallet deduction failed", - zap.Float32("amount", req.Amount), - zap.Int64("user_id", userID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - // - default: - s.mongoLogger.Error("unknown role type", - zap.String("role", string(role)), - zap.Int64("user_id", userID), - ) - return domain.CreateBetRes{}, fmt.Errorf("unknown role type") - } - - bet, err := s.CreateBet(ctx, newBet) - if err != nil { - s.mongoLogger.Error("failed to create bet", - zap.Int64("user_id", userID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - for i := range outcomes { - outcomes[i].BetID = bet.ID - } - rows, err := s.betStore.CreateBetOutcome(ctx, outcomes) - if err != nil { - s.mongoLogger.Error("failed to create bet outcomes", - zap.Int64("bet_id", bet.ID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - for i := range outcomes { - // flag odds with large amount of users betting on them - count, err := s.betStore.GetBetOutcomeCountByOddID(ctx, outcomes[i].OddID) - if err != nil { - s.mongoLogger.Error("failed to get count of bet outcome", - zap.Int64("bet_id", bet.ID), - zap.Int64("odd_id", outcomes[i].OddID), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - // TODO: fetch cap from settings in db - if count > 20 { - flag := domain.CreateFlagReq{ - BetID: 0, - OddID: outcomes[i].OddID, - Reason: fmt.Sprintf("too many users targeting odd - (%d)", outcomes[i].OddID), - } - - _, err := s.betStore.CreateFlag(ctx, flag) - if err != nil { - s.mongoLogger.Error("failed to create flag for bet", - zap.Int64("bet_id", bet.ID), - zap.Error(err), - ) - } - } - } - - // flag bets that have more than three outcomes - if len(outcomes) > 3 { - flag := domain.CreateFlagReq{ - BetID: bet.ID, - OddID: 0, - Reason: fmt.Sprintf("too many outcomes - (%d)", len(outcomes)), - } - - _, err := s.betStore.CreateFlag(ctx, flag) - if err != nil { - s.mongoLogger.Error("failed to create flag for bet", - zap.Int64("bet_id", bet.ID), - zap.Error(err), - ) - } - } - - // large amount of users betting on the same bet_outcomes - total_bet_count, err := s.betStore.GetBetCountByOutcomesHash(ctx, outcomesHash) - if err != nil { - s.mongoLogger.Error("failed to get bet outcomes count", - zap.String("outcomes_hash", outcomesHash), - zap.Error(err), - ) - return domain.CreateBetRes{}, err - } - - if total_bet_count > 10 { - flag := domain.CreateFlagReq{ - BetID: bet.ID, - OddID: 0, - Reason: fmt.Sprintf("too many users bet on same outcomes - (%s)", outcomesHash), - } - _, err := s.betStore.CreateFlag(ctx, flag) - - if err != nil { - s.mongoLogger.Error("failed to get bet outcomes count", - zap.String("outcomes_hash", outcomesHash), - zap.Error(err), - ) - } - } - - if totalWinnings > settingsList.TotalWinningNotify.Float32() { - err = s.SendAdminLargeBetNotification(ctx, bet.ID, totalWinnings, "", companyID) - if err != nil { - s.mongoLogger.Error("Failed to send large bet notification", zap.Int64("betID", bet.ID), - zap.Int64("companyID", companyID), zap.Float32("totalWinnings", totalWinnings)) - } - } - - res := domain.ConvertCreateBetRes(bet, rows) - - return res, nil -} - -func (s *Service) DeductBetFromBranchWallet(ctx context.Context, amount float32, walletID int64, companyID int64, userID int64) error { - - company, err := s.companySvc.GetCompanyByID(ctx, companyID) - - if err != nil { - s.mongoLogger.Error("failed to get company", - zap.Int64("company_id", companyID), - zap.Error(err), - ) - return err - } - - if company.DeductedPercentage > 1 { - s.mongoLogger.Error("Invalid company deducted percentage", - zap.Int64("wallet_id", walletID), - zap.Float32("amount", company.DeductedPercentage), - zap.Error(err), - ) - return ErrCompanyDeductedPercentInvalid - } - - _, err = s.walletSvc.DeductFromWallet(ctx, - walletID, domain.ToCurrency(amount), domain.ValidInt64{ - Value: userID, - Valid: true, - }, domain.TRANSFER_DIRECT, - fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", amount)) - - if err != nil { - s.mongoLogger.Error("failed to deduct from wallet", - zap.Int64("wallet_id", walletID), - zap.Float32("amount", amount), - zap.Error(err), - ) - return err - } - - // This is the amount that we take from a company/tenant when they - // create a bet. I.e. if its 5% (0.05), then thats the percentage we take every - // deductedAmount := amount * company.DeductedPercentage - - // if deductedAmount == 0 { - // s.mongoLogger.Fatal("Amount", - // zap.Int64("wallet_id", walletID), - // zap.Float32("amount", deductedAmount), - // zap.Error(err), - // ) - // return err - // } - // _, err = s.walletSvc.DeductFromWallet(ctx, - // walletID, domain.ToCurrency(deductedAmount), domain.ValidInt64{ - // Value: userID, - // Valid: true, - // }, domain.TRANSFER_DIRECT, - // fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", deductedAmount)) - - // if err != nil { - // s.mongoLogger.Error("failed to deduct from wallet", - // zap.Int64("wallet_id", walletID), - // zap.Float32("amount", deductedAmount), - // zap.Error(err), - // ) - // return err - // } - - return nil -} - -func (s *Service) DeductBetFromCustomerWallet(ctx context.Context, amount float32, userID int64) error { - wallets, err := s.walletSvc.GetCustomerWallet(ctx, userID) - if err != nil { - s.mongoLogger.Error("failed to get customer wallets", - zap.Int64("user_id", userID), - zap.Error(err), - ) - return err - } - if amount < wallets.RegularBalance.Float32() { - _, err = s.walletSvc.DeductFromWallet(ctx, wallets.RegularID, - domain.ToCurrency(amount), domain.ValidInt64{}, - domain.TRANSFER_DIRECT, fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", amount)) - if err != nil { - s.mongoLogger.Error("wallet deduction failed for customer regular wallet", - zap.Int64("customer_id", wallets.CustomerID), - zap.Int64("customer_wallet_id", wallets.ID), - zap.Int64("regular wallet_id", wallets.RegularID), - zap.Float32("amount", amount), - zap.Error(err), - ) - return err - } - } else { - combinedBalance := wallets.RegularBalance + wallets.StaticBalance - if amount > combinedBalance.Float32() { - return ErrTotalBalanceNotEnough - } - // Empty the regular balance - _, err = s.walletSvc.DeductFromWallet(ctx, wallets.RegularID, - wallets.RegularBalance, domain.ValidInt64{}, domain.TRANSFER_DIRECT, - fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", wallets.RegularBalance.Float32())) - if err != nil { - s.mongoLogger.Error("wallet deduction failed for customer regular wallet", - zap.Int64("customer_id", wallets.CustomerID), - zap.Int64("customer_wallet_id", wallets.ID), - zap.Int64("regular wallet_id", wallets.RegularID), - zap.Float32("amount", amount), - zap.Error(err), - ) - return err - } - // Empty remaining from static balance - remainingAmount := wallets.RegularBalance - domain.ToCurrency(amount) - _, err = s.walletSvc.DeductFromWallet(ctx, wallets.StaticID, - remainingAmount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, - fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", remainingAmount.Float32())) - if err != nil { - s.mongoLogger.Error("wallet deduction failed for customer static wallet", - zap.Int64("customer_id", wallets.CustomerID), - zap.Int64("customer_wallet_id", wallets.ID), - zap.Int64("static wallet_id", wallets.StaticID), - zap.Float32("amount", amount), - zap.Error(err), - ) - return err - } - } - - return nil -} - -func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID int64, sportID int32, HomeTeam, AwayTeam string, StartTime time.Time, numMarkets int) ([]domain.CreateBetOutcome, float32, error) { - - var newOdds []domain.CreateBetOutcome - var totalOdds float32 = 1 - - eventLogger := s.mongoLogger.With( - zap.Int64("eventID", eventID), - zap.Int32("sportID", sportID), - zap.String("homeTeam", HomeTeam), - zap.String("awayTeam", AwayTeam), - ) - markets, err := s.prematchSvc.GetOddsByEventID(ctx, eventID, domain.OddMarketWithEventFilter{}) - - if err != nil { - eventLogger.Error("failed to get odds for event", zap.Error(err)) - return nil, 0, err - } - - if len(markets) == 0 { - eventLogger.Warn("empty odds for event") - return nil, 0, fmt.Errorf("empty odds or event %v", eventID) - } - - var selectedMarkets []domain.OddMarket - numMarkets = min(numMarkets, len(markets)) - for i := 0; i < numMarkets; i++ { - randomIndex := random.Intn(len(markets)) - selectedMarkets = append(selectedMarkets, markets[randomIndex]) - markets = append(markets[:randomIndex], markets[randomIndex+1:]...) - } - - for _, market := range selectedMarkets { - - randomRawOdd := market.RawOdds[random.Intn(len(market.RawOdds))] - - type rawOddType struct { - ID string - Name string - Odds string - Header string - Handicap string - } - - var selectedOdd rawOddType - rawBytes, err := json.Marshal(randomRawOdd) - err = json.Unmarshal(rawBytes, &selectedOdd) - - if err != nil { - eventLogger.Warn("Failed to unmarshal raw odd", zap.Error(err)) - continue - } - - parsedOdd, err := strconv.ParseFloat(selectedOdd.Odds, 32) - if err != nil { - eventLogger.Warn("Failed to parse odd", - zap.String("oddValue", selectedOdd.Odds), - zap.Error(err)) - continue - } - - oddID, err := strconv.ParseInt(selectedOdd.ID, 10, 64) - if err != nil { - eventLogger.Warn("Failed to parse oddID", - zap.String("oddID", selectedOdd.ID), - zap.Error(err)) - continue - } - - marketName := market.MarketName - - newOdds = append(newOdds, domain.CreateBetOutcome{ - EventID: eventID, - OddID: oddID, - MarketID: market.MarketID, - SportID: int64(sportID), - HomeTeamName: HomeTeam, - AwayTeamName: AwayTeam, - MarketName: marketName, - Odd: float32(parsedOdd), - OddName: selectedOdd.Name, - OddHeader: selectedOdd.Header, - OddHandicap: selectedOdd.Handicap, - Expires: StartTime, - }) - - totalOdds *= float32(parsedOdd) - } - - if len(newOdds) == 0 { - eventLogger.Error("Bet Outcomes is empty for market", zap.Int("selectedMarkets", len(selectedMarkets))) - return nil, 0, ErrGenerateRandomOutcome - } - - // ✅ Final success log (optional) - eventLogger.Info("Random bet outcomes generated successfully", zap.Int("numOutcomes", len(newOdds)), zap.Float32("totalOdds", totalOdds)) - - return newOdds, totalOdds, nil -} - -func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID, companyID int64, leagueID domain.ValidInt64, sportID domain.ValidInt32, firstStartTime, lastStartTime domain.ValidTime) (domain.CreateBetRes, error) { - - // Get a unexpired event id - randomBetLogger := s.mongoLogger.With( - zap.Int64("userID", userID), - zap.Int64("branchID", branchID), - zap.Int64("companyID", companyID), - zap.Any("leagueID", leagueID), - zap.Any("sportID", sportID), - zap.Any("firstStartTime", firstStartTime), - zap.Any("lastStartTime", lastStartTime), - ) - events, _, err := s.eventSvc.GetAllEvents(ctx, - domain.EventFilter{ - SportID: sportID, - LeagueID: leagueID, - FirstStartTime: firstStartTime, - LastStartTime: lastStartTime, - Status: domain.ValidEventStatus{ - Value: domain.STATUS_PENDING, - }, - }) - - if err != nil { - randomBetLogger.Error("failed to get paginated upcoming events", zap.Error(err)) - return domain.CreateBetRes{}, err - } - - if len(events) == 0 { - randomBetLogger.Warn("no events available for random bet") - return domain.CreateBetRes{}, ErrNoEventsAvailable - } - - // TODO: Add the option of passing number of created events - var selectedUpcomingEvents []domain.BaseEvent - numEventsPerBet := min(random.Intn(4)+1, len(events)) //Eliminate the option of 0 - - for i := 0; i < int(numEventsPerBet); i++ { - randomIndex := random.Intn(len(events)) - selectedUpcomingEvents = append(selectedUpcomingEvents, events[randomIndex]) - events = append(events[:randomIndex], events[randomIndex+1:]...) - - } - - // s.logger.Info("Generating random bet events", "selectedUpcomingEvents", len(selectedUpcomingEvents)) - - // Get market and odds for that - var randomOdds []domain.CreateBetOutcome - var totalOdds float32 = 1 - numMarketsPerBet := random.Intn(2) + 1 - for _, event := range selectedUpcomingEvents { - - newOdds, total, err := s.GenerateRandomBetOutcomes(ctx, event.ID, event.SportID, event.HomeTeam, event.AwayTeam, event.StartTime, numMarketsPerBet) - - if err != nil { - s.mongoLogger.Error("failed to generate random bet outcome", zap.Int64("eventID", event.ID), zap.Error(err)) - continue - } - - randomOdds = append(randomOdds, newOdds...) - totalOdds = totalOdds * total - - } - if len(randomOdds) == 0 { - randomBetLogger.Error("Failed to generate random any outcomes for all events") - return domain.CreateBetRes{}, ErrGenerateRandomOutcome - } - - // s.logger.Info("Generated Random bet Outcome", "randomOdds", len(randomOdds)) - - outcomesHash, err := generateOutcomeHash(randomOdds) - if err != nil { - randomBetLogger.Error("failed to generate outcome hash", zap.Error(err)) - return domain.CreateBetRes{}, err - } - - count, err := s.GetBetCountByUserID(ctx, userID, outcomesHash) - if err != nil { - randomBetLogger.Error("failed to get bet count", zap.String("outcome_hash", outcomesHash), zap.Error(err)) - return domain.CreateBetRes{}, err - } - - if count >= 2 { - return domain.CreateBetRes{}, fmt.Errorf("bet already placed twice") - } - - fastCode := helpers.GenerateFastCode() - - newBet := domain.CreateBet{ - Amount: domain.ToCurrency(123.5), - TotalOdds: totalOdds, - Status: domain.OUTCOME_STATUS_PENDING, - UserID: userID, - CompanyID: companyID, - IsShopBet: true, - FastCode: fastCode, - } - - bet, err := s.CreateBet(ctx, newBet) - if err != nil { - randomBetLogger.Error("Failed to create a new random bet", zap.Error(err)) - return domain.CreateBetRes{}, err - } - - for i := range randomOdds { - randomOdds[i].BetID = bet.ID - } - - rows, err := s.betStore.CreateBetOutcome(ctx, randomOdds) - if err != nil { - randomBetLogger.Error("Failed to create a new random bet outcome", zap.Any("randomOdds", randomOdds)) - return domain.CreateBetRes{}, err - } - - res := domain.ConvertCreateBetRes(bet, rows) - - randomBetLogger.Info("Random bets placed successfully") - return res, nil -} - -func (s *Service) CreateBet(ctx context.Context, bet domain.CreateBet) (domain.Bet, error) { - return s.betStore.CreateBet(ctx, bet) -} - -func (s *Service) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBetOutcome) (int64, error) { - return s.betStore.CreateBetOutcome(ctx, outcomes) -} - -func (s *Service) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) { - return s.betStore.GetBetByID(ctx, id) -} -func (s *Service) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, int64, error) { - return s.betStore.GetAllBets(ctx, filter) -} - -func (s *Service) GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error) { - return s.betStore.GetBetByUserID(ctx, UserID) -} - -func (s *Service) GetBetOutcomeByBetID(ctx context.Context, UserID int64) ([]domain.BetOutcome, error) { - return s.betStore.GetBetOutcomeByBetID(ctx, UserID) -} - -func (s *Service) GetBetOutcomeViewByEventID(ctx context.Context, eventID int64, filter domain.BetOutcomeViewFilter) ([]domain.BetOutcomeViewRes, int64, error) { - return s.betStore.GetBetOutcomeViewByEventID(ctx, eventID, filter) -} - -func (s *Service) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) { - return s.betStore.GetBetOutcomeByEventID(ctx, eventID, is_filtered) -} - -func (s *Service) GetBetByFastCode(ctx context.Context, fastcode string) (domain.GetBet, error) { - return s.betStore.GetBetByFastCode(ctx, fastcode) -} - -func (s *Service) GetBetCountByUserID(ctx context.Context, UserID int64, outcomesHash string) (int64, error) { - return s.betStore.GetBetCountByUserID(ctx, UserID, outcomesHash) -} - -func (s *Service) GetBetCountByOutcomesHash(ctx context.Context, outcomesHash string) (int64, error) { - return s.betStore.GetBetCountByOutcomesHash(ctx, outcomesHash) -} - -func (s *Service) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error { - return s.betStore.UpdateCashOut(ctx, id, cashedOut) -} - -func (s *Service) UpdateStatus(ctx context.Context, betId int64, status domain.OutcomeStatus) error { - - updateLogger := s.mongoLogger.With( - zap.Int64("bet_id", betId), - zap.String("status", status.String()), - ) - bet, err := s.GetBetByID(ctx, betId) - if err != nil { - updateLogger.Error("failed to update bet status: invalid bet ID", zap.Error(err)) - return err - } - - settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, bet.CompanyID) - if err != nil { - updateLogger.Error("failed to get settings", zap.Error(err)) - return err - } - - if status == domain.OUTCOME_STATUS_ERROR || status == domain.OUTCOME_STATUS_PENDING { - if err := s.SendAdminErrorNotification(ctx, betId, status, "", bet.CompanyID); err != nil { - updateLogger.Error("failed to send admin notification", zap.Error(err)) - return err - } - - if err := s.SendErrorStatusNotification(ctx, betId, status, bet.UserID, ""); err != nil { - updateLogger.Error("failed to send error notification to user", zap.Error(err)) - return err - } - updateLogger.Error("bet entered error/pending state") - return s.betStore.UpdateStatus(ctx, betId, status) - } - - if bet.IsShopBet { - return s.betStore.UpdateStatus(ctx, betId, status) - } - // After this point the bet is known to be a online customer bet - - customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, bet.UserID) - if err != nil { - updateLogger.Error("failed to get customer wallet", zap.Error(err)) - return err - } - - resultNotification := SendResultNotificationParam{ - BetID: betId, - Status: status, - UserID: bet.UserID, - SendEmail: settingsList.SendEmailOnBetFinish, - SendSMS: settingsList.SendSMSOnBetFinish, - } - var amount domain.Currency - switch status { - case domain.OUTCOME_STATUS_LOSS: - err := s.SendLosingStatusNotification(ctx, resultNotification) - if err != nil { - updateLogger.Error("failed to send notification", zap.Error(err)) - return err - } - return s.betStore.UpdateStatus(ctx, betId, status) - case domain.OUTCOME_STATUS_WIN: - amount = domain.CalculateWinnings(bet.Amount, bet.TotalOdds) - case domain.OUTCOME_STATUS_HALF: - amount = domain.CalculateWinnings(bet.Amount, bet.TotalOdds) / 2 - case domain.OUTCOME_STATUS_VOID: - amount = bet.Amount - default: - updateLogger.Error("invalid outcome status") - return fmt.Errorf("invalid outcome status") - } - - _, err = s.walletSvc.AddToWallet(ctx, customerWallet.RegularID, amount, domain.ValidInt64{}, - domain.TRANSFER_DIRECT, domain.PaymentDetails{}, fmt.Sprintf("Added %v to wallet by system for winning a bet", amount.Float32())) - - if err != nil { - updateLogger.Error("failed to add winnings to wallet", - zap.Int64("wallet_id", customerWallet.RegularID), - zap.Float32("amount", float32(amount)), - zap.Error(err), - ) - return err - } - - if err := s.betStore.UpdateStatus(ctx, betId, status); err != nil { - updateLogger.Error("failed to update bet status", - zap.String("status", status.String()), - zap.Error(err), - ) - return err - } - - resultNotification.WinningAmount = amount - if err := s.SendWinningStatusNotification(ctx, resultNotification); err != nil { - - updateLogger.Error("failed to send winning notification", - zap.Error(err), - ) - return err - } - - return nil -} - -func (s *Service) CheckBetOutcomeForBet(ctx context.Context, betID int64) (domain.OutcomeStatus, error) { - betOutcomes, err := s.betStore.GetBetOutcomeByBetID(ctx, betID) - if err != nil { - s.mongoLogger.Error("failed to get bet outcomes", - zap.Int64("bet_id", betID), - zap.Error(err), - ) - return domain.OUTCOME_STATUS_PENDING, err - } - - status := domain.OUTCOME_STATUS_PENDING - - for _, betOutcome := range betOutcomes { - if betOutcome.Status == domain.OUTCOME_STATUS_PENDING { - s.mongoLogger.Info("outcome still pending", - zap.Int64("bet_id", betID), - ) - return domain.OUTCOME_STATUS_PENDING, ErrOutcomesNotCompleted - } - if betOutcome.Status == domain.OUTCOME_STATUS_ERROR { - s.mongoLogger.Info("outcome contains error", - zap.Int64("bet_id", betID), - ) - return domain.OUTCOME_STATUS_ERROR, nil - } - - switch status { - case domain.OUTCOME_STATUS_PENDING: - status = betOutcome.Status - case domain.OUTCOME_STATUS_WIN: - switch betOutcome.Status { - case domain.OUTCOME_STATUS_LOSS: - status = domain.OUTCOME_STATUS_LOSS - case domain.OUTCOME_STATUS_HALF: - status = domain.OUTCOME_STATUS_HALF - case domain.OUTCOME_STATUS_VOID: - status = domain.OUTCOME_STATUS_VOID - case domain.OUTCOME_STATUS_WIN: - // remain win - default: - status = domain.OUTCOME_STATUS_ERROR - } - case domain.OUTCOME_STATUS_LOSS: - // stay as LOSS regardless of others - case domain.OUTCOME_STATUS_VOID: - switch betOutcome.Status { - case domain.OUTCOME_STATUS_LOSS: - status = domain.OUTCOME_STATUS_LOSS - case domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_HALF, domain.OUTCOME_STATUS_VOID: - // remain VOID - default: - status = domain.OUTCOME_STATUS_ERROR - } - case domain.OUTCOME_STATUS_HALF: - switch betOutcome.Status { - case domain.OUTCOME_STATUS_LOSS: - status = domain.OUTCOME_STATUS_LOSS - case domain.OUTCOME_STATUS_VOID: - status = domain.OUTCOME_STATUS_VOID - case domain.OUTCOME_STATUS_HALF, domain.OUTCOME_STATUS_WIN: - // remain HALF - default: - status = domain.OUTCOME_STATUS_ERROR - } - default: - status = domain.OUTCOME_STATUS_ERROR - } - } - - if status == domain.OUTCOME_STATUS_PENDING || status == domain.OUTCOME_STATUS_ERROR { - s.mongoLogger.Info("bet status not updated due to status", - zap.Int64("bet_id", betID), - zap.String("final_status", string(status)), - ) - } - - return status, nil -} - -func (s *Service) UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error) { - betOutcome, err := s.betStore.UpdateBetOutcomeStatus(ctx, id, status) - if err != nil { - s.mongoLogger.Error("failed to update bet outcome status", - zap.Int64("betID", id), - zap.Error(err), - ) - return domain.BetOutcome{}, err - } - - return betOutcome, err - -} - -func (s *Service) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) { - outcomes, err := s.betStore.UpdateBetOutcomeStatusForEvent(ctx, eventID, status) - if err != nil { - s.mongoLogger.Error("failed to update bet outcome status", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - return nil, err - } - - return outcomes, nil -} - -func (s *Service) UpdateBetOutcomeStatusForOddId(ctx context.Context, oddID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) { - outcomes, err := s.betStore.UpdateBetOutcomeStatusForOddId(ctx, oddID, status) - if err != nil { - s.mongoLogger.Error("failed to update bet outcome status", - zap.Int64("oddID", oddID), - zap.Error(err), - ) - return nil, err - } - - return outcomes, nil -} - -func (s *Service) BulkUpdateBetOutcomeStatusForOddIds(ctx context.Context, oddID []int64, status domain.OutcomeStatus) error { - err := s.betStore.BulkUpdateBetOutcomeStatusForOddIds(ctx, oddID, status) - if err != nil { - s.mongoLogger.Error("failed to update bet outcome status by oddIds", - zap.Int64s("oddID", oddID), - zap.Error(err), - ) - return err - } - - return nil -} - -func (s *Service) SetBetToRemoved(ctx context.Context, id int64) error { - _, err := s.betStore.UpdateBetOutcomeStatusByBetID(ctx, id, domain.OUTCOME_STATUS_VOID) - if err != nil { - s.mongoLogger.Error("failed to update bet outcome to void", zap.Int64("id", id), - zap.Error(err), - ) - return err - } - - err = s.betStore.UpdateStatus(ctx, id, domain.OUTCOME_STATUS_VOID) - if err != nil { - s.mongoLogger.Error("failed to update bet to void", zap.Int64("id", id), - zap.Error(err), - ) - return err - } - return nil -} - -func (s *Service) ProcessBetCashback(ctx context.Context) error { - - bets, err := s.betStore.GetBetsForCashback(ctx) - if err != nil { - s.mongoLogger.Error("failed to fetch bets", - zap.Error(err), - ) - return err - } - - for _, bet := range bets { - shouldProcess := true - loseCount := 0 - - for _, outcome := range bet.Outcomes { - // stop if other outcomes exists in bet outcomes - if outcome.Status != domain.OUTCOME_STATUS_LOSS && outcome.Status != domain.OUTCOME_STATUS_WIN { - shouldProcess = false - break - } - - if outcome.Status == domain.OUTCOME_STATUS_LOSS { - loseCount++ - // only process caseback if bet is lost by one - if loseCount > 1 { - break - } - } - } - - if !shouldProcess || loseCount != 1 { - continue - } - - if err := s.betStore.UpdateBetWithCashback(ctx, bet.ID, true); err != nil { - s.mongoLogger.Error("failed to process cashback for bet", - zap.Int64("betID", bet.ID), - zap.Error(err), - ) - continue - } - - wallets, err := s.walletSvc.GetCustomerWallet(ctx, bet.UserID) - if err != nil { - s.mongoLogger.Error("failed to get wallets of a user", - zap.Int64("userID", bet.UserID), - zap.Error(err), - ) - continue - } - - settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, bet.CompanyID) - if err != nil { - s.mongoLogger.Error("Failed to get settings", - zap.Int64("userID", bet.UserID), - zap.Error(err)) - - return err - } - - cashbackAmount := math.Min(float64(settingsList.CashbackAmountCap.Float32()), float64(calculateCashbackAmount(bet.Amount.Float32(), bet.TotalOdds))) - - _, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, domain.ToCurrency(float32(cashbackAmount)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, - domain.PaymentDetails{}, fmt.Sprintf("cashback amount of %f added to users static wallet", cashbackAmount)) - - if err != nil { - s.mongoLogger.Error("Failed to update wallet for user", - zap.Int64("userID", bet.UserID), - zap.Error(err)) - } - } - return nil -} - -func generateOutcomeHash(outcomes []domain.CreateBetOutcome) (string, error) { - // should always be in the same order for producing the same hash - sort.Slice(outcomes, func(i, j int) bool { - if outcomes[i].EventID != outcomes[j].EventID { - return outcomes[i].EventID < outcomes[j].EventID - } - if outcomes[i].MarketID != outcomes[j].MarketID { - return outcomes[i].MarketID < outcomes[j].MarketID - } - return outcomes[i].OddID < outcomes[j].OddID - }) - - var sb strings.Builder - for _, o := range outcomes { - sb.WriteString(fmt.Sprintf("%d-%d-%d;", o.EventID, o.MarketID, o.OddID)) - } - - sum := sha256.Sum256([]byte(sb.String())) - return hex.EncodeToString(sum[:]), nil -} - -func calculateCashbackAmount(amount, total_odds float32) float32 { - var multiplier float32 - - if total_odds < 18 { - multiplier = 0 - } else if total_odds >= 18 && total_odds <= 35 { - multiplier = 1 - } else if total_odds > 35 && total_odds <= 55 { - multiplier = 2 - } else if total_odds > 55 && total_odds <= 95 { - multiplier = 3 - } else if total_odds > 95 && total_odds <= 250 { - multiplier = 5 - } else if total_odds > 250 && total_odds <= 450 { - multiplier = 10 - } else if total_odds > 450 && total_odds <= 1000 { - multiplier = 50 - } else if total_odds > 1000 && total_odds <= 2000 { - multiplier = 100 - } else { - multiplier = 500 - } - - return amount * multiplier -} - -func calculateAccumulator(outcomesCount int) float32 { - switch outcomesCount { - case 3: - return 0.05 - case 4: - return 0.08 - case 5: - return 0.09 - case 6: - return 0.10 - case 7: - return 0.15 - case 8: - return 0.20 - case 9: - return 0.25 - case 10: - return 0.30 - case 11: - return 0.35 - case 12: - return 0.40 - case 13: - return 0.45 - case 14: - return 0.50 - case 15: - return 0.55 - case 16: - return 0.60 - case 17: - return 0.65 - case 18: - return 0.70 - case 19: - return 0.75 - case 20: - return 0.80 - case 21: - return 0.85 - case 22: - return 0.90 - case 23: - return 0.95 - case 24: - return 1.00 - case 25: - return 1.10 - case 26: - return 1.30 - case 27: - return 1.50 - case 28: - return 1.70 - case 29: - return 2.00 - case 30: - return 2.10 - case 31: - return 2.30 - case 32: - return 2.50 - case 33: - return 2.70 - case 34: - return 2.90 - case 35: - return 3.10 - case 36: - return 3.20 - case 37: - return 3.40 - case 38: - return 5.00 - case 39: - return 6.00 - case 40: - return 10.00 - default: - return 0 - } -} - -func (s *Service) CheckIfBetError(err error) bool { - betErrors := []error{ - ErrNoEventsAvailable, - ErrGenerateRandomOutcome, - ErrOutcomesNotCompleted, - ErrEventHasBeenRemoved, - ErrEventHasNotEnded, - ErrRawOddInvalid, - ErrBranchIDRequired, - ErrOutcomeLimit, - ErrTotalBalanceNotEnough, - ErrInvalidAmount, - ErrBetAmountTooHigh, - ErrBetWinningTooHigh, - } - - for _, e := range betErrors { - if errors.Is(err, e) { - return true - } - } - return false -} diff --git a/internal/services/bonus/interface.go b/internal/services/bonus/interface.go deleted file mode 100644 index e7f0dc1..0000000 --- a/internal/services/bonus/interface.go +++ /dev/null @@ -1,17 +0,0 @@ -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/notification.go b/internal/services/bonus/notification.go deleted file mode 100644 index b1f5363..0000000 --- a/internal/services/bonus/notification.go +++ /dev/null @@ -1,85 +0,0 @@ -package bonus - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type SendBonusNotificationParam struct { - BonusID int64 - UserID int64 - Type domain.BonusType - Amount domain.Currency - SendEmail bool - SendSMS bool -} - -func shouldSend(channel domain.DeliveryChannel, sendEmail, sendSMS bool) bool { - switch { - case channel == domain.DeliveryChannelEmail && sendEmail: - return true - case channel == domain.DeliveryChannelSMS && sendSMS: - return true - case channel == domain.DeliveryChannelInApp: - return true - default: - return false - } -} - -func (s *Service) SendBonusNotification(ctx context.Context, param SendBonusNotificationParam) error { - - var ( - headline string - message string - ) - - switch param.Type { - case domain.WelcomeBonus: - headline = "You've been awarded a welcome bonus!" - message = fmt.Sprintf( - "Congratulations! A you've been given %.2f as a welcome bonus for you to bet on.", - param.Amount.Float32(), - ) - default: - return fmt.Errorf("unsupported bonus type: %v", param.Type) - } - for _, channel := range []domain.DeliveryChannel{ - domain.DeliveryChannelInApp, - domain.DeliveryChannelEmail, - domain.DeliveryChannelSMS, - } { - if !shouldSend(channel, param.SendEmail, param.SendSMS) { - continue - } - - raw, _ := json.Marshal(map[string]any{ - "bonus_id": param.BonusID, - "type": param.Type, - }) - - n := &domain.Notification{ - RecipientID: param.UserID, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Type: domain.NOTIFICATION_TYPE_BONUS_AWARDED, - Level: domain.NotificationLevelSuccess, - Reciever: domain.NotificationRecieverSideCustomer, - DeliveryChannel: channel, - Payload: domain.NotificationPayload{ - Headline: headline, - Message: message, - }, - Priority: 2, - Metadata: raw, - } - if err := s.notificationSvc.SendNotification(ctx, n); err != nil { - return err - } - } - - return nil -} diff --git a/internal/services/bonus/service.go b/internal/services/bonus/service.go deleted file mode 100644 index e30c009..0000000 --- a/internal/services/bonus/service.go +++ /dev/null @@ -1,170 +0,0 @@ -package bonus - -import ( - "context" - "errors" - "fmt" - "math" - "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" - "go.uber.org/zap" -) - -type Service struct { - bonusStore ports.BonusStore - walletSvc *wallet.Service - settingSvc *settings.Service - notificationSvc *notificationservice.Service - mongoLogger *zap.Logger -} - -func NewService( - bonusStore ports.BonusStore, - walletSvc *wallet.Service, - settingSvc *settings.Service, - notificationSvc *notificationservice.Service, - mongoLogger *zap.Logger, - ) *Service { - return &Service{ - bonusStore: bonusStore, - walletSvc: walletSvc, - settingSvc: settingSvc, - notificationSvc: notificationSvc, - mongoLogger: mongoLogger, - } -} - -var ( - ErrWelcomeBonusNotActive = errors.New("welcome bonus is not active") - ErrWelcomeBonusCountReached = errors.New("welcome bonus max deposit count reached") -) - -func (s *Service) CreateWelcomeBonus(ctx context.Context, amount domain.Currency, companyID int64, userID int64) error { - settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID) - if err != nil { - s.mongoLogger.Error("Failed to get settings", - zap.Int64("companyID", companyID), - zap.Error(err)) - - return err - } - - if !settingsList.WelcomeBonusActive { - return ErrWelcomeBonusNotActive - } - - wallet, err := s.walletSvc.GetCustomerWallet(ctx, userID) - if err != nil { - return err - } - - stats, err := s.walletSvc.GetTransferStats(ctx, wallet.ID) - - if err != nil { - return err - } - - if stats.TotalDeposits > settingsList.WelcomeBonusCount { - return ErrWelcomeBonusCountReached - } - - newBalance := math.Min(float64(amount)*float64(settingsList.WelcomeBonusMultiplier), float64(settingsList.WelcomeBonusCap)) - - bonus, err := s.CreateUserBonus(ctx, domain.CreateBonus{ - Name: "Welcome Bonus", - Description: fmt.Sprintf("Awarded for deposit number (%v / %v)", stats.TotalDeposits, settingsList.WelcomeBonusCount), - UserID: userID, - Type: domain.WelcomeBonus, - RewardAmount: domain.Currency(newBalance), - ExpiresAt: time.Now().Add(time.Duration(settingsList.WelcomeBonusExpire) * 24 * time.Hour), - }) - - if err != nil { - return err - } - - err = s.SendBonusNotification(ctx, SendBonusNotificationParam{ - BonusID: bonus.ID, - UserID: userID, - Type: domain.DepositBonus, - Amount: domain.Currency(newBalance), - SendEmail: true, - SendSMS: false, - }) - - if err != nil { - return err - } - - return nil -} - -var ( - ErrBonusIsAlreadyClaimed = errors.New("bonus is already claimed") - ErrBonusUserIDNotMatch = errors.New("bonus user id is not a match") -) - -func (s *Service) ProcessBonusClaim(ctx context.Context, bonusID, userID int64) error { - - bonus, err := s.GetBonusByID(ctx, bonusID) - - if err != nil { - return err - } - - if bonus.UserID != userID { - - } - if bonus.IsClaimed { - return ErrBonusIsAlreadyClaimed - } - - wallet, err := s.walletSvc.GetCustomerWallet(ctx, bonus.UserID) - - if err != nil { - return err - } - - _, err = s.walletSvc.AddToWallet( - ctx, wallet.StaticID, bonus.RewardAmount, - domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, - fmt.Sprintf("Added %v to bonus wallet due to %v", bonus.RewardAmount, bonus.Type), - ) - if err != nil { - return err - } - - if err := s.UpdateUserBonus(ctx, bonusID, true); err != nil { - return err - } - - return nil -} - -func (s *Service) CreateUserBonus(ctx context.Context, bonus domain.CreateBonus) (domain.UserBonus, error) { - return s.bonusStore.CreateUserBonus(ctx, bonus) -} -func (s *Service) GetAllUserBonuses(ctx context.Context, filter domain.BonusFilter) ([]domain.UserBonus, error) { - return s.bonusStore.GetAllUserBonuses(ctx, filter) -} - -func (s *Service) GetBonusCount(ctx context.Context, filter domain.BonusFilter) (int64, error) { - return s.bonusStore.GetBonusCount(ctx, filter) -} -func (s *Service) GetBonusByID(ctx context.Context, bonusID int64) (domain.UserBonus, error) { - return s.bonusStore.GetBonusByID(ctx, bonusID) -} -func (s *Service) GetBonusStats(ctx context.Context, filter domain.BonusFilter) (domain.BonusStats, error) { - return s.bonusStore.GetBonusStats(ctx, filter) -} -func (s *Service) UpdateUserBonus(ctx context.Context, bonusID int64, IsClaimed bool) error { - return s.bonusStore.UpdateUserBonus(ctx, bonusID, IsClaimed) -} -func (s *Service) DeleteUserBonus(ctx context.Context, bonusID int64) error { - return s.bonusStore.DeleteUserBonus(ctx, bonusID) -} diff --git a/internal/services/branch/branch_locations.go b/internal/services/branch/branch_locations.go deleted file mode 100644 index ad25f33..0000000 --- a/internal/services/branch/branch_locations.go +++ /dev/null @@ -1,11 +0,0 @@ -package branch - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Service) GetAllBranchLocations(ctx context.Context, query domain.ValidString) ([]domain.BranchLocation, error) { - return s.branchStore.GetAllBranchLocations(ctx, query) -} diff --git a/internal/services/branch/interface.go b/internal/services/branch/interface.go deleted file mode 100644 index 4a15e7e..0000000 --- a/internal/services/branch/interface.go +++ /dev/null @@ -1,34 +0,0 @@ -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 deleted file mode 100644 index 50f9889..0000000 --- a/internal/services/branch/service.go +++ /dev/null @@ -1,81 +0,0 @@ -package branch - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" -) - -type Service struct { - branchStore ports.BranchStore -} - -func NewService(branchStore ports.BranchStore) *Service { - return &Service{ - branchStore: branchStore, - } -} - -func (s *Service) CreateBranch(ctx context.Context, branch domain.CreateBranch) (domain.Branch, error) { - return s.branchStore.CreateBranch(ctx, branch) -} -func (s *Service) CreateSupportedOperation(ctx context.Context, supportedOperation domain.CreateSupportedOperation) (domain.SupportedOperation, error) { - return s.branchStore.CreateSupportedOperation(ctx, supportedOperation) -} -func (s *Service) CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error { - return s.branchStore.CreateBranchOperation(ctx, branchOperation) -} - -func (s *Service) CreateBranchCashier(ctx context.Context, branchID int64, userID int64) error { - return s.branchStore.CreateBranchCashier(ctx, branchID, userID) -} - -func (s *Service) GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error) { - return s.branchStore.GetBranchByID(ctx, id) -} -func (s *Service) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error) { - return s.branchStore.GetBranchByManagerID(ctx, branchManagerID) -} -func (s *Service) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error) { - return s.branchStore.GetBranchByCompanyID(ctx, companyID) -} -func (s *Service) GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error) { - return s.branchStore.GetBranchOperations(ctx, branchID) -} -func (s *Service) GetAllBranches(ctx context.Context, filter domain.BranchFilter) ([]domain.BranchDetail, error) { - return s.branchStore.GetAllBranches(ctx, filter) -} - -func (s *Service) GetBranchByCashier(ctx context.Context, userID int64) (domain.Branch, error) { - return s.branchStore.GetBranchByCashier(ctx, userID) -} - -func (s *Service) GetAllSupportedOperations(ctx context.Context) ([]domain.SupportedOperation, error) { - return s.branchStore.GetAllSupportedOperations(ctx) -} - -func (s *Service) SearchBranchByName(ctx context.Context, name string, companyID domain.ValidInt64) ([]domain.BranchDetail, error) { - return s.branchStore.SearchBranchByName(ctx, name, companyID) -} -func (s *Service) UpdateBranch(ctx context.Context, branch domain.UpdateBranch) (domain.Branch, error) { - return s.branchStore.UpdateBranch(ctx, branch) -} -func (s *Service) DeleteBranch(ctx context.Context, id int64) error { - return s.branchStore.DeleteBranch(ctx, id) -} -func (s *Service) DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error { - return s.branchStore.DeleteBranchOperation(ctx, branchID, operationID) -} - -func (s *Service) DeleteBranchCashier(ctx context.Context, userID int64) error { - return s.branchStore.DeleteBranchCashier(ctx, userID) -} - -func (s *Service) GetAllCompaniesBranch(ctx context.Context) ([]domain.Company, error) { - return s.branchStore.GetAllCompaniesBranch(ctx) -} - -func (s *Service) GetBranchesByCompany(ctx context.Context, companyID int64) ([]domain.Branch, error) { - return s.branchStore.GetBranchesByCompany(ctx, companyID) -} diff --git a/internal/services/chapa/client.go b/internal/services/chapa/client.go deleted file mode 100644 index 764c273..0000000 --- a/internal/services/chapa/client.go +++ /dev/null @@ -1,456 +0,0 @@ -package chapa - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type Client struct { - baseURL string - secretKey string - httpClient *http.Client -} - -func NewClient(baseURL, secretKey string) *Client { - return &Client{ - baseURL: baseURL, - secretKey: secretKey, - httpClient: &http.Client{ - Timeout: 30 * time.Second, - }, - } -} - -func (c *Client) InitializePayment(ctx context.Context, req domain.ChapaInitDepositRequest) (domain.ChapaDepositResponse, error) { - payload := map[string]interface{}{ - "amount": fmt.Sprintf("%.2f", float64(req.Amount)), - "currency": req.Currency, - "email": req.Email, - "first_name": req.FirstName, - "last_name": req.LastName, - "tx_ref": req.TxRef, - // "callback_url": req.CallbackURL, - "return_url": req.ReturnURL, - "phone_number": req.PhoneNumber, - } - - fmt.Printf("\n\nChapa Payload: %+v\n\n", payload) - - payloadBytes, err := json.Marshal(payload) - if err != nil { - return domain.ChapaDepositResponse{}, fmt.Errorf("failed to marshal payload: %w", err) - } - - httpReq, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/transaction/initialize", bytes.NewBuffer(payloadBytes)) - if err != nil { - return domain.ChapaDepositResponse{}, fmt.Errorf("failed to create request: %w", err) - } - - fmt.Printf("\n\nBase URL is: %+v\n\n", c.baseURL) - - httpReq.Header.Set("Authorization", "Bearer "+c.secretKey) - httpReq.Header.Set("Content-Type", "application/json") - - resp, err := c.httpClient.Do(httpReq) - if err != nil { - return domain.ChapaDepositResponse{}, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) // <-- Add this - return domain.ChapaDepositResponse{}, fmt.Errorf("unexpected status code: %d - %s", resp.StatusCode, string(body)) // <-- Log it - } - - // if resp.StatusCode != http.StatusOK { - // return domain.ChapaDepositResponse{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode) - // } - - var response struct { - Message string `json:"message"` - Status string `json:"status"` - Data struct { - CheckoutURL string `json:"checkout_url"` - } `json:"data"` - } - - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return domain.ChapaDepositResponse{}, fmt.Errorf("failed to decode response: %w", err) - } - - return domain.ChapaDepositResponse{ - CheckoutURL: response.Data.CheckoutURL, - Reference: req.TxRef, - }, nil -} - -func (c *Client) VerifyPayment(ctx context.Context, reference string) (domain.ChapaDepositVerification, error) { - httpReq, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/transaction/verify/"+reference, nil) - if err != nil { - return domain.ChapaDepositVerification{}, fmt.Errorf("failed to create request: %w", err) - } - - httpReq.Header.Set("Authorization", "Bearer "+c.secretKey) - - resp, err := c.httpClient.Do(httpReq) - if err != nil { - return domain.ChapaDepositVerification{}, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return domain.ChapaDepositVerification{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } - - var verification domain.ChapaDepositVerification - - if err := json.NewDecoder(resp.Body).Decode(&verification); err != nil { - return domain.ChapaDepositVerification{}, fmt.Errorf("failed to decode response: %w", err) - } - - var status domain.PaymentStatus - switch verification.Status { - case "success": - status = domain.PaymentStatusCompleted - default: - status = domain.PaymentStatusFailed - } - - return domain.ChapaDepositVerification{ - Status: status, - Amount: verification.Amount, - Currency: verification.Currency, - }, nil -} - -func (c *Client) ManualVerifyPayment(ctx context.Context, txRef string) (*domain.ChapaPaymentVerificationResponse, error) { - url := fmt.Sprintf("%s/transaction/verify/%s", c.baseURL, txRef) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Authorization", "Bearer "+c.secretKey) - req.Header.Set("Content-Type", "application/json") - - resp, err := c.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status code: %d - %s", resp.StatusCode, string(body)) - } - - var verification domain.ChapaPaymentVerificationResponse - if err := json.NewDecoder(resp.Body).Decode(&verification); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - // Normalize payment status for internal use - // switch strings.ToLower(verification.Data.Status) { - // case "success": - // verification.Status = string(domain.PaymentStatusCompleted) - // default: - // verification.Status = string(domain.PaymentStatusFailed) - // } - - return &verification, nil -} - -func (c *Client) ManualVerifyTransfer(ctx context.Context, txRef string) (*domain.ChapaTransferVerificationResponse, error) { - url := fmt.Sprintf("%s/transfers/verify/%s", c.baseURL, txRef) - - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Authorization", "Bearer "+c.secretKey) - - resp, err := c.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } - - var response struct { - Status string `json:"status"` - Amount float64 `json:"amount"` - Currency string `json:"currency"` - } - - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - var status domain.PaymentStatus - switch response.Status { - case "success": - status = domain.PaymentStatusCompleted - default: - status = domain.PaymentStatusFailed - } - - return &domain.ChapaTransferVerificationResponse{ - Status: string(status), - // Amount: response.Amount, - // Currency: response.Currency, - }, nil -} - -func (c *Client) GetAllTransactions(ctx context.Context) (domain.ChapaAllTransactionsResponse, error) { - httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+"/transactions", nil) - if err != nil { - return domain.ChapaAllTransactionsResponse{}, fmt.Errorf("failed to create request: %w", err) - } - - httpReq.Header.Set("Authorization", "Bearer "+c.secretKey) - httpReq.Header.Set("Content-Type", "application/json") - - resp, err := c.httpClient.Do(httpReq) - if err != nil { - return domain.ChapaAllTransactionsResponse{}, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return domain.ChapaAllTransactionsResponse{}, fmt.Errorf("unexpected status code: %d - %s", resp.StatusCode, string(body)) - } - - var response domain.ChapaAllTransactionsResponse - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return domain.ChapaAllTransactionsResponse{}, fmt.Errorf("failed to decode response: %w", err) - } - - return response, nil -} - -func (c *Client) GetTransactionEvents(ctx context.Context, refId string) ([]domain.ChapaTransactionEvent, error) { - url := fmt.Sprintf("%s/transaction/events/%s", c.baseURL, refId) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Authorization", "Bearer "+c.secretKey) - req.Header.Set("Content-Type", "application/json") - - resp, err := c.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status code: %d - %s", resp.StatusCode, string(body)) - } - - var response struct { - Message string `json:"message"` - Status string `json:"status"` - Data []domain.ChapaTransactionEvent `json:"data"` - } - - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return response.Data, nil -} - -func (c *Client) FetchSupportedBanks(ctx context.Context) ([]domain.BankData, error) { - req, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/banks", nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Authorization", "Bearer "+c.secretKey) - - resp, err := c.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } - - var bankResponse domain.BankResponse - if err := json.NewDecoder(resp.Body).Decode(&bankResponse); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - var banks []domain.BankData - for _, bankData := range bankResponse.Data { - bank := domain.BankData{ - ID: bankData.ID, - Slug: bankData.Slug, - Swift: bankData.Swift, - Name: bankData.Name, - AcctLength: bankData.AcctLength, - CountryID: bankData.CountryID, - IsMobileMoney: bankData.IsMobileMoney, - IsActive: bankData.IsActive, - IsRTGS: bankData.IsRTGS, - Active: bankData.Active, - Is24Hrs: bankData.Is24Hrs, - CreatedAt: bankData.CreatedAt, - UpdatedAt: bankData.UpdatedAt, - Currency: bankData.Currency, - } - banks = append(banks, bank) - } - - return banks, nil -} - -func (c *Client) InitializeTransfer(ctx context.Context, req domain.ChapaWithdrawalRequest) (bool, error) { - endpoint := c.baseURL + "/transfers" - fmt.Printf("\n\nChapa withdrawal URL is %v\n\n", endpoint) - - reqBody, err := json.Marshal(req) - if err != nil { - return false, fmt.Errorf("failed to marshal request: %w", err) - } - - httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewBuffer(reqBody)) - if err != nil { - return false, fmt.Errorf("failed to create request: %w", err) - } - - // Set headers here - httpReq.Header.Set("Authorization", "Bearer "+c.secretKey) - httpReq.Header.Set("Content-Type", "application/json") - - resp, err := c.httpClient.Do(httpReq) - if err != nil { - return false, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return false, fmt.Errorf("chapa api returned status: %d - %s", resp.StatusCode, string(body)) - } - - var response domain.ChapaWithdrawalResponse - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return false, fmt.Errorf("failed to decode response: %w", err) - } - - return response.Status == string(domain.WithdrawalStatusSuccessful), nil -} - -func (c *Client) VerifyTransfer(ctx context.Context, reference string) (*domain.ChapaTransferVerificationResponse, error) { - base, err := url.Parse(c.baseURL) - if err != nil { - return nil, fmt.Errorf("invalid base URL: %w", err) - } - endpoint := base.ResolveReference(&url.URL{Path: fmt.Sprintf("/v1/transfers/%s/verify", reference)}) - - httpReq, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - c.setHeaders(httpReq) - - resp, err := c.httpClient.Do(httpReq) - if err != nil { - return nil, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("chapa api returned status: %d", resp.StatusCode) - } - - var verification domain.ChapaTransferVerificationResponse - if err := json.NewDecoder(resp.Body).Decode(&verification); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &verification, nil -} - -func (c *Client) CancelTransaction(ctx context.Context, txRef string) (domain.ChapaCancelResponse, error) { - // Construct URL for the cancel transaction endpoint - url := fmt.Sprintf("%s/transaction/cancel/%s", c.baseURL, txRef) - - // Create HTTP request with context - httpReq, err := http.NewRequestWithContext(ctx, http.MethodPut, url, nil) - if err != nil { - return domain.ChapaCancelResponse{}, fmt.Errorf("failed to create request: %w", err) - } - - // Set authorization header - httpReq.Header.Set("Authorization", "Bearer "+c.secretKey) - httpReq.Header.Set("Content-Type", "application/json") - - // Execute the HTTP request - resp, err := c.httpClient.Do(httpReq) - if err != nil { - return domain.ChapaCancelResponse{}, fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - // Handle non-OK responses - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return domain.ChapaCancelResponse{}, fmt.Errorf("unexpected status code: %d - %s", resp.StatusCode, string(body)) - } - - // Decode successful response - var response struct { - Message string `json:"message"` - Status string `json:"status"` - Data struct { - TxRef string `json:"tx_ref"` - Amount float64 `json:"amount"` - Currency string `json:"currency"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` - } `json:"data"` - } - - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { - return domain.ChapaCancelResponse{}, fmt.Errorf("failed to decode response: %w", err) - } - - // Return mapped domain response - return domain.ChapaCancelResponse{ - Message: response.Message, - Status: response.Status, - TxRef: response.Data.TxRef, - Amount: response.Data.Amount, - Currency: response.Data.Currency, - CreatedAt: response.Data.CreatedAt, - UpdatedAt: response.Data.UpdatedAt, - }, nil -} - -func (c *Client) setHeaders(req *http.Request) { - req.Header.Set("Authorization", "Bearer "+c.secretKey) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Accept", "application/json") -} diff --git a/internal/services/chapa/port.go b/internal/services/chapa/port.go deleted file mode 100644 index 1739482..0000000 --- a/internal/services/chapa/port.go +++ /dev/null @@ -1,28 +0,0 @@ -package chapa - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -// type ChapaPort interface { -// HandleChapaTransferWebhook(ctx context.Context, req domain.ChapaWebHookTransfer) error -// HandleChapaPaymentWebhook(ctx context.Context, req domain.ChapaWebHookPayment) error -// WithdrawUsingChapa(ctx context.Context, userID int64, req domain.ChapaWithdrawRequest) error -// DepositUsingChapa(ctx context.Context, userID int64, req domain.ChapaDepositRequest) (string, error) -// GetSupportedBanks() ([]domain.ChapaSupportedBank, error) -// } - -type ChapaStore interface { - InitializePayment(request domain.ChapaInitDepositRequest) (domain.ChapaDepositResponse, error) - ManualVerifTransaction(ctx context.Context, txRef string) (*domain.ChapaTransferVerificationResponse, error) - FetchSupportedBanks(ctx context.Context) ([]domain.Bank, error) - CreateWithdrawal(userID string, amount float64, accountNumber, bankCode string) (*domain.ChapaWithdrawal, error) - HandleVerifyDepositWebhook(ctx context.Context, transfer domain.ChapaWebhookTransfer) error - HandleVerifyWithdrawWebhook(ctx context.Context, payment domain.ChapaWebhookPayment) error - GetPaymentReceiptURL(ctx context.Context, chapaRef string) (string, error) - GetAllTransfers(ctx context.Context) ([]domain.Transfer, error) - GetAccountBalance(ctx context.Context, currencyCode string) ([]domain.Balance, error) - InitiateSwap(ctx context.Context, amount float64, from, to string) (*domain.SwapResponse, error) -} diff --git a/internal/services/chapa/service.go b/internal/services/chapa/service.go deleted file mode 100644 index afbc99c..0000000 --- a/internal/services/chapa/service.go +++ /dev/null @@ -1,721 +0,0 @@ -package chapa - -import ( - "bytes" - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "strconv" - "strings" - - "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 Service struct { - transferStore ports.TransferStore - walletStore wallet.Service - userStore ports.UserStore - cfg *config.Config - chapaClient *Client -} - -func NewService( - transferStore ports.TransferStore, - walletStore wallet.Service, - userStore ports.UserStore, - cfg *config.Config, - chapaClient *Client, - -) *Service { - return &Service{ - transferStore: transferStore, - walletStore: walletStore, - userStore: userStore, - cfg: cfg, - chapaClient: chapaClient, - } -} - -func (s *Service) VerifyWebhookSignature(ctx context.Context, payload []byte, chapaSignature, xChapaSignature string) (bool, error) { - secret := s.cfg.CHAPA_WEBHOOK_SECRET // or os.Getenv("CHAPA_SECRET_KEY") - if secret == "" { - return false, fmt.Errorf("missing Chapa secret key in configuration") - } - - // Compute expected signature using HMAC SHA256 - h := hmac.New(sha256.New, []byte(secret)) - h.Write(payload) - expected := hex.EncodeToString(h.Sum(nil)) - - // Check either header - if chapaSignature == expected || xChapaSignature == expected { - return true, nil - } - - // Optionally log for debugging - var pretty map[string]interface{} - _ = json.Unmarshal(payload, &pretty) - fmt.Printf("[Webhook Verification Failed]\nExpected: %s\nGot chapa-signature: %s\nGot x-chapa-signature: %s\nPayload: %+v\n", - expected, chapaSignature, xChapaSignature, pretty) - - return false, fmt.Errorf("invalid webhook signature") -} - -// InitiateDeposit starts a new deposit process -func (s *Service) InitiateDeposit(ctx context.Context, userID int64, amount domain.Currency) (string, error) { - // Validate amount - if amount <= 0 { - return "", domain.ErrInvalidPaymentAmount - } - - // Get user details - user, err := s.userStore.GetUserByID(ctx, userID) - if err != nil { - return "", fmt.Errorf("failed to get user: %w", err) - } - - // var senderWallet domain.Wallet - - // Generate unique reference - // reference := uuid.New().String() - reference := fmt.Sprintf("chapa-deposit-%d-%s", userID, uuid.New().String()) - - senderWallet, err := s.walletStore.GetCustomerWallet(ctx, userID) - if err != nil { - return "", fmt.Errorf("failed to get sender wallet: %w", err) - } - // for _, wallet := range senderWallets { - // if wallet.IsTransferable { - // senderWallet = wallet - // break - // } - // } - - // Check if payment with this reference already exists - // if transfer, err := s.transferStore.GetTransferByReference(ctx, reference); err == nil { - - // return fmt.Sprintf("%v", transfer), ErrPaymentAlreadyExists - // } - - // Create payment record - transfer := domain.CreateTransfer{ - Message: fmt.Sprintf("Depositing %v into wallet using chapa. Reference Number %v", amount.Float32(), reference), - Amount: amount, - Type: domain.DEPOSIT, - PaymentMethod: domain.TRANSFER_CHAPA, - ReferenceNumber: reference, - // ReceiverWalletID: 1, - SenderWalletID: domain.ValidInt64{ - Value: senderWallet.RegularID, - Valid: true, - }, - Verified: false, - Status: string(domain.STATUS_PENDING), - } - - userPhoneNum := user.PhoneNumber[len(user.PhoneNumber)-9:] - - if len(user.PhoneNumber) >= 9 { - userPhoneNum = "0" + userPhoneNum - } - - payload := domain.ChapaInitDepositRequest{ - Amount: amount, - Currency: "ETB", - Email: user.Email, - FirstName: user.FirstName, - LastName: user.LastName, - TxRef: reference, - CallbackURL: s.cfg.CHAPA_CALLBACK_URL, - ReturnURL: s.cfg.CHAPA_RETURN_URL, - PhoneNumber: userPhoneNum, - } - - // Initialize payment with Chapa - response, err := s.chapaClient.InitializePayment(ctx, payload) - - fmt.Printf("\n\nChapa payload is: %+v\n\n", payload) - - if err != nil { - // Update payment status to failed - // _ = s.transferStore.(payment.ID, domain.PaymentStatusFailed) - return "", fmt.Errorf("failed to initialize payment: %w", err) - } - - tempTransfer, err := s.transferStore.CreateTransfer(ctx, transfer) - - if err != nil { - return "", fmt.Errorf("failed to save payment: %w", err) - } - - fmt.Printf("\n\nTemp transfer is: %v\n\n", tempTransfer) - - return response.CheckoutURL, nil -} - -func (s *Service) ProcessVerifyDepositWebhook(ctx context.Context, req domain.ChapaWebhookPayment) error { - // Find payment by reference - payment, err := s.transferStore.GetTransferByReference(ctx, req.TxRef) - if err != nil { - return domain.ErrPaymentNotFound - } - - if payment.Verified { - return nil - } - - // Verify payment with Chapa - // verification, err := s.chapaClient.VerifyPayment(ctx, transfer.Reference) - // if err != nil { - // return fmt.Errorf("failed to verify payment: %w", err) - // } - - // Update payment status - // verified := false - // if transfer.Status == string(domain.PaymentStatusCompleted) { - // verified = true - // } - - // If payment is completed, credit user's wallet - if req.Status == string(domain.PaymentStatusSuccessful) { - - if err := s.transferStore.UpdateTransferVerification(ctx, payment.ID, true); err != nil { - return fmt.Errorf("failed to update is payment verified value: %w", err) - } - - if err := s.transferStore.UpdateTransferStatus(ctx, payment.ID, string(domain.DepositStatusCompleted)); err != nil { - return fmt.Errorf("failed to update payment status: %w", err) - } - - if _, err := s.walletStore.AddToWallet(ctx, payment.SenderWalletID.Value, payment.Amount, domain.ValidInt64{}, domain.TRANSFER_CHAPA, domain.PaymentDetails{ - ReferenceNumber: domain.ValidString{ - Value: req.TxRef, - }, - }, fmt.Sprintf("Added %v to wallet using Chapa", payment.Amount)); err != nil { - return fmt.Errorf("failed to credit user wallet: %w", err) - } - } - - return nil -} - -func (s *Service) CancelDeposit(ctx context.Context, userID int64, txRef string) (domain.ChapaCancelResponse, error) { - // Validate input - if txRef == "" { - return domain.ChapaCancelResponse{}, fmt.Errorf("transaction reference is required") - } - - // Retrieve user to verify ownership / context (optional but good practice) - user, err := s.userStore.GetUserByID(ctx, userID) - if err != nil { - return domain.ChapaCancelResponse{}, fmt.Errorf("failed to get user: %w", err) - } - - fmt.Printf("\n\nAttempting to cancel Chapa transaction: %s for user %s (%d)\n\n", txRef, user.Email, userID) - - // Call Chapa API to cancel transaction - cancelResp, err := s.chapaClient.CancelTransaction(ctx, txRef) - if err != nil { - return domain.ChapaCancelResponse{}, fmt.Errorf("failed to cancel transaction via Chapa: %w", err) - } - - // Update transfer/payment status locally - transfer, err := s.transferStore.GetTransferByReference(ctx, txRef) - if err != nil { - // Log but do not block cancellation if remote succeeded - fmt.Printf("Warning: unable to find local transfer for txRef %s: %v\n", txRef, err) - } else { - if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.STATUS_CANCELLED)); err != nil { - fmt.Printf("Warning: failed to update transfer status for txRef %s: %v\n", txRef, err) - } - - if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, false); err != nil { - fmt.Printf("Warning: failed to update transfer status for txRef %s: %v\n", txRef, err) - } - } - - fmt.Printf("\n\nChapa cancellation response: %+v\n\n", cancelResp) - - return cancelResp, nil -} - -func (s *Service) FetchAllTransactions(ctx context.Context) ([]domain.ChapaTransaction, error) { - // Call Chapa API to get all transactions - resp, err := s.chapaClient.GetAllTransactions(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch transactions from Chapa: %w", err) - } - - if resp.Status != "success" { - return nil, fmt.Errorf("chapa API returned non-success status: %s", resp.Status) - } - - transactions := make([]domain.ChapaTransaction, 0, len(resp.Data.Transactions)) - - // Map API transactions to domain transactions - for _, t := range resp.Data.Transactions { - tx := domain.ChapaTransaction{ - Status: t.Status, - RefID: t.RefID, - Type: t.Type, - CreatedAt: t.CreatedAt, - Currency: t.Currency, - Amount: t.Amount, - Charge: t.Charge, - TransID: t.TransID, - PaymentMethod: t.PaymentMethod, - Customer: domain.ChapaCustomer{ - ID: t.Customer.ID, - Email: t.Customer.Email, - FirstName: t.Customer.FirstName, - LastName: t.Customer.LastName, - Mobile: t.Customer.Mobile, - }, - } - transactions = append(transactions, tx) - } - - return transactions, nil -} - -func (s *Service) FetchTransactionEvents(ctx context.Context, refID string) ([]domain.ChapaTransactionEvent, error) { - if refID == "" { - return nil, fmt.Errorf("transaction reference ID is required") - } - - // Call Chapa client to fetch transaction events - events, err := s.chapaClient.GetTransactionEvents(ctx, refID) - if err != nil { - return nil, fmt.Errorf("failed to fetch transaction events from Chapa: %w", err) - } - - // Optional: Transform or filter events if needed - transformedEvents := make([]domain.ChapaTransactionEvent, 0, len(events)) - for _, e := range events { - transformedEvents = append(transformedEvents, domain.ChapaTransactionEvent{ - Item: e.Item, - Message: e.Message, - Type: e.Type, - CreatedAt: e.CreatedAt, - UpdatedAt: e.UpdatedAt, - }) - } - - return transformedEvents, nil -} - -func (s *Service) InitiateWithdrawal(ctx context.Context, userID int64, req domain.ChapaWithdrawalRequest) (*domain.Transfer, error) { - // Parse and validate amount - amount, err := strconv.ParseFloat(req.Amount, 64) - if err != nil || amount <= 0 { - return nil, domain.ErrInvalidWithdrawalAmount - } - - // Get user's wallet - wallet, err := s.walletStore.GetCustomerWallet(ctx, userID) - if err != nil { - return nil, fmt.Errorf("failed to get user wallets: %w", err) - } - - // var withdrawWallet domain.Wallet - // for _, wallet := range wallets { - // if wallet.IsWithdraw { - // withdrawWallet = wallet - // break - // } - // } - - // if withdrawWallet.ID == 0 { - // return nil, errors.New("withdrawal wallet not found") - // } - // Check balance - if float64(wallet.RegularBalance) < float64(amount) { - return nil, domain.ErrInsufficientBalance - } - - // Generate unique reference - reference := uuid.New().String() - - createTransfer := domain.CreateTransfer{ - Message: fmt.Sprintf("Withdrawing %f into wallet using chapa. Reference Number %s", amount, reference), - Amount: domain.Currency(amount), - Type: domain.WITHDRAW, - SenderWalletID: domain.ValidInt64{ - Value: wallet.RegularID, - Valid: true, - }, - Status: string(domain.PaymentStatusPending), - Verified: false, - ReferenceNumber: reference, - PaymentMethod: domain.TRANSFER_CHAPA, - } - - transfer, err := s.transferStore.CreateTransfer(ctx, createTransfer) - - if err != nil { - return nil, fmt.Errorf("failed to create transfer record: %w", err) - } - // Initiate transfer with Chapa - transferReq := domain.ChapaWithdrawalRequest{ - AccountName: req.AccountName, - AccountNumber: req.AccountNumber, - Amount: fmt.Sprintf("%f", amount), - Currency: req.Currency, - Reference: reference, - // BeneficiaryName: fmt.Sprintf("%s %s", user.FirstName, user.LastName), - BankCode: req.BankCode, - } - - newBalance := float64(wallet.RegularBalance) - float64(amount) - if err := s.walletStore.UpdateBalance(ctx, wallet.RegularID, domain.Currency(newBalance)); err != nil { - return nil, fmt.Errorf("failed to update wallet balance: %w", err) - } - - success, err := s.chapaClient.InitializeTransfer(ctx, transferReq) - if err != nil { - _ = s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusFailed)) - newBalance := float64(wallet.RegularBalance) + float64(amount) - if err := s.walletStore.UpdateBalance(ctx, wallet.RegularID, domain.Currency(newBalance)); err != nil { - return nil, fmt.Errorf("failed to update wallet balance: %w", err) - } - return nil, fmt.Errorf("failed to initiate transfer: %w", err) - } - - if !success { - _ = s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusFailed)) - newBalance := float64(wallet.RegularBalance) + float64(amount) - if err := s.walletStore.UpdateBalance(ctx, wallet.RegularID, domain.Currency(newBalance)); err != nil { - return nil, fmt.Errorf("failed to update wallet balance: %w", err) - } - return nil, errors.New("chapa rejected the transfer request") - } - - // Update withdrawal status to processing - // if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusProcessing)); err != nil { - // return nil, fmt.Errorf("failed to update withdrawal status: %w", err) - // } - // Deduct from wallet (or wait for webhook confirmation depending on your flow) - - return &transfer, nil -} - -func (s *Service) ProcessVerifyWithdrawWebhook(ctx context.Context, req domain.ChapaWebhookTransfer) error { - // Find payment by reference - transfer, err := s.transferStore.GetTransferByReference(ctx, req.Reference) - if err != nil { - return domain.ErrPaymentNotFound - } - - if transfer.Verified { - return nil - } - - // Verify payment with Chapa - // verification, err := s.chapaClient.VerifyPayment(ctx, payment.Reference) - // if err != nil { - // return fmt.Errorf("failed to verify payment: %w", err) - // } - - // Update payment status - // verified := false - // if transfer.Status == string(domain.PaymentStatusCompleted) { - // verified = true - // } - - if req.Status == string(domain.PaymentStatusSuccessful) { - if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil { - return fmt.Errorf("failed to update payment status: %w", err) - } // If payment is completed, credit user's walle - } else { - _, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, transfer.Amount, domain.ValidInt64{}, - domain.TRANSFER_DIRECT, domain.PaymentDetails{}, - fmt.Sprintf("Added %v to wallet by system because chapa withdraw is unsuccessful", transfer.Amount)) - if err != nil { - return fmt.Errorf("failed to credit user wallet: %w", err) - } - } - - return nil -} - -func (s *Service) GetPaymentReceiptURL(refId string) (string, error) { - if refId == "" { - return "", fmt.Errorf("reference ID cannot be empty") - } - - receiptURL := s.cfg.CHAPA_RECEIPT_URL + refId - return receiptURL, nil -} - -func (s *Service) ManuallyVerify(ctx context.Context, txRef string) (any, error) { - // Lookup transfer by reference - transfer, err := s.transferStore.GetTransferByReference(ctx, txRef) - if err != nil { - return nil, fmt.Errorf("transfer not found for reference %s: %w", txRef, err) - } - - // If already verified, just return a completed response - if transfer.Verified { - return map[string]any{}, errors.New("transfer already verified") - } - - // Validate sender wallet - if !transfer.SenderWalletID.Valid { - return nil, fmt.Errorf("invalid sender wallet ID: %v", transfer.SenderWalletID) - } - - var verification any - - switch strings.ToLower(string(transfer.Type)) { - case string(domain.DEPOSIT): - // Verify Chapa payment - verification, err := s.chapaClient.ManualVerifyPayment(ctx, txRef) - if err != nil { - return nil, fmt.Errorf("failed to verify deposit with Chapa: %w", err) - } - - if strings.ToLower(verification.Data.Status) == "success" || - verification.Status == string(domain.PaymentStatusCompleted) { - - // Credit wallet - _, err := s.walletStore.AddToWallet(ctx, - transfer.SenderWalletID.Value, - transfer.Amount, - domain.ValidInt64{}, - domain.TRANSFER_CHAPA, - domain.PaymentDetails{}, - fmt.Sprintf("Added %.2f ETB to wallet using Chapa", transfer.Amount.Float32())) - if err != nil { - return nil, fmt.Errorf("failed to credit wallet: %w", err) - } - - // Mark verified in DB - if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil { - return nil, fmt.Errorf("failed to mark deposit transfer as verified: %w", err) - } - if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.DepositStatusCompleted)); err != nil { - return nil, fmt.Errorf("failed to update deposit transfer status: %w", err) - } - } - - case string(domain.WITHDRAW): - // Verify Chapa transfer - verification, err := s.chapaClient.ManualVerifyTransfer(ctx, txRef) - if err != nil { - return nil, fmt.Errorf("failed to verify withdrawal with Chapa: %w", err) - } - - if strings.ToLower(verification.Data.Status) == "success" || - verification.Status == string(domain.PaymentStatusCompleted) { - - // Deduct wallet - _, err := s.walletStore.DeductFromWallet(ctx, - transfer.SenderWalletID.Value, - transfer.Amount, - domain.ValidInt64{}, - domain.TRANSFER_CHAPA, - fmt.Sprintf("Deducted %.2f ETB from wallet using Chapa", transfer.Amount.Float32())) - if err != nil { - return nil, fmt.Errorf("failed to debit wallet: %w", err) - } - - // Mark verified in DB - if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil { - return nil, fmt.Errorf("failed to mark withdraw transfer as verified: %w", err) - } - if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.WithdrawalStatusCompleted)); err != nil { - return nil, fmt.Errorf("failed to update withdraw transfer status: %w", err) - } - } - - default: - return nil, fmt.Errorf("unsupported transfer type: %s", transfer.Type) - } - - return verification, nil -} - -func (s *Service) GetSupportedBanks(ctx context.Context) ([]domain.BankData, error) { - banks, err := s.chapaClient.FetchSupportedBanks(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch banks: %w", err) - } - return banks, nil -} - -func (s *Service) GetAllTransfers(ctx context.Context) (*domain.ChapaTransfersListResponse, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, s.cfg.CHAPA_BASE_URL+"/transfers", nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.cfg.CHAPA_SECRET_KEY)) - - resp, err := s.chapaClient.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to fetch transfers: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - bodyBytes, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(bodyBytes)) - } - - var result domain.ChapaTransfersListResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - // Return the decoded result directly; no intermediate dynamic map needed - return &result, nil -} - -func (s *Service) GetAccountBalance(ctx context.Context, currencyCode string) ([]domain.Balance, error) { - URL := s.cfg.CHAPA_BASE_URL + "/balances" - if currencyCode != "" { - URL = fmt.Sprintf("%s/%s", URL, strings.ToLower(currencyCode)) - } - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL, nil) - if err != nil { - return nil, fmt.Errorf("failed to create balance request: %w", err) - } - - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.cfg.CHAPA_SECRET_KEY)) - - resp, err := s.chapaClient.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to execute balance request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - bodyBytes, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(bodyBytes)) - } - - var result struct { - Status string `json:"status"` - Message string `json:"message"` - Data []domain.Balance `json:"data"` - } - - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode balance response: %w", err) - } - - return result.Data, nil -} - -func (s *Service) SwapCurrency(ctx context.Context, reqBody domain.SwapRequest) (*domain.SwapResponse, error) { - URL := s.cfg.CHAPA_BASE_URL + "/swap" - - // Normalize currency codes - reqBody.From = strings.ToUpper(reqBody.From) - reqBody.To = strings.ToUpper(reqBody.To) - - // Marshal request body - body, err := json.Marshal(reqBody) - if err != nil { - return nil, fmt.Errorf("failed to marshal swap payload: %w", err) - } - - // Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodPost, URL, bytes.NewBuffer(body)) - if err != nil { - return nil, fmt.Errorf("failed to create swap request: %w", err) - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.cfg.CHAPA_SECRET_KEY)) - - // Execute request - resp, err := s.chapaClient.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to execute swap request: %w", err) - } - defer resp.Body.Close() - - // Handle unexpected status - if resp.StatusCode != http.StatusOK { - bodyBytes, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(bodyBytes)) - } - - // Decode response - var result domain.SwapResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode swap response: %w", err) - } - - return &result, nil -} - -// func (s *Service) InitiateSwap(ctx context.Context, amount float64, from, to string) (*domain.SwapResponse, error) { -// if amount < 1 { -// return nil, fmt.Errorf("amount must be at least 1 USD") -// } -// if strings.ToUpper(from) != "USD" || strings.ToUpper(to) != "ETB" { -// return nil, fmt.Errorf("only USD to ETB swap is supported") -// } - -// payload := domain.SwapRequest{ -// Amount: amount, -// From: strings.ToUpper(from), -// To: strings.ToUpper(to), -// } - -// // payload := map[string]any{ -// // "amount": amount, -// // "from": strings.ToUpper(from), -// // "to": strings.ToUpper(to), -// // } - -// body, err := json.Marshal(payload) -// if err != nil { -// return nil, fmt.Errorf("failed to encode swap payload: %w", err) -// } - -// req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.chapa.co/v1/swap", bytes.NewBuffer(body)) -// if err != nil { -// return nil, fmt.Errorf("failed to create swap request: %w", err) -// } - -// req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.cfg.CHAPA_SECRET_KEY)) -// req.Header.Set("Content-Type", "application/json") - -// resp, err := s.chapaClient.httpClient.Do(req) -// if err != nil { -// return nil, fmt.Errorf("failed to execute swap request: %w", err) -// } -// defer resp.Body.Close() - -// if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { -// bodyBytes, _ := io.ReadAll(resp.Body) -// return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(bodyBytes)) -// } - -// var result struct { -// Message string `json:"message"` -// Status string `json:"status"` -// Data domain.SwapResponse `json:"data"` -// } - -// if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { -// return nil, fmt.Errorf("failed to decode swap response: %w", err) -// } - -// return &result.Data, nil -// } diff --git a/internal/services/company/interface.go b/internal/services/company/interface.go deleted file mode 100644 index 5fff5aa..0000000 --- a/internal/services/company/interface.go +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index 756a9c9..0000000 --- a/internal/services/company/service.go +++ /dev/null @@ -1,43 +0,0 @@ -package company - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" -) - -type Service struct { - companyStore ports.CompanyStore -} - -func NewService(companyStore ports.CompanyStore) *Service { - return &Service{ - companyStore: companyStore, - } -} - -func (s *Service) CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error) { - return s.companyStore.CreateCompany(ctx, company) -} -func (s *Service) GetAllCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.GetCompany, error) { - return s.companyStore.GetAllCompanies(ctx, filter) -} - -func (s *Service) GetCompanyByID(ctx context.Context, id int64) (domain.GetCompany, error) { - return s.companyStore.GetCompanyByID(ctx, id) -} -func (s *Service) GetCompanyBySlug(ctx context.Context, slug string) (domain.Company, error) { - return s.companyStore.GetCompanyBySlug(ctx, slug) -} - -func (s *Service) SearchCompanyByName(ctx context.Context, name string) ([]domain.GetCompany, error) { - return s.companyStore.SearchCompanyByName(ctx, name) -} - -func (s *Service) UpdateCompany(ctx context.Context, company domain.UpdateCompany) (error) { - return s.companyStore.UpdateCompany(ctx, company) -} -func (s *Service) DeleteCompany(ctx context.Context, id int64) error { - return s.companyStore.DeleteCompany(ctx, id) -} diff --git a/internal/services/currency/fetcher.go b/internal/services/currency/fetcher.go index 76d4739..87d0fec 100644 --- a/internal/services/currency/fetcher.go +++ b/internal/services/currency/fetcher.go @@ -1,13 +1,12 @@ package currency import ( + "Yimaru-Backend/internal/domain" "context" "encoding/json" "fmt" "net/http" "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) type FixerFetcher struct { @@ -17,6 +16,7 @@ type FixerFetcher struct { } func NewFixerFetcher(apiKey string, baseURL string) *FixerFetcher { + return &FixerFetcher{ apiKey: apiKey, baseURL: baseURL, diff --git a/internal/services/currency/service.go b/internal/services/currency/service.go index 5351b11..b5d96a3 100644 --- a/internal/services/currency/service.go +++ b/internal/services/currency/service.go @@ -1,12 +1,12 @@ package currency import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/repository" "context" "fmt" "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" + ) type Service struct { diff --git a/internal/services/currency/worker.go b/internal/services/currency/worker.go index c020e08..286d4a1 100644 --- a/internal/services/currency/worker.go +++ b/internal/services/currency/worker.go @@ -1,12 +1,12 @@ package currency import ( + "Yimaru-Backend/internal/config" "context" "fmt" "log/slog" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/go-co-op/gocron" ) diff --git a/internal/services/direct_deposit/service.go b/internal/services/direct_deposit/service.go deleted file mode 100644 index 2b73c3d..0000000 --- a/internal/services/direct_deposit/service.go +++ /dev/null @@ -1,305 +0,0 @@ -package directdeposit - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" - notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" -) - -type Service struct { - walletSvc wallet.Service - transferStore ports.TransferStore - directDepositStore repository.DirectDepositRepository - notificationSvc *notificationservice.Service - userSvc *user.Service - // mongoLogger *zap.Logger - // logger *slog.Logger -} - -func NewService( - walletSvc wallet.Service, - transferStore ports.TransferStore, - directDepositStore repository.DirectDepositRepository, - notificationSvc *notificationservice.Service, - userSvc *user.Service, - // mongoLogger *zap.Logger, - // logger *slog.Logger, -) *Service { - return &Service{ - walletSvc: walletSvc, - transferStore: transferStore, - directDepositStore: directDepositStore, - notificationSvc: notificationSvc, - userSvc: userSvc, - // mongoLogger: mongoLogger, - // logger: logger, - } -} - -func (s *Service) CreateDirectDeposit( - ctx context.Context, - req domain.CreateDirectDeposit, -) (domain.DirectDeposit, error) { - - deposit := domain.DirectDeposit{ - CustomerID: req.CustomerID, - WalletID: req.WalletID, - BankName: req.BankName, - AccountNumber: req.AccountNumber, - AccountHolder: req.AccountHolder, - Amount: req.Amount, - ReferenceNumber: req.ReferenceNumber, - TransferScreenshot: req.TransferScreenshot, - Status: "PENDING", - } - - // Step 1: create the deposit in DB - if err := s.directDepositStore.CreateDirectDeposit(ctx, &deposit); err != nil { - return domain.DirectDeposit{}, err - } - - // Step 2: prepare common notification metadata - raw, _ := json.Marshal(map[string]any{ - "deposit_id": deposit.ID, - "customer_id": deposit.CustomerID, - "amount": deposit.Amount, - "status": deposit.Status, - "timestamp": time.Now(), - }) - - // ------------------------------- - // Step 3a: notify the customer - customerNotification := &domain.Notification{ - RecipientID: int64(deposit.CustomerID), - DeliveryChannel: domain.DeliveryChannelInApp, - Reciever: domain.NotificationRecieverSideCustomer, - Type: domain.NOTIFICATION_TYPE_WALLET, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Level: domain.NotificationLevelInfo, - Priority: 2, - Metadata: raw, - Payload: domain.NotificationPayload{ - Headline: "Direct Deposit Created", - Message: fmt.Sprintf("Your direct deposit of %.2f is now pending approval.", deposit.Amount), - }, - } - - if err := s.notificationSvc.SendNotification(ctx, customerNotification); err != nil { - return domain.DirectDeposit{}, err - } - - // ------------------------------- - // Step 3b: notify admins - adminNotification := &domain.Notification{ - RecipientID: 0, // 0 or special ID for admin-wide notifications - DeliveryChannel: domain.DeliveryChannelInApp, - Reciever: domain.NotificationRecieverSideAdmin, - Type: domain.NOTIFICATION_TYPE_APPROVAL_REQUIRED, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Level: domain.NotificationLevelInfo, - Priority: 2, - Metadata: raw, - Payload: domain.NotificationPayload{ - Headline: "New Direct Deposit Pending Approval", - Message: fmt.Sprintf("Customer #%d has created a direct deposit of %.2f that requires your approval.", deposit.CustomerID, deposit.Amount), - }, - } - - if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { - return domain.DirectDeposit{}, err - } - - return deposit, nil -} - -func (s *Service) GetDirectDepositsByStatus( - ctx context.Context, - status string, - page int, - pageSize int, -) ([]domain.DirectDeposit, int64, error) { - - deposits, total, err := s.directDepositStore.GetDirectDepositsByStatus( - ctx, - status, - page, - pageSize, - ) - if err != nil { - return nil, 0, err - } - - return deposits, total, nil -} - -func (s *Service) ApproveDirectDeposit( - ctx context.Context, - depositID int, - adminID int, -) error { - - // Step 1: fetch deposit (ensure it exists) - deposit, err := s.directDepositStore.GetDirectDepositByID(ctx, depositID) - if err != nil { - return err - } - - // Step 2: approve in DB - if err := s.directDepositStore.ApproveDirectDeposit(ctx, depositID, adminID); err != nil { - return err - } - - // Step 3: credit wallet balance - wallet, err := s.walletSvc.GetCustomerWallet(ctx, int64(deposit.CustomerID)) - if err != nil { - return err - } - if _, err := s.walletSvc.AddToWallet(ctx, - wallet.RegularID, - domain.Currency(deposit.Amount), - domain.ValidInt64{}, - domain.TRANSFER_DIRECT, - domain.PaymentDetails{}, - "", - ); err != nil { - return err - } - - // Step 4: record transfer - transfer := domain.CreateTransfer{ - Amount: domain.Currency(deposit.Amount), - Verified: true, - Message: "Direct deposit approved and credited", - Type: domain.DEPOSIT, - PaymentMethod: domain.TRANSFER_DIRECT, - ReceiverWalletID: domain.ValidInt64{Valid: true, Value: wallet.RegularID}, - ReferenceNumber: deposit.ReferenceNumber, - Status: string(domain.DepositStatusCompleted), - } - - if _, err := s.transferStore.CreateTransfer(ctx, transfer); err != nil { - return err - } - - // Step 5: send customer notification - raw, _ := json.Marshal(map[string]any{ - "deposit_id": deposit.ID, - "amount": deposit.Amount, - "status": "APPROVED", - "timestamp": time.Now(), - }) - - notification := &domain.Notification{ - RecipientID: int64(deposit.CustomerID), - DeliveryChannel: domain.DeliveryChannelInApp, - Reciever: domain.NotificationRecieverSideCustomer, - Type: domain.NOTIFICATION_TYPE_TRANSFER_SUCCESS, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Level: domain.NotificationLevelInfo, - Priority: 2, - Metadata: raw, - Payload: domain.NotificationPayload{ - Headline: "Direct Deposit Approved", - Message: fmt.Sprintf("Your direct deposit of %.2f has been approved and credited to your wallet.", deposit.Amount), - }, - } - - if err := s.notificationSvc.SendNotification(ctx, notification); err != nil { - return err - } - - return nil -} - -func (s *Service) RejectDirectDeposit( - ctx context.Context, - depositID int, - adminID int, - reason string, -) error { - - // Step 1: fetch deposit to ensure it exists - deposit, err := s.directDepositStore.GetDirectDepositByID(ctx, depositID) - if err != nil { - return err - } - - // Step 2: reject operation - if err := s.directDepositStore.RejectDirectDeposit(ctx, depositID, adminID, reason); err != nil { - return err - } - - // Step 3: send customer notification - raw, _ := json.Marshal(map[string]any{ - "deposit_id": deposit.ID, - "amount": deposit.Amount, - "status": "REJECTED", - "reason": reason, - "timestamp": time.Now(), - }) - - notification := &domain.Notification{ - RecipientID: int64(deposit.CustomerID), - DeliveryChannel: domain.DeliveryChannelInApp, - Reciever: domain.NotificationRecieverSideCustomer, - Type: domain.NOTIFICATION_TYPE_TRANSFER_REJECTED, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Level: domain.NotificationLevelWarning, - Priority: 2, - Metadata: raw, - Payload: domain.NotificationPayload{ - Headline: "Direct Deposit Rejected", - Message: fmt.Sprintf("Your direct deposit of %.2f was rejected. Reason: %s", deposit.Amount, reason), - }, - } - - if err := s.notificationSvc.SendNotification(ctx, notification); err != nil { - return err - } - - return nil -} - -func (s *Service) GetDirectDepositByID( - ctx context.Context, - depositID int, -) (*domain.DirectDeposit, error) { - - deposit, err := s.directDepositStore.GetDirectDepositByID(ctx, depositID) - if err != nil { - return nil, err - } - - return deposit, nil -} - -func (s *Service) DeleteDirectDeposit( - ctx context.Context, - depositID int, -) error { - - // Optional: fetch first to ensure deposit exists - _, err := s.directDepositStore.GetDirectDepositByID(ctx, depositID) - if err != nil { - return err - } - - // Perform deletion - if err := s.directDepositStore.DeleteDirectDeposit(ctx, depositID); err != nil { - return err - } - - return nil -} diff --git a/internal/services/enet_pulse/port.go b/internal/services/enet_pulse/port.go deleted file mode 100644 index e9e0b1b..0000000 --- a/internal/services/enet_pulse/port.go +++ /dev/null @@ -1,18 +0,0 @@ -package enetpulse - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type EnetPulseService interface { - FetchSports(ctx context.Context) error - FetchTournamentTemplates(ctx context.Context) (*domain.TournamentTemplatesResponse, error) - FetchTournamentTemplateParticipants(ctx context.Context, templateID string, opts domain.ParticipantsOptions) (*domain.TournamentTemplateParticipantsResponse, error) - FetchTournaments(ctx context.Context, templateID string) error - FetchTournamentParticipants(ctx context.Context, tournamentID string) error - FetchPreMatchOdds(ctx context.Context, params domain.PreMatchOddsRequest) (*domain.PreMatchOddsResponse, error) - FetchCountryFlag(ctx context.Context, countryFK int64) (*domain.ImageResponse, error) - GetAllPreoddsWithBettingOffers(ctx context.Context) ([]domain.EnetpulsePreodds, error) -} diff --git a/internal/services/enet_pulse/service.go b/internal/services/enet_pulse/service.go deleted file mode 100644 index fd9b7d2..0000000 --- a/internal/services/enet_pulse/service.go +++ /dev/null @@ -1,2119 +0,0 @@ -package enetpulse - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "strconv" - "strings" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" -) - -type Service struct { - // username string - // token string - cfg config.Config - store *repository.Store - // mongoLogger *zap.Logger - httpClient *http.Client -} - -func New(cfg config.Config, store *repository.Store) *Service { - return &Service{ - cfg: cfg, - store: store, - // mongoLogger: mongoLogger, - httpClient: &http.Client{ - Timeout: 15 * time.Second, - }, - } -} - -func (s *Service) FetchAndStoreSports(ctx context.Context) error { - // 1️⃣ Compose URL with credentials - url := fmt.Sprintf( - "http://eapi.enetpulse.com/sport/list/?username=%s&token=%s", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, - ) - - // 2️⃣ Create HTTP request with context - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return fmt.Errorf("creating sport request: %w", err) - } - - // 3️⃣ Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - return fmt.Errorf("requesting sports: %w", err) - } - defer resp.Body.Close() - - // 4️⃣ Check response status - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to fetch sports (status %d): %s", resp.StatusCode, string(body)) - } - - // 5️⃣ Decode JSON response - var sportsResp struct { - Sports map[string]struct { - ID string `json:"id"` - N string `json:"n"` // updates count - Name string `json:"name"` - UT string `json:"ut"` // timestamp string - } `json:"sports"` - } - if err := json.NewDecoder(resp.Body).Decode(&sportsResp); err != nil { - return fmt.Errorf("decoding sports response: %w", err) - } - - // 6️⃣ Iterate and store each sport - for _, sport := range sportsResp.Sports { - // Parse updates count - updatesCount := 0 - if sport.N != "" { - if n, err := strconv.Atoi(sport.N); err == nil { - updatesCount = n - } - } - - // Parse timestamp - lastUpdatedAt, err := time.Parse(time.RFC3339, sport.UT) - if err != nil { - // Fallback to zero time if parsing fails - lastUpdatedAt = time.Time{} - } - - // Build domain object - createSport := domain.CreateEnetpulseSport{ - SportID: sport.ID, - Name: sport.Name, - UpdatesCount: updatesCount, - LastUpdatedAt: lastUpdatedAt, - Status: 1, // default active - } - - // Insert or update in DB - if _, err := s.store.CreateEnetpulseSport(ctx, createSport); err != nil { - // Log error but continue - // s.logger.Error("failed to store sport", zap.String("sport_id", sport.ID), zap.Error(err)) - continue - } - } - - // s.logger.Info("Successfully fetched and stored sports", zap.Int("count", len(sportsResp.Sports))) - return nil -} - -func (s *Service) GetAllSports(ctx context.Context) ([]domain.EnetpulseSport, error) { - // 1️⃣ Fetch all sports from the store (database) - sports, err := s.store.GetAllEnetpulseSports(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch sports from DB: %w", err) - } - - // 2️⃣ Optionally, you can log the count or perform other transformations - // s.logger.Info("Fetched sports from DB", zap.Int("count", len(sports))) - - return sports, nil -} - -func (s *Service) FetchAndStoreTournamentTemplates(ctx context.Context) error { - // 1️⃣ Fetch all sports from the database - sports, err := s.store.GetAllEnetpulseSports(ctx) - if err != nil { - return fmt.Errorf("failed to fetch sports from DB: %w", err) - } - - // Template struct - type TournamentTemplate struct { - ID string `json:"id"` - Name string `json:"name"` - SportFK string `json:"sportFK"` - Gender string `json:"gender"` - N string `json:"n"` - UT string `json:"ut"` - } - - for _, sport := range sports { - - if sport.SportID != "1" { - continue - } else { - // 2️⃣ Compose URL for each sport using its Enetpulse sportFK - url := fmt.Sprintf( - "http://eapi.enetpulse.com/tournament_template/list/?sportFK=%s&username=%s&token=%s", - sport.SportID, // must be Enetpulse sportFK - s.cfg.EnetPulseConfig.UserName, - s.cfg.EnetPulseConfig.Token, - ) - - fmt.Println("Fetching tournament templates:", url) - - // 3️⃣ Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return fmt.Errorf("creating tournament template request for sport %s: %w", sport.SportID, err) - } - - // 4️⃣ Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - return fmt.Errorf("requesting tournament templates for sport %s: %w", sport.SportID, err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to fetch tournament templates for sport %s (status %d): %s", - sport.SportID, resp.StatusCode, string(body)) - } - - // 5️⃣ Decode JSON response flexibly - var raw struct { - TournamentTemplates json.RawMessage `json:"tournament_templates"` - } - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("reading tournament templates response for sport %s: %w", sport.SportID, err) - } - if err := json.Unmarshal(bodyBytes, &raw); err != nil { - return fmt.Errorf("unmarshalling raw tournament templates for sport %s: %w", sport.SportID, err) - } - - // 6️⃣ Parse depending on object or array - templates := map[string]TournamentTemplate{} - if len(raw.TournamentTemplates) > 0 && raw.TournamentTemplates[0] == '{' { - // Object (normal case) - if err := json.Unmarshal(raw.TournamentTemplates, &templates); err != nil { - return fmt.Errorf("decoding tournament templates (object) for sport %s: %w", sport.SportID, err) - } - } else { - // Array or empty → skip safely - fmt.Printf("No tournament templates found for sport %s\n", sport.SportID) - continue - } - - // 7️⃣ Iterate and store each tournament template - for _, tmpl := range templates { - updatesCount := 0 - if tmpl.N != "" { - if n, err := strconv.Atoi(tmpl.N); err == nil { - updatesCount = n - } - } - - lastUpdatedAt, err := time.Parse(time.RFC3339, tmpl.UT) - if err != nil { - lastUpdatedAt = time.Time{} - } - - // Convert sport.SportID from string to int64 - sportFK, err := strconv.ParseInt(sport.SportID, 10, 64) - if err != nil { - fmt.Printf("failed to convert sport.SportID '%s' to int64: %v\n", sport.SportID, err) - continue - } - - createTemplate := domain.CreateEnetpulseTournamentTemplate{ - TemplateID: tmpl.ID, - Name: tmpl.Name, - SportFK: sportFK, // use DB sport ID internally - Gender: tmpl.Gender, - UpdatesCount: updatesCount, - LastUpdatedAt: lastUpdatedAt, - Status: 1, // default active - } - - if _, err := s.store.CreateEnetpulseTournamentTemplate(ctx, createTemplate); err != nil { - fmt.Printf("failed to store tournament template %s: %v\n", tmpl.ID, err) - continue - } - } - break - } - } - - // fmt.Println("✅ Successfully fetched and stored all tournament templates") - return nil -} - -func (s *Service) GetAllTournamentTemplates(ctx context.Context) ([]domain.EnetpulseTournamentTemplate, error) { - // 1️⃣ Fetch all tournament templates from the store (database) - templates, err := s.store.GetAllEnetpulseTournamentTemplates(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch tournament templates from DB: %w", err) - } - - // 2️⃣ Optionally, you can log the count or perform other transformations - // s.logger.Info("Fetched tournament templates from DB", zap.Int("count", len(templates))) - - return templates, nil -} - -func (s *Service) FetchAndStoreTournaments(ctx context.Context) error { - // 1️⃣ Fetch all tournament templates from the database - templates, err := s.store.GetAllEnetpulseTournamentTemplates(ctx) - if err != nil { - return fmt.Errorf("failed to fetch tournament templates from DB: %w", err) - } - - for _, tmpl := range templates { - // 2️⃣ Compose URL for each tournament template - url := fmt.Sprintf( - "http://eapi.enetpulse.com/tournament/list/?tournament_templateFK=%s&username=%s&token=%s", - tmpl.TemplateID, - s.cfg.EnetPulseConfig.UserName, - s.cfg.EnetPulseConfig.Token, - ) - - // 3️⃣ Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return fmt.Errorf("creating tournament request for template %s: %w", tmpl.TemplateID, err) - } - - // 4️⃣ Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - return fmt.Errorf("requesting tournaments for template %s: %w", tmpl.TemplateID, err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to fetch tournaments for template %s (status %d): %s", - tmpl.TemplateID, resp.StatusCode, string(body)) - } - - // 5️⃣ Decode JSON response - var tournamentsResp struct { - Tournaments map[string]struct { - ID string `json:"id"` - Name string `json:"name"` - TournamentTemplateFK string `json:"tournament_templateFK"` - N string `json:"n"` // updates count - UT string `json:"ut"` // timestamp - } `json:"tournaments"` - } - if err := json.NewDecoder(resp.Body).Decode(&tournamentsResp); err != nil { - return fmt.Errorf("decoding tournaments for template %s: %w", tmpl.TemplateID, err) - } - - // 6️⃣ Iterate and store each tournament - for _, t := range tournamentsResp.Tournaments { - updatesCount := 0 - if t.N != "" { - if n, err := strconv.Atoi(t.N); err == nil { - updatesCount = n - } - } - - lastUpdatedAt, err := time.Parse(time.RFC3339, t.UT) - if err != nil { - lastUpdatedAt = time.Time{} - } - - createTournament := domain.CreateEnetpulseTournament{ - TournamentID: t.ID, - Name: t.Name, - TournamentTemplateFK: tmpl.TemplateID, // DB ID of template - UpdatesCount: updatesCount, - LastUpdatedAt: lastUpdatedAt, - Status: 1, // default active - } - - // Insert into DB - if _, err := s.store.CreateEnetpulseTournament(ctx, createTournament); err != nil { - // Log error but continue - // s.logger.Error("failed to store tournament", zap.String("tournament_id", t.ID), zap.Error(err)) - continue - } - } - } - - // s.logger.Info("Successfully fetched and stored all tournaments") - return nil -} - -func (s *Service) GetAllTournaments(ctx context.Context) ([]domain.EnetpulseTournament, error) { - // 1️⃣ Fetch all tournaments from the store (database) - tournaments, err := s.store.GetAllEnetpulseTournaments(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch tournaments from DB: %w", err) - } - - // 2️⃣ Optionally log count or do transformations - // s.logger.Info("Fetched tournaments from DB", zap.Int("count", len(tournaments))) - - return tournaments, nil -} - -func (s *Service) FetchAndStoreTournamentStages(ctx context.Context) error { - // 1️⃣ Get all tournaments from DB - tournaments, err := s.store.GetAllEnetpulseTournaments(ctx) - if err != nil { - return fmt.Errorf("failed to fetch tournaments from DB: %w", err) - } - - // 2️⃣ Loop through each tournament - for _, t := range tournaments { - // Compose URL for each tournament - url := fmt.Sprintf( - "https://eapi.enetpulse.com/tournament_stage/list/?language_typeFK=3&tz=Europe/Sofia&tournamentFK=%s&username=%s&token=%s", - t.TournamentID, - s.cfg.EnetPulseConfig.UserName, - s.cfg.EnetPulseConfig.Token, - ) - - // 3️⃣ Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - // log and skip - // s.logger.Error("creating tournament stages request", zap.String("tournament_id", t.TournamentID), zap.Error(err)) - continue - } - - // 4️⃣ Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - // s.logger.Error("requesting tournament stages", zap.String("tournament_id", t.TournamentID), zap.Error(err)) - continue - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - // s.logger.Error("unexpected status code fetching stages", zap.String("tournament_id", t.TournamentID), zap.Int("status", resp.StatusCode)) - _ = body - continue - } - - // 5️⃣ Decode JSON response (based on EnetPulse response format) - var stagesResp struct { - Stages map[string]struct { - ID string `json:"id"` - Name string `json:"name"` - TournamentFK string `json:"tournamentFK"` - N string `json:"n"` // updates count - UT string `json:"ut"` // timestamp - } `json:"tournament_stages"` - } - if err := json.NewDecoder(resp.Body).Decode(&stagesResp); err != nil { - // s.logger.Error("decoding tournament stages", zap.String("tournament_id", t.TournamentID), zap.Error(err)) - continue - } - - // 6️⃣ Iterate and store each stage - for _, st := range stagesResp.Stages { - updatesCount := 0 - if st.N != "" { - if n, err := strconv.Atoi(st.N); err == nil { - updatesCount = n - } - } - - lastUpdatedAt, err := time.Parse(time.RFC3339, st.UT) - if err != nil { - lastUpdatedAt = time.Time{} - } - - createStage := domain.CreateEnetpulseTournamentStage{ - StageID: st.ID, - Name: st.Name, - TournamentFK: t.TournamentID, // DB ID of the tournament - UpdatesCount: updatesCount, - LastUpdatedAt: lastUpdatedAt, - Status: 1, - } - - // Insert into DB - if _, err := s.store.CreateEnetpulseTournamentStage(ctx, createStage); err != nil { - // s.logger.Error("failed to store tournament stage", zap.String("stage_id", st.ID), zap.Error(err)) - continue - } - } - } - - // s.logger.Info("Successfully fetched and stored all tournament stages for all tournaments") - return nil -} - -func (s *Service) GetAllTournamentStages(ctx context.Context) ([]domain.EnetpulseTournamentStage, error) { - // 1️⃣ Fetch all tournament stages from the store (database) - stages, err := s.store.GetAllEnetpulseTournamentStages(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch tournament stages from DB: %w", err) - } - - // 2️⃣ Optionally log count or perform transformations - // s.logger.Info("Fetched tournament stages from DB", zap.Int("count", len(stages))) - - return stages, nil -} - -func (s *Service) FetchAndStoreFixtures(ctx context.Context, date string) error { - // 1️⃣ Fetch all sports from DB - sports, err := s.store.GetAllEnetpulseSports(ctx) - if err != nil { - return fmt.Errorf("failed to fetch sports from DB: %w", err) - } - - // Define API fixture struct - type Fixture struct { - FixtureID string `json:"id"` - Name string `json:"name"` - SportFK string `json:"sportFK"` - TournamentFK string `json:"tournamentFK"` - TournamentTemplateFK string `json:"tournament_templateFK"` - TournamentStageFK string `json:"tournament_stageFK"` - TournamentStageName string `json:"tournament_stage_name"` - TournamentName string `json:"tournament_name"` - TournamentTemplateName string `json:"tournament_template_name"` - SportName string `json:"sport_name"` - Gender string `json:"gender"` - StartDate string `json:"startdate"` // ISO 8601 - StatusType string `json:"status_type"` - StatusDescFK string `json:"status_descFK"` - RoundTypeFK string `json:"round_typeFK"` - UpdatesCount string `json:"n"` // convert to int - LastUpdatedAt string `json:"ut"` // parse to time.Time - } - - // 2️⃣ Loop through each sport - for _, sport := range sports { - if sport.SportID != "1" { - continue - } - - url := fmt.Sprintf( - "https://eapi.enetpulse.com/event/fixtures/?username=%s&token=%s&sportFK=%s&language_typeFK=3&date=%s", - s.cfg.EnetPulseConfig.UserName, - s.cfg.EnetPulseConfig.Token, - sport.SportID, - date, - ) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - fmt.Printf("creating request for sport %s: %v\n", sport.SportID, err) - continue - } - - resp, err := s.httpClient.Do(req) - if err != nil { - fmt.Printf("requesting fixtures for sport %s: %v\n", sport.SportID, err) - continue - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - fmt.Printf("failed to fetch fixtures for sport %s (status %d): %s\n", - sport.SportID, resp.StatusCode, string(body)) - continue - } - - // 3️⃣ Decode API response - var fixturesResp struct { - Events map[string]Fixture `json:"events"` - } - if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil { - fmt.Printf("decoding fixtures for sport %s: %v\n", sport.SportID, err) - continue - } - - // 4️⃣ Iterate and upsert fixtures - for _, fx := range fixturesResp.Events { - // Parse StartDate and LastUpdatedAt - startDate, err := time.Parse(time.RFC3339, fx.StartDate) - if err != nil { - fmt.Printf("invalid startDate for fixture %s: %v\n", fx.FixtureID, err) - continue - } - lastUpdated, err := time.Parse(time.RFC3339, fx.LastUpdatedAt) - if err != nil { - fmt.Printf("invalid lastUpdatedAt for fixture %s: %v\n", fx.FixtureID, err) - continue - } - - // Convert UpdatesCount - updatesCount, err := strconv.Atoi(fx.UpdatesCount) - if err != nil { - fmt.Printf("invalid updatesCount for fixture %s: %v\n", fx.FixtureID, err) - updatesCount = 0 - } - - fixture := domain.CreateEnetpulseFixture{ - FixtureID: fx.FixtureID, - Name: fx.Name, - SportFK: fx.SportFK, - TournamentFK: fx.TournamentFK, - TournamentTemplateFK: fx.TournamentTemplateFK, - TournamentStageFK: fx.TournamentStageFK, - TournamentStageName: fx.TournamentStageName, - TournamentName: fx.TournamentName, - TournamentTemplateName: fx.TournamentTemplateName, - SportName: fx.SportName, - Gender: fx.Gender, - StartDate: startDate, - StatusType: fx.StatusType, - StatusDescFK: fx.StatusDescFK, - RoundTypeFK: fx.RoundTypeFK, - UpdatesCount: updatesCount, - LastUpdatedAt: lastUpdated, - } - - // 5️⃣ Save fixture using UPSERT repository method - if _, err := s.store.CreateEnetpulseFixture(ctx, fixture); err != nil { - fmt.Printf("failed upserting fixture %s: %v\n", fx.FixtureID, err) - continue - } - } - - fmt.Printf("✅ Successfully fetched and stored fixtures for sport %s\n", sport.SportID) - break - } - - return nil -} - -func (s *Service) GetAllFixtures(ctx context.Context) ([]domain.EnetpulseFixture, error) { - // 1️⃣ Fetch all from store - fixtures, err := s.store.GetAllEnetpulseFixtures(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch fixtures from DB: %w", err) - } - return fixtures, nil -} - -func (s *Service) FetchAndStoreResults(ctx context.Context) error { - sports, err := s.store.GetAllEnetpulseSports(ctx) - if err != nil { - return fmt.Errorf("failed to fetch sports from DB: %w", err) - } - - for _, sport := range sports { - if sport.SportID != "1" { - continue - } - - today := time.Now().Format("2006-01-02") - url := fmt.Sprintf( - "http://eapi.enetpulse.com/event/results/?sportFK=%s&date=%s&username=%s&token=%s", - sport.SportID, - today, - s.cfg.EnetPulseConfig.UserName, - s.cfg.EnetPulseConfig.Token, - ) - fmt.Println("Fetching results:", url) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return fmt.Errorf("creating results request for sport %s: %w", sport.SportID, err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return fmt.Errorf("requesting results for sport %s: %w", sport.SportID, err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to fetch results for sport %s (status %d): %s", - sport.SportID, resp.StatusCode, string(body)) - } - - var data struct { - Events []struct { - ID string `json:"id"` - Name string `json:"name"` - SportFK string `json:"sportFK"` - TournamentFK string `json:"tournamentFK"` - TournamentTemplateFK string `json:"tournament_templateFK"` - TournamentStageFK string `json:"tournament_stageFK"` - TournamentStageName string `json:"tournament_stage_name"` - TournamentName string `json:"tournament_name"` - TournamentTemplateName string `json:"tournament_template_name"` - SportName string `json:"sport_name"` - StartDate string `json:"startdate"` - StatusType string `json:"status_type"` - StatusDescFK string `json:"status_descFK"` - RoundTypeFK string `json:"round_typeFK"` - N string `json:"n"` - UT string `json:"ut"` - - Property map[string]struct { - ID string `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Value string `json:"value"` - N string `json:"n"` - UT string `json:"ut"` - } `json:"property"` - - EventParticipants map[string]struct { - ID string `json:"id"` - Number string `json:"number"` - ParticipantFK string `json:"participantFK"` - EventFK string `json:"eventFK"` - - Result map[string]struct { - ID string `json:"id"` - EventParticipantsFK string `json:"event_participantsFK"` - ResultTypeFK string `json:"result_typeFK"` - ResultCode string `json:"result_code"` - Value string `json:"value"` - N string `json:"n"` - UT string `json:"ut"` - } `json:"result"` - - Participant struct { - ID string `json:"id"` - Name string `json:"name"` - Gender string `json:"gender"` - Type string `json:"type"` - CountryFK string `json:"countryFK"` - CountryName string `json:"country_name"` - } `json:"participant"` - } `json:"event_participants"` - } `json:"events"` - } - - bodyBytes, _ := io.ReadAll(resp.Body) - if err := json.Unmarshal(bodyBytes, &data); err != nil { - return fmt.Errorf("decoding results failed: %w", err) - } - - for _, event := range data.Events { - // 1️⃣ Create result record - lastUpdatedAt, _ := time.Parse(time.RFC3339, event.UT) - startDate, _ := time.Parse(time.RFC3339, event.StartDate) - - createResult := domain.CreateEnetpulseResult{ - ResultID: event.ID, - Name: event.Name, - SportFK: event.SportFK, - TournamentFK: event.TournamentFK, - TournamentTemplateFK: event.TournamentTemplateFK, - TournamentStageName: event.TournamentStageName, - TournamentName: event.TournamentName, - TournamentTemplateName: event.TournamentTemplateName, - SportName: event.SportName, - StartDate: startDate, - StatusType: event.StatusType, - StatusDescFK: event.StatusDescFK, - RoundTypeFK: event.RoundTypeFK, - LastUpdatedAt: lastUpdatedAt, - } - - if _, err := s.store.CreateEnetpulseResult(ctx, createResult); err != nil { - fmt.Printf("❌ failed to store result %s: %v\n", event.ID, err) - continue - } - - // 2️⃣ Create referees (type == "ref:participant") - for _, prop := range event.Property { - if strings.HasPrefix(prop.Type, "ref:participant") { - refCreatedAt, _ := time.Parse(time.RFC3339, prop.UT) - ref := domain.CreateEnetpulseResultReferee{ - ResultFk: event.ID, - RefereeFk: prop.Value, - LastUpdatedAt: refCreatedAt, - } - if _, err := s.store.CreateEnetpulseResultReferee(ctx, ref); err != nil { - fmt.Printf("⚠️ failed to create referee %s: %v\n", prop.Name, err) - } - } - } - - // 3️⃣ Create participants + their results - for _, ep := range event.EventParticipants { - p := domain.CreateEnetpulseResultParticipant{ - ParticipantMapID: ep.ID, - ResultFk: ep.EventFK, - ParticipantFk: ep.ParticipantFK, - Name: ep.Participant.Name, - CountryFk: ep.Participant.CountryFK, - CountryName: ep.Participant.CountryName, - } - if _, err := s.store.CreateEnetpulseResultParticipant(ctx, p); err != nil { - fmt.Printf("⚠️ failed to create participant %s: %v\n", ep.Participant.Name, err) - continue - } - } - } - - break // stop after the first sport (football) - } - - fmt.Println("✅ Successfully fetched and stored EnetPulse results + participants + referees") - return nil -} - -func (s *Service) GetAllResults(ctx context.Context) ([]domain.EnetpulseResult, error) { - results, err := s.store.GetAllEnetpulseResults(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch results from DB: %w", err) - } - - fmt.Printf("✅ Retrieved %d results from DB\n", len(results)) - return results, nil -} - -// FetchAndStoreOutcomeTypes fetches outcome types from EnetPulse API and stores them in the DB. -func (s *Service) FetchAndStoreOutcomeTypes(ctx context.Context) error { - // 1️⃣ Compose EnetPulse API URL - url := fmt.Sprintf( - "http://eapi.enetpulse.com/static/outcome_type/?language_typeFK=3&username=%s&token=%s", - s.cfg.EnetPulseConfig.UserName, - s.cfg.EnetPulseConfig.Token, - ) - - // 2️⃣ Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return fmt.Errorf("failed to create outcome types request: %w", err) - } - - // 3️⃣ Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - return fmt.Errorf("failed to call EnetPulse outcome_type API: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("unexpected status %d fetching outcome types: %s", resp.StatusCode, string(body)) - } - - // 4️⃣ Decode JSON response - var outcomeResp struct { - OutcomeTypes map[string]struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - N string `json:"n"` - UT string `json:"ut"` - } `json:"outcome_type"` - } - - if err := json.NewDecoder(resp.Body).Decode(&outcomeResp); err != nil { - return fmt.Errorf("failed to decode outcome types JSON: %w", err) - } - - // 5️⃣ Iterate and store each outcome type - for _, ot := range outcomeResp.OutcomeTypes { - updatesCount := 0 - if ot.N != "" { - if n, err := strconv.Atoi(ot.N); err == nil { - updatesCount = n - } - } - - lastUpdatedAt, err := time.Parse(time.RFC3339, ot.UT) - if err != nil { - lastUpdatedAt = time.Time{} - } - - createOutcome := domain.CreateEnetpulseOutcomeType{ - OutcomeTypeID: ot.ID, - Name: ot.Name, - Description: ot.Description, - UpdatesCount: int32(updatesCount), - LastUpdatedAt: lastUpdatedAt, - } - - // 6️⃣ Save to DB (upsert) - if _, err := s.store.CreateEnetpulseOutcomeType(ctx, createOutcome); err != nil { - // Optionally log the failure, continue to next - continue - } - } - - // s.logger.Info("✅ Successfully fetched and stored all EnetPulse outcome types") - return nil -} - -// GetAllOutcomeTypes retrieves all stored outcome types from the DB. -func (s *Service) GetAllOutcomeTypes(ctx context.Context) ([]domain.EnetpulseOutcomeType, error) { - outcomes, err := s.store.GetAllEnetpulseOutcomeTypes(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch outcome types from DB: %w", err) - } - - // s.logger.Info("✅ Fetched outcome types from DB", zap.Int("count", len(outcomes))) - return outcomes, nil -} - -func (s *Service) FetchAndStorePreodds(ctx context.Context) error { - // 1️⃣ Fetch all fixtures - fixtures, err := s.store.GetAllEnetpulseFixtures(ctx) - if err != nil { - return fmt.Errorf("failed to fetch fixtures: %w", err) - } - - // 2️⃣ Fetch all outcome types - outcomeTypes, err := s.store.GetAllEnetpulseOutcomeTypes(ctx) - if err != nil { - return fmt.Errorf("failed to fetch outcome types: %w", err) - } - - // 3️⃣ Loop through each fixture - for _, fixture := range fixtures { - // 4️⃣ Loop through each outcome type - for _, outcome := range outcomeTypes { - - url := fmt.Sprintf( - "http://eapi.enetpulse.com/preodds/event/?objectFK=%s&odds_providerFK=%s&outcome_typeFK=%s&username=%s&token=%s", - fixture.FixtureID, - s.cfg.EnetPulseConfig.ProviderID, - outcome.OutcomeTypeID, - s.cfg.EnetPulseConfig.UserName, - s.cfg.EnetPulseConfig.Token, - ) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - continue - } - - resp, err := s.httpClient.Do(req) - if err != nil { - continue - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - continue - } - - // Struct adjusted exactly to match JSON structure - var preoddsResp struct { - Preodds map[string]struct { - ID string `json:"id"` - OutcomeTypeFK string `json:"outcome_typeFK"` - OutcomeScopeFK string `json:"outcome_scopeFK"` - OutcomeSubtypeFK string `json:"outcome_subtypeFK"` - EventParticipantNumber string `json:"event_participant_number"` - Iparam string `json:"iparam"` - Iparam2 string `json:"iparam2"` - Dparam string `json:"dparam"` - Dparam2 string `json:"dparam2"` - Sparam string `json:"sparam"` - N string `json:"n"` - UT string `json:"ut"` - - PreoddsBettingOffers map[string]struct { - ID string `json:"id"` - BettingOfferStatusFK string `json:"bettingoffer_statusFK"` - OddsProviderFK string `json:"odds_providerFK"` - Odds string `json:"odds"` - OddsOld string `json:"odds_old"` - Active string `json:"active"` - CouponKey string `json:"couponKey"` - N string `json:"n"` - UT string `json:"ut"` - } `json:"preodds_bettingoffers"` - } `json:"preodds"` - } - - if err := json.NewDecoder(resp.Body).Decode(&preoddsResp); err != nil { - continue - } - - for _, p := range preoddsResp.Preodds { - // Convert numeric/string fields safely - updatesCount, _ := strconv.Atoi(defaultIfEmpty(p.N, "0")) - eventParticipantNumber, _ := strconv.Atoi(defaultIfEmpty(p.EventParticipantNumber, "0")) - lastUpdatedAt := parseTimeOrNow(p.UT) - - createPreodds := domain.CreateEnetpulsePreodds{ - PreoddsID: p.ID, - EventFK: fixture.FixtureID, - OutcomeTypeFK: p.OutcomeTypeFK, - OutcomeScopeFK: p.OutcomeScopeFK, - OutcomeSubtypeFK: p.OutcomeSubtypeFK, - EventParticipantNumber: eventParticipantNumber, - IParam: p.Iparam, - IParam2: p.Iparam2, - DParam: p.Dparam, - DParam2: p.Dparam2, - SParam: p.Sparam, - UpdatesCount: updatesCount, - LastUpdatedAt: lastUpdatedAt, - } - - // Store preodds in DB - _, err := s.store.CreateEnetpulsePreodds(ctx, createPreodds) - if err != nil { - continue - } - - // 5️⃣ Loop through betting offers map - for _, o := range p.PreoddsBettingOffers { - bettingUpdates, _ := strconv.Atoi(defaultIfEmpty(o.N, "0")) - bettingLastUpdatedAt := parseTimeOrNow(o.UT) - - odds, _ := strconv.ParseFloat(defaultIfEmpty(o.Odds, "0"), 64) - oddsOld, _ := strconv.ParseFloat(defaultIfEmpty(o.OddsOld, "0"), 64) - bettingOfferStatusFK, _ := strconv.Atoi(defaultIfEmpty(o.BettingOfferStatusFK, "0")) - oddsProviderFK, _ := strconv.Atoi(defaultIfEmpty(o.OddsProviderFK, "0")) - - createOffer := domain.CreateEnetpulsePreoddsBettingOffer{ - BettingOfferID: o.ID, - PreoddsFK: createPreodds.PreoddsID, - BettingOfferStatusFK: int32(bettingOfferStatusFK), - OddsProviderFK: int32(oddsProviderFK), - Odds: odds, - OddsOld: oddsOld, - Active: o.Active, - CouponKey: o.CouponKey, - UpdatesCount: bettingUpdates, - LastUpdatedAt: bettingLastUpdatedAt, - } - - _, _ = s.store.CreateEnetpulsePreoddsBettingOffer(ctx, createOffer) - } - } - } - } - - return nil -} - -// Utility helpers -func defaultIfEmpty(val, def string) string { - if val == "" { - return def - } - return val -} - -func parseTimeOrNow(t string) time.Time { - parsed, err := time.Parse(time.RFC3339, t) - if err != nil { - return time.Now().UTC() - } - return parsed -} - - -// helper function to parse string to int32 safely -func ParseStringToInt32(s string) int32 { - if s == "" { - return 0 - } - i, _ := strconv.Atoi(s) - return int32(i) -} - -func (s *Service) GetAllPreodds(ctx context.Context) ([]domain.EnetpulsePreodds, error) { - preodds, err := s.store.GetAllEnetpulsePreodds(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch preodds from DB: %w", err) - } - fmt.Printf("\n\nFetched Preodds are:%v\n\n", preodds) - return preodds, nil -} - -// FetchAndStoreBettingOffers fetches betting offers from EnetPulse API and stores them in the DB. -func (s *Service) StoreBettingOffers(ctx context.Context, preoddsID string, oddsProviderIDs []int32) error { - // 1️⃣ Compose API URL - providers := make([]string, len(oddsProviderIDs)) - for i, p := range oddsProviderIDs { - providers[i] = strconv.Itoa(int(p)) - } - url := fmt.Sprintf( - "http://eapi.enetpulse.com/preodds_bettingoffer/?preoddsFK=%s&odds_providerFK=%s&username=%s&token=%s", - preoddsID, - strings.Join(providers, ","), - s.cfg.EnetPulseConfig.UserName, - s.cfg.EnetPulseConfig.Token, - ) - - // 2️⃣ Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return fmt.Errorf("failed to create betting offer request: %w", err) - } - - // 3️⃣ Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - return fmt.Errorf("failed to call EnetPulse betting offer API: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("unexpected status %d fetching betting offers: %s", resp.StatusCode, string(body)) - } - - // 4️⃣ Decode JSON response - var offerResp struct { - BettingOffers map[string]struct { - ID string `json:"id"` - PreoddsFK string `json:"preodds_fk"` - BettingOfferStatusFK int32 `json:"bettingoffer_status_fk"` - OddsProviderFK int32 `json:"odds_provider_fk"` - Odds float64 `json:"odds"` - OddsOld float64 `json:"odds_old"` - Active string `json:"active"` - CouponKey string `json:"coupon_key"` - N string `json:"n"` - UT string `json:"ut"` - } `json:"bettingoffer"` - } - - if err := json.NewDecoder(resp.Body).Decode(&offerResp); err != nil { - return fmt.Errorf("failed to decode betting offers JSON: %w", err) - } - - // 5️⃣ Iterate and store each betting offer - for _, o := range offerResp.BettingOffers { - updatesCount := 0 - if o.N != "" { - if n, err := strconv.Atoi(o.N); err == nil { - updatesCount = n - } - } - - lastUpdatedAt, err := time.Parse(time.RFC3339, o.UT) - if err != nil { - lastUpdatedAt = time.Time{} - } - - createOffer := domain.CreateEnetpulsePreoddsBettingOffer{ - BettingOfferID: o.ID, - PreoddsFK: preoddsID, - BettingOfferStatusFK: o.BettingOfferStatusFK, - OddsProviderFK: o.OddsProviderFK, - Odds: o.Odds, - OddsOld: o.OddsOld, - Active: o.Active, - CouponKey: o.CouponKey, - UpdatesCount: int(updatesCount), - LastUpdatedAt: lastUpdatedAt, - } - - // 6️⃣ Save to DB - if _, err := s.store.CreateEnetpulsePreoddsBettingOffer(ctx, createOffer); err != nil { - // optionally log the failure and continue - continue - } - } - - return nil -} - -// GetAllBettingOffers retrieves all stored betting offers from the DB. -func (s *Service) GetAllBettingOffers(ctx context.Context) ([]domain.EnetpulsePreoddsBettingOffer, error) { - offers, err := s.store.GetAllEnetpulsePreoddsBettingOffers(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch betting offers from DB: %w", err) - } - return offers, nil -} - -func (s *Service) GetAllPreoddsWithBettingOffers(ctx context.Context) ([]domain.EnetpulsePreodds, error) { - preodds, err := s.store.GetAllEnetpulsePreoddsWithBettingOffers(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch preodds with betting offers from DB: %w", err) - } - - return preodds, nil -} - -func (s *Service) GetFixturesWithPreodds(ctx context.Context) ([]domain.EnetpulseFixtureWithPreodds, error) { - // 1️⃣ Fetch fixtures and their associated preodds from the repository - fixtures, err := s.store.GetFixturesWithPreodds(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch fixtures with preodds from DB: %w", err) - } - - return fixtures, nil -} - -func (s *Service) FetchTournamentTemplates(ctx context.Context) (*domain.TournamentTemplatesResponse, error) { - url := fmt.Sprintf( - "http://eapi.enetpulse.com/tournamenttemplate/list/?username=%s&token=%s", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, - ) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating tournament template request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting tournament templates: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("failed to fetch tournament templates: %s, body: %s", resp.Status, string(body)) - } - - var templatesResp domain.TournamentTemplatesResponse - if err := json.NewDecoder(resp.Body).Decode(&templatesResp); err != nil { - return nil, fmt.Errorf("decoding tournament templates response: %w", err) - } - - // Optionally save to DB or cache - return &templatesResp, nil -} - -func (s *Service) FetchTournamentTemplateParticipants(ctx context.Context, templateID string, opts domain.ParticipantsOptions) (*domain.TournamentTemplateParticipantsResponse, error) { - // Build URL with optional parameters - url := fmt.Sprintf( - "http://eapi.enetpulse.com/tournamenttemplate/participants/?username=%s&token=%s&id=%s", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, templateID, - ) - - // Append optional params if set - if opts.IncludeProperties { - url += "&includeProperties=yes" - } - if opts.IncludeParticipantProperties { - url += "&includeParticipantProperties=yes" - } - if opts.IncludeParticipantSports { - url += "&includeParticipantSports=yes" - } - if opts.IncludeCountries { - url += "&includeCountries=yes" - } - if opts.IncludeCountryCodes { - url += "&includeCountryCodes=yes" - } - if opts.ParticipantType != "" { - url += "&participantType=" + opts.ParticipantType - } - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating tournament participants request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting tournament participants: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("failed to fetch tournament participants: %s, body: %s", resp.Status, string(body)) - } - - var participantsResp domain.TournamentTemplateParticipantsResponse - if err := json.NewDecoder(resp.Body).Decode(&participantsResp); err != nil { - return nil, fmt.Errorf("decoding tournament participants response: %w", err) - } - - // Optionally save to DB or cache - return &participantsResp, nil -} - -func (s *Service) FetchTournaments(ctx context.Context, templateID string) error { - url := fmt.Sprintf( - "http://eapi.enetpulse.com/tournament/list/?tournament_templateFK=%s&username=%s&token=%s", - templateID, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, - ) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return fmt.Errorf("creating tournament request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return fmt.Errorf("requesting tournaments: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to fetch tournaments (status %d): %s", - resp.StatusCode, string(body)) - } - - var tournamentsResp domain.TournamentsResponse - if err := json.NewDecoder(resp.Body).Decode(&tournamentsResp); err != nil { - return fmt.Errorf("decoding tournaments response: %w", err) - } - - // Example: save tournaments to store or log them - for _, tournament := range tournamentsResp.Tournaments { - fmt.Printf("Tournament ID=%s Name=%s TemplateFK=%s UpdatedAt=%s\n", - tournament.ID, tournament.Name, tournament.TournamentTemplateFK, tournament.UT) - // e.g. s.store.SaveTournament(ctx, tournament) - } - - return nil -} - -func (s *Service) FetchTournamentParticipants(ctx context.Context, tournamentID string) error { - url := fmt.Sprintf( - "http://eapi.enetpulse.com/tournament_stage/participants/?id=%s&participantType=team&username=%s&token=%s", - tournamentID, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, - ) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return fmt.Errorf("creating tournament participants request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return fmt.Errorf("requesting tournament participants: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to fetch tournament participants (status %d): %s", - resp.StatusCode, string(body)) - } - - var participantsResp domain.TournamentParticipantsResponse - if err := json.NewDecoder(resp.Body).Decode(&participantsResp); err != nil { - return fmt.Errorf("decoding tournament participants response: %w", err) - } - - // Example: loop participants - for tid, t := range participantsResp.Tournaments { - fmt.Printf("Tournament ID=%s Name=%s has %d participants\n", - tid, t.Name, len(t.Participants)) - // You can loop deeper into t.Participants and store - } - - return nil -} - -func (s *Service) FetchTournamentStages(ctx context.Context, tournamentFK string) (*domain.TournamentStagesResponse, error) { - url := fmt.Sprintf( - "http://eapi.enetpulse.com/tournament_stage/list/?language_typeFK=3&tz=Europe/Sofia&tournamentFK=%s&username=%s&token=%s", - tournamentFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, - ) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating tournament stages request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting tournament stages: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body)) - } - - var stagesResp domain.TournamentStagesResponse - if err := json.NewDecoder(resp.Body).Decode(&stagesResp); err != nil { - return nil, fmt.Errorf("decoding tournament stages response: %w", err) - } - - // optionally save to DB or cache here - return &stagesResp, nil -} - -func (s *Service) FetchTournamentStageParticipants( - ctx context.Context, - stageID string, - includeProps, includeParticipantProps, includeCountries, includeCountryCodes bool, -) (*domain.TournamentStageParticipantsResponse, error) { - url := fmt.Sprintf( - "http://eapi.enetpulse.com/tournament_stage/participants/?id=%s&includeProperties=%s&includeParticipantProperties=%s&includeCountries=%s&includeCountryCodes=%s&username=%s&token=%s", - stageID, - boolToYesNo(includeProps), - boolToYesNo(includeParticipantProps), - boolToYesNo(includeCountries), - boolToYesNo(includeCountryCodes), - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, - ) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating participants request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting participants: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body)) - } - - var participantsResp domain.TournamentStageParticipantsResponse - if err := json.NewDecoder(resp.Body).Decode(&participantsResp); err != nil { - return nil, fmt.Errorf("decoding participants response: %w", err) - } - - // optionally save to DB or cache here - return &participantsResp, nil -} - -// helper function to convert bool to yes/no -func boolToYesNo(v bool) string { - if v { - return "yes" - } - return "no" -} - -func (s *Service) FetchDailyEvents(ctx context.Context, req domain.DailyEventsRequest) (*domain.DailyEventsResponse, error) { - baseURL := "http://eapi.enetpulse.com/event/daily/" - query := fmt.Sprintf("?username=%s&token=%s&language_typeFK=3", s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Required at least one of sportFK / tournament_templateFK / tournament_stageFK - if req.SportFK != 0 { - query += fmt.Sprintf("&sportFK=%d", req.SportFK) - } - if req.TournamentTemplateFK != 0 { - query += fmt.Sprintf("&tournament_templateFK=%d", req.TournamentTemplateFK) - } - // if req.TournamentStageFK != 0 { - // query += fmt.Sprintf("&tournament_stageFK=%d", req.TournamentStageFK) - // } - - // Optionals - if req.Date != "" { - query += fmt.Sprintf("&date=%s", req.Date) - } - if req.Live != "" { - query += fmt.Sprintf("&live=%s", req.Live) - } - if req.IncludeVenue != "" { - query += fmt.Sprintf("&includeVenue=%s", req.IncludeVenue) - } - if req.StatusType != "" { - query += fmt.Sprintf("&status_type=%s", req.StatusType) - } - if req.IncludeEventProperties != "" { - query += fmt.Sprintf("&includeEventProperties=%s", req.IncludeEventProperties) - } - if req.IncludeCountryCodes != "" { - query += fmt.Sprintf("&includeCountryCodes=%s", req.IncludeCountryCodes) - } - if req.IncludeFirstLastName != "" { - query += fmt.Sprintf("&includeFirstLastName=%s", req.IncludeFirstLastName) - } - if req.IncludeDeleted != "" { - query += fmt.Sprintf("&includeDeleted=%s", req.IncludeDeleted) - } - - fullURL := baseURL + query - - httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil) - if err != nil { - return nil, err - } - - resp, err := s.httpClient.Do(httpReq) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("failed to fetch daily events: status %d body: %s", resp.StatusCode, string(body)) - } - - var dailyResp domain.DailyEventsResponse - if err := json.NewDecoder(resp.Body).Decode(&dailyResp); err != nil { - return nil, err - } - - return &dailyResp, nil -} - -func (s *Service) FetchResults(ctx context.Context, params domain.ResultsRequest) (*domain.ResultsResponse, error) { - // Build base URL - url := fmt.Sprintf("http://eapi.enetpulse.com/event/results/?username=%s&token=%s", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Required filters (at least one of them) - if params.SportFK != 0 { - url += fmt.Sprintf("&sportFK=%d", params.SportFK) - } - if params.TournamentTemplateFK != 0 { - url += fmt.Sprintf("&tournament_templateFK=%d", params.TournamentTemplateFK) - } - // if params.TournamentStageFK != 0 { - // url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK) - // } - - // Optional filters - if params.LanguageTypeFK != 0 { - url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK) - } else { - url += "&language_typeFK=3" // default English - } - if params.Date != "" { - url += fmt.Sprintf("&date=%s", params.Date) - } - if params.Live != "" { - url += fmt.Sprintf("&live=%s", params.Live) - } - if params.IncludeVenue { - url += "&includeVenue=yes" - } - if !params.IncludeEventProperties { - url += "&includeEventProperties=no" - } - if params.IncludeCountryCodes { - url += "&includeCountryCodes=yes" - } - if params.IncludeFirstLastName { - url += "&includeFirstLastName=yes" - } - if params.Limit > 0 { - url += fmt.Sprintf("&limit=%d", params.Limit) - } - if params.Offset > 0 { - url += fmt.Sprintf("&offset=%d", params.Offset) - } - - // Make request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating results request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting results: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Decode response - var resultsResp domain.ResultsResponse - if err := json.NewDecoder(resp.Body).Decode(&resultsResp); err != nil { - return nil, fmt.Errorf("decoding results response: %w", err) - } - - return &resultsResp, nil -} - -func (s *Service) FetchEventDetails(ctx context.Context, params domain.EventDetailsRequest) (*domain.EventDetailsResponse, error) { - // Base URL - url := fmt.Sprintf("http://eapi.enetpulse.com/event/details/?username=%s&token=%s&id=%d", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, params.ID) - - // Optional flags - if params.IncludeLineups { - url += "&includeLineups=yes" - } - if params.IncludeIncidents { - url += "&includeIncidents=yes" - } - if !params.IncludeExtendedResults { - url += "&includeExtendedResults=no" - } - if params.IncludeProperties { - url += "&includeProperties=yes" - } - if params.IncludeLivestats { - url += "&includeLivestats=yes" - } - if params.IncludeVenue { - url += "&includeVenue=yes" - } - if params.IncludeCountryCodes { - url += "&includeCountryCodes=yes" - } - if params.IncludeFirstLastName { - url += "&includeFirstLastName=yes" - } - if params.IncludeDeleted { - url += "&includeDeleted=yes" - } - if params.IncludeReference { - url += "&includeReference=yes" - } - if params.IncludeObjectParticipants { - url += "&includeObjectParticipants=yes" - } - if params.IncludeEventIncidentRelation { - url += "&includeEventIncidentRelation=yes" - } - if params.IncludeTeamProperties { - url += "&includeTeamProperties=yes" - } - if params.IncludeObjectRound { - url += "&includeObjectRound=yes" - } - - // Make request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating event details request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting event details: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Decode response - var detailsResp domain.EventDetailsResponse - if err := json.NewDecoder(resp.Body).Decode(&detailsResp); err != nil { - return nil, fmt.Errorf("decoding event details response: %w", err) - } - - return &detailsResp, nil -} - -func (s *Service) FetchEventList(ctx context.Context, params domain.EventListRequest) (*domain.EventListResponse, error) { - // You must provide either TournamentFK or TournamentStageFK - if params.TournamentFK == 0 { - return nil, fmt.Errorf("either TournamentFK or TournamentStageFK is required") - } - - // Base URL - url := fmt.Sprintf("http://eapi.enetpulse.com/event/list/?username=%s&token=%s", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Mandatory one of - if params.TournamentFK != 0 { - url += fmt.Sprintf("&tournamentFK=%d", params.TournamentFK) - } - // if params.TournamentStageFK != 0 { - // url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK) - // } - - // Optional parameters - if !params.IncludeEventProperties { - url += "&includeEventProperties=no" - } - if params.StatusType != "" { - url += "&status_type=" + params.StatusType - } - if params.IncludeVenue { - url += "&includeVenue=yes" - } - if params.IncludeDeleted { - url += "&includeDeleted=yes" - } - if params.Limit > 0 { - url += fmt.Sprintf("&limit=%d", params.Limit) - } - if params.Offset > 0 { - url += fmt.Sprintf("&offset=%d", params.Offset) - } - - // Make request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating event list request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting event list: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Decode response - var eventListResp domain.EventListResponse - if err := json.NewDecoder(resp.Body).Decode(&eventListResp); err != nil { - return nil, fmt.Errorf("decoding event list response: %w", err) - } - - return &eventListResp, nil -} - -func (s *Service) FetchParticipantFixtures(ctx context.Context, params domain.ParticipantFixturesRequest) (*domain.ParticipantFixturesResponse, error) { - // You must provide ParticipantFK - if params.ParticipantFK == 0 { - return nil, fmt.Errorf("ParticipantFK is required") - } - - // Base URL - url := fmt.Sprintf("http://eapi.enetpulse.com/event/participant_fixtures/?username=%s&token=%s", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Mandatory param - url += fmt.Sprintf("&participantFK=%d", params.ParticipantFK) - - // Optionals - if params.SportFK != 0 { - url += fmt.Sprintf("&sportFK=%d", params.SportFK) - } - if params.TournamentFK != 0 { - url += fmt.Sprintf("&tournamentFK=%d", params.TournamentFK) - } - if params.TournamentTemplateFK != 0 { - url += fmt.Sprintf("&tournament_templateFK=%d", params.TournamentTemplateFK) - } - // if params.TournamentStageFK != 0 { - // url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK) - // } - if params.Date != "" { - url += "&date=" + params.Date - } - if params.Live != "" { - url += "&live=" + params.Live - } - if params.Limit > 0 { - url += fmt.Sprintf("&limit=%d", params.Limit) - } - if params.Offset > 0 { - url += fmt.Sprintf("&offset=%d", params.Offset) - } - - if params.IncludeVenue { - url += "&includeVenue=yes" - } - if params.IncludeCountryCodes { - url += "&includeCountryCodes=yes" - } - if params.IncludeFirstLastName { - url += "&includeFirstLastName=yes" - } - if !params.IncludeEventProperties { - // default yes → only append when false - url += "&includeEventProperties=no" - } - if params.LanguageTypeFK != 0 { - url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK) - } - - // Make request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating participant fixtures request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting participant fixtures: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Decode response - var fixturesResp domain.ParticipantFixturesResponse - if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil { - return nil, fmt.Errorf("decoding participant fixtures response: %w", err) - } - - return &fixturesResp, nil -} - -func (s *Service) FetchParticipantResults(ctx context.Context, params domain.ParticipantResultsRequest) (*domain.ParticipantResultsResponse, error) { - // Required param - if params.ParticipantFK == 0 { - return nil, fmt.Errorf("participantFK is required") - } - - // Base URL - url := fmt.Sprintf("http://eapi.enetpulse.com/event/participant_results/?username=%s&token=%s&participantFK=%d", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, params.ParticipantFK) - - // Optional parameters - if params.Limit > 0 { - url += fmt.Sprintf("&limit=%d", params.Limit) - } - if params.Offset > 0 { - url += fmt.Sprintf("&offset=%d", params.Offset) - } - if params.IncludeDeleted { - url += "&includeDeleted=yes" - } - if params.IncludeVenue { - url += "&includeVenue=yes" - } - - // Make request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating participant_results request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting participant_results: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Decode response - var prResp domain.ParticipantResultsResponse - if err := json.NewDecoder(resp.Body).Decode(&prResp); err != nil { - return nil, fmt.Errorf("decoding participant_results response: %w", err) - } - - return &prResp, nil -} - -func (s *Service) FetchTeamLogo(ctx context.Context, teamFK int64) (*domain.TeamLogoResponse, error) { - if teamFK == 0 { - return nil, fmt.Errorf("teamFK is required") - } - - // Build URL - url := fmt.Sprintf("http://eapi.enetpulse.com/image/team_logo/?teamFK=%d&username=%s&token=%s", - teamFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Make request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating team logo request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting team logo: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Read image bytes - data, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("reading team logo body: %w", err) - } - - // Build response - return &domain.TeamLogoResponse{ - ContentType: resp.Header.Get("Content-Type"), - Data: data, - URL: url, // optional: you can also return the URL for reference - }, nil -} - -func (s *Service) FetchTeamShirts(ctx context.Context, teamFK int64) (*domain.TeamShirtsResponse, error) { - if teamFK == 0 { - return nil, fmt.Errorf("teamFK is required") - } - - // Build URL - url := fmt.Sprintf("http://eapi.enetpulse.com/image/team_shirt/?teamFK=%d&username=%s&token=%s", - teamFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Make request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating team shirts request: %w", err) - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting team shirts: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Read response bytes — because shirts may come back as JSON list of URLs *or* image bytes - data, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("reading team shirts body: %w", err) - } - - // Build response — since Enetpulse typically returns a list of image URLs, - // we’ll return raw bytes if only one image, or JSON if multiple images. - return &domain.TeamShirtsResponse{ - ContentType: resp.Header.Get("Content-Type"), - RawData: data, - URL: url, - }, nil -} - -func (s *Service) FetchCountryFlag(ctx context.Context, countryFK int64) (*domain.ImageResponse, error) { - if countryFK == 0 { - return nil, fmt.Errorf("countryFK is required") - } - - // Build URL - url := fmt.Sprintf("http://eapi.enetpulse.com/image/country_flag/?countryFK=%d&username=%s&token=%s", - countryFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating country flag request: %w", err) - } - - // Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting country flag: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Read image bytes - data, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("reading country flag body: %w", err) - } - - // Return response - return &domain.ImageResponse{ - ContentType: resp.Header.Get("Content-Type"), - RawData: data, - URL: url, - }, nil -} - -func (s *Service) FetchPreMatchOdds(ctx context.Context, params domain.PreMatchOddsRequest) (*domain.PreMatchOddsResponse, error) { - // Mandatory parameter check - if params.ObjectFK == 0 { - return nil, fmt.Errorf("objectFK (event ID) is required") - } - - // Base URL - url := fmt.Sprintf("http://eapi.enetpulse.com/preodds/event/?username=%s&token=%s", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Mandatory parameters - url += fmt.Sprintf("&objectFK=%d", params.ObjectFK) - if len(params.OddsProviderFK) > 0 { - url += "&odds_providerFK=" + joinIntSlice(params.OddsProviderFK) - } - if params.OutcomeTypeFK != 0 { - url += fmt.Sprintf("&outcome_typeFK=%d", params.OutcomeTypeFK) - } - if params.OutcomeScopeFK != 0 { - url += fmt.Sprintf("&outcome_scopeFK=%d", params.OutcomeScopeFK) - } - if params.OutcomeSubtypeFK != 0 { - url += fmt.Sprintf("&outcome_subtypeFK=%d", params.OutcomeSubtypeFK) - } - - // Optional parameters - if params.Limit > 0 { - url += fmt.Sprintf("&limit=%d", params.Limit) - } - if params.Offset > 0 { - url += fmt.Sprintf("&offset=%d", params.Offset) - } - if params.LanguageTypeFK != 0 { - url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK) - } - - // Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating pre-match odds request: %w", err) - } - - // Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting pre-match odds: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Decode response - var oddsResp domain.PreMatchOddsResponse - if err := json.NewDecoder(resp.Body).Decode(&oddsResp); err != nil { - return nil, fmt.Errorf("decoding pre-match odds response: %w", err) - } - - return &oddsResp, nil -} - -// Helper function to join int slice with comma -func joinIntSlice(slice []int64) string { - result := "" - for i, v := range slice { - if i > 0 { - result += "," - } - result += fmt.Sprintf("%d", v) - } - return result -} - -func (s *Service) FetchTournamentStageOdds(ctx context.Context, params domain.TournamentStageOddsRequest) (*domain.TournamentStageOddsResponse, error) { - // Mandatory parameter check - if params.ObjectFK == 0 { - return nil, fmt.Errorf("objectFK (tournament stage ID) is required") - } - if params.OutcomeTypeFK == 0 { - return nil, fmt.Errorf("outcomeTypeFK is required") - } - - // Base URL - url := fmt.Sprintf("http://eapi.enetpulse.com/preodds/tournament_stage/?username=%s&token=%s", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Mandatory parameters - url += fmt.Sprintf("&objectFK=%d", params.ObjectFK) - url += fmt.Sprintf("&outcome_typeFK=%d", params.OutcomeTypeFK) - if params.OddsProviderFK != 0 { - url += fmt.Sprintf("&odds_providerFK=%d", params.OddsProviderFK) - } - if params.OutcomeScopeFK != 0 { - url += fmt.Sprintf("&outcome_scopeFK=%d", params.OutcomeScopeFK) - } - if params.OutcomeSubtypeFK != 0 { - url += fmt.Sprintf("&outcome_subtypeFK=%d", params.OutcomeSubtypeFK) - } - - // Optional parameters - if params.Limit > 0 { - url += fmt.Sprintf("&limit=%d", params.Limit) - } - if params.Offset > 0 { - url += fmt.Sprintf("&offset=%d", params.Offset) - } - if params.LanguageTypeFK != 0 { - url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK) - } - - // Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating tournament stage odds request: %w", err) - } - - // Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting tournament stage odds: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Decode response - var oddsResp domain.TournamentStageOddsResponse - if err := json.NewDecoder(resp.Body).Decode(&oddsResp); err != nil { - return nil, fmt.Errorf("decoding tournament stage odds response: %w", err) - } - - return &oddsResp, nil -} - -func (s *Service) FetchTournamentOdds(ctx context.Context, params domain.TournamentOddsRequest) (*domain.TournamentOddsResponse, error) { - // Mandatory parameter check - if params.ObjectFK == 0 { - return nil, fmt.Errorf("objectFK (tournament ID) is required") - } - if params.OutcomeTypeFK == 0 { - return nil, fmt.Errorf("outcomeTypeFK is required") - } - - // Base URL - url := fmt.Sprintf("http://eapi.enetpulse.com/preodds/tournament/?username=%s&token=%s", - s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token) - - // Mandatory parameters - url += fmt.Sprintf("&objectFK=%d", params.ObjectFK) - url += fmt.Sprintf("&outcome_typeFK=%d", params.OutcomeTypeFK) - if params.OddsProviderFK != 0 { - url += fmt.Sprintf("&odds_providerFK=%d", params.OddsProviderFK) - } - if params.OutcomeScopeFK != 0 { - url += fmt.Sprintf("&outcome_scopeFK=%d", params.OutcomeScopeFK) - } - if params.OutcomeSubtypeFK != 0 { - url += fmt.Sprintf("&outcome_subtypeFK=%d", params.OutcomeSubtypeFK) - } - - // Optional parameters - if params.Limit > 0 { - url += fmt.Sprintf("&limit=%d", params.Limit) - } - if params.Offset > 0 { - url += fmt.Sprintf("&offset=%d", params.Offset) - } - if params.LanguageTypeFK != 0 { - url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK) - } - - // Create HTTP request - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating tournament odds request: %w", err) - } - - // Execute request - resp, err := s.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("requesting tournament odds: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) - } - - // Decode response - var oddsResp domain.TournamentOddsResponse - if err := json.NewDecoder(resp.Body).Decode(&oddsResp); err != nil { - return nil, fmt.Errorf("decoding tournament odds response: %w", err) - } - - return &oddsResp, nil -} diff --git a/internal/services/event/interface.go b/internal/services/event/interface.go deleted file mode 100644 index e84aa1e..0000000 --- a/internal/services/event/interface.go +++ /dev/null @@ -1,29 +0,0 @@ -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 deleted file mode 100644 index 5b72fe4..0000000 --- a/internal/services/event/service.go +++ /dev/null @@ -1,510 +0,0 @@ -package event - -import ( - "context" - "encoding/json" - "fmt" - "io" - "log" - "net/http" - "slices" - "strconv" - "sync" - "time" - - "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/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 - eventStore ports.EventStore - eventHistoryStore ports.EventHistoryStore - leagueSvc league.Service - settingSvc *settings.Service - mongoLogger *zap.Logger - cfg *config.Config -} - -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, - eventStore: eventStore, - eventHistoryStore: eventHistoryStore, - leagueSvc: leagueSvc, - settingSvc: settingSvc, - mongoLogger: mongoLogger, - cfg: cfg, - } -} - -// func (s *service) FetchLiveEvents(ctx context.Context) error { -// var wg sync.WaitGroup -// urls := []struct { -// name string -// source string -// }{ -// {"https://api.b365api.com/v1/bet365/inplay?sport_id=%d&token=%s", "bet365"}, -// {"https://api.b365api.com/v1/betfair/sb/inplay?sport_id=%d&token=%s", "betfair"}, -// {"https://api.b365api.com/v1/1xbet/inplay?sport_id=%d&token=%s", "1xbet"}, -// } - -// for _, url := range urls { -// wg.Add(1) - -// go func() { -// defer wg.Done() -// s.fetchLiveEvents(ctx, url.name, url.source) -// }() -// } -// wg.Wait() -// return nil -// } - -// func (s *service) fetchLiveEvents(ctx context.Context, url, source string) error { -// sportIDs := []int{1, 13, 78, 18, 91, 16, 17, 14, 12, 3, 2, 4, 83, 15, 92, 94, 8, 19, 36, 66, 9, 75, 90, 95, 110, 107, 151, 162, 148} - -// var wg sync.WaitGroup - -// for _, sportID := range sportIDs { -// wg.Add(1) -// go func(sportID int) { -// defer wg.Done() - -// url := fmt.Sprintf(url, sportID, s.token) -// resp, err := http.Get(url) -// if err != nil { -// fmt.Printf(" Failed request for sport_id=%d: %v\n", sportID, err) -// return -// } -// defer resp.Body.Close() - -// body, _ := io.ReadAll(resp.Body) - -// events := []domain.Event{} -// switch source { -// case "bet365": -// events = handleBet365prematch(body, sportID, source) -// case "betfair": -// events = handleBetfairprematch(body, sportID, source) -// case "1xbet": -// // betfair and 1xbet have the same result structure -// events = handleBetfairprematch(body, sportID, source) -// } - -// for _, event := range events { -// if err := s.eventStore.SaveEvent(ctx, event); err != nil { -// fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) -// } -// } -// }(sportID) -// } - -// wg.Wait() -// fmt.Println("All live events fetched and stored.") -// return nil - -// } - -// func handleBet365prematch(body []byte, sportID int, source string) []domain.Event { -// var data struct { -// Success int `json:"success"` -// Results [][]map[string]interface{} `json:"results"` -// } - -// events := []domain.Event{} -// if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { -// fmt.Printf("%s: Decode failed for sport_id=%d\nRaw: %s\n", source, sportID, string(body)) -// return events -// } - -// for _, group := range data.Results { -// for _, ev := range group { -// if getString(ev["type"]) != "EV" { -// continue -// } - -// event := domain.Event{ -// ID: getString(ev["ID"]), -// SportID: int32(sportID), -// MatchName: getString(ev["NA"]), -// Score: getString(ev["SS"]), -// MatchMinute: getInt(ev["TM"]), -// TimerStatus: getString(ev["TT"]), -// HomeTeamID: getInt32(ev["HT"]), -// AwayTeamID: getInt32(ev["AT"]), -// HomeKitImage: getString(ev["K1"]), -// AwayKitImage: getString(ev["K2"]), -// LeagueName: getString(ev["CT"]), -// LeagueID: getInt32(ev["C2"]), -// LeagueCC: getString(ev["CB"]), -// StartTime: time.Now().UTC().Format(time.RFC3339), -// IsLive: true, -// Status: "live", -// MatchPeriod: getInt(ev["MD"]), -// AddedTime: getInt(ev["TA"]), -// Source: source, -// } - -// events = append(events, event) -// } -// } - -// return events -// } - -// func handleBetfairprematch(body []byte, sportID int, source string) []domain.Event { -// var data struct { -// Success int `json:"success"` -// Results []map[string]interface{} `json:"results"` -// } - -// events := []domain.Event{} -// if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { -// fmt.Printf("%s: Decode failed for sport_id=%d\nRaw: %s\n", source, sportID, string(body)) -// return events -// } - -// for _, ev := range data.Results { -// homeRaw, _ := ev["home"].(map[string]interface{}) -// awayRaw, _ := ev["home"].(map[string]interface{}) - -// event := domain.Event{ -// ID: getString(ev["id"]), -// SportID: int32(sportID), -// TimerStatus: getString(ev["time_status"]), -// HomeTeamID: getInt32(homeRaw["id"]), -// AwayTeamID: getInt32(awayRaw["id"]), -// StartTime: time.Now().UTC().Format(time.RFC3339), -// IsLive: true, -// Status: "live", -// Source: source, -// } - -// events = append(events, event) -// } - -// return events -// } - -func (s *Service) FetchUpcomingEvents(ctx context.Context) error { - var wg sync.WaitGroup - urls := []struct { - name string - source domain.EventSource - }{ - {"https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s&page=%d", domain.EVENT_SOURCE_BET365}, - // {"https://api.b365api.com/v1/betfair/sb/upcoming?sport_id=%d&token=%s&page=%d", "betfair"}, - // {"https://api.b365api.com/v1/1xbet/upcoming?sport_id=%d&token=%s&page=%d", "1xbet"}, - } - - for _, url := range urls { - wg.Add(1) - - go func() { - defer wg.Done() - s.fetchUpcomingEventsFromProvider(ctx, url.name, url.source) - }() - } - - wg.Wait() - return nil -} - -func (s *Service) fetchUpcomingEventsFromProvider(ctx context.Context, source_url string, source domain.EventSource) { - - settingsList, err := s.settingSvc.GetGlobalSettingList(ctx) - - if err != nil { - s.mongoLogger.Error("Failed to fetch event data for page", zap.Error(err)) - return - } - - var pageLimit int - var sportIDs []int - - // Restricting the page to 1 on development, which drastically reduces the amount of events that is fetched - if s.cfg.Env == "development" { - pageLimit = 2 - sportIDs = []int{1} - } else { - pageLimit = 200 - sportIDs = []int{1, 18, 17, 3, 83, 15, 12, 19, 8, 16, 91} - } - - var skippedLeague []string - var totalEvents = 0 - nilAway := 0 - for sportIndex, sportID := range sportIDs { - var totalPages int = 1 - var page int = 0 - var pageCount int = 0 - var sportEvents = 0 - logger := s.mongoLogger.With( - zap.String("source", string(source)), - zap.Int("sport_id", sportID), - zap.String("sport_name", domain.Sport(sportID).String()), - zap.Int("count", pageCount), - zap.Int("Skipped leagues", len(skippedLeague)), - ) - for page <= totalPages { - page = page + 1 - url := fmt.Sprintf(source_url, sportID, s.token, page) - log.Printf("📡 Fetching data from %s - sport %d (%d/%d), for event data page (%d/%d)", - source, sportID, sportIndex+1, len(sportIDs), page, totalPages) - - eventLogger := logger.With( - zap.Int("page", page), - zap.Int("total_pages", totalPages), - ) - resp, err := http.Get(url) - if err != nil { - eventLogger.Error("Failed to fetch event data for page", zap.Error(err)) - continue - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - - if err != nil { - eventLogger.Error("Failed to read event response body", zap.Error(err)) - continue - - } - var data domain.B365UpcomingRes - - if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { - eventLogger.Error("Failed to parse event json data", zap.Error(err)) - continue - } - - for _, ev := range data.Results { - startUnix, err := strconv.ParseInt(ev.Time, 10, 64) - dataLogger := eventLogger.With( - zap.String("time", ev.Time), - zap.String("leagueID", ev.League.ID), - zap.String("leagueName", ev.League.Name), - ) - if err != nil { - dataLogger.Error("Invalid time", zap.Error(err)) - continue - } - leagueID, err := strconv.ParseInt(ev.League.ID, 10, 64) - if err != nil { - dataLogger.Error("Invalid league id", zap.Error(err)) - continue - } - - // doesn't make sense to save and check back to back, but for now it can be here - // 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.leagueSvc.SaveLeague(ctx, domain.CreateLeague{ - ID: leagueID, - Name: ev.League.Name, - DefaultIsActive: true, - DefaultIsFeatured: slices.Contains(domain.FeaturedLeagues, leagueID), - SportID: convertInt32(ev.SportID), - }) - - if err != nil { - dataLogger.Error("error while saving league", zap.Error(err)) - continue - } - - // Since the system is multi-vendor now, no events are going to be skipped - // if supported, err := s.eventStore.CheckLeagueSupport(ctx, leagueID); !supported || err != nil { - // dataLogger.Warn( - // "Skipping league", - // zap.Bool("is_supported", supported), - // zap.Error(err), - // ) - // skippedLeague = append(skippedLeague, ev.League.Name) - // continue - // } - - event := domain.CreateEvent{ - SourceEventID: ev.ID, - SportID: convertInt32(ev.SportID), - HomeTeam: ev.Home.Name, - AwayTeam: "", // handle nil safely - HomeTeamID: convertInt64(ev.Home.ID), - AwayTeamID: 0, - LeagueID: convertInt64(ev.League.ID), - LeagueName: ev.League.Name, - StartTime: time.Unix(startUnix, 0).UTC(), - Source: source, - IsLive: false, - Status: domain.STATUS_PENDING, - DefaultWinningUpperLimit: settingsList.DefaultWinningLimit, - } - - if ev.Away != nil { - event.AwayTeam = ev.Away.Name - event.AwayTeamID = convertInt64(ev.Away.ID) - event.MatchName = ev.Home.Name + " vs " + ev.Away.Name - } else { - nilAway += 1 - } - ok, _ := s.CheckAndInsertEventHistory(ctx, event) - - // if err != nil { - // dataLogger.Error("failed to check and insert event history", zap.Error(err)) - // } - - if ok { - dataLogger.Info("event history has been recorded") - } - - err = s.eventStore.SaveEvent(ctx, event) - if err != nil { - dataLogger.Error("failed to save upcoming event", zap.Error(err)) - } - sportEvents += 1 - - } - - // log.Printf("⚠️ Skipped leagues %v", len(skippedLeague)) - // log.Printf("⚠️ Total pages %v", data.Pager.Total/data.Pager.PerPage) - - totalPages = data.Pager.Total / data.Pager.PerPage - - if pageCount >= pageLimit { - break - } - if page > totalPages { - break - } - pageCount++ - } - - logger.Info("Completed adding sport", zap.Int("number_of_events_in_sport", sportEvents)) - totalEvents += sportEvents - } - - s.mongoLogger.Info( - "Successfully fetched upcoming events", - zap.String("source", string(source)), - zap.Int("totalEvents", totalEvents), - zap.Int("Skipped leagues", len(skippedLeague)), - zap.Int("Events with empty away data", nilAway), - ) -} - -func (s *Service) CheckAndInsertEventHistory(ctx context.Context, newEvent domain.CreateEvent) (bool, error) { - - eventLogger := s.mongoLogger.With( - zap.String("sourceEventID", newEvent.SourceEventID), - zap.String("source", string(newEvent.Source)), - zap.Int64("leagueID", newEvent.LeagueID), - zap.String("leagueName", newEvent.LeagueName), - zap.Int32("sport_id", newEvent.SportID), - ) - - oldEvent, err := s.eventStore.GetEventBySourceID(ctx, newEvent.SourceEventID, newEvent.Source) - - if err != nil { - return false, err - } - - if !oldEvent.IsMonitored { - return false, nil - } - - if oldEvent.Status != newEvent.Status { - _, err := s.eventHistoryStore.InsertEventHistory(ctx, domain.CreateEventHistory{ - EventID: oldEvent.ID, - Status: string(newEvent.Status), - }) - - if err != nil { - eventLogger.Error("failed to get event by id", zap.Error(err)) - return false, err - } - - return true, nil - } - - return false, nil -} - -func getString(v interface{}) string { - if str, ok := v.(string); ok { - return str - } - return "" -} - -func GetInt(v interface{}) int { - if f, ok := v.(float64); ok { - return int(f) - } - return 0 -} - -func GetInt32(v interface{}) int32 { - if n, err := strconv.Atoi(getString(v)); err == nil { - return int32(n) - } - return 0 -} - -func convertInt32(num string) int32 { - if n, err := strconv.Atoi(num); err == nil { - return int32(n) - } - return 0 -} -func convertInt64(num string) int64 { - if n, err := strconv.Atoi(num); err == nil { - return int64(n) - } - return 0 -} -func (s *Service) GetAllEvents(ctx context.Context, filter domain.EventFilter) ([]domain.BaseEvent, int64, error) { - return s.eventStore.GetAllEvents(ctx, filter) -} - -func (s *Service) GetEventByID(ctx context.Context, ID int64) (domain.BaseEvent, error) { - return s.eventStore.GetEventByID(ctx, ID) -} - -func (s *Service) UpdateFinalScore(ctx context.Context, eventID int64, fullScore string, status domain.EventStatus) error { - return s.eventStore.UpdateFinalScore(ctx, eventID, fullScore, status) -} -func (s *Service) UpdateEventStatus(ctx context.Context, eventID int64, status domain.EventStatus) error { - return s.eventStore.UpdateEventStatus(ctx, eventID, status) -} - -func (s *Service) IsEventMonitored(ctx context.Context, eventID int64) (bool, error) { - return s.eventStore.IsEventMonitored(ctx, eventID) -} -func (s *Service) UpdateEventMonitored(ctx context.Context, eventID int64, IsMonitored bool) error { - return s.eventStore.UpdateEventMonitored(ctx, eventID, IsMonitored) -} - -func (s *Service) GetSportAndLeagueIDs(ctx context.Context, eventID int64) ([]int64, error) { - 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 deleted file mode 100644 index 1044ef3..0000000 --- a/internal/services/event/settings.go +++ /dev/null @@ -1,22 +0,0 @@ -package event - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Service) GetEventsWithSettings(ctx context.Context, companyID int64, filter domain.EventFilter) ([]domain.EventWithSettings, int64, error) { - return s.eventStore.GetEventsWithSettings(ctx, companyID, filter) -} - -func (s *Service) GetEventWithSettingByID(ctx context.Context, ID int64, companyID int64) (domain.EventWithSettings, error) { - return s.eventStore.GetEventWithSettingByID(ctx, ID, companyID) -} - -func (s *Service) UpdateTenantEventSettings(ctx context.Context, event domain.UpdateTenantEventSettings) error { - return s.eventStore.UpdateTenantEventSettings(ctx, event) -} -func (s *Service) UpdateGlobalEventSettings(ctx context.Context, event domain.UpdateGlobalEventSettings) error { - return s.eventStore.UpdateGlobalEventSettings(ctx, event) -} diff --git a/internal/services/institutions/port.go b/internal/services/institutions/port.go deleted file mode 100644 index b73a84a..0000000 --- a/internal/services/institutions/port.go +++ /dev/null @@ -1 +0,0 @@ -package institutions diff --git a/internal/services/institutions/service.go b/internal/services/institutions/service.go deleted file mode 100644 index b27a4d4..0000000 --- a/internal/services/institutions/service.go +++ /dev/null @@ -1,66 +0,0 @@ -package institutions - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" -) - -type Service struct { - repo repository.BankRepository -} - -func New(repo repository.BankRepository) *Service { - return &Service{repo: repo} -} - -func (s *Service) Create(ctx context.Context, bank *domain.Bank) error { - return s.repo.CreateBank(ctx, bank) -} - -func (s *Service) Update(ctx context.Context, bank *domain.Bank) error { - return s.repo.UpdateBank(ctx, bank) -} - -func (s *Service) GetByID(ctx context.Context, id int64) (*domain.Bank, error) { - return s.repo.GetBankByID(ctx, int(id)) -} - -func (s *Service) Delete(ctx context.Context, id int64) error { - return s.repo.DeleteBank(ctx, int(id)) -} - -func (s *Service) List( - ctx context.Context, - countryID *int, - isActive *bool, - searchTerm *string, - page int, - pageSize int, -) ([]*domain.Bank, *domain.Pagination, error) { - banks, total, err := s.repo.GetAllBanks(ctx, countryID, isActive, searchTerm, page, pageSize) - if err != nil { - return nil, nil, err - } - - result := make([]*domain.Bank, len(banks)) - for i := range banks { - result[i] = &banks[i] - } - - // Calculate pagination metadata - totalPages := int(total) / pageSize - if int(total)%pageSize != 0 { - totalPages++ - } - - pagination := &domain.Pagination{ - Total: int(total), - TotalPages: totalPages, - CurrentPage: page, - Limit: pageSize, - } - - return result, pagination, nil -} diff --git a/internal/services/issue_reporting/service.go b/internal/services/issue_reporting/service.go deleted file mode 100644 index 83c79a8..0000000 --- a/internal/services/issue_reporting/service.go +++ /dev/null @@ -1,92 +0,0 @@ -package issuereporting - -import ( - "context" - "errors" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" -) - -type Service struct { - repo repository.ReportedIssueRepository -} - -func New(repo repository.ReportedIssueRepository) *Service { - return &Service{repo: repo} -} - -func (s *Service) CreateReportedIssue(ctx context.Context, issue domain.ReportedIssueReq, userID int64, role domain.Role) (domain.ReportedIssue, error) { - - // metadata, err := json.Marshal(issue.Metadata) - // if err != nil { - // return domain.ReportedIssue{}, err - // } - params := dbgen.CreateReportedIssueParams{ - UserID: userID, - UserRole: string(role), - Subject: issue.Subject, - Description: issue.Description, - IssueType: string(issue.IssueType), - // Metadata: metadata, - } - dbIssue, err := s.repo.CreateReportedIssue(ctx, params) - if err != nil { - return domain.ReportedIssue{}, err - } - // Map dbgen.ReportedIssue to domain.ReportedIssue - reportedIssue := domain.ReportedIssue{ - ID: dbIssue.ID, - Subject: dbIssue.Subject, - Description: dbIssue.Description, - UserID: dbIssue.UserID, - UserRole: domain.Role(dbIssue.UserRole), - Status: domain.ReportedIssueStatus(dbIssue.Status), - IssueType: domain.ReportedIssueType(dbIssue.IssueType), - CreatedAt: dbIssue.CreatedAt.Time, - UpdatedAt: dbIssue.UpdatedAt.Time, - - // Add other fields as necessary - } - return reportedIssue, nil -} - -func (s *Service) GetIssuesForUser(ctx context.Context, userID int64, limit, offset int) ([]domain.ReportedIssue, error) { - dbIssues, err := s.repo.ListReportedIssuesByUser(ctx, userID, int32(limit), int32(offset)) - if err != nil { - return nil, err - } - reportedIssues := make([]domain.ReportedIssue, len(dbIssues)) - for i, dbIssue := range dbIssues { - reportedIssues[i] = domain.ReportedIssue{ - ID: dbIssue.ID, - Subject: dbIssue.Subject, - Description: dbIssue.Description, - UserID: dbIssue.UserID, - UserRole: domain.Role(dbIssue.UserRole), - Status: domain.ReportedIssueStatus(dbIssue.Status), - IssueType: domain.ReportedIssueType(dbIssue.IssueType), - CreatedAt: dbIssue.CreatedAt.Time, - UpdatedAt: dbIssue.UpdatedAt.Time, - // Add other fields as necessary - } - } - return reportedIssues, nil -} - -func (s *Service) GetAllIssues(ctx context.Context, limit, offset int) ([]dbgen.ReportedIssue, error) { - return s.repo.ListReportedIssues(ctx, int32(limit), int32(offset)) -} - -func (s *Service) UpdateIssueStatus(ctx context.Context, issueID int64, status string) error { - validStatuses := map[string]bool{"pending": true, "in_progress": true, "resolved": true, "rejected": true} - if !validStatuses[status] { - return errors.New("invalid status") - } - return s.repo.UpdateReportedIssueStatus(ctx, issueID, status) -} - -func (s *Service) DeleteIssue(ctx context.Context, issueID int64) error { - return s.repo.DeleteReportedIssue(ctx, issueID) -} diff --git a/internal/services/kafka/consumer.go b/internal/services/kafka/consumer.go index fb55eea..e66ae28 100644 --- a/internal/services/kafka/consumer.go +++ b/internal/services/kafka/consumer.go @@ -5,8 +5,6 @@ package kafka // "encoding/json" // "log" -// "github.com/SamuelTariku/FortuneBet-Backend/internal/event" -// "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws" // "github.com/segmentio/kafka-go" // ) diff --git a/internal/services/league/interface.go b/internal/services/league/interface.go deleted file mode 100644 index ca4c4b8..0000000 --- a/internal/services/league/interface.go +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index 6c7f3cd..0000000 --- a/internal/services/league/service.go +++ /dev/null @@ -1,47 +0,0 @@ -package league - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" - -) - -type Service struct { - store ports.LeagueStore -} - -func New(store ports.LeagueStore) *Service { - return &Service{ - store: store, - } -} - -func (s *Service) SaveLeague(ctx context.Context, league domain.CreateLeague) error { - return s.store.SaveLeague(ctx, league) -} - -func (s *Service) SaveLeagueSettings(ctx context.Context, leagueSettings domain.CreateLeagueSettings) error { - return s.store.SaveLeagueSettings(ctx, leagueSettings) -} - -func (s *Service) GetAllLeagues(ctx context.Context, filter domain.LeagueFilter) ([]domain.BaseLeague, int64, error) { - return s.store.GetAllLeagues(ctx, filter) -} - -func (s *Service) GetAllLeaguesByCompany(ctx context.Context, companyID int64, filter domain.LeagueFilter) ([]domain.LeagueWithSettings, int64, error) { - return s.store.GetAllLeaguesByCompany(ctx, companyID, filter) -} - -func (s *Service) CheckLeagueSupport(ctx context.Context, leagueID int64, companyID int64) (bool, error) { - return s.store.CheckLeagueSupport(ctx, leagueID, companyID) -} - -func (s *Service) UpdateLeague(ctx context.Context, league domain.UpdateLeague) error { - return s.store.UpdateLeague(ctx, league) -} - -func (s *Service) UpdateGlobalLeagueSettings(ctx context.Context, league domain.UpdateGlobalLeagueSettings) error { - return s.store.UpdateGlobalLeagueSettings(ctx, league) -} diff --git a/internal/services/messenger/service.go b/internal/services/messenger/service.go index 27a48af..3b315ee 100644 --- a/internal/services/messenger/service.go +++ b/internal/services/messenger/service.go @@ -1,8 +1,8 @@ package messenger import ( - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" + "Yimaru-Backend/internal/config" + "Yimaru-Backend/internal/services/settings" ) type Service struct { diff --git a/internal/services/messenger/sms.go b/internal/services/messenger/sms.go index 77e1cd8..c01e54e 100644 --- a/internal/services/messenger/sms.go +++ b/internal/services/messenger/sms.go @@ -1,11 +1,11 @@ package messenger import ( + "Yimaru-Backend/internal/domain" "context" "errors" "fmt" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" afro "github.com/amanuelabay/afrosms-go" "github.com/twilio/twilio-go" twilioApi "github.com/twilio/twilio-go/rest/api/v2010" diff --git a/internal/services/notification/interface.go b/internal/services/notification/interface.go index 46fff84..33cadd0 100644 --- a/internal/services/notification/interface.go +++ b/internal/services/notification/interface.go @@ -3,7 +3,6 @@ package notificationservice // import ( // "context" -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" // "github.com/gorilla/websocket" // ) diff --git a/internal/services/notification/service.go b/internal/services/notification/service.go index 2a07f1d..9a889b9 100644 --- a/internal/services/notification/service.go +++ b/internal/services/notification/service.go @@ -1,6 +1,13 @@ package notificationservice import ( + "Yimaru-Backend/internal/config" + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/pkgs/helpers" + "Yimaru-Backend/internal/ports" + "Yimaru-Backend/internal/services/messenger" + "Yimaru-Backend/internal/services/user" + "Yimaru-Backend/internal/web_server/ws" "context" "fmt" @@ -8,19 +15,8 @@ import ( "log/slog" "sync" "time" - - "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/services/messenger" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" // "github.com/segmentio/kafka-go" "go.uber.org/zap" - - // "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws" // afro "github.com/amanuelabay/afrosms-go" "github.com/gorilla/websocket" // "github.com/redis/go-redis/v9" @@ -50,7 +46,7 @@ func New( hub := ws.NewNotificationHub() svc := &Service{ - store: store, + store: store, Hub: hub, mongoLogger: mongoLogger, logger: logger, @@ -338,13 +334,13 @@ func (s *Service) SendNotificationSMS(ctx context.Context, recipientID int64, me if user.PhoneNumber == "" { return fmt.Errorf("phone Number is invalid") } - err = s.messengerSvc.SendSMS(ctx, user.PhoneNumber, message, user.CompanyID) + err = s.messengerSvc.SendSMS(ctx, user.PhoneNumber, message, user.OrganizationID) if err != nil { s.mongoLogger.Error("[NotificationSvc.HandleNotification] Failed to send notification SMS", zap.Int64("recipient_id", recipientID), zap.String("user_phone_number", user.PhoneNumber), zap.String("message", message), - zap.Int64("company_id", user.CompanyID.Value), + zap.Int64("company_id", user.OrganizationID.Value), zap.Error(err), zap.Time("timestamp", time.Now()), ) @@ -375,7 +371,7 @@ func (s *Service) SendNotificationEmail(ctx context.Context, recipientID int64, zap.Int64("recipient_id", recipientID), zap.String("user_email", user.Email), zap.String("message", message), - zap.Int64("company_id", user.CompanyID.Value), + zap.Int64("company_id", user.OrganizationID.Value), zap.Error(err), zap.Time("timestamp", time.Now()), ) diff --git a/internal/services/odds/custom_odds.go b/internal/services/odds/custom_odds.go deleted file mode 100644 index f3c7d5d..0000000 --- a/internal/services/odds/custom_odds.go +++ /dev/null @@ -1,29 +0,0 @@ -package odds - -// import ( -// "context" - -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -// ) - -// func (s *ServiceImpl) InsertCustomOdds(ctx context.Context, odd domain.CreateCustomOdd) (domain.CustomOdd, error) { -// return s.store.InsertCustomOdds(ctx, odd) -// } -// func (s *ServiceImpl) GetAllCustomOdds(ctx context.Context, filter domain.CustomOddFilter) ([]domain.CustomOdd, error){ -// return s.store.GetAllCustomOdds(ctx, filter) -// } -// func (s *ServiceImpl) GetCustomOddByID(ctx context.Context, id int64) (domain.CustomOdd, error){ -// return s.store.GetCustomOddByID(ctx, id) -// } -// func (s *ServiceImpl) GetCustomOddByOddID(ctx context.Context, oddId int64, companyID int64) (domain.CustomOdd, error){ -// return s.store.GetCustomOddByOddID(ctx, oddId, companyID) -// } -// func (s *ServiceImpl) DeleteCustomOddByID(ctx context.Context, id int64) error{ -// return s.store.DeleteCustomOddByID(ctx, id) -// } -// func (s *ServiceImpl) DeleteCustomOddsByOddID(ctx context.Context, oddId int64, companyID int64) error{ -// return s.store.DeleteCustomOddsByOddID(ctx, oddId, companyID) -// } -// func (s *ServiceImpl) DeleteCustomOddByEventID(ctx context.Context, eventID string) error{ -// return s.store.DeleteCustomOddByEventID(ctx, eventID) -// } diff --git a/internal/services/odds/interface.go b/internal/services/odds/interface.go deleted file mode 100644 index b67b91e..0000000 --- a/internal/services/odds/interface.go +++ /dev/null @@ -1,33 +0,0 @@ -package odds - -import ( - "context" - "encoding/json" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type Service interface { - FetchNonLiveOdds(ctx context.Context) error - FetchNonLiveOddsByEventID(ctx context.Context, eventIDStr string) (domain.BaseNonLiveOddResponse, error) - ParseOddSections(ctx context.Context, res json.RawMessage, sportID int64) (domain.ParseOddSectionsRes, error) - GetPrematchOdds(ctx context.Context, eventID string) ([]domain.OddMarket, error) - GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string) ([]domain.OddMarket, error) - GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit domain.ValidInt64, offset domain.ValidInt64) ([]domain.OddMarket, error) - GetALLPrematchOdds(ctx context.Context) ([]domain.OddMarket, error) - // GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.OddMarket, error) - DeleteOddsForEvent(ctx context.Context, eventID string) error - GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error) - GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error) - - // Settings - SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error - UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) 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/odds/market_settings.go b/internal/services/odds/market_settings.go deleted file mode 100644 index d9c5fcc..0000000 --- a/internal/services/odds/market_settings.go +++ /dev/null @@ -1,38 +0,0 @@ -package odds - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *ServiceImpl) InsertGlobalMarketSettings(ctx context.Context, setting domain.CreateGlobalMarketSettings) error { - return s.marketSettingStore.InsertGlobalMarketSettings(ctx, setting) -} -func (s *ServiceImpl) InsertCompanyMarketSettings(ctx context.Context, setting domain.CreateCompanyMarketSettings) error { - return s.marketSettingStore.InsertCompanyMarketSettings(ctx, setting) -} -func (s *ServiceImpl) GetAllGlobalMarketSettings(ctx context.Context, filter domain.MarketSettingFilter) ([]domain.MarketSettings, error) { - return s.marketSettingStore.GetAllGlobalMarketSettings(ctx, filter) -} -func (s *ServiceImpl) GetGlobalMarketSettingsByID(ctx context.Context, Id int64) (domain.MarketSettings, error) { - return s.marketSettingStore.GetGlobalMarketSettingsByID(ctx, Id) -} -func (s *ServiceImpl) GetAllCompanyMarketSettings(ctx context.Context, filter domain.CompanyMarketSettingFilter) ([]domain.CompanyMarketSettings, error) { - return s.marketSettingStore.GetAllCompanyMarketSettings(ctx, filter) -} -func (s *ServiceImpl) GetCompanyMarketSettings(ctx context.Context, ID int64) (domain.CompanyMarketSettings, error) { - return s.marketSettingStore.GetCompanyMarketSettings(ctx, ID) -} -func (s *ServiceImpl) GetAllOverrideMarketSettings(ctx context.Context, companyID int64, filter domain.MarketSettingFilter) ([]domain.MarketSettings, error) { - return s.marketSettingStore.GetAllOverrideMarketSettings(ctx, companyID, filter) -} -func (s *ServiceImpl) GetOverrideMarketSettingByID(ctx context.Context, companyID int64, marketID int64) (domain.MarketSettings, error) { - return s.marketSettingStore.GetOverrideMarketSettingByID(ctx, companyID, marketID) -} -func (s *ServiceImpl) DeleteAllCompanyMarketSettings(ctx context.Context, companyID int64) error { - return s.marketSettingStore.DeleteAllCompanyMarketSettings(ctx, companyID) -} -func (s *ServiceImpl) DeleteCompanyMarketSettings(ctx context.Context, companyID int64, marketID int64) error { - return s.marketSettingStore.DeleteCompanyMarketSettings(ctx, companyID, marketID) -} diff --git a/internal/services/odds/odds_history.go b/internal/services/odds/odds_history.go deleted file mode 100644 index 48b8421..0000000 --- a/internal/services/odds/odds_history.go +++ /dev/null @@ -1,15 +0,0 @@ -package odds - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *ServiceImpl) GetAllOddHistory(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) { - return s.store.GetAllOddHistory(ctx, filter) -} - -func (s *ServiceImpl) GetInitialOddPerDay(ctx context.Context, filter domain.OddHistoryFilter) ([]domain.OddHistory, error) { - return s.store.GetInitialOddPerDay(ctx, filter) -} \ No newline at end of file diff --git a/internal/services/odds/service.go b/internal/services/odds/service.go deleted file mode 100644 index 0b5b822..0000000 --- a/internal/services/odds/service.go +++ /dev/null @@ -1,816 +0,0 @@ -package odds - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "log/slog" - "net/http" - "strconv" - "sync" - "time" - - "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/event" - "go.uber.org/zap" -) - -type ServiceImpl struct { - store ports.OddStore - marketSettingStore ports.MarketSettingStore - config *config.Config - eventSvc *event.Service - logger *slog.Logger - mongoLogger *zap.Logger - client *http.Client -} - -func New(store ports.OddStore, marketSettingStore ports.MarketSettingStore, cfg *config.Config, eventSvc *event.Service, logger *slog.Logger, mongoLogger *zap.Logger) *ServiceImpl { - return &ServiceImpl{ - store: store, - marketSettingStore: marketSettingStore, - config: cfg, - eventSvc: eventSvc, - logger: logger, - mongoLogger: mongoLogger, - client: &http.Client{Timeout: 10 * time.Second}, - } -} - -// TODO Add the optimization to get 10 events at the same time -func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error { - var wg sync.WaitGroup - errChan := make(chan error, 2) - // wg.Add(2) - wg.Add(1) - - go func() { - defer wg.Done() - if err := s.ProcessBet365Odds(ctx); err != nil { - errChan <- fmt.Errorf("failed while processing bet365 odds error: %w", err) - } - }() - - // go func() { - // defer wg.Done() - // if err := s.fetchBwinOdds(ctx); err != nil { - // errChan <- fmt.Errorf("bwin odds fetching error: %w", err) - // } - // }() - - go func() { - wg.Wait() - close(errChan) - }() - - var errs []error - for err := range errChan { - errs = append(errs, err) - } - - if len(errs) > 0 { - return errors.Join(errs...) - } - - return nil -} - -func (s *ServiceImpl) ProcessBet365Odds(ctx context.Context) error { - eventIDs, _, err := s.eventSvc.GetAllEvents(ctx, domain.EventFilter{ - Status: domain.ValidEventStatus{ - Value: domain.STATUS_PENDING, - Valid: true, - }, - // Source: domain.ValidEventSource{ - // Value: domain.EVENT_SOURCE_BET365, - // }, - }) - if err != nil { - s.mongoLogger.Error( - "Failed to fetch upcoming event IDs", - zap.Error(err), - ) - return err - } - - for index, event := range eventIDs { - if s.config.Env == "development" { - log.Printf("📡 Fetching prematch odds for event ID: %v (%d/%d) ", event.ID, index, len(eventIDs)) - } - - eventLogger := s.mongoLogger.With( - zap.Int64("eventID", event.ID), - zap.Int32("sportID", event.SportID), - ) - oddsData, err := s.FetchB365Odds(ctx, event.SourceEventID) - if err != nil || oddsData.Success != 1 { - eventLogger.Error("Failed to fetch prematch odds", zap.Error(err)) - continue - } - - parsedOddSections, err := s.ParseOddSections(ctx, oddsData.Results[0], event.SportID) - if err != nil { - eventLogger.Error("Failed to parse odd section", zap.Error(err)) - continue - } - - parsedOddLogger := eventLogger.With( - zap.String("parsedOddSectionFI", parsedOddSections.EventFI), - zap.Int("main_sections_count", len(parsedOddSections.Sections)), - zap.Int("other_sections_count", len(parsedOddSections.OtherRes)), - ) - - if parsedOddSections.EventFI == "" { - parsedOddLogger.Error("Skipping result with no valid Event FI field", zap.Error(err)) - continue - } - - if len(parsedOddSections.Sections) == 0 { - parsedOddLogger.Warn("Event has no odds in main sections", zap.Error(err)) - } - for oddCategory, section := range parsedOddSections.Sections { - if err := s.storeSection(ctx, event.ID, parsedOddSections.EventFI, oddCategory, section); err != nil { - parsedOddLogger.Error("Error storing odd section", zap.String("odd", oddCategory), zap.Error(err)) - } - } - for _, section := range parsedOddSections.OtherRes { - if err := s.storeSection(ctx, event.ID, parsedOddSections.EventFI, "others", section); err != nil { - parsedOddLogger.Error("Error storing odd other section", zap.Error(err)) - continue - } - } - - // result := oddsData.Results[0] - - } - - return nil -} - -// func (s *ServiceImpl) fetchBwinOdds(ctx context.Context) error { -// // getting odds for a specific event is not possible for bwin, most specific we can get is fetch odds on a single sport -// // so instead of having event and odds fetched separetly event will also be fetched along with the odds -// sportIds := []int{4, 12, 7} -// for _, sportId := range sportIds { -// url := fmt.Sprintf("https://api.b365api.com/v1/bwin/prematch?sport_id=%d&token=%s", sportId, s.config.Bet365Token) -// req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) -// if err != nil { -// s.mongoLogger.Error( -// "Failed to create request for sportId", -// zap.Int("sportID", sportId), -// zap.Error(err), -// ) -// continue -// } - -// resp, err := s.client.Do(req) -// if err != nil { -// s.mongoLogger.Error( -// "Failed to fetch request for sportId", -// zap.Int("sportID", sportId), -// zap.Error(err), -// ) -// continue -// } -// defer resp.Body.Close() - -// body, err := io.ReadAll(resp.Body) -// if err != nil { -// s.mongoLogger.Error( -// "Failed to read response body for sportId", -// zap.Int("sportID", sportId), -// zap.Error(err), -// ) -// continue -// } - -// var data struct { -// Success int `json:"success"` -// Results []map[string]interface{} `json:"results"` -// } - -// if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { -// fmt.Printf("Decode failed for sport_id=%d\nRaw: %s\n", sportId, string(body)) -// s.mongoLogger.Error( -// "Failed to decode BWin response body", -// zap.Int("sportID", sportId), -// zap.Error(err), -// ) -// continue -// } - -// for _, res := range data.Results { -// if getInt(res["Id"]) == -1 { -// continue -// } - -// event := domain.CreateEvent{ -// ID: strconv.Itoa(getInt(res["Id"])), -// SportID: int32(getInt(res["SportId"])), -// LeagueID: int64(getInt(res["LeagueId"])), -// LeagueName: getString(res["Leaguename"]), -// HomeTeam: getString(res["HomeTeam"]), -// HomeTeamID: int64(getInt(res["HomeTeamId"])), -// AwayTeam: getString(res["AwayTeam"]), -// AwayTeamID: int64(getInt(res["AwayTeamId"])), -// StartTime: time.Now().UTC(), -// IsLive: true, -// Status: domain.STATUS_IN_PLAY, -// Source: domain.EVENT_SOURCE_BWIN, -// MatchName: "", -// HomeTeamImage: "", -// AwayTeamImage: "", -// } - -// if err := s.store.SaveEvent(ctx, event); err != nil { -// fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) -// s.mongoLogger.Error( -// "Could not store live event", -// zap.Int("sportID", sportId), -// zap.String("eventID", event.ID), -// zap.Error(err), -// ) -// continue -// } - -// for _, market := range []string{"Markets, optionMarkets"} { -// for _, m := range getMapArray(res[market]) { -// name := getMap(m["name"]) -// marketName := getString(name["value"]) - -// market := domain.CreateOddMarket{ -// EventID: event.ID, -// MarketID: getString(m["id"]), -// MarketCategory: getString(m["category"]), -// MarketName: marketName, - -// } - -// results := getMapArray(m["results"]) -// market.Odds = results - -// s.store.SaveOddMarket(ctx, market) - -// } -// } -// } - -// } -// return nil -// } - -func (s *ServiceImpl) FetchB365Odds(ctx context.Context, eventID string) (domain.BaseNonLiveOddResponse, error) { - - url := fmt.Sprintf("https://api.b365api.com/v3/bet365/prematch?token=%s&FI=%v", s.config.Bet365Token, eventID) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - s.mongoLogger.Error( - "Failed to create request for event", - zap.String("eventID", eventID), - zap.Error(err), - ) - - } - - resp, err := s.client.Do(req) - if err != nil { - s.mongoLogger.Error( - "Failed to fetch prematch odds for event", - zap.String("eventID", eventID), - zap.Error(err), - ) - return domain.BaseNonLiveOddResponse{}, err - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - s.mongoLogger.Error( - "Failed to read response body for event", - zap.String("eventID", eventID), - zap.Error(err), - ) - return domain.BaseNonLiveOddResponse{}, err - } - var oddsData domain.BaseNonLiveOddResponse - - if err := json.Unmarshal(body, &oddsData); err != nil || oddsData.Success != 1 || len(oddsData.Results) == 0 { - s.mongoLogger.Error( - "Invalid prematch data for event", - zap.String("eventID", eventID), - - zap.Error(err), - ) - return domain.BaseNonLiveOddResponse{}, err - } - - return oddsData, nil -} - -func (s *ServiceImpl) ParseOddSections(ctx context.Context, res json.RawMessage, sportID int32) (domain.ParseOddSectionsRes, error) { - var sections map[string]domain.OddsSection - var OtherRes []domain.OddsSection - var eventFI string - switch sportID { - case domain.FOOTBALL: - var footballRes domain.FootballOddsResponse - if err := json.Unmarshal(res, &footballRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal football result", - - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = footballRes.FI - OtherRes = footballRes.Others - sections = map[string]domain.OddsSection{ - "main": footballRes.Main, - "asian_lines": footballRes.AsianLines, - "goals": footballRes.Goals, - "half": footballRes.Half, - "cards": footballRes.Cards, - "corners": footballRes.Corners, - "player": footballRes.Player, - "minutes": footballRes.Minutes, - "specials": footballRes.Specials, - } - case domain.BASKETBALL: - var basketballRes domain.BasketballOddsResponse - if err := json.Unmarshal(res, &basketballRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal basketball result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = basketballRes.FI - OtherRes = basketballRes.Others - sections = map[string]domain.OddsSection{ - "main": basketballRes.Main, - "half_props": basketballRes.HalfProps, - "quarter_props": basketballRes.QuarterProps, - "team_props": basketballRes.TeamProps, - } - - case domain.ICE_HOCKEY: - var iceHockeyRes domain.IceHockeyOddsResponse - if err := json.Unmarshal(res, &iceHockeyRes); err != nil { - - s.mongoLogger.Error( - "Failed to unmarshal ice hockey result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = iceHockeyRes.FI - OtherRes = iceHockeyRes.Others - sections = map[string]domain.OddsSection{ - "main": iceHockeyRes.Main, - "main_2": iceHockeyRes.Main2, - "1st_period": iceHockeyRes.FirstPeriod, - } - case domain.CRICKET: - var cricketRes domain.CricketOddsResponse - if err := json.Unmarshal(res, &cricketRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal cricket result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = cricketRes.FI - OtherRes = cricketRes.Others - sections = map[string]domain.OddsSection{ - "1st_over": cricketRes.Main, - "innings_1": cricketRes.First_Innings, - "main": cricketRes.Main, - "match": cricketRes.Match, - "player": cricketRes.Player, - "team": cricketRes.Team, - } - case domain.VOLLEYBALL: - var volleyballRes domain.VolleyballOddsResponse - if err := json.Unmarshal(res, &volleyballRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal volleyball result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = volleyballRes.FI - OtherRes = volleyballRes.Others - sections = map[string]domain.OddsSection{ - "main": volleyballRes.Main, - } - case domain.DARTS: - var dartsRes domain.DartsOddsResponse - if err := json.Unmarshal(res, &dartsRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal darts result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = dartsRes.FI - OtherRes = dartsRes.Others - sections = map[string]domain.OddsSection{ - "180s": dartsRes.OneEightys, - "extra": dartsRes.Extra, - "leg": dartsRes.Leg, - "main": dartsRes.Main, - } - case domain.FUTSAL: - var futsalRes domain.FutsalOddsResponse - if err := json.Unmarshal(res, &futsalRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal futsal result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = futsalRes.FI - OtherRes = futsalRes.Others - sections = map[string]domain.OddsSection{ - "main": futsalRes.Main, - "score": futsalRes.Score, - } - case domain.AMERICAN_FOOTBALL: - var americanFootballRes domain.AmericanFootballOddsResponse - if err := json.Unmarshal(res, &americanFootballRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal american football result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = americanFootballRes.FI - OtherRes = americanFootballRes.Others - sections = map[string]domain.OddsSection{ - "half_props": americanFootballRes.HalfProps, - "main": americanFootballRes.Main, - "quarter_props": americanFootballRes.QuarterProps, - } - case domain.RUGBY_LEAGUE: - var rugbyLeagueRes domain.RugbyLeagueOddsResponse - if err := json.Unmarshal(res, &rugbyLeagueRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal rugby league result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = rugbyLeagueRes.FI - OtherRes = rugbyLeagueRes.Others - sections = map[string]domain.OddsSection{ - "10minute": rugbyLeagueRes.TenMinute, - "main": rugbyLeagueRes.Main, - "main_2": rugbyLeagueRes.Main2, - "player": rugbyLeagueRes.Player, - "Score": rugbyLeagueRes.Score, - "Team": rugbyLeagueRes.Team, - } - case domain.RUGBY_UNION: - var rugbyUnionRes domain.RugbyUnionOddsResponse - if err := json.Unmarshal(res, &rugbyUnionRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal rugby union result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = rugbyUnionRes.FI - OtherRes = rugbyUnionRes.Others - sections = map[string]domain.OddsSection{ - "main": rugbyUnionRes.Main, - "main_2": rugbyUnionRes.Main2, - "player": rugbyUnionRes.Player, - "Score": rugbyUnionRes.Score, - "Team": rugbyUnionRes.Team, - } - case domain.BASEBALL: - var baseballRes domain.BaseballOddsResponse - if err := json.Unmarshal(res, &baseballRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal baseball result", - zap.Error(err), - ) - return domain.ParseOddSectionsRes{}, err - } - eventFI = baseballRes.FI - sections = map[string]domain.OddsSection{ - "main": baseballRes.Main, - "mani_props": baseballRes.MainProps, - } - } - - return domain.ParseOddSectionsRes{ - Sections: sections, - OtherRes: OtherRes, - EventFI: eventFI, - }, nil -} - -func (s *ServiceImpl) storeSection(ctx context.Context, eventID int64, fi, sectionName string, section domain.OddsSection) error { - - if len(section.Sp) == 0 { - s.mongoLogger.Warn("Event Section is empty", - zap.Int64("eventID", eventID), - zap.String("sectionName", sectionName), - ) - return nil - } - - updatedAtUnix, _ := strconv.ParseInt(section.UpdatedAt, 10, 64) - updatedAt := time.Unix(updatedAtUnix, 0) - - var errs []error - for marketType, market := range section.Sp { - marketLogger := s.mongoLogger.With( - zap.Int64("eventID", eventID), - zap.String("sectionName", sectionName), - zap.String("market_id", string(market.ID)), - zap.String("marketType", marketType), - zap.String("marketName", market.Name), - ) - if len(market.Odds) == 0 { - // marketLogger.Warn("Skipping market with no odds") - continue - } - - marketIDint, err := strconv.ParseInt(string(market.ID), 10, 64) - if err != nil { - marketLogger.Warn("skipping market section where market_id is not int") - continue - } - - isSupported, ok := domain.SupportedMarkets[marketIDint] - - if !ok || !isSupported { - // marketLogger.Warn("skipping market that isn't supported", zap.Bool("is_market_found", ok)) - continue - } - - marketOdds, err := convertRawMessage(market.Odds) - if err != nil { - marketLogger.Error("failed to convert market.Odds to json.RawMessage to []map[string]interface{}", zap.Error(err)) - errs = append(errs, err) - continue - } - - marketRecord := domain.CreateOddMarket{ - EventID: eventID, - MarketCategory: sectionName, - MarketType: marketType, - MarketName: market.Name, - MarketID: marketIDint, - NumberOfOutcomes: int64(len(market.Odds)), - UpdatedAt: updatedAt, - Odds: marketOdds, - // bwin won't reach this code so bet365 is hardcoded for now - } - - if err := s.CheckAndInsertOddHistory(ctx, marketRecord); err != nil { - marketLogger.Error("failed to check and insert odd history", zap.Error(err)) - continue - } - - err = s.store.SaveOddMarket(ctx, marketRecord) - if err != nil { - marketLogger.Error("failed to save market", zap.Error(err)) - errs = append(errs, fmt.Errorf("market %v: %w", market.ID, err)) - continue - } - } - - if len(errs) > 0 { - return errors.Join(errs...) - } - return nil -} - -func (s *ServiceImpl) CheckAndInsertOddHistory(ctx context.Context, market domain.CreateOddMarket) error { - isEventMonitored, err := s.eventSvc.IsEventMonitored(ctx, market.EventID) - - marketLogger := s.mongoLogger.With( - zap.Int64("market_id", market.MarketID), - zap.String("market_name", market.MarketName), - zap.Int64("eventID", market.EventID), - ) - if err != nil { - marketLogger.Error("failed to get is_monitored", zap.Error(err)) - } - - if !isEventMonitored { - return nil - } - - oldOdds, err := s.store.GetOddsByMarketID(ctx, market.MarketID, market.EventID) - - if err != nil { - marketLogger.Error("failed to get raw odds by market id", zap.Error(err)) - return err - } - - if len(oldOdds.RawOdds) != len(market.Odds) { - marketLogger.Error("new odds data does not match old odds data", zap.Error(err)) - return fmt.Errorf("new odds data does not match old odds data") - } - - oldRawOdds, err := convertRawMessage(oldOdds.RawOdds) - - if err != nil { - marketLogger.Error("failed to convert raw odds to map", zap.Error(err)) - return err - } - - for _, oddData := range market.Odds { - newRawOddID := getInt(oddData["id"]) - newOddsVal := getFloat(oddData["odds"]) - isFound := false - for _, oldOddData := range oldRawOdds { - oldRawOddID := getInt(oldOddData["id"]) - oldOddsVal := getFloat(oldOddData["odds"]) - if newRawOddID == oldRawOddID { - if newOddsVal != oldOddsVal { - _, err := s.store.InsertOddHistory(ctx, domain.CreateOddHistory{ - OddID: oldOdds.ID, - MarketID: market.MarketID, - RawOddID: int64(newRawOddID), - EventID: market.EventID, - OddValue: newOddsVal, - }) - - if err != nil { - s.mongoLogger.Error( - "failed to insert odd history", - zap.Int64("market_id", market.MarketID), - zap.String("market_name", market.MarketName), - zap.Int64("eventID", market.EventID), - zap.Int64("odd_id", oldOdds.ID), - zap.Int("raw_odd_id", newRawOddID), - zap.Error(err), - ) - } - } - isFound = true - } - } - if !isFound { - fmt.Printf("raw odd id %d not found", newRawOddID) - } - } - return nil -} - -func (s *ServiceImpl) GetAllOdds(ctx context.Context, filter domain.OddMarketFilter) ([]domain.OddMarket, error) { - return s.store.GetAllOdds(ctx, filter) -} - -func (s *ServiceImpl) GetAllOddsWithSettings(ctx context.Context, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) { - return s.store.GetAllOddsWithSettings(ctx, companyID, filter) -} - -func (s *ServiceImpl) GetOddByID(ctx context.Context, id int64) (domain.OddMarket, error) { - return s.store.GetOddByID(ctx, id) -} - -func (s *ServiceImpl) SaveOddsSetting(ctx context.Context, odd domain.CreateOddMarketSettings) error { - return s.store.SaveOddsSetting(ctx, odd) -} - -func (s *ServiceImpl) UpdateGlobalOddsSetting(ctx context.Context, odd domain.UpdateGlobalOddMarketSettings) error { - return s.store.UpdateGlobalOddsSetting(ctx, odd) -} - -func (s *ServiceImpl) SaveOddsSettingReq(ctx context.Context, companyID int64, req domain.CreateOddMarketSettingsReq) error { - - odd, err := s.GetOddsWithSettingsByID(ctx, req.OddMarketID, companyID) - if err != nil { - return err - } - - newOdds, err := convertRawMessage(odd.RawOdds) - if err != nil { - return err - } - - if len(req.CustomOdd) != 0 { - for _, customOdd := range req.CustomOdd { - for _, newOdd := range newOdds { - oldRawOddID := getInt(newOdd["id"]) - - if oldRawOddID == int(customOdd.OddID) { - newOdd["odds"] = customOdd.OddValue - } - } - } - } - - return s.SaveOddsSetting(ctx, domain.CreateOddMarketSettings{ - CompanyID: companyID, - OddMarketID: req.OddMarketID, - IsActive: domain.ConvertBoolPtr(req.IsActive), - CustomRawOdds: newOdds, - }) -} - -func (s *ServiceImpl) GetOddsByMarketID(ctx context.Context, marketID int64, eventID int64) (domain.OddMarket, error) { - rows, err := s.store.GetOddsByMarketID(ctx, marketID, eventID) - if err != nil { - return domain.OddMarket{}, err - } - - return rows, nil -} -func (s *ServiceImpl) GetOddsWithSettingsByMarketID(ctx context.Context, marketID int64, eventID int64, companyID int64) (domain.OddMarketWithSettings, error) { - rows, err := s.store.GetOddsWithSettingsByMarketID(ctx, marketID, eventID, companyID) - if err != nil { - return domain.OddMarketWithSettings{}, err - } - - return rows, nil -} - -func (s *ServiceImpl) GetOddsWithSettingsByID(ctx context.Context, ID int64, companyID int64) (domain.OddMarketWithSettings, error) { - return s.store.GetOddsWithSettingsByID(ctx, ID, companyID) -} - -func (s *ServiceImpl) GetOddsByEventID(ctx context.Context, eventID int64, filter domain.OddMarketWithEventFilter) ([]domain.OddMarket, error) { - return s.store.GetOddsByEventID(ctx, eventID, filter) -} - -func (s *ServiceImpl) GetOddsWithSettingsByEventID(ctx context.Context, eventID int64, companyID int64, filter domain.OddMarketFilter) ([]domain.OddMarketWithSettings, error) { - return s.store.GetOddsWithSettingsByEventID(ctx, eventID, companyID, filter) -} - -func (s *ServiceImpl) DeleteOddsForEvent(ctx context.Context, eventID int64) error { - return s.store.DeleteOddsForEvent(ctx, eventID) -} - -func (s *ServiceImpl) DeleteAllCompanyOddsSetting(ctx context.Context, companyID int64) error { - return s.store.DeleteAllCompanyOddsSetting(ctx, companyID) -} - -func (s *ServiceImpl) DeleteCompanyOddsSettingByOddMarketID(ctx context.Context, companyID int64, oddMarketID int64) error { - return s.store.DeleteCompanyOddsSettingByOddMarketID(ctx, companyID, oddMarketID) -} - -// func getString(v interface{}) string { -// if str, ok := v.(string); ok { -// return str -// } -// return "" -// } - -func getInt(v interface{}) int { - if n, ok := v.(float64); ok { - return int(n) - } - return -1 -} -func getFloat(v interface{}) float64 { - if n, ok := v.(float64); ok { - return n - } - return 0 -} - -func getMap(v interface{}) map[string]interface{} { - if m, ok := v.(map[string]interface{}); ok { - return m - } - return nil -} - -// func getMapArray(v interface{}) []map[string]interface{} { -// result := []map[string]interface{}{} -// if arr, ok := v.([]interface{}); ok { -// for _, item := range arr { -// if m, ok := item.(map[string]interface{}); ok { -// result = append(result, m) -// } -// } -// } -// return result -// } - -func convertRawMessage(rawMessages []json.RawMessage) ([]map[string]interface{}, error) { - var result []map[string]interface{} - for _, raw := range rawMessages { - var m map[string]interface{} - if err := json.Unmarshal(raw, &m); err != nil { - return nil, err - } - result = append(result, m) - } - - return result, nil -} diff --git a/internal/services/raffle/interface.go b/internal/services/raffle/interface.go deleted file mode 100644 index 6f06cad..0000000 --- a/internal/services/raffle/interface.go +++ /dev/null @@ -1,27 +0,0 @@ -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 deleted file mode 100644 index 8326ddb..0000000 --- a/internal/services/raffle/service.go +++ /dev/null @@ -1,79 +0,0 @@ -package raffle - -import ( - "context" - - 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 ports.RaffleStore -} - -func NewService(raffleStore ports.RaffleStore) *Service { - return &Service{ - raffleStore: raffleStore, - } -} - -func (s *Service) CreateRaffle(ctx context.Context, raffle domain.CreateRaffle) (domain.Raffle, error) { - return s.raffleStore.CreateRaffle(ctx, raffle) -} - -func (s *Service) AddSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) error { - return s.raffleStore.AddSportRaffleFilter(ctx, raffleID, sportID, leagueID) -} - -func (s *Service) DeleteRaffle(ctx context.Context, raffleID int32) (domain.Raffle, error) { - return s.raffleStore.DeleteRaffle(ctx, raffleID) -} - -func (s *Service) GetRafflesOfCompany(ctx context.Context, companyID int32) ([]dbgen.Raffle, error) { - return s.raffleStore.GetRafflesOfCompany(ctx, companyID) -} - -func (s *Service) GetRaffleStanding(ctx context.Context, raffleID, limit int32) ([]domain.RaffleStanding, error) { - return s.raffleStore.GetRaffleStanding(ctx, raffleID, limit) -} - -func (s *Service) CreateRaffleWinner(ctx context.Context, raffleWinnerParams domain.RaffleWinnerParams) error { - return s.raffleStore.CreateRaffleWinner(ctx, raffleWinnerParams) -} - -func (s *Service) SetRaffleComplete(ctx context.Context, raffleID int32) error { - return s.raffleStore.SetRaffleComplete(ctx, raffleID) -} - -func (s *Service) CreateRaffleTicket(ctx context.Context, raffleTicketParams domain.CreateRaffleTicket) (domain.RaffleTicket, error) { - return s.raffleStore.CreateRaffleTicket(ctx, raffleTicketParams) -} - -func (s *Service) GetUserRaffleTickets(ctx context.Context, userID int32) ([]domain.RaffleTicketRes, error) { - return s.raffleStore.GetUserRaffleTickets(ctx, userID) -} - -func (s *Service) SuspendRaffleTicket(ctx context.Context, raffleTicketID int32) error { - return s.raffleStore.SuspendRaffleTicket(ctx, raffleTicketID) -} - -func (s *Service) UnSuspendRaffleTicket(ctx context.Context, raffleID int32) error { - return s.raffleStore.UnSuspendRaffleTicket(ctx, raffleID) -} - -func (s *Service) CheckValidSportRaffleFilter(ctx context.Context, raffleID int32, sportID, leagueID int64) (bool, error) { - return s.raffleStore.CheckValidSportRaffleFilter(ctx, raffleID, sportID, leagueID) -} - -func (s *Service) CheckSportRaffleHasFilter(ctx context.Context, raffleID int32) (bool, error) { - return s.raffleStore.CheckSportRaffleHasFilter(ctx, raffleID) -} - -func (s *Service) GetRaffleTicketCount(ctx context.Context, raffleID, userID int32) (int64, error) { - return s.raffleStore.GetRaffleTicketCount(ctx, raffleID, userID) -} - -func (s *Service) GetRaffleTicketLimit(ctx context.Context, raffleID int32) (int32, error) { - return s.raffleStore.GetRaffleTicketLimit(ctx, raffleID) -} diff --git a/internal/services/recommendation/service.go b/internal/services/recommendation/service.go index bb86789..97d7a5f 100644 --- a/internal/services/recommendation/service.go +++ b/internal/services/recommendation/service.go @@ -1,10 +1,9 @@ package recommendation import ( + "Yimaru-Backend/internal/repository" "context" "sort" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" ) // type RecommendationService interface { diff --git a/internal/services/referal/interface.go b/internal/services/referal/interface.go index a192995..3b2f239 100644 --- a/internal/services/referal/interface.go +++ b/internal/services/referal/interface.go @@ -3,7 +3,6 @@ package referralservice // import ( // "context" -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" // ) // type ReferralStore interface { diff --git a/internal/services/referal/service.go b/internal/services/referal/service.go index 015c7e9..dcffb31 100644 --- a/internal/services/referal/service.go +++ b/internal/services/referal/service.go @@ -1,25 +1,21 @@ package referralservice import ( + "Yimaru-Backend/internal/config" + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/ports" + "Yimaru-Backend/internal/services/settings" "context" "crypto/rand" "encoding/base32" "errors" - "fmt" "log/slog" - "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/settings" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" "go.uber.org/zap" ) type Service struct { repo ports.ReferralStore - walletSvc wallet.Service settingSvc settings.Service config *config.Config logger *slog.Logger @@ -27,16 +23,14 @@ type Service struct { } func New( - repo ports.ReferralStore, - walletSvc wallet.Service, - settingSvc settings.Service, - cfg *config.Config, - logger *slog.Logger, + repo ports.ReferralStore, + settingSvc settings.Service, + cfg *config.Config, + logger *slog.Logger, mongoLogger *zap.Logger, - ) *Service { +) *Service { return &Service{ repo: repo, - walletSvc: walletSvc, settingSvc: settingSvc, config: cfg, logger: logger, @@ -115,20 +109,20 @@ func (s *Service) ProcessReferral(ctx context.Context, referredID int64, referra return err } - wallets, err := s.walletSvc.GetCustomerWallet(ctx, referral.ReferrerID) - if err != nil { - paramLogger.Error("Failed to get referrer wallets", zap.Error(err)) - return err - } + // wallets, err := s.walletSvc.GetCustomerWallet(ctx, referral.ReferrerID) + // if err != nil { + // paramLogger.Error("Failed to get referrer wallets", zap.Error(err)) + // return err + // } - _, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, - referral.RewardAmount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, - fmt.Sprintf("Added %v to static wallet due to %v referral code being used", referral.RewardAmount, referral.ReferralCode), - ) - if err != nil { - paramLogger.Error("Failed to add referral reward to static wallet", zap.Int64("static_wallet_id", wallets.StaticID), zap.Error(err)) - return err - } + // _, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, + // referral.RewardAmount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, + // fmt.Sprintf("Added %v to static wallet due to %v referral code being used", referral.RewardAmount, referral.ReferralCode), + // ) + // if err != nil { + // paramLogger.Error("Failed to add referral reward to static wallet", zap.Int64("static_wallet_id", wallets.StaticID), zap.Error(err)) + // return err + // } _, err = s.repo.CreateUserReferral(ctx, domain.CreateUserReferrals{ ReferredID: referredID, diff --git a/internal/services/report/bet.go b/internal/services/report/bet.go deleted file mode 100644 index a5061de..0000000 --- a/internal/services/report/bet.go +++ /dev/null @@ -1,95 +0,0 @@ -package report - -import ( - "context" - - "fmt" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" -) - -type BetIntervalRow struct { - Period string `csv:"Period"` - TotalBets int64 `csv:"Total Bets"` - TotalStake int64 `csv:"Total Stake"` - ActiveBets int64 `csv:"Active Bets"` - TotalWins int64 `csv:"Total Wins"` - TotalLosses int64 `csv:"Total Losses"` - WinBalance int64 `csv:"Win Balance"` - NumberOfUnsettled int64 `csv:"Number Of Unsettled"` - TotalUnsettledAmount int64 `csv:"Total Unsettled Amount"` - TotalShopBets int64 `csv:"Total Shop Bets"` -} - -func (s *Service) GenerateBetIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) { - if request.Metadata.Interval == nil { - s.mongoLogger.Error("[GenerateBetIntervalReport] Metadata interval is empty") - return "", domain.ErrInvalidInterval - } - - interval, err := domain.ParseDateInterval(*request.Metadata.Interval) - if err != nil { - s.mongoLogger.Error("[GenerateBetIntervalReport] Failed to parse date interval", - zap.String("interval", *request.Metadata.Interval), - zap.Error(err), - ) - return "", domain.ErrInvalidInterval - } - - stats, err := s.statService.GetBetStatsByInterval(ctx, domain.BetStatsByIntervalFilter{ - Interval: domain.ValidDateInterval{ - Value: interval, - Valid: true, - }, - CompanyID: request.CompanyID, - }) - if err != nil { - s.mongoLogger.Error("[GenerateBetIntervalReport] Failed to fetch bet stats", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("fetching bet stats: %w", err) - } - - var rows [][]string - var headers []string - for _, stat := range stats { - endDate, err := domain.GetEndDateFromInterval(interval, stat.Date) - if err != nil { - s.mongoLogger.Error("[GenerateBetIntervalReport] Failed to get end date from interval", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("invalid interval end date: %w", err) - } - - period := fmt.Sprintf("%s to %s", - stat.Date.Format("2006-01-02"), - endDate.Format("2006-01-02"), - ) - - r := BetIntervalRow{ - Period: period, - TotalBets: stat.TotalBets, - TotalStake: stat.TotalStake, - ActiveBets: stat.ActiveBets, - TotalWins: stat.TotalWins, - TotalLosses: stat.TotalLosses, - WinBalance: stat.WinBalance, - NumberOfUnsettled: stat.NumberOfUnsettled, - TotalUnsettledAmount: stat.TotalUnsettledAmount, - TotalShopBets: stat.TotalShopBets, - } - - if headers == nil { - headers, _ = StructToCSVRow(r) - rows = append(rows, headers) - } - - _, row := StructToCSVRow(r) - rows = append(rows, row) - } - - return s.WriteCSV(rows, "bet_interval") -} diff --git a/internal/services/report/branch.go b/internal/services/report/branch.go deleted file mode 100644 index 251bfbf..0000000 --- a/internal/services/report/branch.go +++ /dev/null @@ -1,99 +0,0 @@ -package report - -import ( - "context" - - "fmt" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" -) - -type BranchIntervalRow struct { - Period string `csv:"Period"` - BranchName string `csv:"CompanyName"` - CompanyName string `csv:"CompanyName"` - TotalBets int64 `csv:"Total Bets"` - TotalStake float32 `csv:"Total Stake"` - DeductedStake float32 `csv:"Deducted Stake"` - TotalCashOut float32 `csv:"Total CashOut"` - TotalCashBacks float32 `csv:"Total CashBacks"` - NumberOfUnsettled int64 `csv:"Number Of Unsettled"` - TotalUnsettledAmount float32 `csv:"Total Unsettled Amount"` - TotalCashiers int64 `csv:"Total Cashiers"` -} - -func (s *Service) GenerateBranchIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) { - - if request.Metadata.Interval == nil { - s.mongoLogger.Error("[GenerateBranchIntervalReport] Metadata interval is empty") - return "", domain.ErrInvalidInterval - } - - interval, err := domain.ParseDateInterval(*request.Metadata.Interval) - if err != nil { - s.mongoLogger.Error("[GenerateBranchIntervalReport] Failed to parse date interval", - zap.String("interval", *request.Metadata.Interval), - zap.Error(err), - ) - return "", domain.ErrInvalidInterval - } - - stats, err := s.statService.GetBranchStatsByInterval(ctx, domain.BranchStatFilter{ - Interval: domain.ValidDateInterval{ - Value: interval, - Valid: true, - }, - CompanyID: request.CompanyID, - BranchID: domain.ConvertInt64Ptr(request.Metadata.BranchID), - }) - if err != nil { - s.mongoLogger.Error("[GenerateBranchIntervalReport] Failed to fetch branch stats", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("fetching branch stats: %w", err) - } - - var rows [][]string - var headers []string - for _, stat := range stats { - endDate, err := domain.GetEndDateFromInterval(interval, stat.IntervalStart) - if err != nil { - s.mongoLogger.Error("[GenerateBranchIntervalReport] Failed to get end date from interval", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("invalid interval end date: %w", err) - } - - period := fmt.Sprintf("%s to %s", - stat.IntervalStart.Format("2006-01-02"), - endDate.Format("2006-01-02"), - ) - - r := BranchIntervalRow{ - Period: period, - BranchName: stat.BranchName, - CompanyName: stat.CompanyName, - TotalBets: stat.TotalBets, - TotalStake: stat.TotalStake.Float32(), - DeductedStake: stat.DeductedStake.Float32(), - TotalCashOut: stat.TotalCashOut.Float32(), - TotalCashBacks: stat.TotalCashBacks.Float32(), - NumberOfUnsettled: stat.NumberOfUnsettled, - TotalUnsettledAmount: stat.TotalUnsettledAmount.Float32(), - TotalCashiers: stat.TotalCashiers, - } - - if headers == nil { - headers, _ = StructToCSVRow(r) - rows = append(rows, headers) - } - - _, row := StructToCSVRow(r) - rows = append(rows, row) - } - - return s.WriteCSV(rows, "branch_interval") -} diff --git a/internal/services/report/company.go b/internal/services/report/company.go deleted file mode 100644 index 94dd998..0000000 --- a/internal/services/report/company.go +++ /dev/null @@ -1,111 +0,0 @@ -package report - -import ( - "context" - - "fmt" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" -) - -type CompanyIntervalRow struct { - Period string `csv:"Period"` - CompanyName string `csv:"CompanyName"` - TotalBets int64 `csv:"Total Bets"` - TotalStake float32 `csv:"Total Stake"` - DeductedStake float32 `csv:"Deducted Stake"` - TotalCashOut float32 `csv:"Total CashOut"` - TotalCashBacks float32 `csv:"Total CashBacks"` - NumberOfUnsettled int64 `csv:"Number Of Unsettled"` - TotalUnsettledAmount float32 `csv:"Total Unsettled Amount"` - TotalAdmins int64 `csv:"Total Admins"` - TotalManagers int64 `csv:"Total Managers"` - TotalCashiers int64 `csv:"Total Cashiers"` - TotalCustomers int64 `csv:"Total Customers"` - TotalApprovers int64 `csv:"Total Approvers"` - TotalBranches int64 `csv:"Total Branches"` -} - -func (s *Service) GenerateCompanyIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) { - - // Only a super-admin is allowed to generate this type of report - if request.RequesterRole.Valid && request.RequesterRole.Value != domain.RoleSuperAdmin { - s.mongoLogger.Error("[GenerateCompanyIntervalReport] Unauthorized user report") - return "", ErrUnauthorizedUserReport - } - - if request.Metadata.Interval == nil { - s.mongoLogger.Error("[GenerateCompanyIntervalReport] Metadata interval is empty") - return "", domain.ErrInvalidInterval - } - - interval, err := domain.ParseDateInterval(*request.Metadata.Interval) - if err != nil { - s.mongoLogger.Error("[GenerateCompanyIntervalReport] Failed to parse date interval", - zap.String("interval", *request.Metadata.Interval), - zap.Error(err), - ) - return "", domain.ErrInvalidInterval - } - - stats, err := s.statService.GetCompanyStatsByInterval(ctx, domain.CompanyStatFilter{ - Interval: domain.ValidDateInterval{ - Value: interval, - Valid: true, - }, - }) - if err != nil { - s.mongoLogger.Error("[GenerateCompanyIntervalReport] Failed to fetch company stats", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("fetching company stats: %w", err) - } - - var rows [][]string - var headers []string - for _, stat := range stats { - endDate, err := domain.GetEndDateFromInterval(interval, stat.IntervalStart) - if err != nil { - s.mongoLogger.Error("[GenerateCompanyIntervalReport] Failed to get end date from interval", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("invalid interval end date: %w", err) - } - - period := fmt.Sprintf("%s to %s", - stat.IntervalStart.Format("2006-01-02"), - endDate.Format("2006-01-02"), - ) - - r := CompanyIntervalRow{ - Period: period, - CompanyName: stat.CompanyName, - TotalBets: stat.TotalBets, - TotalStake: stat.TotalStake.Float32(), - DeductedStake: stat.DeductedStake.Float32(), - TotalCashOut: stat.TotalCashOut.Float32(), - TotalCashBacks: stat.TotalCashBacks.Float32(), - NumberOfUnsettled: stat.NumberOfUnsettled, - TotalUnsettledAmount: stat.TotalUnsettledAmount.Float32(), - TotalAdmins: stat.TotalAdmins, - TotalManagers: stat.TotalManagers, - TotalCashiers: stat.TotalCashiers, - TotalCustomers: stat.TotalCustomers, - TotalApprovers: stat.TotalApprovers, - TotalBranches: stat.TotalBranches, - } - - if headers == nil { - headers, _ = StructToCSVRow(r) - rows = append(rows, headers) - } - - _, row := StructToCSVRow(r) - rows = append(rows, row) - } - - return s.WriteCSV(rows, "company_interval") -} diff --git a/internal/services/report/csv.go b/internal/services/report/csv.go deleted file mode 100644 index cd59729..0000000 --- a/internal/services/report/csv.go +++ /dev/null @@ -1,120 +0,0 @@ -package report - -import ( - "context" - "encoding/csv" - "errors" - "fmt" - "os" - "path/filepath" - "time" - "reflect" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/google/uuid" - "go.uber.org/zap" -) - - -var ( - ErrReportFileNotFound = errors.New("failed to find report file") - ErrReportFileError = errors.New("unknown error with report file") - ErrReportNotComplete = errors.New("report is not completed") - ErrReportFilePathInvalid = errors.New("report file path is invalid") -) - -func StructToCSVRow(v any) ([]string, []string) { - t := reflect.TypeOf(v) - val := reflect.ValueOf(v) - headers := make([]string, t.NumField()) - row := make([]string, t.NumField()) - - for i := 0; i < t.NumField(); i++ { - field := t.Field(i) - headers[i] = field.Tag.Get("csv") - row[i] = fmt.Sprint(val.Field(i).Interface()) - } - return headers, row -} - -func (s *Service) WriteCSV(rows [][]string, filePrefix string) (string, error) { - if len(rows) == 0 { - s.mongoLogger.Error("[WriteCSV] CSV with no data", - zap.String("file_prefix", filePrefix), - ) - return "", errors.New("no data to write") - } - - filename := fmt.Sprintf("%s_%s_%s.csv", - filePrefix, - time.Now().Format("2006-01-02_15-04-05"), - uuid.NewString()[:8], - ) - - filePath := filepath.Join(s.cfg.ReportExportPath, filename) - - file, err := os.Create(filePath) - if err != nil { - s.mongoLogger.Error("[WriteCSV] Failed to create file", - zap.String("file", filename), - zap.String("path", s.cfg.ReportExportPath), - zap.Error(err), - ) - return "", fmt.Errorf("create csv: %w", err) - } - defer file.Close() - - writer := csv.NewWriter(file) - if err := writer.WriteAll(rows); err != nil { - s.mongoLogger.Error("[WriteCSV] Error while writing csv", - zap.String("file", filename), - zap.String("path", s.cfg.ReportExportPath), - zap.Error(err), - ) - return "", fmt.Errorf("write csv: %w", err) - } - - return filePath, nil -} - -func (s *Service) CheckAndFetchReportFile(ctx context.Context, ID int64) (string, error) { - report, err := s.GetReportRequestByID(ctx, ID) - if err != nil { - s.mongoLogger.Error("[CheckAndFetchReportFile] Failed to get report request by id", - zap.Error(err), - ) - return "", fmt.Errorf("failed to get report request:%w", err) - } - - if report.Status != domain.SuccessReportRequest { - s.mongoLogger.Error("[CheckAndFetchReportFile] Attempted download of report that isn't completed", - zap.String("status", string(report.Status)), - ) - return "", ErrReportNotComplete - } - - if !report.FilePath.Valid { - s.mongoLogger.Error("[CheckAndFetchReportFile] File Path is invalid even though the report is a success", - zap.String("file path", report.FilePath.Value), - ) - return "", ErrReportFilePathInvalid - } - - // Check if the report file exists - if _, err := os.Stat(report.FilePath.Value); err != nil { - if os.IsNotExist(err) { - s.mongoLogger.Error("[CheckAndFetchReportFile] Unable to find report file", - zap.String("file path", report.FilePath.Value), - ) - return "", ErrReportFileNotFound - } - - s.mongoLogger.Error("[CheckAndFetchReportFile] Unable to check report file", - zap.String("file path", report.FilePath.Value), - zap.Error(err), - ) - return "", ErrReportFileError - } - - return report.FilePath.Value, nil -} diff --git a/internal/services/report/event.go b/internal/services/report/event.go deleted file mode 100644 index 95f4399..0000000 --- a/internal/services/report/event.go +++ /dev/null @@ -1,118 +0,0 @@ -package report - -import ( - "context" - - "fmt" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" -) - -type EventIntervalRow struct { - Period string `csv:"Period"` - TotalEvents int64 `csv:"Total Events"` - ActiveEvents int64 `csv:"Active Events"` - InActiveEvents int64 `csv:"In-Active Events"` - FeaturedEvents int64 `csv:"Featured Events"` - Leagues int64 `csv:"Leagues"` - Pending int64 `csv:"Pending"` - InPlay int64 `csv:"In-Play"` - ToBeFixed int64 `csv:"To-Be-Fixed"` - Ended int64 `csv:"Ended"` - Postponed int64 `csv:"Postponed"` - Cancelled int64 `csv:"Cancelled"` - Walkover int64 `csv:"Walkover"` - Interrupted int64 `csv:"Interrupted"` - Abandoned int64 `csv:"Abandoned"` - Retired int64 `csv:"Retired"` - Suspended int64 `csv:"Suspended"` - DecidedByFA int64 `csv:"Decided-By-FA"` - Removed int64 `csv:"Removed"` -} - -func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) { - // Only a super-admin is allowed to generate this type of report - if request.RequesterRole.Valid && request.RequesterRole.Value != domain.RoleSuperAdmin { - s.mongoLogger.Error("[GenerateEventIntervalReport] Unauthorized user report") - return "", ErrUnauthorizedUserReport - } - - if request.Metadata.Interval == nil { - s.mongoLogger.Error("[GenerateEventIntervalReport] Metadata interval is empty") - return "", domain.ErrInvalidInterval - } - - interval, err := domain.ParseDateInterval(*request.Metadata.Interval) - if err != nil { - s.mongoLogger.Error("[GenerateEventIntervalReport] Failed to parse date interval", - zap.String("interval", *request.Metadata.Interval), - zap.Error(err), - ) - return "", domain.ErrInvalidInterval - } - - stats, err := s.statService.GetTotalEventStatsByInterval(ctx, domain.EventStatsByIntervalFilter{ - Interval: domain.ValidDateInterval{ - Value: interval, - Valid: true, - }, - }) - if err != nil { - s.mongoLogger.Error("[GenerateEventIntervalReport] Failed to fetch event stats", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("fetching event stats: %w", err) - } - - var rows [][]string - var headers []string - for _, stat := range stats { - endDate, err := domain.GetEndDateFromInterval(interval, stat.Date) - if err != nil { - s.mongoLogger.Error("[GenerateEventIntervalReport] Failed to get end date from interval", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("invalid interval end date: %w", err) - } - - period := fmt.Sprintf("%s to %s", - stat.Date.Format("2006-01-02"), - endDate.Format("2006-01-02"), - ) - - r := EventIntervalRow{ - Period: period, - TotalEvents: stat.EventCount, - ActiveEvents: stat.TotalActiveEvents, - InActiveEvents: stat.TotalInActiveEvents, - FeaturedEvents: stat.TotalFeaturedEvents, - Leagues: stat.TotalLeagues, - Pending: stat.Pending, - InPlay: stat.InPlay, - ToBeFixed: stat.ToBeFixed, - Ended: stat.Ended, - Postponed: stat.Postponed, - Cancelled: stat.Cancelled, - Walkover: stat.Walkover, - Interrupted: stat.Interrupted, - Abandoned: stat.Abandoned, - Retired: stat.Retired, - Suspended: stat.Suspended, - DecidedByFA: stat.DecidedByFa, - Removed: stat.Removed, - } - - if headers == nil { - headers, _ = StructToCSVRow(r) - rows = append(rows, headers) - } - - _, row := StructToCSVRow(r) - rows = append(rows, row) - } - - return s.WriteCSV(rows, "event_interval") -} diff --git a/internal/services/report/interface.go b/internal/services/report/interface.go deleted file mode 100644 index 4b7ee3a..0000000 --- a/internal/services/report/interface.go +++ /dev/null @@ -1,28 +0,0 @@ -package report - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -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) - // 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/notification.go b/internal/services/report/notification.go deleted file mode 100644 index a1cac51..0000000 --- a/internal/services/report/notification.go +++ /dev/null @@ -1,72 +0,0 @@ -package report - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -var ( - ErrInvalidRequestedByID = errors.New("requested_by needs to be filled in to send report notification") -) - -func (s *Service) SendReportRequestNotification(ctx context.Context, param domain.ReportRequestDetail) error { - if !param.RequestedBy.Valid { - return ErrInvalidRequestedByID - } - - var ( - headline string - message string - level domain.NotificationLevel - ) - - switch param.Status { - case domain.SuccessReportRequest: - headline = "Report Ready for Download" - message = fmt.Sprintf( - "Your %s report has been successfully generated and is now available for download.", - strings.ToLower(string(param.Type)), - ) - level = domain.NotificationLevelSuccess - - case domain.RejectReportRequest: - headline = "Report Generation Failed" - message = fmt.Sprintf( - "We were unable to generate your %s report. Please review your request and try again.", - strings.ToLower(string(param.Type)), - ) - level = domain.NotificationLevelError - - default: - return fmt.Errorf("unsupported request status: %v", param.Status) - } - - raw, _ := json.Marshal(map[string]any{ - "report_id": param.ID, - "type": param.Type, - "status": param.Status, - }) - - n := &domain.Notification{ - RecipientID: param.RequestedBy.Value, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Type: domain.NotificationTypeReportRequest, - Level: level, - Reciever: domain.NotificationRecieverSideCustomer, - DeliveryChannel: domain.DeliveryChannelInApp, - Payload: domain.NotificationPayload{ - Headline: headline, - Message: message, - }, - Priority: 2, - Metadata: raw, - } - - return s.notificationSvc.SendNotification(ctx, n) -} diff --git a/internal/services/report/process.go b/internal/services/report/process.go deleted file mode 100644 index eeb4be7..0000000 --- a/internal/services/report/process.go +++ /dev/null @@ -1,108 +0,0 @@ -package report - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" -) - - -var ( - ErrUnauthorizedUserReport = errors.New("unauthorized user report") -) - -func (s *Service) ProcessReportRequests(ctx context.Context) error { - requests, total, err := s.GetAllReportRequests(ctx, domain.ReportRequestFilter{ - Status: domain.ValidReportRequestStatus{ - Value: domain.PendingReportRequest, - Valid: true, - }, - }) - if err != nil { - s.mongoLogger.Error("failed to get pending report requests", zap.Error(err)) - return err - } - - for i, req := range requests { - if err := s.processSingleReportRequest(ctx, req); err != nil { - s.mongoLogger.Error("failed to process report request", - zap.Int64("id", req.ID), - zap.Int("index", i), - zap.Int64("total", total), - zap.String("type", string(req.Type)), - zap.Error(err), - ) - } - } - return nil -} - -func (s *Service) processSingleReportRequest(ctx context.Context, req domain.ReportRequestDetail) error { - var ( - filePath string - rejectReason string - status = domain.SuccessReportRequest - ) - - start := time.Now() - defer func() { - s.mongoLogger.Info("report request processed", - zap.Int64("id", req.ID), - zap.String("type", string(req.Type)), - zap.String("status", string(status)), - zap.Duration("duration", time.Since(start)), - ) - }() - - 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 = fmt.Sprintf("failed to generate report: %v", err) - } else { - filePath = fp - } - } - - update := domain.UpdateRequestRequest{ - ID: req.ID, - Status: domain.ValidReportRequestStatus{ - Value: status, - Valid: true, - }, - FilePath: domain.ValidString{ - Value: filePath, - Valid: filePath != "", - }, - RejectReason: domain.ValidString{ - Value: rejectReason, - Valid: rejectReason != "", - }, - } - - 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) - } - - // Prepare updated object for notification - updatedReq := req - updatedReq.FilePath = update.FilePath - updatedReq.Status = update.Status.Value - updatedReq.RejectReason = update.RejectReason - - if err := s.SendReportRequestNotification(ctx, updatedReq); err != nil { - s.mongoLogger.Warn("failed to send notification", zap.Int64("id", req.ID), zap.Error(err)) - } - - return nil -} diff --git a/internal/services/report/request.go b/internal/services/report/request.go deleted file mode 100644 index 89567ff..0000000 --- a/internal/services/report/request.go +++ /dev/null @@ -1,24 +0,0 @@ -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) -} -func (s *Service) GetAllReportRequests(ctx context.Context, filter domain.ReportRequestFilter) ([]domain.ReportRequestDetail, int64, error) { - return s.store.GetAllReportRequests(ctx, filter) -} -func (s *Service) GetReportRequestByRequestedByID(ctx context.Context, requestedBy int64, filter domain.ReportRequestFilter) ([]domain.ReportRequestDetail, error) { - return s.store.GetReportRequestByRequestedByID(ctx, requestedBy, filter) -} -func (s *Service) GetReportRequestByID(ctx context.Context, ID int64) (domain.ReportRequestDetail, error) { - return s.store.GetReportRequestByID(ctx, ID) -} - -func (s *Service) UpdateReportRequest(ctx context.Context, report domain.UpdateRequestRequest) error { - return s.store.UpdateReportRequest(ctx, report) -} diff --git a/internal/services/report/service.go b/internal/services/report/service.go deleted file mode 100644 index 54f22c6..0000000 --- a/internal/services/report/service.go +++ /dev/null @@ -1,791 +0,0 @@ -package report - -import ( - "context" - // "encoding/csv" - "errors" - // "fmt" - "log/slog" - // "os" - "sort" - "strconv" - "strings" - "time" - - "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/stats" - "go.uber.org/zap" - - notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" - // virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" -) - -var ( - ErrInvalidTimeRange = errors.New("invalid time range - start time must be before end time") - ErrInvalidReportCriteria = errors.New("invalid report criteria") -) - -type Service struct { - 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 - notificationSvc *notificationservice.Service - statService *stats.Service - logger *slog.Logger - mongoLogger *zap.Logger - cfg *config.Config - generators map[domain.ReportRequestType]ReportGeneratorFunc -} - -func NewService( - 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 ports.NotificationStore, - notificationSvc *notificationservice.Service, - statService *stats.Service, - logger *slog.Logger, - mongoLogger *zap.Logger, - cfg *config.Config, -) ReportService { - s := &Service{ - store: store, - betStore: betStore, - walletStore: walletStore, - transactionStore: transactionStore, - branchStore: branchStore, - userStore: userStore, - repo: repo, - companyStore: companyStore, - virtulaGamesStore: virtulaGamesStore, - notificationStore: notificationStore, - notificationSvc: notificationSvc, - 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.CompanyIntervalReportRequest: s.GenerateCompanyIntervalReport, - domain.BranchIntervalReportRequest: s.GenerateBranchIntervalReport, - domain.BetIntervalReportRequest: s.GenerateBetIntervalReport, - domain.WalletIntervalReportRequest: s.GenerateWalletIntervalReport, - } -} - -// GetDashboardSummary returns comprehensive dashboard metrics -func (s *Service) GetDashboardSummary(ctx context.Context, filter domain.ReportFilter) (domain.DashboardSummary, error) { - if err := validateTimeRange(filter); err != nil { - return domain.DashboardSummary{}, err - } - - var summary domain.DashboardSummary - var err error - - // Get bets summary - summary.TotalStakes, summary.TotalBets, summary.ActiveBets, summary.TotalWins, summary.TotalLosses, summary.WinBalance, err = - s.betStore.GetBetSummary(ctx, filter) - if err != nil { - s.logger.Error("failed to get bet summary", "error", err) - return domain.DashboardSummary{}, err - } - - // Get customer metrics - summary.CustomerCount, summary.ActiveCustomers, summary.InactiveCustomers, err = s.userStore.GetCustomerCounts(ctx, filter) - if err != nil { - s.logger.Error("failed to get customer counts", "error", err) - return domain.DashboardSummary{}, err - } - - // Get branch metrics - summary.BranchesCount, summary.ActiveBranches, summary.InactiveBranches, err = s.branchStore.GetBranchCounts(ctx, filter) - if err != nil { - s.logger.Error("failed to get branch counts", "error", err) - return domain.DashboardSummary{}, err - } - - // Get transaction metrics - summary.TotalDeposits, summary.TotalWithdrawals, err = s.transactionStore.GetTransactionTotals(ctx, filter) - if err != nil { - s.logger.Error("failed to get transaction totals", "error", err) - return domain.DashboardSummary{}, err - } - - // Get user role metrics - summary.TotalCashiers, summary.ActiveCashiers, summary.InactiveCashiers, err = s.userStore.GetRoleCounts(ctx, string(domain.RoleCashier), filter) - if err != nil { - s.logger.Error("failed to get cashier counts", "error", err) - return domain.DashboardSummary{}, err - } - - summary.TotalManagers, summary.ActiveManagers, summary.InactiveManagers, err = s.userStore.GetRoleCounts(ctx, string(domain.RoleBranchManager), filter) - if err != nil { - s.logger.Error("failed to get manager counts", "error", err) - return domain.DashboardSummary{}, err - } - - summary.TotalAdmins, summary.ActiveAdmins, summary.InactiveAdmins, err = s.userStore.GetRoleCounts(ctx, string(domain.RoleAdmin), filter) - if err != nil { - s.logger.Error("failed to get admin counts", "error", err) - return domain.DashboardSummary{}, err - } - - // Get wallet metrics - summary.TotalWallets, err = s.walletStore.GetTotalWallets(ctx, filter) - if err != nil { - s.logger.Error("failed to get wallet counts", "error", err) - return domain.DashboardSummary{}, err - } - - // Get sport/game metrics - // summary.TotalGames, summary.ActiveGames, summary.InactiveGames, err = s.virtulaGamesStore.GetGameCounts(ctx, filter) - // if err != nil { - // s.logger.Error("failed to get game counts", "error", err) - // return domain.DashboardSummary{}, err - // } - - // Get company metrics - summary.TotalCompanies, summary.ActiveCompanies, summary.InactiveCompanies, err = s.companyStore.GetCompanyCounts(ctx, filter) - if err != nil { - s.logger.Error("failed to get company counts", "error", err) - return domain.DashboardSummary{}, err - } - - // Get notification metrics - summary.TotalNotifications, summary.ReadNotifications, summary.UnreadNotifications, err = s.notificationStore.GetNotificationCounts(ctx, filter) - if err != nil { - s.logger.Error("failed to get notification counts", "error", err) - return domain.DashboardSummary{}, err - } - - // Calculate derived metrics - if summary.TotalBets > 0 { - summary.AverageStake = summary.TotalStakes / domain.Currency(summary.TotalBets) - summary.WinRate = float64(summary.TotalWins) / float64(summary.TotalBets) * 100 - summary.Profit = summary.TotalStakes - summary.WinBalance - } - - return summary, nil -} - -// Getdomain.BetAnalysis returns detailed bet analysis -func (s *Service) GetBetAnalysis(ctx context.Context, filter domain.ReportFilter) ([]domain.BetAnalysis, error) { - if err := validateTimeRange(filter); err != nil { - return nil, err - } - - // Get basic bet stats - betStats, err := s.betStore.GetBetStats(ctx, filter) - if err != nil { - s.logger.Error("failed to get bet stats", "error", err) - return nil, err - } - - // Get sport popularity - sportPopularity, err := s.betStore.GetSportPopularity(ctx, filter) - if err != nil { - s.logger.Error("failed to get sport popularity", "error", err) - return nil, err - } - - // Get market popularity - marketPopularity, err := s.betStore.GetMarketPopularity(ctx, filter) - if err != nil { - s.logger.Error("failed to get market popularity", "error", err) - return nil, err - } - - // Get extreme values - extremeValues, err := s.betStore.GetExtremeValues(ctx, filter) - if err != nil { - s.logger.Error("failed to get extreme values", "error", err) - return nil, err - } - - // Combine data into analysis - var analysis []domain.BetAnalysis - for _, stat := range betStats { - a := domain.BetAnalysis{ - Date: stat.Date, - TotalBets: stat.TotalBets, - TotalStakes: stat.TotalStakes, - TotalWins: stat.TotalWins, - TotalPayouts: stat.TotalPayouts, - Profit: stat.TotalStakes - stat.TotalPayouts, - AverageOdds: stat.AverageOdds, - } - - // Add sport popularity - if sport, ok := sportPopularity[stat.Date]; ok { - a.MostPopularSport = sport - } - - // Add market popularity - if market, ok := marketPopularity[stat.Date]; ok { - a.MostPopularMarket = market - } - - // Add extreme values - if extremes, ok := extremeValues[stat.Date]; ok { - a.HighestStake = extremes.HighestStake - a.HighestPayout = extremes.HighestPayout - } - - analysis = append(analysis, a) - } - - // Sort by date - sort.Slice(analysis, func(i, j int) bool { - return analysis[i].Date.Before(analysis[j].Date) - }) - - return analysis, nil -} - -// Getdomain.CustomerActivity returns customer activity report -func (s *Service) GetCustomerActivity(ctx context.Context, filter domain.ReportFilter) ([]domain.CustomerActivity, error) { - if err := validateTimeRange(filter); err != nil { - return nil, err - } - - // Get customer bet activity - customerBets, err := s.betStore.GetCustomerBetActivity(ctx, filter) - if err != nil { - s.logger.Error("failed to get customer bet activity", "error", err) - return nil, err - } - - // Get customer details - customerDetails, err := s.userStore.GetCustomerDetails(ctx, filter) - if err != nil { - s.logger.Error("failed to get customer details", "error", err) - return nil, err - } - - // Get customer preferences - customerPrefs, err := s.betStore.GetCustomerPreferences(ctx, filter) - if err != nil { - s.logger.Error("failed to get customer preferences", "error", err) - return nil, err - } - - // Combine data into activity report - var activities []domain.CustomerActivity - for _, bet := range customerBets { - activity := domain.CustomerActivity{ - CustomerID: bet.CustomerID, - TotalBets: bet.TotalBets, - TotalStakes: bet.TotalStakes, - TotalWins: bet.TotalWins, - TotalPayouts: bet.TotalPayouts, - Profit: bet.TotalStakes - bet.TotalPayouts, - FirstBetDate: bet.FirstBetDate, - LastBetDate: bet.LastBetDate, - AverageStake: bet.TotalStakes / domain.Currency(bet.TotalBets), - AverageOdds: bet.AverageOdds, - } - - // Add customer details - if details, ok := customerDetails[bet.CustomerID]; ok { - activity.CustomerName = details.Name - } - - // Add preferences - if prefs, ok := customerPrefs[bet.CustomerID]; ok { - activity.FavoriteSport = prefs.FavoriteSport - activity.FavoriteMarket = prefs.FavoriteMarket - } - - // Calculate win rate - if bet.TotalBets > 0 { - activity.WinRate = float64(bet.TotalWins) / float64(bet.TotalBets) * 100 - } - - // Determine activity level - activity.ActivityLevel = calculateActivityLevel(bet.TotalBets, bet.TotalStakes) - - activities = append(activities, activity) - } - - // Sort by total stakes (descending) - sort.Slice(activities, func(i, j int) bool { - return activities[i].TotalStakes > activities[j].TotalStakes - }) - - return activities, nil -} - -// Getdomain.BranchPerformance returns branch performance report -func (s *Service) GetBranchPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.BranchPerformance, error) { - // Get branch bet activity - branchBets, err := s.betStore.GetBranchBetActivity(ctx, filter) - if err != nil { - s.logger.Error("failed to get branch bet activity", "error", err) - return nil, err - } - - // Get branch details - branchDetails, err := s.branchStore.GetBranchDetails(ctx, filter) - if err != nil { - s.logger.Error("failed to get branch details", "error", err) - return nil, err - } - - // Get branch transactions - branchTransactions, err := s.transactionStore.GetBranchTransactionTotals(ctx, filter) - if err != nil { - s.logger.Error("failed to get branch transactions", "error", err) - return nil, err - } - - // Get branch customer counts - branchCustomers, err := s.userStore.GetBranchCustomerCounts(ctx, filter) - if err != nil { - s.logger.Error("failed to get branch customer counts", "error", err) - return nil, err - } - - // Combine data into performance report - var performances []domain.BranchPerformance - for _, bet := range branchBets { - performance := domain.BranchPerformance{ - BranchID: bet.BranchID, - TotalBets: bet.TotalBets, - TotalStakes: bet.TotalStakes, - TotalWins: bet.TotalWins, - TotalPayouts: bet.TotalPayouts, - Profit: bet.TotalStakes - bet.TotalPayouts, - } - - // Add branch details - if details, ok := branchDetails[bet.BranchID]; ok { - performance.BranchName = details.Name - performance.Location = details.Location - performance.ManagerName = details.ManagerName - } - - // Add transactions - if transactions, ok := branchTransactions[bet.BranchID]; ok { - performance.Deposits = transactions.Deposits - performance.Withdrawals = transactions.Withdrawals - } - - // Add customer counts - if customers, ok := branchCustomers[bet.BranchID]; ok { - performance.CustomerCount = customers - } - - // Calculate metrics - if bet.TotalBets > 0 { - performance.WinRate = float64(bet.TotalWins) / float64(bet.TotalBets) * 100 - performance.AverageStake = bet.TotalStakes / domain.Currency(bet.TotalBets) - } - - // Calculate performance score - performance.PerformanceScore = calculatePerformanceScore(performance) - - performances = append(performances, performance) - } - - // Sort by performance score (descending) - sort.Slice(performances, func(i, j int) bool { - return performances[i].PerformanceScore > performances[j].PerformanceScore - }) - - return performances, nil -} - -// Getdomain.SportPerformance returns sport performance report -func (s *Service) GetSportPerformance(ctx context.Context, filter domain.ReportFilter) ([]domain.SportPerformance, error) { - // Get sport bet activity - sportBets, err := s.betStore.GetSportBetActivity(ctx, filter) - if err != nil { - s.logger.Error("failed to get sport bet activity", "error", err) - return nil, err - } - - // Get sport details (names) - sportDetails, err := s.betStore.GetSportDetails(ctx, filter) - if err != nil { - s.logger.Error("failed to get sport details", "error", err) - return nil, err - } - - // Get sport market popularity - sportMarkets, err := s.betStore.GetSportMarketPopularity(ctx, filter) - if err != nil { - s.logger.Error("failed to get sport market popularity", "error", err) - return nil, err - } - - // Combine data into performance report - var performances []domain.SportPerformance - for _, bet := range sportBets { - performance := domain.SportPerformance{ - SportID: bet.SportID, - TotalBets: bet.TotalBets, - TotalStakes: bet.TotalStakes, - TotalWins: bet.TotalWins, - TotalPayouts: bet.TotalPayouts, - Profit: bet.TotalStakes - bet.TotalPayouts, - AverageOdds: bet.AverageOdds, - } - - // Add sport details - if details, ok := sportDetails[bet.SportID]; ok { - performance.SportName = details - } - - // Add market popularity - if market, ok := sportMarkets[bet.SportID]; ok { - performance.MostPopularMarket = market - } - - // Calculate metrics - if bet.TotalBets > 0 { - performance.WinRate = float64(bet.TotalWins) / float64(bet.TotalBets) * 100 - performance.AverageStake = bet.TotalStakes / domain.Currency(bet.TotalBets) - } - - performances = append(performances, performance) - } - - // Sort by total stakes (descending) and assign popularity rank - sort.Slice(performances, func(i, j int) bool { - return performances[i].TotalStakes > performances[j].TotalStakes - }) - - for i := range performances { - performances[i].PopularityRank = i + 1 - } - - return performances, nil -} - -// func (s *service) GenerateReport(ctx context.Context, from, to time.Time) error { -// // Hardcoded output directory -// outputDir := "reports" - -// // Ensure directory exists -// if err := os.MkdirAll(outputDir, os.ModePerm); err != nil { -// return fmt.Errorf("failed to create report directory: %w", err) -// } - -// companies, branchMap, err := s.fetchReportData(ctx, from, to) -// if err != nil { -// return err -// } - -// // per-company reports -// for _, company := range companies { -// branches := branchMap[company.CompanyID] -// if err := writeCompanyCSV(company, branches, from, to, outputDir); err != nil { -// return fmt.Errorf("company %d CSV: %w", company.CompanyID, err) -// } -// } - -// // summary report -// if err := writeSummaryCSV(companies, from, to, outputDir); err != nil { -// return fmt.Errorf("summary CSV: %w", err) -// } - -// return nil -// } - -// // writeCompanyCSV writes the company report to CSV in the hardcoded folder -// func writeCompanyCSV(company domain.CompanyReport, branches []domain.BranchReport, from, to time.Time, outputDir string) error { -// period := fmt.Sprintf("%s to %s", from.Format("2006-01-02"), to.Format("2006-01-02")) - -// filePath := fmt.Sprintf("%s/company_%d_%s_%s_%s.csv", -// outputDir, -// company.CompanyID, -// from.Format("2006-01-02"), -// to.Format("2006-01-02"), -// time.Now().Format("2006-01-02_15-04"), -// ) - -// file, err := os.Create(filePath) -// if err != nil { -// return fmt.Errorf("create company csv: %w", err) -// } -// defer file.Close() - -// writer := csv.NewWriter(file) -// defer writer.Flush() - -// // Company summary section -// writer.Write([]string{"Company Betting Report"}) -// writer.Write([]string{"Period", "Company ID", "Company Name", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"}) -// writer.Write([]string{ -// period, -// fmt.Sprintf("%d", company.CompanyID), -// company.CompanyName, -// fmt.Sprintf("%d", company.TotalBets), -// fmt.Sprintf("%.2f", company.TotalCashIn), -// fmt.Sprintf("%.2f", company.TotalCashOut), -// fmt.Sprintf("%.2f", company.TotalCashBacks), -// }) -// writer.Write([]string{}) // Empty line - -// // Branch reports -// writer.Write([]string{"Branch Reports"}) -// writer.Write([]string{"Branch ID", "Branch Name", "Company ID", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"}) -// for _, br := range branches { -// writer.Write([]string{ -// fmt.Sprintf("%d", br.BranchID), -// br.BranchName, -// fmt.Sprintf("%d", br.CompanyID), -// fmt.Sprintf("%d", br.TotalBets), -// fmt.Sprintf("%.2f", br.TotalCashIn), -// fmt.Sprintf("%.2f", br.TotalCashOut), -// fmt.Sprintf("%.2f", br.TotalCashBacks), -// }) -// } - -// if err := writer.Error(); err != nil { -// return fmt.Errorf("flush error: %w", err) -// } - -// return nil -// } - -// writeSummaryCSV writes the summary report to CSV in the hardcoded folder -// func writeSummaryCSV(companies []domain.CompanyReport, from, to time.Time, outputDir string) error { -// period := fmt.Sprintf("%s to %s", from.Format("2006-01-02"), to.Format("2006-01-02")) - -// filePath := fmt.Sprintf("%s/summary_%s_%s_%s.csv", -// outputDir, -// from.Format("2006-01-02"), -// to.Format("2006-01-02"), -// time.Now().Format("2006-01-02_15-04"), -// ) - -// file, err := os.Create(filePath) -// if err != nil { -// return fmt.Errorf("create summary csv: %w", err) -// } -// defer file.Close() - -// writer := csv.NewWriter(file) -// defer writer.Flush() - -// // Global summary -// writer.Write([]string{"Global Betting Summary"}) -// writer.Write([]string{"Period", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"}) - -// var totalBets int64 -// var totalIn, totalOut, totalBack float64 -// for _, c := range companies { -// totalBets += c.TotalBets -// totalIn += c.TotalCashIn -// totalOut += c.TotalCashOut -// totalBack += c.TotalCashBacks -// } - -// writer.Write([]string{ -// period, -// fmt.Sprintf("%d", totalBets), -// fmt.Sprintf("%.2f", totalIn), -// fmt.Sprintf("%.2f", totalOut), -// fmt.Sprintf("%.2f", totalBack), -// }) -// writer.Write([]string{}) // Empty line - -// // Company breakdown -// writer.Write([]string{"Company Reports"}) -// writer.Write([]string{"Company ID", "Company Name", "Total Bets", "Total Cash In", "Total Cash Out", "Total Cash Backs"}) -// for _, cr := range companies { -// writer.Write([]string{ -// fmt.Sprintf("%d", cr.CompanyID), -// cr.CompanyName, -// fmt.Sprintf("%d", cr.TotalBets), -// fmt.Sprintf("%.2f", cr.TotalCashIn), -// fmt.Sprintf("%.2f", cr.TotalCashOut), -// fmt.Sprintf("%.2f", cr.TotalCashBacks), -// }) -// } - -// if err := writer.Error(); err != nil { -// return fmt.Errorf("flush error: %w", err) -// } - -// return nil -// } - -// func (s *service) fetchReportData(ctx context.Context, from, to time.Time) ( -// []domain.CompanyReport, map[int64][]domain.BranchReport, error, -// ) { -// // --- company level --- -// companyRows, err := s.repo.GetCompanyWiseReport(ctx, from, to) -// if err != nil { -// return nil, nil, fmt.Errorf("company-wise report: %w", err) -// } - -// companies := make([]domain.CompanyReport, 0, len(companyRows)) -// for _, row := range companyRows { -// companies = append(companies, domain.CompanyReport{ -// CompanyID: row.CompanyID, -// CompanyName: row.CompanyName, -// TotalBets: row.TotalBets, -// TotalCashIn: toFloat(row.TotalCashMade), -// TotalCashOut: toFloat(row.TotalCashOut), -// TotalCashBacks: toFloat(row.TotalCashBacks), -// }) -// } - -// // --- branch level --- -// branchRows, err := s.repo.GetBranchWiseReport(ctx, from, to) -// if err != nil { -// return nil, nil, fmt.Errorf("branch-wise report: %w", err) -// } - -// branchMap := make(map[int64][]domain.BranchReport) -// for _, row := range branchRows { -// branch := domain.BranchReport{ -// BranchID: row.BranchID, -// BranchName: row.BranchName, -// CompanyID: row.CompanyID, -// TotalBets: row.TotalBets, -// TotalCashIn: toFloat(row.TotalCashMade), -// TotalCashOut: toFloat(row.TotalCashOut), -// TotalCashBacks: toFloat(row.TotalCashBacks), -// } -// branchMap[row.CompanyID] = append(branchMap[row.CompanyID], branch) -// } - -// return companies, branchMap, nil -// } - -// helper to unify float conversions -func toFloat(val interface{}) float64 { - switch v := val.(type) { - case string: - if f, err := strconv.ParseFloat(v, 64); err == nil { - return f - } - case float64: - return v - case int: - return float64(v) - case int64: - return float64(v) - } - return 0 -} - -func GetTimeRange(period string) (time.Time, time.Time) { - now := time.Now() - switch strings.ToLower(period) { - case "daily": - start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) - end := start.Add(5 * time.Minute) - return start, end - case "weekly": - weekday := int(now.Weekday()) - if weekday == 0 { - weekday = 7 - } - start := now.AddDate(0, 0, -weekday+1) - start = time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, now.Location()) - end := start.AddDate(0, 0, 7) - return start, end - case "monthly": - start := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location()) - end := start.AddDate(0, 1, 0) - return start, end - default: - // Default to daily - start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) - end := start.Add(24 * time.Hour) - return start, end - } -} - -// Helper functions -func validateTimeRange(filter domain.ReportFilter) error { - if filter.StartTime.Valid && filter.EndTime.Valid { - if filter.StartTime.Value.After(filter.EndTime.Value) { - return ErrInvalidTimeRange - } - } - return nil -} - -func calculateActivityLevel(totalBets int64, totalStakes domain.Currency) string { - switch { - case totalBets > 100 || totalStakes > 10000: - return "High" - case totalBets > 50 || totalStakes > 5000: - return "Medium" - default: - return "Low" - } -} - -func calculatePerformanceScore(perf domain.BranchPerformance) float64 { - // Simple scoring algorithm - can be enhanced based on business rules - profitScore := float64(perf.Profit) / 1000 - customerScore := float64(perf.CustomerCount) * 0.1 - betScore := float64(perf.TotalBets) * 0.01 - winRateScore := perf.WinRate * 0.1 - - return profitScore + customerScore + betScore + winRateScore -} - -// toCompanyReport converts grouped data into []CompanyReport -// func toCompanyReport(grouped map[int64]map[int64][]interface{}) []domain.CompanyReport { -// companyReports := []domain.CompanyReport{} -// for companyID, branches := range grouped { -// companyReport := domain.CompanyReport{ -// CompanyID: companyID, -// Branches: []domain.BranchReport{}, -// } -// for branchID, rows := range branches { -// branchReport := domain.BranchReport{ -// BranchID: branchID, -// Rows: rows, -// } -// companyReport.Branches = append(companyReport.Branches, branchReport) -// } -// companyReports = append(companyReports, companyReport) -// } -// return companyReports -// } - -// // toBranchReport converts []interface{} to []BranchReport -// func toBranchReport(rows []interface{}, branchID int64) domain.BranchReport { -// return domain.BranchReport{ -// BranchID: branchID, -// Rows: rows, -// } -// } diff --git a/internal/services/report/wallet.go b/internal/services/report/wallet.go deleted file mode 100644 index eef4267..0000000 --- a/internal/services/report/wallet.go +++ /dev/null @@ -1,107 +0,0 @@ -package report - -import ( - "context" - - "fmt" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" -) - -type WalletIntervalRow struct { - Period string `csv:"Period"` - WalletUserFirstName string `csv:"User First Name"` - WalletUserLastName string `csv:"User Last Name"` - WalletID int64 `csv:"Wallet Id"` - WalletType string `csv:"Wallet Type"` - NumberOfTransactions int64 `csv:"Number Of Transactions"` - TotalTransactions float32 `csv:"Total Transactions"` - NumberOfDeposits int64 `csv:"Number Of Deposits"` - TotalDepositsAmount float32 `csv:"Total Deposits Amount"` - NumberOfWithdraws int64 `csv:"Number Of Withdraws"` - TotalWithdrawsAmount float32 `csv:"Total Withdraws Amount"` - NumberOfTransfers int64 `csv:"Number Of Transfers"` - TotalTransfersAmount float32 `csv:"Total Transfers Amount"` -} - -func (s *Service) GenerateWalletIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) { - - // Only a super-admin is allowed to generate this type of report - if request.RequesterRole.Valid && request.RequesterRole.Value != domain.RoleSuperAdmin { - s.mongoLogger.Error("[GenerateWalletIntervalReport] Unauthorized user report") - return "", ErrUnauthorizedUserReport - } - - if request.Metadata.Interval == nil { - s.mongoLogger.Error("[GenerateWalletIntervalReport] Metadata interval is empty") - return "", domain.ErrInvalidInterval - } - - interval, err := domain.ParseDateInterval(*request.Metadata.Interval) - if err != nil { - s.mongoLogger.Error("[GenerateWalletIntervalReport] Failed to parse date interval", - zap.String("interval", *request.Metadata.Interval), - zap.Error(err), - ) - return "", domain.ErrInvalidInterval - } - - stats, err := s.statService.GetWalletStatsByInterval(ctx, domain.WalletStatFilter{ - Interval: domain.ValidDateInterval{ - Value: interval, - Valid: true, - }, - }) - if err != nil { - s.mongoLogger.Error("[GenerateWalletIntervalReport] Failed to fetch wallet stats", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("fetching wallet stats: %w", err) - } - - var rows [][]string - var headers []string - for _, stat := range stats { - endDate, err := domain.GetEndDateFromInterval(interval, stat.IntervalStart) - if err != nil { - s.mongoLogger.Error("[GenerateWalletIntervalReport] Failed to get end date from interval", - zap.String("interval", string(interval)), - zap.Error(err), - ) - return "", fmt.Errorf("invalid interval end date: %w", err) - } - - period := fmt.Sprintf("%s to %s", - stat.IntervalStart.Format("2006-01-02"), - endDate.Format("2006-01-02"), - ) - - r := WalletIntervalRow{ - Period: period, - WalletUserFirstName: stat.WalletUserFirstName, - WalletUserLastName: stat.WalletUserLastName, - WalletID: stat.WalletID, - WalletType: stat.WalletType, - NumberOfTransactions: stat.NumberOfTransactions, - TotalTransactions: stat.TotalTransactions.Float32(), - NumberOfDeposits: stat.NumberOfDeposits, - TotalDepositsAmount: stat.TotalDepositsAmount.Float32(), - NumberOfWithdraws: stat.NumberOfWithdraws, - TotalWithdrawsAmount: stat.TotalWithdrawsAmount.Float32(), - NumberOfTransfers: stat.NumberOfTransfers, - TotalTransfersAmount: stat.TotalTransfersAmount.Float32(), - } - - if headers == nil { - headers, _ = StructToCSVRow(r) - rows = append(rows, headers) - } - - _, row := StructToCSVRow(r) - rows = append(rows, row) - } - - return s.WriteCSV(rows, "wallet_interval") -} diff --git a/internal/services/result/eval.go b/internal/services/result/eval.go deleted file mode 100644 index a2f0ffb..0000000 --- a/internal/services/result/eval.go +++ /dev/null @@ -1,1619 +0,0 @@ -package result - -import ( - "fmt" - "strconv" - "strings" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -// Football evaluations - -// helper function to parse minute from event string like "77'" or "90+1'" -func parseEventMinute(eventText string) (int, error) { - parts := strings.Split(eventText, "'") - if len(parts) == 0 { - return 0, fmt.Errorf("invalid event text format") - } - timeStr := strings.TrimSpace(parts[0]) - if strings.Contains(timeStr, "+") { - timeParts := strings.Split(timeStr, "+") - base, err1 := strconv.Atoi(timeParts[0]) - extra, err2 := strconv.Atoi(timeParts[1]) - if err1 != nil || err2 != nil { - return 0, fmt.Errorf("invalid injury time format") - } - return base + extra, nil - } - return strconv.Atoi(timeStr) -} - -// Full Time Result betting is a type of bet where the bettor predicts the outcome of a match at the end of the full 90 minutes of play. -func evaluateFullTimeResult(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddName { - case "1": // Home win - if score.Home > score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Draw": - if score.Home == score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": // Away win - if score.Away > score.Home { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName) - } -} - -// Over/Under betting is a type of bet where the bettor predicts whether the total number of goals scored in a match will be over or under a specified number. -func evaluateGoalsOverUnder(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - totalGoals := float64(score.Home + score.Away) - threshold, err := strconv.ParseFloat(outcome.OddName, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - - switch outcome.OddHeader { - case "Over": - if totalGoals > threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if totalGoals == threshold { - return domain.OUTCOME_STATUS_VOID, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Under": - if totalGoals < threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if totalGoals == threshold { - return domain.OUTCOME_STATUS_VOID, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) -} - -// Correct Score betting is a type of bet where the bettor predicts the exact final score of a match. -func evaluateCorrectScore(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - expectedScore := fmt.Sprintf("%d-%d", score.Home, score.Away) - if outcome.OddName == expectedScore { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// Half Time Result betting is a type of bet where the bettor predicts the outcome of a match at the end of the first half. -// This is the same as the full time result but only for the first half of the game -func evaluateHalfTimeResult(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - return evaluateFullTimeResult(outcome, score) -} - -// This is a multiple outcome checker for the asian handicap and other kinds of bets -// The only outcome that are allowed are "Both Bets win", "Both Bets Lose", "Half Win and Half Void" -func checkMultiOutcome(outcome domain.OutcomeStatus, secondOutcome domain.OutcomeStatus) (domain.OutcomeStatus, error) { - if secondOutcome == domain.OUTCOME_STATUS_PENDING { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("cannot check pending outcome") - } - - if outcome == domain.OUTCOME_STATUS_ERROR || secondOutcome == domain.OUTCOME_STATUS_ERROR { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("❌ mutli outcome: %v -> %v \n", outcome.String(), secondOutcome.String()) - } - - // fmt.Printf("| Multi Outcome | %v -> %v \n", outcome.String(), secondOutcome.String()) - - switch outcome { - case domain.OUTCOME_STATUS_PENDING: - return secondOutcome, nil - case domain.OUTCOME_STATUS_WIN: - switch secondOutcome { - case domain.OUTCOME_STATUS_WIN: - return domain.OUTCOME_STATUS_WIN, nil - case domain.OUTCOME_STATUS_LOSS: - return domain.OUTCOME_STATUS_LOSS, nil - case domain.OUTCOME_STATUS_HALF: - return domain.OUTCOME_STATUS_VOID, nil - case domain.OUTCOME_STATUS_VOID: - return domain.OUTCOME_STATUS_HALF, nil - default: - fmt.Printf("❌ multi outcome: %v -> %v \n", outcome.String(), secondOutcome.String()) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid multi outcome") - } - case domain.OUTCOME_STATUS_LOSS: - switch secondOutcome { - case domain.OUTCOME_STATUS_LOSS, domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_HALF: - return domain.OUTCOME_STATUS_LOSS, nil - case domain.OUTCOME_STATUS_VOID: - return domain.OUTCOME_STATUS_VOID, nil - default: - fmt.Printf("❌ multi outcome: %v -> %v \n", outcome.String(), secondOutcome.String()) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid multi outcome") - } - case domain.OUTCOME_STATUS_VOID: - switch secondOutcome { - case domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_LOSS: - return domain.OUTCOME_STATUS_VOID, nil - case domain.OUTCOME_STATUS_VOID, domain.OUTCOME_STATUS_HALF: - return domain.OUTCOME_STATUS_VOID, nil - default: - fmt.Printf("❌ multi outcome: %v -> %v \n", outcome.String(), secondOutcome.String()) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid multi outcome") - } - case domain.OUTCOME_STATUS_HALF: - switch secondOutcome { - case domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_HALF: - return domain.OUTCOME_STATUS_VOID, nil - case domain.OUTCOME_STATUS_LOSS: - return domain.OUTCOME_STATUS_LOSS, nil - case domain.OUTCOME_STATUS_VOID: - return domain.OUTCOME_STATUS_VOID, nil - default: - fmt.Printf("❌ multi outcome: %v -> %v \n", outcome.String(), secondOutcome.String()) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid multi outcome") - } - default: - fmt.Printf("❌ multi outcome: %v -> %v \n", outcome.String(), secondOutcome.String()) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid multi outcome") - } -} - -// Asian Handicap betting is a type of betting that eliminates the possibility of a draw by giving one team a virtual advantage or disadvantage. -// When the handicap has two values like "+0.5, +1.0" or "-0.5, -1.0", then it a multi outcome bet -// . -// -// { -// "id": "548319135", -// "odds": "1.750", -// "header": "1", -// "handicap": "+0.5, +1.0" -// }, -// -// { -// "id": "548319139", -// "odds": "1.950", -// "header": "2", -// "handicap": "-0.5, -1.0" -// } -func evaluateAsianHandicap(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - handicapList := strings.Split(outcome.OddHandicap, ",") - newOutcome := domain.OUTCOME_STATUS_PENDING - for _, handicapStr := range handicapList { - handicapStr = strings.TrimSpace(handicapStr) - handicap, err := strconv.ParseFloat(handicapStr, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap: %s", outcome.OddHandicap) - } - adjustedHomeScore := float64(score.Home) - adjustedAwayScore := float64(score.Away) - switch outcome.OddHeader { - case "1": // Home team - adjustedHomeScore += handicap - case "2": // Away team - adjustedAwayScore += handicap - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } - - if adjustedHomeScore > adjustedAwayScore { - if outcome.OddHeader == "1" { - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_WIN) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, err - } - } else { - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS) - if err != nil { - fmt.Printf("multi outcome check error") - return domain.OUTCOME_STATUS_ERROR, err - } - } - } else if adjustedHomeScore < adjustedAwayScore { - if outcome.OddHeader == "2" { - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_WIN) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, err - } - } else { - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS) - if err != nil { - fmt.Printf("multi outcome check error") - return domain.OUTCOME_STATUS_ERROR, err - } - } - } - if newOutcome == domain.OUTCOME_STATUS_PENDING { - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_VOID) - if err != nil { - fmt.Printf("multi outcome check error") - return domain.OUTCOME_STATUS_ERROR, err - } - } - } - return newOutcome, nil -} - -// Goal Line betting, also known as Over/Under betting, -// involves predicting the total number of goals scored in a match, regardless of which team wins. -// -// { -// "id": "548319141", -// "odds": "1.800", -// "header": "Over", -// "name": "1.5, 2.0" -// }, -// -// { -// "id": "548319146", -// "odds": "1.900", -// "header": "Under", -// "name": "1.5, 2.0" -// } -func evaluateGoalLine(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - - totalGoals := float64(score.Home + score.Away) - thresholdList := strings.Split(outcome.OddName, ",") - - newOutcome := domain.OUTCOME_STATUS_PENDING - for _, thresholdStr := range thresholdList { - thresholdStr = strings.TrimSpace(thresholdStr) - threshold, err := strconv.ParseFloat(thresholdStr, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: '%s', %v", thresholdStr, err) - } - - oddHeader := strings.TrimSpace(outcome.OddHeader) - switch oddHeader { - case "Over": - if totalGoals > threshold { - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_WIN) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, err - } - } else if totalGoals == threshold { - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_VOID) - if err != nil { - - return domain.OUTCOME_STATUS_ERROR, err - } - } - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, err - } - case "Under": - if totalGoals < threshold { - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_WIN) - if err != nil { - - return domain.OUTCOME_STATUS_ERROR, err - } - } else if totalGoals == threshold { - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_VOID) - if err != nil { - - return domain.OUTCOME_STATUS_ERROR, err - } - } - newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, err - } - - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: '%s'", oddHeader) - } - - } - - return newOutcome, nil -} - -// First Team To Score betting is a type of bet where the bettor predicts which team will score first in a match. -// We can get this from the "events" field on the result json -func evaluateFirstTeamToScore(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) { - for _, event := range events { - if strings.Contains(event["text"], "1st Goal") || strings.Contains(event["text"], "Goal 1") { - if strings.Contains(event["text"], outcome.HomeTeamName) && outcome.OddName == "1" { - return domain.OUTCOME_STATUS_WIN, nil - } else if strings.Contains(event["text"], outcome.AwayTeamName) && outcome.OddName == "2" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - } - return domain.OUTCOME_STATUS_VOID, nil // No goals scored -} - -// Goals Odd/Even betting is a type of bet where the bettor predicts whether the total number of goals scored in a match will be odd or even. -func evaluateGoalsOddEven(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - totalGoals := score.Home + score.Away - isOdd := totalGoals%2 == 1 - if outcome.OddName == "Odd" && isOdd { - return domain.OUTCOME_STATUS_WIN, nil - } else if outcome.OddName == "Even" && !isOdd { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -func evaluateTeamOddEven(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - - switch outcome.OddHeader { - case "1": - switch outcome.OddHandicap { - case "Odd": - if score.Home%2 == 1 { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Even": - if score.Home%2 == 0 { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd handicap: %s", outcome.OddHandicap) - } - case "2": - switch outcome.OddHandicap { - case "Odd": - if score.Away%2 == 1 { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Even": - if score.Away%2 == 0 { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd handicap: %s", outcome.OddHandicap) - } - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - - } -} - -// Double Chance betting is a type of bet where the bettor predicts two of the three possible outcomes of a match. -func evaluateDoubleChance(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - isHomeWin := score.Home > score.Away - isDraw := score.Home == score.Away - isAwayWin := score.Away > score.Home - switch outcome.OddName { - case "1 or Draw", (outcome.HomeTeamName + " or " + "Draw"), ("Draw" + " or " + outcome.HomeTeamName): - if isHomeWin || isDraw { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Draw or 2", ("Draw" + " or " + outcome.AwayTeamName), (outcome.AwayTeamName + " or " + "Draw"): - if isDraw || isAwayWin { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "1 or 2", (outcome.HomeTeamName + " or " + outcome.AwayTeamName), (outcome.AwayTeamName + " or " + outcome.HomeTeamName): - if isHomeWin || isAwayWin { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName) - } -} - -// Draw No Bet betting is a type of bet where the bettor predicts the outcome of a match, but if the match ends in a draw, the bet is voided. -func evaluateDrawNoBet(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - if score.Home == score.Away { - return domain.OUTCOME_STATUS_VOID, nil - } - if outcome.OddName == "1" && score.Home > score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } else if outcome.OddName == "2" && score.Away > score.Home { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -func evaluateCorners(outcome domain.BetOutcome, corners struct{ Home, Away int }) (domain.OutcomeStatus, error) { - - totalCorners := corners.Home + corners.Away - threshold, err := strconv.ParseFloat(outcome.OddName, 10) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - switch outcome.OddHeader { - case "Over": - if totalCorners > int(threshold) { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Under": - if totalCorners < int(threshold) { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Exactly": - if totalCorners == int(threshold) { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } -} - - -// evaluateBothTeamsToScore checks if both teams scored in the match. -func evaluateBothTeamsToScore(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - btts := score.Home > 0 && score.Away > 0 - switch outcome.OddName { - case "Yes": - if btts { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "No": - if !btts { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for BTTS: %s", outcome.OddName) - } -} - -// evaluateResultAndBTTS checks for a combination of the match result and if both teams scored. -func evaluateResultAndBTTS(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - btts := score.Home > 0 && score.Away > 0 - var bttsMatch bool - switch outcome.OddHeader { - case "Yes": - bttsMatch = btts - case "No": - bttsMatch = !btts - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Result/BTTS: %s", outcome.OddHeader) - } - - var resultMatch bool - switch outcome.OddName { - case "1": - resultMatch = score.Home > score.Away - case "2": - resultMatch = score.Away > score.Home - case "Draw": - resultMatch = score.Home == score.Away - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Result/BTTS: %s", outcome.OddName) - } - - if bttsMatch && resultMatch { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateCleanSheet checks if a selected team did not concede any goals. -func evaluateCleanSheet(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - var cleanSheet bool - switch outcome.OddHeader { - case "1": // Corresponds to Home team - cleanSheet = (score.Away == 0) - case "2": // Corresponds to Away team - cleanSheet = (score.Home == 0) - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Clean Sheet: %s", outcome.OddHeader) - } - - betOnYes := outcome.OddHandicap == "Yes" - - if cleanSheet == betOnYes { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateLastTeamToScore finds the last team that scored by checking events in reverse. -func evaluateLastTeamToScore(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) { - var lastGoalTeam string - for i := len(events) - 1; i >= 0; i-- { - event := events[i] - // A simple check for "Goal" in the event text - if strings.Contains(event["text"], "Goal") && !strings.Contains(event["text"], "disallowed") { - if strings.Contains(event["text"], outcome.HomeTeamName) { - lastGoalTeam = "1" - break - } else if strings.Contains(event["text"], outcome.AwayTeamName) { - lastGoalTeam = "2" - break - } - } - } - - switch outcome.OddName { - case "1": - if lastGoalTeam == "1" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": - if lastGoalTeam == "2" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "No Goal", "No Goals": - if lastGoalTeam == "" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Last Team to Score: %s", outcome.OddName) - } -} - -// evaluateWinningMargin checks the margin of victory. -func evaluateFootballWinningMargin(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - homeWin := score.Home > score.Away - awayWin := score.Away > score.Home - margin := score.Home - score.Away - - switch outcome.OddName { - case "Score Draw": - if margin == 0 && score.Home > 0 { - return domain.OUTCOME_STATUS_WIN, nil - } - case "No Goal": - if margin == 0 && score.Home == 0 { - return domain.OUTCOME_STATUS_WIN, nil - } - default: - // Handles margins like "1", "2", "3", "4+" - var expectedMargin int - isPlus := strings.HasSuffix(outcome.OddName, "+") - marginStr := strings.TrimSuffix(outcome.OddName, "+") - - _, err := fmt.Sscanf(marginStr, "%d", &expectedMargin) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("could not parse winning margin: %s", outcome.OddName) - } - - teamWon := (outcome.OddHeader == "1" && homeWin) || (outcome.OddHeader == "2" && awayWin) - if !teamWon { - return domain.OUTCOME_STATUS_LOSS, nil - } - - actualMargin := abs(margin) - if isPlus { - if actualMargin >= expectedMargin { - return domain.OUTCOME_STATUS_WIN, nil - } - } else { - if actualMargin == expectedMargin { - return domain.OUTCOME_STATUS_WIN, nil - } - } - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -func abs(x int) int { - if x < 0 { - return -x - } - return x -} - -// evaluateBothTeamsToReceiveCards checks if both teams received at least one card. -func evaluateBothTeamsToReceiveCards(outcome domain.BetOutcome, yellowCards, redCards struct{ Home, Away int }) (domain.OutcomeStatus, error) { - homeCards := yellowCards.Home + redCards.Home - awayCards := yellowCards.Away + redCards.Away - - var conditionMet bool - switch outcome.OddName { - case "a Card": - conditionMet = homeCards > 0 && awayCards > 0 - case "2+ Cards": - conditionMet = homeCards >= 2 && awayCards >= 2 - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Both Teams To Receive Cards: %s", outcome.OddName) - } - - isBetOnYes := outcome.OddHeader == "Yes" - - if conditionMet == isBetOnYes { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateHalfWithMostGoals compares total goals in each half. -func evaluateHalfWithMostGoals(outcome domain.BetOutcome, firstHalf, secondHalf struct{ Home, Away int }) (domain.OutcomeStatus, error) { - firstHalfGoals := firstHalf.Home + firstHalf.Away - secondHalfGoals := secondHalf.Home + secondHalf.Away - - var won bool - switch outcome.OddName { - case "1st Half": - won = firstHalfGoals > secondHalfGoals - case "2nd Half": - won = secondHalfGoals > firstHalfGoals - case "Tie": - won = firstHalfGoals == secondHalfGoals - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Half with Most Goals: %s", outcome.OddName) - } - - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateTeamHighestScoringHalf checks which half a specific team scored more goals in. -func evaluateTeamHighestScoringHalf(outcome domain.BetOutcome, firstHalf, secondHalf struct{ Home, Away int }, team string) (domain.OutcomeStatus, error) { - var first, second int - if team == "home" { - first = firstHalf.Home - second = secondHalf.Home - } else { - first = firstHalf.Away - second = secondHalf.Away - } - - var won bool - switch outcome.OddName { - case "1st Half": - won = first > second - case "2nd Half": - won = second > first - case "Tie": - won = first == second - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Team Highest Scoring Half: %s", outcome.OddName) - } - - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateTeamTotalGoals checks the total goals for a single team. -func evaluateTeamTotalGoals(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - var teamGoals int - if outcome.OddHeader == "1" { - teamGoals = score.Home - } else if outcome.OddHeader == "2" { - teamGoals = score.Away - } else { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Team Total Goals: %s", outcome.OddHeader) - } - - handicapStr := strings.TrimPrefix(outcome.OddHandicap, "Over ") - handicapStr = strings.TrimPrefix(handicapStr, "Under ") - handicap, err := strconv.ParseFloat(handicapStr, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap for Team Total Goals: %s", outcome.OddHandicap) - } - - var won bool - if strings.HasPrefix(outcome.OddHandicap, "Over") { - won = float64(teamGoals) > handicap - } else if strings.HasPrefix(outcome.OddHandicap, "Under") { - won = float64(teamGoals) < handicap - } else { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap type for Team Total Goals: %s", outcome.OddHandicap) - } - - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} -// evaluateExactTotalGoals checks for the exact number of goals scored. -func evaluateExactTotalGoals(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - totalGoals := score.Home + score.Away - betGoalsStr := strings.TrimSuffix(strings.Fields(outcome.OddName)[0], "+") - betGoals, err := strconv.Atoi(betGoalsStr) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid bet value for Exact Total Goals: %s", outcome.OddName) - } - - var won bool - if strings.HasSuffix(outcome.OddName, "+") { - won = totalGoals >= betGoals - } else { - won = totalGoals == betGoals - } - - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateTeamsToScore checks which teams scored in the match. -func evaluateTeamsToScore(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - homeScored := score.Home > 0 - awayScored := score.Away > 0 - - var won bool - switch outcome.OddName { - case "Both Teams": - won = homeScored && awayScored - case "No Goal": - won = !homeScored && !awayScored - default: - if strings.HasSuffix(outcome.OddName, "Only") { - teamName := strings.TrimSuffix(outcome.OddName, " Only") - if teamName == outcome.HomeTeamName { - won = homeScored && !awayScored - } else if teamName == outcome.AwayTeamName { - won = !homeScored && awayScored - } - } else { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Teams to Score: %s", outcome.OddName) - } - } - - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateFirstMatchCorner checks which team took the first corner. -func evaluateFirstMatchCorner(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) { - for _, event := range events { - if strings.Contains(event["text"], "Corner") { - var firstCornerTeam string - if strings.Contains(event["text"], outcome.HomeTeamName) { - firstCornerTeam = "1" - } else if strings.Contains(event["text"], outcome.AwayTeamName) { - firstCornerTeam = "2" - } - - if outcome.OddName == firstCornerTeam { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - } - return domain.OUTCOME_STATUS_LOSS, nil // No corners in the match -} - -// evaluateLastMatchCorner checks which team took the last corner. -func evaluateLastMatchCorner(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) { - for i := len(events) - 1; i >= 0; i-- { - event := events[i] - if strings.Contains(event["text"], "Corner") { - var lastCornerTeam string - if strings.Contains(event["text"], outcome.HomeTeamName) { - lastCornerTeam = "1" - } else if strings.Contains(event["text"], outcome.AwayTeamName) { - lastCornerTeam = "2" - } - - if outcome.OddName == lastCornerTeam { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - } - return domain.OUTCOME_STATUS_LOSS, nil // No corners in the match -} - -// evaluateCornerMatchBet determines which team had more corners. -func evaluateCornerMatchBet(outcome domain.BetOutcome, corners struct{ Home, Away int }) (domain.OutcomeStatus, error) { - var won bool - switch outcome.OddName { - case "1": - won = corners.Home > corners.Away - case "Tie": - won = corners.Home == corners.Away - case "2": - won = corners.Away > corners.Home - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Corner Match Bet: %s", outcome.OddName) - } - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateMultiCorners multiplies 1st half corners by 2nd half corners. -func evaluateMultiCorners(outcome domain.BetOutcome, totalCorners, halfTimeCorners struct{ Home, Away int }) (domain.OutcomeStatus, error) { - firstHalfTotal := halfTimeCorners.Home + halfTimeCorners.Away - secondHalfTotal := (totalCorners.Home + totalCorners.Away) - firstHalfTotal - multiCornerValue := firstHalfTotal * secondHalfTotal - - handicap, err := strconv.ParseFloat(outcome.OddName, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap for Multi Corners: %s", outcome.OddName) - } - - var won bool - if outcome.OddHeader == "Over" { - won = float64(multiCornerValue) > handicap - } else if outcome.OddHeader == "Under" { - won = float64(multiCornerValue) < handicap - } else { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Multi Corners: %s", outcome.OddHeader) - } - - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateMatchShotsOnTarget evaluates over/under for total shots on target. -func evaluateMatchShotsOnTarget(outcome domain.BetOutcome, onTarget struct{ Home, Away int }) (domain.OutcomeStatus, error) { - totalSOT := onTarget.Home + onTarget.Away - handicap, err := strconv.ParseFloat(outcome.OddName, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap for Match Shots on Target: %s", outcome.OddName) - } - - var won bool - if outcome.OddHeader == "Over" { - won = float64(totalSOT) > handicap - } else if outcome.OddHeader == "Under" { - won = float64(totalSOT) < handicap - } else { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Match Shots on Target: %s", outcome.OddHeader) - } - - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateTeamShotsOnTarget evaluates over/under for a single team's shots on target. -func evaluateTeamShotsOnTarget(outcome domain.BetOutcome, onTarget struct{ Home, Away int }) (domain.OutcomeStatus, error) { - var teamSOT int - if outcome.OddHeader == "1" { - teamSOT = onTarget.Home - } else if outcome.OddHeader == "2" { - teamSOT = onTarget.Away - } else { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Team Shots on Target: %s", outcome.OddHeader) - } - - handicapStr := strings.TrimPrefix(outcome.OddHandicap, "Over ") - handicapStr = strings.TrimPrefix(handicapStr, "Under ") - handicap, err := strconv.ParseFloat(handicapStr, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap for Team Shots on Target: %s", outcome.OddHandicap) - } - - var won bool - if strings.HasPrefix(outcome.OddHandicap, "Over") { - won = float64(teamSOT) > handicap - } else if strings.HasPrefix(outcome.OddHandicap, "Under") { - won = float64(teamSOT) < handicap - } else { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap type for Team Shots on Target: %s", outcome.OddHandicap) - } - - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// evaluateTimeOfFirstGoal checks if the first goal's timing matches the bet. -func evaluateTimeOfFirstGoal(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) { - firstGoalMin := -1 - for _, event := range events { - if strings.Contains(event["text"], "1st Goal") { - min, err := parseEventMinute(event["text"]) - if err == nil { - firstGoalMin = min - break - } - } - } - - // Logic for Late Goal / Early Goal - if strings.Contains(outcome.OddName, "before") || strings.Contains(outcome.OddName, "after") { - var timeMarker int - var isBefore, betOnYes bool - - if _, err := fmt.Sscanf(outcome.OddName, "Goal before %d:%d", &timeMarker, new(int)); err == nil { - isBefore = true - } else if _, err := fmt.Sscanf(outcome.OddName, "No Goal before %d:%d", &timeMarker, new(int)); err == nil { - isBefore = true - betOnYes = false - } else if _, err := fmt.Sscanf(outcome.OddName, "Goal after %d:%d", &timeMarker, new(int)); err == nil { - isBefore = false - } else if _, err := fmt.Sscanf(outcome.OddName, "No Goal after %d:%d", &timeMarker, new(int)); err == nil { - isBefore = false - betOnYes = false - } else { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("could not parse time from odd name: %s", outcome.OddName) - } - - var conditionMet bool - if isBefore { - conditionMet = firstGoalMin != -1 && firstGoalMin <= timeMarker - } else { // isAfter - // This requires finding the last goal, not just the first. This implementation is simplified for first goal. - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("late goal logic not fully implemented for all cases") - } - - if conditionMet == betOnYes { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - - // Logic for Time Brackets - if firstGoalMin == -1 { // No Goal - if outcome.OddName == "No Goal" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - parts := strings.Split(outcome.OddName, "-") - if len(parts) == 2 { - start, _ := strconv.Atoi(strings.TrimSpace(parts[0])) - endStr := strings.Fields(parts[1])[0] - end, _ := strconv.Atoi(endStr) - - if firstGoalMin >= start && firstGoalMin <= end { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unhandled time of goal format: %s", outcome.OddName) -} - -// evaluateSpecials handles various markets grouped under the "Specials" ID. -func evaluateSpecials(outcome domain.BetOutcome, finalScore, firstHalfScore, secondHalfScore struct{ Home, Away int }) (domain.OutcomeStatus, error) { - var team, betType string - team = outcome.OddHeader // "1" for home, "2" for away - betType = outcome.OddName - - var won bool - switch betType { - case "To Win From Behind": - if team == "1" { - won = firstHalfScore.Home < firstHalfScore.Away && finalScore.Home > finalScore.Away - } else { - won = firstHalfScore.Away < firstHalfScore.Home && finalScore.Away > finalScore.Home - } - case "To Win to Nil": - if team == "1" { - won = finalScore.Home > finalScore.Away && finalScore.Away == 0 - } else { - won = finalScore.Away > finalScore.Home && finalScore.Home == 0 - } - case "To Win Either Half": - if team == "1" { - won = (firstHalfScore.Home > firstHalfScore.Away) || (secondHalfScore.Home > secondHalfScore.Away) - } else { - won = (firstHalfScore.Away > firstHalfScore.Home) || (secondHalfScore.Away > secondHalfScore.Home) - } - case "To Win Both Halves": - if team == "1" { - won = (firstHalfScore.Home > firstHalfScore.Away) && (secondHalfScore.Home > secondHalfScore.Away) - } else { - won = (firstHalfScore.Away > firstHalfScore.Home) && (secondHalfScore.Away > secondHalfScore.Home) - } - case "To Score in Both Halves": - if team == "1" { - won = firstHalfScore.Home > 0 && secondHalfScore.Home > 0 - } else { - won = firstHalfScore.Away > 0 && secondHalfScore.Away > 0 - } - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unsupported special market: %s", betType) - } - - if won { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil -} - - -// Basketball evaluations - -// Game Lines is an aggregate of money line, spread and total betting markets in one -func evaluateGameLines(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddName { - case "Money Line": - return evaluateMoneyLine(outcome, score) - - case "Spread", "Line", "Run Line": - // Since Spread betting is essentially the same thing - return evaluateAsianHandicap(outcome, score) - case "Total": - return evaluateTotalOverUnder(outcome, score) - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName) - } -} - -// Money Line betting is a type of bet where the bettor predicts the outcome of a match without any point spread. -func evaluateMoneyLine(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddHeader { - case "1": - if score.Home > score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - - case "2": - if score.Home < score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Tie", "Draw": - if score.Home == score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName) - } -} - -// Total Over/Under betting is a type of bet where the bettor predicts whether the total number of points scored in a match will be over or under a specified number. -func evaluateTotalOverUnder(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - - // The handicap will be in the format "U {float}" or "O {float}" - // U and O denoting over and under for this case - overUnderStr := strings.Split(outcome.OddHandicap, " ") - if len(overUnderStr) != 2 { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - threshold, err := strconv.ParseFloat(overUnderStr[1], 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - - // Since the threshold will come in a xx.5 format, there is no VOID for this kind of bet - totalScore := float64(score.Home + score.Away) - - switch overUnderStr[0] { - case "O": - if totalScore > threshold { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "U": - if totalScore < threshold { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "E": - if totalScore == threshold { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) -} - -func evaluateTotalLegs(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - total, err := strconv.ParseFloat(outcome.OddName, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid : %s", outcome.OddName) - } - - totalLegs := float64(score.Home + score.Away) - - switch outcome.OddHeader { - case "Over": - if totalLegs > total { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Under": - if totalLegs < total { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) -} - -// Result and Total betting is a type of bet where the bettor predicts -// the outcome of a match and whether the total number of points scored will be over or under a specified number. -func evaluateResultAndTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - - // The handicap will be in the format "U {float}" or "O {float}" - // U and O denoting over and under for this case - overUnderStr := strings.Split(outcome.OddHandicap, " ") - if len(overUnderStr) != 2 { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - - overUnder := overUnderStr[0] - - if overUnder != "Over" && overUnder != "Under" { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing over under: %s", outcome.OddHeader) - } - threshold, err := strconv.ParseFloat(overUnderStr[1], 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - - // Since the threshold will come in a xx.5 format, there is no VOID for this kind of bet - totalScore := float64(score.Home + score.Away) - - switch outcome.OddHeader { - case "1": - if score.Home < score.Away { - return domain.OUTCOME_STATUS_LOSS, nil - } - - if overUnder == "Over" && totalScore > threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if overUnder == "Under" && totalScore < threshold { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": - if score.Away < score.Home { - return domain.OUTCOME_STATUS_LOSS, nil - } - if overUnder == "Over" && totalScore > threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if overUnder == "Under" && totalScore < threshold { - return domain.OUTCOME_STATUS_WIN, nil - } - - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed to parse over and under: %s", outcome.OddName) - } -} - -// Team Total betting is a type of bet where the bettor predicts the total number of points scored by a specific team in a match -// is over or under a specified number. -func evaluateTeamTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - - // The handicap will be in the format "U {float}" or "O {float}" - // U and O denoting over and under for this case - overUnderStr := strings.Split(outcome.OddHandicap, " ") - if len(overUnderStr) != 2 { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddHandicap) - } - - overUnder := overUnderStr[0] - - if overUnder != "Over" && overUnder != "Under" { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing over under: %s", outcome.OddHeader) - } - threshold, err := strconv.ParseFloat(overUnderStr[1], 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddHandicap) - } - - // Since the threshold will come in a xx.5 format, there is no VOID for this kind of bet - HomeScore := float64(score.Home) - AwayScore := float64(score.Away) - - switch outcome.OddHeader { - case "1": - if overUnder == "Over" && HomeScore > threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if overUnder == "Under" && HomeScore < threshold { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": - if overUnder == "Over" && AwayScore > threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if overUnder == "Under" && AwayScore < threshold { - return domain.OUTCOME_STATUS_WIN, nil - } - - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed to parse over and under: %s", outcome.OddName) - } -} - - - - -// Result and Both Teams To Score X Points is a type of bet where the bettor predicts whether both teams will score a certain number of points -// and also the result fo the match -func evaluateResultAndBTTSX(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - - // The name parameter will hold value "name": "{team_name} and {Yes | No}" - // The best way to do this is to evaluate backwards since there might be - // teams with 'and' in their name - // We know that there is going to be "Yes" and "No " - oddNameSplit := strings.Split(outcome.OddName, " ") - - scoreCheckSplit := oddNameSplit[len(oddNameSplit)-1] - var isScorePoints bool - switch scoreCheckSplit { - case "Yes": - isScorePoints = true - case "No": - isScorePoints = false - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName) - } - - teamName := strings.TrimSpace(strings.Join(oddNameSplit[:len(oddNameSplit)-2], "")) - - threshold, err := strconv.ParseInt(outcome.OddHeader, 10, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddHeader) - } - - // above code removes any space from team name, so do the same for outcome.HomeTeamName and outcome.AwayTeamName - outcome.HomeTeamName = strings.Join(strings.Split(outcome.HomeTeamName, " "), "") - outcome.AwayTeamName = strings.Join(strings.Split(outcome.AwayTeamName, " "), "") - - switch teamName { - case outcome.HomeTeamName: - if score.Home > score.Away { - if isScorePoints && score.Home >= int(threshold) && score.Away >= int(threshold) { - return domain.OUTCOME_STATUS_WIN, nil - } else if !isScorePoints && score.Home < int(threshold) && score.Away < int(threshold) { - return domain.OUTCOME_STATUS_WIN, nil - } - } - case outcome.AwayTeamName: - if score.Away > score.Home { - if isScorePoints && score.Home >= int(threshold) && score.Away >= int(threshold) { - return domain.OUTCOME_STATUS_WIN, nil - } else if !isScorePoints && score.Home < int(threshold) && score.Away < int(threshold) { - return domain.OUTCOME_STATUS_WIN, nil - } - } - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("team name error: %s", teamName) - } - - return domain.OUTCOME_STATUS_LOSS, nil - -} - -// Both Teams To Score X Points is a type of bet where the bettor predicts whether both teams will score a certain number of points. -func evaluateBTTSX(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - threshold, err := strconv.ParseInt(outcome.OddName, 10, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - - switch outcome.OddHeader { - case "Yes": - if score.Home >= int(threshold) && score.Away >= int(threshold) { - return domain.OUTCOME_STATUS_WIN, nil - } - case "No": - if score.Home < int(threshold) && score.Away < int(threshold) { - return domain.OUTCOME_STATUS_WIN, nil - } - - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } - - return domain.OUTCOME_STATUS_LOSS, nil -} - -// Money Line 3 Way betting is a type of bet where the bettor predicts the outcome of a match with three possible outcomes: home win, away win, or draw. -func evaluateMoneyLine3Way(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddName { - case "1": // Home win - if score.Home > score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Tie": - if score.Home == score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": // Away win - if score.Away > score.Home { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName) - } -} - -func evaluateDoubleResult(outcome domain.BetOutcome, firstHalfScore struct{ Home, Away int }, fullTimeScore struct{ Home, Away int }) (domain.OutcomeStatus, error) { - halfWins := strings.Split(outcome.OddName, "-") - if len(halfWins) != 2 { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName) - } - firstHalfWinner := strings.TrimSpace(halfWins[0]) - fullTimeWinner := strings.TrimSpace(halfWins[1]) - - if firstHalfWinner != outcome.HomeTeamName && firstHalfWinner != outcome.AwayTeamName && firstHalfWinner != "Tie" { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", firstHalfWinner) - } - if fullTimeWinner != outcome.HomeTeamName && fullTimeWinner != outcome.AwayTeamName && fullTimeWinner != "Tie" { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", firstHalfWinner) - } - - switch { - case firstHalfWinner == outcome.HomeTeamName && firstHalfScore.Home < firstHalfScore.Away: - return domain.OUTCOME_STATUS_LOSS, nil - case firstHalfWinner == outcome.AwayTeamName && firstHalfScore.Away < firstHalfScore.Home: - return domain.OUTCOME_STATUS_LOSS, nil - case firstHalfWinner == "Tie" && firstHalfScore.Home != firstHalfScore.Away: - return domain.OUTCOME_STATUS_LOSS, nil - } - - switch { - case fullTimeWinner == outcome.HomeTeamName && fullTimeScore.Home < fullTimeScore.Away: - return domain.OUTCOME_STATUS_LOSS, nil - case fullTimeWinner == outcome.AwayTeamName && fullTimeScore.Away < fullTimeScore.Home: - return domain.OUTCOME_STATUS_LOSS, nil - case fullTimeWinner == "Tie" && fullTimeScore.Home != fullTimeScore.Away: - return domain.OUTCOME_STATUS_LOSS, nil - } - - return domain.OUTCOME_STATUS_WIN, nil -} - -// Highest Scoring Half betting is a type of bet where the bettor predicts which half of the match will have the highest total score. -func evaluateHighestScoringHalf(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }) (domain.OutcomeStatus, error) { - firstHalfTotal := firstScore.Home + firstScore.Away - secondHalfTotal := secondScore.Home + secondScore.Away - switch outcome.OddName { - case "1st Half": - if firstHalfTotal > secondHalfTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - case "2nd Half": - if firstHalfTotal < secondHalfTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - case "Tie": - if firstHalfTotal == secondHalfTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName) - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// Highest Scoring Quarter betting is a type of bet where the bettor predicts which quarter of the match will have the highest score. -func evaluateHighestScoringQuarter(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }, thirdScore struct{ Home, Away int }, fourthScore struct{ Home, Away int }) (domain.OutcomeStatus, error) { - firstQuarterTotal := firstScore.Home + firstScore.Away - secondQuarterTotal := secondScore.Home + secondScore.Away - thirdQuarterTotal := thirdScore.Home + thirdScore.Away - fourthQuarterTotal := fourthScore.Home + fourthScore.Away - - switch outcome.OddName { - case "1st Quarter": - if firstQuarterTotal > secondQuarterTotal && firstQuarterTotal > thirdQuarterTotal && firstQuarterTotal > fourthQuarterTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - case "2nd Quarter": - if secondQuarterTotal > firstQuarterTotal && secondQuarterTotal > thirdQuarterTotal && secondQuarterTotal > fourthQuarterTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - case "3rd Quarter": - if thirdQuarterTotal > firstQuarterTotal && thirdQuarterTotal > secondQuarterTotal && thirdQuarterTotal > fourthQuarterTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - case "4th Quarter": - if fourthQuarterTotal > firstQuarterTotal && fourthQuarterTotal > secondQuarterTotal && fourthQuarterTotal > thirdQuarterTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - case "Tie": - if firstQuarterTotal == secondQuarterTotal || secondQuarterTotal == thirdQuarterTotal || thirdQuarterTotal == fourthQuarterTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName) - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// Team With Highest Scoring Quarter betting is a type of bet where the bettor predicts which team will have the highest score in a specific quarter. -func evaluateTeamWithHighestScoringQuarter(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }, thirdScore struct{ Home, Away int }, fourthScore struct{ Home, Away int }) (domain.OutcomeStatus, error) { - homeTeamHighestQuarter := max(firstScore.Home, secondScore.Home, thirdScore.Home, fourthScore.Home) - awayTeamHighestQuarter := max(firstScore.Away, secondScore.Away, thirdScore.Away, fourthScore.Away) - - switch outcome.OddName { - case "1": - if homeTeamHighestQuarter > awayTeamHighestQuarter { - return domain.OUTCOME_STATUS_WIN, nil - } - case "2": - if awayTeamHighestQuarter > homeTeamHighestQuarter { - return domain.OUTCOME_STATUS_WIN, nil - } - case "Tie": - if homeTeamHighestQuarter == awayTeamHighestQuarter { - return domain.OUTCOME_STATUS_WIN, nil - } - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName) - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// Handicap and Total betting is a combination of spread betting and total points betting -// where the bettor predicts the outcome of a match with a point spread and the total number of points scored is over or under a specified number. -func evaluateHandicapAndTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - - nameSplit := strings.Split(outcome.OddName, " ") - // Evaluate from bottom to get the threshold and find out if its over or under - threshold, err := strconv.ParseFloat(nameSplit[len(nameSplit)-1], 10) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing threshold: %s", outcome.OddName) - } - total := float64(score.Home + score.Away) - overUnder := nameSplit[len(nameSplit)-2] - switch overUnder { - case "Over": - if total < threshold { - return domain.OUTCOME_STATUS_LOSS, nil - } - case "Under": - if total > threshold { - return domain.OUTCOME_STATUS_LOSS, nil - } - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing over and under: %s", outcome.OddName) - } - - handicap, err := strconv.ParseFloat(nameSplit[len(nameSplit)-4], 10) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing handicap: %s", outcome.OddName) - } - - teamName := strings.TrimSpace(strings.Join(nameSplit[:len(nameSplit)-4], "")) - adjustedHomeScore := float64(score.Home) - adjustedAwayScore := float64(score.Away) - - outcome.HomeTeamName = strings.Join(strings.Split(outcome.HomeTeamName, " "), "") - outcome.AwayTeamName = strings.Join(strings.Split(outcome.AwayTeamName, " "), "") - - switch teamName { - case outcome.HomeTeamName: - adjustedHomeScore += handicap - if adjustedHomeScore > adjustedAwayScore { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case outcome.AwayTeamName: - adjustedAwayScore += handicap - if adjustedAwayScore > adjustedHomeScore { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing team name: %s", outcome.OddName) - } - -} - -func evaluateWinningMargin(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - if len(outcome.OddName) < 1 { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName) - } - - isGtr := false - idx := len(outcome.OddName) - if outcome.OddName[idx-1] == '+' { - isGtr = true - idx-- - } - - margin, err := strconv.ParseInt(outcome.OddName[:idx], 10, 64) - if err != nil { - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName) - } - - switch outcome.OddHeader { - case "1": - if score.Home == (score.Away + int(margin)) { - return domain.OUTCOME_STATUS_WIN, nil - } else if isGtr && score.Home > (score.Away+int(margin)) { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": - if score.Away == (score.Home + int(margin)) { - return domain.OUTCOME_STATUS_WIN, nil - } else if isGtr && score.Away > (score.Home+int(margin)) { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddheader: %s", outcome.OddHeader) -} - -// Highest Scoring Period betting is a type of bet where the bettor predicts which period of the match will have the highest total score. -func evaluateHighestScoringPeriod(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }, thirdScore struct{ Home, Away int }) (domain.OutcomeStatus, error) { - firstPeriodTotal := firstScore.Home + firstScore.Away - secondPeriodTotal := secondScore.Home + secondScore.Away - thirdPeriodTotal := thirdScore.Home + thirdScore.Away - - switch outcome.OddName { - case "Period 1": - if firstPeriodTotal > secondPeriodTotal && firstPeriodTotal > thirdPeriodTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - case "Period 2": - if secondPeriodTotal > firstPeriodTotal && secondPeriodTotal > thirdPeriodTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - case "Period 3": - if thirdPeriodTotal > firstPeriodTotal && thirdPeriodTotal > secondPeriodTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - case "Tie": - if firstPeriodTotal == secondPeriodTotal || secondPeriodTotal == thirdPeriodTotal { - return domain.OUTCOME_STATUS_WIN, nil - } - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName) - } - return domain.OUTCOME_STATUS_LOSS, nil -} - -// Tied After Regulation is a type of bet where the bettor predicts whether the match will end in a tie after regulation time. -func evaluateTiedAfterRegulation(outcome domain.BetOutcome, scores []struct{ Home, Away int }) (domain.OutcomeStatus, error) { - totalScore := struct{ Home, Away int }{0, 0} - for _, score := range scores { - totalScore.Home += score.Home - totalScore.Away += score.Away - } - switch outcome.OddName { - case "Yes": - if totalScore.Home == totalScore.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "No": - if totalScore.Home != totalScore.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName) -} - -func evaluateVolleyballGamelines(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddName { - case "Total": - return evaluateTotalOverUnder(outcome, score) - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName) - } -} - -func evaluateGameBettingTwoWay(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddName { - case "Handicap": - return evaluateAsianHandicap(outcome, score) - case "Total": - return evaluateTotalOverUnder(outcome, score) - case "To Win": - return evaluateFullTimeResult(outcome, score) - default: - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName) - } -} diff --git a/internal/services/result/football_test.go b/internal/services/result/football_test.go deleted file mode 100644 index 0130cf6..0000000 --- a/internal/services/result/football_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package result - -import ( - "testing" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func TestEvaluateFullTimeResult(t *testing.T) { - tests := []struct { - name string - outcome domain.BetOutcome - score struct{ Home, Away int } - expected domain.OutcomeStatus - }{ - {"Home win", domain.BetOutcome{OddName: "1"}, struct{ Home, Away int }{2, 1}, domain.OUTCOME_STATUS_WIN}, - {"Away win", domain.BetOutcome{OddName: "2"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_WIN}, - {"Draw", domain.BetOutcome{OddName: "Draw"}, struct{ Home, Away int }{1, 1}, domain.OUTCOME_STATUS_WIN}, - {"Home selected, but Draw", domain.BetOutcome{OddName: "1"}, struct{ Home, Away int }{1, 1}, domain.OUTCOME_STATUS_LOSS}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - status, _ := evaluateFullTimeResult(tt.outcome, tt.score) - if status != tt.expected { - t.Errorf("expected %d, got %d", tt.expected, status) - } - }) - } -} diff --git a/internal/services/result/interface.go b/internal/services/result/interface.go deleted file mode 100644 index 4796528..0000000 --- a/internal/services/result/interface.go +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index f98841b..0000000 --- a/internal/services/result/notification.go +++ /dev/null @@ -1,292 +0,0 @@ -package result - -import ( - "context" - "encoding/json" - "fmt" - "strings" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" -) - -func (s *Service) CheckAndSendResultNotifications(ctx context.Context, createdAfter time.Time) error { - - resultLog, err := s.resultLogStore.GetAllResultLog(ctx, domain.ResultLogFilter{ - CreatedAfter: domain.ValidTime{ - Value: createdAfter, - Valid: true, - }, - }) - - if err != nil { - s.mongoLogger.Error( - "Failed to get result log", - zap.Time("CreatedAfter", createdAfter), - zap.Error(err), - ) - return err - } - - if len(resultLog) == 0 { - s.mongoLogger.Info( - "No results found for check and send result notification", - zap.Time("CreatedAfter", createdAfter), - ) - return nil - } - - totalResultLog := domain.ResultLog{ - StatusNotFinishedCount: resultLog[0].StatusNotFinishedCount, - StatusPostponedCount: resultLog[0].StatusPostponedCount, - } - for _, log := range resultLog { - // Add all the bets - totalResultLog.StatusNotFinishedBets += log.StatusNotFinishedBets - totalResultLog.StatusPostponedBets += log.StatusPostponedBets - totalResultLog.StatusToBeFixedBets += log.StatusToBeFixedBets - totalResultLog.StatusRemovedBets += log.StatusRemovedBets - totalResultLog.StatusEndedBets += log.StatusEndedBets - - totalResultLog.StatusToBeFixedCount += log.StatusToBeFixedCount - totalResultLog.StatusRemovedCount += log.StatusRemovedCount - totalResultLog.StatusEndedCount += log.StatusEndedCount - totalResultLog.RemovedCount += log.RemovedCount - } - - err = s.SendAdminResultStatusErrorNotification(ctx, totalResultLog, createdAfter, time.Now()) - if err != nil { - s.mongoLogger.Error( - "Failed to send admin result status notification", - zap.Time("CreatedAfter", createdAfter), - zap.Error(err), - ) - return err - } - - return nil -} - -func buildHeadlineAndMessage(counts domain.ResultLog, createdAfter time.Time, endTime time.Time) (string, string) { - period := fmt.Sprintf("%s - %s", createdAfter.Format("02 Jan 2006"), endTime.Format("02 Jan 2006")) - - totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount - totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets + counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets - if totalIssues == 0 { - return "✅ Successfully Processed Event Results", fmt.Sprintf( - "%d total ended events with %d total bets. No issues detected", counts.StatusEndedCount, totalBets, - ) - } - - parts := []string{} - if counts.StatusNotFinishedCount > 0 { - parts = append(parts, fmt.Sprintf("%d unfinished with %d bets", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets)) - } - if counts.StatusToBeFixedCount > 0 { - parts = append(parts, fmt.Sprintf("%d to-fix with %d bets", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets)) - } - if counts.StatusPostponedCount > 0 { - parts = append(parts, fmt.Sprintf("%d postponed with %d bets", counts.StatusPostponedCount, counts.StatusPostponedBets)) - } - if counts.StatusRemovedCount > 0 { - parts = append(parts, fmt.Sprintf("%d removed with %d bets", counts.StatusRemovedCount, counts.StatusRemovedBets)) - } - if counts.StatusEndedCount > 0 { - parts = append(parts, fmt.Sprintf("%d ended with %d bets", counts.StatusEndedCount, counts.StatusEndedBets)) - } - - headline := "⚠️ Issues Found Processing Event Results" - message := fmt.Sprintf("Processed expired event results (%s): %s. Please review pending entries.", - period, strings.Join(parts, ", ")) - return headline, message -} - -func buildHeadlineAndMessageEmail(counts domain.ResultLog, user domain.User, createdAfter time.Time, endTime time.Time) (string, string, string) { - period := fmt.Sprintf("%s - %s", createdAfter.Format("02 Jan 2006"), endTime.Format("02 Jan 2006")) - - totalIssues := counts.StatusNotFinishedCount + counts.StatusToBeFixedCount + - counts.StatusPostponedCount + counts.StatusRemovedCount - totalEvents := counts.StatusEndedCount + counts.StatusNotFinishedCount + - counts.StatusToBeFixedCount + counts.StatusPostponedCount + counts.StatusRemovedCount - totalBets := counts.StatusEndedBets + counts.StatusNotFinishedBets + - counts.StatusPostponedBets + counts.StatusRemovedBets + counts.StatusToBeFixedBets - - greeting := fmt.Sprintf("Hi %s %s,", user.FirstName, user.LastName) - - if totalIssues == 0 { - headline := "✅ Weekly Results Report — All Events Processed Successfully" - plain := fmt.Sprintf(`%s - -Weekly Results Summary (%s): -- %d Ended Events -- %d Total Bets - -All events were processed successfully, and no issues were detected. - -Best regards, -The System`, greeting, period, counts.StatusEndedCount, totalBets) - - html := fmt.Sprintf(`

%s

-

Weekly Results Summary

-

Period: %s

- -

All events were processed successfully, and no issues were detected.

-

Best regards,
The System

`, - greeting, period, counts.StatusEndedCount, totalBets) - - return headline, plain, html - } - - partsPlain := []string{} - partsHTML := []string{} - - if counts.StatusNotFinishedCount > 0 { - partsPlain = append(partsPlain, - fmt.Sprintf("- %d Incomplete Events (%d Bets)", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets)) - partsHTML = append(partsHTML, - fmt.Sprintf("
  • %d Incomplete Events (%d Bets)
  • ", counts.StatusNotFinishedCount, counts.StatusNotFinishedBets)) - } - if counts.StatusToBeFixedCount > 0 { - partsPlain = append(partsPlain, - fmt.Sprintf("- %d Requires Review (%d Bets)", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets)) - partsHTML = append(partsHTML, - fmt.Sprintf("
  • %d Requires Review (%d Bets)
  • ", counts.StatusToBeFixedCount, counts.StatusToBeFixedBets)) - } - if counts.StatusPostponedCount > 0 { - partsPlain = append(partsPlain, - fmt.Sprintf("- %d Postponed Events (%d Bets)", counts.StatusPostponedCount, counts.StatusPostponedBets)) - partsHTML = append(partsHTML, - fmt.Sprintf("
  • %d Postponed Events (%d Bets)
  • ", counts.StatusPostponedCount, counts.StatusPostponedBets)) - } - if counts.StatusRemovedCount > 0 { - partsPlain = append(partsPlain, - fmt.Sprintf("- %d Discarded Events (%d Bets)", counts.StatusRemovedCount, counts.StatusRemovedBets)) - partsHTML = append(partsHTML, - fmt.Sprintf("
  • %d Discarded Events (%d Bets)
  • ", counts.StatusRemovedCount, counts.StatusRemovedBets)) - } - if counts.StatusEndedCount > 0 { - partsPlain = append(partsPlain, - fmt.Sprintf("- %d Successfully Ended Events (%d Bets)", counts.StatusEndedCount, counts.StatusEndedBets)) - partsHTML = append(partsHTML, - fmt.Sprintf("
  • %d Successfully Ended Events (%d Bets)
  • ", counts.StatusEndedCount, counts.StatusEndedBets)) - } - - headline := "⚠️ Weekly Results Report — Review Required" - - plain := fmt.Sprintf(`%s - -Weekly Results Summary (%s): -%s - -Totals: -- %d Events Processed -- %d Total Bets - -Next Steps: -Some events require your attention. Please log into the admin dashboard to review pending issues. - -Best regards, -The System`, - greeting, - period, - strings.Join(partsPlain, "\n"), - totalEvents, - totalBets, - ) - - html := fmt.Sprintf(`

    %s

    -

    Weekly Results Summary

    -

    Period: %s

    - -

    Totals

    - -

    Next Steps:
    Some events require your attention. Please log into the admin dashboard to review pending issues.

    -

    Best regards,
    The System

    `, - greeting, - period, - strings.Join(partsHTML, "\n"), - totalEvents, - totalBets, - ) - - return headline, plain, html -} - -func (s *Service) SendAdminResultStatusErrorNotification( - ctx context.Context, - counts domain.ResultLog, - createdAfter time.Time, - endTime time.Time, -) error { - - superAdmins, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{ - Role: string(domain.RoleSuperAdmin), - }) - if err != nil { - s.mongoLogger.Error("failed to get super_admin recipients", zap.Error(err)) - return err - } - - metaBytes, err := json.Marshal(counts) - if err != nil { - s.mongoLogger.Error("failed to marshal metadata", zap.Error(err)) - return err - } - - headline, message := buildHeadlineAndMessage(counts, createdAfter, endTime) - - notification := &domain.Notification{ - ErrorSeverity: domain.NotificationErrorSeverityHigh, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Type: domain.NOTIFICATION_TYPE_BET_RESULT, - Level: domain.NotificationLevelWarning, - Reciever: domain.NotificationRecieverSideAdmin, - DeliveryChannel: domain.DeliveryChannelInApp, - Payload: domain.NotificationPayload{ - Headline: headline, - Message: message, - }, - Priority: 2, - Metadata: metaBytes, - } - - var sendErrors []error - for _, user := range superAdmins { - notification.RecipientID = user.ID - if err := s.notificationSvc.SendNotification(ctx, notification); err != nil { - s.mongoLogger.Error("failed to send admin notification", - zap.Int64("admin_id", user.ID), - zap.Error(err), - ) - sendErrors = append(sendErrors, err) - } - // notification.DeliveryChannel = domain.DeliveryChannelEmail - if user.Email == "" { - continue - } - - subject, plain, html := buildHeadlineAndMessageEmail(counts, user, createdAfter, endTime) - if err := s.messengerSvc.SendEmail(ctx, user.Email, plain, html, subject); err != nil { - s.mongoLogger.Error("failed to send admin result report email", - zap.Int64("admin_id", user.ID), - zap.Error(err), - ) - sendErrors = append(sendErrors, err) - } - } - - if len(sendErrors) > 0 { - return fmt.Errorf("sent with partial failure: %d errors", len(sendErrors)) - } - return nil -} diff --git a/internal/services/result/service.go b/internal/services/result/service.go deleted file mode 100644 index 9586667..0000000 --- a/internal/services/result/service.go +++ /dev/null @@ -1,2008 +0,0 @@ -package result - -import ( - "context" - "encoding/json" - "fmt" - "log" - "log/slog" - "net/http" - "strconv" - "strings" - "time" - - "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/bet" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/league" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger" - notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" - "go.uber.org/zap" -) - -type Service struct { - resultLogStore ports.ResultLogStore - config *config.Config - logger *slog.Logger - mongoLogger *zap.Logger - client *http.Client - betSvc bet.Service - oddSvc odds.ServiceImpl - eventSvc *event.Service - leagueSvc *league.Service - notificationSvc *notificationservice.Service - messengerSvc *messenger.Service - userSvc user.Service -} - -func NewService( - resultLogStore ports.ResultLogStore, - cfg *config.Config, - logger *slog.Logger, - mongoLogger *zap.Logger, - betSvc bet.Service, - oddSvc odds.ServiceImpl, - eventSvc *event.Service, - leagueSvc *league.Service, - notificationSvc *notificationservice.Service, - messengerSvc *messenger.Service, - userSvc user.Service, -) *Service { - return &Service{ - resultLogStore: resultLogStore, - config: cfg, - logger: logger, - mongoLogger: mongoLogger, - client: &http.Client{Timeout: 10 * time.Second}, - betSvc: betSvc, - oddSvc: oddSvc, - eventSvc: eventSvc, - leagueSvc: leagueSvc, - notificationSvc: notificationSvc, - messengerSvc: messengerSvc, - userSvc: userSvc, - } -} - -var ( - ErrEventIsNotActive = fmt.Errorf("event has been cancelled or postponed") -) - -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.betSvc.GetBetOutcomeByEventID(ctx, eventID, true) - logger := s.mongoLogger.With( - zap.Int64("eventID", eventID), - zap.Int64("sportID", sportID), - ) - - if err != nil { - logger.Error("Failed to get pending bet outcomes", zap.Error(err)) - - return fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err) - } - for _, outcome := range outcomes { - outcomeLogger := logger.With( - zap.Int64("outcome_id", outcome.ID), - zap.Int32("outcome_status", int32(outcome.Status)), - zap.Int64("outcome_bet_id", outcome.BetID), - zap.String("outcome_market_id", outcome.MarketName), - ) - - if outcome.Expires.After(time.Now()) { - outcomeLogger.Warn("Outcome is not expired yet", zap.Error(err)) - // return fmt.Errorf("Outcome has not expired yet") - continue - } - - parseResult, err := s.ParseB365Result(resultRes, outcome, sportID) - - if err != nil { - outcomeLogger.Error("Failed to parse result", zap.Error(err)) - continue - } - outcome, err = s.betSvc.UpdateBetOutcomeStatus(ctx, outcome.ID, parseResult.Status) - if err != nil { - outcomeLogger.Error("Failed to update bet outcome status", zap.Error(err)) - continue - } - if outcome.Status == domain.OUTCOME_STATUS_ERROR || outcome.Status == domain.OUTCOME_STATUS_PENDING { - outcomeLogger.Error("Outcome has been updated to pending or error", zap.Error(err)) - // return fmt.Errorf("error while updating outcome") - continue - } - - status, err := s.betSvc.CheckBetOutcomeForBet(ctx, outcome.BetID) - if err != nil { - if err != bet.ErrOutcomesNotCompleted { - outcomeLogger.Error("Failed to check bet outcome for bet", zap.Error(err)) - } - // return err - continue - } - outcomeLogger.Info("Updating bet status", zap.String("status", status.String())) - - err = s.betSvc.UpdateStatus(ctx, outcome.BetID, status) - if err != nil { - outcomeLogger.Error("Failed to update bet status", zap.Error(err)) - // return err - continue - } - } - return nil - -} - -func (s *Service) GetTotalBetsForEvents(ctx context.Context, eventID int64) (map[int64]int64, error) { - outcomes, err := s.betSvc.GetBetOutcomeByEventID(ctx, eventID, false) - - if err != nil { - s.mongoLogger.Error( - "[GetTotalBetsForEvent] Failed to get all the pending bet outcomes", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - return nil, fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err) - } - - // Get all the unique bet_ids and how many outcomes have this bet_id - betIDSet := make(map[int64]int64) - for _, outcome := range outcomes { - betIDSet[outcome.BetID] += 1 - } - - return betIDSet, nil - -} - -// Returns total number of bets refunded -func (s *Service) RefundAllOutcomes(ctx context.Context, eventID int64) (map[int64]int64, error) { - - outcomes, err := s.betSvc.UpdateBetOutcomeStatusForEvent(ctx, eventID, domain.OUTCOME_STATUS_VOID) - - if err != nil { - s.mongoLogger.Error( - "[RefundAllOutcomes] Failed to update all outcomes for event", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - } - - // Get all the unique bet_ids and how many outcomes have this bet_id - betIDSet := make(map[int64]int64) - for _, outcome := range outcomes { - betIDSet[outcome.BetID] += 1 - } - - for betID := range betIDSet { - status, err := s.betSvc.CheckBetOutcomeForBet(ctx, betID) - if err != nil { - if err != bet.ErrOutcomesNotCompleted { - s.mongoLogger.Error( - "[RefundAllOutcomes] Failed to check bet outcome for bet", - zap.Int64("eventID", eventID), - zap.Int64("betID", betID), - zap.Error(err), - ) - } - return nil, err - } - err = s.betSvc.UpdateStatus(ctx, betID, status) - if err != nil { - s.mongoLogger.Error( - "[RefundAllOutcomes] Failed to update bet status", - zap.Int64("eventID", eventID), - zap.Int64("betID", betID), - zap.Error(err), - ) - continue - } - } - return betIDSet, nil - // for _, outcome := range outcomes { - // outcome, err = s.betSvc.UpdateBetOutcomeStatus(ctx, outcome.ID, domain.OUTCOME_STATUS_VOID) - // if err != nil { - // s.logger.Error("Failed to refund all outcome status", "bet_outcome_id", outcome.ID, "error", err) - // return err - // } - - // // Check if all the bet outcomes have been set to refund for - // status, err := s.betSvc.CheckBetOutcomeForBet(ctx, outcome.BetID) - // if err != nil { - // if err != bet.ErrOutcomesNotCompleted { - // s.logger.Error("Failed to check bet outcome for bet", "event_id", outcome.EventID, "error", err) - // } - // return err - // } - // err = s.betSvc.UpdateStatus(ctx, outcome.BetID, domain.OUTCOME_STATUS_VOID) - - // if err != nil { - // s.logger.Error("Failed to update bet status", "event id", outcome.EventID, "error", err) - // return err - // } - // } - -} - -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.eventSvc.GetAllEvents(ctx, domain.EventFilter{ - LastStartTime: domain.ValidTime{ - Value: time.Now(), - Valid: true, - }, - // Source: domain.ValidEventSource{ - // Value: domain.EVENT_SOURCE_BET365, - // Valid: true, - // }, - }) - - if err != nil { - s.logger.Error("Failed to fetch events") - s.mongoLogger.Error( - "[FetchAndProcessResult] Failed to fetch events", - zap.Error(err), - ) - return err - } - - empty_sport_id := make([]int64, 0) - var resultLog domain.CreateResultLog - var resultStatusBets domain.ResultStatusBets - for i, event := range events { - if s.config.Env == "development" { - log.Printf("⚙️ Processing Bets For Event %v (%d/%d) \n", event.ID, i+1, len(events)) - } - - eventLogger := s.mongoLogger.With( - zap.Int64("eventID", event.ID), - ) - result, err := s.FetchB365Result(ctx, event.SourceEventID) - if err != nil { - if err == ErrEventIsNotActive { - eventLogger.Warn("Event is not active", zap.Error(err)) - continue - } - - eventLogger.Error("Failed to fetch result", zap.Error(err)) - continue - } - var commonResp domain.CommonResultResponse - if err := json.Unmarshal(result.Results[0], &commonResp); err != nil { - eventLogger.Error("Failed to unmarshal common result", zap.Error(err)) - continue - } - - timeStatusParsed := commonResp.TimeStatus.Value - // if err != nil { - // s.mongoLogger.Error( - // "Failed to parse time status", - // zap.Int64("time_status", timeStatusParsed), - // zap.Error(err), - // ) - // continue - // } - - // TODO: Figure out what to do with the events that have been cancelled or postponed, etc... - // if timeStatusParsed != int64(domain.TIME_STATUS_ENDED) { - // s.logger.Warn("Event is not ended yet", "event_id", eventID, "time_status", commonResp.TimeStatus) - // fmt.Printf("⚠️ Event %v is not ended yet (%d/%d) \n", event.ID, i+1, len(events)) - // isDeleted = false - // continue - // } - // Admin users will be able to review the events - commonRespLogger := eventLogger.With( - zap.Int64("parsed_time_status", timeStatusParsed), - zap.String("response_sport_id", commonResp.SportID), - ) - switch timeStatusParsed { - case int64(domain.TIME_STATUS_NOT_STARTED), int64(domain.TIME_STATUS_IN_PLAY): - resultLog.StatusNotFinishedCount += 1 - bets, err := s.GetTotalBetsForEvents(ctx, event.ID) - if err != nil { - continue - } - resultLog.StatusNotFinishedBets = len(bets) - for k := range bets { - resultStatusBets.StatusNotFinished = append(resultStatusBets.StatusNotFinished, k) - } - - case int64(domain.TIME_STATUS_TO_BE_FIXED): - totalBetsRefunded, err := s.RefundAllOutcomes(ctx, event.ID) - if err != nil { - commonRespLogger.Error("Failed to refund all outcomes", zap.Error(err)) - continue - } - err = s.eventSvc.DeleteEvent(ctx, event.ID) - if err != nil { - commonRespLogger.Error("Failed to remove event", zap.Error(err)) - continue - } - err = s.oddSvc.DeleteOddsForEvent(ctx, event.ID) - if err != nil { - commonRespLogger.Error("Failed to remove odds for event", zap.Error(err)) - continue - } - resultLog.RemovedCount += 1 - resultLog.StatusToBeFixedCount += 1 - resultLog.StatusToBeFixedBets = len(totalBetsRefunded) - for k := range totalBetsRefunded { - resultStatusBets.StatusToBeFixed = append(resultStatusBets.StatusToBeFixed, k) - } - // s.mongoLogger.Warn( - // "Event needs to be rescheduled or corrected", - // zap.Int64("eventID", eventID), - // zap.Error(err), - // ) - case int64(domain.TIME_STATUS_POSTPONED), int64(domain.TIME_STATUS_SUSPENDED): - - bets, err := s.GetTotalBetsForEvents(ctx, event.ID) - if err != nil { - continue - } - - resultLog.StatusPostponedCount += 1 - resultLog.StatusPostponedBets = len(bets) - for k := range bets { - resultStatusBets.StatusPostponed = append(resultStatusBets.StatusPostponed, k) - } - // s.mongoLogger.Warn( - // "Event has been temporarily postponed", - // zap.Int64("eventID", eventID), - // zap.Error(err), - // ) - case int64(domain.TIME_STATUS_ENDED), int64(domain.TIME_STATUS_WALKOVER), int64(domain.TIME_STATUS_DECIDED_BY_FA): - if commonResp.SportID == "" { - empty_sport_id = append(empty_sport_id, event.ID) - continue - } - sportID, err := strconv.ParseInt(commonResp.SportID, 10, 64) - if err != nil { - commonRespLogger.Error("Failed to parse sport id", zap.Error(err)) - continue - } - err = s.UpdateResultForOutcomes(ctx, event.ID, result.Results[0], sportID) - if err != nil { - commonRespLogger.Error("Error while updating result for event", zap.Error(err)) - } - err = s.eventSvc.DeleteEvent(ctx, event.ID) - if err != nil { - commonRespLogger.Error("Failed to remove event", zap.Error(err)) - continue - } - err = s.oddSvc.DeleteOddsForEvent(ctx, event.ID) - if err != nil { - commonRespLogger.Error("Failed to remove odds for event", zap.Error(err)) - continue - } - resultLog.RemovedCount += 1 - resultLog.StatusEndedCount += 1 - bets, err := s.GetTotalBetsForEvents(ctx, event.ID) - if err != nil { - continue - } - resultLog.StatusEndedBets = len(bets) - for k := range bets { - resultStatusBets.StatusEnded = append(resultStatusBets.StatusEnded, k) - } - case int64(domain.TIME_STATUS_ABANDONED), int64(domain.TIME_STATUS_CANCELLED), int64(domain.TIME_STATUS_REMOVED): - // s.SendAdminResultStatusErrorNotification( - // ctx, - // "Cannot Update outcomes for event (ABANDONED | CANCELLED | REMOVED)", - // "Event abandoned/cancelled/removed", - // event.ID, - // ) - // s.mongoLogger.Info( - // "Event abandoned/cancelled/removed", - // zap.Int64("eventID", eventID), - // zap.Int64("status", timeStatusParsed), - // ) - totalBetsRefunded, err := s.RefundAllOutcomes(ctx, event.ID) - if err != nil { - commonRespLogger.Error("Failed to refund outcomes", zap.Error(err)) - } - - err = s.eventSvc.DeleteEvent(ctx, event.ID) - if err != nil { - commonRespLogger.Error("Failed to remove event", zap.Error(err)) - continue - } - err = s.oddSvc.DeleteOddsForEvent(ctx, event.ID) - if err != nil { - commonRespLogger.Error("Failed to remove odds for event", zap.Error(err)) - continue - } - resultLog.RemovedCount += 1 - resultLog.StatusRemovedCount += 1 - resultLog.StatusRemovedBets = len(totalBetsRefunded) - for k := range totalBetsRefunded { - resultStatusBets.StatusRemoved = append(resultStatusBets.StatusRemoved, k) - } - } - - } - - // This will be used to send daily notifications, since events will be removed - _, err = s.resultLogStore.CreateResultLog(ctx, resultLog) - if err != nil { - s.mongoLogger.Warn( - "Failed to store result log", - zap.Error(err), - ) - } - - var logMessage string - if resultLog.StatusNotFinishedCount != 0 || resultLog.StatusPostponedCount != 0 || - resultLog.StatusRemovedCount != 0 || resultLog.StatusToBeFixedCount != 0 { - logMessage = "Completed processing results with issues" - } else { - logMessage = "Successfully processed results with no issues" - } - - s.mongoLogger.Info( - logMessage, - zap.Int("number_of_removed_events", resultLog.RemovedCount), - zap.Int("total_expired_events", len(events)), - zap.Any("events_with_empty_sport_id", empty_sport_id), - zap.Any("result status counts", resultLog), - zap.Any("bets by event status", resultStatusBets), - ) - - return nil -} - -func (s *Service) CheckAndUpdateExpiredB365Events(ctx context.Context) (int64, error) { - events, _, err := s.eventSvc.GetAllEvents(ctx, domain.EventFilter{ - LastStartTime: domain.ValidTime{ - Value: time.Now(), - Valid: true, - }, - // Source: domain.ValidEventSource{ - // Value: domain.EVENT_SOURCE_BET365, - // Valid: true, - // }, - }) - if err != nil { - s.mongoLogger.Error( - "Failed to fetch events", - zap.Error(err), - ) - return 0, err - } - skipped := 0 - updated := 0 - var leagueCountries []string - eventResultStats := make(map[string]int) - for i, event := range events { - if s.config.Env == "development" { - log.Printf("⚙️ Checking and Updating Status for Event %v (%d/%d) \n", event.ID, i+1, len(events)) - } - - if event.Status == domain.STATUS_REMOVED { - skipped += 1 - continue - } - result, err := s.FetchB365Result(ctx, event.SourceEventID) - if err != nil { - s.mongoLogger.Error( - "Failed to fetch result", - zap.Int64("eventID", event.ID), - zap.Error(err), - ) - continue - } - if result.Success != 1 || len(result.Results) == 0 { - s.mongoLogger.Error( - "Invalid API result response", - zap.Int64("eventID", event.ID), - zap.Error(err), - ) - continue - } - - var commonResp domain.CommonResultResponse - if err := json.Unmarshal(result.Results[0], &commonResp); err != nil { - fmt.Printf("UnMarshalling error %v \n", err) - s.mongoLogger.Error( - "Failed to unmarshal common result", - zap.Int64("eventID", event.ID), - zap.Error(err), - ) - continue - } - - var eventStatus domain.EventStatus - // TODO Change event status to int64 enum - timeStatus := commonResp.TimeStatus.Value - // if err != nil { - // s.mongoLogger.Error( - // "Invalid time status", - // zap.Int64("eventID", eventID), - // zap.Error(err), - // ) - // } - switch timeStatus { - case int64(domain.TIME_STATUS_NOT_STARTED): - eventStatus = domain.STATUS_PENDING - eventResultStats["STATUS_PENDING"] += 1 - case int64(domain.TIME_STATUS_IN_PLAY): - eventStatus = domain.STATUS_IN_PLAY - eventResultStats["STATUS_IN_PLAY"] += 1 - case int64(domain.TIME_STATUS_TO_BE_FIXED): - eventStatus = domain.STATUS_TO_BE_FIXED - eventResultStats["STATUS_TO_BE_FIXED"] += 1 - case int64(domain.TIME_STATUS_ENDED): - eventStatus = domain.STATUS_ENDED - eventResultStats["STATUS_ENDED"] += 1 - case int64(domain.TIME_STATUS_POSTPONED): - eventStatus = domain.STATUS_POSTPONED - eventResultStats["STATUS_POSTPONED"] += 1 - case int64(domain.TIME_STATUS_CANCELLED): - eventStatus = domain.STATUS_CANCELLED - eventResultStats["STATUS_CANCELLED"] += 1 - case int64(domain.TIME_STATUS_WALKOVER): - eventStatus = domain.STATUS_WALKOVER - eventResultStats["STATUS_WALKOVER"] += 1 - case int64(domain.TIME_STATUS_INTERRUPTED): - eventStatus = domain.STATUS_INTERRUPTED - eventResultStats["STATUS_INTERRUPTED"] += 1 - case int64(domain.TIME_STATUS_ABANDONED): - eventStatus = domain.STATUS_ABANDONED - eventResultStats["STATUS_ABANDONED"] += 1 - case int64(domain.TIME_STATUS_RETIRED): - eventStatus = domain.STATUS_RETIRED - eventResultStats["STATUS_RETIRED"] += 1 - case int64(domain.TIME_STATUS_SUSPENDED): - eventStatus = domain.STATUS_SUSPENDED - eventResultStats["STATUS_SUSPENDED"] += 1 - case int64(domain.TIME_STATUS_DECIDED_BY_FA): - eventStatus = domain.STATUS_DECIDED_BY_FA - eventResultStats["STATUS_DECIDED_BY_FA"] += 1 - case int64(domain.TIME_STATUS_REMOVED): - eventStatus = domain.STATUS_REMOVED - eventResultStats["STATUS_REMOVED"] += 1 - default: - s.mongoLogger.Error( - "Invalid time status", - zap.Int64("time_status", timeStatus), - zap.Int64("eventID", event.ID), - ) - continue - } - - err = s.eventSvc.UpdateFinalScore(ctx, event.ID, commonResp.SS, eventStatus) - if err != nil { - s.mongoLogger.Error( - "Failed to update final score", - zap.Int64("eventID", event.ID), - zap.String("SS", commonResp.SS), - zap.String("eventStatus", string(eventStatus)), - zap.Error(err), - ) - continue - } - updated++ - // fmt.Printf("✅ Successfully updated event %v to %v (%d/%d) \n", event.ID, eventStatus, i+1, len(events)) - // s.mongoLogger.Info( - // "Updated Event Status", - // zap.Int64("eventID", eventID), - // zap.String("MatchName", event.MatchName), - // zap.String("SS", commonResp.SS), - // zap.String("status", string(eventStatus)), - // ) - // Update the league because the league country code is only found on the result response - if commonResp.League.ID == "" { - s.mongoLogger.Warn( - "League ID empty on result response", - zap.Int64("eventID", event.ID), - zap.String("leagueID", commonResp.League.ID), - ) - continue - } - leagueID, err := strconv.ParseInt(commonResp.League.ID, 10, 64) - if err != nil { - s.mongoLogger.Error( - "Invalid League ID", - zap.Int64("eventID", event.ID), - zap.Error(err), - ) - continue - } - - err = s.leagueSvc.UpdateLeague(ctx, domain.UpdateLeague{ - ID: int64(event.LeagueID), - CountryCode: domain.ValidString{ - Value: commonResp.League.CC, - Valid: true, - }, - Bet365ID: domain.ValidInt32{ - Value: int32(leagueID), - Valid: true, - }, - }) - - if err != nil { - s.mongoLogger.Error( - "Error Updating League", - zap.String("League Name", commonResp.League.Name), - zap.Int64("eventID", event.ID), - zap.Error(err), - ) - continue - } - // fmt.Printf("✅ Updated League %v with country code %v \n", leagueID, commonResp.League.CC) - // s.logger.Info("Updated League with country code", "leagueID", leagueID, "code", commonResp.League.CC) - leagueCountries = append(leagueCountries, commonResp.League.CC) - } - - if updated == 0 { - s.logger.Info("No events were updated") - s.mongoLogger.Info( - "No events were updated", - ) - return 0, nil - } - - if skipped != 0 { - s.mongoLogger.Info( - "Skipping updating event due to removal", - zap.Int("eventID", skipped), - ) - } - - s.mongoLogger.Info( - "Successfully updated expired events", - zap.Int("updated_events", updated), - zap.Int("total_events", len(events)), - zap.Int("updated_leagues countries", len(leagueCountries)), - zap.Any("event_result_stats", eventResultStats), - ) - return int64(updated), nil - -} - -// Gets a B365 Result and returns the outcomes that this result would give -func (s *Service) GetBet365ResultForEvent(ctx context.Context, b365EventID string) (json.RawMessage, []domain.BetOutcome, error) { - - result, err := s.FetchB365Result(ctx, b365EventID) - if err != nil { - s.mongoLogger.Error( - "Failed to fetch result", - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - } - if result.Success != 1 || len(result.Results) == 0 { - s.mongoLogger.Error( - "Invalid API result response", - zap.Any("result", result), - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return json.RawMessage{}, nil, fmt.Errorf("invalid API response for event %s", b365EventID) - } - - var commonResp domain.CommonResultResponse - if err := json.Unmarshal(result.Results[0], &commonResp); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal common result", - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return json.RawMessage{}, nil, err - } - if commonResp.SportID == "" { - s.mongoLogger.Warn( - "Sport ID is empty", - zap.String("b365EventID", b365EventID), - ) - return json.RawMessage{}, nil, fmt.Errorf("sport id empty for event: %v", b365EventID) - } - sportID, err := strconv.ParseInt(commonResp.SportID, 10, 32) - if err != nil { - s.mongoLogger.Error( - "Failed to parse sport id", - zap.String("sportID", commonResp.SportID), - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return json.RawMessage{}, nil, fmt.Errorf("failed to parse sport id: %w", err) - } - - expireUnix, err := strconv.ParseInt(commonResp.Time, 10, 64) - if err != nil { - s.mongoLogger.Error( - "Failed to parse expire time", - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return json.RawMessage{}, nil, fmt.Errorf("failed to parse expire time for event %s: %w", b365EventID, err) - } - - expires := time.Unix(expireUnix, 0) - - odds, err := s.oddSvc.FetchB365Odds(ctx, b365EventID) - if err != nil { - s.mongoLogger.Error( - "Failed to fetch non-live odds by event ID", - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return json.RawMessage{}, nil, fmt.Errorf("failed to fetch non-live odds for event %s: %w", b365EventID, err) - } - - parsedOddSections, err := s.oddSvc.ParseOddSections(ctx, odds.Results[0], int32(sportID)) - if err != nil { - s.mongoLogger.Error( - "Failed to parse odd section", - zap.Int64("sportID", sportID), - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return json.RawMessage{}, nil, fmt.Errorf("failed to parse odd section for event %v: %w", b365EventID, err) - } - - outcomes := make([]domain.BetOutcome, 0) - for _, section := range parsedOddSections.Sections { - for _, market := range section.Sp { - marketIDint, err := strconv.ParseInt(string(market.ID), 10, 64) - if err != nil { - s.mongoLogger.Warn( - "Invalid market id", - zap.Int64("market_id", marketIDint), - zap.String("market_name", market.Name), - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - continue - } - - isSupported, ok := domain.SupportedMarkets[marketIDint] - - if !ok || !isSupported { - s.mongoLogger.Info( - "Unsupported market", - zap.Int64("marketID", marketIDint), - zap.String("marketName", market.Name), - ) - continue - } - for _, oddRes := range market.Odds { - var odd domain.RawOdd - if err := json.Unmarshal(oddRes, &odd); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal odd", - zap.Int64("marketID", marketIDint), - zap.String("marketName", market.Name), - zap.Error(err), - ) - continue - } - - oddID, err := strconv.ParseInt(odd.ID, 10, 64) - - if err != nil { - s.mongoLogger.Error( - "Failed to parse odd id", - zap.String("odd_id", odd.ID), - zap.Int64("marketID", marketIDint), - zap.String("marketName", market.Name), - zap.Error(err), - ) - continue - } - - oddValue, err := strconv.ParseFloat(odd.Odds, 64) - if err != nil { - s.mongoLogger.Error( - "Failed to parse odd value", - zap.String("odd_id", odd.ID), - zap.String("odd_value", odd.Odds), - zap.Int64("marketID", marketIDint), - zap.String("marketName", market.Name), - zap.Error(err), - ) - continue - } - - outcome := domain.BetOutcome{ - MarketID: marketIDint, - OddID: oddID, - MarketName: market.Name, - OddHeader: odd.Header, - OddHandicap: odd.Handicap, - OddName: odd.Name, - Odd: float32(oddValue), - SportID: sportID, - HomeTeamName: commonResp.Home.Name, - AwayTeamName: commonResp.Away.Name, - Status: domain.OUTCOME_STATUS_PENDING, - Expires: expires, - BetID: 0, // This won't be set - } - outcomes = append(outcomes, outcome) - } - - } - - } - - if len(outcomes) == 0 { - s.mongoLogger.Warn( - "No outcomes found for event", - zap.String("b365EventID", b365EventID), - ) - return json.RawMessage{}, nil, fmt.Errorf("no outcomes found for event %v", b365EventID) - } - - s.mongoLogger.Info( - "Successfully fetched outcomes for event", - zap.String("b365EventID", b365EventID), - zap.Int("outcomes", len(outcomes)), - ) - - // Get results for outcome - for i, outcome := range outcomes { - // Parse the result based on sport type - parsedResult, err := s.ParseB365Result(result.Results[0], outcome, sportID) - if err != nil { - s.mongoLogger.Error( - "Failed to parse result for outcome", - zap.Int64("event_id", outcome.EventID), - zap.Int64("sport_id", sportID), - zap.Int64("outcomeID", outcome.ID), - zap.Error(err), - ) - return json.RawMessage{}, nil, fmt.Errorf("failed to parse result for outcome %d: %w", i, err) - } - outcomes[i].Status = parsedResult.Status - } - - return result.Results[0], outcomes, err -} - -// Fetch B365 Base Result -func (s *Service) FetchB365Result(ctx context.Context, b365EventID string) (domain.BaseResultResponse, error) { - url := fmt.Sprintf("https://api.b365api.com/v1/bet365/result?token=%s&event_id=%v", s.config.Bet365Token, b365EventID) - // url := fmt.Sprintf("https://api.b365api.com/v1/event/view?token=%s&event_id=%d", s.config.Bet365Token, eventID) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - s.mongoLogger.Error( - "Failed to create request", - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return domain.BaseResultResponse{}, err - } - - resp, err := s.client.Do(req) - if err != nil { - s.mongoLogger.Error( - "Failed to get fetch result response", - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return domain.BaseResultResponse{}, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - s.mongoLogger.Error( - "Unexpected status code", - zap.String("b365EventID", b365EventID), - zap.Int("status_code", resp.StatusCode), - zap.Error(err), - ) - return domain.BaseResultResponse{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } - - var resultResp domain.BaseResultResponse - if err := json.NewDecoder(resp.Body).Decode(&resultResp); err != nil { - s.mongoLogger.Error( - "Failed to decode result", - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return domain.BaseResultResponse{}, err - } - - if resultResp.Success != 1 || len(resultResp.Results) == 0 { - s.mongoLogger.Error( - "Invalid API response", - zap.String("b365EventID", b365EventID), - zap.Error(err), - ) - return domain.BaseResultResponse{}, fmt.Errorf("invalid API response") - } - - return resultResp, nil -} - -func (s *Service) ParseB365Result(resultResp json.RawMessage, outcome domain.BetOutcome, sportID int64) (domain.CreateResult, error) { - - var result domain.CreateResult - var err error - logFields := []zap.Field{ - zap.Int64("event id", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("sport_id", sportID), - } - switch sportID { - case domain.FOOTBALL: - result, err = s.parseFootball(resultResp, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse football", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.BASKETBALL: - result, err = s.parseBasketball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse basketball", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.ICE_HOCKEY: - result, err = s.parseIceHockey(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse ice hockey", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.CRICKET: - result, err = s.parseCricket(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse cricket", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.VOLLEYBALL: - result, err = s.parseVolleyball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse volleyball", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.DARTS: - result, err = s.parseDarts(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse darts", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.FUTSAL: - result, err = s.parseFutsal(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse futsal", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.AMERICAN_FOOTBALL: - result, err = s.parseNFL(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse american football", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.RUGBY_UNION: - result, err = s.parseRugbyUnion(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse rugby_union", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.RUGBY_LEAGUE: - result, err = s.parseRugbyLeague(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse rugby_league", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - case domain.BASEBALL: - result, err = s.parseBaseball(resultResp, outcome.EventID, outcome.OddID, outcome.MarketID, outcome) - if err != nil { - s.mongoLogger.Error("Failed to parse baseball", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, err - } - default: - s.mongoLogger.Error("Unsupported sport", append(logFields, zap.Error(err))...) - return domain.CreateResult{}, fmt.Errorf("unsupported sport: %v", sportID) - } - - return result, nil -} - -func (s *Service) parseFootball(resultRes json.RawMessage, outcome domain.BetOutcome) (domain.CreateResult, error) { - var fbResp domain.FootballResultResponse - if err := json.Unmarshal(resultRes, &fbResp); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal football result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - result := fbResp - - finalScore := parseSS(result.SS) - firstHalfScore := parseScore(result.Scores.FirstHalf.Home, result.Scores.FirstHalf.Away) - secondHalfScore := parseScore(result.Scores.SecondHalf.Home, result.Scores.SecondHalf.Away) - - corners := parseStats(result.Stats.Corners) - halfTimeCorners := parseStats(result.Stats.HalfTimeCorners) - - yellowCards := parseStats(result.Stats.YellowCards) - redCards := parseStats(result.Stats.RedCards) - onTarget := parseStats(result.Stats.OnTarget) - offTarget := parseStats(result.Stats.OffTarget) - - status, err := s.evaluateFootballOutcome(outcome, finalScore, firstHalfScore, secondHalfScore, corners, halfTimeCorners, yellowCards, redCards, onTarget, offTarget, result.Events) - if err != nil { - - s.mongoLogger.Error( - "Failed to evaluate football outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - - return domain.CreateResult{ - BetOutcomeID: 0, - EventID: outcome.EventID, - OddID: outcome.OddID, - MarketID: outcome.MarketID, - Status: status, - Score: result.SS, - }, nil - -} - -func (s *Service) parseBasketball(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var basketBallRes domain.BasketballResultResponse - - if err := json.Unmarshal(response, &basketBallRes); err != nil { - s.logger.Error("Failed to unmarshal basketball result", "event_id", eventID, "error", err) - s.mongoLogger.Error( - "Failed to unmarshal basketball result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - - return domain.CreateResult{}, err - } - - status, err := s.evaluateBasketballOutcome(outcome, basketBallRes) - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate basketball outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - - return domain.CreateResult{ - BetOutcomeID: 0, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - Score: basketBallRes.SS, - }, nil - -} - -func (s *Service) parseIceHockey(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var iceHockeyRes domain.IceHockeyResultResponse - if err := json.Unmarshal(response, &iceHockeyRes); err != nil { - s.logger.Error("Failed to unmarshal ice hockey result", "event_id", eventID, "error", err) - s.mongoLogger.Error( - "Failed to unmarshal ice hockey result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - - status, err := s.evaluateIceHockeyOutcome(outcome, iceHockeyRes) - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate ice hockey outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - - return domain.CreateResult{ - BetOutcomeID: 0, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - Score: iceHockeyRes.SS, - }, nil - -} - -func (s *Service) parseCricket(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var cricketRes domain.CricketResultResponse - - if err := json.Unmarshal(response, &cricketRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal cricket result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - - return domain.CreateResult{}, err - } - if cricketRes.TimeStatus != "3" { - s.mongoLogger.Warn( - "Match not yet completed", - zap.Int64("eventID", outcome.EventID), - zap.String("TimeStatus", cricketRes.TimeStatus), - ) - - return domain.CreateResult{}, fmt.Errorf("match not yet completed") - } - - status, err := s.evaluateCricketOutcome(outcome, cricketRes) - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate cricket outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - - return domain.CreateResult{ - BetOutcomeID: 0, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - }, nil -} - -func (s *Service) parseVolleyball(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var volleyballRes domain.VolleyballResultResponse - - if err := json.Unmarshal(response, &volleyballRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal volleyball result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - if volleyballRes.TimeStatus != "3" { - s.mongoLogger.Warn( - "Match not yet completed", - zap.Int64("eventID", outcome.EventID), - zap.String("TimeStatus", volleyballRes.TimeStatus), - ) - return domain.CreateResult{}, fmt.Errorf("match not yet completed") - } - - status, err := s.evaluateVolleyballOutcome(outcome, volleyballRes) - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate volleyball outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - - return domain.CreateResult{ - BetOutcomeID: 0, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - }, nil - -} - -func (s *Service) parseDarts(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var dartsRes domain.DartsResultResponse - - if err := json.Unmarshal(response, &dartsRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal darts result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - - return domain.CreateResult{}, err - } - - if dartsRes.TimeStatus != "3" { - s.logger.Warn("Match not yet completed", "event_id", eventID) - s.mongoLogger.Warn( - "Match not yet completed", - zap.Int64("eventID", outcome.EventID), - zap.String("TimeStatus", dartsRes.TimeStatus), - ) - return domain.CreateResult{}, fmt.Errorf("match not yet completed") - } - - // result for dart is limited - // only ss is given, format with 2-4 - status, err := s.evaluateDartsOutcome(outcome, dartsRes) - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate darts outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - - return domain.CreateResult{ - BetOutcomeID: 0, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - }, nil - -} - -func (s *Service) parseFutsal(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var futsalRes domain.FutsalResultResponse - - if err := json.Unmarshal(response, &futsalRes); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal futsal result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - - return domain.CreateResult{}, err - } - - if futsalRes.TimeStatus != "3" { - s.mongoLogger.Warn( - "Match not yet completed", - zap.Int64("eventID", outcome.EventID), - zap.String("TimeStatus", futsalRes.TimeStatus), - ) - return domain.CreateResult{}, fmt.Errorf("match not yet completed") - } - - status, err := s.evaluateFutsalOutcome(outcome, futsalRes) - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate futsal outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - - return domain.CreateResult{ - BetOutcomeID: 0, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - }, nil - -} - -func (s *Service) parseNFL(resultRes json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var nflResp domain.NFLResultResponse - if err := json.Unmarshal(resultRes, &nflResp); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal NFL result", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - - return domain.CreateResult{}, err - } - - if nflResp.TimeStatus != "3" { - s.mongoLogger.Warn( - "Match not yet completed", - zap.Int64("eventID", outcome.EventID), - zap.String("TimeStatus", nflResp.TimeStatus), - ) - return domain.CreateResult{}, fmt.Errorf("match not yet completed") - } - - status, err := s.evaluateNFLOutcome(outcome, nflResp) - - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate NFL outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - - return domain.CreateResult{ - BetOutcomeID: outcome.ID, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - Score: nflResp.SS, - }, nil -} - -func (s *Service) parseRugbyUnion(resultRes json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var rugbyResp domain.RugbyResultResponse - if err := json.Unmarshal(resultRes, &rugbyResp); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal Rugby Union result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - - return domain.CreateResult{}, err - } - if rugbyResp.TimeStatus != "3" { - s.mongoLogger.Warn( - "Match not yet completed", - zap.Int64("eventID", outcome.EventID), - zap.String("TimeStatus", rugbyResp.TimeStatus), - ) - return domain.CreateResult{}, fmt.Errorf("match not yet completed") - } - status, err := s.evaluateRugbyOutcome(outcome, rugbyResp) - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate rugby union outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - return domain.CreateResult{ - BetOutcomeID: outcome.ID, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - Score: rugbyResp.SS, - }, nil -} - -func (s *Service) parseRugbyLeague(resultRes json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var rugbyResp domain.RugbyResultResponse - if err := json.Unmarshal(resultRes, &rugbyResp); err != nil { - s.mongoLogger.Error( - "Failed to unmarshal Rugby League result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - - return domain.CreateResult{}, err - } - if rugbyResp.TimeStatus != "3" { - s.mongoLogger.Warn( - "Match not yet completed", - zap.Int64("eventID", outcome.EventID), - zap.String("TimeStatus", rugbyResp.TimeStatus), - ) - return domain.CreateResult{}, fmt.Errorf("match not yet completed") - } - status, err := s.evaluateRugbyOutcome(outcome, rugbyResp) - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate Rugby League outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - return domain.CreateResult{ - BetOutcomeID: outcome.ID, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - Score: rugbyResp.SS, - }, nil -} - -func (s *Service) parseBaseball(resultRes json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) { - var baseballResp domain.BaseballResultResponse - if err := json.Unmarshal(resultRes, &baseballResp); err != nil { - s.logger.Error("Failed to unmarshal Baseball result", "event_id", eventID, "error", err) - s.mongoLogger.Error( - "Failed to unmarshal Baseball result", - zap.Int64("eventID", outcome.EventID), - zap.Error(err), - ) - - return domain.CreateResult{}, err - } - if baseballResp.TimeStatus != "3" { - s.mongoLogger.Warn( - "Match not yet completed", - zap.Int64("eventID", outcome.EventID), - zap.String("TimeStatus", baseballResp.TimeStatus), - ) - return domain.CreateResult{}, fmt.Errorf("match not yet completed") - } - status, err := s.evaluateBaseballOutcome(outcome, baseballResp) - if err != nil { - s.mongoLogger.Error( - "Failed to evaluate baseball outcome", - zap.Int64("eventID", outcome.EventID), - zap.Int64("market_id", outcome.MarketID), - zap.Error(err), - ) - return domain.CreateResult{}, err - } - return domain.CreateResult{ - BetOutcomeID: outcome.ID, - EventID: eventID, - OddID: oddID, - MarketID: marketID, - Status: status, - Score: baseballResp.SS, - }, nil -} - -func parseScore(home string, away string) struct{ Home, Away int } { - homeVal, _ := strconv.Atoi(strings.TrimSpace(home)) - awaVal, _ := strconv.Atoi(strings.TrimSpace(away)) - return struct{ Home, Away int }{Home: homeVal, Away: awaVal} -} - -func parseSS(scoreStr string) struct{ Home, Away int } { - parts := strings.Split(scoreStr, "-") - if len(parts) != 2 { - return struct{ Home, Away int }{0, 0} - } - home, _ := strconv.Atoi(strings.TrimSpace(parts[0])) - away, _ := strconv.Atoi(strings.TrimSpace(parts[1])) - return struct{ Home, Away int }{Home: home, Away: away} -} - -func parseStats(stats []string) struct{ Home, Away int } { - if len(stats) != 2 { - return struct{ Home, Away int }{0, 0} - } - home, _ := strconv.Atoi(stats[0]) - away, _ := strconv.Atoi(stats[1]) - return struct{ Home, Away int }{Home: home, Away: away} -} - -// evaluateFootballOutcome determines the outcome status based on market type and odd. -// It uses helper functions to process the logic for each specific market. -func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore, - firstHalfScore, secondHalfScore struct{ Home, Away int }, - corners, halfTimeCorners struct{ Home, Away int }, - yellowCards struct{ Home, Away int }, - redCards struct{ Home, Away int }, - onTarget struct{ Home, Away int }, - offTarget struct{ Home, Away int }, - events []map[string]string) (domain.OutcomeStatus, error) { - - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - - switch outcome.MarketID { - case int64(domain.FOOTBALL_FULL_TIME_RESULT), int64(domain.FOOTBALL_FULL_TIME_RESULT_ENHANCED): - return evaluateFullTimeResult(outcome, finalScore) - case int64(domain.FOOTBALL_GOALS_OVER_UNDER): - return evaluateGoalsOverUnder(outcome, finalScore) - case int64(domain.FOOTBALL_CORRECT_SCORE): - return evaluateCorrectScore(outcome, finalScore) - case int64(domain.FOOTBALL_HALF_TIME_RESULT): - return evaluateHalfTimeResult(outcome, firstHalfScore) - case int64(domain.FOOTBALL_ASIAN_HANDICAP), int64(domain.FOOTBALL_ALTERNATIVE_ASIAN_HANDICAP): - return evaluateAsianHandicap(outcome, finalScore) - case int64(domain.FOOTBALL_GOAL_LINE), int64(domain.FOOTBALL_ALTERNATIVE_GOAL_LINE): - return evaluateGoalLine(outcome, finalScore) - case int64(domain.FOOTBALL_FIRST_HALF_ASIAN_HANDICAP), int64(domain.FOOTBALL_ALTERNATE_FIRST_HALF_ASIAN_HANDICAP): - return evaluateAsianHandicap(outcome, firstHalfScore) - case int64(domain.FOOTBALL_FIRST_HALF_GOAL_LINE), int64(domain.FOOTBALL_ALTERNATE_FIRST_HALF_GOAL_LINE): - return evaluateGoalLine(outcome, firstHalfScore) - case int64(domain.FOOTBALL_FIRST_TEAM_TO_SCORE): - return evaluateFirstTeamToScore(outcome, events) - case int64(domain.FOOTBALL_GOALS_ODD_EVEN): - return evaluateGoalsOddEven(outcome, finalScore) - case int64(domain.FOOTBALL_DOUBLE_CHANCE): - return evaluateDoubleChance(outcome, finalScore) - case int64(domain.FOOTBALL_DRAW_NO_BET): - return evaluateDrawNoBet(outcome, finalScore) - case int64(domain.FOOTBALL_CORNERS), int64(domain.FOOTBALL_CORNERS_TWO_WAY), int64(domain.FOOTBALL_ASIAN_TOTAL_CORNERS), int64(domain.FOOTBALL_ALTERNATIVE_CORNER): - return evaluateCorners(outcome, corners) - case int64(domain.FOOTBALL_FIRST_HALF_CORNERS), int64(domain.FOOTBALL_FIRST_HALF_ASIAN_CORNERS): - return evaluateCorners(outcome, halfTimeCorners) - case int64(domain.FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN): - return evaluateGoalsOddEven(outcome, firstHalfScore) - case int64(domain.FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN): - return evaluateGoalsOddEven(outcome, secondHalfScore) - case int64(domain.FOOTBALL_BOTH_TEAMS_TO_SCORE): - return evaluateBothTeamsToScore(outcome, finalScore) - case int64(domain.FOOTBALL_RESULT_BOTH_TEAMS_TO_SCORE): - return evaluateResultAndBTTS(outcome, finalScore) - case int64(domain.FOOTBALL_HALF_TIME_CORRECT_SCORE): - return evaluateCorrectScore(outcome, firstHalfScore) - case int64(domain.FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF): - return evaluateBothTeamsToScore(outcome, firstHalfScore) - case int64(domain.FOOTBALL_BOTH_TEAMS_TO_SCORE_SECOND_HALF): - return evaluateBothTeamsToScore(outcome, secondHalfScore) - case int64(domain.FOOTBALL_SECOND_HALF_RESULT): - return evaluateFullTimeResult(outcome, secondHalfScore) - case int64(domain.FOOTBALL_CLEAN_SHEET): - return evaluateCleanSheet(outcome, finalScore) - case int64(domain.FOOTBALL_LAST_TEAM_TO_SCORE): - return evaluateLastTeamToScore(outcome, events) - case int64(domain.FOOTBALL_WINNING_MARGIN): - return evaluateFootballWinningMargin(outcome, finalScore) - case int64(domain.FOOTBALL_BOTH_TEAMS_TO_RECEIVE_CARDS): - return evaluateBothTeamsToReceiveCards(outcome, yellowCards, redCards) - case int64(domain.FOOTBALL_HALF_TIME_DOUBLE_CHANCE): - return evaluateDoubleChance(outcome, firstHalfScore) - case int64(domain.FOOTBALL_HALF_TIME_RESULT_BOTH_TEAMS_TO_SCORE): - return evaluateResultAndBTTS(outcome, firstHalfScore) - case int64(domain.FOOTBALL_HALF_WITH_MOST_GOALS): - return evaluateHalfWithMostGoals(outcome, firstHalfScore, secondHalfScore) - case int64(domain.FOOTBALL_HOME_TEAM_WITH_HIGHEST_SCORING_HALF): - return evaluateTeamHighestScoringHalf(outcome, firstHalfScore, secondHalfScore, "home") - case int64(domain.FOOTBALL_AWAY_TEAM_WITH_HIGHEST_SCORING_HALF): - return evaluateTeamHighestScoringHalf(outcome, firstHalfScore, secondHalfScore, "away") - case int64(domain.FOOTBALL_SECOND_HALF_GOALS): - return evaluateGoalsOverUnder(outcome, secondHalfScore) - case int64(domain.FOOTBALL_TEAM_TOTAL_GOALS): - return evaluateTeamTotalGoals(outcome, finalScore) - case int64(domain.FOOTBALL_EXACT_TOTAL_GOALS): - return evaluateExactTotalGoals(outcome, finalScore) - case int64(domain.FOOTBALL_EXACT_FIRST_HALF_GOALS): - return evaluateExactTotalGoals(outcome, firstHalfScore) - case int64(domain.FOOTBALL_TEAMS_TO_SCORE): - return evaluateTeamsToScore(outcome, finalScore) - case int64(domain.FOOTBALL_EXACT_SECOND_HALF_GOALS): - return evaluateExactTotalGoals(outcome, secondHalfScore) - case int64(domain.FOOTBALL_FIRST_MATCH_CORNER): - return evaluateFirstMatchCorner(outcome, events) - case int64(domain.FOOTBALL_LAST_MATCH_CORNER): - return evaluateLastMatchCorner(outcome, events) - case int64(domain.FOOTBALL_CORNER_MATCH_BET): - return evaluateCornerMatchBet(outcome, corners) - case int64(domain.FOOTBALL_MULTI_CORNERS): - return evaluateMultiCorners(outcome, corners, halfTimeCorners) - case int64(domain.FOOTBALL_MATCH_SHOTS_ON_TARGET): - return evaluateMatchShotsOnTarget(outcome, onTarget) - case int64(domain.FOOTBALL_TEAM_SHOTS_ON_TARGET): - return evaluateTeamShotsOnTarget(outcome, onTarget) - case int64(domain.FOOTBALL_SPECIALS): - return evaluateSpecials(outcome, finalScore, firstHalfScore, secondHalfScore) - case int64(domain.FOOTBALL_ASIAN_HANDICAP_CORNERS), int64(domain.FOOTBALL_CORNER_HANDICAP): - return evaluateAsianHandicap(outcome, corners) // Re-use AsianHandicap logic with corner data - case int64(domain.FOOTBALL_ASIAN_TOTAL_CARDS), int64(domain.FOOTBALL_NUMBER_OF_CARDS_IN_MATCH): - // Assuming 1 point for a yellow card and 2 for a red card. - totalCards := yellowCards.Home + yellowCards.Away + redCards.Home + redCards.Away - cardScore := struct{ Home, Away int }{totalCards, 0} - return evaluateGoalLine(outcome, cardScore) // Re-use GoalLine logic - case int64(domain.FOOTBALL_TIME_OF_FIRST_GOAL_BRACKETS), int64(domain.FOOTBALL_EARLY_GOAL), int64(domain.FOOTBALL_LATE_GOAL): - return evaluateTimeOfFirstGoal(outcome, events) - - // --- Unimplemented Markets --- - // Reason: The logic for these markets often requires specific data points that are not available - // or not clearly structured in the provided result JSON and BetOutcome data structure. - case int64(domain.FOOTBALL_MATCH_GOAL_RANGE), - int64(domain.FOOTBALL_TEAM_GOAL_RANGE), - int64(domain.FOOTBALL_FIRST_HALF_GOAL_RANGE), - int64(domain.FOOTBALL_SECOND_HALF_GOAL_RANGE), - int64(domain.FOOTBALL_RESULT_GOAL_RANGE), - int64(domain.FOOTBALL_DOUBLE_CHANCE_GOAL_RANGE): - // Unimplemented: The logic requires a goal range (e.g., "1-2", "3-4"), often specified in odds data as 'ED'. - // This range data is not available as a field in the domain.BetOutcome struct provided to this function. - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: data for range (ED) not in BetOutcome") - case int64(domain.FOOTBALL_FIRST_HALF_HANDICAP), - int64(domain.FOOTBALL_ALTERNATE_FIRST_HALF_HANDICAP), - int64(domain.FOOTBALL_ALTERNATIVE_HANDICAP_RESULT), - int64(domain.FOOTBALL_HANDICAP_RESULT): - // Unimplemented: Standard handicap markets (3-way) require specific logic to handle the "Tie" outcome based on the handicap, - // which differs from Asian Handicap. This logic needs to be built out. - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: 3-way handicap logic is not yet built") - case int64(domain.FOOTBALL_TO_SCORE_IN_HALF), - int64(domain.FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF_SECOND_HALF): - // Unimplemented: The BetOutcome struct does not clearly distinguish which half the bet applies to for this market type. - // A field indicating "1st Half", "2nd Half", or "Yes/No" for each half is needed. - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: BetOutcome struct lacks required fields for half-specific bets") - case int64(domain.FOOTBALL_TEN_MINUTE_RESULT), - int64(domain.FOOTBALL_FIRST_TEN_MINUTE), - int64(domain.FOOTBALL_TIME_OF_FIRST_TEAM_GOAL), - int64(domain.FOOTBALL_TIME_OF_FIRST_CARD), - int64(domain.FOOTBALL_TIME_OF_FIRST_CORNER): - // Unimplemented: Requires parsing event timestamps and comparing them to specific minute markers. - // While event data is available, the specific logic for each of these time-based markets needs to be implemented. - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: requires detailed time-based event parsing logic") - case int64(domain.FOOTBALL_FIRST_GOAL_METHOD), - int64(domain.FOOTBALL_OWN_GOAL), - int64(domain.FOOTBALL_TO_MISS_PENALTY): - // Unimplemented: The event result data does not specify the method of the goal (e.g., Shot, Header, Penalty, Own Goal) - // or provide details about penalties that were missed. - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: result data lacks goal method or missed penalty info") - case int64(domain.FOOTBALL_GOALSCORER), - int64(domain.FOOTBALL_MULTI_SCORERS), - int64(domain.FOOTBALL_PLAYER_TO_SCORE_ASSIST), - int64(domain.FOOTBALL_PLAYER_CARD), - int64(domain.FOOTBALL_PLAYER_SHOTS_ON_TARGET), - int64(domain.FOOTBALL_PLAYER_SHOTS), - int64(domain.FOOTBALL_TEAM_GOALSCORER): - // Unimplemented: All player-specific markets require data mapping player names to actions (goals, cards, shots). - // The provided event result data is anonymous (e.g., "Goal - (Temperley)") and does not contain this information. - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: requires player-specific data not present in results") - - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - } -} - -func (s *Service) evaluateBasketballOutcome(outcome domain.BetOutcome, res domain.BasketballResultResponse) (domain.OutcomeStatus, error) { - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - - finalScore := parseSS(res.SS) - - firstHalfScore := parseScore(res.Scores.FirstHalf.Home, res.Scores.FirstHalf.Away) - secondHalfScore := struct{ Home, Away int }{Home: finalScore.Home - firstHalfScore.Home, Away: finalScore.Away - firstHalfScore.Away} - - firstQuarter := parseScore(res.Scores.FirstQuarter.Home, res.Scores.FirstQuarter.Away) - secondQuarter := parseScore(res.Scores.SecondQuarter.Home, res.Scores.SecondQuarter.Away) - thirdQuarter := parseScore(res.Scores.ThirdQuarter.Home, res.Scores.ThirdQuarter.Away) - fourthQuarter := parseScore(res.Scores.FourthQuarter.Home, res.Scores.FourthQuarter.Away) - - switch outcome.MarketID { - case int64(domain.BASKETBALL_GAME_LINES): - return evaluateGameLines(outcome, finalScore) - case int64(domain.BASKETBALL_RESULT_AND_BOTH_TEAMS_TO_SCORE_X_POINTS): - return evaluateResultAndBTTSX(outcome, finalScore) - case int64(domain.BASKETBALL_DOUBLE_RESULT): - return evaluateDoubleResult(outcome, firstHalfScore, secondHalfScore) - case int64(domain.BASKETBALL_MATCH_RESULT_AND_TOTAL): - return evaluateResultAndTotal(outcome, finalScore) - case int64(domain.BASKETBALL_MATCH_HANDICAP_AND_TOTAL): - return evaluateHandicapAndTotal(outcome, finalScore) - case int64(domain.BASKETBALL_GAME_TOTAL_ODD_EVEN): - return evaluateGoalsOddEven(outcome, finalScore) - case int64(domain.BASKETBALL_TEAM_TOTALS): - return evaluateTeamTotal(outcome, finalScore) - case int64(domain.BASKETBALL_TEAM_TOTAL_ODD_EVEN): - return evaluateTeamOddEven(outcome, finalScore) - - case int64(domain.BASKETBALL_FIRST_HALF): - return evaluateGameLines(outcome, firstHalfScore) - case int64(domain.BASKETBALL_FIRST_HALF_TEAM_TOTALS): - return evaluateTeamTotal(outcome, firstHalfScore) - case int64(domain.BASKETBALL_FIRST_HALF_HANDICAP_AND_TOTAL): - return evaluateHandicapAndTotal(outcome, firstHalfScore) - case int64(domain.BASKETBALL_FIRST_HALF_BOTH_TEAMS_TO_SCORE_X_POINTS): - return evaluateBTTSX(outcome, firstHalfScore) - case int64(domain.BASKETBALL_FIRST_HALF_DOUBLE_CHANCE): - return evaluateDoubleChance(outcome, firstHalfScore) - case int64(domain.BASKETBALL_FIRST_HALF_TOTAL_ODD_EVEN): - return evaluateGoalsOddEven(outcome, firstHalfScore) - case int64(domain.BASKETBALL_FIRST_HALF_MONEY_LINE_3_WAY): - return evaluateMoneyLine3Way(outcome, firstHalfScore) - case int64(domain.BASKETBALL_HIGHEST_SCORING_HALF): - return evaluateHighestScoringHalf(outcome, firstHalfScore, secondHalfScore) - - case int64(domain.BASKETBALL_FIRST_QUARTER): - return evaluateGameLines(outcome, firstQuarter) - case int64(domain.BASKETBALL_FIRST_QUARTER_TEAM_TOTALS): - return evaluateTeamTotal(outcome, firstQuarter) - case int64(domain.BASKETBALL_FIRST_QUARTER_TOTAL_ODD_EVEN): - return evaluateGoalsOddEven(outcome, firstQuarter) - case int64(domain.BASKETBALL_FIRST_QUARTER_HANDICAP_AND_TOTAL): - return evaluateHandicapAndTotal(outcome, firstQuarter) - case int64(domain.BASKETBALL_FIRST_QUARTER_DOUBLE_CHANCE): - return evaluateDoubleChance(outcome, firstQuarter) - case int64(domain.BASKETBALL_HIGHEST_SCORING_QUARTER): - return evaluateHighestScoringQuarter(outcome, firstQuarter, secondQuarter, thirdQuarter, fourthQuarter) - case int64(domain.BASKETBALL_FIRST_QUARTER_RESULT_AND_TOTAL): - return evaluateResultAndTotal(outcome, firstQuarter) - - case int64(domain.BASKETBALL_TEAM_WITH_HIGHEST_SCORING_QUARTER): - return evaluateTeamWithHighestScoringQuarter(outcome, firstQuarter, secondQuarter, thirdQuarter, fourthQuarter) - - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - } -} - -func (s *Service) evaluateIceHockeyOutcome(outcome domain.BetOutcome, res domain.IceHockeyResultResponse) (domain.OutcomeStatus, error) { - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - finalScore := parseSS(res.SS) - firstPeriod := parseScore(res.Scores.FirstPeriod.Home, res.Scores.FirstPeriod.Away) - secondPeriod := parseScore(res.Scores.SecondPeriod.Home, res.Scores.SecondPeriod.Away) - thirdPeriod := parseScore(res.Scores.ThirdPeriod.Home, res.Scores.ThirdPeriod.Away) - - regulation := []struct{ Home, Away int }{ - firstPeriod, - secondPeriod, - thirdPeriod, - } - switch outcome.MarketID { - case int64(domain.ICE_HOCKEY_GAME_LINES): - return evaluateGameLines(outcome, finalScore) - case int64(domain.ICE_HOCKEY_THREE_WAY): - return evaluateGameLines(outcome, finalScore) - case int64(domain.ICE_HOCKEY_FIRST_PERIOD): - return evaluateGameLines(outcome, firstPeriod) - case int64(domain.ICE_HOCKEY_DRAW_NO_BET): - return evaluateDrawNoBet(outcome, finalScore) - case int64(domain.ICE_HOCKEY_DOUBLE_CHANCE): - return evaluateDoubleChance(outcome, finalScore) - case int64(domain.ICE_HOCKEY_WINNING_MARGIN): - return evaluateWinningMargin(outcome, finalScore) - case int64(domain.ICE_HOCKEY_HIGHEST_SCORING_PERIOD): - return evaluateHighestScoringPeriod(outcome, firstPeriod, secondPeriod, thirdPeriod) - case int64(domain.ICE_HOCKEY_TIED_AFTER_REGULATION): - return evaluateTiedAfterRegulation(outcome, regulation) - case int64(domain.ICE_HOCKEY_GAME_TOTAL_ODD_EVEN): - return evaluateGoalsOddEven(outcome, finalScore) - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - } -} - -func (s *Service) evaluateCricketOutcome(outcome domain.BetOutcome, res domain.CricketResultResponse) (domain.OutcomeStatus, error) { - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - - score := parseSS(res.SS) - - switch outcome.MarketID { - case int64(domain.CRICKET_TO_WIN_THE_MATCH): - return evaluateFullTimeResult(outcome, score) - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - } - -} - -func (s *Service) evaluateVolleyballOutcome(outcome domain.BetOutcome, res domain.VolleyballResultResponse) (domain.OutcomeStatus, error) { - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - - score := parseSS(res.SS) - - // res.SS example: { 2-3 } is the win count not actuall score of sets - // for total score we need every set's score - firstSet := parseScore(res.Scores.FirstSet.Home, res.Scores.FirstSet.Away) - secondSet := parseScore(res.Scores.SecondSet.Home, res.Scores.SecondSet.Away) - thirdSet := parseScore(res.Scores.ThirdSet.Home, res.Scores.ThirdSet.Away) - fourthSet := parseScore(res.Scores.FourthSet.Home, res.Scores.FourthSet.Away) - fivethSet := parseScore(res.Scores.FivethSet.Home, res.Scores.FivethSet.Away) - - totalScore := struct{ Home, Away int }{Home: 0, Away: 0} - totalScore.Home = firstSet.Home + secondSet.Home + thirdSet.Home + fourthSet.Home + fivethSet.Home - totalScore.Away = firstSet.Away + secondSet.Away + thirdSet.Away + fourthSet.Away + fivethSet.Away - - switch outcome.MarketID { - case int64(domain.VOLLEYBALL_GAME_LINES): - return evaluateVolleyballGamelines(outcome, totalScore) - case int64(domain.VOLLEYBALL_MATCH_TOTAL_ODD_EVEN): - return evaluateGoalsOddEven(outcome, totalScore) - case int64(domain.VOLLEYBALL_CORRECT_SET_SCORE): - return evaluateCorrectScore(outcome, score) - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - - } - -} - -func (s *Service) evaluateDartsOutcome(outcome domain.BetOutcome, res domain.DartsResultResponse) (domain.OutcomeStatus, error) { - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - - score := parseSS(res.SS) - - switch outcome.MarketID { - case int64(domain.DARTS_MATCH_WINNER): - return evaluateFullTimeResult(outcome, score) - case int64(domain.DARTS_TOTAL_LEGS): - return evaluateTotalLegs(outcome, score) - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - - } - -} - -func (s *Service) evaluateFutsalOutcome(outcome domain.BetOutcome, res domain.FutsalResultResponse) (domain.OutcomeStatus, error) { - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - - score := parseSS(res.SS) - - switch outcome.MarketID { - case int64(domain.FUTSAL_GAME_LINES): - return evaluateGameLines(outcome, score) - case int64(domain.FUTSAL_MONEY_LINE): - return evaluateMoneyLine(outcome, score) - case int64(domain.FUTSAL_TEAM_TO_SCORE_FIRST): - return evaluateFirstTeamToScore(outcome, res.Events) - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - - } - -} - -func (s *Service) evaluateNFLOutcome(outcome domain.BetOutcome, res domain.NFLResultResponse) (domain.OutcomeStatus, error) { - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - - finalScore := parseSS(res.SS) - - switch outcome.MarketID { - case int64(domain.AMERICAN_FOOTBALL_GAME_LINES): - return evaluateGameLines(outcome, finalScore) - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - } -} - -func (s *Service) evaluateRugbyOutcome(outcome domain.BetOutcome, result domain.RugbyResultResponse) (domain.OutcomeStatus, error) { - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - - finalScore := parseSS(result.SS) - - switch outcome.MarketID { - case int64(domain.RUGBY_L_GAME_BETTING_2_WAY): - return evaluateGameBettingTwoWay(outcome, finalScore) - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - } -} - -func (s *Service) evaluateBaseballOutcome(outcome domain.BetOutcome, res domain.BaseballResultResponse) (domain.OutcomeStatus, error) { - if !domain.SupportedMarkets[outcome.MarketID] { - s.mongoLogger.Warn( - "Unsupported market type", - zap.String("market_name", outcome.MarketName), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName) - } - - finalScore := parseSS(res.SS) - - switch outcome.MarketID { - case int64(domain.BASEBALL_GAME_LINES): - return evaluateGameLines(outcome, finalScore) - default: - s.mongoLogger.Warn( - "Market type not implemented", - zap.String("market_name", outcome.MarketName), - zap.Int64("market_id", outcome.MarketID), - zap.Int64("outcome_id", outcome.ID), - ) - return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName) - } -} diff --git a/internal/services/result/service_test.go b/internal/services/result/service_test.go deleted file mode 100644 index 2705049..0000000 --- a/internal/services/result/service_test.go +++ /dev/null @@ -1 +0,0 @@ -package result diff --git a/internal/services/result/sports_eval.go b/internal/services/result/sports_eval.go deleted file mode 100644 index 2a4f013..0000000 --- a/internal/services/result/sports_eval.go +++ /dev/null @@ -1,286 +0,0 @@ -package result - -import ( - "fmt" - "strconv" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -// NFL evaluations -func EvaluateNFLMoneyLine(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddHeader { - case "1": - if score.Home > score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": - if score.Away > score.Home { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } -} - -func EvaluateNFLSpread(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - handicap, err := strconv.ParseFloat(outcome.OddHandicap, 64) - if err != nil { - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid handicap: %s", outcome.OddHandicap) - } - - adjustedHomeScore := float64(score.Home) - adjustedAwayScore := float64(score.Away) - - switch outcome.OddHeader { - case "1": - adjustedHomeScore += handicap - case "2": - adjustedAwayScore += handicap - default: - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } - - if adjustedHomeScore > adjustedAwayScore { - if outcome.OddHeader == "1" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } else if adjustedHomeScore < adjustedAwayScore { - if outcome.OddHeader == "2" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - return domain.OUTCOME_STATUS_VOID, nil -} - -func EvaluateNFLTotalPoints(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - totalPoints := float64(score.Home + score.Away) - threshold, err := strconv.ParseFloat(outcome.OddName, 64) - if err != nil { - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - - switch outcome.OddHeader { - case "Over": - if totalPoints > threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if totalPoints == threshold { - return domain.OUTCOME_STATUS_VOID, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Under": - if totalPoints < threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if totalPoints == threshold { - return domain.OUTCOME_STATUS_VOID, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) -} - -// EvaluateRugbyMoneyLine Evaluates Rugby money line bets -func EvaluateRugbyMoneyLine(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddHeader { - case "1": - if score.Home > score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": - if score.Away > score.Home { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } -} - -// EvaluateRugbySpread Evaluates Rugby spread bets -func EvaluateRugbySpread(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - handicap, err := strconv.ParseFloat(outcome.OddHandicap, 64) - if err != nil { - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid handicap: %s", outcome.OddHandicap) - } - - adjustedHomeScore := float64(score.Home) - adjustedAwayScore := float64(score.Away) - - switch outcome.OddHeader { - case "1": - adjustedHomeScore += handicap - case "2": - adjustedAwayScore += handicap - default: - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } - - if adjustedHomeScore > adjustedAwayScore { - if outcome.OddHeader == "1" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } else if adjustedHomeScore < adjustedAwayScore { - if outcome.OddHeader == "2" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - return domain.OUTCOME_STATUS_VOID, nil -} - -// EvaluateRugbyTotalPoints Evaluates Rugby total points bets -func EvaluateRugbyTotalPoints(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - totalPoints := float64(score.Home + score.Away) - threshold, err := strconv.ParseFloat(outcome.OddName, 64) - if err != nil { - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - - switch outcome.OddHeader { - case "Over": - if totalPoints > threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if totalPoints == threshold { - return domain.OUTCOME_STATUS_VOID, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Under": - if totalPoints < threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if totalPoints == threshold { - return domain.OUTCOME_STATUS_VOID, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) -} - -// EvaluateBaseballMoneyLine Evaluates Baseball money line bets -func EvaluateBaseballMoneyLine(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddHeader { - case "1": - if score.Home > score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": - if score.Away > score.Home { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } -} - -// EvaluateBaseballSpread Evaluates Baseball spread bets -func EvaluateBaseballSpread(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - handicap, err := strconv.ParseFloat(outcome.OddHandicap, 64) - if err != nil { - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid handicap: %s", outcome.OddHandicap) - } - - adjustedHomeScore := float64(score.Home) - adjustedAwayScore := float64(score.Away) - - switch outcome.OddHeader { - case "1": - adjustedHomeScore += handicap - case "2": - adjustedAwayScore += handicap - default: - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } - - if adjustedHomeScore > adjustedAwayScore { - if outcome.OddHeader == "1" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } else if adjustedHomeScore < adjustedAwayScore { - if outcome.OddHeader == "2" { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - return domain.OUTCOME_STATUS_VOID, nil -} - -// EvaluateBaseballTotalRuns Evaluates Baseball total runs bets -func EvaluateBaseballTotalRuns(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - totalRuns := float64(score.Home + score.Away) - threshold, err := strconv.ParseFloat(outcome.OddName, 64) - if err != nil { - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName) - } - - switch outcome.OddHeader { - case "Over": - if totalRuns > threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if totalRuns == threshold { - return domain.OUTCOME_STATUS_VOID, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "Under": - if totalRuns < threshold { - return domain.OUTCOME_STATUS_WIN, nil - } else if totalRuns == threshold { - return domain.OUTCOME_STATUS_VOID, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - } - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) -} - -// EvaluateBaseballFirstInning Evaluates Baseball first inning bets -func EvaluateBaseballFirstInning(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddHeader { - case "1": - if score.Home > score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": - if score.Away > score.Home { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "X": - if score.Home == score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } -} - -// EvaluateBaseballFirst5Innings Evaluates Baseball first 5 innings bets -func EvaluateBaseballFirst5Innings(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) { - switch outcome.OddHeader { - case "1": - if score.Home > score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "2": - if score.Away > score.Home { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - case "X": - if score.Home == score.Away { - return domain.OUTCOME_STATUS_WIN, nil - } - return domain.OUTCOME_STATUS_LOSS, nil - default: - return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader) - } -} diff --git a/internal/services/result/sports_eval_test.go b/internal/services/result/sports_eval_test.go deleted file mode 100644 index f45bd58..0000000 --- a/internal/services/result/sports_eval_test.go +++ /dev/null @@ -1,980 +0,0 @@ -package result - -// import ( -// "fmt" -// "testing" -// "time" - -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -// "github.com/stretchr/testify/assert" -// ) - -// // TestNFLMarkets covers all American Football (NFL) market types defined in the domain. -// // For each market (Money Line, Spread, Total Points), it tests home/away win, draw, void, and invalid input scenarios. -// func TestNFLMarkets(t *testing.T) { -// t.Log("Testing NFL (American Football) Markets") -// markets := []struct { -// marketID int64 -// name string -// }{ -// {int64(domain.AMERICAN_FOOTBALL_MONEY_LINE), "MONEY_LINE"}, -// {int64(domain.AMERICAN_FOOTBALL_SPREAD), "SPREAD"}, -// {int64(domain.AMERICAN_FOOTBALL_TOTAL_POINTS), "TOTAL_POINTS"}, -// } - -// for _, m := range markets { -// t.Run(m.name, func(t *testing.T) { -// // Each subtest below covers a key scenario for the given NFL market. -// switch m.marketID { -// case int64(domain.AMERICAN_FOOTBALL_MONEY_LINE): -// // Home win, away win, draw, and invalid OddHeader for Money Line -// t.Run("Home Win", func(t *testing.T) { -// status, err := EvaluateNFLMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1"}, struct{ Home, Away int }{Home: 21, Away: 14}) -// t.Logf("Market: %s, Scenario: Home Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Away Win", func(t *testing.T) { -// status, err := EvaluateNFLMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "2"}, struct{ Home, Away int }{Home: 14, Away: 21}) -// t.Logf("Market: %s, Scenario: Away Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Draw", func(t *testing.T) { -// status, err := EvaluateNFLMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1"}, struct{ Home, Away int }{Home: 17, Away: 17}) -// t.Logf("Market: %s, Scenario: Draw", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_LOSS, status) -// }) -// t.Run("Invalid OddHeader", func(t *testing.T) { -// status, err := EvaluateNFLMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "X"}, struct{ Home, Away int }{Home: 10, Away: 7}) -// t.Logf("Market: %s, Scenario: Invalid OddHeader", m.name) -// assert.Error(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_PENDING, status) -// }) -// case int64(domain.AMERICAN_FOOTBALL_SPREAD): -// t.Run("Home Win with Handicap", func(t *testing.T) { -// status, err := EvaluateNFLSpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1", OddHandicap: "-3.5"}, struct{ Home, Away int }{Home: 24, Away: 20}) -// t.Logf("Market: %s, Scenario: Home Win with Handicap", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Away Win with Handicap", func(t *testing.T) { -// status, err := EvaluateNFLSpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "2", OddHandicap: "+3.5"}, struct{ Home, Away int }{Home: 20, Away: 24}) -// t.Logf("Market: %s, Scenario: Away Win with Handicap", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Push (Void)", func(t *testing.T) { -// status, err := EvaluateNFLSpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1", OddHandicap: "0"}, struct{ Home, Away int }{Home: 21, Away: 21}) -// t.Logf("Market: %s, Scenario: Push (Void)", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_VOID, status) -// }) -// t.Run("Non-numeric Handicap", func(t *testing.T) { -// status, err := EvaluateNFLSpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1", OddHandicap: "notanumber"}, struct{ Home, Away int }{Home: 21, Away: 14}) -// t.Logf("Market: %s, Scenario: Non-numeric Handicap", m.name) -// assert.Error(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_PENDING, status) -// }) -// case int64(domain.AMERICAN_FOOTBALL_TOTAL_POINTS): -// t.Run("Over Win", func(t *testing.T) { -// status, err := EvaluateNFLTotalPoints(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Over", OddName: "44.5"}, struct{ Home, Away int }{Home: 30, Away: 20}) -// t.Logf("Market: %s, Scenario: Over Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Under Win", func(t *testing.T) { -// status, err := EvaluateNFLTotalPoints(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Under", OddName: "44.5"}, struct{ Home, Away int }{Home: 20, Away: 17}) -// t.Logf("Market: %s, Scenario: Under Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Push (Void)", func(t *testing.T) { -// status, err := EvaluateNFLTotalPoints(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Over", OddName: "37"}, struct{ Home, Away int }{Home: 20, Away: 17}) -// t.Logf("Market: %s, Scenario: Push (Void)", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_VOID, status) -// }) -// t.Run("Non-numeric OddName", func(t *testing.T) { -// status, err := EvaluateNFLTotalPoints(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Over", OddName: "notanumber"}, struct{ Home, Away int }{Home: 20, Away: 17}) -// t.Logf("Market: %s, Scenario: Non-numeric OddName", m.name) -// assert.Error(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_PENDING, status) -// }) -// } -// }) -// } -// } - -// // TestRugbyMarkets covers all Rugby (Union & League) market types defined in the domain. -// // For each market (Money Line, Spread, Handicap, Total Points), it tests home/away win, draw, void, and invalid input scenarios. -// func TestRugbyMarkets(t *testing.T) { -// t.Log("Testing Rugby Markets (Union & League)") -// markets := []struct { -// marketID int64 -// name string -// }{ -// {int64(domain.RUGBY_MONEY_LINE), "MONEY_LINE"}, -// {int64(domain.RUGBY_SPREAD), "SPREAD"}, -// {int64(domain.RUGBY_TOTAL_POINTS), "TOTAL_POINTS"}, -// {int64(domain.RUGBY_HANDICAP), "HANDICAP"}, -// } - -// for _, m := range markets { -// t.Run(m.name, func(t *testing.T) { -// // Each subtest below covers a key scenario for the given Rugby market. -// switch m.marketID { -// case int64(domain.RUGBY_MONEY_LINE): -// // Home win, away win, draw, and invalid OddHeader for Money Line -// t.Run("Home Win", func(t *testing.T) { -// status, err := EvaluateRugbyMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1"}, struct{ Home, Away int }{Home: 30, Away: 20}) -// t.Logf("Market: %s, Scenario: Home Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Away Win", func(t *testing.T) { -// status, err := EvaluateRugbyMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "2"}, struct{ Home, Away int }{Home: 20, Away: 30}) -// t.Logf("Market: %s, Scenario: Away Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Draw", func(t *testing.T) { -// status, err := EvaluateRugbyMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1"}, struct{ Home, Away int }{Home: 25, Away: 25}) -// t.Logf("Market: %s, Scenario: Draw", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_LOSS, status) -// }) -// t.Run("Invalid OddHeader", func(t *testing.T) { -// status, err := EvaluateRugbyMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "X"}, struct{ Home, Away int }{Home: 10, Away: 7}) -// t.Logf("Market: %s, Scenario: Invalid OddHeader", m.name) -// assert.Error(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_PENDING, status) -// }) -// case int64(domain.RUGBY_SPREAD), int64(domain.RUGBY_HANDICAP): -// t.Run("Home Win with Handicap", func(t *testing.T) { -// status, err := EvaluateRugbySpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1", OddHandicap: "-6.5"}, struct{ Home, Away int }{Home: 28, Away: 20}) -// t.Logf("Market: %s, Scenario: Home Win with Handicap", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Away Win with Handicap", func(t *testing.T) { -// status, err := EvaluateRugbySpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "2", OddHandicap: "+6.5"}, struct{ Home, Away int }{Home: 20, Away: 28}) -// t.Logf("Market: %s, Scenario: Away Win with Handicap", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Push (Void)", func(t *testing.T) { -// status, err := EvaluateRugbySpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1", OddHandicap: "0"}, struct{ Home, Away int }{Home: 21, Away: 21}) -// t.Logf("Market: %s, Scenario: Push (Void)", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_VOID, status) -// }) -// t.Run("Non-numeric Handicap", func(t *testing.T) { -// status, err := EvaluateRugbySpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1", OddHandicap: "notanumber"}, struct{ Home, Away int }{Home: 21, Away: 14}) -// t.Logf("Market: %s, Scenario: Non-numeric Handicap", m.name) -// assert.Error(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_PENDING, status) -// }) -// case int64(domain.RUGBY_TOTAL_POINTS): -// t.Run("Over Win", func(t *testing.T) { -// status, err := EvaluateRugbyTotalPoints(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Over", OddName: "40.5"}, struct{ Home, Away int }{Home: 25, Away: 20}) -// t.Logf("Market: %s, Scenario: Over Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Under Win", func(t *testing.T) { -// status, err := EvaluateRugbyTotalPoints(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Under", OddName: "40.5"}, struct{ Home, Away int }{Home: 15, Away: 20}) -// t.Logf("Market: %s, Scenario: Under Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Push (Void)", func(t *testing.T) { -// status, err := EvaluateRugbyTotalPoints(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Over", OddName: "35"}, struct{ Home, Away int }{Home: 20, Away: 15}) -// t.Logf("Market: %s, Scenario: Push (Void)", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_VOID, status) -// }) -// t.Run("Non-numeric OddName", func(t *testing.T) { -// status, err := EvaluateRugbyTotalPoints(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Over", OddName: "notanumber"}, struct{ Home, Away int }{Home: 20, Away: 15}) -// t.Logf("Market: %s, Scenario: Non-numeric OddName", m.name) -// assert.Error(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_PENDING, status) -// }) -// } -// }) -// } -// } - -// // TestBaseballMarkets covers all Baseball market types defined in the domain. -// // For each market (Money Line, Spread, Total Runs), it tests home/away win, draw, void, and invalid input scenarios. -// func TestBaseballMarkets(t *testing.T) { -// t.Log("Testing Baseball Markets") -// markets := []struct { -// marketID int64 -// name string -// }{ -// {int64(domain.BASEBALL_MONEY_LINE), "MONEY_LINE"}, -// {int64(domain.BASEBALL_SPREAD), "SPREAD"}, -// {int64(domain.BASEBALL_TOTAL_RUNS), "TOTAL_RUNS"}, -// } - -// for _, m := range markets { -// t.Run(m.name, func(t *testing.T) { -// // Each subtest below covers a key scenario for the given Baseball market. -// switch m.marketID { -// case int64(domain.BASEBALL_MONEY_LINE): -// // Home win, away win, draw, and invalid OddHeader for Money Line -// t.Run("Home Win", func(t *testing.T) { -// status, err := EvaluateBaseballMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1"}, struct{ Home, Away int }{Home: 6, Away: 3}) -// t.Logf("Market: %s, Scenario: Home Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Away Win", func(t *testing.T) { -// status, err := EvaluateBaseballMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "2"}, struct{ Home, Away int }{Home: 2, Away: 5}) -// t.Logf("Market: %s, Scenario: Away Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Draw", func(t *testing.T) { -// status, err := EvaluateBaseballMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1"}, struct{ Home, Away int }{Home: 4, Away: 4}) -// t.Logf("Market: %s, Scenario: Draw", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_LOSS, status) -// }) -// t.Run("Invalid OddHeader", func(t *testing.T) { -// status, err := EvaluateBaseballMoneyLine(domain.BetOutcome{MarketID: m.marketID, OddHeader: "X"}, struct{ Home, Away int }{Home: 10, Away: 7}) -// t.Logf("Market: %s, Scenario: Invalid OddHeader", m.name) -// assert.Error(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_PENDING, status) -// }) -// case int64(domain.BASEBALL_SPREAD): -// t.Run("Home Win with Handicap", func(t *testing.T) { -// status, err := EvaluateBaseballSpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1", OddHandicap: "-1.5"}, struct{ Home, Away int }{Home: 5, Away: 3}) -// t.Logf("Market: %s, Scenario: Home Win with Handicap", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Away Win with Handicap", func(t *testing.T) { -// status, err := EvaluateBaseballSpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "2", OddHandicap: "+1.5"}, struct{ Home, Away int }{Home: 3, Away: 5}) -// t.Logf("Market: %s, Scenario: Away Win with Handicap", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Push (Void)", func(t *testing.T) { -// status, err := EvaluateBaseballSpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1", OddHandicap: "0"}, struct{ Home, Away int }{Home: 4, Away: 4}) -// t.Logf("Market: %s, Scenario: Push (Void)", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_VOID, status) -// }) -// t.Run("Non-numeric Handicap", func(t *testing.T) { -// status, err := EvaluateBaseballSpread(domain.BetOutcome{MarketID: m.marketID, OddHeader: "1", OddHandicap: "notanumber"}, struct{ Home, Away int }{Home: 5, Away: 3}) -// t.Logf("Market: %s, Scenario: Non-numeric Handicap", m.name) -// assert.Error(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_PENDING, status) -// }) -// case int64(domain.BASEBALL_TOTAL_RUNS): -// t.Run("Over Win", func(t *testing.T) { -// status, err := EvaluateBaseballTotalRuns(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Over", OddName: "7.5"}, struct{ Home, Away int }{Home: 5, Away: 4}) -// t.Logf("Market: %s, Scenario: Over Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Under Win", func(t *testing.T) { -// status, err := EvaluateBaseballTotalRuns(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Under", OddName: "7.5"}, struct{ Home, Away int }{Home: 2, Away: 3}) -// t.Logf("Market: %s, Scenario: Under Win", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_WIN, status) -// }) -// t.Run("Push (Void)", func(t *testing.T) { -// status, err := EvaluateBaseballTotalRuns(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Over", OddName: "7"}, struct{ Home, Away int }{Home: 4, Away: 3}) -// t.Logf("Market: %s, Scenario: Push (Void)", m.name) -// assert.NoError(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_VOID, status) -// }) -// t.Run("Non-numeric OddName", func(t *testing.T) { -// status, err := EvaluateBaseballTotalRuns(domain.BetOutcome{MarketID: m.marketID, OddHeader: "Over", OddName: "notanumber"}, struct{ Home, Away int }{Home: 4, Away: 3}) -// t.Logf("Market: %s, Scenario: Non-numeric OddName", m.name) -// assert.Error(t, err) -// assert.Equal(t, domain.OUTCOME_STATUS_PENDING, status) -// }) -// } -// }) -// } -// } - -// func TestEvaluateFootballOutcome(t *testing.T) { -// service := &Service{} // or your real logger - -// // Mock outcome -// outcome := domain.BetOutcome{ -// ID: 1, -// BetID: 1, -// EventID: 1001, -// OddID: 2001, -// SportID: 1, // Assuming 1 = Football -// HomeTeamName: "Manchester", -// AwayTeamName: "Liverpool", -// MarketID: int64(domain.FOOTBALL_FULL_TIME_RESULT), -// MarketName: "Full Time Result", -// Odd: 1.75, -// OddName: "2", // Home win -// OddHeader: "1", -// OddHandicap: "", -// Status: domain.OUTCOME_STATUS_PENDING, // Initial status -// Expires: time.Now().Add(24 * time.Hour), -// } - -// // Parsed result (simulate Bet365 JSON) -// finalScore := struct{ Home, Away int }{Home: 2, Away: 1} -// firstHalfScore := struct{ Home, Away int }{Home: 1, Away: 1} -// secondHalfScore := struct{ Home, Away int }{Home: 1, Away: 0} -// corners := struct{ Home, Away int }{Home: 5, Away: 3} -// halfTimeCorners := struct{ Home, Away int }{Home: 2, Away: 2} -// events := []map[string]string{ -// {"type": "goal", "team": "home", "minute": "23"}, -// {"type": "goal", "team": "away", "minute": "34"}, -// } - -// // Act -// status, _ := service.EvaluateFootballOutcome(outcome, finalScore, firstHalfScore, secondHalfScore, corners, halfTimeCorners, events) - -// fmt.Printf("\n\nBet Outcome: %v\n\n", &status) - -// } -// func TestEvaluateTotalLegs(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"OverTotalLegs", domain.BetOutcome{OddName: "3", OddHeader: "Over"}, struct{ Home, Away int }{2, 4}, domain.OUTCOME_STATUS_WIN}, -// {"OverTotalLegs", domain.BetOutcome{OddName: "3", OddHeader: "Under"}, struct{ Home, Away int }{2, 4}, domain.OUTCOME_STATUS_LOSS}, -// {"UnderTotalLegs", domain.BetOutcome{OddName: "7", OddHeader: "Under"}, struct{ Home, Away int }{2, 3}, domain.OUTCOME_STATUS_WIN}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateTotalLegs(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateGameLines(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"GameLines - Total", domain.BetOutcome{OddName: "Total", OddHandicap: "O 5.5"}, struct{ Home, Away int }{2, 4}, domain.OUTCOME_STATUS_WIN}, -// {"GameLines - Total", domain.BetOutcome{OddName: "Total", OddHandicap: "O 5.5"}, struct{ Home, Away int }{2, 3}, domain.OUTCOME_STATUS_LOSS}, -// {"GameLines - Money Line", domain.BetOutcome{OddName: "Money Line", OddHeader: "1"}, struct{ Home, Away int }{2, 3}, domain.OUTCOME_STATUS_LOSS}, -// {"GameLines - Money Line", domain.BetOutcome{OddName: "Money Line", OddHeader: "1"}, struct{ Home, Away int }{3, 2}, domain.OUTCOME_STATUS_WIN}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateGameLines(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateFirstTeamToScore(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// events []map[string]string -// expected domain.OutcomeStatus -// }{ -// {"HomeScoreFirst", domain.BetOutcome{OddName: "1", HomeTeamName: "Team A", AwayTeamName: "Team B"}, []map[string]string{ -// {"text": "1st Goal - Team A"}, -// }, domain.OUTCOME_STATUS_WIN}, -// {"AwayScoreFirst", domain.BetOutcome{OddName: "2", HomeTeamName: "Team A", AwayTeamName: "Team B"}, []map[string]string{ -// {"text": "1st Goal - Team A"}, -// }, domain.OUTCOME_STATUS_LOSS}, -// {"AwayScoreFirst", domain.BetOutcome{OddName: "2", HomeTeamName: "Team A", AwayTeamName: "Team B"}, []map[string]string{ -// {"text": "1st Goal - Team B"}, -// }, domain.OUTCOME_STATUS_WIN}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateFirstTeamToScore(tt.outcome, tt.events) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateGoalsOverUnder(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"LosingGoalsOver", domain.BetOutcome{OddHeader: "Over", OddName: "13"}, struct{ Home, Away int }{7, 5}, domain.OUTCOME_STATUS_LOSS}, -// {"WinningGoalsOver", domain.BetOutcome{OddHeader: "Over", OddName: "11"}, struct{ Home, Away int }{7, 5}, domain.OUTCOME_STATUS_WIN}, -// {"WinningGoalsUnder", domain.BetOutcome{OddHeader: "Under", OddName: "12"}, struct{ Home, Away int }{6, 5}, domain.OUTCOME_STATUS_WIN}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateGoalsOverUnder(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateGoalsOddEven(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningOddGoals", domain.BetOutcome{OddName: "Odd"}, struct{ Home, Away int }{7, 4}, domain.OUTCOME_STATUS_WIN}, -// {"LosingEvenGoals", domain.BetOutcome{OddName: "Even"}, struct{ Home, Away int }{7, 4}, domain.OUTCOME_STATUS_LOSS}, -// {"WinningEvenGoals", domain.BetOutcome{OddName: "Even"}, struct{ Home, Away int }{6, 6}, domain.OUTCOME_STATUS_WIN}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateGoalsOddEven(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateCorrectScore(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"CorrectScore", domain.BetOutcome{OddName: "7-4"}, struct{ Home, Away int }{7, 4}, domain.OUTCOME_STATUS_WIN}, -// {"CorrectScore", domain.BetOutcome{OddName: "6-6"}, struct{ Home, Away int }{6, 6}, domain.OUTCOME_STATUS_WIN}, -// {"IncorrectScore", domain.BetOutcome{OddName: "2-3"}, struct{ Home, Away int }{7, 4}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateCorrectScore(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateHighestScoringHalf(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// firstScore struct{ Home, Away int } -// secondScore struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"Winning1stHalf", domain.BetOutcome{OddName: "1st Half"}, struct{ Home, Away int }{1, 1}, struct{ Home, Away int }{0, 0}, domain.OUTCOME_STATUS_WIN}, -// {"Losing1stHalf", domain.BetOutcome{OddName: "1st Half"}, struct{ Home, Away int }{1, 1}, struct{ Home, Away int }{2, 2}, domain.OUTCOME_STATUS_LOSS}, -// {"Losing2ndHalf", domain.BetOutcome{OddName: "2nd Half"}, struct{ Home, Away int }{0, 0}, struct{ Home, Away int }{0, 0}, domain.OUTCOME_STATUS_LOSS}, -// {"Winning2ndHalf", domain.BetOutcome{OddName: "2nd Half"}, struct{ Home, Away int }{1, 1}, struct{ Home, Away int }{2, 2}, domain.OUTCOME_STATUS_WIN}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateHighestScoringHalf(tt.outcome, tt.firstScore, tt.secondScore) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } - -// } - -// func TestEvaluateHighestScoringQuarter(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// firstScore struct{ Home, Away int } -// secondScore struct{ Home, Away int } -// thirdScore struct{ Home, Away int } -// fourthScore struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"Winning1stQuarter", domain.BetOutcome{OddName: "1st Quarter"}, -// struct{ Home, Away int }{1, 1}, -// struct{ Home, Away int }{0, 0}, -// struct{ Home, Away int }{1, 0}, -// struct{ Home, Away int }{0, 0}, -// domain.OUTCOME_STATUS_WIN}, -// {"Losing1stQuarter", domain.BetOutcome{OddName: "1st Quarter"}, -// struct{ Home, Away int }{1, 1}, -// struct{ Home, Away int }{0, 0}, -// struct{ Home, Away int }{1, 1}, -// struct{ Home, Away int }{0, 0}, -// domain.OUTCOME_STATUS_LOSS}, -// {"Losing2ndQuarter", domain.BetOutcome{OddName: "2nd Quarter"}, -// struct{ Home, Away int }{1, 1}, -// struct{ Home, Away int }{0, 0}, -// struct{ Home, Away int }{1, 1}, -// struct{ Home, Away int }{0, 0}, -// domain.OUTCOME_STATUS_LOSS}, -// {"Winning3rdQuarter", domain.BetOutcome{OddName: "3rd Quarter"}, -// struct{ Home, Away int }{1, 0}, -// struct{ Home, Away int }{0, 0}, -// struct{ Home, Away int }{1, 1}, -// struct{ Home, Away int }{0, 0}, -// domain.OUTCOME_STATUS_WIN}, -// {"Wining4thQuarter", domain.BetOutcome{OddName: "4th Quarter"}, -// struct{ Home, Away int }{1, 1}, -// struct{ Home, Away int }{0, 0}, -// struct{ Home, Away int }{1, 1}, -// struct{ Home, Away int }{2, 2}, -// domain.OUTCOME_STATUS_WIN}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateHighestScoringQuarter(tt.outcome, tt.firstScore, tt.secondScore, tt.thirdScore, tt.fourthScore) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } - -// } - -// func TestEvaluateWinningMargin(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningMargin", domain.BetOutcome{OddHeader: "1", OddName: "12"}, struct{ Home, Away int }{12, 0}, domain.OUTCOME_STATUS_WIN}, -// {"WinningMargin", domain.BetOutcome{OddHeader: "2", OddName: "3"}, struct{ Home, Away int }{1, 4}, domain.OUTCOME_STATUS_WIN}, -// {"WinningMargin", domain.BetOutcome{OddHeader: "1", OddName: "3+"}, struct{ Home, Away int }{4, 0}, domain.OUTCOME_STATUS_WIN}, -// {"WinningMargin", domain.BetOutcome{OddHeader: "2", OddName: "12+"}, struct{ Home, Away int }{0, 13}, domain.OUTCOME_STATUS_WIN}, -// {"LosingMargin", domain.BetOutcome{OddHeader: "2", OddName: "3"}, struct{ Home, Away int }{0, 4}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingMargin", domain.BetOutcome{OddHeader: "2", OddName: "3+"}, struct{ Home, Away int }{1, 3}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateWinningMargin(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateDoubleResult(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// firstHalfScore struct{ Home, Away int } -// fullTimeScore struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningHomeAway", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A - Team B"}, struct{ Home, Away int }{1, 0}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_WIN}, -// {"WinningAwayHome", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team B - Team A"}, struct{ Home, Away int }{0, 1}, struct{ Home, Away int }{2, 1}, domain.OUTCOME_STATUS_WIN}, -// {"WinningTie", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Tie - Tie"}, struct{ Home, Away int }{1, 1}, struct{ Home, Away int }{2, 2}, domain.OUTCOME_STATUS_WIN}, -// {"WinningTieAway", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Tie - Team B"}, struct{ Home, Away int }{1, 1}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_WIN}, -// {"LosingHomeAway", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A - Team B"}, struct{ Home, Away int }{1, 0}, struct{ Home, Away int }{2, 0}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingTie", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Tie - Tie"}, struct{ Home, Away int }{1, 0}, struct{ Home, Away int }{1, 1}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingTieAway", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Tie - Team A"}, struct{ Home, Away int }{1, 1}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_LOSS}, -// {"BadInput", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A - "}, struct{ Home, Away int }{1, 1}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_PENDING}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateDoubleResult(tt.outcome, tt.firstHalfScore, tt.fullTimeScore) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateHighestScoringPeriod(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// firstScore struct{ Home, Away int } -// secondScore struct{ Home, Away int } -// thirdScore struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"Winning1stPeriod", domain.BetOutcome{OddName: "Period 1"}, struct{ Home, Away int }{2, 2}, struct{ Home, Away int }{1, 1}, struct{ Home, Away int }{0, 0}, domain.OUTCOME_STATUS_WIN}, -// {"Winning2ndPeriod", domain.BetOutcome{OddName: "Period 2"}, struct{ Home, Away int }{2, 2}, struct{ Home, Away int }{2, 3}, struct{ Home, Away int }{0, 0}, domain.OUTCOME_STATUS_WIN}, -// {"Winning3rdPeriod", domain.BetOutcome{OddName: "Period 3"}, struct{ Home, Away int }{2, 2}, struct{ Home, Away int }{2, 3}, struct{ Home, Away int }{3, 3}, domain.OUTCOME_STATUS_WIN}, -// {"WinningTie", domain.BetOutcome{OddName: "Tie"}, struct{ Home, Away int }{2, 2}, struct{ Home, Away int }{2, 2}, struct{ Home, Away int }{0, 0}, domain.OUTCOME_STATUS_WIN}, -// {"Losing1stPeriod", domain.BetOutcome{OddName: "Period 1"}, struct{ Home, Away int }{2, 2}, struct{ Home, Away int }{2, 3}, struct{ Home, Away int }{0, 0}, domain.OUTCOME_STATUS_LOSS}, -// {"Losing3rdPeriod", domain.BetOutcome{OddName: "Period 3"}, struct{ Home, Away int }{2, 2}, struct{ Home, Away int }{2, 3}, struct{ Home, Away int }{0, 0}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateHighestScoringPeriod(tt.outcome, tt.firstScore, tt.secondScore, tt.thirdScore) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvalauteTiedAfterRegulation(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score []struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningTied", domain.BetOutcome{OddName: "Yes"}, []struct{ Home, Away int }{{1, 0}, {0, 1}, {2, 2}}, domain.OUTCOME_STATUS_WIN}, -// {"WinningTied", domain.BetOutcome{OddName: "Yes"}, []struct{ Home, Away int }{{1, 1}, {0, 1}, {2, 2}, {2, 1}}, domain.OUTCOME_STATUS_WIN}, -// {"WinningNotTied", domain.BetOutcome{OddName: "No"}, []struct{ Home, Away int }{{0, 0}, {0, 0}, {0, 0}, {1, 0}}, domain.OUTCOME_STATUS_WIN}, -// {"LosingTied", domain.BetOutcome{OddName: "Yes"}, []struct{ Home, Away int }{{0, 2}, {0, 0}, {0, 0}, {0, 0}}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingNotTied", domain.BetOutcome{OddName: "No"}, []struct{ Home, Away int }{{0, 0}, {0, 0}, {0, 0}, {0, 0}}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateTiedAfterRegulation(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateTeamTotal(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningHomeUnder", domain.BetOutcome{OddHandicap: "Under 3", OddHeader: "1"}, struct{ Home, Away int }{2, 0}, domain.OUTCOME_STATUS_WIN}, -// {"WinningHomeOver", domain.BetOutcome{OddHandicap: "Over 2", OddHeader: "1"}, struct{ Home, Away int }{3, 1}, domain.OUTCOME_STATUS_WIN}, -// {"WinningAwayOver", domain.BetOutcome{OddHandicap: "Over 2", OddHeader: "2"}, struct{ Home, Away int }{1, 3}, domain.OUTCOME_STATUS_WIN}, -// {"LosingHomeOver", domain.BetOutcome{OddHandicap: "Over 2", OddHeader: "1"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingAwayOver", domain.BetOutcome{OddHandicap: "Over 2", OddHeader: "2"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateTeamTotal(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestDrawNoBet(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningHome", domain.BetOutcome{OddName: "1"}, struct{ Home, Away int }{1, 0}, domain.OUTCOME_STATUS_WIN}, -// {"WinningAway", domain.BetOutcome{OddName: "2"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_WIN}, -// {"LosingHome", domain.BetOutcome{OddName: "1"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_LOSS}, -// {"Tie", domain.BetOutcome{OddName: "1"}, struct{ Home, Away int }{1, 1}, domain.OUTCOME_STATUS_VOID}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateDrawNoBet(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateMoneyLine(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningHome", domain.BetOutcome{OddHeader: "1"}, struct{ Home, Away int }{1, 0}, domain.OUTCOME_STATUS_WIN}, -// {"WinningAway", domain.BetOutcome{OddHeader: "2"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_WIN}, -// {"WinningTie", domain.BetOutcome{OddHeader: "Tie"}, struct{ Home, Away int }{2, 2}, domain.OUTCOME_STATUS_WIN}, -// {"LosingTie", domain.BetOutcome{OddHeader: "1"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingAway", domain.BetOutcome{OddHeader: "2"}, struct{ Home, Away int }{3, 2}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateMoneyLine(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateDoubleChance(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningHomeOrDraw", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "1 or Draw"}, struct{ Home, Away int }{1, 0}, domain.OUTCOME_STATUS_WIN}, -// {"WinningHomeOrDraw", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A or Draw"}, struct{ Home, Away int }{1, 0}, domain.OUTCOME_STATUS_WIN}, -// {"WinningAwayOrDraw", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Draw or Team B"}, struct{ Home, Away int }{0, 1}, domain.OUTCOME_STATUS_WIN}, -// {"LosingHomeorAway", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "1 or 2"}, struct{ Home, Away int }{1, 1}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingAwayOrDraw", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Draw or 2"}, struct{ Home, Away int }{2, 1}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateDoubleChance(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateResultAndTotal(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningHomeOver", domain.BetOutcome{OddHeader: "1", OddHandicap: "Over 4"}, struct{ Home, Away int }{3, 2}, domain.OUTCOME_STATUS_WIN}, -// {"WinningHomeUnder", domain.BetOutcome{OddHeader: "1", OddHandicap: "Under 4"}, struct{ Home, Away int }{2, 1}, domain.OUTCOME_STATUS_WIN}, -// {"WinningAwayUnder", domain.BetOutcome{OddHeader: "2", OddHandicap: "Under 4"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_WIN}, -// {"LosingHomeOver", domain.BetOutcome{OddHeader: "1", OddHandicap: "Under 4"}, struct{ Home, Away int }{3, 2}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingAwayUnder", domain.BetOutcome{OddHeader: "2", OddHandicap: "Under 4"}, struct{ Home, Away int }{2, 2}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateResultAndTotal(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestCheckMultiOutcome(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.OutcomeStatus -// secondOutcome domain.OutcomeStatus -// expected domain.OutcomeStatus -// }{ -// {"Win-Win", domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_WIN}, -// {"Win-Void", domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_VOID, domain.OUTCOME_STATUS_HALF}, -// {"Win-Loss", domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_LOSS, domain.OUTCOME_STATUS_LOSS}, -// {"Loss-Loss", domain.OUTCOME_STATUS_LOSS, domain.OUTCOME_STATUS_LOSS, domain.OUTCOME_STATUS_LOSS}, -// {"Loss-Void", domain.OUTCOME_STATUS_LOSS, domain.OUTCOME_STATUS_VOID, domain.OUTCOME_STATUS_VOID}, -// {"Loss-Win", domain.OUTCOME_STATUS_LOSS, domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_LOSS}, -// {"Void-Win", domain.OUTCOME_STATUS_VOID, domain.OUTCOME_STATUS_WIN, domain.OUTCOME_STATUS_VOID}, -// {"Void-Loss", domain.OUTCOME_STATUS_VOID, domain.OUTCOME_STATUS_LOSS, domain.OUTCOME_STATUS_VOID}, -// {"Void-Void", domain.OUTCOME_STATUS_VOID, domain.OUTCOME_STATUS_VOID, domain.OUTCOME_STATUS_VOID}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := checkMultiOutcome(tt.outcome, tt.secondOutcome) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateBTTSX(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningBothScoreX", domain.BetOutcome{OddName: "3", OddHeader: "Yes"}, struct{ Home, Away int }{3, 4}, domain.OUTCOME_STATUS_WIN}, -// {"WinningBothScoreLess", domain.BetOutcome{OddName: "3", OddHeader: "No"}, struct{ Home, Away int }{2, 1}, domain.OUTCOME_STATUS_WIN}, -// {"LosingBothScoreX", domain.BetOutcome{OddName: "3", OddHeader: "Yes"}, struct{ Home, Away int }{2, 4}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingBothScoreLess", domain.BetOutcome{OddName: "3", OddHeader: "No"}, struct{ Home, Away int }{2, 4}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateBTTSX(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateResultAndBTTSX(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningHomeAndBothScoreX", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A and Yes", OddHeader: "3"}, struct{ Home, Away int }{4, 3}, domain.OUTCOME_STATUS_WIN}, -// {"WinningHomeAndBothScoreLess", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A and No", OddHeader: "3"}, struct{ Home, Away int }{2, 1}, domain.OUTCOME_STATUS_WIN}, -// {"WinningAwayAndBothScoreX", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team B and Yes", OddHeader: "3"}, struct{ Home, Away int }{3, 4}, domain.OUTCOME_STATUS_WIN}, -// {"WinningAwayAndBothScoreLess", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team B and No", OddHeader: "3"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_WIN}, -// {"LosingHomeAndBothScoreX", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A and Yes", OddHeader: "3"}, struct{ Home, Away int }{3, 4}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingHomeAndBothScoreX", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team B and Yes", OddHeader: "3"}, struct{ Home, Away int }{4, 2}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingAwayAndBothScoreX", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team B and Yes", OddHeader: "3"}, struct{ Home, Away int }{2, 4}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateResultAndBTTSX(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateMoneyLine3Way(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"WinningHome", domain.BetOutcome{OddName: "1"}, struct{ Home, Away int }{1, 0}, domain.OUTCOME_STATUS_WIN}, -// {"WinningAway", domain.BetOutcome{OddName: "2"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_WIN}, -// {"WinningTie", domain.BetOutcome{OddName: "Tie"}, struct{ Home, Away int }{2, 2}, domain.OUTCOME_STATUS_WIN}, -// {"LosingTie", domain.BetOutcome{OddName: "1"}, struct{ Home, Away int }{1, 2}, domain.OUTCOME_STATUS_LOSS}, -// {"LosingAway", domain.BetOutcome{OddName: "2"}, struct{ Home, Away int }{3, 2}, domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateMoneyLine3Way(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateAsianHandicap(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// { -// name: "Home -1 Win", -// outcome: domain.BetOutcome{OddHeader: "1", OddHandicap: "-1"}, -// score: struct{ Home, Away int }{Home: 2, Away: 0}, -// expected: domain.OUTCOME_STATUS_WIN, -// }, -// { -// name: "Home -0.5 Win", -// outcome: domain.BetOutcome{OddHeader: "1", OddHandicap: "-0.5"}, -// score: struct{ Home, Away int }{Home: 1, Away: 0}, -// expected: domain.OUTCOME_STATUS_WIN, -// }, -// { -// name: "Home -1 Void", -// outcome: domain.BetOutcome{OddHeader: "1", OddHandicap: "-1"}, -// score: struct{ Home, Away int }{Home: 1, Away: 0}, -// expected: domain.OUTCOME_STATUS_VOID, -// }, -// { -// name: "Away +3 Win", -// outcome: domain.BetOutcome{OddHeader: "2", OddHandicap: "3"}, -// score: struct{ Home, Away int }{Home: 1, Away: 2}, -// expected: domain.OUTCOME_STATUS_WIN, -// }, -// { -// name: "Split Handicap Home -0.5,-1 Win/Win", -// outcome: domain.BetOutcome{OddHeader: "1", OddHandicap: "-0.5,-1"}, -// score: struct{ Home, Away int }{Home: 2, Away: 0}, -// expected: domain.OUTCOME_STATUS_WIN, -// }, -// { -// name: "Split Handicap Home -0.5,-1 Win/Void", -// outcome: domain.BetOutcome{OddHeader: "1", OddHandicap: "-0.5,-1"}, -// score: struct{ Home, Away int }{Home: 1, Away: 0}, -// expected: domain.OUTCOME_STATUS_WIN, -// }, -// { -// name: "Invalid Handicap", -// outcome: domain.BetOutcome{OddHeader: "1", OddHandicap: "invalid"}, -// score: struct{ Home, Away int }{Home: 1, Away: 0}, -// expected: domain.OUTCOME_STATUS_ERROR, -// }, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateAsianHandicap(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } - -// func TestEvaluateHandicapAndTotal(t *testing.T) { -// tests := []struct { -// name string -// outcome domain.BetOutcome -// score struct{ Home, Away int } -// expected domain.OutcomeStatus -// }{ -// {"Home +2.5 Over 3", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A +2.5 & Over 3"}, -// struct{ Home, Away int }{4, 0}, -// domain.OUTCOME_STATUS_WIN}, -// {"Away +2.5 Over 4", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team B +2.5 & Over 4"}, -// struct{ Home, Away int }{1, 5}, -// domain.OUTCOME_STATUS_WIN}, -// {"Home +2.5 Over 3", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A +2.5 & Over 3"}, -// struct{ Home, Away int }{2, 0}, -// domain.OUTCOME_STATUS_LOSS}, -// {"Home -3.5 Over 3", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team A -2.5 & Over 3"}, -// struct{ Home, Away int }{4, 3}, -// domain.OUTCOME_STATUS_LOSS}, -// {"Away -3 Over 4", domain.BetOutcome{HomeTeamName: "Team A", AwayTeamName: "Team B", OddName: "Team B -3 & Over 4"}, -// struct{ Home, Away int }{3, 5}, -// domain.OUTCOME_STATUS_LOSS}, -// } - -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// status, _ := EvaluateHandicapAndTotal(tt.outcome, tt.score) -// if status != tt.expected { -// t.Errorf("expected %d, got %d", tt.expected, status) -// } -// }) -// } -// } diff --git a/internal/services/result_checker.go b/internal/services/result_checker.go deleted file mode 100644 index b16ffe1..0000000 --- a/internal/services/result_checker.go +++ /dev/null @@ -1,189 +0,0 @@ -package services - -// import ( -// "encoding/json" -// "fmt" -// "time" - -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -// ) - -// // ResultCheckerService handles the checking of game results -// type ResultCheckerService struct { -// // Add any dependencies here (e.g., repositories, external APIs) -// } - -// // NewResultCheckerService creates a new instance of ResultCheckerService -// func NewResultCheckerService() *ResultCheckerService { -// return &ResultCheckerService{} -// } - -// // CheckNFLResult checks the result of an NFL game -// func (s *ResultCheckerService) CheckNFLResult(data json.RawMessage) (*domain.Result, error) { -// nflResult, err := domain.ParseNFLResult(data) -// if err != nil { -// return nil, fmt.Errorf("failed to parse NFL result: %w", err) -// } - -// winner, err := domain.GetNFLWinner(nflResult) -// if err != nil { -// return nil, fmt.Errorf("failed to determine NFL winner: %w", err) -// } - -// score := domain.FormatNFLScore(nflResult) - -// return &domain.Result{ -// Status: determineOutcomeStatus(winner, nflResult.Home.Name, nflResult.Away.Name), -// Score: score, -// FullTimeScore: score, -// SS: nflResult.SS, -// Scores: map[string]domain.Score{ -// "1": nflResult.Scores.FirstQuarter, -// "2": nflResult.Scores.SecondQuarter, -// "3": nflResult.Scores.ThirdQuarter, -// "4": nflResult.Scores.FourthQuarter, -// "5": nflResult.Scores.Overtime, -// "7": nflResult.Scores.TotalScore, -// }, -// CreatedAt: time.Now(), -// UpdatedAt: time.Now(), -// }, nil -// } - -// // determineOutcomeStatus determines the outcome status based on the winner and teams -// func determineOutcomeStatus(winner, homeTeam, awayTeam string) domain.OutcomeStatus { -// if winner == "Draw" { -// return domain.OUTCOME_STATUS_VOID -// } -// if winner == homeTeam { -// return domain.OUTCOME_STATUS_WIN -// } -// if winner == awayTeam { -// return domain.OUTCOME_STATUS_LOSS -// } -// return domain.OUTCOME_STATUS_PENDING -// } - -// // CheckRugbyResult checks the result of a Rugby game -// func (s *ResultCheckerService) CheckRugbyResult(data json.RawMessage) (*domain.Result, error) { -// rugbyResult, err := domain.ParseRugbyResult(data) -// if err != nil { -// return nil, fmt.Errorf("failed to parse Rugby result: %w", err) -// } - -// winner, err := domain.GetRugbyWinner(rugbyResult) -// if err != nil { -// return nil, fmt.Errorf("failed to determine Rugby winner: %w", err) -// } - -// score := domain.FormatRugbyScore(rugbyResult) - -// return &domain.Result{ -// Status: determineOutcomeStatus(winner, rugbyResult.Home.Name, rugbyResult.Away.Name), -// Score: score, -// FullTimeScore: score, -// SS: rugbyResult.SS, -// Scores: map[string]domain.Score{ -// "1": rugbyResult.Scores.FirstHalf, -// "2": rugbyResult.Scores.SecondHalf, -// "7": rugbyResult.Scores.TotalScore, -// }, -// CreatedAt: time.Now(), -// UpdatedAt: time.Now(), -// }, nil -// } - -// // CheckBaseballResult checks the result of a Baseball game -// func (s *ResultCheckerService) CheckBaseballResult(data json.RawMessage) (*domain.Result, error) { -// baseballResult, err := domain.ParseBaseballResult(data) -// if err != nil { -// return nil, fmt.Errorf("failed to parse Baseball result: %w", err) -// } - -// winner, err := domain.GetBaseballWinner(baseballResult) -// if err != nil { -// return nil, fmt.Errorf("failed to determine Baseball winner: %w", err) -// } - -// score := domain.FormatBaseballScore(baseballResult) - -// return &domain.Result{ -// Status: determineOutcomeStatus(winner, baseballResult.Home.Name, baseballResult.Away.Name), -// Score: score, -// FullTimeScore: score, -// SS: baseballResult.SS, -// Scores: map[string]domain.Score{ -// "1": baseballResult.Scores.FirstInning, -// "2": baseballResult.Scores.SecondInning, -// "3": baseballResult.Scores.ThirdInning, -// "4": baseballResult.Scores.FourthInning, -// "5": baseballResult.Scores.FifthInning, -// "6": baseballResult.Scores.SixthInning, -// "7": baseballResult.Scores.SeventhInning, -// "8": baseballResult.Scores.EighthInning, -// "9": baseballResult.Scores.NinthInning, -// "10": baseballResult.Scores.ExtraInnings, -// "T": baseballResult.Scores.TotalScore, -// }, -// CreatedAt: time.Now(), -// UpdatedAt: time.Now(), -// }, nil -// } - -// // CheckRugbyUnionResult checks the result of a Rugby Union game -// func (s *ResultCheckerService) CheckRugbyUnionResult(data json.RawMessage) (*domain.Result, error) { -// rugbyResult, err := domain.ParseRugbyUnionResult(data) -// if err != nil { -// return nil, fmt.Errorf("failed to parse Rugby Union result: %w", err) -// } - -// winner, err := domain.GetRugbyWinner(rugbyResult) -// if err != nil { -// return nil, fmt.Errorf("failed to determine Rugby Union winner: %w", err) -// } - -// score := domain.FormatRugbyScore(rugbyResult) - -// return &domain.Result{ -// Status: determineOutcomeStatus(winner, rugbyResult.Home.Name, rugbyResult.Away.Name), -// Score: score, -// FullTimeScore: score, -// SS: rugbyResult.SS, -// Scores: map[string]domain.Score{ -// "1": rugbyResult.Scores.FirstHalf, -// "2": rugbyResult.Scores.SecondHalf, -// "7": rugbyResult.Scores.TotalScore, -// }, -// CreatedAt: time.Now(), -// UpdatedAt: time.Now(), -// }, nil -// } - -// // CheckRugbyLeagueResult checks the result of a Rugby League game -// func (s *ResultCheckerService) CheckRugbyLeagueResult(data json.RawMessage) (*domain.Result, error) { -// rugbyResult, err := domain.ParseRugbyLeagueResult(data) -// if err != nil { -// return nil, fmt.Errorf("failed to parse Rugby League result: %w", err) -// } - -// winner, err := domain.GetRugbyWinner(rugbyResult) -// if err != nil { -// return nil, fmt.Errorf("failed to determine Rugby League winner: %w", err) -// } - -// score := domain.FormatRugbyScore(rugbyResult) - -// return &domain.Result{ -// Status: determineOutcomeStatus(winner, rugbyResult.Home.Name, rugbyResult.Away.Name), -// Score: score, -// FullTimeScore: score, -// SS: rugbyResult.SS, -// Scores: map[string]domain.Score{ -// "1": rugbyResult.Scores.FirstHalf, -// "2": rugbyResult.Scores.SecondHalf, -// "7": rugbyResult.Scores.TotalScore, -// }, -// CreatedAt: time.Now(), -// UpdatedAt: time.Now(), -// }, nil -// } diff --git a/internal/services/santimpay/client.go b/internal/services/santimpay/client.go deleted file mode 100644 index ead4104..0000000 --- a/internal/services/santimpay/client.go +++ /dev/null @@ -1,64 +0,0 @@ -package santimpay - -import ( - "fmt" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/golang-jwt/jwt/v5" -) - -type SantimPayClient interface { - GenerateSignedToken(payload domain.SantimTokenPayload) (string, error) - CheckTransactionStatus(id string) -} - -type santimClient struct { - cfg *config.Config -} - -func NewSantimPayClient(cfg *config.Config) SantimPayClient { - return &santimClient{ - cfg: cfg, - } -} - -func (c *santimClient) GenerateSignedToken(payload domain.SantimTokenPayload) (string, error) { - now := time.Now().Unix() - - claims := jwt.MapClaims{ - "amount": payload.Amount, - "paymentReason": payload.Reason, - "merchantId": c.cfg.SANTIMPAY.MerchantID, - "generated": now, - } - - // Optional fields - if payload.PaymentMethod != "" { - claims["paymentMethod"] = payload.PaymentMethod - } - if payload.PhoneNumber != "" { - claims["phoneNumber"] = payload.PhoneNumber - } - - token := jwt.NewWithClaims(jwt.SigningMethodES256, claims) - - privateKey, err := jwt.ParseECPrivateKeyFromPEM([]byte(c.cfg.SANTIMPAY.SecretKey)) - if err != nil { - return "", fmt.Errorf("invalid private key: %w", err) - } - - signedToken, err := token.SignedString(privateKey) - if err != nil { - return "", fmt.Errorf("signing failed: %w", err) - } - - return signedToken, nil -} - - -func (c *santimClient) CheckTransactionStatus(id string) { - // optional async checker — can log or poll transaction status - fmt.Println("Checking transaction status for:", id) -} diff --git a/internal/services/santimpay/service.go b/internal/services/santimpay/service.go deleted file mode 100644 index de58b2e..0000000 --- a/internal/services/santimpay/service.go +++ /dev/null @@ -1,416 +0,0 @@ -package santimpay - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "strconv" - "time" - - "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 SantimPayService interface { -// GeneratePaymentURL(req domain.GeneratePaymentURLreq) (map[string]string, error) -// } - -type SantimPayService struct { - client SantimPayClient - cfg *config.Config - transferStore ports.TransferStore - walletSvc *wallet.Service -} - -func NewSantimPayService(client SantimPayClient, cfg *config.Config, transferStore ports.TransferStore, walletSvc *wallet.Service) *SantimPayService { - return &SantimPayService{ - client: client, - cfg: cfg, - transferStore: transferStore, - walletSvc: walletSvc, - } -} - -func (s *SantimPayService) InitiatePayment(req domain.GeneratePaymentURLRequest) (map[string]string, error) { - paymentID := uuid.NewString() - - tokenPayload := domain.SantimTokenPayload{ - Amount: req.Amount, - Reason: req.Reason, - } - - // 1. Generate signed token (used as Bearer token in headers) - token, err := s.client.GenerateSignedToken(tokenPayload) - if err != nil { - return nil, fmt.Errorf("token generation failed: %w", err) - } - - // 2. Prepare payload (without token in body) - payload := domain.InitiatePaymentRequest{ - ID: paymentID, - Amount: req.Amount, - Reason: req.Reason, - MerchantID: s.cfg.SANTIMPAY.MerchantID, - SuccessRedirectURL: s.cfg.SANTIMPAY.SuccessUrl, - FailureRedirectURL: s.cfg.SANTIMPAY.CancelUrl, - NotifyURL: s.cfg.SANTIMPAY.NotifyURL, - CancelRedirectURL: s.cfg.SANTIMPAY.CancelUrl, - PhoneNumber: req.PhoneNumber, - SignedToken: token, - } - - jsonData, err := json.Marshal(payload) - if err != nil { - return nil, fmt.Errorf("failed to marshal payload: %w", err) - } - - // 3. Prepare request with Bearer token header - httpReq, err := http.NewRequest("POST", s.cfg.SANTIMPAY.BaseURL+"/gateway/initiate-payment", bytes.NewBuffer(jsonData)) - if err != nil { - return nil, fmt.Errorf("failed to create HTTP request: %w", err) - } - httpReq.Header.Set("Content-Type", "application/json") - httpReq.Header.Set("Authorization", "Bearer "+token) - - client := &http.Client{Timeout: 15 * time.Second} - resp, err := client.Do(httpReq) - if err != nil { - return nil, fmt.Errorf("failed to send HTTP request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("non-200 status code received: %d", resp.StatusCode) - } - - var responseBody map[string]string - if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - // 4. Save transfer - transfer := domain.CreateTransfer{ - Amount: domain.Currency(req.Amount), - Verified: false, - Type: domain.DEPOSIT, - ReferenceNumber: paymentID, - Status: string(domain.PaymentStatusPending), - } - if _, err := s.transferStore.CreateTransfer(context.Background(), transfer); err != nil { - return nil, fmt.Errorf("failed to create transfer: %w", err) - } - - // 5. Optionally check transaction status asynchronously - // go s.client.CheckTransactionStatus(paymentID) - - return responseBody, nil -} - -func (s *SantimPayService) ProcessCallback(ctx context.Context, payload domain.SantimPayCallbackPayload) error { - // 1. Parse amount - amount, err := strconv.ParseFloat(payload.Amount, 64) - if err != nil { - return fmt.Errorf("invalid amount in callback: %w", err) - } - - // 2. Retrieve the corresponding transfer by txnId or refId - transfer, err := s.transferStore.GetTransferByReference(ctx, payload.TxnId) - if err != nil { - return fmt.Errorf("failed to fetch transfer for txnId %s: %w", payload.TxnId, err) - } - - // 3. Update transfer status based on callback status - switch payload.Status { - case "COMPLETED": - transfer.Status = string(domain.PaymentStatusSuccessful) - transfer.Verified = true - - userID, err := strconv.ParseInt(payload.ThirdPartyId, 10, 64) - if err != nil { - return fmt.Errorf("invalid ThirdPartyId '%s': %w", payload.ThirdPartyId, err) - } - - wallet, err := s.walletSvc.GetCustomerWallet(ctx, userID) - if err != nil { - return fmt.Errorf("failed to get wallets for customer %d: %w", userID, err) - } - - // Optionally, credit user wallet - if transfer.Type == domain.DEPOSIT { - if _, err := s.walletSvc.AddToWallet( - ctx, - wallet.RegularID, - domain.Currency(amount), - domain.ValidInt64{}, - domain.TRANSFER_SANTIMPAY, - domain.PaymentDetails{ - ReferenceNumber: domain.ValidString{ - Value: payload.TxnId, - Valid: true, - }, - BankNumber: domain.ValidString{}, - }, - "", - ); err != nil { - return fmt.Errorf("failed to credit wallet: %w", err) - } - } - - case "FAILED", "CANCELLED": - transfer.Status = string(domain.PaymentStatusFailed) - transfer.Verified = false - default: - // Unknown status - return fmt.Errorf("unknown callback status: %s", payload.Status) - } - - // 4. Save the updated transfer - if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.PaymentStatusCompleted)); err != nil { - return fmt.Errorf("failed to update transfer status: %w", err) - } - - if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil { - return fmt.Errorf("failed to update transfer verification: %w", err) - } - - return nil -} - -func (s *SantimPayService) ProcessDirectPayment(ctx context.Context, req domain.GeneratePaymentURLRequest) (map[string]any, error) { - paymentID := uuid.NewString() - - tokenPayload := domain.SantimTokenPayload{ - Amount: req.Amount, - Reason: req.Reason, - PaymentMethod: req.PaymentMethod, - PhoneNumber: req.PhoneNumber, - } - - // 1. Generate signed token for direct payment - token, err := s.client.GenerateSignedToken(tokenPayload) - if err != nil { - return nil, fmt.Errorf("failed to generate signed token: %w", err) - } - - // 2. Build payload - payload := domain.InitiatePaymentRequest{ - ID: paymentID, - Amount: req.Amount, - Reason: req.Reason, - MerchantID: s.cfg.SANTIMPAY.MerchantID, - SignedToken: token, - PhoneNumber: req.PhoneNumber, - NotifyURL: s.cfg.SANTIMPAY.NotifyURL, - PaymentMethod: req.PaymentMethod, - } - - jsonData, err := json.Marshal(payload) - if err != nil { - return nil, fmt.Errorf("failed to marshal payload: %w", err) - } - - // 3. Prepare HTTP request - httpReq, err := http.NewRequest("POST", s.cfg.SANTIMPAY.BaseURL+"/direct-payment", bytes.NewBuffer(jsonData)) - if err != nil { - return nil, fmt.Errorf("failed to create HTTP request: %w", err) - } - httpReq.Header.Set("Content-Type", "application/json") - httpReq.Header.Set("Authorization", "Bearer "+token) - - client := &http.Client{Timeout: 15 * time.Second} - resp, err := client.Do(httpReq) - if err != nil { - return nil, fmt.Errorf("failed to send HTTP request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("non-200 status code received: %d", resp.StatusCode) - } - - // 4. Decode response - var responseBody map[string]any - 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), - Verified: false, - Type: domain.DEPOSIT, - ReferenceNumber: paymentID, - Status: string(domain.PaymentStatusPending), - } - if _, err := s.transferStore.CreateTransfer(context.Background(), transfer); err != nil { - return nil, fmt.Errorf("failed to create transfer: %w", err) - } - - // 6. Optionally check transaction status async - // go s.client.CheckTransactionStatus(paymentID) - - return responseBody, nil -} - -func (s *SantimPayService) GetB2CPartners(ctx context.Context) (*domain.B2CPartnersResponse, error) { - url := fmt.Sprintf("%s/api/v1/gateway/payout/partners", s.cfg.SANTIMPAY.BaseURL) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - HTTPClient := &http.Client{Timeout: 15 * time.Second} - - resp, err := HTTPClient.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to call SantimPay API: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } - - var partnersResp domain.B2CPartnersResponse - if err := json.NewDecoder(resp.Body).Decode(&partnersResp); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return &partnersResp, nil -} - -func (s *SantimPayService) ProcessB2CWithdrawal(ctx context.Context, req domain.GeneratePaymentURLRequest, userId int64) (map[string]any, error) { - - transactID := uuid.NewString() - - // 1. Generate signed token for B2C - tokenPayload := domain.SantimTokenPayload{ - Amount: req.Amount, - Reason: req.Reason, - PaymentMethod: req.PaymentMethod, - PhoneNumber: req.PhoneNumber, - } - - signedToken, err := s.client.GenerateSignedToken(tokenPayload) - if err != nil { - return nil, fmt.Errorf("failed to generate signed token for B2C: %w", err) - } - - // 2. Build payload - payload := domain.SantimpayB2CWithdrawalRequest{ - ID: transactID, - ClientReference: string(rune(userId)), - Amount: float64(req.Amount), - Reason: req.Reason, - MerchantID: s.cfg.SANTIMPAY.MerchantID, - SignedToken: signedToken, - ReceiverAccountNumber: req.PhoneNumber, - NotifyURL: s.cfg.SANTIMPAY.NotifyURL, - PaymentMethod: req.PaymentMethod, - } - - jsonData, err := json.Marshal(payload) - if err != nil { - return nil, fmt.Errorf("failed to marshal B2C payload: %w", err) - } - - // 3. Send HTTP request - url := s.cfg.SANTIMPAY.BaseURL + "/payout-transfer" - httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) - if err != nil { - return nil, fmt.Errorf("failed to create B2C request: %w", err) - } - httpReq.Header.Set("Content-Type", "application/json") - httpReq.Header.Set("Authorization", "Bearer "+signedToken) - - client := &http.Client{Timeout: 15 * time.Second} - resp, err := client.Do(httpReq) - if err != nil { - return nil, fmt.Errorf("failed to send B2C request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("B2C request failed with status code: %d", resp.StatusCode) - } - - // 4. Decode response - var responseBody map[string]any - if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil { - return nil, fmt.Errorf("failed to decode B2C response: %w", err) - } - - // 5. Persist withdrawal record in DB - withdrawal := domain.CreateTransfer{ - Amount: domain.Currency(req.Amount), - Verified: false, - Type: domain.WITHDRAW, - ReferenceNumber: transactID, - Status: string(domain.PaymentStatusPending), - } - if _, err := s.transferStore.CreateTransfer(context.Background(), withdrawal); err != nil { - return nil, fmt.Errorf("failed to create withdrawal transfer: %w", err) - } - - return responseBody, nil -} - -func (s *SantimPayService) CheckTransactionStatus(ctx context.Context, req domain.TransactionStatusRequest) (map[string]any, error) { - // 1. Generate signed token for status check - tokenPayload := domain.SantimTokenPayload{ - ID: req.TransactionID, - } - - signedToken, err := s.client.GenerateSignedToken(tokenPayload) - if err != nil { - return nil, fmt.Errorf("failed to generate signed token for transaction status: %w", err) - } - - // 2. Build request payload - payload := map[string]any{ - "id": req.TransactionID, - "merchantId": s.cfg.SANTIMPAY.MerchantID, - "signedToken": signedToken, - "fullParams": req.FullParams, - "generated": time.Now().Unix(), - } - - jsonData, err := json.Marshal(payload) - if err != nil { - return nil, fmt.Errorf("failed to marshal transaction status payload: %w", err) - } - - // 3. Send HTTP request - url := s.cfg.SANTIMPAY.BaseURL + "/fetch-transaction-status" - httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData)) - if err != nil { - return nil, fmt.Errorf("failed to create transaction status request: %w", err) - } - httpReq.Header.Set("Content-Type", "application/json") - httpReq.Header.Set("Authorization", "Bearer "+signedToken) - - client := &http.Client{Timeout: 15 * time.Second} - resp, err := client.Do(httpReq) - if err != nil { - return nil, fmt.Errorf("failed to send transaction status request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("transaction status request failed with status code: %d", resp.StatusCode) - } - - // 4. Decode response - var responseBody map[string]any - if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil { - return nil, fmt.Errorf("failed to decode transaction status response: %w", err) - } - - return responseBody, nil -} diff --git a/internal/services/settings/interface.go b/internal/services/settings/interface.go index 22250e0..87c2b50 100644 --- a/internal/services/settings/interface.go +++ b/internal/services/settings/interface.go @@ -3,7 +3,6 @@ package settings // import ( // "context" -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" // ) // type SettingStore interface { diff --git a/internal/services/settings/service.go b/internal/services/settings/service.go index 8b9339a..fbea10b 100644 --- a/internal/services/settings/service.go +++ b/internal/services/settings/service.go @@ -1,10 +1,9 @@ package settings import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/ports" "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" ) type Service struct { diff --git a/internal/services/sportsbook/events.go b/internal/services/sportsbook/events.go deleted file mode 100644 index 9dd0eb7..0000000 --- a/internal/services/sportsbook/events.go +++ /dev/null @@ -1 +0,0 @@ -package sportsbook diff --git a/internal/services/sportsbook/odds.go b/internal/services/sportsbook/odds.go deleted file mode 100644 index 9dd0eb7..0000000 --- a/internal/services/sportsbook/odds.go +++ /dev/null @@ -1 +0,0 @@ -package sportsbook diff --git a/internal/services/sportsbook/service.go b/internal/services/sportsbook/service.go deleted file mode 100644 index 9dd0eb7..0000000 --- a/internal/services/sportsbook/service.go +++ /dev/null @@ -1 +0,0 @@ -package sportsbook diff --git a/internal/services/stats/bet.go b/internal/services/stats/bet.go deleted file mode 100644 index 67e78f9..0000000 --- a/internal/services/stats/bet.go +++ /dev/null @@ -1,11 +0,0 @@ -package stats - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Service) GetBetStatsByInterval(ctx context.Context, filter domain.BetStatsByIntervalFilter) ([]domain.BetStatsByInterval, error) { - return s.betStatStore.GetBetStatsByInterval(ctx, filter) -} diff --git a/internal/services/stats/branch.go b/internal/services/stats/branch.go deleted file mode 100644 index e35b4f6..0000000 --- a/internal/services/stats/branch.go +++ /dev/null @@ -1,16 +0,0 @@ -package stats - -import ( - "context" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Service) UpdateBranchStats(ctx context.Context) error { - return s.branchStatStore.UpdateBranchStats(ctx) -} -func (s *Service) GetBranchStatByID(ctx context.Context, branchID int64) ([]domain.BranchStat, error) { - return s.branchStatStore.GetBranchStatByID(ctx, branchID) -} -func (s *Service) GetBranchStatsByInterval(ctx context.Context, filter domain.BranchStatFilter) ([]domain.BranchStat, error) { - return s.branchStatStore.GetBranchStatsByInterval(ctx, filter) -} diff --git a/internal/services/stats/company.go b/internal/services/stats/company.go deleted file mode 100644 index a77cbba..0000000 --- a/internal/services/stats/company.go +++ /dev/null @@ -1,16 +0,0 @@ -package stats - -import ( - "context" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Service) UpdateCompanyStats(ctx context.Context) error { - return s.companyStatStore.UpdateCompanyStats(ctx) -} -func (s *Service) GetCompanyStatByID(ctx context.Context, companyID int64) ([]domain.CompanyStat, error) { - return s.companyStatStore.GetCompanyStatByID(ctx, companyID) -} -func (s *Service) GetCompanyStatsByInterval(ctx context.Context, filter domain.CompanyStatFilter) ([]domain.CompanyStat, error) { - return s.companyStatStore.GetCompanyStatsByInterval(ctx, filter) -} diff --git a/internal/services/stats/event.go b/internal/services/stats/event.go deleted file mode 100644 index 32a78e5..0000000 --- a/internal/services/stats/event.go +++ /dev/null @@ -1,18 +0,0 @@ -package stats - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Service) GetTotalEventStats(ctx context.Context, filter domain.EventStatsFilter) (domain.EventStats, error) { - return s.eventStatStore.GetTotalEventStats(ctx, filter) -} -func (s *Service) GetTotalEventStatsByInterval(ctx context.Context, filter domain.EventStatsByIntervalFilter) ([]domain.EventStatsByInterval, error) { - return s.eventStatStore.GetTotalEventStatsByInterval(ctx, filter) -} - -func (s *Service) UpdateEventBetStats(ctx context.Context) error { - 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 deleted file mode 100644 index ec74ba4..0000000 --- a/internal/services/stats/service.go +++ /dev/null @@ -1,27 +0,0 @@ -package stats - -import "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" - -type Service struct { - companyStatStore ports.CompanyStatStore - branchStatStore ports.BranchStatStore - eventStatStore ports.EventStatStore - betStatStore ports.BetStatStore - walletStatStore ports.WalletStatStore -} - -func NewService( - companyStatStore ports.CompanyStatStore, - branchStatStore ports.BranchStatStore, - eventStatStore ports.EventStatStore, - betStatStore ports.BetStatStore, - walletStatStore ports.WalletStatStore, -) *Service { - return &Service{ - companyStatStore: companyStatStore, - branchStatStore: branchStatStore, - eventStatStore: eventStatStore, - betStatStore: betStatStore, - walletStatStore: walletStatStore, - } -} diff --git a/internal/services/stats/wallet.go b/internal/services/stats/wallet.go deleted file mode 100644 index dc826f2..0000000 --- a/internal/services/stats/wallet.go +++ /dev/null @@ -1,16 +0,0 @@ -package stats - -import ( - "context" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Service) UpdateWalletStats(ctx context.Context) error { - return s.walletStatStore.UpdateWalletStats(ctx) -} -func (s *Service) GetWalletStatByID(ctx context.Context, walletID int64) ([]domain.WalletStat, error) { - return s.walletStatStore.GetWalletStatByID(ctx, walletID) -} -func (s *Service) GetWalletStatsByInterval(ctx context.Context, filter domain.WalletStatFilter) ([]domain.WalletStat, error) { - return s.walletStatStore.GetWalletStatsByInterval(ctx, filter) -} diff --git a/internal/services/telebirr/service.go b/internal/services/telebirr/service.go deleted file mode 100644 index 6258ecd..0000000 --- a/internal/services/telebirr/service.go +++ /dev/null @@ -1,407 +0,0 @@ -package telebirr - -import ( - "bytes" - "context" - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "crypto/x509" - "encoding/base64" - "encoding/json" - "encoding/pem" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "sort" - "strconv" - "strings" - "time" - - "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" -) - -// TokenResponse is the expected response from Telebirr - -type TelebirrService struct { - // client TelebirrClient - cfg *config.Config - transferStore ports.TransferStore - walletSvc *wallet.Service -} - -func NewTelebirrService(cfg *config.Config, transferStore ports.TransferStore, walletSvc *wallet.Service) *TelebirrService { - return &TelebirrService{ - cfg: cfg, - transferStore: transferStore, - walletSvc: walletSvc, - } -} - -// GetFabricToken fetches the fabric token from Telebirr -func GetTelebirrFabricToken(s *TelebirrService) (*domain.TelebirrFabricTokenResponse, error) { - // Prepare the request body - bodyMap := map[string]string{ - "appSecret": s.cfg.TELEBIRR.TelebirrAppSecret, - } - bodyBytes, err := json.Marshal(bodyMap) - if err != nil { - return nil, fmt.Errorf("failed to marshal request body: %v", err) - } - - // Prepare the HTTP request - req, err := http.NewRequest("POST", s.cfg.TELEBIRR.TelebirrBaseURL+"/payment/v1/token", bytes.NewBuffer(bodyBytes)) - if err != nil { - return nil, fmt.Errorf("failed to create request: %v", err) - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-APP-Key", s.cfg.TELEBIRR.TelebirrFabricAppID) - - // Perform the request - client := &http.Client{ - Timeout: 10 * time.Second, - } - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to perform request: %v", err) - } - defer resp.Body.Close() - - // Read and parse the response - respBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %v", err) - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("non-200 response: %d, body: %s", resp.StatusCode, string(respBody)) - } - - var tokenResp domain.TelebirrFabricTokenResponse - if err := json.Unmarshal(respBody, &tokenResp); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %v", err) - } - - return &tokenResp, nil -} - -func (s *TelebirrService) CreateTelebirrOrder(title string, amount float32, userID int64) (string, error) { - // Step 1: Get Fabric Token - tokenResp, err := GetTelebirrFabricToken(s) - if err != nil { - return "", fmt.Errorf("failed to get token: %v", err) - } - fabricToken := tokenResp.Token - - // Step 2: Create request object - orderID := fmt.Sprintf("%d", time.Now().UnixNano()) - bizContent := domain.TelebirrBizContent{ - NotifyURL: s.cfg.TELEBIRR.TelebirrCallbackURL, // Replace with actual - AppID: s.cfg.TELEBIRR.TelebirrFabricAppID, - MerchCode: s.cfg.TELEBIRR.TelebirrMerchantCode, - MerchOrderID: orderID, - TradeType: "Checkout", - Title: title, - TotalAmount: fmt.Sprintf("%.2f", amount), - TransCurrency: "ETB", - TimeoutExpress: "120m", - BusinessType: "WalletRefill", - PayeeIdentifier: s.cfg.TELEBIRR.TelebirrMerchantCode, - PayeeIdentifierType: "04", - PayeeType: "5000", - RedirectURL: s.cfg.ARIFPAY.SuccessUrl, // Replace with actual - CallbackInfo: "From web", - } - - requestPayload := domain.TelebirrPreOrderRequestPayload{ - Timestamp: fmt.Sprintf("%d", time.Now().Unix()), - NonceStr: generateNonce(), - Method: "payment.preorder", - Version: "1.0", - BizContent: bizContent, - SignType: "SHA256WithRSA", - } - - // Sign the request - signStr := canonicalSignString(preOrderPayloadToMap(requestPayload)) - signature, err := signSHA256WithRSA(signStr, s.cfg.TELEBIRR.TelebirrAppSecret) - if err != nil { - return "", fmt.Errorf("failed to sign request: %v", err) - } - requestPayload.Sign = signature - - // Marshal to JSON - bodyBytes, _ := json.Marshal(requestPayload) - - // Step 3: Make the request - req, _ := http.NewRequest("POST", s.cfg.TELEBIRR.TelebirrBaseURL+"/payment/v1/merchant/preOrder", bytes.NewReader(bodyBytes)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-APP-Key", s.cfg.TELEBIRR.TelebirrFabricAppID) - req.Header.Set("Authorization", fabricToken) - - client := &http.Client{Timeout: 15 * time.Second} - resp, err := client.Do(req) - if err != nil { - return "", fmt.Errorf("telebirr preOrder request failed: %v", err) - } - defer resp.Body.Close() - - body, _ := io.ReadAll(resp.Body) - if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("telebirr preOrder failed: %s", string(body)) - } - - var response map[string]interface{} - if err := json.Unmarshal(body, &response); err != nil { - return "", fmt.Errorf("telebirr response parse error: %v", err) - } - - biz := response["biz_content"].(map[string]interface{}) - prepayID := biz["prepay_id"].(string) - - // Step 4: Build checkout URL - checkoutURL, err := s.BuildTelebirrCheckoutURL(prepayID) - if err != nil { - return "", err - } - - SenderWallets, err := s.walletSvc.GetWalletsByUser(req.Context(), userID) - if err != nil { - return "", fmt.Errorf("failed to get user wallets: %v", err) - } - - s.transferStore.CreateTransfer(req.Context(), domain.CreateTransfer{ - Amount: domain.Currency(amount), - Verified: false, - Type: domain.DEPOSIT, - ReferenceNumber: orderID, - Status: string(domain.PaymentStatusPending), - SenderWalletID: domain.ValidInt64{ - Value: SenderWallets[0].ID, - Valid: true, - }, - Message: fmt.Sprintf("Telebirr order created with ID: %s and amount: %f", orderID, amount), - }) - - return checkoutURL, nil -} - -func (s *TelebirrService) BuildTelebirrCheckoutURL(prepayID string) (string, error) { - - // Convert params struct to map[string]string for signing - params := map[string]string{ - "app_id": s.cfg.TELEBIRR.TelebirrFabricAppID, - "merch_code": s.cfg.TELEBIRR.TelebirrMerchantCode, - "nonce_str": generateNonce(), - "prepay_id": prepayID, - "timestamp": fmt.Sprintf("%d", time.Now().Unix()), - } - signStr := canonicalSignString(params) - signature, err := signSHA256WithRSA(signStr, s.cfg.TELEBIRR.TelebirrAppSecret) - if err != nil { - return "", fmt.Errorf("failed to sign checkout URL: %v", err) - } - - query := url.Values{} - for k, v := range params { - query.Set(k, v) - } - query.Set("sign", signature) - query.Set("sign_type", "SHA256WithRSA") - query.Set("version", "1.0") - query.Set("trade_type", "Checkout") - - // Step 4: Build final URL - return s.cfg.TELEBIRR.TelebirrBaseURL + query.Encode(), nil -} - -func (s *TelebirrService) HandleTelebirrPaymentCallback(ctx context.Context, payload *domain.TelebirrPaymentCallbackPayload) error { - - transfer, err := s.transferStore.GetTransferByReference(ctx, payload.PaymentOrderID) - - if err != nil { - return fmt.Errorf("failed to fetch transfer by reference: %w", err) - } - - if transfer.Status != string(domain.PaymentStatusPending) { - return fmt.Errorf("payment not pending, status: %s", transfer.Status) - } else if transfer.Verified == true { - return fmt.Errorf("payment already verified") - } - - if payload.TradeStatus != "Completed" { - return fmt.Errorf("payment not completed, status: %s", payload.TradeStatus) - } - - // 1. Validate the signature - // if err := s.VerifyCallbackSignature(payload); err != nil { - // return fmt.Errorf("invalid callback signature: %w", err) - // } - - if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.PaymentStatusCompleted)); err != nil { - return fmt.Errorf("failed to update transfer status: %w", err) - } - - // 4. Parse amount - amount, err := strconv.ParseFloat(payload.TotalAmount, 64) - if err != nil { - return fmt.Errorf("invalid amount format: %s", payload.TotalAmount) - } - _, err = s.walletSvc.AddToWallet(ctx, transfer.SenderWalletID.Value, domain.Currency(amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, fmt.Sprintf("Added %v to wallet for Telebirr payment", amount)) - if err != nil { - return fmt.Errorf("failed to add amount to wallet: %w", err) - } - - if err := s.transferStore.UpdateTransferVerification(ctx, transfer.ID, true); err != nil { - return fmt.Errorf("failed to update transfer verification: %w", err) - } - - return nil -} - -// Verifies the RSA-SHA256 signature of the payload -// func (s *TelebirrService) VerifyCallbackSignature(payload *domain.TelebirrPaymentCallbackPayload) error { -// // 1. Extract the signature from the payload -// signatureBase64 := payload.Sign -// signType := payload.SignType - -// if signType != "SHA256WithRSA" { -// return fmt.Errorf("unsupported sign_type: %s", signType) -// } - -// // 2. Convert the payload to map (excluding 'sign' and 'sign_type') -// payloadMap := map[string]string{ -// "notify_url": payload.NotifyURL, -// "appid": payload.AppID, -// "notify_time": payload.NotifyTime, -// "merch_code": payload.MerchCode, -// "merch_order_id": payload.MerchOrderID, -// "payment_order_id": payload.PaymentOrderID, -// "total_amount": payload.TotalAmount, -// "trans_id": payload.TransID, -// "trans_currency": payload.TransCurrency, -// "trade_status": payload.TradeStatus, -// "trans_end_time": payload.TransEndTime, -// } - -// // 3. Sort the keys and build the canonical string -// var keys []string -// for k := range payloadMap { -// keys = append(keys, k) -// } -// sort.Strings(keys) - -// var canonicalParts []string -// for _, k := range keys { -// canonicalParts = append(canonicalParts, fmt.Sprintf("%s=%s", k, payloadMap[k])) -// } -// canonicalString := strings.Join(canonicalParts, "&") - -// // 4. Hash the canonical string -// hashed := sha256.Sum256([]byte(canonicalString)) - -// // 5. Decode the base64 signature -// signature, err := base64.StdEncoding.DecodeString(signatureBase64) -// if err != nil { -// return fmt.Errorf("failed to decode signature: %w", err) -// } - -// // 6. Load the RSA public key (PEM format) -// pubKeyPEM := []byte(s.cfg.TELEBIRR.PublicKey) // Must be full PEM string - -// block, _ := pem.Decode(pubKeyPEM) -// if block == nil || block.Type != "PUBLIC KEY" { -// return errors.New("invalid public key PEM block") -// } - -// pubKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes) -// if err != nil { -// return fmt.Errorf("failed to parse RSA public key: %w", err) -// } - -// rsaPubKey, ok := pubKeyInterface.(*rsa.PublicKey) -// if !ok { -// return errors.New("not a valid RSA public key") -// } - -// // 7. Verify the signature -// err = rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, hashed[:], signature) -// if err != nil { -// return fmt.Errorf("RSA signature verification failed: %w", err) -// } - -// return nil -// } - -func generateNonce() string { - return fmt.Sprintf("telebirr%x", time.Now().UnixNano()) -} - -func canonicalSignString(data map[string]string) string { - keys := make([]string, 0, len(data)) - for k := range data { - if k != "sign" && k != "sign_type" { - keys = append(keys, k) - } - } - sort.Strings(keys) - - var b strings.Builder - for i, k := range keys { - value := data[k] - var valStr string - if k == "biz_content" { - jsonVal, _ := json.Marshal(value) - valStr = string(jsonVal) - } else { - valStr = fmt.Sprintf("%v", value) - } - b.WriteString(fmt.Sprintf("%s=%s", k, valStr)) - if i < len(keys)-1 { - b.WriteString("&") - } - } - return b.String() -} - -func signSHA256WithRSA(signStr, privateKeyPEM string) (string, error) { - block, _ := pem.Decode([]byte(privateKeyPEM)) - if block == nil { - return "", fmt.Errorf("invalid PEM private key") - } - - priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - return "", fmt.Errorf("unable to parse private key: %v", err) - } - - hashed := sha256.Sum256([]byte(signStr)) - - sig, err := rsa.SignPKCS1v15(rand.Reader, priv.(*rsa.PrivateKey), crypto.SHA256, hashed[:]) - if err != nil { - return "", fmt.Errorf("signing failed: %v", err) - } - - return base64.StdEncoding.EncodeToString(sig), nil -} - -// Helper function to convert TelebirrPreOrderRequestPayload to map[string]string for signing -func preOrderPayloadToMap(payload domain.TelebirrPreOrderRequestPayload) map[string]string { - m := map[string]string{ - "timestamp": payload.Timestamp, - "nonce_str": payload.NonceStr, - "method": payload.Method, - "version": payload.Version, - "sign_type": payload.SignType, - } - // BizContent needs to be marshaled as JSON string - bizContentBytes, _ := json.Marshal(payload.BizContent) - m["biz_content"] = string(bizContentBytes) - return m -} diff --git a/internal/services/ticket/interface.go b/internal/services/ticket/interface.go deleted file mode 100644 index 43ef339..0000000 --- a/internal/services/ticket/interface.go +++ /dev/null @@ -1,2 +0,0 @@ -package ticket - diff --git a/internal/services/ticket/service.go b/internal/services/ticket/service.go deleted file mode 100644 index efe0910..0000000 --- a/internal/services/ticket/service.go +++ /dev/null @@ -1,290 +0,0 @@ -package ticket - -import ( - "context" - "encoding/json" - "errors" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/settings" - "go.uber.org/zap" -) - -var ( - // ErrGenerateRandomOutcome = errors.New("Failed to generate any random outcome for events") - // ErrOutcomesNotCompleted = errors.New("Some bet outcomes are still pending") - ErrTicketHasExpired = errors.New("Ticket has expired") - ErrNoEventsAvailable = errors.New("Not enough events available with the given filters") - ErrEventHasBeenRemoved = errors.New("Event has been removed") - ErrTooManyOutcomesForTicket = errors.New("Too many odds/outcomes for a single ticket") - ErrTicketAmountTooHigh = errors.New("Cannot create a ticket with an amount above limit") - ErrTicketLimitForSingleUser = errors.New("Number of Ticket Limit reached") - ErrTicketWinningTooHigh = errors.New("Total Winnings over set limit") - ErrInvalidAmount = errors.New("Invalid amount") - ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid") -) - -type Service struct { - ticketStore ports.TicketStore - eventSvc *event.Service - prematchSvc odds.ServiceImpl - mongoLogger *zap.Logger - settingSvc *settings.Service -} - -func NewService( - ticketStore ports.TicketStore, - eventSvc *event.Service, - prematchSvc odds.ServiceImpl, - mongoLogger *zap.Logger, - settingSvc *settings.Service, -) *Service { - return &Service{ - ticketStore: ticketStore, - eventSvc: eventSvc, - prematchSvc: prematchSvc, - mongoLogger: mongoLogger, - settingSvc: settingSvc, - } -} - -func (s *Service) GenerateTicketOutcome(ctx context.Context, settings domain.SettingList, eventID int64, marketID int64, oddID int64) (domain.CreateTicketOutcome, error) { - oddIDStr := strconv.FormatInt(oddID, 10) - event, err := s.eventSvc.GetEventByID(ctx, eventID) - if err != nil { - s.mongoLogger.Error("failed to fetch upcoming event by ID", - zap.Int64("event_id", eventID), - zap.Error(err), - ) - return domain.CreateTicketOutcome{}, ErrEventHasBeenRemoved - } - - // Checking to make sure the event hasn't already started - currentTime := time.Now() - if event.StartTime.Before(currentTime) { - s.mongoLogger.Error("event has already started", - zap.Int64("event_id", eventID), - zap.Time("event_start_time", event.StartTime), - zap.Time("current_time", currentTime), - ) - return domain.CreateTicketOutcome{}, ErrTicketHasExpired - } - - odds, err := s.prematchSvc.GetOddsByMarketID(ctx, marketID, eventID) - - if err != nil { - s.mongoLogger.Error("failed to get raw odds by market ID", - zap.Int64("event_id", eventID), - zap.Int64("market_id", marketID), - zap.Error(err), - ) - // return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid market id", err, nil) - - } - type rawOddType struct { - ID string - Name string - Odds string - Header string - Handicap string - } - var selectedOdd rawOddType - var isOddFound bool = false - for _, raw := range odds.RawOdds { - var rawOdd rawOddType - rawBytes, err := json.Marshal(raw) - err = json.Unmarshal(rawBytes, &rawOdd) - if err != nil { - s.mongoLogger.Error("failed to unmarshal raw ods", - zap.Int64("event_id", eventID), - zap.String("rawOddID", rawOdd.ID), - zap.Error(err), - ) - continue - } - - if rawOdd.ID == oddIDStr { - selectedOdd = rawOdd - isOddFound = true - } - } - - if !isOddFound { - // return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid odd id", nil, nil) - s.mongoLogger.Error("Invalid Odd ID", - zap.Int64("event_id", eventID), - zap.String("oddIDStr", oddIDStr), - ) - return domain.CreateTicketOutcome{}, ErrRawOddInvalid - } - - parsedOdd, err := strconv.ParseFloat(selectedOdd.Odds, 32) - if err != nil { - s.mongoLogger.Error("failed to parse selected odd value", - zap.String("odd", selectedOdd.Odds), - zap.Int64("odd_id", oddID), - zap.Error(err), - ) - return domain.CreateTicketOutcome{}, err - } - - newOutcome := domain.CreateTicketOutcome{ - EventID: eventID, - OddID: oddID, - MarketID: marketID, - HomeTeamName: event.HomeTeam, - AwayTeamName: event.AwayTeam, - MarketName: odds.MarketName, - Odd: float32(parsedOdd), - OddName: selectedOdd.Name, - OddHeader: selectedOdd.Header, - OddHandicap: selectedOdd.Handicap, - Expires: event.StartTime, - } - - // outcomes = append(outcomes, ) - - return newOutcome, nil - -} - -func (s *Service) CreateTicket(ctx context.Context, req domain.CreateTicketReq, clientIP string, companyID int64) (domain.Ticket, int64, error) { - settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID) - - // Check to see if the number of outcomes is above a set limit - if len(req.Outcomes) > int(settingsList.MaxNumberOfOutcomes) { - return domain.Ticket{}, 0, ErrTooManyOutcomesForTicket - - } - - if req.Amount < 1 { - return domain.Ticket{}, 0, ErrInvalidAmount - } - - // Check to see if the amount is above a set limit - if req.Amount > settingsList.BetAmountLimit.Float32() { - return domain.Ticket{}, 0, ErrTicketAmountTooHigh - } - - count, err := s.CountTicketByIP(ctx, clientIP) - - if err != nil { - s.mongoLogger.Error("failed to count number of ticket using ip", - zap.Error(err), - ) - return domain.Ticket{}, 0, err - } - - // Check to see how many tickets a single anonymous user has created - if count > settingsList.DailyTicketPerIP { - return domain.Ticket{}, 0, ErrTicketLimitForSingleUser - } - - var outcomes []domain.CreateTicketOutcome = make([]domain.CreateTicketOutcome, 0, len(req.Outcomes)) - var totalOdds float32 = 1 - for _, outcomeReq := range req.Outcomes { - newOutcome, err := s.GenerateTicketOutcome(ctx, settingsList, outcomeReq.EventID, outcomeReq.MarketID, outcomeReq.OddID) - if err != nil { - s.mongoLogger.Error("failed to generate outcome", - zap.Int64("event_id", outcomeReq.EventID), - zap.Int64("market_id", outcomeReq.MarketID), - zap.Int64("odd_id", outcomeReq.OddID), - zap.Error(err), - ) - return domain.Ticket{}, 0, err - } - totalOdds *= float32(newOutcome.Odd) - outcomes = append(outcomes, newOutcome) - } - totalWinnings := req.Amount * totalOdds - - // Check to see if the total winning amount is over a set limit - if totalWinnings > settingsList.TotalWinningLimit.Float32() { - s.mongoLogger.Info("Total Winnings over limit", - zap.Float32("Total Odds", totalOdds), - zap.Float32("amount", req.Amount), - zap.Float32("limit", settingsList.TotalWinningLimit.Float32())) - return domain.Ticket{}, 0, ErrTicketWinningTooHigh - } - - ticket, err := s.ticketStore.CreateTicket(ctx, domain.CreateTicket{ - Amount: domain.ToCurrency(req.Amount), - TotalOdds: totalOdds, - IP: clientIP, - CompanyID: companyID, - }) - if err != nil { - s.mongoLogger.Error("Error Creating Ticket", zap.Float32("Total Odds", totalOdds), zap.Float32("amount", req.Amount)) - return domain.Ticket{}, 0, err - } - - // Add the ticket id now that it has fetched from the database - for index := range outcomes { - outcomes[index].TicketID = ticket.ID - } - - rows, err := s.CreateTicketOutcome(ctx, outcomes) - - if err != nil { - s.mongoLogger.Error("Error Creating Ticket Outcomes", zap.Any("outcomes", outcomes)) - return domain.Ticket{}, rows, err - } - - return ticket, rows, nil -} - -// func (s *Service) CreateTicket(ctx context.Context, ticket domain.CreateTicket) (domain.Ticket, error) { -// return s.ticketStore.CreateTicket(ctx, ticket) -// } - -func (s *Service) CreateTicketOutcome(ctx context.Context, outcomes []domain.CreateTicketOutcome) (int64, error) { - return s.ticketStore.CreateTicketOutcome(ctx, outcomes) -} - -func (s *Service) GetTicketByID(ctx context.Context, id int64) (domain.GetTicket, error) { - return s.ticketStore.GetTicketByID(ctx, id) -} -func (s *Service) GetAllTickets(ctx context.Context, filter domain.TicketFilter) ([]domain.GetTicket, error) { - return s.ticketStore.GetAllTickets(ctx, filter) -} - -func (s *Service) CountTicketByIP(ctx context.Context, IP string) (int64, error) { - return s.ticketStore.CountTicketByIP(ctx, IP) -} - -func (s *Service) UpdateTicketOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error { - return s.ticketStore.UpdateTicketOutcomeStatus(ctx, id, status) -} -func (s *Service) DeleteTicket(ctx context.Context, id int64) error { - return s.ticketStore.DeleteTicket(ctx, id) -} - -func (s *Service) DeleteOldTickets(ctx context.Context) error { - return s.ticketStore.DeleteOldTickets(ctx) -} - -func (s *Service) CheckTicketError(err error) bool { - ticketError := []error{ - ErrTicketHasExpired, - ErrNoEventsAvailable, - ErrEventHasBeenRemoved, - ErrTooManyOutcomesForTicket, - ErrTicketAmountTooHigh, - ErrTicketLimitForSingleUser, - ErrTicketWinningTooHigh, - ErrInvalidAmount, - ErrRawOddInvalid, - } - - for _, e := range ticketError { - if errors.Is(err, e) { - return true - } - } - return false -} diff --git a/internal/services/transaction/interface.go b/internal/services/transaction/interface.go index 055412e..8f70532 100644 --- a/internal/services/transaction/interface.go +++ b/internal/services/transaction/interface.go @@ -3,7 +3,6 @@ package transaction // import ( // "context" -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" // ) // type TransactionStore interface { diff --git a/internal/services/transaction/service.go b/internal/services/transaction/service.go index 9e71f8f..7947f7b 100644 --- a/internal/services/transaction/service.go +++ b/internal/services/transaction/service.go @@ -1,17 +1,11 @@ package transaction import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/ports" + "Yimaru-Backend/internal/services/user" "context" "errors" - "fmt" - "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" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" ) var ( @@ -26,123 +20,114 @@ var ( type Service struct { transactionStore ports.TransactionStore - branchSvc branch.Service - betSvc bet.Service - walletSvc wallet.Service userSvc user.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, - betSvc: betSvc, - walletSvc: walletSvc, userSvc: userSvc, } } -func (s *Service) CreateShopTransaction(ctx context.Context, transaction domain.CreateShopTransaction) (domain.ShopTransaction, error) { - return s.transactionStore.CreateShopTransaction(ctx, transaction) -} -func (s *Service) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransactionDetail, error) { - return s.transactionStore.GetShopTransactionByID(ctx, id) -} -func (s *Service) GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransactionDetail, error) { - return s.transactionStore.GetAllShopTransactions(ctx, filter) -} -func (s *Service) GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransactionDetail, error) { - return s.transactionStore.GetShopTransactionByBranch(ctx, id) -} -func (s *Service) UpdateShopTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, role domain.Role, companyID domain.ValidInt64, branchID domain.ValidInt64) error { - // TODO: Move this into a service role verification service - // Checks to make sure only the same company and branch can modify this - transaction, err := s.GetShopTransactionByID(ctx, id) - if role != domain.RoleSuperAdmin { - if !companyID.Valid || companyID.Value != transaction.CompanyID { - // s.logger.Error("Failed to parse UpdateTransactionVerified request", "error", err) - return fmt.Errorf("user cannot modify another companies data") - } - if role == domain.RoleCashier { - if !branchID.Valid || branchID.Value != transaction.BranchID { - // h.logger.Error("Failed to parse UpdateTransactionVerified request", "error", "Unauthorized") - return fmt.Errorf("user cannot modify another companies data") - } - } - } +// func (s *Service) CreateShopTransaction(ctx context.Context, transaction domain.CreateShopTransaction) (domain.ShopTransaction, error) { +// return s.transactionStore.CreateShopTransaction(ctx, transaction) +// } +// func (s *Service) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransactionDetail, error) { +// return s.transactionStore.GetShopTransactionByID(ctx, id) +// } +// func (s *Service) GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransactionDetail, error) { +// return s.transactionStore.GetAllShopTransactions(ctx, filter) +// } +// func (s *Service) GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransactionDetail, error) { +// return s.transactionStore.GetShopTransactionByBranch(ctx, id) +// } +// func (s *Service) UpdateShopTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, role domain.Role, companyID domain.ValidInt64, branchID domain.ValidInt64) error { +// // TODO: Move this into a service role verification service +// // Checks to make sure only the same company and branch can modify this +// transaction, err := s.GetShopTransactionByID(ctx, id) +// if role != domain.RoleSuperAdmin { +// if !companyID.Valid || companyID.Value != transaction.CompanyID { +// // s.logger.Error("Failed to parse UpdateTransactionVerified request", "error", err) +// return fmt.Errorf("user cannot modify another companies data") +// } +// if role == domain.RoleCashier { +// if !branchID.Valid || branchID.Value != transaction.BranchID { +// // h.logger.Error("Failed to parse UpdateTransactionVerified request", "error", "Unauthorized") +// return fmt.Errorf("user cannot modify another companies data") +// } +// } +// } - if err != nil { - return err - } - switch transaction.Type { - case domain.TRANSACTION_BET: - if verified { - bet, err := s.GetShopBetByShopTransactionID(ctx, transaction.ID) +// if err != nil { +// return err +// } +// // switch transaction.Type { +// // case domain.TRANSACTION_BET: +// // if verified { +// // bet, err := s.GetShopBetByShopTransactionID(ctx, transaction.ID) - if err != nil { - return err - } +// // if err != nil { +// // return err +// // } - var firstEvent time.Time = bet.Outcomes[0].Expires - for _, outcome := range bet.Outcomes { - if outcome.Expires.Before(firstEvent) { - firstEvent = outcome.Expires - } - } +// // var firstEvent time.Time = bet.Outcomes[0].Expires +// // for _, outcome := range bet.Outcomes { +// // if outcome.Expires.Before(firstEvent) { +// // firstEvent = outcome.Expires +// // } +// // } - fmt.Printf("\n\n Shop bet expire %v - now %v \n\n", firstEvent, time.Now().UTC()) - if firstEvent.Before(time.Now()) { - return ErrShopBetHasExpired - } +// // fmt.Printf("\n\n Shop bet expire %v - now %v \n\n", firstEvent, time.Now().UTC()) +// // if firstEvent.Before(time.Now()) { +// // return ErrShopBetHasExpired +// // } - } +// // } - case domain.TRANSACTION_DEPOSIT: - if !verified { - // TODO: Figure out what to do here? Should i return the money or verify the faulty verify - return ErrDepositCannotBeUnverified - } +// // case domain.TRANSACTION_DEPOSIT: +// // if !verified { +// // // TODO: Figure out what to do here? Should i return the money or verify the faulty verify +// // return ErrDepositCannotBeUnverified +// // } - deposit, err := s.GetShopDepositByShopTransactionID(ctx, transaction.ID) - if err != nil { - return err - } - customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, deposit.CustomerID) +// // deposit, err := s.GetShopDepositByShopTransactionID(ctx, transaction.ID) +// // if err != nil { +// // return err +// // } +// // customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, deposit.CustomerID) - if err != nil { - return ErrUserHasNoCustomerWallet - } +// // if err != nil { +// // return ErrUserHasNoCustomerWallet +// // } - transfer, err := s.walletSvc.TransferToWallet(ctx, - deposit.BranchWalletID, customerWallet.RegularID, transaction.Amount, domain.TRANSFER_DIRECT, domain.ValidInt64{ - Value: transaction.UserID, - Valid: true, - }, - fmt.Sprintf("Transferred %v to customer wallet due to shop deposit", transaction.Amount), - ) +// // transfer, err := s.walletSvc.TransferToWallet(ctx, +// // deposit.BranchWalletID, customerWallet.RegularID, transaction.Amount, domain.TRANSFER_DIRECT, domain.ValidInt64{ +// // Value: transaction.UserID, +// // Valid: true, +// // }, +// // fmt.Sprintf("Transferred %v to customer wallet due to shop deposit", transaction.Amount), +// // ) - if err != nil { - return err - } +// // if err != nil { +// // return err +// // } - err = s.UpdateShopDepositTransferID(ctx, deposit.ID, domain.ValidInt64{ - Value: transfer.ID, - Valid: true, - }) +// // err = s.UpdateShopDepositTransferID(ctx, deposit.ID, domain.ValidInt64{ +// // Value: transfer.ID, +// // Valid: true, +// // }) - if err != nil { - return err - } - } +// // if err != nil { +// // return err +// // } +// // } - return s.transactionStore.UpdateShopTransactionVerified(ctx, id, verified, approvedBy) -} +// return s.transactionStore.UpdateShopTransactionVerified(ctx, id, verified, approvedBy) +// } func (s *Service) GetBranchByRole(ctx context.Context, branchID *int64, role domain.Role, userID int64, userCompanyID domain.ValidInt64) (*int64, *int64, error) { // var branchID int64 @@ -157,7 +142,7 @@ func (s *Service) GetBranchByRole(ctx context.Context, branchID *int64, role dom branch, err := s.branchSvc.GetBranchByID(ctx, *branchID) if err != nil { // h.logger.Error("CashoutReq no branches") - return nil, nil, ErrInvalidBetID + return nil, nil, err } // Check if the user has access to the company diff --git a/internal/services/transaction/shop_bet.go b/internal/services/transaction/shop_bet.go deleted file mode 100644 index 372726f..0000000 --- a/internal/services/transaction/shop_bet.go +++ /dev/null @@ -1,211 +0,0 @@ -package transaction - -import ( - "context" - "crypto/rand" - "errors" - "math/big" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -var ( - ErrInvalidBetID = errors.New("invalid bet id") - ErrUserHasNotWonBet = errors.New("user has not won bet") - ErrUserHasAlreadyCashoutOut = errors.New("user has already cashout") - ErrTransactionNotVerified = errors.New("transaction hasn't been verified") -) - -func (s *Service) GenerateCashoutID() (string, error) { - const chars = "abcdefghijklmnopqrstuvwxyz0123456789" - const length int = 13 - charLen := big.NewInt(int64(len(chars))) - result := make([]byte, length) - - for i := 0; i < length; i++ { - index, err := rand.Int(rand.Reader, charLen) - if err != nil { - // s.mongoLogger.Error("failed to generate random index for cashout ID", - // zap.Int("position", i), - // zap.Error(err), - // ) - return "", err - } - result[i] = chars[index.Int64()] - } - - return string(result), nil -} - -func (s *Service) CreateShopBet(ctx context.Context, userID int64, role domain.Role, userCompanyID domain.ValidInt64, req domain.ShopBetReq) (domain.ShopBet, error) { - - branchID, companyID, err := s.GetBranchByRole(ctx, req.BranchID, role, userID, userCompanyID) - - if err != nil { - return domain.ShopBet{}, err - } - cashoutID, err := s.GenerateCashoutID() - if err != nil { - return domain.ShopBet{}, err - } - - newBet, err := s.betSvc.PlaceBet(ctx, domain.CreateBetReq{ - Outcomes: req.Outcomes, - Amount: req.Amount, - BranchID: branchID, - }, userID, role, *companyID) - - if err != nil { - return domain.ShopBet{}, err - } - - newTransaction, err := s.CreateShopTransaction(ctx, domain.CreateShopTransaction{ - Amount: domain.ToCurrency(req.Amount), - BranchID: *branchID, - CompanyID: *companyID, - UserID: userID, - Type: domain.TRANSACTION_BET, - FullName: req.FullName, - PhoneNumber: req.PhoneNumber, - PaymentOption: req.PaymentOption, - BankCode: domain.ValidString{ - Value: req.BankCode, - Valid: req.BankCode != "", - }, - BeneficiaryName: domain.ValidString{ - Value: req.BeneficiaryName, - Valid: req.BeneficiaryName != "", - }, - AccountName: domain.ValidString{ - Value: req.AccountName, - Valid: req.AccountName != "", - }, - AccountNumber: domain.ValidString{ - Value: req.AccountNumber, - Valid: req.AccountNumber != "", - }, - ReferenceNumber: domain.ValidString{ - Value: req.ReferenceNumber, - Valid: req.ReferenceNumber != "", - }, - Verified: false, - ApprovedBy: domain.ValidInt64{ - Value: userID, - Valid: true, - }, - }) - - if err != nil { - return domain.ShopBet{}, err - } - - return s.transactionStore.CreateShopBet(ctx, domain.CreateShopBet{ - ShopTransactionID: newTransaction.ID, - CashoutID: cashoutID, - BetID: newBet.ID, - NumberOfOutcomes: int64(len(req.Outcomes)), - }) -} - -// func (s *Service) CreateShopBet(ctx context.Context, bet domain.CreateShopBet) (domain.ShopBet, error) { -// return s.transactionStore.CreateShopBet(ctx, bet) -// } - -func (s *Service) CashoutBet(ctx context.Context, betID int64, userID int64, role domain.Role, req domain.CashoutReq, userCompanyID domain.ValidInt64) (domain.ShopTransaction, error) { - branchID, companyID, err := s.GetBranchByRole(ctx, req.BranchID, role, userID, userCompanyID) - - if err != nil { - return domain.ShopTransaction{}, nil - } - - bet, err := s.GetShopBetByBetID(ctx, betID) - if err != nil { - // h.logger.Error("CashoutReq failed", "error", err) - return domain.ShopTransaction{}, ErrInvalidBetID - } - - if bet.Status != domain.OUTCOME_STATUS_WIN { - // h.logger.Error("CashoutReq failed, bet has not won") - return domain.ShopTransaction{}, ErrUserHasNotWonBet - } - - if bet.CashedOut { - // s.logger.Error(("Bet has already been cashed out")) - return domain.ShopTransaction{}, ErrUserHasAlreadyCashoutOut - } - - if !bet.TransactionVerified { - return domain.ShopTransaction{}, ErrTransactionNotVerified - } - - err = s.UpdateShopBetCashOut(ctx, bet.ID, true) - - if err != nil { - return domain.ShopTransaction{}, err - } - - return s.CreateShopTransaction(ctx, domain.CreateShopTransaction{ - Amount: bet.Amount, - BranchID: *branchID, - CompanyID: *companyID, - UserID: userID, - Type: domain.TRANSACTION_CASHOUT, - FullName: bet.FullName, - PhoneNumber: bet.PhoneNumber, - PaymentOption: req.PaymentOption, - BankCode: domain.ValidString{ - Value: req.BankCode, - Valid: req.BankCode != "", - }, - BeneficiaryName: domain.ValidString{ - Value: req.BeneficiaryName, - Valid: req.BeneficiaryName != "", - }, - AccountName: domain.ValidString{ - Value: req.AccountName, - Valid: req.AccountName != "", - }, - AccountNumber: domain.ValidString{ - Value: req.AccountNumber, - Valid: req.AccountNumber != "", - }, - ReferenceNumber: domain.ValidString{ - Value: req.ReferenceNumber, - Valid: req.ReferenceNumber != "", - }, - Verified: false, - ApprovedBy: domain.ValidInt64{ - Value: userID, - Valid: true, - }, - }) - -} - -func (s *Service) GetAllShopBet(ctx context.Context, filter domain.ShopBetFilter) ([]domain.ShopBetDetail, error) { - return s.transactionStore.GetAllShopBet(ctx, filter) -} - -func (s *Service) GetShopBetByID(ctx context.Context, id int64) (domain.ShopBetDetail, error) { - return s.transactionStore.GetShopBetByBetID(ctx, id) -} - -func (s *Service) GetShopBetByBetID(ctx context.Context, betID int64) (domain.ShopBetDetail, error) { - return s.transactionStore.GetShopBetByBetID(ctx, betID) -} - -func (s *Service) GetShopBetByCashoutID(ctx context.Context, cashoutID string) (domain.ShopBetDetail, error) { - return s.transactionStore.GetShopBetByCashoutID(ctx, cashoutID) -} - -func (s *Service) GetShopBetByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopBetDetail, error) { - return s.transactionStore.GetShopBetByShopTransactionID(ctx, shopTransactionID) -} - -func (s *Service) UpdateShopBetCashOut(ctx context.Context, id int64, cashedOut bool) error { - return s.transactionStore.UpdateShopBetCashOut(ctx, id, cashedOut) -} - -func (s *Service) UpdateShopBetCashoutID(ctx context.Context, id int64, cashoutID string) error { - return s.transactionStore.UpdateShopBetCashoutID(ctx, id, cashoutID) -} diff --git a/internal/services/transaction/shop_deposit.go b/internal/services/transaction/shop_deposit.go deleted file mode 100644 index aaef480..0000000 --- a/internal/services/transaction/shop_deposit.go +++ /dev/null @@ -1,121 +0,0 @@ -package transaction - -import ( - "context" - "errors" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -var ( - ErrUserHasNoCustomerWallet = errors.New("user has no customer wallet") -) - -func (s *Service) CreateShopDeposit(ctx context.Context, userID int64, role domain.Role, req domain.ShopDepositReq) (domain.ShopDeposit, error) { - var branchID int64 - var companyID int64 - var senderID int64 - switch role { - case domain.RoleAdmin, domain.RoleBranchManager, domain.RoleSuperAdmin: - if req.BranchID == nil { - // h.logger.Error("CashoutReq Branch ID is required for this user role") - return domain.ShopDeposit{}, ErrBranchRequiredForRole - } - branch, err := s.branchSvc.GetBranchByID(ctx, *req.BranchID) - if err != nil { - // h.logger.Error("CashoutReq no branches") - return domain.ShopDeposit{}, ErrInvalidBetID - } - - branchID = branch.ID - companyID = branch.CompanyID - senderID = branch.WalletID - case domain.RoleCashier: - branch, err := s.branchSvc.GetBranchByCashier(ctx, userID) - if err != nil { - // h.logger.Error("CashoutReq failed, branch id invalid") - return domain.ShopDeposit{}, ErrInvalidBranchID - } - branchID = branch.ID - companyID = branch.CompanyID - senderID = branch.WalletID - default: - return domain.ShopDeposit{}, ErrCustomerRoleNotAuthorized - } - - user, err := s.userSvc.GetUserByID(ctx, req.CustomerID) - - if err != nil { - return domain.ShopDeposit{}, err - } - - // if err != nil { - // return domain.ShopDeposit{}, err - // } - - newTransaction, err := s.CreateShopTransaction(ctx, domain.CreateShopTransaction{ - Amount: domain.ToCurrency(req.Amount), - BranchID: branchID, - CompanyID: companyID, - UserID: userID, - Type: domain.TRANSACTION_DEPOSIT, - FullName: user.FirstName + " " + user.LastName, - PhoneNumber: user.PhoneNumber, - PaymentOption: req.PaymentOption, - BankCode: domain.ValidString{ - Value: req.BankCode, - Valid: req.BankCode != "", - }, - BeneficiaryName: domain.ValidString{ - Value: req.BeneficiaryName, - Valid: req.BeneficiaryName != "", - }, - AccountName: domain.ValidString{ - Value: req.AccountName, - Valid: req.AccountName != "", - }, - AccountNumber: domain.ValidString{ - Value: req.AccountNumber, - Valid: req.AccountNumber != "", - }, - ReferenceNumber: domain.ValidString{ - Value: req.ReferenceNumber, - Valid: req.ReferenceNumber != "", - }, - Verified: false, - ApprovedBy: domain.ValidInt64{ - Value: userID, - Valid: true, - }, - }) - - if err != nil { - return domain.ShopDeposit{}, err - } - - return s.transactionStore.CreateShopDeposit(ctx, domain.CreateShopDeposit{ - ShopTransactionID: newTransaction.ID, - CustomerID: req.CustomerID, - BranchWalletID: senderID, - }) -} - -// func (s *Service) CreateShopDeposit(ctx context.Context, deposit domain.CreateShopDeposit) (domain.ShopDeposit, error) { -// return s.transactionStore.CreateShopDeposit(ctx, deposit) -// } - -func (s *Service) GetAllShopDeposit(ctx context.Context, filter domain.ShopDepositFilter) ([]domain.ShopDepositDetail, error) { - return s.transactionStore.GetAllShopDeposit(ctx, filter) -} - -func (s *Service) GetShopDepositByID(ctx context.Context, id int64) (domain.ShopDepositDetail, error) { - return s.transactionStore.GetShopDepositByID(ctx, id) -} - -func (s *Service) GetShopDepositByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopDepositDetail, error) { - return s.transactionStore.GetShopDepositByShopTransactionID(ctx, shopTransactionID) -} - -func (s *Service) UpdateShopDepositTransferID(ctx context.Context, id int64, transferID domain.ValidInt64) error { - return s.transactionStore.UpdateShopDepositTransferID(ctx, id, transferID) -} diff --git a/internal/services/user/common.go b/internal/services/user/common.go index 40b1814..acd2381 100644 --- a/internal/services/user/common.go +++ b/internal/services/user/common.go @@ -1,12 +1,12 @@ package user import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/pkgs/helpers" "context" "fmt" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers" "golang.org/x/crypto/bcrypt" ) @@ -31,7 +31,7 @@ func (s *Service) SendOtp(ctx context.Context, sentTo string, otpFor domain.OtpF return fmt.Errorf("invalid sms provider: %s", provider) } case domain.OtpMediumEmail: - if err := s.messengerSvc.SendEmail(ctx, sentTo, message, message, "FortuneBets - One Time Password"); err != nil { + if err := s.messengerSvc.SendEmail(ctx, sentTo, message, message, "FortuneBets - One Time Password"); err != nil { return err } } diff --git a/internal/services/user/direct.go b/internal/services/user/direct.go index 9f1a40f..71a8e03 100644 --- a/internal/services/user/direct.go +++ b/internal/services/user/direct.go @@ -1,9 +1,8 @@ package user import ( + "Yimaru-Backend/internal/domain" "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) func (s *Service) CreateUser(ctx context.Context, User domain.CreateUserReq, is_company bool) (domain.User, error) { @@ -19,7 +18,7 @@ func (s *Service) CreateUser(ctx context.Context, User domain.CreateUserReq, is_ // User.BranchID = branchId // User.Role = string(domain.RoleBranchManager) // } - + hashedPassword, err := hashPassword(User.Password) if err != nil { return domain.User{}, err diff --git a/internal/services/user/interface.go b/internal/services/user/interface.go index a590a74..479bf6f 100644 --- a/internal/services/user/interface.go +++ b/internal/services/user/interface.go @@ -3,7 +3,6 @@ package user // import ( // "context" -// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" // ) // type UserStore interface { diff --git a/internal/services/user/register.go b/internal/services/user/register.go index d09a7a7..1de928e 100644 --- a/internal/services/user/register.go +++ b/internal/services/user/register.go @@ -1,10 +1,9 @@ package user import ( + "Yimaru-Backend/internal/domain" "context" "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) func (s *Service) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error) { // email,phone,error diff --git a/internal/services/user/reset.go b/internal/services/user/reset.go index 4039e68..b507a4e 100644 --- a/internal/services/user/reset.go +++ b/internal/services/user/reset.go @@ -1,11 +1,10 @@ package user import ( + "Yimaru-Backend/internal/domain" "context" "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) func (s *Service) SendResetCode(ctx context.Context, medium domain.OtpMedium, sentTo string, provider domain.SMSProvider, companyID domain.ValidInt64) error { diff --git a/internal/services/user/service.go b/internal/services/user/service.go index 2e9fbb6..27f77cd 100644 --- a/internal/services/user/service.go +++ b/internal/services/user/service.go @@ -1,11 +1,10 @@ package user import ( + "Yimaru-Backend/internal/config" + "Yimaru-Backend/internal/ports" + "Yimaru-Backend/internal/services/messenger" "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/messenger" ) const ( diff --git a/internal/services/user/user.go b/internal/services/user/user.go index 6529c16..b825d58 100644 --- a/internal/services/user/user.go +++ b/internal/services/user/user.go @@ -1,9 +1,8 @@ package user import ( + "Yimaru-Backend/internal/domain" "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) func (s *Service) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) { diff --git a/internal/services/virtualGame/Alea/port.go b/internal/services/virtualGame/Alea/port.go deleted file mode 100644 index c5d4ac0..0000000 --- a/internal/services/virtualGame/Alea/port.go +++ /dev/null @@ -1,12 +0,0 @@ -package alea - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type AleaVirtualGameService interface { - GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) - HandleCallback(ctx context.Context, callback *domain.AleaPlayCallback) error -} diff --git a/internal/services/virtualGame/Alea/service.go b/internal/services/virtualGame/Alea/service.go deleted file mode 100644 index 5c7b367..0000000 --- a/internal/services/virtualGame/Alea/service.go +++ /dev/null @@ -1,160 +0,0 @@ -package alea - -import ( - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "errors" - "fmt" - "log/slog" - "net/url" - "time" - - "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/services/wallet" -) - -type AleaPlayService struct { - repo repository.VirtualGameRepository - walletSvc wallet.Service - config *config.AleaPlayConfig - logger *slog.Logger -} - -func NewAleaPlayService( - repo repository.VirtualGameRepository, - walletSvc wallet.Service, - cfg *config.Config, - logger *slog.Logger, -) *AleaPlayService { - return &AleaPlayService{ - repo: repo, - walletSvc: walletSvc, - config: &cfg.AleaPlay, - logger: logger, - } -} - -func (s *AleaPlayService) GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) { - session := &domain.VirtualGameSession{ - UserID: userID, - GameID: gameID, - SessionToken: generateSessionToken(userID), - Currency: currency, - Status: "ACTIVE", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - } - - if err := s.repo.CreateVirtualGameSession(ctx, session); err != nil { - return "", fmt.Errorf("failed to create game session: %w", err) - } - - params := url.Values{ - "operator_id": []string{s.config.OperatorID}, - "user_id": []string{fmt.Sprintf("%d", userID)}, - "game_id": []string{gameID}, - "currency": []string{currency}, - "session_token": []string{session.SessionToken}, - "mode": []string{mode}, - "timestamp": []string{fmt.Sprintf("%d", time.Now().Unix())}, - } - - signature := s.generateSignature(params.Encode()) - params.Add("signature", signature) - - return fmt.Sprintf("%s/launch?%s", s.config.BaseURL, params.Encode()), nil -} - -func (s *AleaPlayService) HandleCallback(ctx context.Context, callback *domain.AleaPlayCallback) error { - if !s.verifyCallbackSignature(callback) { - return errors.New("invalid callback signature") - } - - if existing, _ := s.repo.GetVirtualGameTransactionByExternalID(ctx, callback.TransactionID); existing != nil { - s.logger.Warn("duplicate transaction detected", "tx_id", callback.TransactionID) - return nil - } - - session, err := s.repo.GetVirtualGameSessionByToken(ctx, callback.SessionID) - if err != nil { - return fmt.Errorf("failed to get game session: %w", err) - } - - tx := &domain.VirtualGameTransaction{ - SessionID: session.ID, - UserID: session.UserID, - TransactionType: callback.Type, - Amount: convertAmount(callback.Amount, callback.Type), - Currency: callback.Currency, - ExternalTransactionID: callback.TransactionID, - Status: "COMPLETED", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - - if err := s.processTransaction(ctx, tx, session.UserID); err != nil { - return fmt.Errorf("failed to process transaction: %w", err) - } - - // Update session status using the proper repository method - // if callback.Type == "SESSION_END" { - // if err := s.repo.UpdateVirtualGameSessionStatus(ctx, session.ID, "COMPLETED"); err != nil { - // s.logger.Error("failed to update session status", - // "sessionID", session.ID, - // "error", err) - // } - // } - - return nil -} - -func convertAmount(amount float64, txType string) int64 { - cents := int64(amount * 100) - if txType == "BET" { - return -cents - } - return cents -} - -func (s *AleaPlayService) processTransaction(ctx context.Context, tx *domain.VirtualGameTransaction, userID int64) error { - wallets, err := s.walletSvc.GetWalletsByUser(ctx, userID) - if err != nil || len(wallets) == 0 { - return errors.New("no wallet available for user") - } - tx.WalletID = wallets[0].ID - _, err = s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, - fmt.Sprintf("Added %v to wallet for winning AleaPlay Virtual Bet", tx.Amount)) - if err != nil { - return fmt.Errorf("wallet update failed: %w", err) - } - - return s.repo.CreateVirtualGameTransaction(ctx, tx) -} - -func (s *AleaPlayService) generateSignature(data string) string { - h := hmac.New(sha256.New, []byte(s.config.SecretKey)) - h.Write([]byte(data)) - return hex.EncodeToString(h.Sum(nil)) -} - -func (s *AleaPlayService) verifyCallbackSignature(cb *domain.AleaPlayCallback) bool { - signData := fmt.Sprintf("%s%s%s%.2f%s%d", - cb.TransactionID, - cb.SessionID, - cb.Type, - cb.Amount, - cb.Currency, - cb.Timestamp, - ) - expectedSig := s.generateSignature(signData) - return expectedSig == cb.Signature -} - -func generateSessionToken(userID int64) string { - return fmt.Sprintf("alea-%d-%d", userID, time.Now().UnixNano()) -} diff --git a/internal/services/virtualGame/atlas/client.go b/internal/services/virtualGame/atlas/client.go deleted file mode 100644 index c504e9c..0000000 --- a/internal/services/virtualGame/atlas/client.go +++ /dev/null @@ -1,91 +0,0 @@ -package atlas - -import ( - "bytes" - "context" - "crypto/sha1" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "net/http" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" -) - -type Client struct { - http *http.Client - BaseURL string - PrivateKey string - CasinoID string - PartnerID string - walletSvc *wallet.Service -} - -func NewClient(cfg *config.Config, walletSvc *wallet.Service) *Client { - return &Client{ - http: &http.Client{Timeout: 10 * time.Second}, - BaseURL: cfg.Atlas.BaseURL, - PrivateKey: cfg.Atlas.SecretKey, // PRIVATE_KEY from Atlas - CasinoID: cfg.Atlas.CasinoID, // provided by Atlas - PartnerID: cfg.Atlas.PartnerID, // aggregator/casino partner_id - walletSvc: walletSvc, - } -} - -// Generate timestamp in ms -func nowTimestamp() string { - return fmt.Sprintf("%d", time.Now().UnixMilli()) -} - -// Signature generator: sha1 hex of (REQUEST_BODY + PRIVATE_KEY + timestamp) -func (c *Client) generateHash(body []byte, timestamp string) string { - plain := string(body) + c.PrivateKey + timestamp - h := sha1.New() - h.Write([]byte(plain)) - return hex.EncodeToString(h.Sum(nil)) -} - -// POST helper -func (c *Client) post(ctx context.Context, path string, body map[string]any, result any) error { - // Add timestamp first - timestamp := nowTimestamp() - - // Marshal without hash first - tmp, _ := json.Marshal(body) - - // Generate hash using original body - hash := c.generateHash(tmp, timestamp) - body["hash"] = hash - - body["timestamp"] = timestamp - - fmt.Printf("atlasPost: %v \n", body) - // Marshal final body - data, _ := json.Marshal(body) - - req, _ := http.NewRequestWithContext(ctx, "POST", c.BaseURL+path, bytes.NewReader(data)) - req.Header.Set("Content-Type", "application/json") - - // Debug - fmt.Println("Request URL:", c.BaseURL+path) - fmt.Println("Request Body:", string(data)) - - // Send request - res, err := c.http.Do(req) - if err != nil { - return err - } - defer res.Body.Close() - - b, _ := io.ReadAll(res.Body) - if res.StatusCode >= 400 { - return fmt.Errorf("error: %s", string(b)) - } - if result != nil { - return json.Unmarshal(b, result) - } - return nil -} diff --git a/internal/services/virtualGame/atlas/port.go b/internal/services/virtualGame/atlas/port.go deleted file mode 100644 index 832c828..0000000 --- a/internal/services/virtualGame/atlas/port.go +++ /dev/null @@ -1,20 +0,0 @@ -// services/veli/service.go -package atlas - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type AtlasVirtualGameService interface { - InitGame(ctx context.Context, req domain.AtlasGameInitRequest) (*domain.AtlasGameInitResponse, error) - GetUserData(ctx context.Context, req domain.AtlasGetUserDataRequest) (*domain.AtlasGetUserDataResponse, error) - ProcessBet(ctx context.Context, req domain.AtlasBetRequest) (*domain.AtlasBetResponse, error) - ProcessBetWin(ctx context.Context, req domain.AtlasBetWinRequest) (*domain.AtlasBetWinResponse, error) - ProcessRoundResult(ctx context.Context, req domain.RoundResultRequest) (*domain.RoundResultResponse, error) - ProcessRollBack(ctx context.Context, req domain.RollbackRequest) (*domain.RollbackResponse, error) - CreateFreeSpin(ctx context.Context, req domain.FreeSpinRequest) (*domain.FreeSpinResponse, error) - ProcessFreeSpinResult(ctx context.Context, req domain.FreeSpinResultRequest) (*domain.FreeSpinResultResponse, error) - ProcessJackPot(ctx context.Context, req domain.JackpotRequest) (*domain.JackpotResponse, error) -} diff --git a/internal/services/virtualGame/atlas/service.go b/internal/services/virtualGame/atlas/service.go deleted file mode 100644 index 9d70cee..0000000 --- a/internal/services/virtualGame/atlas/service.go +++ /dev/null @@ -1,350 +0,0 @@ -package atlas - -import ( - "context" - "errors" - "fmt" - "strconv" - - "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" -) - -type Service struct { - virtualGameSvc virtualgameservice.VirtualGameService - repo repository.VirtualGameRepository - client *Client - walletSvc *wallet.Service - transfetStore ports.TransferStore - cfg *config.Config -} - -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, - client: client, - walletSvc: walletSvc, - transfetStore: transferStore, - cfg: cfg, - } -} - -func (s *Service) InitGame(ctx context.Context, req domain.AtlasGameInitRequest) (*domain.AtlasGameInitResponse, error) { - - body := map[string]any{ - "game": req.Game, - "partner_id": s.client.PartnerID, - "casino_id": s.client.CasinoID, - "language": req.Language, - "currency": req.Currency, - "player_id": req.PlayerID, - } - - // 3. Call the Atlas client - var res domain.AtlasGameInitResponse - if err := s.client.post(ctx, "/init", body, &res); err != nil { - return nil, fmt.Errorf("failed to initialize game: %w", err) - } - - return &res, nil -} - -func (s *Service) GetUserData(ctx context.Context, req domain.AtlasGetUserDataRequest) (*domain.AtlasGetUserDataResponse, error) { - // 1. Validate casino_id and hash if needed - if req.CasinoID != s.client.CasinoID { - return nil, fmt.Errorf("invalid casino_id") - } - // 2. Fetch player from DB - playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid playerID: %w", err) - } - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) - if err != nil { - return nil, fmt.Errorf("failed to fetch walllets for player %d: %w", playerIDInt, err) - } - - // 4. Build response - res := &domain.AtlasGetUserDataResponse{ - PlayerID: req.PlayerID, - Balance: float64(wallet.RegularBalance), - } - - return res, nil -} - -func (s *Service) ProcessBet(ctx context.Context, req domain.AtlasBetRequest) (*domain.AtlasBetResponse, error) { - // --- 1. Validate CasinoID --- - if req.CasinoID != s.client.CasinoID { - return nil, fmt.Errorf("BAD_REQUEST: invalid casino_id") - } - - // --- 2. Validate PlayerID --- - playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID) - } - - // --- 3. Fetch player wallet --- - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) - } - - // --- 4. Convert balance using Float32() --- - realBalance := float64(wallet.RegularBalance) - - // --- 5. Ensure sufficient balance --- - if realBalance < req.Amount { - return nil, domain.ErrInsufficientBalance - } - - // --- 6. Deduct amount from wallet --- - newBalance := realBalance - req.Amount - err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(newBalance))) - if err != nil { - return nil, fmt.Errorf("failed to debit wallet: %w", err) - } - - // --- 7. Build response --- - res := &domain.AtlasBetResponse{ - PlayerID: req.PlayerID, - Balance: newBalance, - } - - return res, nil -} - -func (s *Service) ProcessBetWin(ctx context.Context, req domain.AtlasBetWinRequest) (*domain.AtlasBetWinResponse, error) { - // --- 1. Validate CasinoID --- - if req.CasinoID != s.client.CasinoID { - return nil, fmt.Errorf("BAD_REQUEST: invalid casino_id") - } - - // --- 2. Validate PlayerID --- - playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID) - } - - // --- 3. Fetch player wallet --- - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) - } - - // --- 4. Convert balance using Float32() --- - realBalance := float64(wallet.RegularBalance) - - // --- 5. Ensure sufficient balance for bet --- - if realBalance < req.BetAmount { - return nil, domain.ErrInsufficientBalance - } - - // --- 6. Deduct bet amount --- - debitedBalance := realBalance - req.BetAmount - err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(debitedBalance))) - if err != nil { - return nil, fmt.Errorf("failed to debit wallet: %w", err) - } - - // --- 7. Apply win amount (if any) --- - finalBalance := debitedBalance - if req.WinAmount > 0 { - finalBalance = debitedBalance + req.WinAmount - err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(finalBalance))) - if err != nil { - return nil, fmt.Errorf("failed to credit wallet: %w", err) - } - } - - // --- 8. Build response --- - res := &domain.AtlasBetWinResponse{ - PlayerID: req.PlayerID, - Balance: finalBalance, - } - - return res, nil -} - -func (s *Service) ProcessRoundResult(ctx context.Context, req domain.RoundResultRequest) (*domain.RoundResultResponse, error) { - // --- 1. Validate required fields --- - if req.PlayerID == "" || req.TransactionID == "" { - return nil, fmt.Errorf("BAD_REQUEST: missing player_id or transaction_id") - } - - // --- 2. Credit player if win amount > 0 --- - if req.Amount > 0 { - playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID) - } - - // --- 3. Fetch player wallet --- - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) - } - - // --- 4. Convert balance and apply win amount --- - currentBalance := float64(wallet.RegularBalance) - newBalance := currentBalance + req.Amount - - err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(newBalance))) - if err != nil { - return nil, fmt.Errorf("failed to credit wallet: %w", err) - } - } - - // --- 5. Build response --- - return &domain.RoundResultResponse{ - Success: true, - }, nil -} - -func (s *Service) ProcessRollBack(ctx context.Context, req domain.RollbackRequest) (*domain.RollbackResponse, error) { - // --- 1. Validate required fields --- - if req.PlayerID == "" || req.BetTransactionID == "" { - return nil, fmt.Errorf("BAD_REQUEST: missing player_id or bet_transaction_id") - } - - // --- 2. Parse PlayerID --- - playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID) - } - - // --- 3. Fetch player wallet --- - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) - } - - // --- 4. Fetch original transfer --- - transfer, err := s.transfetStore.GetTransferByReference(ctx, req.BetTransactionID) - if err != nil { - return nil, fmt.Errorf("failed to fetch transfer for reference %s: %w", req.BetTransactionID, err) - } - - // --- 5. Compute new balance using Float32() conversion --- - currentBalance := float64(wallet.RegularBalance) - newBalance := currentBalance + float64(transfer.Amount) - - // --- 6. Credit player wallet --- - err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(newBalance))) - if err != nil { - return nil, fmt.Errorf("failed to credit wallet: %w", err) - } - - // --- 7. Update transfer status and verification --- - err = s.transfetStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.STATUS_CANCELLED)) - if err != nil { - return nil, fmt.Errorf("failed to update transfer status: %w", err) - } - - err = s.transfetStore.UpdateTransferVerification(ctx, transfer.ID, true) - if err != nil { - return nil, fmt.Errorf("failed to update transfer verification: %w", err) - } - - // --- 8. Build response --- - return &domain.RollbackResponse{ - Success: true, - }, nil -} - -func (s *Service) CreateFreeSpin(ctx context.Context, req domain.FreeSpinRequest) (*domain.FreeSpinResponse, error) { - - body := map[string]any{ - "casino_id": s.client.CasinoID, - "freespins_count": req.FreeSpinsCount, - "end_date": req.EndDate, - "player_id": req.PlayerID, - } - - // 3. Call the Atlas client - var res domain.FreeSpinResponse - if err := s.client.post(ctx, "/freespin", body, &res); err != nil { - return nil, fmt.Errorf("failed to create free spin: %w", err) - } - - return &res, nil -} - -func (s *Service) ProcessFreeSpinResult(ctx context.Context, req domain.FreeSpinResultRequest) (*domain.FreeSpinResultResponse, error) { - if req.PlayerID == "" || req.TransactionID == "" { - return nil, errors.New("missing player_id or transaction_id") - } - - if req.Amount <= 0 { - // No winnings to process - return &domain.FreeSpinResultResponse{Success: true}, nil - } - - playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid playerID: %w", err) - } - - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) - } - - err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.Currency(float64(wallet.RegularBalance)+req.Amount)) - if err != nil { - return nil, fmt.Errorf("failed to credit wallet: %w", err) - } - - // Optionally record the transaction in your transfer history for auditing - // _ = s.transfetStore.CreateGamePayoutTransfer(...) - - return &domain.FreeSpinResultResponse{Success: true}, nil -} - -func (s *Service) ProcessJackPot(ctx context.Context, req domain.JackpotRequest) (*domain.JackpotResponse, error) { - if req.PlayerID == "" || req.TransactionID == "" { - return nil, errors.New("missing player_id or transaction_id") - } - - if req.Amount <= 0 { - // No jackpot winnings - return &domain.JackpotResponse{Success: true}, nil - } - - playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid playerID: %w", err) - } - - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) - } - - _, err = s.walletSvc.AddToWallet( - ctx, - wallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.PaymentMethod(domain.DEPOSIT), - domain.PaymentDetails{ - ReferenceNumber: domain.ValidString{ - Value: req.TransactionID, - Valid: true, - }, - // Description: fmt.Sprintf("Jackpot win - Txn: %s", req.TransactionID), - }, - "", - ) - if err != nil { - return nil, fmt.Errorf("failed to credit wallet: %w", err) - } - - return &domain.JackpotResponse{Success: true}, nil -} diff --git a/internal/services/virtualGame/orchestration/favourites.go b/internal/services/virtualGame/orchestration/favourites.go deleted file mode 100644 index f3f2408..0000000 --- a/internal/services/virtualGame/orchestration/favourites.go +++ /dev/null @@ -1,71 +0,0 @@ -package orchestration - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -func (s *Service) AddFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error { - return s.repo.AddFavoriteGame(ctx, userID, gameID, providerID) -} - -func (s *Service) RemoveFavoriteGame(ctx context.Context, userID, gameID int64, providerID string) error { - return s.repo.RemoveFavoriteGame(ctx, userID, gameID, providerID) -} - -func (s *Service) ListFavoriteGames( - ctx context.Context, - userID int64, - providerID *string, - limit, offset int32, -) ([]domain.UnifiedGame, error) { - - // Fetch favorite games directly from repository - games, err := s.repo.ListFavoriteGames(ctx, userID, providerID, limit, offset) - if err != nil { - // s.logger.Error("Failed to list favorite games", "userID", userID, "error", err) - return nil, err - } - - // If no favorites, return empty list - if len(games) == 0 { - return []domain.UnifiedGame{}, nil - } - - favorites := make([]domain.UnifiedGame, 0, len(games)) - - for _, g := range games { - var rtpPtr *float64 - if g.Rtp.Valid { - if f, err := g.Rtp.Float64Value(); err == nil { - rtpPtr = new(float64) - *rtpPtr = f.Float64 - } - } - - bets := make([]float64, len(g.Bets)) - for i, b := range g.Bets { - bets[i] = float64(b.Exp) - } - - favorites = append(favorites, domain.UnifiedGame{ - GameID: g.GameID, // assuming g.GameID is string - ProviderID: g.ProviderID, // provider id - Provider: g.ProviderID, // you can map a full name if needed - Name: g.Name, - Category: g.Category.String, // nullable - DeviceType: g.DeviceType.String, // nullable - Volatility: g.Volatility.String, // nullable - RTP: rtpPtr, - HasDemo: g.HasDemo.Bool, - HasFreeBets: g.HasFreeBets.Bool, - Bets: bets, - Thumbnail: g.Thumbnail.String, - Status: int(g.Status.Int32), - // DemoURL: g..String, - }) - } - - return favorites, nil -} diff --git a/internal/services/virtualGame/orchestration/port.go b/internal/services/virtualGame/orchestration/port.go deleted file mode 100644 index d44ccd7..0000000 --- a/internal/services/virtualGame/orchestration/port.go +++ /dev/null @@ -1,14 +0,0 @@ -package orchestration - -import ( - "context" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type OrchestrationService interface { - FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error) - GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVirtualGamesParams) ([]domain.UnifiedGame, error) - AddProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) -} diff --git a/internal/services/virtualGame/orchestration/report.go b/internal/services/virtualGame/orchestration/report.go deleted file mode 100644 index 5c6203e..0000000 --- a/internal/services/virtualGame/orchestration/report.go +++ /dev/null @@ -1 +0,0 @@ -package orchestration \ No newline at end of file diff --git a/internal/services/virtualGame/orchestration/service.go b/internal/services/virtualGame/orchestration/service.go deleted file mode 100644 index cbc3ca2..0000000 --- a/internal/services/virtualGame/orchestration/service.go +++ /dev/null @@ -1,535 +0,0 @@ -package orchestration - -import ( - "context" - "fmt" - "time" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" - virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" - "github.com/jackc/pgx/v5/pgtype" -) - -type Service struct { - virtualGameSvc virtualgameservice.VirtualGameService - veliVirtualGameSvc veli.VeliVirtualGameService - repo repository.VirtualGameRepository - cfg *config.Config - client *veli.Client -} - -func New(virtualGameSvc virtualgameservice.VirtualGameService, repo repository.VirtualGameRepository, cfg *config.Config, client *veli.Client) *Service { - return &Service{ - virtualGameSvc: virtualGameSvc, - repo: repo, - cfg: cfg, - client: client, - } -} - -func (s *Service) AddProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) { - - // logger := s.mongoLogger.With(zap.String("service", "AddProviders"), zap.Any("ProviderRequest", req)) - - // 0. Remove all existing providers first - if err := s.repo.DeleteAllVirtualGameProviders(ctx); err != nil { - // logger.Error("failed to delete all virtual game providers", zap.Error(err)) - return nil, fmt.Errorf("failed to clear existing providers: %w", err) - } - - // 1. Prepare signature parameters - sigParams := map[string]any{ - "brandId": req.BrandID, - } - - // Optional fields - sigParams["extraData"] = fmt.Sprintf("%t", req.ExtraData) // false is still included - if req.Size > 0 { - sigParams["size"] = fmt.Sprintf("%d", req.Size) - } else { - sigParams["size"] = "" - } - - if req.Page > 0 { - sigParams["page"] = fmt.Sprintf("%d", req.Page) - } else { - sigParams["page"] = "" - } - - // 2. Call external API - var res domain.ProviderResponse - if err := s.client.Post(ctx, "/game-lists/public/providers", req, sigParams, &res); err != nil { - return nil, fmt.Errorf("failed to fetch providers: %w", err) - } - - // 3. Loop through fetched providers and insert into DB - for _, p := range res.Items { - createParams := dbgen.CreateVirtualGameProviderParams{ - ProviderID: p.ProviderID, - ProviderName: p.ProviderName, - LogoDark: pgtype.Text{String: p.LogoForDark, Valid: p.LogoForDark != ""}, - LogoLight: pgtype.Text{String: p.LogoForLight, Valid: p.LogoForLight != ""}, - Enabled: true, - } - - if _, err := s.repo.CreateVirtualGameProvider(ctx, createParams); err != nil { - // logger.Error("failed to add provider", zap.Error(err)) - return nil, fmt.Errorf("failed to add provider %s: %w", p.ProviderID, err) - } - } - - // 4. Always add "popok" provider manually - popokParams := dbgen.CreateVirtualGameProviderParams{ - ProviderID: "popok", - ProviderName: "Popok Gaming", - LogoDark: pgtype.Text{String: fmt.Sprintf("%v/static/logos/popok-dark.png", s.cfg.PopOK.CallbackURL), Valid: true}, // adjust as needed - LogoLight: pgtype.Text{String: fmt.Sprintf("%v/static/logos/popok-light.png", s.cfg.PopOK.CallbackURL), Valid: true}, // adjust as needed - Enabled: true, - } - - atlasParams := dbgen.CreateVirtualGameProviderParams{ - ProviderID: "atlas", - ProviderName: "Atlas Gaming", - LogoDark: pgtype.Text{String: "/static/logos/atlas-dark.png", Valid: true}, // adjust as needed - LogoLight: pgtype.Text{String: "/static/logos/atlas-light.png", Valid: true}, // adjust as needed - Enabled: true, - } - - if _, err := s.repo.CreateVirtualGameProvider(ctx, popokParams); err != nil { - // logger.Error("failed to add popok provider", zap.Any("popokParams", popokParams), zap.Error(err)) - return nil, fmt.Errorf("failed to add popok provider: %w", err) - } - - if _, err := s.repo.CreateVirtualGameProvider(ctx, atlasParams); err != nil { - return nil, fmt.Errorf("failed to add atlas provider: %w", err) - } - - // Optionally also append it to the response for consistency - // res.Items = append(res.Items, domain.VirtualGameProvider{ - // ProviderID: uuid.New().String(), - // ProviderName: "Popok Gaming", - // LogoForDark: "/static/logos/popok-dark.png", - // LogoForLight: "/static/logos/popok-light.png", - // }) - - return &res, nil -} - -func (s *Service) GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVirtualGamesParams) ([]domain.UnifiedGame, error) { - // Build params for repo call - // logger := s.mongoLogger.With(zap.String("service", "GetAllVirtualGames"), zap.Any("params", params)) - rows, err := s.repo.ListAllVirtualGames(ctx, params) - if err != nil { - // logger.Error("[GetAllVirtualGames] Failed to fetch virtual games", zap.Error(err)) - return nil, fmt.Errorf("failed to fetch virtual games: %w", err) - } - - var allGames []domain.UnifiedGame - for _, r := range rows { - // --- Convert nullable Rtp to *float64 --- - var rtpPtr *float64 - if r.Rtp.Valid { - rtpFloat, err := r.Rtp.Float64Value() - if err == nil { - rtpPtr = new(float64) - *rtpPtr = rtpFloat.Float64 - } - } - var betsFloat64 []float64 - for _, bet := range r.Bets { - if bet.Valid { - betFloat, err := bet.Float64Value() - if err == nil { - betsFloat64 = append(betsFloat64, betFloat.Float64) - } - } - } - - allGames = append(allGames, domain.UnifiedGame{ - GameID: r.GameID, - ProviderID: r.ProviderID, - Provider: r.ProviderName, - Name: r.Name, - Category: r.Category.String, - DeviceType: r.DeviceType.String, - Volatility: r.Volatility.String, - RTP: rtpPtr, - HasDemo: r.HasDemo.Bool, - HasFreeBets: r.HasFreeBets.Bool, - Bets: betsFloat64, - Thumbnail: r.Thumbnail.String, - Status: int(r.Status.Int32), // nullable status - }) - } - - return allGames, nil -} - -func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error) { - // logger := s.mongoLogger.With( - // zap.String("service", "FetchAndStoreAllVirtualGames"), - // zap.Any("ProviderRequest", req), - // ) - - // This is necessary since the provider is a foreign key - _, err := s.AddProviders(ctx, req) - if err != nil { - return nil, fmt.Errorf("failed to add providers to database: %w", err) - } - - var allGames []domain.UnifiedGame - - // --- 1. Existing providers (Veli Games) --- - providersRes, err := s.veliVirtualGameSvc.GetProviders(ctx, req) - if err != nil { - // logger.Error("Failed to fetch provider", zap.Error(err)) - return nil, fmt.Errorf("failed to fetch providers: %w", err) - } - - // --- 2. Fetch games for each provider (Veli Games) --- - for _, p := range providersRes.Items { - games, err := s.veliVirtualGameSvc.GetGames(ctx, domain.GameListRequest{ - BrandID: s.cfg.VeliGames.BrandID, - ProviderID: p.ProviderID, - Page: req.Page, - Size: req.Size, - }) - if err != nil { - // logger.Error("failed to get veli games", zap.String("ProviderID", p.ProviderID), zap.Error(err)) - continue // skip failing provider but continue others - } - - for _, g := range games { - unified := domain.UnifiedGame{ - GameID: g.GameID, - ProviderID: g.ProviderID, - Provider: p.ProviderName, - Name: g.Name, - Category: g.Category, - DeviceType: g.DeviceType, - HasDemo: g.HasDemoMode, - HasFreeBets: g.HasFreeBets, - } - allGames = append(allGames, unified) - - // Save to DB - _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ - GameID: g.GameID, - ProviderID: g.ProviderID, - Name: g.Name, - Category: pgtype.Text{ - String: g.Category, - Valid: g.Category != "", - }, - DeviceType: pgtype.Text{ - String: g.DeviceType, - Valid: g.DeviceType != "", - }, - HasDemo: pgtype.Bool{ - Bool: g.HasDemoMode, - Valid: true, - }, - HasFreeBets: pgtype.Bool{ - Bool: g.HasFreeBets, - Valid: true, - }, - }) - if err != nil { - // logger.Error("failed to create virtual game", zap.Error(err)) - } - } - } - - // --- 3. Fetch Atlas-V games --- - atlasGames, err := s.veliVirtualGameSvc.GetAtlasVGames(ctx) - if err != nil { - // logger.Error("failed to fetch Atlas-V games", zap.Error(err)) - } else { - for _, g := range atlasGames { - unified := domain.UnifiedGame{ - GameID: g.GameID, - ProviderID: "atlasv", - Provider: "Atlas-V Gaming", // "Atlas-V" - Name: g.Name, - Category: g.Category, // using Type as Category - Thumbnail: g.Thumbnail, - HasDemo: true, - DemoURL: g.DemoURL, - } - allGames = append(allGames, unified) - - // Save to DB - _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ - GameID: g.GameID, - ProviderID: "atlasv", - Name: g.Name, - Category: pgtype.Text{ - String: g.Category, - Valid: g.Category != "", - }, - Thumbnail: pgtype.Text{ - String: g.Thumbnail, - Valid: g.Thumbnail != "", - }, - HasDemo: pgtype.Bool{ - Bool: g.HasDemoMode, - Valid: true, - }, - }) - if err != nil { - // logger.Error("failed to create Atlas-V virtual game", zap.Error(err)) - } - } - } - - // --- 4. Handle PopOK separately --- - popokGames, err := s.virtualGameSvc.ListGames(ctx, currency) - if err != nil { - // logger.Error("failed to fetch PopOk games", zap.Error(err)) - return nil, fmt.Errorf("failed to fetch PopOK games: %w", err) - } - - for _, g := range popokGames { - unified := domain.UnifiedGame{ - GameID: fmt.Sprintf("%d", g.ID), - ProviderID: "popok", - Provider: "Popok Gaming", - Name: g.GameName, - Category: "Crash", - Bets: g.Bets, - Thumbnail: g.Thumbnail, - Status: g.Status, - } - allGames = append(allGames, unified) - - // Convert []float64 to []pgtype.Numeric - var betsNumeric []pgtype.Numeric - for _, bet := range g.Bets { - var num pgtype.Numeric - _ = num.Scan(bet) - betsNumeric = append(betsNumeric, num) - } - - // Save to DB - _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ - GameID: fmt.Sprintf("%d", g.ID), - ProviderID: "popok", - Name: g.GameName, - Bets: betsNumeric, - Thumbnail: pgtype.Text{ - String: g.Thumbnail, - Valid: g.Thumbnail != "", - }, - Status: pgtype.Int4{ - Int32: int32(g.Status), - Valid: true, - }, - HasDemo: pgtype.Bool{ - Bool: true, - Valid: true, - }, - Category: pgtype.Text{ - String: "Crash", - Valid: true, - }, - }) - if err != nil { - // logger.Error("failed to create PopOK virtual game", zap.Error(err)) - } - } - - return allGames, nil -} - -func (s *Service) RemoveProvider(ctx context.Context, providerID string) error { - return s.repo.DeleteVirtualGameProvider(ctx, providerID) -} - -// Fetch provider by provider_id -func (s *Service) GetProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) { - return s.repo.GetVirtualGameProviderByID(ctx, providerID) -} - -// List providers with pagination -func (s *Service) ListProviders(ctx context.Context, limit, offset int32) ([]domain.VirtualGameProvider, int64, error) { - providers, err := s.repo.ListVirtualGameProviders(ctx, limit, offset) - if err != nil { - return nil, 0, err - } - - total, err := s.repo.CountVirtualGameProviders(ctx) - if err != nil { - return nil, 0, err - } - - // Convert []dbgen.VirtualGameProvider to []domain.VirtualGameProvider - domainProviders := make([]domain.VirtualGameProvider, len(providers)) - for i, p := range providers { - var logoDark *string - if p.LogoDark.Valid { - logoDark = &p.LogoDark.String - } - - var logoLight *string - if p.LogoLight.Valid { - logoLight = &p.LogoLight.String - } - - domainProviders[i] = domain.VirtualGameProvider{ - ProviderID: p.ProviderID, - ProviderName: p.ProviderName, - Enabled: p.Enabled, - LogoDark: logoDark, - LogoLight: logoLight, - CreatedAt: p.CreatedAt.Time, - UpdatedAt: &p.UpdatedAt.Time, - // Add other fields as needed - } - } - - return domainProviders, total, nil -} - -// Enable/Disable a provider -func (s *Service) SetProviderEnabled(ctx context.Context, providerID string, enabled bool) (*domain.VirtualGameProvider, error) { - provider, err := s.repo.UpdateVirtualGameProviderEnabled(ctx, providerID, enabled) - if err != nil { - //s.logger.Error("Failed to update provider enabled status", "provider_id", providerID, "enabled", enabled, "error", err) - return nil, err - } - now := time.Now() - provider.UpdatedAt.Time = now - - domainProvider := &domain.VirtualGameProvider{ - ProviderID: provider.ProviderID, - ProviderName: provider.ProviderName, - Enabled: provider.Enabled, - CreatedAt: provider.CreatedAt.Time, - UpdatedAt: &provider.UpdatedAt.Time, - // Add other fields as needed - } - - return domainProvider, nil -} - - - - - - - - - - -func (s *Service) CreateVirtualGameProviderReport(ctx context.Context, report domain.CreateVirtualGameProviderReport) (domain.VirtualGameProviderReport, error) { - // Example: logger := s.mongoLogger.With(zap.String("service", "CreateVirtualGameProviderReport"), zap.Any("Report", report)) - - // Call repository to create the report - created, err := s.repo.CreateVirtualGameProviderReport(ctx, report) - if err != nil { - // logger.Error("failed to create provider report", zap.Error(err), zap.Any("report", report)) - return domain.VirtualGameProviderReport{}, fmt.Errorf("failed to create provider report for provider %s: %w", report.ProviderID, err) - } - - // Return the created report - return created, nil -} - -func (s *Service) CreateVirtualGameReport(ctx context.Context, report domain.CreateVirtualGameReport) (domain.VirtualGameReport, error) { - // Example: logger := s.mongoLogger.With(zap.String("service", "CreateVirtualGameReport"), zap.Any("Report", report)) - - // Call repository to create the report - created, err := s.repo.CreateVirtualGameReport(ctx, report) - if err != nil { - // logger.Error("failed to create game report", zap.Error(err), zap.Any("report", report)) - return domain.VirtualGameReport{}, fmt.Errorf("failed to create game report for game %s: %w", report.GameID, err) - } - - // Return the created report - return created, nil -} - -func (s *Service) GetVirtualGameProviderReportByProviderAndDate( - ctx context.Context, - providerID string, - reportDate time.Time, - reportType string, -) (domain.VirtualGameProviderReport, error) { - // Example logger if needed - // logger := s.mongoLogger.With(zap.String("service", "GetVirtualGameProviderReportByProviderAndDate"), - // zap.String("provider_id", providerID), - // zap.Time("report_date", reportDate), - // zap.String("report_type", reportType), - // ) - - report, err := s.repo.GetVirtualGameProviderReportByProviderAndDate(ctx, providerID, reportDate, reportType) - if err != nil { - // logger.Error("failed to retrieve virtual game provider report", zap.Error(err)) - return domain.VirtualGameProviderReport{}, fmt.Errorf( - "failed to retrieve provider report for provider %s on %s (%s): %w", - providerID, reportDate.Format("2006-01-02"), reportType, err, - ) - } - - return report, nil -} - -func (s *Service) UpdateVirtualGameProviderReportByDate( - ctx context.Context, - providerID string, - reportDate time.Time, - reportType string, - totalGamesPlayed int64, - totalBets float64, - totalPayouts float64, - totalPlayers int64, -) error { - // Optionally log or trace the update - // Example: s.mongoLogger.Info("Updating virtual game provider report", - // zap.String("provider_id", providerID), - // zap.Time("report_date", reportDate), - // zap.String("report_type", reportType), - // ) - - err := s.repo.UpdateVirtualGameProviderReportByDate( - ctx, - providerID, - reportDate, - reportType, - totalGamesPlayed, - totalBets, - totalPayouts, - totalPlayers, - ) - if err != nil { - return fmt.Errorf("failed to update provider report for provider %s on %s (%s): %w", - providerID, - reportDate.Format("2006-01-02"), - reportType, - err, - ) - } - - return nil -} - -func (s *Service) ListVirtualGameProviderReportsByGamesPlayedAsc(ctx context.Context) ([]domain.VirtualGameProviderReport, error) { - reports, err := s.repo.ListVirtualGameProviderReportsByGamesPlayedAsc(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch virtual game provider reports ascending: %w", err) - } - return reports, nil -} - -// ListVirtualGameProviderReportsByGamesPlayedDesc fetches all reports sorted by total_games_played descending. -func (s *Service) ListVirtualGameProviderReportsByGamesPlayedDesc(ctx context.Context) ([]domain.VirtualGameProviderReport, error) { - reports, err := s.repo.ListVirtualGameProviderReportsByGamesPlayedDesc(ctx) - if err != nil { - return nil, fmt.Errorf("failed to fetch virtual game provider reports descending: %w", err) - } - return reports, nil -} diff --git a/internal/services/virtualGame/port.go b/internal/services/virtualGame/port.go deleted file mode 100644 index 83537c3..0000000 --- a/internal/services/virtualGame/port.go +++ /dev/null @@ -1,31 +0,0 @@ -package virtualgameservice - -import ( - "context" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type VirtualGameService interface { - // AddProvider(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) - // RemoveProvider(ctx context.Context, providerID string) error - // GetProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) - // ListProviders(ctx context.Context, limit, offset int32) ([]domain.VirtualGameProvider, int64, error) - // SetProviderEnabled(ctx context.Context, providerID string, enabled bool) (*domain.VirtualGameProvider, error) - - GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) - HandleCallback(ctx context.Context, callback *domain.PopOKCallback) error - ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (*domain.PopOKBetResponse, error) - GetPlayerInfo(ctx context.Context, req *domain.PopOKPlayerInfoRequest) (*domain.PopOKPlayerInfoResponse, error) - ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) - ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequest) (*domain.PopOKCancelResponse, error) - ProcessTournamentWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) - ProcessPromoWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) - - // GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) - ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error) - RecommendGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error) - // AddFavoriteGame(ctx context.Context, userID, gameID int64) error - // RemoveFavoriteGame(ctx context.Context, userID, gameID int64) error - // ListFavoriteGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error) -} diff --git a/internal/services/virtualGame/service.go b/internal/services/virtualGame/service.go deleted file mode 100644 index cd28f48..0000000 --- a/internal/services/virtualGame/service.go +++ /dev/null @@ -1,827 +0,0 @@ -package virtualgameservice - -import ( - "bytes" - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "log/slog" - "math/rand/v2" - "net/http" - "sort" - "strconv" - "time" - - "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/services/wallet" - jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" -) - -type service struct { - repo repository.VirtualGameRepository - walletSvc wallet.Service - store *repository.Store - // virtualGameStore repository.VirtualGameRepository - config *config.Config - logger *slog.Logger -} - -func New(repo repository.VirtualGameRepository, walletSvc wallet.Service, store *repository.Store, cfg *config.Config, logger *slog.Logger) VirtualGameService { - return &service{ - repo: repo, - walletSvc: walletSvc, - store: store, - config: cfg, - logger: logger, - } -} - -func (s *service) GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) { - // 1. Fetch user - user, err := s.store.GetUserByID(ctx, userID) - if err != nil { - s.logger.Error("Failed to get user", "userID", userID, "error", err) - return "", err - } - - // 2. Generate session and token - sessionID := fmt.Sprintf("%d-%s-%d", userID, gameID, time.Now().UnixNano()) - token, err := jwtutil.CreatePopOKJwt( - userID, - user.CompanyID, - user.PhoneNumber, - currency, - "en", - mode, - sessionID, - s.config.PopOK.SecretKey, - 24*time.Hour, - ) - if err != nil { - s.logger.Error("Failed to create PopOK JWT", "userID", userID, "error", err) - return "", err - } - - // 3. Record virtual game history (optional but recommended) - history := &domain.VirtualGameHistory{ - // SessionID: sessionID, - UserID: userID, - CompanyID: user.CompanyID.Value, - Provider: string(domain.PROVIDER_POPOK), - GameID: toInt64Ptr(gameID), - TransactionType: "LAUNCH", - Amount: 0, - Currency: currency, - ExternalTransactionID: sessionID, - Status: "COMPLETED", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - if err := s.repo.CreateVirtualGameHistory(ctx, history); err != nil { - s.logger.Error("Failed to record game launch transaction", "error", err) - // Non-fatal: log and continue - } - - // 4. Prepare PopOK API request - timestamp := time.Now().Format("02-01-2006 15:04:05") - partnerID, err := strconv.Atoi(s.config.PopOK.ClientID) - if err != nil { - return "", fmt.Errorf("invalid PopOK ClientID: %v", err) - } - - data := domain.PopokLaunchRequestData{ - GameMode: mode, - GameID: gameID, - Lang: "en", - Token: token, - ExitURL: "", - } - - hash, err := generatePopOKHash(s.config.PopOK.SecretKey, timestamp, data) - if err != nil { - return "", fmt.Errorf("failed to generate PopOK hash: %w", err) - } - - platformInt, err := strconv.Atoi(s.config.PopOK.Platform) - if err != nil { - return "", fmt.Errorf("invalid PopOK Platform: %v", err) - } - reqBody := domain.PopokLaunchRequest{ - Action: "getLauncherURL", - Platform: platformInt, - PartnerID: partnerID, - Time: timestamp, - Hash: hash, - Data: data, - } - - // 5. Make API request - bodyBytes, _ := json.Marshal(reqBody) - resp, err := http.Post(s.config.PopOK.BaseURL+"/serviceApi.php", "application/json", bytes.NewReader(bodyBytes)) - if err != nil { - return "", fmt.Errorf("PopOK POST failed: %w", err) - } - defer resp.Body.Close() - - var parsedResp domain.PopokLaunchResponse - if err := json.NewDecoder(resp.Body).Decode(&parsedResp); err != nil { - return "", fmt.Errorf("failed to parse PopOK response: %w", err) - } - if parsedResp.Code != 0 { - return "", fmt.Errorf("PopOK error: %s", parsedResp.Message) - } - - return parsedResp.Data.LauncherURL, nil -} - -func (s *service) HandleCallback(ctx context.Context, callback *domain.PopOKCallback) error { - s.logger.Info("Handling PopOK callback", "transactionID", callback.TransactionID, "type", callback.Type) - - if !s.verifySignature(callback) { - s.logger.Error("Invalid callback signature", "transactionID", callback.TransactionID) - return errors.New("invalid signature") - } - - existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, callback.TransactionID) - if err != nil { - s.logger.Error("Failed to check existing transaction", "transactionID", callback.TransactionID, "error", err) - return err - } - if existingTx != nil { - s.logger.Warn("Transaction already processed", "transactionID", callback.TransactionID) - return nil // Idempotency - } - - session, err := s.repo.GetVirtualGameSessionByToken(ctx, callback.SessionID) - if err != nil || session == nil { - s.logger.Error("Invalid or missing session", "sessionID", callback.SessionID, "error", err) - return errors.New("invalid session") - } - - wallets, err := s.walletSvc.GetWalletsByUser(ctx, session.UserID) - if err != nil || len(wallets) == 0 { - s.logger.Error("Failed to get wallets or no wallet found", "userID", session.UserID, "error", err) - return errors.New("user has no wallet") - } - - walletID := wallets[0].ID - amount := int64(callback.Amount) // Convert to cents - transactionType := callback.Type - - switch transactionType { - case "BET": - amount = -amount // Debit for bets - case "WIN", "JACKPOT_WIN", "REFUND": - default: - s.logger.Error("Unknown transaction type", "transactionID", callback.TransactionID, "type", transactionType) - return errors.New("unknown transaction type") - } - - _, err = s.walletSvc.AddToWallet(ctx, walletID, domain.Currency(amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, - fmt.Sprintf("Added %v to wallet for winning PopOkBet", amount), - ) - if err != nil { - s.logger.Error("Failed to update wallet", "walletID", walletID, "userID", session.UserID, "amount", amount, "error", err) - return err - } - - // Record transaction - tx := &domain.VirtualGameTransaction{ - SessionID: session.ID, - UserID: session.UserID, - WalletID: walletID, - TransactionType: transactionType, - Amount: amount, - Currency: callback.Currency, - ExternalTransactionID: callback.TransactionID, - Status: "COMPLETED", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - - if err := s.repo.CreateVirtualGameTransaction(ctx, tx); err != nil { - s.logger.Error("Failed to create transaction", "transactionID", callback.TransactionID, "error", err) - return err - } - - s.logger.Info("Callback processed successfully", "transactionID", callback.TransactionID, "type", transactionType, "amount", callback.Amount) - return nil -} - -func (s *service) GetPlayerInfo(ctx context.Context, req *domain.PopOKPlayerInfoRequest) (*domain.PopOKPlayerInfoResponse, error) { - claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey) - fmt.Printf("\n\nClaims: %+v\n\n", claims) - fmt.Printf("\n\nExternal token: %+v\n\n", req.ExternalToken) - if err != nil { - s.logger.Error("Failed to parse JWT", "error", err) - return nil, fmt.Errorf("invalid token") - } - - wallet, err := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - if err != nil { - s.logger.Error("No wallets found for user", "userID", claims.UserID) - return nil, err - } - - return &domain.PopOKPlayerInfoResponse{ - Country: "ET", - Currency: claims.Currency, - Balance: float64(wallet.RegularBalance), // Convert cents to currency - PlayerID: fmt.Sprintf("%d", claims.UserID), - }, nil -} - -func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (*domain.PopOKBetResponse, error) { - // Validate external token and extract user context - claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey) - if err != nil { - return nil, fmt.Errorf("invalid or expired token: %w", err) - } - - // Validate required fields - if req.Amount <= 0 { - return nil, errors.New("invalid bet amount") - } - if req.TransactionID == "" { - return nil, errors.New("missing transaction_id") - } - - // Retrieve user's wallet - wallet, err := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for user %d: %w", claims.UserID, err) - } - - // Deduct amount from wallet - _, err = s.walletSvc.DeductFromWallet( - ctx, - wallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, // optional linked transfer ID - domain.TRANSFER_DIRECT, - fmt.Sprintf("Virtual game bet placed (PopOK) - Txn: %s", req.TransactionID), - ) - if err != nil { - return nil, fmt.Errorf("failed to deduct from wallet (insufficient balance or wallet error): %w", err) - } - - // Record the transaction - tx := &domain.VirtualGameTransaction{ - UserID: claims.UserID, - CompanyID: claims.CompanyID.Value, - Provider: string(domain.PROVIDER_POPOK), - GameID: req.GameID, - TransactionType: "BET", - Amount: -int64(req.Amount), // Represent bet as negative in accounting - Currency: req.Currency, - ExternalTransactionID: req.TransactionID, - Status: "COMPLETED", - CreatedAt: time.Now(), - } - - if err := s.repo.CreateVirtualGameTransaction(ctx, tx); err != nil { - // Optionally rollback wallet deduction if transaction record fails - _, addErr := s.walletSvc.AddToWallet( - ctx, - wallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_DIRECT, - domain.PaymentDetails{}, - fmt.Sprintf("Rollback refund for failed bet transaction (PopOK) - Txn: %s", req.TransactionID), - ) - - if addErr != nil { - return nil, fmt.Errorf("failed to credit wallet: %w", addErr) - } - s.logger.Error("Failed to create bet transaction", "error", err, "txn_id", req.TransactionID, "user_id", claims.UserID) - return nil, fmt.Errorf("failed to record bet transaction: %w", err) - } - - // Return updated wallet balance - updatedWallet, err := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - if err != nil { - return nil, fmt.Errorf("failed to refresh wallet balance: %w", err) - } - - return &domain.PopOKBetResponse{ - TransactionID: req.TransactionID, - ExternalTrxID: fmt.Sprintf("%v", tx.ID), // internal reference - Balance: float64(updatedWallet.RegularBalance.Float32()), - }, nil -} - -func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) { - // 1. Validate token and get user ID - claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey) - if err != nil { - s.logger.Error("Invalid token in win request", "error", err) - return nil, fmt.Errorf("invalid token") - } - - fmt.Printf("\n\nClaims: %+v\n\n", claims) - - // 2. Check for duplicate transaction (idempotency) - existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID) - if err != nil { - s.logger.Error("Failed to check existing transaction", "error", err) - return nil, fmt.Errorf("transaction check failed") - } - - if existingTx != nil && existingTx.TransactionType == "WIN" { - s.logger.Warn("Duplicate win transaction", "transactionID", req.TransactionID) - wallet, _ := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - // balance := 0.0 - // if len(wallets) > 0 { - // balance = float64(wallets[0].Balance) - // } - return &domain.PopOKWinResponse{ - TransactionID: req.TransactionID, - ExternalTrxID: fmt.Sprintf("%d", existingTx.ID), - Balance: float64(wallet.RegularBalance), - }, nil - } - - // 3. Convert amount to cents - // amountCents := int64(req.Amount) - - wallet, err := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - if err != nil { - return nil, fmt.Errorf("failed to read user wallets") - } - - // 4. Credit to wallet - _, err = s.walletSvc.AddToWallet(ctx, wallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, - domain.TRANSFER_DIRECT, domain.PaymentDetails{}, - fmt.Sprintf("Added %v to wallet for winning PopOkBet", req.Amount), - ) - if err != nil { - s.logger.Error("Failed to credit wallet", "userID", claims.UserID, "error", err) - return nil, fmt.Errorf("wallet credit failed") - } - - // userWallets, err := s.walletSvc.GetWalletsByUser(ctx, claims.UserID) - // if err != nil { - // return &domain.PopOKWinResponse{}, fmt.Errorf("Failed to read user wallets") - // } - // 5. Create transaction record - tx := &domain.VirtualGameTransaction{ - UserID: claims.UserID, - CompanyID: claims.CompanyID.Value, - Provider: string(domain.PROVIDER_POPOK), - GameID: req.GameID, - TransactionType: "WIN", - Amount: int64(req.Amount), - Currency: req.Currency, - ExternalTransactionID: req.TransactionID, - Status: "COMPLETED", - CreatedAt: time.Now(), - } - - if err := s.repo.CreateVirtualGameTransaction(ctx, tx); err != nil { - s.logger.Error("Failed to create win transaction", "error", err) - return nil, fmt.Errorf("transaction recording failed") - } - - fmt.Printf("\n\n Win balance is:%v\n\n", float64(wallet.RegularBalance)) - - return &domain.PopOKWinResponse{ - TransactionID: req.TransactionID, - ExternalTrxID: fmt.Sprintf("%v", tx.ID), - Balance: float64(wallet.RegularBalance), - }, nil -} - -func (s *service) ProcessTournamentWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) { - // --- 1. Validate token and get user ID --- - claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey) - if err != nil { - s.logger.Error("Invalid token in tournament win request", "error", err) - return nil, fmt.Errorf("invalid token") - } - - // --- 2. Check for duplicate tournament win transaction (idempotency) --- - existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID) - if err != nil { - s.logger.Error("Failed to check existing tournament transaction", "error", err) - return nil, fmt.Errorf("transaction check failed") - } - if existingTx != nil && existingTx.TransactionType == "TOURNAMENT_WIN" { - s.logger.Warn("Duplicate tournament win", "transactionID", req.TransactionID) - wallet, _ := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - return &domain.PopOKWinResponse{ - TransactionID: req.TransactionID, - ExternalTrxID: fmt.Sprintf("%v", existingTx.ID), - Balance: float64(wallet.RegularBalance), - }, nil - } - - // --- 3. Fetch wallet --- - wallet, err := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for user %d: %w", claims.UserID, err) - } - - // --- 4. Credit user wallet --- - _, err = s.walletSvc.AddToWallet( - ctx, - wallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_DIRECT, - domain.PaymentDetails{ - ReferenceNumber: domain.ValidString{ - Value: req.TransactionID, - Valid: true, - }, - }, - fmt.Sprintf("Added %v to wallet for winning PopOK Tournament", req.Amount), - ) - if err != nil { - s.logger.Error("Failed to credit wallet for tournament", "userID", claims.UserID, "error", err) - return nil, fmt.Errorf("wallet credit failed") - } - - // --- 5. Record tournament win transaction --- - tx := &domain.VirtualGameTransaction{ - UserID: claims.UserID, - CompanyID: claims.CompanyID.Value, - Provider: string(domain.PROVIDER_POPOK), - GameID: req.GameID, - TransactionType: "TOURNAMENT_WIN", - Amount: int64(req.Amount), - Currency: req.Currency, - ExternalTransactionID: req.TransactionID, - Status: "COMPLETED", - CreatedAt: time.Now(), - } - - if err := s.repo.CreateVirtualGameTransaction(ctx, tx); err != nil { - s.logger.Error("Failed to record tournament win transaction", "error", err) - return nil, fmt.Errorf("transaction recording failed") - } - - // --- 6. Return response with updated balance --- - return &domain.PopOKWinResponse{ - TransactionID: req.TransactionID, - ExternalTrxID: fmt.Sprintf("%v", tx.ID), - Balance: float64(wallet.RegularBalance), - }, nil -} - -func (s *service) ProcessPromoWin(ctx context.Context, req *domain.PopOKWinRequest) (*domain.PopOKWinResponse, error) { - // --- 1. Validate token and get user ID --- - claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey) - if err != nil { - s.logger.Error("Invalid token in promo win request", "error", err) - return nil, fmt.Errorf("invalid token") - } - - // --- 2. Check for duplicate promo win transaction (idempotency) --- - existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID) - if err != nil { - s.logger.Error("Failed to check existing promo transaction", "error", err) - return nil, fmt.Errorf("transaction check failed") - } - if existingTx != nil && existingTx.TransactionType == "PROMO_WIN" { - s.logger.Warn("Duplicate promo win", "transactionID", req.TransactionID) - wallet, _ := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - return &domain.PopOKWinResponse{ - TransactionID: req.TransactionID, - ExternalTrxID: fmt.Sprintf("%v", existingTx.ID), - Balance: float64(wallet.RegularBalance), - }, nil - } - - // --- 3. Fetch wallet --- - wallet, err := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for user %d: %w", claims.UserID, err) - } - - // --- 4. Credit user wallet --- - _, err = s.walletSvc.AddToWallet( - ctx, - wallet.RegularID, - domain.Currency(req.Amount), - domain.ValidInt64{}, - domain.TRANSFER_DIRECT, - domain.PaymentDetails{ - ReferenceNumber: domain.ValidString{ - Value: req.TransactionID, - Valid: true, - }, - }, - fmt.Sprintf("Added %v to wallet for winning PopOK Promo Win", req.Amount), - ) - if err != nil { - s.logger.Error("Failed to credit wallet for promo", "userID", claims.UserID, "error", err) - return nil, fmt.Errorf("wallet credit failed") - } - - // --- 5. Record promo win transaction --- - tx := &domain.VirtualGameTransaction{ - UserID: claims.UserID, - CompanyID: claims.CompanyID.Value, - Provider: string(domain.PROVIDER_POPOK), - GameID: req.GameID, - TransactionType: "PROMO_WIN", - Amount: int64(req.Amount), - Currency: req.Currency, - ExternalTransactionID: req.TransactionID, - Status: "COMPLETED", - CreatedAt: time.Now(), - } - - if err := s.repo.CreateVirtualGameTransaction(ctx, tx); err != nil { - s.logger.Error("Failed to record promo win transaction", "error", err) - return nil, fmt.Errorf("transaction recording failed") - } - - // --- 6. Return response with updated balance --- - return &domain.PopOKWinResponse{ - TransactionID: req.TransactionID, - ExternalTrxID: fmt.Sprintf("%v", tx.ID), - Balance: float64(wallet.RegularBalance), - }, nil -} - -func (s *service) ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequest) (*domain.PopOKCancelResponse, error) { - // --- 1. Validate token and get user ID --- - claims, err := jwtutil.ParsePopOKJwt(req.ExternalToken, s.config.PopOK.SecretKey) - if err != nil { - s.logger.Error("Invalid token in cancel request", "error", err) - return nil, fmt.Errorf("invalid token") - } - - // --- 2. Fetch wallet --- - wallet, err := s.walletSvc.GetCustomerWallet(ctx, claims.UserID) - if err != nil { - return nil, fmt.Errorf("failed to fetch wallet for user %d: %w", claims.UserID, err) - } - - // --- 3. Find the original bet transaction --- - originalBet, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID) - if err != nil { - s.logger.Error("Failed to find original bet", "transactionID", req.TransactionID, "error", err) - return nil, fmt.Errorf("original bet not found") - } - - // --- 4. Validate original transaction --- - if originalBet == nil || originalBet.TransactionType != "BET" { - s.logger.Error("Invalid original transaction for cancel", "transactionID", req.TransactionID) - return nil, fmt.Errorf("invalid original transaction") - } - - // --- 5. Check for duplicate cancellation --- - if originalBet.Status == "CANCELLED" { - s.logger.Warn("Transaction already cancelled", "transactionID", req.TransactionID) - return &domain.PopOKCancelResponse{ - TransactionID: req.TransactionID, - ExternalTrxID: fmt.Sprintf("%v", originalBet.ID), - Balance: float64(wallet.RegularBalance), - }, nil - } - - // --- 6. Refund the bet amount --- - refundAmount := -originalBet.Amount // Bet amounts are negative - _, err = s.walletSvc.AddToWallet( - ctx, - wallet.RegularID, - domain.Currency(refundAmount), - domain.ValidInt64{}, - domain.TRANSFER_DIRECT, - domain.PaymentDetails{ - ReferenceNumber: domain.ValidString{ - Value: req.TransactionID, - Valid: true, - }, - }, - fmt.Sprintf("Refunded %v to wallet for cancelling PopOK bet", refundAmount), - ) - if err != nil { - s.logger.Error("Failed to refund bet", "userID", claims.UserID, "error", err) - return nil, fmt.Errorf("refund failed") - } - - // --- 7. Mark original bet as cancelled and create cancel record --- - cancelTx := &domain.VirtualGameTransaction{ - UserID: claims.UserID, - TransactionType: "CANCEL", - Amount: refundAmount, - Currency: originalBet.Currency, - ExternalTransactionID: req.TransactionID, - ReferenceTransactionID: fmt.Sprintf("%v", originalBet.ID), - Status: "COMPLETED", - CreatedAt: time.Now(), - } - - // Update original transaction status - if err := s.repo.UpdateVirtualGameTransactionStatus(ctx, originalBet.ID, "CANCELLED"); err != nil { - s.logger.Error("Failed to update transaction status", "error", err) - return nil, fmt.Errorf("update failed") - } - - // Create cancel transaction - if err := s.repo.CreateVirtualGameTransaction(ctx, cancelTx); err != nil { - s.logger.Error("Failed to create cancel transaction", "error", err) - // Attempt to revert original transaction status - if revertErr := s.repo.UpdateVirtualGameTransactionStatus(ctx, originalBet.ID, originalBet.Status); revertErr != nil { - s.logger.Error("Failed to revert transaction status", "error", revertErr) - } - return nil, fmt.Errorf("create cancel transaction failed") - } - - // --- 8. Return response with updated balance --- - return &domain.PopOKCancelResponse{ - TransactionID: req.TransactionID, - ExternalTrxID: fmt.Sprintf("%v", cancelTx.ID), - Balance: float64(wallet.RegularBalance), - }, nil -} - -func generatePopOKHash(privateKey, timestamp string, data domain.PopokLaunchRequestData) (string, error) { - // Marshal data to JSON (compact format, like json_encode in PHP) - dataBytes, err := json.Marshal(data) - if err != nil { - return "", err - } - - // Concatenate: privateKey + time + json_encoded(data) - hashInput := fmt.Sprintf("%s%s%s", privateKey, timestamp, string(dataBytes)) - - // SHA-256 hash - hash := sha256.Sum256([]byte(hashInput)) - return hex.EncodeToString(hash[:]), nil -} - -func (s *service) verifySignature(callback *domain.PopOKCallback) bool { - data, _ := json.Marshal(struct { - TransactionID string `json:"transaction_id"` - SessionID string `json:"session_id"` - Type string `json:"type"` - Amount float64 `json:"amount"` - Currency string `json:"currency"` - Timestamp int64 `json:"timestamp"` - }{ - TransactionID: callback.TransactionID, - SessionID: callback.SessionID, - Type: callback.Type, - Amount: callback.Amount, - Currency: callback.Currency, - Timestamp: callback.Timestamp, - }) - - h := hmac.New(sha256.New, []byte(s.config.PopOK.SecretKey)) - h.Write(data) - expected := hex.EncodeToString(h.Sum(nil)) - return expected == callback.Signature -} - -func (s *service) ListGames(ctx context.Context, currency string) ([]domain.PopOKGame, error) { - now := time.Now().Format("02-01-2006 15:04:05") // dd-mm-yyyy hh:mm:ss - - // Step 1: Construct payload without the hash - data := map[string]interface{}{ - "action": "gameList", - "platform": s.config.PopOK.Platform, - "partnerId": s.config.PopOK.ClientID, - "currency": currency, - "time": now, - } - - // Step 2: Marshal data to JSON for hash calculation - // dataBytes, err := json.Marshal(data) - // if err != nil { - // s.logger.Error("Failed to marshal data for hash generation", "error", err) - // return nil, err - // } - - // Step 3: Calculate hash: sha256(privateKey + time + json(data)) - rawHash := s.config.PopOK.SecretKey + now - hash := fmt.Sprintf("%x", sha256.Sum256([]byte(rawHash))) - - // Step 4: Add the hash to the payload - data["hash"] = hash - - // Step 5: Marshal full payload with hash - bodyBytes, err := json.Marshal(data) - if err != nil { - s.logger.Error("Failed to marshal final payload with hash", "error", err) - return nil, err - } - - // Step 6: Create and send the request - req, err := http.NewRequestWithContext(ctx, "POST", s.config.PopOK.BaseURL+"/serviceApi.php", bytes.NewReader(bodyBytes)) - if err != nil { - s.logger.Error("Failed to create game list request", "error", err) - return nil, err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36") - req.Header.Set("Accept", "application/json, text/plain, */*") - req.Header.Set("Accept-Language", "en-US,en;q=0.9") - - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) - if err != nil { - s.logger.Error("Failed to send game list request", "error", err) - return nil, err - } - defer resp.Body.Close() - - // Step 7: Handle response - if resp.StatusCode != http.StatusOK { - b, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("PopOK game list failed with status %d: %s", resp.StatusCode, string(b)) - } - - var gameList domain.PopOKGameListResponse - if err := json.NewDecoder(resp.Body).Decode(&gameList); err != nil { - s.logger.Error("Failed to decode game list response", "error", err) - return nil, err - } - - if gameList.Code != 0 { - return nil, fmt.Errorf("PopOK error: %s", gameList.Message) - } - - return gameList.Data.Slots, nil -} - -func (s *service) RecommendGames(ctx context.Context, userID int64) ([]domain.GameRecommendation, error) { - // Fetch all available games - games, err := s.ListGames(ctx, "ETB") - if err != nil || len(games) == 0 { - return nil, fmt.Errorf("could not fetch games") - } - - // Check if user has existing interaction - history, err := s.repo.GetUserGameHistory(ctx, userID) - if err != nil { - s.logger.Warn("No previous game history", "userID", userID) - } - - recommendations := []domain.GameRecommendation{} - - if len(history) > 0 { - // Score games based on interaction frequency - gameScores := map[int64]int{} - for _, h := range history { - if h.GameID != nil { - gameScores[*h.GameID]++ - } - } - - // Sort by score descending - sort.SliceStable(games, func(i, j int) bool { - return gameScores[int64(games[i].ID)] > gameScores[int64(games[j].ID)] - }) - - // Pick top 3 - for _, g := range games[:min(3, len(games))] { - recommendations = append(recommendations, domain.GameRecommendation{ - GameID: g.ID, - GameName: g.GameName, - Thumbnail: g.Thumbnail, - Bets: g.Bets, - Reason: "Based on your activity", - }) - } - } else { - // Pick 3 random games for new users - rand.Shuffle(len(games), func(i, j int) { - games[i], games[j] = games[j], games[i] - }) - - for _, g := range games[:min(3, len(games))] { - recommendations = append(recommendations, domain.GameRecommendation{ - GameID: g.ID, - GameName: g.GameName, - Thumbnail: g.Thumbnail, - Bets: g.Bets, - Reason: "Random pick", - }) - } - } - - return recommendations, nil -} - -func toInt64Ptr(s string) *int64 { - id, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return nil - } - return &id -} - - - diff --git a/internal/services/virtualGame/veli/client.go b/internal/services/virtualGame/veli/client.go deleted file mode 100644 index 529dd41..0000000 --- a/internal/services/virtualGame/veli/client.go +++ /dev/null @@ -1,128 +0,0 @@ -package veli - -import ( - "bytes" - "context" - "crypto/hmac" - "crypto/sha512" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "net/http" - "sort" - "strings" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" -) - -type Client struct { - http *http.Client - BaseURL string - OperatorID string - SecretKey string - BrandID string - walletSvc *wallet.Service -} - -func NewClient(cfg *config.Config, walletSvc *wallet.Service) *Client { - return &Client{ - http: &http.Client{Timeout: 10 * time.Second}, - BaseURL: cfg.VeliGames.BaseURL, - OperatorID: cfg.VeliGames.OperatorID, - SecretKey: cfg.VeliGames.SecretKey, - BrandID: cfg.VeliGames.BrandID, - walletSvc: walletSvc, - } -} - -// Signature generator -func (c *Client) generateSignature(params map[string]any) (string, error) { - keys := make([]string, 0, len(params)) - for k := range params { - keys = append(keys, k) - } - sort.Strings(keys) - - var b strings.Builder - first := true - - appendKV := func(_ string, value string) { - if !first { - b.WriteString(";") - } - b.WriteString(value) - first = false - } - - for _, k := range keys { - v := params[k] - - switch val := v.(type) { - case []string: - for i, item := range val { - appendKV(k, fmt.Sprintf("%s:%d:%s", k, i, item)) - } - case []any: - for i, item := range val { - appendKV(k, fmt.Sprintf("%s:%d:%v", k, i, item)) - } - default: - appendKV(k, fmt.Sprintf("%s:%v", k, val)) - } - } - - fmt.Println("String being signed:", b.String()) - fmt.Println("Using secret key:", c.SecretKey) - - h := hmac.New(sha512.New, []byte(c.SecretKey)) - h.Write([]byte(b.String())) - hash := h.Sum(nil) - signature := base64.StdEncoding.EncodeToString(hash) - fmt.Println("Generated signature:", signature) - return fmt.Sprintf("%s:%s", c.OperatorID, signature), nil -} - - - -// POST helper -func (c *Client) Post(ctx context.Context, path string, body any, sigParams map[string]any, result any) error { - data, _ := json.Marshal(body) - sig, err := c.generateSignature(sigParams) - if err != nil { - return err - } - - req, _ := http.NewRequestWithContext(ctx, "POST", c.BaseURL+path, bytes.NewReader(data)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("signature", sig) - - // Print request info - fmt.Println("Request URL:", c.BaseURL+path) - fmt.Println("Request Headers:") - for k, v := range req.Header { - for _, val := range v { - fmt.Printf(" %s: %s\n", k, val) - } - } - fmt.Println("Request Body:", string(data)) - - // Send request - res, err := c.http.Do(req) - if err != nil { - return err - } - defer res.Body.Close() - - // Read response - b, _ := io.ReadAll(res.Body) - if res.StatusCode >= 400 { - return fmt.Errorf("error: %s", b) - } - if result != nil { - return json.Unmarshal(b, result) - } - return nil -} diff --git a/internal/services/virtualGame/veli/port.go b/internal/services/virtualGame/veli/port.go deleted file mode 100644 index 5e50cba..0000000 --- a/internal/services/virtualGame/veli/port.go +++ /dev/null @@ -1,27 +0,0 @@ -// services/veli/service.go -package veli - -import ( - "context" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -type VeliVirtualGameService interface { - GetAtlasVGames(ctx context.Context) ([]domain.AtlasGameEntity, error) - FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error) - GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVirtualGamesParams) ([]domain.UnifiedGame, error) - AddProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) - GetProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) - GetGames(ctx context.Context, req domain.GameListRequest) ([]domain.GameEntity, error) - StartGame(ctx context.Context, req domain.GameStartRequest) (*domain.GameStartResponse, error) - StartDemoGame(ctx context.Context, req domain.DemoGameRequest) (*domain.GameStartResponse, error) - GetBalance(ctx context.Context, req domain.BalanceRequest) (*domain.BalanceResponse, error) - ProcessBet(ctx context.Context, req domain.BetRequest) (*domain.BetResponse, error) - ProcessWin(ctx context.Context, req domain.WinRequest) (*domain.WinResponse, error) - ProcessCancel(ctx context.Context, req domain.CancelRequest) (*domain.CancelResponse, error) - GetGamingActivity(ctx context.Context, req domain.GamingActivityRequest) (*domain.GamingActivityResponse, error) - GetHugeWins(ctx context.Context, req domain.HugeWinsRequest) (*domain.HugeWinsResponse, error) - GetCreditBalances(ctx context.Context, brandID string) ([]domain.CreditBalance, error) -} diff --git a/internal/services/virtualGame/veli/service.go b/internal/services/virtualGame/veli/service.go deleted file mode 100644 index c190d14..0000000 --- a/internal/services/virtualGame/veli/service.go +++ /dev/null @@ -1,632 +0,0 @@ -package veli - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "strconv" - "time" - - "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" - "github.com/google/uuid" - "go.uber.org/zap" -) - -var ( - ErrPlayerNotFound = errors.New("PLAYER_NOT_FOUND") - ErrSessionExpired = errors.New("SESSION_EXPIRED") - ErrInsufficientBalance = errors.New("INSUFFICIENT_BALANCE") - ErrDuplicateTransaction = errors.New("DUPLICATE_TRANSACTION") -) - -type Service struct { - virtualGameSvc virtualgameservice.VirtualGameService - repo repository.VirtualGameRepository - genRepo repository.Store - client *Client - walletSvc *wallet.Service - transfetStore ports.TransferStore - mongoLogger *zap.Logger - cfg *config.Config -} - -func New( - virtualGameSvc virtualgameservice.VirtualGameService, - repo repository.VirtualGameRepository, - genRepo repository.Store, - client *Client, - walletSvc *wallet.Service, - transferStore ports.TransferStore, - mongoLogger *zap.Logger, - cfg *config.Config, -) *Service { - return &Service{ - virtualGameSvc: virtualGameSvc, - repo: repo, - genRepo: genRepo, - client: client, - walletSvc: walletSvc, - transfetStore: transferStore, - mongoLogger: mongoLogger, - cfg: cfg, - } -} - -func (s *Service) GetAtlasVGames(ctx context.Context) ([]domain.AtlasGameEntity, error) { - // 1. Compose URL (could be configurable) - url := "https://atlas-v.com/partner/35fr5784dbgr4dfw234wsdsw" + - "?hash=b3596faa6185180e9b2ca01cb5a052d316511872×tamp=1700244963080" - - // 2. Create a dedicated HTTP client with timeout - client := &http.Client{Timeout: 15 * time.Second} - - // 3. Prepare request with context - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating request: %w", err) - } - - // 4. Execute request - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("calling Atlas-V API: %w", err) - } - defer resp.Body.Close() - - // 5. Check response status - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("Atlas-V API error: status %d, body: %s", resp.StatusCode, body) - } - - // 6. Decode response into slice of GameEntity - var games []domain.AtlasGameEntity - if err := json.NewDecoder(resp.Body).Decode(&games); err != nil { - return nil, fmt.Errorf("decoding Atlas-V games: %w", err) - } - - return games, nil -} - -func (s *Service) GetProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) { - // Always mirror request body fields into sigParams - sigParams := map[string]any{ - "brandId": req.BrandID, - } - - // Optional fields - sigParams["extraData"] = fmt.Sprintf("%t", req.ExtraData) // false is still included - if req.Size > 0 { - sigParams["size"] = fmt.Sprintf("%d", req.Size) - } else { - sigParams["size"] = "" // keep empty if not set - } - if req.Page > 0 { - sigParams["page"] = fmt.Sprintf("%d", req.Page) - } else { - sigParams["page"] = "" - } - - var res domain.ProviderResponse - err := s.client.Post(ctx, "/game-lists/public/providers", req, sigParams, &res) - return &res, err -} - -func (s *Service) GetGames(ctx context.Context, req domain.GameListRequest) ([]domain.GameEntity, error) { - // 1. Check if provider is enabled in DB - // provider, err := s.repo.GetVirtualGameProviderByID(ctx, req.ProviderID) - // if err != nil { - // return nil, fmt.Errorf("failed to check provider %s: %w", req.ProviderID, err) - // } - - // if !provider.Enabled { - // // Provider exists but is disabled → return empty list (or error if you prefer) - // return nil, fmt.Errorf("provider %s is disabled", req.ProviderID) - // } - - // 2. Prepare signature params - sigParams := map[string]any{ - "brandId": req.BrandID, - "providerId": req.ProviderID, - "size": req.Size, - "page": req.Page, - } - - // 3. Call external API - var res struct { - Items []domain.GameEntity `json:"items"` - } - if err := s.client.Post(ctx, "/game-lists/public/games", req, sigParams, &res); err != nil { - return nil, fmt.Errorf("failed to fetch games for provider %s: %w", req.ProviderID, err) - } - - return res.Items, nil -} - -func (s *Service) StartGame(ctx context.Context, req domain.GameStartRequest) (*domain.GameStartResponse, error) { - // 1. Check if provider is enabled in DB - // provider, err := s.repo.GetVirtualGameProviderByID(ctx, req.ProviderID) - // if err != nil { - // return nil, fmt.Errorf("failed to check provider %s: %w", req.ProviderID, err) - // } - - // if !provider.Enabled { - // // Provider exists but is disabled → return error - // return nil, fmt.Errorf("provider %s is disabled", req.ProviderID) - // } - - playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid PlayerID: %w", err) - } - - if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { - return nil, ErrPlayerNotFound - } - - // 2. Prepare signature params - sigParams := map[string]any{ - "sessionId": req.SessionID, - "providerId": req.ProviderID, - "gameId": req.GameID, - "language": req.Language, - "playerId": req.PlayerID, - "currency": req.Currency, - "deviceType": req.DeviceType, - "country": req.Country, - "ip": req.IP, - "brandId": req.BrandID, - } - - // 3. Call external API - var res domain.GameStartResponse - if err := s.client.Post(ctx, "/unified-api/public/start-game", req, sigParams, &res); err != nil { - return nil, fmt.Errorf("failed to start game with provider %s: %w", req.ProviderID, err) - } - - // playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) - // if err != nil { - // return nil, fmt.Errorf("invalid PlayerID: %w", err) - // } - - session := &domain.VirtualGameSession{ - UserID: playerIDInt64, - GameID: req.GameID, - SessionToken: uuid.NewString(), - } - - if err := s.repo.CreateVirtualGameSession(ctx, session); err != nil { - return nil, fmt.Errorf("failed to create virtual game session: %w", err) - } - - return &res, nil -} - -func (s *Service) StartDemoGame(ctx context.Context, req domain.DemoGameRequest) (*domain.GameStartResponse, error) { - // 1. Check if provider is enabled in DB - // provider, err := s.repo.GetVirtualGameProviderByID(ctx, req.ProviderID) - // if err != nil { - // return nil, fmt.Errorf("failed to check provider %s: %w", req.ProviderID, err) - // } - - // if !provider.Enabled { - // // Provider exists but is disabled → return error - // return nil, fmt.Errorf("provider %s is disabled", req.ProviderID) - // } - - // 2. Prepare signature params - sigParams := map[string]any{ - "providerId": req.ProviderID, - "gameId": req.GameID, - "language": req.Language, - "deviceType": req.DeviceType, - "ip": req.IP, - "brandId": req.BrandID, - } - - // 3. Call external API - var res domain.GameStartResponse - if err := s.client.Post(ctx, "/unified-api/public/start-demo-game", req, sigParams, &res); err != nil { - return nil, fmt.Errorf("failed to start demo game with provider %s: %w", req.ProviderID, err) - } - - return &res, nil -} - -func (s *Service) GetBalance(ctx context.Context, req domain.BalanceRequest) (*domain.BalanceResponse, error) { - // Retrieve player's real balance from wallet Service - playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid PlayerID: %w", err) - } - // playerWallets, err := s.walletSvc.GetWalletsByUser(ctx, playerIDInt64) - // if err != nil { - // return nil, fmt.Errorf("failed to get real balance: %w", err) - // } - // if len(playerWallets) == 0 { - // return nil, fmt.Errorf("PLAYER_NOT_FOUND: no wallet found for player %s", req.PlayerID) - // } - - // playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) - // if err != nil { - // return nil, fmt.Errorf("invalid PlayerID: %w", err) - // } - - if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { - return nil, ErrPlayerNotFound - } - - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt64) - if err != nil { - return nil, fmt.Errorf("failed to read user wallets") - } - - // realBalance := playerWallets[0].Balance - - // Retrieve bonus balance if applicable - // var bonusBalance float64 - // bonusBalance := float64(wallet.StaticBalance) - - // Build the response - res := &domain.BalanceResponse{ - Real: struct { - Currency string `json:"currency"` - Amount float64 `json:"amount"` - }{ - Currency: req.Currency, - Amount: (float64(wallet.RegularBalance)), - }, - } - - // if bonusBalance > 0 { - // res.Bonus = &struct { - // Currency string `json:"currency"` - // Amount float64 `json:"amount"` - // }{ - // Currency: req.Currency, - // Amount: bonusBalance, - // } - // } - - return res, nil -} - -func (s *Service) ProcessBet(ctx context.Context, req domain.BetRequest) (*domain.BetResponse, error) { - // --- 1. Validate PlayerID --- - playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %s", req.PlayerID) - } - - if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { - return nil, ErrPlayerNotFound - } - - existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID) - if err != nil { - return nil, fmt.Errorf("failed checking idempotency: %w", err) - } - - if existingTx != nil { - // Idempotent return — already processed - return nil, fmt.Errorf("DUPLICATE_TRANSACTION") - } - - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt64) - if err != nil { - return nil, fmt.Errorf("failed to read user wallets") - } - - // bonusBalance := float64(wallet.StaticBalance.Float32()) - - // --- 4. Check sufficient balance --- - if float64(wallet.RegularBalance) < req.Amount.Amount { - return nil, fmt.Errorf("INSUFFICIENT_BALANCE") - } - - // --- 6. Persist wallet deductions --- - - _, err = s.walletSvc.DeductFromWallet(ctx, wallet.RegularID, - domain.ToCurrency(float32(req.Amount.Amount)), - domain.ValidInt64{}, - domain.TRANSFER_DIRECT, - fmt.Sprintf("Deduct amount %.2f for bet %s", req.Amount.Amount, req.TransactionID), - ) - if err != nil { - return nil, fmt.Errorf("bonus deduction failed: %w", err) - } - - // --- 7. Build response --- - res := &domain.BetResponse{ - Real: domain.BalanceDetail{ - Currency: req.Amount.Currency, - Amount: float64(wallet.RegularBalance) - req.Amount.Amount, - }, - WalletTransactionID: req.TransactionID, - UsedRealAmount: req.Amount.Amount, - UsedBonusAmount: 0, - } - - // if bonusBalance > 0 { - // res.Bonus = &domain.BalanceDetail{ - // Currency: req.Amount.Currency, - // Amount: bonusBalance, - // } - // } - - if err = s.repo.CreateVirtualGameTransaction(ctx, &domain.VirtualGameTransaction{ - UserID: playerIDInt64, - Provider: req.ProviderID, - GameID: req.GameID, - WalletID: wallet.RegularID, - TransactionType: "BET", - Amount: int64(req.Amount.Amount), - Currency: req.Amount.Currency, - ExternalTransactionID: req.TransactionID, - Status: "pending", - GameRoundID: req.RoundID, - }); err != nil { - return nil, fmt.Errorf("failed to log virtual game transaction: %w", err) - } - - return res, nil -} - -func (s *Service) ProcessWin(ctx context.Context, req domain.WinRequest) (*domain.WinResponse, error) { - // --- 1. Validate PlayerID --- - playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %s", req.PlayerID) - } - - if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { - return nil, ErrPlayerNotFound - } - - // --- 2. Get player wallets --- - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt64) - if err != nil { - return nil, fmt.Errorf("failed to read user wallets") - } - - // --- 3. Convert balances safely using Float32() --- - realBalance := float64(wallet.RegularBalance) - // bonusBalance := float64(wallet.StaticBalance.Float32()) - - // --- 4. Apply winnings --- - winAmount := req.Amount.Amount - usedReal := winAmount - usedBonus := 0.0 - - // Future extension: split winnings between bonus and real wallets if needed - _, err = s.walletSvc.AddToWallet( - ctx, - wallet.RegularID, - domain.ToCurrency(float32(winAmount)), - domain.ValidInt64{}, - domain.TRANSFER_DIRECT, - domain.PaymentDetails{}, - fmt.Sprintf("Win %.2f for transaction %s", winAmount, req.TransactionID), - ) - if err != nil { - return nil, fmt.Errorf("failed to credit real wallet: %w", err) - } - - // --- 5. Build response --- - res := &domain.WinResponse{ - Real: domain.BalanceDetail{ - Currency: req.Amount.Currency, - Amount: realBalance + winAmount, // reflect the credited win amount - }, - WalletTransactionID: req.TransactionID, - UsedRealAmount: usedReal, - UsedBonusAmount: usedBonus, - } - - // if bonusBalance > 0 { - // res.Bonus = &domain.BalanceDetail{ - // Currency: req.Amount.Currency, - // Amount: bonusBalance, - // } - // } - - return res, nil -} - -func (s *Service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (*domain.CancelResponse, error) { - // --- 1. Validate PlayerID --- - playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %q", req.PlayerID) - } - - if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { - return nil, ErrPlayerNotFound - } - - // --- 2. Get player wallets --- - wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt64) - if err != nil { - return nil, fmt.Errorf("failed to read user wallets") - } - - // --- 3. Convert balances using Float32() --- - realBalance := float64(wallet.RegularBalance) - // bonusBalance := float64(wallet.StaticBalance.Float32()) - - // --- 4. Determine refund amount --- - var refundAmount float64 - if req.IsAdjustment { - if req.AdjustmentRefund.Amount <= 0 { - return nil, fmt.Errorf("missing adjustmentRefund for adjustment cancel") - } - refundAmount = req.AdjustmentRefund.Amount - } else { - originalTransfer, err := s.transfetStore.GetTransferByReference(ctx, req.RefTransactionID) - if err != nil { - return nil, fmt.Errorf("failed to get original bet for cancellation: %w", err) - } - refundAmount = float64(originalTransfer.Amount) - } - - // --- 5. Refund to wallet --- - usedReal := refundAmount - usedBonus := 0.0 - - _, err = s.walletSvc.AddToWallet( - ctx, - wallet.RegularID, - domain.ToCurrency(float32(refundAmount)), - domain.ValidInt64{}, - domain.TRANSFER_DIRECT, - domain.PaymentDetails{ - ReferenceNumber: domain.ValidString{ - Value: req.TransactionID, - Valid: true, - }, - BankNumber: domain.ValidString{}, - }, - fmt.Sprintf("Cancel %s refunded %.2f for transaction %s", req.CancelType, refundAmount, req.RefTransactionID), - ) - if err != nil { - return nil, fmt.Errorf("failed to refund wallet: %w", err) - } - - // --- 6. Build response --- - res := &domain.CancelResponse{ - WalletTransactionID: req.TransactionID, - Real: domain.BalanceDetail{ - Currency: req.AdjustmentRefund.Currency, - Amount: realBalance + refundAmount, // reflect refunded balance - }, - UsedRealAmount: usedReal, - UsedBonusAmount: usedBonus, - } - - // if bonusBalance > 0 { - // res.Bonus = &domain.BalanceDetail{ - // Currency: req.AdjustmentRefund.Currency, - // Amount: bonusBalance, - // } - // } - - return res, nil -} - -func (s *Service) GetGamingActivity(ctx context.Context, req domain.GamingActivityRequest) (*domain.GamingActivityResponse, error) { - // --- Signature Params (flattened strings for signing) --- - sigParams := map[string]any{ - "fromDate": req.FromDate, - "toDate": req.ToDate, - "brandId": s.cfg.VeliGames.BrandID, - } - - // Optional filters - if req.ProviderID != "" { - sigParams["providerId"] = req.ProviderID - } - if len(req.PlayerIDs) > 0 { - sigParams["playerIds"] = req.PlayerIDs // pass as []string, not joined - } - if len(req.GameIDs) > 0 { - sigParams["gameIds"] = req.GameIDs // pass as []string - } - if len(req.Currencies) > 0 { - sigParams["currencies"] = req.Currencies // pass as []string - } - if req.Page > 0 { - sigParams["page"] = req.Page - } else { - sigParams["page"] = 1 - req.Page = 1 - } - if req.Size > 0 { - sigParams["size"] = req.Size - } else { - sigParams["size"] = 100 - req.Size = 100 - } - if req.ExcludeFreeWin != nil { - sigParams["excludeFreeWin"] = *req.ExcludeFreeWin - } - - // --- Actual API Call --- - var res domain.GamingActivityResponse - err := s.client.Post(ctx, "/report-api/public/gaming-activity", req, sigParams, &res) - if err != nil { - return nil, err - } - - // --- Return parsed response --- - return &res, nil -} - -func (s *Service) GetHugeWins(ctx context.Context, req domain.HugeWinsRequest) (*domain.HugeWinsResponse, error) { - // --- Signature Params (flattened strings for signing) --- - sigParams := map[string]any{ - "fromDate": req.FromDate, - "toDate": req.ToDate, - "brandId": req.BrandID, - } - - if req.ProviderID != "" { - sigParams["providerId"] = req.ProviderID - } - if len(req.GameIDs) > 0 { - sigParams["gameIds"] = req.GameIDs // pass slice directly - } - if len(req.Currencies) > 0 { - sigParams["currencies"] = req.Currencies // pass slice directly - } - if req.Page > 0 { - sigParams["page"] = req.Page - } else { - sigParams["page"] = 1 - req.Page = 1 - } - if req.Size > 0 { - sigParams["size"] = req.Size - } else { - sigParams["size"] = 100 - req.Size = 100 - } - - // --- Actual API Call --- - var res domain.HugeWinsResponse - err := s.client.Post(ctx, "/report-api/public/gaming-activity/huge-wins", req, sigParams, &res) - if err != nil { - return nil, err - } - - return &res, nil -} - -func (s *Service) GetCreditBalances(ctx context.Context, brandID string) ([]domain.CreditBalance, error) { - if brandID == "" { - return nil, fmt.Errorf("brandID cannot be empty") - } - - // Prepare request body - body := map[string]any{ - "brandId": brandID, - } - - // Call the VeliGames API - var res struct { - Credits []domain.CreditBalance `json:"credits"` - } - - if err := s.client.Post(ctx, "/report-api/public/credit/balances", body, nil, &res); err != nil { - return nil, fmt.Errorf("failed to fetch credit balances: %w", err) - } - - return res.Credits, nil -} diff --git a/internal/services/wallet/interface.go b/internal/services/wallet/interface.go deleted file mode 100644 index 0a9c88b..0000000 --- a/internal/services/wallet/interface.go +++ /dev/null @@ -1,56 +0,0 @@ -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/monitor/service.go b/internal/services/wallet/monitor/service.go deleted file mode 100644 index e67ef6e..0000000 --- a/internal/services/wallet/monitor/service.go +++ /dev/null @@ -1,215 +0,0 @@ -// internal/services/walletmonitor/service.go -package monitor - -import ( - "context" - "fmt" - "log/slog" - "sync" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" - notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" -) - -type Service struct { - walletSvc wallet.Service - branchSvc branch.Service - notificationSvc *notificationservice.Service - logger *slog.Logger - thresholds []float64 - checkInterval time.Duration - stopCh chan struct{} - wg sync.WaitGroup - initialDeposits map[int64]domain.Currency // companyID -> initial deposit - mu sync.RWMutex -} - -func NewService( - walletSvc wallet.Service, - branchSvc branch.Service, - notificationSvc *notificationservice.Service, // Change to pointer - logger *slog.Logger, - checkInterval time.Duration, -) *Service { - return &Service{ - walletSvc: walletSvc, - branchSvc: branchSvc, - notificationSvc: notificationSvc, // Now storing the pointer - logger: logger, - thresholds: []float64{0.75, 0.50, 0.25, 0.10, 0.05}, - checkInterval: checkInterval, - stopCh: make(chan struct{}), - initialDeposits: make(map[int64]domain.Currency), - } -} - -func (s *Service) Start() { - s.wg.Add(1) - go s.monitorWallets() -} - -func (s *Service) Stop() { - close(s.stopCh) - s.wg.Wait() -} - -func (s *Service) monitorWallets() { - defer s.wg.Done() - - ticker := time.NewTicker(s.checkInterval) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - s.checkWalletThresholds() - case <-s.stopCh: - return - } - } -} - -func (s *Service) checkWalletThresholds() { - ctx := context.Background() - - // Get all company wallets - companies, err := s.branchSvc.GetAllCompaniesBranch(ctx) - if err != nil { - s.logger.Error("failed to get companies", "error", err) - return - } - - for _, company := range companies { - wallet, err := s.walletSvc.GetWalletByID(ctx, company.WalletID) - if err != nil { - s.logger.Error("failed to get company wallet", "company_id", company.ID, "error", err) - continue - } - - // Initialize initial deposit if not set - s.mu.Lock() - initialDeposit, exists := s.initialDeposits[company.ID] - if !exists || wallet.Balance > initialDeposit { - s.initialDeposits[company.ID] = wallet.Balance - initialDeposit = wallet.Balance // update local variable - } - s.mu.Unlock() - - if initialDeposit == 0 { - continue // avoid division by zero - } - - currentBalance := wallet.Balance - currentPercentage := float64(currentBalance) / float64(initialDeposit) - - for _, threshold := range s.thresholds { - if currentPercentage <= threshold { - // Check if we've already notified for this threshold - key := notificationKey(company.ID, threshold) - if s.hasNotified(key) { - continue - } - - // Send notifications - s.sendThresholdNotifications(ctx, company.ID, threshold, currentBalance, initialDeposit) - s.markAsNotified(key) - } - } - } -} - -func (s *Service) sendThresholdNotifications( - ctx context.Context, - companyID int64, - threshold float64, - currentBalance domain.Currency, - initialDeposit domain.Currency, -) { - // Get all recipients (branch managers, admins, super admins for this company) - recipients, err := s.getNotificationRecipients(ctx, companyID) - if err != nil { - s.logger.Error("failed to get notification recipients", "company_id", companyID, "error", err) - return - } - - thresholdPercent := int(threshold * 100) - message := buildNotificationMessage(thresholdPercent, currentBalance, initialDeposit) - - for _, recipientID := range recipients { - notification := &domain.Notification{ - RecipientID: recipientID, - Type: domain.NOTIFICATION_TYPE_WALLET, - Level: domain.NotificationLevelWarning, - Reciever: domain.NotificationRecieverSideAdmin, - DeliveryChannel: domain.DeliveryChannelInApp, - Payload: domain.NotificationPayload{ - Headline: "Wallet Threshold Alert", - Message: message, - }, - Priority: 2, // Medium priority - } - - if err := s.notificationSvc.SendNotification(ctx, notification); err != nil { - s.logger.Error("failed to send threshold notification", - "recipient_id", recipientID, - "company_id", companyID, - "threshold", thresholdPercent, - "error", err) - } - } - - s.logger.Info("sent wallet threshold notifications", - "company_id", companyID, - "threshold", thresholdPercent, - "recipient_count", len(recipients)) -} - -func (s *Service) getNotificationRecipients(ctx context.Context, companyID int64) ([]int64, error) { - // Get branch managers for this company - branches, err := s.branchSvc.GetBranchesByCompany(ctx, companyID) - if err != nil { - return nil, err - } - - var recipientIDs []int64 - - // Add branch managers - for _, branch := range branches { - if branch.BranchManagerID != 0 { - recipientIDs = append(recipientIDs, branch.BranchManagerID) - } - } - - // Add company admins (implementation depends on your user service) - // This would typically query users with admin role for this company - - return recipientIDs, nil -} - -func (s *Service) hasNotified(key string) bool { - fmt.Println(key) - // Implement your notification tracking logic here - // Could use a cache or database to track which thresholds have been notified - return false -} - -func (s *Service) markAsNotified(key string) { - // Implement your notification tracking logic here - // Mark that this threshold has been notified -} - -func notificationKey(companyID int64, threshold float64) string { - return fmt.Sprintf("%d_%.2f", companyID, threshold) -} - -func buildNotificationMessage(thresholdPercent int, currentBalance, initialDeposit domain.Currency) string { - return fmt.Sprintf( - "Company wallet balance has reached %d%% of initial deposit. Current balance: %.2f, Initial deposit: %.2f", - thresholdPercent, - float64(currentBalance), // Assuming currency is in cents - float64(initialDeposit), - ) -} diff --git a/internal/services/wallet/notification.go b/internal/services/wallet/notification.go deleted file mode 100644 index 8b9bfbe..0000000 --- a/internal/services/wallet/notification.go +++ /dev/null @@ -1,289 +0,0 @@ -package wallet - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" -) - -func (s *Service) GetAdminNotificationRecipients(ctx context.Context, walletID int64, walletType domain.WalletType) ([]int64, error) { - var recipients []int64 - - switch walletType { - case domain.BranchWalletType: - branch, err := s.GetBranchByWalletID(ctx, walletID) - if err != nil { - s.mongoLogger.Error("[GetAdminNotificationRecipients] failed to GetBranchWalletByID", zap.Int64("walletID", walletID)) - return nil, err - } - - // Branch managers will be notified when branch wallet is empty - recipients = append(recipients, branch.BranchManagerID) - - // Cashier will be notified - cashiers, err := s.userSvc.GetCashiersByBranch(ctx, branch.ID) - if err != nil { - return nil, err - } - for _, cashier := range cashiers { - recipients = append(recipients, cashier.ID) - } - - // Admin will also be notified - admin, err := s.userSvc.GetAdminByCompanyID(ctx, branch.CompanyID) - if err != nil { - return nil, err - } - recipients = append(recipients, admin.ID) - - case domain.CompanyWalletType: - company, err := s.GetCompanyByWalletID(ctx, walletID) - if err != nil { - return nil, err - } - recipients = append(recipients, company.AdminID) - default: - return nil, fmt.Errorf("Invalid wallet type") - } - - users, _, err := s.userSvc.GetAllUsers(ctx, domain.UserFilter{ - Role: string(domain.RoleSuperAdmin), - }) - - if err != nil { - return nil, err - } - - for _, user := range users { - recipients = append(recipients, user.ID) - } - - return recipients, nil -} - -func (s *Service) SendWalletUpdateNotification(ctx context.Context, wallet domain.Wallet) error { - raw, _ := json.Marshal(map[string]any{ - "balance": wallet.Balance.Float32(), - "type": wallet.Type, - "timestamp": time.Now(), - }) - - headline := "" - message := "" - var receiver domain.NotificationRecieverSide - switch wallet.Type { - case domain.StaticWalletType: - headline = "Referral and Bonus Wallet Updated" - message = fmt.Sprintf("Your referral and bonus wallet balance is now %.2f", wallet.Balance.Float32()) - receiver = domain.NotificationRecieverSideCustomer - case domain.RegularWalletType: - headline = "Wallet Updated" - message = fmt.Sprintf("Your wallet balance is now %.2f", wallet.Balance.Float32()) - receiver = domain.NotificationRecieverSideCustomer - case domain.BranchWalletType: - headline = "Branch Wallet Updated" - message = fmt.Sprintf("branch wallet balance is now %.2f", wallet.Balance.Float32()) - receiver = domain.NotificationRecieverSideBranchManager - case domain.CompanyWalletType: - headline = "Company Wallet Updated" - message = fmt.Sprintf("company wallet balance is now %.2f", wallet.Balance.Float32()) - receiver = domain.NotificationRecieverSideAdmin - } - // Handle the wallet event: send notification - notification := &domain.Notification{ - RecipientID: wallet.UserID, - DeliveryChannel: domain.DeliveryChannelInApp, - Reciever: receiver, - Type: domain.NotificationTypeWalletUpdated, - DeliveryStatus: domain.DeliveryStatusPending, - IsRead: false, - Level: domain.NotificationLevelInfo, - Priority: 2, - Metadata: raw, - Payload: domain.NotificationPayload{ - Headline: headline, - Message: message, - }, - } - - if err := s.notificationSvc.SendNotification(ctx, notification); err != nil { - s.mongoLogger.Error("[WalletSvc.SendWalletUpdateNotification] Failed to send notification", - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return err - } - - return nil -} - -func (s *Service) SendAdminWalletLowNotification(ctx context.Context, adminWallet domain.Wallet) error { - // Send different messages - - // Send notification to admin team - adminNotification := &domain.Notification{ - ErrorSeverity: "low", - IsRead: false, - DeliveryStatus: domain.DeliveryStatusPending, - RecipientID: adminWallet.UserID, - Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT, - Level: domain.NotificationLevelWarning, - Reciever: domain.NotificationRecieverSideAdmin, - DeliveryChannel: domain.DeliveryChannelInApp, // Or any preferred admin channel - Payload: domain.NotificationPayload{ - Headline: "CREDIT WARNING: System Running Out of Funds", - Message: fmt.Sprintf( - "Wallet ID %d is running low. Current balance: %.2f", - adminWallet.ID, - adminWallet.Balance.Float32(), - ), - }, - Priority: 1, // High priority for admin alerts - Metadata: fmt.Appendf(nil, `{ - "wallet_id": %d, - "balance": %d, - "notification_type": "admin_alert" - }`, adminWallet.ID, adminWallet.Balance), - } - - // Get admin recipients and send to all - adminRecipients, err := s.GetAdminNotificationRecipients(ctx, adminWallet.ID, adminWallet.Type) - if err != nil { - s.mongoLogger.Error("failed to get admin recipients", - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return err - } - - for _, adminID := range adminRecipients { - adminNotification.RecipientID = adminID - if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { - s.mongoLogger.Error("failed to send admin notification", - zap.Int64("admin_id", adminID), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - } - - // adminNotification.DeliveryChannel = domain.DeliveryChannelEmail - - // if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { - // s.mongoLogger.Error("failed to send email admin notification", - // zap.Int64("admin_id", adminID), - // zap.Error(err), - // zap.Time("timestamp", time.Now()), - // ) - // return err - // } - } - return nil -} - -func (s *Service) SendAdminWalletInsufficientNotification(ctx context.Context, adminWallet domain.Wallet, amount domain.Currency) error { - // Send notification to admin team - adminNotification := &domain.Notification{ - ErrorSeverity: domain.NotificationErrorSeverityLow, - IsRead: false, - DeliveryStatus: domain.DeliveryStatusPending, - RecipientID: adminWallet.UserID, - Type: domain.NOTIFICATION_TYPE_ADMIN_ALERT, - Level: domain.NotificationLevelError, - Reciever: domain.NotificationRecieverSideAdmin, - DeliveryChannel: domain.DeliveryChannelInApp, // Or any preferred admin channel - Payload: domain.NotificationPayload{ - Headline: "CREDIT Error: Admin Wallet insufficient to process customer request", - Message: fmt.Sprintf( - "Wallet ID %d. Transaction Amount %.2f. Current balance: %.2f", - adminWallet.ID, - amount.Float32(), - adminWallet.Balance.Float32(), - ), - }, - Priority: 1, // High priority for admin alerts - Metadata: fmt.Appendf(nil, `{ - "wallet_id": %d, - "balance": %d, - "transaction amount": %.2f, - "notification_type": "admin_alert" - }`, adminWallet.ID, adminWallet.Balance, amount.Float32()), - } - - // Get admin recipients and send to all - - recipients, err := s.GetAdminNotificationRecipients(ctx, adminWallet.ID, adminWallet.Type) - if err != nil { - s.mongoLogger.Error("failed to get admin recipients", - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return err - } - for _, adminID := range recipients { - adminNotification.RecipientID = adminID - if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { - s.mongoLogger.Error("failed to send admin notification", - zap.Int64("admin_id", adminID), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - } - adminNotification.DeliveryChannel = domain.DeliveryChannelEmail - - if err := s.notificationSvc.SendNotification(ctx, adminNotification); err != nil { - s.mongoLogger.Error("failed to send email admin notification", - zap.Int64("admin_id", adminID), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return err - } - - } - return nil -} - -func (s *Service) SendCustomerWalletInsufficientNotification(ctx context.Context, customerWallet domain.Wallet, amount domain.Currency) error { - // Send notification to admin team - customerNotification := &domain.Notification{ - ErrorSeverity: domain.NotificationErrorSeverityLow, - IsRead: false, - DeliveryStatus: domain.DeliveryStatusPending, - RecipientID: customerWallet.UserID, - Type: domain.NOTIFICATION_TYPE_WALLET, - Level: domain.NotificationLevelError, - Reciever: domain.NotificationRecieverSideCustomer, - DeliveryChannel: domain.DeliveryChannelInApp, // Or any preferred admin channel - Payload: domain.NotificationPayload{ - Headline: "CREDIT Error: Wallet insufficient", - Message: fmt.Sprintf( - "Wallet ID %d. Transaction Amount %.2f. Current balance: %.2f", - customerWallet.ID, - amount.Float32(), - customerWallet.Balance.Float32(), - ), - }, - Priority: 1, // High priority for admin alerts - Metadata: fmt.Appendf(nil, `{ - "wallet_id": %d, - "balance": %d, - "transaction amount": %.2f, - "notification_type": "admin_alert" - }`, customerWallet.ID, customerWallet.Balance, amount.Float32()), - } - - if err := s.notificationSvc.SendNotification(ctx, customerNotification); err != nil { - s.mongoLogger.Error("failed to create customer notification", - zap.Int64("customer_id", customerWallet.UserID), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return err - } - - return nil -} diff --git a/internal/services/wallet/service.go b/internal/services/wallet/service.go deleted file mode 100644 index 1cf6bd9..0000000 --- a/internal/services/wallet/service.go +++ /dev/null @@ -1,43 +0,0 @@ -package wallet - -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" -) - -type Service struct { - // approvalStore ApprovalStore - walletStore ports.WalletStore - transferStore ports.TransferStore - // directDepositStore ports.DirectDepositStore - notificationSvc *notificationservice.Service - userSvc *user.Service - mongoLogger *zap.Logger - logger *slog.Logger -} - -func NewService( - walletStore ports.WalletStore, - transferStore ports.TransferStore, - // directDepositStore ports.DirectDepositStore, - notificationSvc *notificationservice.Service, - userSvc *user.Service, - mongoLogger *zap.Logger, - logger *slog.Logger, -) *Service { - return &Service{ - walletStore: walletStore, - transferStore: transferStore, - // directDepositStore: directDepositStore, - // approvalStore: approvalStore, - notificationSvc: notificationSvc, - userSvc: userSvc, - mongoLogger: mongoLogger, - logger: logger, - } -} diff --git a/internal/services/wallet/transfer.go b/internal/services/wallet/transfer.go deleted file mode 100644 index b5a19c0..0000000 --- a/internal/services/wallet/transfer.go +++ /dev/null @@ -1,392 +0,0 @@ -package wallet - -import ( - "context" - "errors" - "fmt" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" -) - -var ( - ErrSenderWalletNotTransferable = errors.New("sender wallet is not transferable") - ErrReceiverWalletNotTransferable = errors.New("receiver wallet is not transferable") - ErrInsufficientBalance = errors.New("wallet balance is insufficient") -) - -func (s *Service) CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) { - // This is just a transfer log when - return s.transferStore.CreateTransfer(ctx, transfer) -} - -func (s *Service) GetAllTransfers(ctx context.Context) ([]domain.TransferDetail, error) { - return s.transferStore.GetAllTransfers(ctx) -} - -func (s *Service) GetTransferByReference(ctx context.Context, reference string) (domain.TransferDetail, error) { - return s.transferStore.GetTransferByReference(ctx, reference) -} - -func (s *Service) GetTransferByID(ctx context.Context, id int64) (domain.TransferDetail, error) { - return s.transferStore.GetTransferByID(ctx, id) -} - -func (s *Service) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.TransferDetail, error) { - return s.transferStore.GetTransfersByWallet(ctx, walletID) -} - -func (s *Service) GetTransferStats(ctx context.Context, walletID int64) (domain.TransferStats, error) { - return s.transferStore.GetTransferStats(ctx, walletID) -} -func (s *Service) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error { - return s.transferStore.UpdateTransferVerification(ctx, id, verified) -} - -func (s *Service) UpdateTransferStatus(ctx context.Context, id int64, status string) error { - return s.transferStore.UpdateTransferStatus(ctx, id, status) -} - -func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiverID int64, - amount domain.Currency, paymentMethod domain.PaymentMethod, - cashierID domain.ValidInt64, message string) (domain.Transfer, error) { - - senderWallet, err := s.GetWalletByID(ctx, senderID) - if err != nil { - return domain.Transfer{}, err - } - if !senderWallet.IsActive { - return domain.Transfer{}, ErrWalletIsDisabled - } - - if !senderWallet.IsTransferable { - fmt.Printf("Error: %d Sender Wallet is not transferable \n", senderWallet.ID) - return domain.Transfer{}, ErrSenderWalletNotTransferable - } - - receiverWallet, err := s.GetWalletByID(ctx, receiverID) - if err != nil { - return domain.Transfer{}, err - } - if !receiverWallet.IsActive { - return domain.Transfer{}, ErrWalletIsDisabled - } - if !receiverWallet.IsTransferable { - fmt.Printf("Error: %d Receiver Wallet is not transferable \n", senderWallet.ID) - return domain.Transfer{}, ErrReceiverWalletNotTransferable - } - - // Deduct from sender - if senderWallet.Balance < amount { - return domain.Transfer{}, ErrBalanceInsufficient - } - - err = s.walletStore.UpdateBalance(ctx, senderID, senderWallet.Balance-amount) - - if err != nil { - return domain.Transfer{}, err - } - - // Add to receiver - err = s.walletStore.UpdateBalance(ctx, receiverID, receiverWallet.Balance+amount) - - if err != nil { - return domain.Transfer{}, err - } - - // Log the transfer so that if there is a mistake, it can be reverted - transfer, err := s.CreateTransfer(ctx, domain.CreateTransfer{ - Message: message, - SenderWalletID: domain.ValidInt64{ - Value: senderID, - Valid: true, - }, - ReceiverWalletID: domain.ValidInt64{ - Value: receiverID, - Valid: true, - }, - CashierID: cashierID, - Amount: amount, - Type: domain.WALLET, - PaymentMethod: paymentMethod, - Verified: true, - }) - if err != nil { - return domain.Transfer{}, err - } - - return transfer, nil -} - -func (s *Service) SendTransferNotification(ctx context.Context, senderWallet domain.Wallet, receiverWallet domain.Wallet, - senderRole domain.Role, receiverRole domain.Role, amount domain.Currency) error { - // Send notification to sender (this could be any role) that money was transferred - senderWalletReceiverSide := domain.ReceiverFromRole(senderRole) - - senderNotify := &domain.Notification{ - RecipientID: senderWallet.UserID, - Type: domain.NOTIFICATION_TYPE_TRANSFER_SUCCESS, - Level: domain.NotificationLevelSuccess, - Reciever: senderWalletReceiverSide, - DeliveryChannel: domain.DeliveryChannelInApp, - Payload: domain.NotificationPayload{ - Headline: "Wallet has been deducted", - Message: fmt.Sprintf(`%s %d has been transferred from your wallet`, senderWallet.Currency, amount), - }, - Priority: 2, - Metadata: []byte(fmt.Sprintf(`{ - "transfer_amount": %d, - "current_balance": %d, - "wallet_id": %d, - "notification_type": "customer_facing" - }`, amount, senderWallet.Balance, senderWallet.ID)), - } - - // Sender notifications - if err := s.notificationSvc.SendNotification(ctx, senderNotify); err != nil { - s.logger.Error("failed to send sender notification", - "user_id", "", - "error", err) - return err - } - - receiverWalletReceiverSide := domain.ReceiverFromRole(receiverRole) - - receiverNotify := &domain.Notification{ - RecipientID: receiverWallet.UserID, - Type: domain.NOTIFICATION_TYPE_TRANSFER_SUCCESS, - Level: domain.NotificationLevelSuccess, - Reciever: receiverWalletReceiverSide, - DeliveryChannel: domain.DeliveryChannelInApp, - Payload: domain.NotificationPayload{ - Headline: "Wallet has been credited", - Message: fmt.Sprintf(`%s %d has been transferred to your wallet`, receiverWallet.Currency, amount), - }, - Priority: 2, - Metadata: []byte(fmt.Sprintf(`{ - "transfer_amount": %d, - "current_balance": %d, - "wallet_id": %d, - "notification_type": "customer_facing" - }`, amount, receiverWallet.Balance, receiverWallet.ID)), - } - // Sender notifications - if err := s.notificationSvc.SendNotification(ctx, receiverNotify); err != nil { - s.logger.Error("failed to send sender notification", - "user_id", "", - "error", err) - return err - } - - return nil -} - -func ApproveTransfer(ctx context.Context, approval domain.ApprovalRequest) error { - return nil -} - -/////////////////////////////////Transaction Make-Cheker///////////////////////////////////////////////// - -// func (s *Service) InitiateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) { -// // Set transfer as unverified by default -// transfer.Verified = false - -// // Create the transfer record -// newTransfer, err := s.transferStore.CreateTransfer(ctx, transfer) -// if err != nil { -// return domain.Transfer{}, err -// } - -// // Create approval record -// approval := domain.TransactionApproval{ -// TransferID: newTransfer.ID, -// Status: "pending", -// } - -// if err := s.approvalStore.CreateApproval(ctx, approval); err != nil { -// return domain.Transfer{}, err -// } - -// // Notify approvers -// go s.notifyApprovers(ctx, newTransfer.ID) - -// return newTransfer, nil -// } - -// func (s *Service) ApproveTransfer(ctx context.Context, approval domain.ApprovalRequest) error { -// // Get the transfer -// transfer, err := s.transferStore.GetTransferByID(ctx, approval.TransferID) -// if err != nil { -// return err -// } - -// // Only allow approval of pending transfers -// if transfer.Status != "pending" { -// return errors.New("only pending transfers can be approved") -// } - -// // Update approval record -// if err := s.approvalStore.UpdateApprovalStatus(ctx, approval.TransferID, "approved", approval.Comments); err != nil { -// return err -// } - -// // Execute the actual transfer -// if transfer.Type == domain.WALLET { -// _, err = s.executeWalletTransfer(ctx, transfer) -// return err -// } - -// // For other transfer types, implement similar execution logic - -// return nil -// } - -// func (s *Service) executeWalletTransfer(ctx context.Context, transfer domain.TransferDetail) (domain.Transfer, error) { -// // Original transfer logic from TransferToWallet, but now guaranteed to be approved -// senderWallet, err := s.GetWalletByID(ctx, transfer.SenderWalletID.Value) -// if err != nil { -// return domain.Transfer{}, err -// } - -// receiverWallet, err := s.GetWalletByID(ctx, transfer.ReceiverWalletID.Value) -// if err != nil { -// return domain.Transfer{}, err -// } - -// // Deduct from sender -// if senderWallet.Balance < transfer.Amount { -// return domain.Transfer{}, ErrBalanceInsufficient -// } - -// err = s.walletStore.UpdateBalance(ctx, transfer.SenderWalletID.Value, senderWallet.Balance-transfer.Amount) -// if err != nil { -// return domain.Transfer{}, err -// } - -// // Add to receiver -// err = s.walletStore.UpdateBalance(ctx, transfer.ReceiverWalletID.Value, receiverWallet.Balance+transfer.Amount) -// if err != nil { -// return domain.Transfer{}, err -// } - -// // Mark transfer as completed -// if err := s.transferStore.UpdateTransferStatus(ctx, transfer.ID, "completed"); err != nil { -// return domain.Transfer{}, err -// } - -// // Send notifications -// go s.SendTransferNotification(ctx, senderWallet, receiverWallet, -// domain.RoleFromString(transfer.SenderWalletID.Value), -// domain.RoleFromString(transfer.ReceiverWalletID.Value), -// transfer.Amount) - -// return transfer, nil -// } - -// func (s *Service) RejectTransfer(ctx context.Context, approval domain.ApprovalRequest) error { -// // Update approval record -// if err := s.approvalStore.UpdateApprovalStatus(ctx, approval.TransferID, "rejected", approval.Comments); err != nil { -// return err -// } - -// // Update transfer status -// if err := s.transferStore.UpdateTransferStatus(ctx, approval.TransferID, "rejected"); err != nil { -// return err -// } - -// // Notify the initiator -// go s.notifyInitiator(ctx, approval.TransferID, approval.Comments) - -// return nil -// } - -// func (s *Service) GetPendingApprovals(ctx context.Context) ([]domain.TransferDetail, error) { -// return s.approvalStore.GetPendingApprovals(ctx) -// } - -// func (s *Service) GetTransferApprovalHistory(ctx context.Context, transferID int64) ([]domain.TransactionApproval, error) { -// return s.approvalStore.GetApprovalsByTransfer(ctx, transferID) -// } - -// func (s *Service) notifyApprovers(ctx context.Context, transferID int64) { -// // Get approvers (could be from a config or role-based) -// approvers, err := s.userStore.GetUsersByRole(ctx, "approver") -// if err != nil { -// s.logger.Error("failed to get approvers", zap.Error(err)) -// return -// } - -// transfer, err := s.transferStore.GetTransferByID(ctx, transferID) -// if err != nil { -// s.logger.Error("failed to get transfer for notification", zap.Error(err)) -// return -// } - -// for _, approver := range approvers { -// notification := &domain.Notification{ -// RecipientID: approver.ID, -// Type: domain.NOTIFICATION_TYPE_APPROVAL_REQUIRED, -// Level: domain.NotificationLevelWarning, -// Reciever: domain.NotificationRecieverSideAdmin, -// DeliveryChannel: domain.DeliveryChannelInApp, -// Payload: domain.NotificationPayload{ -// Headline: "Transfer Approval Required", -// Message: fmt.Sprintf("Transfer #%d requires your approval", transfer.ID), -// }, -// Priority: 1, -// Metadata: []byte(fmt.Sprintf(`{ -// "transfer_id": %d, -// "amount": %d, -// "notification_type": "approval_request" -// }`, transfer.ID, transfer.Amount)), -// } - -// if err := s.notificationStore.SendNotification(ctx, notification); err != nil { -// s.logger.Error("failed to send approval notification", -// zap.Int64("approver_id", approver.ID), -// zap.Error(err)) -// } -// } -// } - -// func (s *Service) notifyInitiator(ctx context.Context, transferID int64, comments string) { -// transfer, err := s.transferStore.GetTransferByID(ctx, transferID) -// if err != nil { -// s.logger.Error("failed to get transfer for notification", zap.Error(err)) -// return -// } - -// // Determine who initiated the transfer (could be cashier or user) -// recipientID := transfer.CashierID.Value -// if !transfer.CashierID.Valid { -// // If no cashier, assume user initiated -// wallet, err := s.GetWalletByID(ctx, transfer.SenderWalletID.Value) -// if err != nil { -// s.logger.Error("failed to get wallet for notification", zap.Error(err)) -// return -// } -// recipientID = wallet.UserID -// } - -// notification := &domain.Notification{ -// RecipientID: recipientID, -// Type: domain.NOTIFICATION_TYPE_TRANSFER_REJECTED, -// Level: domain.NotificationLevelWarning, -// Reciever: domain.NotificationRecieverSideCustomer, -// DeliveryChannel: domain.DeliveryChannelInApp, -// Payload: domain.NotificationPayload{ -// Headline: "Transfer Rejected", -// Message: fmt.Sprintf("Your transfer #%d was rejected. Comments: %s", transfer.ID, comments), -// }, -// Priority: 2, -// Metadata: []byte(fmt.Sprintf(`{ -// "transfer_id": %d, -// "notification_type": "transfer_rejected" -// }`, transfer.ID)), -// } - -// if err := s.notificationStore.SendNotification(ctx, notification); err != nil { -// s.logger.Error("failed to send rejection notification", -// zap.Int64("recipient_id", recipientID), -// zap.Error(err)) -// } -// } diff --git a/internal/services/wallet/wallet.go b/internal/services/wallet/wallet.go deleted file mode 100644 index ba48240..0000000 --- a/internal/services/wallet/wallet.go +++ /dev/null @@ -1,287 +0,0 @@ -package wallet - -import ( - "context" - "errors" - - // "fmt" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "go.uber.org/zap" - // "github.com/SamuelTariku/FortuneBet-Backend/internal/event" -) - -var ( - ErrBalanceInsufficient = errors.New("wallet balance is insufficient") - ErrWalletIsDisabled = errors.New("wallet is disabled") -) - -func (s *Service) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) { - return s.walletStore.CreateWallet(ctx, wallet) -} - -func (s *Service) CreateCustomerWallet(ctx context.Context, customerID int64) (domain.CustomerWallet, error) { - - regularWallet, err := s.CreateWallet(ctx, domain.CreateWallet{ - IsWithdraw: true, - IsBettable: true, - IsTransferable: true, - UserID: customerID, - Type: domain.RegularWalletType, - }) - - if err != nil { - return domain.CustomerWallet{}, err - } - - staticWallet, err := s.CreateWallet(ctx, domain.CreateWallet{ - IsWithdraw: false, - IsBettable: true, - IsTransferable: true, - UserID: customerID, - Type: domain.StaticWalletType, - }) - - if err != nil { - return domain.CustomerWallet{}, err - } - - return s.walletStore.CreateCustomerWallet(ctx, domain.CreateCustomerWallet{ - CustomerID: customerID, - RegularWalletID: regularWallet.ID, - StaticWalletID: staticWallet.ID, - }) -} - -func (s *Service) GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error) { - return s.walletStore.GetWalletByID(ctx, id) -} - -func (s *Service) GetAllWallets(ctx context.Context) ([]domain.Wallet, error) { - return s.walletStore.GetAllWallets(ctx) -} - -func (s *Service) GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wallet, error) { - return s.walletStore.GetWalletsByUser(ctx, id) -} - -func (s *Service) GetCompanyByWalletID(ctx context.Context, walletID int64) (domain.Company, error) { - return s.walletStore.GetCompanyByWalletID(ctx, walletID) -} - -func (s *Service) GetBranchByWalletID(ctx context.Context, walletID int64) (domain.Branch, error) { - return s.walletStore.GetBranchByWalletID(ctx, walletID) -} - -func (s *Service) GetAllCustomerWallet(ctx context.Context) ([]domain.GetCustomerWallet, error) { - return s.walletStore.GetAllCustomerWallets(ctx) -} -func (s *Service) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) { - return s.walletStore.GetCustomerWallet(ctx, customerID) -} - -func (s *Service) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error) { - return s.walletStore.GetAllBranchWallets(ctx) -} - -func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error { - - wallet, err := s.GetWalletByID(ctx, id) - if err != nil { - return err - } - - if !wallet.IsActive { - return ErrWalletIsDisabled - } - - err = s.walletStore.UpdateBalance(ctx, id, balance) - if err != nil { - return err - } - - // go func() { - // s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.UserID), event.WalletEvent{ - // EventType: event.WalletBalanceUpdated, - // WalletID: wallet.ID, - // UserID: wallet.UserID, - // Balance: balance, - // WalletType: wallet.Type, - // Trigger: "UpdateBalance", - // }) - // }() - - if err := s.SendWalletUpdateNotification(ctx, wallet); err != nil { - s.mongoLogger.Info("Failed to send wallet update notification", - zap.Int64("wallet_id", wallet.ID), - zap.Error(err)) - } - - return nil -} - -func (s *Service) AddToWallet( - ctx context.Context, id int64, amount domain.Currency, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod, paymentDetails domain.PaymentDetails, message string) (domain.Transfer, error) { - wallet, err := s.GetWalletByID(ctx, id) - if err != nil { - return domain.Transfer{}, err - } - if !wallet.IsActive { - return domain.Transfer{}, ErrWalletIsDisabled - } - err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount) - if err != nil { - return domain.Transfer{}, err - } - - // go func() { - // s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{ - // EventType: event.WalletBalanceUpdated, - // WalletID: wallet.ID, - // UserID: wallet.UserID, - // Balance: wallet.Balance + amount, - // WalletType: wallet.Type, - // Trigger: "AddToWallet", - // }) - // }() - if err := s.SendWalletUpdateNotification(ctx, wallet); err != nil { - s.mongoLogger.Info("Failed to send wallet update notification", - zap.Int64("wallet_id", wallet.ID), - zap.Error(err)) - } - - // Log the transfer here for reference - newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{ - Message: message, - Amount: amount, - Verified: true, - ReceiverWalletID: domain.ValidInt64{ - Value: wallet.ID, - Valid: true, - }, - CashierID: cashierID, - PaymentMethod: paymentMethod, - Type: domain.DEPOSIT, - ReferenceNumber: paymentDetails.ReferenceNumber.Value, - }) - - return newTransfer, err -} - -func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.Currency, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod, message string) (domain.Transfer, error) { - wallet, err := s.GetWalletByID(ctx, id) - if err != nil { - return domain.Transfer{}, err - } - - if !wallet.IsActive { - return domain.Transfer{}, ErrWalletIsDisabled - } - if wallet.Balance < amount { - // Send Wallet low to admin - if wallet.Type == domain.CompanyWalletType || wallet.Type == domain.BranchWalletType { - s.SendAdminWalletInsufficientNotification(ctx, wallet, amount) - } else { - s.SendCustomerWalletInsufficientNotification(ctx, wallet, amount) - } - return domain.Transfer{}, ErrBalanceInsufficient - } - - if wallet.Type == domain.BranchWalletType || wallet.Type == domain.CompanyWalletType { - var thresholds []float32 - - if wallet.Type == domain.CompanyWalletType { - thresholds = []float32{100000, 50000, 25000, 10000, 5000, 3000, 1000, 500} - } else { - thresholds = []float32{5000, 3000, 1000, 500} - } - - balance := wallet.Balance.Float32() - for _, thresholds := range thresholds { - if thresholds < balance && thresholds > (balance-amount.Float32()) { - s.SendAdminWalletLowNotification(ctx, wallet) - break - } - } - } - - err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance-amount) - - if err != nil { - return domain.Transfer{}, nil - } - - // go func() { - // s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{ - // EventType: event.WalletBalanceUpdated, - // WalletID: wallet.ID, - // UserID: wallet.UserID, - // Balance: wallet.Balance - amount, - // WalletType: wallet.Type, - // Trigger: "DeductFromWallet", - // }) - // }() - - if err := s.SendWalletUpdateNotification(ctx, wallet); err != nil { - s.mongoLogger.Info("Failed to send wallet update notification", - zap.Int64("wallet_id", wallet.ID), - zap.Error(err)) - } - - // Log the transfer here for reference - newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{ - Message: message, - Amount: amount, - Verified: true, - SenderWalletID: domain.ValidInt64{ - Value: wallet.ID, - Valid: true, - }, - CashierID: cashierID, - PaymentMethod: paymentMethod, - Type: domain.WITHDRAW, - ReferenceNumber: "", - }) - - return newTransfer, err -} - -// Directly Refilling wallet without -// func (s *Service) RefillWallet(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) { -// receiverWallet, err := s.GetWalletByID(ctx, transfer.ReceiverWalletID) -// if err != nil { -// return domain.Transfer{}, err -// } - -// // Add to receiver -// senderWallet, err := s.GetWalletByID(ctx, transfer.SenderWalletID) -// if err != nil { -// return domain.Transfer{}, err -// } else if senderWallet.Balance < transfer.Amount { -// return domain.Transfer{}, ErrInsufficientBalance -// } - -// err = s.walletStore.UpdateBalance(ctx, receiverWallet.ID, receiverWallet.Balance+transfer.Amount) -// if err != nil { -// return domain.Transfer{}, err -// } - -// // Log the transfer so that if there is a mistake, it can be reverted -// newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{ -// CashierID: transfer.CashierID, -// ReceiverWalletID: transfer.ReceiverWalletID, -// Amount: transfer.Amount, -// Type: domain.DEPOSIT, -// PaymentMethod: transfer.PaymentMethod, -// Verified: true, -// }) -// if err != nil { -// return domain.Transfer{}, err -// } - -// return newTransfer, nil -// } - -func (s *Service) UpdateWalletActive(ctx context.Context, id int64, isActive bool) error { - return s.walletStore.UpdateWalletActive(ctx, id, isActive) -} diff --git a/internal/web_server/app.go b/internal/web_server/app.go index 7994537..007d8cb 100644 --- a/internal/web_server/app.go +++ b/internal/web_server/app.go @@ -1,136 +1,82 @@ package httpserver import ( + "Yimaru-Backend/internal/config" + "Yimaru-Backend/internal/services/arifpay" + "Yimaru-Backend/internal/services/authentication" + notificationservice "Yimaru-Backend/internal/services/notification" + "Yimaru-Backend/internal/services/recommendation" + referralservice "Yimaru-Backend/internal/services/referal" + "Yimaru-Backend/internal/services/settings" + "Yimaru-Backend/internal/services/transaction" + "Yimaru-Backend/internal/services/user" + jwtutil "Yimaru-Backend/internal/web_server/jwt" + customvalidator "Yimaru-Backend/internal/web_server/validator" "fmt" "log/slog" - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/arifpay" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bonus" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/company" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/currency" - directdeposit "github.com/SamuelTariku/FortuneBet-Backend/internal/services/direct_deposit" - enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions" - issuereporting "github.com/SamuelTariku/FortuneBet-Backend/internal/services/issue_reporting" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/league" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/raffle" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/recommendation" - referralservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/referal" - "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" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" - virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" - alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/atlas" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/orchestration" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" - jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" - customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator" "go.uber.org/zap" - notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" "github.com/bytedance/sonic" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" ) type App struct { - directDepositSvc *directdeposit.Service - enetPulseSvc *enetpulse.Service - atlasVirtualGameService atlas.AtlasVirtualGameService - veliVirtualGameService *veli.Service - orchestrationSvc *orchestration.Service - telebirrSvc *telebirr.TelebirrService - arifpaySvc *arifpay.ArifpayService - santimpaySvc *santimpay.SantimPayService - issueReportingSvc *issuereporting.Service - instSvc *institutions.Service - currSvc *currency.Service - fiber *fiber.App - aleaVirtualGameService alea.AleaVirtualGameService - recommendationSvc recommendation.RecommendationService - cfg *config.Config - logger *slog.Logger - NotidicationStore *notificationservice.Service - referralSvc *referralservice.Service - raffleSvc *raffle.Service - bonusSvc *bonus.Service - port int - settingSvc *settings.Service - authSvc *authentication.Service - userSvc *user.Service - betSvc *bet.Service - virtualGameSvc virtualgameservice.VirtualGameService - // reportSvc report.ReportService - chapaSvc *chapa.Service - walletSvc *wallet.Service - transactionSvc *transaction.Service - ticketSvc *ticket.Service - branchSvc *branch.Service - companySvc *company.Service - validator *customvalidator.CustomValidator - JwtConfig jwtutil.JwtConfig - Logger *slog.Logger - prematchSvc *odds.ServiceImpl - eventSvc *event.Service - leagueSvc *league.Service - resultSvc *result.Service - statSvc *stats.Service - mongoLoggerSvc *zap.Logger + // directDepositSvc *directdeposit.Service + // telebirrSvc *telebirr.TelebirrService + arifpaySvc *arifpay.ArifpayService + // santimpaySvc *santimpay.SantimPayService + // issueReportingSvc *issuereporting.Service + // instSvc *institutions.Service + // currSvc *currency.Service + fiber *fiber.App + recommendationSvc recommendation.RecommendationService + cfg *config.Config + logger *slog.Logger + NotidicationStore *notificationservice.Service + referralSvc *referralservice.Service + // raffleSvc *raffle.Service + // bonusSvc *bonus.Service + port int + settingSvc *settings.Service + authSvc *authentication.Service + userSvc *user.Service + // chapaSvc *chapa.Service + transactionSvc *transaction.Service + // branchSvc *branch.Service + // companySvc *company.Service + validator *customvalidator.CustomValidator + JwtConfig jwtutil.JwtConfig + Logger *slog.Logger + // statSvc *stats.Service + mongoLoggerSvc *zap.Logger } func NewApp( - directDepositSvc *directdeposit.Service, - enetPulseSvc *enetpulse.Service, - atlasVirtualGameService atlas.AtlasVirtualGameService, - veliVirtualGameService *veli.Service, - orchestrationSvc *orchestration.Service, - telebirrSvc *telebirr.TelebirrService, + // directDepositSvc *directdeposit.Service, + // telebirrSvc *telebirr.TelebirrService, arifpaySvc *arifpay.ArifpayService, - santimpaySvc *santimpay.SantimPayService, - issueReportingSvc *issuereporting.Service, - instSvc *institutions.Service, - currSvc *currency.Service, + // santimpaySvc *santimpay.SantimPayService, + // issueReportingSvc *issuereporting.Service, + // instSvc *institutions.Service, + // currSvc *currency.Service, port int, validator *customvalidator.CustomValidator, settingSvc *settings.Service, authSvc *authentication.Service, logger *slog.Logger, JwtConfig jwtutil.JwtConfig, userSvc *user.Service, - ticketSvc *ticket.Service, - betSvc *bet.Service, - // reportSvc report.ReportService, - chapaSvc *chapa.Service, - walletSvc *wallet.Service, + // chapaSvc *chapa.Service, transactionSvc *transaction.Service, - branchSvc *branch.Service, - companySvc *company.Service, + // branchSvc *branch.Service, + // companySvc *company.Service, notidicationStore *notificationservice.Service, - prematchSvc *odds.ServiceImpl, - eventSvc *event.Service, - leagueSvc *league.Service, referralSvc *referralservice.Service, - raffleSvc *raffle.Service, - bonusSvc *bonus.Service, - virtualGameSvc virtualgameservice.VirtualGameService, - aleaVirtualGameService alea.AleaVirtualGameService, - // veliVirtualGameService veli.VeliVirtualGameService, + // raffleSvc *raffle.Service, + // bonusSvc *bonus.Service, recommendationSvc recommendation.RecommendationService, - resultSvc *result.Service, - statSvc *stats.Service, + // statSvc *stats.Service, cfg *config.Config, mongoLoggerSvc *zap.Logger, ) *App { @@ -151,48 +97,33 @@ func NewApp( app.Static("/static", "./static") s := &App{ - directDepositSvc: directDepositSvc, - enetPulseSvc: enetPulseSvc, - atlasVirtualGameService: atlasVirtualGameService, - veliVirtualGameService: veliVirtualGameService, - orchestrationSvc: orchestrationSvc, - telebirrSvc: telebirrSvc, - arifpaySvc: arifpaySvc, - santimpaySvc: santimpaySvc, - issueReportingSvc: issueReportingSvc, - instSvc: instSvc, - currSvc: currSvc, - fiber: app, - port: port, - - settingSvc: settingSvc, - authSvc: authSvc, - validator: validator, - logger: logger, - JwtConfig: JwtConfig, - userSvc: userSvc, - ticketSvc: ticketSvc, - betSvc: betSvc, - // reportSvc: reportSvc, - chapaSvc: chapaSvc, - walletSvc: walletSvc, - transactionSvc: transactionSvc, - branchSvc: branchSvc, - companySvc: companySvc, - NotidicationStore: notidicationStore, - referralSvc: referralSvc, - raffleSvc: raffleSvc, - bonusSvc: bonusSvc, - Logger: logger, - prematchSvc: prematchSvc, - eventSvc: eventSvc, - leagueSvc: leagueSvc, - virtualGameSvc: virtualGameSvc, - aleaVirtualGameService: aleaVirtualGameService, - // veliVirtualGameService: veliVirtualGameService, + // directDepositSvc: directDepositSvc, + // telebirrSvc: telebirrSvc, + arifpaySvc: arifpaySvc, + // santimpaySvc: santimpaySvc, + // issueReportingSvc: issueReportingSvc, + // instSvc: instSvc, + // currSvc: currSvc, + fiber: app, + port: port, + settingSvc: settingSvc, + authSvc: authSvc, + validator: validator, + logger: logger, + JwtConfig: JwtConfig, + userSvc: userSvc, + // ticketSvc: ticketSvc, + // chapaSvc: chapaSvc, + transactionSvc: transactionSvc, + // branchSvc: branchSvc, + // companySvc: companySvc, + NotidicationStore: notidicationStore, + referralSvc: referralSvc, + // raffleSvc: raffleSvc, + // bonusSvc: bonusSvc, + Logger: logger, recommendationSvc: recommendationSvc, - resultSvc: resultSvc, - statSvc: statSvc, + // statSvc: statSvc, cfg: cfg, mongoLoggerSvc: mongoLoggerSvc, } diff --git a/internal/web_server/cron.go b/internal/web_server/cron.go index 2969daa..2ac1d43 100644 --- a/internal/web_server/cron.go +++ b/internal/web_server/cron.go @@ -1,562 +1,539 @@ package httpserver -import ( - "context" - "os" - "time" +// "time" - // "time" +// func StartBetAPIDataFetchingCrons( +// eventService *eventsvc.Service, +// oddsService oddssvc.ServiceImpl, +// resultService *resultsvc.Service, +// mongoLogger *zap.Logger, +// ) { +// c := cron.New(cron.WithSeconds()) - "log" +// schedule := []struct { +// spec string +// task func() +// }{ +// { +// spec: "0 0 * * * *", // Every 1 hour +// task: func() { +// 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("[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("[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 0 * * 1", // Every Monday +// task: func() { +// 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("[BetAPI Send Result Notification Crons] Failed to process result", +// zap.Time("timestamp", time.Now()), +// zap.Error(err), +// ) +// } else { +// mongoLogger.Info("[BetAPI Send Result Notification Crons] Completed sending weekly result notification without errors", +// zap.Time("timestamp", time.Now()), +// ) +// } +// }, +// }, +// } - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - betSvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" +// for _, job := range schedule { +// // job.task() +// if _, err := c.AddFunc(job.spec, job.task); err != nil { +// mongoLogger.Error("[BetAPI Data Fetching Crons] Failed to schedule data fetching cron job", +// zap.Time("timestamp", time.Now()), +// zap.Error(err), +// ) +// } +// } - 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/orchestration" - "github.com/robfig/cron/v3" - "go.uber.org/zap" -) +// c.Start() +// mongoLogger.Info("[BetAPI Data Fetching Crons] Started Data Fetching Cron jobs", +// zap.Time("timestamp", time.Now()), +// ) +// } -func StartBetAPIDataFetchingCrons( - eventService *eventsvc.Service, - oddsService oddssvc.ServiceImpl, - resultService *resultsvc.Service, - mongoLogger *zap.Logger, -) { - c := cron.New(cron.WithSeconds()) +// func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificationservice.Service, mongoLogger *zap.Logger) { +// c := cron.New(cron.WithSeconds()) - schedule := []struct { - spec string - task func() - }{ - { - spec: "0 0 * * * *", // Every 1 hour - task: func() { - 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("[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("[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 0 * * 1", // Every Monday - task: func() { - 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("[BetAPI Send Result Notification Crons] Failed to process result", - zap.Time("timestamp", time.Now()), - zap.Error(err), - ) - } else { - mongoLogger.Info("[BetAPI Send Result Notification Crons] Completed sending weekly result notification without errors", - zap.Time("timestamp", time.Now()), - ) - } - }, - }, - } +// schedule := []struct { +// spec string +// task func() +// }{ +// { +// spec: "0 0 * * * *", // Every hour +// task: func() { +// mongoLogger.Info("[Delete Old Ticket Crons] Deleting old tickets", zap.Time("timestamp", time.Now())) +// if err := ticketService.DeleteOldTickets(context.Background()); err != nil { +// mongoLogger.Error("[Delete Old Ticket Crons] Failed to remove old ticket", +// zap.Error(err), +// zap.Time("timestamp", time.Now()), +// ) +// } else { +// 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("[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("[Delete Old Notification Crons] Successfully deleted old notifications", +// zap.Time("timestamp", time.Now()), +// ) +// } +// }, +// }, +// } - for _, job := range schedule { - // job.task() - if _, err := c.AddFunc(job.spec, job.task); err != nil { - mongoLogger.Error("[BetAPI Data Fetching Crons] Failed to schedule data fetching cron job", - zap.Time("timestamp", time.Now()), - zap.Error(err), - ) - } - } +// for _, job := range schedule { +// if _, err := c.AddFunc(job.spec, job.task); err != nil { +// mongoLogger.Error("[Start Cleanup Crons] Failed to schedule cleanup cron job", +// zap.Time("timestamp", time.Now()), +// zap.Error(err), +// ) +// } +// } - c.Start() - mongoLogger.Info("[BetAPI Data Fetching Crons] Started Data Fetching Cron jobs", - zap.Time("timestamp", time.Now()), - ) -} +// c.Start() +// mongoLogger.Info("[Start Cleanup Crons] Started cleanup cron jobs", +// zap.Time("timestamp", time.Now()), +// ) +// } -func StartCleanupCrons(ticketService ticket.Service, notificationSvc *notificationservice.Service, mongoLogger *zap.Logger) { - c := cron.New(cron.WithSeconds()) +// func StartStatCrons(statService *stats.Service, mongoLogger *zap.Logger) { +// c := cron.New(cron.WithSeconds()) - schedule := []struct { - spec string - task func() - }{ - { - spec: "0 0 * * * *", // Every hour - task: func() { - mongoLogger.Info("[Delete Old Ticket Crons] Deleting old tickets", zap.Time("timestamp", time.Now())) - if err := ticketService.DeleteOldTickets(context.Background()); err != nil { - mongoLogger.Error("[Delete Old Ticket Crons] Failed to remove old ticket", - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - } else { - 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("[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("[Delete Old Notification Crons] Successfully deleted old notifications", - zap.Time("timestamp", time.Now()), - ) - } - }, - }, - } +// schedule := []struct { +// spec string +// task func() +// }{ +// { +// spec: "0 0 * * * *", // Every hour +// task: func() { +// 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", +// zap.Time("timestamp", time.Now()), +// zap.Duration("duration", time.Since(start)), +// ) +// } +// }, +// }, +// { +// spec: "0 0 * * * *", // Every hour +// task: func() { +// start := time.Now() +// mongoLogger.Info("[Branch Stats Crons] Updating branch stats", zap.Time("timestamp", time.Now())) +// if err := statService.UpdateBranchStats(context.Background()); err != nil { +// mongoLogger.Error("[Branch Stats Crons] Failed to update branch stats", +// zap.Error(err), +// zap.Time("timestamp", time.Now()), +// zap.Duration("duration", time.Since(start)), +// ) +// } else { +// mongoLogger.Info("[Branch Stats Crons] Successfully updated branch stats", +// zap.Time("timestamp", time.Now()), +// zap.Duration("duration", time.Since(start)), +// ) +// } +// }, +// }, +// { +// spec: "0 0 * * * *", // Every hour +// task: func() { +// start := time.Now() +// mongoLogger.Info("[Wallet Stats Crons] Updating wallet stats", zap.Time("timestamp", time.Now())) +// if err := statService.UpdateWalletStats(context.Background()); err != nil { +// mongoLogger.Error("[Wallet Stats Crons] Failed to update wallet stats", +// zap.Error(err), +// zap.Time("timestamp", time.Now()), +// zap.Duration("duration", time.Since(start)), +// ) +// } else { +// mongoLogger.Info("[Wallet Stats Crons] Successfully updated wallet 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 := 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", +// zap.Time("timestamp", time.Now()), +// zap.Duration("duration", time.Since(start))) +// } +// }, +// }, +// } - for _, job := range schedule { - if _, err := c.AddFunc(job.spec, job.task); err != nil { - mongoLogger.Error("[Start Cleanup Crons] Failed to schedule cleanup cron job", - zap.Time("timestamp", time.Now()), - zap.Error(err), - ) - } - } +// for _, job := range schedule { +// job.task() +// if _, err := c.AddFunc(job.spec, job.task); err != nil { +// mongoLogger.Error("[Starting Stats Crons] Failed to schedule stats cron job", +// zap.Error(err), +// zap.Time("timestamp", time.Now()), +// ) +// } +// } - c.Start() - mongoLogger.Info("[Start Cleanup Crons] Started cleanup cron jobs", - zap.Time("timestamp", time.Now()), - ) -} +// c.Start() +// mongoLogger.Info("Starting Stats Crons] Started Stat Cron Jobs", zap.Time("timestamp", time.Now())) +// } -func StartStatCrons(statService *stats.Service, mongoLogger *zap.Logger) { - c := cron.New(cron.WithSeconds()) +// func StartReportCrons(reportService report.ReportService, mongoLogger *zap.Logger) { +// c := cron.New(cron.WithSeconds()) - schedule := []struct { - spec string - task func() - }{ - { - spec: "0 0 * * * *", // Every hour - task: func() { - 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", - zap.Time("timestamp", time.Now()), - zap.Duration("duration", time.Since(start)), - ) - } - }, - }, - { - spec: "0 0 * * * *", // Every hour - task: func() { - start := time.Now() - mongoLogger.Info("[Branch Stats Crons] Updating branch stats", zap.Time("timestamp", time.Now())) - if err := statService.UpdateBranchStats(context.Background()); err != nil { - mongoLogger.Error("[Branch Stats Crons] Failed to update branch stats", - zap.Error(err), - zap.Time("timestamp", time.Now()), - zap.Duration("duration", time.Since(start)), - ) - } else { - mongoLogger.Info("[Branch Stats Crons] Successfully updated branch stats", - zap.Time("timestamp", time.Now()), - zap.Duration("duration", time.Since(start)), - ) - } - }, - }, - { - spec: "0 0 * * * *", // Every hour - task: func() { - start := time.Now() - mongoLogger.Info("[Wallet Stats Crons] Updating wallet stats", zap.Time("timestamp", time.Now())) - if err := statService.UpdateWalletStats(context.Background()); err != nil { - mongoLogger.Error("[Wallet Stats Crons] Failed to update wallet stats", - zap.Error(err), - zap.Time("timestamp", time.Now()), - zap.Duration("duration", time.Since(start)), - ) - } else { - mongoLogger.Info("[Wallet Stats Crons] Successfully updated wallet 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 := 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", - zap.Time("timestamp", time.Now()), - zap.Duration("duration", time.Since(start))) - } - }, - }, - } +// schedule := []struct { +// spec string +// task func() +// }{ +// { +// spec: "0 * * * * *", // Every 5 Minutes +// task: func() { +// mongoLogger.Info("[Process Report Crons] Started Checking and Processing Reports") +// if err := reportService.ProcessReportRequests(context.Background()); err != nil { +// mongoLogger.Error("[Process Report Crons] Failed to process reports", +// zap.Error(err), +// ) +// } else { +// mongoLogger.Info("[Process Report Crons] Successfully processed all reports") +// } +// }, +// }, +// } - for _, job := range schedule { - job.task() - if _, err := c.AddFunc(job.spec, job.task); err != nil { - mongoLogger.Error("[Starting Stats Crons] Failed to schedule stats cron job", - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - } - } +// for _, job := range schedule { +// 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("Starting Stats Crons] Started Stat Cron Jobs", zap.Time("timestamp", time.Now())) -} +// c.Start() +// mongoLogger.Info("[Report Crons] Cron jobs started for reports", zap.Time("timestamp", time.Now())) +// } -func StartReportCrons(reportService report.ReportService, mongoLogger *zap.Logger) { - c := cron.New(cron.WithSeconds()) +// // SetupReportCronJobs schedules periodic report generation +// func SetupReportandVirtualGameCronJobs( +// ctx context.Context, +// reportService report.ReportService, +// virtualGameOrchestrationService *orchestration.Service, +// outputDir string, +// ) { +// c := cron.New(cron.WithSeconds()) // WithSeconds for testing, remove in prod - schedule := []struct { - spec string - task func() - }{ - { - spec: "0 * * * * *", // Every 5 Minutes - task: func() { - mongoLogger.Info("[Process Report Crons] Started Checking and Processing Reports") - if err := reportService.ProcessReportRequests(context.Background()); err != nil { - mongoLogger.Error("[Process Report Crons] Failed to process reports", - zap.Error(err), - ) - } else { - mongoLogger.Info("[Process Report Crons] Successfully processed all reports") - } - }, - }, - } +// schedule := []struct { +// spec string +// period string +// }{ +// // { spec: "*/60 * * * * *", period: "test" }, // every 60 seconds for testing +// { +// spec: "0 0 0 * * *", // daily at midnight +// period: "daily", +// }, +// } - for _, job := range schedule { - 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), - ) - } - } +// for _, job := range schedule { +// period := job.period - c.Start() - mongoLogger.Info("[Report Crons] Cron jobs started for reports", zap.Time("timestamp", time.Now())) -} +// if _, err := c.AddFunc(job.spec, func() { +// log.Printf("[%s] Running virtual game & provider report job...", period) -// SetupReportCronJobs schedules periodic report generation -func SetupReportandVirtualGameCronJobs( - ctx context.Context, - reportService report.ReportService, - virtualGameOrchestrationService *orchestration.Service, - outputDir string, -) { - c := cron.New(cron.WithSeconds()) // WithSeconds for testing, remove in prod +// brandID := os.Getenv("VELI_BRAND_ID") +// if brandID == "" { +// log.Println("VELI_BRAND_ID not set, skipping virtual game sync") +// return +// } - schedule := []struct { - spec string - period string - }{ - // { spec: "*/60 * * * * *", period: "test" }, // every 60 seconds for testing - { - spec: "0 0 0 * * *", // daily at midnight - period: "daily", - }, - } +// // Step 1. Fetch and store all virtual games +// req := domain.ProviderRequest{ +// BrandID: brandID, +// ExtraData: true, +// Size: 1000, +// Page: 1, +// } - for _, job := range schedule { - period := job.period +// allGames, err := virtualGameOrchestrationService.FetchAndStoreAllVirtualGames(ctx, req, "ETB") +// if err != nil { +// log.Printf("[%s] Error fetching/storing virtual games: %v", period, err) +// return +// } - if _, err := c.AddFunc(job.spec, func() { - log.Printf("[%s] Running virtual game & provider report job...", period) +// log.Printf("[%s] Successfully fetched & stored %d virtual games", period, len(allGames)) - brandID := os.Getenv("VELI_BRAND_ID") - if brandID == "" { - log.Println("VELI_BRAND_ID not set, skipping virtual game sync") - return - } +// // Step 2. Fetch all providers +// providers, total, err := virtualGameOrchestrationService.ListProviders(ctx, 1000, 0) +// if err != nil { +// log.Printf("[%s] Failed to list providers: %v", period, err) +// return +// } else if total == 0 { +// log.Printf("[%s] No providers found, skipping report generation", period) +// return +// } - // Step 1. Fetch and store all virtual games - req := domain.ProviderRequest{ - BrandID: brandID, - ExtraData: true, - Size: 1000, - Page: 1, - } +// log.Printf("[%s] Found %d total providers", period, total) - allGames, err := virtualGameOrchestrationService.FetchAndStoreAllVirtualGames(ctx, req, "ETB") - if err != nil { - log.Printf("[%s] Error fetching/storing virtual games: %v", period, err) - return - } +// // Step 3. Create provider-level daily report entries +// reportDate := time.Now().UTC().Truncate(24 * time.Hour) +// for _, p := range providers { +// createReq := domain.CreateVirtualGameProviderReport{ +// ProviderID: p.ProviderID, +// ReportDate: reportDate, +// TotalGamesPlayed: 0, +// TotalBets: 0, +// TotalPayouts: 0, +// TotalPlayers: 0, +// ReportType: period, // "daily" +// } - log.Printf("[%s] Successfully fetched & stored %d virtual games", period, len(allGames)) +// _, err := virtualGameOrchestrationService.CreateVirtualGameProviderReport(ctx, createReq) +// if err != nil { +// log.Printf("[%s] Failed to create report for provider %s: %v", period, p.ProviderID, err) +// continue +// } - // Step 2. Fetch all providers - providers, total, err := virtualGameOrchestrationService.ListProviders(ctx, 1000, 0) - if err != nil { - log.Printf("[%s] Failed to list providers: %v", period, err) - return - } else if total == 0 { - log.Printf("[%s] No providers found, skipping report generation", period) - return - } +// log.Printf("[%s] Created daily report row for provider: %s", period, p.ProviderID) +// } - log.Printf("[%s] Found %d total providers", period, total) +// log.Printf("[%s] Daily provider reports created successfully", period) - // Step 3. Create provider-level daily report entries - reportDate := time.Now().UTC().Truncate(24 * time.Hour) - for _, p := range providers { - createReq := domain.CreateVirtualGameProviderReport{ - ProviderID: p.ProviderID, - ReportDate: reportDate, - TotalGamesPlayed: 0, - TotalBets: 0, - TotalPayouts: 0, - TotalPlayers: 0, - ReportType: period, // "daily" - } +// }); err != nil { +// log.Fatalf("Failed to schedule %s cron job: %v", period, err) +// } +// } - _, err := virtualGameOrchestrationService.CreateVirtualGameProviderReport(ctx, createReq) - if err != nil { - log.Printf("[%s] Failed to create report for provider %s: %v", period, p.ProviderID, err) - continue - } +// c.Start() +// log.Printf("Cron jobs started. Reports will be saved to: %s", outputDir) +// } - log.Printf("[%s] Created daily report row for provider: %s", period, p.ProviderID) - } +// func ProcessBetCashback(ctx context.Context, betService *betSvc.Service) { +// c := cron.New(cron.WithSeconds()) - log.Printf("[%s] Daily provider reports created successfully", period) +// schedule := []struct { +// spec string +// task func() +// }{ +// { +// spec: "0 0 0 * * *", // Daily at midnight +// task: func() { +// log.Println("process bet cashbacks...") +// if err := betService.ProcessBetCashback(ctx); err != nil { +// log.Printf("Failed to process bet cashbacks: %v", err) +// } else { +// log.Printf("Successfully processed bet cashbacks") +// } +// }, +// }, +// } - }); err != nil { - log.Fatalf("Failed to schedule %s cron job: %v", period, err) - } - } +// for _, job := range schedule { +// if _, err := c.AddFunc(job.spec, job.task); err != nil { +// log.Fatalf("Failed to schedule cron job: %v", err) +// } +// } - c.Start() - log.Printf("Cron jobs started. Reports will be saved to: %s", outputDir) -} +// c.Start() +// log.Println("Cron jobs started for bet cashbacks") +// } -func ProcessBetCashback(ctx context.Context, betService *betSvc.Service) { - c := cron.New(cron.WithSeconds()) +// func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger) { +// c := cron.New(cron.WithSeconds()) - schedule := []struct { - spec string - task func() - }{ - { - spec: "0 0 0 * * *", // Daily at midnight - task: func() { - log.Println("process bet cashbacks...") - if err := betService.ProcessBetCashback(ctx); err != nil { - log.Printf("Failed to process bet cashbacks: %v", err) - } else { - log.Printf("Successfully processed bet cashbacks") - } - }, - }, - } +// schedule := []struct { +// spec string +// task func() +// }{ +// { +// spec: "0 0 */2 * * *", // Every 2 hours +// task: func() { +// ctx := context.Background() - for _, job := range schedule { - if _, err := c.AddFunc(job.spec, job.task); err != nil { - log.Fatalf("Failed to schedule cron job: %v", err) - } - } +// // 1️⃣ Sports +// mongoLogger.Info("Began fetching and storing sports cron task") +// if err := enetPulseSvc.FetchAndStoreSports(ctx); err != nil { +// mongoLogger.Error("Failed to fetch and store sports", zap.Error(err)) +// } else { +// mongoLogger.Info("\n\n✅ Completed fetching and storing sports\n\n") +// } - c.Start() - log.Println("Cron jobs started for bet cashbacks") -} +// // 2️⃣ Tournament Templates +// mongoLogger.Info("Began fetching and storing tournament templates cron task") +// if err := enetPulseSvc.FetchAndStoreTournamentTemplates(ctx); err != nil { +// mongoLogger.Error("Failed to fetch and store tournament templates", zap.Error(err)) +// } else { +// mongoLogger.Info("\n\n✅ Completed fetching and storing tournament templates\n\n") +// } -func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger) { - c := cron.New(cron.WithSeconds()) +// // 3️⃣ Tournaments +// mongoLogger.Info("Began fetching and storing tournaments cron task") +// if err := enetPulseSvc.FetchAndStoreTournaments(ctx); err != nil { +// mongoLogger.Error("Failed to fetch and store tournaments", zap.Error(err)) +// } else { +// mongoLogger.Info("\n\n✅ Completed fetching and storing tournaments\n\n") +// } - schedule := []struct { - spec string - task func() - }{ - { - spec: "0 0 */2 * * *", // Every 2 hours - task: func() { - ctx := context.Background() +// // 4️⃣ Tournament Stages +// // mongoLogger.Info("Began fetching and storing tournament stages cron task") +// // if err := enetPulseSvc.FetchAndStoreTournamentStages(ctx); err != nil { +// // mongoLogger.Error("Failed to fetch and store tournament stages", zap.Error(err)) +// // } else { +// // mongoLogger.Info("✅ \n\nCompleted fetching and storing tournament stages\n\n") +// // } - // 1️⃣ Sports - mongoLogger.Info("Began fetching and storing sports cron task") - if err := enetPulseSvc.FetchAndStoreSports(ctx); err != nil { - mongoLogger.Error("Failed to fetch and store sports", zap.Error(err)) - } else { - mongoLogger.Info("\n\n✅ Completed fetching and storing sports\n\n") - } +// // // 5️⃣ Fixtures +// mongoLogger.Info("Began fetching and storing fixtures cron task") +// today := time.Now().Format("2006-01-02") +// if err := enetPulseSvc.FetchAndStoreFixtures(ctx, today); err != nil { +// mongoLogger.Error("Failed to fetch and store fixtures", zap.Error(err)) +// } else { +// mongoLogger.Info("\n\n✅ Completed fetching and storing fixtures\n\n") +// } - // 2️⃣ Tournament Templates - mongoLogger.Info("Began fetching and storing tournament templates cron task") - if err := enetPulseSvc.FetchAndStoreTournamentTemplates(ctx); err != nil { - mongoLogger.Error("Failed to fetch and store tournament templates", zap.Error(err)) - } else { - mongoLogger.Info("\n\n✅ Completed fetching and storing tournament templates\n\n") - } +// // 6️⃣ Results +// // mongoLogger.Info("Began fetching and storing results cron task") +// // if err := enetPulseSvc.FetchAndStoreResults(ctx); err != nil { +// // mongoLogger.Error("Failed to fetch and store results", zap.Error(err)) +// // } else { +// // mongoLogger.Info("\n\n✅ Completed fetching and storing results\n\n") +// // } - // 3️⃣ Tournaments - mongoLogger.Info("Began fetching and storing tournaments cron task") - if err := enetPulseSvc.FetchAndStoreTournaments(ctx); err != nil { - mongoLogger.Error("Failed to fetch and store tournaments", zap.Error(err)) - } else { - mongoLogger.Info("\n\n✅ Completed fetching and storing tournaments\n\n") - } +// // 7 Outcome Types +// mongoLogger.Info("Began fetching and storing outcome_types cron task") +// if err := enetPulseSvc.FetchAndStoreOutcomeTypes(ctx); err != nil { +// mongoLogger.Error("Failed to fetch and store outcome_types", zap.Error(err)) +// } else { +// mongoLogger.Info("\n\n✅ Completed fetching and storing outcome_types\n\n") +// } - // 4️⃣ Tournament Stages - // mongoLogger.Info("Began fetching and storing tournament stages cron task") - // if err := enetPulseSvc.FetchAndStoreTournamentStages(ctx); err != nil { - // mongoLogger.Error("Failed to fetch and store tournament stages", zap.Error(err)) - // } else { - // mongoLogger.Info("✅ \n\nCompleted fetching and storing tournament stages\n\n") - // } +// // 8 Preodds +// mongoLogger.Info("Began fetching and storing preodds cron task") +// if err := enetPulseSvc.FetchAndStorePreodds(ctx); err != nil { +// mongoLogger.Error("Failed to fetch and store preodds", zap.Error(err)) +// } else { +// mongoLogger.Info("\n\n✅ Completed fetching and storing preodds\n\n") +// } +// }, +// }, +// } - // // 5️⃣ Fixtures - mongoLogger.Info("Began fetching and storing fixtures cron task") - today := time.Now().Format("2006-01-02") - if err := enetPulseSvc.FetchAndStoreFixtures(ctx, today); err != nil { - mongoLogger.Error("Failed to fetch and store fixtures", zap.Error(err)) - } else { - mongoLogger.Info("\n\n✅ Completed fetching and storing fixtures\n\n") - } +// for _, job := range schedule { +// // Run immediately at startup +// job.task() - // 6️⃣ Results - // mongoLogger.Info("Began fetching and storing results cron task") - // if err := enetPulseSvc.FetchAndStoreResults(ctx); err != nil { - // mongoLogger.Error("Failed to fetch and store results", zap.Error(err)) - // } else { - // mongoLogger.Info("\n\n✅ Completed fetching and storing results\n\n") - // } +// // Schedule the task +// if _, err := c.AddFunc(job.spec, job.task); err != nil { +// mongoLogger.Error("Failed to schedule EnetPulse cron job", zap.Error(err)) +// } +// } - // 7 Outcome Types - mongoLogger.Info("Began fetching and storing outcome_types cron task") - if err := enetPulseSvc.FetchAndStoreOutcomeTypes(ctx); err != nil { - mongoLogger.Error("Failed to fetch and store outcome_types", zap.Error(err)) - } else { - mongoLogger.Info("\n\n✅ Completed fetching and storing outcome_types\n\n") - } - - // 8 Preodds - mongoLogger.Info("Began fetching and storing preodds cron task") - if err := enetPulseSvc.FetchAndStorePreodds(ctx); err != nil { - mongoLogger.Error("Failed to fetch and store preodds", zap.Error(err)) - } else { - mongoLogger.Info("\n\n✅ Completed fetching and storing preodds\n\n") - } - }, - }, - } - - for _, job := range schedule { - // Run immediately at startup - job.task() - - // Schedule the task - if _, err := c.AddFunc(job.spec, job.task); err != nil { - mongoLogger.Error("Failed to schedule EnetPulse cron job", zap.Error(err)) - } - } - - c.Start() - log.Println("EnetPulse cron jobs started for sports, tournament templates, tournaments, tournament stages, fixtures, and results") - mongoLogger.Info("EnetPulse cron jobs started for sports, tournament templates, tournaments, tournament stages, fixtures, and results") -} +// c.Start() +// log.Println("EnetPulse cron jobs started for sports, tournament templates, tournaments, tournament stages, fixtures, and results") +// mongoLogger.Info("EnetPulse cron jobs started for sports, tournament templates, tournaments, tournament stages, fixtures, and results") +// } diff --git a/internal/web_server/handlers/admin.go b/internal/web_server/handlers/admin.go index 036f96a..4578706 100644 --- a/internal/web_server/handlers/admin.go +++ b/internal/web_server/handlers/admin.go @@ -1,13 +1,13 @@ package handlers import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/services/authentication" + "Yimaru-Backend/internal/web_server/response" "fmt" "strconv" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) @@ -66,16 +66,16 @@ func (h *Handler) CreateAdmin(c *fiber.Ctx) error { Valid: false, } } else { - _, err := h.companySvc.GetCompanyByID(c.Context(), *req.CompanyID) - if err != nil { - h.mongoLoggerSvc.Error("invalid company ID for CreateAdmin", - zap.Int64("status_code", fiber.StatusInternalServerError), - zap.Int64("company_id", *req.CompanyID), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Company ID is invalid:"+err.Error()) - } + // _, err := h.companySvc.GetCompanyByID(c.Context(), *req.CompanyID) + // if err != nil { + // h.mongoLoggerSvc.Error("invalid company ID for CreateAdmin", + // zap.Int64("status_code", fiber.StatusInternalServerError), + // zap.Int64("company_id", *req.CompanyID), + // zap.Error(err), + // zap.Time("timestamp", time.Now()), + // ) + // return fiber.NewError(fiber.StatusInternalServerError, "Company ID is invalid:"+err.Error()) + // } companyID = domain.ValidInt64{ Value: *req.CompanyID, Valid: true, diff --git a/internal/web_server/handlers/alea_games.go b/internal/web_server/handlers/alea_games.go deleted file mode 100644 index 0fc8ea3..0000000 --- a/internal/web_server/handlers/alea_games.go +++ /dev/null @@ -1,70 +0,0 @@ -package handlers - -import ( - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" -) - -// LaunchAleaGame godoc -// @Summary Launch an Alea Play virtual game -// @Description Generates an authenticated launch URL for Alea Play virtual games -// @Tags Alea Virtual Games -// @Accept json -// @Produce json -// @Security BearerAuth -// @Param game_id query string true "Game identifier (e.g., 'aviator', 'plinko')" -// @Param currency query string false "Currency code (ISO 4217)" Enums(USD, EUR, GBP) default(USD) -// @Param mode query string false "Game mode" Enums(real, demo) default(real) -// @Success 200 {object} map[string]string{launch_url=string} "Returns authenticated game launch URL" -// @Router /api/v1/alea-games/launch [get] -func (h *Handler) LaunchAleaGame(c *fiber.Ctx) error { - userID := c.Locals("user_id").(int64) - gameID := c.Query("game_id") - currency := c.Query("currency", "USD") - mode := c.Query("mode", "real") // real or demo - - launchURL, err := h.aleaVirtualGameSvc.GenerateGameLaunchURL(c.Context(), userID, gameID, currency, mode) - if err != nil { - h.logger.Error("failed to generate Alea launch URL", "error", err) - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "failed to launch game", - }) - } - - return c.JSON(fiber.Map{ - "launch_url": launchURL, - "message": "Game launched successfully", - }) -} - -// HandleAleaCallback godoc -// @Summary Process Alea Play game callback -// @Description Handles webhook callbacks from Alea Play virtual games for bet settlement -// @Tags Alea Virtual Games -// @Accept json -// @Produce json -// @Param callback body domain.AleaPlayCallback true "Callback payload" -// @Success 200 {object} map[string]string{status=string} "Callback processed successfully" -// @Router /api/v1/webhooks/alea [post] -func (h *Handler) HandleAleaCallback(c *fiber.Ctx) error { - var cb domain.AleaPlayCallback - if err := c.BodyParser(&cb); err != nil { - h.logger.Error("invalid Alea callback format", "error", err) - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "error": "invalid callback format", - }) - } - - if err := h.aleaVirtualGameSvc.HandleCallback(c.Context(), &cb); err != nil { - h.logger.Error("failed to process Alea callback", - "transactionID", cb.TransactionID, - "error", err) - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "failed to process callback", - }) - } - - return c.JSON(fiber.Map{ - "status": "processed", - }) -} diff --git a/internal/web_server/handlers/arifpay.go b/internal/web_server/handlers/arifpay.go index 51e248f..18564d2 100644 --- a/internal/web_server/handlers/arifpay.go +++ b/internal/web_server/handlers/arifpay.go @@ -1,341 +1,342 @@ package handlers -import ( - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" -) +// import ( +// "Yimaru-Backend/internal/domain" -// CreateCheckoutSessionHandler initializes a checkout session with Arifpay. -// -// @Summary Create Arifpay Checkout Session -// @Description Creates a payment session using Arifpay and returns a redirect URL. -// @Tags Arifpay -// @Accept json -// @Produce json -// @Param request body domain.CheckoutSessionClientRequest true "Checkout session request payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/arifpay/checkout [post] -func (h *Handler) CreateCheckoutSessionHandler(c *fiber.Ctx) error { +// "github.com/gofiber/fiber/v2" +// ) - userId, ok := c.Locals("user_id").(int64) - if !ok { - return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ - Error: "missing user id", - Message: "Unauthorized", - }) - } +// // CreateCheckoutSessionHandler initializes a checkout session with Arifpay. +// // +// // @Summary Create Arifpay Checkout Session +// // @Description Creates a payment session using Arifpay and returns a redirect URL. +// // @Tags Arifpay +// // @Accept json +// // @Produce json +// // @Param request body domain.CheckoutSessionClientRequest true "Checkout session request payload" +// // @Success 200 {object} domain.Response +// // @Failure 400 {object} domain.ErrorResponse +// // @Failure 500 {object} domain.ErrorResponse +// // @Router /api/v1/arifpay/checkout [post] +// func (h *Handler) CreateCheckoutSessionHandler(c *fiber.Ctx) error { - var req domain.CheckoutSessionClientRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to process your request", - }) - } +// userId, ok := c.Locals("user_id").(int64) +// if !ok { +// return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ +// Error: "missing user id", +// Message: "Unauthorized", +// }) +// } - data, err := h.arifpaySvc.CreateCheckoutSession(req, true, userId) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to process your request", - }) - } +// var req domain.CheckoutSessionClientRequest +// if err := c.BodyParser(&req); err != nil { +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Failed to process your request", +// }) +// } - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Checkout session created successfully", - Data: data, - Success: true, - StatusCode: fiber.StatusOK, - }) -} +// data, err := h.arifpaySvc.CreateCheckoutSession(req, true, userId) +// if err != nil { +// return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Failed to process your request", +// }) +// } -// CancelCheckoutSessionHandler cancels an existing Arifpay checkout session. -// -// @Summary Cancel Arifpay Checkout Session -// @Description Cancels a payment session using Arifpay before completion. -// @Tags Arifpay -// @Accept json -// @Produce json -// @Param sessionId path string true "Checkout session ID" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/arifpay/checkout/cancel/{sessionId} [post] -func (h *Handler) CancelCheckoutSessionHandler(c *fiber.Ctx) error { - sessionID := c.Params("sessionId") - if sessionID == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: "missing session ID", - Message: "Session ID is required", - }) - } +// return c.Status(fiber.StatusOK).JSON(domain.Response{ +// Message: "Checkout session created successfully", +// Data: data, +// Success: true, +// StatusCode: fiber.StatusOK, +// }) +// } - data, err := h.arifpaySvc.CancelCheckoutSession(c.Context(), sessionID) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to cancel checkout session", - }) - } +// // CancelCheckoutSessionHandler cancels an existing Arifpay checkout session. +// // +// // @Summary Cancel Arifpay Checkout Session +// // @Description Cancels a payment session using Arifpay before completion. +// // @Tags Arifpay +// // @Accept json +// // @Produce json +// // @Param sessionId path string true "Checkout session ID" +// // @Success 200 {object} domain.Response +// // @Failure 400 {object} domain.ErrorResponse +// // @Failure 500 {object} domain.ErrorResponse +// // @Router /api/v1/arifpay/checkout/cancel/{sessionId} [post] +// func (h *Handler) CancelCheckoutSessionHandler(c *fiber.Ctx) error { +// sessionID := c.Params("sessionId") +// if sessionID == "" { +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Error: "missing session ID", +// Message: "Session ID is required", +// }) +// } - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Checkout session canceled successfully", - Data: data, - Success: true, - StatusCode: fiber.StatusOK, - }) -} +// data, err := h.arifpaySvc.CancelCheckoutSession(c.Context(), sessionID) +// if err != nil { +// return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Failed to cancel checkout session", +// }) +// } -// HandleWebhook processes Arifpay webhook notifications. -// -// @Summary Handle Arifpay C2B Webhook -// @Description Handles webhook notifications from Arifpay for C2B transfers and updates transfer + wallet status. -// @Tags Arifpay -// @Accept json -// @Produce json -// @Param request body domain.WebhookRequest true "Arifpay webhook payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/arifpay/c2b-webhook [post] -func (h *Handler) HandleArifpayC2BWebhook(c *fiber.Ctx) error { - var req domain.WebhookRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Invalid webhook payload", - }) - } +// return c.Status(fiber.StatusOK).JSON(domain.Response{ +// Message: "Checkout session canceled successfully", +// Data: data, +// Success: true, +// StatusCode: fiber.StatusOK, +// }) +// } - // 🚨 Decide how to get userId: - // If you get it from auth context/middleware, extract it here. - // For now, let's assume userId comes from your auth claims: - // userId, ok := c.Locals("user_id").(int64) - // if !ok { - // return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ - // Error: "missing user id", - // Message: "Unauthorized", - // }) - // } +// // HandleWebhook processes Arifpay webhook notifications. +// // +// // @Summary Handle Arifpay C2B Webhook +// // @Description Handles webhook notifications from Arifpay for C2B transfers and updates transfer + wallet status. +// // @Tags Arifpay +// // @Accept json +// // @Produce json +// // @Param request body domain.WebhookRequest true "Arifpay webhook payload" +// // @Success 200 {object} domain.Response +// // @Failure 400 {object} domain.ErrorResponse +// // @Failure 500 {object} domain.ErrorResponse +// // @Router /api/v1/arifpay/c2b-webhook [post] +// func (h *Handler) HandleArifpayC2BWebhook(c *fiber.Ctx) error { +// var req domain.WebhookRequest +// if err := c.BodyParser(&req); err != nil { +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Invalid webhook payload", +// }) +// } - err := h.arifpaySvc.ProcessWebhook(c.Context(), req, true) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to process webhook", - }) - } +// // 🚨 Decide how to get userId: +// // If you get it from auth context/middleware, extract it here. +// // For now, let's assume userId comes from your auth claims: +// // userId, ok := c.Locals("user_id").(int64) +// // if !ok { +// // return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ +// // Error: "missing user id", +// // Message: "Unauthorized", +// // }) +// // } - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Webhook processed successfully", - Success: true, - StatusCode: fiber.StatusOK, - }) -} +// err := h.arifpaySvc.ProcessWebhook(c.Context(), req, true) +// if err != nil { +// return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Failed to process webhook", +// }) +// } -// HandleWebhook processes Arifpay webhook notifications. -// -// @Summary Handle Arifpay B2C Webhook -// @Description Handles webhook notifications from Arifpay for B2C transfers and updates transfer + wallet status. -// @Tags Arifpay -// @Accept json -// @Produce json -// @Param request body domain.WebhookRequest true "Arifpay webhook payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/arifpay/b2c-webhook [post] -func (h *Handler) HandleArifpayB2CWebhook(c *fiber.Ctx) error { - var req domain.WebhookRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Invalid webhook payload", - }) - } +// return c.Status(fiber.StatusOK).JSON(domain.Response{ +// Message: "Webhook processed successfully", +// Success: true, +// StatusCode: fiber.StatusOK, +// }) +// } - // 🚨 Decide how to get userId: - // If you get it from auth context/middleware, extract it here. - // For now, let's assume userId comes from your auth claims: - // userId, ok := c.Locals("user_id").(int64) - // if !ok { - // return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ - // Error: "missing user id", - // Message: "Unauthorized", - // }) - // } +// // HandleWebhook processes Arifpay webhook notifications. +// // +// // @Summary Handle Arifpay B2C Webhook +// // @Description Handles webhook notifications from Arifpay for B2C transfers and updates transfer + wallet status. +// // @Tags Arifpay +// // @Accept json +// // @Produce json +// // @Param request body domain.WebhookRequest true "Arifpay webhook payload" +// // @Success 200 {object} domain.Response +// // @Failure 400 {object} domain.ErrorResponse +// // @Failure 500 {object} domain.ErrorResponse +// // @Router /api/v1/arifpay/b2c-webhook [post] +// func (h *Handler) HandleArifpayB2CWebhook(c *fiber.Ctx) error { +// var req domain.WebhookRequest +// if err := c.BodyParser(&req); err != nil { +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Invalid webhook payload", +// }) +// } - err := h.arifpaySvc.ProcessWebhook(c.Context(), req, false) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to process webhook", - }) - } +// // 🚨 Decide how to get userId: +// // If you get it from auth context/middleware, extract it here. +// // For now, let's assume userId comes from your auth claims: +// // userId, ok := c.Locals("user_id").(int64) +// // if !ok { +// // return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ +// // Error: "missing user id", +// // Message: "Unauthorized", +// // }) +// // } - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Webhook processed successfully", - Success: true, - StatusCode: fiber.StatusOK, - }) -} +// err := h.arifpaySvc.ProcessWebhook(c.Context(), req, false) +// if err != nil { +// return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Failed to process webhook", +// }) +// } -// ArifpayVerifyByTransactionIDHandler godoc -// @Summary Verify Arifpay Transaction -// @Description Verifies a transaction using transaction ID and payment type -// @Tags Arifpay -// @Accept json -// @Produce json -// @Param request body domain.ArifpayVerifyByTransactionIDRequest true "Transaction verification payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/arifpay/transaction-id/verify-transaction [post] -func (h *Handler) ArifpayVerifyByTransactionIDHandler(c *fiber.Ctx) error { - var req domain.ArifpayVerifyByTransactionIDRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to parse request body", - }) - } +// return c.Status(fiber.StatusOK).JSON(domain.Response{ +// Message: "Webhook processed successfully", +// Success: true, +// StatusCode: fiber.StatusOK, +// }) +// } - resp, err := h.arifpaySvc.VerifyTransactionByTransactionID(c.Context(), req) - if err != nil { - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to verify transaction", - }) - } +// // ArifpayVerifyByTransactionIDHandler godoc +// // @Summary Verify Arifpay Transaction +// // @Description Verifies a transaction using transaction ID and payment type +// // @Tags Arifpay +// // @Accept json +// // @Produce json +// // @Param request body domain.ArifpayVerifyByTransactionIDRequest true "Transaction verification payload" +// // @Success 200 {object} domain.Response +// // @Failure 400 {object} domain.ErrorResponse +// // @Failure 502 {object} domain.ErrorResponse +// // @Router /api/v1/arifpay/transaction-id/verify-transaction [post] +// func (h *Handler) ArifpayVerifyByTransactionIDHandler(c *fiber.Ctx) error { +// var req domain.ArifpayVerifyByTransactionIDRequest +// if err := c.BodyParser(&req); err != nil { +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Failed to parse request body", +// }) +// } - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Transaction verified successfully", - Data: resp, - Success: true, - StatusCode: fiber.StatusOK, - }) -} +// resp, err := h.arifpaySvc.VerifyTransactionByTransactionID(c.Context(), req) +// if err != nil { +// return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Failed to verify transaction", +// }) +// } -// ArifpayVerifyBySessionIDHandler godoc -// @Summary Verify Arifpay Transaction by Session ID -// @Description Verifies an Arifpay transaction using a session ID -// @Tags Arifpay -// @Accept json -// @Produce json -// @Param session_id query string true "Arifpay Session ID" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/arifpay/session-id/verify-transaction/{session_id} [get] -func (h *Handler) ArifpayVerifyBySessionIDHandler(c *fiber.Ctx) error { - sessionID := c.Query("session_id") - if sessionID == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: "missing session_id", - Message: "session_id query parameter is required", - }) - } +// return c.Status(fiber.StatusOK).JSON(domain.Response{ +// Message: "Transaction verified successfully", +// Data: resp, +// Success: true, +// StatusCode: fiber.StatusOK, +// }) +// } - resp, err := h.arifpaySvc.VerifyTransactionBySessionID(c.Context(), sessionID) - if err != nil { - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to verify transaction", - }) - } +// // ArifpayVerifyBySessionIDHandler godoc +// // @Summary Verify Arifpay Transaction by Session ID +// // @Description Verifies an Arifpay transaction using a session ID +// // @Tags Arifpay +// // @Accept json +// // @Produce json +// // @Param session_id query string true "Arifpay Session ID" +// // @Success 200 {object} domain.Response +// // @Failure 400 {object} domain.ErrorResponse +// // @Failure 502 {object} domain.ErrorResponse +// // @Router /api/v1/arifpay/session-id/verify-transaction/{session_id} [get] +// func (h *Handler) ArifpayVerifyBySessionIDHandler(c *fiber.Ctx) error { +// sessionID := c.Query("session_id") +// if sessionID == "" { +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Error: "missing session_id", +// Message: "session_id query parameter is required", +// }) +// } - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Transaction verified successfully", - Data: resp, - Success: true, - StatusCode: fiber.StatusOK, - }) -} +// resp, err := h.arifpaySvc.VerifyTransactionBySessionID(c.Context(), sessionID) +// if err != nil { +// return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ +// Error: err.Error(), +// Message: "Failed to verify transaction", +// }) +// } -// ExecuteTransfer handles B2C transfers via Telebirr, CBE, or MPESA. -// -// @Summary Execute B2C Transfer -// @Description Initiates a B2C transfer using Telebirr, CBE, or MPESA depending on the "type" query parameter -// @Tags Arifpay -// @Accept json -// @Produce json -// @Param type query string true "Transfer type (telebirr, cbe, mpesa)" -// @Param request body domain.CheckoutSessionClientRequest true "Transfer request payload" -// @Success 200 {object} map[string]string "message: transfer executed successfully" -// @Failure 400 {object} map[string]string "error: invalid request or unsupported transfer type" -// @Failure 500 {object} map[string]string "error: internal server error" -// @Router /api/v1/arifpay/b2c/transfer [post] -func (h *Handler) ExecuteArifpayB2CTransfer(c *fiber.Ctx) error { - transferType := c.Query("type") - if transferType == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to process your withdrawal request", - Error: "missing query parameter: type (telebirr, cbe, mpesa)", - }) - } +// return c.Status(fiber.StatusOK).JSON(domain.Response{ +// Message: "Transaction verified successfully", +// Data: resp, +// Success: true, +// StatusCode: fiber.StatusOK, +// }) +// } - userId, ok := c.Locals("user_id").(int64) - if !ok { - return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ - Error: "missing user id", - Message: "Unauthorized", - }) - } +// // ExecuteTransfer handles B2C transfers via Telebirr, CBE, or MPESA. +// // +// // @Summary Execute B2C Transfer +// // @Description Initiates a B2C transfer using Telebirr, CBE, or MPESA depending on the "type" query parameter +// // @Tags Arifpay +// // @Accept json +// // @Produce json +// // @Param type query string true "Transfer type (telebirr, cbe, mpesa)" +// // @Param request body domain.CheckoutSessionClientRequest true "Transfer request payload" +// // @Success 200 {object} map[string]string "message: transfer executed successfully" +// // @Failure 400 {object} map[string]string "error: invalid request or unsupported transfer type" +// // @Failure 500 {object} map[string]string "error: internal server error" +// // @Router /api/v1/arifpay/b2c/transfer [post] +// func (h *Handler) ExecuteArifpayB2CTransfer(c *fiber.Ctx) error { +// transferType := c.Query("type") +// if transferType == "" { +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Message: "Failed to process your withdrawal request", +// Error: "missing query parameter: type (telebirr, cbe, mpesa)", +// }) +// } - var req domain.CheckoutSessionClientRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to process your withdrawal request", - Error: "invalid request body", - }) - } +// userId, ok := c.Locals("user_id").(int64) +// if !ok { +// return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ +// Error: "missing user id", +// Message: "Unauthorized", +// }) +// } - var err error - switch transferType { - case "telebirr": - err = h.arifpaySvc.ExecuteTelebirrB2CTransfer(c.Context(), req, userId) - case "cbe": - err = h.arifpaySvc.ExecuteCBEB2CTransfer(c.Context(), req, userId) - case "mpesa": - err = h.arifpaySvc.ExecuteMPesaB2CTransfer(c.Context(), req, userId) - default: - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to process your withdrawal request", - Error: "unsupported transfer type, must be one of: telebirr, cbe, mpesa", - }) - } +// var req domain.CheckoutSessionClientRequest +// if err := c.BodyParser(&req); err != nil { +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Message: "Failed to process your withdrawal request", +// Error: "invalid request body", +// }) +// } - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to process your withdrawal request", - Error: err.Error(), - }) - } +// var err error +// switch transferType { +// case "telebirr": +// err = h.arifpaySvc.ExecuteTelebirrB2CTransfer(c.Context(), req, userId) +// case "cbe": +// err = h.arifpaySvc.ExecuteCBEB2CTransfer(c.Context(), req, userId) +// case "mpesa": +// err = h.arifpaySvc.ExecuteMPesaB2CTransfer(c.Context(), req, userId) +// default: +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Message: "Failed to process your withdrawal request", +// Error: "unsupported transfer type, must be one of: telebirr, cbe, mpesa", +// }) +// } - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Withdrawal process initiated successfully", - Success: true, - StatusCode: fiber.StatusOK, - }) -} +// if err != nil { +// return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ +// Message: "Failed to process your withdrawal request", +// Error: err.Error(), +// }) +// } -// GetPaymentMethodsHandler returns the list of all Arifpay payment methods -// -// @Summary List Arifpay Payment Methods -// @Description Returns all payment method IDs and names for Arifpay -// @Tags Arifpay -// @Produce json -// @Success 200 {object} []domain.ARIFPAYPaymentMethod -// @Router /api/v1/arifpay/payment-methods [get] -func (h *Handler) GetArifpayPaymentMethodsHandler(c *fiber.Ctx) error { - methods := h.arifpaySvc.GetPaymentMethodsMapping() +// return c.Status(fiber.StatusOK).JSON(domain.Response{ +// Message: "Withdrawal process initiated successfully", +// Success: true, +// StatusCode: fiber.StatusOK, +// }) +// } - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Success: true, - Message: "Arifpay payment methods fetched successfully", - Data: methods, - StatusCode: fiber.StatusOK, - }) -} +// // GetPaymentMethodsHandler returns the list of all Arifpay payment methods +// // +// // @Summary List Arifpay Payment Methods +// // @Description Returns all payment method IDs and names for Arifpay +// // @Tags Arifpay +// // @Produce json +// // @Success 200 {object} []domain.ARIFPAYPaymentMethod +// // @Router /api/v1/arifpay/payment-methods [get] +// func (h *Handler) GetArifpayPaymentMethodsHandler(c *fiber.Ctx) error { +// methods := h.arifpaySvc.GetPaymentMethodsMapping() + +// return c.Status(fiber.StatusOK).JSON(domain.Response{ +// Success: true, +// Message: "Arifpay payment methods fetched successfully", +// Data: methods, +// StatusCode: fiber.StatusOK, +// }) +// } diff --git a/internal/web_server/handlers/atlas.go b/internal/web_server/handlers/atlas.go deleted file mode 100644 index 1b44b69..0000000 --- a/internal/web_server/handlers/atlas.go +++ /dev/null @@ -1,452 +0,0 @@ -package handlers - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "log" - "strings" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// GetAtlasVGames godoc -// @Summary List Atlas virtual games -// @Description Retrieves available Atlas virtual games from the provider -// @Tags Virtual Games - Atlas -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.AtlasGameEntity} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/atlas/games [get] -func (h *Handler) GetAtlasVGames(c *fiber.Ctx) error { - // Call the service - games, err := h.veliVirtualGameSvc.GetAtlasVGames(c.Context()) - if err != nil { - log.Println("GetAtlasVGames error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch Atlas virtual games", - Error: err.Error(), - }) - } - - // Return the list of games - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Atlas virtual games retrieved successfully", - Data: games, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// InitAtlasGame godoc -// @Summary Start an Atlas virtual game session -// @Description Initializes a game session for the given player using Atlas virtual game provider -// @Tags Virtual Games - Atlas -// @Accept json -// @Produce json -// @Param request body domain.AtlasGameInitRequest true "Start game input" -// @Success 200 {object} domain.Response{data=domain.AtlasGameInitResponse} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/atlas/init-game [post] -func (h *Handler) InitAtlasGame(c *fiber.Ctx) error { - // 1️⃣ Retrieve user ID from context - userId, ok := c.Locals("user_id").(int64) - if !ok { - return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ - Error: "missing user id", - Message: "Unauthorized", - }) - } - - // 2️⃣ Parse request body - var req domain.AtlasGameInitRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - // 3️⃣ Attach user ID to request - req.PlayerID = fmt.Sprintf("%d", userId) - - // 4️⃣ Set defaults if not provided - if req.Language == "" { - req.Language = "en" - } - if req.Currency == "" { - req.Currency = "USD" - } - - // 5️⃣ Call the Atlas service - res, err := h.atlasVirtualGameSvc.InitGame(context.Background(), req) - if err != nil { - log.Println("InitAtlasGame error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to initialize Atlas game", - Error: err.Error(), - }) - } - - // 6️⃣ Update provider report: increment total_games_played - go func() { - ctx := context.Background() - reportDate := time.Now().Truncate(24 * time.Hour) - reportType := "daily" - providerID := "atlas" // all Atlas games belong to this provider - - err := h.orchestrationSvc.UpdateVirtualGameProviderReportByDate( - ctx, - providerID, - reportDate, - reportType, - 1, // increment total_games_played by 1 - 0, // total_bets (no change) - 0, // total_payouts (no change) - 1, // total_players (no change) - ) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update total_games_played for Atlas game", - zap.String("provider_id", providerID), - zap.Error(err), - ) - } - }() - - // 7️⃣ Return response to user - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Game initialized successfully", - Data: res, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// AtlasGetUserDataCallback godoc -// @Summary Atlas Get User Data callback -// @Description Callback endpoint for Atlas game server to fetch player balance -// @Tags Virtual Games - Atlas -// @Accept json -// @Produce json -// @Param request body domain.AtlasGetUserDataRequest true "Get user data input" -// @Success 200 {object} domain.AtlasGetUserDataResponse -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /account [post] -func (h *Handler) AtlasGetUserDataCallback(c *fiber.Ctx) error { - var req domain.AtlasGetUserDataRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - // Optional: validate casino_id matches your configured Atlas casino - if req.CasinoID != h.Cfg.Atlas.CasinoID { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid casino_id", - Error: "unauthorized request", - }) - } - - // Call service to get player data - res, err := h.atlasVirtualGameSvc.GetUserData(c.Context(), req) - if err != nil { - log.Println("AtlasGetUserDataCallback error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch user data", - Error: err.Error(), - }) - } - - // Return Atlas expected response - return c.JSON(res) -} - -// HandleAtlasBetWin godoc -// @Summary Atlas BetWin callback -// @Description Processes a Bet and Win request from Atlas provider -// @Tags Virtual Games - Atlas -// @Accept json -// @Produce json -// @Param request body domain.AtlasBetWinRequest true "Atlas BetWin input" -// @Success 200 {object} domain.AtlasBetWinResponse -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /betwin [post] -func (h *Handler) HandleAtlasBetWin(c *fiber.Ctx) error { - body := c.Body() - if len(body) == 0 { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Empty request body", - Error: "Request body cannot be empty", - }) - } - - var req domain.AtlasBetWinRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid Atlas BetWin request", - Error: err.Error(), - }) - } - - res, err := h.atlasVirtualGameSvc.ProcessBetWin(c.Context(), req) - if err != nil { - // Handle known errors specifically - code := fiber.StatusInternalServerError - errMsg := err.Error() - - switch { - case errors.Is(err, domain.ErrInsufficientBalance): - code = fiber.StatusBadRequest - errMsg = "INSUFFICIENT_BALANCE" - case strings.Contains(err.Error(), "invalid casino_id"): - code = fiber.StatusBadRequest - case strings.Contains(err.Error(), "invalid playerID"): - code = fiber.StatusBadRequest - } - - return c.Status(code).JSON(domain.ErrorResponse{ - Message: "Failed to process Atlas BetWin", - Error: errMsg, - }) - } - - return c.Status(fiber.StatusOK).JSON(res) -} - -// HandleRoundResult godoc -// @Summary Atlas Round Result callback -// @Description Processes a round result from Atlas or other providers -// @Tags Virtual Games - Atlas -// @Accept json -// @Produce json -// @Param request body domain.RoundResultRequest true "Round result input" -// @Success 200 {object} domain.RoundResultResponse -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /result [post] -func (h *Handler) HandleRoundResult(c *fiber.Ctx) error { - body := c.Body() - if len(body) == 0 { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Empty request body", - Error: "Request body cannot be empty", - }) - } - - var req domain.RoundResultRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid RoundResult request", - Error: err.Error(), - }) - } - - res, err := h.atlasVirtualGameSvc.ProcessRoundResult(c.Context(), req) - if err != nil { - code := fiber.StatusInternalServerError - errMsg := err.Error() - - // Validation errors - if strings.Contains(err.Error(), "missing player_id") || strings.Contains(err.Error(), "missing transaction_id") { - code = fiber.StatusBadRequest - } - - return c.Status(code).JSON(domain.ErrorResponse{ - Message: "Failed to process round result", - Error: errMsg, - }) - } - - return c.Status(fiber.StatusOK).JSON(res) -} - -// HandleRollback godoc -// @Summary Atlas Rollback callback -// @Description Processes a rollback request from Atlas or other providers -// @Tags Virtual Games - Atlas -// @Accept json -// @Produce json -// @Param request body domain.RollbackRequest true "Rollback request input" -// @Success 200 {object} domain.RollbackResponse -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /rollback [post] -func (h *Handler) HandleRollback(c *fiber.Ctx) error { - body := c.Body() - if len(body) == 0 { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Empty request body", - Error: "Request body cannot be empty", - }) - } - - var req domain.RollbackRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid Rollback request", - Error: err.Error(), - }) - } - - res, err := h.atlasVirtualGameSvc.ProcessRollBack(c.Context(), req) - if err != nil { - code := fiber.StatusInternalServerError - errMsg := err.Error() - - // Validation errors - if strings.Contains(err.Error(), "missing player_id") || strings.Contains(err.Error(), "missing transaction_id") { - code = fiber.StatusBadRequest - } - - return c.Status(code).JSON(domain.ErrorResponse{ - Message: "Failed to process rollback", - Error: errMsg, - }) - } - - return c.Status(fiber.StatusOK).JSON(res) -} - -// CreateFreeSpin godoc -// @Summary Create free spins for a player -// @Description Sends a request to Atlas to create free spins/bets for a given player -// @Tags Virtual Games - Atlas -// @Accept json -// @Produce json -// @Param request body domain.FreeSpinRequest true "Free spin input" -// @Success 200 {object} domain.Response{data=domain.FreeSpinResponse} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/atlas/freespin [post] -func (h *Handler) CreateFreeSpin(c *fiber.Ctx) error { - // Get the authenticated user ID - userId, ok := c.Locals("user_id").(int64) - if !ok { - return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ - Error: "missing user id", - Message: "Unauthorized", - }) - } - - var req domain.FreeSpinRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - // Attach player ID from authenticated user - req.PlayerID = fmt.Sprintf("%d", userId) - - res, err := h.atlasVirtualGameSvc.CreateFreeSpin(c.Context(), req) - if err != nil { - log.Println("CreateFreeSpin error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to create free spins", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Free spins created successfully", - Data: res, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// FreeSpinResultCallback godoc -// @Summary Free Spin/Bet result callback -// @Description Handles the result of a free spin/bet from the game server -// @Tags Virtual Games - Atlas -// @Accept json -// @Produce json -// @Param request body domain.FreeSpinResultRequest true "Free spin result input" -// @Success 200 {object} domain.FreeSpinResultResponse -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /freespin [post] -func (h *Handler) FreeSpinResultCallback(c *fiber.Ctx) error { - // Read raw request body - body := c.Body() - if len(body) == 0 { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Empty request body", - Error: "Request body cannot be empty", - }) - } - - // Unmarshal into FreeSpinResultRequest - var req domain.FreeSpinResultRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid free spin result request", - Error: err.Error(), - }) - } - - // Process the free spin result - res, err := h.atlasVirtualGameSvc.ProcessFreeSpinResult(c.Context(), req) - if err != nil { - log.Println("FreeSpinResultCallback error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to process free spin result", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(res) -} - -// JackpotCallback godoc -// @Summary Jackpot result callback -// @Description Handles the jackpot result from the game server -// @Tags Virtual Games - Atlas -// @Accept json -// @Produce json -// @Param request body domain.JackpotRequest true "Jackpot result input" -// @Success 200 {object} domain.JackpotResponse -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /jackpot [post] -func (h *Handler) JackpotCallback(c *fiber.Ctx) error { - // Read raw request body - body := c.Body() - if len(body) == 0 { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Empty request body", - Error: "Request body cannot be empty", - }) - } - - // Unmarshal into JackpotRequest - var req domain.JackpotRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid jackpot request", - Error: err.Error(), - }) - } - - // Process the jackpot - res, err := h.atlasVirtualGameSvc.ProcessJackPot(c.Context(), req) - if err != nil { - log.Println("JackpotCallback error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to process jackpot", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(res) -} diff --git a/internal/web_server/handlers/auth_handler.go b/internal/web_server/handlers/auth_handler.go index 183e536..02f7943 100644 --- a/internal/web_server/handlers/auth_handler.go +++ b/internal/web_server/handlers/auth_handler.go @@ -1,14 +1,14 @@ package handlers import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/services/authentication" + jwtutil "Yimaru-Backend/internal/web_server/jwt" + "Yimaru-Backend/internal/web_server/response" "errors" "fmt" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) @@ -161,7 +161,7 @@ type LoginAdminRes struct { // @Accept json // @Produce json // @Param login body loginAdminReq true "Login admin" -// @Success 200 {object} loginAdminRes +// @Success 200 {object} LoginAdminRes // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse @@ -169,7 +169,7 @@ type LoginAdminRes struct { func (h *Handler) LoginAdmin(c *fiber.Ctx) error { companyID := c.Locals("company_id").(domain.ValidInt64) if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") + h.BadRequestLogger().Error("invalid company id") return fiber.NewError(fiber.StatusBadRequest, "invalid company id") } var req loginAdminReq @@ -267,7 +267,7 @@ func (h *Handler) LoginAdmin(c *fiber.Ctx) error { // @Accept json // @Produce json // @Param login body loginAdminReq true "Login super-admin" -// @Success 200 {object} loginAdminRes +// @Success 200 {object} LoginAdminRes // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse diff --git a/internal/web_server/handlers/bet_handler.go b/internal/web_server/handlers/bet_handler.go deleted file mode 100644 index 4550395..0000000 --- a/internal/web_server/handlers/bet_handler.go +++ /dev/null @@ -1,1147 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// CreateBet godoc -// @Summary Create a bet -// @Description Creates a bet -// @Tags bet -// @Accept json -// @Produce json -// @Param createBet body domain.CreateBetReq true "Creates bet" -// @Success 200 {object} domain.BetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/sport/bet [post] -func (h *Handler) CreateBet(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - userID := c.Locals("user_id").(int64) - role := c.Locals("role").(domain.Role) - - var req domain.CreateBetReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Error("Failed to parse CreateBet request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error()) - } - - res, err := h.CreateBetInternal(c, req, userID, role, companyID.Value) - if err != nil { - switch err { - case bet.ErrEventHasBeenRemoved, bet.ErrEventHasNotEnded, bet.ErrRawOddInvalid, bet.ErrTotalBalanceNotEnough: - h.mongoLoggerSvc.Info("PlaceBet failed", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Int64("userID", userID), - zap.Int64("companyID", companyID.Value), - zap.String("role", string(role)), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - h.mongoLoggerSvc.Error("Failed to create bet", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Int64("user_id", userID), - zap.String("role", string(role)), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create bet:"+err.Error()) - } - - h.mongoLoggerSvc.Info("Bet created successfully", - zap.Int("status_code", fiber.StatusOK), - zap.Int64("user_id", userID), - zap.Time("timestamp", time.Now()), - ) - - return response.WriteJSON(c, fiber.StatusOK, "Bet Created", res, nil) -} - -// CreateBetWithFastCode godoc -// @Summary Create a bet with fast code -// @Description Creates a bet with fast code -// @Tags bet -// @Accept json -// @Produce json -// @Param createBetWithFastCode body domain.CreateBetWithFastCodeReq true "Creates bet" -// @Success 200 {object} domain.BetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/sport/bet/fastcode [post] -func (h *Handler) CreateBetWithFastCode(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - userID := c.Locals("user_id").(int64) - role := c.Locals("role").(domain.Role) - - var req domain.CreateBetWithFastCodeReq - - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Error("Failed to parse CreateBet request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error()) - } - - bet, err := h.betSvc.GetBetByFastCode(c.Context(), req.FastCode) - if err != nil { - h.mongoLoggerSvc.Info("failed to get bet with fast code", - zap.String("fast_code", req.FastCode), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "failed to get bet with fast code:"+err.Error()) - } - - if bet.UserID == userID { - h.mongoLoggerSvc.Info("User cannot refer himself", - zap.Int64("bet_id", bet.ID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "User cannot use his own referral code") - } - outcomes, err := h.betSvc.GetBetOutcomeByBetID(c.Context(), bet.ID) - if err != nil { - h.mongoLoggerSvc.Info("failed to get BetOutcomes by BetID", - zap.Int64("bet_id", bet.ID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "failed to get BetOutcomes by BetID:"+err.Error()) - } - - bet_outcomes := []domain.CreateBetOutcomeReq{} - for _, outcome := range outcomes { - bet_outcomes = append(bet_outcomes, domain.CreateBetOutcomeReq{ - EventID: outcome.EventID, - OddID: outcome.OddID, - MarketID: outcome.MarketID, - }) - } - - // This can be for both online and offline bets - // If bet is an online bet (if the customer role creates the bet on their own) - // then the branchID is null - newReq := domain.CreateBetReq{ - Amount: req.Amount, - Outcomes: bet_outcomes, - BranchID: req.BranchID, - } - - res, err := h.CreateBetInternal(c, newReq, userID, role, companyID.Value) - if err != nil { - h.mongoLoggerSvc.Error("Failed to create bet", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Int64("user_id", userID), - zap.String("role", string(role)), - zap.Any("newReq", newReq), - zap.Time("timestamp", time.Now()), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create bet:"+err.Error()) - } - - wallet, _ := h.walletSvc.GetCustomerWallet(c.Context(), bet.UserID) - - // amount added for fast code owner can be fetched from settings in db - settingList, _ := h.settingSvc.GetOverrideSettingsList(c.Context(), companyID.Value) - amount := settingList.AmountForBetReferral - - _, err = h.walletSvc.AddToWallet(c.Context(), wallet.StaticID, amount, domain.ValidInt64{}, - domain.TRANSFER_DIRECT, domain.PaymentDetails{}, fmt.Sprintf("Added %v to static wallet by referring using fast_code", amount.Float32())) - if err != nil { - h.mongoLoggerSvc.Error("Failed to add reward to static bet", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Int64("user_id", userID), - zap.Float32("amount", amount.Float32()), - zap.Int64("static wallet_id", wallet.StaticID), - zap.Time("timestamp", time.Now()), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Failed to add reward to static bet:"+err.Error()) - } - - h.mongoLoggerSvc.Info("Bet created successfully", - zap.Int("status_code", fiber.StatusOK), - zap.Int64("user_id", userID), - zap.Time("timestamp", time.Now()), - ) - return response.WriteJSON(c, fiber.StatusOK, "Bet Created", res, nil) -} - -func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userID int64, role domain.Role, companyID int64) (domain.CreateBetRes, error) { - valErrs, ok := h.validator.Validate(c, req) - if !ok { - h.mongoLoggerSvc.Error("CreateBet validation failed", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Any("validation_errors", valErrs), - zap.Time("timestamp", time.Now()), - ) - return domain.CreateBetRes{}, fmt.Errorf("%s", valErrs) - } - - res, err := h.betSvc.PlaceBet(c.Context(), req, userID, role, companyID) - if err != nil { - switch err { - case bet.ErrEventHasBeenRemoved, bet.ErrEventHasNotEnded, bet.ErrRawOddInvalid, bet.ErrTotalBalanceNotEnough: - h.mongoLoggerSvc.Info("PlaceBet failed", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Int64("userID", userID), - zap.Int64("companyID", companyID), - zap.String("role", string(role)), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return domain.CreateBetRes{}, err - } - - h.mongoLoggerSvc.Error("PlaceBet failed", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Int64("userID", userID), - zap.Int64("companyID", companyID), - zap.String("role", string(role)), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - - return domain.CreateBetRes{}, err - } - - // create raffle tickets - raffles, err := h.raffleSvc.GetRafflesOfCompany(c.Context(), int32(companyID)) - if err != nil { - h.mongoLoggerSvc.Error("Failed to fetch raffle of company", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Int64("userID", userID), - zap.Int64("companyID", companyID), - zap.String("role", string(role)), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - } - - sportAndLeagueIDs := [][]int64{} - for _, outcome := range req.Outcomes { - ids, err := h.eventSvc.GetSportAndLeagueIDs(c.Context(), outcome.EventID) - if err != nil { - continue - } - - sportAndLeagueIDs = append(sportAndLeagueIDs, ids) - } - - for _, raffle := range raffles { - // TODO: only fetch pending raffles from db - if raffle.Status == "completed" { - continue - } - - raffleTicketLimit, err := h.raffleSvc.GetRaffleTicketLimit(c.Context(), raffle.ID) - if err != nil { - continue - } - - // check raffle ticke count - userTicketCount, err := h.raffleSvc.GetRaffleTicketCount(c.Context(), raffle.ID, int32(userID)) - if err != nil { - continue - } - - if userTicketCount == int64(raffleTicketLimit) { - h.mongoLoggerSvc.Info("User reached max ticket count allowed for current raffle", - zap.Int("status_code", fiber.StatusForbidden), - zap.Int64("raffleID", int64(raffle.ID)), - zap.Int64("userID", userID), - zap.Int64("companyID", companyID), - zap.Time("timestamp", time.Now()), - ) - continue - } - - // empty raffle filter means there is no filter (all is allowed) - hasFilter, err := h.raffleSvc.CheckSportRaffleHasFilter(c.Context(), raffle.ID) - if err != nil { - continue - } - - foundValid := !hasFilter - - // only require one sport and league combo to be valid to make the raffle ticket - for _, sportAndLeagueID := range sportAndLeagueIDs { - if foundValid { - break - } - - res, err := h.raffleSvc.CheckValidSportRaffleFilter(c.Context(), raffle.ID, sportAndLeagueID[0], sportAndLeagueID[1]) - if err != nil { - continue - } - - foundValid = foundValid || res - } - - if !foundValid { - continue - } - - raffleTicket := domain.CreateRaffleTicket{ - RaffleID: raffle.ID, - UserID: int32(userID), - } - - _, err = h.raffleSvc.CreateRaffleTicket(c.Context(), raffleTicket) - if err != nil { - h.mongoLoggerSvc.Error("Failed to create raffle ticket", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Int64("raffleID", int64(raffle.ID)), - zap.Int64("userID", userID), - zap.Int64("companyID", companyID), - zap.String("role", string(role)), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - } - } - - return res, nil -} - -// RandomBet godoc -// @Summary Generate a random bet -// @Description Generate a random bet -// @Tags bet -// @Accept json -// @Produce json -// @Param createBet body domain.RandomBetReq true "Create Random bet" -// @Success 200 {object} domain.BetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/sport/random/bet [post] -func (h *Handler) RandomBet(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - userID := c.Locals("user_id").(int64) - - leagueIDQuery, err := strconv.ParseInt(c.Query("league_id"), 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("invalid league id", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - } - - sportIDQuery, err := strconv.Atoi(c.Query("sport_id")) - if err != nil { - h.mongoLoggerSvc.Info("invalid sport id", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") - } - - firstStartTimeQuery := c.Query("first_start_time") - lastStartTimeQuery := c.Query("last_start_time") - - leagueID := domain.ValidInt64{ - Value: leagueIDQuery, - Valid: leagueIDQuery != 0, - } - sportID := domain.ValidInt32{ - Value: int32(sportIDQuery), - Valid: sportIDQuery != 0, - } - - var firstStartTime domain.ValidTime - if firstStartTimeQuery != "" { - firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid first_start_time format", - zap.String("first_start_time", firstStartTimeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid first_start_time format") - } - firstStartTime = domain.ValidTime{ - Value: firstStartTimeParsed, - Valid: true, - } - } - - var lastStartTime domain.ValidTime - if lastStartTimeQuery != "" { - lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid last_start_time format", - zap.String("last_start_time", lastStartTimeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid last_start_time format") - } - lastStartTime = domain.ValidTime{ - Value: lastStartTimeParsed, - Valid: true, - } - } - - var req domain.RandomBetReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("Failed to parse RandomBet request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error()) - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Error("RandomBet validation failed", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Any("validation_errors", valErrs), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - var res domain.CreateBetRes - for i := 0; i < int(req.NumberOfBets); i++ { - res, err = h.betSvc.PlaceRandomBet(c.Context(), userID, req.BranchID, companyID.Value, leagueID, sportID, firstStartTime, lastStartTime) - if err != nil { - switch err { - case bet.ErrNoEventsAvailable: - return fiber.NewError(fiber.StatusNotFound, "No events found") - } - - h.mongoLoggerSvc.Error("Random Bet failed place random bet", - zap.Int64("userID", userID), - zap.Int64("branch_id", req.BranchID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Unable to create random bet:"+err.Error()) - } - } - - h.mongoLoggerSvc.Info("Random bet(s) created successfully", - zap.Int("status_code", fiber.StatusOK), - zap.Int64("user_id", userID), - zap.Time("timestamp", time.Now()), - ) - - return response.WriteJSON(c, fiber.StatusOK, "Bet Created", res, nil) -} - -// GetAllBet godoc -// @Summary Gets all bets -// @Description Gets all the bets -// @Tags bet -// @Accept json -// @Produce json -// @Success 200 {array} domain.BetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/sport/bet [get] -func (h *Handler) GetAllBet(c *fiber.Ctx) error { - role := c.Locals("role").(domain.Role) - - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 10) - limit := domain.ValidInt32{ - Value: int32(pageSize), - Valid: true, - } - offset := domain.ValidInt32{ - Value: int32(page - 1), - Valid: true, - } - - var isShopBet domain.ValidBool - isShopBetQuery := c.Query("is_shop") - if isShopBetQuery != "" && role == domain.RoleSuperAdmin { - isShopBetParse, err := strconv.ParseBool(isShopBetQuery) - if err != nil { - h.mongoLoggerSvc.Info("failed to parse is_shop_bet", - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("is_shop", isShopBetQuery), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "failed to parse is_shop_bet") - } - isShopBet = domain.ValidBool{ - Value: isShopBetParse, - Valid: true, - } - } - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - createdBeforeQuery := c.Query("created_before") - var createdBefore domain.ValidTime - if createdBeforeQuery != "" { - createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_before format", - zap.String("time", createdBeforeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format") - } - createdBefore = domain.ValidTime{ - Value: createdBeforeParsed, - Valid: true, - } - } - - createdAfterQuery := c.Query("created_after") - var createdAfter domain.ValidTime - if createdAfterQuery != "" { - createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_after format", - zap.String("created_after", createdAfterQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format") - } - createdAfter = domain.ValidTime{ - Value: createdAfterParsed, - Valid: true, - } - } - - var statusFilter domain.ValidOutcomeStatus - statusQuery := c.Query("status") - if statusQuery != "" { - statusParsed, err := strconv.ParseInt(statusQuery, 10, 32) - if err != nil { - h.mongoLoggerSvc.Info("invalid status format", - zap.String("status", statusQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid status format") - } - statusFilter = domain.ValidOutcomeStatus{ - Value: domain.OutcomeStatus(statusParsed), - Valid: true, - } - } - - var companyID domain.ValidInt64 - companyIDQuery := c.Query("company_id") - if companyIDQuery != "" { - companyIDParsed, err := strconv.ParseInt(companyIDQuery, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("invalid company_id format", - zap.String("company_id", companyIDQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid company_id format") - } - companyID = domain.ValidInt64{ - Value: companyIDParsed, - Valid: true, - } - } - bets, total, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{ - IsShopBet: isShopBet, - Query: searchString, - CreatedBefore: createdBefore, - CreatedAfter: createdAfter, - Status: statusFilter, - Limit: limit, - Offset: offset, - CompanyID: companyID, - }) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get all bets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets፡"+err.Error()) - } - - res := make([]domain.BetRes, len(bets)) - for i, bet := range bets { - res[i] = domain.ConvertBet(bet) - } - - return response.WritePaginatedJSON(c, fiber.StatusOK, "All bets retrieved successfully", res, nil, page, int(total)) -} - -// GetAllTenants godoc -// @Summary Gets all bets -// @Description Gets all the bets -// @Tags bet -// @Accept json -// @Produce json -// @Success 200 {array} domain.BetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/sport/bet [get] -func (h *Handler) GetAllTenantBets(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id", zap.Any("company_id", companyID)) - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 10) - limit := domain.ValidInt32{ - Value: int32(pageSize), - Valid: true, - } - offset := domain.ValidInt32{ - Value: int32(page - 1), - Valid: true, - } - var isShopBet domain.ValidBool - isShopBetQuery := c.Query("is_shop") - if isShopBetQuery != "" { - isShopBetParse, err := strconv.ParseBool(isShopBetQuery) - if err != nil { - h.mongoLoggerSvc.Info("failed to parse is_shop_bet", - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("is_shop", isShopBetQuery), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "failed to parse is_shop_bet") - } - isShopBet = domain.ValidBool{ - Value: isShopBetParse, - Valid: true, - } - } - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - createdBeforeQuery := c.Query("created_before") - var createdBefore domain.ValidTime - if createdBeforeQuery != "" { - createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_before format", - zap.String("time", createdBeforeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format") - } - createdBefore = domain.ValidTime{ - Value: createdBeforeParsed, - Valid: true, - } - } - - createdAfterQuery := c.Query("created_after") - var createdAfter domain.ValidTime - if createdAfterQuery != "" { - createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_after format", - zap.String("created_after", createdAfterQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format") - } - createdAfter = domain.ValidTime{ - Value: createdAfterParsed, - Valid: true, - } - } - - var statusFilter domain.ValidOutcomeStatus - statusQuery := c.Query("status") - if statusQuery != "" { - statusParsed, err := strconv.ParseInt(statusQuery, 10, 32) - if err != nil { - h.mongoLoggerSvc.Info("invalid status format", - zap.String("status", statusQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid status format") - } - statusFilter = domain.ValidOutcomeStatus{ - Value: domain.OutcomeStatus(statusParsed), - Valid: true, - } - } - bets, total, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{ - CompanyID: companyID, - IsShopBet: isShopBet, - Query: searchString, - CreatedBefore: createdBefore, - CreatedAfter: createdAfter, - Status: statusFilter, - Limit: limit, - Offset: offset, - }) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get all bets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets፡"+err.Error()) - } - - res := make([]domain.BetRes, len(bets)) - for i, bet := range bets { - res[i] = domain.ConvertBet(bet) - } - - return response.WritePaginatedJSON(c, fiber.StatusOK, "All bets retrieved successfully", res, nil, page, int(total)) -} - -// GetBetByID godoc -// @Summary Gets bet by id -// @Description Gets a single bet by id -// @Tags bet -// @Accept json -// @Produce json -// @Param id path int true "Bet ID" -// @Success 200 {object} domain.BetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/sport/bet/{id} [get] -func (h *Handler) GetBetByID(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - - betID := c.Params("id") - id, err := strconv.ParseInt(betID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid bet ID", - zap.String("betID", betID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID") - } - - bet, err := h.betSvc.GetBetByID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get bet by ID", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet") - } - - res := domain.ConvertBet(bet) - - if companyID.Valid && bet.CompanyID != companyID.Value { - h.mongoLoggerSvc.Warn("Warn - Company is trying to access another companies bet", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet") - } - // h.mongoLoggerSvc.Info("Bet retrieved successfully", - // zap.Int64("betID", id), - // zap.Int("status_code", fiber.StatusOK), - // zap.Time("timestamp", time.Now()), - // ) - - return response.WriteJSON(c, fiber.StatusOK, "Bet retrieved successfully", res, nil) -} - -// GetTenantBetByID godoc -// @Summary Gets bet by id -// @Description Gets a single bet by id -// @Tags bet -// @Accept json -// @Produce json -// @Param id path int true "Bet ID" -// @Success 200 {object} domain.BetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/sport/bet/{id} [get] -func (h *Handler) GetTenantBetByID(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - betID := c.Params("id") - id, err := strconv.ParseInt(betID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid bet ID", - zap.String("betID", betID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID") - } - - bet, err := h.betSvc.GetBetByID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get bet by ID", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet") - } - - if bet.CompanyID != companyID.Value { - h.mongoLoggerSvc.Warn("User Attempt to access another company bet", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet") - } - - res := domain.ConvertBet(bet) - - // h.mongoLoggerSvc.Info("Bet retrieved successfully", - // zap.Int64("betID", id), - // zap.Int("status_code", fiber.StatusOK), - // zap.Time("timestamp", time.Now()), - // ) - - return response.WriteJSON(c, fiber.StatusOK, "Bet retrieved successfully", res, nil) -} - -// GetBetByFastCode godoc -// @Summary Gets bet by fast_code -// @Description Gets a single bet by fast_code -// @Tags bet -// @Accept json -// @Produce json -// @Param fast_code path int true "Bet ID" -// @Success 200 {object} domain.BetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/sport/bet/fastcode/{fast_code} [get] -func (h *Handler) GetBetByFastCode(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - fastCode := c.Params("fast_code") - - bet, err := h.betSvc.GetBetByFastCode(c.Context(), fastCode) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get bet by fast code", - zap.String("fast_code", fastCode), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to find bet by fast code") - } - - if bet.CompanyID != companyID.Value { - h.mongoLoggerSvc.Warn("User Attempt to access another company bet", - zap.String("fast_code", fastCode), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet") - } - res := domain.ConvertBet(bet) - - // h.mongoLoggerSvc.Info("Bet retrieved successfully", - // zap.Int64("betID", id), - // zap.Int("status_code", fiber.StatusOK), - // zap.Time("timestamp", time.Now()), - // ) - - return response.WriteJSON(c, fiber.StatusOK, "Bet retrieved successfully", res, nil) -} - -type UpdateCashOutReq struct { - CashedOut bool -} - -// UpdateCashOut godoc -// @Summary Updates the cashed out field -// @Description Updates the cashed out field -// @Tags bet -// @Accept json -// @Produce json -// @Param id path int true "Bet ID" -// @Param updateCashOut body UpdateCashOutReq true "Updates Cashed Out" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/sport/bet/{id} [patch] -func (h *Handler) UpdateCashOut(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - type UpdateCashOutReq struct { - CashedOut bool `json:"cashed_out" validate:"required" example:"true"` - } - - betID := c.Params("id") - id, err := strconv.ParseInt(betID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Error("Invalid bet ID", - zap.String("betID", betID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID") - } - - var req UpdateCashOutReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Error("Failed to parse UpdateCashOut request", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "failed to parse request body:"+err.Error()) - } - - if valErrs, ok := h.validator.Validate(c, req); !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - bet, err := h.betSvc.GetBetByID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get bet", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet") - } - - if bet.CompanyID != companyID.Value { - h.mongoLoggerSvc.Warn("User Attempt to access another company bet", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet") - } - - err = h.betSvc.UpdateCashOut(c.Context(), id, req.CashedOut) - if err != nil { - h.mongoLoggerSvc.Error("Failed to update cash out bet", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update cash out bet") - } - - h.mongoLoggerSvc.Info("Bet updated successfully", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusOK), - zap.Time("timestamp", time.Now()), - ) - - return response.WriteJSON(c, fiber.StatusOK, "Bet updated successfully", nil, nil) -} - -// DeleteBet godoc -// @Summary Deletes bet by id -// @Description Deletes bet by id -// @Tags bet -// @Accept json -// @Produce json -// @Param id path int true "Bet ID" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/sport/bet/{id} [delete] -func (h *Handler) DeleteBet(c *fiber.Ctx) error { - betID := c.Params("id") - id, err := strconv.ParseInt(betID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Error("Invalid bet ID", - zap.String("betID", betID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID") - } - - err = h.betSvc.SetBetToRemoved(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Error("Failed to delete bet by ID", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete bet:"+err.Error()) - } - - h.mongoLoggerSvc.Info("Bet removed successfully", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusOK), - zap.Time("timestamp", time.Now()), - ) - - return response.WriteJSON(c, fiber.StatusOK, "Bet removed successfully", nil, nil) -} - -// DeleteTenantBet godoc -// @Summary Deletes bet by id -// @Description Deletes bet by id -// @Tags bet -// @Accept json -// @Produce json -// @Param id path int true "Bet ID" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/sport/bet/{id} [delete] -func (h *Handler) DeleteTenantBet(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - betID := c.Params("id") - id, err := strconv.ParseInt(betID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Error("Invalid bet ID", - zap.String("betID", betID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID") - } - - // This is to make sure that you can remove a bet only from the right route - bet, err := h.betSvc.GetBetByID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get bet", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet") - } - - if bet.CompanyID != companyID.Value { - h.mongoLoggerSvc.Warn("User Attempt to access another company bet", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet") - } - - err = h.betSvc.SetBetToRemoved(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Error("Failed to delete bet by ID", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete bet:"+err.Error()) - } - - h.mongoLoggerSvc.Info("Bet removed successfully", - zap.Int64("betID", id), - zap.Int("status_code", fiber.StatusOK), - zap.Time("timestamp", time.Now()), - ) - - return response.WriteJSON(c, fiber.StatusOK, "Bet removed successfully", nil, nil) -} diff --git a/internal/web_server/handlers/bonus.go b/internal/web_server/handlers/bonus.go deleted file mode 100644 index a273dff..0000000 --- a/internal/web_server/handlers/bonus.go +++ /dev/null @@ -1,206 +0,0 @@ -package handlers - -import ( - "strconv" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// func (h *Handler) CreateBonusMultiplier(c *fiber.Ctx) error { -// var req struct { -// Multiplier float32 `json:"multiplier"` -// BalanceCap int64 `json:"balance_cap"` -// } - -// if err := c.BodyParser(&req); err != nil { -// h.logger.Error("failed to parse bonus multiplier request", "error", err) -// h.mongoLoggerSvc.Info("failed to parse bonus multiplier", -// zap.Int("status_code", fiber.StatusBadRequest), -// zap.Error(err), -// zap.Time("timestamp", time.Now()), -// ) -// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error()) -// } - -// // currently only one multiplier is allowed -// // we can add an active bool in the db and have mulitple bonus if needed -// multipliers, err := h.bonusSvc.GetBonusMultiplier(c.Context()) -// if err != nil { -// h.logger.Error("failed to get bonus multiplier", "error", err) -// h.mongoLoggerSvc.Info("Failed to get bonus multiplier", -// zap.Int("status_code", fiber.StatusBadRequest), -// zap.Error(err), -// zap.Time("timestamp", time.Now()), -// ) -// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error()) -// } - -// if len(multipliers) > 0 { -// return fiber.NewError(fiber.StatusBadRequest, "only one multiplier is allowed") -// } - -// if err := h.bonusSvc.CreateBonusMultiplier(c.Context(), req.Multiplier, req.BalanceCap); err != nil { -// h.mongoLoggerSvc.Error("failed to create bonus multiplier", -// zap.Int("status_code", fiber.StatusInternalServerError), -// zap.Error(err), -// zap.Time("timestamp", time.Now()), -// ) -// return fiber.NewError(fiber.StatusInternalServerError, "failed to create bonus multiplier"+err.Error()) -// } - -// return response.WriteJSON(c, fiber.StatusOK, "Create bonus multiplier successfully", nil, nil) -// } - -// func (h *Handler) GetBonusMultiplier(c *fiber.Ctx) error { -// multipliers, err := h.bonusSvc.GetBonusMultiplier(c.Context()) -// if err != nil { -// h.mongoLoggerSvc.Info("failed to get bonus multiplier", -// zap.Int("status_code", fiber.StatusBadRequest), -// zap.Error(err), -// zap.Time("timestamp", time.Now()), -// ) -// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body"+err.Error()) -// } - -// return response.WriteJSON(c, fiber.StatusOK, "Fetched bonus multiplier successfully", multipliers, nil) -// } - -// func (h *Handler) UpdateBonusMultiplier(c *fiber.Ctx) error { -// var req struct { -// ID int64 `json:"id"` -// Multiplier float32 `json:"multiplier"` -// BalanceCap int64 `json:"balance_cap"` -// } - -// if err := c.BodyParser(&req); err != nil { -// h.mongoLoggerSvc.Info("failed to parse bonus multiplier", -// zap.Int("status_code", fiber.StatusBadRequest), -// zap.Error(err), -// zap.Time("timestamp", time.Now()), -// ) -// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error()) -// } - -// if err := h.bonusSvc.UpdateBonusMultiplier(c.Context(), req.ID, req.Multiplier, req.BalanceCap); err != nil { -// h.logger.Error("failed to update bonus multiplier", "error", err) -// h.mongoLoggerSvc.Error("failed to update bonus multiplier", -// zap.Int64("id", req.ID), -// zap.Int("status_code", fiber.StatusInternalServerError), -// zap.Error(err), -// zap.Time("timestamp", time.Now()), -// ) -// return fiber.NewError(fiber.StatusInternalServerError, "failed to update bonus multiplier:"+err.Error()) -// } - -// return response.WriteJSON(c, fiber.StatusOK, "Updated bonus multiplier successfully", nil, nil) -// } - -func (h *Handler) GetBonusesByUserID(c *fiber.Ctx) error { - userID, ok := c.Locals("user_id").(int64) - if !ok || userID == 0 { - h.InternalServerErrorLogger().Error("Invalid user ID in context", - zap.Int64("userID", userID), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification") - } - - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 10) - limit := domain.ValidInt{ - Value: pageSize, - Valid: true, - } - offset := domain.ValidInt{ - Value: page - 1, - Valid: true, - } - - filter := domain.BonusFilter{ - UserID: domain.ValidInt64{ - Value: userID, - Valid: true, - }, - Limit: limit, - Offset: offset, - } - - bonuses, err := h.bonusSvc.GetAllUserBonuses(c.Context(), filter) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to bonus by userID", zap.Int64("userId", userID)) - return fiber.NewError(fiber.StatusInternalServerError, "failed to get bonus by user ID") - } - - count, err := h.bonusSvc.GetBonusCount(c.Context(), filter) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to get bonus count", zap.Int64("userId", userID)) - return fiber.NewError(fiber.StatusInternalServerError, "failed to get bonus count by user ID") - } - - res := domain.ConvertToBonusResList(bonuses) - - return response.WritePaginatedJSON(c, fiber.StatusOK, "Fetched User Bonuses", res, nil, page, int(count)) - -} - -func (h *Handler) GetBonusStats(c *fiber.Ctx) error { - userID, ok := c.Locals("user_id").(int64) - if !ok || userID == 0 { - h.InternalServerErrorLogger().Error("Invalid user ID in context", - zap.Int64("userID", userID), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification") - } - - stats, err := h.bonusSvc.GetBonusStats(c.Context(), domain.BonusFilter{ - UserID: domain.ValidInt64{ - Value: userID, - Valid: true, - }, - }) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to get bonus stats", - zap.Int64("userID", userID), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get bonus stats") - } - - res := domain.ConvertToBonusStatsRes(stats) - - return response.WriteJSON(c, fiber.StatusOK, "Get Bonus Stats", res, nil) -} - -// bonus/:id/claim -func (h *Handler) ClaimBonus(c *fiber.Ctx) error { - bonusIDParam := c.Params("id") - bonusID, err := strconv.ParseInt(bonusIDParam, 10, 64) - if err != nil { - h.BadRequestLogger().Error("Invalid bonus ID", - zap.Int64("bonusID", bonusID), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid bonus id") - } - - userID, ok := c.Locals("user_id").(int64) - if !ok || userID == 0 { - h.InternalServerErrorLogger().Error("Invalid user ID in context", - zap.Int64("userID", userID), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification") - } - - if err := h.bonusSvc.ProcessBonusClaim(c.Context(), bonusID, userID); err != nil { - h.InternalServerErrorLogger().Error("Failed to update bonus claim", - zap.Int64("userID", userID), - zap.Int64("bonusID", bonusID), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update bonus claim") - } - - return response.WriteJSON(c, fiber.StatusOK, "Bonus has successfully been claimed", nil, nil) - -} diff --git a/internal/web_server/handlers/branch_handler.go b/internal/web_server/handlers/branch_handler.go deleted file mode 100644 index 94a91c6..0000000 --- a/internal/web_server/handlers/branch_handler.go +++ /dev/null @@ -1,1167 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - "strings" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// CreateBranch godoc -// @Summary Create a branch -// @Description Creates a branch -// @Tags branch -// @Accept json -// @Produce json -// @Param createBranch body domain.CreateBranchReq true "Creates branch" -// @Success 200 {object} domain.BranchRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch [post] -func (h *Handler) CreateBranch(c *fiber.Ctx) error { - // Check if user is either branch manager / super main - // role := string(c.Locals("role").(domain.Role)) - - // if role != string(domain.RoleAdmin) && role != string(domain.RoleSuperAdmin) && role != string(domain.RoleBranchManager) { - // logger.Error("Unauthorized access", "role", role) - // return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil) - // } - - role := c.Locals("role").(domain.Role) - companyID := c.Locals("company_id").(domain.ValidInt64) - - var req domain.CreateBranchReq - - if err := c.BodyParser(&req); err != nil { - // h.logger.Error("CreateBranchReq failed", "error", err) - h.mongoLoggerSvc.Info("CreateBranchReq failed", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - var IsSelfOwned bool - var checkedCompanyID int64 - if role == domain.RoleSuperAdmin { - if req.IsSelfOwned == nil { - h.mongoLoggerSvc.Info("is_self_owned is required for super admin", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "is_self_owned is required for super admin") - } - if req.CompanyID == nil { - h.mongoLoggerSvc.Info("company_id is required for super admin", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "company_id is required for super admin") - } - IsSelfOwned = *req.IsSelfOwned - checkedCompanyID = *req.CompanyID - } else { - IsSelfOwned = false - checkedCompanyID = companyID.Value - } - - // Create Branch Wallet - newWallet, err := h.walletSvc.CreateWallet(c.Context(), domain.CreateWallet{ - IsWithdraw: false, - IsBettable: true, - IsTransferable: true, - UserID: req.BranchManagerID, - Type: domain.BranchWalletType, - }) - - if err != nil { - h.mongoLoggerSvc.Error("Create Branch Wallet failed", - zap.Int64("branch_manager_id", req.BranchManagerID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - branch, err := h.branchSvc.CreateBranch(c.Context(), domain.CreateBranch{ - Name: req.Name, - Location: req.Location, - WalletID: newWallet.ID, - BranchManagerID: req.BranchManagerID, - CompanyID: checkedCompanyID, - IsSelfOwned: IsSelfOwned, - ProfitPercentage: req.ProfitPercentage, - }) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to create branch", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - for _, operation := range req.Operations { - err := h.branchSvc.CreateBranchOperation(c.Context(), domain.CreateBranchOperation{ - BranchID: branch.ID, - OperationID: operation, - }) - if err != nil { - h.mongoLoggerSvc.Error("Failed to create branch operations", - zap.Int64("branchID", branch.ID), - zap.Int64("operation", operation), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - } - - res := domain.ConvertBranch(branch) - - return response.WriteJSON(c, fiber.StatusCreated, "Branch Created", res, nil) -} - -// CreateSupportedOperation godoc -// @Summary Create a supported operation -// @Description Creates a supported operation -// @Tags branch -// @Accept json -// @Produce json -// @Param createSupportedOperation body domain.CreateSupportedOperationReq true "Creates supported operation" -// @Success 200 {object} domain.SupportedOperationRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/supportedOperation [post] -func (h *Handler) CreateSupportedOperation(c *fiber.Ctx) error { - var req domain.CreateSupportedOperationReq - - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("Failed to parse CreateSupportedOperationReq", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - - h.mongoLoggerSvc.Error("Failed to validate CreateSupportedOperationReq", - zap.Any("request", req), - zap.String("error", errMsg), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - operation, err := h.branchSvc.CreateSupportedOperation(c.Context(), domain.CreateSupportedOperation{ - Name: req.Name, - Description: req.Description, - }) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to create supported operation", - zap.String("name", req.Name), - zap.String("description", req.Description), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create supported operation") - } - - res := domain.SupportedOperationRes{ - Name: operation.Name, - Description: operation.Description, - } - - return response.WriteJSON(c, fiber.StatusOK, "Operation Created", res, nil) -} - -// CreateBranchOperation godoc -// @Summary Create a operation -// @Description Creates a operation -// @Tags branch -// @Accept json -// @Produce json -// @Param createBranchOperation body domain.CreateBranchOperationReq true "Creates operation" -// @Success 200 {object} domain.BranchOperationRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/operation [post] -func (h *Handler) CreateBranchOperation(c *fiber.Ctx) error { - var req domain.CreateBranchOperationReq - if err := c.BodyParser(&req); err != nil { - h.logger.Error("CreateBranchOperationReq failed", "error", err) - h.mongoLoggerSvc.Info("Failed to parse CreateBranchOperationReq", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validated CreateBranchOperationReq", - zap.String("errMsg", errMsg), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err := h.branchSvc.CreateBranchOperation(c.Context(), domain.CreateBranchOperation{ - BranchID: req.BranchID, - OperationID: req.OperationID, - }) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to create branch operation", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Branch Operation Created", nil, nil) -} - -// GetBranchByID godoc -// @Summary Gets branch by id -// @Description Gets a single branch by id -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "Branch ID" -// @Success 200 {object} domain.BranchDetailRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch/{id} [get] -func (h *Handler) GetBranchByID(c *fiber.Ctx) error { - branchID := c.Params("id") - id, err := strconv.ParseInt(branchID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid branch ID", - zap.String("branch", branchID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") - } - - branch, err := h.branchSvc.GetBranchByID(c.Context(), id) - - if err != nil { - h.mongoLoggerSvc.Info("Failed to get branch by ID", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertBranchDetail(branch) - - return response.WriteJSON(c, fiber.StatusOK, "Branch retrieved successfully", res, nil) -} - -// ReturnBranchWallet godoc -// @Summary Unassign the branch wallet to company -// @Description Unassign the branch wallet to company -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "Branch ID" -// @Success 200 {object} domain.BranchDetailRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch/{id}/return [post] -func (h *Handler) ReturnBranchWallet(c *fiber.Ctx) error { - userID := c.Locals("user_id").(int64) - - branchID := c.Params("id") - id, err := strconv.ParseInt(branchID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid branch ID", - zap.String("branch", branchID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") - } - - branch, err := h.branchSvc.GetBranchByID(c.Context(), id) - - if err != nil { - h.mongoLoggerSvc.Info("Failed to get branch by ID", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - company, err := h.companySvc.GetCompanyByID(c.Context(), branch.CompanyID) - - if err != nil { - h.mongoLoggerSvc.Info("Failed to get company by ID", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - branchWallet, err := h.walletSvc.GetWalletByID(c.Context(), branch.WalletID) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get wallet by ID", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - _, err = h.walletSvc.AddToWallet(c.Context(), - company.WalletID, - branchWallet.Balance, - domain.ValidInt64{Value: userID, Valid: true}, - domain.TRANSFER_DIRECT, - domain.PaymentDetails{}, - fmt.Sprintf("Returning Branch %v Wallet to Company", branch.Name)) - - if err != nil { - h.mongoLoggerSvc.Info("Failed to add to company wallet", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - _, err = h.walletSvc.DeductFromWallet(c.Context(), - branchWallet.ID, - branchWallet.Balance, - domain.ValidInt64{Value: userID, Valid: true}, - domain.TRANSFER_DIRECT, - "Branch Wallet Balance has been returned to Company", - ) - - if err != nil { - h.mongoLoggerSvc.Info("Failed to deduct from branch wallet", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertBranchDetail(branch) - - return response.WriteJSON(c, fiber.StatusOK, "Branch Wallet Has been emptied", res, nil) -} - -// GetBranchByManagerID godoc -// @Summary Gets branches by manager id -// @Description Gets a branches by manager id -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "User ID" -// @Success 200 {array} domain.BranchDetailRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/manager/{id}/branch [get] -func (h *Handler) GetBranchByManagerID(c *fiber.Ctx) error { - // TODO: Restrict any who isn't branch manager or higher - userID := c.Params("id") - id, err := strconv.ParseInt(userID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid user ID", - zap.String("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID") - } - - branches, err := h.branchSvc.GetBranchByManagerID(c.Context(), id) - - if err != nil { - h.mongoLoggerSvc.Info("Failed to get branches", - zap.String("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - var result []domain.BranchDetailRes = make([]domain.BranchDetailRes, 0, len(branches)) - for _, branch := range branches { - result = append(result, domain.ConvertBranchDetail(branch)) - } - return response.WriteJSON(c, fiber.StatusOK, "Branches for Branch Manager retrieved", result, nil) -} - -// GetBranchByCompanyID godoc -// @Summary Gets branches by company id -// @Description Gets branches by company id -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "Company ID" -// @Success 200 {array} domain.BranchDetailRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/company/{id}/branch [get] -func (h *Handler) GetBranchByCompanyID(c *fiber.Ctx) error { - companyID := c.Params("id") - id, err := strconv.ParseInt(companyID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid company ID", - zap.String("companyID", companyID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid company ID") - } - - branches, err := h.branchSvc.GetBranchByCompanyID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get branches", - zap.String("companyID", companyID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var result []domain.BranchDetailRes = make([]domain.BranchDetailRes, 0, len(branches)) - for _, branch := range branches { - result = append(result, domain.ConvertBranchDetail(branch)) - } - return response.WriteJSON(c, fiber.StatusOK, "Branches for Company retrieved", result, nil) -} - -// GetAllBranches godoc -// @Summary Gets all branches -// @Description Gets all branches -// @Tags branch -// @Accept json -// @Produce json -// @Success 200 {array} domain.BranchDetailRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch [get] -func (h *Handler) GetAllBranches(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - isActiveParam := c.Params("is_active") - isActiveValid := isActiveParam != "" - isActive, err := strconv.ParseBool(isActiveParam) - - if isActiveValid && err != nil { - h.mongoLoggerSvc.Info("Invalid is_active param", - zap.String("isActive", isActiveParam), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid is_active param") - } - - branchManagerQuery := c.Query("branch_manager_id") - var branchManagerID domain.ValidInt64 - if branchManagerQuery != "" { - parseManagerID, err := strconv.ParseInt(branchManagerQuery, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Failed to parse branch_manager_id", - zap.String("userID", branchManagerQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Failed to parse branch_manager_id") - } - branchManagerID = domain.ValidInt64{ - Value: parseManagerID, - Valid: true, - } - } - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - createdBeforeQuery := c.Query("created_before") - var createdBefore domain.ValidTime - if createdBeforeQuery != "" { - createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_before format", - zap.String("createdBeforeQuery", createdBeforeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format") - } - createdBefore = domain.ValidTime{ - Value: createdBeforeParsed, - Valid: true, - } - } - - createdAfterQuery := c.Query("created_after") - var createdAfter domain.ValidTime - if createdAfterQuery != "" { - createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_after format", - zap.String("createdAfterQuery", createdAfterQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format") - } - createdAfter = domain.ValidTime{ - Value: createdAfterParsed, - Valid: true, - } - } - - branches, err := h.branchSvc.GetAllBranches(c.Context(), - domain.BranchFilter{ - CompanyID: companyID, - IsActive: domain.ValidBool{ - Value: isActive, - Valid: isActiveValid, - }, - BranchManagerID: branchManagerID, - Query: searchString, - CreatedBefore: createdBefore, - CreatedAfter: createdAfter, - }) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get branches", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var result []domain.BranchDetailRes = make([]domain.BranchDetailRes, 0, len(branches)) - for _, branch := range branches { - result = append(result, domain.ConvertBranchDetail(branch)) - } - return response.WriteJSON(c, fiber.StatusOK, "Branches for Company retrieved", result, nil) -} - -// SearchBranch godoc -// @Summary Search branches -// @Description Search branches by name or location -// @Tags branch -// @Accept json -// @Produce json -// @Param q query string true "Search query" -// @Success 200 {array} domain.BranchDetailRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/search/branch [get] -func (h *Handler) SearchBranch(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - - // Get search query from request - searchQuery := c.Query("q") - if searchQuery == "" { - h.mongoLoggerSvc.Info("Search query is required", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Search query is required") - } - - // Call the service to search for branches - branches, err := h.branchSvc.SearchBranchByName(c.Context(), searchQuery, companyID) - if err != nil { - h.mongoLoggerSvc.Info("Failed to search branches", - zap.String("query", searchQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - // Convert branches to response format - var result []domain.BranchDetailRes - for _, branch := range branches { - result = append(result, domain.ConvertBranchDetail(branch)) - } - - return response.WriteJSON(c, fiber.StatusOK, "Branches retrieved successfully", result, nil) -} - -// GetAllSupportedOperations godoc -// @Summary Gets all supported operations -// @Description Gets all supported operations -// @Tags branch -// @Accept json -// @Produce json -// @Success 200 {array} domain.BranchDetailRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/supportedOperation [get] -func (h *Handler) GetAllSupportedOperations(c *fiber.Ctx) error { - operations, err := h.branchSvc.GetAllSupportedOperations(c.Context()) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get operations", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var result []domain.SupportedOperationRes = make([]domain.SupportedOperationRes, 0, len(operations)) - for _, operation := range operations { - result = append(result, domain.SupportedOperationRes{ - ID: operation.ID, - Name: operation.Name, - Description: operation.Description, - }) - } - return response.WriteJSON(c, fiber.StatusOK, "SupportedOperations for Company retrieved", result, nil) - -} - -// GetBranchOperations godoc -// @Summary Gets branch operations -// @Description Gets branch operations -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "Branch ID" -// @Success 200 {array} domain.BranchOperationRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch/{id}/operation [get] -func (h *Handler) GetBranchOperations(c *fiber.Ctx) error { - branchID := c.Params("id") - id, err := strconv.ParseInt(branchID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Error("Invalid branch ID", - zap.String("branchID", branchID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") - } - - operations, err := h.branchSvc.GetBranchOperations(c.Context(), id) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get operation by ID", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var result []domain.BranchOperationRes = make([]domain.BranchOperationRes, 0, len(operations)) - - for _, branch := range operations { - result = append(result, domain.BranchOperationRes{ - Name: branch.OperationName, - Description: branch.OperationDescription, - }) - } - - return response.WriteJSON(c, fiber.StatusOK, "Branch Operations retrieved successfully", result, nil) -} - -// GetAllBranchLocations godoc -// @Summary Gets all branch locations -// @Description Gets all branch locations -// @Tags branch -// @Accept json -// @Produce json -// @Success 200 {array} domain.BranchLocation -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branchLocation [get] -func (h *Handler) GetAllBranchLocations(c *fiber.Ctx) error { - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - locations, err := h.branchSvc.GetAllBranchLocations(c.Context(), searchString) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get branch locations", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Branch Location successfully fetched", locations, nil) -} - -// GetBranchCashiers godoc -// @Summary Gets branch cashiers -// @Description Gets branch cashiers -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "Branch ID" -// @Success 200 {array} GetCashierRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch/{id}/cashier [get] -func (h *Handler) GetBranchCashiers(c *fiber.Ctx) error { - branchID := c.Params("id") - id, err := strconv.ParseInt(branchID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid branch ID", - zap.String("branchID", branchID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") - } - - cashiers, err := h.userSvc.GetCashiersByBranch(c.Context(), id) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get cashier by branch ID", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var result []GetCashierRes = make([]GetCashierRes, 0, len(cashiers)) - - for _, cashier := range cashiers { - lastLogin, err := h.authSvc.GetLastLogin(c.Context(), cashier.ID) - if err != nil { - if err == authentication.ErrRefreshTokenNotFound { - lastLogin = &cashier.CreatedAt - } else { - h.mongoLoggerSvc.Error("Failed to get user last login", - zap.Int64("cashier ID", cashier.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login") - } - } - result = append(result, GetCashierRes{ - ID: cashier.ID, - FirstName: cashier.FirstName, - LastName: cashier.LastName, - Email: cashier.Email, - PhoneNumber: cashier.PhoneNumber, - Role: cashier.Role, - EmailVerified: cashier.EmailVerified, - PhoneVerified: cashier.PhoneVerified, - CreatedAt: cashier.CreatedAt, - UpdatedAt: cashier.UpdatedAt, - SuspendedAt: cashier.SuspendedAt, - Suspended: cashier.Suspended, - LastLogin: *lastLogin, - }) - } - - return response.WriteJSON(c, fiber.StatusOK, "Branch Cashiers retrieved successfully", result, nil) -} - -// GetBranchForCashier godoc -// @Summary Gets branch for cahier -// @Description Gets branch for cahier -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "Branch ID" -// @Success 200 {object} domain.BranchDetailRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branchCashier [get] -func (h *Handler) GetBranchForCashier(c *fiber.Ctx) error { - cashierID, ok := c.Locals("user_id").(int64) - - if !ok { - h.mongoLoggerSvc.Error("Invalid cashier ID in context", - zap.Int64("cashierID", cashierID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Invalid user_id") - } - - role, ok := c.Locals("role").(domain.Role) - - if !ok || role != domain.RoleCashier { - - h.mongoLoggerSvc.Error("Unauthorized access", - zap.Int64("cashierID", cashierID), - zap.String("role", string(role)), - zap.Int("status_code", fiber.StatusForbidden), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusForbidden, "Unauthorized access") - } - - branchID, ok := c.Locals("branch_id").(domain.ValidInt64) - if !ok || !branchID.Valid { - h.mongoLoggerSvc.Info("Invalid branch ID in context", - zap.Int64("cashierID", cashierID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") - } - - branch, err := h.branchSvc.GetBranchByID(c.Context(), branchID.Value) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get branch by ID", - zap.Int64("branchID", branchID.Value), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertBranchDetail(branch) - - return response.WriteJSON(c, fiber.StatusOK, "Branch retrieved successfully", res, nil) -} - -// GetBetByBranchID godoc -// @Summary Gets bets by its branch id -// @Description Gets bets by its branch id -// @Tags branch -// @Accept json -// @Produce json -// @Success 200 {array} domain.BetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch/{id}/bets [get] -func (h *Handler) GetBetByBranchID(c *fiber.Ctx) error { - branchID := c.Params("id") - id, err := strconv.ParseInt(branchID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid branch ID", - zap.String("branchID", branchID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") - } - - bets, err := h.transactionSvc.GetAllShopBet(c.Context(), domain.ShopBetFilter{ - BranchID: domain.ValidInt64{ - Value: id, - Valid: true, - }, - }) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get bets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var res []domain.ShopBetRes = make([]domain.ShopBetRes, 0, len(bets)) - for _, bet := range bets { - res = append(res, domain.ConvertShopBetDetail(bet)) - } - - return response.WriteJSON(c, fiber.StatusOK, "Branch Bets Retrieved", res, nil) -} - -// UpdateBranch godoc -// @Summary Updates a branch -// @Description Updates a branch -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "Branch ID" -// @Param updateBranch body domain.CreateBranchReq true "Update Branch" -// @Success 200 {object} domain.BranchRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch/{id} [put] -func (h *Handler) UpdateBranch(c *fiber.Ctx) error { - branchID := c.Params("id") - id, err := strconv.ParseInt(branchID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid branch ID", - zap.String("branchID", branchID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") - } - - var req domain.UpdateBranchReq - - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("Failed to parse UpdateBranchReq", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate UpdateBranchReq", - zap.String("branchID", branchID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("errMsg", errMsg), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - branch, err := h.branchSvc.UpdateBranch(c.Context(), domain.UpdateBranch{ - ID: id, - Name: req.Name, - Location: req.Location, - BranchManagerID: req.BranchManagerID, - CompanyID: req.CompanyID, - IsSelfOwned: req.IsSelfOwned, - IsActive: req.IsActive, - }) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to update branch", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertBranch(branch) - - return response.WriteJSON(c, fiber.StatusOK, "Branch Updated", res, nil) - -} - -func (h *Handler) UpdateBranchStatus(c *fiber.Ctx) error { - branchID := c.Params("id") - id, err := strconv.ParseInt(branchID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid branch ID", - zap.String("branchID", branchID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - var isActive bool - path := strings.Split(strings.Trim(c.Path(), "/"), "/") - - if path[len(path)-1] == "set-active" { - isActive = true - } else if path[len(path)-1] == "set-inactive" { - isActive = false - } else { - h.mongoLoggerSvc.Info("Invalid branch status", - zap.Bool("status", isActive), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - - return fiber.NewError(fiber.StatusBadRequest, "Invalid branch status") - } - - branch, err := h.branchSvc.UpdateBranch(c.Context(), domain.UpdateBranch{ - ID: id, - IsActive: &isActive, - }) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to update branch", - zap.Int64("branchID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertBranch(branch) - - return response.WriteJSON(c, fiber.StatusOK, "Branch Updated", res, nil) - -} - -// DeleteBranch godoc -// @Summary Delete the branch -// @Description Delete the branch -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "Branch ID"" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch/{id} [delete] -func (h *Handler) DeleteBranch(c *fiber.Ctx) error { - branchID := c.Params("id") - id, err := strconv.ParseInt(branchID, 10, 64) - - if err != nil { - h.mongoLoggerSvc.Info("Invalid Branch ID", - zap.String("branchID", branchID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid Branch ID") - } - - err = h.branchSvc.DeleteBranch(c.Context(), id) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to delete by ID", - zap.Int64("Branch ID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Branch removed successfully", nil, nil) -} - -// DeleteBranchOperation godoc -// @Summary Delete the branch operation -// @Description Delete the branch operation -// @Tags branch -// @Accept json -// @Produce json -// @Param id path int true "Branch ID" -// @Param opID path int true "Branch Operation ID" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branch/{id}/operation/{opID} [delete] -func (h *Handler) DeleteBranchOperation(c *fiber.Ctx) error { - branchID := c.Params("id") - opID := c.Params("opID") - - id, err := strconv.ParseInt(branchID, 10, 64) - - if err != nil { - - h.mongoLoggerSvc.Error("Invalid Branch ID", - zap.String("branchID", branchID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid Branch ID") - } - - operationID, err := strconv.ParseInt(opID, 10, 64) - - if err != nil { - - h.mongoLoggerSvc.Info("Invalid Operation ID", - zap.String("operationID", opID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid Operation ID") - } - - err = h.branchSvc.DeleteBranchOperation(c.Context(), id, operationID) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to delete operation", - zap.Int64("Branch ID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Branch Operation removed successfully", nil, nil) - -} diff --git a/internal/web_server/handlers/cashier.go b/internal/web_server/handlers/cashier.go index 1efb392..87f4950 100644 --- a/internal/web_server/handlers/cashier.go +++ b/internal/web_server/handlers/cashier.go @@ -1,13 +1,13 @@ package handlers import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/services/authentication" + "Yimaru-Backend/internal/web_server/response" "fmt" "strconv" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) @@ -64,47 +64,47 @@ func (h *Handler) CreateCashier(c *fiber.Ctx) error { // Cashiers inherit the company id from the branch id // TODO add a check here to make sure that the admin/manager if from same company - branch, err := h.branchSvc.GetBranchByID(c.Context(), req.BranchID) + // branch, err := h.branchSvc.GetBranchByID(c.Context(), req.BranchID) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Branch ID is invalid") - } - userRequest := domain.CreateUserReq{ - FirstName: req.FirstName, - LastName: req.LastName, - Email: req.Email, - PhoneNumber: req.PhoneNumber, - Password: req.Password, - Role: string(domain.RoleCashier), - Suspended: req.Suspended, - CompanyID: domain.ValidInt64{ - Value: branch.CompanyID, - Valid: true, - }, - } - fmt.Print(req.Suspended) - newUser, err := h.userSvc.CreateUser(c.Context(), userRequest, true) - if err != nil { - h.mongoLoggerSvc.Error("Failed to create cashier user", - zap.Any("userRequest", userRequest), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create cashier user:"+err.Error()) - } + // if err != nil { + // return fiber.NewError(fiber.StatusBadRequest, "Branch ID is invalid") + // } + // userRequest := domain.CreateUserReq{ + // FirstName: req.FirstName, + // LastName: req.LastName, + // Email: req.Email, + // PhoneNumber: req.PhoneNumber, + // Password: req.Password, + // Role: string(domain.RoleCashier), + // Suspended: req.Suspended, + // CompanyID: domain.ValidInt64{ + // Value: 0, + // Valid: true, + // }, + // } + // fmt.Print(req.Suspended) + // newUser, err := h.userSvc.CreateUser(c.Context(), userRequest, true) + // if err != nil { + // h.mongoLoggerSvc.Error("Failed to create cashier user", + // zap.Any("userRequest", userRequest), + // zap.Int("status_code", fiber.StatusInternalServerError), + // zap.Error(err), + // zap.Time("timestamp", time.Now()), + // ) + // return fiber.NewError(fiber.StatusInternalServerError, "Failed to create cashier user:"+err.Error()) + // } - err = h.branchSvc.CreateBranchCashier(c.Context(), req.BranchID, newUser.ID) - if err != nil { - h.mongoLoggerSvc.Error("failed to create branch cashier", - zap.Int64("branchID", req.BranchID), - zap.Int64("userID", newUser.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create branch cashier:"+err.Error()) - } + // err = h.branchSvc.CreateBranchCashier(c.Context(), req.BranchID, newUser.ID) + // if err != nil { + // h.mongoLoggerSvc.Error("failed to create branch cashier", + // zap.Int64("branchID", req.BranchID), + // zap.Int64("userID", newUser.ID), + // zap.Int("status_code", fiber.StatusInternalServerError), + // zap.Error(err), + // zap.Time("timestamp", time.Now()), + // ) + // return fiber.NewError(fiber.StatusInternalServerError, "Failed to create branch cashier:"+err.Error()) + // } return response.WriteJSON(c, fiber.StatusOK, "Cashier created successfully", nil, nil) } @@ -228,7 +228,7 @@ func (h *Handler) GetAllCashiers(c *fiber.Ctx) error { } cashiers, total, err := h.userSvc.GetAllCashiers(c.Context(), filter) - + if err != nil { h.mongoLoggerSvc.Error("failed to get all cashiers", zap.Int("status_code", fiber.StatusInternalServerError), diff --git a/internal/web_server/handlers/chapa.go b/internal/web_server/handlers/chapa.go deleted file mode 100644 index 4af65bc..0000000 --- a/internal/web_server/handlers/chapa.go +++ /dev/null @@ -1,485 +0,0 @@ -package handlers - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" -) - -// InitiateDeposit godoc -// @Summary Initiate a deposit -// @Description Starts a new deposit process using Chapa payment gateway -// @Tags Chapa -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Param request body domain.ChapaDepositRequestPayload true "Deposit request" -// @Success 200 {object} domain.ChapaDepositResponse -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/payments/deposit [post] -func (h *Handler) InitiateDeposit(c *fiber.Ctx) error { - // Get user ID from context (set by your auth middleware) - userID, ok := c.Locals("user_id").(int64) - if !ok { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: "invalid user ID", - Message: "User ID is required to initiate a deposit", - }) - } - - var req domain.ChapaDepositRequestPayload - - if err := c.BodyParser(&req); err != nil { - // fmt.Println("We first first are here init Chapa payment") - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to parse request body", - }) - } - - amount := domain.Currency(req.Amount) - - fmt.Println("We are here init Chapa payment") - - checkoutURL, err := h.chapaSvc.InitiateDeposit(c.Context(), userID, amount) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to initiate Chapa deposit", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Chapa deposit process initiated successfully", - Data: checkoutURL, - StatusCode: 200, - Success: true, - }) -} - -// WebhookCallback godoc -// @Summary Chapa payment webhook callback (used by Chapa) -// @Description Handles payment and transfer notifications from Chapa -// @Tags Chapa -// @Accept json -// @Produce json -// @Param request body domain.ChapaWebhookPayment true "Webhook payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/payments/webhook/verify [post] -func (h *Handler) WebhookCallback(c *fiber.Ctx) error { - body := c.Body() - - // Retrieve signature headers - chapaSignature := c.Get("chapa-signature") - xChapaSignature := c.Get("x-chapa-signature") - - // Verify webhook signature - valid, err := h.chapaSvc.VerifyWebhookSignature(c.Context(), body, chapaSignature, xChapaSignature) - if err != nil || !valid { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid Chapa webhook signature", - Error: err.Error(), - }) - } - - // Try parsing as transfer webhook first - var transfer domain.ChapaWebhookTransfer - if err := json.Unmarshal(body, &transfer); err == nil && - strings.EqualFold(transfer.Type, "payout") { - - if err := h.chapaSvc.ProcessVerifyWithdrawWebhook(c.Context(), transfer); err != nil { - return domain.UnExpectedErrorResponse(c) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - StatusCode: 200, - Message: "Chapa withdrawal webhook processed successfully", - // Data: transfer, - Success: true, - }) - } - - // Otherwise, try as payment webhook - var payment domain.ChapaWebhookPayment - if err := json.Unmarshal(body, &payment); err != nil { - return domain.UnProcessableEntityResponse(c) - } - - if err := h.chapaSvc.ProcessVerifyDepositWebhook(c.Context(), payment); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to verify Chapa deposit", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - StatusCode: 200, - Message: "Chapa deposit webhook processed successfully", - // Data: payment, - Success: true, - }) -} - -// CancelDeposit godoc -// @Summary Cancel a Chapa deposit transaction -// @Description Cancels an active Chapa transaction using its transaction reference -// @Tags Chapa -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Param tx_ref path string true "Transaction Reference" -// @Success 200 {object} domain.ChapaCancelResponse -// @Failure 400 {object} domain.ErrorResponse -// @Failure 404 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/transaction/cancel/{tx_ref} [put] -func (h *Handler) CancelDeposit(c *fiber.Ctx) error { - // Get user ID from context (set by your auth middleware) - userID, ok := c.Locals("user_id").(int64) - if !ok { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: "invalid user ID", - Message: "User ID is required to cancel a deposit", - }) - } - - // Extract tx_ref from URL path - txRef := c.Params("tx_ref") - if txRef == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: "missing transaction reference", - Message: "Transaction reference is required in the path", - }) - } - - fmt.Printf("\n\nReceived request to cancel Chapa transaction: %s (User ID: %d)\n\n", txRef, userID) - - // Call the service layer to cancel deposit - cancelResp, err := h.chapaSvc.CancelDeposit(c.Context(), userID, txRef) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to cancel Chapa deposit", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Chapa transaction cancelled successfully", - Data: cancelResp, - StatusCode: 200, - Success: true, - }) -} - -// FetchAllTransactions godoc -// @Summary Get all Chapa transactions -// @Description Retrieves all transactions from Chapa payment gateway -// @Tags Chapa -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Success 200 {array} domain.ChapaTransaction -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/transactions [get] -func (h *Handler) FetchAllTransactions(c *fiber.Ctx) error { - transactions, err := h.chapaSvc.FetchAllTransactions(c.Context()) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to fetch Chapa transactions", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Chapa transactions retrieved successfully", - Data: transactions, - StatusCode: 200, - Success: true, - }) -} - -// GetTransactionEvents godoc -// @Summary Fetch transaction events -// @Description Retrieve the timeline of events for a specific Chapa transaction -// @Tags Chapa -// @Accept json -// @Produce json -// @Param ref_id path string true "Transaction Reference" -// @Success 200 {array} domain.ChapaTransactionEvent -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/transaction/events/{ref_id} [get] -func (h *Handler) GetTransactionEvents(c *fiber.Ctx) error { - refID := c.Params("ref_id") - if refID == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to fetch transaction events", - Error: "Transaction reference is required", - }) - } - - events, err := h.chapaSvc.FetchTransactionEvents(c.Context(), refID) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to fetch transaction events", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Transaction events fetched successfully", - Data: events, - StatusCode: 200, - Success: true, - }) -} - -// VerifyPayment godoc -// @Summary Verify a payment manually -// @Description Manually verify a payment using Chapa's API -// @Tags Chapa -// @Accept json -// @Produce json -// @Param tx_ref path string true "Transaction Reference" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/transaction/manual/verify/{tx_ref} [get] -func (h *Handler) ManualVerifyTransaction(c *fiber.Ctx) error { - txRef := c.Params("tx_ref") - if txRef == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to verify Chapa transaction", - Error: "Transaction reference is required", - }) - } - - verification, err := h.chapaSvc.ManuallyVerify(c.Context(), txRef) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to verify Chapa transaction", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Chapa transaction verified successfully", - Data: verification, - StatusCode: 200, - Success: true, - }) -} - -// GetSupportedBanks godoc -// @Summary Get supported banks -// @Description Get list of banks supported by Chapa -// @Tags Chapa -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/banks [get] -func (h *Handler) GetSupportedBanks(c *fiber.Ctx) error { - banks, err := h.chapaSvc.GetSupportedBanks(c.Context()) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to fetch banks", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Banks fetched successfully", - StatusCode: 200, - Success: true, - Data: banks, - }) -} - -// InitiateWithdrawal initiates a withdrawal request via Chapa payment gateway -// @Summary Initiate a withdrawal -// @Description Initiates a withdrawal request to transfer funds to a bank account via Chapa -// @Tags Chapa -// @Accept json -// @Produce json -// @Security ApiKeyAuth -// @Param request body domain.ChapaWithdrawalRequest true "Withdrawal request details" -// @Success 200 {object} domain.Response "Chapa withdrawal process initiated successfully" -// @Failure 400 {object} domain.ErrorResponse "Invalid request body" -// @Failure 401 {object} domain.ErrorResponse "Unauthorized" -// @Failure 422 {object} domain.ErrorResponse "Unprocessable entity" -// @Failure 500 {object} domain.ErrorResponse "Internal server error" -// @Router /api/v1/chapa/payments/withdraw [post] -func (h *Handler) InitiateWithdrawal(c *fiber.Ctx) error { - userID, ok := c.Locals("user_id").(int64) - if !ok { - return domain.UnProcessableEntityResponse(c) - } - - var req domain.ChapaWithdrawalRequest - if err := c.BodyParser(&req); err != nil { - return domain.UnProcessableEntityResponse(c) - } - - withdrawal, err := h.chapaSvc.InitiateWithdrawal(c.Context(), userID, req) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to initiate Chapa withdrawal", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Chapa withdrawal process initiated successfully", - StatusCode: 200, - Success: true, - Data: withdrawal, - }) -} - -// GetPaymentReceipt godoc -// @Summary Get Chapa Payment Receipt URL -// @Description Retrieve the Chapa payment receipt URL using the reference ID -// @Tags Chapa -// @Accept json -// @Produce json -// @Param chapa_ref path string true "Chapa Reference ID" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/payments/receipt/{chapa_ref} [get] -func (h *Handler) GetPaymentReceipt(c *fiber.Ctx) error { - chapaRef := c.Params("chapa_ref") - if chapaRef == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to get Chapa payment receipt", - Error: "Chapa reference ID is required", - }) - } - - receiptURL, err := h.chapaSvc.GetPaymentReceiptURL(chapaRef) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to get Chapa payment receipt", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Payment receipt URL generated successfully", - Data: receiptURL, - StatusCode: 200, - Success: true, - }) -} - -// GetAllTransfers godoc -// @Summary Get all Chapa transfers -// @Description Retrieve all transfer records from Chapa -// @Tags Chapa -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/transfers [get] -func (h *Handler) GetAllTransfers(c *fiber.Ctx) error { - transfers, err := h.chapaSvc.GetAllTransfers(c.Context()) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to fetch Chapa transfers", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Chapa transfers retrieved successfully", - Data: transfers, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetAccountBalance godoc -// @Summary Get Chapa account balance -// @Description Retrieve Chapa account balance, optionally filtered by currency code (e.g., ETB, USD) -// @Tags Chapa -// @Accept json -// @Produce json -// @Param currency_code query string false "Currency code (optional)" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/balances [get] -func (h *Handler) GetAccountBalance(c *fiber.Ctx) error { - currencyCode := c.Query("currency_code", "") - - balances, err := h.chapaSvc.GetAccountBalance(c.Context(), currencyCode) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to fetch Chapa account balance", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Chapa account balances retrieved successfully", - Data: balances, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// SwapCurrency godoc -// @Summary Swap currency using Chapa API -// @Description Convert an amount from one currency to another using Chapa's currency swap API -// @Tags Chapa -// @Accept json -// @Produce json -// @Param request body domain.SwapRequest true "Swap request payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/chapa/swap [post] -func (h *Handler) SwapCurrency(c *fiber.Ctx) error { - var reqBody domain.SwapRequest - - // Parse request body - if err := c.BodyParser(&reqBody); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request payload", - Error: err.Error(), - }) - } - - // Validate input - if reqBody.From == "" || reqBody.To == "" || reqBody.Amount <= 0 { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Missing or invalid swap parameters", - Error: "from, to, and amount are required fields", - }) - } - - // Call service - resp, err := h.chapaSvc.SwapCurrency(c.Context(), reqBody) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to perform currency swap", - Error: err.Error(), - }) - } - - // Success response - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Currency swapped successfully", - Data: resp, - StatusCode: fiber.StatusOK, - Success: true, - }) -} diff --git a/internal/web_server/handlers/company_handler.go b/internal/web_server/handlers/company_handler.go deleted file mode 100644 index d353a29..0000000 --- a/internal/web_server/handlers/company_handler.go +++ /dev/null @@ -1,435 +0,0 @@ -package handlers - -import ( - "errors" - "fmt" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// CreateCompany godoc -// @Summary Create a company -// @Description Creates a company -// @Tags company -// @Accept json -// @Produce json -// @Param createCompany body domain.CreateCompanyReq true "Creates company" -// @Success 200 {object} domain.CompanyRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/company [post] -func (h *Handler) CreateCompany(c *fiber.Ctx) error { - var req domain.CreateCompanyReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("CreateCompanyReq failed", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate create company", - zap.String("errMsg", errMsg), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - user, err := h.userSvc.GetUserByID(c.Context(), req.AdminID) - if err != nil { - h.mongoLoggerSvc.Error("Error fetching user", - zap.Int("admin_id", int(req.AdminID)), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - // Create Company Wallet - newWallet, err := h.walletSvc.CreateWallet(c.Context(), domain.CreateWallet{ - IsWithdraw: false, - IsBettable: true, - IsTransferable: true, - UserID: req.AdminID, - Type: domain.CompanyWalletType, - }) - - if err != nil { - h.mongoLoggerSvc.Error("Create Company Wallet failed", - zap.Int64("admin", req.AdminID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - company, err := h.companySvc.CreateCompany(c.Context(), domain.CreateCompany{ - Name: req.Name, - AdminID: user.ID, - WalletID: newWallet.ID, - DeductedPercentage: req.DeductedPercentage, - Slug: req.Slug, - IsActive: req.IsActive, - }) - - if err != nil { - if errors.Is(err, domain.ErrWalletIDDuplicate) { - h.mongoLoggerSvc.Error("CreateCompanyReq failed to create company", - zap.Int64("userID", user.ID), - zap.String("name", req.Name), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - - return fiber.NewError(fiber.StatusBadRequest, "this admin already has a company assigned to him") - } - - h.mongoLoggerSvc.Error("CreateCompanyReq failed to create company", - zap.Int64("userID", user.ID), - zap.String("name", req.Name), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - err = h.userSvc.UpdateUserCompany(c.Context(), user.ID, company.ID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to update user company", - zap.Int64("userID", user.ID), - zap.Int64("companyID", company.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - res := domain.ConvertCompany(company) - - return response.WriteJSON(c, fiber.StatusCreated, "Company Created", res, nil) -} - -// GetAllCompanies godoc -// @Summary Gets all companies -// @Description Gets all companies -// @Tags company -// @Accept json -// @Produce json -// @Success 200 {array} domain.GetCompanyRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/company [get] -func (h *Handler) GetAllCompanies(c *fiber.Ctx) error { - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - createdBeforeQuery := c.Query("created_before") - var createdBefore domain.ValidTime - if createdBeforeQuery != "" { - createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_before format", - zap.String("createdBeforeQuery", createdBeforeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format") - } - createdBefore = domain.ValidTime{ - Value: createdBeforeParsed, - Valid: true, - } - } - - createdAfterQuery := c.Query("created_after") - var createdAfter domain.ValidTime - if createdAfterQuery != "" { - createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_after format", - zap.String("created_after", createdAfterQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format") - } - createdAfter = domain.ValidTime{ - Value: createdAfterParsed, - Valid: true, - } - } - - companies, err := h.companySvc.GetAllCompanies(c.Context(), domain.CompanyFilter{ - Query: searchString, - CreatedBefore: createdBefore, - CreatedAfter: createdAfter, - }) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get companies", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var result []domain.GetCompanyRes = make([]domain.GetCompanyRes, 0, len(companies)) - - for _, company := range companies { - result = append(result, domain.ConvertGetCompany(company)) - } - - return response.WriteJSON(c, fiber.StatusOK, "All Companies retrieved", result, nil) - -} - -// GetCompanyByID godoc -// @Summary Gets company by id -// @Description Gets a single company by id -// @Tags company -// @Accept json -// @Produce json -// @Param id path int true "Company ID" -// @Success 200 {object} domain.GetCompanyRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/company/{id} [get] -func (h *Handler) GetCompanyByID(c *fiber.Ctx) error { - - companyID := c.Params("id") - id, err := strconv.ParseInt(companyID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid company ID", - zap.String("companyID", companyID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid company ID") - } - - company, err := h.companySvc.GetCompanyByID(c.Context(), id) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get company by ID", - zap.Int64("companyID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertGetCompany(company) - - return response.WriteJSON(c, fiber.StatusOK, "Company retrieved successfully", res, nil) -} - -// GetCompanyForAdmin godoc -// @Summary Gets company by id -// @Description Gets a single company by id -// @Tags company -// @Accept json -// @Produce json -// @Success 200 {object} domain.GetCompanyRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/admin-company [get] -func (h *Handler) GetCompanyForAdmin(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - - if !companyID.Valid { - h.mongoLoggerSvc.Error("Invalid company ID", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Invalid company ID") - } - company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get company by ID", - zap.Int64("companyID", companyID.Value), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertGetCompany(company) - - return response.WriteJSON(c, fiber.StatusOK, "Company retrieved successfully", res, nil) -} - -// GetAllCompanies godoc -// @Summary Gets all companies -// @Description Gets all companies -// @Tags company -// @Accept json -// @Produce json -// @Success 200 {array} domain.CompanyRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/search/company [get] -func (h *Handler) SearchCompany(c *fiber.Ctx) error { - searchQuery := c.Query("q") - if searchQuery == "" { - h.mongoLoggerSvc.Info("Search query is required", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Search query is required") - } - companies, err := h.companySvc.SearchCompanyByName(c.Context(), searchQuery) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get companies", - zap.String("search query", searchQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var result []domain.GetCompanyRes = make([]domain.GetCompanyRes, 0, len(companies)) - - for _, company := range companies { - result = append(result, domain.ConvertGetCompany(company)) - } - - return response.WriteJSON(c, fiber.StatusOK, "All Companies retrieved", result, nil) - -} - -// UpdateCompany godoc -// @Summary Updates a company -// @Description Updates a company -// @Tags company -// @Accept json -// @Produce json -// @Param id path int true "Company ID" -// @Param updateCompany body domain.UpdateCompanyReq true "Update Company" -// @Success 200 {object} domain.CompanyRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/company/{id} [put] -func (h *Handler) UpdateCompany(c *fiber.Ctx) error { - - companyID := c.Params("id") - id, err := strconv.ParseInt(companyID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid company ID", - zap.String("companyID", companyID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid company ID") - } - - var req domain.UpdateCompanyReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("UpdateCompanyReq failed", - zap.String("companyID", companyID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("UpdateCompanyReq failed to validate", - zap.String("companyID", companyID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err = h.companySvc.UpdateCompany(c.Context(), domain.ConvertUpdateCompanyReq(req, id)) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to update company", - zap.Int64("companyID", id), - zap.Any("req", req), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Company Updated", nil, nil) - -} - -// DeleteCompany godoc -// @Summary Delete the company -// @Description Delete the company -// @Tags company -// @Accept json -// @Produce json -// @Param id path int true "Company ID"" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/company/{id} [delete] -func (h *Handler) DeleteCompany(c *fiber.Ctx) error { - - companyID := c.Params("id") - id, err := strconv.ParseInt(companyID, 10, 64) - - if err != nil { - h.mongoLoggerSvc.Info("Invalid Company ID", - zap.String("companyID", companyID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid Company ID") - } - - err = h.companySvc.DeleteCompany(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Info("Failed to delete by ID", - zap.Int64("Company ID", id), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Company removed successfully", nil, nil) - -} diff --git a/internal/web_server/handlers/currency.go b/internal/web_server/handlers/currency.go index dd9541f..ad78e3e 100644 --- a/internal/web_server/handlers/currency.go +++ b/internal/web_server/handlers/currency.go @@ -1,7 +1,8 @@ package handlers import ( - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "Yimaru-Backend/internal/domain" + "github.com/gofiber/fiber/v2" ) diff --git a/internal/web_server/handlers/customer.go b/internal/web_server/handlers/customer.go deleted file mode 100644 index ec24fa4..0000000 --- a/internal/web_server/handlers/customer.go +++ /dev/null @@ -1,877 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -type CustomersRes struct { - ID int64 `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email string `json:"email"` - PhoneNumber string `json:"phone_number"` - Role domain.Role `json:"role"` - EmailVerified bool `json:"email_verified"` - PhoneVerified bool `json:"phone_verified"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - LastLogin time.Time `json:"last_login"` - SuspendedAt time.Time `json:"suspended_at"` - Suspended bool `json:"suspended"` - CompanyID int64 `json:"company_id"` - CompanyName string `json:"company_name"` -} - -// GetAllCustomers godoc -// @Summary Get all Customers -// @Description Get all Customers -// @Tags customer -// @Accept json -// @Produce json -// @Param page query int false "Page number" -// @Param page_size query int false "Page size" -// @Success 200 {object} CustomersRes -// @Failure 400 {object} response.APIResponse -// @Failure 401 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/customer [get] -func (h *Handler) GetAllCustomers(c *fiber.Ctx) error { - role := c.Locals("role").(domain.Role) - companyId := c.Locals("company_id").(domain.ValidInt64) - - // Checking to make sure that admin user has a company id in the token - if role != domain.RoleSuperAdmin && !companyId.Valid { - h.mongoLoggerSvc.Error("Cannot get company ID from context", - zap.String("role", string(role)), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID") - } - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - createdBeforeQuery := c.Query("created_before") - var createdBefore domain.ValidTime - if createdBeforeQuery != "" { - createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_before format", - zap.String("createdBefore", createdBeforeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format") - } - createdBefore = domain.ValidTime{ - Value: createdBeforeParsed, - Valid: true, - } - } - - createdAfterQuery := c.Query("created_after") - var createdAfter domain.ValidTime - if createdAfterQuery != "" { - createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_after format", - zap.String("createdAfter", createdAfterQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format") - } - createdAfter = domain.ValidTime{ - Value: createdAfterParsed, - Valid: true, - } - } - - filter := domain.UserFilter{ - Role: string(domain.RoleCustomer), - CompanyID: companyId, - Page: domain.ValidInt{ - Value: c.QueryInt("page", 1) - 1, - Valid: true, - }, - PageSize: domain.ValidInt{ - Value: c.QueryInt("page_size", 10), - Valid: true, - }, - Query: searchString, - CreatedBefore: createdBefore, - CreatedAfter: createdAfter, - } - valErrs, ok := h.validator.Validate(c, filter) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate GetAllCustomer filters", - zap.Any("filter", filter), - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("errMsg", errMsg), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - customers, total, err := h.userSvc.GetAllUsers(c.Context(), filter) - if err != nil { - h.mongoLoggerSvc.Error("GetAllCustomers failed to get all users", - zap.Any("filter", filter), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get Customers:"+err.Error()) - } - - var result []CustomersRes = make([]CustomersRes, len(customers)) - for index, customer := range customers { - lastLogin, err := h.authSvc.GetLastLogin(c.Context(), customer.ID) - if err != nil { - if err == authentication.ErrRefreshTokenNotFound { - lastLogin = &customer.CreatedAt - } else { - h.mongoLoggerSvc.Error("Failed to get user last login", - zap.Int64("userID", customer.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, - "Failed to retrieve user last login:"+err.Error()) - } - } - - if !customer.CompanyID.Valid { - h.mongoLoggerSvc.Error("Invalid user company ID", - zap.Int64("userID", customer.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, - "Failed to retrieve company id for customer:") - } - - company, err := h.companySvc.GetCompanyByID(c.Context(), customer.CompanyID.Value) - - if err != nil { - h.mongoLoggerSvc.Error("Invalid user company value", - zap.Int64("userID", customer.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, - "Failed to fetch company for customer:") - } - result[index] = CustomersRes{ - ID: customer.ID, - FirstName: customer.FirstName, - LastName: customer.LastName, - Email: customer.Email, - PhoneNumber: customer.PhoneNumber, - Role: customer.Role, - EmailVerified: customer.EmailVerified, - PhoneVerified: customer.PhoneVerified, - CreatedAt: customer.CreatedAt, - UpdatedAt: customer.UpdatedAt, - SuspendedAt: customer.SuspendedAt, - Suspended: customer.Suspended, - LastLogin: *lastLogin, - CompanyID: company.ID, - CompanyName: company.Name, - } - } - - return response.WritePaginatedJSON(c, fiber.StatusOK, "Customers retrieved successfully", result, nil, filter.Page.Value, int(total)) - -} - -// GetAllTenantCustomers godoc -// @Summary Get all Customers -// @Description Get all Customers -// @Tags customer -// @Accept json -// @Produce json -// @Param page query int false "Page number" -// @Param page_size query int false "Page size" -// @Success 200 {object} CustomersRes -// @Failure 400 {object} response.APIResponse -// @Failure 401 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/tenant/{tenant_slug}/customer [get] -func (h *Handler) GetAllTenantCustomers(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - createdBeforeQuery := c.Query("created_before") - var createdBefore domain.ValidTime - if createdBeforeQuery != "" { - createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_before format", - zap.String("createdBefore", createdBeforeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format") - } - createdBefore = domain.ValidTime{ - Value: createdBeforeParsed, - Valid: true, - } - } - - createdAfterQuery := c.Query("created_after") - var createdAfter domain.ValidTime - if createdAfterQuery != "" { - createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_after format", - zap.String("createdAfter", createdAfterQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format") - } - createdAfter = domain.ValidTime{ - Value: createdAfterParsed, - Valid: true, - } - } - - filter := domain.UserFilter{ - Role: string(domain.RoleCustomer), - CompanyID: companyID, - Page: domain.ValidInt{ - Value: c.QueryInt("page", 1) - 1, - Valid: true, - }, - PageSize: domain.ValidInt{ - Value: c.QueryInt("page_size", 10), - Valid: true, - }, - Query: searchString, - CreatedBefore: createdBefore, - CreatedAfter: createdAfter, - } - valErrs, ok := h.validator.Validate(c, filter) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate GetAllCustomer filters", - zap.Any("filter", filter), - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("errMsg", errMsg), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - customers, total, err := h.userSvc.GetAllUsers(c.Context(), filter) - if err != nil { - h.mongoLoggerSvc.Error("GetAllCustomers failed to get all users", - zap.Any("filter", filter), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get Customers:"+err.Error()) - } - - var result []CustomersRes = make([]CustomersRes, len(customers)) - for index, customer := range customers { - lastLogin, err := h.authSvc.GetLastLogin(c.Context(), customer.ID) - if err != nil { - if err == authentication.ErrRefreshTokenNotFound { - lastLogin = &customer.CreatedAt - } else { - h.mongoLoggerSvc.Error("Failed to get user last login", - zap.Int64("userID", customer.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, - "Failed to retrieve user last login:"+err.Error()) - } - } - - if !customer.CompanyID.Valid { - h.mongoLoggerSvc.Error("Invalid user company ID", - zap.Int64("userID", customer.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, - "Failed to retrieve company id for customer:") - } - - company, err := h.companySvc.GetCompanyByID(c.Context(), customer.CompanyID.Value) - - if err != nil { - h.mongoLoggerSvc.Error("Invalid user company value", - zap.Int64("userID", customer.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, - "Failed to fetch company for customer:") - } - result[index] = CustomersRes{ - ID: customer.ID, - FirstName: customer.FirstName, - LastName: customer.LastName, - Email: customer.Email, - PhoneNumber: customer.PhoneNumber, - Role: customer.Role, - EmailVerified: customer.EmailVerified, - PhoneVerified: customer.PhoneVerified, - CreatedAt: customer.CreatedAt, - UpdatedAt: customer.UpdatedAt, - SuspendedAt: customer.SuspendedAt, - Suspended: customer.Suspended, - LastLogin: *lastLogin, - CompanyID: company.ID, - CompanyName: company.Name, - } - } - - return response.WritePaginatedJSON(c, fiber.StatusOK, "Customers retrieved successfully", result, nil, filter.Page.Value, int(total)) - -} - -// GetCustomerByID godoc -// @Summary Get customer by id -// @Description Get a single customer by id -// @Tags customer -// @Accept json -// @Produce json -// @Param id path int true "User ID" -// @Success 200 {object} CustomersRes -// @Failure 400 {object} response.APIResponse -// @Failure 401 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/customer/{id} [get] -func (h *Handler) GetCustomerByID(c *fiber.Ctx) error { - userIDstr := c.Params("id") - userID, err := strconv.ParseInt(userIDstr, 10, 64) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid customers ID") - } - - user, err := h.userSvc.GetUserByID(c.Context(), userID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get customers", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get customers:"+err.Error()) - } - - lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID) - if err != nil { - if err != authentication.ErrRefreshTokenNotFound { - h.mongoLoggerSvc.Error("Failed to get user last login", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login:"+err.Error()) - } - - lastLogin = &user.CreatedAt - } - - if !user.CompanyID.Valid { - h.mongoLoggerSvc.Error("Invalid user company ID", - zap.Int64("userID", user.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, - "Failed to retrieve company id for customer:") - } - - company, err := h.companySvc.GetCompanyByID(c.Context(), user.CompanyID.Value) - - if err != nil { - h.mongoLoggerSvc.Error("Invalid user company value", - zap.Int64("userID", user.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, - "Failed to fetch company for customer:") - } - res := CustomersRes{ - ID: user.ID, - FirstName: user.FirstName, - LastName: user.LastName, - Email: user.Email, - PhoneNumber: user.PhoneNumber, - Role: user.Role, - EmailVerified: user.EmailVerified, - PhoneVerified: user.PhoneVerified, - CreatedAt: user.CreatedAt, - UpdatedAt: user.UpdatedAt, - SuspendedAt: user.SuspendedAt, - Suspended: user.Suspended, - LastLogin: *lastLogin, - CompanyID: company.ID, - CompanyName: company.Name, - } - - return response.WriteJSON(c, fiber.StatusOK, "User retrieved successfully", res, nil) -} - -// GetCustomerByID godoc -// @Summary Get customer by id -// @Description Get a single customer by id -// @Tags customer -// @Accept json -// @Produce json -// @Param id path int true "User ID" -// @Success 200 {object} CustomersRes -// @Failure 400 {object} response.APIResponse -// @Failure 401 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/tenant/{tenant_slug}/customer/{id} [get] -func (h *Handler) GetTenantCustomerByID(c *fiber.Ctx) error { - - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - userIDstr := c.Params("id") - userID, err := strconv.ParseInt(userIDstr, 10, 64) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid customers ID") - } - - user, err := h.userSvc.GetUserByID(c.Context(), userID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get customers", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get customers:"+err.Error()) - } - - lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID) - if err != nil { - if err != authentication.ErrRefreshTokenNotFound { - h.mongoLoggerSvc.Error("Failed to get user last login", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login:"+err.Error()) - } - - lastLogin = &user.CreatedAt - } - - if !user.CompanyID.Valid || user.CompanyID.Value != companyID.Value { - h.mongoLoggerSvc.Error("Failed to get customer", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get customer:"+err.Error()) - } - - company, err := h.companySvc.GetCompanyByID(c.Context(), user.CompanyID.Value) - - if err != nil { - h.mongoLoggerSvc.Error("Invalid user company value", - zap.Int64("userID", user.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, - "Failed to fetch company for customer:") - } - res := CustomersRes{ - ID: user.ID, - FirstName: user.FirstName, - LastName: user.LastName, - Email: user.Email, - PhoneNumber: user.PhoneNumber, - Role: user.Role, - EmailVerified: user.EmailVerified, - PhoneVerified: user.PhoneVerified, - CreatedAt: user.CreatedAt, - UpdatedAt: user.UpdatedAt, - SuspendedAt: user.SuspendedAt, - Suspended: user.Suspended, - LastLogin: *lastLogin, - CompanyID: company.ID, - CompanyName: company.Name, - } - - return response.WriteJSON(c, fiber.StatusOK, "User retrieved successfully", res, nil) -} - -// GetTenantCustomerBets godoc -// @Summary Get tenant customer bets -// @Description Get tenant customer bets -// @Tags customer -// @Accept json -// @Produce json -// @Param id path int true "User ID" -// @Success 200 {object} CustomersRes -// @Failure 400 {object} response.APIResponse -// @Failure 401 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/tenant/{tenant_slug}/customer/{id}/bets [get] -func (h *Handler) GetTenantCustomerBets(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - userIDstr := c.Params("id") - userID, err := strconv.ParseInt(userIDstr, 10, 64) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid customers ID") - } - - user, err := h.userSvc.GetUserByID(c.Context(), userID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get customers", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get customers:"+err.Error()) - } - - if !user.CompanyID.Valid || user.CompanyID.Value != companyID.Value { - h.mongoLoggerSvc.Warn("User Attempt to access another companies customer", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get customer bet") - } - - bets, err := h.betSvc.GetBetByUserID(c.Context(), user.ID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get user bets", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get user bet:"+err.Error()) - } - - res := make([]domain.BetRes, len(bets)) - for i, bet := range bets { - res[i] = domain.ConvertBet(bet) - } - - return response.WriteJSON(c, fiber.StatusOK, "User's Bets retrieved successfully", res, nil) -} - -// GetCustomerBets godoc -// @Summary Get customer bets -// @Description Get customer bets -// @Tags customer -// @Accept json -// @Produce json -// @Param id path int true "User ID" -// @Success 200 {object} CustomersRes -// @Failure 400 {object} response.APIResponse -// @Failure 401 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/customer/{id}/bets [get] -func (h *Handler) GetCustomerBets(c *fiber.Ctx) error { - userIDstr := c.Params("id") - userID, err := strconv.ParseInt(userIDstr, 10, 64) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid customers ID") - } - - user, err := h.userSvc.GetUserByID(c.Context(), userID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get customers", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get customers:"+err.Error()) - } - - bets, err := h.betSvc.GetBetByUserID(c.Context(), user.ID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get user bets", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get user bet:"+err.Error()) - } - - res := make([]domain.BetRes, len(bets)) - for i, bet := range bets { - res[i] = domain.ConvertBet(bet) - } - - return response.WriteJSON(c, fiber.StatusOK, "User's Bets retrieved successfully", res, nil) -} - -type updateCustomerReq struct { - FirstName string `json:"first_name" example:"John"` - LastName string `json:"last_name" example:"Doe"` - Suspended bool `json:"suspended" example:"false"` -} - -// UpdateCustomers godoc -// @Summary Update Customers -// @Description Update Customers -// @Tags customer -// @Accept json -// @Produce json -// @Param Customers body updateCustomerReq true "Update Customers" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 401 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/customer/{id} [put] -func (h *Handler) UpdateCustomer(c *fiber.Ctx) error { - - var req updateCustomerReq - - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Error("UpdateCustomers invalid request body", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body"+err.Error()) - } - - valErrs, ok := h.validator.Validate(c, req) - - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate UpdateCustomerReq", - zap.Any("request", req), - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("ErrMsg", errMsg), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - CustomersIdStr := c.Params("id") - CustomersId, err := strconv.ParseInt(CustomersIdStr, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid Customers ID", - zap.String("userID", CustomersIdStr), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid Customers ID") - } - - // var companyID domain.ValidInt64 - // role := c.Locals("role").(domain.Role) - // if req.CompanyID != nil { - // if role != domain.RoleSuperAdmin { - // h.logger.Error("UpdateCustomers failed", "error", err) - // return response.WriteJSON(c, fiber.StatusUnauthorized, "This user role cannot modify company ID", nil, nil) - // } - // companyID = domain.ValidInt64{ - // Value: *req.CompanyID, - // Valid: true, - // } - // } - - err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{ - UserId: CustomersId, - FirstName: domain.ValidString{ - Value: req.FirstName, - Valid: req.FirstName != "", - }, - LastName: domain.ValidString{ - Value: req.LastName, - Valid: req.LastName != "", - }, - Suspended: domain.ValidBool{ - Value: req.Suspended, - Valid: true, - }, - }, - ) - if err != nil { - h.mongoLoggerSvc.Error("Failed to update Customers", - zap.Int64("userID", CustomersId), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update Customers:"+err.Error()) - } - return response.WriteJSON(c, fiber.StatusOK, "Customers updated successfully", nil, nil) - -} - -// UpdateTenantCustomer godoc -// @Summary Update Customers -// @Description Update Customers -// @Tags customer -// @Accept json -// @Produce json -// @Param Customers body updateCustomerReq true "Update Customers" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 401 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/tenant/{tenant_slug}/customer/{id} [put] -func (h *Handler) UpdateTenantCustomer(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - var req updateCustomerReq - - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Error("UpdateCustomers invalid request body", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body"+err.Error()) - } - - valErrs, ok := h.validator.Validate(c, req) - - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate UpdateCustomerReq", - zap.Any("request", req), - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("ErrMsg", errMsg), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - CustomersIdStr := c.Params("id") - CustomersId, err := strconv.ParseInt(CustomersIdStr, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid Customers ID", - zap.String("userID", CustomersIdStr), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid Customers ID") - } - - user, err := h.userSvc.GetUserByID(c.Context(), CustomersId) - if err != nil { - h.mongoLoggerSvc.Info("Customers Not Found", - zap.String("userID", CustomersIdStr), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Customers Not Found") - } - - if user.CompanyID.Value != companyID.Value { - h.mongoLoggerSvc.Warn("User Attempt to update another companies customer", - zap.String("userID", CustomersIdStr), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Customers Not Found") - } - err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{ - UserId: CustomersId, - FirstName: domain.ValidString{ - Value: req.FirstName, - Valid: req.FirstName != "", - }, - LastName: domain.ValidString{ - Value: req.LastName, - Valid: req.LastName != "", - }, - Suspended: domain.ValidBool{ - Value: req.Suspended, - Valid: true, - }, - }, - ) - if err != nil { - h.mongoLoggerSvc.Error("Failed to update Customers", - zap.Int64("userID", CustomersId), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update Customers:"+err.Error()) - } - return response.WriteJSON(c, fiber.StatusOK, "Customers updated successfully", nil, nil) - -} diff --git a/internal/web_server/handlers/direct_deposit.go b/internal/web_server/handlers/direct_deposit.go deleted file mode 100644 index 23b6ae6..0000000 --- a/internal/web_server/handlers/direct_deposit.go +++ /dev/null @@ -1,267 +0,0 @@ -package handlers - -import ( - "fmt" - "math" - "strconv" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" -) - -// CreateDirectDeposit godoc -// @Summary Create a new direct deposit -// @Description Creates a direct deposit for a customer and notifies both the customer and admins -// @Tags DirectDeposit -// @Accept json -// @Produce json -// @Param body body domain.CreateDirectDeposit true "Direct deposit details" -// @Success 200 {object} domain.Response{data=domain.DirectDeposit} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/direct-deposits [post] -func (h *Handler) CreateDirectDeposit(c *fiber.Ctx) error { - var req domain.CreateDirectDeposit - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request payload", - Error: err.Error(), - }) - } - - // Call service - deposit, err := h.directDepositSvc.CreateDirectDeposit(c.Context(), req) - if err != nil { - // h.logger.Error("CreateDirectDeposit error", err.Error()) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to create direct deposit", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Direct deposit created successfully", - Data: deposit, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetDirectDepositsByStatus godoc -// @Summary Get direct deposits by status -// @Description Fetches direct deposits filtered by status with pagination -// @Tags DirectDeposit -// @Accept json -// @Produce json -// @Param status query string true "Deposit status (e.g., PENDING, APPROVED, REJECTED)" -// @Param page query int false "Page number" -// @Param pageSize query int false "Page size" -// @Success 200 {object} domain.Response{data=[]domain.DirectDeposit} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/direct-deposits [get] -func (h *Handler) GetDirectDepositsByStatus(c *fiber.Ctx) error { - status := c.Query("status") - if status == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "status query parameter is required", - }) - } - - page, _ := strconv.Atoi(c.Query("page", "1")) - pageSize, _ := strconv.Atoi(c.Query("pageSize", "50")) - - deposits, total, err := h.directDepositSvc.GetDirectDepositsByStatus(c.Context(), status, page, pageSize) - if err != nil { - // h.logger.Error("GetDirectDepositsByStatus error", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch direct deposits", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: fmt.Sprintf("Direct deposits with status '%s' fetched successfully", status), - Data: deposits, - StatusCode: fiber.StatusOK, - Success: true, - // Optional: include pagination info - MetaData: map[string]any{ - "page": page, - "pageSize": pageSize, - "total": total, - "totalPage": int(math.Ceil(float64(total) / float64(pageSize))), - }, - }) -} - -// ApproveDirectDeposit godoc -// @Summary Approve a direct deposit -// @Description Approves a direct deposit by admin and credits customer wallet -// @Tags DirectDeposit -// @Accept json -// @Produce json -// @Param depositID path int true "Deposit ID" -// @Param adminID query int true "Admin ID performing the approval" -// @Success 200 {object} domain.Response{data=string} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/direct-deposits/{depositID}/approve [post] -func (h *Handler) ApproveDirectDeposit(c *fiber.Ctx) error { - depositID, err := strconv.Atoi(c.Params("depositID")) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid deposit ID", - Error: err.Error(), - }) - } - - adminID, err := strconv.Atoi(c.Query("adminID")) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid admin ID", - Error: err.Error(), - }) - } - - if err := h.directDepositSvc.ApproveDirectDeposit(c.Context(), depositID, adminID); err != nil { - // h.logger.Error("ApproveDirectDeposit error", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to approve direct deposit", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: fmt.Sprintf("Direct deposit #%d approved successfully", depositID), - Data: fmt.Sprintf("Deposit #%d approved", depositID), - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// RejectDirectDeposit godoc -// @Summary Reject a direct deposit -// @Description Rejects a direct deposit by admin and notifies the customer -// @Tags DirectDeposit -// @Accept json -// @Produce json -// @Param depositID path int true "Deposit ID" -// @Param adminID query int true "Admin ID performing the rejection" -// @Param reason query string true "Reason for rejection" -// @Success 200 {object} domain.Response{data=string} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/direct-deposits/{depositID}/reject [post] -func (h *Handler) RejectDirectDeposit(c *fiber.Ctx) error { - depositID, err := strconv.Atoi(c.Params("depositID")) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid deposit ID", - Error: err.Error(), - }) - } - - adminID, err := strconv.Atoi(c.Query("adminID")) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid admin ID", - Error: err.Error(), - }) - } - - reason := c.Query("reason") - if reason == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Rejection reason is required", - }) - } - - if err := h.directDepositSvc.RejectDirectDeposit(c.Context(), depositID, adminID, reason); err != nil { - // h.logger.Error("RejectDirectDeposit error", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to reject direct deposit", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: fmt.Sprintf("Direct deposit #%d rejected successfully", depositID), - Data: fmt.Sprintf("Deposit #%d rejected", depositID), - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetDirectDepositByID godoc -// @Summary Get a direct deposit by ID -// @Description Fetches a single direct deposit by its ID -// @Tags DirectDeposit -// @Accept json -// @Produce json -// @Param depositID path int true "Deposit ID" -// @Success 200 {object} domain.Response{data=domain.DirectDeposit} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/direct-deposits/{depositID} [get] -func (h *Handler) GetDirectDepositByID(c *fiber.Ctx) error { - depositID, err := strconv.Atoi(c.Params("depositID")) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid deposit ID", - Error: err.Error(), - }) - } - - deposit, err := h.directDepositSvc.GetDirectDepositByID(c.Context(), depositID) - if err != nil { - // h.logger.Error("GetDirectDepositByID error", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch direct deposit", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: fmt.Sprintf("Direct deposit #%d fetched successfully", depositID), - Data: deposit, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// DeleteDirectDeposit godoc -// @Summary Delete a direct deposit -// @Description Deletes a direct deposit by its ID -// @Tags DirectDeposit -// @Accept json -// @Produce json -// @Param depositID path int true "Deposit ID" -// @Success 200 {object} domain.Response{data=string} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/direct-deposits/{depositID} [delete] -func (h *Handler) DeleteDirectDeposit(c *fiber.Ctx) error { - depositID, err := strconv.Atoi(c.Params("depositID")) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid deposit ID", - Error: err.Error(), - }) - } - - if err := h.directDepositSvc.DeleteDirectDeposit(c.Context(), depositID); err != nil { - // h.logger.Error("DeleteDirectDeposit error", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to delete direct deposit", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: fmt.Sprintf("Direct deposit #%d deleted successfully", depositID), - Data: fmt.Sprintf("Deposit #%d deleted", depositID), - StatusCode: fiber.StatusOK, - Success: true, - }) -} diff --git a/internal/web_server/handlers/enet_pulse.go b/internal/web_server/handlers/enet_pulse.go deleted file mode 100644 index e89c7b2..0000000 --- a/internal/web_server/handlers/enet_pulse.go +++ /dev/null @@ -1,318 +0,0 @@ -package handlers - -import ( - "log" - "strconv" - "strings" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" -) - -// GetAllSports godoc -// @Summary Get all sports -// @Description Fetches all sports stored in the database -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulseSport} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/sports [get] -func (h *Handler) GetAllSports(c *fiber.Ctx) error { - // Call service - sports, err := h.enetPulseSvc.GetAllSports(c.Context()) - if err != nil { - log.Println("GetAllSports error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch sports", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Sports fetched successfully", - Data: sports, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetAllTournamentTemplates godoc -// @Summary Get all tournament templates -// @Description Fetches all tournament templates stored in the database -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulseTournamentTemplate} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/tournament-templates [get] -func (h *Handler) GetAllTournamentTemplates(c *fiber.Ctx) error { - // Call service - templates, err := h.enetPulseSvc.GetAllTournamentTemplates(c.Context()) - if err != nil { - log.Println("GetAllTournamentTemplates error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch tournament templates", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Tournament templates fetched successfully", - Data: templates, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetAllTournaments godoc -// @Summary Get all tournaments -// @Description Fetches all tournaments stored in the database -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulseTournament} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/tournaments [get] -func (h *Handler) GetAllTournaments(c *fiber.Ctx) error { - // Call service - tournaments, err := h.enetPulseSvc.GetAllTournaments(c.Context()) - if err != nil { - log.Println("GetAllTournaments error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch tournaments", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Tournaments fetched successfully", - Data: tournaments, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetAllTournamentStages godoc -// @Summary Get all tournament stages -// @Description Fetches all tournament stages stored in the database -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulseTournamentStage} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/tournament-stages [get] -func (h *Handler) GetAllTournamentStages(c *fiber.Ctx) error { - // Call service - stages, err := h.enetPulseSvc.GetAllTournamentStages(c.Context()) - if err != nil { - log.Println("GetAllTournamentStages error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch tournament stages", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Tournament stages fetched successfully", - Data: stages, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetFixturesByDate godoc -// @Summary Get all stored fixtures -// @Description Fetches all fixtures stored in the database -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulseFixture} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/fixtures [get] -func (h *Handler) GetFixturesByDate(c *fiber.Ctx) error { - // Call service to get all fixtures from DB - fixtures, err := h.enetPulseSvc.GetAllFixtures(c.Context()) - if err != nil { - log.Println("GetAllFixtures error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch fixtures from database", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Fixtures fetched successfully", - Data: fixtures, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetAllResults godoc -// @Summary Get all results -// @Description Fetches all EnetPulse match results stored in the database -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulseResult} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/results [get] -func (h *Handler) GetAllResults(c *fiber.Ctx) error { - // Call service - results, err := h.enetPulseSvc.GetAllResults(c.Context()) - if err != nil { - log.Println("GetAllResults error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch EnetPulse results", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "EnetPulse results fetched successfully", - Data: results, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetAllPreodds godoc -// @Summary Get all preodds -// @Description Fetches all EnetPulse pre-match odds stored in the database -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulsePreodds} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/preodds [get] -func (h *Handler) GetAllPreodds(c *fiber.Ctx) error { - // Call service - preodds, err := h.enetPulseSvc.GetAllPreodds(c.Context()) - if err != nil { - log.Println("GetAllPreodds error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch EnetPulse preodds", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "EnetPulse preodds fetched successfully", - Data: preodds, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetAllBettingOffers godoc -// @Summary Get all betting offers -// @Description Fetches all EnetPulse preodds betting offers stored in the database -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulsePreoddsBettingOffer} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/betting-offers [get] -func (h *Handler) GetAllBettingOffers(c *fiber.Ctx) error { - // Call service - offers, err := h.enetPulseSvc.GetAllBettingOffers(c.Context()) - if err != nil { - log.Println("GetAllBettingOffers error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch EnetPulse betting offers", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "EnetPulse betting offers fetched successfully", - Data: offers, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetAllPreoddsWithBettingOffers godoc -// @Summary Get all preodds with betting offers -// @Description Fetches all EnetPulse pre-match odds along with their associated betting offers stored in the database -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulsePreodds} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/preodds-with-offers [get] -func (h *Handler) GetAllPreoddsWithBettingOffers(c *fiber.Ctx) error { - // Call service - preodds, err := h.enetPulseSvc.GetAllPreoddsWithBettingOffers(c.Context()) - if err != nil { - log.Println("GetAllPreoddsWithBettingOffers error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch EnetPulse preodds with betting offers", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "EnetPulse preodds with betting offers fetched successfully", - Data: preodds, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetFixturesWithPreodds godoc -// @Summary Get fixtures with preodds -// @Description Fetches all EnetPulse fixtures along with their associated pre-match odds -// @Tags EnetPulse -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response{data=[]domain.EnetpulseFixtureWithPreodds} -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/enetpulse/fixtures/preodds [get] -func (h *Handler) GetFixturesWithPreodds(c *fiber.Ctx) error { - // Call service - fixtures, err := h.enetPulseSvc.GetFixturesWithPreodds(c.Context()) - if err != nil { - log.Println("GetFixturesWithPreodds error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch EnetPulse fixtures with preodds", - Error: err.Error(), - }) - } - - // Return success response - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "EnetPulse fixtures with preodds fetched successfully", - Data: fixtures, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// Helper: parse comma-separated string into []int -func ParseIntSlice(input string) []int { - if input == "" { - return nil - } - parts := strings.Split(input, ",") - result := make([]int, 0, len(parts)) - for _, p := range parts { - if n, err := strconv.Atoi(strings.TrimSpace(p)); err == nil { - result = append(result, n) - } - } - return result -} - -// Helper: convert []int to []int64 -func IntSliceToInt64Slice(input []int) []int64 { - if input == nil { - return nil - } - result := make([]int64, len(input)) - for i, v := range input { - result[i] = int64(v) - } - return result -} diff --git a/internal/web_server/handlers/event_handler.go b/internal/web_server/handlers/event_handler.go deleted file mode 100644 index 9178756..0000000 --- a/internal/web_server/handlers/event_handler.go +++ /dev/null @@ -1,1117 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - - - -func ParseLeagueIDFromQuery(c *fiber.Ctx) (domain.ValidInt64, error) { - leagueIDQuery := c.Query("league_id") - if leagueIDQuery != "" { - leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64) - if err != nil { - return domain.ValidInt64{}, fmt.Errorf("failed to parse league_id %v: %w", leagueIDQuery, err) - } - return domain.ValidInt64{ - Value: leagueIDInt, - Valid: true, - }, nil - } - return domain.ValidInt64{}, nil -} - -// @Summary Retrieve all upcoming events -// @Description Retrieve all upcoming events from the database -// @Tags prematch -// @Accept json -// @Produce json -// @Param page query int false "Page number" -// @Param page_size query int false "Page size" -// @Param league_id query string false "League ID Filter" -// @Param sport_id query string false "Sport ID Filter" -// @Param cc query string false "Country Code Filter" -// @Param first_start_time query string false "Start Time" -// @Param last_start_time query string false "End Time" -// @Success 200 {array} domain.BaseEvent -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/events [get] -func (h *Handler) GetAllEvents(c *fiber.Ctx) error { - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 10) - limit := domain.ValidInt32{ - Value: int32(pageSize), - Valid: true, - } - offset := domain.ValidInt32{ - Value: int32(page - 1), - Valid: true, - } - - leagueIDQuery := c.Query("league_id") - var leagueID domain.ValidInt64 - if leagueIDQuery != "" { - leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64) - if err != nil { - h.BadRequestLogger().Error("invalid league id", - zap.String("league_id", leagueIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - } - leagueID = domain.ValidInt64{ - Value: leagueIDInt, - Valid: true, - } - } - - // TODO: Go through the all the handler functions and change them into something like this - // leagueID, err := ParseLeagueIDFromQuery(c) - // if err != nil { - // h.BadRequestLogger().Info("invalid league id", zap.Error(err)) - // return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - // } - - sportIDQuery := c.Query("sport_id") - var sportID domain.ValidInt32 - if sportIDQuery != "" { - sportIDint, err := strconv.Atoi(sportIDQuery) - if err != nil { - h.BadRequestLogger().Info("invalid sport id", - zap.String("sportID", sportIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") - } - sportID = domain.ValidInt32{ - Value: int32(sportIDint), - Valid: true, - } - } - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - firstStartTimeQuery := c.Query("first_start_time") - var firstStartTime domain.ValidTime - if firstStartTimeQuery != "" { - firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery) - if err != nil { - h.BadRequestLogger().Info("invalid start_time format", - zap.String("first_start_time", firstStartTimeQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") - } - firstStartTime = domain.ValidTime{ - Value: firstStartTimeParsed, - Valid: true, - } - } - - lastStartTimeQuery := c.Query("last_start_time") - var lastStartTime domain.ValidTime - if lastStartTimeQuery != "" { - lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery) - if err != nil { - h.BadRequestLogger().Info("invalid last_start_time format", - zap.String("last_start_time", lastStartTimeQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") - } - lastStartTime = domain.ValidTime{ - Value: lastStartTimeParsed, - Valid: true, - } - } - - countryCodeQuery := c.Query("cc") - countryCode := domain.ValidString{ - Value: countryCodeQuery, - Valid: countryCodeQuery != "", - } - - isFeaturedQuery := c.Query("is_featured") - var isFeatured domain.ValidBool - if isFeaturedQuery != "" { - isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery) - if err != nil { - h.BadRequestLogger().Error("Failed to parse isFeatured", - zap.String("is_featured", isFeaturedQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_shop_bet") - } - - isFeatured = domain.ValidBool{ - Value: isFeaturedParsed, - Valid: true, - } - } - - isActiveQuery := c.Query("is_active") - var isActive domain.ValidBool - if isActiveQuery != "" { - isActiveParsed, err := strconv.ParseBool(isActiveQuery) - if err != nil { - h.BadRequestLogger().Error("Failed to parse isActive", - zap.String("is_active", isActiveQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_active") - } - - isActive = domain.ValidBool{ - Value: isActiveParsed, - Valid: true, - } - } - - statusQuery := c.Query("status") - var eventStatus domain.ValidEventStatus - if statusQuery != "" { - eventStatusParsed, err := domain.ParseEventStatus(statusQuery) - if err != nil { - h.BadRequestLogger().Error("Failed to parse statusQuery", - zap.String("status", statusQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid event status string") - } - eventStatus = domain.ValidEventStatus{ - Value: eventStatusParsed, - Valid: true, - } - } - events, total, err := h.eventSvc.GetAllEvents( - c.Context(), domain.EventFilter{ - SportID: sportID, - LeagueID: leagueID, - Query: searchString, - FirstStartTime: firstStartTime, - LastStartTime: lastStartTime, - Limit: limit, - Offset: offset, - CountryCode: countryCode, - Featured: isFeatured, - Active: isActive, - Status: eventStatus, - }) - - // fmt.Printf("League ID: %v", leagueID) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve all upcoming events", - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertEventResList(events) - - return response.WritePaginatedJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", res, nil, page, int(total)) -} - - -// @Summary Retrieve all upcoming events with settings -// @Description Retrieve all upcoming events settings from the database -// @Tags prematch -// @Accept json -// @Produce json -// @Param page query int false "Page number" -// @Param page_size query int false "Page size" -// @Param league_id query string false "League ID Filter" -// @Param sport_id query string false "Sport ID Filter" -// @Param cc query string false "Country Code Filter" -// @Param first_start_time query string false "Start Time" -// @Param last_start_time query string false "End Time" -// @Success 200 {array} domain.BaseEvent -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/events [get] -func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 10) - limit := domain.ValidInt32{ - Value: int32(pageSize), - Valid: true, - } - offset := domain.ValidInt32{ - Value: int32(page - 1), - Valid: true, - } - - leagueIDQuery := c.Query("league_id") - var leagueID domain.ValidInt64 - if leagueIDQuery != "" { - leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64) - if err != nil { - h.BadRequestLogger().Error("invalid league id", - zap.String("league_id", leagueIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - } - leagueID = domain.ValidInt64{ - Value: leagueIDInt, - Valid: true, - } - } - sportIDQuery := c.Query("sport_id") - var sportID domain.ValidInt32 - if sportIDQuery != "" { - sportIDint, err := strconv.Atoi(sportIDQuery) - if err != nil { - h.BadRequestLogger().Info("invalid sport id", - zap.String("sportID", sportIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") - } - sportID = domain.ValidInt32{ - Value: int32(sportIDint), - Valid: true, - } - } - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - firstStartTimeQuery := c.Query("first_start_time") - var firstStartTime domain.ValidTime - if firstStartTimeQuery != "" { - firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery) - if err != nil { - h.BadRequestLogger().Info("invalid start_time format", - zap.String("first_start_time", firstStartTimeQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") - } - firstStartTime = domain.ValidTime{ - Value: firstStartTimeParsed, - Valid: true, - } - } else { - firstStartTime = domain.ValidTime{ - Value: time.Now(), - Valid: true, - } - } - - lastStartTimeQuery := c.Query("last_start_time") - var lastStartTime domain.ValidTime - if lastStartTimeQuery != "" { - lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery) - if err != nil { - h.BadRequestLogger().Info("invalid last_start_time format", - zap.String("last_start_time", lastStartTimeQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") - } - lastStartTime = domain.ValidTime{ - Value: lastStartTimeParsed, - Valid: true, - } - } - - countryCodeQuery := c.Query("cc") - countryCode := domain.ValidString{ - Value: countryCodeQuery, - Valid: countryCodeQuery != "", - } - - isFeaturedQuery := c.Query("is_featured") - var isFeatured domain.ValidBool - if isFeaturedQuery != "" { - isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery) - if err != nil { - h.BadRequestLogger().Error("Failed to parse isFeatured", - zap.String("is_featured", isFeaturedQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_shop_bet") - } - - isFeatured = domain.ValidBool{ - Value: isFeaturedParsed, - Valid: true, - } - } - - events, total, err := h.eventSvc.GetEventsWithSettings( - c.Context(), companyID.Value, domain.EventFilter{ - SportID: sportID, - LeagueID: leagueID, - Query: searchString, - FirstStartTime: firstStartTime, - LastStartTime: lastStartTime, - Limit: limit, - Offset: offset, - CountryCode: countryCode, - Featured: isFeatured, - Status: domain.ValidEventStatus{ - Value: domain.STATUS_PENDING, - Valid: true, - }, - IsLive: domain.ValidBool{ - Value: false, - Valid: true, - }, - Active: domain.ValidBool{ - Value: true, - Valid: true, - }, - }) - - // fmt.Printf("League ID: %v", leagueID) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve all upcoming events", - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertEventWithSettingResList(events) - - return response.WritePaginatedJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", res, nil, page, int(total)) - -} - -// @Summary Retrieve all upcoming events with settings -// @Description Retrieve all upcoming events settings from the database -// @Tags prematch -// @Accept json -// @Produce json -// @Param page query int false "Page number" -// @Param page_size query int false "Page size" -// @Param league_id query string false "League ID Filter" -// @Param sport_id query string false "Sport ID Filter" -// @Param cc query string false "Country Code Filter" -// @Param first_start_time query string false "Start Time" -// @Param last_start_time query string false "End Time" -// @Success 200 {array} domain.BaseEvent -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/events [get] -func (h *Handler) GetTenantEvents(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 10) - limit := domain.ValidInt32{ - Value: int32(pageSize), - Valid: true, - } - offset := domain.ValidInt32{ - Value: int32(page - 1), - Valid: true, - } - - leagueIDQuery := c.Query("league_id") - var leagueID domain.ValidInt64 - if leagueIDQuery != "" { - leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64) - if err != nil { - h.BadRequestLogger().Error("invalid league id", - zap.String("league_id", leagueIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - } - leagueID = domain.ValidInt64{ - Value: leagueIDInt, - Valid: true, - } - } - sportIDQuery := c.Query("sport_id") - var sportID domain.ValidInt32 - if sportIDQuery != "" { - sportIDint, err := strconv.Atoi(sportIDQuery) - if err != nil { - h.BadRequestLogger().Info("invalid sport id", - zap.String("sportID", sportIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") - } - sportID = domain.ValidInt32{ - Value: int32(sportIDint), - Valid: true, - } - } - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - firstStartTimeQuery := c.Query("first_start_time") - var firstStartTime domain.ValidTime - if firstStartTimeQuery != "" { - firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery) - if err != nil { - h.BadRequestLogger().Info("invalid start_time format", - zap.String("first_start_time", firstStartTimeQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") - } - firstStartTime = domain.ValidTime{ - Value: firstStartTimeParsed, - Valid: true, - } - } - - lastStartTimeQuery := c.Query("last_start_time") - var lastStartTime domain.ValidTime - if lastStartTimeQuery != "" { - lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery) - if err != nil { - h.BadRequestLogger().Info("invalid last_start_time format", - zap.String("last_start_time", lastStartTimeQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format") - } - lastStartTime = domain.ValidTime{ - Value: lastStartTimeParsed, - Valid: true, - } - } - - countryCodeQuery := c.Query("cc") - countryCode := domain.ValidString{ - Value: countryCodeQuery, - Valid: countryCodeQuery != "", - } - - isFeaturedQuery := c.Query("is_featured") - var isFeatured domain.ValidBool - if isFeaturedQuery != "" { - isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery) - if err != nil { - h.BadRequestLogger().Error("Failed to parse isFeatured", - zap.String("is_featured", isFeaturedQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_featured") - } - - isFeatured = domain.ValidBool{ - Value: isFeaturedParsed, - Valid: true, - } - } - - isActiveQuery := c.Query("is_active") - var isActive domain.ValidBool - if isActiveQuery != "" { - isActiveParsed, err := strconv.ParseBool(isActiveQuery) - if err != nil { - h.BadRequestLogger().Error("Failed to parse isActive", - zap.String("is_active", isActiveQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "Failed to parse is_active") - } - - isActive = domain.ValidBool{ - Value: isActiveParsed, - Valid: true, - } - } - - statusQuery := c.Query("status") - var eventStatus domain.ValidEventStatus - if statusQuery != "" { - eventStatusParsed, err := domain.ParseEventStatus(statusQuery) - if err != nil { - h.BadRequestLogger().Error("Failed to parse statusQuery", - zap.String("is_featured", isFeaturedQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid event status string") - } - eventStatus = domain.ValidEventStatus{ - Value: eventStatusParsed, - Valid: true, - } - } - - events, total, err := h.eventSvc.GetEventsWithSettings( - c.Context(), companyID.Value, domain.EventFilter{ - SportID: sportID, - LeagueID: leagueID, - Query: searchString, - FirstStartTime: firstStartTime, - LastStartTime: lastStartTime, - Limit: limit, - Offset: offset, - CountryCode: countryCode, - Featured: isFeatured, - Status: eventStatus, - Active: isActive, - }) - - // fmt.Printf("League ID: %v", leagueID) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve all upcoming events", - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertEventWithSettingResList(events) - - return response.WritePaginatedJSON(c, fiber.StatusOK, "All upcoming events retrieved successfully", res, nil, page, int(total)) - -} - -type TopLeaguesRes struct { - Leagues []TopLeague `json:"leagues"` -} - -type TopLeague struct { - LeagueID int64 `json:"league_id"` - LeagueName string `json:"league_name"` - LeagueCC string `json:"league_cc"` - LeagueSportID int32 `json:"league_sport_id"` - Events []domain.EventWithSettingsRes `json:"events"` - // Total int64 `json:"total"` -} - -// @Summary Retrieve all top leagues -// @Description Retrieve all top leagues -// @Tags prematch -// @Accept json -// @Produce json -// @Success 200 {array} TopLeague -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/top-leagues [get] -func (h *Handler) GetTopLeagues(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - leagues, _, err := h.leagueSvc.GetAllLeaguesByCompany(c.Context(), companyID.Value, domain.LeagueFilter{ - IsFeatured: domain.ValidBool{ - Value: true, - Valid: true, - }, - }) - - if err != nil { - h.InternalServerErrorLogger().Error("Error while fetching top leagues", - zap.Int64("company_id", companyID.Value), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var topLeague []TopLeague = make([]TopLeague, 0, len(leagues)) - for _, league := range leagues { - events, _, err := h.eventSvc.GetEventsWithSettings( - c.Context(), companyID.Value, domain.EventFilter{ - LeagueID: domain.ValidInt64{ - Value: league.ID, - Valid: true, - }, - Active: domain.ValidBool{Value: true, Valid: true}, - }) - if err != nil { - h.InternalServerErrorLogger().Warn("Error while fetching events for top league", - zap.Int64("LeagueID", league.ID), - zap.Int64("company_id", companyID.Value), - zap.Error(err), - ) - } - topLeague = append(topLeague, TopLeague{ - LeagueID: league.ID, - LeagueName: league.Name, - LeagueCC: league.CountryCode.Value, - LeagueSportID: league.SportID, - Events: domain.ConvertEventWithSettingResList(events), - }) - } - - res := TopLeaguesRes{ - Leagues: topLeague, - } - return response.WriteJSON(c, fiber.StatusOK, "All top leagues events retrieved successfully", res, nil) - -} - -// @Summary Retrieve an upcoming by ID -// @Description Retrieve an upcoming event by ID -// @Tags prematch -// @Accept json -// @Produce json -// @Param id path string true "ID" -// @Success 200 {object} domain.BaseEvent -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/events/{id} [get] -func (h *Handler) GetEventByID(c *fiber.Ctx) error { - - idStr := c.Params("id") - eventID, err := strconv.ParseInt(idStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr)) - return fiber.NewError(fiber.StatusBadRequest, "Missing id") - } - - event, err := h.eventSvc.GetEventByID(c.Context(), eventID) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to get event by id", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertEventRes(event) - - return response.WriteJSON(c, fiber.StatusOK, "Upcoming event retrieved successfully", res, nil) - -} - -// @Summary Retrieve an upcoming by ID -// @Description Retrieve an upcoming event by ID -// @Tags prematch -// @Accept json -// @Produce json -// @Param id path string true "ID" -// @Success 200 {object} domain.BaseEvent -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}events/{id} [get] -func (h *Handler) GetTenantEventByID(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - idStr := c.Params("id") - eventID, err := strconv.ParseInt(idStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr)) - return fiber.NewError(fiber.StatusBadRequest, "Missing id") - } - - event, err := h.eventSvc.GetEventWithSettingByID(c.Context(), eventID, companyID.Value) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to get upcoming event by id", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertEventWitSettingRes(event) - - return response.WriteJSON(c, fiber.StatusOK, "Upcoming event retrieved successfully", res, nil) - -} - -// @Summary Retrieve bet outcomes by event id -// @Description Retrieve bet outcomes by event id -// @Tags prematch -// @Accept json -// @Produce json -// @Param id path string true "ID" -// @Success 200 {object} domain.BaseEvent -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/tenant/{tenant_slug}/events/{id}/bets [get] -func (h *Handler) GetTenantBetsByEventID(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - idStr := c.Params("id") - eventID, err := strconv.ParseInt(idStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr)) - return fiber.NewError(fiber.StatusBadRequest, "Missing id") - } - - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 10) - limit := domain.ValidInt32{ - Value: int32(pageSize), - Valid: true, - } - offset := domain.ValidInt32{ - Value: int32(page - 1), - Valid: true, - } - - statusQuery := c.Params("status") - var status domain.ValidOutcomeStatus - if statusQuery != "" { - statusIntParse, err := strconv.ParseInt(statusQuery, 10, 32) - if err != nil { - h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid status query") - } - - statusParsed, err := domain.ParseOutcomeStatus(int(statusIntParse)) - if err != nil { - h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid status query") - } - - status = domain.ValidOutcomeStatus{ - Value: statusParsed, - Valid: true, - } - } - - res, total, err := h.betSvc.GetBetOutcomeViewByEventID(c.Context(), eventID, domain.BetOutcomeViewFilter{ - OutcomeStatus: status, - CompanyID: companyID, - Limit: limit, - Offset: offset, - }) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to get upcoming event by id", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - return response.WritePaginatedJSON(c, fiber.StatusOK, "Bet Outcomes retrieved successfully", res, nil, page, int(total)) -} - -// @Summary Retrieve bet outcomes by event id -// @Description Retrieve bet outcomes by event id -// @Tags prematch -// @Accept json -// @Produce json -// @Param id path string true "ID" -// @Success 200 {object} domain.BaseEvent -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/events/{id}/bets [get] -func (h *Handler) GetBetsByEventID(c *fiber.Ctx) error { - idStr := c.Params("id") - eventID, err := strconv.ParseInt(idStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr)) - return fiber.NewError(fiber.StatusBadRequest, "Missing id") - } - - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 10) - limit := domain.ValidInt32{ - Value: int32(pageSize), - Valid: true, - } - offset := domain.ValidInt32{ - Value: int32(page - 1), - Valid: true, - } - - statusQuery := c.Params("status") - var status domain.ValidOutcomeStatus - if statusQuery != "" { - statusIntParse, err := strconv.ParseInt(statusQuery, 10, 32) - if err != nil { - h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid status query") - } - - statusParsed, err := domain.ParseOutcomeStatus(int(statusIntParse)) - if err != nil { - h.BadRequestLogger().Info("Failed to parse status", zap.String("status", statusQuery)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid status query") - } - - status = domain.ValidOutcomeStatus{ - Value: statusParsed, - Valid: true, - } - } - - res, total, err := h.betSvc.GetBetOutcomeViewByEventID(c.Context(), eventID, domain.BetOutcomeViewFilter{ - OutcomeStatus: status, - Limit: limit, - Offset: offset, - }) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to get upcoming event by id", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - return response.WritePaginatedJSON(c, fiber.StatusOK, "Bet Outcomes retrieved successfully", res, nil, page, int(total)) -} - -type UpdateEventStatusReq struct { -} - -// SetEventStatusToRemoved godoc -// @Summary Set the event status to removed -// @Description Set the event status to removed -// @Tags event -// @Accept json -// @Produce json -// @Param id path int true "Event ID" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/events/{id} [delete] -func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error { - idStr := c.Params("id") - eventID, err := strconv.ParseInt(idStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr)) - return fiber.NewError(fiber.StatusBadRequest, "Missing id") - } - - err = h.eventSvc.UpdateEventStatus(c.Context(), eventID, domain.STATUS_REMOVED) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update event status", - zap.Int64("EventID", eventID), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update event status") - } - - return response.WriteJSON(c, fiber.StatusOK, "Event updated successfully", nil, nil) - -} - -type UpdateEventSettingsReq struct { - Featured *bool `json:"is_featured" example:"true"` - IsActive *bool `json:"is_active" example:"true"` - WinningUpperLimit *int64 `json:"winning_upper_limit" example:"10000"` -} - -// UpdateEventSettings godoc -// @Summary update the event settings -// @Description Update the event settings -// @Tags event -// @Accept json -// @Produce json -// @Param id path int true "Event ID" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/events/{id}/settings [put] -func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error { - eventIDStr := c.Params("id") - - eventID, err := strconv.ParseInt(eventIDStr, 10, 64) - if err != nil { - h.BadRequestLogger().Error("invalid event id") - return fiber.NewError(fiber.StatusBadRequest, "invalid event id") - } - - var req UpdateEventSettingsReq - - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("Failed to parse event id", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - logFields := []zap.Field{ - zap.Int64("eventID", eventID), - zap.Any("is_featured", req.Featured), - zap.Any("is_active", req.IsActive), - zap.Any("winning_upper_limit", req.WinningUpperLimit), - } - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Error("Failed to update event settings", - append(logFields, zap.String("errMsg", errMsg))..., - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err = h.eventSvc.UpdateGlobalEventSettings(c.Context(), domain.UpdateGlobalEventSettings{ - EventID: eventID, - IsFeatured: domain.ConvertBoolPtr(req.Featured), - IsActive: domain.ConvertBoolPtr(req.IsActive), - WinningUpperLimit: domain.ConvertInt64Ptr(req.WinningUpperLimit), - }) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update event settings", append(logFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Event updated successfully", nil, nil) - -} - -// UpdateTenantEventSettings godoc -// @Summary update the event settings -// @Description Update the event settings -// @Tags event -// @Accept json -// @Produce json -// @Param id path int true "Event ID" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/tenant/{tenant_slug}/events/{id}/settings [put] -func (h *Handler) UpdateTenantEventSettings(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - eventIDStr := c.Params("id") - - eventID, err := strconv.ParseInt(eventIDStr, 10, 64) - if err != nil { - h.BadRequestLogger().Error("invalid event id") - return fiber.NewError(fiber.StatusBadRequest, "invalid event id") - } - - var req UpdateEventSettingsReq - - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("Failed to parse event id", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - logFields := []zap.Field{ - zap.Int64("eventID", eventID), - zap.Int64("companyID", companyID.Value), - zap.Any("is_featured", req.Featured), - zap.Any("is_active", req.IsActive), - zap.Any("winning_upper_limit", req.WinningUpperLimit), - } - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Error("Failed to update event settings", - append(logFields, zap.String("errMsg", errMsg))..., - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err = h.eventSvc.UpdateTenantEventSettings(c.Context(), domain.UpdateTenantEventSettings{ - CompanyID: companyID.Value, - EventID: eventID, - IsFeatured: domain.ConvertBoolPtr(req.Featured), - IsActive: domain.ConvertBoolPtr(req.IsActive), - WinningUpperLimit: domain.ConvertInt64Ptr(req.WinningUpperLimit), - }) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update event settings", append(logFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Event updated successfully", nil, nil) - -} - -type SetEventIsMonitoredReq struct { - IsMonitored bool `json:"is_monitored"` -} - -// SetEventIsMonitored godoc -// @Summary update the event is_monitored -// @Description Update the event is_monitored -// @Tags event -// @Accept json -// @Produce json -// @Param id path int true "Event ID" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/events/{id}/is_monitored [patch] -func (h *Handler) SetEventIsMonitored(c *fiber.Ctx) error { - idStr := c.Params("id") - eventID, err := strconv.ParseInt(idStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", idStr)) - return fiber.NewError(fiber.StatusBadRequest, "Missing id") - } - - var req SetEventIsMonitoredReq - - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("Failed to parse bet id", - zap.Int64("eventID", eventID), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - logFields := []zap.Field{ - zap.Int64("eventID", eventID), - zap.Any("is_featured", req.IsMonitored), - } - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Error("Failed to update event featured", - append(logFields, zap.String("errMsg", errMsg))..., - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err = h.eventSvc.UpdateEventMonitored(c.Context(), eventID, req.IsMonitored) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update event is_monitored", append(logFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Event updated successfully", nil, nil) - -} diff --git a/internal/web_server/handlers/event_stats_handler.go b/internal/web_server/handlers/event_stats_handler.go deleted file mode 100644 index db3d29c..0000000 --- a/internal/web_server/handlers/event_stats_handler.go +++ /dev/null @@ -1,123 +0,0 @@ -package handlers - -import ( - "strconv" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -func (h *Handler) GetTotalEventStats(c *fiber.Ctx) error { - leagueIDQuery := c.Query("league_id") - var leagueID domain.ValidInt64 - if leagueIDQuery != "" { - leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64) - if err != nil { - h.BadRequestLogger().Error("invalid league id", - zap.String("league_id", leagueIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - } - leagueID = domain.ValidInt64{ - Value: leagueIDInt, - Valid: true, - } - } - - sportIDQuery := c.Query("sport_id") - var sportID domain.ValidInt32 - if sportIDQuery != "" { - sportIDint, err := strconv.Atoi(sportIDQuery) - if err != nil { - h.BadRequestLogger().Info("invalid sport id", - zap.String("sportID", sportIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") - } - sportID = domain.ValidInt32{ - Value: int32(sportIDint), - Valid: true, - } - } - - stats, err := h.statSvc.GetTotalEventStats(c.Context(), domain.EventStatsFilter{ - LeagueID: leagueID, - SportID: sportID, - }) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve event status", - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Event Statistics retrieved successfully", stats, nil) -} - -func (h *Handler) GetTotalEventStatsByInterval(c *fiber.Ctx) error { - intervalParam := c.Query("interval", "day") - interval, err := domain.ParseDateInterval(intervalParam) - if err != nil { - h.BadRequestLogger().Error("invalid date interval", - zap.String("interval", c.Query("interval", "day")), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid date interval") - } - leagueIDQuery := c.Query("league_id") - var leagueID domain.ValidInt64 - if leagueIDQuery != "" { - leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64) - if err != nil { - h.BadRequestLogger().Error("invalid league id", - zap.String("league_id", leagueIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - } - leagueID = domain.ValidInt64{ - Value: leagueIDInt, - Valid: true, - } - } - - sportIDQuery := c.Query("sport_id") - var sportID domain.ValidInt32 - if sportIDQuery != "" { - sportIDint, err := strconv.Atoi(sportIDQuery) - if err != nil { - h.BadRequestLogger().Info("invalid sport id", - zap.String("sportID", sportIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") - } - sportID = domain.ValidInt32{ - Value: int32(sportIDint), - Valid: true, - } - } - - stats, err := h.statSvc.GetTotalEventStatsByInterval(c.Context(), domain.EventStatsByIntervalFilter{ - Interval: domain.ValidDateInterval{ - Value: interval, - Valid: true, - }, - LeagueID: leagueID, - SportID: sportID, - }) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve event status interval", - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Event Statistics retrieved successfully", stats, nil) -} diff --git a/internal/web_server/handlers/handlers.go b/internal/web_server/handlers/handlers.go index 1353ab1..a0dcb73 100644 --- a/internal/web_server/handlers/handlers.go +++ b/internal/web_server/handlers/handlers.go @@ -1,169 +1,81 @@ package handlers import ( + "Yimaru-Backend/internal/config" + "Yimaru-Backend/internal/services/arifpay" + "Yimaru-Backend/internal/services/authentication" + "Yimaru-Backend/internal/services/currency" + notificationservice "Yimaru-Backend/internal/services/notification" + "Yimaru-Backend/internal/services/recommendation" + referralservice "Yimaru-Backend/internal/services/referal" + + "Yimaru-Backend/internal/services/settings" + + "Yimaru-Backend/internal/services/transaction" + "Yimaru-Backend/internal/services/user" + jwtutil "Yimaru-Backend/internal/web_server/jwt" + customvalidator "Yimaru-Backend/internal/web_server/validator" "log/slog" - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/arifpay" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bonus" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/chapa" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/company" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/currency" - directdeposit "github.com/SamuelTariku/FortuneBet-Backend/internal/services/direct_deposit" - enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/institutions" - issuereporting "github.com/SamuelTariku/FortuneBet-Backend/internal/services/issue_reporting" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/league" - notificationservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/notification" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/raffle" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/recommendation" - referralservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/referal" - "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" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" - virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" - alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/atlas" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/orchestration" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" - jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" - customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator" "go.uber.org/zap" ) type Handler struct { - directDepositSvc *directdeposit.Service - orchestrationSvc *orchestration.Service - enetPulseSvc *enetpulse.Service - telebirrSvc *telebirr.TelebirrService arifpaySvc *arifpay.ArifpayService - santimpaySvc *santimpay.SantimPayService - issueReportingSvc *issuereporting.Service - instSvc *institutions.Service + // instSvc *institutions.Service currSvc *currency.Service logger *slog.Logger settingSvc *settings.Service notificationSvc *notificationservice.Service userSvc *user.Service referralSvc *referralservice.Service - raffleSvc *raffle.Service - bonusSvc *bonus.Service - // reportSvc report.ReportService - chapaSvc *chapa.Service - walletSvc *wallet.Service - transactionSvc *transaction.Service - ticketSvc *ticket.Service - betSvc *bet.Service - branchSvc *branch.Service - companySvc *company.Service - prematchSvc *odds.ServiceImpl - eventSvc *event.Service - leagueSvc *league.Service - virtualGameSvc virtualgameservice.VirtualGameService - aleaVirtualGameSvc alea.AleaVirtualGameService - veliVirtualGameSvc *veli.Service - atlasVirtualGameSvc atlas.AtlasVirtualGameService - recommendationSvc recommendation.RecommendationService - authSvc *authentication.Service - resultSvc result.Service - statSvc *stats.Service - jwtConfig jwtutil.JwtConfig - validator *customvalidator.CustomValidator - Cfg *config.Config - mongoLoggerSvc *zap.Logger + transactionSvc *transaction.Service + recommendationSvc recommendation.RecommendationService + authSvc *authentication.Service + jwtConfig jwtutil.JwtConfig + validator *customvalidator.CustomValidator + Cfg *config.Config + mongoLoggerSvc *zap.Logger } func New( - directDepositSvc *directdeposit.Service, - orchestrationSvc *orchestration.Service, - enetPulseSvc *enetpulse.Service, - telebirrSvc *telebirr.TelebirrService, + // directDepositSvc *directdeposit.Service, + // telebirrSvc *telebirr.TelebirrService, arifpaySvc *arifpay.ArifpayService, - santimpaySvc *santimpay.SantimPayService, - issueReportingSvc *issuereporting.Service, - instSvc *institutions.Service, + // santimpaySvc *santimpay.SantimPayService, + // instSvc *institutions.Service, currSvc *currency.Service, logger *slog.Logger, settingSvc *settings.Service, notificationSvc *notificationservice.Service, validator *customvalidator.CustomValidator, // reportSvc report.ReportService, - chapaSvc *chapa.Service, - walletSvc *wallet.Service, + // chapaSvc *chapa.Service, referralSvc *referralservice.Service, - raffleSvc *raffle.Service, - bonusSvc *bonus.Service, - virtualGameSvc virtualgameservice.VirtualGameService, - aleaVirtualGameSvc alea.AleaVirtualGameService, - veliVirtualGameSvc *veli.Service, - atlasVirtualGameSvc atlas.AtlasVirtualGameService, recommendationSvc recommendation.RecommendationService, userSvc *user.Service, transactionSvc *transaction.Service, - ticketSvc *ticket.Service, - betSvc *bet.Service, + // ticketSvc *ticket.Service, authSvc *authentication.Service, jwtConfig jwtutil.JwtConfig, - branchSvc *branch.Service, - companySvc *company.Service, - prematchSvc *odds.ServiceImpl, - eventSvc *event.Service, - leagueSvc *league.Service, - resultSvc result.Service, - statSvc *stats.Service, cfg *config.Config, mongoLoggerSvc *zap.Logger, ) *Handler { return &Handler{ - directDepositSvc: directDepositSvc, - orchestrationSvc: orchestrationSvc, - enetPulseSvc: enetPulseSvc, - telebirrSvc: telebirrSvc, arifpaySvc: arifpaySvc, - santimpaySvc: santimpaySvc, - issueReportingSvc: issueReportingSvc, instSvc: instSvc, currSvc: currSvc, logger: logger, settingSvc: settingSvc, notificationSvc: notificationSvc, - // reportSvc: reportSvc, - chapaSvc: chapaSvc, - walletSvc: walletSvc, - referralSvc: referralSvc, - raffleSvc: raffleSvc, - bonusSvc: bonusSvc, - validator: validator, - userSvc: userSvc, - transactionSvc: transactionSvc, - ticketSvc: ticketSvc, - betSvc: betSvc, - branchSvc: branchSvc, - companySvc: companySvc, - prematchSvc: prematchSvc, - eventSvc: eventSvc, - leagueSvc: leagueSvc, - virtualGameSvc: virtualGameSvc, - aleaVirtualGameSvc: aleaVirtualGameSvc, - veliVirtualGameSvc: veliVirtualGameSvc, - atlasVirtualGameSvc: atlasVirtualGameSvc, - recommendationSvc: recommendationSvc, - authSvc: authSvc, - resultSvc: resultSvc, - statSvc: statSvc, - jwtConfig: jwtConfig, - Cfg: cfg, - mongoLoggerSvc: mongoLoggerSvc, + referralSvc: referralSvc, + validator: validator, + userSvc: userSvc, + transactionSvc: transactionSvc, + recommendationSvc: recommendationSvc, + authSvc: authSvc, + jwtConfig: jwtConfig, + Cfg: cfg, + mongoLoggerSvc: mongoLoggerSvc, } } diff --git a/internal/web_server/handlers/institutions.go b/internal/web_server/handlers/institutions.go index 8e171d7..219b86a 100644 --- a/internal/web_server/handlers/institutions.go +++ b/internal/web_server/handlers/institutions.go @@ -1,9 +1,9 @@ package handlers import ( + "Yimaru-Backend/internal/domain" "strconv" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) diff --git a/internal/web_server/handlers/issue_reporting.go b/internal/web_server/handlers/issue_reporting.go deleted file mode 100644 index 0dd334e..0000000 --- a/internal/web_server/handlers/issue_reporting.go +++ /dev/null @@ -1,219 +0,0 @@ -package handlers - -import ( - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// CreateIssue godoc -// @Summary Report an issue -// @Description Allows a customer to report a new issue related to the betting platform -// @Tags Issues -// @Accept json -// @Produce json -// @Param issue body domain.ReportedIssue true "Issue to report" -// @Success 201 {object} domain.ReportedIssue -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/issues [post] -func (h *Handler) CreateIssue(c *fiber.Ctx) error { - role := c.Locals("role").(domain.Role) - userID := c.Locals("user_id").(int64) - - var req domain.ReportedIssueReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("Invalid request body", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error()) - } - - created, err := h.issueReportingSvc.CreateReportedIssue(c.Context(), req, userID, role) - if err != nil { - h.mongoLoggerSvc.Error("Failed to report issue", - zap.String("role", string(role)), - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "failed to report issue:"+err.Error()) - } - - return c.Status(fiber.StatusCreated).JSON(created) -} - -// GetUserIssues godoc -// @Summary Get reported issues by a user -// @Description Returns all issues reported by a specific user -// @Tags Issues -// @Produce json -// @Param user_id path int true "User ID" -// @Param limit query int false "Limit" -// @Param offset query int false "Offset" -// @Success 200 {array} domain.ReportedIssue -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/issues/user/{user_id} [get] -func (h *Handler) GetUserIssues(c *fiber.Ctx) error { - userID, err := strconv.ParseInt(c.Params("user_id"), 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Failed to parse user id", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID") - } - - limit, offset := getPaginationParams(c) - - issues, err := h.issueReportingSvc.GetIssuesForUser(c.Context(), userID, limit, offset) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get user issue", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get user issue"+err.Error()) - } - - return c.JSON(issues) -} - -// GetAllIssues godoc -// @Summary Get all reported issues -// @Description Admin endpoint to list all reported issues with pagination -// @Tags Issues -// @Produce json -// @Param limit query int false "Limit" -// @Param offset query int false "Offset" -// @Success 200 {array} domain.ReportedIssue -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/issues [get] -func (h *Handler) GetAllIssues(c *fiber.Ctx) error { - limit, offset := getPaginationParams(c) - - issues, err := h.issueReportingSvc.GetAllIssues(c.Context(), limit, offset) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get all issues", - zap.Int("limit", limit), - zap.Int("offset", offset), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get all issues:"+err.Error()) - } - - results := make([]domain.ReportedIssue, len(issues)) - for i, issue := range issues { - results[i] = domain.ReportedIssue{ - ID: issue.ID, - UserID: issue.UserID, - UserRole: domain.Role(issue.UserRole), - Subject: issue.Subject, - Description: issue.Description, - IssueType: domain.ReportedIssueType(issue.IssueType), - Status: domain.ReportedIssueStatus(issue.Status), - // Metadata: issue.Metadata, - CreatedAt: issue.CreatedAt.Time, - UpdatedAt: issue.UpdatedAt.Time, - } - } - - return c.JSON(results) -} - -// UpdateIssueStatus godoc -// @Summary Update issue status -// @Description Admin endpoint to update the status of a reported issue -// @Tags Issues -// @Accept json -// @Param issue_id path int true "Issue ID" -// @Param status body object{status=string} true "New issue status (pending, in_progress, resolved, rejected)" -// @Success 204 -// @Failure 400 {object} domain.ErrorResponse -// @Router /api/v1/issues/{issue_id}/status [patch] -func (h *Handler) UpdateIssueStatus(c *fiber.Ctx) error { - issueID, err := strconv.ParseInt(c.Params("issue_id"), 10, 64) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid issue ID") - } - - var body struct { - Status string `json:"status"` - } - if err := c.BodyParser(&body); err != nil || body.Status == "" { - h.mongoLoggerSvc.Info("Invalid status payload", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid status payload"+err.Error()) - } - - if err := h.issueReportingSvc.UpdateIssueStatus(c.Context(), issueID, body.Status); err != nil { - h.mongoLoggerSvc.Error("Failed to update issue status", - zap.Int64("issueID", issueID), - zap.String("status", body.Status), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - - } - - return c.SendStatus(fiber.StatusNoContent) -} - -// DeleteIssue godoc -// @Summary Delete a reported issue -// @Description Admin endpoint to delete a reported issue -// @Tags Issues -// @Param issue_id path int true "Issue ID" -// @Success 204 -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/issues/{issue_id} [delete] -func (h *Handler) DeleteIssue(c *fiber.Ctx) error { - issueID, err := strconv.ParseInt(c.Params("issue_id"), 10, 64) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid issue ID") - } - - if err := h.issueReportingSvc.DeleteIssue(c.Context(), issueID); err != nil { - h.mongoLoggerSvc.Error("Failed to delete issue", - zap.Int64("issueID", issueID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return c.SendStatus(fiber.StatusNoContent) -} - -func getPaginationParams(c *fiber.Ctx) (limit, offset int) { - limit = 20 - offset = 0 - - if l, err := strconv.Atoi(c.Query("limit")); err == nil && l > 0 { - limit = l - } - if o, err := strconv.Atoi(c.Query("offset")); err == nil && o >= 0 { - offset = o - } - return -} diff --git a/internal/web_server/handlers/leagues.go b/internal/web_server/handlers/leagues.go deleted file mode 100644 index 9abc0f5..0000000 --- a/internal/web_server/handlers/leagues.go +++ /dev/null @@ -1,395 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// GetAllLeagues godoc -// @Summary Gets all leagues -// @Description Gets all leagues -// @Tags leagues -// @Accept json -// @Produce json -// @Success 200 {array} domain.BaseLeague -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/leagues [get] -func (h *Handler) GetAllLeagues(c *fiber.Ctx) error { - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 0) - - limit := domain.ValidInt64{ - Value: int64(pageSize), - Valid: pageSize != 0, - } - offset := domain.ValidInt64{ - Value: int64(page - 1), - Valid: page != 1, - } - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - countryCodeQuery := c.Query("cc") - countryCode := domain.ValidString{ - Value: countryCodeQuery, - Valid: countryCodeQuery != "", - } - isActiveQuery := c.QueryBool("is_active", false) - isActiveFilter := c.QueryBool("is_active_filter", false) - isActive := domain.ValidBool{ - Value: isActiveQuery, - Valid: isActiveFilter, - } - - sportIDQuery := c.Query("sport_id") - var sportID domain.ValidInt32 - if sportIDQuery != "" { - sportIDint, err := strconv.ParseInt(sportIDQuery, 10, 64) - if err != nil { - h.BadRequestLogger().Info("invalid sport id", - zap.String("sport_id", sportIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") - } - sportID = domain.ValidInt32{ - Value: int32(sportIDint), - Valid: true, - } - } - - queryLogFields := []zap.Field{ - zap.Int("page", page), - zap.Int("page_size", pageSize), - zap.Int64("limit_value", limit.Value), - zap.Bool("limit_valid", limit.Valid), - zap.Int64("offset_value", offset.Value), - zap.Bool("offset_valid", offset.Valid), - zap.Bool("is_active_value", isActive.Value), - zap.Bool("is_active_valid", isActive.Valid), - zap.String("cc_query", countryCodeQuery), - zap.String("cc", countryCode.Value), - zap.String("query", searchQuery), - zap.Bool("cc_valid", countryCode.Valid), - zap.String("sport_id_query", sportIDQuery), - zap.Int32("sport_id", sportID.Value), - zap.Bool("sport_id_valid", sportID.Valid), - } - - leagues, total, err := h.leagueSvc.GetAllLeagues(c.Context(), domain.LeagueFilter{ - CountryCode: countryCode, - IsActive: isActive, - SportID: sportID, - Limit: limit, - Offset: offset, - Query: searchString, - }) - - if err != nil { - fmt.Printf("Error fetching league %v \n", err) - h.InternalServerErrorLogger().Error("Failed to get all leagues", append(queryLogFields, zap.Error(err))..., - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get leagues:"+err.Error()) - } - - res := domain.ConvertBaseLeagueResList(leagues) - - return response.WritePaginatedJSON(c, fiber.StatusOK, "All leagues retrieved successfully", res, nil, page, int(total)) -} - -// GetAllLeaguesForTenant godoc -// @Summary Gets all leagues -// @Description Gets all leagues -// @Tags leagues -// @Accept json -// @Produce json -// @Success 200 {array} domain.BaseLeague -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/leagues [get] -func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - page := c.QueryInt("page", 1) - pageSize := c.QueryInt("page_size", 10) - - limit := domain.ValidInt64{ - Value: int64(pageSize), - Valid: pageSize == 0, - } - offset := domain.ValidInt64{ - Value: int64(page - 1), - Valid: true, - } - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - countryCodeQuery := c.Query("cc") - countryCode := domain.ValidString{ - Value: countryCodeQuery, - Valid: countryCodeQuery != "", - } - isActiveQuery := c.QueryBool("is_active", false) - isActiveFilter := c.QueryBool("is_active_filter", false) - isActive := domain.ValidBool{ - Value: isActiveQuery, - Valid: isActiveFilter, - } - - sportIDQuery := c.Query("sport_id") - var sportID domain.ValidInt32 - if sportIDQuery != "" { - sportIDint, err := strconv.ParseInt(sportIDQuery, 10, 64) - if err != nil { - h.BadRequestLogger().Info("invalid sport id", - zap.String("sport_id", sportIDQuery), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid sport id") - } - sportID = domain.ValidInt32{ - Value: int32(sportIDint), - Valid: true, - } - } - - queryLogFields := []zap.Field{ - zap.Int64("company_id", companyID.Value), - zap.Bool("company_valid", companyID.Valid), - zap.Int("page", page), - zap.Int("page_size", pageSize), - zap.Int64("limit_value", limit.Value), - zap.Bool("limit_valid", limit.Valid), - zap.Int64("offset_value", offset.Value), - zap.Bool("offset_valid", offset.Valid), - zap.Bool("is_active_value", isActive.Value), - zap.Bool("is_active_valid", isActive.Valid), - zap.String("cc_query", countryCodeQuery), - zap.String("cc", countryCode.Value), - zap.Bool("cc_valid", countryCode.Valid), - zap.String("sport_id_query", sportIDQuery), - zap.Int32("sport_id", sportID.Value), - zap.Bool("sport_id_valid", sportID.Valid), - } - - leagues, total, err := h.leagueSvc.GetAllLeaguesByCompany(c.Context(), companyID.Value, domain.LeagueFilter{ - CountryCode: countryCode, - IsActive: isActive, - SportID: sportID, - Limit: limit, - Offset: offset, - Query: searchString, - }) - - if err != nil { - fmt.Printf("Error fetching league %v \n", err) - h.InternalServerErrorLogger().Error("Failed to get all leagues", append(queryLogFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get leagues:"+err.Error()) - } - - res := domain.ConvertLeagueWithSettingResList(leagues) - - return response.WritePaginatedJSON(c, fiber.StatusOK, "All leagues retrieved", res, nil, page, int(total)) -} - -type SetLeagueActiveReq struct { - IsActive bool `json:"is_active"` -} - -// SetLeagueActive godoc -// @Summary Set the league to active -// @Description Set the league to active -// @Tags leagues -// @Accept json -// @Produce json -// @Param id path int true "League ID" -// @Param active body SetLeagueActiveReq true "League Active Request" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/leagues/{id}/set-active [put] -func (h *Handler) SetLeagueActive(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.mongoLoggerSvc.Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - leagueIdStr := c.Params("id") - if leagueIdStr == "" { - return fiber.NewError(fiber.StatusBadRequest, "Missing league id") - } - leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - } - - queryLogFields := []zap.Field{ - zap.Int64("company_id", companyID.Value), - zap.Bool("company_valid", companyID.Valid), - zap.String("league_id", leagueIdStr), - } - - var req SetLeagueActiveReq - if err := c.BodyParser(&req); err != nil { - h.InternalServerErrorLogger().Error("SetLeagueReq failed to parse request body", - append(queryLogFields, zap.Error(err))..., - ) - return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request:"+err.Error()) - } - - queryLogFields = append(queryLogFields, zap.Any("is_active", req.IsActive)) - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Info("Failed to validate SetLeagueActiveReq", append(queryLogFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - if err := h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{ - LeagueID: leagueId, - CompanyID: companyID.Value, - IsActive: domain.ValidBool{ - Value: req.IsActive, - Valid: true, - }, - }); err != nil { - h.InternalServerErrorLogger().Error("Failed to update league active", append(queryLogFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error()) - } - - h.SuccessResLogger().Info("League Active has been successfully updated", queryLogFields...) - - return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil) -} - -type SetLeagueAsFeatured struct { - IsFeatured bool `json:"is_featured" example:"true"` -} - -// SetLeagueFeatured godoc -// @Summary Set the league to featured/un-featured -// @Description Set the league to featured/un-featured -// @Tags leagues -// @Accept json -// @Produce json -// @Param id path int true "League ID" -// @Param active body SetLeagueAsFeatured true "League Featured Request" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/leagues/{id}/featured [put] -func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.mongoLoggerSvc.Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - leagueIdStr := c.Params("id") - if leagueIdStr == "" { - return fiber.NewError(fiber.StatusBadRequest, "Missing league id") - } - leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - } - - queryLogFields := []zap.Field{ - zap.Int64("company_id", companyID.Value), - zap.Bool("company_valid", companyID.Valid), - zap.String("league_id", leagueIdStr), - } - var req SetLeagueAsFeatured - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("SetLeagueFeaturedReq failed to parse request body", append(queryLogFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request body:"+err.Error()) - } - - valErrs, ok := h.validator.Validate(c, req) - queryLogFields = append(queryLogFields, zap.Bool("is_featured", req.IsFeatured)) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Info("Failed to validate SetLeagueFeaturedReq", append(queryLogFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - - } - err = h.leagueSvc.SaveLeagueSettings(c.Context(), domain.CreateLeagueSettings{ - LeagueID: leagueId, - CompanyID: companyID.Value, - IsFeatured: domain.ValidBool{ - Value: req.IsFeatured, - Valid: true, - }, - }) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update league", append(queryLogFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error()) - } - - h.SuccessResLogger().Info("League Featured has been successfully updated", queryLogFields...) - return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil) -} - -func (h *Handler) UpdateGlobalLeagueSetting(c *fiber.Ctx) error { - leagueIdStr := c.Params("id") - if leagueIdStr == "" { - return fiber.NewError(fiber.StatusBadRequest, "Missing league id") - } - leagueId, err := strconv.ParseInt(leagueIdStr, 10, 64) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "invalid league id") - } - - var req domain.UpdateLeagueSettingsReq - - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("UpdateLeagueSettingsReq failed to parse request body", zap.String("league_id", leagueIdStr), zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request body:"+err.Error()) - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Info("Failed to validate UpdateLeagueSettingsReq", zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - - } - - err = h.leagueSvc.UpdateGlobalLeagueSettings(c.Context(), domain.UpdateGlobalLeagueSettings{ - ID: leagueId, - DefaultIsActive: domain.ConvertBoolPtr(req.IsActive), - DefaultIsFeatured: domain.ConvertBoolPtr(req.IsFeatured), - }) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update league", zap.Error(err), zap.String("leagueId", leagueIdStr)) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error()) - } - return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil) - -} diff --git a/internal/web_server/handlers/manager.go b/internal/web_server/handlers/manager.go index c0eafa9..5a61815 100644 --- a/internal/web_server/handlers/manager.go +++ b/internal/web_server/handlers/manager.go @@ -1,13 +1,14 @@ package handlers import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/services/authentication" + "Yimaru-Backend/internal/web_server/response" + "fmt" "strconv" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) @@ -403,7 +404,7 @@ func (h *Handler) UpdateManagers(c *fiber.Ctx) error { if err := c.BodyParser(&req); err != nil { h.logger.Error("UpdateManagers failed", "error", err) - + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", nil, nil) } diff --git a/internal/web_server/handlers/market_settings_handler.go b/internal/web_server/handlers/market_settings_handler.go deleted file mode 100644 index 6304020..0000000 --- a/internal/web_server/handlers/market_settings_handler.go +++ /dev/null @@ -1,195 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// InsertCompanyMarketSettings -// @Summary Insert company-specific market settings -// @Description Insert new market settings for a specific tenant/company -// @Tags market_settings -// @Accept json -// @Produce json -// @Param body body domain.CreateCompanyMarketSettings true "Market Settings" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/market-settings [post] -func (h *Handler) InsertCompanyMarketSettings(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - var req domain.CreateCompanyMarketSettingsReq - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("Failed to parse request", zap.Int64("company_id", companyID.Value), zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - valErrs, ok := h.validator.Validate(c, req) - if !ok { - errMsg := "" - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Error("Validation failed", zap.String("errors", errMsg)) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - if err := h.prematchSvc.InsertCompanyMarketSettings(c.Context(), domain.ConvertCreateCompanyMarketSettingsReq( - req, companyID.Value, - )); err != nil { - h.InternalServerErrorLogger().Error("Failed to insert company market settings", zap.Int64("company_id", companyID.Value), zap.Error(err)) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to insert market settings: "+err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Company market settings inserted successfully", nil, nil) -} - -// GetAllGlobalMarketSettings -// @Summary Retrieve all global market settings -// @Description Get all market settings that apply globally -// @Tags market_settings -// @Accept json -// @Produce json -// @Param limit query int false "Number of results to return (default 10)" -// @Param offset query int false "Number of results to skip (default 0)" -// @Success 200 {array} domain.MarketSettings -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/market-settings [get] -func (h *Handler) GetAllGlobalMarketSettings(c *fiber.Ctx) error { - limit, err := strconv.Atoi(c.Query("limit", "10")) - if err != nil || limit <= 0 { - h.BadRequestLogger().Info("Invalid limit value", zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value") - } - - offset, err := strconv.Atoi(c.Query("offset", "0")) - if err != nil || offset < 0 { - h.BadRequestLogger().Info("Invalid offset value", zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid offset value") - } - - settings, err := h.prematchSvc.GetAllGlobalMarketSettings(c.Context(), domain.MarketSettingFilter{ - Limit: domain.ValidInt32{Value: int32(limit), Valid: true}, - Offset: domain.ValidInt32{Value: int32(offset), Valid: true}, - }) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve global market settings", zap.Error(err)) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve global market settings: "+err.Error()) - } - - res := domain.ConvertMarketSettingsResList(settings) - - return response.WriteJSON(c, fiber.StatusOK, "Global market settings retrieved successfully", res, nil) -} - -// GetAllTenantMarketSettings -// @Summary Retrieve all market settings for a tenant -// @Description Get all market settings overridden for a specific tenant -// @Tags market_settings -// @Accept json -// @Produce json -// @Param limit query int false "Number of results to return (default 10)" -// @Param offset query int false "Number of results to skip (default 0)" -// @Success 200 {array} domain.CompanyMarketSettings -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/market-settings [get] -func (h *Handler) GetAllTenantMarketSettings(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - limit, err := strconv.Atoi(c.Query("limit", "10")) - if err != nil || limit <= 0 { - h.BadRequestLogger().Info("Invalid limit value", zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value") - } - - offset, err := strconv.Atoi(c.Query("offset", "0")) - if err != nil || offset < 0 { - h.BadRequestLogger().Info("Invalid offset value", zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid offset value") - } - - settings, err := h.prematchSvc.GetAllOverrideMarketSettings(c.Context(), companyID.Value, domain.MarketSettingFilter{ - Limit: domain.ValidInt32{Value: int32(limit), Valid: true}, - Offset: domain.ValidInt32{Value: int32(offset), Valid: true}, - }) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve tenant market settings", zap.Int64("company_id", companyID.Value), zap.Error(err)) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve tenant market settings: "+err.Error()) - } - res := domain.ConvertMarketSettingsResList(settings) - - return response.WriteJSON(c, fiber.StatusOK, "Tenant market settings retrieved successfully", res, nil) -} - -// DeleteAllCompanyMarketSettings -// @Summary Delete all market settings for a tenant -// @Description Remove all overridden market settings for a specific tenant -// @Tags market_settings -// @Accept json -// @Produce json -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/market-settings [delete] -func (h *Handler) DeleteAllCompanyMarketSettings(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - if err := h.prematchSvc.DeleteAllCompanyMarketSettings(c.Context(), companyID.Value); err != nil { - h.InternalServerErrorLogger().Error("Failed to delete all company market settings", zap.Int64("company_id", companyID.Value), zap.Error(err)) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete all company market settings: "+err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "All tenant market settings removed successfully", nil, nil) -} - -// DeleteCompanyMarketSettings -// @Summary Delete a specific market setting for a tenant -// @Description Remove a specific overridden market setting for a tenant -// @Tags market_settings -// @Accept json -// @Produce json -// @Param id path int true "Market ID" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/market-settings/{id} [delete] -func (h *Handler) DeleteCompanyMarketSettings(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - marketIDStr := c.Params("id") - marketID, err := strconv.ParseInt(marketIDStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Failed to parse market id", zap.String("id", marketIDStr)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid market id") - } - - if err := h.prematchSvc.DeleteCompanyMarketSettings(c.Context(), companyID.Value, marketID); err != nil { - h.InternalServerErrorLogger().Error("Failed to delete company market setting", zap.Int64("company_id", companyID.Value), zap.Int64("market_id", marketID), zap.Error(err)) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete market setting: "+err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Tenant market setting removed successfully", nil, nil) -} diff --git a/internal/web_server/handlers/mongoLogger.go b/internal/web_server/handlers/mongoLogger.go index 6655ae3..39593d3 100644 --- a/internal/web_server/handlers/mongoLogger.go +++ b/internal/web_server/handlers/mongoLogger.go @@ -4,7 +4,7 @@ import ( "context" "os" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "Yimaru-Backend/internal/domain" "github.com/gofiber/fiber/v2" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" diff --git a/internal/web_server/handlers/notification_handler.go b/internal/web_server/handlers/notification_handler.go index e644b96..adc5e85 100644 --- a/internal/web_server/handlers/notification_handler.go +++ b/internal/web_server/handlers/notification_handler.go @@ -1,6 +1,8 @@ package handlers import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/web_server/ws" "context" "encoding/json" "fmt" @@ -10,8 +12,6 @@ import ( "strings" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/adaptor" "github.com/gorilla/websocket" @@ -353,7 +353,7 @@ func (h *Handler) GetUserNotification(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification") } - notifications, total, err := h.notificationSvc.GetUserNotifications(context.Background(), userID, limit, offset) + notifications, total, err := h.notificationSvc.GetUserNotifications(context.Background(), userID, limit, offset) if err != nil { h.mongoLoggerSvc.Error("[NotificationSvc.GetUserNotification] Failed to fetch notifications", zap.Int64("userID", userID), diff --git a/internal/web_server/handlers/odd_handler.go b/internal/web_server/handlers/odd_handler.go deleted file mode 100644 index f7d04c6..0000000 --- a/internal/web_server/handlers/odd_handler.go +++ /dev/null @@ -1,527 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// GetAllOdds -// @Summary Retrieve all odds -// @Description Retrieve all odds from the database -// @Tags prematch -// @Accept json -// @Produce json -// @Success 200 {array} domain.OddMarketFilter -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/odds [get] -func (h *Handler) GetAllOdds(c *fiber.Ctx) error { - limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10 - if err != nil || limit <= 0 { - h.BadRequestLogger().Info("Invalid limit value", zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value") - } - - offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0 - if err != nil || offset < 0 { - h.BadRequestLogger().Info("Invalid offset value", zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - odds, err := h.prematchSvc.GetAllOdds(c.Context(), domain.OddMarketFilter{ - Limit: domain.ValidInt32{ - Value: int32(limit), - Valid: true, - }, - Offset: domain.ValidInt32{ - Value: int32(offset), - Valid: true, - }, - }) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve all odds", - zap.Int("limit", limit), - zap.Int("offset", offset), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "All odds retrieved successfully", odds, nil) -} - -// GetAllOdds -// @Summary Retrieve all odds -// @Description Retrieve all odds from the database -// @Tags prematch -// @Accept json -// @Produce json -// @Success 200 {array} domain.OddMarketFilter -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/odds [get] -func (h *Handler) GetAllTenantOdds(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10 - if err != nil || limit <= 0 { - h.BadRequestLogger().Info("Invalid limit value", zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value") - } - - offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0 - if err != nil || offset < 0 { - h.BadRequestLogger().Info("Invalid offset value", zap.Error(err)) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - odds, err := h.prematchSvc.GetAllOddsWithSettings(c.Context(), companyID.Value, domain.OddMarketFilter{ - Limit: domain.ValidInt32{ - Value: int32(limit), - Valid: true, - }, - Offset: domain.ValidInt32{ - Value: int32(offset), - Valid: true, - }, - }) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve all odds", - zap.Int("limit", limit), - zap.Int("offset", offset), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "All odds retrieved successfully", odds, nil) -} - -// GetOddsByMarketID -// @Summary Retrieve raw odds by Market ID -// @Description Retrieve raw odds records using a Market ID -// @Tags prematch -// @Accept json -// @Produce json -// @Param upcoming_id path string true "Upcoming ID" -// @Param market_id path string true "Market ID" -// @Success 200 {array} domain.RawOddsByMarketID -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/odds/upcoming/{upcoming_id}/market/{market_id} [get] -func (h *Handler) GetOddsByMarketID(c *fiber.Ctx) error { - logFields := []zap.Field{ - zap.String("market_id", c.Params("market_id")), - zap.String("upcoming_id", c.Params("upcoming_id")), - } - - marketIDStr := c.Params("market_id") - marketID, err := strconv.ParseInt(marketIDStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Missing market_id", logFields...) - return fiber.NewError(fiber.StatusBadRequest, "Missing market_id") - } - - eventIDStr := c.Params("upcoming_id") - eventID, err := strconv.ParseInt(eventIDStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Missing upcoming_id", logFields...) - return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id") - } - - rawOdds, err := h.prematchSvc.GetOddsByMarketID(c.Context(), marketID, eventID) - if err != nil { - // Lets turn this into a warn because this is constantly going off - // h.InternalServerErrorLogger().Warn("Failed to get raw odds by market ID", append(logFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Raw odds retrieved successfully", rawOdds, nil) - -} - -// GetTenantOddsByMarketID -// @Summary Retrieve raw odds by Market ID -// @Description Retrieve raw odds records using a Market ID -// @Tags prematch -// @Accept json -// @Produce json -// @Param upcoming_id path string true "Upcoming ID" -// @Param market_id path string true "Market ID" -// @Success 200 {array} domain.RawOddsByMarketID -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/odds/upcoming/{upcoming_id}/market/{market_id} [get] -func (h *Handler) GetTenantOddsByMarketID(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - logFields := []zap.Field{ - zap.String("market_id", c.Params("market_id")), - zap.String("upcoming_id", c.Params("upcoming_id")), - zap.Int64("company_id", companyID.Value), - } - - marketIDStr := c.Params("market_id") - marketID, err := strconv.ParseInt(marketIDStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Missing market_id", logFields...) - return fiber.NewError(fiber.StatusBadRequest, "Missing market_id") - } - - eventIDStr := c.Params("upcoming_id") - eventID, err := strconv.ParseInt(eventIDStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Missing upcoming_id", logFields...) - return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id") - } - - oddMarket, err := h.prematchSvc.GetOddsWithSettingsByMarketID(c.Context(), marketID, eventID, companyID.Value) - - if err != nil { - // Lets turn this into a warn because this is constantly going off - // h.InternalServerErrorLogger().Warn("Failed to get raw odds by market ID", append(logFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Raw odds retrieved successfully", oddMarket, nil) - -} - -// GetOddsByUpcomingID -// @Summary Retrieve prematch odds by upcoming ID (FI) -// @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination -// @Tags prematch -// @Accept json -// @Produce json -// @Param upcoming_id path string true "Upcoming Event ID (FI)" -// @Param limit query int false "Number of results to return (default: 10)" -// @Param offset query int false "Number of results to skip (default: 0)" -// @Success 200 {array} domain.OddMarketWithEventFilter -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/odds/upcoming/{upcoming_id} [get] -func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error { - logFields := []zap.Field{ - zap.String("upcoming_id", c.Params("upcoming_id")), - zap.String("limit_param", c.Query("limit", "10")), - zap.String("offset_param", c.Query("offset", "0")), - } - - eventIDStr := c.Params("upcoming_id") - eventID, err := strconv.ParseInt(eventIDStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Missing upcoming_id", logFields...) - return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id") - } - - limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10 - if err != nil || limit <= 0 { - logFields = append(logFields, zap.Error(err)) - h.BadRequestLogger().Info("Invalid limit value", logFields...) - return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value") - } - - offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0 - if err != nil || offset < 0 { - logFields = append(logFields, zap.Error(err)) - h.BadRequestLogger().Info("Invalid offset value", logFields...) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - odds, err := h.prematchSvc.GetOddsByEventID(c.Context(), eventID, domain.OddMarketWithEventFilter{}) - if err != nil { - logFields = append(logFields, zap.Error(err)) - h.InternalServerErrorLogger().Error("Failed to retrieve odds", append(logFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve odds"+err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Odds retrieved successfully", odds, nil) - -} - -// GetTenantOddsByUpcomingID -// @Summary Retrieve prematch odds by upcoming ID (FI) -// @Description Retrieve prematch odds by upcoming event ID (FI from Bet365) with optional pagination -// @Tags prematch -// @Accept json -// @Produce json -// @Param upcoming_id path string true "Upcoming Event ID (FI)" -// @Param limit query int false "Number of results to return (default: 10)" -// @Param offset query int false "Number of results to skip (default: 0)" -// @Success 200 {array} domain.OddMarketFilter -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/odds/upcoming/{upcoming_id} [get] -func (h *Handler) GetTenantOddsByUpcomingID(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - logFields := []zap.Field{ - zap.String("upcoming_id", c.Params("upcoming_id")), - zap.String("limit_param", c.Query("limit", "10")), - zap.String("offset_param", c.Query("offset", "0")), - zap.Int64("company_id", companyID.Value), - } - - eventIDStr := c.Params("upcoming_id") - eventID, err := strconv.ParseInt(eventIDStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Missing upcoming_id", logFields...) - return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id") - } - - limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10 - if err != nil || limit <= 0 { - logFields = append(logFields, zap.Error(err)) - h.BadRequestLogger().Info("Invalid limit value", logFields...) - return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value") - } - - offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0 - if err != nil || offset < 0 { - logFields = append(logFields, zap.Error(err)) - h.BadRequestLogger().Info("Invalid offset value", logFields...) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - odds, err := h.prematchSvc.GetOddsWithSettingsByEventID(c.Context(), eventID, companyID.Value, domain.OddMarketFilter{}) - if err != nil { - logFields = append(logFields, zap.Error(err)) - h.InternalServerErrorLogger().Error("Failed to retrieve odds", append(logFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve odds"+err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Odds retrieved successfully", odds, nil) - -} - -func (h *Handler) SaveTenantOddsSetting(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - var req domain.CreateOddMarketSettingsReq - - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("Failed to parse CreateOddMarketSettingsReq", - zap.Int64("CompanyID", companyID.Value), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - logFields := []zap.Field{ - zap.Int64("companyID", companyID.Value), - zap.Any("is_active", req.IsActive), - zap.Any("custom_odd", req.CustomOdd), - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Error("Failed to validate insert odd settings", - append(logFields, zap.String("errMsg", errMsg))..., - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err := h.prematchSvc.SaveOddsSettingReq(c.Context(), companyID.Value, req) - - if err != nil { - logFields = append(logFields, zap.Error(err)) - h.InternalServerErrorLogger().Error("Failed to save odds settings", append(logFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to save odds settings"+err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Odds Settings saved successfully", nil, nil) - -} - -func (h *Handler) SaveOddSettings(c *fiber.Ctx) error { - var req domain.UpdateGlobalOddMarketSettingsReq - - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("Failed to parse CreateOddMarketSettingsReq", - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - logFields := []zap.Field{ - zap.Any("is_active", req.IsActive), - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Error("Failed to validate insert odd settings", - append(logFields, zap.String("errMsg", errMsg))..., - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err := h.prematchSvc.UpdateGlobalOddsSetting(c.Context(), domain.UpdateGlobalOddMarketSettings{ - OddMarketID: req.OddMarketID, - IsActive: domain.ConvertBoolPtr(req.IsActive), - }) - - if err != nil { - logFields = append(logFields, zap.Error(err)) - h.InternalServerErrorLogger().Error("Failed to save odds settings", append(logFields, zap.Error(err))...) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to save odds settings"+err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Odds Settings saved successfully", nil, nil) -} - -func (h *Handler) RemoveOddsSettings(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - idStr := c.Params("id") - oddID, err := strconv.ParseInt(idStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Failed to parse odd id", zap.String("id", idStr)) - return fiber.NewError(fiber.StatusBadRequest, "Missing id") - } - - err = h.prematchSvc.DeleteCompanyOddsSettingByOddMarketID(c.Context(), companyID.Value, oddID) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to retrieve odds", zap.Error(err)) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to remove odds settings"+err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Odds settings successfully removed ", nil, nil) -} -func (h *Handler) RemoveAllOddsSettings(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - err := h.prematchSvc.DeleteAllCompanyOddsSetting(c.Context(), companyID.Value) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to remove all odd settings", zap.Int64("company_id", companyID.Value), zap.Error(err)) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to remove all odds settings"+err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Odds settings successfully removed ", nil, nil) -} - -type UpdateAllBetStatusByOddIDReq struct { - Status domain.OutcomeStatus `json:"status"` -} - -func (h *Handler) UpdateAllBetOutcomeStatusByOddID(c *fiber.Ctx) error { - - idStr := c.Params("id") - oddID, err := strconv.ParseInt(idStr, 10, 64) - if err != nil { - h.BadRequestLogger().Info("Failed to parse odd_id", zap.String("id", idStr)) - return fiber.NewError(fiber.StatusBadRequest, "Missing id") - } - - var req UpdateAllBetStatusByOddIDReq - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("Failed to parse event id", - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - logFields := []zap.Field{ - zap.Any("status", req.Status), - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Error("Failed to insert odd settings", - append(logFields, zap.String("errMsg", errMsg))..., - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - _, err = h.betSvc.UpdateBetOutcomeStatusForOddId(c.Context(), oddID, req.Status) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update bet status by odd id", - zap.Int64("oddID", oddID), - zap.Error(err), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Updated All Bet Outcome Status Successfully", nil, nil) -} -type BulkUpdateAllBetStatusByOddIDsReq struct { - OddIDs []int64 `json:"odd_ids"` - Status domain.OutcomeStatus `json:"status"` -} - -func (h *Handler) BulkUpdateAllBetOutcomeStatusByOddID(c *fiber.Ctx) error { - var req BulkUpdateAllBetStatusByOddIDsReq - if err := c.BodyParser(&req); err != nil { - h.BadRequestLogger().Info("Failed to parse event id", - zap.Error(err), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - logFields := []zap.Field{ - zap.Int64s("odd_ids", req.OddIDs), - zap.Any("status", req.Status), - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.BadRequestLogger().Error("Failed to insert odd settings", - append(logFields, zap.String("errMsg", errMsg))..., - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err := h.betSvc.BulkUpdateBetOutcomeStatusForOddIds(c.Context(), req.OddIDs, req.Status) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to bulk update bet status by odd ids", - append(logFields, zap.Error(err))..., - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return response.WriteJSON(c, fiber.StatusOK, "Bulk Updated All Bet Outcome Status Successfully", nil, nil) -} diff --git a/internal/web_server/handlers/raffle_handler.go b/internal/web_server/handlers/raffle_handler.go deleted file mode 100644 index 4c316f8..0000000 --- a/internal/web_server/handlers/raffle_handler.go +++ /dev/null @@ -1,377 +0,0 @@ -package handlers - -import ( - "errors" - "fmt" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/pkgs/helpers" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -func (h *Handler) CreateRaffle(c *fiber.Ctx) error { - var req domain.CreateRaffle - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("Failed to parse raffle request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - } - - if valErrs, ok := h.validator.Validate(c, req); !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate settings", - zap.String("errMsg", errMsg), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - raffle, err := h.raffleSvc.CreateRaffle(c.Context(), req) - if err != nil { - h.mongoLoggerSvc.Error("Failed to create raffle", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create raffle") - } - - return response.WriteJSON(c, fiber.StatusOK, "Raffle created successfully", raffle, nil) -} - -func (h *Handler) AddRaffleFilter(c *fiber.Ctx) error { - var filter domain.RaffleFilter - - if err := c.BodyParser(&filter); err != nil { - h.mongoLoggerSvc.Info("Failed to parse raffle filter request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - } - - if valErrs, ok := h.validator.Validate(c, filter); !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate raffle filter", - zap.String("errMsg", errMsg), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - switch filter.Type { - case "sport": - err := h.raffleSvc.AddSportRaffleFilter(c.Context(), filter.RaffleID, int64(filter.SportID), int64(filter.LeagueID)) - if err != nil { - h.mongoLoggerSvc.Error("Failed to add raffle filter", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to add raffle filter") - } - - // handle game case below - // there won't be a default case since its being handled in the validator - } - - return response.WriteJSON(c, fiber.StatusOK, "Raffle filter added successfully", nil, nil) -} - -func (h *Handler) DeleteRaffle(c *fiber.Ctx) error { - stringRaffleID := c.Params("id") - raffleID, err := strconv.Atoi(stringRaffleID) - if err != nil { - h.mongoLoggerSvc.Info("failed to parse raffle id", - zap.String("stringRaffleID", stringRaffleID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid raffle id") - } - - raffle, err := h.raffleSvc.DeleteRaffle(c.Context(), int32(raffleID)) - if err != nil { - fmt.Println("raffle delete error: ", err) - h.mongoLoggerSvc.Error("Failed to delete raffle", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete raffle") - } - - return response.WriteJSON(c, fiber.StatusOK, "Raffle deleted successfully", raffle, nil) -} - -func (h *Handler) GetRafflesOfCompany(c *fiber.Ctx) error { - stringCompanyID := c.Params("id") - companyID, err := strconv.Atoi(stringCompanyID) - if err != nil || companyID == 0 { - h.mongoLoggerSvc.Info("failed to parse company id", - zap.String("stringCompanyID", stringCompanyID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid company ID") - } - - companyRaffles, err := h.raffleSvc.GetRafflesOfCompany(c.Context(), int32(companyID)) - if err != nil { - h.mongoLoggerSvc.Error("Failed to fetch company raffle", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch company raffle") - } - - return response.WriteJSON(c, fiber.StatusOK, "Company Raffles fetched successfully", companyRaffles, nil) -} - -func (h *Handler) GetTenantRaffles(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - companyRaffles, err := h.raffleSvc.GetRafflesOfCompany(c.Context(), int32(companyID.Value)) - if err != nil { - h.mongoLoggerSvc.Error("Failed to fetch company raffle", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch company raffle") - } - - return response.WriteJSON(c, fiber.StatusOK, "Company Raffles fetched successfully", companyRaffles, nil) -} - -func (h *Handler) GetRaffleStanding(c *fiber.Ctx) error { - raffleIDStr := c.Params("id") - limitStr := c.Params("limit") - - // if error happens while parsing, it just uses zero values - // resulting in empty standing - raffleID, _ := strconv.Atoi(raffleIDStr) - limit, _ := strconv.Atoi(limitStr) - - raffleStanding, err := h.raffleSvc.GetRaffleStanding(c.Context(), int32(raffleID), int32(limit)) - if err != nil { - h.mongoLoggerSvc.Error("Failed to fetch raffle standing", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch raffle standing") - } - - maskedRaffleStanding := make([]domain.RaffleStandingRes, 0, len(raffleStanding)) - - for _, standing := range raffleStanding { - maskedStanding := domain.RaffleStandingRes{ - UserID: standing.UserID, - RaffleID: standing.RaffleID, - FirstName: standing.FirstName, - LastName: standing.LastName, - PhoneNumber: helpers.MaskPhone(standing.PhoneNumber), - Email: helpers.MaskEmail(standing.Email), - TicketCount: standing.TicketCount, - } - - maskedRaffleStanding = append(maskedRaffleStanding, maskedStanding) - } - - return response.WriteJSON(c, fiber.StatusOK, "Raffles standing fetched successfully", maskedRaffleStanding, nil) -} - -func (h *Handler) GetRaffleWinners(c *fiber.Ctx) error { - raffleIDStr := c.Params("id") - limitStr := c.Params("limit") - - // if error happens while parsing, it just uses zero values - // resulting in empty standing - raffleID, _ := strconv.Atoi(raffleIDStr) - limit, _ := strconv.Atoi(limitStr) - - raffleStanding, err := h.raffleSvc.GetRaffleStanding(c.Context(), int32(raffleID), int32(limit)) - if err != nil { - h.mongoLoggerSvc.Error("Failed to fetch raffle standing", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch raffle standing") - } - - // set raffle as complete - if err := h.raffleSvc.SetRaffleComplete(c.Context(), int32(raffleID)); err != nil { - h.mongoLoggerSvc.Error("Failed to set raffle complete", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to set raffle complete") - } - - // add winners to table - var errs []error - for i, standing := range raffleStanding { - err = h.raffleSvc.CreateRaffleWinner(c.Context(), domain.RaffleWinnerParams{ - RaffleID: standing.RaffleID, - UserID: int32(standing.UserID), - Rank: int32(i + 1), - }) - - if err != nil { - errs = append(errs, err) - } - } - - if len(errs) != 0 { - h.mongoLoggerSvc.Error("Failed to create raffle winners", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(errors.Join(errs...)), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create raffle winners") - } - - return nil -} - -func (h *Handler) CreateRaffleTicket(c *fiber.Ctx) error { - var req domain.CreateRaffleTicket - if err := c.BodyParser(&req); err != nil { - fmt.Println("parser error: ", err) - h.mongoLoggerSvc.Info("Failed to parse raffle ticket request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - } - - if valErrs, ok := h.validator.Validate(c, req); !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate settings", - zap.String("errMsg", errMsg), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - raffleTicket, err := h.raffleSvc.CreateRaffleTicket(c.Context(), req) - if err != nil { - fmt.Println("raffle ticket create error: ", err) - h.mongoLoggerSvc.Error("Failed to create raffle ticket", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create raffle ticket") - } - - return response.WriteJSON(c, fiber.StatusOK, "Raffle created successfully", raffleTicket, nil) -} - -func (h *Handler) GetUserRaffleTickets(c *fiber.Ctx) error { - stringUserID := c.Params("id") - userID, err := strconv.Atoi(stringUserID) - if err != nil { - h.mongoLoggerSvc.Info("failed to parse company id", - zap.String("stringUserID", stringUserID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID") - } - - raffleTickets, err := h.raffleSvc.GetUserRaffleTickets(c.Context(), int32(userID)) - if err != nil { - h.mongoLoggerSvc.Error("Failed to fetch user raffle tickets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch user raffle tickets") - } - - return response.WriteJSON(c, fiber.StatusOK, "User raffle tickets fetched successfully", raffleTickets, nil) -} - -func (h *Handler) SuspendRaffleTicket(c *fiber.Ctx) error { - stringRaffleTicketID := c.Params("id") - raffleTicketID, err := strconv.Atoi(stringRaffleTicketID) - if err != nil { - h.mongoLoggerSvc.Info("failed to parse raffle ticket id", - zap.String("stringUserID", stringRaffleTicketID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid raffel ticket id") - } - - if err := h.raffleSvc.SuspendRaffleTicket(c.Context(), int32(raffleTicketID)); err != nil { - h.mongoLoggerSvc.Error("Failed to suspend raffle ticket", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to suspend raffle ticket") - } - - return response.WriteJSON(c, fiber.StatusOK, "User raffle tickets suspended successfully", nil, nil) -} - -func (h *Handler) UnSuspendRaffleTicket(c *fiber.Ctx) error { - stringRaffleTicketID := c.Params("id") - raffleTicketID, err := strconv.Atoi(stringRaffleTicketID) - if err != nil { - h.mongoLoggerSvc.Info("failed to parse raffle ticket id", - zap.String("stringUserID", stringRaffleTicketID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid raffel ticket id") - } - - if err := h.raffleSvc.UnSuspendRaffleTicket(c.Context(), int32(raffleTicketID)); err != nil { - h.mongoLoggerSvc.Error("Failed to unsuspend raffle ticket", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to unsuspend raffle ticket") - } - - return response.WriteJSON(c, fiber.StatusOK, "User raffle tickets unsuspended successfully", nil, nil) - -} diff --git a/internal/web_server/handlers/referal_handlers.go b/internal/web_server/handlers/referal_handlers.go index 7ca3856..b9fa633 100644 --- a/internal/web_server/handlers/referal_handlers.go +++ b/internal/web_server/handlers/referal_handlers.go @@ -1,11 +1,11 @@ package handlers import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/web_server/response" "fmt" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) diff --git a/internal/web_server/handlers/result_handler.go b/internal/web_server/handlers/result_handler.go deleted file mode 100644 index ae153c1..0000000 --- a/internal/web_server/handlers/result_handler.go +++ /dev/null @@ -1,50 +0,0 @@ -package handlers - -import ( - "encoding/json" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -type ResultRes struct { - ResultData json.RawMessage `json:"result_data" swaggerignore:"true"` - Outcomes []domain.BetOutcome `json:"outcomes"` -} - -// This will take an event ID and return the success results for -// all the odds for that event. -// @Summary Get results for an event -// @Description Get results for an event -// @Tags result -// @Accept json -// @Produce json -// @Param id path string true "Event ID" -// @Success 200 {array} ResultRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/result/b365/{id} [get] -func (h *Handler) GetBet365ResultsByEventID(c *fiber.Ctx) error { - b365EventID := c.Params("id") - - results, outcomes, err := h.resultSvc.GetBet365ResultForEvent(c.Context(), b365EventID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get results by Event ID", - zap.String("b365EventID", b365EventID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve results") - } - - resultRes := ResultRes{ - ResultData: results, - Outcomes: outcomes, - } - - return response.WriteJSON(c, fiber.StatusOK, "Results retrieved successfully", resultRes, nil) -} diff --git a/internal/web_server/handlers/santimpay.go b/internal/web_server/handlers/santimpay.go deleted file mode 100644 index 557716a..0000000 --- a/internal/web_server/handlers/santimpay.go +++ /dev/null @@ -1,232 +0,0 @@ -package handlers - -import ( - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" -) - -// CreateSantimPayPaymentHandler initializes a payment session with SantimPay. -// -// @Summary Create SantimPay Payment Session -// @Description Generates a payment URL using SantimPay and returns it to the client. -// @Tags SantimPay -// @Accept json -// @Produce json -// @Param request body domain.GeneratePaymentURLRequest true "SantimPay payment request payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/santimpay/payment [post] -func (h *Handler) InititateSantimPayPaymentHandler(c *fiber.Ctx) error { - var req domain.GeneratePaymentURLRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to process your request", - }) - } - - paymentURL, err := h.santimpaySvc.InitiatePayment(req) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to initiate SantimPay payment session", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "SantimPay payment URL generated successfully", - Data: paymentURL, - Success: true, - StatusCode: fiber.StatusOK, - }) -} - -// ProcessSantimPayCallbackHandler handles incoming SantimPay payment callbacks. -// -// @Summary Process SantimPay Payment Callback -// @Description Processes a callback from SantimPay, updates transfer status, and credits user wallet if payment was successful. -// @Tags SantimPay -// @Accept json -// @Produce json -// @Param request body domain.SantimPayCallbackPayload true "SantimPay callback payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/santimpay/callback [post] -func (h *Handler) ProcessSantimPayCallbackHandler(c *fiber.Ctx) error { - var payload domain.SantimPayCallbackPayload - if err := c.BodyParser(&payload); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Invalid callback payload", - }) - } - - if err := h.santimpaySvc.ProcessCallback(c.Context(), payload); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to process SantimPay callback", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "SantimPay callback processed successfully", - Data: nil, - Success: true, - StatusCode: fiber.StatusOK, - }) -} - -// ProcessSantimPayDirectPaymentHandler initializes a direct payment session with SantimPay. -// -// @Summary Process SantimPay Direct Payment -// @Description Initiates a direct payment request with SantimPay and returns the response. -// @Tags SantimPay -// @Accept json -// @Produce json -// @Param request body domain.GeneratePaymentURLRequest true "SantimPay direct payment request payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/santimpay/direct-payment [post] -func (h *Handler) ProcessSantimPayDirectPaymentHandler(c *fiber.Ctx) error { - var req domain.GeneratePaymentURLRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Invalid direct payment request payload", - }) - } - - response, err := h.santimpaySvc.ProcessDirectPayment(c.Context(), req) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to process SantimPay direct payment", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "SantimPay direct payment processed successfully", - Data: response, - Success: true, - StatusCode: fiber.StatusOK, - }) -} - -// GetSantimPayB2CPartnersHandler retrieves all available SantimPay B2C payout partners. -// -// @Summary Get SantimPay B2C Partners -// @Description Fetches a list of available B2C payout partners (e.g., Telebirr, Mpesa, Banks) from SantimPay. -// @Tags SantimPay -// @Accept json -// @Produce json -// @Success 200 {object} domain.Response -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/santimpay/b2c/partners [get] -func (h *Handler) GetSantimPayB2CPartnersHandler(c *fiber.Ctx) error { - partners, err := h.santimpaySvc.GetB2CPartners(c.Context()) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to fetch SantimPay B2C partners", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "SantimPay B2C partners retrieved successfully", - Data: partners, - Success: true, - StatusCode: fiber.StatusOK, - }) -} - -// ProcessSantimPayB2CWithdrawalHandler processes a B2C (Withdrawal) transaction with SantimPay. -// -// @Summary Process SantimPay B2C Withdrawal -// @Description Initiates a B2C withdrawal request with SantimPay and returns the response. -// @Tags SantimPay -// @Accept json -// @Produce json -// @Param request body domain.GeneratePaymentURLRequest true "SantimPay B2C withdrawal request payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/santimpay/b2c-withdrawal [post] -func (h *Handler) ProcessSantimPayB2CWithdrawalHandler(c *fiber.Ctx) error { - var req domain.GeneratePaymentURLRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Invalid B2C withdrawal request payload", - }) - } - - // Extract userId from context/session (adapt based on your auth flow) - userId, ok := c.Locals("userId").(int64) - if !ok { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: "missing userId in context", - Message: "Could not process withdrawal without user ID", - }) - } - - response, err := h.santimpaySvc.ProcessB2CWithdrawal(c.Context(), req, userId) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to process SantimPay B2C withdrawal", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "SantimPay B2C withdrawal processed successfully", - Data: response, - Success: true, - StatusCode: fiber.StatusOK, - }) -} - -// CheckSantimPayTransactionStatusHandler checks the status of a SantimPay transaction. -// -// @Summary Check SantimPay Transaction Status -// @Description Retrieves the real-time status of a transaction from SantimPay. -// @Tags SantimPay -// @Accept json -// @Produce json -// @Param request body domain.TransactionStatusRequest true "Transaction status request payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/santimpay/transaction-status [post] -func (h *Handler) CheckSantimPayTransactionStatusHandler(c *fiber.Ctx) error { - var req domain.TransactionStatusRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Invalid transaction status request payload", - }) - } - - // Optional: extract fullParams from request, default to true if not provided - fullParams := true - if req.FullParams == nil { - req.FullParams = &fullParams - } - - response, err := h.santimpaySvc.CheckTransactionStatus(c.Context(), req) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to check SantimPay transaction status", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "SantimPay transaction status retrieved successfully", - Data: response, - Success: true, - StatusCode: fiber.StatusOK, - }) -} diff --git a/internal/web_server/handlers/settings_handler.go b/internal/web_server/handlers/settings_handler.go index 13e9890..6e2a058 100644 --- a/internal/web_server/handlers/settings_handler.go +++ b/internal/web_server/handlers/settings_handler.go @@ -1,11 +1,11 @@ package handlers import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/web_server/response" "fmt" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) diff --git a/internal/web_server/handlers/shop_handler.go b/internal/web_server/handlers/shop_handler.go deleted file mode 100644 index ff363b8..0000000 --- a/internal/web_server/handlers/shop_handler.go +++ /dev/null @@ -1,692 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// CreateShopBet godoc -// @Summary Create bet at branch -// @Description Create bet at branch -// @Tags transaction -// @Accept json -// @Produce json -// @Param createBet body domain.ShopBetReq true "create bet" -// @Success 200 {object} domain.ShopTransactionRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/bet [post] -func (h *Handler) CreateShopBet(c *fiber.Ctx) error { - - userID := c.Locals("user_id").(int64) - role := c.Locals("role").(domain.Role) - company_id := c.Locals("company_id").(domain.ValidInt64) - - var req domain.ShopBetReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("CreateBetReq failed to parse request", - zap.Any("request", req), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "invalid request body"+err.Error()) - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - shopBet, err := h.transactionSvc.CreateShopBet(c.Context(), userID, role, company_id, req) - - if err != nil { - var statusCode int - if isBetError := h.betSvc.CheckIfBetError(err); isBetError { - statusCode = fiber.StatusBadRequest - } else { - statusCode = fiber.StatusInternalServerError - } - - h.mongoLoggerSvc.Info("Failed to create shop bet", - zap.Int64("userID", userID), - zap.String("role", string(role)), - zap.Int("status_code", statusCode), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(statusCode, err.Error()) - } - res := domain.ConvertShopBet(shopBet) - - return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil) -} - -// CashoutBet godoc -// @Summary Cashout bet at branch -// @Description Cashout bet at branch -// @Tags transaction -// @Accept json -// @Produce json -// @Param createBet body domain.CashoutReq true "cashout bet" -// @Success 200 {object} domain.ShopTransactionRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/bet/{id} [get] -func (h *Handler) GetShopBetByBetID(c *fiber.Ctx) error { - - betIDstr := c.Params("id") - - betID, err := strconv.ParseInt(betIDstr, 10, 64) - - if err != nil { - h.mongoLoggerSvc.Info("GetShopBetByBetID failed bet id is invalid", - zap.String("betID", betIDstr), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "bet ID is invalid:"+err.Error()) - } - - bet, err := h.transactionSvc.GetShopBetByBetID(c.Context(), betID) - - if err != nil { - h.mongoLoggerSvc.Error("GetShopBetByBetID failed invalid bet id", - zap.Int64("betID", betID), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, err.Error()) - } - - res := domain.ConvertShopBetDetail(bet) - - return response.WriteJSON(c, fiber.StatusOK, "Shop bet fetched successfully", res, nil) -} - -// GetAllShopBets godoc -// @Summary Gets all shop bets -// @Description Gets all the shop bets -// @Tags bet -// @Accept json -// @Produce json -// @Success 200 {array} domain.ShopBetRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/bet [get] -func (h *Handler) GetAllShopBets(c *fiber.Ctx) error { - // role := c.Locals("role").(domain.Role) - companyID := c.Locals("company_id").(domain.ValidInt64) - branchID := c.Locals("branch_id").(domain.ValidInt64) - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - createdBeforeQuery := c.Query("created_before") - var createdBefore domain.ValidTime - if createdBeforeQuery != "" { - createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_before format", - zap.String("time", createdBeforeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format") - } - createdBefore = domain.ValidTime{ - Value: createdBeforeParsed, - Valid: true, - } - } - - createdAfterQuery := c.Query("created_after") - var createdAfter domain.ValidTime - if createdAfterQuery != "" { - createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid created_after format", - zap.String("created_after", createdAfterQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format") - } - createdAfter = domain.ValidTime{ - Value: createdAfterParsed, - Valid: true, - } - } - - bets, err := h.transactionSvc.GetAllShopBet(c.Context(), domain.ShopBetFilter{ - Query: searchString, - CreatedBefore: createdBefore, - CreatedAfter: createdAfter, - CompanyID: companyID, - BranchID: branchID, - }) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get all bets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets፡"+err.Error()) - } - - res := make([]domain.ShopBetRes, len(bets)) - for i, bet := range bets { - res[i] = domain.ConvertShopBetDetail(bet) - } - - return response.WriteJSON(c, fiber.StatusOK, "All bets retrieved successfully", res, nil) -} - -// CashoutBet godoc -// @Summary Cashout bet at branch -// @Description Cashout bet at branch -// @Tags transaction -// @Accept json -// @Produce json -// @Param cashoutBet body domain.CashoutReq true "cashout bet" -// @Success 200 {object} domain.ShopTransactionRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/bet/{id}/cashout [post] -func (h *Handler) CashoutBet(c *fiber.Ctx) error { - - betIDStr := c.Params("id") - - betID, err := strconv.ParseInt(betIDStr, 10, 64) - - if err != nil { - h.mongoLoggerSvc.Info("CashoutReq invalid bet id", - zap.String("betID", betIDStr), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid bet id") - } - - userID := c.Locals("user_id").(int64) - role := c.Locals("role").(domain.Role) - companyID := c.Locals("company_id").(domain.ValidInt64) - - var req domain.CashoutReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("CashoutReq failed to parse request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("CashoutReq validation failed", - zap.Any("request", req), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - transaction, err := h.transactionSvc.CashoutBet(c.Context(), betID, userID, role, req, companyID) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to cashout bet", - zap.Int64("userID", userID), - zap.Int64("betID", betID), - zap.String("role", string(role)), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - res := domain.ConvertShopTransaction(transaction) - - return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil) -} - -// CashoutByCashoutID godoc -// @Summary Cashout bet by cashoutID -// @Description Cashout bet by cashoutID -// @Tags transaction -// @Accept json -// @Produce json -// @Param cashoutBet body domain.CashoutReq true "cashout bet" -// @Success 200 {object} domain.ShopTransactionRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/cashout [post] -func (h *Handler) CashoutByCashoutID(c *fiber.Ctx) error { - - userID := c.Locals("user_id").(int64) - role := c.Locals("role").(domain.Role) - companyID := c.Locals("company_id").(domain.ValidInt64) - - var req domain.CashoutReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("CashoutReq failed to parse request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("CashoutReq validation failed", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("error", errMsg), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - bet, err := h.transactionSvc.GetShopBetByCashoutID(c.Context(), req.CashoutID) - - if err != nil { - h.mongoLoggerSvc.Info("CashoutReq failed invalid cashout id", - zap.String("CashoutID", req.CashoutID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - transaction, err := h.transactionSvc.CashoutBet(c.Context(), bet.BetID, userID, role, req, companyID) - - if err != nil { - h.mongoLoggerSvc.Info("Failed to cashout bet", - zap.Int64("BetID", bet.BetID), - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - res := domain.ConvertShopTransaction(transaction) - - return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil) -} - -// CashoutBet godoc -// @Summary Cashout bet at branch -// @Description Cashout bet at branch -// @Tags transaction -// @Accept json -// @Produce json -// @Param createBet body domain.CashoutReq true "cashout bet" -// @Success 200 {object} domain.ShopTransactionRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/cashout/{id} [get] -func (h *Handler) GetShopBetByCashoutID(c *fiber.Ctx) error { - - cashoutID := c.Params("id") - - if cashoutID == "" { - h.mongoLoggerSvc.Info("CashoutReq failed cashout id is required", - zap.String("cashoutID", cashoutID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "cashout ID is required") - } - - bet, err := h.transactionSvc.GetShopBetByCashoutID(c.Context(), cashoutID) - - if err != nil { - h.mongoLoggerSvc.Info("CashoutReq failed invalid cashout id", - zap.String("CashoutID", cashoutID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - res := domain.ConvertShopBetDetail(bet) - - return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil) -} - -// DepositForCustomer godoc -// @Summary Shop deposit into customer wallet -// @Description Transfers money from branch wallet to customer wallet -// @Tags transaction -// @Accept json -// @Produce json -// @Param transferToWallet body domain.ShopDepositReq true "ShopDepositReq" -// @Success 200 {object} domain.ShopDepositRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/deposit [post] -func (h *Handler) DepositForCustomer(c *fiber.Ctx) error { - - // Get sender ID from the cashier - userID := c.Locals("user_id").(int64) - role := c.Locals("role").(domain.Role) - - var req domain.ShopDepositReq - - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("CreateTransferReq failed", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate customer deposit", - zap.Any("request", req), - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("error", errMsg), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - deposit, err := h.transactionSvc.CreateShopDeposit(c.Context(), userID, role, req) - - if err != nil { - h.mongoLoggerSvc.Info("failed to create shop deposit", - zap.Int64("userID", userID), - zap.String("role", string(role)), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := domain.ConvertShopDeposit(deposit) - - return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil) - -} - -// GetAllTransactions godoc -// @Summary Gets all transactions -// @Description Gets all the transactions -// @Tags transaction -// @Accept json -// @Produce json -// @Success 200 {array} domain.ShopTransactionRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/transaction [get] -func (h *Handler) GetAllTransactions(c *fiber.Ctx) error { - // Get user_id from middleware - // userID := c.Locals("user_id").(int64) - // role := c.Locals("role").(domain.Role) - companyID := c.Locals("company_id").(domain.ValidInt64) - branchID := c.Locals("branch_id").(domain.ValidInt64) - role := c.Locals("role").(domain.Role) - - searchQuery := c.Query("query") - searchString := domain.ValidString{ - Value: searchQuery, - Valid: searchQuery != "", - } - - createdBeforeQuery := c.Query("created_before") - var createdBefore domain.ValidTime - if createdBeforeQuery != "" { - createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid start_time format", - zap.String("createdBefore", createdBeforeQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format") - } - createdBefore = domain.ValidTime{ - Value: createdBeforeParsed, - Valid: true, - } - } - - createdAfterQuery := c.Query("created_after") - var createdAfter domain.ValidTime - if createdAfterQuery != "" { - createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery) - if err != nil { - h.mongoLoggerSvc.Info("invalid start_time format", - zap.String("createdAfter", createdAfterQuery), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format") - } - createdAfter = domain.ValidTime{ - Value: createdAfterParsed, - Valid: true, - } - } - - companyFilter := int64(c.QueryInt("company_id")) - if role == domain.RoleSuperAdmin { - companyID = domain.ValidInt64{ - Value: companyFilter, - Valid: companyFilter != 0, - } - } - - // Check user role and fetch transactions accordingly - transactions, err := h.transactionSvc.GetAllShopTransactions(c.Context(), domain.ShopTransactionFilter{ - CompanyID: companyID, - BranchID: branchID, - Query: searchString, - CreatedBefore: createdBefore, - CreatedAfter: createdAfter, - }) - - if err != nil { - h.mongoLoggerSvc.Info("Failed to get transactions", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := make([]domain.ShopTransactionRes, len(transactions)) - for i, transaction := range transactions { - res[i] = domain.ConvertShopTransactionDetail(transaction) - } - - return response.WriteJSON(c, fiber.StatusOK, "Transactions retrieved successfully", res, nil) - -} - -// GetTransactionByID godoc -// @Summary Gets transaction by id -// @Description Gets a single transaction by id -// @Tags transaction -// @Accept json -// @Produce json -// @Param id path int true "Transaction ID" -// @Success 200 {object} domain.ShopTransactionRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/transaction/{id} [get] -func (h *Handler) GetTransactionByID(c *fiber.Ctx) error { - transactionID := c.Params("id") - id, err := strconv.ParseInt(transactionID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid transaction ID", - zap.String("transactionID", transactionID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction ID") - } - - transaction, err := h.transactionSvc.GetShopTransactionByID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get shop transaction by ID", - zap.Int64("transactionID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve transaction") - } - - res := domain.ConvertShopTransactionDetail(transaction) - return response.WriteJSON(c, fiber.StatusOK, "Transaction retrieved successfully", res, nil) -} - -// GetShopBetByTransactionID godoc -// @Summary Gets shop bet by transaction id -// @Description Gets a single shop bet by transaction id -// @Tags transaction -// @Accept json -// @Produce json -// @Param id path int true "Transaction ID" -// @Success 200 {object} domain.ShopTransactionRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/transaction/{id}/bet [get] -func (h *Handler) GetShopBetByTransactionID(c *fiber.Ctx) error { - transactionID := c.Params("id") - id, err := strconv.ParseInt(transactionID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid transaction ID", - zap.String("transactionID", transactionID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - transaction, err := h.transactionSvc.GetShopBetByShopTransactionID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get transaction by ID", - zap.Int64("transactionID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - res := domain.ConvertShopBetDetail(transaction) - return response.WriteJSON(c, fiber.StatusOK, "Shop bet retrieved successfully", res, nil) -} - -// UpdateTransactionVerified godoc -// @Summary Updates the verified field of a transaction -// @Description Updates the verified status of a transaction -// @Tags transaction -// @Accept json -// @Produce json -// @Param id path int true "Transaction ID" -// @Param updateVerified body domain.UpdateTransactionVerifiedReq true "Updates Transaction Verification" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/shop/transaction/{id} [put] -func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error { - transactionID := c.Params("id") - userID := c.Locals("user_id").(int64) - companyID := c.Locals("company_id").(domain.ValidInt64) - branchID := c.Locals("branch_id").(domain.ValidInt64) - role := c.Locals("role").(domain.Role) - - id, err := strconv.ParseInt(transactionID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid transaction ID", - zap.String("transactionID", transactionID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction ID") - } - - var req domain.UpdateTransactionVerifiedReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("Failed to parse UpdateTransactionVerified request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - - if valErrs, ok := h.validator.Validate(c, req); !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate UpdateTransactionVerified", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - zap.String("errMsg", errMsg), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err = h.transactionSvc.UpdateShopTransactionVerified(c.Context(), id, req.Verified, userID, role, companyID, branchID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to update transaction verification", - zap.Int64("transactionID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update transaction verification") - } - - return response.WriteJSON(c, fiber.StatusOK, "Transaction updated successfully", nil, nil) -} diff --git a/internal/web_server/handlers/telebirr.go b/internal/web_server/handlers/telebirr.go deleted file mode 100644 index dbb808e..0000000 --- a/internal/web_server/handlers/telebirr.go +++ /dev/null @@ -1,98 +0,0 @@ -package handlers - -import ( - "strconv" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" -) - -// CreateTelebirrPaymentHandler initializes a payment session with Telebirr. -// -// @Summary Create Telebirr Payment Session -// @Description Generates a payment URL using Telebirr and returns it to the client. -// @Tags Telebirr -// @Accept json -// @Produce json -// @Param request body domain.GeneratePaymentURLRequest true "Telebirr payment request payload" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/telebirr/payment [post] -func (h *Handler) CreateTelebirrPaymentHandler(c *fiber.Ctx) error { - var req domain.TelebirrPreOrderRequestPayload - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Invalid request payload", - }) - } - totalAmount, err := strconv.ParseFloat(req.BizContent.TotalAmount, 32) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "TotalAmount must be a valid number", - }) - } - userID, ok := c.Locals("user_id").(int64) - if !ok { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: "Invalid user_id type", - Message: "user_id must be an int64", - }) - } - paymentURL, err := h.telebirrSvc.CreateTelebirrOrder(req.BizContent.Title, float32(totalAmount), userID) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to create Telebirr payment session", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Telebirr payment URL generated successfully", - Data: paymentURL, - Success: true, - StatusCode: fiber.StatusOK, - }) -} - -// HandleTelebirrCallbackHandler handles the Telebirr payment callback. -// -// @Summary Handle Telebirr Payment Callback -// @Description Processes the Telebirr payment result and updates wallet balance. -// @Tags Telebirr -// @Accept json -// @Produce json -// @Param payload body domain.TelebirrPaymentCallbackPayload true "Callback payload from Telebirr" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/telebirr/callback [post] -func (h *Handler) HandleTelebirrCallback(c *fiber.Ctx) error { - var payload domain.TelebirrPaymentCallbackPayload - - if err := c.BodyParser(&payload); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Invalid callback payload", - }) - } - - ctx := c.Context() - - err := h.telebirrSvc.HandleTelebirrPaymentCallback(ctx, &payload) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Error: err.Error(), - Message: "Failed to handle Telebirr payment callback", - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Telebirr payment processed successfully", - Data: nil, - Success: true, - StatusCode: fiber.StatusOK, - }) -} diff --git a/internal/web_server/handlers/ticket_handler.go b/internal/web_server/handlers/ticket_handler.go deleted file mode 100644 index ff265df..0000000 --- a/internal/web_server/handlers/ticket_handler.go +++ /dev/null @@ -1,269 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// CreateTicket godoc -// @Summary Create a temporary ticket -// @Description Creates a temporary ticket -// @Tags ticket -// @Accept json -// @Produce json -// @Param createTicket body domain.CreateTicketReq true "Creates ticket" -// @Success 200 {object} domain.CreateTicketRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/ticket [post] -func (h *Handler) CreateTenantTicket(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - var req domain.CreateTicketReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("Failed to parse CreateTicket request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - } - - if valErrs, ok := h.validator.Validate(c, req); !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate CreateTicketReq", - zap.Any("CreateTicketReq", req), - zap.Int("status_code", fiber.StatusBadRequest), - zap.String("error", errMsg), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - newTicket, rows, err := h.ticketSvc.CreateTicket(c.Context(), req, c.IP(), companyID.Value) - - if err != nil { - - var statusCode int - - if isTicketError := h.ticketSvc.CheckTicketError(err); isTicketError { - statusCode = fiber.StatusBadRequest - } else { - statusCode = fiber.StatusInternalServerError - } - - h.mongoLoggerSvc.Error("Failed to create ticket", - zap.Any("req", req), - zap.Int("status_code", statusCode), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(statusCode, err.Error()) - } - res := domain.CreateTicketRes{ - FastCode: newTicket.ID, - CreatedNumber: rows, - } - return response.WriteJSON(c, fiber.StatusOK, "Ticket Created", res, nil) - -} - -// GetTicketByID godoc -// @Summary Get ticket by ID -// @Description Retrieve ticket details by ticket ID -// @Tags ticket -// @Accept json -// @Produce json -// @Param id path int true "Ticket ID" -// @Success 200 {object} domain.TicketRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/ticket/{id} [get] -func (h *Handler) GetTenantTicketByID(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - ticketID := c.Params("id") - id, err := strconv.ParseInt(ticketID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid ticket ID", - zap.String("ticketID", ticketID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid ticket ID") - } - - ticket, err := h.ticketSvc.GetTicketByID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get ticket by ID", - zap.Int64("ticketID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket") - } - - if ticket.CompanyID != companyID.Value { - h.mongoLoggerSvc.Warn("User attempt to access another company ticket", - zap.Int64("ticketID", id), - zap.Int64("ticket CompanyID", ticket.CompanyID), - zap.Int64("companyID", companyID.Value), - zap.Bool("companyID Valid", companyID.Valid), - - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket") - } - - res := domain.TicketRes{ - ID: ticket.ID, - Outcomes: ticket.Outcomes, - Amount: ticket.Amount.Float32(), - TotalOdds: ticket.TotalOdds, - CompanyID: ticket.CompanyID, - } - return response.WriteJSON(c, fiber.StatusOK, "Ticket retrieved successfully", res, nil) -} - -// GetAllTickets godoc -// @Summary Get all tickets -// @Description Retrieve all tickets -// @Tags ticket -// @Accept json -// @Produce json -// @Success 200 {array} domain.TicketRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/ticket [get] -func (h *Handler) GetAllTenantTickets(c *fiber.Ctx) error { - companyID := c.Locals("company_id").(domain.ValidInt64) - if !companyID.Valid { - h.BadRequestLogger().Error("invalid company id") - return fiber.NewError(fiber.StatusBadRequest, "invalid company id") - } - - tickets, err := h.ticketSvc.GetAllTickets(c.Context(), domain.TicketFilter{ - CompanyID: companyID, - }) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get tickets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve tickets") - } - - res := make([]domain.TicketRes, len(tickets)) - for i, ticket := range tickets { - res[i] = domain.TicketRes{ - ID: ticket.ID, - Outcomes: ticket.Outcomes, - Amount: ticket.Amount.Float32(), - TotalOdds: ticket.TotalOdds, - } - } - - return response.WriteJSON(c, fiber.StatusOK, "All tickets retrieved successfully", res, nil) -} - -// GetTicketByID godoc -// @Summary Get ticket by ID -// @Description Retrieve ticket details by ticket ID -// @Tags ticket -// @Accept json -// @Produce json -// @Param id path int true "Ticket ID" -// @Success 200 {object} domain.TicketRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/ticket/{id} [get] -func (h *Handler) GetTicketByID(c *fiber.Ctx) error { - ticketID := c.Params("id") - id, err := strconv.ParseInt(ticketID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid ticket ID", - zap.String("ticketID", ticketID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid ticket ID") - } - - ticket, err := h.ticketSvc.GetTicketByID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Info("Failed to get ticket by ID", - zap.Int64("ticketID", id), - zap.Int("status_code", fiber.StatusNotFound), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket") - } - - res := domain.TicketRes{ - ID: ticket.ID, - Outcomes: ticket.Outcomes, - Amount: ticket.Amount.Float32(), - TotalOdds: ticket.TotalOdds, - CompanyID: ticket.CompanyID, - } - return response.WriteJSON(c, fiber.StatusOK, "Ticket retrieved successfully", res, nil) -} - -// GetAllTickets godoc -// @Summary Get all tickets -// @Description Retrieve all tickets -// @Tags ticket -// @Accept json -// @Produce json -// @Success 200 {array} domain.TicketRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/ticket [get] -func (h *Handler) GetAllTickets(c *fiber.Ctx) error { - - tickets, err := h.ticketSvc.GetAllTickets(c.Context(), domain.TicketFilter{}) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get tickets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve tickets") - } - - res := make([]domain.TicketRes, len(tickets)) - for i, ticket := range tickets { - res[i] = domain.TicketRes{ - ID: ticket.ID, - Outcomes: ticket.Outcomes, - Amount: ticket.Amount.Float32(), - TotalOdds: ticket.TotalOdds, - } - } - - return response.WriteJSON(c, fiber.StatusOK, "All tickets retrieved successfully", res, nil) -} diff --git a/internal/web_server/handlers/transaction_approver.go b/internal/web_server/handlers/transaction_approver.go index e949a9d..7e230e0 100644 --- a/internal/web_server/handlers/transaction_approver.go +++ b/internal/web_server/handlers/transaction_approver.go @@ -1,13 +1,13 @@ package handlers import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/services/authentication" + "Yimaru-Backend/internal/web_server/response" "fmt" "strconv" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) @@ -60,16 +60,16 @@ func (h *Handler) CreateTransactionApprover(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, errMsg) } - _, err := h.companySvc.GetCompanyByID(c.Context(), req.CompanyID) - if err != nil { - h.mongoLoggerSvc.Error("invalid company ID for CreateAdmin", - zap.Int64("status_code", fiber.StatusInternalServerError), - zap.Int64("company_id", req.CompanyID), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Company ID is invalid:"+err.Error()) - } + // _, err := h.companySvc.GetCompanyByID(c.Context(), req.CompanyID) + // if err != nil { + // h.mongoLoggerSvc.Error("invalid company ID for CreateAdmin", + // zap.Int64("status_code", fiber.StatusInternalServerError), + // zap.Int64("company_id", req.CompanyID), + // zap.Error(err), + // zap.Time("timestamp", time.Now()), + // ) + // return fiber.NewError(fiber.StatusInternalServerError, "Company ID is invalid:"+err.Error()) + // } companyID = domain.ValidInt64{ Value: req.CompanyID, Valid: true, @@ -175,7 +175,7 @@ func (h *Handler) GetAllTransactionApprovers(c *fiber.Ctx) error { var companyIDFilter domain.ValidInt64 if role == domain.RoleSuperAdmin { companyIDQuery := int64(c.QueryInt("company_id")) - companyIDFilter = domain.ValidInt64{ + companyIDFilter = domain.ValidInt64{ Value: companyIDQuery, Valid: companyIDQuery != 0, } diff --git a/internal/web_server/handlers/transfer_handler.go b/internal/web_server/handlers/transfer_handler.go deleted file mode 100644 index efe9f5a..0000000 --- a/internal/web_server/handlers/transfer_handler.go +++ /dev/null @@ -1,381 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -type TransferWalletRes struct { - ID int64 `json:"id"` - Amount float32 `json:"amount"` - Verified bool `json:"verified"` - Message string `json:"message"` - Type string `json:"type"` - PaymentMethod string `json:"payment_method"` - ReceiverWalletID *int64 `json:"receiver_wallet_id,omitempty"` - SenderWalletID *int64 `json:"sender_wallet_id,omitempty"` - DepositorID *int64 `json:"depositor_id,omitempty"` - DepositorFirstName string `json:"depositor_first_name"` - DepositorLastName string `json:"depositor_last_name"` - DepositorPhoneNumber string `json:"depositor_phone_number"` - ReferenceNumber string `json:"reference_number"` // ← Add this - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -type RefillRes struct { - ID int64 `json:"id" example:"1"` - Amount float32 `json:"amount" example:"100.0"` - Verified bool `json:"verified" example:"true"` - Type string `json:"type" example:"transfer"` - PaymentMethod string `json:"payment_method" example:"bank"` - ReceiverWalletID *int64 `json:"receiver_wallet_id" example:"1"` - SenderWalletID *int64 `json:"sender_wallet_id" example:"1"` - CashierID *int64 `json:"cashier_id" example:"789"` - CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"` - UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"` -} - -func convertTransfer(t domain.Transfer) TransferWalletRes { - var receiverID *int64 - if t.ReceiverWalletID.Valid { - receiverID = &t.ReceiverWalletID.Value - } - - var senderID *int64 - if t.SenderWalletID.Valid { - senderID = &t.SenderWalletID.Value - } - - var depositorID *int64 - if t.DepositorID.Valid { - depositorID = &t.DepositorID.Value - } - - return TransferWalletRes{ - ID: t.ID, - Amount: t.Amount.Float32(), - Verified: t.Verified, - Message: t.Message, - Type: string(t.Type), - PaymentMethod: string(t.PaymentMethod), - ReceiverWalletID: receiverID, - SenderWalletID: senderID, - DepositorID: depositorID, - ReferenceNumber: t.ReferenceNumber, - CreatedAt: t.CreatedAt, - UpdatedAt: t.UpdatedAt, - } -} -func convertTransferDetail(t domain.TransferDetail) TransferWalletRes { - var receiverID *int64 - if t.ReceiverWalletID.Valid { - receiverID = &t.ReceiverWalletID.Value - } - - var senderID *int64 - if t.SenderWalletID.Valid { - senderID = &t.SenderWalletID.Value - } - - var depositorID *int64 - if t.DepositorID.Valid { - depositorID = &t.DepositorID.Value - } - - return TransferWalletRes{ - ID: t.ID, - Amount: t.Amount.Float32(), - Verified: t.Verified, - Message: t.Message, - Type: string(t.Type), - PaymentMethod: string(t.PaymentMethod), - ReceiverWalletID: receiverID, - SenderWalletID: senderID, - DepositorID: depositorID, - DepositorFirstName: t.DepositorFirstName, - DepositorLastName: t.DepositorLastName, - DepositorPhoneNumber: t.DepositorPhoneNumber, - ReferenceNumber: t.ReferenceNumber, - CreatedAt: t.CreatedAt, - UpdatedAt: t.UpdatedAt, - } -} - -type CreateTransferReq struct { - Amount float32 `json:"amount" example:"100.0"` - PaymentMethod string `json:"payment_method" example:"cash"` -} - -type CreateRefillReq struct { - Amount float32 `json:"amount" example:"100.0"` -} - -// GetTransfersByWallet godoc -// @Summary Get transfer by wallet -// @Description Get transfer by wallet -// @Tags transfer -// @Accept json -// @Produce json -// @Param transferToWallet body CreateTransferReq true "Create Transfer" -// @Success 200 {object} TransferWalletRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/transfer/wallet/{id} [get] -func (h *Handler) GetTransfersByWallet(c *fiber.Ctx) error { - - walletID := c.Params("id") - - id, err := strconv.ParseInt(walletID, 10, 64) - - if err != nil { - h.mongoLoggerSvc.Error("Invalid wallet ID", - zap.String("walletID", walletID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID") - } - - transfers, err := h.walletSvc.GetTransfersByWallet(c.Context(), int64(id)) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get transfers by wallet", - zap.String("walletID", walletID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var transferResponses []TransferWalletRes - for _, transfer := range transfers { - transferResponses = append(transferResponses, convertTransferDetail(transfer)) - } - - return response.WriteJSON(c, fiber.StatusOK, "Transfers retrieved successfully", transferResponses, nil) - -} - -// TransferToWallet godoc -// @Summary Create a transfer to wallet -// @Description Create a transfer to wallet -// @Tags transfer -// @Accept json -// @Produce json -// @Param transferToWallet body CreateTransferReq true "Create Transfer" -// @Success 200 {object} TransferWalletRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/transfer/wallet/:id [post] -func (h *Handler) TransferToWallet(c *fiber.Ctx) error { - - receiverIDString := c.Params("id") - - receiverID, err := strconv.ParseInt(receiverIDString, 10, 64) - - if err != nil { - h.mongoLoggerSvc.Info("Invalid wallet ID", - zap.Int64("walletID", receiverID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID") - } - // Get sender ID from the cashier - userID := c.Locals("user_id").(int64) - role := c.Locals("role").(domain.Role) - companyID := c.Locals("company_id").(domain.ValidInt64) - - // fmt.Printf("\n\nCompany ID: %v\n\n", companyID.Value) - - var senderID int64 - //TODO: check to make sure that the cashiers aren't transferring TO branch wallet - switch role { - case domain.RoleCustomer: - h.mongoLoggerSvc.Error("Unauthorized access", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusForbidden), - zap.String("role", string(role)), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusForbidden, "Unauthorized access") - case domain.RoleAdmin: - company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value) - if err != nil { - h.mongoLoggerSvc.Error("Failed to fetch company", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - senderID = company.WalletID - // h.logger.Error("Will", "userID", userID, "role", role) - - case domain.RoleSuperAdmin: - return fiber.NewError(fiber.StatusBadRequest, "Super Admin does not have a wallet") - case domain.RoleBranchManager: - return fiber.NewError(fiber.StatusBadRequest, "Branch Manager does not have a wallet") - case domain.RoleCashier: - cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get branch by cashier", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - senderID = cashierBranch.WalletID - default: - h.mongoLoggerSvc.Error("Unknown Role", - zap.Int64("userID", userID), - zap.String("role", string(role)), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Unknown Role") - } - - var req CreateTransferReq - - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Error("CreateTransferReq failed to parse body", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - - h.mongoLoggerSvc.Error("Failed to validate CreateTransferReq", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - transfer, err := h.walletSvc.TransferToWallet(c.Context(), - senderID, receiverID, domain.ToCurrency(req.Amount), domain.PaymentMethod(req.PaymentMethod), - domain.ValidInt64{Value: userID, Valid: true}, - fmt.Sprintf("Transferred %v from wallet to another", req.Amount), - ) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to transfer money to wallet", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := convertTransfer(transfer) - - return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil) -} - -// RefillWallet godoc -// @Summary Refill wallet -// @Description Super Admin route to refill a wallet -// @Tags transfer -// @Accept json -// @Produce json -// @Param refillWallet body CreateTransferReq true "Create Transfer" -// @Success 200 {object} TransferWalletRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/transfer/refill/:id [post] -func (h *Handler) RefillWallet(c *fiber.Ctx) error { - - receiverIDString := c.Params("id") - - userID := c.Locals("user_id").(int64) - receiverID, err := strconv.ParseInt(receiverIDString, 10, 64) - - if err != nil { - h.mongoLoggerSvc.Error("Invalid wallet ID", - zap.Int64("walletID", receiverID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID") - } - // Get sender ID from the cashier - - var req CreateRefillReq - - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("CreateRefillReq failed to parse CreateRefillReq", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - - valErrs, ok := h.validator.Validate(c, req) - if !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate CreateRefillReq", - zap.Int64("userID", userID), - zap.String("errMsg", errMsg), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - - } - - transfer, err := h.walletSvc.AddToWallet( - c.Context(), receiverID, domain.ToCurrency(req.Amount), domain.ValidInt64{ - Value: userID, - Valid: true, - }, domain.TRANSFER_BANK, domain.PaymentDetails{}, fmt.Sprintf("Added %v to wallet directly by super-admin", req.Amount)) - - if !ok { - h.mongoLoggerSvc.Error("Creating Transfer Failed", - zap.Int64("userID", userID), - zap.Float32("Amount", req.Amount), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - - } - - res := convertTransfer(transfer) - - return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil) - -} diff --git a/internal/web_server/handlers/user.go b/internal/web_server/handlers/user.go index 5103d0d..fd7bda0 100644 --- a/internal/web_server/handlers/user.go +++ b/internal/web_server/handlers/user.go @@ -1,15 +1,14 @@ package handlers import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/services/authentication" + "Yimaru-Backend/internal/web_server/response" "errors" "fmt" "strconv" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) @@ -63,20 +62,20 @@ func (h *Handler) GetTenantSlugByToken(c *fiber.Ctx) error { ) return fiber.NewError(fiber.StatusInternalServerError, "Unknown Error: User doesn't have a company-id") } - company, err := h.companySvc.GetCompanyByID(c.Context(), user.CompanyID.Value) + // company, err := h.companySvc.GetCompanyByID(c.Context(), user.CompanyID.Value) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get company by id", - zap.Int64("company", user.CompanyID.Value), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve company:"+err.Error()) - } + // if err != nil { + // h.mongoLoggerSvc.Error("Failed to get company by id", + // zap.Int64("company", user.CompanyID.Value), + // zap.Int("status_code", fiber.StatusInternalServerError), + // zap.Error(err), + // zap.Time("timestamp", time.Now()), + // ) + // return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve company:"+err.Error()) + // } res := GetTenantSlugByToken{ - Slug: company.Slug, + Slug: strconv.FormatInt(user.CompanyID.Value,10), } return response.WriteJSON(c, fiber.StatusOK, "Tenant Slug retrieved successfully", res, nil) @@ -304,16 +303,16 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusInternalServerError, "failed to register user:"+err.Error()) } - _, err = h.walletSvc.CreateCustomerWallet(c.Context(), newUser.ID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to create wallet for user", - zap.Int64("userID", newUser.ID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create user wallet:"+err.Error()) - } + // _, err = h.walletSvc.CreateCustomerWallet(c.Context(), newUser.ID) + // if err != nil { + // h.mongoLoggerSvc.Error("Failed to create wallet for user", + // zap.Int64("userID", newUser.ID), + // zap.Int("status_code", fiber.StatusInternalServerError), + // zap.Error(err), + // zap.Time("timestamp", time.Now()), + // ) + // return fiber.NewError(fiber.StatusInternalServerError, "Failed to create user wallet:"+err.Error()) + // } if req.ReferralCode != "" { err = h.referralSvc.ProcessReferral(c.Context(), newUser.ID, req.ReferralCode, companyID.Value) @@ -1174,32 +1173,32 @@ func (h *Handler) UpdateUserSuspend(c *fiber.Ctx) error { // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/{tenant_slug}/user/bets [get] -func (h *Handler) GetBetByUserID(c *fiber.Ctx) error { - userID, ok := c.Locals("user_id").(int64) - if !ok || userID == 0 { - h.mongoLoggerSvc.Error("Invalid user ID in context", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification") - } +// func (h *Handler) GetBetByUserID(c *fiber.Ctx) error { +// userID, ok := c.Locals("user_id").(int64) +// if !ok || userID == 0 { +// h.mongoLoggerSvc.Error("Invalid user ID in context", +// zap.Int64("userID", userID), +// zap.Int("status_code", fiber.StatusInternalServerError), +// zap.Time("timestamp", time.Now()), +// ) +// return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification") +// } - bets, err := h.betSvc.GetBetByUserID(c.Context(), userID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get bets", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets:"+err.Error()) - } +// // bets, err := h.betSvc.GetBetByUserID(c.Context(), userID) +// // if err != nil { +// // h.mongoLoggerSvc.Error("Failed to get bets", +// // zap.Int64("userID", userID), +// // zap.Int("status_code", fiber.StatusInternalServerError), +// // zap.Error(err), +// // zap.Time("timestamp", time.Now()), +// // ) +// // return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets:"+err.Error()) +// // } - res := make([]domain.BetRes, len(bets)) - for i, bet := range bets { - res[i] = domain.ConvertBet(bet) - } +// // res := make([]domain.BetRes, len(bets)) +// // for i, bet := range bets { +// // res[i] = domain.ConvertBet(bet) +// // } - return response.WriteJSON(c, fiber.StatusOK, "User bets retrieved successfully", res, nil) -} +// return response.WriteJSON(c, fiber.StatusOK, "User bets retrieved successfully", res, nil) +// } diff --git a/internal/web_server/handlers/veli_games.go b/internal/web_server/handlers/veli_games.go deleted file mode 100644 index 723777f..0000000 --- a/internal/web_server/handlers/veli_games.go +++ /dev/null @@ -1,506 +0,0 @@ -package handlers - -import ( - "context" - "errors" - "fmt" - "time" - - // "fmt" - "strings" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -// GetProviders godoc -// @Summary Get game providers -// @Description Retrieves the list of VeliGames providers -// @Tags Virtual Games - VeliGames -// @Accept json -// @Produce json -// @Param request body domain.ProviderRequest true "Brand ID and paging options" -// @Success 200 {object} domain.Response{data=[]domain.ProviderResponse} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 401 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/veli/providers [post] -func (h *Handler) GetProviders(c *fiber.Ctx) error { - var req domain.ProviderRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to retrieve providers", - Error: err.Error(), - }) - } - if req.BrandID == "" { - req.BrandID = h.Cfg.VeliGames.BrandID // default - } - res, err := h.veliVirtualGameSvc.GetProviders(context.Background(), req) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetProviders", - zap.Any("request", req), - zap.Error(err), - ) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to retrieve providers", - Error: err.Error(), - }) - } - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Providers retrieved successfully", - Data: res, - StatusCode: 200, - Success: true, - }) -} - -// GetGamesByProvider godoc -// @Summary Get games by provider -// @Description Retrieves games for the specified provider -// @Tags Virtual Games - VeliGames -// @Accept json -// @Produce json -// @Param request body domain.GameListRequest true "Brand and Provider ID" -// @Success 200 {object} domain.Response -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/veli/games-list [post] -func (h *Handler) GetGamesByProvider(c *fiber.Ctx) error { - var req domain.GameListRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - // Default brand if not provided - if req.BrandID == "" { - req.BrandID = h.Cfg.VeliGames.BrandID - } - - res, err := h.veliVirtualGameSvc.GetGames(context.Background(), req) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetGames", - zap.Any("request", req), - zap.Error(err), - ) - // Handle provider disabled case specifically - if strings.Contains(err.Error(), "is disabled") { - return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{ - Message: "Provider is disabled", - Error: err.Error(), - }) - } - - // Fallback for other errors - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to retrieve games", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Games retrieved successfully", - Data: res, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// StartGame godoc -// @Summary Start a real game session -// @Description Starts a real VeliGames session with the given player and game info -// @Tags Virtual Games - VeliGames -// @Accept json -// @Produce json -// @Param request body domain.GameStartRequest true "Start game input" -// @Success 200 {object} domain.Response{data=domain.GameStartResponse} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/veli/start-game [post] -func (h *Handler) StartGame(c *fiber.Ctx) error { - var req domain.GameStartRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - // Default brand if not provided - if req.BrandID == "" { - req.BrandID = h.Cfg.VeliGames.BrandID - } - - // userId := c.Locals("user_id") - - req.IP = c.IP() - req.PlayerID = fmt.Sprintf("%v", c.Locals("user_id")) - - // 1️⃣ Call StartGame service - res, err := h.veliVirtualGameSvc.StartGame(context.Background(), req) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]StartGame", - zap.Any("request", req), - zap.Error(err), - ) - - if strings.Contains(err.Error(), "is disabled") { - return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{ - Message: "Provider is disabled", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to start game", - Error: err.Error(), - }) - } - - // 2️⃣ Game started successfully → Update total_games_played - go func() { - ctx := context.Background() - reportDate := time.Now().Truncate(24 * time.Hour) - reportType := "daily" - - // Increment total_games_played by 1 - err := h.orchestrationSvc.UpdateVirtualGameProviderReportByDate( - ctx, - req.ProviderID, - reportDate, - reportType, - 1, // increment total_games_played by 1 - 0, - 0, - 1, - ) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update total_games_played", - zap.String("provider_id", req.ProviderID), - zap.Error(err), - ) - } - }() - - // 3️⃣ Return response to user - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Game started successfully", - Data: res, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// StartDemoGame godoc -// @Summary Start a demo game session -// @Description Starts a demo session of the specified game (must support demo mode) -// @Tags Virtual Games - VeliGames -// @Accept json -// @Produce json -// @Param request body domain.DemoGameRequest true "Start demo game input" -// @Success 200 {object} domain.Response{data=domain.GameStartResponse} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/veli/start-demo-game [post] -func (h *Handler) StartDemoGame(c *fiber.Ctx) error { - var req domain.DemoGameRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - // Default brand if not provided - if req.BrandID == "" { - req.BrandID = h.Cfg.VeliGames.BrandID - } - - req.IP = c.IP() - - res, err := h.veliVirtualGameSvc.StartDemoGame(context.Background(), req) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]StartDemoGame", - zap.Any("request", req), - zap.Error(err), - ) - // Handle provider disabled case specifically - if strings.Contains(err.Error(), "is disabled") { - return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{ - Message: "Provider is disabled", - Error: err.Error(), - }) - } - - // Fallback for other errors - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to start demo game", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Demo game started successfully", - Data: res, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -func (h *Handler) GetBalance(c *fiber.Ctx) error { - var req domain.BalanceRequest - if err := c.BodyParser(&req); err != nil { - // return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - // Optionally verify signature here... - - balance, err := h.veliVirtualGameSvc.GetBalance(c.Context(), req) - if err != nil { - // return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to retrieve balance", - Error: err.Error(), - }) - } - - return c.JSON(balance) -} - -func (h *Handler) PlaceBet(c *fiber.Ctx) error { - var req domain.BetRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - // 1️⃣ Process the bet with the external provider - res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), req) - if err != nil { - if errors.Is(err, veli.ErrDuplicateTransaction) { - return fiber.NewError(fiber.StatusConflict, "DUPLICATE_TRANSACTION") - } - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to process bet", - Error: err.Error(), - }) - } - - // 2️⃣ If bet successful → update total_bets in the report - go func() { - ctx := context.Background() - reportDate := time.Now().Truncate(24 * time.Hour) - reportType := "daily" - - // Increment total_bets by the bet amount - err := h.orchestrationSvc.UpdateVirtualGameProviderReportByDate( - ctx, - req.ProviderID, - reportDate, - reportType, - 0, // total_games_played (no change) - req.Amount.Amount, // add this bet to total_bets - 0, // total_payouts (no change) - 0, // total_players (no change) - ) - - if err != nil { - h.InternalServerErrorLogger().Error("Failed to update total_bets after bet", - zap.String("provider_id", req.ProviderID), - zap.Float64("bet_amount", req.Amount.Amount), - zap.Error(err), - ) - } - }() - - // 3️⃣ Return success response - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Bet processed successfully", - Data: res, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -func (h *Handler) RegisterWin(c *fiber.Ctx) error { - var req domain.WinRequest - if err := c.BodyParser(&req); err != nil { - // return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - res, err := h.veliVirtualGameSvc.ProcessWin(c.Context(), req) - if err != nil { - if errors.Is(err, veli.ErrDuplicateTransaction) { - return fiber.NewError(fiber.StatusConflict, "DUPLICATE_TRANSACTION") - } - // return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to process win", - Error: err.Error(), - }) - } - - return c.JSON(res) -} - -func (h *Handler) CancelTransaction(c *fiber.Ctx) error { - var req domain.CancelRequest - if err := c.BodyParser(&req); err != nil { - // return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request body", - Error: err.Error(), - }) - } - - res, err := h.veliVirtualGameSvc.ProcessCancel(c.Context(), req) - if err != nil { - if errors.Is(err, veli.ErrDuplicateTransaction) { - return fiber.NewError(fiber.StatusConflict, "DUPLICATE_TRANSACTION") - } - // return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Failed to process cancel", - Error: err.Error(), - }) - } - - return c.JSON(res) -} - -// GetGamingActivity godoc -// @Summary Get Veli Gaming Activity -// @Description Retrieves successfully processed gaming activity transactions (BET, WIN, CANCEL) from Veli Games -// @Tags Virtual Games - VeliGames -// @Accept json -// @Produce json -// @Param request body domain.GamingActivityRequest true "Gaming Activity Request" -// @Success 200 {object} domain.Response{data=domain.GamingActivityResponse} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/veli/gaming-activity [post] -func (h *Handler) GetGamingActivity(c *fiber.Ctx) error { - var req domain.GamingActivityRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request payload", - Error: err.Error(), - }) - } - - resp, err := h.veliVirtualGameSvc.GetGamingActivity(c.Context(), req) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetGamingActivity", - zap.Any("request", req), - zap.Error(err), - ) - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to retrieve gaming activity", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Gaming activity retrieved successfully", - Data: resp, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetHugeWins godoc -// @Summary Get Veli Huge Wins -// @Description Retrieves huge win transactions based on brand configuration (e.g. win > 10000 USD or 100x bet) -// @Tags Virtual Games - VeliGames -// @Accept json -// @Produce json -// @Param request body domain.HugeWinsRequest true "Huge Wins Request" -// @Success 200 {object} domain.Response{data=domain.HugeWinsResponse} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/veli/huge-wins [post] -func (h *Handler) GetHugeWins(c *fiber.Ctx) error { - var req domain.HugeWinsRequest - if err := c.BodyParser(&req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid request payload", - Error: err.Error(), - }) - } - - resp, err := h.veliVirtualGameSvc.GetHugeWins(c.Context(), req) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetHugeWins", - zap.Any("request", req), - zap.Error(err), - ) - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to retrieve huge wins", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Huge wins retrieved successfully", - Data: resp, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// GetCreditBalances godoc -// @Summary Get VeliGames credit balances for a brand -// @Description Fetches current credit balances per currency for the specified brand -// @Tags Virtual Games - VeliGames -// @Accept json -// @Produce json -// @Param brandId query string true "Brand ID" -// @Success 200 {object} domain.Response{data=[]domain.CreditBalance} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/veli/credit-balances [get] -func (h *Handler) GetCreditBalances(c *fiber.Ctx) error { - brandID := c.Query("brandId", h.Cfg.VeliGames.BrandID) // Default brand if not provided - - if brandID == "" { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Brand ID is required", - Error: "missing brandId", - }) - } - - res, err := h.veliVirtualGameSvc.GetCreditBalances(c.Context(), brandID) - if err != nil { - h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetCreditBalances", - zap.String("brandID", brandID), - zap.Error(err), - ) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch credit balances", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Credit balances fetched successfully", - Data: res, - StatusCode: fiber.StatusOK, - Success: true, - }) -} diff --git a/internal/web_server/handlers/virtual_games_hadlers.go b/internal/web_server/handlers/virtual_games_hadlers.go deleted file mode 100644 index 20de8db..0000000 --- a/internal/web_server/handlers/virtual_games_hadlers.go +++ /dev/null @@ -1,971 +0,0 @@ -package handlers - -import ( - "encoding/json" - "fmt" - "log" - "strconv" - "strings" - - dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v5/pgtype" -) - -type launchVirtualGameReq struct { - GameID string `json:"game_id" validate:"required" example:"1"` - Currency string `json:"currency" validate:"required,len=3" example:"USD"` - Mode string `json:"mode" validate:"required,oneof=fun real" example:"real"` -} - -type launchVirtualGameRes struct { - LaunchURL string `json:"launch_url"` -} - -// ListVirtualGameProviderReportsAscHandler -// @Summary List virtual game provider reports (ascending) -// @Description Retrieves all virtual game provider reports sorted by total_games_played in ascending order -// @Tags VirtualGames - Orchestration -// @Success 200 {array} domain.VirtualGameProviderReport -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/orchestrator/virtual-game/provider-reports/asc [get] -func (h *Handler) ListVirtualGameProviderReportsAscHandler(c *fiber.Ctx) error { - reports, err := h.orchestrationSvc.ListVirtualGameProviderReportsByGamesPlayedAsc(c.Context()) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to fetch virtual game provider reports ascending", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Virtual game provider reports retrieved successfully", - Data: reports, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// ListVirtualGameProviderReportsDescHandler -// @Summary List virtual game provider reports (descending) -// @Description Retrieves all virtual game provider reports sorted by total_games_played in descending order -// @Tags VirtualGames - Orchestration -// @Success 200 {array} domain.VirtualGameProviderReport -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/orchestrator/virtual-game/provider-reports/desc [get] -func (h *Handler) ListVirtualGameProviderReportsDescHandler(c *fiber.Ctx) error { - reports, err := h.orchestrationSvc.ListVirtualGameProviderReportsByGamesPlayedDesc(c.Context()) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Failed to fetch virtual game provider reports descending", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Virtual game provider reports retrieved successfully", - Data: reports, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// ListVirtualGames godoc -// @Summary List all virtual games -// @Description Returns all virtual games with optional filters (category, search, pagination) -// @Tags VirtualGames - Orchestration -// @Accept json -// @Produce json -// @Param category query string false "Filter by category" -// @Param search query string false "Search by game name" -// @Param limit query int false "Pagination limit" -// @Param offset query int false "Pagination offset" -// @Success 200 {object} domain.Response{data=[]domain.UnifiedGame} -// @Failure 400 {object} domain.ErrorResponse -// @Failure 502 {object} domain.ErrorResponse -// @Router /api/v1/orchestrator/virtual-games [get] -func (h *Handler) ListVirtualGames(c *fiber.Ctx) error { - // --- Parse query parameters --- - limit := c.QueryInt("limit", 100) - if limit <= 0 { - limit = 100 - } - offset := c.QueryInt("offset", 0) - if offset < 0 { - offset = 0 - } - category := c.Query("category", "") - search := c.Query("search", "") - providerID := c.Query("providerID", "") - - params := dbgen.GetAllVirtualGamesParams{ - Category: pgtype.Text{ - String: category, - Valid: category != "", - }, - Name: pgtype.Text{ - String: search, - Valid: search != "", - }, - ProviderID: pgtype.Text{ - String: providerID, - Valid: providerID != "", - }, - Offset: pgtype.Int4{ - Int32: int32(offset), - Valid: offset != 0, - }, - Limit: pgtype.Int4{ - Int32: int32(limit), - Valid: limit != 0, - }, - } - - // --- Call service method --- - games, err := h.orchestrationSvc.GetAllVirtualGames(c.Context(), params) - if err != nil { - log.Println("ListVirtualGames error:", err) - return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ - Message: "Failed to fetch virtual games", - Error: err.Error(), - }) - } - - // --- Return response --- - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Virtual games fetched successfully", - Data: games, - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// RemoveProviderHandler -// @Summary Remove a virtual game provider -// @Description Deletes a provider by provider_id -// @Tags VirtualGames - Orchestration -// @Param provider_id path string true "Provider ID" -// @Success 200 -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/virtual-game/providers/{provider_id} [delete] -func (h *Handler) RemoveProvider(c *fiber.Ctx) error { - providerID := c.Params("providerID") - if err := h.orchestrationSvc.RemoveProvider(c.Context(), providerID); err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Could not remove provider") - } - return c.SendStatus(fiber.StatusOK) -} - -// GetProviderHandler -// @Summary Get a virtual game provider -// @Description Fetches a provider by provider_id -// @Tags VirtualGames - Orchestration -// @Param provider_id path string true "Provider ID" -// @Produce json -// @Success 200 {object} domain.VirtualGameProvider -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/virtual-game/providers/{provider_id} [get] -func (h *Handler) GetProviderByID(c *fiber.Ctx) error { - providerID := c.Params("providerID") - provider, err := h.orchestrationSvc.GetProviderByID(c.Context(), providerID) - if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Could not fetch provider") - } - return c.Status(fiber.StatusOK).JSON(provider) -} - -// ListProvidersHandler -// @Summary List virtual game providers -// @Description Lists all providers with pagination -// @Tags VirtualGames - Orchestration -// @Produce json -// @Param limit query int false "Limit" -// @Param offset query int false "Offset" -// @Success 200 {object} map[string]interface{} -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/virtual-game/providers [get] -func (h *Handler) ListProviders(c *fiber.Ctx) error { - limit, _ := strconv.Atoi(c.Query("limit", "20")) - offset, _ := strconv.Atoi(c.Query("offset", "0")) - - providers, total, err := h.orchestrationSvc.ListProviders(c.Context(), int32(limit), int32(offset)) - if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Could not list providers") - } - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "total": total, - "providers": providers, - }) -} - -// SetProviderEnabledHandler -// @Summary Enable/Disable a provider -// @Description Sets the enabled status of a provider -// @Tags VirtualGames - Orchestration -// @Param provider_id path string true "Provider ID" -// @Param enabled query bool true "Enable or Disable" -// @Produce json -// @Success 200 {object} domain.VirtualGameProvider -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/virtual-game/orchestrator/providers/status [patch] -func (h *Handler) SetProviderEnabled(c *fiber.Ctx) error { - providerID := c.Params("providerID") - enabled, _ := strconv.ParseBool(c.Query("enabled", "true")) - - provider, err := h.orchestrationSvc.SetProviderEnabled(c.Context(), providerID, enabled) - if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Could not update provider status") - } - - return c.Status(fiber.StatusOK).JSON(provider) -} - -// LaunchVirtualGame godoc -// @Summary Launch a PopOK virtual game -// @Description Generates a URL to launch a PopOK game -// @Tags Virtual Games - PopOK -// @Accept json -// @Produce json -// @Security Bearer -// @Param launch body launchVirtualGameReq true "Game launch details" -// @Success 200 {object} launchVirtualGameRes -// @Failure 400 {object} domain.ErrorResponse -// @Failure 401 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /virtual-game/launch [post] -func (h *Handler) LaunchVirtualGame(c *fiber.Ctx) error { - - userID, ok := c.Locals("user_id").(int64) - if !ok || userID == 0 { - h.logger.Error("Invalid user ID in context") - return fiber.NewError(fiber.StatusUnauthorized, "Invalid user identification") - } - - // companyID, ok := c.Locals("company_id").(int64) - // if !ok || companyID == 0 { - // h.logger.Error("Invalid company ID in context") - // return fiber.NewError(fiber.StatusUnauthorized, "Invalid company identification") - // } - - var req launchVirtualGameReq - if err := c.BodyParser(&req); err != nil { - h.logger.Error("Failed to parse LaunchVirtualGame request", "error", err) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - } - - if valErrs, ok := h.validator.Validate(c, req); !ok { - return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) - } - - url, err := h.virtualGameSvc.GenerateGameLaunchURL(c.Context(), userID, req.GameID, req.Currency, req.Mode) - if err != nil { - h.logger.Error("Failed to generate game launch URL", "userID", userID, "gameID", req.GameID, "error", err) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to launch game") - } - - res := launchVirtualGameRes{LaunchURL: url} - return response.WriteJSON(c, fiber.StatusOK, "Game launched successfully", res, nil) -} - -// HandleVirtualGameCallback godoc -// @Summary Handle PopOK game callback -// @Description Processes callbacks from PopOK for game events -// @Tags Virtual Games - PopOK -// @Accept json -// @Produce json -// @Param callback body domain.PopOKCallback true "Callback data" -// @Success 200 {object} domain.ErrorResponse -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /virtual-game/callback [post] -func (h *Handler) HandleVirtualGameCallback(c *fiber.Ctx) error { - var callback domain.PopOKCallback - if err := c.BodyParser(&callback); err != nil { - h.logger.Error("Failed to parse callback", "error", err) - return fiber.NewError(fiber.StatusBadRequest, "Invalid callback data") - } - - if err := h.virtualGameSvc.HandleCallback(c.Context(), &callback); err != nil { - h.logger.Error("Failed to handle callback", "transactionID", callback.TransactionID, "error", err) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to process callback") - } - - return response.WriteJSON(c, fiber.StatusOK, "Callback processed successfully", nil, nil) -} - -func (h *Handler) HandlePlayerInfo(c *fiber.Ctx) error { - var req domain.PopOKPlayerInfoRequest - if err := c.BodyParser(&req); err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - } - - resp, err := h.virtualGameSvc.GetPlayerInfo(c.Context(), &req) - if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - return c.Status(fiber.StatusOK).JSON(resp) -} - -func (h *Handler) HandleBet(c *fiber.Ctx) error { - // Read the raw body - body := c.Body() - if len(body) == 0 { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Empty request body", - Error: "Request body cannot be empty", - }) - } - - // Identify the provider based on request structure - provider, err := IdentifyBetProvider(body) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Unrecognized request format", - Error: err.Error(), - }) - } - - switch provider { - case "veli": - var req domain.BetRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid Veli bet request", - Error: err.Error(), - }) - } - - // req.PlayerID = fmt.Sprintf("%v", userID) - - res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), req) - if err != nil { - if strings.Contains(err.Error(), veli.ErrDuplicateTransaction.Error()) { - return c.Status(fiber.StatusConflict).JSON(domain.VeliCallbackErrorResponse{ - // Message: "Duplicate transaction", - ErrorStatus: 409, - ErrorData: struct { - Error string `json:"error"` - Details *string `json:"details"` - }{ - Error: veli.ErrDuplicateTransaction.Error(), - Details: nil, - }, - // ErrorData.Er: veli.ErrDuplicateTransaction.Error(), - }) - } else if strings.Contains(err.Error(), veli.ErrInsufficientBalance.Error()) { - return c.Status(fiber.StatusConflict).JSON(domain.VeliCallbackErrorResponse{ - // Message: "Wallet balance is insufficient", - ErrorStatus: 409, - ErrorData: struct { - Error string `json:"error"` - Details *string `json:"details"` - }{ - Error: veli.ErrInsufficientBalance.Error(), - Details: nil, - }}) - } else if strings.Contains(err.Error(), veli.ErrPlayerNotFound.Error()) { - return c.Status(fiber.StatusConflict).JSON(domain.VeliCallbackErrorResponse{ - // Message: "User not found", - ErrorStatus: 409, - ErrorData: struct { - Error string `json:"error"` - Details *string `json:"details"` - }{ - Error: veli.ErrPlayerNotFound.Error(), - Details: nil, - }, - }) - } - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Veli bet processing failed", - Error: err.Error(), - }) - } - return c.JSON(res) - - case "popok": - var req domain.PopOKBetRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid PopOK bet request", - Error: err.Error(), - }) - } - - // req.PlayerID = fmt.Sprintf("%v", userID) - - resp, err := h.virtualGameSvc.ProcessBet(c.Context(), &req) - if err != nil { - code := fiber.StatusInternalServerError - switch err.Error() { - case "invalid token": - code = fiber.StatusUnauthorized - case "insufficient balance": - code = fiber.StatusBadRequest - } - return c.Status(code).JSON(domain.ErrorResponse{ - Message: "PopOK bet processing failed", - Error: err.Error(), - }) - } - return c.JSON(resp) - - case "atlas": - var req domain.AtlasBetRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid Atlas bet request", - Error: err.Error(), - }) - } - - // req.PlayerID = fmt.Sprintf("%v", userID) - - resp, err := h.atlasVirtualGameSvc.ProcessBet(c.Context(), req) - if err != nil { - // code := fiber.StatusInternalServerError - // if errors.Is(err, ErrDuplicateTransaction) { - // code = fiber.StatusConflict - // } - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Atlas bet processing failed", - Error: err.Error(), - }) - } - return c.JSON(resp) - - default: - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Unsupported provider", - Error: "Request format doesn't match any supported provider", - }) - } -} - -// identifyProvider examines the request body to determine the provider - -// WinHandler godoc -// @Summary Handle win callback (Veli or PopOK) -// @Description Processes win callbacks from either Veli or PopOK providers by auto-detecting the format -// @Tags Wins -// @Accept json -// @Produce json -// @Success 200 {object} interface{} "Win processing result" -// @Failure 400 {object} domain.ErrorResponse "Invalid request format" -// @Failure 401 {object} domain.ErrorResponse "Authentication failed" -// @Failure 409 {object} domain.ErrorResponse "Duplicate transaction" -// @Failure 500 {object} domain.ErrorResponse "Internal server error" -// @Router /win [post] -func (h *Handler) HandleWin(c *fiber.Ctx) error { - // Read the raw body to avoid parsing issues - body := c.Body() - if len(body) == 0 { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Empty request body", - Error: "Request body cannot be empty", - }) - } - - // Try to identify the provider based on the request structure - provider, err := identifyWinProvider(body) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Unrecognized request format", - Error: err.Error(), - }) - } - - switch provider { - case "veli": - var req domain.WinRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid Veli win request", - Error: err.Error(), - }) - } - - res, err := h.veliVirtualGameSvc.ProcessWin(c.Context(), req) - if err != nil { - if strings.Contains(err.Error(), veli.ErrDuplicateTransaction.Error()) { - return c.Status(fiber.StatusConflict).JSON(domain.VeliCallbackErrorResponse{ - // Message: "Duplicate transaction", - ErrorStatus: 409, - ErrorData: struct { - Error string `json:"error"` - Details *string `json:"details"` - }{ - Error: veli.ErrDuplicateTransaction.Error(), - Details: nil, - }, - }) - } else if strings.Contains(err.Error(), veli.ErrPlayerNotFound.Error()) { - return c.Status(fiber.StatusConflict).JSON(domain.VeliCallbackErrorResponse{ - // Message: "Duplicate transaction", - ErrorStatus: 409, - ErrorData: struct { - Error string `json:"error"` - Details *string `json:"details"` - }{ - Error: veli.ErrPlayerNotFound.Error(), - Details: nil, - }, - }) - } - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Veli win processing failed", - Error: err.Error(), - }) - } - return c.JSON(res) - - case "popok": - var req domain.PopOKWinRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid PopOK win request", - Error: err.Error(), - }) - } - - resp, err := h.virtualGameSvc.ProcessWin(c.Context(), &req) - if err != nil { - code := fiber.StatusInternalServerError - if err.Error() == "invalid token" { - code = fiber.StatusUnauthorized - } - return c.Status(code).JSON(domain.ErrorResponse{ - Message: "PopOK win processing failed", - Error: err.Error(), - }) - } - return c.JSON(resp) - - default: - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Unsupported provider", - Error: "Request format doesn't match any supported provider", - }) - } -} - -func (h *Handler) HandleCancel(c *fiber.Ctx) error { - body := c.Body() - if len(body) == 0 { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Empty request body", - Error: "Request body cannot be empty", - }) - } - - provider, err := identifyCancelProvider(body) - if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Unrecognized request format", - Error: err.Error(), - }) - } - - switch provider { - case "veli": - var req domain.CancelRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid Veli cancel request", - Error: err.Error(), - }) - } - - res, err := h.veliVirtualGameSvc.ProcessCancel(c.Context(), req) - if err != nil { - if strings.Contains(err.Error(), veli.ErrDuplicateTransaction.Error()) { - return c.Status(fiber.StatusConflict).JSON(domain.VeliCallbackErrorResponse{ - // Message: "Duplicate transaction", - ErrorStatus: 409, - ErrorData: struct { - Error string `json:"error"` - Details *string `json:"details"` - }{ - Error: veli.ErrDuplicateTransaction.Error(), - Details: nil, - }, - }) - } else if strings.Contains(err.Error(), veli.ErrPlayerNotFound.Error()) { - return c.Status(fiber.StatusConflict).JSON(domain.VeliCallbackErrorResponse{ - // Message: "User not found", - ErrorStatus: 409, - ErrorData: struct { - Error string `json:"error"` - Details *string `json:"details"` - }{ - Error: veli.ErrPlayerNotFound.Error(), - Details: nil, - }, - }) - } - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Veli cancel processing failed", - Error: err.Error(), - }) - } - return c.JSON(res) - - case "popok": - var req domain.PopOKCancelRequest - if err := json.Unmarshal(body, &req); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Invalid PopOK cancel request", - Error: err.Error(), - }) - } - - resp, err := h.virtualGameSvc.ProcessCancel(c.Context(), &req) - if err != nil { - code := fiber.StatusInternalServerError - switch err.Error() { - case "invalid token": - code = fiber.StatusUnauthorized - case "original bet not found", "invalid original transaction": - code = fiber.StatusBadRequest - } - return c.Status(code).JSON(domain.ErrorResponse{ - Message: "PopOK cancel processing failed", - Error: err.Error(), - }) - } - return c.JSON(resp) - - default: - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Unsupported provider", - Error: "Request format doesn't match any supported provider", - }) - } -} - -// GetGameList godoc -// @Summary Get PopOK Games List -// @Description Retrieves the list of available PopOK slot games -// @Tags Virtual Games - PopOK -// @Accept json -// @Produce json -// @Param currency query string false "Currency (e.g. USD, ETB)" default(USD) -// @Success 200 {array} domain.PopOKGame -// @Failure 500 {object} domain.ErrorResponse -// @Router /popok/games [get] -func (h *Handler) GetGameList(c *fiber.Ctx) error { - currency := c.Query("currency", "ETB") // fallback default - - games, err := h.virtualGameSvc.ListGames(c.Context(), currency) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Falied to fetch games", - Error: err.Error(), - }) - // return fiber.NewError(fiber.StatusBadGateway, "failed to fetch games") - } - return c.JSON(games) -} - -// RecommendGames godoc -// @Summary Recommend virtual games -// @Description Recommends games based on user history or randomly -// @Tags Virtual Games - PopOK -// @Produce json -// @Param user_id query int true "User ID" -// @Success 200 {array} domain.GameRecommendation -// @Failure 500 {object} domain.ErrorResponse -// @Router /popok/games/recommend [get] -func (h *Handler) RecommendGames(c *fiber.Ctx) error { - userIDVal := c.Locals("user_id") - userID, ok := userIDVal.(int64) - if !ok || userID == 0 { - return fiber.NewError(fiber.StatusBadRequest, "invalid user ID") - } - - recommendations, err := h.virtualGameSvc.RecommendGames(c.Context(), userID) - if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "failed to recommend games") - } - - return c.JSON(recommendations) -} - -func (h *Handler) HandleTournamentWin(c *fiber.Ctx) error { - var req domain.PopOKWinRequest - - if err := c.BodyParser(&req); err != nil { - h.logger.Error("Invalid tournament win request body", "error", err) - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "error": "Invalid request body", - }) - } - - resp, err := h.virtualGameSvc.ProcessTournamentWin(c.Context(), &req) - if err != nil { - h.logger.Error("Failed to process tournament win", "error", err) - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": err.Error(), - }) - } - - return c.JSON(resp) -} - -func (h *Handler) HandlePromoWin(c *fiber.Ctx) error { - var req domain.PopOKWinRequest - - if err := c.BodyParser(&req); err != nil { - h.logger.Error("Invalid promo win request body", "error", err) - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "error": "Invalid request body", - }) - } - - resp, err := h.virtualGameSvc.ProcessPromoWin(c.Context(), &req) - if err != nil { - h.logger.Error("Failed to process promo win", "error", err) - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": err.Error(), - }) - } - - return c.JSON(resp) -} - -// AddFavoriteGame godoc -// @Summary Add game to favorites -// @Description Adds a game to the user's favorite games list -// @Tags VirtualGames - Favourites -// @Accept json -// @Produce json -// @Param body body domain.FavoriteGameRequest true "Game ID and Provider ID to add" -// @Success 201 {string} domain.Response "created" -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/virtual-game/favorites [post] -func (h *Handler) AddFavorite(c *fiber.Ctx) error { - userID := c.Locals("user_id").(int64) - - var req domain.FavoriteGameRequest - if err := c.BodyParser(&req); err != nil { - // return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Could not add favorite game", - Error: err.Error(), - }) - } - - // Validate required fields - if req.GameID == 0 || req.ProviderID == "" { - // return fiber.NewError(fiber.StatusBadRequest, "game_id and provider_id are required") - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Could not remove favorite", - Error: "game_id and provider_id are required", - }) - } - - // Call service layer with providerID - err := h.orchestrationSvc.AddFavoriteGame(c.Context(), userID, req.GameID, req.ProviderID) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Could not add favorite", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusCreated).JSON(domain.Response{ - Message: "Game added to favorites", - StatusCode: fiber.StatusCreated, - Success: true, - }) -} - -// RemoveFavoriteGame godoc -// @Summary Remove game from favorites -// @Description Removes a game from the user's favorites -// @Tags VirtualGames - Favourites -// @Produce json -// @Param gameID path int64 true "Game ID to remove" -// @Param providerID query string true "Provider ID of the game" -// @Success 200 {object} domain.Response "removed" -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/virtual-game/favorites/{gameID} [delete] -func (h *Handler) RemoveFavorite(c *fiber.Ctx) error { - userID := c.Locals("user_id").(int64) - - var req domain.FavoriteGameRequest - if err := c.BodyParser(&req); err != nil { - // return fiber.NewError(fiber.StatusBadRequest, "Invalid request") - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Could not remove favorite", - Error: err.Error(), - }) - } - - // Parse gameID from path - if req.GameID == 0 || req.ProviderID == "" { - // return fiber.NewError(fiber.StatusBadRequest, "game_id and provider_id are required") - return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - Message: "Could not remove favorite", - Error: "game_id and provider_id are required", - }) - } - - // Call service layer - err := h.orchestrationSvc.RemoveFavoriteGame(c.Context(), userID, req.GameID, req.ProviderID) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Could not remove favorite", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Game removed from favorites", - StatusCode: fiber.StatusOK, - Success: true, - }) -} - -// ListFavoriteGames godoc -// @Summary Get user's favorite games -// @Description Lists the games that the user marked as favorite -// @Tags VirtualGames - Favourites -// @Produce json -// @Param providerID query string false "Filter by provider ID" -// @Param limit query int false "Number of results to return" -// @Param offset query int false "Results offset" -// @Success 200 {array} domain.UnifiedGame -// @Failure 400 {object} domain.ErrorResponse -// @Failure 500 {object} domain.ErrorResponse -// @Router /api/v1/virtual-game/favorites [get] -func (h *Handler) ListFavorites(c *fiber.Ctx) error { - userID := c.Locals("user_id").(int64) - - // Parse optional query params - providerIDQuery := c.Query("providerID") - var providerID *string - if providerIDQuery != "" { - providerID = &providerIDQuery - } - - limitQuery := c.QueryInt("limit", 10) // default limit 20 - offsetQuery := c.QueryInt("offset", 0) // default offset 0 - - // Call service method - games, err := h.orchestrationSvc.ListFavoriteGames(c.Context(), userID, providerID, int32(limitQuery), int32(offsetQuery)) - if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ - Message: "Could not fetch favorites", - Error: err.Error(), - }) - } - - return c.Status(fiber.StatusOK).JSON(domain.Response{ - Message: "Favorite games retrieved successfully", - Data: games, - StatusCode: fiber.StatusOK, - Success: true, - MetaData: map[string]interface{}{ - "offset": offsetQuery, - "limit": limitQuery, - }, - }) -} - -func IdentifyBetProvider(body []byte) (string, error) { - // Check for Veli signature fields - var veliCheck struct { - SessionID string `json:"sessionId"` - BrandID string `json:"brandId"` - } - if json.Unmarshal(body, &veliCheck) == nil { - if veliCheck.SessionID != "" && veliCheck.BrandID != "" { - return "veli", nil - } - } - - // Check for PopOK signature fields - var popokCheck struct { - Token string `json:"externalToken"` - } - - if json.Unmarshal(body, &popokCheck) == nil { - if popokCheck.Token != "" { - return "popok", nil - } - } - - var atlasCheck struct { - CasinoID string `json:"casino_id"` - SessionID string `json:"session_id"` - } - - if json.Unmarshal(body, &atlasCheck) == nil { - if atlasCheck.CasinoID != "" && atlasCheck.SessionID != "" { - return "atlas", nil - } - } - - return "", fmt.Errorf("could not identify provider from request structure") -} - -// identifyWinProvider examines the request body to determine the provider for win callbacks -func identifyWinProvider(body []byte) (string, error) { - // Check for Veli signature fields - var veliCheck struct { - SessionID string `json:"sessionId"` - BrandID string `json:"brandId"` - } - if json.Unmarshal(body, &veliCheck) == nil { - if veliCheck.SessionID != "" && veliCheck.BrandID != "" { - return "veli", nil - } - } - - // Check for PopOK signature fields - var popokCheck struct { - Token string `json:"externalToken"` - } - - if json.Unmarshal(body, &popokCheck) == nil { - if popokCheck.Token != "" { - return "popok", nil - } - } - - return "", fmt.Errorf("could not identify provider from request structure") -} - -func identifyCancelProvider(body []byte) (string, error) { - // Check for Veli cancel signature - var veliCheck struct { - SessionID string `json:"sessionId"` - BrandID string `json:"brandId"` - } - if json.Unmarshal(body, &veliCheck) == nil { - if veliCheck.SessionID != "" && veliCheck.BrandID != "" { - return "veli", nil - } - } - - // Check for PopOK cancel signature - var popokCheck struct { - Token string `json:"externalToken"` - } - - if json.Unmarshal(body, &popokCheck) == nil { - if popokCheck.Token != "" { - return "popok", nil - } - } - - return "", fmt.Errorf("could not identify cancel provider from request structure") -} diff --git a/internal/web_server/handlers/wallet_handler.go b/internal/web_server/handlers/wallet_handler.go deleted file mode 100644 index f97efe1..0000000 --- a/internal/web_server/handlers/wallet_handler.go +++ /dev/null @@ -1,473 +0,0 @@ -package handlers - -import ( - "fmt" - "strconv" - "time" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" - "github.com/gofiber/fiber/v2" - "go.uber.org/zap" -) - -type WalletRes struct { - ID int64 `json:"id" example:"1"` - Balance float32 `json:"amount" example:"100.0"` - IsWithdraw bool `json:"is_withdraw" example:"true"` - IsBettable bool `json:"is_bettable" example:"true"` - IsTransferable bool `json:"is_transferable" example:"true"` - IsActive bool `json:"is_active" example:"true"` - UserID int64 `json:"user_id" example:"1"` - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` -} - -func convertWallet(wallet domain.Wallet) WalletRes { - return WalletRes{ - ID: wallet.ID, - Balance: wallet.Balance.Float32(), - IsWithdraw: wallet.IsWithdraw, - IsBettable: wallet.IsBettable, - IsTransferable: wallet.IsTransferable, - IsActive: wallet.IsActive, - UserID: wallet.UserID, - UpdatedAt: wallet.UpdatedAt, - CreatedAt: wallet.CreatedAt, - } -} - -type CustomerWalletRes struct { - ID int64 `json:"id" example:"1"` - RegularID int64 `json:"regular_id" example:"1"` - RegularBalance float32 `json:"regular_balance" example:"100.0"` - StaticID int64 `json:"static_id" example:"1"` - StaticBalance float32 `json:"static_balance" example:"100.0"` - CustomerID int64 `json:"customer_id" example:"1"` - RegularIsActive bool `json:"regular_is_active" example:"true"` - StaticIsActive bool `json:"static_is_active" example:"true"` - RegularUpdatedAt time.Time `json:"regular_updated_at"` - StaticUpdatedAt time.Time `json:"static_updated_at"` - CreatedAt time.Time `json:"created_at"` - FirstName string `json:"first_name" example:"John"` - LastName string `json:"last_name" example:"Smith"` - PhoneNumber string `json:"phone_number" example:"0911111111"` - NumberOfTransactions int64 `json:"number_of_transactions"` - TotalTransactions float32 `json:"total_transactions"` - NumberOfDeposits int64 `json:"number_of_deposits"` - TotalDepositsAmount float32 `json:"total_deposits_amount"` - NumberOfWithdraws int64 `json:"number_of_withdraws"` - TotalWithdrawsAmount float32 `json:"total_withdraws_amount"` - NumberOfTransfers int64 `json:"number_of_transfers"` - TotalTransfersAmount float32 `json:"total_transfers_amount"` - UpdatedAt time.Time `json:"updated_at"` -} - -func ConvertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes { - return CustomerWalletRes{ - ID: wallet.ID, - RegularID: wallet.RegularID, - RegularBalance: wallet.RegularBalance.Float32(), - StaticID: wallet.StaticID, - StaticBalance: wallet.StaticBalance.Float32(), - CustomerID: wallet.CustomerID, - RegularIsActive: wallet.RegularIsActive, - StaticIsActive: wallet.StaticIsActive, - RegularUpdatedAt: wallet.RegularUpdatedAt, - StaticUpdatedAt: wallet.StaticUpdatedAt, - CreatedAt: wallet.CreatedAt, - FirstName: wallet.FirstName, - LastName: wallet.LastName, - PhoneNumber: wallet.PhoneNumber, - NumberOfTransactions: wallet.NumberOfTransactions, - TotalTransactions: wallet.TotalTransactions.Float32(), - NumberOfDeposits: wallet.NumberOfDeposits, - TotalDepositsAmount: wallet.TotalDepositsAmount.Float32(), - NumberOfWithdraws: wallet.NumberOfWithdraws, - TotalWithdrawsAmount: wallet.TotalWithdrawsAmount.Float32(), - NumberOfTransfers: wallet.NumberOfTransfers, - TotalTransfersAmount: wallet.TotalTransfersAmount.Float32(), - UpdatedAt: wallet.UpdatedAt, - } -} - -type BranchWalletRes struct { - ID int64 `json:"id" example:"1"` - Balance float32 `json:"balance" example:"100.0"` - IsActive bool `json:"is_active" example:"true"` - Name string `json:"name" example:"true"` - Location string `json:"location" example:"somewhere"` - BranchManagerID int64 `json:"branch_manager_id" example:"1"` - CompanyID int64 `json:"company_id" example:"1"` - IsSelfOwned bool `json:"is_self_owned" example:"false"` - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` -} - -// GetWalletByID godoc -// @Summary Get wallet by ID -// @Description Retrieve wallet details by wallet ID -// @Tags wallet -// @Accept json -// @Produce json -// @Param id path int true "Wallet ID" -// @Success 200 {object} WalletRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/wallet/{id} [get] -func (h *Handler) GetWalletByID(c *fiber.Ctx) error { - walletID := c.Params("id") - id, err := strconv.ParseInt(walletID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Error("Invalid wallet ID", - zap.String("walletID", walletID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID") - } - - wallet, err := h.walletSvc.GetWalletByID(c.Context(), id) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get wallet by ID", - zap.Int64("walletID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallet") - } - - res := convertWallet(wallet) - - return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil) -} - -// GetAllWallets godoc -// @Summary Get all wallets -// @Description Retrieve all wallets -// @Tags wallet -// @Accept json -// @Produce json -// @Success 200 {array} WalletRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/wallet [get] -func (h *Handler) GetAllWallets(c *fiber.Ctx) error { - - wallets, err := h.walletSvc.GetAllWallets(c.Context()) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get wallets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - var res []WalletRes = make([]WalletRes, 0, len(wallets)) - - for _, wallet := range wallets { - res = append(res, convertWallet(wallet)) - } - - return response.WriteJSON(c, fiber.StatusOK, "All wallets retrieved successfully", res, nil) - -} - -// GetAllBranchWallets godoc -// @Summary Get all branch wallets -// @Description Retrieve all branch wallets -// @Tags wallet -// @Accept json -// @Produce json -// @Success 200 {array} WalletRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/branchWallet [get] -func (h *Handler) GetAllBranchWallets(c *fiber.Ctx) error { - - wallets, err := h.walletSvc.GetAllBranchWallets(c.Context()) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get wallets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallets") - } - - var res []BranchWalletRes = make([]BranchWalletRes, 0, len(wallets)) - - for _, wallet := range wallets { - res = append(res, BranchWalletRes{ - ID: wallet.ID, - Balance: wallet.Balance.Float32(), - IsActive: wallet.IsActive, - Name: wallet.Name, - Location: wallet.Location, - BranchManagerID: wallet.BranchManagerID, - CompanyID: wallet.CompanyID, - IsSelfOwned: wallet.IsSelfOwned, - UpdatedAt: wallet.UpdatedAt, - CreatedAt: wallet.CreatedAt, - }) - } - - return response.WriteJSON(c, fiber.StatusOK, "All Wallets retrieved", res, nil) -} - -// GetAllCustomerWallets godoc -// @Summary Get all customer wallets -// @Description Retrieve all customer wallets -// @Tags wallet -// @Accept json -// @Produce json -// @Success 200 {array} CustomerWalletRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/customerWallet [get] -func (h *Handler) GetAllCustomerWallets(c *fiber.Ctx) error { - - wallets, err := h.walletSvc.GetAllCustomerWallet(c.Context()) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get customer wallets", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallets") - } - - var res []CustomerWalletRes = make([]CustomerWalletRes, 0, len(wallets)) - - for _, wallet := range wallets { - res = append(res, ConvertCustomerWallet(wallet)) - } - - return response.WriteJSON(c, fiber.StatusOK, "All Wallets retrieved", res, nil) -} - -type UpdateWalletActiveReq struct { - IsActive bool `json:"is_active" validate:"required" example:"true"` -} - -// UpdateWalletActive godoc -// @Summary Activate and Deactivate Wallet -// @Description Can activate and deactivate wallet -// @Tags wallet -// @Accept json -// @Produce json -// @Param id path int true "Wallet ID" -// @Param updateCashOut body UpdateWalletActiveReq true "Update Wallet Active" -// @Success 200 {object} response.APIResponse -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/wallet/{id} [patch] -func (h *Handler) UpdateWalletActive(c *fiber.Ctx) error { - - walletID := c.Params("id") - id, err := strconv.ParseInt(walletID, 10, 64) - if err != nil { - h.mongoLoggerSvc.Info("Invalid wallet ID", - zap.String("walletID", walletID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID") - } - - var req UpdateWalletActiveReq - if err := c.BodyParser(&req); err != nil { - h.mongoLoggerSvc.Info("Failed to parse UpdateWalletActive request", - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") - } - - if valErrs, ok := h.validator.Validate(c, req); !ok { - var errMsg string - for field, msg := range valErrs { - errMsg += fmt.Sprintf("%s: %s; ", field, msg) - } - h.mongoLoggerSvc.Info("Failed to validate UpdateWalletActiveReq", - zap.String("errMsg", errMsg), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, errMsg) - } - - err = h.walletSvc.UpdateWalletActive(c.Context(), id, req.IsActive) - if err != nil { - h.mongoLoggerSvc.Error("Failed to update wallet active status", - zap.Int64("walletID", id), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update wallet") - } - - return response.WriteJSON(c, fiber.StatusOK, "Wallet successfully updated", nil, nil) -} - -// GetCustomerWallet godoc -// @Summary Get customer wallet -// @Description Retrieve customer wallet details -// @Tags wallet -// @Accept json -// @Produce json -// @Param company_id header int true "Company ID" -// @Security Bearer -// @Success 200 {object} CustomerWalletRes -// @Failure 400 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/{tenant_slug}/user/wallet [get] -func (h *Handler) GetCustomerWallet(c *fiber.Ctx) error { - - userID, ok := c.Locals("user_id").(int64) - if !ok || userID == 0 { - h.mongoLoggerSvc.Info("Invalid user ID in context", - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Invalid user id in context") - } - - role, ok := c.Locals("role").(domain.Role) - if !ok { - h.mongoLoggerSvc.Error("Invalid role in context", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.String("role", string(role)), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Invalid role") - } - - if role != domain.RoleCustomer { - h.mongoLoggerSvc.Error("Unauthorized access", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusForbidden), - zap.String("role", string(role)), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusForbidden, "Unauthorized access") - } - - // companyID, err := strconv.ParseInt(c.Get("company_id"), 10, 64) - // if err != nil { - // h.logger.Error("Invalid company_id header", "value", c.Get("company_id"), "error", err) - // return fiber.NewError(fiber.StatusBadRequest, "Invalid company_id") - // } - - // h.logger.Info("Fetching customer wallet", "userID", userID) - - wallet, err := h.walletSvc.GetCustomerWallet(c.Context(), userID) - if err != nil { - h.mongoLoggerSvc.Error("Failed to get customer wallet", - zap.Int64("userID", userID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallet") - } - - res := ConvertCustomerWallet(wallet) - - return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil) -} - -// GetWalletForCashier godoc -// @Summary Get wallet for cashier -// @Description Get wallet for cashier -// @Tags cashier -// @Accept json -// @Produce json -// @Param id path int true "User ID" -// @Success 200 {object} UserProfileRes -// @Failure 400 {object} response.APIResponse -// @Failure 401 {object} response.APIResponse -// @Failure 500 {object} response.APIResponse -// @Router /api/v1/cashierWallet [get] -func (h *Handler) GetWalletForCashier(c *fiber.Ctx) error { - cashierID, ok := c.Locals("user_id").(int64) - - if !ok || cashierID == 0 { - h.mongoLoggerSvc.Error("Invalid cashier ID in context", - zap.Int64("cashierID", cashierID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusUnauthorized, "Invalid cashier id") - } - - role, ok := c.Locals("role").(domain.Role) - - if !ok || role != domain.RoleCashier { - h.mongoLoggerSvc.Error("Unauthorized access", - zap.Int64("cashierID", cashierID), - zap.Int("status_code", fiber.StatusForbidden), - zap.String("role", string(role)), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusForbidden, "Unauthorized access") - } - - branchID, ok := c.Locals("branch_id").(domain.ValidInt64) - if !ok || !branchID.Valid { - h.mongoLoggerSvc.Info("Invalid branch ID in context", - zap.Int64("cashierID", cashierID), - zap.Int("status_code", fiber.StatusBadRequest), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") - } - - branch, err := h.branchSvc.GetBranchByID(c.Context(), branchID.Value) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get branch by ID", - zap.Int64("branchID", branchID.Value), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - wallet, err := h.walletSvc.GetWalletByID(c.Context(), branch.WalletID) - - if err != nil { - h.mongoLoggerSvc.Error("Failed to get wallet for cashier", - zap.Int64("cashierID", cashierID), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - res := WalletRes{ - ID: wallet.ID, - Balance: wallet.Balance.Float32(), - IsWithdraw: wallet.IsWithdraw, - IsBettable: wallet.IsBettable, - IsTransferable: wallet.IsTransferable, - IsActive: wallet.IsActive, - UserID: wallet.UserID, - UpdatedAt: wallet.UpdatedAt, - CreatedAt: wallet.CreatedAt, - } - return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil) -} diff --git a/internal/web_server/jwt/jwt.go b/internal/web_server/jwt/jwt.go index b9313ae..e1be621 100644 --- a/internal/web_server/jwt/jwt.go +++ b/internal/web_server/jwt/jwt.go @@ -1,11 +1,11 @@ package jwtutil import ( + "Yimaru-Backend/internal/domain" "errors" "fmt" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/golang-jwt/jwt/v5" ) @@ -16,7 +16,6 @@ var ( ErrRefreshTokenNotFound = errors.New("refresh token not found") ) - type UserClaim struct { jwt.RegisteredClaims UserId int64 diff --git a/internal/web_server/middleware.go b/internal/web_server/middleware.go index 179cf59..88d78a3 100644 --- a/internal/web_server/middleware.go +++ b/internal/web_server/middleware.go @@ -1,13 +1,13 @@ package httpserver import ( + "Yimaru-Backend/internal/domain" + jwtutil "Yimaru-Backend/internal/web_server/jwt" "errors" "fmt" "strings" "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) @@ -89,21 +89,21 @@ func (a *App) authMiddleware(c *fiber.Ctx) error { var branchID domain.ValidInt64 - if claim.Role == domain.RoleCashier { - branch, err := a.branchSvc.GetBranchByCashier(c.Context(), claim.UserId) - if err != nil { - a.mongoLoggerSvc.Error("Failed to get branch id for cashier", - zap.Int64("userID", claim.UserId), - zap.Int("status_code", fiber.StatusInternalServerError), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to branch id for cashier") - } - branchID = domain.ValidInt64{ - Value: branch.ID, - Valid: true, - } + if claim.Role == domain.RoleAdmin { + // branch, err := a.branchSvc.GetBranchByCashier(c.Context(), claim.UserId) + // if err != nil { + // a.mongoLoggerSvc.Error("Failed to get branch id for cashier", + // zap.Int64("userID", claim.UserId), + // zap.Int("status_code", fiber.StatusInternalServerError), + // zap.Error(err), + // zap.Time("timestamp", time.Now()), + // ) + // return fiber.NewError(fiber.StatusInternalServerError, "Failed to branch id for cashier") + // } + // branchID = domain.ValidInt64{ + // Value: branch.ID, + // Valid: true, + // } } @@ -236,27 +236,27 @@ func (a *App) TenantMiddleware(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, "tenant is required for this route") } - company, err := a.companySvc.GetCompanyBySlug(c.Context(), tenantSlug) - if err != nil { - a.mongoLoggerSvc.Info("failed to resolve tenant", - zap.String("tenant_slug", tenantSlug), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusBadRequest, "failed to resolve tenant") - } + // company, err := a.companySvc.GetCompanyBySlug(c.Context(), tenantSlug) + // if err != nil { + // a.mongoLoggerSvc.Info("failed to resolve tenant", + // zap.String("tenant_slug", tenantSlug), + // zap.Time("timestamp", time.Now()), + // ) + // return fiber.NewError(fiber.StatusBadRequest, "failed to resolve tenant") + // } - if !company.IsActive { - a.mongoLoggerSvc.Info("request using deactivated tenant", - zap.String("tenant_slug", tenantSlug), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusForbidden, "this tenant has been deactivated") - } + // if !company.IsActive { + // a.mongoLoggerSvc.Info("request using deactivated tenant", + // zap.String("tenant_slug", tenantSlug), + // zap.Time("timestamp", time.Now()), + // ) + // return fiber.NewError(fiber.StatusForbidden, "this tenant has been deactivated") + // } - c.Locals("company_id", domain.ValidInt64{ - Value: company.ID, - Valid: true, - }) + // c.Locals("company_id", domain.ValidInt64{ + // Value: company.ID, + // Valid: true, + // }) return c.Next() } diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index fca33a4..48cee48 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -1,70 +1,37 @@ package httpserver import ( + "Yimaru-Backend/internal/domain" + "Yimaru-Backend/internal/web_server/handlers" "context" "fmt" "strconv" - _ "github.com/SamuelTariku/FortuneBet-Backend/docs" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - - // "github.com/SamuelTariku/FortuneBet-Backend/internal/logger/mongoLogger" - - // "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet/monitor" - - "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/handlers" - "github.com/gofiber/fiber/v2" fiberSwagger "github.com/swaggo/fiber-swagger" ) func (a *App) initAppRoutes() { h := handlers.New( - a.directDepositSvc, - a.orchestrationSvc, - a.enetPulseSvc, - a.telebirrSvc, a.arifpaySvc, - a.santimpaySvc, - a.issueReportingSvc, - a.instSvc, - a.currSvc, a.logger, a.settingSvc, a.NotidicationStore, a.validator, - // a.reportSvc, - a.chapaSvc, - a.walletSvc, a.referralSvc, - a.raffleSvc, - a.bonusSvc, - a.virtualGameSvc, - a.aleaVirtualGameService, - a.veliVirtualGameService, - a.atlasVirtualGameService, a.recommendationSvc, a.userSvc, a.transactionSvc, - a.ticketSvc, - a.betSvc, a.authSvc, a.JwtConfig, - a.branchSvc, - a.companySvc, - a.prematchSvc, - a.eventSvc, - a.leagueSvc, - *a.resultSvc, - a.statSvc, a.cfg, a.mongoLoggerSvc, ) a.fiber.Get("/", func(c *fiber.Ctx) error { return c.JSON(fiber.Map{ - "message": "Welcome to the FortuneBet API", - "version": "1.0.4", + "message": "Welcome to Yimaru Backend API", + "version": a.cfg.APP_VERSION, }) }) @@ -104,12 +71,12 @@ func (a *App) initAppRoutes() { groupV1.Get("/tenant", a.authMiddleware, h.GetTenantSlugByToken) //Direct_deposit - groupV1.Post("/direct-deposits", a.authMiddleware, h.CreateDirectDeposit) - groupV1.Post("/direct-deposits/:depositID/approve", a.authMiddleware, h.ApproveDirectDeposit) - groupV1.Post("/direct-deposits/:depositID/reject", a.authMiddleware, h.RejectDirectDeposit) - groupV1.Get("/direct-deposits", a.authMiddleware, h.GetDirectDepositsByStatus) - groupV1.Get("/direct-deposits/:depositID", a.authMiddleware, h.GetDirectDepositByID) - groupV1.Delete("/direct-deposits/:depositID", a.authMiddleware, h.DeleteDirectDeposit) + // groupV1.Post("/direct-deposits", a.authMiddleware, h.CreateDirectDeposit) + // groupV1.Post("/direct-deposits/:depositID/approve", a.authMiddleware, h.ApproveDirectDeposit) + // groupV1.Post("/direct-deposits/:depositID/reject", a.authMiddleware, h.RejectDirectDeposit) + // groupV1.Get("/direct-deposits", a.authMiddleware, h.GetDirectDepositsByStatus) + // groupV1.Get("/direct-deposits/:depositID", a.authMiddleware, h.GetDirectDepositByID) + // groupV1.Delete("/direct-deposits/:depositID", a.authMiddleware, h.DeleteDirectDeposit) // Swagger a.fiber.Get("/swagger/*", fiberSwagger.FiberWrapHandler()) @@ -154,26 +121,26 @@ func (a *App) initAppRoutes() { }) //Arifpay - groupV1.Post("/arifpay/checkout", a.authMiddleware, h.CreateCheckoutSessionHandler) - groupV1.Post("/arifpay/checkout/cancel/:sessionId", a.authMiddleware, h.CancelCheckoutSessionHandler) - groupV1.Post("/api/v1/arifpay/c2b-webhook", h.HandleArifpayC2BWebhook) - groupV1.Post("/api/v1/arifpay/b2c-webhook", h.HandleArifpayB2CWebhook) - groupV1.Post("/arifpay/b2c/transfer", a.authMiddleware, h.ExecuteArifpayB2CTransfer) - groupV1.Post("/arifpay/transaction-id/verify-transaction", a.authMiddleware, h.ArifpayVerifyByTransactionIDHandler) - groupV1.Get("/arifpay/session-id/verify-transaction/:session_id", a.authMiddleware, h.ArifpayVerifyBySessionIDHandler) - groupV1.Get("/arifpay/payment-methods", a.authMiddleware, h.GetArifpayPaymentMethodsHandler) + // groupV1.Post("/arifpay/checkout", a.authMiddleware, h.CreateCheckoutSessionHandler) + // groupV1.Post("/arifpay/checkout/cancel/:sessionId", a.authMiddleware, h.CancelCheckoutSessionHandler) + // groupV1.Post("/api/v1/arifpay/c2b-webhook", h.HandleArifpayC2BWebhook) + // groupV1.Post("/api/v1/arifpay/b2c-webhook", h.HandleArifpayB2CWebhook) + // groupV1.Post("/arifpay/b2c/transfer", a.authMiddleware, h.ExecuteArifpayB2CTransfer) + // groupV1.Post("/arifpay/transaction-id/verify-transaction", a.authMiddleware, h.ArifpayVerifyByTransactionIDHandler) + // groupV1.Get("/arifpay/session-id/verify-transaction/:session_id", a.authMiddleware, h.ArifpayVerifyBySessionIDHandler) + // groupV1.Get("/arifpay/payment-methods", a.authMiddleware, h.GetArifpayPaymentMethodsHandler) //Telebirr - groupV1.Post("/telebirr/init-payment", a.authMiddleware, h.CreateTelebirrPaymentHandler) - groupV1.Post("/telebirr/callback", h.HandleTelebirrCallback) + // groupV1.Post("/telebirr/init-payment", a.authMiddleware, h.CreateTelebirrPaymentHandler) + // groupV1.Post("/telebirr/callback", h.HandleTelebirrCallback) //Santimpay - groupV1.Post("/santimpay/init-payment", a.authMiddleware, h.InititateSantimPayPaymentHandler) - groupV1.Post("/santimpay/callback", h.ProcessSantimPayCallbackHandler) - groupV1.Post("/santimpay/direct-payment", a.authMiddleware, h.ProcessSantimPayDirectPaymentHandler) - groupV1.Get("/santimpay/b2c/partners", h.GetSantimPayB2CPartnersHandler) - groupV1.Post("/santimpay/b2c/withdraw", a.authMiddleware, h.ProcessSantimPayB2CWithdrawalHandler) - groupV1.Post("/santimpay/transaction/verify", a.authMiddleware, h.CheckSantimPayTransactionStatusHandler) + // groupV1.Post("/santimpay/init-payment", a.authMiddleware, h.InititateSantimPayPaymentHandler) + // groupV1.Post("/santimpay/callback", h.ProcessSantimPayCallbackHandler) + // groupV1.Post("/santimpay/direct-payment", a.authMiddleware, h.ProcessSantimPayDirectPaymentHandler) + // groupV1.Get("/santimpay/b2c/partners", h.GetSantimPayB2CPartnersHandler) + // groupV1.Post("/santimpay/b2c/withdraw", a.authMiddleware, h.ProcessSantimPayB2CWithdrawalHandler) + // groupV1.Post("/santimpay/transaction/verify", a.authMiddleware, h.CheckSantimPayTransactionStatusHandler) // groupV1.Post("/arifpay/b2c/transfer", a.authMiddleware, h.B2CTransferHandler) // groupV1.Post("/arifpay/transaction-id/verify-transaction", a.authMiddleware, h.ArifpayVerifyByTransactionIDHandler) @@ -192,13 +159,13 @@ func (a *App) initAppRoutes() { groupV1.Get("/user/admin-profile", a.authMiddleware, h.AdminProfile) tenant.Get("/user/customer-profile", a.authMiddleware, h.CustomerProfile) - tenant.Get("/user/bets", a.authMiddleware, h.GetBetByUserID) + // tenant.Get("/user/bets", a.authMiddleware, h.GetBetByUserID) groupV1.Get("/user/single/:id", a.authMiddleware, h.GetUserByID) groupV1.Post("/user/suspend", a.authMiddleware, h.UpdateUserSuspend) groupV1.Delete("/user/delete/:id", a.authMiddleware, h.DeleteUser) groupV1.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone) - tenant.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet) + // tenant.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet) // Referral Routes tenant.Post("/referral/create", a.authMiddleware, h.CreateReferralCode) @@ -210,23 +177,23 @@ func (a *App) initAppRoutes() { // groupV1.Patch("/referral/settings", a.authMiddleware, h.UpdateReferralSettings) // Raffle Routes - tenant.Get("/raffle/list", h.GetTenantRaffles) - a.fiber.Get("/raffle/standing/:id/:limit", h.GetRaffleStanding) //This needs to be accessible by non-login user - a.fiber.Post("/raffle/create", a.authMiddleware, h.CreateRaffle) - a.fiber.Post("/raffle/add-filter", a.authMiddleware, h.AddRaffleFilter) - a.fiber.Get("/raffle/delete/:id", a.authMiddleware, h.DeleteRaffle) - a.fiber.Get("/raffle/company/:id", a.authMiddleware, h.GetRafflesOfCompany) - a.fiber.Get("raffle/winners/:id/:limit", a.authMiddleware, h.GetRaffleWinners) + // tenant.Get("/raffle/list", h.GetTenantRaffles) + // a.fiber.Get("/raffle/standing/:id/:limit", h.GetRaffleStanding) //This needs to be accessible by non-login user + // a.fiber.Post("/raffle/create", a.authMiddleware, h.CreateRaffle) + // a.fiber.Post("/raffle/add-filter", a.authMiddleware, h.AddRaffleFilter) + // a.fiber.Get("/raffle/delete/:id", a.authMiddleware, h.DeleteRaffle) + // a.fiber.Get("/raffle/company/:id", a.authMiddleware, h.GetRafflesOfCompany) + // a.fiber.Get("raffle/winners/:id/:limit", a.authMiddleware, h.GetRaffleWinners) - a.fiber.Post("/raffle-ticket/create", a.authMiddleware, h.CreateRaffleTicket) - a.fiber.Get("/raffle-ticket/:id", a.authMiddleware, h.GetUserRaffleTickets) - a.fiber.Get("/raffle-ticket/suspend/:id", a.authMiddleware, h.SuspendRaffleTicket) - a.fiber.Get("/raffle-ticket/unsuspend/:id", a.authMiddleware, h.UnSuspendRaffleTicket) + // a.fiber.Post("/raffle-ticket/create", a.authMiddleware, h.CreateRaffleTicket) + // a.fiber.Get("/raffle-ticket/:id", a.authMiddleware, h.GetUserRaffleTickets) + // a.fiber.Get("/raffle-ticket/suspend/:id", a.authMiddleware, h.SuspendRaffleTicket) + // a.fiber.Get("/raffle-ticket/unsuspend/:id", a.authMiddleware, h.UnSuspendRaffleTicket) // Bonus Routes - tenant.Get("/bonus", a.authMiddleware, h.GetBonusesByUserID) - tenant.Get("/bonus/stats", a.authMiddleware, h.GetBonusStats) - tenant.Post("/bonus/claim/:id", a.authMiddleware, h.ClaimBonus) + // tenant.Get("/bonus", a.authMiddleware, h.GetBonusesByUserID) + // tenant.Get("/bonus/stats", a.authMiddleware, h.GetBonusStats) + // tenant.Post("/bonus/claim/:id", a.authMiddleware, h.ClaimBonus) // groupV1.Post("/bonus/create", a.authMiddleware, h.CreateBonusMultiplier) // groupV1.Put("/bonus/update", a.authMiddleware, h.UpdateBonusMultiplier) @@ -235,15 +202,15 @@ func (a *App) initAppRoutes() { groupV1.Post("/cashiers", a.authMiddleware, a.CompanyOnly, h.CreateCashier) groupV1.Put("/cashiers/:id", a.authMiddleware, a.CompanyOnly, h.UpdateCashier) - tenant.Get("/customer", a.authMiddleware, a.CompanyOnly, h.GetAllTenantCustomers) - tenant.Get("/customer/:id", a.authMiddleware, a.CompanyOnly, h.GetTenantCustomerByID) - tenant.Put("/customer/:id", a.authMiddleware, a.CompanyOnly, h.UpdateTenantCustomer) - tenant.Get("/customer/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetTenantCustomerBets) + // tenant.Get("/customer", a.authMiddleware, a.CompanyOnly, h.GetAllTenantCustomers) + // tenant.Get("/customer/:id", a.authMiddleware, a.CompanyOnly, h.GetTenantCustomerByID) + // tenant.Put("/customer/:id", a.authMiddleware, a.CompanyOnly, h.UpdateTenantCustomer) + // // tenant.Get("/customer/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetTenantCustomerBets) - groupV1.Get("/customer", a.authMiddleware, a.SuperAdminOnly, h.GetAllCustomers) - groupV1.Get("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCustomerByID) - groupV1.Put("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCustomer) - groupV1.Get("/customer/:id/bets", a.authMiddleware, h.GetCustomerBets) + // groupV1.Get("/customer", a.authMiddleware, a.SuperAdminOnly, h.GetAllCustomers) + // groupV1.Get("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCustomerByID) + // groupV1.Put("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCustomer) + // groupV1.Get("/customer/:id/bets", a.authMiddleware, h.GetCustomerBets) groupV1.Get("/admin", a.authMiddleware, a.SuperAdminOnly, h.GetAllAdmins) groupV1.Get("/admin/:id", a.authMiddleware, a.SuperAdminOnly, h.GetAdminByID) @@ -259,149 +226,149 @@ func (a *App) initAppRoutes() { groupV1.Get("/managers/:id", a.authMiddleware, h.GetManagerByID) groupV1.Post("/managers", a.authMiddleware, a.OnlyAdminAndAbove, h.CreateManager) groupV1.Put("/managers/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateManagers) - groupV1.Get("/manager/:id/branch", a.authMiddleware, a.OnlyAdminAndAbove, h.GetBranchByManagerID) + // groupV1.Get("/manager/:id/branch", a.authMiddleware, a.OnlyAdminAndAbove, h.GetBranchByManagerID) - groupV1.Get("/odds", a.authMiddleware, a.SuperAdminOnly, h.GetAllOdds) - groupV1.Get("/odds/upcoming/:upcoming_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByUpcomingID) - groupV1.Get("/odds/upcoming/:upcoming_id/market/:market_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByMarketID) - groupV1.Post("/odds/settings", a.authMiddleware, a.SuperAdminOnly, h.SaveOddSettings) - groupV1.Get("/odds/market-settings", a.authMiddleware, a.SuperAdminOnly, h.GetAllGlobalMarketSettings) - groupV1.Put("/odds/bet-outcome/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateAllBetOutcomeStatusByOddID) - groupV1.Put("/odds/bet-outcome", a.authMiddleware, a.SuperAdminOnly, h.BulkUpdateAllBetOutcomeStatusByOddID) + // groupV1.Get("/odds", a.authMiddleware, a.SuperAdminOnly, h.GetAllOdds) + // groupV1.Get("/odds/upcoming/:upcoming_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByUpcomingID) + // groupV1.Get("/odds/upcoming/:upcoming_id/market/:market_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByMarketID) + // groupV1.Post("/odds/settings", a.authMiddleware, a.SuperAdminOnly, h.SaveOddSettings) + // groupV1.Get("/odds/market-settings", a.authMiddleware, a.SuperAdminOnly, h.GetAllGlobalMarketSettings) + // groupV1.Put("/odds/bet-outcome/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateAllBetOutcomeStatusByOddID) + // groupV1.Put("/odds/bet-outcome", a.authMiddleware, a.SuperAdminOnly, h.BulkUpdateAllBetOutcomeStatusByOddID) - tenant.Get("/odds", h.GetAllTenantOdds) - tenant.Get("/odds/upcoming/:upcoming_id", h.GetTenantOddsByUpcomingID) - tenant.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetTenantOddsByMarketID) - tenant.Post("/odds/settings", a.authMiddleware, a.CompanyOnly, h.SaveTenantOddsSetting) - tenant.Delete("/odds/settings/:id", a.authMiddleware, a.CompanyOnly, h.RemoveOddsSettings) - tenant.Delete("/odds/settings", a.authMiddleware, a.CompanyOnly, h.RemoveAllOddsSettings) - tenant.Post("/odds/market-settings", a.authMiddleware, a.CompanyOnly, h.InsertCompanyMarketSettings) - tenant.Get("/odds/market-settings", a.authMiddleware, a.CompanyOnly, h.GetAllTenantMarketSettings) - tenant.Delete("/odds/market-settings", a.authMiddleware, a.CompanyOnly, h.DeleteAllCompanyMarketSettings) - tenant.Delete("/odds/market-settings/:id", a.authMiddleware, a.CompanyOnly, h.DeleteCompanyMarketSettings) + // tenant.Get("/odds", h.GetAllTenantOdds) + // tenant.Get("/odds/upcoming/:upcoming_id", h.GetTenantOddsByUpcomingID) + // tenant.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetTenantOddsByMarketID) + // tenant.Post("/odds/settings", a.authMiddleware, a.CompanyOnly, h.SaveTenantOddsSetting) + // tenant.Delete("/odds/settings/:id", a.authMiddleware, a.CompanyOnly, h.RemoveOddsSettings) + // tenant.Delete("/odds/settings", a.authMiddleware, a.CompanyOnly, h.RemoveAllOddsSettings) + // tenant.Post("/odds/market-settings", a.authMiddleware, a.CompanyOnly, h.InsertCompanyMarketSettings) + // tenant.Get("/odds/market-settings", a.authMiddleware, a.CompanyOnly, h.GetAllTenantMarketSettings) + // tenant.Delete("/odds/market-settings", a.authMiddleware, a.CompanyOnly, h.DeleteAllCompanyMarketSettings) + // tenant.Delete("/odds/market-settings/:id", a.authMiddleware, a.CompanyOnly, h.DeleteCompanyMarketSettings) - groupV1.Get("/events", h.GetAllEvents) - groupV1.Get("/events/:id", a.authMiddleware, h.GetEventByID) - groupV1.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved) - groupV1.Patch("/events/:id/is_monitored", a.authMiddleware, a.SuperAdminOnly, h.SetEventIsMonitored) - groupV1.Put("/events/:id/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalSettingList) - groupV1.Get("/events/:id/bets", a.authMiddleware, a.SuperAdminOnly, h.GetBetsByEventID) + // groupV1.Get("/events", h.GetAllEvents) + // groupV1.Get("/events/:id", a.authMiddleware, h.GetEventByID) + // groupV1.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved) + // groupV1.Patch("/events/:id/is_monitored", a.authMiddleware, a.SuperAdminOnly, h.SetEventIsMonitored) + // groupV1.Put("/events/:id/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalSettingList) + // groupV1.Get("/events/:id/bets", a.authMiddleware, a.SuperAdminOnly, h.GetBetsByEventID) - tenant.Get("/upcoming-events", h.GetTenantUpcomingEvents) - tenant.Get("/top-leagues", h.GetTopLeagues) - tenant.Get("/events", h.GetTenantEvents) - tenant.Get("/events/:id", h.GetTenantEventByID) - tenant.Put("/events/:id/settings", a.authMiddleware, a.CompanyOnly, h.UpdateTenantEventSettings) - tenant.Get("/events/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetTenantBetsByEventID) + // tenant.Get("/upcoming-events", h.GetTenantUpcomingEvents) + // tenant.Get("/top-leagues", h.GetTopLeagues) + // tenant.Get("/events", h.GetTenantEvents) + // tenant.Get("/events/:id", h.GetTenantEventByID) + // tenant.Put("/events/:id/settings", a.authMiddleware, a.CompanyOnly, h.UpdateTenantEventSettings) + // tenant.Get("/events/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetTenantBetsByEventID) //EnetPulse // groupV1.Get("/odds/pre-match", h.GetPreMatchOdds) - groupV1.Get("/sports", h.GetAllSports) - groupV1.Get("/tournament_templates", h.GetAllTournamentTemplates) - groupV1.Get("/tournaments", h.GetAllTournaments) - groupV1.Get("/tournament_stages", h.GetAllTournamentStages) - groupV1.Get("/fixtures", h.GetFixturesByDate) - groupV1.Get("/results", h.GetAllResults) - groupV1.Get("/preodds", h.GetAllPreoddsWithBettingOffers) - groupV1.Get("/bettingoffers", h.GetAllBettingOffers) - groupV1.Get("/fixtures/preodds", h.GetFixturesWithPreodds) + // groupV1.Get("/sports", h.GetAllSports) + // groupV1.Get("/tournament_templates", h.GetAllTournamentTemplates) + // groupV1.Get("/tournaments", h.GetAllTournaments) + // groupV1.Get("/tournament_stages", h.GetAllTournamentStages) + // groupV1.Get("/fixtures", h.GetFixturesByDate) + // groupV1.Get("/results", h.GetAllResults) + // groupV1.Get("/preodds", h.GetAllPreoddsWithBettingOffers) + // groupV1.Get("/bettingoffers", h.GetAllBettingOffers) + // groupV1.Get("/fixtures/preodds", h.GetFixturesWithPreodds) - // Leagues - groupV1.Get("/leagues", a.authMiddleware, a.SuperAdminOnly, h.GetAllLeagues) - groupV1.Put("/leagues/:id/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalLeagueSetting) + // // Leagues + // groupV1.Get("/leagues", a.authMiddleware, a.SuperAdminOnly, h.GetAllLeagues) + // groupV1.Put("/leagues/:id/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalLeagueSetting) - tenant.Get("/leagues", h.GetAllLeagues) - tenant.Put("/leagues/:id/featured", a.authMiddleware, a.CompanyOnly, h.SetLeagueFeatured) - tenant.Put("/leagues/:id/set-active", a.authMiddleware, a.CompanyOnly, h.SetLeagueActive) + // tenant.Get("/leagues", h.GetAllLeagues) + // tenant.Put("/leagues/:id/featured", a.authMiddleware, a.CompanyOnly, h.SetLeagueFeatured) + // tenant.Put("/leagues/:id/set-active", a.authMiddleware, a.CompanyOnly, h.SetLeagueActive) - groupV1.Get("/result/b365/:id", h.GetBet365ResultsByEventID) + // groupV1.Get("/result/b365/:id", h.GetBet365ResultsByEventID) // Branch - groupV1.Post("/branch", a.authMiddleware, a.CompanyOnly, h.CreateBranch) - groupV1.Get("/branch", a.authMiddleware, a.CompanyOnly, h.GetAllBranches) - groupV1.Get("/branch/:id", a.authMiddleware, a.CompanyOnly, h.GetBranchByID) - groupV1.Post("/branch/:id/return", a.authMiddleware, a.CompanyOnly, h.ReturnBranchWallet) - groupV1.Get("/branch/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetBetByBranchID) - groupV1.Put("/branch/:id", a.authMiddleware, a.CompanyOnly, h.UpdateBranch) - groupV1.Put("/branch/:id/set-active", a.authMiddleware, a.CompanyOnly, h.UpdateBranchStatus) - groupV1.Put("/branch/:id/set-inactive", a.authMiddleware, a.CompanyOnly, h.UpdateBranchStatus) - groupV1.Delete("/branch/:id", a.authMiddleware, a.CompanyOnly, h.DeleteBranch) + // groupV1.Post("/branch", a.authMiddleware, a.CompanyOnly, h.CreateBranch) + // groupV1.Get("/branch", a.authMiddleware, a.CompanyOnly, h.GetAllBranches) + // groupV1.Get("/branch/:id", a.authMiddleware, a.CompanyOnly, h.GetBranchByID) + // groupV1.Post("/branch/:id/return", a.authMiddleware, a.CompanyOnly, h.ReturnBranchWallet) + // // groupV1.Get("/branch/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetBetByBranchID) + // groupV1.Put("/branch/:id", a.authMiddleware, a.CompanyOnly, h.UpdateBranch) + // groupV1.Put("/branch/:id/set-active", a.authMiddleware, a.CompanyOnly, h.UpdateBranchStatus) + // groupV1.Put("/branch/:id/set-inactive", a.authMiddleware, a.CompanyOnly, h.UpdateBranchStatus) + // groupV1.Delete("/branch/:id", a.authMiddleware, a.CompanyOnly, h.DeleteBranch) - groupV1.Get("/search/branch", a.authMiddleware, a.CompanyOnly, h.SearchBranch) + // groupV1.Get("/search/branch", a.authMiddleware, a.CompanyOnly, h.SearchBranch) - groupV1.Get("/branchLocation", a.authMiddleware, a.CompanyOnly, h.GetAllBranchLocations) + // groupV1.Get("/branchLocation", a.authMiddleware, a.CompanyOnly, h.GetAllBranchLocations) - groupV1.Get("/branch/:id/cashiers", a.authMiddleware, a.CompanyOnly, h.GetBranchCashiers) - groupV1.Get("/branchCashier", a.authMiddleware, a.CompanyOnly, h.GetBranchForCashier) + // groupV1.Get("/branch/:id/cashiers", a.authMiddleware, a.CompanyOnly, h.GetBranchCashiers) + // groupV1.Get("/branchCashier", a.authMiddleware, a.CompanyOnly, h.GetBranchForCashier) - // Branch Operation - groupV1.Get("/supportedOperation", a.authMiddleware, h.GetAllSupportedOperations) - groupV1.Post("/supportedOperation", a.authMiddleware, a.SuperAdminOnly, h.CreateSupportedOperation) - groupV1.Post("/operation", a.authMiddleware, a.CompanyOnly, h.CreateBranchOperation) - groupV1.Get("/branch/:id/operation", a.authMiddleware, a.CompanyOnly, h.GetBranchOperations) + // // Branch Operation + // groupV1.Get("/supportedOperation", a.authMiddleware, h.GetAllSupportedOperations) + // groupV1.Post("/supportedOperation", a.authMiddleware, a.SuperAdminOnly, h.CreateSupportedOperation) + // groupV1.Post("/operation", a.authMiddleware, a.CompanyOnly, h.CreateBranchOperation) + // groupV1.Get("/branch/:id/operation", a.authMiddleware, a.CompanyOnly, h.GetBranchOperations) - groupV1.Delete("/branch/:id/operation/:opID", a.authMiddleware, a.CompanyOnly, h.DeleteBranchOperation) + // groupV1.Delete("/branch/:id/operation/:opID", a.authMiddleware, a.CompanyOnly, h.DeleteBranchOperation) - // Company - groupV1.Post("/company", a.authMiddleware, a.SuperAdminOnly, h.CreateCompany) - groupV1.Get("/company", a.authMiddleware, a.SuperAdminOnly, h.GetAllCompanies) - groupV1.Get("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCompanyByID) - groupV1.Put("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCompany) - groupV1.Delete("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteCompany) - groupV1.Get("/company/:id/branch", a.authMiddleware, a.CompanyOnly, h.GetBranchByCompanyID) - groupV1.Get("/search/company", a.authMiddleware, a.CompanyOnly, h.SearchCompany) - groupV1.Get("/admin-company", a.authMiddleware, a.CompanyOnly, h.GetCompanyForAdmin) + // // Company + // groupV1.Post("/company", a.authMiddleware, a.SuperAdminOnly, h.CreateCompany) + // groupV1.Get("/company", a.authMiddleware, a.SuperAdminOnly, h.GetAllCompanies) + // groupV1.Get("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCompanyByID) + // groupV1.Put("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCompany) + // groupV1.Delete("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteCompany) + // groupV1.Get("/company/:id/branch", a.authMiddleware, a.CompanyOnly, h.GetBranchByCompanyID) + // groupV1.Get("/search/company", a.authMiddleware, a.CompanyOnly, h.SearchCompany) + // groupV1.Get("/admin-company", a.authMiddleware, a.CompanyOnly, h.GetCompanyForAdmin) - groupV1.Get("/ticket", h.GetAllTickets) - groupV1.Get("/ticket/:id", h.GetTicketByID) + // groupV1.Get("/ticket", h.GetAllTickets) + // groupV1.Get("/ticket/:id", h.GetTicketByID) // Ticket Routes - tenant.Post("/ticket", h.CreateTenantTicket) - tenant.Get("/ticket", h.GetAllTenantTickets) - tenant.Get("/ticket/:id", h.GetTenantTicketByID) + // tenant.Post("/ticket", h.CreateTenantTicket) + // tenant.Get("/ticket", h.GetAllTenantTickets) + // tenant.Get("/ticket/:id", h.GetTenantTicketByID) // Bet Routes - tenant.Post("/sport/bet", a.authMiddleware, h.CreateBet) - tenant.Post("/sport/bet/fastcode", a.authMiddleware, h.CreateBetWithFastCode) - tenant.Get("/sport/bet/fastcode/:fast_code", h.GetBetByFastCode) - tenant.Get("/sport/bet", a.authMiddleware, a.CompanyOnly, h.GetAllTenantBets) - tenant.Get("/sport/bet/:id", a.authMiddleware, h.GetTenantBetByID) - tenant.Patch("/sport/bet/:id", a.authMiddleware, h.UpdateCashOut) - tenant.Delete("/sport/bet/:id", a.authMiddleware, h.DeleteTenantBet) + // tenant.Post("/sport/bet", a.authMiddleware, h.CreateBet) + // tenant.Post("/sport/bet/fastcode", a.authMiddleware, h.CreateBetWithFastCode) + // tenant.Get("/sport/bet/fastcode/:fast_code", h.GetBetByFastCode) + // tenant.Get("/sport/bet", a.authMiddleware, a.CompanyOnly, h.GetAllTenantBets) + // tenant.Get("/sport/bet/:id", a.authMiddleware, h.GetTenantBetByID) + // tenant.Patch("/sport/bet/:id", a.authMiddleware, h.UpdateCashOut) + // tenant.Delete("/sport/bet/:id", a.authMiddleware, h.DeleteTenantBet) - groupV1.Get("/sport/bet/:id", a.authMiddleware, a.CompanyOnly, h.GetBetByID) - groupV1.Get("/sport/bet", a.authMiddleware, a.SuperAdminOnly, h.GetAllBet) - groupV1.Delete("/sport/bet/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteBet) + // groupV1.Get("/sport/bet/:id", a.authMiddleware, a.CompanyOnly, h.GetBetByID) + // groupV1.Get("/sport/bet", a.authMiddleware, a.SuperAdminOnly, h.GetAllBet) + // groupV1.Delete("/sport/bet/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteBet) - tenant.Post("/sport/random/bet", a.authMiddleware, h.RandomBet) + // tenant.Post("/sport/random/bet", a.authMiddleware, h.RandomBet) - // Wallet - groupV1.Get("/wallet", h.GetAllWallets) - groupV1.Get("/wallet/:id", h.GetWalletByID) - groupV1.Put("/wallet/:id", h.UpdateWalletActive) - groupV1.Get("/branchWallet", a.authMiddleware, h.GetAllBranchWallets) - groupV1.Get("/customerWallet", a.authMiddleware, h.GetAllCustomerWallets) - groupV1.Get("/cashierWallet", a.authMiddleware, h.GetWalletForCashier) + // // Wallet + // groupV1.Get("/wallet", h.GetAllWallets) + // groupV1.Get("/wallet/:id", h.GetWalletByID) + // groupV1.Put("/wallet/:id", h.UpdateWalletActive) + // groupV1.Get("/branchWallet", a.authMiddleware, h.GetAllBranchWallets) + // groupV1.Get("/customerWallet", a.authMiddleware, h.GetAllCustomerWallets) + // groupV1.Get("/cashierWallet", a.authMiddleware, h.GetWalletForCashier) // Transfer // /transfer/wallet - transfer from one wallet to another wallet - groupV1.Post("/transfer/wallet/:id", a.authMiddleware, h.TransferToWallet) - groupV1.Get("/transfer/wallet/:id", a.authMiddleware, h.GetTransfersByWallet) - groupV1.Post("/transfer/refill/:id", a.authMiddleware, h.RefillWallet) + // groupV1.Post("/transfer/wallet/:id", a.authMiddleware, h.TransferToWallet) + // groupV1.Get("/transfer/wallet/:id", a.authMiddleware, h.GetTransfersByWallet) + // groupV1.Post("/transfer/refill/:id", a.authMiddleware, h.RefillWallet) //Chapa Routes - groupV1.Post("/chapa/payments/webhook/verify", h.WebhookCallback) - groupV1.Get("/chapa/transaction/manual/verify/:tx_ref", a.authMiddleware, h.ManualVerifyTransaction) - groupV1.Put("/chapa/transaction/cancel/:tx_ref", a.authMiddleware, h.CancelDeposit) - groupV1.Get("/chapa/transactions", a.authMiddleware, h.FetchAllTransactions) - groupV1.Get("/chapa/transaction/events/:ref_id", a.authMiddleware, h.GetTransactionEvents) - groupV1.Post("/chapa/payments/deposit", a.authMiddleware, h.InitiateDeposit) - groupV1.Post("/chapa/payments/withdraw", a.authMiddleware, h.InitiateWithdrawal) - groupV1.Get("/chapa/banks", h.GetSupportedBanks) - groupV1.Get("/chapa/payments/receipt/:chapa_ref", a.authMiddleware, h.GetPaymentReceipt) - groupV1.Get("/chapa/transfers", a.authMiddleware, h.GetAllTransfers) - groupV1.Get("/chapa/balance", a.authMiddleware, h.GetAccountBalance) - groupV1.Post("/chapa/swap", a.authMiddleware, h.SwapCurrency) + // groupV1.Post("/chapa/payments/webhook/verify", h.WebhookCallback) + // groupV1.Get("/chapa/transaction/manual/verify/:tx_ref", a.authMiddleware, h.ManualVerifyTransaction) + // groupV1.Put("/chapa/transaction/cancel/:tx_ref", a.authMiddleware, h.CancelDeposit) + // groupV1.Get("/chapa/transactions", a.authMiddleware, h.FetchAllTransactions) + // groupV1.Get("/chapa/transaction/events/:ref_id", a.authMiddleware, h.GetTransactionEvents) + // groupV1.Post("/chapa/payments/deposit", a.authMiddleware, h.InitiateDeposit) + // groupV1.Post("/chapa/payments/withdraw", a.authMiddleware, h.InitiateWithdrawal) + // groupV1.Get("/chapa/banks", h.GetSupportedBanks) + // groupV1.Get("/chapa/payments/receipt/:chapa_ref", a.authMiddleware, h.GetPaymentReceipt) + // groupV1.Get("/chapa/transfers", a.authMiddleware, h.GetAllTransfers) + // groupV1.Get("/chapa/balance", a.authMiddleware, h.GetAccountBalance) + // groupV1.Post("/chapa/swap", a.authMiddleware, h.SwapCurrency) // Currencies groupV1.Get("/currencies", h.GetSupportedCurrencies) @@ -417,29 +384,29 @@ func (a *App) initAppRoutes() { // groupV1.Get("/reports/download/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.DownloadReportByID) //Alea Play Virtual Game Routes - groupV1.Get("/alea-play/launch", a.authMiddleware, h.LaunchAleaGame) - groupV1.Post("/webhooks/alea-play", a.authMiddleware, h.HandleAleaCallback) + // groupV1.Get("/alea-play/launch", a.authMiddleware, h.LaunchAleaGame) + // groupV1.Post("/webhooks/alea-play", a.authMiddleware, h.HandleAleaCallback) - //Veli Virtual Game Routes - groupV1.Post("/veli/providers", h.GetProviders) - groupV1.Post("/veli/games-list", h.GetGamesByProvider) - groupV1.Post("/veli/start-game", a.authMiddleware, h.StartGame) - groupV1.Post("/veli/start-demo-game", h.StartDemoGame) - a.fiber.Post("/balance", h.GetBalance) - groupV1.Post("/veli/gaming-activity", a.authMiddleware, h.GetGamingActivity) - groupV1.Post("/veli/huge-wins", a.authMiddleware, h.GetHugeWins) - groupV1.Post("/veli/credit-balances", a.authMiddleware, h.GetCreditBalances) + // //Veli Virtual Game Routes + // groupV1.Post("/veli/providers", h.GetProviders) + // groupV1.Post("/veli/games-list", h.GetGamesByProvider) + // groupV1.Post("/veli/start-game", a.authMiddleware, h.StartGame) + // groupV1.Post("/veli/start-demo-game", h.StartDemoGame) + // a.fiber.Post("/balance", h.GetBalance) + // groupV1.Post("/veli/gaming-activity", a.authMiddleware, h.GetGamingActivity) + // groupV1.Post("/veli/huge-wins", a.authMiddleware, h.GetHugeWins) + // groupV1.Post("/veli/credit-balances", a.authMiddleware, h.GetCreditBalances) - //Atlas Virtual Game Routes - groupV1.Get("/atlas/games", h.GetAtlasVGames) - groupV1.Post("/atlas/init-game", a.authMiddleware, h.InitAtlasGame) - a.fiber.Post("/account", h.AtlasGetUserDataCallback) - a.fiber.Post("/betwin", h.HandleAtlasBetWin) - a.fiber.Post("/result", h.HandleRoundResult) - a.fiber.Post("/rollback", h.HandleRollback) - a.fiber.Post("/freespin", h.FreeSpinResultCallback) - a.fiber.Post("/jackpot", h.JackpotCallback) - groupV1.Post("/atlas/freespin", a.authMiddleware, h.CreateFreeSpin) + // //Atlas Virtual Game Routes + // groupV1.Get("/atlas/games", h.GetAtlasVGames) + // groupV1.Post("/atlas/init-game", a.authMiddleware, h.InitAtlasGame) + // a.fiber.Post("/account", h.AtlasGetUserDataCallback) + // a.fiber.Post("/betwin", h.HandleAtlasBetWin) + // a.fiber.Post("/result", h.HandleRoundResult) + // a.fiber.Post("/rollback", h.HandleRollback) + // a.fiber.Post("/freespin", h.FreeSpinResultCallback) + // a.fiber.Post("/jackpot", h.JackpotCallback) + // groupV1.Post("/atlas/freespin", a.authMiddleware, h.CreateFreeSpin) //mongoDB logs groupV1.Get("/logs", a.authMiddleware, a.SuperAdminOnly, handlers.GetLogsHandler(context.Background())) @@ -448,20 +415,20 @@ func (a *App) initAppRoutes() { // group.Get("/virtual-games/recommendations/:userID", h.GetRecommendations) // Transactions /shop/transactions - groupV1.Post("/shop/bet", a.authMiddleware, a.CompanyOnly, h.CreateShopBet) - groupV1.Get("/shop/bet", a.authMiddleware, a.CompanyOnly, h.GetAllShopBets) - groupV1.Get("/shop/bet/:id", a.authMiddleware, a.CompanyOnly, h.GetShopBetByBetID) - groupV1.Post("/shop/bet/:id/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutBet) - groupV1.Post("/shop/bet/:id/generate", a.authMiddleware, a.CompanyOnly, h.CashoutBet) - groupV1.Get("/shop/cashout/:id", a.authMiddleware, a.CompanyOnly, h.GetShopBetByCashoutID) - groupV1.Post("/shop/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutByCashoutID) - groupV1.Post("/shop/deposit", a.authMiddleware, a.CompanyOnly, h.DepositForCustomer) + // groupV1.Post("/shop/bet", a.authMiddleware, a.CompanyOnly, h.CreateShopBet) + // groupV1.Get("/shop/bet", a.authMiddleware, a.CompanyOnly, h.GetAllShopBets) + // groupV1.Get("/shop/bet/:id", a.authMiddleware, a.CompanyOnly, h.GetShopBetByBetID) + // groupV1.Post("/shop/bet/:id/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutBet) + // groupV1.Post("/shop/bet/:id/generate", a.authMiddleware, a.CompanyOnly, h.CashoutBet) + // groupV1.Get("/shop/cashout/:id", a.authMiddleware, a.CompanyOnly, h.GetShopBetByCashoutID) + // groupV1.Post("/shop/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutByCashoutID) + // groupV1.Post("/shop/deposit", a.authMiddleware, a.CompanyOnly, h.DepositForCustomer) // groupV1.Get("/shop/deposit", a.authMiddleware, a.CompanyOnly, h.DepositForCustomer) - groupV1.Get("/shop/transaction", a.authMiddleware, a.CompanyOnly, h.GetAllTransactions) - groupV1.Get("/shop/transaction/:id", a.authMiddleware, a.CompanyOnly, h.GetTransactionByID) - groupV1.Get("/shop/transaction/:id/bet", a.authMiddleware, a.CompanyOnly, h.GetShopBetByTransactionID) - groupV1.Put("/shop/transaction/:id", a.authMiddleware, a.CompanyOnly, h.UpdateTransactionVerified) + // groupV1.Get("/shop/transaction", a.authMiddleware, a.CompanyOnly, h.GetAllTransactions) + // groupV1.Get("/shop/transaction/:id", a.authMiddleware, a.CompanyOnly, h.GetTransactionByID) + // groupV1.Get("/shop/transaction/:id/bet", a.authMiddleware, a.CompanyOnly, h.GetShopBetByTransactionID) + // groupV1.Put("/shop/transaction/:id", a.authMiddleware, a.CompanyOnly, h.UpdateTransactionVerified) // Notification Routes groupV1.Get("/ws/connect", a.WebsocketAuthMiddleware, h.ConnectSocket) @@ -472,34 +439,34 @@ func (a *App) initAppRoutes() { groupV1.Post("/notifications/create", a.authMiddleware, h.CreateAndSendNotification) // Virtual Game Routes - a.fiber.Post("/virtual-game/launch", a.authMiddleware, h.LaunchVirtualGame) - a.fiber.Post("/virtual-game/callback", h.HandleVirtualGameCallback) - a.fiber.Post("/playerInfo", h.HandlePlayerInfo) - a.fiber.Post("/bet", h.HandleBet) - a.fiber.Post("/win", h.HandleWin) - a.fiber.Post("/cancel", h.HandleCancel) - a.fiber.Post("/promoWin ", h.HandlePromoWin) - a.fiber.Post("/tournamentWin ", h.HandleTournamentWin) - a.fiber.Get("/popok/games", h.GetGameList) - a.fiber.Get("/popok/games/recommend", a.authMiddleware, h.RecommendGames) - groupV1.Post("/virtual-game/favorites", a.authMiddleware, h.AddFavorite) - groupV1.Delete("/virtual-game/favorites/:gameID", a.authMiddleware, h.RemoveFavorite) - groupV1.Get("/virtual-game/favorites", a.authMiddleware, h.ListFavorites) + // a.fiber.Post("/virtual-game/launch", a.authMiddleware, h.LaunchVirtualGame) + // a.fiber.Post("/virtual-game/callback", h.HandleVirtualGameCallback) + // a.fiber.Post("/playerInfo", h.HandlePlayerInfo) + // a.fiber.Post("/bet", h.HandleBet) + // a.fiber.Post("/win", h.HandleWin) + // a.fiber.Post("/cancel", h.HandleCancel) + // a.fiber.Post("/promoWin ", h.HandlePromoWin) + // a.fiber.Post("/tournamentWin ", h.HandleTournamentWin) + // a.fiber.Get("/popok/games", h.GetGameList) + // a.fiber.Get("/popok/games/recommend", a.authMiddleware, h.RecommendGames) + // groupV1.Post("/virtual-game/favorites", a.authMiddleware, h.AddFavorite) + // groupV1.Delete("/virtual-game/favorites/:gameID", a.authMiddleware, h.RemoveFavorite) + // groupV1.Get("/virtual-game/favorites", a.authMiddleware, h.ListFavorites) - groupV1.Get("/orchestrator/virtual-game/provider-reports/asc", a.OnlyAdminAndAbove, h.ListVirtualGameProviderReportsAscHandler) - groupV1.Get("/orchestrator/virtual-game/provider-reports/desc", a.OnlyAdminAndAbove, h.ListVirtualGameProviderReportsDescHandler) - groupV1.Delete("/virtual-game/orchestrator/providers/:provideID", a.authMiddleware, h.RemoveProvider) - groupV1.Get("/virtual-game/orchestrator/providers/:provideID", a.authMiddleware, h.GetProviderByID) - groupV1.Get("/virtual-game/orchestrator/games", h.ListVirtualGames) - groupV1.Get("/virtual-game/orchestrator/providers", h.ListProviders) - groupV1.Patch("/virtual-game/orchestrator/providers/:provideID/status", a.authMiddleware, h.SetProviderEnabled) + // groupV1.Get("/orchestrator/virtual-game/provider-reports/asc", a.OnlyAdminAndAbove, h.ListVirtualGameProviderReportsAscHandler) + // groupV1.Get("/orchestrator/virtual-game/provider-reports/desc", a.OnlyAdminAndAbove, h.ListVirtualGameProviderReportsDescHandler) + // groupV1.Delete("/virtual-game/orchestrator/providers/:provideID", a.authMiddleware, h.RemoveProvider) + // groupV1.Get("/virtual-game/orchestrator/providers/:provideID", a.authMiddleware, h.GetProviderByID) + // groupV1.Get("/virtual-game/orchestrator/games", h.ListVirtualGames) + // groupV1.Get("/virtual-game/orchestrator/providers", h.ListProviders) + // groupV1.Patch("/virtual-game/orchestrator/providers/:provideID/status", a.authMiddleware, h.SetProviderEnabled) //Issue Reporting Routes - groupV1.Post("/issues", a.authMiddleware, h.CreateIssue) //anyone who has logged can report a - groupV1.Get("/issues/customer/:customer_id", a.authMiddleware, a.OnlyAdminAndAbove, h.GetUserIssues) - groupV1.Get("/issues", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllIssues) - groupV1.Patch("/issues/:issue_id/status", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateIssueStatus) - groupV1.Delete("/issues/:issue_id", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteIssue) + // groupV1.Post("/issues", a.authMiddleware, h.CreateIssue) //anyone who has logged can report a + // groupV1.Get("/issues/customer/:customer_id", a.authMiddleware, a.OnlyAdminAndAbove, h.GetUserIssues) + // groupV1.Get("/issues", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllIssues) + // groupV1.Patch("/issues/:issue_id/status", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateIssueStatus) + // groupV1.Delete("/issues/:issue_id", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteIssue) // Settings groupV1.Get("/settings", a.authMiddleware, a.SuperAdminOnly, h.GetGlobalSettingList) @@ -511,7 +478,7 @@ func (a *App) initAppRoutes() { tenant.Delete("/settings/:key", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteCompanySetting) tenant.Delete("/settings", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteAllCompanySetting) - groupV1.Get("/stats/total/events", h.GetTotalEventStats) - groupV1.Get("/stats/interval/events", h.GetTotalEventStatsByInterval) + // groupV1.Get("/stats/total/events", h.GetTotalEventStats) + // groupV1.Get("/stats/interval/events", h.GetTotalEventStatsByInterval) } diff --git a/internal/web_server/ws/ws.go b/internal/web_server/ws/ws.go index f4203a0..55c7dd8 100644 --- a/internal/web_server/ws/ws.go +++ b/internal/web_server/ws/ws.go @@ -5,7 +5,6 @@ import ( "strconv" "sync" - "github.com/SamuelTariku/FortuneBet-Backend/internal/event" "github.com/gofiber/fiber/v2" "github.com/gorilla/websocket" "github.com/valyala/fasthttp" @@ -161,15 +160,15 @@ func (h *NotificationHub) WebSocketHandler() fiber.Handler { } } -func (h *NotificationHub) BroadcastWalletUpdate(userID int64, event event.WalletEvent) { - payload := map[string]interface{}{ - "type": event.EventType, - "wallet_id": event.WalletID, - "wallet_type": event.WalletType, - "user_id": event.UserID, - "balance": event.Balance, - "trigger": event.Trigger, - "recipient_id": userID, - } - h.Broadcast <- payload -} +// func (h *NotificationHub) BroadcastWalletUpdate(userID int64, event event.WalletEvent) { +// payload := map[string]interface{}{ +// "type": event.EventType, +// "wallet_id": event.WalletID, +// "wallet_type": event.WalletType, +// "user_id": event.UserID, +// "balance": event.Balance, +// "trigger": event.Trigger, +// "recipient_id": userID, +// } +// h.Broadcast <- payload +// } diff --git a/new.env b/new.env index 1d2ac4c..996b69e 100755 --- a/new.env +++ b/new.env @@ -1,13 +1,9 @@ -# REPORT_EXPORT_PATH="C:\\ProgramData\\FortuneBet\\exported_reports" #prod env -# REPORT_EXPORT_PATH ="./exported_reports" #dev env - - # Telebirr TELEBIRR_BASE_URL="https://developerportal.ethiotelebirr.et:38443/payment/web/paygate?" TELEBIRR_APP_SECRET=your_telebirr_app_secret TELEBIRR_FABRIC_APP_ID=your_telebirr_fabric_app_id TELEBIRR_MERCHANT_CODE=your_telebirr_merchant_code -TELEBIRR_CALLBACK_URL=https://api.fortunebets.net/api/v1/telebirr/callback +TELEBIRR_CALLBACK_URL=https://api.yimaru.net/api/v1/telebirr/callback RESEND_SENDER_EMAIL=email RESEND_API_KEY=123 @@ -23,16 +19,6 @@ AFRO_SMS_API_KEY=1 AFRO_SMS_SENDER_NAME= AFRO_SMS_RECEIVER_PHONE_NUMBER= -BET365_TOKEN=158046-hesJDP2Cay2M5G -POPOK_CLIENT_ID=1 -POPOK_PLATFORM=111 -POPOK_SECRET_KEY=XwFQ76Y59zBxGryh -# POPOK_BASE_URL=https://api.pokgaming.com/game/launch #Production -# POPOK_BASE_URL=https://games.pokgaming.com/launch #Production -# POPOK_BASE_URL=https://sandbox.pokgaming.com/game/launch #Staging -# POPOK_BASE_URL=https://test-api.pokgaming.com/launch #Staging -POPOK_BASE_URL=https://st.pokgaming.com/ #Staging -POPOK_CALLBACK_URL=https://api.fortunebets.net #Muli-currency Support FIXER_API_KEY=3b0f1eb30d-63c875026d-sxy9pl @@ -46,37 +32,15 @@ CHAPA_BASE_URL=https://api.chapa.co/v1 CHAPA_ENCRYPTION_KEY=zLdYrjnBCknMvFikmP5jBfen CHAPA_PUBLIC_KEY=CHAPUBK_TEST-HJR0qhQRPLTkauNy9Q8UrmskPTOR31aC CHAPA_SECRET_KEY=CHASECK_TEST-q3jypwmFK6XJGYOK3aX4z9Kogd9KaHhF -CHAPA_CALLBACK_URL=https://api.fortunebets.net/api/v1/payments/webhook/verify -CHAPA_RETURN_URL=https://fortunebets.net/dashboard - -#Alea Play -ALEA_ENABLED=true -ALEA_BASE_URL=https://api.aleaplay.com -ALEA_OPERATOR_ID=operator_id -ALEA_SECRET_KEY=hmac_secret -ALEA_GAME_LIST_URL=https://api.aleaplay.com/games/list # Optional -ALEA_DEFAULT_CURRENCY=USD # Optional (default: USD) -ALEA_SESSION_TIMEOUT=24 # Optional (hours, default: 24) -ALEA_GAME_ID_AVIATOR=aviator_prod - - -# Veli Games -VELI_ENABLED=true -VELI_BASE_URL=https://stage-api.velitech.games -VELI_REGIONAL_URL=https://stage-api.velitech.games -VELI_WEBHOOK_URL=https://api.fortunebets.net/ -VELI_SECRET_KEY=qPfg7PyhyOI9d2_bcx3ovKqzMqtjaVen -VELI_OPERATOR_KEY=111 -VELI_OPERATOR_ID=fortune_bets -VELI_BRAND_ID=fortune_bets -VELI_DEFAULT_CURRENCY=ETB +CHAPA_CALLBACK_URL=https://api.yimaru.net/api/v1/payments/webhook/verify +CHAPA_RETURN_URL=https://yimaru.net/dashboard # Arifpay ARIFPAY_API_KEY=OV7MLkA39tgWiEApLLSg6aNf4F1RDobV -ARIFPAY_CANCEL_URL=https://api.fortunebets.net/api/v1/payments/arifpay/cancel -ARIFPAY_ERROR_URL=https://api.fortunebets.net/api/v1/payments/arifpay/error -ARIFPAY_NOTIFY_URL=https://api.fortunebets.net/api/v1/payments/arifpay/notify -ARIFPAY_SUCCESS_URL=https://api.fortunebets.net/api/v1/payments/arifpay/success +ARIFPAY_CANCEL_URL=https://api.yimaru.net/api/v1/payments/arifpay/cancel +ARIFPAY_ERROR_URL=https://api.yimaru.net/api/v1/payments/arifpay/error +ARIFPAY_NOTIFY_URL=https://api.yimaru.net/api/v1/payments/arifpay/notify +ARIFPAY_SUCCESS_URL=https://api.yimaru.net/api/v1/payments/arifpay/success # Santimpay SANTIMPAY_SECRET_KEY=your_santim_pay_secret_key diff --git a/static/logos/atlas-dark.png b/static/logos/atlas-dark.png deleted file mode 100644 index 9d28625..0000000 Binary files a/static/logos/atlas-dark.png and /dev/null differ diff --git a/static/logos/atlas-light.png b/static/logos/atlas-light.png deleted file mode 100644 index 63b7ec9..0000000 Binary files a/static/logos/atlas-light.png and /dev/null differ diff --git a/static/logos/popok-dark.png b/static/logos/popok-dark.png deleted file mode 100644 index 71c9e50..0000000 Binary files a/static/logos/popok-dark.png and /dev/null differ diff --git a/static/logos/popok-light.png b/static/logos/popok-light.png deleted file mode 100644 index 40f051b..0000000 Binary files a/static/logos/popok-light.png and /dev/null differ