diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index 56f3d51..50a4b75 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -183,15 +183,15 @@ CREATE TABLE IF NOT EXISTS branch_cashiers ( ); CREATE TABLE events ( id TEXT PRIMARY KEY, - sport_id TEXT, + sport_id INT, match_name TEXT, home_team TEXT, away_team TEXT, - home_team_id TEXT, - away_team_id TEXT, + home_team_id INT, + away_team_id INT, home_kit_image TEXT, away_kit_image TEXT, - league_id TEXT, + league_id INT, league_name TEXT, league_cc TEXT, start_time TIMESTAMP, diff --git a/gen/db/cashier.sql.go b/gen/db/cashier.sql.go index d0f6768..bb71cb2 100644 --- a/gen/db/cashier.sql.go +++ b/gen/db/cashier.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.28.0 +// sqlc v1.29.0 // source: cashier.sql package dbgen diff --git a/gen/db/events.sql.go b/gen/db/events.sql.go index d7c6824..a2a53d6 100644 --- a/gen/db/events.sql.go +++ b/gen/db/events.sql.go @@ -47,15 +47,15 @@ ORDER BY start_time ASC type GetAllUpcomingEventsRow struct { ID string `json:"id"` - SportID pgtype.Text `json:"sport_id"` + SportID pgtype.Int4 `json:"sport_id"` MatchName pgtype.Text `json:"match_name"` HomeTeam pgtype.Text `json:"home_team"` AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Text `json:"home_team_id"` - AwayTeamID pgtype.Text `json:"away_team_id"` + HomeTeamID pgtype.Int4 `json:"home_team_id"` + AwayTeamID pgtype.Int4 `json:"away_team_id"` HomeKitImage pgtype.Text `json:"home_kit_image"` AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Text `json:"league_id"` + LeagueID pgtype.Int4 `json:"league_id"` LeagueName pgtype.Text `json:"league_name"` LeagueCc pgtype.Text `json:"league_cc"` StartTime pgtype.Timestamp `json:"start_time"` @@ -128,15 +128,15 @@ ORDER BY start_time ASC type GetExpiredUpcomingEventsRow struct { ID string `json:"id"` - SportID pgtype.Text `json:"sport_id"` + SportID pgtype.Int4 `json:"sport_id"` MatchName pgtype.Text `json:"match_name"` HomeTeam pgtype.Text `json:"home_team"` AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Text `json:"home_team_id"` - AwayTeamID pgtype.Text `json:"away_team_id"` + HomeTeamID pgtype.Int4 `json:"home_team_id"` + AwayTeamID pgtype.Int4 `json:"away_team_id"` HomeKitImage pgtype.Text `json:"home_kit_image"` AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Text `json:"league_id"` + LeagueID pgtype.Int4 `json:"league_id"` LeagueName pgtype.Text `json:"league_name"` LeagueCc pgtype.Text `json:"league_cc"` StartTime pgtype.Timestamp `json:"start_time"` @@ -226,8 +226,8 @@ LIMIT $6 OFFSET $5 ` type GetPaginatedUpcomingEventsParams struct { - LeagueID pgtype.Text `json:"league_id"` - SportID pgtype.Text `json:"sport_id"` + LeagueID pgtype.Int4 `json:"league_id"` + SportID pgtype.Int4 `json:"sport_id"` LastStartTime pgtype.Timestamp `json:"last_start_time"` FirstStartTime pgtype.Timestamp `json:"first_start_time"` Offset pgtype.Int4 `json:"offset"` @@ -236,15 +236,15 @@ type GetPaginatedUpcomingEventsParams struct { type GetPaginatedUpcomingEventsRow struct { ID string `json:"id"` - SportID pgtype.Text `json:"sport_id"` + SportID pgtype.Int4 `json:"sport_id"` MatchName pgtype.Text `json:"match_name"` HomeTeam pgtype.Text `json:"home_team"` AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Text `json:"home_team_id"` - AwayTeamID pgtype.Text `json:"away_team_id"` + HomeTeamID pgtype.Int4 `json:"home_team_id"` + AwayTeamID pgtype.Int4 `json:"away_team_id"` HomeKitImage pgtype.Text `json:"home_kit_image"` AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Text `json:"league_id"` + LeagueID pgtype.Int4 `json:"league_id"` LeagueName pgtype.Text `json:"league_name"` LeagueCc pgtype.Text `json:"league_cc"` StartTime pgtype.Timestamp `json:"start_time"` @@ -323,8 +323,8 @@ WHERE is_live = false ` type GetTotalEventsParams struct { - LeagueID pgtype.Text `json:"league_id"` - SportID pgtype.Text `json:"sport_id"` + LeagueID pgtype.Int4 `json:"league_id"` + SportID pgtype.Int4 `json:"sport_id"` LastStartTime pgtype.Timestamp `json:"last_start_time"` FirstStartTime pgtype.Timestamp `json:"first_start_time"` } @@ -368,15 +368,15 @@ LIMIT 1 type GetUpcomingByIDRow struct { ID string `json:"id"` - SportID pgtype.Text `json:"sport_id"` + SportID pgtype.Int4 `json:"sport_id"` MatchName pgtype.Text `json:"match_name"` HomeTeam pgtype.Text `json:"home_team"` AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Text `json:"home_team_id"` - AwayTeamID pgtype.Text `json:"away_team_id"` + HomeTeamID pgtype.Int4 `json:"home_team_id"` + AwayTeamID pgtype.Int4 `json:"away_team_id"` HomeKitImage pgtype.Text `json:"home_kit_image"` AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Text `json:"league_id"` + LeagueID pgtype.Int4 `json:"league_id"` LeagueName pgtype.Text `json:"league_name"` LeagueCc pgtype.Text `json:"league_cc"` StartTime pgtype.Timestamp `json:"start_time"` @@ -484,15 +484,15 @@ SET sport_id = EXCLUDED.sport_id, type InsertEventParams struct { ID string `json:"id"` - SportID pgtype.Text `json:"sport_id"` + SportID pgtype.Int4 `json:"sport_id"` MatchName pgtype.Text `json:"match_name"` HomeTeam pgtype.Text `json:"home_team"` AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Text `json:"home_team_id"` - AwayTeamID pgtype.Text `json:"away_team_id"` + HomeTeamID pgtype.Int4 `json:"home_team_id"` + AwayTeamID pgtype.Int4 `json:"away_team_id"` HomeKitImage pgtype.Text `json:"home_kit_image"` AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Text `json:"league_id"` + LeagueID pgtype.Int4 `json:"league_id"` LeagueName pgtype.Text `json:"league_name"` LeagueCc pgtype.Text `json:"league_cc"` StartTime pgtype.Timestamp `json:"start_time"` @@ -591,15 +591,15 @@ SET sport_id = EXCLUDED.sport_id, type InsertUpcomingEventParams struct { ID string `json:"id"` - SportID pgtype.Text `json:"sport_id"` + SportID pgtype.Int4 `json:"sport_id"` MatchName pgtype.Text `json:"match_name"` HomeTeam pgtype.Text `json:"home_team"` AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Text `json:"home_team_id"` - AwayTeamID pgtype.Text `json:"away_team_id"` + HomeTeamID pgtype.Int4 `json:"home_team_id"` + AwayTeamID pgtype.Int4 `json:"away_team_id"` HomeKitImage pgtype.Text `json:"home_kit_image"` AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Text `json:"league_id"` + LeagueID pgtype.Int4 `json:"league_id"` LeagueName pgtype.Text `json:"league_name"` LeagueCc pgtype.Text `json:"league_cc"` StartTime pgtype.Timestamp `json:"start_time"` diff --git a/gen/db/models.go b/gen/db/models.go index 7c65cfc..75c6af8 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -176,15 +176,15 @@ type CustomerWallet struct { type Event struct { ID string `json:"id"` - SportID pgtype.Text `json:"sport_id"` + SportID pgtype.Int4 `json:"sport_id"` MatchName pgtype.Text `json:"match_name"` HomeTeam pgtype.Text `json:"home_team"` AwayTeam pgtype.Text `json:"away_team"` - HomeTeamID pgtype.Text `json:"home_team_id"` - AwayTeamID pgtype.Text `json:"away_team_id"` + HomeTeamID pgtype.Int4 `json:"home_team_id"` + AwayTeamID pgtype.Int4 `json:"away_team_id"` HomeKitImage pgtype.Text `json:"home_kit_image"` AwayKitImage pgtype.Text `json:"away_kit_image"` - LeagueID pgtype.Text `json:"league_id"` + LeagueID pgtype.Int4 `json:"league_id"` LeagueName pgtype.Text `json:"league_name"` LeagueCc pgtype.Text `json:"league_cc"` StartTime pgtype.Timestamp `json:"start_time"` @@ -383,6 +383,32 @@ type User struct { ReferredBy pgtype.Text `json:"referred_by"` } +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 VirtualGame struct { + ID int64 `json:"id"` + Name string `json:"name"` + Provider string `json:"provider"` + Category string `json:"category"` + MinBet pgtype.Numeric `json:"min_bet"` + MaxBet pgtype.Numeric `json:"max_bet"` + Volatility string `json:"volatility"` + Rtp pgtype.Numeric `json:"rtp"` + IsFeatured pgtype.Bool `json:"is_featured"` + PopularityScore pgtype.Int4 `json:"popularity_score"` + ThumbnailUrl pgtype.Text `json:"thumbnail_url"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + type VirtualGameSession struct { ID int64 `json:"id"` UserID int64 `json:"user_id"` diff --git a/internal/domain/common.go b/internal/domain/common.go index fc652d1..43c5ed0 100644 --- a/internal/domain/common.go +++ b/internal/domain/common.go @@ -13,6 +13,10 @@ type ValidInt struct { Value int Valid bool } +type ValidInt32 struct { + Value int32 + Valid bool +} type ValidString struct { Value string diff --git a/internal/domain/event.go b/internal/domain/event.go index 9a463ca..a1c2a6f 100644 --- a/internal/domain/event.go +++ b/internal/domain/event.go @@ -4,15 +4,15 @@ import "time" type Event struct { ID string - SportID string + SportID int32 MatchName string HomeTeam string AwayTeam string - HomeTeamID string - AwayTeamID string + HomeTeamID int32 + AwayTeamID int32 HomeKitImage string AwayKitImage string - LeagueID string + LeagueID int32 LeagueName string LeagueCC string StartTime string @@ -54,15 +54,15 @@ type BetResult struct { type UpcomingEvent struct { ID string // Event ID - SportID string // Sport ID + SportID int32 // Sport ID MatchName string // Match or event name HomeTeam string // Home team name (if available) AwayTeam string // Away team name (can be empty/null) - HomeTeamID string // Home team ID - AwayTeamID string // Away team ID (can be empty/null) + HomeTeamID int32 // Home team ID + AwayTeamID int32 // Away team ID (can be empty/null) HomeKitImage string // Kit or image for home team (optional) AwayKitImage string // Kit or image for away team (optional) - LeagueID string // League ID + LeagueID int32 // League ID LeagueName string // League name LeagueCC string // League country code StartTime time.Time // Converted from "time" field in UNIX format diff --git a/internal/repository/event.go b/internal/repository/event.go index 8f2ade8..2366e75 100644 --- a/internal/repository/event.go +++ b/internal/repository/event.go @@ -21,15 +21,15 @@ func (s *Store) SaveEvent(ctx context.Context, e domain.Event) error { return s.queries.InsertEvent(ctx, dbgen.InsertEventParams{ ID: e.ID, - SportID: pgtype.Text{String: e.SportID, Valid: true}, + SportID: pgtype.Int4{Int32: e.SportID, Valid: true}, MatchName: pgtype.Text{String: e.MatchName, Valid: true}, HomeTeam: pgtype.Text{String: e.HomeTeam, Valid: true}, AwayTeam: pgtype.Text{String: e.AwayTeam, Valid: true}, - HomeTeamID: pgtype.Text{String: e.HomeTeamID, Valid: true}, - AwayTeamID: pgtype.Text{String: e.AwayTeamID, Valid: true}, + HomeTeamID: pgtype.Int4{Int32: e.HomeTeamID, Valid: true}, + AwayTeamID: pgtype.Int4{Int32: e.AwayTeamID, Valid: true}, HomeKitImage: pgtype.Text{String: e.HomeKitImage, Valid: true}, AwayKitImage: pgtype.Text{String: e.AwayKitImage, Valid: true}, - LeagueID: pgtype.Text{String: e.LeagueID, Valid: true}, + LeagueID: pgtype.Int4{Int32: e.LeagueID, Valid: true}, LeagueName: pgtype.Text{String: e.LeagueName, Valid: true}, LeagueCc: pgtype.Text{String: e.LeagueCC, Valid: true}, StartTime: pgtype.Timestamp{Time: parsedTime, Valid: true}, @@ -46,15 +46,15 @@ func (s *Store) SaveEvent(ctx context.Context, e domain.Event) error { func (s *Store) SaveUpcomingEvent(ctx context.Context, e domain.UpcomingEvent) error { return s.queries.InsertUpcomingEvent(ctx, dbgen.InsertUpcomingEventParams{ ID: e.ID, - SportID: pgtype.Text{String: e.SportID, Valid: true}, + SportID: pgtype.Int4{Int32: e.SportID, Valid: true}, MatchName: pgtype.Text{String: e.MatchName, Valid: true}, HomeTeam: pgtype.Text{String: e.HomeTeam, Valid: true}, AwayTeam: pgtype.Text{String: e.AwayTeam, Valid: true}, - HomeTeamID: pgtype.Text{String: e.HomeTeamID, Valid: true}, - AwayTeamID: pgtype.Text{String: e.AwayTeamID, Valid: true}, + HomeTeamID: pgtype.Int4{Int32: e.HomeTeamID, Valid: true}, + AwayTeamID: pgtype.Int4{Int32: e.AwayTeamID, Valid: true}, HomeKitImage: pgtype.Text{String: e.HomeKitImage, Valid: true}, AwayKitImage: pgtype.Text{String: e.AwayKitImage, Valid: true}, - LeagueID: pgtype.Text{String: e.LeagueID, Valid: true}, + LeagueID: pgtype.Int4{Int32: e.LeagueID, Valid: true}, LeagueName: pgtype.Text{String: e.LeagueName, Valid: true}, LeagueCc: pgtype.Text{String: e.LeagueCC, Valid: true}, StartTime: pgtype.Timestamp{Time: e.StartTime, Valid: true}, @@ -75,15 +75,15 @@ func (s *Store) GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEven for i, e := range events { upcomingEvents[i] = domain.UpcomingEvent{ ID: e.ID, - SportID: e.SportID.String, + SportID: e.SportID.Int32, MatchName: e.MatchName.String, HomeTeam: e.HomeTeam.String, AwayTeam: e.AwayTeam.String, - HomeTeamID: e.HomeTeamID.String, - AwayTeamID: e.AwayTeamID.String, + HomeTeamID: e.HomeTeamID.Int32, + AwayTeamID: e.AwayTeamID.Int32, HomeKitImage: e.HomeKitImage.String, AwayKitImage: e.AwayKitImage.String, - LeagueID: e.LeagueID.String, + LeagueID: e.LeagueID.Int32, LeagueName: e.LeagueName.String, LeagueCC: e.LeagueCc.String, StartTime: e.StartTime.Time.UTC(), @@ -103,15 +103,15 @@ func (s *Store) GetExpiredUpcomingEvents(ctx context.Context) ([]domain.Upcoming for i, e := range events { upcomingEvents[i] = domain.UpcomingEvent{ ID: e.ID, - SportID: e.SportID.String, + SportID: e.SportID.Int32, MatchName: e.MatchName.String, HomeTeam: e.HomeTeam.String, AwayTeam: e.AwayTeam.String, - HomeTeamID: e.HomeTeamID.String, - AwayTeamID: e.AwayTeamID.String, + HomeTeamID: e.HomeTeamID.Int32, + AwayTeamID: e.AwayTeamID.Int32, HomeKitImage: e.HomeKitImage.String, AwayKitImage: e.AwayKitImage.String, - LeagueID: e.LeagueID.String, + LeagueID: e.LeagueID.Int32, LeagueName: e.LeagueName.String, LeagueCC: e.LeagueCc.String, StartTime: e.StartTime.Time.UTC(), @@ -121,16 +121,16 @@ func (s *Store) GetExpiredUpcomingEvents(ctx context.Context) ([]domain.Upcoming return upcomingEvents, nil } -func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, limit domain.ValidInt64, offset domain.ValidInt64, leagueID domain.ValidString, sportID domain.ValidString, firstStartTime domain.ValidTime, lastStartTime domain.ValidTime) ([]domain.UpcomingEvent, int64, error) { +func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, limit domain.ValidInt64, offset domain.ValidInt64, leagueID domain.ValidInt32, sportID domain.ValidInt32, firstStartTime domain.ValidTime, lastStartTime domain.ValidTime) ([]domain.UpcomingEvent, int64, error) { events, err := s.queries.GetPaginatedUpcomingEvents(ctx, dbgen.GetPaginatedUpcomingEventsParams{ - LeagueID: pgtype.Text{ - String: leagueID.Value, - Valid: leagueID.Valid, + LeagueID: pgtype.Int4{ + Int32: leagueID.Value, + Valid: leagueID.Valid, }, - SportID: pgtype.Text{ - String: sportID.Value, - Valid: sportID.Valid, + SportID: pgtype.Int4{ + Int32: sportID.Value, + Valid: sportID.Valid, }, Limit: pgtype.Int4{ Int32: int32(limit.Value), @@ -157,15 +157,15 @@ func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, limit domain.Val for i, e := range events { upcomingEvents[i] = domain.UpcomingEvent{ ID: e.ID, - SportID: e.SportID.String, + SportID: e.SportID.Int32, MatchName: e.MatchName.String, HomeTeam: e.HomeTeam.String, AwayTeam: e.AwayTeam.String, - HomeTeamID: e.HomeTeamID.String, - AwayTeamID: e.AwayTeamID.String, + HomeTeamID: e.HomeTeamID.Int32, + AwayTeamID: e.AwayTeamID.Int32, HomeKitImage: e.HomeKitImage.String, AwayKitImage: e.AwayKitImage.String, - LeagueID: e.LeagueID.String, + LeagueID: e.LeagueID.Int32, LeagueName: e.LeagueName.String, LeagueCC: e.LeagueCc.String, StartTime: e.StartTime.Time.UTC(), @@ -173,13 +173,13 @@ func (s *Store) GetPaginatedUpcomingEvents(ctx context.Context, limit domain.Val } } totalCount, err := s.queries.GetTotalEvents(ctx, dbgen.GetTotalEventsParams{ - LeagueID: pgtype.Text{ - String: leagueID.Value, - Valid: leagueID.Valid, + LeagueID: pgtype.Int4{ + Int32: leagueID.Value, + Valid: leagueID.Valid, }, - SportID: pgtype.Text{ - String: sportID.Value, - Valid: sportID.Valid, + SportID: pgtype.Int4{ + Int32: sportID.Value, + Valid: sportID.Valid, }, FirstStartTime: pgtype.Timestamp{ Time: firstStartTime.Value.UTC(), @@ -205,15 +205,15 @@ func (s *Store) GetUpcomingEventByID(ctx context.Context, ID string) (domain.Upc return domain.UpcomingEvent{ ID: event.ID, - SportID: event.SportID.String, + SportID: event.SportID.Int32, MatchName: event.MatchName.String, HomeTeam: event.HomeTeam.String, AwayTeam: event.AwayTeam.String, - HomeTeamID: event.HomeTeamID.String, - AwayTeamID: event.AwayTeamID.String, + HomeTeamID: event.HomeTeamID.Int32, + AwayTeamID: event.AwayTeamID.Int32, HomeKitImage: event.HomeKitImage.String, AwayKitImage: event.AwayKitImage.String, - LeagueID: event.LeagueID.String, + LeagueID: event.LeagueID.Int32, LeagueName: event.LeagueName.String, LeagueCC: event.LeagueCc.String, StartTime: event.StartTime.Time.UTC(), diff --git a/internal/services/bet/service.go b/internal/services/bet/service.go index 65af3d7..cb65c9d 100644 --- a/internal/services/bet/service.go +++ b/internal/services/bet/service.go @@ -121,15 +121,11 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI if err != nil { return domain.CreateBetOutcome{}, err } - sportID, err := strconv.ParseInt(event.SportID, 10, 64) - if err != nil { - return domain.CreateBetOutcome{}, err - } newOutcome := domain.CreateBetOutcome{ EventID: eventID, OddID: oddID, MarketID: marketID, - SportID: sportID, + SportID: int64(event.SportID), HomeTeamName: event.HomeTeam, AwayTeamName: event.AwayTeam, MarketName: odds.MarketName, @@ -278,7 +274,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID return res, nil } -func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID, sportID, HomeTeam, AwayTeam string, StartTime time.Time, numMarkets int) ([]domain.CreateBetOutcome, float32, error) { +func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string, sportID int32, HomeTeam, AwayTeam string, StartTime time.Time, numMarkets int) ([]domain.CreateBetOutcome, float32, error) { var newOdds []domain.CreateBetOutcome var totalOdds float32 = 1 @@ -328,11 +324,6 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID, sportI s.logger.Error("Failed to parse odd", "error", err) continue } - sportID, err := strconv.ParseInt(sportID, 10, 64) - if err != nil { - s.logger.Error("Failed to get sport id", "error", err) - continue - } eventID, err := strconv.ParseInt(eventID, 10, 64) if err != nil { s.logger.Error("Failed to get event id", "error", err) @@ -356,7 +347,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID, sportI EventID: eventID, OddID: oddID, MarketID: marketID, - SportID: sportID, + SportID: int64(sportID), HomeTeamName: HomeTeam, AwayTeamName: AwayTeam, MarketName: marketName, @@ -379,7 +370,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID, sportI return newOdds, totalOdds, nil } -func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, leagueID, sportID domain.ValidString, firstStartTime, lastStartTime domain.ValidTime) (domain.CreateBetRes, error) { +func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, leagueID, sportID domain.ValidInt32, firstStartTime, lastStartTime domain.ValidTime) (domain.CreateBetRes, error) { // Get a unexpired event id diff --git a/internal/services/event/port.go b/internal/services/event/port.go index 94f4313..af8397e 100644 --- a/internal/services/event/port.go +++ b/internal/services/event/port.go @@ -11,7 +11,7 @@ type Service interface { FetchUpcomingEvents(ctx context.Context) error GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error) GetExpiredUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error) - GetPaginatedUpcomingEvents(ctx context.Context, limit domain.ValidInt64, offset domain.ValidInt64, leagueID domain.ValidString, sportID domain.ValidString, firstStartTime domain.ValidTime, lastStartTime domain.ValidTime) ([]domain.UpcomingEvent, int64, error) + GetPaginatedUpcomingEvents(ctx context.Context, limit domain.ValidInt64, offset domain.ValidInt64, leagueID domain.ValidInt32, sportID domain.ValidInt32, firstStartTime domain.ValidTime, lastStartTime domain.ValidTime) ([]domain.UpcomingEvent, int64, error) GetUpcomingEventByID(ctx context.Context, ID string) (domain.UpcomingEvent, error) // GetAndStoreMatchResult(ctx context.Context, eventID string) error diff --git a/internal/services/event/service.go b/internal/services/event/service.go index 1ad4310..2c6bc52 100644 --- a/internal/services/event/service.go +++ b/internal/services/event/service.go @@ -35,7 +35,7 @@ func (s *service) FetchLiveEvents(ctx context.Context) error { name string source string }{ - {"https://api.b365api.com/v1/bet365/inplay?sport_id=%d&&token=%s", "bet365"}, + {"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"}, } @@ -48,7 +48,6 @@ func (s *service) FetchLiveEvents(ctx context.Context) error { s.fetchLiveEvents(ctx, url.name, url.source) }() } - wg.Wait() return nil } @@ -118,17 +117,17 @@ func handleBet365prematch(body []byte, sportID int, source string) []domain.Even event := domain.Event{ ID: getString(ev["ID"]), - SportID: fmt.Sprintf("%d", sportID), + SportID: int32(sportID), MatchName: getString(ev["NA"]), Score: getString(ev["SS"]), MatchMinute: getInt(ev["TM"]), TimerStatus: getString(ev["TT"]), - HomeTeamID: getString(ev["HT"]), - AwayTeamID: getString(ev["AT"]), + HomeTeamID: getInt32(ev["HT"]), + AwayTeamID: getInt32(ev["AT"]), HomeKitImage: getString(ev["K1"]), AwayKitImage: getString(ev["K2"]), LeagueName: getString(ev["CT"]), - LeagueID: getString(ev["C2"]), + LeagueID: getInt32(ev["C2"]), LeagueCC: getString(ev["CB"]), StartTime: time.Now().UTC().Format(time.RFC3339), IsLive: true, @@ -159,17 +158,14 @@ func handleBetfairprematch(body []byte, sportID int, source string) []domain.Eve for _, ev := range data.Results { homeRaw, _ := ev["home"].(map[string]interface{}) - homeId, _ := homeRaw["id"].(string) - awayRaw, _ := ev["home"].(map[string]interface{}) - awayId, _ := awayRaw["id"].(string) event := domain.Event{ ID: getString(ev["id"]), - SportID: fmt.Sprintf("%d", sportID), + SportID: int32(sportID), TimerStatus: getString(ev["time_status"]), - HomeTeamID: homeId, - AwayTeamID: awayId, + HomeTeamID: getInt32(homeRaw["id"]), + AwayTeamID: getInt32(awayRaw["id"]), StartTime: time.Now().UTC().Format(time.RFC3339), IsLive: true, Status: "live", @@ -249,20 +245,21 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, url, sour if !slices.Contains(domain.SupportedLeagues, leagueID) { // fmt.Printf("⚠️ Skipping league %s (%d) as it is not supported\n", ev.League.Name, leagueID) skippedLeague = append(skippedLeague, ev.League.Name) - continue + // ! for now + // continue } event := domain.UpcomingEvent{ ID: ev.ID, - SportID: ev.SportID, + SportID: convertInt32(ev.SportID), MatchName: "", HomeTeam: ev.Home.Name, AwayTeam: "", // handle nil safely - HomeTeamID: ev.Home.ID, - AwayTeamID: "", + HomeTeamID: convertInt32(ev.Home.ID), + AwayTeamID: 0, HomeKitImage: "", AwayKitImage: "", - LeagueID: ev.League.ID, + LeagueID: convertInt32(ev.League.ID), LeagueName: ev.League.Name, LeagueCC: "", StartTime: time.Unix(startUnix, 0).UTC(), @@ -271,7 +268,7 @@ func (s *service) fetchUpcomingEventsFromProvider(ctx context.Context, url, sour if ev.Away != nil { event.AwayTeam = ev.Away.Name - event.AwayTeamID = ev.Away.ID + event.AwayTeamID = convertInt32(ev.Away.ID) event.MatchName = ev.Home.Name + " vs " + ev.Away.Name } @@ -309,6 +306,20 @@ func getInt(v interface{}) int { } 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 (s *service) GetAllUpcomingEvents(ctx context.Context) ([]domain.UpcomingEvent, error) { return s.store.GetAllUpcomingEvents(ctx) } @@ -317,7 +328,7 @@ func (s *service) GetExpiredUpcomingEvents(ctx context.Context) ([]domain.Upcomi return s.store.GetExpiredUpcomingEvents(ctx) } -func (s *service) GetPaginatedUpcomingEvents(ctx context.Context, limit domain.ValidInt64, offset domain.ValidInt64, leagueID domain.ValidString, sportID domain.ValidString, firstStartTime domain.ValidTime, lastStartTime domain.ValidTime) ([]domain.UpcomingEvent, int64, error) { +func (s *service) GetPaginatedUpcomingEvents(ctx context.Context, limit domain.ValidInt64, offset domain.ValidInt64, leagueID domain.ValidInt32, sportID domain.ValidInt32, firstStartTime domain.ValidTime, lastStartTime domain.ValidTime) ([]domain.UpcomingEvent, int64, error) { return s.store.GetPaginatedUpcomingEvents(ctx, limit, offset, leagueID, sportID, firstStartTime, lastStartTime) } diff --git a/internal/services/odds/service.go b/internal/services/odds/service.go index 85ca2f7..488c9bb 100644 --- a/internal/services/odds/service.go +++ b/internal/services/odds/service.go @@ -38,14 +38,15 @@ func New(store *repository.Store, cfg *config.Config, logger *slog.Logger) *Serv func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error { var wg sync.WaitGroup errChan := make(chan error, 2) - wg.Add(2) + // wg.Add(2) + wg.Add(1) - go func() { - defer wg.Done() - if err := s.fetchBet365Odds(ctx); err != nil { - errChan <- fmt.Errorf("bet365 odds fetching error: %w", err) - } - }() + // go func() { + // defer wg.Done() + // if err := s.fetchBet365Odds(ctx); err != nil { + // errChan <- fmt.Errorf("bet365 odds fetching error: %w", err) + // } + // }() go func() { defer wg.Done() @@ -112,9 +113,7 @@ func (s *ServiceImpl) fetchBet365Odds(ctx context.Context) error { continue } - sportID, err := strconv.ParseInt(event.SportID, 10, 64) - - switch sportID { + switch event.SportID { case domain.FOOTBALL: if err := s.parseFootball(ctx, oddsData.Results[0]); err != nil { s.logger.Error("Error while inserting football odd") @@ -142,7 +141,7 @@ func (s *ServiceImpl) fetchBet365Odds(ctx context.Context) error { 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{12, 7} + 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) @@ -181,13 +180,13 @@ func (s *ServiceImpl) fetchBwinOdds(ctx context.Context) error { event := domain.Event{ ID: strconv.Itoa(getInt(res["Id"])), - SportID: strconv.Itoa(getInt(res["SportId"])), - LeagueID: strconv.Itoa(getInt(res["LeagueId"])), + SportID: int32(getInt(res["SportId"])), + LeagueID: int32(getInt(res["LeagueId"])), LeagueName: getString(res["Leaguename"]), HomeTeam: getString(res["HomeTeam"]), - HomeTeamID: strconv.Itoa(getInt(res["HomeTeamId"])), + HomeTeamID: int32(getInt(res["HomeTeamId"])), AwayTeam: getString(res["AwayTeam"]), - AwayTeamID: strconv.Itoa(getInt(res["AwayTeamId"])), + AwayTeamID: int32(getInt(res["AwayTeamId"])), StartTime: time.Now().UTC().Format(time.RFC3339), TimerStatus: "1", IsLive: true, @@ -200,23 +199,25 @@ func (s *ServiceImpl) fetchBwinOdds(ctx context.Context) error { continue } - for _, m := range getMapArray(res["Markets"]) { - name := getMap(m["name"]) - marketName := getString(name["value"]) + for _, market := range []string{"Markets, optionMarkets"} { + for _, m := range getMapArray(res[market]) { + name := getMap(m["name"]) + marketName := getString(name["value"]) + + market := domain.Market{ + EventID: event.ID, + MarketID: getString(m["id"]), + MarketCategory: getString(m["category"]), + MarketName: marketName, + Source: "bwin", + } + + results := getMapArray(m["results"]) + market.Odds = results + + s.store.SaveNonLiveMarket(ctx, market) - market := domain.Market{ - EventID: event.ID, - MarketID: getString(m["id"]), - MarketCategory: getString(m["category"]), - MarketName: marketName, - Source: "bwin", } - - results := getMapArray(m["results"]) - market.Odds = results - - s.store.SaveNonLiveMarket(ctx, market) - } } diff --git a/internal/services/result/service.go b/internal/services/result/service.go index bc73535..b3f285d 100644 --- a/internal/services/result/service.go +++ b/internal/services/result/service.go @@ -80,14 +80,8 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error { continue } - sportID, err := strconv.ParseInt(event.SportID, 10, 64) - if err != nil { - s.logger.Error("Sport ID is invalid", "event_id", outcome.EventID, "error", err) - isDeleted = false - continue - } // TODO: optimize this because the result is being fetched for each outcome which will have the same event id but different market id - result, err := s.fetchResult(ctx, outcome.EventID, outcome.OddID, outcome.MarketID, sportID, outcome) + result, err := s.fetchResult(ctx, outcome.EventID, outcome.OddID, outcome.MarketID, int64(event.SportID), outcome) if err != nil { if err == ErrEventIsNotActive { s.logger.Warn("Event is not active", "event_id", outcome.EventID, "error", err) diff --git a/internal/web_server/handlers/bet_handler.go b/internal/web_server/handlers/bet_handler.go index b5f87ec..6070740 100644 --- a/internal/web_server/handlers/bet_handler.go +++ b/internal/web_server/handlers/bet_handler.go @@ -71,18 +71,28 @@ func (h *Handler) RandomBet(c *fiber.Ctx) error { userID := c.Locals("user_id").(int64) // role := c.Locals("role").(domain.Role) - leagueIDQuery := c.Query("league_id") - sportIDQuery := c.Query("sport_id") + leagueIDQuery, err := strconv.Atoi(c.Query("league_id")) + if err != nil { + h.logger.Error("invalid league id", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "invalid league id", nil, nil) + } + + sportIDQuery, err := strconv.Atoi(c.Query("sport_id")) + if err != nil { + h.logger.Error("invalid sport id", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "invalid sport id", nil, nil) + } + firstStartTimeQuery := c.Query("first_start_time") lastStartTimeQuery := c.Query("last_start_time") - leagueID := domain.ValidString{ - Value: leagueIDQuery, - Valid: leagueIDQuery != "", + leagueID := domain.ValidInt32{ + Value: int32(leagueIDQuery), + Valid: leagueIDQuery != 0, } - sportID := domain.ValidString{ - Value: sportIDQuery, - Valid: sportIDQuery != "", + sportID := domain.ValidInt32{ + Value: int32(sportIDQuery), + Valid: sportIDQuery != 0, } var firstStartTime domain.ValidTime @@ -122,7 +132,6 @@ func (h *Handler) RandomBet(c *fiber.Ctx) error { } var res domain.CreateBetRes - var err error for i := 0; i < int(req.NumberOfBets); i++ { res, err = h.betSvc.PlaceRandomBet(c.Context(), userID, req.BranchID, leagueID, sportID, firstStartTime, lastStartTime) diff --git a/internal/web_server/handlers/prematch.go b/internal/web_server/handlers/prematch.go index b8d3778..f070d5e 100644 --- a/internal/web_server/handlers/prematch.go +++ b/internal/web_server/handlers/prematch.go @@ -107,18 +107,27 @@ func (h *Handler) GetRawOddsByMarketID(c *fiber.Ctx) error { func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error { page := c.QueryInt("page", 1) pageSize := c.QueryInt("page_size", 10) - leagueIDQuery := c.Query("league_id") - sportIDQuery := c.Query("sport_id") + leagueIDQuery, err := strconv.Atoi(c.Query("league_id")) + if err != nil { + h.logger.Error("invalid league id", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "invalid league id", nil, nil) + } + + sportIDQuery, err := strconv.Atoi(c.Query("sport_id")) + if err != nil { + h.logger.Error("invalid sport id", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "invalid sport id", nil, nil) + } firstStartTimeQuery := c.Query("first_start_time") lastStartTimeQuery := c.Query("last_start_time") - leagueID := domain.ValidString{ - Value: leagueIDQuery, - Valid: leagueIDQuery != "", + leagueID := domain.ValidInt32{ + Value: int32(leagueIDQuery), + Valid: leagueIDQuery != 0, } - sportID := domain.ValidString{ - Value: sportIDQuery, - Valid: sportIDQuery != "", + sportID := domain.ValidInt32{ + Value: int32(sportIDQuery), + Valid: sportIDQuery != 0, } var firstStartTime domain.ValidTime